@datum-cloud/datum-ui 0.3.0-alpha.ffa8392 → 0.4.0

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 (193) hide show
  1. package/README.md +114 -49
  2. package/dist/app-navigation/index.mjs +12 -0
  3. package/dist/app-navigation-DsCKgfPe.mjs +416 -0
  4. package/dist/autocomplete/index.mjs +5 -5
  5. package/dist/{autocomplete-e33EmvBu.mjs → autocomplete-DRB_kSVx.mjs} +3 -3
  6. package/dist/avatar/index.mjs +4 -0
  7. package/dist/avatar-DyLq0xkt.mjs +30 -0
  8. package/dist/avatar-stack/index.mjs +4 -3
  9. package/dist/{avatar-stack-Ci0cnjxv.mjs → avatar-stack-BT0dBswq.mjs} +2 -27
  10. package/dist/badge/index.mjs +1 -1
  11. package/dist/breadcrumb/index.mjs +2 -2
  12. package/dist/{breadcrumb-BGYJgom_.mjs → breadcrumb-CJNaYyk1.mjs} +1 -1
  13. package/dist/button/index.mjs +2 -2
  14. package/dist/{button-C1wRfGtT.mjs → button-0N61fmAR.mjs} +1 -1
  15. package/dist/{button-AzpnV-WB.mjs → button-D6AORsOz.mjs} +1 -1
  16. package/dist/button-group/index.mjs +3 -3
  17. package/dist/{button-group-C1IB2K5s.mjs → button-group-BDk8btAy.mjs} +2 -2
  18. package/dist/calendar/index.mjs +3 -3
  19. package/dist/{calendar-DlIHeWb0.mjs → calendar-BtfraIvX.mjs} +2 -2
  20. package/dist/{calendar-date-picker-BBAg78Lg.mjs → calendar-date-picker-B9mxJM7f.mjs} +6 -5
  21. package/dist/card/index.mjs +2 -2
  22. package/dist/{card-3Kd0VdNf.mjs → card-BiHXFt4s.mjs} +1 -1
  23. package/dist/chart/index.mjs +2 -2
  24. package/dist/{chart-BZqUKpkh.mjs → chart-CL0i-xIt.mjs} +1 -1
  25. package/dist/checkbox/index.mjs +2 -3
  26. package/dist/checkbox-CQrjygFt.mjs +34 -0
  27. package/dist/command/index.mjs +3 -3
  28. package/dist/{command-DQlO6uTL.mjs → command-DVroicgn.mjs} +2 -2
  29. package/dist/components/base/avatar/index.d.ts +2 -0
  30. package/dist/components/base/avatar/index.d.ts.map +1 -0
  31. package/dist/components/base/index.d.ts +2 -0
  32. package/dist/components/base/index.d.ts.map +1 -1
  33. package/dist/components/base/sidebar/index.d.ts +2 -0
  34. package/dist/components/base/sidebar/index.d.ts.map +1 -0
  35. package/dist/components/{features → base}/sidebar/sidebar.d.ts +1 -1
  36. package/dist/components/base/sidebar/sidebar.d.ts.map +1 -0
  37. package/dist/components/base/skeleton/index.d.ts +1 -1
  38. package/dist/components/base/skeleton/index.d.ts.map +1 -1
  39. package/dist/components/base/skeleton/skeleton.d.ts +22 -0
  40. package/dist/components/base/skeleton/skeleton.d.ts.map +1 -0
  41. package/dist/components/base/typography/typography.d.ts +2 -2
  42. package/dist/components/features/app-navigation/app-navigation.d.ts +14 -0
  43. package/dist/components/features/app-navigation/app-navigation.d.ts.map +1 -0
  44. package/dist/components/features/app-navigation/index.d.ts +4 -0
  45. package/dist/components/features/app-navigation/index.d.ts.map +1 -0
  46. package/dist/components/features/{sidebar/nav-main.d.ts → app-navigation/nav-menu.d.ts} +3 -3
  47. package/dist/components/features/app-navigation/nav-menu.d.ts.map +1 -0
  48. package/dist/components/features/calendar-date-picker/calendar-date-picker.d.ts +2 -1
  49. package/dist/components/features/calendar-date-picker/calendar-date-picker.d.ts.map +1 -1
  50. package/dist/components/features/data-table/components/active-filters.d.ts +5 -0
  51. package/dist/components/features/data-table/components/active-filters.d.ts.map +1 -0
  52. package/dist/components/features/data-table/components/column-header.d.ts.map +1 -1
  53. package/dist/components/features/data-table/components/content.d.ts.map +1 -1
  54. package/dist/components/features/data-table/components/loading.d.ts.map +1 -1
  55. package/dist/components/features/data-table/components/pagination.d.ts.map +1 -1
  56. package/dist/components/features/data-table/components/search.d.ts +1 -1
  57. package/dist/components/features/data-table/components/search.d.ts.map +1 -1
  58. package/dist/components/features/data-table/core/client-provider.d.ts +5 -7
  59. package/dist/components/features/data-table/core/client-provider.d.ts.map +1 -1
  60. package/dist/components/features/data-table/core/data-table-context.d.ts +23 -0
  61. package/dist/components/features/data-table/core/data-table-context.d.ts.map +1 -1
  62. package/dist/components/features/data-table/core/filter-engine.d.ts +5 -0
  63. package/dist/components/features/data-table/core/filter-engine.d.ts.map +1 -1
  64. package/dist/components/features/data-table/core/server-provider.d.ts +4 -7
  65. package/dist/components/features/data-table/core/server-provider.d.ts.map +1 -1
  66. package/dist/components/features/data-table/core/store.d.ts.map +1 -1
  67. package/dist/components/features/data-table/data-table.d.ts +1 -0
  68. package/dist/components/features/data-table/data-table.d.ts.map +1 -1
  69. package/dist/components/features/data-table/filters/checkbox-filter.d.ts +1 -1
  70. package/dist/components/features/data-table/filters/checkbox-filter.d.ts.map +1 -1
  71. package/dist/components/features/data-table/filters/date-picker-filter.d.ts +1 -1
  72. package/dist/components/features/data-table/filters/date-picker-filter.d.ts.map +1 -1
  73. package/dist/components/features/data-table/filters/select-filter.d.ts +1 -1
  74. package/dist/components/features/data-table/filters/select-filter.d.ts.map +1 -1
  75. package/dist/components/features/data-table/hooks/index.d.ts +1 -1
  76. package/dist/components/features/data-table/hooks/index.d.ts.map +1 -1
  77. package/dist/components/features/data-table/hooks/use-data-table-client.d.ts +11 -10
  78. package/dist/components/features/data-table/hooks/use-data-table-client.d.ts.map +1 -1
  79. package/dist/components/features/data-table/hooks/use-data-table-server.d.ts +12 -17
  80. package/dist/components/features/data-table/hooks/use-data-table-server.d.ts.map +1 -1
  81. package/dist/components/features/data-table/hooks/use-selectors.d.ts +4 -33
  82. package/dist/components/features/data-table/hooks/use-selectors.d.ts.map +1 -1
  83. package/dist/components/features/data-table/index.d.ts +2 -4
  84. package/dist/components/features/data-table/index.d.ts.map +1 -1
  85. package/dist/components/features/data-table/types.d.ts +45 -31
  86. package/dist/components/features/data-table/types.d.ts.map +1 -1
  87. package/dist/components/features/index.d.ts +1 -1
  88. package/dist/components/features/index.d.ts.map +1 -1
  89. package/dist/data-table/index.mjs +659 -414
  90. package/dist/date-picker/index.mjs +8 -8
  91. package/dist/dialog/index.mjs +3 -3
  92. package/dist/{dialog-B2EZJW-q.mjs → dialog-B0B3Kbfk.mjs} +1 -1
  93. package/dist/{dialog-Bm4trnic.mjs → dialog-DdrHeboM.mjs} +1 -1
  94. package/dist/dropdown/index.mjs +1 -1
  95. package/dist/{dropdown-DLZXinlT.mjs → dropdown-Cdx7rOKv.mjs} +1 -1
  96. package/dist/{dropdown-menu-Xahj42Gr.mjs → dropdown-menu-CdShrDz_.mjs} +1 -1
  97. package/dist/dropzone/index.mjs +3 -3
  98. package/dist/{dropzone-CGyjGnER.mjs → dropzone-B6kSN3DY.mjs} +3 -3
  99. package/dist/empty-content/index.mjs +1 -1
  100. package/dist/{empty-content-ByvwjHUs.mjs → empty-content-B1lwLr40.mjs} +1 -1
  101. package/dist/form/index.mjs +14 -14
  102. package/dist/grid/index.mjs +1 -1
  103. package/dist/hooks/index.mjs +2 -2
  104. package/dist/hover-card/index.mjs +2 -2
  105. package/dist/{hover-card-BNrHtWy6.mjs → hover-card-CEIauuie.mjs} +1 -1
  106. package/dist/icons/index.mjs +2 -2
  107. package/dist/index.mjs +65 -64
  108. package/dist/input/index.mjs +3 -3
  109. package/dist/{input-D241oNEm.mjs → input-CYFN0Ap2.mjs} +1 -1
  110. package/dist/{input-C-ZmsHkk.mjs → input-DEMoi_8F.mjs} +1 -1
  111. package/dist/input-group/index.mjs +5 -5
  112. package/dist/{input-group-uobp64zr.mjs → input-group-DJgYpOlq.mjs} +4 -4
  113. package/dist/input-number/index.mjs +4 -4
  114. package/dist/{input-number-CEMgBk8-.mjs → input-number-Cuy9CCg_.mjs} +3 -3
  115. package/dist/input-with-addons/index.mjs +1 -1
  116. package/dist/label/index.mjs +2 -3
  117. package/dist/{label-byipFGok.mjs → label-mOg07fuQ.mjs} +12 -1
  118. package/dist/{link-button-TIF2Zdrk.mjs → link-button-Cby0p4LW.mjs} +1 -1
  119. package/dist/loader-overlay/index.mjs +1 -1
  120. package/dist/{loader-overlay-CbxcjyHV.mjs → loader-overlay-8IWX_1Ga.mjs} +1 -1
  121. package/dist/map/index.mjs +12 -12
  122. package/dist/{map-DupFPkJT.mjs → map-CaI1EshG.mjs} +8 -8
  123. package/dist/more-actions/index.mjs +3 -3
  124. package/dist/{more-actions-D6OyqZQS.mjs → more-actions-BO5ikUxY.mjs} +3 -3
  125. package/dist/page-title/index.mjs +1 -1
  126. package/dist/popover/index.mjs +2 -2
  127. package/dist/{popover-CYzXdp9q.mjs → popover-ugw5MpuT.mjs} +1 -1
  128. package/dist/radio-group/index.mjs +2 -2
  129. package/dist/{radio-group-WZCIDQCH.mjs → radio-group-_gMymwnb.mjs} +1 -1
  130. package/dist/select/index.mjs +2 -2
  131. package/dist/{select-BznmyqBr.mjs → select-BZOKWjlH.mjs} +2 -2
  132. package/dist/separator/index.mjs +2 -2
  133. package/dist/{separator-T2ppyD-8.mjs → separator-BzyALya2.mjs} +1 -1
  134. package/dist/sheet/index.mjs +3 -3
  135. package/dist/{sheet-Bmayi68h.mjs → sheet-BX6lae56.mjs} +1 -1
  136. package/dist/{sheet-b9V9soz8.mjs → sheet-DAcFjaGw.mjs} +1 -1
  137. package/dist/sidebar/index.mjs +9 -10
  138. package/dist/{sidebar-D2zE7rPy.mjs → sidebar-B3EV33mG.mjs} +11 -420
  139. package/dist/skeleton/index.mjs +3 -2
  140. package/dist/{skeleton-D3qW_KvG.mjs → skeleton-2vQ0vFQk.mjs} +1 -1
  141. package/dist/skeleton-BgOwIgE0.mjs +28 -0
  142. package/dist/spinner/index.mjs +2 -2
  143. package/dist/{spinner-CKTGKv5n.mjs → spinner-osyXAlhr.mjs} +1 -1
  144. package/dist/stepper/index.mjs +3 -3
  145. package/dist/{stepper-B07hPGG7.mjs → stepper-BMsn7I78.mjs} +1 -1
  146. package/dist/styles/root.css +3 -0
  147. package/dist/switch/index.mjs +2 -2
  148. package/dist/{switch-CujyyOi6.mjs → switch-C60FpEal.mjs} +1 -1
  149. package/dist/table/index.mjs +2 -2
  150. package/dist/{table-fZEvpdD-.mjs → table-Cl3UzIhI.mjs} +1 -1
  151. package/dist/tabs/index.mjs +1 -1
  152. package/dist/tag-input/index.mjs +3 -3
  153. package/dist/{tag-input-BI8IRBDH.mjs → tag-input-DR2gukhL.mjs} +3 -3
  154. package/dist/task-queue/index.mjs +5 -5
  155. package/dist/{task-queue-dropdown-D6k067_W.mjs → task-queue-dropdown-C9KHKbGh.mjs} +8 -8
  156. package/dist/textarea/index.mjs +3 -3
  157. package/dist/{textarea-BSkDKiej.mjs → textarea-CVo38n3S.mjs} +1 -1
  158. package/dist/{textarea-BZ85VFsJ.mjs → textarea-CZF5n57i.mjs} +1 -1
  159. package/dist/theme/index.mjs +1 -1
  160. package/dist/{to-api-format-CXQ7knV4.mjs → to-api-format-naIpF-NI.mjs} +7 -7
  161. package/dist/toast/index.mjs +1 -1
  162. package/dist/tooltip/index.mjs +2 -2
  163. package/dist/{tooltip-CbCWKEzu.mjs → tooltip-CuX2jQA9.mjs} +1 -1
  164. package/dist/typography/index.mjs +1 -1
  165. package/dist/{use-copy-to-clipboard-CC2hhyYI.mjs → use-copy-to-clipboard-n29wJwvW.mjs} +1 -1
  166. package/dist/{use-stepper-CU75TdjZ.mjs → use-stepper-DigoyHhX.mjs} +16 -16
  167. package/dist/visually-hidden/index.mjs +1 -1
  168. package/package.json +88 -77
  169. package/dist/checkbox-DB5_3E_l.mjs +0 -22
  170. package/dist/checkbox-DMC1Mhaw.mjs +0 -17
  171. package/dist/components/features/data-table/hooks/use-data-table-context.d.ts +0 -2
  172. package/dist/components/features/data-table/hooks/use-data-table-context.d.ts.map +0 -1
  173. package/dist/components/features/sidebar/app-sidebar.d.ts +0 -14
  174. package/dist/components/features/sidebar/app-sidebar.d.ts.map +0 -1
  175. package/dist/components/features/sidebar/index.d.ts +0 -4
  176. package/dist/components/features/sidebar/index.d.ts.map +0 -1
  177. package/dist/components/features/sidebar/nav-main.d.ts.map +0 -1
  178. package/dist/components/features/sidebar/sidebar.d.ts.map +0 -1
  179. package/dist/label-ClzLBWRT.mjs +0 -16
  180. /package/dist/{badge-bFgeYceE.mjs → badge-BgFj4Nsc.mjs} +0 -0
  181. /package/dist/{col-Cg_2sTDA.mjs → col-C9PDhvm5.mjs} +0 -0
  182. /package/dist/{icon-wrapper-9ticVbRL.mjs → icon-wrapper-BBK4z4tj.mjs} +0 -0
  183. /package/dist/{input-with-addons-DzuyGa6G.mjs → input-with-addons-B8rzNhpq.mjs} +0 -0
  184. /package/dist/{map-leaflet-imports-CgEyVRnp.mjs → map-leaflet-imports-J7w1V7mh.mjs} +0 -0
  185. /package/dist/{page-title-CrYQ091u.mjs → page-title-DWteBy1E.mjs} +0 -0
  186. /package/dist/{spinner.icon-Bg8zgGh0.mjs → spinner.icon-C0MbtgqX.mjs} +0 -0
  187. /package/dist/{tabs-B7cW59gB.mjs → tabs-DJU7JA3h.mjs} +0 -0
  188. /package/dist/{theme.provider-BG3cS9xe.mjs → theme.provider-TUHlMsjM.mjs} +0 -0
  189. /package/dist/{typography-DdrxIJMd.mjs → typography-Iap9fU5P.mjs} +0 -0
  190. /package/dist/{use-debounce-Dc95PFRX.mjs → use-debounce-MnfjH51L.mjs} +0 -0
  191. /package/dist/{use-toast-BLBGnOC3.mjs → use-toast-By9HuFwP.mjs} +0 -0
  192. /package/dist/{utils-Bfgoe-Gm.mjs → utils-DJboNGQM.mjs} +0 -0
  193. /package/dist/{visuallyhidden-CfBnXfvh.mjs → visuallyhidden-BJsQCmg-.mjs} +0 -0
