@nexgrid/react 0.1.0 → 0.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -1,6 +1,6 @@
1
1
  "use client";
2
2
 
3
- // src/nex-grid.tsx
3
+ // src/table-x.tsx
4
4
  import * as React4 from "react";
5
5
  import {
6
6
  DENSITIES,
@@ -16,8 +16,11 @@ import {
16
16
  getPageNumbers,
17
17
  getRecordRange,
18
18
  initialHiddenColumns,
19
+ isEditable,
20
+ isFilterable,
19
21
  isHideable,
20
22
  isPageSize,
23
+ isPinned,
21
24
  isSortable,
22
25
  primarySort,
23
26
  resolveLocale,
@@ -26,10 +29,22 @@ import {
26
29
  toExportColumns,
27
30
  totalPagesFor,
28
31
  visibleColumns,
32
+ withFilter,
29
33
  withPage,
30
34
  withPageSize,
31
35
  withSearch,
32
- withToggledSort
36
+ withToggledSort,
37
+ withToggledMultiSort,
38
+ copyToClipboard,
39
+ clearGridState,
40
+ loadGridState,
41
+ saveGridState,
42
+ getCellValue as getCellValue2,
43
+ getCellText as getCellText2,
44
+ queryClientData,
45
+ defaultQuery,
46
+ flattenColumns,
47
+ buildHeaderRows
33
48
  } from "@nexgrid/core";
34
49
 
35
50
  // src/icons.tsx
@@ -70,29 +85,22 @@ function XIcon(props) {
70
85
  /* @__PURE__ */ jsx("path", { d: "m6 6 12 12" })
71
86
  ] });
72
87
  }
73
- function SlidersIcon(props) {
88
+ function ColumnsIcon(props) {
74
89
  return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
75
- /* @__PURE__ */ jsx("line", { x1: "21", x2: "14", y1: "4", y2: "4" }),
76
- /* @__PURE__ */ jsx("line", { x1: "10", x2: "3", y1: "4", y2: "4" }),
77
- /* @__PURE__ */ jsx("line", { x1: "21", x2: "12", y1: "12", y2: "12" }),
78
- /* @__PURE__ */ jsx("line", { x1: "8", x2: "3", y1: "12", y2: "12" }),
79
- /* @__PURE__ */ jsx("line", { x1: "21", x2: "16", y1: "20", y2: "20" }),
80
- /* @__PURE__ */ jsx("line", { x1: "12", x2: "3", y1: "20", y2: "20" }),
81
- /* @__PURE__ */ jsx("line", { x1: "14", x2: "14", y1: "2", y2: "6" }),
82
- /* @__PURE__ */ jsx("line", { x1: "8", x2: "8", y1: "10", y2: "14" }),
83
- /* @__PURE__ */ jsx("line", { x1: "16", x2: "16", y1: "18", y2: "22" })
90
+ /* @__PURE__ */ jsx("rect", { x: "3", y: "3", width: "18", height: "18", rx: "2" }),
91
+ /* @__PURE__ */ jsx("path", { d: "M9 3v18" }),
92
+ /* @__PURE__ */ jsx("path", { d: "M15 3v18" })
84
93
  ] });
85
94
  }
86
- function FilterIcon(props) {
87
- return /* @__PURE__ */ jsx(Icon, { ...props, children: /* @__PURE__ */ jsx("polygon", { points: "22 3 2 3 10 12.46 10 19 14 21 14 12.46 22 3" }) });
88
- }
89
- function DownloadIcon(props) {
95
+ function DensityIcon(props) {
90
96
  return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
91
- /* @__PURE__ */ jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
92
- /* @__PURE__ */ jsx("polyline", { points: "7 10 12 15 17 10" }),
93
- /* @__PURE__ */ jsx("line", { x1: "12", x2: "12", y1: "15", y2: "3" })
97
+ /* @__PURE__ */ jsx("rect", { x: "3", y: "4.5", width: "18", height: "6", rx: "1.5" }),
98
+ /* @__PURE__ */ jsx("rect", { x: "3", y: "13.5", width: "18", height: "6", rx: "1.5" })
94
99
  ] });
95
100
  }
101
+ function ChevronDownIcon(props) {
102
+ return /* @__PURE__ */ jsx(Icon, { ...props, children: /* @__PURE__ */ jsx("path", { d: "m6 9 6 6 6-6" }) });
103
+ }
96
104
  function FileSpreadsheetIcon(props) {
97
105
  return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
98
106
  /* @__PURE__ */ jsx("path", { d: "M15 2H6a2 2 0 0 0-2 2v16a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V7Z" }),
@@ -141,6 +149,26 @@ function ArrowUpDownIcon(props) {
141
149
  function CheckIcon(props) {
142
150
  return /* @__PURE__ */ jsx(Icon, { ...props, children: /* @__PURE__ */ jsx("path", { d: "M20 6 9 17l-5-5" }) });
143
151
  }
152
+ function RotateCcwIcon(props) {
153
+ return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
154
+ /* @__PURE__ */ jsx("path", { d: "M3 12a9 9 0 1 0 9-9 9.75 9.75 0 0 0-6.74 2.74L3 8" }),
155
+ /* @__PURE__ */ jsx("path", { d: "M3 3v5h5" })
156
+ ] });
157
+ }
158
+ function DownloadTrayIcon(props) {
159
+ return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
160
+ /* @__PURE__ */ jsx("path", { d: "M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4" }),
161
+ /* @__PURE__ */ jsx("polyline", { points: "7 10 12 15 17 10" }),
162
+ /* @__PURE__ */ jsx("line", { x1: "12", x2: "12", y1: "15", y2: "3" })
163
+ ] });
164
+ }
165
+ function DotsVerticalIcon(props) {
166
+ return /* @__PURE__ */ jsxs(Icon, { ...props, children: [
167
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "5", r: "1.5" }),
168
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "12", r: "1.5" }),
169
+ /* @__PURE__ */ jsx("circle", { cx: "12", cy: "19", r: "1.5" })
170
+ ] });
171
+ }
144
172
 
145
173
  // src/render.tsx
146
174
  import * as React from "react";
@@ -176,9 +204,9 @@ function bodyCellStyle(col) {
176
204
  }
177
205
  function headerInnerClass(col) {
178
206
  const align = col.meta?.align;
179
- if (align === "center") return "nxg-th-inner nxg-th-inner--center";
180
- if (align === "right") return "nxg-th-inner nxg-th-inner--right";
181
- return "nxg-th-inner";
207
+ if (align === "center") return "tbx-th-inner tbx-th-inner--center";
208
+ if (align === "right") return "tbx-th-inner tbx-th-inner--right";
209
+ return "tbx-th-inner";
182
210
  }
