@datum-cloud/datum-ui 0.3.0-alpha.3670fb2 → 0.3.0-alpha.4702cdb

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.
Files changed (131) hide show
  1. package/dist/app-navigation/index.mjs +12 -0
  2. package/dist/app-navigation-CCvjPijd.mjs +416 -0
  3. package/dist/autocomplete/index.mjs +1 -1
  4. package/dist/{autocomplete-B9bCkXtz.mjs → autocomplete-DcKO7pj5.mjs} +1 -1
  5. package/dist/avatar-stack/index.mjs +2 -2
  6. package/dist/{avatar-stack-Bh-tLz0X.mjs → avatar-stack-B21McFeb.mjs} +1 -1
  7. package/dist/{calendar-date-picker-mlbzp3xR.mjs → calendar-date-picker-Bw6Mrr-P.mjs} +2 -1
  8. package/dist/components/base/index.d.ts +1 -0
  9. package/dist/components/base/index.d.ts.map +1 -1
  10. package/dist/components/base/sidebar/index.d.ts +2 -0
  11. package/dist/components/base/sidebar/index.d.ts.map +1 -0
  12. package/dist/components/{features → base}/sidebar/sidebar.d.ts +1 -1
  13. package/dist/components/base/sidebar/sidebar.d.ts.map +1 -0
  14. package/dist/components/base/skeleton/index.d.ts +1 -1
  15. package/dist/components/base/skeleton/index.d.ts.map +1 -1
  16. package/dist/components/base/skeleton/skeleton.d.ts +22 -0
  17. package/dist/components/base/skeleton/skeleton.d.ts.map +1 -0
  18. package/dist/components/base/typography/typography.d.ts +2 -2
  19. package/dist/components/features/app-navigation/app-navigation.d.ts +14 -0
  20. package/dist/components/features/app-navigation/app-navigation.d.ts.map +1 -0
  21. package/dist/components/features/app-navigation/index.d.ts +4 -0
  22. package/dist/components/features/app-navigation/index.d.ts.map +1 -0
  23. package/dist/components/features/{sidebar/nav-main.d.ts → app-navigation/nav-menu.d.ts} +3 -3
  24. package/dist/components/features/app-navigation/nav-menu.d.ts.map +1 -0
  25. package/dist/components/features/calendar-date-picker/calendar-date-picker.d.ts +2 -1
  26. package/dist/components/features/calendar-date-picker/calendar-date-picker.d.ts.map +1 -1
  27. package/dist/components/features/data-table/components/active-filters.d.ts +1 -1
  28. package/dist/components/features/data-table/components/active-filters.d.ts.map +1 -1
  29. package/dist/components/features/data-table/components/content.d.ts.map +1 -1
  30. package/dist/components/features/data-table/components/loading.d.ts.map +1 -1
  31. package/dist/components/features/data-table/components/search.d.ts +1 -1
  32. package/dist/components/features/data-table/components/search.d.ts.map +1 -1
  33. package/dist/components/features/data-table/core/client-provider.d.ts +5 -7
  34. package/dist/components/features/data-table/core/client-provider.d.ts.map +1 -1
  35. package/dist/components/features/data-table/core/data-table-context.d.ts +23 -0
  36. package/dist/components/features/data-table/core/data-table-context.d.ts.map +1 -1
  37. package/dist/components/features/data-table/core/filter-engine.d.ts +5 -0
  38. package/dist/components/features/data-table/core/filter-engine.d.ts.map +1 -1
  39. package/dist/components/features/data-table/core/server-provider.d.ts +4 -7
  40. package/dist/components/features/data-table/core/server-provider.d.ts.map +1 -1
  41. package/dist/components/features/data-table/core/store.d.ts.map +1 -1
  42. package/dist/components/features/data-table/data-table.d.ts +1 -1
  43. package/dist/components/features/data-table/filters/checkbox-filter.d.ts +1 -1
  44. package/dist/components/features/data-table/filters/checkbox-filter.d.ts.map +1 -1
  45. package/dist/components/features/data-table/filters/date-picker-filter.d.ts +1 -1
  46. package/dist/components/features/data-table/filters/date-picker-filter.d.ts.map +1 -1
  47. package/dist/components/features/data-table/filters/select-filter.d.ts +1 -1
  48. package/dist/components/features/data-table/filters/select-filter.d.ts.map +1 -1
  49. package/dist/components/features/data-table/hooks/index.d.ts +1 -1
  50. package/dist/components/features/data-table/hooks/index.d.ts.map +1 -1
  51. package/dist/components/features/data-table/hooks/use-data-table-client.d.ts +11 -10
  52. package/dist/components/features/data-table/hooks/use-data-table-client.d.ts.map +1 -1
  53. package/dist/components/features/data-table/hooks/use-data-table-server.d.ts +12 -17
  54. package/dist/components/features/data-table/hooks/use-data-table-server.d.ts.map +1 -1
  55. package/dist/components/features/data-table/hooks/use-selectors.d.ts +4 -33
  56. package/dist/components/features/data-table/hooks/use-selectors.d.ts.map +1 -1
  57. package/dist/components/features/data-table/index.d.ts +2 -4
  58. package/dist/components/features/data-table/index.d.ts.map +1 -1
  59. package/dist/components/features/data-table/types.d.ts +28 -33
  60. package/dist/components/features/data-table/types.d.ts.map +1 -1
  61. package/dist/components/features/index.d.ts +1 -1
  62. package/dist/components/features/index.d.ts.map +1 -1
  63. package/dist/data-table/index.mjs +481 -388
  64. package/dist/date-picker/index.mjs +2 -2
  65. package/dist/dropdown/index.mjs +1 -1
  66. package/dist/dropzone/index.mjs +1 -1
  67. package/dist/empty-content/index.mjs +1 -1
  68. package/dist/form/index.mjs +3 -3
  69. package/dist/grid/index.mjs +1 -1
  70. package/dist/hooks/index.mjs +2 -2
  71. package/dist/index.mjs +34 -32
  72. package/dist/input-number/index.mjs +1 -1
  73. package/dist/input-with-addons/index.mjs +1 -1
  74. package/dist/loader-overlay/index.mjs +1 -1
  75. package/dist/map/index.mjs +1 -1
  76. package/dist/{map-ClJD-qxm.mjs → map-2RG9pYZR.mjs} +1 -1
  77. package/dist/more-actions/index.mjs +2 -2
  78. package/dist/{more-actions-DbC8dyed.mjs → more-actions-BODYgG1C.mjs} +2 -2
  79. package/dist/page-title/index.mjs +1 -1
  80. package/dist/sidebar/index.mjs +4 -5
  81. package/dist/{sidebar-C4NqSr4r.mjs → sidebar-BW76ss_f.mjs} +6 -415
  82. package/dist/skeleton/index.mjs +2 -1
  83. package/dist/skeleton-DZ31pU4B.mjs +28 -0
  84. package/dist/stepper/index.mjs +1 -1
  85. package/dist/styles/root.css +3 -0
  86. package/dist/switch/index.mjs +1 -1
  87. package/dist/table/index.mjs +1 -1
  88. package/dist/tabs/index.mjs +1 -1
  89. package/dist/tag-input/index.mjs +1 -1
  90. package/dist/task-queue/index.mjs +3 -3
  91. package/dist/{task-queue-dropdown-fo3TX58Q.mjs → task-queue-dropdown-DtS0IKci.mjs} +3 -3
  92. package/dist/textarea/index.mjs +1 -1
  93. package/dist/theme/index.mjs +1 -1
  94. package/dist/toast/index.mjs +1 -1
  95. package/dist/tooltip/index.mjs +1 -1
  96. package/dist/typography/index.mjs +1 -1
  97. package/dist/{use-copy-to-clipboard-C7xqNxBX.mjs → use-copy-to-clipboard-C9cT2Qb-.mjs} +1 -1
  98. package/dist/{use-stepper-CB1injte.mjs → use-stepper-DJd8o9dV.mjs} +8 -8
  99. package/dist/visually-hidden/index.mjs +1 -1
  100. package/package.json +83 -77
  101. package/dist/components/features/data-table/hooks/use-data-table-context.d.ts +0 -2
  102. package/dist/components/features/data-table/hooks/use-data-table-context.d.ts.map +0 -1
  103. package/dist/components/features/sidebar/app-sidebar.d.ts +0 -14
  104. package/dist/components/features/sidebar/app-sidebar.d.ts.map +0 -1
  105. package/dist/components/features/sidebar/index.d.ts +0 -4
  106. package/dist/components/features/sidebar/index.d.ts.map +0 -1
  107. package/dist/components/features/sidebar/nav-main.d.ts.map +0 -1
  108. package/dist/components/features/sidebar/sidebar.d.ts.map +0 -1
  109. /package/dist/{col-RfO7d6AR.mjs → col-DCneNxQj.mjs} +0 -0
  110. /package/dist/{dropdown-Cs7Xr8w7.mjs → dropdown-Dgm_b6Mm.mjs} +0 -0
  111. /package/dist/{dropzone-BT5fEDEF.mjs → dropzone-DR6O9OdU.mjs} +0 -0
  112. /package/dist/{empty-content-iDu3NUqG.mjs → empty-content-Dm7_5jO9.mjs} +0 -0
  113. /package/dist/{input-number-D9ydFith.mjs → input-number-D1HCcTXO.mjs} +0 -0
  114. /package/dist/{input-with-addons-CdgiUQce.mjs → input-with-addons-DN9LGwUU.mjs} +0 -0
  115. /package/dist/{loader-overlay-D83QeQNj.mjs → loader-overlay-CpA0zV8D.mjs} +0 -0
  116. /package/dist/{map-leaflet-imports-CdzvEnzY.mjs → map-leaflet-imports-CgEyVRnp.mjs} +0 -0
  117. /package/dist/{page-title-SGchAF6Y.mjs → page-title-D62FV6vD.mjs} +0 -0
  118. /package/dist/{skeleton-Cs6Q5GQc.mjs → skeleton-CkE23wsL.mjs} +0 -0
  119. /package/dist/{stepper-BG9DIzN5.mjs → stepper-CZeks9Ex.mjs} +0 -0
  120. /package/dist/{switch-B2VVauH6.mjs → switch-Cn9IM2gC.mjs} +0 -0
  121. /package/dist/{table-Dc3HfbM4.mjs → table-Dpzh0VPK.mjs} +0 -0
  122. /package/dist/{tabs-Ccb4uqbe.mjs → tabs-OYVCDOif.mjs} +0 -0
  123. /package/dist/{tag-input-BfHaKoMF.mjs → tag-input-DorFQ9bA.mjs} +0 -0
  124. /package/dist/{textarea-X4OjkqLJ.mjs → textarea-KZUKGHlO.mjs} +0 -0
  125. /package/dist/{theme.provider-Nun_O9-O.mjs → theme.provider-BG3cS9xe.mjs} +0 -0
  126. /package/dist/{to-api-format-zI26rEBI.mjs → to-api-format-CzPt5UAX.mjs} +0 -0
  127. /package/dist/{tooltip-DZFG1iMs.mjs → tooltip-U3XxlW4l.mjs} +0 -0
  128. /package/dist/{typography-T7WgvO77.mjs → typography-DdrxIJMd.mjs} +0 -0
  129. /package/dist/{use-debounce-Ctljs3MB.mjs → use-debounce-Dc95PFRX.mjs} +0 -0
  130. /package/dist/{use-toast-DN-fZBzJ.mjs → use-toast-DBmysDS6.mjs} +0 -0
  131. /package/dist/{visuallyhidden-CgkVhApW.mjs → visuallyhidden-CfBnXfvh.mjs} +0 -0