@@ -1,20 +1,24 @@
1
- import { t as cn } from "../utils-Bfgoe-Gm.mjs";
2
- import { t as Button } from "../button-AzpnV-WB.mjs";
3
- import { t as Checkbox } from "../checkbox-DB5_3E_l.mjs";
4
- import "../dialog-Bm4trnic.mjs";
5
- import { a as CommandInput, i as CommandGroup, o as CommandItem, r as CommandEmpty, s as CommandList, t as Command } from "../command-DQlO6uTL.mjs";
6
- import { t as Input } from "../input-C-ZmsHkk.mjs";
7
- import { t as Label } from "../label-ClzLBWRT.mjs";
8
- import { i as DropdownMenuItem, l as DropdownMenuTrigger, r as DropdownMenuContent, t as DropdownMenu } from "../dropdown-menu-Xahj42Gr.mjs";
9
- import { i as PopoverTrigger, r as PopoverContent, t as Popover } from "../popover-CYzXdp9q.mjs";
10
- import { d as Select, f as SelectContent, h as SelectValue, m as SelectTrigger, p as SelectItem } from "../select-BznmyqBr.mjs";
11
- import { t as Skeleton } from "../skeleton-D3qW_KvG.mjs";
12
- import { c as TableRow, i as TableCell, n as TableBody, o as TableHead, s as TableHeader, t as Table } from "../table-fZEvpdD-.mjs";
13
- import { t as CalendarDatePicker } from "../calendar-date-picker-BBAg78Lg.mjs";
14
- import { cva } from "class-variance-authority";
1
+ import { t as cn } from "../cn-DWCc1QRE.mjs";
2
+ import "../utils-DJboNGQM.mjs";
3
+ import { t as Badge } from "../badge-BgFj4Nsc.mjs";
4
+ import { t as Button } from "../button-0N61fmAR.mjs";
5
+ import "../button-D6AORsOz.mjs";
6
+ import { t as Checkbox } from "../checkbox-CQrjygFt.mjs";
7
+ import "../dialog-DdrHeboM.mjs";
8
+ import { a as CommandInput, i as CommandGroup, o as CommandItem, r as CommandEmpty, s as CommandList, t as Command } from "../command-DVroicgn.mjs";
9
+ import "../input-DEMoi_8F.mjs";
10
+ import { t as Input } from "../input-CYFN0Ap2.mjs";
11
+ import { t as Label } from "../label-mOg07fuQ.mjs";
12
+ import { i as DropdownMenuItem, l as DropdownMenuTrigger, r as DropdownMenuContent, t as DropdownMenu } from "../dropdown-menu-CdShrDz_.mjs";
13
+ import { i as PopoverTrigger, r as PopoverContent, t as Popover } from "../popover-ugw5MpuT.mjs";
14
+ import { i as SelectItem, l as SelectTrigger, n as SelectContent, t as Select, u as SelectValue } from "../select-BZOKWjlH.mjs";
15
+ import "../skeleton-2vQ0vFQk.mjs";
16
+ import { t as Skeleton } from "../skeleton-BgOwIgE0.mjs";
17
+ import { c as TableRow, i as TableCell, n as TableBody, o as TableHead, s as TableHeader, t as Table } from "../table-Cl3UzIhI.mjs";
18
+ import { t as CalendarDatePicker } from "../calendar-date-picker-B9mxJM7f.mjs";
15
19
  import { ArrowDown, ArrowUp, ArrowUpDown, Check, ChevronDown, ChevronLeft, ChevronRight, MoreHorizontal, X } from "lucide-react";
