@datum-cloud/datum-ui 0.3.0-alpha.ffa8392 → 0.3.2-dev.5bd364b
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/app-navigation/index.mjs +12 -0
- package/dist/app-navigation-CCvjPijd.mjs +416 -0
- package/dist/autocomplete/index.mjs +4 -4
- package/dist/{autocomplete-e33EmvBu.mjs → autocomplete-DcKO7pj5.mjs} +3 -3
- package/dist/avatar-stack/index.mjs +2 -2
- package/dist/{avatar-stack-Ci0cnjxv.mjs → avatar-stack-B21McFeb.mjs} +1 -1
- package/dist/{calendar-date-picker-BBAg78Lg.mjs → calendar-date-picker-Bw6Mrr-P.mjs} +4 -3
- package/dist/checkbox/index.mjs +1 -2
- package/dist/checkbox-LG1OKTpG.mjs +34 -0
- package/dist/collapsible/index.mjs +1 -1
- package/dist/command/index.mjs +2 -2
- package/dist/{command-DQlO6uTL.mjs → command-s0Yv3abE.mjs} +1 -1
- package/dist/components/base/index.d.ts +1 -0
- package/dist/components/base/index.d.ts.map +1 -1
- package/dist/components/base/sidebar/index.d.ts +2 -0
- package/dist/components/base/sidebar/index.d.ts.map +1 -0
- package/dist/components/{features → base}/sidebar/sidebar.d.ts +1 -1
- package/dist/components/base/sidebar/sidebar.d.ts.map +1 -0
- package/dist/components/base/skeleton/index.d.ts +1 -1
- package/dist/components/base/skeleton/index.d.ts.map +1 -1
- package/dist/components/base/skeleton/skeleton.d.ts +22 -0
- package/dist/components/base/skeleton/skeleton.d.ts.map +1 -0
- package/dist/components/base/typography/typography.d.ts +2 -2
- package/dist/components/features/app-navigation/app-navigation.d.ts +14 -0
- package/dist/components/features/app-navigation/app-navigation.d.ts.map +1 -0
- package/dist/components/features/app-navigation/index.d.ts +4 -0
- package/dist/components/features/app-navigation/index.d.ts.map +1 -0
- package/dist/components/features/{sidebar/nav-main.d.ts → app-navigation/nav-menu.d.ts} +3 -3
- package/dist/components/features/app-navigation/nav-menu.d.ts.map +1 -0
- package/dist/components/features/calendar-date-picker/calendar-date-picker.d.ts +2 -1
- package/dist/components/features/calendar-date-picker/calendar-date-picker.d.ts.map +1 -1
- package/dist/components/features/data-table/components/active-filters.d.ts +5 -0
- package/dist/components/features/data-table/components/active-filters.d.ts.map +1 -0
- package/dist/components/features/data-table/components/column-header.d.ts.map +1 -1
- package/dist/components/features/data-table/components/content.d.ts.map +1 -1
- package/dist/components/features/data-table/components/loading.d.ts.map +1 -1
- package/dist/components/features/data-table/components/pagination.d.ts.map +1 -1
- package/dist/components/features/data-table/components/search.d.ts +1 -1
- package/dist/components/features/data-table/components/search.d.ts.map +1 -1
- package/dist/components/features/data-table/core/client-provider.d.ts +5 -7
- package/dist/components/features/data-table/core/client-provider.d.ts.map +1 -1
- package/dist/components/features/data-table/core/data-table-context.d.ts +23 -0
- package/dist/components/features/data-table/core/data-table-context.d.ts.map +1 -1
- package/dist/components/features/data-table/core/filter-engine.d.ts +5 -0
- package/dist/components/features/data-table/core/filter-engine.d.ts.map +1 -1
- package/dist/components/features/data-table/core/server-provider.d.ts +4 -7
- package/dist/components/features/data-table/core/server-provider.d.ts.map +1 -1
- package/dist/components/features/data-table/core/store.d.ts.map +1 -1
- package/dist/components/features/data-table/data-table.d.ts +1 -0
- package/dist/components/features/data-table/data-table.d.ts.map +1 -1
- package/dist/components/features/data-table/filters/checkbox-filter.d.ts +1 -1
- package/dist/components/features/data-table/filters/checkbox-filter.d.ts.map +1 -1
- package/dist/components/features/data-table/filters/date-picker-filter.d.ts +1 -1
- package/dist/components/features/data-table/filters/date-picker-filter.d.ts.map +1 -1
- package/dist/components/features/data-table/filters/select-filter.d.ts +1 -1
- package/dist/components/features/data-table/filters/select-filter.d.ts.map +1 -1
- package/dist/components/features/data-table/hooks/index.d.ts +1 -1
- package/dist/components/features/data-table/hooks/index.d.ts.map +1 -1
- package/dist/components/features/data-table/hooks/use-data-table-client.d.ts +11 -10
- package/dist/components/features/data-table/hooks/use-data-table-client.d.ts.map +1 -1
- package/dist/components/features/data-table/hooks/use-data-table-server.d.ts +12 -17
- package/dist/components/features/data-table/hooks/use-data-table-server.d.ts.map +1 -1
- package/dist/components/features/data-table/hooks/use-selectors.d.ts +4 -33
- package/dist/components/features/data-table/hooks/use-selectors.d.ts.map +1 -1
- package/dist/components/features/data-table/index.d.ts +2 -4
- package/dist/components/features/data-table/index.d.ts.map +1 -1
- package/dist/components/features/data-table/types.d.ts +45 -31
- package/dist/components/features/data-table/types.d.ts.map +1 -1
- package/dist/components/features/index.d.ts +1 -1
- package/dist/components/features/index.d.ts.map +1 -1
- package/dist/data-table/index.mjs +659 -414
- package/dist/date-picker/index.mjs +5 -5
- package/dist/dialog/index.mjs +2 -2
- package/dist/{dialog-B2EZJW-q.mjs → dialog-bnMMf9GD.mjs} +2 -2
- package/dist/dropdown/index.mjs +1 -1
- package/dist/dropzone/index.mjs +1 -1
- package/dist/empty-content/index.mjs +1 -1
- package/dist/form/index.mjs +12 -12
- package/dist/grid/index.mjs +1 -1
- package/dist/hooks/index.mjs +1 -1
- package/dist/hover-card/index.mjs +1 -1
- package/dist/icons/index.mjs +1 -1
- package/dist/index.mjs +49 -49
- package/dist/input/index.mjs +2 -2
- package/dist/{input-D241oNEm.mjs → input-fzXBheCN.mjs} +1 -1
- package/dist/input-group/index.mjs +3 -3
- package/dist/{input-group-uobp64zr.mjs → input-group-CPaFSTEV.mjs} +2 -2
- package/dist/input-number/index.mjs +2 -2
- package/dist/{input-number-CEMgBk8-.mjs → input-number-D1HCcTXO.mjs} +1 -1
- package/dist/input-with-addons/index.mjs +1 -1
- package/dist/label/index.mjs +1 -2
- package/dist/{label-byipFGok.mjs → label-_ste_Re3.mjs} +12 -1
- package/dist/loader-overlay/index.mjs +1 -1
- package/dist/map/index.mjs +8 -8
- package/dist/{map-DupFPkJT.mjs → map-2RG9pYZR.mjs} +4 -4
- package/dist/more-actions/index.mjs +2 -2
- package/dist/{more-actions-D6OyqZQS.mjs → more-actions-BODYgG1C.mjs} +2 -2
- package/dist/page-title/index.mjs +1 -1
- package/dist/popover/index.mjs +1 -1
- package/dist/radio-group/index.mjs +1 -1
- package/dist/select/index.mjs +1 -1
- package/dist/{select-BznmyqBr.mjs → select-CwVIFWFO.mjs} +1 -1
- package/dist/sheet/index.mjs +2 -2
- package/dist/{sheet-Bmayi68h.mjs → sheet-mx5XjyEY.mjs} +2 -2
- package/dist/sidebar/index.mjs +6 -7
- package/dist/{sidebar-D2zE7rPy.mjs → sidebar-BW76ss_f.mjs} +8 -417
- package/dist/skeleton/index.mjs +2 -1
- package/dist/skeleton-DZ31pU4B.mjs +28 -0
- package/dist/spinner/index.mjs +1 -1
- package/dist/stepper/index.mjs +1 -1
- package/dist/styles/root.css +3 -0
- package/dist/switch/index.mjs +1 -1
- package/dist/table/index.mjs +1 -1
- package/dist/tabs/index.mjs +1 -1
- package/dist/tag-input/index.mjs +2 -2
- package/dist/{tag-input-BI8IRBDH.mjs → tag-input-DorFQ9bA.mjs} +1 -1
- package/dist/task-queue/index.mjs +4 -4
- package/dist/{task-queue-dropdown-D6k067_W.mjs → task-queue-dropdown-DtS0IKci.mjs} +4 -4
- package/dist/textarea/index.mjs +2 -2
- package/dist/{textarea-BZ85VFsJ.mjs → textarea-KZUKGHlO.mjs} +1 -1
- package/dist/{to-api-format-CXQ7knV4.mjs → to-api-format-CzPt5UAX.mjs} +3 -3
- package/dist/toast/index.mjs +1 -1
- package/dist/tooltip/index.mjs +1 -1
- package/dist/{use-copy-to-clipboard-CC2hhyYI.mjs → use-copy-to-clipboard-C9cT2Qb-.mjs} +1 -1
- package/dist/{use-stepper-CU75TdjZ.mjs → use-stepper-DJd8o9dV.mjs} +14 -14
- package/dist/{use-toast-BLBGnOC3.mjs → use-toast-DBmysDS6.mjs} +1 -1
- package/package.json +85 -79
- package/dist/checkbox-DB5_3E_l.mjs +0 -22
- package/dist/checkbox-DMC1Mhaw.mjs +0 -17
- package/dist/components/features/data-table/hooks/use-data-table-context.d.ts +0 -2
- package/dist/components/features/data-table/hooks/use-data-table-context.d.ts.map +0 -1
- package/dist/components/features/sidebar/app-sidebar.d.ts +0 -14
- package/dist/components/features/sidebar/app-sidebar.d.ts.map +0 -1
- package/dist/components/features/sidebar/index.d.ts +0 -4
- package/dist/components/features/sidebar/index.d.ts.map +0 -1
- package/dist/components/features/sidebar/nav-main.d.ts.map +0 -1
- package/dist/components/features/sidebar/sidebar.d.ts.map +0 -1
- package/dist/label-ClzLBWRT.mjs +0 -16
- /package/dist/{close.icon-D2r5q3bj.mjs → close.icon-CMNMoXM_.mjs} +0 -0
- /package/dist/{col-Cg_2sTDA.mjs → col-DCneNxQj.mjs} +0 -0
- /package/dist/{collapsible-Dw71o2um.mjs → collapsible-Bt9UYfv3.mjs} +0 -0
- /package/dist/{dialog-Bm4trnic.mjs → dialog-DXBaT9gA.mjs} +0 -0
- /package/dist/{dropdown-DLZXinlT.mjs → dropdown-Dgm_b6Mm.mjs} +0 -0
- /package/dist/{dropdown-menu-Xahj42Gr.mjs → dropdown-menu-DAFyO-qD.mjs} +0 -0
- /package/dist/{dropzone-CGyjGnER.mjs → dropzone-DR6O9OdU.mjs} +0 -0
- /package/dist/{empty-content-ByvwjHUs.mjs → empty-content-Dm7_5jO9.mjs} +0 -0
- /package/dist/{hover-card-BNrHtWy6.mjs → hover-card-CUPfFUqE.mjs} +0 -0
- /package/dist/{input-C-ZmsHkk.mjs → input-DuyjEKEW.mjs} +0 -0
- /package/dist/{input-with-addons-DzuyGa6G.mjs → input-with-addons-DN9LGwUU.mjs} +0 -0
- /package/dist/{loader-overlay-CbxcjyHV.mjs → loader-overlay-CpA0zV8D.mjs} +0 -0
- /package/dist/{page-title-CrYQ091u.mjs → page-title-D62FV6vD.mjs} +0 -0
- /package/dist/{popover-CYzXdp9q.mjs → popover-Ds9624qY.mjs} +0 -0
- /package/dist/{radio-group-WZCIDQCH.mjs → radio-group-B9Hm77LQ.mjs} +0 -0
- /package/dist/{sheet-b9V9soz8.mjs → sheet-Cemwh78x.mjs} +0 -0
- /package/dist/{skeleton-D3qW_KvG.mjs → skeleton-CkE23wsL.mjs} +0 -0
- /package/dist/{spinner-CKTGKv5n.mjs → spinner-earfjpJs.mjs} +0 -0
- /package/dist/{stepper-B07hPGG7.mjs → stepper-CZeks9Ex.mjs} +0 -0
- /package/dist/{switch-CujyyOi6.mjs → switch-Cn9IM2gC.mjs} +0 -0
- /package/dist/{table-fZEvpdD-.mjs → table-Dpzh0VPK.mjs} +0 -0
- /package/dist/{tabs-B7cW59gB.mjs → tabs-OYVCDOif.mjs} +0 -0
- /package/dist/{textarea-BSkDKiej.mjs → textarea-QYRcDEpK.mjs} +0 -0
- /package/dist/{tooltip-CbCWKEzu.mjs → tooltip-U3XxlW4l.mjs} +0 -0
|
@@ -1,20 +1,24 @@
|
|
|
1
|
-
import { t as cn } from "../
|
|
2
|
-
import { t as
|
|
3
|
-
import
|
|
4
|
-
import "../
|
|
5
|
-
import
|
|
6
|
-
import { t as
|
|
7
|
-
import
|
|
8
|
-
import { i as
|
|
9
|
-
import
|
|
10
|
-
import {
|
|
11
|
-
import { t as
|
|
12
|
-
import {
|
|
13
|
-
import { t as
|
|
14
|
-
import {
|
|
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 "../skeleton-CkE23wsL.mjs";
|
|
16
|
+
import { t as Skeleton } from "../skeleton-DZ31pU4B.mjs";
|
|
17
|
+
import { c as TableRow, i as TableCell, n as TableBody, o as TableHead, s as TableHeader, t as Table } from "../table-Dpzh0VPK.mjs";
|
|
18
|
+
import { t as CalendarDatePicker } from "../calendar-date-picker-Bw6Mrr-P.mjs";
|
|
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
|
-
|
|
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
|
|
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
|
-
|
|
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 =
|
|
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
|
|
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
|
|
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
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
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 =
|
|
497
|
-
return
|
|
498
|
-
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
|
-
}
|
|
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 =
|
|
506
|
-
const
|
|
507
|
-
|
|
508
|
-
|
|
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
|
-
|
|
528
|
-
|
|
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
|
-
}
|
|
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
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
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
|
-
|
|
750
|
-
|
|
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("
|
|
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
|
-
|
|
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
|
-
|
|
857
|
-
size: "
|
|
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
|
-
|
|
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
|
-
|
|
897
|
-
size: "
|
|
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
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
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
|
-
|
|
963
|
-
|
|
964
|
-
|
|
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(
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
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
|
|
1287
|
+
//#region src/components/features/data-table/hooks/use-data-table-server.ts
|
|
979
1288
|
/**
|
|
980
|
-
*
|
|
981
|
-
*
|
|
982
|
-
*
|
|
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
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
}
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
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
|
-
|
|
1008
|
-
|
|
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
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1362
|
+
sorting,
|
|
1363
|
+
filters,
|
|
1364
|
+
search,
|
|
1365
|
+
pageSize,
|
|
1366
|
+
pageIndex,
|
|
1367
|
+
store
|
|
1013
1368
|
]);
|
|
1014
|
-
const
|
|
1015
|
-
const
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
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
|
-
|
|
1034
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1083
|
-
size: "
|
|
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
|
-
|
|
1655
|
+
theme: "outline",
|
|
1174
1656
|
role: "combobox",
|
|
1175
1657
|
"aria-expanded": open,
|
|
1176
|
-
|
|
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
|
-
|
|
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 };
|