@@ -12,11 +12,12 @@ import { t as Label } from "../label-_ste_Re3.mjs";
12
12
  import { i as DropdownMenuItem, l as DropdownMenuTrigger, r as DropdownMenuContent, t as DropdownMenu } from "../dropdown-menu-DAFyO-qD.mjs";
13
13
  import { i as PopoverTrigger, r as PopoverContent, t as Popover } from "../popover-Ds9624qY.mjs";
14
14
  import { i as SelectItem, l as SelectTrigger, n as SelectContent, t as Select, u as SelectValue } from "../select-CwVIFWFO.mjs";
15
- import { t as Skeleton } from "../skeleton-Cs6Q5GQc.mjs";
16
- import { c as TableRow, i as TableCell, n as TableBody, o as TableHead, s as TableHeader, t as Table } from "../table-Dc3HfbM4.mjs";
17
- import { t as CalendarDatePicker } from "../calendar-date-picker-mlbzp3xR.mjs";
15
+ import "../skeleton-CkE23wsL.mjs";
16
+ import { t as Skeleton } from "../skeleton-DZ31pU4B.mjs";
17
+ import { c as TableRow, i as TableCell, n as TableBody, o as TableHead, s as TableHeader, t as Table } from "../table-Dpzh0VPK.mjs";
18
+ import { t as CalendarDatePicker } from "../calendar-date-picker-Bw6Mrr-P.mjs";
18
19
  import { ArrowDown, ArrowUp, ArrowUpDown, Check, ChevronDown, ChevronLeft, ChevronRight, MoreHorizontal, X } from "lucide-react";
19
- import { createContext, memo, use, useCallback, useEffect, useId, useMemo, useRef, useState, useSyncExternalStore } from "react";
20
+ import { createContext, memo, use, useCallback, useContext, useEffect, useId, useMemo, useRef, useState, useSyncExternalStore } from "react";
20
21
  import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
21
22
  import { parseAsInteger, parseAsString, useQueryStates } from "nuqs";
22
23
  import { flexRender, getCoreRowModel, getPaginationRowModel, getSortedRowModel, useReactTable } from "@tanstack/react-table";