16
- import { createContext, use, useCallback, useEffect, useId, useMemo, useRef, useState, useSyncExternalStore } from "react";
17
- import { jsx, jsxs } from "react/jsx-runtime";
20
+ import { createContext, memo, use, useCallback, useContext, useEffect, useId, useMemo, useRef, useState, useSyncExternalStore } from "react";
21
+ import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
18
22
  import { parseAsInteger, parseAsString, useQueryStates } from "nuqs";
19
23
  import { flexRender, getCoreRowModel, getPaginationRowModel, getSortedRowModel, useReactTable } from "@tanstack/react-table";
20
24
 
@@ -154,6 +158,16 @@ function withSelectionColumn(columns, options = {}) {
154
158
 
155
159
  //#endregion
156
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
+ }
157
171
  const FILTER_STRATEGIES = {
158
172
  "checkbox": (cellValue, filterValue) => {
159
173
  if (filterValue == null) return true;
@@ -197,14 +211,13 @@ function applyFilters(data, filters, search, registeredFilters, customFilterFns,
197
211
  console.warn(`[DataTable] No filter strategy registered for column "${column}". Filter ignored.`);
198
212
  continue;
199
213
  }
200
- const cellValue = row[column];
201
- if (!fn(cellValue, value)) return false;
214
+ if (!fn(resolvePath(row, column), value)) return false;
202
215
  }
203
216
  if (hasSearch) {
204
217
  const query = search.toLowerCase();
205
218
  if (searchConfig.searchFn) return searchConfig.searchFn(row, search);
206
219
  if (searchConfig.searchableColumns && searchConfig.searchableColumns.length > 0) return searchConfig.searchableColumns.some((col) => {
207
- const cellValue = row[col];
220
+ const cellValue = resolvePath(row, col);
208
221
  return cellValue != null && String(cellValue).toLowerCase().includes(query);
209
222
  });
210
223
  return Object.values(row).some((val) => {
@@ -219,7 +232,7 @@ function applyFilters(data, filters, search, registeredFilters, customFilterFns,
219
232
  //#endregion
220
233
  //#region src/components/features/data-table/core/store.ts
221
234
  function createDataTableStore(options) {
222
- const registeredFilters = /* @__PURE__ */ new Map();
235
+ let registeredFilters = /* @__PURE__ */ new Map();
223
236
  const listeners = /* @__PURE__ */ new Set();
224
237
  function computeFilteredData(s) {
225
238
  if (s.mode === "server") return s.data;
@@ -237,10 +250,12 @@ function createDataTableStore(options) {
237
250
  rowSelection: {},
238
251
  pageIndex: 0,
239
252
  pageSize: options.pageSize ?? DEFAULT_PAGE_SIZE,
253
+ columnCount: options.columnCount ?? 0,
240
254
  mode: options.mode,
241
- isLoading: false,
255
+ isLoading: options.isLoading ?? false,
242
256
  error: null,
243
- inlineContents: []
257
+ inlineContents: [],
258
+ _version: 0
244
259
  };
245
260
  if (options.defaultFilters && Object.keys(options.defaultFilters).length > 0) state = {
246
261
  ...state,
@@ -250,7 +265,10 @@ function createDataTableStore(options) {
250
265
  for (const listener of listeners) listener();
251
266
  }
252
267
  function setState(next) {
253
- state = next;
268
+ state = {
269
+ ...next,
270
+ _version: state._version + 1
271
+ };
254
272
  notify();
255
273
  }
256
274
  return {
@@ -278,7 +296,6 @@ function createDataTableStore(options) {
278
296
  filteredData: data
279
297
  });
280
298
  },
281
- setTable: (_t) => {},
282
299
  setSorting: (sorting) => {
283
300
  setState({
284
301
  ...state,
@@ -357,20 +374,32 @@ function createDataTableStore(options) {
357
374
  });
358
375
  },
359
376
  setPageIndex: (pageIndex) => {
377
+ if (!Number.isFinite(pageIndex) || pageIndex < 0) return;
360
378
  setState({
361
379
  ...state,
362
- pageIndex,
380
+ pageIndex: Math.floor(pageIndex),
363
381
  rowSelection: {}
364
382
  });
365
383
  },
366
384
  setPageSize: (pageSize) => {
385
+ if (!Number.isFinite(pageSize) || pageSize < 1) return;
367
386
  setState({
368
387
  ...state,
369
- pageSize,
388
+ pageSize: Math.floor(pageSize),
370
389
  pageIndex: 0,
371
390
  rowSelection: {}
372
391
  });
373
392
  },
393
+ setPagination: (pageIndex, pageSize) => {
394
+ const safeIndex = Number.isFinite(pageIndex) ? Math.max(0, Math.floor(pageIndex)) : state.pageIndex;
395
+ const safeSize = Number.isFinite(pageSize) ? Math.max(1, Math.floor(pageSize)) : state.pageSize;
396
+ setState({
397
+ ...state,
398
+ pageIndex: safeIndex,
399
+ pageSize: safeSize,
400
+ rowSelection: {}
401
+ });
402
+ },
374
403
  setLoading: (isLoading) => {
375
404
  setState({
376
405
  ...state,
@@ -384,7 +413,9 @@ function createDataTableStore(options) {
384
413
  });
385
414
  },
386
415
  registerFilter: (column, strategy) => {
387
- registeredFilters.set(column, strategy);
416
+ const next = new Map(registeredFilters);
417
+ next.set(column, strategy);
418
+ registeredFilters = next;
388
419
  const filteredData = computeFilteredData(state);
389
420
  setState({
390
421
  ...state,
@@ -392,7 +423,9 @@ function createDataTableStore(options) {
392
423
  });
393
424
  },
394
425
  unregisterFilter: (column) => {
395
- registeredFilters.delete(column);
426
+ const next = new Map(registeredFilters);
427
+ next.delete(column);
428
+ registeredFilters = next;
396
429
  if (column in state.filters) {
397
430
  const filteredData = computeFilteredData(state);
398
431
  setState({
@@ -423,16 +456,34 @@ function createDataTableStore(options) {
423
456
  //#region src/components/features/data-table/core/data-table-context.tsx
424
457
  const DataTableStoreContext = createContext(null);
425
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
+ }
426
475
  const DataTableContext = createContext(null);
427
476
  function useDataTableStore() {
428
477
  const store = use(DataTableStoreContext);
429
478
  if (!store) throw new Error("useDataTableStore must be used within a <DataTable.Client> or <DataTable.Server> provider");
430
479
  return store;
431
480
  }
432
- function useTableInstance() {
433
- const table = use(TableInstanceContext);
434
- if (!table) throw new Error("useTableInstance must be used within a <DataTable.Client> or <DataTable.Server> provider");
435
- 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);
436
487
  }
437
488
 
438
489
  //#endregion
@@ -463,7 +514,7 @@ function useSliceSelector(selector) {
463
514
  cachedRef.current = next;
464
515
  return next;
465
516
  }, [store, selector]);
466
- return useSyncExternalStore(store.subscribe, getSnapshot);
517
+ return useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);
467
518
  }
468
519
  function useDataTableFilters() {
469
520
  const store = useDataTableStore();
@@ -492,49 +543,64 @@ function useDataTableSorting() {
492
543
  }), [store]));
493
544
  }
494
545
  function useDataTableSelection() {
546
+ useRenderKey();
495
547
  const store = useDataTableStore();
496
- const table = useTableInstance();
497
- return useSliceSelector(useCallback((state) => ({
498
- rowSelection: state.rowSelection,
548
+ const table = useTableInstanceOrNull();
549
+ return {
550
+ rowSelection: store.getSnapshot().rowSelection,
499
551
  setRowSelection: store.setRowSelection,
500
- selectedRows: table.getFilteredSelectedRowModel().rows.map((r) => r.original)
501
- }), [store, table]));
552
+ selectedRows: table ? table.getFilteredSelectedRowModel().rows.map((r) => r.original) : []
553
+ };
502
554
  }
503
555
  function useDataTablePagination() {
556
+ useRenderKey();
504
557
  const store = useDataTableStore();
505
- const table = useTableInstance();
506
- const nextPage = useCallback(() => table.nextPage(), [table]);
507
- const prevPage = useCallback(() => table.previousPage(), [table]);
508
- 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 {
509
573
  canNextPage: table.getCanNextPage(),
510
574
  canPrevPage: table.getCanPreviousPage(),
511
- nextPage,
512
- prevPage,
575
+ nextPage: () => table.nextPage(),
576
+ prevPage: () => table.previousPage(),
513
577
  pageIndex: state.pageIndex,
514
578
  pageCount: table.getPageCount(),
515
579
  setPageIndex: store.setPageIndex,
516
580
  pageSize: state.pageSize,
517
581
  setPageSize: store.setPageSize,
518
582
  totalRows: state.filteredData.length
519
- }), [
520
- store,
521
- table,
522
- nextPage,
523
- prevPage
524
- ]));
583
+ };
525
584
  }
526
585
  function useDataTableRows() {
527
- const table = useTableInstance();
528
- return useSliceSelector(useCallback((_state) => ({
586
+ useRenderKey();
587
+ const table = useTableInstanceOrNull();
588
+ if (!table) return {
589
+ rows: [],
590
+ headerGroups: [],
591
+ totalColumns: 0
592
+ };
593
+ return {
529
594
  rows: table.getRowModel().rows,
530
595
  headerGroups: table.getHeaderGroups(),
531
596
  totalColumns: table.getAllColumns().length
532
- }), [table]));
597
+ };
533
598
  }
534
599
  function useDataTableLoading() {
535
600
  return useSliceSelector(useCallback((state) => ({
536
601
  isLoading: state.isLoading,
537
- error: state.error
602
+ error: state.error,
603
+ columnCount: state.columnCount
538
604
  }), []));
539
605
  }
540
606
  function useDataTableInlineContents() {
@@ -545,41 +611,153 @@ function useDataTableInlineContents() {
545
611
  unregisterInlineContent: store.unregisterInlineContent
546
612
  }), [store]));
547
613
  }
