@datum-cloud/datum-ui 0.3.0-alpha.3670fb2 → 0.3.0-alpha.7487874
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 +1 -1
- package/dist/{autocomplete-B9bCkXtz.mjs → autocomplete-DcKO7pj5.mjs} +1 -1
- package/dist/avatar-stack/index.mjs +2 -2
- package/dist/{avatar-stack-Bh-tLz0X.mjs → avatar-stack-B21McFeb.mjs} +1 -1
- package/dist/{calendar-date-picker-mlbzp3xR.mjs → calendar-date-picker-Bw6Mrr-P.mjs} +2 -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 +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/components/features/index.d.ts +1 -1
- package/dist/components/features/index.d.ts.map +1 -1
- package/dist/data-table/index.mjs +395 -363
- package/dist/date-picker/index.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 +3 -3
- package/dist/grid/index.mjs +1 -1
- package/dist/hooks/index.mjs +2 -2
- package/dist/index.mjs +34 -32
- package/dist/input-number/index.mjs +1 -1
- package/dist/input-with-addons/index.mjs +1 -1
- package/dist/loader-overlay/index.mjs +1 -1
- package/dist/map/index.mjs +1 -1
- package/dist/{map-ClJD-qxm.mjs → map-2RG9pYZR.mjs} +1 -1
- package/dist/more-actions/index.mjs +2 -2
- package/dist/{more-actions-DbC8dyed.mjs → more-actions-BODYgG1C.mjs} +2 -2
- package/dist/page-title/index.mjs +1 -1
- package/dist/sidebar/index.mjs +4 -5
- package/dist/{sidebar-C4NqSr4r.mjs → sidebar-BW76ss_f.mjs} +6 -415
- package/dist/skeleton/index.mjs +2 -1
- package/dist/skeleton-DZ31pU4B.mjs +28 -0
- 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 +1 -1
- package/dist/task-queue/index.mjs +3 -3
- package/dist/{task-queue-dropdown-fo3TX58Q.mjs → task-queue-dropdown-DtS0IKci.mjs} +3 -3
- package/dist/textarea/index.mjs +1 -1
- package/dist/theme/index.mjs +1 -1
- package/dist/toast/index.mjs +1 -1
- package/dist/tooltip/index.mjs +1 -1
- package/dist/typography/index.mjs +1 -1
- package/dist/{use-copy-to-clipboard-C7xqNxBX.mjs → use-copy-to-clipboard-C9cT2Qb-.mjs} +1 -1
- package/dist/{use-stepper-CB1injte.mjs → use-stepper-DJd8o9dV.mjs} +8 -8
- package/dist/visually-hidden/index.mjs +1 -1
- package/package.json +85 -79
- 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/{col-RfO7d6AR.mjs → col-DCneNxQj.mjs} +0 -0
- /package/dist/{dropdown-Cs7Xr8w7.mjs → dropdown-Dgm_b6Mm.mjs} +0 -0
- /package/dist/{dropzone-BT5fEDEF.mjs → dropzone-DR6O9OdU.mjs} +0 -0
- /package/dist/{empty-content-iDu3NUqG.mjs → empty-content-Dm7_5jO9.mjs} +0 -0
- /package/dist/{input-number-D9ydFith.mjs → input-number-D1HCcTXO.mjs} +0 -0
- /package/dist/{input-with-addons-CdgiUQce.mjs → input-with-addons-DN9LGwUU.mjs} +0 -0
- /package/dist/{loader-overlay-D83QeQNj.mjs → loader-overlay-CpA0zV8D.mjs} +0 -0
- /package/dist/{map-leaflet-imports-CdzvEnzY.mjs → map-leaflet-imports-CgEyVRnp.mjs} +0 -0
- /package/dist/{page-title-SGchAF6Y.mjs → page-title-D62FV6vD.mjs} +0 -0
- /package/dist/{skeleton-Cs6Q5GQc.mjs → skeleton-CkE23wsL.mjs} +0 -0
- /package/dist/{stepper-BG9DIzN5.mjs → stepper-CZeks9Ex.mjs} +0 -0
- /package/dist/{switch-B2VVauH6.mjs → switch-Cn9IM2gC.mjs} +0 -0
- /package/dist/{table-Dc3HfbM4.mjs → table-Dpzh0VPK.mjs} +0 -0
- /package/dist/{tabs-Ccb4uqbe.mjs → tabs-OYVCDOif.mjs} +0 -0
- /package/dist/{tag-input-BfHaKoMF.mjs → tag-input-DorFQ9bA.mjs} +0 -0
- /package/dist/{textarea-X4OjkqLJ.mjs → textarea-KZUKGHlO.mjs} +0 -0
- /package/dist/{theme.provider-Nun_O9-O.mjs → theme.provider-BG3cS9xe.mjs} +0 -0
- /package/dist/{to-api-format-zI26rEBI.mjs → to-api-format-CzPt5UAX.mjs} +0 -0
- /package/dist/{tooltip-DZFG1iMs.mjs → tooltip-U3XxlW4l.mjs} +0 -0
- /package/dist/{typography-T7WgvO77.mjs → typography-DdrxIJMd.mjs} +0 -0
- /package/dist/{use-debounce-Ctljs3MB.mjs → use-debounce-Dc95PFRX.mjs} +0 -0
- /package/dist/{use-toast-DN-fZBzJ.mjs → use-toast-DBmysDS6.mjs} +0 -0
- /package/dist/{visuallyhidden-CgkVhApW.mjs → visuallyhidden-CfBnXfvh.mjs} +0 -0
|
@@ -12,11 +12,12 @@ import { t as Label } from "../label-_ste_Re3.mjs";
|
|
|
12
12
|
import { i as DropdownMenuItem, l as DropdownMenuTrigger, r as DropdownMenuContent, t as DropdownMenu } from "../dropdown-menu-DAFyO-qD.mjs";
|
|
13
13
|
import { i as PopoverTrigger, r as PopoverContent, t as Popover } from "../popover-Ds9624qY.mjs";
|
|
14
14
|
import { i as SelectItem, l as SelectTrigger, n as SelectContent, t as Select, u as SelectValue } from "../select-CwVIFWFO.mjs";
|
|
15
|
-
import
|
|
16
|
-
import {
|
|
17
|
-
import { t as
|
|
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";
|
|
18
19
|
import { ArrowDown, ArrowUp, ArrowUpDown, Check, ChevronDown, ChevronLeft, ChevronRight, MoreHorizontal, X } from "lucide-react";
|
|
19
|
-
import { createContext, memo, use, useCallback, useEffect, useId, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
20
|
+
import { createContext, memo, use, useCallback, useContext, useEffect, useId, useMemo, useRef, useState, useSyncExternalStore } from "react";
|
|
20
21
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
21
22
|
import { parseAsInteger, parseAsString, useQueryStates } from "nuqs";
|
|
22
23
|
import { flexRender, getCoreRowModel, getPaginationRowModel, getSortedRowModel, useReactTable } from "@tanstack/react-table";
|
|
@@ -157,6 +158,16 @@ function withSelectionColumn(columns, options = {}) {
|
|
|
157
158
|
|
|
158
159
|
//#endregion
|
|
159
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
|
+
}
|
|
160
171
|
const FILTER_STRATEGIES = {
|
|
161
172
|
"checkbox": (cellValue, filterValue) => {
|
|
162
173
|
if (filterValue == null) return true;
|
|
@@ -200,14 +211,13 @@ function applyFilters(data, filters, search, registeredFilters, customFilterFns,
|
|
|
200
211
|
console.warn(`[DataTable] No filter strategy registered for column "${column}". Filter ignored.`);
|
|
201
212
|
continue;
|
|
202
213
|
}
|
|
203
|
-
|
|
204
|
-
if (!fn(cellValue, value)) return false;
|
|
214
|
+
if (!fn(resolvePath(row, column), value)) return false;
|
|
205
215
|
}
|
|
206
216
|
if (hasSearch) {
|
|
207
217
|
const query = search.toLowerCase();
|
|
208
218
|
if (searchConfig.searchFn) return searchConfig.searchFn(row, search);
|
|
209
219
|
if (searchConfig.searchableColumns && searchConfig.searchableColumns.length > 0) return searchConfig.searchableColumns.some((col) => {
|
|
210
|
-
const cellValue = row
|
|
220
|
+
const cellValue = resolvePath(row, col);
|
|
211
221
|
return cellValue != null && String(cellValue).toLowerCase().includes(query);
|
|
212
222
|
});
|
|
213
223
|
return Object.values(row).some((val) => {
|
|
@@ -243,7 +253,8 @@ function createDataTableStore(options) {
|
|
|
243
253
|
mode: options.mode,
|
|
244
254
|
isLoading: false,
|
|
245
255
|
error: null,
|
|
246
|
-
inlineContents: []
|
|
256
|
+
inlineContents: [],
|
|
257
|
+
_version: 0
|
|
247
258
|
};
|
|
248
259
|
if (options.defaultFilters && Object.keys(options.defaultFilters).length > 0) state = {
|
|
249
260
|
...state,
|
|
@@ -253,7 +264,10 @@ function createDataTableStore(options) {
|
|
|
253
264
|
for (const listener of listeners) listener();
|
|
254
265
|
}
|
|
255
266
|
function setState(next) {
|
|
256
|
-
state =
|
|
267
|
+
state = {
|
|
268
|
+
...next,
|
|
269
|
+
_version: state._version + 1
|
|
270
|
+
};
|
|
257
271
|
notify();
|
|
258
272
|
}
|
|
259
273
|
return {
|
|
@@ -442,6 +456,22 @@ function createDataTableStore(options) {
|
|
|
442
456
|
//#region src/components/features/data-table/core/data-table-context.tsx
|
|
443
457
|
const DataTableStoreContext = createContext(null);
|
|
444
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
|
+
}
|
|
445
475
|
const DataTableContext = createContext(null);
|
|
446
476
|
function useDataTableStore() {
|
|
447
477
|
const store = use(DataTableStoreContext);
|
|
@@ -482,7 +512,7 @@ function useSliceSelector(selector) {
|
|
|
482
512
|
cachedRef.current = next;
|
|
483
513
|
return next;
|
|
484
514
|
}, [store, selector]);
|
|
485
|
-
return useSyncExternalStore(store.subscribe, getSnapshot);
|
|
515
|
+
return useSyncExternalStore(store.subscribe, getSnapshot, getSnapshot);
|
|
486
516
|
}
|
|
487
517
|
function useDataTableFilters() {
|
|
488
518
|
const store = useDataTableStore();
|
|
@@ -511,20 +541,23 @@ function useDataTableSorting() {
|
|
|
511
541
|
}), [store]));
|
|
512
542
|
}
|
|
513
543
|
function useDataTableSelection() {
|
|
544
|
+
useRenderKey();
|
|
514
545
|
const store = useDataTableStore();
|
|
515
546
|
const table = useTableInstance();
|
|
516
|
-
return
|
|
517
|
-
rowSelection:
|
|
547
|
+
return {
|
|
548
|
+
rowSelection: store.getSnapshot().rowSelection,
|
|
518
549
|
setRowSelection: store.setRowSelection,
|
|
519
550
|
selectedRows: table.getFilteredSelectedRowModel().rows.map((r) => r.original)
|
|
520
|
-
}
|
|
551
|
+
};
|
|
521
552
|
}
|
|
522
553
|
function useDataTablePagination() {
|
|
554
|
+
useRenderKey();
|
|
523
555
|
const store = useDataTableStore();
|
|
524
556
|
const table = useTableInstance();
|
|
557
|
+
const state = store.getSnapshot();
|
|
525
558
|
const nextPage = useCallback(() => table.nextPage(), [table]);
|
|
526
559
|
const prevPage = useCallback(() => table.previousPage(), [table]);
|
|
527
|
-
return
|
|
560
|
+
return {
|
|
528
561
|
canNextPage: table.getCanNextPage(),
|
|
529
562
|
canPrevPage: table.getCanPreviousPage(),
|
|
530
563
|
nextPage,
|
|
@@ -535,20 +568,16 @@ function useDataTablePagination() {
|
|
|
535
568
|
pageSize: state.pageSize,
|
|
536
569
|
setPageSize: store.setPageSize,
|
|
537
570
|
totalRows: state.filteredData.length
|
|
538
|
-
}
|
|
539
|
-
store,
|
|
540
|
-
table,
|
|
541
|
-
nextPage,
|
|
542
|
-
prevPage
|
|
543
|
-
]));
|
|
571
|
+
};
|
|
544
572
|
}
|
|
545
573
|
function useDataTableRows() {
|
|
574
|
+
useRenderKey();
|
|
546
575
|
const table = useTableInstance();
|
|
547
|
-
return
|
|
576
|
+
return {
|
|
548
577
|
rows: table.getRowModel().rows,
|
|
549
578
|
headerGroups: table.getHeaderGroups(),
|
|
550
579
|
totalColumns: table.getAllColumns().length
|
|
551
|
-
}
|
|
580
|
+
};
|
|
552
581
|
}
|
|
553
582
|
function useDataTableLoading() {
|
|
554
583
|
return useSliceSelector(useCallback((state) => ({
|
|
@@ -564,41 +593,6 @@ function useDataTableInlineContents() {
|
|
|
564
593
|
unregisterInlineContent: store.unregisterInlineContent
|
|
565
594
|
}), [store]));
|
|
566
595
|
}
|
|
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
596
|
|
|
603
597
|
//#endregion
|
|
604
598
|
//#region src/components/features/data-table/components/active-filters.tsx
|
|
@@ -621,14 +615,15 @@ function FilterGroup({ label, children, className }) {
|
|
|
621
615
|
});
|
|
622
616
|
}
|
|
623
617
|
const EMPTY_LABELS = {};
|
|
624
|
-
function ActiveFiltersInner({ label = "Selected Filters", filterLabels = EMPTY_LABELS, formatFilterValue: formatter, clearAll = "icon", clearAllLabel = "Clear all", className, groupClassName, badgeClassName }) {
|
|
618
|
+
function ActiveFiltersInner({ label = "Selected Filters", excludeFilters, filterLabels = EMPTY_LABELS, formatFilterValue: formatter, clearAll = "icon", clearAllLabel = "Clear all", className, groupClassName, badgeClassName }) {
|
|
625
619
|
const { filters, setFilter, clearFilter, clearAllFilters } = useDataTableFilters();
|
|
626
620
|
const { search, clearSearch } = useDataTableSearch();
|
|
627
|
-
const
|
|
628
|
-
const
|
|
621
|
+
const excludeSet = useMemo(() => new Set(excludeFilters ?? []), [excludeFilters]);
|
|
622
|
+
const activeFilterEntries = Object.entries(filters).filter(([key, value]) => !excludeSet.has(key) && value != null && value !== "" && !(Array.isArray(value) && value.length === 0));
|
|
623
|
+
const showSearch = search.length > 0 && !excludeSet.has("search");
|
|
629
624
|
const hasFilters = activeFilterEntries.length > 0;
|
|
630
|
-
if (!
|
|
631
|
-
const totalGroups = activeFilterEntries.length + (
|
|
625
|
+
if (!showSearch && !hasFilters) return null;
|
|
626
|
+
const totalGroups = activeFilterEntries.length + (showSearch ? 1 : 0);
|
|
632
627
|
const removeArrayItem = (column, items, item) => {
|
|
633
628
|
const remaining = items.filter((v) => v !== item);
|
|
634
629
|
if (remaining.length > 0) setFilter(column, remaining);
|
|
@@ -636,7 +631,7 @@ function ActiveFiltersInner({ label = "Selected Filters", filterLabels = EMPTY_L
|
|
|
636
631
|
};
|
|
637
632
|
const handleClearAll = () => {
|
|
638
633
|
clearAllFilters();
|
|
639
|
-
if (
|
|
634
|
+
if (search.length > 0) clearSearch();
|
|
640
635
|
};
|
|
641
636
|
const badgeCn = cn("flex items-center gap-1.5 px-2 py-0.5 text-xs", badgeClassName);
|
|
642
637
|
return /* @__PURE__ */ jsxs("div", {
|
|
@@ -649,7 +644,7 @@ function ActiveFiltersInner({ label = "Selected Filters", filterLabels = EMPTY_L
|
|
|
649
644
|
"data-slot": "dt-active-filters-label",
|
|
650
645
|
children: label
|
|
651
646
|
}),
|
|
652
|
-
|
|
647
|
+
showSearch && /* @__PURE__ */ jsx(FilterGroup, {
|
|
653
648
|
label: "Search",
|
|
654
649
|
className: groupClassName,
|
|
655
650
|
children: /* @__PURE__ */ jsxs(Badge, {
|
|
@@ -914,19 +909,8 @@ function DataTableLoading({ rows = DEFAULT_LOADING_ROWS, columns = 4, className
|
|
|
914
909
|
return /* @__PURE__ */ jsx("div", {
|
|
915
910
|
className,
|
|
916
911
|
"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
|
-
})
|
|
912
|
+
style: { overflowX: "auto" },
|
|
913
|
+
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
914
|
});
|
|
931
915
|
}
|
|
932
916
|
|
|
@@ -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
|
|
@@ -1091,7 +1075,7 @@ function DataTableRowActions({ row, actions, isLoading = false, className }) {
|
|
|
1091
1075
|
|
|
1092
1076
|
//#endregion
|
|
1093
1077
|
//#region src/components/features/data-table/components/search.tsx
|
|
1094
|
-
function DataTableSearch({ placeholder = "Search...", debounceMs = DEFAULT_DEBOUNCE_MS, className }) {
|
|
1078
|
+
function DataTableSearch({ placeholder = "Search...", debounceMs = DEFAULT_DEBOUNCE_MS, className, disabled }) {
|
|
1095
1079
|
const { search, setSearch } = useDataTableSearch();
|
|
1096
1080
|
const [inputValue, setInputValue] = useState(search);
|
|
1097
1081
|
useEffect(() => {
|
|
@@ -1113,68 +1097,351 @@ function DataTableSearch({ placeholder = "Search...", debounceMs = DEFAULT_DEBOU
|
|
|
1113
1097
|
value: inputValue,
|
|
1114
1098
|
onChange: (e) => setInputValue(e.target.value),
|
|
1115
1099
|
className,
|
|
1100
|
+
disabled,
|
|
1116
1101
|
"aria-label": placeholder,
|
|
1117
1102
|
"data-slot": "dt-search"
|
|
1118
1103
|
});
|
|
1119
1104
|
}
|
|
1120
1105
|
|
|
1121
1106
|
//#endregion
|
|
1122
|
-
//#region src/components/features/data-table/
|
|
1123
|
-
function
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1107
|
+
//#region src/components/features/data-table/hooks/use-data-table-client.ts
|
|
1108
|
+
function useDataTableClient(options) {
|
|
1109
|
+
const { data, columns, pageSize, getRowId, enableRowSelection = false, defaultSort, defaultFilters, searchableColumns, searchFn, filterFns, stateAdapter } = options;
|
|
1110
|
+
const store = useMemo(() => createDataTableStore({
|
|
1111
|
+
data,
|
|
1112
|
+
mode: "client",
|
|
1113
|
+
defaultSort,
|
|
1114
|
+
defaultFilters,
|
|
1115
|
+
pageSize,
|
|
1116
|
+
searchableColumns,
|
|
1117
|
+
searchFn,
|
|
1118
|
+
filterFns
|
|
1119
|
+
}), []);
|
|
1120
|
+
const isInitialRender = useRef(true);
|
|
1121
|
+
useEffect(() => {
|
|
1122
|
+
if (isInitialRender.current) {
|
|
1123
|
+
isInitialRender.current = false;
|
|
1124
|
+
return;
|
|
1125
|
+
}
|
|
1126
|
+
store.setData(data);
|
|
1127
|
+
}, [data, store]);
|
|
1128
|
+
const resolvedColumns = useMemo(() => enableRowSelection ? withSelectionColumn(columns, typeof enableRowSelection === "object" ? enableRowSelection : {}) : columns, [columns, enableRowSelection]);
|
|
1129
|
+
const { filteredData, sorting, rowSelection, pageIndex, pageSize: storePageSize, filters, search } = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);
|
|
1130
|
+
const table = useReactTable({
|
|
1131
|
+
data: filteredData,
|
|
1132
|
+
columns: resolvedColumns,
|
|
1133
|
+
state: {
|
|
1134
|
+
sorting,
|
|
1135
|
+
rowSelection,
|
|
1136
|
+
pagination: {
|
|
1137
|
+
pageIndex,
|
|
1138
|
+
pageSize: storePageSize
|
|
1139
|
+
}
|
|
1140
|
+
},
|
|
1141
|
+
onSortingChange: (updater) => {
|
|
1142
|
+
const next = typeof updater === "function" ? updater(sorting) : updater;
|
|
1143
|
+
store.setSorting(next);
|
|
1144
|
+
},
|
|
1145
|
+
onRowSelectionChange: (updater) => {
|
|
1146
|
+
const next = typeof updater === "function" ? updater(rowSelection) : updater;
|
|
1147
|
+
store.setRowSelection(next);
|
|
1148
|
+
},
|
|
1149
|
+
onPaginationChange: (updater) => {
|
|
1150
|
+
const next = typeof updater === "function" ? updater({
|
|
1151
|
+
pageIndex,
|
|
1152
|
+
pageSize: storePageSize
|
|
1153
|
+
}) : updater;
|
|
1154
|
+
store.setPagination(next.pageIndex, next.pageSize);
|
|
1155
|
+
},
|
|
1156
|
+
getCoreRowModel: getCoreRowModel(),
|
|
1157
|
+
getSortedRowModel: getSortedRowModel(),
|
|
1158
|
+
getPaginationRowModel: getPaginationRowModel(),
|
|
1159
|
+
getRowId,
|
|
1160
|
+
enableRowSelection: !!enableRowSelection
|
|
1133
1161
|
});
|
|
1162
|
+
const hydratedRef = useRef(false);
|
|
1163
|
+
useEffect(() => {
|
|
1164
|
+
if (stateAdapter && !hydratedRef.current) {
|
|
1165
|
+
hydratedRef.current = true;
|
|
1166
|
+
const persisted = stateAdapter.read();
|
|
1167
|
+
if (persisted.sorting && persisted.sorting.length > 0) store.setSorting(persisted.sorting);
|
|
1168
|
+
if (persisted.filters) {
|
|
1169
|
+
for (const [key, value] of Object.entries(persisted.filters)) if (value != null) store.setFilter(key, value);
|
|
1170
|
+
}
|
|
1171
|
+
if (persisted.search) store.setSearch(persisted.search);
|
|
1172
|
+
if (persisted.pageIndex != null && persisted.pageIndex > 0) store.setPageIndex(persisted.pageIndex);
|
|
1173
|
+
if (persisted.pageSize != null) store.setPageSize(persisted.pageSize);
|
|
1174
|
+
}
|
|
1175
|
+
}, []);
|
|
1176
|
+
const isFirstWrite = useRef(true);
|
|
1177
|
+
useEffect(() => {
|
|
1178
|
+
if (!stateAdapter) return;
|
|
1179
|
+
if (isFirstWrite.current) {
|
|
1180
|
+
isFirstWrite.current = false;
|
|
1181
|
+
return;
|
|
1182
|
+
}
|
|
1183
|
+
stateAdapter.write({
|
|
1184
|
+
sorting,
|
|
1185
|
+
filters,
|
|
1186
|
+
search,
|
|
1187
|
+
pageIndex,
|
|
1188
|
+
pageSize: storePageSize
|
|
1189
|
+
});
|
|
1190
|
+
}, [
|
|
1191
|
+
sorting,
|
|
1192
|
+
filters,
|
|
1193
|
+
search,
|
|
1194
|
+
pageIndex,
|
|
1195
|
+
storePageSize,
|
|
1196
|
+
stateAdapter
|
|
1197
|
+
]);
|
|
1198
|
+
return {
|
|
1199
|
+
store,
|
|
1200
|
+
table
|
|
1201
|
+
};
|
|
1134
1202
|
}
|
|
1135
1203
|
|
|
1136
1204
|
//#endregion
|
|
1137
|
-
//#region src/components/features/data-table/
|
|
1138
|
-
|
|
1205
|
+
//#region src/components/features/data-table/hooks/use-is-client.ts
|
|
1206
|
+
/**
|
|
1207
|
+
* Returns `false` during SSR and `true` after hydration.
|
|
1208
|
+
* Used to gate components that depend on client-only APIs
|
|
1209
|
+
* (e.g. TanStack Table's internal useSyncExternalStore call
|
|
1210
|
+
* which omits the getServerSnapshot argument).
|
|
1211
|
+
*/
|
|
1212
|
+
function useIsClient() {
|
|
1213
|
+
const [isClient, setIsClient] = useState(false);
|
|
1214
|
+
useEffect(() => setIsClient(true), []);
|
|
1215
|
+
return isClient;
|
|
1216
|
+
}
|
|
1217
|
+
|
|
1218
|
+
//#endregion
|
|
1219
|
+
//#region src/components/features/data-table/core/client-provider.tsx
|
|
1220
|
+
/**
|
|
1221
|
+
* Inner component that calls useDataTableClient.
|
|
1222
|
+
* Only rendered on the client (gated by ClientProvider).
|
|
1223
|
+
*/
|
|
1224
|
+
function ClientProviderInner({ className, children, ssrFallback: _ssrFallback, ...options }) {
|
|
1225
|
+
const { store, table } = useDataTableClient(options);
|
|
1139
1226
|
return /* @__PURE__ */ jsx(DataTableStoreContext, {
|
|
1140
1227
|
value: store,
|
|
1141
1228
|
children: /* @__PURE__ */ jsx(TableInstanceContext, {
|
|
1142
1229
|
value: table,
|
|
1143
|
-
children: /* @__PURE__ */ jsx(
|
|
1144
|
-
|
|
1145
|
-
children
|
|
1230
|
+
children: /* @__PURE__ */ jsx(DataTableRenderKeyContext, {
|
|
1231
|
+
value: store.getSnapshot()._version,
|
|
1232
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
1233
|
+
className,
|
|
1234
|
+
children
|
|
1235
|
+
})
|
|
1146
1236
|
})
|
|
1147
1237
|
})
|
|
1148
1238
|
});
|
|
1149
1239
|
}
|
|
1240
|
+
function ClientProvider(props) {
|
|
1241
|
+
if (!useIsClient()) return /* @__PURE__ */ jsx(Fragment$1, { children: props.ssrFallback === void 0 ? /* @__PURE__ */ jsx(DataTableLoading, {
|
|
1242
|
+
columns: props.columns.length,
|
|
1243
|
+
className: props?.className
|
|
1244
|
+
}) : props.ssrFallback });
|
|
1245
|
+
return /* @__PURE__ */ jsx(ClientProviderInner, { ...props });
|
|
1246
|
+
}
|
|
1150
1247
|
|
|
1151
1248
|
//#endregion
|
|
1152
|
-
//#region src/components/features/data-table/
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
const
|
|
1156
|
-
const
|
|
1249
|
+
//#region src/components/features/data-table/hooks/use-data-table-server.ts
|
|
1250
|
+
function useDataTableServer(options) {
|
|
1251
|
+
const { columns, fetchFn, transform, limit = 20, getRowId, enableRowSelection = false, defaultSort, defaultFilters, stateAdapter } = options;
|
|
1252
|
+
const fetchRef = useRef(fetchFn);
|
|
1253
|
+
const transformRef = useRef(transform);
|
|
1157
1254
|
useEffect(() => {
|
|
1158
|
-
|
|
1159
|
-
|
|
1255
|
+
fetchRef.current = fetchFn;
|
|
1256
|
+
}, [fetchFn]);
|
|
1257
|
+
useEffect(() => {
|
|
1258
|
+
transformRef.current = transform;
|
|
1259
|
+
}, [transform]);
|
|
1260
|
+
const cursorMapRef = useRef(/* @__PURE__ */ new Map());
|
|
1261
|
+
const hasNextPageRef = useRef(false);
|
|
1262
|
+
const store = useMemo(() => createDataTableStore({
|
|
1263
|
+
data: [],
|
|
1264
|
+
mode: "server",
|
|
1265
|
+
defaultSort,
|
|
1266
|
+
defaultFilters,
|
|
1267
|
+
pageSize: limit
|
|
1268
|
+
}), []);
|
|
1269
|
+
const { sorting, filters, search, rowSelection, pageSize, pageIndex } = useSyncExternalStore(store.subscribe, store.getSnapshot, store.getSnapshot);
|
|
1270
|
+
const prevQueryRef = useRef({
|
|
1271
|
+
sorting,
|
|
1272
|
+
filters,
|
|
1273
|
+
search,
|
|
1274
|
+
pageSize
|
|
1275
|
+
});
|
|
1276
|
+
useEffect(() => {
|
|
1277
|
+
const prev = prevQueryRef.current;
|
|
1278
|
+
if (prev.sorting !== sorting || prev.filters !== filters || prev.search !== search || prev.pageSize !== pageSize) {
|
|
1279
|
+
cursorMapRef.current = /* @__PURE__ */ new Map();
|
|
1280
|
+
hasNextPageRef.current = false;
|
|
1281
|
+
if (pageIndex !== 0) {
|
|
1282
|
+
prevQueryRef.current = {
|
|
1283
|
+
sorting,
|
|
1284
|
+
filters,
|
|
1285
|
+
search,
|
|
1286
|
+
pageSize
|
|
1287
|
+
};
|
|
1288
|
+
store.setPageIndex(0);
|
|
1289
|
+
return;
|
|
1290
|
+
}
|
|
1291
|
+
}
|
|
1292
|
+
prevQueryRef.current = {
|
|
1293
|
+
sorting,
|
|
1294
|
+
filters,
|
|
1295
|
+
search,
|
|
1296
|
+
pageSize
|
|
1297
|
+
};
|
|
1298
|
+
let cancelled = false;
|
|
1299
|
+
store.setLoading(true);
|
|
1300
|
+
const cursor = cursorMapRef.current.get(pageIndex);
|
|
1301
|
+
fetchRef.current({
|
|
1302
|
+
sorting,
|
|
1303
|
+
filters,
|
|
1304
|
+
search,
|
|
1305
|
+
cursor,
|
|
1306
|
+
limit: pageSize
|
|
1307
|
+
}).then((response) => {
|
|
1308
|
+
if (cancelled) return;
|
|
1309
|
+
const result = transformRef.current(response);
|
|
1310
|
+
store.setServerData(result.data);
|
|
1311
|
+
store.setError(null);
|
|
1312
|
+
if (result.cursor) cursorMapRef.current.set(pageIndex + 1, result.cursor);
|
|
1313
|
+
hasNextPageRef.current = result.hasNextPage;
|
|
1314
|
+
}).catch((error) => {
|
|
1315
|
+
if (cancelled) return;
|
|
1316
|
+
store.setServerData([]);
|
|
1317
|
+
store.setError(error instanceof Error ? error : new Error(String(error)));
|
|
1318
|
+
hasNextPageRef.current = false;
|
|
1319
|
+
}).finally(() => {
|
|
1320
|
+
if (!cancelled) store.setLoading(false);
|
|
1321
|
+
});
|
|
1322
|
+
return () => {
|
|
1323
|
+
cancelled = true;
|
|
1324
|
+
};
|
|
1160
1325
|
}, [
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1326
|
+
sorting,
|
|
1327
|
+
filters,
|
|
1328
|
+
search,
|
|
1329
|
+
pageSize,
|
|
1330
|
+
pageIndex,
|
|
1331
|
+
store
|
|
1164
1332
|
]);
|
|
1165
|
-
const
|
|
1166
|
-
const
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1333
|
+
const resolvedColumns = useMemo(() => enableRowSelection ? withSelectionColumn(columns, typeof enableRowSelection === "object" ? enableRowSelection : {}) : columns, [columns, enableRowSelection]);
|
|
1334
|
+
const table = useReactTable({
|
|
1335
|
+
data: store.getSnapshot().data,
|
|
1336
|
+
columns: resolvedColumns,
|
|
1337
|
+
state: {
|
|
1338
|
+
sorting,
|
|
1339
|
+
rowSelection,
|
|
1340
|
+
pagination: {
|
|
1341
|
+
pageIndex,
|
|
1342
|
+
pageSize
|
|
1343
|
+
}
|
|
1344
|
+
},
|
|
1345
|
+
manualPagination: true,
|
|
1346
|
+
manualSorting: true,
|
|
1347
|
+
manualFiltering: true,
|
|
1348
|
+
pageCount: hasNextPageRef.current ? pageIndex + 2 : pageIndex + 1,
|
|
1349
|
+
getCoreRowModel: getCoreRowModel(),
|
|
1350
|
+
getRowId,
|
|
1351
|
+
enableRowSelection: !!enableRowSelection,
|
|
1352
|
+
onSortingChange: (updater) => {
|
|
1353
|
+
const next = typeof updater === "function" ? updater(sorting) : updater;
|
|
1354
|
+
store.setSorting(next);
|
|
1355
|
+
},
|
|
1356
|
+
onRowSelectionChange: (updater) => {
|
|
1357
|
+
const next = typeof updater === "function" ? updater(rowSelection) : updater;
|
|
1358
|
+
store.setRowSelection(next);
|
|
1359
|
+
},
|
|
1360
|
+
onPaginationChange: (updater) => {
|
|
1361
|
+
const next = typeof updater === "function" ? updater({
|
|
1362
|
+
pageIndex,
|
|
1363
|
+
pageSize
|
|
1364
|
+
}) : updater;
|
|
1365
|
+
store.setPagination(next.pageIndex, next.pageSize);
|
|
1366
|
+
}
|
|
1367
|
+
});
|
|
1368
|
+
useEffect(() => {
|
|
1369
|
+
if (stateAdapter) stateAdapter.write({
|
|
1370
|
+
sorting,
|
|
1371
|
+
filters,
|
|
1372
|
+
search,
|
|
1373
|
+
pageSize
|
|
1374
|
+
});
|
|
1375
|
+
}, [
|
|
1376
|
+
sorting,
|
|
1377
|
+
filters,
|
|
1378
|
+
search,
|
|
1379
|
+
pageSize,
|
|
1380
|
+
stateAdapter
|
|
1381
|
+
]);
|
|
1382
|
+
return {
|
|
1383
|
+
store,
|
|
1384
|
+
table
|
|
1385
|
+
};
|
|
1386
|
+
}
|
|
1387
|
+
|
|
1388
|
+
//#endregion
|
|
1389
|
+
//#region src/components/features/data-table/core/server-provider.tsx
|
|
1390
|
+
/**
|
|
1391
|
+
* Inner component that calls useDataTableServer.
|
|
1392
|
+
* Only rendered on the client (gated by ServerProvider).
|
|
1393
|
+
*/
|
|
1394
|
+
function ServerProviderInner({ className, children, ssrFallback: _ssrFallback, ...options }) {
|
|
1395
|
+
const { store, table } = useDataTableServer(options);
|
|
1396
|
+
return /* @__PURE__ */ jsx(DataTableStoreContext, {
|
|
1397
|
+
value: store,
|
|
1398
|
+
children: /* @__PURE__ */ jsx(TableInstanceContext, {
|
|
1399
|
+
value: table,
|
|
1400
|
+
children: /* @__PURE__ */ jsx(DataTableRenderKeyContext, {
|
|
1401
|
+
value: store.getSnapshot()._version,
|
|
1402
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
1403
|
+
className,
|
|
1404
|
+
children
|
|
1405
|
+
})
|
|
1406
|
+
})
|
|
1407
|
+
})
|
|
1408
|
+
});
|
|
1409
|
+
}
|
|
1410
|
+
function ServerProvider(props) {
|
|
1411
|
+
if (!useIsClient()) return /* @__PURE__ */ jsx(Fragment$1, { children: props.ssrFallback === void 0 ? /* @__PURE__ */ jsx(DataTableLoading, {
|
|
1412
|
+
columns: props.columns.length,
|
|
1413
|
+
className: props?.className
|
|
1414
|
+
}) : props.ssrFallback });
|
|
1415
|
+
return /* @__PURE__ */ jsx(ServerProviderInner, { ...props });
|
|
1416
|
+
}
|
|
1417
|
+
|
|
1418
|
+
//#endregion
|
|
1419
|
+
//#region src/components/features/data-table/filters/checkbox-filter.tsx
|
|
1420
|
+
const MAX_VISIBLE_BADGES = 2;
|
|
1421
|
+
function CheckboxFilter({ column, label, options, className, checkboxPopoverClassName, disabled }) {
|
|
1422
|
+
const { filters, setFilter, clearFilter, registerFilter, unregisterFilter } = useDataTableFilters();
|
|
1423
|
+
const [open, setOpen] = useState(false);
|
|
1424
|
+
useEffect(() => {
|
|
1425
|
+
registerFilter(column, "checkbox");
|
|
1426
|
+
return () => unregisterFilter(column);
|
|
1427
|
+
}, [
|
|
1428
|
+
column,
|
|
1429
|
+
registerFilter,
|
|
1430
|
+
unregisterFilter
|
|
1431
|
+
]);
|
|
1432
|
+
const selectedValues = filters[column] ?? [];
|
|
1433
|
+
const updateValues = (newValues) => {
|
|
1434
|
+
if (newValues.length > 0) setFilter(column, newValues);
|
|
1435
|
+
else clearFilter(column);
|
|
1436
|
+
};
|
|
1437
|
+
const handleToggle = (optionValue, checked) => {
|
|
1438
|
+
updateValues(checked ? [...selectedValues, optionValue] : selectedValues.filter((v) => v !== optionValue));
|
|
1439
|
+
};
|
|
1440
|
+
const removeValue = (optionValue) => {
|
|
1441
|
+
updateValues(selectedValues.filter((v) => v !== optionValue));
|
|
1442
|
+
};
|
|
1443
|
+
const visibleBadges = selectedValues.slice(0, MAX_VISIBLE_BADGES);
|
|
1444
|
+
const remainingCount = selectedValues.length - MAX_VISIBLE_BADGES;
|
|
1178
1445
|
return /* @__PURE__ */ jsxs(Popover, {
|
|
1179
1446
|
open,
|
|
1180
1447
|
onOpenChange: setOpen,
|
|
@@ -1182,7 +1449,8 @@ function CheckboxFilter({ column, label, options, className, checkboxPopoverClas
|
|
|
1182
1449
|
asChild: true,
|
|
1183
1450
|
children: /* @__PURE__ */ jsxs(Button, {
|
|
1184
1451
|
theme: "outline",
|
|
1185
|
-
|
|
1452
|
+
disabled,
|
|
1453
|
+
className: cn("h-10 justify-between gap-1", className),
|
|
1186
1454
|
"data-slot": "dt-filter",
|
|
1187
1455
|
"data-testid": "dt-filter-trigger",
|
|
1188
1456
|
children: [selectedValues.length > 0 ? /* @__PURE__ */ jsxs("div", {
|
|
@@ -1261,7 +1529,7 @@ function CheckboxFilter({ column, label, options, className, checkboxPopoverClas
|
|
|
1261
1529
|
|
|
1262
1530
|
//#endregion
|
|
1263
1531
|
//#region src/components/features/data-table/filters/date-picker-filter.tsx
|
|
1264
|
-
function DatePickerFilter({ column, label, className, datePickerPopoverClassName, disableFuture, disablePast, minDate, maxDate }) {
|
|
1532
|
+
function DatePickerFilter({ column, label, className, datePickerPopoverClassName, disableFuture, disablePast, minDate, maxDate, disabled }) {
|
|
1265
1533
|
const { filters, setFilter, clearFilter, registerFilter, unregisterFilter } = useDataTableFilters();
|
|
1266
1534
|
const rawValue = filters[column];
|
|
1267
1535
|
useEffect(() => {
|
|
@@ -1286,8 +1554,9 @@ function DatePickerFilter({ column, label, className, datePickerPopoverClassName
|
|
|
1286
1554
|
numberOfMonths: 1,
|
|
1287
1555
|
closeOnSelect: true,
|
|
1288
1556
|
placeholder: label,
|
|
1289
|
-
triggerClassName: className,
|
|
1557
|
+
triggerClassName: cn("h-10", className),
|
|
1290
1558
|
variant: "outline",
|
|
1559
|
+
disabled,
|
|
1291
1560
|
disableFuture,
|
|
1292
1561
|
disablePast,
|
|
1293
1562
|
minDate,
|
|
@@ -1303,7 +1572,7 @@ function DatePickerFilter({ column, label, className, datePickerPopoverClassName
|
|
|
1303
1572
|
|
|
1304
1573
|
//#endregion
|
|
1305
1574
|
//#region src/components/features/data-table/filters/select-filter.tsx
|
|
1306
|
-
function SelectFilter({ column, label, options, placeholder, searchable = true, className, selectPopoverClassName }) {
|
|
1575
|
+
function SelectFilter({ column, label, options, placeholder, searchable = true, className, selectPopoverClassName, disabled }) {
|
|
1307
1576
|
const { filters, setFilter, clearFilter, registerFilter, unregisterFilter } = useDataTableFilters();
|
|
1308
1577
|
const [open, setOpen] = useState(false);
|
|
1309
1578
|
const value = filters[column];
|
|
@@ -1325,7 +1594,8 @@ function SelectFilter({ column, label, options, placeholder, searchable = true,
|
|
|
1325
1594
|
theme: "outline",
|
|
1326
1595
|
role: "combobox",
|
|
1327
1596
|
"aria-expanded": open,
|
|
1328
|
-
|
|
1597
|
+
disabled,
|
|
1598
|
+
className: cn("h-10 justify-between", className),
|
|
1329
1599
|
"data-slot": "dt-filter",
|
|
1330
1600
|
"data-testid": "dt-filter-trigger",
|
|
1331
1601
|
children: [/* @__PURE__ */ jsx("span", {
|
|
@@ -1388,242 +1658,4 @@ const DataTable = {
|
|
|
1388
1658
|
};
|
|
1389
1659
|
|
|
1390
1660
|
//#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 };
|
|
1661
|
+
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 };
|