@datum-cloud/datum-ui 0.2.0-alpha.8 → 0.3.0-alpha.3670fb2

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 (137) hide show
  1. package/dist/autocomplete/index.mjs +2 -2
  2. package/dist/{autocomplete-DZtI97HP.mjs → autocomplete-B9bCkXtz.mjs} +2 -2
  3. package/dist/avatar-stack/index.mjs +2 -2
  4. package/dist/{avatar-stack-JCfBlPB9.mjs → avatar-stack-Bh-tLz0X.mjs} +1 -1
  5. package/dist/calendar-date-picker-mlbzp3xR.mjs +665 -0
  6. package/dist/components/features/calendar-date-picker/calendar-date-picker.d.ts +2 -1
  7. package/dist/components/features/calendar-date-picker/calendar-date-picker.d.ts.map +1 -1
  8. package/dist/components/features/data-table/adapters/nuqs-adapter.d.ts +46 -0
  9. package/dist/components/features/data-table/adapters/nuqs-adapter.d.ts.map +1 -0
  10. package/dist/components/features/data-table/columns/selection-column.d.ts +6 -0
  11. package/dist/components/features/data-table/columns/selection-column.d.ts.map +1 -0
  12. package/dist/components/features/data-table/components/active-filters.d.ts +5 -0
  13. package/dist/components/features/data-table/components/active-filters.d.ts.map +1 -0
  14. package/dist/components/features/data-table/components/bulk-actions.d.ts +3 -0
  15. package/dist/components/features/data-table/components/bulk-actions.d.ts.map +1 -0
  16. package/dist/components/features/data-table/components/column-header.d.ts +3 -0
  17. package/dist/components/features/data-table/components/column-header.d.ts.map +1 -0
  18. package/dist/components/features/data-table/components/content.d.ts +3 -0
  19. package/dist/components/features/data-table/components/content.d.ts.map +1 -0
  20. package/dist/components/features/data-table/components/inline-content.d.ts +3 -0
  21. package/dist/components/features/data-table/components/inline-content.d.ts.map +1 -0
  22. package/dist/components/features/data-table/components/loading.d.ts +3 -0
  23. package/dist/components/features/data-table/components/loading.d.ts.map +1 -0
  24. package/dist/components/features/data-table/components/pagination.d.ts +3 -0
  25. package/dist/components/features/data-table/components/pagination.d.ts.map +1 -0
  26. package/dist/components/features/data-table/components/row-actions.d.ts +3 -0
  27. package/dist/components/features/data-table/components/row-actions.d.ts.map +1 -0
  28. package/dist/components/features/data-table/components/search.d.ts +3 -0
  29. package/dist/components/features/data-table/components/search.d.ts.map +1 -0
  30. package/dist/components/features/data-table/constants.d.ts +5 -0
  31. package/dist/components/features/data-table/constants.d.ts.map +1 -0
  32. package/dist/components/features/data-table/core/client-provider.d.ts +11 -0
  33. package/dist/components/features/data-table/core/client-provider.d.ts.map +1 -0
  34. package/dist/components/features/data-table/core/data-table-context.d.ts +10 -0
  35. package/dist/components/features/data-table/core/data-table-context.d.ts.map +1 -0
  36. package/dist/components/features/data-table/core/filter-engine.d.ts +16 -0
  37. package/dist/components/features/data-table/core/filter-engine.d.ts.map +1 -0
  38. package/dist/components/features/data-table/core/server-provider.d.ts +11 -0
  39. package/dist/components/features/data-table/core/server-provider.d.ts.map +1 -0
  40. package/dist/components/features/data-table/core/store.d.ts +3 -0
  41. package/dist/components/features/data-table/core/store.d.ts.map +1 -0
  42. package/dist/components/features/data-table/data-table.d.ts +30 -0
  43. package/dist/components/features/data-table/data-table.d.ts.map +1 -0
  44. package/dist/components/features/data-table/filters/checkbox-filter.d.ts +3 -0
  45. package/dist/components/features/data-table/filters/checkbox-filter.d.ts.map +1 -0
  46. package/dist/components/features/data-table/filters/date-picker-filter.d.ts +3 -0
  47. package/dist/components/features/data-table/filters/date-picker-filter.d.ts.map +1 -0
  48. package/dist/components/features/data-table/filters/select-filter.d.ts +3 -0
  49. package/dist/components/features/data-table/filters/select-filter.d.ts.map +1 -0
  50. package/dist/components/features/data-table/hooks/index.d.ts +4 -0
  51. package/dist/components/features/data-table/hooks/index.d.ts.map +1 -0
  52. package/dist/components/features/data-table/hooks/use-data-table-client.d.ts +20 -0
  53. package/dist/components/features/data-table/hooks/use-data-table-client.d.ts.map +1 -0
  54. package/dist/components/features/data-table/hooks/use-data-table-context.d.ts +2 -0
  55. package/dist/components/features/data-table/hooks/use-data-table-context.d.ts.map +1 -0
  56. package/dist/components/features/data-table/hooks/use-data-table-server.d.ts +30 -0
  57. package/dist/components/features/data-table/hooks/use-data-table-server.d.ts.map +1 -0
  58. package/dist/components/features/data-table/hooks/use-selectors.d.ts +81 -0
  59. package/dist/components/features/data-table/hooks/use-selectors.d.ts.map +1 -0
  60. package/dist/components/features/data-table/index.d.ts +11 -0
  61. package/dist/components/features/data-table/index.d.ts.map +1 -0
  62. package/dist/components/features/data-table/types.d.ts +324 -0
  63. package/dist/components/features/data-table/types.d.ts.map +1 -0
  64. package/dist/data-table/index.mjs +1629 -0
  65. package/dist/date-picker/index.mjs +4 -3
  66. package/dist/dropdown/index.mjs +1 -1
  67. package/dist/dropdown-menu-DAFyO-qD.mjs +87 -0
  68. package/dist/dropzone/index.mjs +1 -1
  69. package/dist/empty-content/index.mjs +1 -1
  70. package/dist/form/index.mjs +6 -6
  71. package/dist/grid/index.mjs +1 -1
  72. package/dist/hooks/index.mjs +2 -2
  73. package/dist/index.mjs +38 -36
  74. package/dist/input-number/index.mjs +1 -1
  75. package/dist/input-with-addons/index.mjs +1 -1
  76. package/dist/loader-overlay/index.mjs +1 -1
  77. package/dist/map/index.mjs +3 -2
  78. package/dist/{map-WL6jhkSM.mjs → map-ClJD-qxm.mjs} +4 -84
  79. package/dist/more-actions/index.mjs +2 -2
  80. package/dist/{more-actions-Ch1f6Mh3.mjs → more-actions-DbC8dyed.mjs} +2 -2
  81. package/dist/page-title/index.mjs +1 -1
  82. package/dist/popover/index.mjs +1 -1
  83. package/dist/radio-group/index.mjs +1 -1
  84. package/dist/select/index.mjs +1 -1
  85. package/dist/sheet/index.mjs +2 -2
  86. package/dist/{sheet-BKiCwtNO.mjs → sheet-mx5XjyEY.mjs} +1 -1
  87. package/dist/sidebar/index.mjs +4 -4
  88. package/dist/{sidebar-DfqezV8t.mjs → sidebar-C4NqSr4r.mjs} +3 -3
  89. package/dist/skeleton/index.mjs +1 -1
  90. package/dist/spinner/index.mjs +1 -1
  91. package/dist/stepper/index.mjs +1 -1
  92. package/dist/styles/fonts/AllianceNo1-Medium.ttf +0 -0
  93. package/dist/styles/fonts/AllianceNo1-Regular.ttf +0 -0
  94. package/dist/styles/fonts/AllianceNo1-SemiBold.ttf +0 -0
  95. package/dist/switch/index.mjs +1 -1
  96. package/dist/table/index.mjs +1 -1
  97. package/dist/tabs/index.mjs +1 -1
  98. package/dist/tag-input/index.mjs +1 -1
  99. package/dist/task-queue/index.mjs +3 -3
  100. package/dist/{task-queue-dropdown-DW72ikDH.mjs → task-queue-dropdown-fo3TX58Q.mjs} +3 -3
  101. package/dist/textarea/index.mjs +1 -1
  102. package/dist/theme/index.mjs +1 -1
  103. package/dist/{to-api-format-C2xjQUcI.mjs → to-api-format-zI26rEBI.mjs} +6 -661
  104. package/dist/toast/index.mjs +1 -1
  105. package/dist/tooltip/index.mjs +1 -1
  106. package/dist/typography/index.mjs +1 -1
  107. package/dist/{use-copy-to-clipboard-ki-WoTml.mjs → use-copy-to-clipboard-C7xqNxBX.mjs} +1 -1
  108. package/dist/{use-stepper-BaToCYMs.mjs → use-stepper-CB1injte.mjs} +10 -10
  109. package/dist/visually-hidden/index.mjs +1 -1
  110. package/package.json +29 -17
  111. /package/dist/{col-lrLMZaTJ.mjs → col-RfO7d6AR.mjs} +0 -0
  112. /package/dist/{dropdown-DtSa_lqc.mjs → dropdown-Cs7Xr8w7.mjs} +0 -0
  113. /package/dist/{dropzone-BkOnwrS4.mjs → dropzone-BT5fEDEF.mjs} +0 -0
  114. /package/dist/{empty-content-BM9rzI13.mjs → empty-content-iDu3NUqG.mjs} +0 -0
  115. /package/dist/{input-number-9o62JHRl.mjs → input-number-D9ydFith.mjs} +0 -0
  116. /package/dist/{input-with-addons-BQn7KCTU.mjs → input-with-addons-CdgiUQce.mjs} +0 -0
  117. /package/dist/{loader-overlay-DUaQSZQP.mjs → loader-overlay-D83QeQNj.mjs} +0 -0
  118. /package/dist/{map-leaflet-imports-C4JYls8q.mjs → map-leaflet-imports-CdzvEnzY.mjs} +0 -0
  119. /package/dist/{page-title-BJuo81rT.mjs → page-title-SGchAF6Y.mjs} +0 -0
  120. /package/dist/{popover-SQlKSz6L.mjs → popover-Ds9624qY.mjs} +0 -0
  121. /package/dist/{radio-group-Oshv0b-U.mjs → radio-group-B9Hm77LQ.mjs} +0 -0
  122. /package/dist/{select-DVlEzD2W.mjs → select-CwVIFWFO.mjs} +0 -0
  123. /package/dist/{sheet-CtnP6gTD.mjs → sheet-Cemwh78x.mjs} +0 -0
  124. /package/dist/{skeleton-vzbxA-DQ.mjs → skeleton-Cs6Q5GQc.mjs} +0 -0
  125. /package/dist/{spinner-BE7k2bAD.mjs → spinner-earfjpJs.mjs} +0 -0
  126. /package/dist/{stepper-SWB-u_nM.mjs → stepper-BG9DIzN5.mjs} +0 -0
  127. /package/dist/{switch-Calk7Gyw.mjs → switch-B2VVauH6.mjs} +0 -0
  128. /package/dist/{table-CsXBcQLI.mjs → table-Dc3HfbM4.mjs} +0 -0
  129. /package/dist/{tabs-D8n-dqnw.mjs → tabs-Ccb4uqbe.mjs} +0 -0
  130. /package/dist/{tag-input-Di7SDNbK.mjs → tag-input-BfHaKoMF.mjs} +0 -0
  131. /package/dist/{textarea-CxE3YbC7.mjs → textarea-X4OjkqLJ.mjs} +0 -0
  132. /package/dist/{theme.provider-CzCxEFFh.mjs → theme.provider-Nun_O9-O.mjs} +0 -0
  133. /package/dist/{tooltip-Dd3ActSS.mjs → tooltip-DZFG1iMs.mjs} +0 -0
  134. /package/dist/{typography-UA7ZZvgJ.mjs → typography-T7WgvO77.mjs} +0 -0
  135. /package/dist/{use-debounce-B6wPrZV8.mjs → use-debounce-Ctljs3MB.mjs} +0 -0
  136. /package/dist/{use-toast-mdn_CqRY.mjs → use-toast-DN-fZBzJ.mjs} +0 -0
  137. /package/dist/{visuallyhidden-aaTUk4Yo.mjs → visuallyhidden-CgkVhApW.mjs} +0 -0