548
- function useDataTableContext() {
549
- const store = useDataTableStore();
550
- const table = useTableInstance();
551
- const state = useSyncExternalStore(store.subscribe, store.getSnapshot);
552
- return {
553
- table,
554
- mode: state.mode,
555
- sorting: state.sorting,
556
- filters: state.filters,
557
- search: state.search,
558
- rowSelection: state.rowSelection,
559
- isLoading: state.isLoading,
560
- setSorting: store.setSorting,
561
- setFilter: store.setFilter,
562
- clearFilter: store.clearFilter,
563
- clearAllFilters: store.clearAllFilters,
564
- setSearch: store.setSearch,
565
- clearSearch: store.clearSearch,
566
- setRowSelection: store.setRowSelection,
567
- pagination: {
568
- canNextPage: table.getCanNextPage(),
569
- canPrevPage: table.getCanPreviousPage(),
570
- nextPage: () => table.nextPage(),
571
- prevPage: () => table.previousPage(),
572
- pageIndex: state.pageIndex,
573
- pageCount: table.getPageCount(),
574
- setPageIndex: store.setPageIndex,
575
- pageSize: state.pageSize,
576
- setPageSize: store.setPageSize
577
- },
578
- inlineContents: state.inlineContents,
579
- registerInlineContent: store.registerInlineContent,
580
- unregisterInlineContent: store.unregisterInlineContent
614
+
615
+ //#endregion
616
+ //#region src/components/features/data-table/components/active-filters.tsx
617
+ function formatValue(column, value, formatters) {
618
+ const fn = formatters?.[column];
619
+ if (fn) return fn(value);
620
+ return String(value);
621
+ }
622
+ function FilterGroup({ label, children, className }) {
623
+ return /* @__PURE__ */ jsxs("div", {
624
+ className: cn("flex items-center gap-2 rounded-md border px-2 py-1", className),
625
+ "data-slot": "dt-filter-group",
626
+ "data-testid": "dt-filter-group",
627
+ children: [/* @__PURE__ */ jsx("span", {
628
+ className: "text-muted-foreground border-r pr-2 text-xs",
629
+ children: label
630
+ }), children]
631
+ });
632
+ }
633
+ const EMPTY_LABELS = {};
634
+ function ActiveFiltersInner({ label = "Selected Filters", excludeFilters, filterLabels = EMPTY_LABELS, formatFilterValue: formatters, clearAll = "icon", clearAllLabel = "Clear all", className, groupClassName, badgeClassName }) {
635
+ const { filters, setFilter, clearFilter, clearAllFilters } = useDataTableFilters();
636
+ const { search, clearSearch } = useDataTableSearch();
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");
640
+ const hasFilters = activeFilterEntries.length > 0;
641
+ if (!showSearch && !hasFilters) return null;
642
+ const totalGroups = activeFilterEntries.length + (showSearch ? 1 : 0);
643
+ const removeArrayItem = (column, items, item) => {
644
+ const remaining = items.filter((v) => v !== item);
645
+ if (remaining.length > 0) setFilter(column, remaining);
646
+ else clearFilter(column);
581
647
  };
648
+ const handleClearAll = () => {
649
+ clearAllFilters();
650
+ if (search.length > 0) clearSearch();
651
+ };
652
+ const badgeCn = cn("flex items-center gap-1.5 px-2 py-0.5 text-xs", badgeClassName);
653
+ return /* @__PURE__ */ jsxs("div", {
654
+ className: cn("flex flex-wrap items-center gap-2", className),
655
+ "data-slot": "dt-active-filters",
656
+ "data-testid": "dt-active-filters",
657
+ children: [
658
+ label !== null && /* @__PURE__ */ jsx("span", {
659
+ className: "text-sm text-muted-foreground",
660
+ "data-slot": "dt-active-filters-label",
661
+ children: label
662
+ }),
663
+ showSearch && /* @__PURE__ */ jsx(FilterGroup, {
664
+ label: "Search",
665
+ className: groupClassName,
666
+ children: /* @__PURE__ */ jsxs(Badge, {
667
+ type: "muted",
668
+ theme: "solid",
669
+ className: badgeCn,
670
+ children: [/* @__PURE__ */ jsx("span", { children: search }), /* @__PURE__ */ jsx(Button, {
671
+ theme: "borderless",
672
+ size: "small",
673
+ "aria-label": "Clear search",
674
+ className: "h-auto p-0 text-muted-foreground hover:text-foreground",
675
+ onClick: clearSearch,
676
+ children: /* @__PURE__ */ jsx(X, {
677
+ className: "size-2.5",
678
+ "aria-hidden": "true"
679
+ })
680
+ })]
681
+ })
682
+ }),
683
+ activeFilterEntries.map(([column, value]) => {
684
+ const groupLabel = filterLabels[column] ?? column;
685
+ if (Array.isArray(value)) return /* @__PURE__ */ jsx(FilterGroup, {
686
+ label: groupLabel,
687
+ className: groupClassName,
688
+ children: value.map((item) => /* @__PURE__ */ jsxs(Badge, {
689
+ type: "muted",
690
+ theme: "solid",
691
+ className: badgeCn,
692
+ children: [/* @__PURE__ */ jsx("span", { children: formatValue(column, item, formatters) }), /* @__PURE__ */ jsx(Button, {
693
+ theme: "borderless",
694
+ size: "small",
695
+ "aria-label": `Remove ${formatValue(column, item, formatters)} from ${groupLabel}`,
696
+ className: "h-auto p-0 text-muted-foreground hover:text-foreground",
697
+ onClick: () => removeArrayItem(column, value, item),
698
+ children: /* @__PURE__ */ jsx(X, {
699
+ className: "size-2.5",
700
+ "aria-hidden": "true"
701
+ })
702
+ })]
703
+ }, item))
704
+ }, column);
705
+ return /* @__PURE__ */ jsx(FilterGroup, {
706
+ label: groupLabel,
707
+ className: groupClassName,
708
+ children: /* @__PURE__ */ jsxs(Badge, {
709
+ type: "muted",
710
+ theme: "solid",
711
+ className: badgeCn,
712
+ children: [/* @__PURE__ */ jsx("span", { children: formatValue(column, value, formatters) }), /* @__PURE__ */ jsx(Button, {
713
+ theme: "borderless",
714
+ size: "small",
715
+ "aria-label": `Clear ${groupLabel} filter`,
716
+ className: "h-auto p-0 text-muted-foreground hover:text-foreground",
717
+ onClick: () => clearFilter(column),
718
+ children: /* @__PURE__ */ jsx(X, {
719
+ className: "size-2.5",
720
+ "aria-hidden": "true"
721
+ })
722
+ })]
723
+ })
724
+ }, column);
725
+ }),
726
+ totalGroups > 1 && /* @__PURE__ */ jsxs(Fragment$1, { children: [
727
+ clearAll === "icon" && /* @__PURE__ */ jsx(Button, {
728
+ theme: "borderless",
729
+ size: "small",
730
+ "aria-label": clearAllLabel,
731
+ title: clearAllLabel,
732
+ className: "h-auto p-1 text-muted-foreground hover:text-foreground",
733
+ "data-slot": "dt-clear-all-filters",
734
+ "data-testid": "dt-clear-all-filters",
735
+ onClick: handleClearAll,
736
+ children: /* @__PURE__ */ jsx(X, { className: "size-4" })
737
+ }),
738
+ clearAll === "button" && /* @__PURE__ */ jsxs(Button, {
739
+ theme: "outline",
740
+ size: "small",
741
+ className: "h-auto px-2 py-1 text-xs",
742
+ "data-slot": "dt-clear-all-filters",
743
+ "data-testid": "dt-clear-all-filters",
744
+ onClick: handleClearAll,
745
+ children: [/* @__PURE__ */ jsx(X, { className: "size-3 mr-1" }), clearAllLabel]
746
+ }),
747
+ clearAll === "text" && /* @__PURE__ */ jsx(Button, {
748
+ theme: "borderless",
749
+ size: "small",
750
+ className: "h-auto px-1 py-0.5 text-xs text-muted-foreground hover:text-foreground",
751
+ "data-slot": "dt-clear-all-filters",
752
+ "data-testid": "dt-clear-all-filters",
753
+ onClick: handleClearAll,
754
+ children: clearAllLabel
755
+ })
756
+ ] })
757
+ ]
758
+ });
582
759
  }
760
+ const DataTableActiveFilters = memo(ActiveFiltersInner);
583
761
 
584
762
  //#endregion
585
763
  //#region src/components/features/data-table/components/bulk-actions.tsx
@@ -609,6 +787,7 @@ function DataTableColumnHeader({ column, title, className }) {
609
787
  type: "button",
610
788
  className: "flex items-center gap-1 hover:text-foreground -ml-3 h-8 px-3 cursor-pointer",
611
789
  onClick: column.getToggleSortingHandler(),
790
+ "aria-label": `Sort by ${title}${sorted === "asc" ? ", sorted ascending" : sorted === "desc" ? ", sorted descending" : ""}`,
612
791
  children: [/* @__PURE__ */ jsx("span", { children: title }), sorted === "desc" ? /* @__PURE__ */ jsx(ArrowDown, { className: "size-4" }) : sorted === "asc" ? /* @__PURE__ */ jsx(ArrowUp, { className: "size-4" }) : /* @__PURE__ */ jsx(ArrowUpDown, { className: "size-4" })]
613
792
  })
614
793
  });