183
211
  function renderTemplate(template, values) {
184
212
  const parts = [];
@@ -250,8 +278,8 @@ function useDropdown() {
250
278
  return { isOpen, toggle, close, containerRef, triggerRef };
251
279
  }
252
280
 
253
- // src/nex-grid.tsx
254
- import { jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
281
+ // src/table-x.tsx
282
+ import { Fragment as Fragment3, jsx as jsx3, jsxs as jsxs2 } from "react/jsx-runtime";
255
283
  function defaultRowId(row) {
256
284
  const record = row;
257
285
  const id = record === null || record === void 0 ? void 0 : record["id"];
@@ -262,53 +290,189 @@ var DENSITY_LABEL_KEY = {
262
290
  default: "densityDefault",
263
291
  comfortable: "densityComfortable"
264
292
  };
265
- function NexGrid({
266
- columns,
267
- data,
268
- total,
269
- query,
270
- onQueryChange,
271
- caption,
272
- density: initialDensity = "default",
273
- isLoading = false,
274
- error = false,
275
- onRetry,
276
- enableSelection = false,
277
- onSelectionChange,
278
- enableSearch = true,
279
- searchPlaceholder,
280
- toolbarActions,
281
- onRowClick,
282
- getRowId = defaultRowId,
283
- className,
284
- showSerialNumber = true,
285
- enableExport = true,
286
- exportFileName,
287
- onExportAll,
288
- fetchEndpoint,
289
- badgeRules,
290
- locale: localeOverrides,
291
- onNotify,
292
- theme = "light"
293
- }) {
293
+ function TableX(props) {
294
+ const {
295
+ columns,
296
+ data,
297
+ total: totalProp,
298
+ query: queryProp,
299
+ onQueryChange: onQueryChangeProp,
300
+ clientSidePagination,
301
+ paginationMode,
302
+ caption,
303
+ density: initialDensity = "default",
304
+ isLoading = false,
305
+ error = false,
306
+ onRetry,
307
+ enableSelection = false,
308
+ selectionMode = "multi",
309
+ enableColumnFilters = true,
310
+ enableColumnResize = true,
311
+ onSelectionChange,
312
+ enableSearch = true,
313
+ searchPlaceholder,
314
+ enableColumns = true,
315
+ showColumnsButton,
316
+ enableDensity = true,
317
+ showDensityButton,
318
+ enableExport = true,
319
+ showExportButton,
320
+ enableSorting = true,
321
+ enablePagination = true,
322
+ showPagination,
323
+ enableRowsPerPage = true,
324
+ showRowsPerPage,
325
+ enableJumpToPage = true,
326
+ showJumpToPage,
327
+ showToolbar = true,
328
+ showFooter = true,
329
+ renderExpandedRow,
330
+ enableBulkActions = true,
331
+ bulkActions,
332
+ enableSummaryRow,
333
+ enableColumnReorder = false,
334
+ onColumnOrderChange,
335
+ onCellEdit,
336
+ storageKey,
337
+ showFilterPills = true,
338
+ toolbarActions,
339
+ onRowClick,
340
+ getRowId = defaultRowId,
341
+ className,
342
+ showSerialNumber = true,
343
+ exportFileName,
344
+ onExportAll,
345
+ fetchEndpoint,
346
+ badgeRules,
347
+ locale: localeOverrides,
348
+ onNotify,
349
+ theme = "light"
350
+ } = props;
351
+ const isClientSide = clientSidePagination === true || paginationMode === "client" || onQueryChangeProp === void 0 && queryProp === void 0;
352
+ const [internalQuery, setInternalQuery] = React4.useState(() => queryProp ?? defaultQuery());
353
+ const query = queryProp ?? internalQuery;
354
+ const onQueryChange = React4.useCallback(
355
+ (next) => {
356
+ setInternalQuery(next);
357
+ onQueryChangeProp?.(next);
358
+ },
359
+ [onQueryChangeProp]
360
+ );
361
+ const clientPaged = React4.useMemo(() => {
362
+ if (!isClientSide) return null;
363
+ return queryClientData(data, query);
364
+ }, [isClientSide, data, query]);
365
+ const rows = isClientSide ? clientPaged?.items ?? data : data;
366
+ const total = isClientSide ? clientPaged?.total ?? data.length : totalProp ?? data.length;
294
367
  const locale = resolveLocale(localeOverrides);
295
368
  const boolLabels = { yes: locale.booleanYes, no: locale.booleanNo };
369
+ const isColumnsVisible = enableColumns && showColumnsButton !== false;
370
+ const isDensityVisible = enableDensity && showDensityButton !== false;
371
+ const isExportVisible = enableExport && showExportButton !== false;
372
+ const isPaginationVisible = enablePagination && showPagination !== false;
373
+ const isRowsPerPageVisible = enableRowsPerPage && showRowsPerPage !== false;
374
+ const isJumpToPageVisible = enableJumpToPage && showJumpToPage !== false;
375
+ const [colList, setColList] = React4.useState(columns);
376
+ React4.useEffect(() => {
377
+ setColList(columns);
378
+ }, [columns]);
296
379
  const [density, setDensity] = React4.useState(initialDensity);
297
380
  const [hiddenCols, setHiddenCols] = React4.useState(
298
- () => initialHiddenColumns(columns)
381
+ () => initialHiddenColumns(flattenColumns(columns))
299
382
  );
300
383
  const [selectedIds, setSelectedIds] = React4.useState(
301
384
  () => /* @__PURE__ */ new Set()
302
385
  );
386
+ const [expandedRows, setExpandedRows] = React4.useState(
387
+ () => /* @__PURE__ */ new Set()
388
+ );
389
+ const [editingCell, setEditingCell] = React4.useState(null);
390
+ const [draggedColId, setDraggedColId] = React4.useState(null);
303
391
  const [isExporting, setIsExporting] = React4.useState(false);
392
+ const [openFilterCol, setOpenFilterCol] = React4.useState(null);
393
+ const [colWidths, setColWidths] = React4.useState({});
394
+ React4.useEffect(() => {
395
+ if (!storageKey) return;
396
+ const persisted = loadGridState(storageKey);
397
+ if (persisted) {
398
+ if (persisted.density) setDensity(persisted.density);
399
+ if (persisted.columnWidths) setColWidths(persisted.columnWidths);
400
+ if (persisted.hiddenColumns && Array.isArray(persisted.hiddenColumns)) {
401
+ const hiddenMap = {};
402
+ for (const col of columns) {
403
+ const id = getColumnId2(col);
404
+ if (id) hiddenMap[id] = persisted.hiddenColumns.includes(id);
405
+ }
406
+ setHiddenCols(hiddenMap);
407
+ }
408
+ if (persisted.columnOrder && Array.isArray(persisted.columnOrder)) {
409
+ const colMap = new Map(columns.map((c) => [getColumnId2(c), c]));
410
+ const reordered = [];
411
+ for (const id of persisted.columnOrder) {
412
+ const col = colMap.get(id);
413
+ if (col) {
414
+ reordered.push(col);
415
+ colMap.delete(id);
416
+ }
417
+ }
418
+ for (const remaining of colMap.values()) {
419
+ reordered.push(remaining);
420
+ }
421
+ if (reordered.length === columns.length) {
422
+ setColList(reordered);
423
+ }
424
+ }
425
+ }
426
+ }, [storageKey, columns]);
427
+ React4.useEffect(() => {
428
+ if (!storageKey) return;
429
+ const hiddenList = Object.entries(hiddenCols).filter(([_, isHidden]) => isHidden).map(([id]) => id);
430
+ saveGridState(storageKey, {
431
+ density,
432
+ columnWidths: colWidths,
433
+ columnOrder: colList.map(getColumnId2).filter(Boolean),
434
+ hiddenColumns: hiddenList
435
+ });
436
+ }, [storageKey, density, hiddenCols, colList, colWidths]);
304
437
  const columnsMenu = useDropdown();
305
438
  const densityMenu = useDropdown();
306
439
  const exportMenu = useDropdown();
440
+ const handleResetView = React4.useCallback(() => {
441
+ if (storageKey) {
442
+ clearGridState(storageKey);
443
+ }
444
+ setDensity(initialDensity);
445
+ setColWidths({});
446
+ setColList(columns);
447
+ setHiddenCols(initialHiddenColumns(flattenColumns(columns)));
448
+ columnsMenu.close();
449
+ }, [storageKey, initialDensity, columns, columnsMenu]);
450
+ const leafCols = React4.useMemo(() => flattenColumns(colList), [colList]);
451
+ const autoFitColumn = React4.useCallback((id) => {
452
+ const col = leafCols.find((c) => getColumnId2(c) === id);
453
+ if (!col) return;
454
+ const meta = col.meta ?? {};
455
+ let maxContentWidth = 0;
456
+ const headerTitle = getColumnTitle(col) || id;
457
+ maxContentWidth = Math.max(maxContentWidth, headerTitle.length * 8.5 + 50);
458
+ for (const row of rows) {
459
+ const rawVal = getCellValue2(col, row);
460
+ const val = getCellText2(rawVal);
461
+ if (val) {
462
+ maxContentWidth = Math.max(maxContentWidth, String(val).length * 8 + 26);
463
+ }
464
+ }
465
+ const minW = meta.minWidth ?? 60;
466
+ const maxW = 550;
467
+ const finalWidth = Math.min(maxW, Math.max(minW, Math.round(maxContentWidth)));
468
+ setColWidths((prev) => ({ ...prev, [id]: finalWidth }));
469
+ }, [leafCols, rows]);
307
470
  const instanceId = React4.useId();
308
471
  const columnsButtonId = `${instanceId}-columns`;
309
472
  const densityButtonId = `${instanceId}-density`;
310
473
  const exportButtonId = `${instanceId}-export`;
311
474
  const jumpInputId = `${instanceId}-jump`;
475
+ const rootRef = React4.useRef(null);
312
476
  const notify = (type, message) => {
313
477
  onNotify?.({ type, message });
314
478
  };
@@ -317,127 +481,193 @@ function NexGrid({
317
481
  const totalPages = totalPagesFor(total, pageSize);
318
482
  const range = getRecordRange(currentPage, pageSize, total);
319
483
  const sort = primarySort(query);
484
+ const headerRows = React4.useMemo(
485
+ () => buildHeaderRows(colList, hiddenCols),
486
+ [colList, hiddenCols]
487
+ );
320
488
  const visible = React4.useMemo(
321
- () => visibleColumns(columns, hiddenCols),
322
- [columns, hiddenCols]
489
+ () => visibleColumns(leafCols, hiddenCols),
490
+ [leafCols, hiddenCols]
323
491
  );
324
- const hideable = React4.useMemo(() => columns.filter(isHideable), [columns]);
492
+ const hideable = React4.useMemo(() => leafCols.filter(isHideable), [leafCols]);
325
493
  const pageItems = React4.useMemo(
326
494
  () => getPageNumbers(currentPage, totalPages),
327
495
  [currentPage, totalPages]
328
496
  );
329
- const pageRowIds = React4.useMemo(() => data.map((row) => getRowId(row)), [data, getRowId]);
330
- const columnCount = visible.length + (showSerialNumber ? 1 : 0) + (enableSelection ? 1 : 0);
497
+ const pageRowIds = React4.useMemo(() => rows.map((row) => getRowId(row)), [rows, getRowId]);
498
+ const columnCount = visible.length + (renderExpandedRow ? 1 : 0) + (showSerialNumber ? 1 : 0) + (enableSelection ? 1 : 0);
499
+ const getPinnedOffsets = (visibleCols) => {
500
+ const leftOffsets2 = /* @__PURE__ */ new Map();
501
+ const rightOffsets2 = /* @__PURE__ */ new Map();
502
+ let currentLeft = 0;
503
+ let lastLeftPinnedId2 = null;
504
+ if (renderExpandedRow) {
505
+ leftOffsets2.set("__expand", currentLeft);
506
+ currentLeft += 40;
507
+ lastLeftPinnedId2 = "__expand";
508
+ }
509
+ if (enableSelection) {
510
+ leftOffsets2.set("__select", currentLeft);
511
+ currentLeft += 44;
512
+ lastLeftPinnedId2 = "__select";
513
+ }
514
+ if (showSerialNumber) {
515
+ leftOffsets2.set("__serial", currentLeft);
516
+ currentLeft += 56;
517
+ lastLeftPinnedId2 = "__serial";
518
+ }
519
+ for (const col of visibleCols) {
520
+ const id = getColumnId2(col);
521
+ const pin = col.meta?.pinned;
522
+ const width = colWidths[id] ?? col.meta?.width ?? 140;
523
+ if (pin === "left") {
524
+ leftOffsets2.set(id, currentLeft);
525
+ currentLeft += width;
526
+ lastLeftPinnedId2 = id;
527
+ }
528
+ }
529
+ let currentRight = 0;
530
+ let firstRightPinnedId2 = null;
531
+ for (let i = visibleCols.length - 1; i >= 0; i--) {
532
+ const col = visibleCols[i];
533
+ if (!col) continue;
534
+ const id = getColumnId2(col);
535
+ const pin = col.meta?.pinned;
536
+ const width = colWidths[id] ?? col.meta?.width ?? 140;
537
+ if (pin === "right") {
538
+ rightOffsets2.set(id, currentRight);
539
+ currentRight += width;
540
+ firstRightPinnedId2 = id;
541
+ }
542
+ }
543
+ return { leftOffsets: leftOffsets2, rightOffsets: rightOffsets2, lastLeftPinnedId: lastLeftPinnedId2, firstRightPinnedId: firstRightPinnedId2 };
544
+ };
545
+ const { leftOffsets, rightOffsets, lastLeftPinnedId, firstRightPinnedId } = getPinnedOffsets(visible);
331
546
  const [searchText, setSearchText] = useDebouncedSearch(query.q ?? "", (text) => {
332
547
  onQueryChange(withSearch(query, text));
333
548
  });
334
549
  const [jumpText, setJumpText] = React4.useState(() => String(currentPage));
335
550
  React4.useEffect(() => setJumpText(String(currentPage)), [currentPage]);
336
- const submitJump = (event) => {
337
- event?.preventDefault();
338
- const parsed = Number.parseInt(jumpText, 10);
339
- if (Number.isFinite(parsed) && parsed >= 1 && parsed <= totalPages) {
340
- setJumpText(String(parsed));
341
- if (parsed !== currentPage) onQueryChange(withPage(query, parsed, totalPages));
551
+ const submitJump = (raw) => {
552
+ const page = Number.parseInt(raw, 10);
553
+ if (Number.isNaN(page) || page < 1 || page > totalPages) {
554
+ setJumpText(String(currentPage));
342
555
  return;
343
556
  }
344
- setJumpText(String(currentPage));
557
+ if (page !== currentPage) {
558
+ onQueryChange(withPage(query, page, totalPages));
559
+ }
345
560
  };
346
- const toggleSort = (columnId) => {
347
- onQueryChange(withToggledSort(query, columnId));
561
+ const toggleSort = (field, multiSort = false) => {
562
+ const next = multiSort ? withToggledMultiSort(query, field) : withToggledSort(query, field);
563
+ onQueryChange(next);
348
564
  };
349
- const selectedOnPage = pageRowIds.filter((id) => selectedIds.has(id)).length;
350
- const allPageSelected = pageRowIds.length > 0 && selectedOnPage === pageRowIds.length;
351
- const somePageSelected = selectedOnPage > 0 && !allPageSelected;
352
- const commitSelection = (next) => {
565
+ const isSingleSelect = selectionMode === "single";
566
+ const allPageSelected = pageRowIds.length > 0 && pageRowIds.every((id) => selectedIds.has(id));
567
+ const somePageSelected = !allPageSelected && pageRowIds.some((id) => selectedIds.has(id));
568
+ const toggleSelectAll = () => {
569
+ if (isSingleSelect) return;
570
+ const next = new Set(selectedIds);
571
+ if (allPageSelected) {
572
+ for (const id of pageRowIds) next.delete(id);
573
+ } else {
574
+ for (const id of pageRowIds) next.add(id);
575
+ }
353
576
  setSelectedIds(next);
354
577
  onSelectionChange?.(Array.from(next), false);
355
578
  };
356
- const toggleSelectAll = () => {
357
- const next = new Set(selectedIds);
358
- for (const id of pageRowIds) {
359
- if (allPageSelected) next.delete(id);
579
+ const toggleSelectRow = (id) => {
580
+ const next = isSingleSelect ? selectedIds.has(id) ? /* @__PURE__ */ new Set() : /* @__PURE__ */ new Set([id]) : new Set(selectedIds);
581
+ if (!isSingleSelect) {
582
+ if (next.has(id)) next.delete(id);
360
583
  else next.add(id);
361
584
  }
362
- commitSelection(next);
585
+ setSelectedIds(next);
586
+ onSelectionChange?.(Array.from(next), false);
363
587
  };
364
- const toggleSelectRow = (id) => {
365
- const next = new Set(selectedIds);
366
- if (next.has(id)) next.delete(id);
367
- else next.add(id);
368
- commitSelection(next);
588
+ const deselectAll = () => {
589
+ setSelectedIds(/* @__PURE__ */ new Set());
590
+ onSelectionChange?.([], false);
591
+ };
592
+ const toggleExpandRow = (id) => {
593
+ setExpandedRows((prev) => {
594
+ const next = new Set(prev);
595
+ if (next.has(id)) next.delete(id);
596
+ else next.add(id);
597
+ return next;
598
+ });
369
599
  };
370
600
  const toggleColumn = (columnId) => {
371
601
  setHiddenCols((prev) => ({ ...prev, [columnId]: prev[columnId] !== true }));
372
602
  };
373
- const collectExportRows = async () => {
374
- if (data.length >= total || !fetchEndpoint) return data;
375
- notify("info", formatMessage(locale.exportFetchingAll, { total: total.toLocaleString() }));
376
- try {
377
- const result = await fetchAllPages(async (page, size) => {
378
- const url = buildQueryUrl(fetchEndpoint, {
379
- ...query,
380
- page,
381
- pageSize: isPageSize(size) ? size : query.pageSize
382
- });
383
- const response = await fetch(url, { cache: "no-store" });
384
- if (!response.ok) throw new Error(`HTTP ${response.status}`);
385
- return await response.json();
386
- });
387
- return result.items;
388
- } catch {
389
- notify("error", locale.exportFetchFailed);
390
- return data;
391
- }
392
- };
393
603
  const handleExport = async (format) => {
394
604
  exportMenu.close();
395
605
  if (onExportAll) {
396
- void onExportAll();
606
+ try {
607
+ await onExportAll();
608
+ } catch (err) {
609
+ notify("error", err instanceof Error ? err.message : "Export failed");
610
+ }
397
611
  return;
398
612
  }
399
613
  setIsExporting(true);
614
+ notify("info", "Exporting...");
400
615
  try {
401
- const rows = await collectExportRows();
402
- if (rows.length === 0) {
403
- notify("error", locale.exportNoData);
404
- return;
616
+ let exportRows = rows;
617
+ if (fetchEndpoint) {
618
+ const full = await fetchAllPages(async (page, size) => {
619
+ const url = buildQueryUrl(fetchEndpoint, {
620
+ ...query,
621
+ page,
622
+ pageSize: isPageSize(size) ? size : query.pageSize
623
+ });
624
+ const response = await fetch(url, { cache: "no-store" });
625
+ if (!response.ok) throw new Error(`HTTP ${response.status}`);
626
+ return await response.json();
627
+ });
628
+ exportRows = full.items;
629
+ } else if (isClientSide) {
630
+ const full = queryClientData(data, query, { paginate: false });
631
+ exportRows = full.items;
405
632
  }
406
633
  const exportColumns = toExportColumns(visible, boolLabels);
407
634
  const prefix = exportFileName ?? filePrefixFromCaption(caption);
408
- if (format === "excel") {
409
- const count = downloadExcel({
635
+ if (format === "clipboard") {
636
+ const ok = await copyToClipboard(exportRows, exportColumns);
637
+ if (ok) notify("success", "Copied to clipboard");
638
+ else notify("error", "Failed to copy to clipboard");
639
+ } else if (format === "excel") {
640
+ downloadExcel({
410
641
  filename: prefix,
411
642
  caption,
412
- rows,
643
+ rows: exportRows,
413
644
  columns: exportColumns,
414
645
  badgeRules,
415
646
  serialHeader: locale.serialHeader
416
647
  });
417
- notify(
418
- "success",
419
- formatMessage(locale.exportExcelSuccess, { count: count.toLocaleString() })
420
- );
648
+ notify("success", "Excel downloaded");
421
649
  } else {
422
- const count = downloadCsv(timestampedFilename(prefix), rows, exportColumns);
423
- notify(
424
- "success",
425
- formatMessage(locale.exportCsvSuccess, { count: count.toLocaleString() })
426
- );
650
+ downloadCsv(timestampedFilename(prefix), exportRows, exportColumns);
651
+ notify("success", "CSV downloaded");
427
652
  }
653
+ } catch {
654
+ notify("error", "Export failed");
428
655
  } finally {
429
656
  setIsExporting(false);
430
657
  }
431
658
  };
432
- const rootClassName = [
433
- "nxg-root",
434
- theme === "dark" ? "nxg-dark" : theme === "auto" ? "nxg-auto" : void 0,
659
+ const rootClasses = [
660
+ "tbx-root",
661
+ theme === "dark" ? "tbx-dark" : theme === "auto" ? "tbx-auto" : void 0,
435
662
  className
436
- ].filter((part) => Boolean(part)).join(" ");
663
+ ].filter(Boolean);
437
664
  if (error) {
438
- return /* @__PURE__ */ jsx3("div", { className: rootClassName, "data-density": density, children: /* @__PURE__ */ jsxs2("div", { className: "nxg-state-card", children: [
439
- /* @__PURE__ */ jsx3("p", { className: "nxg-state-text", children: locale.errorText }),
440
- onRetry ? /* @__PURE__ */ jsx3("button", { type: "button", className: "nxg-btn", onClick: onRetry, children: locale.retryButton }) : null
665
+ return /* @__PURE__ */ jsx3("div", { className: rootClasses.join(" "), "data-density": density, children: /* @__PURE__ */ jsxs2("div", { className: "tbx-state-card", children: [
666
+ /* @__PURE__ */ jsx3("p", { className: "tbx-state-text", children: locale.errorText }),
667
+ onRetry ? /* @__PURE__ */ jsxs2("button", { type: "button", className: "tbx-btn", onClick: onRetry, children: [
668
+ /* @__PURE__ */ jsx3(RotateCcwIcon, { className: "tbx-icon" }),
669
+ /* @__PURE__ */ jsx3("span", { children: locale.retryButton })
670
+ ] }) : null
441
671
  ] }) });
442
672
  }
443
673
  const rowIsClickable = typeof onRowClick === "function";
@@ -449,56 +679,256 @@ function NexGrid({
449
679
  event.preventDefault();
450
680
  onRowClick?.(row);
451
681
  };
452
- return /* @__PURE__ */ jsxs2("div", { className: rootClassName, "data-density": density, children: [
453
- /* @__PURE__ */ jsxs2("div", { className: "nxg-toolbar", children: [
454
- /* @__PURE__ */ jsxs2("div", { className: "nxg-toolbar-group", children: [
455
- enableSearch ? /* @__PURE__ */ jsxs2("div", { className: "nxg-search", children: [
456
- /* @__PURE__ */ jsx3(SearchIcon, { className: "nxg-search-icon" }),
457
- /* @__PURE__ */ jsx3(
458
- "input",
459
- {
460
- type: "search",
461
- className: "nxg-search-input",
462
- value: searchText,
463
- onChange: (event) => setSearchText(event.target.value),
464
- placeholder: searchPlaceholder ?? locale.searchPlaceholder,
465
- "aria-label": `Search ${caption}`
466
- }
467
- ),
468
- searchText ? /* @__PURE__ */ jsxs2(
469
- "button",
682
+ const hasSummary = enableSummaryRow === true || visible.some((col) => col.meta?.aggregation !== void 0);
683
+ const renderLeafTh = (col, rowSpan = 1, isGroupChild = false) => {
684
+ const id = getColumnId2(col);
685
+ const sortable = isSortable(col) && enableSorting !== false;
686
+ const sortIndex = query.sort.findIndex((s) => s.field === id);
687
+ const sortItem = sortIndex >= 0 ? query.sort[sortIndex] : void 0;
688
+ const sorted = sortable && sortItem !== void 0;
689
+ const title = getColumnTitle(col) || id;
690
+ const meta = col.meta;
691
+ const activeFilter = query.filter?.[id];
692
+ const isFilterActive = activeFilter !== void 0 && activeFilter !== "";
693
+ const customWidth = colWidths[id];
694
+ const baseStyle = headerCellStyle(col);
695
+ const thStyle = {
696
+ ...baseStyle,
697
+ ...customWidth !== void 0 ? { width: `${customWidth}px` } : {},
698
+ ...leftOffsets.has(id) ? { left: leftOffsets.get(id) } : {},
699
+ ...rightOffsets.has(id) ? { right: rightOffsets.get(id) } : {}
700
+ };
701
+ const startResize = (e) => {
702
+ e.preventDefault();
703
+ e.stopPropagation();
704
+ const startX = e.clientX;
705
+ const targetTh = e.currentTarget.parentElement || null;
706
+ const startWidth = targetTh ? targetTh.getBoundingClientRect().width : customWidth ?? meta?.width ?? 120;
707
+ const onMove = (moveEvent) => {
708
+ const nextW = Math.max(meta?.minWidth ?? 60, Math.round(startWidth + (moveEvent.clientX - startX)));
709
+ setColWidths((prev) => ({ ...prev, [id]: nextW }));
710
+ };
711
+ const onUp = () => {
712
+ document.removeEventListener("pointermove", onMove);
713
+ document.removeEventListener("pointerup", onUp);
714
+ document.body?.classList.remove("tbx-resizing");
715
+ };
716
+ document.body?.classList.add("tbx-resizing");
717
+ document.addEventListener("pointermove", onMove);
718
+ document.addEventListener("pointerup", onUp);
719
+ };
720
+ return /* @__PURE__ */ jsxs2(
721
+ "th",
722
+ {
723
+ scope: "col",
724
+ rowSpan: rowSpan > 1 ? rowSpan : void 0,
725
+ "aria-sort": !sortable ? void 0 : sorted ? sortItem?.dir === "asc" ? "ascending" : "descending" : "none",
726
+ tabIndex: sortable ? 0 : void 0,
727
+ "data-column-id": id,
728
+ "data-tbx-focus": sortable ? `sort:${id}` : void 0,
729
+ draggable: enableColumnReorder && !isGroupChild,
730
+ onDragStart: enableColumnReorder && !isGroupChild ? (e) => {
731
+ setDraggedColId(id);
732
+ e.dataTransfer.effectAllowed = "move";
733
+ e.dataTransfer.setData("text/plain", id);
734
+ } : void 0,
735
+ onDragOver: enableColumnReorder && !isGroupChild ? (e) => {
736
+ e.preventDefault();
737
+ } : void 0,
738
+ onDrop: enableColumnReorder && !isGroupChild ? (e) => {
739
+ e.preventDefault();
740
+ if (!draggedColId || draggedColId === id) return;
741
+ const fromIdx = colList.findIndex((c) => getColumnId2(c) === draggedColId);
742
+ const toIdx = colList.findIndex((c) => getColumnId2(c) === id);
743
+ if (fromIdx >= 0 && toIdx >= 0) {
744
+ const nextCols = [...colList];
745
+ const moved = nextCols[fromIdx];
746
+ if (!moved) return;
747
+ nextCols.splice(fromIdx, 1);
748
+ nextCols.splice(toIdx, 0, moved);
749
+ setColList(nextCols);
750
+ onColumnOrderChange?.(nextCols.map(getColumnId2));
751
+ }
752
+ } : void 0,
753
+ onDragEnd: enableColumnReorder && !isGroupChild ? () => {
754
+ setDraggedColId(null);
755
+ } : void 0,
756
+ className: [
757
+ "tbx-th",
758
+ sortable ? "tbx-th--sortable" : void 0,
759
+ isGroupChild ? "tbx-th--grouped-child" : void 0,
760
+ enableColumnReorder && !isGroupChild ? "tbx-th--draggable" : void 0,
761
+ draggedColId === id ? "tbx-th--dragging" : void 0,
762
+ isPinned(col) === "left" ? "tbx-th--pinned-left" : void 0,
763
+ isPinned(col) === "right" ? "tbx-th--pinned-right" : void 0,
764
+ lastLeftPinnedId === id ? "tbx-pinned-border-left" : void 0,
765
+ firstRightPinnedId === id ? "tbx-pinned-border-right" : void 0
766
+ ].filter(Boolean).join(" "),
767
+ style: thStyle,
768
+ onClick: sortable ? (event) => {
769
+ const t = event.target;
770
+ if (t?.closest(".tbx-col-filter-wrap") || t?.closest(".tbx-resize-handle")) return;
771
+ toggleSort(id, event.shiftKey);
772
+ } : void 0,
773
+ onKeyDown: sortable ? (event) => {
774
+ if (event.key !== "Enter" && event.key !== " ") return;
775
+ const t = event.target;
776
+ if (t?.closest(".tbx-col-filter-wrap")) return;
777
+ event.preventDefault();
778
+ toggleSort(id, event.shiftKey);
779
+ } : void 0,
780
+ children: [
781
+ /* @__PURE__ */ jsxs2("div", { className: headerInnerClass(col), children: [
782
+ /* @__PURE__ */ jsx3("span", { children: renderColumnHeader(col) }),
783
+ sortable ? /* @__PURE__ */ jsxs2("span", { className: "tbx-sort-icon-wrap", children: [
784
+ sorted ? sortItem?.dir === "asc" ? /* @__PURE__ */ jsx3(ArrowUpIcon, { className: "tbx-sort-icon" }) : /* @__PURE__ */ jsx3(ArrowDownIcon, { className: "tbx-sort-icon" }) : /* @__PURE__ */ jsx3(ArrowUpDownIcon, { className: "tbx-sort-icon tbx-sort-icon--idle" }),
785
+ query.sort.length > 1 && sortIndex >= 0 ? /* @__PURE__ */ jsx3("span", { className: "tbx-sort-order", children: sortIndex + 1 }) : null
786
+ ] }) : null,
787
+ isFilterable(col, enableColumnFilters) ? /* @__PURE__ */ jsxs2("div", { className: "tbx-col-filter-wrap", children: [
788
+ /* @__PURE__ */ jsx3(
789
+ "button",
790
+ {
791
+ type: "button",
792
+ className: isFilterActive ? "tbx-col-filter-btn tbx-col-filter-btn--active" : "tbx-col-filter-btn",
793
+ "aria-label": `Filter ${title}`,
794
+ onClick: (e) => {
795
+ e.stopPropagation();
796
+ setOpenFilterCol(openFilterCol === id ? null : id);
797
+ },
798
+ children: /* @__PURE__ */ jsx3(DotsVerticalIcon, { className: "tbx-icon" })
799
+ }
800
+ ),
801
+ openFilterCol === id ? /* @__PURE__ */ jsxs2(
802
+ "div",
803
+ {
804
+ className: "tbx-filter-popover",
805
+ onClick: (e) => e.stopPropagation(),
806
+ children: [
807
+ /* @__PURE__ */ jsx3(
808
+ "input",
809
+ {
810
+ autoFocus: true,
811
+ type: "text",
812
+ className: "tbx-filter-popover-input",
813
+ defaultValue: activeFilter ?? "",
814
+ placeholder: `Filter by ${title}...`,
815
+ onKeyDown: (e) => {
816
+ if (e.key === "Enter") {
817
+ e.preventDefault();
818
+ const val = e.currentTarget.value.trim();
819
+ setOpenFilterCol(null);
820
+ onQueryChange(withFilter(query, id, val || void 0));
821
+ } else if (e.key === "Escape") {
822
+ setOpenFilterCol(null);
823
+ }
824
+ }
825
+ }
826
+ ),
827
+ /* @__PURE__ */ jsxs2("div", { className: "tbx-filter-popover-actions", children: [
828
+ /* @__PURE__ */ jsxs2(
829
+ "button",
830
+ {
831
+ type: "button",
832
+ className: "tbx-filter-popover-btn",
833
+ onClick: (e) => {
834
+ e.stopPropagation();
835
+ setOpenFilterCol(null);
836
+ onQueryChange(withFilter(query, id, void 0));
837
+ },
838
+ children: [
839
+ /* @__PURE__ */ jsx3(RotateCcwIcon, { className: "tbx-icon" }),
840
+ /* @__PURE__ */ jsx3("span", { children: locale.clearFilter })
841
+ ]
842
+ }
843
+ ),
844
+ /* @__PURE__ */ jsxs2(
845
+ "button",
846
+ {
847
+ type: "button",
848
+ className: "tbx-filter-popover-btn tbx-filter-popover-btn--primary",
849
+ onClick: (e) => {
850
+ e.stopPropagation();
851
+ const parent = e.currentTarget.closest(".tbx-filter-popover");
852
+ const inputEl = parent?.querySelector("input");
853
+ const val = inputEl?.value.trim();
854
+ setOpenFilterCol(null);
855
+ onQueryChange(withFilter(query, id, val || void 0));
856
+ },
857
+ children: [
858
+ /* @__PURE__ */ jsx3(CheckIcon, { className: "tbx-icon" }),
859
+ /* @__PURE__ */ jsx3("span", { children: locale.applyFilter })
860
+ ]
861
+ }
862
+ )
863
+ ] })
864
+ ]
865
+ }
866
+ ) : null
867
+ ] }) : null
868
+ ] }),
869
+ enableColumnResize ? /* @__PURE__ */ jsx3(
870
+ "div",
470
871
  {
471
- type: "button",
472
- className: "nxg-search-clear",
473
- onClick: () => setSearchText(""),
474
- "aria-label": locale.clearSearch,
475
- children: [
476
- /* @__PURE__ */ jsx3(XIcon, { className: "nxg-icon" }),
477
- /* @__PURE__ */ jsx3("span", { className: "nxg-sr-only", children: locale.clearSearch })
478
- ]
872
+ className: "tbx-resize-handle",
873
+ onPointerDown: startResize,
874
+ onDoubleClick: () => autoFitColumn(id)
479
875
  }
480
876
  ) : null
481
- ] }) : null,
482
- /* @__PURE__ */ jsxs2("div", { className: "nxg-menu-wrap", ref: columnsMenu.containerRef, children: [
877
+ ]
878
+ },
879
+ id
880
+ );
881
+ };
882
+ return /* @__PURE__ */ jsxs2("div", { className: rootClasses.join(" "), "data-density": density, ref: rootRef, children: [
883
+ showToolbar ? /* @__PURE__ */ jsxs2("div", { className: "tbx-toolbar", children: [
884
+ /* @__PURE__ */ jsx3("div", { className: "tbx-toolbar-group", children: enableSearch ? /* @__PURE__ */ jsxs2("div", { className: "tbx-search", children: [
885
+ /* @__PURE__ */ jsx3(SearchIcon, { className: "tbx-search-icon" }),
886
+ /* @__PURE__ */ jsx3(
887
+ "input",
888
+ {
889
+ type: "search",
890
+ className: "tbx-search-input",
891
+ value: searchText,
892
+ onChange: (event) => setSearchText(event.target.value),
893
+ placeholder: searchPlaceholder ?? locale.searchPlaceholder,
894
+ "aria-label": `Search ${caption}`
895
+ }
896
+ ),
897
+ searchText ? /* @__PURE__ */ jsxs2(
898
+ "button",
899
+ {
900
+ type: "button",
901
+ className: "tbx-search-clear",
902
+ onClick: () => setSearchText(""),
903
+ "aria-label": locale.clearSearch,
904
+ children: [
905
+ /* @__PURE__ */ jsx3(XIcon, { className: "tbx-icon" }),
906
+ /* @__PURE__ */ jsx3("span", { className: "tbx-sr-only", children: locale.clearSearch })
907
+ ]
908
+ }
909
+ ) : null
910
+ ] }) : null }),
911
+ /* @__PURE__ */ jsxs2("div", { className: "tbx-toolbar-group tbx-toolbar-group--end", children: [
912
+ isColumnsVisible ? /* @__PURE__ */ jsxs2("div", { className: "tbx-menu-wrap", ref: columnsMenu.containerRef, children: [
483
913
  /* @__PURE__ */ jsxs2(
484
914
  "button",
485
915
  {
486
916
  type: "button",
487
917
  id: columnsButtonId,
488
- className: "nxg-btn",
918
+ className: "tbx-btn",
489
919
  ref: columnsMenu.triggerRef,
490
920
  "aria-haspopup": "menu",
491
921
  "aria-expanded": columnsMenu.isOpen,
492
922
  onClick: columnsMenu.toggle,
493
923
  children: [
494
- /* @__PURE__ */ jsx3(SlidersIcon, { className: "nxg-icon" }),
924
+ /* @__PURE__ */ jsx3(ColumnsIcon, { className: "tbx-icon" }),
495
925
  /* @__PURE__ */ jsx3("span", { children: locale.columnsButton })
496
926
  ]
497
927
  }
498
928
  ),
499
- columnsMenu.isOpen ? /* @__PURE__ */ jsxs2("div", { className: "nxg-menu", role: "menu", "aria-labelledby": columnsButtonId, children: [
500
- /* @__PURE__ */ jsx3("div", { className: "nxg-menu-label", children: locale.toggleColumnsLabel }),
501
- /* @__PURE__ */ jsx3("div", { className: "nxg-menu-separator" }),
929
+ columnsMenu.isOpen ? /* @__PURE__ */ jsxs2("div", { className: "tbx-menu", role: "menu", "aria-labelledby": columnsButtonId, children: [
930
+ /* @__PURE__ */ jsx3("div", { className: "tbx-menu-label", children: locale.toggleColumnsLabel }),
931
+ /* @__PURE__ */ jsx3("div", { className: "tbx-menu-separator" }),
502
932
  hideable.map((col) => {
503
933
  const id = getColumnId2(col);
504
934
  const checked = hiddenCols[id] !== true;
@@ -506,42 +936,55 @@ function NexGrid({
506
936
  "button",
507
937
  {
508
938
  type: "button",
509
- className: "nxg-menu-item",
939
+ className: "tbx-menu-item",
510
940
  role: "menuitemcheckbox",
511
941
  "aria-checked": checked,
512
942
  onClick: () => toggleColumn(id),
513
943
  children: [
514
- /* @__PURE__ */ jsx3(CheckIcon, { className: "nxg-check" }),
944
+ /* @__PURE__ */ jsx3(CheckIcon, { className: "tbx-check" }),
515
945
  /* @__PURE__ */ jsx3("span", { children: getColumnTitle(col) || id })
516
946
  ]
517
947
  },
518
948
  id
519
949
  );
520
- })
950
+ }),
951
+ storageKey ? /* @__PURE__ */ jsxs2(Fragment3, { children: [
952
+ /* @__PURE__ */ jsx3("div", { className: "tbx-menu-separator" }),
953
+ /* @__PURE__ */ jsx3(
954
+ "button",
955
+ {
956
+ type: "button",
957
+ className: "tbx-menu-item tbx-menu-item--reset",
958
+ role: "menuitem",
959
+ onClick: handleResetView,
960
+ children: /* @__PURE__ */ jsx3("span", { children: "Reset to default view" })
961
+ }
962
+ )
963
+ ] }) : null
521
964
  ] }) : null
522
- ] }),
523
- /* @__PURE__ */ jsxs2("div", { className: "nxg-menu-wrap", ref: densityMenu.containerRef, children: [
965
+ ] }) : null,
966
+ isDensityVisible ? /* @__PURE__ */ jsxs2("div", { className: "tbx-menu-wrap", ref: densityMenu.containerRef, children: [
524
967
  /* @__PURE__ */ jsxs2(
525
968
  "button",
526
969
  {
527
970
  type: "button",
528
971
  id: densityButtonId,
529
- className: "nxg-btn nxg-capitalize",
972
+ className: "tbx-btn tbx-capitalize",
530
973
  ref: densityMenu.triggerRef,
531
974
  "aria-haspopup": "menu",
532
975
  "aria-expanded": densityMenu.isOpen,
533
976
  onClick: densityMenu.toggle,
534
977
  children: [
535
- /* @__PURE__ */ jsx3(FilterIcon, { className: "nxg-icon" }),
978
+ /* @__PURE__ */ jsx3(DensityIcon, { className: "tbx-icon" }),
536
979
  /* @__PURE__ */ jsx3("span", { children: formatMessage(locale.densityButton, { density }) })
537
980
  ]
538
981
  }
539
982
  ),
540
- densityMenu.isOpen ? /* @__PURE__ */ jsx3("div", { className: "nxg-menu", role: "menu", "aria-labelledby": densityButtonId, children: DENSITIES.map((option) => /* @__PURE__ */ jsxs2(
983
+ densityMenu.isOpen ? /* @__PURE__ */ jsx3("div", { className: "tbx-menu", role: "menu", "aria-labelledby": densityButtonId, children: DENSITIES.map((option) => /* @__PURE__ */ jsxs2(
541
984
  "button",
542
985
  {
543
986
  type: "button",
544
- className: "nxg-menu-item",
987
+ className: "tbx-menu-item",
545
988
  role: "menuitemcheckbox",
546
989
  "aria-checked": density === option,
547
990
  onClick: () => {
@@ -549,37 +992,36 @@ function NexGrid({
549
992
  densityMenu.close();
550
993
  },
551
994
  children: [
552
- /* @__PURE__ */ jsx3(CheckIcon, { className: "nxg-check" }),
995
+ /* @__PURE__ */ jsx3(CheckIcon, { className: "tbx-check" }),
553
996
  /* @__PURE__ */ jsx3("span", { children: locale[DENSITY_LABEL_KEY[option]] })
554
997
  ]
555
998
  },
556
999
  option
557
1000
  )) }) : null
558
- ] })
559
- ] }),
560
- /* @__PURE__ */ jsxs2("div", { className: "nxg-toolbar-group nxg-toolbar-group--end", children: [
561
- enableExport ? /* @__PURE__ */ jsxs2("div", { className: "nxg-menu-wrap", ref: exportMenu.containerRef, children: [
1001
+ ] }) : null,
1002
+ isExportVisible ? /* @__PURE__ */ jsxs2("div", { className: "tbx-menu-wrap", ref: exportMenu.containerRef, children: [
562
1003
  /* @__PURE__ */ jsxs2(
563
1004
  "button",
564
1005
  {
565
1006
  type: "button",
566
1007
  id: exportButtonId,
567
- className: "nxg-btn nxg-btn--export",
1008
+ className: "tbx-btn tbx-btn--export",
568
1009
  ref: exportMenu.triggerRef,
569
1010
  disabled: isExporting,
570
1011
  "aria-haspopup": "menu",
571
1012
  "aria-expanded": exportMenu.isOpen,
572
1013
  onClick: exportMenu.toggle,
573
1014
  children: [
574
- /* @__PURE__ */ jsx3(DownloadIcon, { className: "nxg-icon" }),
575
- /* @__PURE__ */ jsx3("span", { children: isExporting ? locale.exportingButton : locale.exportButton })
1015
+ /* @__PURE__ */ jsx3(DownloadTrayIcon, { className: "tbx-icon" }),
1016
+ /* @__PURE__ */ jsx3("span", { children: isExporting ? locale.exportingButton : locale.exportButton }),
1017
+ /* @__PURE__ */ jsx3(ChevronDownIcon, { className: "tbx-icon tbx-chevron" })
576
1018
  ]
577
1019
  }
578
1020
  ),
579
1021
  exportMenu.isOpen ? /* @__PURE__ */ jsxs2(
580
1022
  "div",
581
1023
  {
582
- className: "nxg-menu nxg-menu--end",
1024
+ className: "tbx-menu tbx-menu--end",
583
1025
  role: "menu",
584
1026
  "aria-labelledby": exportButtonId,
585
1027
  children: [
@@ -587,12 +1029,12 @@ function NexGrid({
587
1029
  "button",
588
1030
  {
589
1031
  type: "button",
590
- className: "nxg-menu-item",
1032
+ className: "tbx-menu-item",
591
1033
  role: "menuitem",
592
1034
  onClick: () => void handleExport("excel"),
593
1035
  children: [
594
- /* @__PURE__ */ jsx3(FileSpreadsheetIcon, { className: "nxg-icon--excel" }),
595
- /* @__PURE__ */ jsxs2("div", { className: "nxg-menu-item-title", children: [
1036
+ /* @__PURE__ */ jsx3(FileSpreadsheetIcon, { className: "tbx-icon--excel" }),
1037
+ /* @__PURE__ */ jsxs2("div", { className: "tbx-menu-item-title", children: [
596
1038
  /* @__PURE__ */ jsx3("strong", { children: locale.exportExcelTitle }),
597
1039
  /* @__PURE__ */ jsx3("small", { children: locale.exportExcelSubtitle })
598
1040
  ] })
@@ -603,17 +1045,33 @@ function NexGrid({
603
1045
  "button",
604
1046
  {
605
1047
  type: "button",
606
- className: "nxg-menu-item",
1048
+ className: "tbx-menu-item",
607
1049
  role: "menuitem",
608
1050
  onClick: () => void handleExport("csv"),
609
1051
  children: [
610
- /* @__PURE__ */ jsx3(FileTextIcon, { className: "nxg-icon--csv" }),
611
- /* @__PURE__ */ jsxs2("div", { className: "nxg-menu-item-title", children: [
1052
+ /* @__PURE__ */ jsx3(FileTextIcon, { className: "tbx-icon--csv" }),
1053
+ /* @__PURE__ */ jsxs2("div", { className: "tbx-menu-item-title", children: [
612
1054
  /* @__PURE__ */ jsx3("strong", { children: locale.exportCsvTitle }),
613
1055
  /* @__PURE__ */ jsx3("small", { children: locale.exportCsvSubtitle })
614
1056
  ] })
615
1057
  ]
616
1058
  }
1059
+ ),
1060
+ /* @__PURE__ */ jsxs2(
1061
+ "button",
1062
+ {
1063
+ type: "button",
1064
+ className: "tbx-menu-item",
1065
+ role: "menuitem",
1066
+ onClick: () => void handleExport("clipboard"),
1067
+ children: [
1068
+ /* @__PURE__ */ jsx3(FileTextIcon, { className: "tbx-icon--csv" }),
1069
+ /* @__PURE__ */ jsxs2("div", { className: "tbx-menu-item-title", children: [
1070
+ /* @__PURE__ */ jsx3("strong", { children: locale.exportClipboardTitle }),
1071
+ /* @__PURE__ */ jsx3("small", { children: locale.exportClipboardSubtitle })
1072
+ ] })
1073
+ ]
1074
+ }
617
1075
  )
618
1076
  ]
619
1077
  }
@@ -621,137 +1079,400 @@ function NexGrid({
621
1079
  ] }) : null,
622
1080
  toolbarActions
623
1081
  ] })
624
- ] }),
625
- /* @__PURE__ */ jsx3("div", { className: "nxg-table-wrap", children: /* @__PURE__ */ jsxs2("table", { className: "nxg-table", "aria-label": caption, children: [
626
- /* @__PURE__ */ jsx3("thead", { children: /* @__PURE__ */ jsxs2("tr", { children: [
627
- showSerialNumber ? /* @__PURE__ */ jsx3("th", { className: "nxg-th nxg-th--serial", scope: "col", children: locale.serialHeader }) : null,
628
- enableSelection ? /* @__PURE__ */ jsx3("th", { className: "nxg-th nxg-th--select", scope: "col", children: /* @__PURE__ */ jsx3(
629
- "input",
1082
+ ] }) : null,
1083
+ showFilterPills !== false && (Boolean(query.q?.trim()) || Object.keys(query.filter ?? {}).some((k) => query.filter?.[k])) ? /* @__PURE__ */ jsxs2("div", { className: "tbx-filter-pills-bar", children: [
1084
+ /* @__PURE__ */ jsx3("span", { className: "tbx-filter-pills-title", children: "Active filters:" }),
1085
+ query.q?.trim() ? /* @__PURE__ */ jsxs2("div", { className: "tbx-filter-pill", children: [
1086
+ /* @__PURE__ */ jsxs2("span", { className: "tbx-filter-pill-label", children: [
1087
+ searchPlaceholder || locale.searchPlaceholder || "Search",
1088
+ ":"
1089
+ ] }),
1090
+ /* @__PURE__ */ jsxs2("span", { className: "tbx-filter-pill-val", children: [
1091
+ '"',
1092
+ query.q,
1093
+ '"'
1094
+ ] }),
1095
+ /* @__PURE__ */ jsx3(
1096
+ "button",
630
1097
  {
631
- type: "checkbox",
632
- className: "nxg-checkbox",
633
- checked: allPageSelected,
634
- ref: (el) => {
635
- if (el) el.indeterminate = somePageSelected;
636
- },
637
- onChange: toggleSelectAll,
638
- "aria-label": locale.selectAllLabel
1098
+ type: "button",
1099
+ className: "tbx-filter-pill-remove",
1100
+ title: "Clear search",
1101
+ "aria-label": "Clear search",
1102
+ onClick: () => onQueryChange(withSearch(query, "")),
1103
+ children: "\u2715"
639
1104
  }
640
- ) }) : null,
641
- visible.map((col, index) => {
642
- const id = getColumnId2(col);
643
- const sortable = isSortable(col);
644
- const sorted = sortable && id !== "" && sort?.field === id;
645
- const title = getColumnTitle(col) || id;
646
- return /* @__PURE__ */ jsx3(
1105
+ )
1106
+ ] }) : null,
1107
+ Object.entries(query.filter ?? {}).map(([key, val]) => {
1108
+ if (val === void 0 || val === "") return null;
1109
+ const col = leafCols.find((c) => getColumnId2(c) === key);
1110
+ const title = col ? getColumnTitle(col) || key : key;
1111
+ return /* @__PURE__ */ jsxs2("div", { className: "tbx-filter-pill", children: [
1112
+ /* @__PURE__ */ jsxs2("span", { className: "tbx-filter-pill-label", children: [
1113
+ title,
1114
+ ":"
1115
+ ] }),
1116
+ /* @__PURE__ */ jsx3("span", { className: "tbx-filter-pill-val", children: String(val) }),
1117
+ /* @__PURE__ */ jsx3(
1118
+ "button",
1119
+ {
1120
+ type: "button",
1121
+ className: "tbx-filter-pill-remove",
1122
+ title: `Remove ${title} filter`,
1123
+ "aria-label": `Remove filter for ${title}`,
1124
+ onClick: () => onQueryChange(withFilter(query, key, void 0)),
1125
+ children: "\u2715"
1126
+ }
1127
+ )
1128
+ ] }, key);
1129
+ }),
1130
+ /* @__PURE__ */ jsx3(
1131
+ "button",
1132
+ {
1133
+ type: "button",
1134
+ className: "tbx-filter-pill-clear-all",
1135
+ onClick: () => onQueryChange({ ...query, page: 1, q: void 0, filter: {} }),
1136
+ children: "Clear all"
1137
+ }
1138
+ )
1139
+ ] }) : null,
1140
+ /* @__PURE__ */ jsx3("div", { className: "tbx-table-wrap", children: /* @__PURE__ */ jsxs2("table", { className: "tbx-table", "aria-label": caption, children: [
1141
+ /* @__PURE__ */ jsxs2("thead", { children: [
1142
+ /* @__PURE__ */ jsxs2("tr", { children: [
1143
+ renderExpandedRow ? /* @__PURE__ */ jsx3(
647
1144
  "th",
648
1145
  {
1146
+ rowSpan: headerRows.hasGroups ? 2 : void 0,
1147
+ className: [
1148
+ "tbx-th tbx-th--expand",
1149
+ leftOffsets.has("__expand") ? "tbx-th--pinned-left" : void 0,
1150
+ lastLeftPinnedId === "__expand" ? "tbx-pinned-border-left" : void 0
1151
+ ].filter(Boolean).join(" "),
1152
+ style: {
1153
+ width: "40px",
1154
+ left: leftOffsets.get("__expand")
1155
+ },
1156
+ scope: "col"
1157
+ }
1158
+ ) : null,
1159
+ enableSelection ? /* @__PURE__ */ jsx3(
1160
+ "th",
1161
+ {
1162
+ rowSpan: headerRows.hasGroups ? 2 : void 0,
1163
+ className: [
1164
+ "tbx-th tbx-th--select",
1165
+ leftOffsets.has("__select") ? "tbx-th--pinned-left" : void 0,
1166
+ lastLeftPinnedId === "__select" ? "tbx-pinned-border-left" : void 0
1167
+ ].filter(Boolean).join(" "),
1168
+ style: {
1169
+ left: leftOffsets.get("__select")
1170
+ },
649
1171
  scope: "col",
650
- className: sortable ? "nxg-th nxg-th--sortable" : "nxg-th",
651
- style: headerCellStyle(col),
652
- "aria-sort": sorted ? sort?.dir === "asc" ? "ascending" : "descending" : sortable ? "none" : void 0,
653
- "aria-label": title || void 0,
654
- tabIndex: sortable ? 0 : void 0,
655
- onClick: sortable ? () => toggleSort(id) : void 0,
656
- onKeyDown: sortable ? (event) => {
657
- if (event.key !== "Enter" && event.key !== " ") return;
658
- event.preventDefault();
659
- toggleSort(id);
660
- } : void 0,
661
- children: /* @__PURE__ */ jsxs2("div", { className: headerInnerClass(col), children: [
662
- /* @__PURE__ */ jsx3("span", { children: renderColumnHeader(col) }),
663
- sortable ? /* @__PURE__ */ jsx3("span", { children: sorted ? sort?.dir === "asc" ? /* @__PURE__ */ jsx3(ArrowUpIcon, { className: "nxg-sort-icon" }) : /* @__PURE__ */ jsx3(ArrowDownIcon, { className: "nxg-sort-icon" }) : /* @__PURE__ */ jsx3(ArrowUpDownIcon, { className: "nxg-sort-icon nxg-sort-icon--idle" }) }) : null
664
- ] })
665
- },
666
- id || `col-${index}`
667
- );
668
- })
669
- ] }) }),
670
- /* @__PURE__ */ jsx3("tbody", { children: isLoading ? /* @__PURE__ */ jsx3("tr", { children: /* @__PURE__ */ jsxs2("td", { className: "nxg-state", colSpan: Math.max(1, columnCount), children: [
671
- /* @__PURE__ */ jsx3("span", { className: "nxg-spinner", "aria-hidden": "true" }),
672
- /* @__PURE__ */ jsx3("div", { "aria-live": "polite", children: locale.loadingText })
673
- ] }) }) : data.length === 0 ? /* @__PURE__ */ jsx3("tr", { children: /* @__PURE__ */ jsx3("td", { className: "nxg-state", colSpan: Math.max(1, columnCount), children: locale.emptyText }) }) : data.map((row, index) => {
674
- const id = pageRowIds[index] ?? String(index);
675
- const isSelected = selectedIds.has(id);
676
- return /* @__PURE__ */ jsxs2(
677
- "tr",
678
- {
679
- className: [
680
- "nxg-row",
681
- isSelected ? "nxg-row--selected" : void 0,
682
- rowIsClickable ? "nxg-row--clickable" : void 0
683
- ].filter(Boolean).join(" "),
684
- onClick: rowIsClickable ? activateRow(row) : void 0,
685
- onKeyDown: rowIsClickable ? rowKeyDown(row) : void 0,
686
- tabIndex: rowIsClickable ? 0 : void 0,
687
- children: [
688
- showSerialNumber ? /* @__PURE__ */ jsx3("td", { className: "nxg-td nxg-td--serial", children: serialNumber(currentPage, pageSize, index) }) : null,
689
- enableSelection ? /* @__PURE__ */ jsx3(
690
- "td",
1172
+ children: !isSingleSelect ? /* @__PURE__ */ jsx3(
1173
+ "input",
691
1174
  {
692
- className: "nxg-td nxg-td--select",
693
- onClick: (event) => event.stopPropagation(),
694
- children: /* @__PURE__ */ jsx3(
695
- "input",
696
- {
697
- type: "checkbox",
698
- className: "nxg-checkbox",
699
- checked: isSelected,
700
- onChange: () => toggleSelectRow(id),
701
- onClick: (event) => event.stopPropagation(),
702
- "aria-label": formatMessage(locale.selectRowLabel, { id })
703
- }
704
- )
1175
+ type: "checkbox",
1176
+ className: "tbx-checkbox",
1177
+ checked: allPageSelected,
1178
+ ref: (el) => {
1179
+ if (el) el.indeterminate = somePageSelected;
1180
+ },
1181
+ onChange: toggleSelectAll,
1182
+ "aria-label": locale.selectAllLabel
705
1183
  }
706
- ) : null,
707
- visible.map((col, colIndex) => /* @__PURE__ */ jsx3(
708
- "td",
1184
+ ) : null
1185
+ }
1186
+ ) : null,
1187
+ showSerialNumber ? /* @__PURE__ */ jsx3(
1188
+ "th",
1189
+ {
1190
+ rowSpan: headerRows.hasGroups ? 2 : void 0,
1191
+ className: [
1192
+ "tbx-th tbx-th--serial",
1193
+ leftOffsets.has("__serial") ? "tbx-th--pinned-left" : void 0,
1194
+ lastLeftPinnedId === "__serial" ? "tbx-pinned-border-left" : void 0
1195
+ ].filter(Boolean).join(" "),
1196
+ style: {
1197
+ left: leftOffsets.get("__serial")
1198
+ },
1199
+ scope: "col",
1200
+ children: locale.serialHeader
1201
+ }
1202
+ ) : null,
1203
+ !headerRows.hasGroups ? visible.map((col) => renderLeafTh(col, 1, false)) : headerRows.topRow.map((cell, idx) => {
1204
+ if (cell.isGroup) {
1205
+ return /* @__PURE__ */ jsx3(
1206
+ "th",
709
1207
  {
710
- className: "nxg-td",
711
- style: bodyCellStyle(col),
712
- children: renderCellContent(col, row, boolLabels)
1208
+ colSpan: cell.colSpan,
1209
+ className: "tbx-th tbx-th--group",
1210
+ scope: "colgroup",
1211
+ children: /* @__PURE__ */ jsx3("span", { className: "tbx-th-group-title", children: cell.title })
713
1212
  },
714
- getColumnId2(col) || `col-${colIndex}`
715
- ))
716
- ]
717
- },
718
- id || index
719
- );
1213
+ `grp-${cell.id}-${idx}`
1214
+ );
1215
+ }
1216
+ if (cell.leafColumn) {
1217
+ return renderLeafTh(cell.leafColumn, cell.rowSpan, false);
1218
+ }
1219
+ return null;
1220
+ })
1221
+ ] }),
1222
+ headerRows.hasGroups ? /* @__PURE__ */ jsx3("tr", { children: headerRows.bottomRow.map((cell) => {
1223
+ if (cell.leafColumn) {
1224
+ return renderLeafTh(cell.leafColumn, 1, true);
1225
+ }
1226
+ return null;
1227
+ }) }) : null
1228
+ ] }),
1229
+ /* @__PURE__ */ jsx3("tbody", { children: isLoading ? /* @__PURE__ */ jsx3("tr", { children: /* @__PURE__ */ jsxs2("td", { className: "tbx-state", colSpan: Math.max(1, columnCount), children: [
1230
+ /* @__PURE__ */ jsxs2("div", { className: "tbx-dotted-loader", "aria-hidden": "true", children: [
1231
+ /* @__PURE__ */ jsx3("span", { className: "tbx-dot" }),
1232
+ /* @__PURE__ */ jsx3("span", { className: "tbx-dot" }),
1233
+ /* @__PURE__ */ jsx3("span", { className: "tbx-dot" }),
1234
+ /* @__PURE__ */ jsx3("span", { className: "tbx-dot" })
1235
+ ] }),
1236
+ /* @__PURE__ */ jsx3("div", { className: "tbx-loading-text", "aria-live": "polite", children: locale.loadingText })
1237
+ ] }) }) : rows.length === 0 ? /* @__PURE__ */ jsx3("tr", { children: /* @__PURE__ */ jsx3("td", { className: "tbx-state", colSpan: Math.max(1, columnCount), children: locale.emptyText }) }) : rows.map((row, index) => {
1238
+ const id = pageRowIds[index] ?? String(index);
1239
+ const isSelected = selectedIds.has(id);
1240
+ const isExpanded = expandedRows.has(id);
1241
+ return /* @__PURE__ */ jsxs2(React4.Fragment, { children: [
1242
+ /* @__PURE__ */ jsxs2(
1243
+ "tr",
1244
+ {
1245
+ className: [
1246
+ "tbx-row",
1247
+ isSelected ? "tbx-row--selected" : void 0,
1248
+ isExpanded ? "tbx-row--expanded" : void 0,
1249
+ rowIsClickable ? "tbx-row--clickable" : void 0
1250
+ ].filter(Boolean).join(" "),
1251
+ onClick: rowIsClickable ? activateRow(row) : void 0,
1252
+ onKeyDown: rowIsClickable ? rowKeyDown(row) : void 0,
1253
+ tabIndex: rowIsClickable ? 0 : void 0,
1254
+ children: [
1255
+ renderExpandedRow ? /* @__PURE__ */ jsx3(
1256
+ "td",
1257
+ {
1258
+ className: [
1259
+ "tbx-td tbx-td--expand",
1260
+ leftOffsets.has("__expand") ? "tbx-td--pinned-left" : void 0,
1261
+ lastLeftPinnedId === "__expand" ? "tbx-pinned-border-left" : void 0
1262
+ ].filter(Boolean).join(" "),
1263
+ style: {
1264
+ width: "40px",
1265
+ left: leftOffsets.get("__expand")
1266
+ },
1267
+ onClick: (e) => e.stopPropagation(),
1268
+ children: /* @__PURE__ */ jsx3(
1269
+ "button",
1270
+ {
1271
+ type: "button",
1272
+ className: isExpanded ? "tbx-expand-btn tbx-expand-btn--open" : "tbx-expand-btn",
1273
+ "aria-label": isExpanded ? "Collapse row" : "Expand row",
1274
+ onClick: (e) => {
1275
+ e.stopPropagation();
1276
+ toggleExpandRow(id);
1277
+ },
1278
+ children: /* @__PURE__ */ jsx3(ChevronRightIcon, { className: "tbx-icon" })
1279
+ }
1280
+ )
1281
+ }
1282
+ ) : null,
1283
+ enableSelection ? /* @__PURE__ */ jsx3(
1284
+ "td",
1285
+ {
1286
+ className: [
1287
+ "tbx-td tbx-td--select",
1288
+ leftOffsets.has("__select") ? "tbx-td--pinned-left" : void 0,
1289
+ lastLeftPinnedId === "__select" ? "tbx-pinned-border-left" : void 0
1290
+ ].filter(Boolean).join(" "),
1291
+ style: {
1292
+ left: leftOffsets.get("__select")
1293
+ },
1294
+ onClick: (event) => event.stopPropagation(),
1295
+ children: /* @__PURE__ */ jsx3(
1296
+ "input",
1297
+ {
1298
+ type: isSingleSelect ? "radio" : "checkbox",
1299
+ name: isSingleSelect ? `${instanceId}-select` : void 0,
1300
+ className: "tbx-checkbox",
1301
+ checked: isSelected,
1302
+ onChange: () => toggleSelectRow(id),
1303
+ onClick: (event) => event.stopPropagation(),
1304
+ "aria-label": formatMessage(locale.selectRowLabel, { id })
1305
+ }
1306
+ )
1307
+ }
1308
+ ) : null,
1309
+ showSerialNumber ? /* @__PURE__ */ jsx3(
1310
+ "td",
1311
+ {
1312
+ className: [
1313
+ "tbx-td tbx-td--serial",
1314
+ leftOffsets.has("__serial") ? "tbx-td--pinned-left" : void 0,
1315
+ lastLeftPinnedId === "__serial" ? "tbx-pinned-border-left" : void 0
1316
+ ].filter(Boolean).join(" "),
1317
+ style: {
1318
+ left: leftOffsets.get("__serial")
1319
+ },
1320
+ children: serialNumber(currentPage, pageSize, index)
1321
+ }
1322
+ ) : null,
1323
+ visible.map((col, colIndex) => {
1324
+ const colId = getColumnId2(col);
1325
+ const isPinnedLeft = leftOffsets.has(colId);
1326
+ const isPinnedRight = rightOffsets.has(colId);
1327
+ const isLastLeft = lastLeftPinnedId === colId;
1328
+ const isFirstRight = firstRightPinnedId === colId;
1329
+ const isCellEditable = isEditable(col);
1330
+ const isCurrentlyEditing = editingCell?.rowId === id && editingCell?.columnId === colId;
1331
+ const tdStyle = {
1332
+ ...bodyCellStyle(col),
1333
+ ...isPinnedLeft ? { left: leftOffsets.get(colId) } : {},
1334
+ ...isPinnedRight ? { right: rightOffsets.get(colId) } : {}
1335
+ };
1336
+ return /* @__PURE__ */ jsx3(
1337
+ "td",
1338
+ {
1339
+ className: [
1340
+ "tbx-td",
1341
+ isCellEditable ? "tbx-cell--editable" : void 0,
1342
+ isPinnedLeft ? "tbx-td--pinned-left" : void 0,
1343
+ isPinnedRight ? "tbx-td--pinned-right" : void 0,
1344
+ isLastLeft ? "tbx-pinned-border-left" : void 0,
1345
+ isFirstRight ? "tbx-pinned-border-right" : void 0
1346
+ ].filter(Boolean).join(" "),
1347
+ style: tdStyle,
1348
+ onDoubleClick: isCellEditable ? (e) => {
1349
+ e.stopPropagation();
1350
+ setEditingCell({ rowId: id, columnId: colId });
1351
+ } : void 0,
1352
+ children: isCurrentlyEditing ? /* @__PURE__ */ jsxs2(
1353
+ "div",
1354
+ {
1355
+ className: "tbx-cell-edit-wrap",
1356
+ onClick: (e) => e.stopPropagation(),
1357
+ children: [
1358
+ col.meta?.editType === "select" && col.meta.editOptions ? /* @__PURE__ */ jsx3(
1359
+ "select",
1360
+ {
1361
+ autoFocus: true,
1362
+ className: "tbx-cell-edit-select tbx-cell-edit-input",
1363
+ defaultValue: String(row[colId] ?? ""),
1364
+ id: `${instanceId}-edit-${id}-${colId}`,
1365
+ children: col.meta.editOptions.map((opt) => /* @__PURE__ */ jsx3("option", { value: opt, children: opt }, opt))
1366
+ }
1367
+ ) : /* @__PURE__ */ jsx3(
1368
+ "input",
1369
+ {
1370
+ autoFocus: true,
1371
+ type: col.meta?.editType === "number" ? "number" : "text",
1372
+ className: "tbx-cell-edit-input",
1373
+ defaultValue: String(row[colId] ?? ""),
1374
+ id: `${instanceId}-edit-${id}-${colId}`,
1375
+ onKeyDown: (e) => {
1376
+ if (e.key === "Enter") {
1377
+ e.preventDefault();
1378
+ const el = e.currentTarget;
1379
+ const val = col.meta?.editType === "number" ? Number(el.value) : el.value;
1380
+ const oldVal = row[colId];
1381
+ row[colId] = val;
1382
+ onCellEdit?.({ row, columnId: colId, oldValue: oldVal, newValue: val });
1383
+ setEditingCell(null);
1384
+ } else if (e.key === "Escape") {
1385
+ e.preventDefault();
1386
+ setEditingCell(null);
1387
+ }
1388
+ }
1389
+ }
1390
+ ),
1391
+ /* @__PURE__ */ jsxs2("div", { className: "tbx-cell-edit-actions", children: [
1392
+ /* @__PURE__ */ jsx3(
1393
+ "button",
1394
+ {
1395
+ type: "button",
1396
+ className: "tbx-cell-edit-btn tbx-cell-edit-btn--save",
1397
+ onClick: (e) => {
1398
+ e.stopPropagation();
1399
+ const el = document.getElementById(`${instanceId}-edit-${id}-${colId}`);
1400
+ if (el) {
1401
+ const val = col.meta?.editType === "number" ? Number(el.value) : el.value;
1402
+ const oldVal = row[colId];
1403
+ row[colId] = val;
1404
+ onCellEdit?.({ row, columnId: colId, oldValue: oldVal, newValue: val });
1405
+ }
1406
+ setEditingCell(null);
1407
+ },
1408
+ children: /* @__PURE__ */ jsx3(CheckIcon, { className: "tbx-icon" })
1409
+ }
1410
+ ),
1411
+ /* @__PURE__ */ jsx3(
1412
+ "button",
1413
+ {
1414
+ type: "button",
1415
+ className: "tbx-cell-edit-btn tbx-cell-edit-btn--cancel",
1416
+ onClick: (e) => {
1417
+ e.stopPropagation();
1418
+ setEditingCell(null);
1419
+ },
1420
+ children: /* @__PURE__ */ jsx3(XIcon, { className: "tbx-icon" })
1421
+ }
1422
+ )
1423
+ ] })
1424
+ ]
1425
+ }
1426
+ ) : renderCellContent(col, row, boolLabels)
1427
+ },
1428
+ colId || `col-${colIndex}`
1429
+ );
1430
+ })
1431
+ ]
1432
+ }
1433
+ ),
1434
+ isExpanded && renderExpandedRow ? /* @__PURE__ */ jsx3("tr", { className: "tbx-expanded-row", children: /* @__PURE__ */ jsx3("td", { className: "tbx-expanded-cell", colSpan: columnCount, children: /* @__PURE__ */ jsx3("div", { className: "tbx-detail-panel", children: renderExpandedRow(row) }) }) }) : null
1435
+ ] }, id || index);
720
1436
  }) })
721
1437
  ] }) }),
722
- /* @__PURE__ */ jsx3("div", { className: "nxg-cards", children: isLoading ? /* @__PURE__ */ jsx3("div", { className: "nxg-card", children: /* @__PURE__ */ jsxs2("div", { className: "nxg-state", children: [
723
- /* @__PURE__ */ jsx3("span", { className: "nxg-spinner", "aria-hidden": "true" }),
724
- /* @__PURE__ */ jsx3("div", { "aria-live": "polite", children: locale.loadingText })
725
- ] }) }) : data.length === 0 ? /* @__PURE__ */ jsx3("div", { className: "nxg-card", children: /* @__PURE__ */ jsx3("div", { className: "nxg-state", children: locale.emptyText }) }) : data.map((row, index) => {
1438
+ /* @__PURE__ */ jsx3("div", { className: "tbx-cards", children: isLoading ? /* @__PURE__ */ jsx3("div", { className: "tbx-card", children: /* @__PURE__ */ jsxs2("div", { className: "tbx-state", children: [
1439
+ /* @__PURE__ */ jsxs2("div", { className: "tbx-dotted-loader", "aria-hidden": "true", children: [
1440
+ /* @__PURE__ */ jsx3("span", { className: "tbx-dot" }),
1441
+ /* @__PURE__ */ jsx3("span", { className: "tbx-dot" }),
1442
+ /* @__PURE__ */ jsx3("span", { className: "tbx-dot" }),
1443
+ /* @__PURE__ */ jsx3("span", { className: "tbx-dot" })
1444
+ ] }),
1445
+ /* @__PURE__ */ jsx3("div", { className: "tbx-loading-text", "aria-live": "polite", children: locale.loadingText })
1446
+ ] }) }) : rows.length === 0 ? /* @__PURE__ */ jsx3("div", { className: "tbx-card", children: /* @__PURE__ */ jsx3("div", { className: "tbx-state", children: locale.emptyText }) }) : rows.map((row, index) => {
726
1447
  const id = pageRowIds[index] ?? String(index);
727
1448
  const isSelected = selectedIds.has(id);
728
1449
  return /* @__PURE__ */ jsxs2(
729
1450
  "div",
730
1451
  {
731
1452
  className: [
732
- "nxg-card",
733
- isSelected ? "nxg-card--selected" : void 0,
734
- rowIsClickable ? "nxg-card--clickable" : void 0
1453
+ "tbx-card",
1454
+ isSelected ? "tbx-card--selected" : void 0,
1455
+ rowIsClickable ? "tbx-card--clickable" : void 0
735
1456
  ].filter(Boolean).join(" "),
736
1457
  onClick: rowIsClickable ? activateRow(row) : void 0,
737
1458
  onKeyDown: rowIsClickable ? rowKeyDown(row) : void 0,
738
1459
  tabIndex: rowIsClickable ? 0 : void 0,
739
1460
  children: [
740
- showSerialNumber || enableSelection ? /* @__PURE__ */ jsxs2("div", { className: "nxg-card-head", children: [
741
- showSerialNumber ? /* @__PURE__ */ jsxs2("span", { className: "nxg-card-serial", children: [
1461
+ showSerialNumber || enableSelection ? /* @__PURE__ */ jsxs2("div", { className: "tbx-card-head", children: [
1462
+ showSerialNumber ? /* @__PURE__ */ jsxs2("span", { className: "tbx-card-serial", children: [
742
1463
  "#",
743
1464
  serialNumber(currentPage, pageSize, index)
744
1465
  ] }) : null,
745
1466
  enableSelection ? /* @__PURE__ */ jsx3(
746
1467
  "span",
747
1468
  {
748
- className: "nxg-card-select",
1469
+ className: "tbx-card-select",
749
1470
  onClick: (event) => event.stopPropagation(),
750
1471
  children: /* @__PURE__ */ jsx3(
751
1472
  "input",
752
1473
  {
753
1474
  type: "checkbox",
754
- className: "nxg-checkbox",
1475
+ className: "tbx-checkbox",
755
1476
  checked: isSelected,
756
1477
  onChange: () => toggleSelectRow(id),
757
1478
  onClick: (event) => event.stopPropagation(),
@@ -761,9 +1482,9 @@ function NexGrid({
761
1482
  }
762
1483
  ) : null
763
1484
  ] }) : null,
764
- /* @__PURE__ */ jsx3("dl", { className: "nxg-card-rows", children: visible.map((col, colIndex) => {
1485
+ /* @__PURE__ */ jsx3("dl", { className: "tbx-card-rows", children: visible.map((col, colIndex) => {
765
1486
  const colId = getColumnId2(col);
766
- return /* @__PURE__ */ jsxs2("div", { className: "nxg-card-row", children: [
1487
+ return /* @__PURE__ */ jsxs2("div", { className: "tbx-card-row", children: [
767
1488
  /* @__PURE__ */ jsx3("dt", { children: getColumnTitle(col) || colId }),
768
1489
  /* @__PURE__ */ jsx3("dd", { children: renderCellContent(col, row, boolLabels) })
769
1490
  ] }, colId || `col-${colIndex}`);
@@ -773,52 +1494,55 @@ function NexGrid({
773
1494
  id || index
774
1495
  );
775
1496
  }) }),
776
- /* @__PURE__ */ jsxs2("div", { className: "nxg-footer", children: [
777
- /* @__PURE__ */ jsxs2("div", { className: "nxg-range", children: [
1497
+ showFooter ? /* @__PURE__ */ jsxs2("div", { className: "tbx-footer", children: [
1498
+ /* @__PURE__ */ jsxs2("div", { className: "tbx-range", children: [
778
1499
  /* @__PURE__ */ jsx3("span", { children: renderTemplate(locale.showingRange, {
779
1500
  start: /* @__PURE__ */ jsx3("strong", { children: range.start }),
780
1501
  end: /* @__PURE__ */ jsx3("strong", { children: range.end }),
781
- total: /* @__PURE__ */ jsx3("strong", { className: "nxg-range-total", children: total.toLocaleString() })
1502
+ total: /* @__PURE__ */ jsx3("strong", { className: "tbx-range-total", children: total.toLocaleString() })
782
1503
  }) }),
783
- selectedIds.size > 0 ? /* @__PURE__ */ jsx3("span", { className: "nxg-selected-badge", children: formatMessage(locale.selectedBadge, { count: selectedIds.size }) }) : null
1504
+ selectedIds.size > 0 ? /* @__PURE__ */ jsx3("span", { className: "tbx-selected-badge", children: formatMessage(locale.selectedBadge, { count: selectedIds.size }) }) : null
784
1505
  ] }),
785
- /* @__PURE__ */ jsxs2("div", { className: "nxg-pagination", children: [
786
- /* @__PURE__ */ jsxs2("div", { className: "nxg-rows-per-page", children: [
1506
+ /* @__PURE__ */ jsxs2("div", { className: "tbx-pagination", children: [
1507
+ isRowsPerPageVisible ? /* @__PURE__ */ jsxs2("div", { className: "tbx-rows-per-page", children: [
787
1508
  /* @__PURE__ */ jsx3("span", { children: locale.rowsPerPage }),
788
1509
  /* @__PURE__ */ jsx3(
789
1510
  "select",
790
1511
  {
791
- className: "nxg-rows-select",
1512
+ className: "tbx-rows-select",
792
1513
  value: String(pageSize),
793
1514
  onChange: (event) => {
794
1515
  onQueryChange(withPageSize(query, Number.parseInt(event.target.value, 10)));
795
1516
  },
796
1517
  "aria-label": locale.rowsPerPage,
797
- children: PAGE_SIZES.map((size) => /* @__PURE__ */ jsx3("option", { value: size, children: size }, size))
1518
+ children: PAGE_SIZES.map((size) => /* @__PURE__ */ jsxs2("option", { value: size, children: [
1519
+ size,
1520
+ " rows"
1521
+ ] }, size))
798
1522
  }
799
1523
  )
800
- ] }),
801
- /* @__PURE__ */ jsxs2("div", { className: "nxg-pager", children: [
1524
+ ] }) : null,
1525
+ isPaginationVisible ? /* @__PURE__ */ jsxs2("div", { className: "tbx-pager", children: [
802
1526
  /* @__PURE__ */ jsxs2(
803
1527
  "button",
804
1528
  {
805
1529
  type: "button",
806
- className: "nxg-page-nav",
1530
+ className: "tbx-page-nav",
807
1531
  disabled: currentPage <= 1,
808
1532
  onClick: () => onQueryChange(withPage(query, currentPage - 1, totalPages)),
809
1533
  "aria-label": locale.previousPage,
810
1534
  children: [
811
1535
  /* @__PURE__ */ jsx3(ChevronLeftIcon, { size: 16 }),
812
- /* @__PURE__ */ jsx3("span", { className: "nxg-sr-only", children: locale.previousPage })
1536
+ /* @__PURE__ */ jsx3("span", { className: "tbx-sr-only", children: locale.previousPage })
813
1537
  ]
814
1538
  }
815
1539
  ),
816
1540
  pageItems.map(
817
- (item, index) => item === "..." ? /* @__PURE__ */ jsx3("span", { className: "nxg-page-ellipsis", children: "\u2026" }, `gap-${index}`) : /* @__PURE__ */ jsx3(
1541
+ (item, index) => item === "..." ? /* @__PURE__ */ jsx3("span", { className: "tbx-page-ellipsis", children: "\u2026" }, `gap-${index}`) : /* @__PURE__ */ jsx3(
818
1542
  "button",
819
1543
  {
820
1544
  type: "button",
821
- className: item === currentPage ? "nxg-page-btn nxg-page-btn--current" : "nxg-page-btn",
1545
+ className: item === currentPage ? "tbx-page-btn tbx-page-btn--current" : "tbx-page-btn",
822
1546
  onClick: () => onQueryChange(withPage(query, item, totalPages)),
823
1547
  "aria-label": formatMessage(locale.pageLabel, { page: item }),
824
1548
  "aria-current": item === currentPage ? "page" : void 0,
@@ -831,52 +1555,63 @@ function NexGrid({
831
1555
  "button",
832
1556
  {
833
1557
  type: "button",
834
- className: "nxg-page-nav",
1558
+ className: "tbx-page-nav",
835
1559
  disabled: currentPage >= totalPages,
836
1560
  onClick: () => onQueryChange(withPage(query, currentPage + 1, totalPages)),
837
1561
  "aria-label": locale.nextPage,
838
1562
  children: [
839
1563
  /* @__PURE__ */ jsx3(ChevronRightIcon, { size: 16 }),
840
- /* @__PURE__ */ jsx3("span", { className: "nxg-sr-only", children: locale.nextPage })
1564
+ /* @__PURE__ */ jsx3("span", { className: "tbx-sr-only", children: locale.nextPage })
841
1565
  ]
842
1566
  }
843
1567
  )
844
- ] }),
845
- /* @__PURE__ */ jsxs2("form", { className: "nxg-jump", onSubmit: submitJump, children: [
846
- /* @__PURE__ */ jsx3("label", { className: "nxg-jump-label", htmlFor: jumpInputId, children: locale.goToPage }),
847
- /* @__PURE__ */ jsx3(
848
- "input",
849
- {
850
- id: jumpInputId,
851
- type: "number",
852
- className: "nxg-jump-input",
853
- min: 1,
854
- max: totalPages,
855
- value: jumpText,
856
- onChange: (event) => setJumpText(event.target.value),
857
- onBlur: () => submitJump(),
858
- "aria-label": locale.goToPageOf
859
- }
860
- )
861
- ] })
1568
+ ] }) : null,
1569
+ isJumpToPageVisible ? /* @__PURE__ */ jsxs2(
1570
+ "form",
1571
+ {
1572
+ className: "tbx-jump",
1573
+ onSubmit: (event) => {
1574
+ event.preventDefault();
1575
+ submitJump(jumpText);
1576
+ },
1577
+ children: [
1578
+ /* @__PURE__ */ jsx3("label", { className: "tbx-jump-label", htmlFor: jumpInputId, children: locale.goToPage }),
1579
+ /* @__PURE__ */ jsx3(
1580
+ "input",
1581
+ {
1582
+ id: jumpInputId,
1583
+ type: "number",
1584
+ className: "tbx-jump-input",
1585
+ min: 1,
1586
+ max: totalPages,
1587
+ value: jumpText,
1588
+ onChange: (event) => setJumpText(event.target.value),
1589
+ onBlur: () => submitJump(jumpText),
1590
+ "aria-label": locale.goToPageOf
1591
+ }
1592
+ )
1593
+ ]
1594
+ }
1595
+ ) : null
862
1596
  ] })
863
- ] })
1597
+ ] }) : null
864
1598
  ] });
865
1599
  }
1600
+ var NexGrid = TableX;
866
1601
 
867
- // src/use-client-nex-grid.ts
1602
+ // src/use-client-table-x.ts
868
1603
  import { useMemo as useMemo2, useState as useState4 } from "react";
869
1604
  import {
870
- defaultQuery,
871
- queryClientData
1605
+ defaultQuery as defaultQuery2,
1606
+ queryClientData as queryClientData2
872
1607
  } from "@nexgrid/core";
873
- function useClientNexGrid(allData, options) {
1608
+ function useClientTableX(allData, options) {
874
1609
  const [query, setQuery] = useState4(() => ({
875
- ...defaultQuery(),
1610
+ ...defaultQuery2(),
876
1611
  ...options?.initialQuery
877
1612
  }));
878
1613
  const page = useMemo2(
879
- () => queryClientData(allData, query, options),
1614
+ () => queryClientData2(allData, query, options),
880
1615
  [allData, query, options]
881
1616
  );
882
1617
  return {
@@ -890,11 +1625,12 @@ function useClientNexGrid(allData, options) {
890
1625
  setQuery
891
1626
  };
892
1627
  }
1628
+ var useClientNexGrid = useClientTableX;
893
1629
 
894
1630
  // src/index.ts
895
1631
  import {
896
- queryClientData as queryClientData2,
897
- defaultQuery as defaultQuery2,
1632
+ queryClientData as queryClientData3,
1633
+ defaultQuery as defaultQuery3,
898
1634
  parseQuery,
899
1635
  serializeQuery,
900
1636
  buildQueryUrl as buildQueryUrl2,
@@ -903,11 +1639,12 @@ import {
903
1639
  fromODataResponse,
904
1640
  primarySort as primarySort2,
905
1641
  withToggledSort as withToggledSort2,
1642
+ withToggledMultiSort as withToggledMultiSort2,
906
1643
  withSort,
907
1644
  withSearch as withSearch2,
908
1645
  withPage as withPage2,
909
1646
  withPageSize as withPageSize2,
910
- withFilter,
1647
+ withFilter as withFilter2,
911
1648
  totalPagesFor as totalPagesFor2,
912
1649
  isPageSize as isPageSize2,
913
1650
  PAGE_SIZES as PAGE_SIZES2,
@@ -920,24 +1657,27 @@ export {
920
1657
  DEFAULT_PAGE_SIZE,
921
1658
  NexGrid,
922
1659
  PAGE_SIZES2 as PAGE_SIZES,
1660
+ TableX,
923
1661
  buildODataUrl,
924
1662
  buildQueryUrl2 as buildQueryUrl,
925
- defaultQuery2 as defaultQuery,
1663
+ defaultQuery3 as defaultQuery,
926
1664
  fromODataResponse,
927
1665
  isPageSize2 as isPageSize,
928
1666
  parseQuery,
929
1667
  primarySort2 as primarySort,
930
- queryClientData2 as queryClientData,
1668
+ queryClientData3 as queryClientData,
931
1669
  resolveLocale2 as resolveLocale,
932
1670
  serializeQuery,
933
1671
  toODataParams,
934
1672
  totalPagesFor2 as totalPagesFor,
935
1673
  useClientNexGrid,
936
- withFilter,
1674
+ useClientTableX,
1675
+ withFilter2 as withFilter,
937
1676
  withPage2 as withPage,
938
1677
  withPageSize2 as withPageSize,
939
1678
  withSearch2 as withSearch,
940
1679
  withSort,
1680
+ withToggledMultiSort2 as withToggledMultiSort,
941
1681
  withToggledSort2 as withToggledSort
942
1682
  };
943
1683
  //# sourceMappingURL=index.js.map