@@ -157,6 +158,16 @@ function withSelectionColumn(columns, options = {}) {
157
158
 
158
159
  //#endregion
159
160
  //#region src/components/features/data-table/core/filter-engine.ts
161
+ /**
162
+ * Resolve a dot-path on an object (e.g. "status.registrationApproval").
163
+ * Falls back to a flat key lookup when the path has no dots.
164
+ */
165
+ function resolvePath(obj, path) {
166
+ if (obj == null) return void 0;
167
+ const record = obj;
168
+ if (!path.includes(".")) return record[path];
169
+ return path.split(".").reduce((acc, key) => acc != null ? acc[key] : void 0, record);
170
+ }
160
171
  const FILTER_STRATEGIES = {
161
172
  "checkbox": (cellValue, filterValue) => {
162
173
  if (filterValue == null) return true;
@@ -200,14 +211,13 @@ function applyFilters(data, filters, search, registeredFilters, customFilterFns,
200
211
  console.warn(`[DataTable] No filter strategy registered for column "${column}". Filter ignored.`);
201
212
  continue;
202
213
  }
203
- const cellValue = row[column];
204
- if (!fn(cellValue, value)) return false;
214
+ if (!fn(resolvePath(row, column), value)) return false;
205
215
  }
206
216
  if (hasSearch) {
207
217
  const query = search.toLowerCase();
208
218
  if (searchConfig.searchFn) return searchConfig.searchFn(row, search);
209
219
  if (searchConfig.searchableColumns && searchConfig.searchableColumns.length > 0) return searchConfig.searchableColumns.some((col) => {
210
- const cellValue = row[col];
220
+ const cellValue = resolvePath(row, col);
211
221
  return cellValue != null && String(cellValue).toLowerCase().includes(query);
212
222
  });
213
223
  return Object.values(row).some((val) => {
@@ -240,10 +250,12 @@ function createDataTableStore(options) {
240
250
  rowSelection: {},
241
251
  pageIndex: 0,
242
252
  pageSize: options.pageSize ?? DEFAULT_PAGE_SIZE,
253
+ columnCount: options.columnCount ?? 0,
243
254
  mode: options.mode,
244
- isLoading: false,
255
+ isLoading: options.isLoading ?? false,
245
256
  error: null,
246
- inlineContents: []
257
+ inlineContents: [],
258
+ _version: 0
247
259
  };
248
260
  if (options.defaultFilters && Object.keys(options.defaultFilters).length > 0) state = {
249
261
  ...state,
@@ -253,7 +265,10 @@ function createDataTableStore(options) {
253
265
  for (const listener of listeners) listener();
254
266
  }
255
267
  function setState(next) {
256
- state = next;
268
+ state = {
269
+ ...next,
270
+ _version: state._version + 1
271
+ };
257
272
  notify();
258
273
  }
259
274
  return {
@@ -281,7 +296,6 @@ function createDataTableStore(options) {
281
296
  filteredData: data
282
297
  });
283
298
  },
284
- setTable: (_t) => {},
285
299
  setSorting: (sorting) => {
286
300
  setState({
287
301
  ...state,
@@ -442,16 +456,34 @@ function createDataTableStore(options) {
442
456
  //#region src/components/features/data-table/core/data-table-context.tsx
443
457
  const DataTableStoreContext = createContext(null);
444
458
  const TableInstanceContext = createContext(null);
459
+ /**
460
+ * Monotonic counter that increments on every store mutation.
461
+ * Table-dependent hooks consume this context to force re-renders
462
+ * through React's {children} composition boundary, since the
463
+ * mutable table singleton (stable ref) cannot trigger context updates.
464
+ */
465
+ const DataTableRenderKeyContext = createContext(0);
466
+ /**
467
+ * Forces a re-render when the store changes. Used by table-dependent hooks.
468
+ * Uses useContext (not use()) because use() does not reliably register
469
+ * context subscriptions in SSR/hydration scenarios (React Router SSR),
470
+ * preventing re-renders when the context value changes.
471
+ */
472
+ function useRenderKey() {
473
+ return useContext(DataTableRenderKeyContext);
474
+ }
445
475
  const DataTableContext = createContext(null);
446
476
  function useDataTableStore() {
447
477
  const store = use(DataTableStoreContext);
448
478
  if (!store) throw new Error("useDataTableStore must be used within a <DataTable.Client> or <DataTable.Server> provider");
449
479
  return store;
450
480
  }
451
- function useTableInstance() {
452
- const table = use(TableInstanceContext);
453
- if (!table) throw new Error("useTableInstance must be used within a <DataTable.Client> or <DataTable.Server> provider");
454
- return table;
481
+ /**
482
+ * Returns the table instance or null if not yet available.
483
+ * Used by hooks that need to handle the null-table window during SSR.
484
+ */
485
+ function useTableInstanceOrNull() {
486
+ return use(TableInstanceContext);
455
487
  }
456
488
 
457
489
  //#endregion
@@ -482,7 +514,7 @@ function useSliceSelector(selector) {
482
514
  cachedRef.current = next;
483
515
  return next;
484
516
  }, [store, selector]);
485
- return useSyncExternalStore(store.subscribe, getSnapshot);
517
+ return useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);
486
518
  }
487
519
  function useDataTableFilters() {
488
520
  const store = useDataTableStore();
@@ -511,49 +543,64 @@ function useDataTableSorting() {
511
543
  }), [store]));
512
544
  }
513
545
  function useDataTableSelection() {
546
+ useRenderKey();
514
547
  const store = useDataTableStore();
515
- const table = useTableInstance();
516
- return useSliceSelector(useCallback((state) => ({
517
- rowSelection: state.rowSelection,
548
+ const table = useTableInstanceOrNull();
549
+ return {
550
+ rowSelection: store.getSnapshot().rowSelection,
518
551
  setRowSelection: store.setRowSelection,
519
- selectedRows: table.getFilteredSelectedRowModel().rows.map((r) => r.original)
520
- }), [store, table]));
552
+ selectedRows: table ? table.getFilteredSelectedRowModel().rows.map((r) => r.original) : []
553
+ };
521
554
  }
522
555
  function useDataTablePagination() {
556
+ useRenderKey();
523
557
  const store = useDataTableStore();
524
- const table = useTableInstance();
525
- const nextPage = useCallback(() => table.nextPage(), [table]);
526
- const prevPage = useCallback(() => table.previousPage(), [table]);
527
- return useSliceSelector(useCallback((state) => ({
558
+ const table = useTableInstanceOrNull();
559
+ const state = store.getSnapshot();
560
+ if (!table) return {
561
+ canNextPage: false,
562
+ canPrevPage: false,
563
+ nextPage: () => {},
564
+ prevPage: () => {},
565
+ pageIndex: state.pageIndex,
566
+ pageCount: 0,
567
+ setPageIndex: store.setPageIndex,
568
+ pageSize: state.pageSize,
569
+ setPageSize: store.setPageSize,
570
+ totalRows: 0
571
+ };
572
+ return {
528
573
  canNextPage: table.getCanNextPage(),
529
574
  canPrevPage: table.getCanPreviousPage(),
530
- nextPage,
531
- prevPage,
575
+ nextPage: () => table.nextPage(),
576
+ prevPage: () => table.previousPage(),
532
577
  pageIndex: state.pageIndex,
533
578
  pageCount: table.getPageCount(),
534
579
  setPageIndex: store.setPageIndex,
535
580
  pageSize: state.pageSize,
536
581
  setPageSize: store.setPageSize,
537
582
  totalRows: state.filteredData.length
538
- }), [
539
- store,
540
- table,
541
- nextPage,
542
- prevPage
543
- ]));
583
+ };
544
584
  }
545
585
  function useDataTableRows() {
546
- const table = useTableInstance();
547
- return useSliceSelector(useCallback((_state) => ({
586
+ useRenderKey();
587
+ const table = useTableInstanceOrNull();
588
+ if (!table) return {
589
+ rows: [],
590
+ headerGroups: [],
591
+ totalColumns: 0
592
+ };
593
+ return {
548
594
  rows: table.getRowModel().rows,
549
595
  headerGroups: table.getHeaderGroups(),
550
596
  totalColumns: table.getAllColumns().length
551
- }), [table]));
597
+ };
552
598
  }
553
599
  function useDataTableLoading() {
554
600
  return useSliceSelector(useCallback((state) => ({
555
601
  isLoading: state.isLoading,
556
- error: state.error
602
+ error: state.error,
603
+ columnCount: state.columnCount
557
604
  }), []));
558
605
  }
559
606
  function useDataTableInlineContents() {
@@ -564,49 +611,12 @@ function useDataTableInlineContents() {
564
611
  unregisterInlineContent: store.unregisterInlineContent
565
612
  }), [store]));
566
613
  }
567
- function useDataTableContext() {
568
- const store = useDataTableStore();
569
- const table = useTableInstance();
570
- const state = useSyncExternalStore(store.subscribe, store.getSnapshot);
571
- return {
572
- table,
573
- mode: state.mode,
574
- sorting: state.sorting,
575
- filters: state.filters,
576
- search: state.search,
577
- rowSelection: state.rowSelection,
578
- isLoading: state.isLoading,
579
- setSorting: store.setSorting,
580
- setFilter: store.setFilter,
581
- clearFilter: store.clearFilter,
582
- clearAllFilters: store.clearAllFilters,
583
- setSearch: store.setSearch,
584
- clearSearch: store.clearSearch,
585
- setRowSelection: store.setRowSelection,
586
- pagination: {
587
- canNextPage: table.getCanNextPage(),
588
- canPrevPage: table.getCanPreviousPage(),
589
- nextPage: () => table.nextPage(),
590
- prevPage: () => table.previousPage(),
591
- pageIndex: state.pageIndex,
592
- pageCount: table.getPageCount(),
593
- setPageIndex: store.setPageIndex,
594
- pageSize: state.pageSize,
595
- setPageSize: store.setPageSize
596
- },
597
- inlineContents: state.inlineContents,
598
- registerInlineContent: store.registerInlineContent,
599
- unregisterInlineContent: store.unregisterInlineContent
600
- };
601
- }
602
614
 
603
615
  //#endregion
604
616
  //#region src/components/features/data-table/components/active-filters.tsx
605
- function formatValue(column, value, formatter) {
606
- if (formatter) {
607
- const result = formatter(column, value);
608
- if (result !== void 0) return result;
609
- }
617
+ function formatValue(column, value, formatters) {
618
+ const fn = formatters?.[column];
619
+ if (fn) return fn(value);
610
620
  return String(value);
611
621
  }
612
622
  function FilterGroup({ label, children, className }) {
@@ -621,14 +631,15 @@ function FilterGroup({ label, children, className }) {
621
631
  });
622
632
  }
623
633
  const EMPTY_LABELS = {};
624
- function ActiveFiltersInner({ label = "Selected Filters", filterLabels = EMPTY_LABELS, formatFilterValue: formatter, clearAll = "icon", clearAllLabel = "Clear all", className, groupClassName, badgeClassName }) {
634
+ function ActiveFiltersInner({ label = "Selected Filters", excludeFilters, filterLabels = EMPTY_LABELS, formatFilterValue: formatters, clearAll = "icon", clearAllLabel = "Clear all", className, groupClassName, badgeClassName }) {
625
635
  const { filters, setFilter, clearFilter, clearAllFilters } = useDataTableFilters();
626
636
  const { search, clearSearch } = useDataTableSearch();
627
- const activeFilterEntries = Object.entries(filters).filter(([, value]) => value != null && value !== "" && !(Array.isArray(value) && value.length === 0));
628
- const hasSearch = search.length > 0;
637
+ const excludeSet = useMemo(() => new Set(excludeFilters ?? []), [excludeFilters]);
638
+ const activeFilterEntries = Object.entries(filters).filter(([key, value]) => !excludeSet.has(key) && value != null && value !== "" && !(Array.isArray(value) && value.length === 0));
639
+ const showSearch = search.length > 0 && !excludeSet.has("search");
629
640
  const hasFilters = activeFilterEntries.length > 0;
630
- if (!hasSearch && !hasFilters) return null;
631
- const totalGroups = activeFilterEntries.length + (hasSearch ? 1 : 0);
641
+ if (!showSearch && !hasFilters) return null;
642
+ const totalGroups = activeFilterEntries.length + (showSearch ? 1 : 0);
632
643
  const removeArrayItem = (column, items, item) => {
633
644
  const remaining = items.filter((v) => v !== item);
634
645
  if (remaining.length > 0) setFilter(column, remaining);
@@ -636,7 +647,7 @@ function ActiveFiltersInner({ label = "Selected Filters", filterLabels = EMPTY_L
636
647
  };
637
648
  const handleClearAll = () => {
638
649
  clearAllFilters();
639
- if (hasSearch) clearSearch();
650
+ if (search.length > 0) clearSearch();
640
651
  };
641
652
  const badgeCn = cn("flex items-center gap-1.5 px-2 py-0.5 text-xs", badgeClassName);
642
653
  return /* @__PURE__ */ jsxs("div", {
@@ -649,7 +660,7 @@ function ActiveFiltersInner({ label = "Selected Filters", filterLabels = EMPTY_L
649
660
  "data-slot": "dt-active-filters-label",
650
661
  children: label
651
662
  }),
652
- hasSearch && /* @__PURE__ */ jsx(FilterGroup, {
663
+ showSearch && /* @__PURE__ */ jsx(FilterGroup, {
653
664
  label: "Search",
654
665
  className: groupClassName,
655
666
  children: /* @__PURE__ */ jsxs(Badge, {
@@ -678,10 +689,10 @@ function ActiveFiltersInner({ label = "Selected Filters", filterLabels = EMPTY_L
678
689
  type: "muted",
679
690
  theme: "solid",
680
691
  className: badgeCn,
681
- children: [/* @__PURE__ */ jsx("span", { children: formatValue(column, item, formatter) }), /* @__PURE__ */ jsx(Button, {
692
+ children: [/* @__PURE__ */ jsx("span", { children: formatValue(column, item, formatters) }), /* @__PURE__ */ jsx(Button, {
682
693
  theme: "borderless",
683
694
  size: "small",
684
- "aria-label": `Remove ${formatValue(column, item, formatter)} from ${groupLabel}`,
695
+ "aria-label": `Remove ${formatValue(column, item, formatters)} from ${groupLabel}`,
685
696
  className: "h-auto p-0 text-muted-foreground hover:text-foreground",
686
697
  onClick: () => removeArrayItem(column, value, item),
687
698
  children: /* @__PURE__ */ jsx(X, {
@@ -698,7 +709,7 @@ function ActiveFiltersInner({ label = "Selected Filters", filterLabels = EMPTY_L
698
709
  type: "muted",
699
710
  theme: "solid",
700
711
  className: badgeCn,
701
- children: [/* @__PURE__ */ jsx("span", { children: formatValue(column, value, formatter) }), /* @__PURE__ */ jsx(Button, {
712
+ children: [/* @__PURE__ */ jsx("span", { children: formatValue(column, value, formatters) }), /* @__PURE__ */ jsx(Button, {
702
713
  theme: "borderless",
703
714
  size: "small",
704
715
  "aria-label": `Clear ${groupLabel} filter`,
@@ -804,9 +815,12 @@ function renderInlineContentRow(entry, colSpan, rows) {
804
815
  }
805
816
  function DataTableContent({ emptyMessage, className, tableClassName, headerClassName, headerRowClassName, headerCellClassName, bodyClassName, rowClassName, cellClassName }) {
806
817
  const { rows, headerGroups, totalColumns } = useDataTableRows();
818
+ const { isLoading, columnCount } = useDataTableLoading();
819
+ const { pageSize } = useDataTablePagination();
807
820
  const { inlineContents } = useDataTableInlineContents();
808
821
  const openInlineContents = useMemo(() => inlineContents.filter((e) => e.open), [inlineContents]);
809
822
  const colSpan = totalColumns;
823
+ const skeletonColumns = totalColumns || columnCount || DEFAULT_LOADING_ROWS;
810
824
  return /* @__PURE__ */ jsx("div", {
811
825
  className: cn("datum-ui-data-table", className),
812
826
  "data-slot": "dt",
@@ -843,7 +857,13 @@ function DataTableContent({ emptyMessage, className, tableClassName, headerClass
843
857
  children: flexRender(cell.column.columnDef.cell, cell.getContext())
844
858
  }, cell.id))
845
859
  }, row.id);
846
- }) : /* @__PURE__ */ jsx(TableRow, {
860
+ }) : isLoading ? Array.from({ length: pageSize }, (_, i) => /* @__PURE__ */ jsx(TableRow, {
861
+ "data-slot": "dt-skeleton-row",
862
+ children: Array.from({ length: skeletonColumns }, (_, j) => /* @__PURE__ */ jsx(TableCell, {
863
+ "data-slot": "dt-skeleton-cell",
864
+ children: /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-full" })
865
+ }, j))
866
+ }, i)) : /* @__PURE__ */ jsx(TableRow, {
847
867
  "data-slot": "dt-row",
848
868
  children: /* @__PURE__ */ jsx(TableCell, {
849
869
  colSpan,
@@ -914,19 +934,8 @@ function DataTableLoading({ rows = DEFAULT_LOADING_ROWS, columns = 4, className
914
934
  return /* @__PURE__ */ jsx("div", {
915
935
  className,
916
936
  "data-slot": "dt-loading",
917
- children: /* @__PURE__ */ jsxs("div", {
918
- className: "rounded-md border",
919
- children: [/* @__PURE__ */ jsx("div", {
920
- className: "border-b",
921
- children: /* @__PURE__ */ jsx("div", {
922
- className: "flex gap-4 p-4",
923
- children: Array.from({ length: columns }, (_, i) => /* @__PURE__ */ jsx(Skeleton, { className: "h-4 flex-1" }, i))
924
- })
925
- }), Array.from({ length: rows }, (_, rowIndex) => /* @__PURE__ */ jsx("div", {
926
- className: "flex gap-4 border-b p-4 last:border-b-0",
927
- children: Array.from({ length: columns }, (_, colIndex) => /* @__PURE__ */ jsx(Skeleton, { className: "h-4 flex-1" }, colIndex))
928
- }, rowIndex))]
929
- })
937
+ style: { overflowX: "auto" },
938
+ children: /* @__PURE__ */ jsxs(Table, { children: [/* @__PURE__ */ jsx(TableHeader, { children: /* @__PURE__ */ jsx(TableRow, { children: Array.from({ length: columns }, (_, i) => /* @__PURE__ */ jsx(TableHead, { children: /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-24" }) }, i)) }) }), /* @__PURE__ */ jsx(TableBody, { children: Array.from({ length: rows }, (_, rowIndex) => /* @__PURE__ */ jsx(TableRow, { children: Array.from({ length: columns }, (_, colIndex) => /* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-full" }) }, colIndex)) }, rowIndex)) })] })
930
939
  });
931
940
  }
932
941
 
@@ -1025,9 +1034,9 @@ function DataTablePagination({ pageSizes = DEFAULT_PAGE_SIZES, className }) {
1025
1034
  return /* @__PURE__ */ jsx(Button, {
1026
1035
  theme: isActive ? "solid" : "outline",
1027
1036
  size: "small",
1028
- className: cn("h-8 min-w-8 px-2", isActive && "font-semibold"),
1037
+ className: cn("h-8 min-w-8 px-2", isActive && "pointer-events-none font-semibold"),
1029
1038
  onClick: () => setPageIndex(page - 1),
1030
- disabled: isActive,
1039
+ "aria-disabled": isActive || void 0,
1031
1040
  "aria-label": `Page ${page}`,
1032
1041
  "aria-current": isActive ? "page" : void 0,
1033
1042
  children: page
@@ -1091,7 +1100,7 @@ function DataTableRowActions({ row, actions, isLoading = false, className }) {
1091
1100
 
1092
1101
  //#endregion
1093
1102
  //#region src/components/features/data-table/components/search.tsx
1094
- function DataTableSearch({ placeholder = "Search...", debounceMs = DEFAULT_DEBOUNCE_MS, className }) {
1103
+ function DataTableSearch({ placeholder = "Search...", debounceMs = DEFAULT_DEBOUNCE_MS, className, disabled }) {
1095
1104
  const { search, setSearch } = useDataTableSearch();
1096
1105
  const [inputValue, setInputValue] = useState(search);
1097
1106
  useEffect(() => {
@@ -1113,18 +1122,105 @@ function DataTableSearch({ placeholder = "Search...", debounceMs = DEFAULT_DEBOU
1113
1122
  value: inputValue,
1114
1123
  onChange: (e) => setInputValue(e.target.value),
1115
1124
  className,
1125
+ disabled,
1116
1126
  "aria-label": placeholder,
1117
1127
  "data-slot": "dt-search"
1118
1128
  });
1119
1129
  }
1120
1130
 
1131
+ //#endregion
1132
+ //#region src/components/features/data-table/hooks/use-data-table-client.ts
1133
+ /**
1134
+ * Creates a TanStack Table instance from an existing store.
1135
+ * Does NOT create the store or sync data — the caller is responsible for that.
1136
+ */
1137
+ function useClientTable(store, options) {
1138
+ const { columns, getRowId, enableRowSelection = false, stateAdapter } = options;
1139
+ const resolvedColumns = useMemo(() => enableRowSelection ? withSelectionColumn(columns, typeof enableRowSelection === "object" ? enableRowSelection : {}) : columns, [columns, enableRowSelection]);
1140
+ const { filteredData, sorting, rowSelection, pageIndex, pageSize: storePageSize, filters, search } = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);
1141
+ const table = useReactTable({
1142
+ data: filteredData,
1143
+ columns: resolvedColumns,
1144
+ state: {
1145
+ sorting,
1146
+ rowSelection,
1147
+ pagination: {
1148
+ pageIndex,
1149
+ pageSize: storePageSize
1150
+ }
1151
+ },
1152
+ onSortingChange: (updater) => {
1153
+ const next = typeof updater === "function" ? updater(sorting) : updater;
1154
+ store.setSorting(next);
1155
+ },
1156
+ onRowSelectionChange: (updater) => {
1157
+ const next = typeof updater === "function" ? updater(rowSelection) : updater;
1158
+ store.setRowSelection(next);
1159
+ },
1160
+ onPaginationChange: (updater) => {
1161
+ const next = typeof updater === "function" ? updater({
1162
+ pageIndex,
1163
+ pageSize: storePageSize
1164
+ }) : updater;
1165
+ store.setPagination(next.pageIndex, next.pageSize);
1166
+ },
1167
+ getCoreRowModel: getCoreRowModel(),
1168
+ getSortedRowModel: getSortedRowModel(),
1169
+ getPaginationRowModel: getPaginationRowModel(),
1170
+ getRowId,
1171
+ enableRowSelection: !!enableRowSelection
1172
+ });
1173
+ const hydratedRef = useRef(false);
1174
+ useEffect(() => {
1175
+ if (stateAdapter && !hydratedRef.current) {
1176
+ hydratedRef.current = true;
1177
+ const persisted = stateAdapter.read();
1178
+ if (persisted.sorting && persisted.sorting.length > 0) store.setSorting(persisted.sorting);
1179
+ if (persisted.filters) {
1180
+ for (const [key, value] of Object.entries(persisted.filters)) if (value != null) store.setFilter(key, value);
1181
+ }
1182
+ if (persisted.search) store.setSearch(persisted.search);
1183
+ if (persisted.pageIndex != null && persisted.pageIndex > 0) store.setPageIndex(persisted.pageIndex);
1184
+ if (persisted.pageSize != null) store.setPageSize(persisted.pageSize);
1185
+ }
1186
+ }, []);
1187
+ const isFirstWrite = useRef(true);
1188
+ useEffect(() => {
1189
+ if (!stateAdapter) return;
1190
+ if (isFirstWrite.current) {
1191
+ isFirstWrite.current = false;
1192
+ return;
1193
+ }
1194
+ stateAdapter.write({
1195
+ sorting,
1196
+ filters,
1197
+ search,
1198
+ pageIndex,
1199
+ pageSize: storePageSize
1200
+ });
1201
+ }, [
1202
+ sorting,
1203
+ filters,
1204
+ search,
1205
+ pageIndex,
1206
+ storePageSize,
1207
+ stateAdapter
1208
+ ]);
1209
+ return { table };
1210
+ }
1211
+
1121
1212
  //#endregion
1122
1213
  //#region src/components/features/data-table/core/client-provider.tsx
1123
- function ClientProvider({ store, table, className, children }) {
1124
- return /* @__PURE__ */ jsx(DataTableStoreContext, {
1125
- value: store,
1126
- children: /* @__PURE__ */ jsx(TableInstanceContext, {
1127
- value: table,
1214
+ /**
1215
+ * Inner component that calls useClientTable.
1216
+ * Only rendered after hydration (gated by tableReady).
1217
+ */
1218
+ function ClientProviderInner({ store, className, children, ...options }) {
1219
+ const { table } = useClientTable(store, options);
1220
+ return /* @__PURE__ */ jsx(TableInstanceContext, {
1221
+ value: table,
1222
+ children: /* @__PURE__ */ jsx(DataTableRenderKeyContext, {
1223
+ value: store.getSnapshot()._version,
1128
1224
  children: /* @__PURE__ */ jsx("div", {
1129
1225
  className,
1130
1226
  children
@@ -1132,57 +1228,290 @@ function ClientProvider({ store, table, className, children }) {
1132
1228
  })
1133
1229
  });
1134
1230
  }
1135
-
1136
- //#endregion
1137
- //#region src/components/features/data-table/core/server-provider.tsx
1138
- function ServerProvider({ store, table, className, children }) {
1231
+ function ClientProvider(props) {
1232
+ const { data, columns, loading, pageSize, getRowId, enableRowSelection, defaultSort, defaultFilters, searchableColumns, searchFn, filterFns, stateAdapter, className, children } = props;
1233
+ const store = useMemo(() => createDataTableStore({
1234
+ data,
1235
+ mode: "client",
1236
+ isLoading: true,
1237
+ defaultSort,
1238
+ defaultFilters,
1239
+ pageSize,
1240
+ columnCount: columns.length,
1241
+ searchableColumns,
1242
+ searchFn,
1243
+ filterFns
1244
+ }), []);
1245
+ const isInitialRender = useRef(true);
1246
+ useEffect(() => {
1247
+ if (isInitialRender.current) {
1248
+ isInitialRender.current = false;
1249
+ return;
1250
+ }
1251
+ store.setData(data);
1252
+ }, [data, store]);
1253
+ const [tableReady, setTableReady] = useState(false);
1254
+ useEffect(() => setTableReady(true), []);
1255
+ useEffect(() => {
1256
+ if (!tableReady) return;
1257
+ store.setLoading(loading ?? false);
1258
+ }, [
1259
+ loading,
1260
+ tableReady,
1261
+ store
1262
+ ]);
1139
1263
  return /* @__PURE__ */ jsx(DataTableStoreContext, {
1140
1264
  value: store,
1141
- children: /* @__PURE__ */ jsx(TableInstanceContext, {
1142
- value: table,
1143
- children: /* @__PURE__ */ jsx("div", {
1144
- className,
1145
- children
1265
+ children: tableReady ? /* @__PURE__ */ jsx(ClientProviderInner, {
1266
+ store,
1267
+ columns,
1268
+ getRowId,
1269
+ enableRowSelection,
1270
+ stateAdapter,
1271
+ className,
1272
+ children
1273
+ }) : /* @__PURE__ */ jsx(TableInstanceContext, {
1274
+ value: null,
1275
+ children: /* @__PURE__ */ jsx(DataTableRenderKeyContext, {
1276
+ value: 0,
1277
+ children: /* @__PURE__ */ jsx("div", {
1278
+ className,
1279
+ children
1280
+ })
1146
1281
  })
1147
1282
  })
1148
1283
  });
1149
1284
  }
1150
1285
 
1151
1286
  //#endregion
1152
- //#region src/components/features/data-table/filters/checkbox-filter.tsx
1153
- const MAX_VISIBLE_BADGES = 2;
1154
- function CheckboxFilter({ column, label, options, className, checkboxPopoverClassName }) {
1155
- const { filters, setFilter, clearFilter, registerFilter, unregisterFilter } = useDataTableFilters();
1156
- const [open, setOpen] = useState(false);
1287
+ //#region src/components/features/data-table/hooks/use-data-table-server.ts
1288
+ /**
1289
+ * Creates a TanStack Table instance from an existing store.
1290
+ * Handles fetch-on-query-change, cursor pagination, and state adapter sync.
1291
+ * Does NOT create the store — the caller is responsible for that.
1292
+ */
1293
+ function useServerTable(store, options) {
1294
+ const { columns, fetchFn, transform, getRowId, enableRowSelection = false, stateAdapter } = options;
1295
+ const fetchRef = useRef(fetchFn);
1296
+ const transformRef = useRef(transform);
1157
1297
  useEffect(() => {
1158
- registerFilter(column, "checkbox");
1159
- return () => unregisterFilter(column);
1298
+ fetchRef.current = fetchFn;
1299
+ }, [fetchFn]);
1300
+ useEffect(() => {
1301
+ transformRef.current = transform;
1302
+ }, [transform]);
1303
+ const cursorMapRef = useRef(/* @__PURE__ */ new Map());
1304
+ const hasNextPageRef = useRef(false);
1305
+ const { sorting, filters, search, rowSelection, pageSize, pageIndex } = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);
1306
+ const prevQueryRef = useRef({
1307
+ sorting,
1308
+ filters,
1309
+ search,
1310
+ pageSize
1311
+ });
1312
+ useEffect(() => {
1313
+ const prev = prevQueryRef.current;
1314
+ if (prev.sorting !== sorting || prev.filters !== filters || prev.search !== search || prev.pageSize !== pageSize) {
1315
+ cursorMapRef.current = /* @__PURE__ */ new Map();
1316
+ hasNextPageRef.current = false;
1317
+ if (pageIndex !== 0) {
1318
+ prevQueryRef.current = {
1319
+ sorting,
1320
+ filters,
1321
+ search,
1322
+ pageSize
1323
+ };
1324
+ store.setPageIndex(0);
1325
+ return;
1326
+ }
1327
+ }
1328
+ prevQueryRef.current = {
1329
+ sorting,
1330
+ filters,
1331
+ search,
1332
+ pageSize
1333
+ };
1334
+ let cancelled = false;
1335
+ store.setLoading(true);
1336
+ const cursor = cursorMapRef.current.get(pageIndex);
1337
+ fetchRef.current({
1338
+ sorting,
1339
+ filters,
1340
+ search,
1341
+ cursor,
1342
+ limit: pageSize
1343
+ }).then((response) => {
1344
+ if (cancelled) return;
1345
+ const result = transformRef.current(response);
1346
+ store.setServerData(result.data);
1347
+ store.setError(null);
1348
+ if (result.cursor) cursorMapRef.current.set(pageIndex + 1, result.cursor);
1349
+ hasNextPageRef.current = result.hasNextPage;
1350
+ }).catch((error) => {
1351
+ if (cancelled) return;
1352
+ store.setServerData([]);
1353
+ store.setError(error instanceof Error ? error : new Error(String(error)));
1354
+ hasNextPageRef.current = false;
1355
+ }).finally(() => {
1356
+ if (!cancelled) store.setLoading(false);
1357
+ });
1358
+ return () => {
1359
+ cancelled = true;
1360
+ };
1160
1361
  }, [
1161
- column,
1162
- registerFilter,
1163
- unregisterFilter
1362
+ sorting,
1363
+ filters,
1364
+ search,
1365
+ pageSize,
1366
+ pageIndex,
1367
+ store
1164
1368
  ]);
1165
- const selectedValues = filters[column] ?? [];
1166
- const updateValues = (newValues) => {
1167
- if (newValues.length > 0) setFilter(column, newValues);
1168
- else clearFilter(column);
1169
- };
1170
- const handleToggle = (optionValue, checked) => {
1171
- updateValues(checked ? [...selectedValues, optionValue] : selectedValues.filter((v) => v !== optionValue));
1172
- };
1173
- const removeValue = (optionValue) => {
1174
- updateValues(selectedValues.filter((v) => v !== optionValue));
1175
- };
1176
- const visibleBadges = selectedValues.slice(0, MAX_VISIBLE_BADGES);
1177
- const remainingCount = selectedValues.length - MAX_VISIBLE_BADGES;
1178
- return /* @__PURE__ */ jsxs(Popover, {
1179
- open,
1180
- onOpenChange: setOpen,
1369
+ const resolvedColumns = useMemo(() => enableRowSelection ? withSelectionColumn(columns, typeof enableRowSelection === "object" ? enableRowSelection : {}) : columns, [columns, enableRowSelection]);
1370
+ const table = useReactTable({
1371
+ data: store.getSnapshot().data,
1372
+ columns: resolvedColumns,
1373
+ state: {
1374
+ sorting,
1375
+ rowSelection,
1376
+ pagination: {
1377
+ pageIndex,
1378
+ pageSize
1379
+ }
1380
+ },
1381
+ manualPagination: true,
1382
+ manualSorting: true,
1383
+ manualFiltering: true,
1384
+ pageCount: hasNextPageRef.current ? pageIndex + 2 : pageIndex + 1,
1385
+ getCoreRowModel: getCoreRowModel(),
1386
+ getRowId,
1387
+ enableRowSelection: !!enableRowSelection,
1388
+ onSortingChange: (updater) => {
1389
+ const next = typeof updater === "function" ? updater(sorting) : updater;
1390
+ store.setSorting(next);
1391
+ },
1392
+ onRowSelectionChange: (updater) => {
1393
+ const next = typeof updater === "function" ? updater(rowSelection) : updater;
1394
+ store.setRowSelection(next);
1395
+ },
1396
+ onPaginationChange: (updater) => {
1397
+ const next = typeof updater === "function" ? updater({
1398
+ pageIndex,
1399
+ pageSize
1400
+ }) : updater;
1401
+ store.setPagination(next.pageIndex, next.pageSize);
1402
+ }
1403
+ });
1404
+ useEffect(() => {
1405
+ if (stateAdapter) stateAdapter.write({
1406
+ sorting,
1407
+ filters,
1408
+ search,
1409
+ pageSize
1410
+ });
1411
+ }, [
1412
+ sorting,
1413
+ filters,
1414
+ search,
1415
+ pageSize,
1416
+ stateAdapter
1417
+ ]);
1418
+ return { table };
1419
+ }
1420
+
1421
+ //#endregion
1422
+ //#region src/components/features/data-table/core/server-provider.tsx
1423
+ /**
1424
+ * Inner component that calls useServerTable.
1425
+ * Only rendered after hydration (gated by tableReady).
1426
+ */
1427
+ function ServerProviderInner({ store, className, children, ...options }) {
1428
+ const { table } = useServerTable(store, options);
1429
+ return /* @__PURE__ */ jsx(TableInstanceContext, {
1430
+ value: table,
1431
+ children: /* @__PURE__ */ jsx(DataTableRenderKeyContext, {
1432
+ value: store.getSnapshot()._version,
1433
+ children: /* @__PURE__ */ jsx("div", {
1434
+ className,
1435
+ children
1436
+ })
1437
+ })
1438
+ });
1439
+ }
1440
+ function ServerProvider(props) {
1441
+ const { columns, fetchFn, transform, limit = 20, getRowId, enableRowSelection, defaultSort, defaultFilters, stateAdapter, className, children } = props;
1442
+ const store = useMemo(() => createDataTableStore({
1443
+ data: [],
1444
+ mode: "server",
1445
+ isLoading: true,
1446
+ defaultSort,
1447
+ defaultFilters,
1448
+ pageSize: limit,
1449
+ columnCount: columns.length
1450
+ }), []);
1451
+ const [tableReady, setTableReady] = useState(false);
1452
+ useEffect(() => setTableReady(true), []);
1453
+ return /* @__PURE__ */ jsx(DataTableStoreContext, {
1454
+ value: store,
1455
+ children: tableReady ? /* @__PURE__ */ jsx(ServerProviderInner, {
1456
+ store,
1457
+ columns,
1458
+ fetchFn,
1459
+ transform,
1460
+ limit,
1461
+ getRowId,
1462
+ enableRowSelection,
1463
+ stateAdapter,
1464
+ className,
1465
+ children
1466
+ }) : /* @__PURE__ */ jsx(TableInstanceContext, {
1467
+ value: null,
1468
+ children: /* @__PURE__ */ jsx(DataTableRenderKeyContext, {
1469
+ value: 0,
1470
+ children: /* @__PURE__ */ jsx("div", {
1471
+ className,
1472
+ children
1473
+ })
1474
+ })
1475
+ })
1476
+ });
1477
+ }
1478
+
1479
+ //#endregion
1480
+ //#region src/components/features/data-table/filters/checkbox-filter.tsx
1481
+ const MAX_VISIBLE_BADGES = 2;
1482
+ function CheckboxFilter({ column, label, options, className, checkboxPopoverClassName, disabled }) {
1483
+ const { filters, setFilter, clearFilter, registerFilter, unregisterFilter } = useDataTableFilters();
1484
+ const [open, setOpen] = useState(false);
1485
+ useEffect(() => {
1486
+ registerFilter(column, "checkbox");
1487
+ return () => unregisterFilter(column);
1488
+ }, [
1489
+ column,
1490
+ registerFilter,
1491
+ unregisterFilter
1492
+ ]);
1493
+ const selectedValues = filters[column] ?? [];
1494
+ const updateValues = (newValues) => {
1495
+ if (newValues.length > 0) setFilter(column, newValues);
1496
+ else clearFilter(column);
1497
+ };
1498
+ const handleToggle = (optionValue, checked) => {
1499
+ updateValues(checked ? [...selectedValues, optionValue] : selectedValues.filter((v) => v !== optionValue));
1500
+ };
1501
+ const removeValue = (optionValue) => {
1502
+ updateValues(selectedValues.filter((v) => v !== optionValue));
1503
+ };
1504
+ const visibleBadges = selectedValues.slice(0, MAX_VISIBLE_BADGES);
1505
+ const remainingCount = selectedValues.length - MAX_VISIBLE_BADGES;
1506
+ return /* @__PURE__ */ jsxs(Popover, {
1507
+ open,
1508
+ onOpenChange: setOpen,
1181
1509
  children: [/* @__PURE__ */ jsx(PopoverTrigger, {
1182
1510
  asChild: true,
1183
1511
  children: /* @__PURE__ */ jsxs(Button, {
1184
1512
  theme: "outline",
1185
- className: cn("justify-between gap-1", className),
1513
+ disabled,
1514
+ className: cn("h-10 justify-between gap-1", className),
1186
1515
  "data-slot": "dt-filter",
1187
1516
  "data-testid": "dt-filter-trigger",
1188
1517
  children: [selectedValues.length > 0 ? /* @__PURE__ */ jsxs("div", {
@@ -1261,7 +1590,7 @@ function CheckboxFilter({ column, label, options, className, checkboxPopoverClas
1261
1590
 
1262
1591
  //#endregion
1263
1592
  //#region src/components/features/data-table/filters/date-picker-filter.tsx
1264
- function DatePickerFilter({ column, label, className, datePickerPopoverClassName, disableFuture, disablePast, minDate, maxDate }) {
1593
+ function DatePickerFilter({ column, label, className, datePickerPopoverClassName, disableFuture, disablePast, minDate, maxDate, disabled }) {
1265
1594
  const { filters, setFilter, clearFilter, registerFilter, unregisterFilter } = useDataTableFilters();
1266
1595
  const rawValue = filters[column];
1267
1596
  useEffect(() => {
@@ -1286,8 +1615,9 @@ function DatePickerFilter({ column, label, className, datePickerPopoverClassName
1286
1615
  numberOfMonths: 1,
1287
1616
  closeOnSelect: true,
1288
1617
  placeholder: label,
1289
- triggerClassName: className,
1618
+ triggerClassName: cn("h-10", className),
1290
1619
  variant: "outline",
1620
+ disabled,
1291
1621
  disableFuture,
1292
1622
  disablePast,
1293
1623
  minDate,
@@ -1303,7 +1633,7 @@ function DatePickerFilter({ column, label, className, datePickerPopoverClassName
1303
1633
 
1304
1634
  //#endregion
1305
1635
  //#region src/components/features/data-table/filters/select-filter.tsx
1306
- function SelectFilter({ column, label, options, placeholder, searchable = true, className, selectPopoverClassName }) {
1636
+ function SelectFilter({ column, label, options, placeholder, searchable = true, className, selectPopoverClassName, disabled }) {
1307
1637
  const { filters, setFilter, clearFilter, registerFilter, unregisterFilter } = useDataTableFilters();
1308
1638
  const [open, setOpen] = useState(false);
1309
1639
  const value = filters[column];
@@ -1325,7 +1655,8 @@ function SelectFilter({ column, label, options, placeholder, searchable = true,
1325
1655
  theme: "outline",
1326
1656
  role: "combobox",
1327
1657
  "aria-expanded": open,
1328
- className: cn("justify-between", className),
1658
+ disabled,
1659
+ className: cn("h-10 justify-between", className),
1329
1660
  "data-slot": "dt-filter",
1330
1661
  "data-testid": "dt-filter-trigger",
1331
1662
  children: [/* @__PURE__ */ jsx("span", {
@@ -1388,242 +1719,4 @@ const DataTable = {
1388
1719
  };
1389
1720
 
1390
1721
  //#endregion
1391
- //#region src/components/features/data-table/hooks/use-data-table-client.ts
1392
- function useDataTableClient(options) {
1393
- const { data, columns, pageSize, getRowId, enableRowSelection = false, defaultSort, defaultFilters, searchableColumns, searchFn, filterFns, stateAdapter } = options;
1394
- const store = useMemo(() => createDataTableStore({
1395
- data,
1396
- mode: "client",
1397
- defaultSort,
1398
- defaultFilters,
1399
- pageSize,
1400
- searchableColumns,
1401
- searchFn,
1402
- filterFns
1403
- }), []);
1404
- const isInitialRender = useRef(true);
1405
- useEffect(() => {
1406
- if (isInitialRender.current) {
1407
- isInitialRender.current = false;
1408
- return;
1409
- }
1410
- store.setData(data);
1411
- }, [data, store]);
1412
- const resolvedColumns = useMemo(() => enableRowSelection ? withSelectionColumn(columns, typeof enableRowSelection === "object" ? enableRowSelection : {}) : columns, [columns, enableRowSelection]);
1413
- const { filteredData, sorting, rowSelection, pageIndex, pageSize: storePageSize, filters, search } = useSyncExternalStore(store.subscribe, store.getSnapshot);
1414
- const table = useReactTable({
1415
- data: filteredData,
1416
- columns: resolvedColumns,
1417
- state: {
1418
- sorting,
1419
- rowSelection,
1420
- pagination: {
1421
- pageIndex,
1422
- pageSize: storePageSize
1423
- }
1424
- },
1425
- onSortingChange: (updater) => {
1426
- const next = typeof updater === "function" ? updater(sorting) : updater;
1427
- store.setSorting(next);
1428
- },
1429
- onRowSelectionChange: (updater) => {
1430
- const next = typeof updater === "function" ? updater(rowSelection) : updater;
1431
- store.setRowSelection(next);
1432
- },
1433
- onPaginationChange: (updater) => {
1434
- const next = typeof updater === "function" ? updater({
1435
- pageIndex,
1436
- pageSize: storePageSize
1437
- }) : updater;
1438
- store.setPagination(next.pageIndex, next.pageSize);
1439
- },
1440
- getCoreRowModel: getCoreRowModel(),
1441
- getSortedRowModel: getSortedRowModel(),
1442
- getPaginationRowModel: getPaginationRowModel(),
1443
- getRowId,
1444
- enableRowSelection: !!enableRowSelection
1445
- });
1446
- const hydratedRef = useRef(false);
1447
- useEffect(() => {
1448
- if (stateAdapter && !hydratedRef.current) {
1449
- hydratedRef.current = true;
1450
- const persisted = stateAdapter.read();
1451
- if (persisted.sorting && persisted.sorting.length > 0) store.setSorting(persisted.sorting);
1452
- if (persisted.filters) {
1453
- for (const [key, value] of Object.entries(persisted.filters)) if (value != null) store.setFilter(key, value);
1454
- }
1455
- if (persisted.search) store.setSearch(persisted.search);
1456
- if (persisted.pageIndex != null && persisted.pageIndex > 0) store.setPageIndex(persisted.pageIndex);
1457
- if (persisted.pageSize != null) store.setPageSize(persisted.pageSize);
1458
- }
1459
- }, []);
1460
- const isFirstWrite = useRef(true);
1461
- useEffect(() => {
1462
- if (!stateAdapter) return;
1463
- if (isFirstWrite.current) {
1464
- isFirstWrite.current = false;
1465
- return;
1466
- }
1467
- stateAdapter.write({
1468
- sorting,
1469
- filters,
1470
- search,
1471
- pageIndex,
1472
- pageSize: storePageSize
1473
- });
1474
- }, [
1475
- sorting,
1476
- filters,
1477
- search,
1478
- pageIndex,
1479
- storePageSize,
1480
- stateAdapter
1481
- ]);
1482
- return {
1483
- store,
1484
- table
1485
- };
1486
- }
1487
-
1488
- //#endregion
1489
- //#region src/components/features/data-table/hooks/use-data-table-server.ts
1490
- function useDataTableServer(options) {
1491
- const { columns, fetchFn, transform, limit = 20, getRowId, enableRowSelection = false, defaultSort, defaultFilters, stateAdapter } = options;
1492
- const fetchRef = useRef(fetchFn);
1493
- const transformRef = useRef(transform);
1494
- useEffect(() => {
1495
- fetchRef.current = fetchFn;
1496
- }, [fetchFn]);
1497
- useEffect(() => {
1498
- transformRef.current = transform;
1499
- }, [transform]);
1500
- const cursorMapRef = useRef(/* @__PURE__ */ new Map());
1501
- const hasNextPageRef = useRef(false);
1502
- const store = useMemo(() => createDataTableStore({
1503
- data: [],
1504
- mode: "server",
1505
- defaultSort,
1506
- defaultFilters,
1507
- pageSize: limit
1508
- }), []);
1509
- const { sorting, filters, search, rowSelection, pageSize, pageIndex } = useSyncExternalStore(store.subscribe, store.getSnapshot);
1510
- const prevQueryRef = useRef({
1511
- sorting,
1512
- filters,
1513
- search,
1514
- pageSize
1515
- });
1516
- useEffect(() => {
1517
- const prev = prevQueryRef.current;
1518
- if (prev.sorting !== sorting || prev.filters !== filters || prev.search !== search || prev.pageSize !== pageSize) {
1519
- cursorMapRef.current = /* @__PURE__ */ new Map();
1520
- hasNextPageRef.current = false;
1521
- if (pageIndex !== 0) {
1522
- prevQueryRef.current = {
1523
- sorting,
1524
- filters,
1525
- search,
1526
- pageSize
1527
- };
1528
- store.setPageIndex(0);
1529
- return;
1530
- }
1531
- }
1532
- prevQueryRef.current = {
1533
- sorting,
1534
- filters,
1535
- search,
1536
- pageSize
1537
- };
1538
- let cancelled = false;
1539
- store.setLoading(true);
1540
- const cursor = cursorMapRef.current.get(pageIndex);
1541
- fetchRef.current({
1542
- sorting,
1543
- filters,
1544
- search,
1545
- cursor,
1546
- limit: pageSize
1547
- }).then((response) => {
1548
- if (cancelled) return;
1549
- const result = transformRef.current(response);
1550
- store.setServerData(result.data);
1551
- store.setError(null);
1552
- if (result.nextCursor) cursorMapRef.current.set(pageIndex + 1, result.nextCursor);
1553
- hasNextPageRef.current = result.hasNextPage;
1554
- }).catch((error) => {
1555
- if (cancelled) return;
1556
- store.setServerData([]);
1557
- store.setError(error instanceof Error ? error : new Error(String(error)));
1558
- hasNextPageRef.current = false;
1559
- }).finally(() => {
1560
- if (!cancelled) store.setLoading(false);
1561
- });
1562
- return () => {
1563
- cancelled = true;
1564
- };
1565
- }, [
1566
- sorting,
1567
- filters,
1568
- search,
1569
- pageSize,
1570
- pageIndex,
1571
- store
1572
- ]);
1573
- const resolvedColumns = useMemo(() => enableRowSelection ? withSelectionColumn(columns, typeof enableRowSelection === "object" ? enableRowSelection : {}) : columns, [columns, enableRowSelection]);
1574
- const table = useReactTable({
1575
- data: store.getSnapshot().data,
1576
- columns: resolvedColumns,
1577
- state: {
1578
- sorting,
1579
- rowSelection,
1580
- pagination: {
1581
- pageIndex,
1582
- pageSize
1583
- }
1584
- },
1585
- manualPagination: true,
1586
- manualSorting: true,
1587
- manualFiltering: true,
1588
- pageCount: hasNextPageRef.current ? pageIndex + 2 : pageIndex + 1,
1589
- getCoreRowModel: getCoreRowModel(),
1590
- getRowId,
1591
- enableRowSelection: !!enableRowSelection,
1592
- onSortingChange: (updater) => {
1593
- const next = typeof updater === "function" ? updater(sorting) : updater;
1594
- store.setSorting(next);
1595
- },
1596
- onRowSelectionChange: (updater) => {
1597
- const next = typeof updater === "function" ? updater(rowSelection) : updater;
1598
- store.setRowSelection(next);
1599
- },
1600
- onPaginationChange: (updater) => {
1601
- const next = typeof updater === "function" ? updater({
1602
- pageIndex,
1603
- pageSize
1604
- }) : updater;
1605
- store.setPagination(next.pageIndex, next.pageSize);
1606
- }
1607
- });
1608
- useEffect(() => {
1609
- if (stateAdapter) stateAdapter.write({
1610
- sorting,
1611
- filters,
1612
- search,
1613
- pageSize
1614
- });
1615
- }, [
1616
- sorting,
1617
- filters,
1618
- search,
1619
- pageSize,
1620
- stateAdapter
1621
- ]);
1622
- return {
1623
- store,
1624
- table
1625
- };
1626
- }
1627
-
1628
- //#endregion
1629
- export { DEFAULT_DEBOUNCE_MS, DEFAULT_LOADING_ROWS, DEFAULT_PAGE_SIZE, DEFAULT_PAGE_SIZES, DataTable, createDataTableStore, createSelectionColumn, useDataTableClient, useDataTableContext, useDataTableFilters, useDataTableInlineContents, useDataTableLoading, useDataTablePagination, useDataTableRows, useDataTableSearch, useDataTableSelection, useDataTableServer, useDataTableSorting, useNuqsAdapter };
1722
+ export { DEFAULT_DEBOUNCE_MS, DEFAULT_LOADING_ROWS, DEFAULT_PAGE_SIZE, DEFAULT_PAGE_SIZES, DataTable, createDataTableStore, createSelectionColumn, useDataTableFilters, useDataTableInlineContents, useDataTableLoading, useDataTablePagination, useDataTableRows, useDataTableSearch, useDataTableSelection, useDataTableSorting, useNuqsAdapter };