@@ -636,9 +815,12 @@ function renderInlineContentRow(entry, colSpan, rows) {
636
815
  }
637
816
  function DataTableContent({ emptyMessage, className, tableClassName, headerClassName, headerRowClassName, headerCellClassName, bodyClassName, rowClassName, cellClassName }) {
638
817
  const { rows, headerGroups, totalColumns } = useDataTableRows();
818
+ const { isLoading, columnCount } = useDataTableLoading();
819
+ const { pageSize } = useDataTablePagination();
639
820
  const { inlineContents } = useDataTableInlineContents();
640
821
  const openInlineContents = useMemo(() => inlineContents.filter((e) => e.open), [inlineContents]);
641
822
  const colSpan = totalColumns;
823
+ const skeletonColumns = totalColumns || columnCount || DEFAULT_LOADING_ROWS;
642
824
  return /* @__PURE__ */ jsx("div", {
643
825
  className: cn("datum-ui-data-table", className),
644
826
  "data-slot": "dt",
@@ -675,7 +857,13 @@ function DataTableContent({ emptyMessage, className, tableClassName, headerClass
675
857
  children: flexRender(cell.column.columnDef.cell, cell.getContext())
676
858
  }, cell.id))
677
859
  }, row.id);
678
- }) : /* @__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, {
679
867
  "data-slot": "dt-row",
680
868
  children: /* @__PURE__ */ jsx(TableCell, {
681
869
  colSpan,
@@ -746,19 +934,8 @@ function DataTableLoading({ rows = DEFAULT_LOADING_ROWS, columns = 4, className
746
934
  return /* @__PURE__ */ jsx("div", {
747
935
  className,
748
936
  "data-slot": "dt-loading",
749
- children: /* @__PURE__ */ jsxs("div", {
750
- className: "rounded-md border",
751
- children: [/* @__PURE__ */ jsx("div", {
752
- className: "border-b",
753
- children: /* @__PURE__ */ jsx("div", {
754
- className: "flex gap-4 p-4",
755
- children: Array.from({ length: columns }, (_, i) => /* @__PURE__ */ jsx(Skeleton, { className: "h-4 flex-1" }, i))
756
- })
757
- }), Array.from({ length: rows }, (_, rowIndex) => /* @__PURE__ */ jsx("div", {
758
- className: "flex gap-4 border-b p-4 last:border-b-0",
759
- children: Array.from({ length: columns }, (_, colIndex) => /* @__PURE__ */ jsx(Skeleton, { className: "h-4 flex-1" }, colIndex))
760
- }, rowIndex))]
761
- })
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)) })] })
762
939
  });
