@datum-cloud/datum-ui 0.3.0-alpha.1a6419a → 0.3.0-alpha.36807e9
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/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/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/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-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 +1 -3
- package/dist/components/features/data-table/index.d.ts.map +1 -1
- package/dist/components/features/data-table/types.d.ts +2 -0
- package/dist/components/features/data-table/types.d.ts.map +1 -1
- package/dist/data-table/index.mjs +356 -334
- 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
|
@@ -16,7 +16,7 @@ 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
17
|
import { t as CalendarDatePicker } from "../calendar-date-picker-mlbzp3xR.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";
|
|
@@ -243,7 +243,8 @@ function createDataTableStore(options) {
|
|
|
243
243
|
mode: options.mode,
|
|
244
244
|
isLoading: false,
|
|
245
245
|
error: null,
|
|
246
|
-
inlineContents: []
|
|
246
|
+
inlineContents: [],
|
|
247
|
+
_version: 0
|
|
247
248
|
};
|
|
248
249
|
if (options.defaultFilters && Object.keys(options.defaultFilters).length > 0) state = {
|
|
249
250
|
...state,
|
|
@@ -253,7 +254,10 @@ function createDataTableStore(options) {
|
|
|
253
254
|
for (const listener of listeners) listener();
|
|
254
255
|
}
|
|
255
256
|
function setState(next) {
|
|
256
|
-
state =
|
|
257
|
+
state = {
|
|
258
|
+
...next,
|
|
259
|
+
_version: state._version + 1
|
|
260
|
+
};
|
|
257
261
|
notify();
|
|
258
262
|
}
|
|
259
263
|
return {
|
|
@@ -442,6 +446,22 @@ function createDataTableStore(options) {
|
|
|
442
446
|
//#region src/components/features/data-table/core/data-table-context.tsx
|
|
443
447
|
const DataTableStoreContext = createContext(null);
|
|
444
448
|
const TableInstanceContext = createContext(null);
|
|
449
|
+
/**
|
|
450
|
+
* Monotonic counter that increments on every store mutation.
|
|
451
|
+
* Table-dependent hooks consume this context to force re-renders
|
|
452
|
+
* through React's {children} composition boundary, since the
|
|
453
|
+
* mutable table singleton (stable ref) cannot trigger context updates.
|
|
454
|
+
*/
|
|
455
|
+
const DataTableRenderKeyContext = createContext(0);
|
|
456
|
+
/**
|
|
457
|
+
* Forces a re-render when the store changes. Used by table-dependent hooks.
|
|
458
|
+
* Uses useContext (not use()) because use() does not reliably register
|
|
459
|
+
* context subscriptions in SSR/hydration scenarios (React Router SSR),
|
|
460
|
+
* preventing re-renders when the context value changes.
|
|
461
|
+
*/
|
|
462
|
+
function useRenderKey() {
|
|
463
|
+
return useContext(DataTableRenderKeyContext);
|
|
464
|
+
}
|
|
445
465
|
const DataTableContext = createContext(null);
|
|
446
466
|
function useDataTableStore() {
|
|
447
467
|
const store = use(DataTableStoreContext);
|
|
@@ -511,20 +531,23 @@ function useDataTableSorting() {
|
|
|
511
531
|
}), [store]));
|
|
512
532
|
}
|
|
513
533
|
function useDataTableSelection() {
|
|
534
|
+
useRenderKey();
|
|
514
535
|
const store = useDataTableStore();
|
|
515
536
|
const table = useTableInstance();
|
|
516
|
-
return
|
|
517
|
-
rowSelection:
|
|
537
|
+
return {
|
|
538
|
+
rowSelection: store.getSnapshot().rowSelection,
|
|
518
539
|
setRowSelection: store.setRowSelection,
|
|
519
540
|
selectedRows: table.getFilteredSelectedRowModel().rows.map((r) => r.original)
|
|
520
|
-
}
|
|
541
|
+
};
|
|
521
542
|
}
|
|
522
543
|
function useDataTablePagination() {
|
|
544
|
+
useRenderKey();
|
|
523
545
|
const store = useDataTableStore();
|
|
524
546
|
const table = useTableInstance();
|
|
547
|
+
const state = store.getSnapshot();
|
|
525
548
|
const nextPage = useCallback(() => table.nextPage(), [table]);
|
|
526
549
|
const prevPage = useCallback(() => table.previousPage(), [table]);
|
|
527
|
-
return
|
|
550
|
+
return {
|
|
528
551
|
canNextPage: table.getCanNextPage(),
|
|
529
552
|
canPrevPage: table.getCanPreviousPage(),
|
|
530
553
|
nextPage,
|
|
@@ -535,20 +558,16 @@ function useDataTablePagination() {
|
|
|
535
558
|
pageSize: state.pageSize,
|
|
536
559
|
setPageSize: store.setPageSize,
|
|
537
560
|
totalRows: state.filteredData.length
|
|
538
|
-
}
|
|
539
|
-
store,
|
|
540
|
-
table,
|
|
541
|
-
nextPage,
|
|
542
|
-
prevPage
|
|
543
|
-
]));
|
|
561
|
+
};
|
|
544
562
|
}
|
|
545
563
|
function useDataTableRows() {
|
|
564
|
+
useRenderKey();
|
|
546
565
|
const table = useTableInstance();
|
|
547
|
-
return
|
|
566
|
+
return {
|
|
548
567
|
rows: table.getRowModel().rows,
|
|
549
568
|
headerGroups: table.getHeaderGroups(),
|
|
550
569
|
totalColumns: table.getAllColumns().length
|
|
551
|
-
}
|
|
570
|
+
};
|
|
552
571
|
}
|
|
553
572
|
function useDataTableLoading() {
|
|
554
573
|
return useSliceSelector(useCallback((state) => ({
|
|
@@ -564,41 +583,6 @@ function useDataTableInlineContents() {
|
|
|
564
583
|
unregisterInlineContent: store.unregisterInlineContent
|
|
565
584
|
}), [store]));
|
|
566
585
|
}
|
|
567
|
-
function useDataTableContext() {
|
|
568
|
-
const store = useDataTableStore();
|
|
569
|
-
const table = useTableInstance();
|
|
570
|
-
const state = useSyncExternalStore(store.subscribe, store.getSnapshot, 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
586
|
|
|
603
587
|
//#endregion
|
|
604
588
|
//#region src/components/features/data-table/components/active-filters.tsx
|
|
@@ -1025,9 +1009,9 @@ function DataTablePagination({ pageSizes = DEFAULT_PAGE_SIZES, className }) {
|
|
|
1025
1009
|
return /* @__PURE__ */ jsx(Button, {
|
|
1026
1010
|
theme: isActive ? "solid" : "outline",
|
|
1027
1011
|
size: "small",
|
|
1028
|
-
className: cn("h-8 min-w-8 px-2", isActive && "font-semibold"),
|
|
1012
|
+
className: cn("h-8 min-w-8 px-2", isActive && "pointer-events-none font-semibold"),
|
|
1029
1013
|
onClick: () => setPageIndex(page - 1),
|
|
1030
|
-
disabled: isActive,
|
|
1014
|
+
"aria-disabled": isActive || void 0,
|
|
1031
1015
|
"aria-label": `Page ${page}`,
|
|
1032
1016
|
"aria-current": isActive ? "page" : void 0,
|
|
1033
1017
|
children: page
|
|
@@ -1119,66 +1103,342 @@ function DataTableSearch({ placeholder = "Search...", debounceMs = DEFAULT_DEBOU
|
|
|
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 ?? null });
|
|
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.nextCursor) cursorMapRef.current.set(pageIndex + 1, result.nextCursor);
|
|
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
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
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 ?? null });
|
|
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 }) {
|
|
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;
|
|
1438
|
+
return /* @__PURE__ */ jsxs(Popover, {
|
|
1439
|
+
open,
|
|
1440
|
+
onOpenChange: setOpen,
|
|
1441
|
+
children: [/* @__PURE__ */ jsx(PopoverTrigger, {
|
|
1182
1442
|
asChild: true,
|
|
1183
1443
|
children: /* @__PURE__ */ jsxs(Button, {
|
|
1184
1444
|
theme: "outline",
|
|
@@ -1388,242 +1648,4 @@ const DataTable = {
|
|
|
1388
1648
|
};
|
|
1389
1649
|
|
|
1390
1650
|
//#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, 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, 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 };
|
|
1651
|
+
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 };
|