@datum-cloud/datum-ui 0.3.0-alpha.3670fb2 → 0.3.0-alpha.919cfab
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/{calendar-date-picker-mlbzp3xR.mjs → calendar-date-picker-BMzLl1Yn.mjs} +2 -1
- 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 +1 -1
- package/dist/components/features/data-table/components/active-filters.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/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 +6 -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 +14 -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 +6 -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 -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 +2 -15
- 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 +2 -25
- package/dist/components/features/data-table/hooks/use-data-table-server.d.ts.map +1 -1
- package/dist/components/features/data-table/hooks/use-is-client.d.ts +8 -0
- package/dist/components/features/data-table/hooks/use-is-client.d.ts.map +1 -0
- package/dist/components/features/data-table/hooks/use-selectors.d.ts +4 -35
- 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 +23 -30
- package/dist/components/features/data-table/types.d.ts.map +1 -1
- package/dist/data-table/index.mjs +383 -358
- package/dist/date-picker/index.mjs +1 -1
- package/dist/index.mjs +1 -1
- package/package.json +55 -1
- 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
|
@@ -14,9 +14,9 @@ import { i as PopoverTrigger, r as PopoverContent, t as Popover } from "../popov
|
|
|
14
14
|
import { i as SelectItem, l as SelectTrigger, n as SelectContent, t as Select, u as SelectValue } from "../select-CwVIFWFO.mjs";
|
|
15
15
|
import { t as Skeleton } from "../skeleton-Cs6Q5GQc.mjs";
|
|
16
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-
|
|
17
|
+
import { t as CalendarDatePicker } from "../calendar-date-picker-BMzLl1Yn.mjs";
|
|
18
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";
|
|
19
|
+
import { createContext, memo, use, useCallback, useContext, useEffect, useId, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
20
20
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
21
21
|
import { parseAsInteger, parseAsString, useQueryStates } from "nuqs";
|
|
22
22
|
import { flexRender, getCoreRowModel, getPaginationRowModel, getSortedRowModel, useReactTable } from "@tanstack/react-table";
|
|
@@ -157,6 +157,16 @@ function withSelectionColumn(columns, options = {}) {
|
|
|
157
157
|
|
|
158
158
|
//#endregion
|
|
159
159
|
//#region src/components/features/data-table/core/filter-engine.ts
|
|
160
|
+
/**
|
|
161
|
+
* Resolve a dot-path on an object (e.g. "status.registrationApproval").
|
|
162
|
+
* Falls back to a flat key lookup when the path has no dots.
|
|
163
|
+
*/
|
|
164
|
+
function resolvePath(obj, path) {
|
|
165
|
+
if (obj == null) return void 0;
|
|
166
|
+
const record = obj;
|
|
167
|
+
if (!path.includes(".")) return record[path];
|
|
168
|
+
return path.split(".").reduce((acc, key) => acc != null ? acc[key] : void 0, record);
|
|
169
|
+
}
|
|
160
170
|
const FILTER_STRATEGIES = {
|
|
161
171
|
"checkbox": (cellValue, filterValue) => {
|
|
162
172
|
if (filterValue == null) return true;
|
|
@@ -200,14 +210,13 @@ function applyFilters(data, filters, search, registeredFilters, customFilterFns,
|
|
|
200
210
|
console.warn(`[DataTable] No filter strategy registered for column "${column}". Filter ignored.`);
|
|
201
211
|
continue;
|
|
202
212
|
}
|
|
203
|
-
|
|
204
|
-
if (!fn(cellValue, value)) return false;
|
|
213
|
+
if (!fn(resolvePath(row, column), value)) return false;
|
|
205
214
|
}
|
|
206
215
|
if (hasSearch) {
|
|
207
216
|
const query = search.toLowerCase();
|
|
208
217
|
if (searchConfig.searchFn) return searchConfig.searchFn(row, search);
|
|
209
218
|
if (searchConfig.searchableColumns && searchConfig.searchableColumns.length > 0) return searchConfig.searchableColumns.some((col) => {
|
|
210
|
-
const cellValue = row
|
|
219
|
+
const cellValue = resolvePath(row, col);
|
|
211
220
|
return cellValue != null && String(cellValue).toLowerCase().includes(query);
|
|
212
221
|
});
|
|
213
222
|
return Object.values(row).some((val) => {
|
|
@@ -243,7 +252,8 @@ function createDataTableStore(options) {
|
|
|
243
252
|
mode: options.mode,
|
|
244
253
|
isLoading: false,
|
|
245
254
|
error: null,
|
|
246
|
-
inlineContents: []
|
|
255
|
+
inlineContents: [],
|
|
256
|
+
_version: 0
|
|
247
257
|
};
|
|
248
258
|
if (options.defaultFilters && Object.keys(options.defaultFilters).length > 0) state = {
|
|
249
259
|
...state,
|
|
@@ -253,7 +263,10 @@ function createDataTableStore(options) {
|
|
|
253
263
|
for (const listener of listeners) listener();
|
|
254
264
|
}
|
|
255
265
|
function setState(next) {
|
|
256
|
-
state =
|
|
266
|
+
state = {
|
|
267
|
+
...next,
|
|
268
|
+
_version: state._version + 1
|
|
269
|
+
};
|
|
257
270
|
notify();
|
|
258
271
|
}
|
|
259
272
|
return {
|
|
@@ -442,6 +455,22 @@ function createDataTableStore(options) {
|
|
|
442
455
|
//#region src/components/features/data-table/core/data-table-context.tsx
|
|
443
456
|
const DataTableStoreContext = createContext(null);
|
|
444
457
|
const TableInstanceContext = createContext(null);
|
|
458
|
+
/**
|
|
459
|
+
* Monotonic counter that increments on every store mutation.
|
|
460
|
+
* Table-dependent hooks consume this context to force re-renders
|
|
461
|
+
* through React's {children} composition boundary, since the
|
|
462
|
+
* mutable table singleton (stable ref) cannot trigger context updates.
|
|
463
|
+
*/
|
|
464
|
+
const DataTableRenderKeyContext = createContext(0);
|
|
465
|
+
/**
|
|
466
|
+
* Forces a re-render when the store changes. Used by table-dependent hooks.
|
|
467
|
+
* Uses useContext (not use()) because use() does not reliably register
|
|
468
|
+
* context subscriptions in SSR/hydration scenarios (React Router SSR),
|
|
469
|
+
* preventing re-renders when the context value changes.
|
|
470
|
+
*/
|
|
471
|
+
function useRenderKey() {
|
|
472
|
+
return useContext(DataTableRenderKeyContext);
|
|
473
|
+
}
|
|
445
474
|
const DataTableContext = createContext(null);
|
|
446
475
|
function useDataTableStore() {
|
|
447
476
|
const store = use(DataTableStoreContext);
|
|
@@ -482,7 +511,7 @@ function useSliceSelector(selector) {
|
|
|
482
511
|
cachedRef.current = next;
|
|
483
512
|
return next;
|
|
484
513
|
}, [store, selector]);
|
|
485
|
-
return useSyncExternalStore(store.subscribe, getSnapshot);
|
|
514
|
+
return useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);
|
|
486
515
|
}
|
|
487
516
|
function useDataTableFilters() {
|
|
488
517
|
const store = useDataTableStore();
|
|
@@ -511,20 +540,23 @@ function useDataTableSorting() {
|
|
|
511
540
|
}), [store]));
|
|
512
541
|
}
|
|
513
542
|
function useDataTableSelection() {
|
|
543
|
+
useRenderKey();
|
|
514
544
|
const store = useDataTableStore();
|
|
515
545
|
const table = useTableInstance();
|
|
516
|
-
return
|
|
517
|
-
rowSelection:
|
|
546
|
+
return {
|
|
547
|
+
rowSelection: store.getSnapshot().rowSelection,
|
|
518
548
|
setRowSelection: store.setRowSelection,
|
|
519
549
|
selectedRows: table.getFilteredSelectedRowModel().rows.map((r) => r.original)
|
|
520
|
-
}
|
|
550
|
+
};
|
|
521
551
|
}
|
|
522
552
|
function useDataTablePagination() {
|
|
553
|
+
useRenderKey();
|
|
523
554
|
const store = useDataTableStore();
|
|
524
555
|
const table = useTableInstance();
|
|
556
|
+
const state = store.getSnapshot();
|
|
525
557
|
const nextPage = useCallback(() => table.nextPage(), [table]);
|
|
526
558
|
const prevPage = useCallback(() => table.previousPage(), [table]);
|
|
527
|
-
return
|
|
559
|
+
return {
|
|
528
560
|
canNextPage: table.getCanNextPage(),
|
|
529
561
|
canPrevPage: table.getCanPreviousPage(),
|
|
530
562
|
nextPage,
|
|
@@ -535,20 +567,16 @@ function useDataTablePagination() {
|
|
|
535
567
|
pageSize: state.pageSize,
|
|
536
568
|
setPageSize: store.setPageSize,
|
|
537
569
|
totalRows: state.filteredData.length
|
|
538
|
-
}
|
|
539
|
-
store,
|
|
540
|
-
table,
|
|
541
|
-
nextPage,
|
|
542
|
-
prevPage
|
|
543
|
-
]));
|
|
570
|
+
};
|
|
544
571
|
}
|
|
545
572
|
function useDataTableRows() {
|
|
573
|
+
useRenderKey();
|
|
546
574
|
const table = useTableInstance();
|
|
547
|
-
return
|
|
575
|
+
return {
|
|
548
576
|
rows: table.getRowModel().rows,
|
|
549
577
|
headerGroups: table.getHeaderGroups(),
|
|
550
578
|
totalColumns: table.getAllColumns().length
|
|
551
|
-
}
|
|
579
|
+
};
|
|
552
580
|
}
|
|
553
581
|
function useDataTableLoading() {
|
|
554
582
|
return useSliceSelector(useCallback((state) => ({
|
|
@@ -564,41 +592,6 @@ function useDataTableInlineContents() {
|
|
|
564
592
|
unregisterInlineContent: store.unregisterInlineContent
|
|
565
593
|
}), [store]));
|
|
566
594
|
}
|
|
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
595
|
|
|
603
596
|
//#endregion
|
|
604
597
|
//#region src/components/features/data-table/components/active-filters.tsx
|
|
@@ -621,14 +614,15 @@ function FilterGroup({ label, children, className }) {
|
|
|
621
614
|
});
|
|
622
615
|
}
|
|
623
616
|
const EMPTY_LABELS = {};
|
|
624
|
-
function ActiveFiltersInner({ label = "Selected Filters", filterLabels = EMPTY_LABELS, formatFilterValue: formatter, clearAll = "icon", clearAllLabel = "Clear all", className, groupClassName, badgeClassName }) {
|
|
617
|
+
function ActiveFiltersInner({ label = "Selected Filters", excludeFilters, filterLabels = EMPTY_LABELS, formatFilterValue: formatter, clearAll = "icon", clearAllLabel = "Clear all", className, groupClassName, badgeClassName }) {
|
|
625
618
|
const { filters, setFilter, clearFilter, clearAllFilters } = useDataTableFilters();
|
|
626
619
|
const { search, clearSearch } = useDataTableSearch();
|
|
627
|
-
const
|
|
628
|
-
const
|
|
620
|
+
const excludeSet = useMemo(() => new Set(excludeFilters ?? []), [excludeFilters]);
|
|
621
|
+
const activeFilterEntries = Object.entries(filters).filter(([key, value]) => !excludeSet.has(key) && value != null && value !== "" && !(Array.isArray(value) && value.length === 0));
|
|
622
|
+
const showSearch = search.length > 0 && !excludeSet.has("search");
|
|
629
623
|
const hasFilters = activeFilterEntries.length > 0;
|
|
630
|
-
if (!
|
|
631
|
-
const totalGroups = activeFilterEntries.length + (
|
|
624
|
+
if (!showSearch && !hasFilters) return null;
|
|
625
|
+
const totalGroups = activeFilterEntries.length + (showSearch ? 1 : 0);
|
|
632
626
|
const removeArrayItem = (column, items, item) => {
|
|
633
627
|
const remaining = items.filter((v) => v !== item);
|
|
634
628
|
if (remaining.length > 0) setFilter(column, remaining);
|
|
@@ -636,7 +630,7 @@ function ActiveFiltersInner({ label = "Selected Filters", filterLabels = EMPTY_L
|
|
|
636
630
|
};
|
|
637
631
|
const handleClearAll = () => {
|
|
638
632
|
clearAllFilters();
|
|
639
|
-
if (
|
|
633
|
+
if (search.length > 0) clearSearch();
|
|
640
634
|
};
|
|
641
635
|
const badgeCn = cn("flex items-center gap-1.5 px-2 py-0.5 text-xs", badgeClassName);
|
|
642
636
|
return /* @__PURE__ */ jsxs("div", {
|
|
@@ -649,7 +643,7 @@ function ActiveFiltersInner({ label = "Selected Filters", filterLabels = EMPTY_L
|
|
|
649
643
|
"data-slot": "dt-active-filters-label",
|
|
650
644
|
children: label
|
|
651
645
|
}),
|
|
652
|
-
|
|
646
|
+
showSearch && /* @__PURE__ */ jsx(FilterGroup, {
|
|
653
647
|
label: "Search",
|
|
654
648
|
className: groupClassName,
|
|
655
649
|
children: /* @__PURE__ */ jsxs(Badge, {
|
|
@@ -914,19 +908,8 @@ function DataTableLoading({ rows = DEFAULT_LOADING_ROWS, columns = 4, className
|
|
|
914
908
|
return /* @__PURE__ */ jsx("div", {
|
|
915
909
|
className,
|
|
916
910
|
"data-slot": "dt-loading",
|
|
917
|
-
|
|
918
|
-
|
|
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
|
-
})
|
|
911
|
+
style: { overflowX: "auto" },
|
|
912
|
+
children: /* @__PURE__ */ jsxs(Table, { children: [/* @__PURE__ */ jsx(TableHeader, { children: /* @__PURE__ */ jsx(TableRow, { children: Array.from({ length: columns }, (_, i) => /* @__PURE__ */ jsx(TableHead, { children: /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-24" }) }, i)) }) }), /* @__PURE__ */ jsx(TableBody, { children: Array.from({ length: rows }, (_, rowIndex) => /* @__PURE__ */ jsx(TableRow, { children: Array.from({ length: columns }, (_, colIndex) => /* @__PURE__ */ jsx(TableCell, { children: /* @__PURE__ */ jsx(Skeleton, { className: "h-4 w-full" }) }, colIndex)) }, rowIndex)) })] })
|
|
930
913
|
});
|
|
931
914
|
}
|
|
932
915
|
|
|
@@ -1025,9 +1008,9 @@ function DataTablePagination({ pageSizes = DEFAULT_PAGE_SIZES, className }) {
|
|
|
1025
1008
|
return /* @__PURE__ */ jsx(Button, {
|
|
1026
1009
|
theme: isActive ? "solid" : "outline",
|
|
1027
1010
|
size: "small",
|
|
1028
|
-
className: cn("h-8 min-w-8 px-2", isActive && "font-semibold"),
|
|
1011
|
+
className: cn("h-8 min-w-8 px-2", isActive && "pointer-events-none font-semibold"),
|
|
1029
1012
|
onClick: () => setPageIndex(page - 1),
|
|
1030
|
-
disabled: isActive,
|
|
1013
|
+
"aria-disabled": isActive || void 0,
|
|
1031
1014
|
"aria-label": `Page ${page}`,
|
|
1032
1015
|
"aria-current": isActive ? "page" : void 0,
|
|
1033
1016
|
children: page
|
|
@@ -1091,7 +1074,7 @@ function DataTableRowActions({ row, actions, isLoading = false, className }) {
|
|
|
1091
1074
|
|
|
1092
1075
|
//#endregion
|
|
1093
1076
|
//#region src/components/features/data-table/components/search.tsx
|
|
1094
|
-
function DataTableSearch({ placeholder = "Search...", debounceMs = DEFAULT_DEBOUNCE_MS, className }) {
|
|
1077
|
+
function DataTableSearch({ placeholder = "Search...", debounceMs = DEFAULT_DEBOUNCE_MS, className, disabled }) {
|
|
1095
1078
|
const { search, setSearch } = useDataTableSearch();
|
|
1096
1079
|
const [inputValue, setInputValue] = useState(search);
|
|
1097
1080
|
useEffect(() => {
|
|
@@ -1113,68 +1096,345 @@ function DataTableSearch({ placeholder = "Search...", debounceMs = DEFAULT_DEBOU
|
|
|
1113
1096
|
value: inputValue,
|
|
1114
1097
|
onChange: (e) => setInputValue(e.target.value),
|
|
1115
1098
|
className,
|
|
1099
|
+
disabled,
|
|
1116
1100
|
"aria-label": placeholder,
|
|
1117
1101
|
"data-slot": "dt-search"
|
|
1118
1102
|
});
|
|
1119
1103
|
}
|
|
1120
1104
|
|
|
1121
1105
|
//#endregion
|
|
1122
|
-
//#region src/components/features/data-table/
|
|
1123
|
-
function
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1106
|
+
//#region src/components/features/data-table/hooks/use-data-table-client.ts
|
|
1107
|
+
function useDataTableClient(options) {
|
|
1108
|
+
const { data, columns, pageSize, getRowId, enableRowSelection = false, defaultSort, defaultFilters, searchableColumns, searchFn, filterFns, stateAdapter } = options;
|
|
1109
|
+
const store = useMemo(() => createDataTableStore({
|
|
1110
|
+
data,
|
|
1111
|
+
mode: "client",
|
|
1112
|
+
defaultSort,
|
|
1113
|
+
defaultFilters,
|
|
1114
|
+
pageSize,
|
|
1115
|
+
searchableColumns,
|
|
1116
|
+
searchFn,
|
|
1117
|
+
filterFns
|
|
1118
|
+
}), []);
|
|
1119
|
+
const isInitialRender = useRef(true);
|
|
1120
|
+
useEffect(() => {
|
|
1121
|
+
if (isInitialRender.current) {
|
|
1122
|
+
isInitialRender.current = false;
|
|
1123
|
+
return;
|
|
1124
|
+
}
|
|
1125
|
+
store.setData(data);
|
|
1126
|
+
}, [data, store]);
|
|
1127
|
+
const resolvedColumns = useMemo(() => enableRowSelection ? withSelectionColumn(columns, typeof enableRowSelection === "object" ? enableRowSelection : {}) : columns, [columns, enableRowSelection]);
|
|
1128
|
+
const { filteredData, sorting, rowSelection, pageIndex, pageSize: storePageSize, filters, search } = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);
|
|
1129
|
+
const table = useReactTable({
|
|
1130
|
+
data: filteredData,
|
|
1131
|
+
columns: resolvedColumns,
|
|
1132
|
+
state: {
|
|
1133
|
+
sorting,
|
|
1134
|
+
rowSelection,
|
|
1135
|
+
pagination: {
|
|
1136
|
+
pageIndex,
|
|
1137
|
+
pageSize: storePageSize
|
|
1138
|
+
}
|
|
1139
|
+
},
|
|
1140
|
+
onSortingChange: (updater) => {
|
|
1141
|
+
const next = typeof updater === "function" ? updater(sorting) : updater;
|
|
1142
|
+
store.setSorting(next);
|
|
1143
|
+
},
|
|
1144
|
+
onRowSelectionChange: (updater) => {
|
|
1145
|
+
const next = typeof updater === "function" ? updater(rowSelection) : updater;
|
|
1146
|
+
store.setRowSelection(next);
|
|
1147
|
+
},
|
|
1148
|
+
onPaginationChange: (updater) => {
|
|
1149
|
+
const next = typeof updater === "function" ? updater({
|
|
1150
|
+
pageIndex,
|
|
1151
|
+
pageSize: storePageSize
|
|
1152
|
+
}) : updater;
|
|
1153
|
+
store.setPagination(next.pageIndex, next.pageSize);
|
|
1154
|
+
},
|
|
1155
|
+
getCoreRowModel: getCoreRowModel(),
|
|
1156
|
+
getSortedRowModel: getSortedRowModel(),
|
|
1157
|
+
getPaginationRowModel: getPaginationRowModel(),
|
|
1158
|
+
getRowId,
|
|
1159
|
+
enableRowSelection: !!enableRowSelection
|
|
1133
1160
|
});
|
|
1161
|
+
const hydratedRef = useRef(false);
|
|
1162
|
+
useEffect(() => {
|
|
1163
|
+
if (stateAdapter && !hydratedRef.current) {
|
|
1164
|
+
hydratedRef.current = true;
|
|
1165
|
+
const persisted = stateAdapter.read();
|
|
1166
|
+
if (persisted.sorting && persisted.sorting.length > 0) store.setSorting(persisted.sorting);
|
|
1167
|
+
if (persisted.filters) {
|
|
1168
|
+
for (const [key, value] of Object.entries(persisted.filters)) if (value != null) store.setFilter(key, value);
|
|
1169
|
+
}
|
|
1170
|
+
if (persisted.search) store.setSearch(persisted.search);
|
|
1171
|
+
if (persisted.pageIndex != null && persisted.pageIndex > 0) store.setPageIndex(persisted.pageIndex);
|
|
1172
|
+
if (persisted.pageSize != null) store.setPageSize(persisted.pageSize);
|
|
1173
|
+
}
|
|
1174
|
+
}, []);
|
|
1175
|
+
const isFirstWrite = useRef(true);
|
|
1176
|
+
useEffect(() => {
|
|
1177
|
+
if (!stateAdapter) return;
|
|
1178
|
+
if (isFirstWrite.current) {
|
|
1179
|
+
isFirstWrite.current = false;
|
|
1180
|
+
return;
|
|
1181
|
+
}
|
|
1182
|
+
stateAdapter.write({
|
|
1183
|
+
sorting,
|
|
1184
|
+
filters,
|
|
1185
|
+
search,
|
|
1186
|
+
pageIndex,
|
|
1187
|
+
pageSize: storePageSize
|
|
1188
|
+
});
|
|
1189
|
+
}, [
|
|
1190
|
+
sorting,
|
|
1191
|
+
filters,
|
|
1192
|
+
search,
|
|
1193
|
+
pageIndex,
|
|
1194
|
+
storePageSize,
|
|
1195
|
+
stateAdapter
|
|
1196
|
+
]);
|
|
1197
|
+
return {
|
|
1198
|
+
store,
|
|
1199
|
+
table
|
|
1200
|
+
};
|
|
1134
1201
|
}
|
|
1135
1202
|
|
|
1136
1203
|
//#endregion
|
|
1137
|
-
//#region src/components/features/data-table/
|
|
1138
|
-
|
|
1204
|
+
//#region src/components/features/data-table/hooks/use-is-client.ts
|
|
1205
|
+
/**
|
|
1206
|
+
* Returns `false` during SSR and `true` after hydration.
|
|
1207
|
+
* Used to gate components that depend on client-only APIs
|
|
1208
|
+
* (e.g. TanStack Table's internal useSyncExternalStore call
|
|
1209
|
+
* which omits the getServerSnapshot argument).
|
|
1210
|
+
*/
|
|
1211
|
+
function useIsClient() {
|
|
1212
|
+
const [isClient, setIsClient] = useState(false);
|
|
1213
|
+
useEffect(() => setIsClient(true), []);
|
|
1214
|
+
return isClient;
|
|
1215
|
+
}
|
|
1216
|
+
|
|
1217
|
+
//#endregion
|
|
1218
|
+
//#region src/components/features/data-table/core/client-provider.tsx
|
|
1219
|
+
/**
|
|
1220
|
+
* Inner component that calls useDataTableClient.
|
|
1221
|
+
* Only rendered on the client (gated by ClientProvider).
|
|
1222
|
+
*/
|
|
1223
|
+
function ClientProviderInner({ className, children, ssrFallback: _ssrFallback, ...options }) {
|
|
1224
|
+
const { store, table } = useDataTableClient(options);
|
|
1139
1225
|
return /* @__PURE__ */ jsx(DataTableStoreContext, {
|
|
1140
1226
|
value: store,
|
|
1141
1227
|
children: /* @__PURE__ */ jsx(TableInstanceContext, {
|
|
1142
1228
|
value: table,
|
|
1143
|
-
children: /* @__PURE__ */ jsx(
|
|
1144
|
-
|
|
1145
|
-
children
|
|
1229
|
+
children: /* @__PURE__ */ jsx(DataTableRenderKeyContext, {
|
|
1230
|
+
value: store.getSnapshot()._version,
|
|
1231
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
1232
|
+
className,
|
|
1233
|
+
children
|
|
1234
|
+
})
|
|
1146
1235
|
})
|
|
1147
1236
|
})
|
|
1148
1237
|
});
|
|
1149
1238
|
}
|
|
1239
|
+
function ClientProvider(props) {
|
|
1240
|
+
if (!useIsClient()) return /* @__PURE__ */ jsx(Fragment$1, { children: props.ssrFallback === void 0 ? /* @__PURE__ */ jsx(DataTableLoading, { columns: props.columns.length }) : props.ssrFallback });
|
|
1241
|
+
return /* @__PURE__ */ jsx(ClientProviderInner, { ...props });
|
|
1242
|
+
}
|
|
1150
1243
|
|
|
1151
1244
|
//#endregion
|
|
1152
|
-
//#region src/components/features/data-table/
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
const
|
|
1156
|
-
const
|
|
1245
|
+
//#region src/components/features/data-table/hooks/use-data-table-server.ts
|
|
1246
|
+
function useDataTableServer(options) {
|
|
1247
|
+
const { columns, fetchFn, transform, limit = 20, getRowId, enableRowSelection = false, defaultSort, defaultFilters, stateAdapter } = options;
|
|
1248
|
+
const fetchRef = useRef(fetchFn);
|
|
1249
|
+
const transformRef = useRef(transform);
|
|
1157
1250
|
useEffect(() => {
|
|
1158
|
-
|
|
1159
|
-
|
|
1251
|
+
fetchRef.current = fetchFn;
|
|
1252
|
+
}, [fetchFn]);
|
|
1253
|
+
useEffect(() => {
|
|
1254
|
+
transformRef.current = transform;
|
|
1255
|
+
}, [transform]);
|
|
1256
|
+
const cursorMapRef = useRef(/* @__PURE__ */ new Map());
|
|
1257
|
+
const hasNextPageRef = useRef(false);
|
|
1258
|
+
const store = useMemo(() => createDataTableStore({
|
|
1259
|
+
data: [],
|
|
1260
|
+
mode: "server",
|
|
1261
|
+
defaultSort,
|
|
1262
|
+
defaultFilters,
|
|
1263
|
+
pageSize: limit
|
|
1264
|
+
}), []);
|
|
1265
|
+
const { sorting, filters, search, rowSelection, pageSize, pageIndex } = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);
|
|
1266
|
+
const prevQueryRef = useRef({
|
|
1267
|
+
sorting,
|
|
1268
|
+
filters,
|
|
1269
|
+
search,
|
|
1270
|
+
pageSize
|
|
1271
|
+
});
|
|
1272
|
+
useEffect(() => {
|
|
1273
|
+
const prev = prevQueryRef.current;
|
|
1274
|
+
if (prev.sorting !== sorting || prev.filters !== filters || prev.search !== search || prev.pageSize !== pageSize) {
|
|
1275
|
+
cursorMapRef.current = /* @__PURE__ */ new Map();
|
|
1276
|
+
hasNextPageRef.current = false;
|
|
1277
|
+
if (pageIndex !== 0) {
|
|
1278
|
+
prevQueryRef.current = {
|
|
1279
|
+
sorting,
|
|
1280
|
+
filters,
|
|
1281
|
+
search,
|
|
1282
|
+
pageSize
|
|
1283
|
+
};
|
|
1284
|
+
store.setPageIndex(0);
|
|
1285
|
+
return;
|
|
1286
|
+
}
|
|
1287
|
+
}
|
|
1288
|
+
prevQueryRef.current = {
|
|
1289
|
+
sorting,
|
|
1290
|
+
filters,
|
|
1291
|
+
search,
|
|
1292
|
+
pageSize
|
|
1293
|
+
};
|
|
1294
|
+
let cancelled = false;
|
|
1295
|
+
store.setLoading(true);
|
|
1296
|
+
const cursor = cursorMapRef.current.get(pageIndex);
|
|
1297
|
+
fetchRef.current({
|
|
1298
|
+
sorting,
|
|
1299
|
+
filters,
|
|
1300
|
+
search,
|
|
1301
|
+
cursor,
|
|
1302
|
+
limit: pageSize
|
|
1303
|
+
}).then((response) => {
|
|
1304
|
+
if (cancelled) return;
|
|
1305
|
+
const result = transformRef.current(response);
|
|
1306
|
+
store.setServerData(result.data);
|
|
1307
|
+
store.setError(null);
|
|
1308
|
+
if (result.cursor) cursorMapRef.current.set(pageIndex + 1, result.cursor);
|
|
1309
|
+
hasNextPageRef.current = result.hasNextPage;
|
|
1310
|
+
}).catch((error) => {
|
|
1311
|
+
if (cancelled) return;
|
|
1312
|
+
store.setServerData([]);
|
|
1313
|
+
store.setError(error instanceof Error ? error : new Error(String(error)));
|
|
1314
|
+
hasNextPageRef.current = false;
|
|
1315
|
+
}).finally(() => {
|
|
1316
|
+
if (!cancelled) store.setLoading(false);
|
|
1317
|
+
});
|
|
1318
|
+
return () => {
|
|
1319
|
+
cancelled = true;
|
|
1320
|
+
};
|
|
1160
1321
|
}, [
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1322
|
+
sorting,
|
|
1323
|
+
filters,
|
|
1324
|
+
search,
|
|
1325
|
+
pageSize,
|
|
1326
|
+
pageIndex,
|
|
1327
|
+
store
|
|
1164
1328
|
]);
|
|
1165
|
-
const
|
|
1166
|
-
const
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1329
|
+
const resolvedColumns = useMemo(() => enableRowSelection ? withSelectionColumn(columns, typeof enableRowSelection === "object" ? enableRowSelection : {}) : columns, [columns, enableRowSelection]);
|
|
1330
|
+
const table = useReactTable({
|
|
1331
|
+
data: store.getSnapshot().data,
|
|
1332
|
+
columns: resolvedColumns,
|
|
1333
|
+
state: {
|
|
1334
|
+
sorting,
|
|
1335
|
+
rowSelection,
|
|
1336
|
+
pagination: {
|
|
1337
|
+
pageIndex,
|
|
1338
|
+
pageSize
|
|
1339
|
+
}
|
|
1340
|
+
},
|
|
1341
|
+
manualPagination: true,
|
|
1342
|
+
manualSorting: true,
|
|
1343
|
+
manualFiltering: true,
|
|
1344
|
+
pageCount: hasNextPageRef.current ? pageIndex + 2 : pageIndex + 1,
|
|
1345
|
+
getCoreRowModel: getCoreRowModel(),
|
|
1346
|
+
getRowId,
|
|
1347
|
+
enableRowSelection: !!enableRowSelection,
|
|
1348
|
+
onSortingChange: (updater) => {
|
|
1349
|
+
const next = typeof updater === "function" ? updater(sorting) : updater;
|
|
1350
|
+
store.setSorting(next);
|
|
1351
|
+
},
|
|
1352
|
+
onRowSelectionChange: (updater) => {
|
|
1353
|
+
const next = typeof updater === "function" ? updater(rowSelection) : updater;
|
|
1354
|
+
store.setRowSelection(next);
|
|
1355
|
+
},
|
|
1356
|
+
onPaginationChange: (updater) => {
|
|
1357
|
+
const next = typeof updater === "function" ? updater({
|
|
1358
|
+
pageIndex,
|
|
1359
|
+
pageSize
|
|
1360
|
+
}) : updater;
|
|
1361
|
+
store.setPagination(next.pageIndex, next.pageSize);
|
|
1362
|
+
}
|
|
1363
|
+
});
|
|
1364
|
+
useEffect(() => {
|
|
1365
|
+
if (stateAdapter) stateAdapter.write({
|
|
1366
|
+
sorting,
|
|
1367
|
+
filters,
|
|
1368
|
+
search,
|
|
1369
|
+
pageSize
|
|
1370
|
+
});
|
|
1371
|
+
}, [
|
|
1372
|
+
sorting,
|
|
1373
|
+
filters,
|
|
1374
|
+
search,
|
|
1375
|
+
pageSize,
|
|
1376
|
+
stateAdapter
|
|
1377
|
+
]);
|
|
1378
|
+
return {
|
|
1379
|
+
store,
|
|
1380
|
+
table
|
|
1381
|
+
};
|
|
1382
|
+
}
|
|
1383
|
+
|
|
1384
|
+
//#endregion
|
|
1385
|
+
//#region src/components/features/data-table/core/server-provider.tsx
|
|
1386
|
+
/**
|
|
1387
|
+
* Inner component that calls useDataTableServer.
|
|
1388
|
+
* Only rendered on the client (gated by ServerProvider).
|
|
1389
|
+
*/
|
|
1390
|
+
function ServerProviderInner({ className, children, ssrFallback: _ssrFallback, ...options }) {
|
|
1391
|
+
const { store, table } = useDataTableServer(options);
|
|
1392
|
+
return /* @__PURE__ */ jsx(DataTableStoreContext, {
|
|
1393
|
+
value: store,
|
|
1394
|
+
children: /* @__PURE__ */ jsx(TableInstanceContext, {
|
|
1395
|
+
value: table,
|
|
1396
|
+
children: /* @__PURE__ */ jsx(DataTableRenderKeyContext, {
|
|
1397
|
+
value: store.getSnapshot()._version,
|
|
1398
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
1399
|
+
className,
|
|
1400
|
+
children
|
|
1401
|
+
})
|
|
1402
|
+
})
|
|
1403
|
+
})
|
|
1404
|
+
});
|
|
1405
|
+
}
|
|
1406
|
+
function ServerProvider(props) {
|
|
1407
|
+
if (!useIsClient()) return /* @__PURE__ */ jsx(Fragment$1, { children: props.ssrFallback === void 0 ? /* @__PURE__ */ jsx(DataTableLoading, { columns: props.columns.length }) : props.ssrFallback });
|
|
1408
|
+
return /* @__PURE__ */ jsx(ServerProviderInner, { ...props });
|
|
1409
|
+
}
|
|
1410
|
+
|
|
1411
|
+
//#endregion
|
|
1412
|
+
//#region src/components/features/data-table/filters/checkbox-filter.tsx
|
|
1413
|
+
const MAX_VISIBLE_BADGES = 2;
|
|
1414
|
+
function CheckboxFilter({ column, label, options, className, checkboxPopoverClassName, disabled }) {
|
|
1415
|
+
const { filters, setFilter, clearFilter, registerFilter, unregisterFilter } = useDataTableFilters();
|
|
1416
|
+
const [open, setOpen] = useState(false);
|
|
1417
|
+
useEffect(() => {
|
|
1418
|
+
registerFilter(column, "checkbox");
|
|
1419
|
+
return () => unregisterFilter(column);
|
|
1420
|
+
}, [
|
|
1421
|
+
column,
|
|
1422
|
+
registerFilter,
|
|
1423
|
+
unregisterFilter
|
|
1424
|
+
]);
|
|
1425
|
+
const selectedValues = filters[column] ?? [];
|
|
1426
|
+
const updateValues = (newValues) => {
|
|
1427
|
+
if (newValues.length > 0) setFilter(column, newValues);
|
|
1428
|
+
else clearFilter(column);
|
|
1429
|
+
};
|
|
1430
|
+
const handleToggle = (optionValue, checked) => {
|
|
1431
|
+
updateValues(checked ? [...selectedValues, optionValue] : selectedValues.filter((v) => v !== optionValue));
|
|
1432
|
+
};
|
|
1433
|
+
const removeValue = (optionValue) => {
|
|
1434
|
+
updateValues(selectedValues.filter((v) => v !== optionValue));
|
|
1435
|
+
};
|
|
1436
|
+
const visibleBadges = selectedValues.slice(0, MAX_VISIBLE_BADGES);
|
|
1437
|
+
const remainingCount = selectedValues.length - MAX_VISIBLE_BADGES;
|
|
1178
1438
|
return /* @__PURE__ */ jsxs(Popover, {
|
|
1179
1439
|
open,
|
|
1180
1440
|
onOpenChange: setOpen,
|
|
@@ -1182,6 +1442,7 @@ function CheckboxFilter({ column, label, options, className, checkboxPopoverClas
|
|
|
1182
1442
|
asChild: true,
|
|
1183
1443
|
children: /* @__PURE__ */ jsxs(Button, {
|
|
1184
1444
|
theme: "outline",
|
|
1445
|
+
disabled,
|
|
1185
1446
|
className: cn("justify-between gap-1", className),
|
|
1186
1447
|
"data-slot": "dt-filter",
|
|
1187
1448
|
"data-testid": "dt-filter-trigger",
|
|
@@ -1261,7 +1522,7 @@ function CheckboxFilter({ column, label, options, className, checkboxPopoverClas
|
|
|
1261
1522
|
|
|
1262
1523
|
//#endregion
|
|
1263
1524
|
//#region src/components/features/data-table/filters/date-picker-filter.tsx
|
|
1264
|
-
function DatePickerFilter({ column, label, className, datePickerPopoverClassName, disableFuture, disablePast, minDate, maxDate }) {
|
|
1525
|
+
function DatePickerFilter({ column, label, className, datePickerPopoverClassName, disableFuture, disablePast, minDate, maxDate, disabled }) {
|
|
1265
1526
|
const { filters, setFilter, clearFilter, registerFilter, unregisterFilter } = useDataTableFilters();
|
|
1266
1527
|
const rawValue = filters[column];
|
|
1267
1528
|
useEffect(() => {
|
|
@@ -1288,6 +1549,7 @@ function DatePickerFilter({ column, label, className, datePickerPopoverClassName
|
|
|
1288
1549
|
placeholder: label,
|
|
1289
1550
|
triggerClassName: className,
|
|
1290
1551
|
variant: "outline",
|
|
1552
|
+
disabled,
|
|
1291
1553
|
disableFuture,
|
|
1292
1554
|
disablePast,
|
|
1293
1555
|
minDate,
|
|
@@ -1303,7 +1565,7 @@ function DatePickerFilter({ column, label, className, datePickerPopoverClassName
|
|
|
1303
1565
|
|
|
1304
1566
|
//#endregion
|
|
1305
1567
|
//#region src/components/features/data-table/filters/select-filter.tsx
|
|
1306
|
-
function SelectFilter({ column, label, options, placeholder, searchable = true, className, selectPopoverClassName }) {
|
|
1568
|
+
function SelectFilter({ column, label, options, placeholder, searchable = true, className, selectPopoverClassName, disabled }) {
|
|
1307
1569
|
const { filters, setFilter, clearFilter, registerFilter, unregisterFilter } = useDataTableFilters();
|
|
1308
1570
|
const [open, setOpen] = useState(false);
|
|
1309
1571
|
const value = filters[column];
|
|
@@ -1325,6 +1587,7 @@ function SelectFilter({ column, label, options, placeholder, searchable = true,
|
|
|
1325
1587
|
theme: "outline",
|
|
1326
1588
|
role: "combobox",
|
|
1327
1589
|
"aria-expanded": open,
|
|
1590
|
+
disabled,
|
|
1328
1591
|
className: cn("justify-between", className),
|
|
1329
1592
|
"data-slot": "dt-filter",
|
|
1330
1593
|
"data-testid": "dt-filter-trigger",
|
|
@@ -1388,242 +1651,4 @@ const DataTable = {
|
|
|
1388
1651
|
};
|
|
1389
1652
|
|
|
1390
1653
|
//#endregion
|
|
1391
|
-
|
|
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 };
|
|
1654
|
+
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 };
|