763
940
  }
764
941
 
@@ -835,15 +1012,17 @@ function DataTablePagination({ pageSizes = DEFAULT_PAGE_SIZES, className }) {
835
1012
  })]
836
1013
  })]
837
1014
  })]
838
- }), /* @__PURE__ */ jsxs("div", {
1015
+ }), /* @__PURE__ */ jsxs("nav", {
1016
+ "aria-label": "Table pagination",
839
1017
  className: "flex items-center gap-1",
840
1018
  children: [
841
1019
  /* @__PURE__ */ jsx(Button, {
842
- variant: "outline",
1020
+ theme: "outline",
843
1021
  size: "icon",
844
1022
  className: "size-8",
845
1023
  onClick: prevPage,
846
1024
  disabled: !canPrevPage,
1025
+ "aria-label": "Previous page",
847
1026
  children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-4" })
848
1027
  }),
849
1028
  isClientMode && pageCount > 1 ? pageNumbers.map((page, index) => {
@@ -853,11 +1032,13 @@ function DataTablePagination({ pageSizes = DEFAULT_PAGE_SIZES, className }) {
853
1032
  }, `ellipsis-${index}`);
854
1033
  const isActive = page === pageIndex + 1;
855
1034
  return /* @__PURE__ */ jsx(Button, {
856
- variant: isActive ? "default" : "outline",
857
- size: "sm",
858
- className: cn("h-8 min-w-8 px-2", isActive && "font-semibold"),
1035
+ theme: isActive ? "solid" : "outline",
1036
+ size: "small",
1037
+ className: cn("h-8 min-w-8 px-2", isActive && "pointer-events-none font-semibold"),
859
1038
  onClick: () => setPageIndex(page - 1),
860
- disabled: isActive,
1039
+ "aria-disabled": isActive || void 0,
1040
+ "aria-label": `Page ${page}`,
1041
+ "aria-current": isActive ? "page" : void 0,
861
1042
  children: page
862
1043
  }, page);
863
1044
  }) : !isClientMode && /* @__PURE__ */ jsxs("span", {
@@ -869,11 +1050,12 @@ function DataTablePagination({ pageSizes = DEFAULT_PAGE_SIZES, className }) {
869
1050
  ]
870
1051
  }),
871
1052
  /* @__PURE__ */ jsx(Button, {
872
- variant: "outline",
1053
+ theme: "outline",
873
1054
  size: "icon",
874
1055
  className: "size-8",
875
1056
  onClick: nextPage,
876
1057
  disabled: !canNextPage,
1058
+ "aria-label": "Next page",
877
1059
  children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-4" })
878
1060
  })
879
1061
  ]
@@ -893,8 +1075,8 @@ function DataTableRowActions({ row, actions, isLoading = false, className }) {
893
1075
  return /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
894
1076
  asChild: true,
895
1077
  children: /* @__PURE__ */ jsxs(Button, {
896
- variant: "ghost",
897
- size: "sm",
1078
+ theme: "borderless",
1079
+ size: "small",
898
1080
  className,
899
1081
  disabled: isLoading,
900
1082
  "data-slot": "dt-row-actions",
@@ -918,7 +1100,7 @@ function DataTableRowActions({ row, actions, isLoading = false, className }) {
918
1100
 
919
1101
  //#endregion
920
1102
  //#region src/components/features/data-table/components/search.tsx
921
- function DataTableSearch({ placeholder = "Search...", debounceMs = DEFAULT_DEBOUNCE_MS, className }) {
1103
+ function DataTableSearch({ placeholder = "Search...", debounceMs = DEFAULT_DEBOUNCE_MS, className, disabled }) {
922
1104
  const { search, setSearch } = useDataTableSearch();
923
1105
  const [inputValue, setInputValue] = useState(search);
924
1106
  useEffect(() => {
@@ -940,17 +1122,105 @@ function DataTableSearch({ placeholder = "Search...", debounceMs = DEFAULT_DEBOU
940
1122
  value: inputValue,
941
1123
  onChange: (e) => setInputValue(e.target.value),
942
1124
  className,
1125
+ disabled,
1126
+ "aria-label": placeholder,
943
1127
  "data-slot": "dt-search"
944
1128
  });
945
1129
  }
946
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
+
947
1212
  //#endregion
948
1213
  //#region src/components/features/data-table/core/client-provider.tsx
949
- function ClientProvider({ store, table, className, children }) {
950
- return /* @__PURE__ */ jsx(DataTableStoreContext, {
951
- value: store,
952
- children: /* @__PURE__ */ jsx(TableInstanceContext, {
953
- 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,
954
1224
  children: /* @__PURE__ */ jsx("div", {
955
1225
  className,
956
1226
  children
@@ -958,68 +1228,277 @@ function ClientProvider({ store, table, className, children }) {
958
1228
  })
959
1229
  });
960
1230
  }
961
-
962
- //#endregion
963
- //#region src/components/features/data-table/core/server-provider.tsx
964
- 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
+ ]);
965
1263
  return /* @__PURE__ */ jsx(DataTableStoreContext, {
966
1264
  value: store,
967
- children: /* @__PURE__ */ jsx(TableInstanceContext, {
968
- value: table,
969
- children: /* @__PURE__ */ jsx("div", {
970
- className,
971
- 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
+ })
972
1281
  })
973
1282
  })
974
1283
  });
975
1284
  }
976
1285
 
977
1286
  //#endregion
978
- //#region ../shadcn/ui/badge.tsx
1287
+ //#region src/components/features/data-table/hooks/use-data-table-server.ts
979
1288
  /**
980
- * Vanilla shadcn/ui Badge Component
981
- * Pure shadcn badge without Datum customizations
982
- * For Datum-specific variants (sunglow, butter), import from @/modules/datum-ui
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.
983
1292
  */
984
- const badgeVariants = cva("inline-flex items-center rounded-full border px-2.5 py-0.5 text-xs font-semibold transition-colors focus:outline-hidden focus:ring-2 focus:ring-ring focus:ring-offset-2", {
985
- variants: { variant: {
986
- default: "border-transparent bg-primary text-primary-foreground hover:bg-primary/80",
987
- secondary: "border-transparent bg-secondary text-secondary-foreground hover:bg-secondary/80",
988
- destructive: "border-transparent bg-destructive text-destructive-foreground hover:bg-destructive/80",
989
- outline: "text-foreground"
990
- } },
991
- defaultVariants: { variant: "default" }
992
- });
993
- function Badge({ className, variant, ...props }) {
994
- return /* @__PURE__ */ jsx("div", {
995
- className: cn(badgeVariants({ variant }), className),
996
- ...props
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);
1297
+ useEffect(() => {
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
997
1311
  });
998
- }
999
-
1000
- //#endregion
1001
- //#region src/components/features/data-table/filters/checkbox-filter.tsx
1002
- const MAX_VISIBLE_BADGES = 2;
1003
- function CheckboxFilter({ column, label, options, className, checkboxPopoverClassName }) {
1004
- const { filters, setFilter, clearFilter, registerFilter, unregisterFilter } = useDataTableFilters();
1005
- const [open, setOpen] = useState(false);
1006
1312
  useEffect(() => {
1007
- registerFilter(column, "checkbox");
1008
- return () => unregisterFilter(column);
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
+ };
1009
1361
  }, [
1010
- column,
1011
- registerFilter,
1012
- unregisterFilter
1362
+ sorting,
1363
+ filters,
1364
+ search,
1365
+ pageSize,
1366
+ pageIndex,
1367
+ store
1013
1368
  ]);
1014
- const selectedValues = filters[column] ?? [];
1015
- const updateValues = (newValues) => {
1016
- if (newValues.length > 0) setFilter(column, newValues);
1017
- else clearFilter(column);
1018
- };
1019
- const handleToggle = (optionValue, checked) => {
1020
- updateValues(checked ? [...selectedValues, optionValue] : selectedValues.filter((v) => v !== optionValue));
1021
- };
1022
- const removeValue = (optionValue) => {
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) => {
1023
1502
  updateValues(selectedValues.filter((v) => v !== optionValue));
1024
1503
  };
1025
1504
  const visibleBadges = selectedValues.slice(0, MAX_VISIBLE_BADGES);
@@ -1030,8 +1509,9 @@ function CheckboxFilter({ column, label, options, className, checkboxPopoverClas
1030
1509
  children: [/* @__PURE__ */ jsx(PopoverTrigger, {
1031
1510
  asChild: true,
1032
1511
  children: /* @__PURE__ */ jsxs(Button, {
1033
- variant: "outline",
1034
- className: cn("justify-between gap-1", className),
1512
+ theme: "outline",
1513
+ disabled,
1514
+ className: cn("h-10 justify-between gap-1", className),
1035
1515
  "data-slot": "dt-filter",
1036
1516
  "data-testid": "dt-filter-trigger",
1037
1517
  children: [selectedValues.length > 0 ? /* @__PURE__ */ jsxs("div", {
@@ -1039,7 +1519,8 @@ function CheckboxFilter({ column, label, options, className, checkboxPopoverClas
1039
1519
  children: [visibleBadges.map((val) => {
1040
1520
  const opt = options.find((o) => o.value === val);
1041
1521
  return /* @__PURE__ */ jsxs(Badge, {
1042
- variant: "secondary",
1522
+ type: "secondary",
1523
+ theme: "light",
1043
1524
  className: "text-xs px-1.5 py-0",
1044
1525
  children: [opt?.label ?? val, /* @__PURE__ */ jsx("span", {
1045
1526
  role: "button",
@@ -1079,8 +1560,8 @@ function CheckboxFilter({ column, label, options, className, checkboxPopoverClas
1079
1560
  className: "text-sm font-medium",
1080
1561
  children: label
1081
1562
  }), selectedValues.length > 0 && /* @__PURE__ */ jsx(Button, {
1082
- variant: "ghost",
1083
- size: "sm",
1563
+ theme: "borderless",
1564
+ size: "small",
1084
1565
  className: "h-auto p-1 text-xs",
1085
1566
  onClick: () => clearFilter(column),
1086
1567
  children: "Clear"
@@ -1109,7 +1590,7 @@ function CheckboxFilter({ column, label, options, className, checkboxPopoverClas
1109
1590
 
1110
1591
  //#endregion
1111
1592
  //#region src/components/features/data-table/filters/date-picker-filter.tsx
1112
- function DatePickerFilter({ column, label, className, datePickerPopoverClassName, disableFuture, disablePast, minDate, maxDate }) {
1593
+ function DatePickerFilter({ column, label, className, datePickerPopoverClassName, disableFuture, disablePast, minDate, maxDate, disabled }) {
1113
1594
  const { filters, setFilter, clearFilter, registerFilter, unregisterFilter } = useDataTableFilters();
1114
1595
  const rawValue = filters[column];
1115
1596
  useEffect(() => {
@@ -1134,8 +1615,9 @@ function DatePickerFilter({ column, label, className, datePickerPopoverClassName
1134
1615
  numberOfMonths: 1,
1135
1616
  closeOnSelect: true,
1136
1617
  placeholder: label,
1137
- triggerClassName: className,
1618
+ triggerClassName: cn("h-10", className),
1138
1619
  variant: "outline",
1620
+ disabled,
1139
1621
  disableFuture,
1140
1622
  disablePast,
1141
1623
  minDate,
@@ -1151,7 +1633,7 @@ function DatePickerFilter({ column, label, className, datePickerPopoverClassName
1151
1633
 
1152
1634
  //#endregion
1153
1635
  //#region src/components/features/data-table/filters/select-filter.tsx
1154
- function SelectFilter({ column, label, options, placeholder, searchable = true, className, selectPopoverClassName }) {
1636
+ function SelectFilter({ column, label, options, placeholder, searchable = true, className, selectPopoverClassName, disabled }) {
1155
1637
  const { filters, setFilter, clearFilter, registerFilter, unregisterFilter } = useDataTableFilters();
1156
1638
  const [open, setOpen] = useState(false);
1157
1639
  const value = filters[column];
@@ -1170,10 +1652,11 @@ function SelectFilter({ column, label, options, placeholder, searchable = true,
1170
1652
  children: [/* @__PURE__ */ jsx(PopoverTrigger, {
1171
1653
  asChild: true,
1172
1654
  children: /* @__PURE__ */ jsxs(Button, {
1173
- variant: "outline",
1655
+ theme: "outline",
1174
1656
  role: "combobox",
1175
1657
  "aria-expanded": open,
1176
- className: cn("justify-between", className),
1658
+ disabled,
1659
+ className: cn("h-10 justify-between", className),
1177
1660
  "data-slot": "dt-filter",
1178
1661
  "data-testid": "dt-filter-trigger",
1179
1662
  children: [/* @__PURE__ */ jsx("span", {
@@ -1221,6 +1704,7 @@ function SelectFilter({ column, label, options, placeholder, searchable = true,
1221
1704
  const DataTable = {
1222
1705
  Client: ClientProvider,
1223
1706
  Server: ServerProvider,
1707
+ ActiveFilters: DataTableActiveFilters,
1224
1708
  Content: DataTableContent,
1225
1709
  InlineContent: DataTableInlineContent,
1226
1710
  ColumnHeader: DataTableColumnHeader,
@@ -1235,243 +1719,4 @@ const DataTable = {
1235
1719
  };
1236
1720
 
1237
1721
  //#endregion
1238
- //#region src/components/features/data-table/hooks/use-data-table-client.ts
1239
- function useDataTableClient(options) {
1240
- const { data, columns, pageSize, getRowId, enableRowSelection = false, defaultSort, defaultFilters, searchableColumns, searchFn, filterFns, stateAdapter } = options;
1241
- const store = useMemo(() => createDataTableStore({
1242
- data,
1243
- mode: "client",
1244
- defaultSort,
1245
- defaultFilters,
1246
- pageSize,
1247
- searchableColumns,
1248
- searchFn,
1249
- filterFns
1250
- }), []);
1251
- const isInitialRender = useRef(true);
1252
- useEffect(() => {
1253
- if (isInitialRender.current) {
1254
- isInitialRender.current = false;
1255
- return;
1256
- }
1257
- store.setData(data);
1258
- }, [data, store]);
1259
- const resolvedColumns = useMemo(() => enableRowSelection ? withSelectionColumn(columns, typeof enableRowSelection === "object" ? enableRowSelection : {}) : columns, [columns, enableRowSelection]);
1260
- const { filteredData, sorting, rowSelection, pageIndex, pageSize: storePageSize, filters, search } = useSyncExternalStore(store.subscribe, store.getSnapshot);
1261
- const table = useReactTable({
1262
- data: filteredData,
1263
- columns: resolvedColumns,
1264
- state: {
1265
- sorting,
1266
- rowSelection,
1267
- pagination: {
1268
- pageIndex,
1269
- pageSize: storePageSize
1270
- }
1271
- },
1272
- onSortingChange: (updater) => {
1273
- const next = typeof updater === "function" ? updater(sorting) : updater;
1274
- store.setSorting(next);
1275
- },
1276
- onRowSelectionChange: (updater) => {
1277
- const next = typeof updater === "function" ? updater(rowSelection) : updater;
1278
- store.setRowSelection(next);
1279
- },
1280
- onPaginationChange: (updater) => {
1281
- const next = typeof updater === "function" ? updater({
1282
- pageIndex,
1283
- pageSize: storePageSize
1284
- }) : updater;
1285
- store.setPageIndex(next.pageIndex);
1286
- store.setPageSize(next.pageSize);
1287
- },
1288
- getCoreRowModel: getCoreRowModel(),
1289
- getSortedRowModel: getSortedRowModel(),
1290
- getPaginationRowModel: getPaginationRowModel(),
1291
- getRowId,
1292
- enableRowSelection: !!enableRowSelection
1293
- });
1294
- const hydratedRef = useRef(false);
1295
- useEffect(() => {
1296
- if (stateAdapter && !hydratedRef.current) {
1297
- hydratedRef.current = true;
1298
- const persisted = stateAdapter.read();
1299
- if (persisted.sorting && persisted.sorting.length > 0) store.setSorting(persisted.sorting);
1300
- if (persisted.filters) {
1301
- for (const [key, value] of Object.entries(persisted.filters)) if (value != null) store.setFilter(key, value);
1302
- }
1303
- if (persisted.search) store.setSearch(persisted.search);
1304
- if (persisted.pageIndex != null && persisted.pageIndex > 0) store.setPageIndex(persisted.pageIndex);
1305
- if (persisted.pageSize != null) store.setPageSize(persisted.pageSize);
1306
- }
1307
- }, []);
1308
- const isFirstWrite = useRef(true);
1309
- useEffect(() => {
1310
- if (!stateAdapter) return;
1311
- if (isFirstWrite.current) {
1312
- isFirstWrite.current = false;
1313
- return;
1314
- }
1315
- stateAdapter.write({
1316
- sorting,
1317
- filters,
1318
- search,
1319
- pageIndex,
1320
- pageSize: storePageSize
1321
- });
1322
- }, [
1323
- sorting,
1324
- filters,
1325
- search,
1326
- pageIndex,
1327
- storePageSize,
1328
- stateAdapter
1329
- ]);
1330
- return {
1331
- store,
1332
- table
1333
- };
1334
- }
1335
-
1336
- //#endregion
1337
- //#region src/components/features/data-table/hooks/use-data-table-server.ts
1338
- function useDataTableServer(options) {
1339
- const { columns, fetchFn, transform, limit = 20, getRowId, enableRowSelection = false, defaultSort, defaultFilters, stateAdapter } = options;
1340
- const fetchRef = useRef(fetchFn);
1341
- const transformRef = useRef(transform);
1342
- useEffect(() => {
1343
- fetchRef.current = fetchFn;
1344
- }, [fetchFn]);
1345
- useEffect(() => {
1346
- transformRef.current = transform;
1347
- }, [transform]);
1348
- const cursorMapRef = useRef(/* @__PURE__ */ new Map());
1349
- const [hasNextPage, setHasNextPage] = useState(false);
1350
- const store = useMemo(() => createDataTableStore({
1351
- data: [],
1352
- mode: "server",
1353
- defaultSort,
1354
- defaultFilters,
1355
- pageSize: limit
1356
- }), []);
1357
- const { sorting, filters, search, rowSelection, pageSize, pageIndex } = useSyncExternalStore(store.subscribe, store.getSnapshot);
1358
- useEffect(() => {
1359
- let cancelled = false;
1360
- store.setLoading(true);
1361
- const cursor = cursorMapRef.current.get(pageIndex);
1362
- fetchRef.current({
1363
- sorting,
1364
- filters,
1365
- search,
1366
- cursor,
1367
- limit: pageSize
1368
- }).then((response) => {
1369
- if (cancelled) return;
1370
- const result = transformRef.current(response);
1371
- store.setServerData(result.data);
1372
- store.setError(null);
1373
- if (result.nextCursor) cursorMapRef.current.set(pageIndex + 1, result.nextCursor);
1374
- setHasNextPage(result.hasNextPage);
1375
- }).catch((error) => {
1376
- if (cancelled) return;
1377
- store.setServerData([]);
1378
- store.setError(error instanceof Error ? error : new Error(String(error)));
1379
- setHasNextPage(false);
1380
- }).finally(() => {
1381
- if (!cancelled) store.setLoading(false);
1382
- });
1383
- return () => {
1384
- cancelled = true;
1385
- };
1386
- }, [
1387
- sorting,
1388
- filters,
1389
- search,
1390
- pageSize,
1391
- pageIndex,
1392
- store
1393
- ]);
1394
- const prevQueryRef = useRef({
1395
- sorting,
1396
- filters,
1397
- search,
1398
- pageSize
1399
- });
1400
- useEffect(() => {
1401
- const prev = prevQueryRef.current;
1402
- if (prev.sorting !== sorting || prev.filters !== filters || prev.search !== search || prev.pageSize !== pageSize) {
1403
- cursorMapRef.current = /* @__PURE__ */ new Map();
1404
- store.setPageIndex(0);
1405
- setHasNextPage(false);
1406
- }
1407
- prevQueryRef.current = {
1408
- sorting,
1409
- filters,
1410
- search,
1411
- pageSize
1412
- };
1413
- }, [
1414
- sorting,
1415
- filters,
1416
- search,
1417
- pageSize,
1418
- store
1419
- ]);
1420
- const resolvedColumns = useMemo(() => enableRowSelection ? withSelectionColumn(columns, typeof enableRowSelection === "object" ? enableRowSelection : {}) : columns, [columns, enableRowSelection]);
1421
- const table = useReactTable({
1422
- data: store.getSnapshot().data,
1423
- columns: resolvedColumns,
1424
- state: {
1425
- sorting,
1426
- rowSelection,
1427
- pagination: {
1428
- pageIndex,
1429
- pageSize
1430
- }
1431
- },
1432
- manualPagination: true,
1433
- manualSorting: true,
1434
- manualFiltering: true,
1435
- pageCount: hasNextPage ? pageIndex + 2 : pageIndex + 1,
1436
- getCoreRowModel: getCoreRowModel(),
1437
- getRowId,
1438
- enableRowSelection: !!enableRowSelection,
1439
- onSortingChange: (updater) => {
1440
- const next = typeof updater === "function" ? updater(sorting) : updater;
1441
- store.setSorting(next);
1442
- },
1443
- onRowSelectionChange: (updater) => {
1444
- const next = typeof updater === "function" ? updater(rowSelection) : updater;
1445
- store.setRowSelection(next);
1446
- },
1447
- onPaginationChange: (updater) => {
1448
- const next = typeof updater === "function" ? updater({
1449
- pageIndex,
1450
- pageSize
1451
- }) : updater;
1452
- if (next.pageIndex !== pageIndex) store.setPageIndex(next.pageIndex);
1453
- if (next.pageSize !== pageSize) store.setPageSize(next.pageSize);
1454
- }
1455
- });
1456
- useEffect(() => {
1457
- if (stateAdapter) stateAdapter.write({
1458
- sorting,
1459
- filters,
1460
- search,
1461
- pageSize
1462
- });
1463
- }, [
1464
- sorting,
1465
- filters,
1466
- search,
1467
- pageSize,
1468
- stateAdapter
1469
- ]);
1470
- return {
1471
- store,
1472
- table
1473
- };
1474
- }
1475
-
1476
- //#endregion
1477
- 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 };