@@ -0,0 +1,1629 @@
1
+ import { t as cn } from "../cn-DWCc1QRE.mjs";
2
+ import { t as Badge } from "../badge-bFgeYceE.mjs";
3
+ import "../utils-Bfgoe-Gm.mjs";
4
+ import { t as Button } from "../button-C1wRfGtT.mjs";
5
+ import "../button-AzpnV-WB.mjs";
6
+ import { t as Checkbox } from "../checkbox-LG1OKTpG.mjs";
7
+ import "../dialog-DXBaT9gA.mjs";
8
+ import { a as CommandInput, i as CommandGroup, o as CommandItem, r as CommandEmpty, s as CommandList, t as Command } from "../command-s0Yv3abE.mjs";
9
+ import "../input-DuyjEKEW.mjs";
10
+ import { t as Input } from "../input-fzXBheCN.mjs";
11
+ import { t as Label } from "../label-_ste_Re3.mjs";
12
+ import { i as DropdownMenuItem, l as DropdownMenuTrigger, r as DropdownMenuContent, t as DropdownMenu } from "../dropdown-menu-DAFyO-qD.mjs";
13
+ import { i as PopoverTrigger, r as PopoverContent, t as Popover } from "../popover-Ds9624qY.mjs";
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";
18
+ 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 { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
21
+ import { parseAsInteger, parseAsString, useQueryStates } from "nuqs";
22
+ import { flexRender, getCoreRowModel, getPaginationRowModel, getSortedRowModel, useReactTable } from "@tanstack/react-table";
23
+
24
+ //#region src/components/features/data-table/constants.ts
25
+ const DEFAULT_PAGE_SIZE = 20;
26
+ const DEFAULT_PAGE_SIZES = [
27
+ 10,
28
+ 20,
29
+ 30,
30
+ 50
31
+ ];
32
+ const DEFAULT_DEBOUNCE_MS = 300;
33
+ const DEFAULT_LOADING_ROWS = 5;
34
+
35
+ //#endregion
36
+ //#region src/components/features/data-table/adapters/nuqs-adapter.ts
37
+ /**
38
+ * Serialize SortingState to URL-friendly string.
39
+ * Format: "name" (asc), "-name" (desc), comma-separated for multi-sort.
40
+ * Example: "-department,name" → [{id:"department",desc:true},{id:"name",desc:false}]
41
+ */
42
+ function serializeSorting(sorting) {
43
+ if (sorting.length === 0) return "";
44
+ return sorting.map((s) => s.desc ? `-${s.id}` : s.id).join(",");
45
+ }
46
+ /**
47
+ * Parse URL sort string back to SortingState.
48
+ */
49
+ function parseSorting(value) {
50
+ if (!value) return [];
51
+ return value.split(",").filter(Boolean).map((part) => {
52
+ if (part.startsWith("-")) return {
53
+ id: part.slice(1),
54
+ desc: true
55
+ };
56
+ return {
57
+ id: part,
58
+ desc: false
59
+ };
60
+ });
61
+ }
62
+ const coreSearchParams = {
63
+ sort: parseAsString.withDefault(""),
64
+ q: parseAsString.withDefault(""),
65
+ page: parseAsInteger.withDefault(0),
66
+ size: parseAsInteger.withDefault(DEFAULT_PAGE_SIZE)
67
+ };
68
+ /**
69
+ * Hook that creates a StateAdapter backed by nuqs URL query state.
70
+ *
71
+ * URL format:
72
+ * - `?sort=name` (asc) or `?sort=-name` (desc), comma-separated for multi-sort
73
+ * - `?q=search` for search text
74
+ * - `?page=0&size=20` for pagination
75
+ * - Custom filter keys as declared in options
76
+ *
77
+ * Requires `nuqs` to be installed in the consumer app.
78
+ *
79
+ * @example
80
+ * ```tsx
81
+ * const stateAdapter = useNuqsAdapter({
82
+ * filters: {
83
+ * status: parseAsString.withDefault(''),
84
+ * department: parseAsArrayOf(parseAsString).withDefault([]),
85
+ * },
86
+ * })
87
+ * const tableState = useDataTableClient({ data, columns, stateAdapter })
88
+ * ```
89
+ */
90
+ function useNuqsAdapter(options = {}) {
91
+ const { filters: filterParsers } = options;
92
+ const [coreState, setCoreState] = useQueryStates(coreSearchParams);
93
+ const hasFilters = filterParsers != null && Object.keys(filterParsers).length > 0;
94
+ const [filterState, setFilterState] = useQueryStates(hasFilters ? filterParsers : { _dt: parseAsString.withDefault("") });
95
+ return useMemo(() => ({
96
+ read: () => ({
97
+ sorting: parseSorting(coreState.sort),
98
+ search: coreState.q,
99
+ pageIndex: coreState.page,
100
+ pageSize: coreState.size,
101
+ ...hasFilters ? { filters: filterState } : {}
102
+ }),
103
+ write: (state) => {
104
+ setCoreState({
105
+ sort: serializeSorting(state.sorting),
106
+ q: state.search,
107
+ page: state.pageIndex ?? 0,
108
+ size: state.pageSize ?? DEFAULT_PAGE_SIZE
109
+ });
110
+ if (hasFilters && filterParsers) {
111
+ const update = {};
112
+ for (const key of Object.keys(filterParsers)) update[key] = state.filters?.[key] ?? null;
113
+ setFilterState(update);
114
+ }
115
+ }
116
+ }), [
117
+ coreState,
118
+ filterState,
119
+ hasFilters,
120
+ setCoreState,
121
+ setFilterState,
122
+ filterParsers
123
+ ]);
124
+ }
125
+
126
+ //#endregion
127
+ //#region src/components/features/data-table/columns/selection-column.tsx
128
+ const SELECTION_COLUMN_ID = "select";
129
+ function createSelectionColumn(options = {}) {
130
+ const { className, headerClassName, renderHeader, renderCell } = options;
131
+ return {
132
+ id: SELECTION_COLUMN_ID,
133
+ size: 40,
134
+ enableSorting: false,
135
+ enableHiding: false,
136
+ header: renderHeader ?? (({ table }) => /* @__PURE__ */ jsx(Checkbox, {
137
+ checked: table.getIsAllPageRowsSelected() || table.getIsSomePageRowsSelected() && "indeterminate",
138
+ onCheckedChange: (value) => table.toggleAllPageRowsSelected(!!value),
139
+ "aria-label": "Select all",
140
+ className: headerClassName
141
+ })),
142
+ cell: renderCell ?? (({ row }) => /* @__PURE__ */ jsx(Checkbox, {
143
+ checked: row.getIsSelected(),
144
+ onCheckedChange: (value) => row.toggleSelected(!!value),
145
+ "aria-label": "Select row",
146
+ className
147
+ }))
148
+ };
149
+ }
150
+ function hasSelectionColumn(columns) {
151
+ return columns.some((col) => "id" in col && col.id === SELECTION_COLUMN_ID);
152
+ }
153
+ function withSelectionColumn(columns, options = {}) {
154
+ if (hasSelectionColumn(columns)) return columns;
155
+ return [createSelectionColumn(options), ...columns];
156
+ }
157
+
158
+ //#endregion
159
+ //#region src/components/features/data-table/core/filter-engine.ts
160
+ const FILTER_STRATEGIES = {
161
+ "checkbox": (cellValue, filterValue) => {
162
+ if (filterValue == null) return true;
163
+ if (Array.isArray(filterValue) && filterValue.length === 0) return true;
164
+ if (!Array.isArray(filterValue)) return cellValue === filterValue;
165
+ if (Array.isArray(cellValue)) return cellValue.some((v) => filterValue.includes(v));
166
+ return filterValue.includes(cellValue);
167
+ },
168
+ "select": (cellValue, filterValue) => {
169
+ if (filterValue == null || filterValue === "") return true;
170
+ return cellValue === filterValue;
171
+ },
172
+ "date-gte": (cellValue, filterValue) => {
173
+ if (!filterValue) return true;
174
+ const cell = new Date(cellValue);
175
+ const filter = new Date(filterValue);
176
+ if (Number.isNaN(cell.getTime()) || Number.isNaN(filter.getTime())) return true;
177
+ return cell >= filter;
178
+ },
179
+ "date-lte": (cellValue, filterValue) => {
180
+ if (!filterValue) return true;
181
+ const cell = new Date(cellValue);
182
+ const filter = new Date(filterValue);
183
+ if (Number.isNaN(cell.getTime()) || Number.isNaN(filter.getTime())) return true;
184
+ return cell <= filter;
185
+ }
186
+ };
187
+ function resolveStrategy(strategy) {
188
+ if (!strategy) return void 0;
189
+ if (typeof strategy === "function") return strategy;
190
+ return FILTER_STRATEGIES[strategy];
191
+ }
192
+ function applyFilters(data, filters, search, registeredFilters, customFilterFns, searchConfig) {
193
+ const hasFilters = Object.keys(filters).length > 0;
194
+ const hasSearch = search.length > 0;
195
+ if (!hasFilters && !hasSearch) return data;
196
+ return data.filter((row) => {
197
+ if (hasFilters) for (const [column, value] of Object.entries(filters)) {
198
+ const fn = customFilterFns[column] ?? resolveStrategy(registeredFilters.get(column));
199
+ if (!fn) {
200
+ console.warn(`[DataTable] No filter strategy registered for column "${column}". Filter ignored.`);
201
+ continue;
202
+ }
203
+ const cellValue = row[column];
204
+ if (!fn(cellValue, value)) return false;
205
+ }
206
+ if (hasSearch) {
207
+ const query = search.toLowerCase();
208
+ if (searchConfig.searchFn) return searchConfig.searchFn(row, search);
209
+ if (searchConfig.searchableColumns && searchConfig.searchableColumns.length > 0) return searchConfig.searchableColumns.some((col) => {
210
+ const cellValue = row[col];
211
+ return cellValue != null && String(cellValue).toLowerCase().includes(query);
212
+ });
213
+ return Object.values(row).some((val) => {
214
+ if (val == null) return false;
215
+ return String(val).toLowerCase().includes(query);
216
+ });
217
+ }
218
+ return true;
219
+ });
220
+ }
221
+
222
+ //#endregion
223
+ //#region src/components/features/data-table/core/store.ts
224
+ function createDataTableStore(options) {
225
+ let registeredFilters = /* @__PURE__ */ new Map();
226
+ const listeners = /* @__PURE__ */ new Set();
227
+ function computeFilteredData(s) {
228
+ if (s.mode === "server") return s.data;
229
+ return applyFilters(s.data, s.filters, s.search, registeredFilters, options.filterFns ?? {}, {
230
+ searchFn: options.searchFn,
231
+ searchableColumns: options.searchableColumns
232
+ });
233
+ }
234
+ let state = {
235
+ data: options.data,
236
+ filteredData: options.data,
237
+ sorting: options.defaultSort ?? [],
238
+ filters: options.defaultFilters ?? {},
239
+ search: "",
240
+ rowSelection: {},
241
+ pageIndex: 0,
242
+ pageSize: options.pageSize ?? DEFAULT_PAGE_SIZE,
243
+ mode: options.mode,
244
+ isLoading: false,
245
+ error: null,
246
+ inlineContents: []
247
+ };
248
+ if (options.defaultFilters && Object.keys(options.defaultFilters).length > 0) state = {
249
+ ...state,
250
+ filteredData: computeFilteredData(state)
251
+ };
252
+ function notify() {
253
+ for (const listener of listeners) listener();
254
+ }
255
+ function setState(next) {
256
+ state = next;
257
+ notify();
258
+ }
259
+ return {
260
+ getSnapshot: () => state,
261
+ subscribe: (listener) => {
262
+ listeners.add(listener);
263
+ return () => listeners.delete(listener);
264
+ },
265
+ setData: (data) => {
266
+ const next = {
267
+ ...state,
268
+ data,
269
+ pageIndex: 0,
270
+ rowSelection: {}
271
+ };
272
+ setState({
273
+ ...next,
274
+ filteredData: computeFilteredData(next)
275
+ });
276
+ },
277
+ setServerData: (data) => {
278
+ setState({
279
+ ...state,
280
+ data,
281
+ filteredData: data
282
+ });
283
+ },
284
+ setTable: (_t) => {},
285
+ setSorting: (sorting) => {
286
+ setState({
287
+ ...state,
288
+ sorting,
289
+ rowSelection: {}
290
+ });
291
+ },
292
+ setFilter: (key, value) => {
293
+ const next = {
294
+ ...state,
295
+ filters: {
296
+ ...state.filters,
297
+ [key]: value
298
+ },
299
+ rowSelection: {},
300
+ pageIndex: 0
301
+ };
302
+ setState({
303
+ ...next,
304
+ filteredData: computeFilteredData(next)
305
+ });
306
+ },
307
+ clearFilter: (key) => {
308
+ const filters = Object.fromEntries(Object.entries(state.filters).filter(([k]) => k !== key));
309
+ const next = {
310
+ ...state,
311
+ filters,
312
+ rowSelection: {},
313
+ pageIndex: 0
314
+ };
315
+ setState({
316
+ ...next,
317
+ filteredData: computeFilteredData(next)
318
+ });
319
+ },
320
+ clearAllFilters: () => {
321
+ const next = {
322
+ ...state,
323
+ filters: {},
324
+ rowSelection: {},
325
+ pageIndex: 0
326
+ };
327
+ setState({
328
+ ...next,
329
+ filteredData: computeFilteredData(next)
330
+ });
331
+ },
332
+ setSearch: (search) => {
333
+ const next = {
334
+ ...state,
335
+ search,
336
+ rowSelection: {},
337
+ pageIndex: 0
338
+ };
339
+ setState({
340
+ ...next,
341
+ filteredData: computeFilteredData(next)
342
+ });
343
+ },
344
+ clearSearch: () => {
345
+ const next = {
346
+ ...state,
347
+ search: "",
348
+ rowSelection: {},
349
+ pageIndex: 0
350
+ };
351
+ setState({
352
+ ...next,
353
+ filteredData: computeFilteredData(next)
354
+ });
355
+ },
356
+ setRowSelection: (rowSelection) => {
357
+ setState({
358
+ ...state,
359
+ rowSelection
360
+ });
361
+ },
362
+ setPageIndex: (pageIndex) => {
363
+ if (!Number.isFinite(pageIndex) || pageIndex < 0) return;
364
+ setState({
365
+ ...state,
366
+ pageIndex: Math.floor(pageIndex),
367
+ rowSelection: {}
368
+ });
369
+ },
370
+ setPageSize: (pageSize) => {
371
+ if (!Number.isFinite(pageSize) || pageSize < 1) return;
372
+ setState({
373
+ ...state,
374
+ pageSize: Math.floor(pageSize),
375
+ pageIndex: 0,
376
+ rowSelection: {}
377
+ });
378
+ },
379
+ setPagination: (pageIndex, pageSize) => {
380
+ const safeIndex = Number.isFinite(pageIndex) ? Math.max(0, Math.floor(pageIndex)) : state.pageIndex;
381
+ const safeSize = Number.isFinite(pageSize) ? Math.max(1, Math.floor(pageSize)) : state.pageSize;
382
+ setState({
383
+ ...state,
384
+ pageIndex: safeIndex,
385
+ pageSize: safeSize,
386
+ rowSelection: {}
387
+ });
388
+ },
389
+ setLoading: (isLoading) => {
390
+ setState({
391
+ ...state,
392
+ isLoading
393
+ });
394
+ },
395
+ setError: (error) => {
396
+ setState({
397
+ ...state,
398
+ error
399
+ });
400
+ },
401
+ registerFilter: (column, strategy) => {
402
+ const next = new Map(registeredFilters);
403
+ next.set(column, strategy);
404
+ registeredFilters = next;
405
+ const filteredData = computeFilteredData(state);
406
+ setState({
407
+ ...state,
408
+ filteredData
409
+ });
410
+ },
411
+ unregisterFilter: (column) => {
412
+ const next = new Map(registeredFilters);
413
+ next.delete(column);
414
+ registeredFilters = next;
415
+ if (column in state.filters) {
416
+ const filteredData = computeFilteredData(state);
417
+ setState({
418
+ ...state,
419
+ filteredData
420
+ });
421
+ }
422
+ },
423
+ registerInlineContent: (entry) => {
424
+ const existing = state.inlineContents.findIndex((e) => e.id === entry.id);
425
+ const inlineContents = existing >= 0 ? state.inlineContents.map((e, i) => i === existing ? entry : e) : [...state.inlineContents, entry];
426
+ setState({
427
+ ...state,
428
+ inlineContents
429
+ });
430
+ },
431
+ unregisterInlineContent: (id) => {
432
+ const inlineContents = state.inlineContents.filter((e) => e.id !== id);
433
+ setState({
434
+ ...state,
435
+ inlineContents
436
+ });
437
+ }
438
+ };
439
+ }
440
+
441
+ //#endregion
442
+ //#region src/components/features/data-table/core/data-table-context.tsx
443
+ const DataTableStoreContext = createContext(null);
444
+ const TableInstanceContext = createContext(null);
445
+ const DataTableContext = createContext(null);
446
+ function useDataTableStore() {
447
+ const store = use(DataTableStoreContext);
448
+ if (!store) throw new Error("useDataTableStore must be used within a <DataTable.Client> or <DataTable.Server> provider");
449
+ return store;
450
+ }
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;
455
+ }
456
+
457
+ //#endregion
458
+ //#region src/components/features/data-table/hooks/use-selectors.ts
459
+ function shallowEqual(a, b) {
460
+ const keysA = Object.keys(a);
461
+ const keysB = Object.keys(b);
462
+ if (keysA.length !== keysB.length) return false;
463
+ for (const key of keysA) {
464
+ const va = a[key];
465
+ const vb = b[key];
466
+ if (va === vb) continue;
467
+ if (Array.isArray(va) && Array.isArray(vb)) {
468
+ if (va.length !== vb.length) return false;
469
+ for (let i = 0; i < va.length; i++) if (va[i] !== vb[i]) return false;
470
+ continue;
471
+ }
472
+ return false;
473
+ }
474
+ return true;
475
+ }
476
+ function useSliceSelector(selector) {
477
+ const store = useDataTableStore();
478
+ const cachedRef = useRef(null);
479
+ const getSnapshot = useCallback(() => {
480
+ const next = selector(store.getSnapshot());
481
+ if (cachedRef.current && shallowEqual(cachedRef.current, next)) return cachedRef.current;
482
+ cachedRef.current = next;
483
+ return next;
484
+ }, [store, selector]);
485
+ return useSyncExternalStore(store.subscribe, getSnapshot);
486
+ }
487
+ function useDataTableFilters() {
488
+ const store = useDataTableStore();
489
+ return useSliceSelector(useCallback((state) => ({
490
+ filters: state.filters,
491
+ setFilter: store.setFilter,
492
+ clearFilter: store.clearFilter,
493
+ clearAllFilters: store.clearAllFilters,
494
+ registerFilter: store.registerFilter,
495
+ unregisterFilter: store.unregisterFilter
496
+ }), [store]));
497
+ }
498
+ function useDataTableSearch() {
499
+ const store = useDataTableStore();
500
+ return useSliceSelector(useCallback((state) => ({
501
+ search: state.search,
502
+ setSearch: store.setSearch,
503
+ clearSearch: store.clearSearch
504
+ }), [store]));
505
+ }
506
+ function useDataTableSorting() {
507
+ const store = useDataTableStore();
508
+ return useSliceSelector(useCallback((state) => ({
509
+ sorting: state.sorting,
510
+ setSorting: store.setSorting
511
+ }), [store]));
512
+ }
513
+ function useDataTableSelection() {
514
+ const store = useDataTableStore();
515
+ const table = useTableInstance();
516
+ return useSliceSelector(useCallback((state) => ({
517
+ rowSelection: state.rowSelection,
518
+ setRowSelection: store.setRowSelection,
519
+ selectedRows: table.getFilteredSelectedRowModel().rows.map((r) => r.original)
520
+ }), [store, table]));
521
+ }
522
+ function useDataTablePagination() {
523
+ 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) => ({
528
+ canNextPage: table.getCanNextPage(),
529
+ canPrevPage: table.getCanPreviousPage(),
530
+ nextPage,
531
+ prevPage,
532
+ pageIndex: state.pageIndex,
533
+ pageCount: table.getPageCount(),
534
+ setPageIndex: store.setPageIndex,
535
+ pageSize: state.pageSize,
536
+ setPageSize: store.setPageSize,
537
+ totalRows: state.filteredData.length
538
+ }), [
539
+ store,
540
+ table,
541
+ nextPage,
542
+ prevPage
543
+ ]));
544
+ }
545
+ function useDataTableRows() {
546
+ const table = useTableInstance();
547
+ return useSliceSelector(useCallback((_state) => ({
548
+ rows: table.getRowModel().rows,
549
+ headerGroups: table.getHeaderGroups(),
550
+ totalColumns: table.getAllColumns().length
551
+ }), [table]));
552
+ }
553
+ function useDataTableLoading() {
554
+ return useSliceSelector(useCallback((state) => ({
555
+ isLoading: state.isLoading,
556
+ error: state.error
557
+ }), []));
558
+ }
559
+ function useDataTableInlineContents() {
560
+ const store = useDataTableStore();
561
+ return useSliceSelector(useCallback((state) => ({
562
+ inlineContents: state.inlineContents,
563
+ registerInlineContent: store.registerInlineContent,
564
+ unregisterInlineContent: store.unregisterInlineContent
565
+ }), [store]));
566
+ }
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
+
603
+ //#endregion
604
+ //#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
+ }
610
+ return String(value);
611
+ }
612
+ function FilterGroup({ label, children, className }) {
613
+ return /* @__PURE__ */ jsxs("div", {
614
+ className: cn("flex items-center gap-2 rounded-md border px-2 py-1", className),
615
+ "data-slot": "dt-filter-group",
616
+ "data-testid": "dt-filter-group",
617
+ children: [/* @__PURE__ */ jsx("span", {
618
+ className: "text-muted-foreground border-r pr-2 text-xs",
619
+ children: label
620
+ }), children]
621
+ });
622
+ }
623
+ const EMPTY_LABELS = {};
624
+ function ActiveFiltersInner({ label = "Selected Filters", filterLabels = EMPTY_LABELS, formatFilterValue: formatter, clearAll = "icon", clearAllLabel = "Clear all", className, groupClassName, badgeClassName }) {
625
+ const { filters, setFilter, clearFilter, clearAllFilters } = useDataTableFilters();
626
+ 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;
629
+ const hasFilters = activeFilterEntries.length > 0;
630
+ if (!hasSearch && !hasFilters) return null;
631
+ const totalGroups = activeFilterEntries.length + (hasSearch ? 1 : 0);
632
+ const removeArrayItem = (column, items, item) => {
633
+ const remaining = items.filter((v) => v !== item);
634
+ if (remaining.length > 0) setFilter(column, remaining);
635
+ else clearFilter(column);
636
+ };
637
+ const handleClearAll = () => {
638
+ clearAllFilters();
639
+ if (hasSearch) clearSearch();
640
+ };
641
+ const badgeCn = cn("flex items-center gap-1.5 px-2 py-0.5 text-xs", badgeClassName);
642
+ return /* @__PURE__ */ jsxs("div", {
643
+ className: cn("flex flex-wrap items-center gap-2", className),
644
+ "data-slot": "dt-active-filters",
645
+ "data-testid": "dt-active-filters",
646
+ children: [
647
+ label !== null && /* @__PURE__ */ jsx("span", {
648
+ className: "text-sm text-muted-foreground",
649
+ "data-slot": "dt-active-filters-label",
650
+ children: label
651
+ }),
652
+ hasSearch && /* @__PURE__ */ jsx(FilterGroup, {
653
+ label: "Search",
654
+ className: groupClassName,
655
+ children: /* @__PURE__ */ jsxs(Badge, {
656
+ type: "muted",
657
+ theme: "solid",
658
+ className: badgeCn,
659
+ children: [/* @__PURE__ */ jsx("span", { children: search }), /* @__PURE__ */ jsx(Button, {
660
+ theme: "borderless",
661
+ size: "small",
662
+ "aria-label": "Clear search",
663
+ className: "h-auto p-0 text-muted-foreground hover:text-foreground",
664
+ onClick: clearSearch,
665
+ children: /* @__PURE__ */ jsx(X, {
666
+ className: "size-2.5",
667
+ "aria-hidden": "true"
668
+ })
669
+ })]
670
+ })
671
+ }),
672
+ activeFilterEntries.map(([column, value]) => {
673
+ const groupLabel = filterLabels[column] ?? column;
674
+ if (Array.isArray(value)) return /* @__PURE__ */ jsx(FilterGroup, {
675
+ label: groupLabel,
676
+ className: groupClassName,
677
+ children: value.map((item) => /* @__PURE__ */ jsxs(Badge, {
678
+ type: "muted",
679
+ theme: "solid",
680
+ className: badgeCn,
681
+ children: [/* @__PURE__ */ jsx("span", { children: formatValue(column, item, formatter) }), /* @__PURE__ */ jsx(Button, {
682
+ theme: "borderless",
683
+ size: "small",
684
+ "aria-label": `Remove ${formatValue(column, item, formatter)} from ${groupLabel}`,
685
+ className: "h-auto p-0 text-muted-foreground hover:text-foreground",
686
+ onClick: () => removeArrayItem(column, value, item),
687
+ children: /* @__PURE__ */ jsx(X, {
688
+ className: "size-2.5",
689
+ "aria-hidden": "true"
690
+ })
691
+ })]
692
+ }, item))
693
+ }, column);
694
+ return /* @__PURE__ */ jsx(FilterGroup, {
695
+ label: groupLabel,
696
+ className: groupClassName,
697
+ children: /* @__PURE__ */ jsxs(Badge, {
698
+ type: "muted",
699
+ theme: "solid",
700
+ className: badgeCn,
701
+ children: [/* @__PURE__ */ jsx("span", { children: formatValue(column, value, formatter) }), /* @__PURE__ */ jsx(Button, {
702
+ theme: "borderless",
703
+ size: "small",
704
+ "aria-label": `Clear ${groupLabel} filter`,
705
+ className: "h-auto p-0 text-muted-foreground hover:text-foreground",
706
+ onClick: () => clearFilter(column),
707
+ children: /* @__PURE__ */ jsx(X, {
708
+ className: "size-2.5",
709
+ "aria-hidden": "true"
710
+ })
711
+ })]
712
+ })
713
+ }, column);
714
+ }),
715
+ totalGroups > 1 && /* @__PURE__ */ jsxs(Fragment$1, { children: [
716
+ clearAll === "icon" && /* @__PURE__ */ jsx(Button, {
717
+ theme: "borderless",
718
+ size: "small",
719
+ "aria-label": clearAllLabel,
720
+ title: clearAllLabel,
721
+ className: "h-auto p-1 text-muted-foreground hover:text-foreground",
722
+ "data-slot": "dt-clear-all-filters",
723
+ "data-testid": "dt-clear-all-filters",
724
+ onClick: handleClearAll,
725
+ children: /* @__PURE__ */ jsx(X, { className: "size-4" })
726
+ }),
727
+ clearAll === "button" && /* @__PURE__ */ jsxs(Button, {
728
+ theme: "outline",
729
+ size: "small",
730
+ className: "h-auto px-2 py-1 text-xs",
731
+ "data-slot": "dt-clear-all-filters",
732
+ "data-testid": "dt-clear-all-filters",
733
+ onClick: handleClearAll,
734
+ children: [/* @__PURE__ */ jsx(X, { className: "size-3 mr-1" }), clearAllLabel]
735
+ }),
736
+ clearAll === "text" && /* @__PURE__ */ jsx(Button, {
737
+ theme: "borderless",
738
+ size: "small",
739
+ className: "h-auto px-1 py-0.5 text-xs text-muted-foreground hover:text-foreground",
740
+ "data-slot": "dt-clear-all-filters",
741
+ "data-testid": "dt-clear-all-filters",
742
+ onClick: handleClearAll,
743
+ children: clearAllLabel
744
+ })
745
+ ] })
746
+ ]
747
+ });
748
+ }
749
+ const DataTableActiveFilters = memo(ActiveFiltersInner);
750
+
751
+ //#endregion
752
+ //#region src/components/features/data-table/components/bulk-actions.tsx
753
+ function DataTableBulkActions({ children, className }) {
754
+ const { selectedRows } = useDataTableSelection();
755
+ if (selectedRows.length === 0) return null;
756
+ return /* @__PURE__ */ jsx("div", {
757
+ "data-slot": "dt-bulk-actions",
758
+ className,
759
+ children: children(selectedRows)
760
+ });
761
+ }
762
+
763
+ //#endregion
764
+ //#region src/components/features/data-table/components/column-header.tsx
765
+ function DataTableColumnHeader({ column, title, className }) {
766
+ if (!column.getCanSort()) return /* @__PURE__ */ jsx("div", {
767
+ className: cn(className),
768
+ "data-slot": "dt-column-header",
769
+ children: title
770
+ });
771
+ const sorted = column.getIsSorted();
772
+ return /* @__PURE__ */ jsx("div", {
773
+ className: cn("flex items-center gap-2", className),
774
+ "data-slot": "dt-column-header",
775
+ children: /* @__PURE__ */ jsxs("button", {
776
+ type: "button",
777
+ className: "flex items-center gap-1 hover:text-foreground -ml-3 h-8 px-3 cursor-pointer",
778
+ onClick: column.getToggleSortingHandler(),
779
+ "aria-label": `Sort by ${title}${sorted === "asc" ? ", sorted ascending" : sorted === "desc" ? ", sorted descending" : ""}`,
780
+ 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" })]
781
+ })
782
+ });
783
+ }
784
+
785
+ //#endregion
786
+ //#region src/components/features/data-table/components/content.tsx
787
+ function resolveClassName(value, item) {
788
+ if (typeof value === "function") return value(item);
789
+ return value;
790
+ }
791
+ function renderInlineContentRow(entry, colSpan, rows) {
792
+ return /* @__PURE__ */ jsx(TableRow, {
793
+ "data-slot": "dt-inline-content",
794
+ "data-position": entry.position,
795
+ className: cn("transition-all duration-200", entry.className),
796
+ children: /* @__PURE__ */ jsx(TableCell, {
797
+ colSpan,
798
+ children: entry.render({
799
+ onClose: entry.onClose,
800
+ rowData: entry.position === "row" ? rows.find((r) => r.id === entry.rowId)?.original ?? null : null
801
+ })
802
+ })
803
+ }, entry.id);
804
+ }
805
+ function DataTableContent({ emptyMessage, className, tableClassName, headerClassName, headerRowClassName, headerCellClassName, bodyClassName, rowClassName, cellClassName }) {
806
+ const { rows, headerGroups, totalColumns } = useDataTableRows();
807
+ const { inlineContents } = useDataTableInlineContents();
808
+ const openInlineContents = useMemo(() => inlineContents.filter((e) => e.open), [inlineContents]);
809
+ const colSpan = totalColumns;
810
+ return /* @__PURE__ */ jsx("div", {
811
+ className: cn("datum-ui-data-table", className),
812
+ "data-slot": "dt",
813
+ style: { overflowX: "auto" },
814
+ children: /* @__PURE__ */ jsxs(Table, {
815
+ className: cn(tableClassName),
816
+ "data-slot": "dt-table",
817
+ children: [/* @__PURE__ */ jsx(TableHeader, {
818
+ className: cn(headerClassName),
819
+ "data-slot": "dt-header",
820
+ children: headerGroups.map((headerGroup) => /* @__PURE__ */ jsx(TableRow, {
821
+ className: cn(headerRowClassName),
822
+ "data-slot": "dt-header-row",
823
+ children: headerGroup.headers.map((header) => /* @__PURE__ */ jsx(TableHead, {
824
+ className: cn(headerCellClassName),
825
+ "data-slot": "dt-header-cell",
826
+ children: header.isPlaceholder ? null : flexRender(header.column.columnDef.header, header.getContext())
827
+ }, header.id))
828
+ }, headerGroup.id))
829
+ }), /* @__PURE__ */ jsxs(TableBody, {
830
+ className: cn(bodyClassName),
831
+ "data-slot": "dt-body",
832
+ children: [openInlineContents.filter((e) => e.position === "top").map((entry) => renderInlineContentRow(entry, colSpan, rows)), rows.length > 0 ? rows.map((row) => {
833
+ const rowEntry = openInlineContents.find((e) => e.position === "row" && e.rowId === row.id);
834
+ if (rowEntry) return renderInlineContentRow(rowEntry, colSpan, rows);
835
+ return /* @__PURE__ */ jsx(TableRow, {
836
+ className: cn(resolveClassName(rowClassName, row)),
837
+ style: { transitionProperty: "none" },
838
+ "data-slot": "dt-row",
839
+ "data-state": row.getIsSelected() ? "selected" : void 0,
840
+ children: row.getVisibleCells().map((cell) => /* @__PURE__ */ jsx(TableCell, {
841
+ className: cn(resolveClassName(cellClassName, cell)),
842
+ "data-slot": "dt-cell",
843
+ children: flexRender(cell.column.columnDef.cell, cell.getContext())
844
+ }, cell.id))
845
+ }, row.id);
846
+ }) : /* @__PURE__ */ jsx(TableRow, {
847
+ "data-slot": "dt-row",
848
+ children: /* @__PURE__ */ jsx(TableCell, {
849
+ colSpan,
850
+ className: "h-24 text-center",
851
+ "data-slot": "dt-empty",
852
+ children: emptyMessage ?? "No results."
853
+ })
854
+ })]
855
+ })]
856
+ })
857
+ });
858
+ }
859
+
860
+ //#endregion
861
+ //#region src/components/features/data-table/components/inline-content.tsx
862
+ function DataTableInlineContent({ position, rowId, open, onClose, className, children }) {
863
+ const id = useId();
864
+ const { registerInlineContent, unregisterInlineContent } = useDataTableInlineContents();
865
+ const initialRender = useRef(true);
866
+ useEffect(() => {
867
+ registerInlineContent({
868
+ id,
869
+ position,
870
+ rowId,
871
+ open,
872
+ onClose,
873
+ className,
874
+ render: children
875
+ });
876
+ return () => {
877
+ unregisterInlineContent(id);
878
+ };
879
+ }, [
880
+ id,
881
+ registerInlineContent,
882
+ unregisterInlineContent
883
+ ]);
884
+ useEffect(() => {
885
+ if (initialRender.current) {
886
+ initialRender.current = false;
887
+ return;
888
+ }
889
+ registerInlineContent({
890
+ id,
891
+ position,
892
+ rowId,
893
+ open,
894
+ onClose,
895
+ className,
896
+ render: children
897
+ });
898
+ }, [
899
+ id,
900
+ position,
901
+ rowId,
902
+ open,
903
+ onClose,
904
+ className,
905
+ children,
906
+ registerInlineContent
907
+ ]);
908
+ return null;
909
+ }
910
+
911
+ //#endregion
912
+ //#region src/components/features/data-table/components/loading.tsx
913
+ function DataTableLoading({ rows = DEFAULT_LOADING_ROWS, columns = 4, className }) {
914
+ return /* @__PURE__ */ jsx("div", {
915
+ className,
916
+ "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
+ })
930
+ });
931
+ }
932
+
933
+ //#endregion
934
+ //#region src/components/features/data-table/components/pagination.tsx
935
+ /**
936
+ * Generates page numbers with ellipsis for large page counts.
937
+ * Shows up to 7 items: first, last, current +/- 1 neighbor, and ellipsis gaps.
938
+ */
939
+ function getPageNumbers(currentPage, totalPages) {
940
+ if (totalPages <= 7) return Array.from({ length: totalPages }, (_, i) => i + 1);
941
+ const pages = [1];
942
+ const current = currentPage + 1;
943
+ if (current <= 4) {
944
+ for (let i = 2; i <= 5; i++) pages.push(i);
945
+ pages.push("...");
946
+ pages.push(totalPages);
947
+ } else if (current >= totalPages - 3) {
948
+ pages.push("...");
949
+ for (let i = totalPages - 4; i <= totalPages; i++) pages.push(i);
950
+ } else {
951
+ pages.push("...");
952
+ for (let i = current - 1; i <= current + 1; i++) pages.push(i);
953
+ pages.push("...");
954
+ pages.push(totalPages);
955
+ }
956
+ return pages;
957
+ }
958
+ function DataTablePagination({ pageSizes = DEFAULT_PAGE_SIZES, className }) {
959
+ const { canNextPage, canPrevPage, nextPage, prevPage, pageIndex, pageCount, setPageIndex, pageSize, setPageSize, totalRows } = useDataTablePagination();
960
+ const isClientMode = pageCount > 0;
961
+ const startRow = pageIndex * pageSize + 1;
962
+ const endRow = Math.min((pageIndex + 1) * pageSize, totalRows);
963
+ const pageNumbers = useMemo(() => getPageNumbers(pageIndex, pageCount), [pageIndex, pageCount]);
964
+ return /* @__PURE__ */ jsxs("div", {
965
+ className: cn("flex flex-col-reverse items-center justify-between gap-4 px-2 py-4 sm:flex-row", className),
966
+ "data-slot": "dt-pagination",
967
+ children: [/* @__PURE__ */ jsxs("div", {
968
+ className: "flex items-center gap-4",
969
+ children: [isClientMode && totalRows > 0 && /* @__PURE__ */ jsxs("span", {
970
+ className: "text-sm text-muted-foreground whitespace-nowrap",
971
+ children: [
972
+ "Showing",
973
+ " ",
974
+ startRow,
975
+ " ",
976
+ "to",
977
+ " ",
978
+ endRow,
979
+ " ",
980
+ "of",
981
+ " ",
982
+ totalRows,
983
+ " ",
984
+ "rows"
985
+ ]
986
+ }), /* @__PURE__ */ jsxs("div", {
987
+ className: "flex items-center gap-2",
988
+ children: [/* @__PURE__ */ jsx("span", {
989
+ className: "text-sm text-muted-foreground whitespace-nowrap",
990
+ children: "Rows per page"
991
+ }), /* @__PURE__ */ jsxs(Select, {
992
+ value: String(pageSize),
993
+ onValueChange: (value) => setPageSize(Number(value)),
994
+ children: [/* @__PURE__ */ jsx(SelectTrigger, {
995
+ className: "h-8 w-[70px]",
996
+ children: /* @__PURE__ */ jsx(SelectValue, { placeholder: String(pageSize) })
997
+ }), /* @__PURE__ */ jsx(SelectContent, {
998
+ side: "top",
999
+ children: pageSizes.map((size) => /* @__PURE__ */ jsx(SelectItem, {
1000
+ value: String(size),
1001
+ children: size
1002
+ }, size))
1003
+ })]
1004
+ })]
1005
+ })]
1006
+ }), /* @__PURE__ */ jsxs("nav", {
1007
+ "aria-label": "Table pagination",
1008
+ className: "flex items-center gap-1",
1009
+ children: [
1010
+ /* @__PURE__ */ jsx(Button, {
1011
+ theme: "outline",
1012
+ size: "icon",
1013
+ className: "size-8",
1014
+ onClick: prevPage,
1015
+ disabled: !canPrevPage,
1016
+ "aria-label": "Previous page",
1017
+ children: /* @__PURE__ */ jsx(ChevronLeft, { className: "size-4" })
1018
+ }),
1019
+ isClientMode && pageCount > 1 ? pageNumbers.map((page, index) => {
1020
+ if (page === "...") return /* @__PURE__ */ jsx("span", {
1021
+ className: "px-2 text-sm text-muted-foreground",
1022
+ children: "..."
1023
+ }, `ellipsis-${index}`);
1024
+ const isActive = page === pageIndex + 1;
1025
+ return /* @__PURE__ */ jsx(Button, {
1026
+ theme: isActive ? "solid" : "outline",
1027
+ size: "small",
1028
+ className: cn("h-8 min-w-8 px-2", isActive && "font-semibold"),
1029
+ onClick: () => setPageIndex(page - 1),
1030
+ disabled: isActive,
1031
+ "aria-label": `Page ${page}`,
1032
+ "aria-current": isActive ? "page" : void 0,
1033
+ children: page
1034
+ }, page);
1035
+ }) : !isClientMode && /* @__PURE__ */ jsxs("span", {
1036
+ className: "px-2 text-sm text-muted-foreground",
1037
+ children: [
1038
+ "Page",
1039
+ " ",
1040
+ pageIndex + 1
1041
+ ]
1042
+ }),
1043
+ /* @__PURE__ */ jsx(Button, {
1044
+ theme: "outline",
1045
+ size: "icon",
1046
+ className: "size-8",
1047
+ onClick: nextPage,
1048
+ disabled: !canNextPage,
1049
+ "aria-label": "Next page",
1050
+ children: /* @__PURE__ */ jsx(ChevronRight, { className: "size-4" })
1051
+ })
1052
+ ]
1053
+ })]
1054
+ });
1055
+ }
1056
+
1057
+ //#endregion
1058
+ //#region src/components/features/data-table/components/row-actions.tsx
1059
+ function DataTableRowActions({ row, actions, isLoading = false, className }) {
1060
+ const data = row.original;
1061
+ const visibleActions = actions.filter((action) => {
1062
+ if (action.hidden === void 0) return true;
1063
+ return typeof action.hidden === "function" ? !action.hidden(data) : !action.hidden;
1064
+ });
1065
+ if (visibleActions.length === 0) return null;
1066
+ return /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsx(DropdownMenuTrigger, {
1067
+ asChild: true,
1068
+ children: /* @__PURE__ */ jsxs(Button, {
1069
+ theme: "borderless",
1070
+ size: "small",
1071
+ className,
1072
+ disabled: isLoading,
1073
+ "data-slot": "dt-row-actions",
1074
+ children: [/* @__PURE__ */ jsx(MoreHorizontal, { className: "size-4" }), /* @__PURE__ */ jsx("span", {
1075
+ className: "sr-only",
1076
+ children: "Open menu"
1077
+ })]
1078
+ })
1079
+ }), /* @__PURE__ */ jsx(DropdownMenuContent, {
1080
+ align: "end",
1081
+ children: visibleActions.map((action) => {
1082
+ return /* @__PURE__ */ jsxs(DropdownMenuItem, {
1083
+ disabled: typeof action.disabled === "function" ? action.disabled(data) : action.disabled ?? false,
1084
+ onClick: () => action.onClick(data),
1085
+ className: action.variant === "destructive" ? "text-destructive" : void 0,
1086
+ children: [action.icon && /* @__PURE__ */ jsx(action.icon, { className: "mr-2 size-4" }), action.label]
1087
+ }, action.label);
1088
+ })
1089
+ })] });
1090
+ }
1091
+
1092
+ //#endregion
1093
+ //#region src/components/features/data-table/components/search.tsx
1094
+ function DataTableSearch({ placeholder = "Search...", debounceMs = DEFAULT_DEBOUNCE_MS, className }) {
1095
+ const { search, setSearch } = useDataTableSearch();
1096
+ const [inputValue, setInputValue] = useState(search);
1097
+ useEffect(() => {
1098
+ setInputValue(search);
1099
+ }, [search]);
1100
+ useEffect(() => {
1101
+ const timer = setTimeout(() => {
1102
+ if (inputValue !== search) setSearch(inputValue);
1103
+ }, debounceMs);
1104
+ return () => clearTimeout(timer);
1105
+ }, [
1106
+ inputValue,
1107
+ debounceMs,
1108
+ search,
1109
+ setSearch
1110
+ ]);
1111
+ return /* @__PURE__ */ jsx(Input, {
1112
+ placeholder,
1113
+ value: inputValue,
1114
+ onChange: (e) => setInputValue(e.target.value),
1115
+ className,
1116
+ "aria-label": placeholder,
1117
+ "data-slot": "dt-search"
1118
+ });
1119
+ }
1120
+
1121
+ //#endregion
1122
+ //#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,
1128
+ children: /* @__PURE__ */ jsx("div", {
1129
+ className,
1130
+ children
1131
+ })
1132
+ })
1133
+ });
1134
+ }
1135
+
1136
+ //#endregion
1137
+ //#region src/components/features/data-table/core/server-provider.tsx
1138
+ function ServerProvider({ store, table, className, children }) {
1139
+ return /* @__PURE__ */ jsx(DataTableStoreContext, {
1140
+ value: store,
1141
+ children: /* @__PURE__ */ jsx(TableInstanceContext, {
1142
+ value: table,
1143
+ children: /* @__PURE__ */ jsx("div", {
1144
+ className,
1145
+ children
1146
+ })
1147
+ })
1148
+ });
1149
+ }
1150
+
1151
+ //#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);
1157
+ useEffect(() => {
1158
+ registerFilter(column, "checkbox");
1159
+ return () => unregisterFilter(column);
1160
+ }, [
1161
+ column,
1162
+ registerFilter,
1163
+ unregisterFilter
1164
+ ]);
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,
1181
+ children: [/* @__PURE__ */ jsx(PopoverTrigger, {
1182
+ asChild: true,
1183
+ children: /* @__PURE__ */ jsxs(Button, {
1184
+ theme: "outline",
1185
+ className: cn("justify-between gap-1", className),
1186
+ "data-slot": "dt-filter",
1187
+ "data-testid": "dt-filter-trigger",
1188
+ children: [selectedValues.length > 0 ? /* @__PURE__ */ jsxs("div", {
1189
+ className: "flex items-center gap-1 flex-wrap",
1190
+ children: [visibleBadges.map((val) => {
1191
+ const opt = options.find((o) => o.value === val);
1192
+ return /* @__PURE__ */ jsxs(Badge, {
1193
+ type: "secondary",
1194
+ theme: "light",
1195
+ className: "text-xs px-1.5 py-0",
1196
+ children: [opt?.label ?? val, /* @__PURE__ */ jsx("span", {
1197
+ role: "button",
1198
+ tabIndex: 0,
1199
+ "aria-label": `Remove ${opt?.label ?? val}`,
1200
+ className: "ml-1 rounded-sm hover:bg-muted",
1201
+ onClick: (e) => {
1202
+ e.stopPropagation();
1203
+ removeValue(val);
1204
+ },
1205
+ onKeyDown: (e) => {
1206
+ if (e.key === "Enter" || e.key === " ") {
1207
+ e.preventDefault();
1208
+ removeValue(val);
1209
+ }
1210
+ },
1211
+ children: /* @__PURE__ */ jsx(X, { className: "size-3" })
1212
+ })]
1213
+ }, val);
1214
+ }), remainingCount > 0 && /* @__PURE__ */ jsxs("span", {
1215
+ className: "text-xs text-muted-foreground",
1216
+ children: [
1217
+ "+",
1218
+ remainingCount,
1219
+ " ",
1220
+ "more"
1221
+ ]
1222
+ })]
1223
+ }) : /* @__PURE__ */ jsx("span", { children: label }), /* @__PURE__ */ jsx(ChevronDown, { className: "size-4 opacity-50 ml-auto shrink-0" })]
1224
+ })
1225
+ }), /* @__PURE__ */ jsxs(PopoverContent, {
1226
+ className: cn("popover-content-width-full p-0", checkboxPopoverClassName),
1227
+ align: "start",
1228
+ children: [/* @__PURE__ */ jsxs("div", {
1229
+ className: "flex items-center justify-between p-2 border-b",
1230
+ children: [/* @__PURE__ */ jsx("span", {
1231
+ className: "text-sm font-medium",
1232
+ children: label
1233
+ }), selectedValues.length > 0 && /* @__PURE__ */ jsx(Button, {
1234
+ theme: "borderless",
1235
+ size: "small",
1236
+ className: "h-auto p-1 text-xs",
1237
+ onClick: () => clearFilter(column),
1238
+ children: "Clear"
1239
+ })]
1240
+ }), /* @__PURE__ */ jsx("div", {
1241
+ className: "max-h-48 overflow-y-auto p-2",
1242
+ children: /* @__PURE__ */ jsx("div", {
1243
+ className: "flex flex-col gap-2",
1244
+ children: options.map((option) => /* @__PURE__ */ jsxs("div", {
1245
+ className: "flex items-center gap-2",
1246
+ children: [/* @__PURE__ */ jsx(Checkbox, {
1247
+ id: `${column}-${option.value}`,
1248
+ checked: selectedValues.includes(option.value),
1249
+ onCheckedChange: (checked) => handleToggle(option.value, checked === true)
1250
+ }), /* @__PURE__ */ jsx(Label, {
1251
+ htmlFor: `${column}-${option.value}`,
1252
+ className: "text-sm font-normal cursor-pointer",
1253
+ children: option.label
1254
+ })]
1255
+ }, option.value))
1256
+ })
1257
+ })]
1258
+ })]
1259
+ });
1260
+ }
1261
+
1262
+ //#endregion
1263
+ //#region src/components/features/data-table/filters/date-picker-filter.tsx
1264
+ function DatePickerFilter({ column, label, className, datePickerPopoverClassName, disableFuture, disablePast, minDate, maxDate }) {
1265
+ const { filters, setFilter, clearFilter, registerFilter, unregisterFilter } = useDataTableFilters();
1266
+ const rawValue = filters[column];
1267
+ useEffect(() => {
1268
+ registerFilter(column, "date-gte");
1269
+ return () => unregisterFilter(column);
1270
+ }, [
1271
+ column,
1272
+ registerFilter,
1273
+ unregisterFilter
1274
+ ]);
1275
+ const dateRange = useMemo(() => {
1276
+ const date = rawValue ? new Date(rawValue) : void 0;
1277
+ return {
1278
+ from: date,
1279
+ to: date
1280
+ };
1281
+ }, [rawValue]);
1282
+ return /* @__PURE__ */ jsx("div", {
1283
+ "data-slot": "dt-filter",
1284
+ children: /* @__PURE__ */ jsx(CalendarDatePicker, {
1285
+ date: dateRange,
1286
+ numberOfMonths: 1,
1287
+ closeOnSelect: true,
1288
+ placeholder: label,
1289
+ triggerClassName: className,
1290
+ variant: "outline",
1291
+ disableFuture,
1292
+ disablePast,
1293
+ minDate,
1294
+ maxDate,
1295
+ popoverClassName: datePickerPopoverClassName,
1296
+ onDateSelect: (range) => {
1297
+ if (range?.from) setFilter(column, range.from.toISOString());
1298
+ else clearFilter(column);
1299
+ }
1300
+ })
1301
+ });
1302
+ }
1303
+
1304
+ //#endregion
1305
+ //#region src/components/features/data-table/filters/select-filter.tsx
1306
+ function SelectFilter({ column, label, options, placeholder, searchable = true, className, selectPopoverClassName }) {
1307
+ const { filters, setFilter, clearFilter, registerFilter, unregisterFilter } = useDataTableFilters();
1308
+ const [open, setOpen] = useState(false);
1309
+ const value = filters[column];
1310
+ useEffect(() => {
1311
+ registerFilter(column, "select");
1312
+ return () => unregisterFilter(column);
1313
+ }, [
1314
+ column,
1315
+ registerFilter,
1316
+ unregisterFilter
1317
+ ]);
1318
+ const selectedOption = options.find((o) => o.value === value);
1319
+ return /* @__PURE__ */ jsxs(Popover, {
1320
+ open,
1321
+ onOpenChange: setOpen,
1322
+ children: [/* @__PURE__ */ jsx(PopoverTrigger, {
1323
+ asChild: true,
1324
+ children: /* @__PURE__ */ jsxs(Button, {
1325
+ theme: "outline",
1326
+ role: "combobox",
1327
+ "aria-expanded": open,
1328
+ className: cn("justify-between", className),
1329
+ "data-slot": "dt-filter",
1330
+ "data-testid": "dt-filter-trigger",
1331
+ children: [/* @__PURE__ */ jsx("span", {
1332
+ className: "truncate",
1333
+ children: selectedOption ? selectedOption.label : placeholder ?? label
1334
+ }), /* @__PURE__ */ jsxs("div", {
1335
+ className: "ml-2 flex items-center gap-1",
1336
+ children: [value && /* @__PURE__ */ jsx("span", {
1337
+ role: "button",
1338
+ "aria-label": `Clear ${label} filter`,
1339
+ className: "rounded-sm opacity-70 hover:opacity-100",
1340
+ onClick: (e) => {
1341
+ e.stopPropagation();
1342
+ clearFilter(column);
1343
+ },
1344
+ onKeyDown: (e) => {
1345
+ if (e.key === "Enter" || e.key === " ") {
1346
+ e.preventDefault();
1347
+ e.stopPropagation();
1348
+ clearFilter(column);
1349
+ }
1350
+ },
1351
+ tabIndex: 0,
1352
+ children: /* @__PURE__ */ jsx(X, { className: "size-3" })
1353
+ }), /* @__PURE__ */ jsx(ChevronDown, { className: "size-4 opacity-50" })]
1354
+ })]
1355
+ })
1356
+ }), /* @__PURE__ */ jsx(PopoverContent, {
1357
+ className: cn("popover-content-width-full p-0", selectPopoverClassName),
1358
+ align: "start",
1359
+ children: /* @__PURE__ */ jsxs(Command, { children: [searchable && /* @__PURE__ */ jsx(CommandInput, { placeholder: `Search ${label.toLowerCase()}...` }), /* @__PURE__ */ jsxs(CommandList, { children: [/* @__PURE__ */ jsx(CommandEmpty, { children: "No results found." }), /* @__PURE__ */ jsx(CommandGroup, { children: options.map((option) => /* @__PURE__ */ jsxs(CommandItem, {
1360
+ value: option.value,
1361
+ onSelect: () => {
1362
+ setFilter(column, option.value);
1363
+ setOpen(false);
1364
+ },
1365
+ children: [/* @__PURE__ */ jsx(Check, { className: cn("mr-2 size-4", value === option.value ? "opacity-100" : "opacity-0") }), option.label]
1366
+ }, option.value)) })] })] })
1367
+ })]
1368
+ });
1369
+ }
1370
+
1371
+ //#endregion
1372
+ //#region src/components/features/data-table/data-table.tsx
1373
+ const DataTable = {
1374
+ Client: ClientProvider,
1375
+ Server: ServerProvider,
1376
+ ActiveFilters: DataTableActiveFilters,
1377
+ Content: DataTableContent,
1378
+ InlineContent: DataTableInlineContent,
1379
+ ColumnHeader: DataTableColumnHeader,
1380
+ Pagination: DataTablePagination,
1381
+ Search: DataTableSearch,
1382
+ RowActions: DataTableRowActions,
1383
+ BulkActions: DataTableBulkActions,
1384
+ Loading: DataTableLoading,
1385
+ SelectFilter,
1386
+ CheckboxFilter,
1387
+ DatePickerFilter
1388
+ };
1389
+
1390
+ //#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 };