@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.
Files changed (21) hide show
  1. package/dist/components/features/data-table/core/client-provider.d.ts +6 -7
  2. package/dist/components/features/data-table/core/client-provider.d.ts.map +1 -1
  3. package/dist/components/features/data-table/core/data-table-context.d.ts +14 -0
  4. package/dist/components/features/data-table/core/data-table-context.d.ts.map +1 -1
  5. package/dist/components/features/data-table/core/server-provider.d.ts +6 -7
  6. package/dist/components/features/data-table/core/server-provider.d.ts.map +1 -1
  7. package/dist/components/features/data-table/core/store.d.ts.map +1 -1
  8. package/dist/components/features/data-table/hooks/index.d.ts +1 -1
  9. package/dist/components/features/data-table/hooks/index.d.ts.map +1 -1
  10. package/dist/components/features/data-table/hooks/use-is-client.d.ts +8 -0
  11. package/dist/components/features/data-table/hooks/use-is-client.d.ts.map +1 -0
  12. package/dist/components/features/data-table/hooks/use-selectors.d.ts +4 -35
  13. package/dist/components/features/data-table/hooks/use-selectors.d.ts.map +1 -1
  14. package/dist/components/features/data-table/index.d.ts +1 -3
  15. package/dist/components/features/data-table/index.d.ts.map +1 -1
  16. package/dist/components/features/data-table/types.d.ts +2 -0
  17. package/dist/components/features/data-table/types.d.ts.map +1 -1
  18. package/dist/data-table/index.mjs +356 -334
  19. package/package.json +55 -1
  20. package/dist/components/features/data-table/hooks/use-data-table-context.d.ts +0 -2
  21. 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 = next;
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 useSliceSelector(useCallback((state) => ({
517
- rowSelection: state.rowSelection,
537
+ return {
538
+ rowSelection: store.getSnapshot().rowSelection,
518
539
  setRowSelection: store.setRowSelection,
519
540
  selectedRows: table.getFilteredSelectedRowModel().rows.map((r) => r.original)
520
- }), [store, table]));
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 useSliceSelector(useCallback((state) => ({
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 useSliceSelector(useCallback((_state) => ({
566
+ return {
548
567
  rows: table.getRowModel().rows,
549
568
  headerGroups: table.getHeaderGroups(),
550
569
  totalColumns: table.getAllColumns().length
551
- }), [table]));
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/core/client-provider.tsx
1123
- function ClientProvider({ store, table, className, children }) {
1124
- return /* @__PURE__ */ jsx(DataTableStoreContext, {
1125
- value: store,
1126
- children: /* @__PURE__ */ jsx(TableInstanceContext, {
1127
- value: table,
1128
- children: /* @__PURE__ */ jsx("div", {
1129
- className,
1130
- children
1131
- })
1132
- })
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/core/server-provider.tsx
1138
- function ServerProvider({ store, table, className, children }) {
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("div", {
1144
- className,
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/filters/checkbox-filter.tsx
1153
- const MAX_VISIBLE_BADGES = 2;
1154
- function CheckboxFilter({ column, label, options, className, checkboxPopoverClassName }) {
1155
- const { filters, setFilter, clearFilter, registerFilter, unregisterFilter } = useDataTableFilters();
1156
- const [open, setOpen] = useState(false);
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
- registerFilter(column, "checkbox");
1159
- return () => unregisterFilter(column);
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
- column,
1162
- registerFilter,
1163
- unregisterFilter
1322
+ sorting,
1323
+ filters,
1324
+ search,
1325
+ pageSize,
1326
+ pageIndex,
1327
+ store
1164
1328
  ]);
1165
- const selectedValues = filters[column] ?? [];
1166
- const updateValues = (newValues) => {
1167
- if (newValues.length > 0) setFilter(column, newValues);
1168
- else clearFilter(column);
1169
- };
1170
- const handleToggle = (optionValue, checked) => {
1171
- updateValues(checked ? [...selectedValues, optionValue] : selectedValues.filter((v) => v !== optionValue));
1172
- };
1173
- const removeValue = (optionValue) => {
1174
- updateValues(selectedValues.filter((v) => v !== optionValue));
1175
- };
1176
- const visibleBadges = selectedValues.slice(0, MAX_VISIBLE_BADGES);
1177
- const remainingCount = selectedValues.length - MAX_VISIBLE_BADGES;
1178
- return /* @__PURE__ */ jsxs(Popover, {
1179
- open,
1180
- onOpenChange: setOpen,
1181
- children: [/* @__PURE__ */ jsx(PopoverTrigger, {
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
- //#region src/components/features/data-table/hooks/use-data-table-client.ts
1392
- function useDataTableClient(options) {
1393
- const { data, columns, pageSize, getRowId, enableRowSelection = false, defaultSort, defaultFilters, searchableColumns, searchFn, filterFns, stateAdapter } = options;
1394
- const store = useMemo(() => createDataTableStore({
1395
- data,
1396
- mode: "client",
1397
- defaultSort,
1398
- defaultFilters,
1399
- pageSize,
1400
- searchableColumns,
1401
- searchFn,
1402
- filterFns
1403
- }), []);
1404
- const isInitialRender = useRef(true);
1405
- useEffect(() => {
1406
- if (isInitialRender.current) {
1407
- isInitialRender.current = false;
1408
- return;
1409
- }
1410
- store.setData(data);
1411
- }, [data, store]);
1412
- const resolvedColumns = useMemo(() => enableRowSelection ? withSelectionColumn(columns, typeof enableRowSelection === "object" ? enableRowSelection : {}) : columns, [columns, enableRowSelection]);
1413
- const { filteredData, sorting, rowSelection, pageIndex, pageSize: storePageSize, filters, search } = useSyncExternalStore(store.subscribe, store.getSnapshot, 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 };