@facetui/react 1.0.0

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/index.cjs ADDED
@@ -0,0 +1,1220 @@
1
+ "use client";
2
+ "use strict";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/components/data-table/index.ts
22
+ var index_exports = {};
23
+ __export(index_exports, {
24
+ DataTable: () => DataTable,
25
+ DataTableBody: () => DataTableBody,
26
+ DataTableColumnToggle: () => DataTableColumnToggle,
27
+ DataTableHeader: () => DataTableHeader,
28
+ DataTablePagination: () => DataTablePagination,
29
+ DataTableToolbar: () => DataTableToolbar,
30
+ useColumnSort: () => useColumnSort,
31
+ useColumnVisibility: () => useColumnVisibility,
32
+ useDataTable: () => useDataTable,
33
+ useDataTableContext: () => useDataTableContext,
34
+ useGlobalFilter: () => useGlobalFilter,
35
+ usePagination: () => usePagination,
36
+ useRowSelection: () => useRowSelection
37
+ });
38
+ module.exports = __toCommonJS(index_exports);
39
+
40
+ // src/components/data-table/core/DataTableContext.tsx
41
+ var import_react = require("react");
42
+ var DataTableContext = (0, import_react.createContext)(null);
43
+ function useDataTableContext() {
44
+ const ctx = (0, import_react.useContext)(DataTableContext);
45
+ if (!ctx) {
46
+ throw new Error(
47
+ "useDataTableContext must be called inside a <DataTable> component tree."
48
+ );
49
+ }
50
+ return ctx;
51
+ }
52
+
53
+ // src/components/data-table/core/useDataTable.ts
54
+ var import_react2 = require("react");
55
+ var import_react_table = require("@tanstack/react-table");
56
+ var SELECT_COLUMN_ID = "__select__";
57
+ var facetTextFilter = (row, columnId, filterValue) => {
58
+ const query = String(filterValue ?? "").trim().toLowerCase();
59
+ if (query === "") return true;
60
+ return String(row.getValue(columnId) ?? "").toLowerCase().includes(query);
61
+ };
62
+ function useDataTable(props) {
63
+ const {
64
+ data,
65
+ columns,
66
+ getRowId,
67
+ // pagination
68
+ manualPagination = false,
69
+ rowCount,
70
+ pageSizeOptions = [10, 25, 50, 100],
71
+ onPaginationChange,
72
+ // sorting
73
+ manualSorting = false,
74
+ enableMultiSort = false,
75
+ onSortingChange,
76
+ // filtering
77
+ manualFiltering = false,
78
+ onGlobalFilterChange,
79
+ onColumnFiltersChange,
80
+ // row selection
81
+ enableRowSelection,
82
+ enableMultiRowSelection = true,
83
+ onRowSelectionChange
84
+ } = props;
85
+ const resolvedColumns = (0, import_react2.useMemo)(() => {
86
+ const wantsSelectionColumn = enableRowSelection !== void 0 && enableRowSelection !== false;
87
+ if (!wantsSelectionColumn || columns.some((c) => c.id === SELECT_COLUMN_ID)) {
88
+ return columns;
89
+ }
90
+ const selectColumn = {
91
+ id: SELECT_COLUMN_ID,
92
+ header: "",
93
+ enableSorting: false,
94
+ enableHiding: false,
95
+ size: 40
96
+ };
97
+ return [selectColumn, ...columns];
98
+ }, [columns, enableRowSelection]);
99
+ const engineColumns = (0, import_react2.useMemo)(
100
+ () => resolvedColumns.map((c) => {
101
+ const accessorFn = c.accessorFn ? (row) => c.accessorFn(row) : c.accessorKey != null ? (row) => row[c.accessorKey] : void 0;
102
+ return {
103
+ id: c.id,
104
+ ...accessorFn ? { accessorFn } : {},
105
+ enableSorting: c.enableSorting ?? true,
106
+ enableHiding: c.enableHiding ?? true,
107
+ sortingFn: "basic",
108
+ filterFn: facetTextFilter,
109
+ size: c.size ?? 150
110
+ };
111
+ }),
112
+ [resolvedColumns]
113
+ );
114
+ const [pagination, setPaginationState] = (0, import_react2.useState)({
115
+ pageIndex: 0,
116
+ pageSize: pageSizeOptions[0]
117
+ });
118
+ const [sorting, setSortingState] = (0, import_react2.useState)([]);
119
+ const [globalFilterState, setGlobalFilterState] = (0, import_react2.useState)(
120
+ props.globalFilter ?? ""
121
+ );
122
+ const [columnFiltersState, setColumnFiltersState] = (0, import_react2.useState)(
123
+ props.columnFilters ?? []
124
+ );
125
+ const [rowSelection, setRowSelectionState] = (0, import_react2.useState)({});
126
+ const [columnVisibility, setColumnVisibilityState] = (0, import_react2.useState)({});
127
+ const globalFilter = props.globalFilter !== void 0 ? props.globalFilter : globalFilterState;
128
+ const columnFilters = props.columnFilters !== void 0 ? props.columnFilters : columnFiltersState;
129
+ const enableRowSelectionOption = (0, import_react2.useMemo)(() => {
130
+ if (typeof enableRowSelection === "function") {
131
+ return (tsRow) => enableRowSelection({
132
+ id: tsRow.id,
133
+ original: tsRow.original,
134
+ index: tsRow.index,
135
+ getIsSelected: () => tsRow.getIsSelected(),
136
+ getCanSelect: () => true,
137
+ toggleSelected: (value) => tsRow.toggleSelected(value)
138
+ });
139
+ }
140
+ return enableRowSelection ?? true;
141
+ }, [enableRowSelection]);
142
+ const table = (0, import_react_table.useReactTable)({
143
+ data,
144
+ columns: engineColumns,
145
+ state: {
146
+ pagination,
147
+ sorting,
148
+ globalFilter,
149
+ columnFilters,
150
+ rowSelection,
151
+ columnVisibility
152
+ },
153
+ getRowId,
154
+ enableRowSelection: enableRowSelectionOption,
155
+ enableMultiRowSelection,
156
+ enableMultiSort,
157
+ enableSortingRemoval: true,
158
+ sortDescFirst: false,
159
+ manualPagination,
160
+ manualSorting,
161
+ manualFiltering,
162
+ ...manualPagination && rowCount != null ? { rowCount } : {},
163
+ // Page-reset on filter change is done explicitly in the setters below so
164
+ // it stays synchronous and matches FacetUI's historical behaviour.
165
+ autoResetPageIndex: false,
166
+ globalFilterFn: facetTextFilter,
167
+ getCoreRowModel: (0, import_react_table.getCoreRowModel)(),
168
+ getFilteredRowModel: (0, import_react_table.getFilteredRowModel)(),
169
+ getSortedRowModel: (0, import_react_table.getSortedRowModel)(),
170
+ getPaginationRowModel: (0, import_react_table.getPaginationRowModel)(),
171
+ onPaginationChange: (updater) => setPaginationState((prev) => {
172
+ const next = (0, import_react_table.functionalUpdate)(updater, prev);
173
+ onPaginationChange?.(next);
174
+ return next;
175
+ }),
176
+ onSortingChange: (updater) => setSortingState((prev) => {
177
+ const next = (0, import_react_table.functionalUpdate)(updater, prev);
178
+ onSortingChange?.(next);
179
+ return next;
180
+ }),
181
+ onGlobalFilterChange: (updater) => setGlobalFilterState((prev) => {
182
+ const next = (0, import_react_table.functionalUpdate)(updater, prev);
183
+ onGlobalFilterChange?.(next);
184
+ return next;
185
+ }),
186
+ onColumnFiltersChange: (updater) => setColumnFiltersState((prev) => {
187
+ const next = (0, import_react_table.functionalUpdate)(updater, prev);
188
+ onColumnFiltersChange?.(next);
189
+ return next;
190
+ }),
191
+ onRowSelectionChange: (updater) => setRowSelectionState((prev) => {
192
+ const next = (0, import_react_table.functionalUpdate)(updater, prev);
193
+ onRowSelectionChange?.(next);
194
+ return next;
195
+ }),
196
+ onColumnVisibilityChange: setColumnVisibilityState
197
+ });
198
+ const setPagination = (0, import_react2.useCallback)(
199
+ (updater) => table.setPagination(updater),
200
+ [table]
201
+ );
202
+ const setSorting = (0, import_react2.useCallback)(
203
+ (updater) => table.setSorting(updater),
204
+ [table]
205
+ );
206
+ const setGlobalFilter = (0, import_react2.useCallback)(
207
+ (value) => {
208
+ table.setGlobalFilter(value);
209
+ table.setPageIndex(0);
210
+ },
211
+ [table]
212
+ );
213
+ const setColumnFilters = (0, import_react2.useCallback)(
214
+ (updater) => {
215
+ table.setColumnFilters(updater);
216
+ table.setPageIndex(0);
217
+ },
218
+ [table]
219
+ );
220
+ const runtimeColumns = (0, import_react2.useMemo)(() => {
221
+ const defById = new Map(resolvedColumns.map((c) => [c.id, c]));
222
+ return table.getAllLeafColumns().map((tsCol) => {
223
+ const def = defById.get(tsCol.id);
224
+ return {
225
+ ...def,
226
+ enableSorting: def.enableSorting ?? true,
227
+ enableHiding: def.enableHiding ?? true,
228
+ getIsSorted: () => tsCol.getIsSorted(),
229
+ toggleSort: (multiSort = false) => tsCol.toggleSorting(void 0, multiSort && enableMultiSort),
230
+ getIsVisible: () => tsCol.getIsVisible(),
231
+ toggleVisibility: (value) => tsCol.toggleVisibility(value),
232
+ getSize: () => def.size ?? 150
233
+ };
234
+ });
235
+ }, [table, resolvedColumns, enableMultiSort]);
236
+ const rows = table.getRowModel().rows.map((r, index) => ({
237
+ id: r.id,
238
+ original: r.original,
239
+ index,
240
+ getIsSelected: () => r.getIsSelected(),
241
+ getCanSelect: () => r.getCanSelect(),
242
+ toggleSelected: (value) => r.toggleSelected(value)
243
+ }));
244
+ const getIsAllRowsSelected = (0, import_react2.useCallback)(
245
+ () => table.getIsAllPageRowsSelected(),
246
+ [table]
247
+ );
248
+ const getIsSomeRowsSelected = (0, import_react2.useCallback)(
249
+ () => table.getIsSomePageRowsSelected(),
250
+ [table]
251
+ );
252
+ const toggleAllRowsSelected = (0, import_react2.useCallback)(
253
+ (value) => table.toggleAllPageRowsSelected(value),
254
+ [table]
255
+ );
256
+ const enginePageCount = table.getPageCount();
257
+ return {
258
+ rows,
259
+ columns: runtimeColumns,
260
+ pagination: table.getState().pagination,
261
+ sorting: table.getState().sorting,
262
+ rowSelection: table.getState().rowSelection,
263
+ globalFilter,
264
+ columnFilters,
265
+ // TanStack returns -1 when a manual page count is unknown; FacetUI has
266
+ // always reported 0 in that case.
267
+ pageCount: enginePageCount < 0 ? 0 : enginePageCount,
268
+ filteredRowCount: manualFiltering ? data.length : table.getFilteredRowModel().rows.length,
269
+ getIsAllRowsSelected,
270
+ getIsSomeRowsSelected,
271
+ toggleAllRowsSelected,
272
+ setSorting,
273
+ setPagination,
274
+ setGlobalFilter,
275
+ setColumnFilters
276
+ };
277
+ }
278
+
279
+ // src/components/data-table/primitives/DataTableToolbar.tsx
280
+ var import_lucide_react2 = require("lucide-react");
281
+
282
+ // src/components/data-table/primitives/DataTableColumnToggle.tsx
283
+ var import_react_aria_components = require("react-aria-components");
284
+ var import_lucide_react = require("lucide-react");
285
+
286
+ // src/components/data-table/utils.ts
287
+ var import_clsx = require("clsx");
288
+ var import_tailwind_merge = require("tailwind-merge");
289
+ function cn(...inputs) {
290
+ return (0, import_tailwind_merge.twMerge)((0, import_clsx.clsx)(inputs));
291
+ }
292
+
293
+ // src/components/data-table/primitives/DataTableColumnToggle.tsx
294
+ var import_jsx_runtime = require("react/jsx-runtime");
295
+ function DataTableColumnToggle({
296
+ table
297
+ }) {
298
+ const toggleableColumns = table.columns.filter(
299
+ (col) => col.enableHiding !== false && col.id !== "__select__"
300
+ );
301
+ const selectedKeys = new Set(
302
+ toggleableColumns.filter((col) => col.getIsVisible()).map((col) => col.id)
303
+ );
304
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_react_aria_components.MenuTrigger, { children: [
305
+ /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(
306
+ import_react_aria_components.Button,
307
+ {
308
+ "aria-label": "Toggle column visibility",
309
+ className: cn(
310
+ "inline-flex h-9 items-center gap-1.5 rounded-md border border-input",
311
+ "bg-background px-3 text-sm shadow-xs",
312
+ "transition-colors hover:bg-accent hover:text-accent-foreground",
313
+ "data-[focus-visible]:outline-hidden data-[focus-visible]:ring-2 data-[focus-visible]:ring-ring",
314
+ "data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50"
315
+ ),
316
+ children: [
317
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.Columns3Icon, { className: "h-4 w-4", "aria-hidden": "true" }),
318
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "hidden sm:inline", children: "Columns" })
319
+ ]
320
+ }
321
+ ),
322
+ /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
323
+ import_react_aria_components.Popover,
324
+ {
325
+ placement: "bottom end",
326
+ offset: 4,
327
+ className: cn(
328
+ "z-50 min-w-[160px] overflow-hidden rounded-md border border-border",
329
+ "bg-popover p-1 text-popover-foreground shadow-md",
330
+ "data-[entering]:animate-in data-[entering]:fade-in-0 data-[entering]:zoom-in-95",
331
+ "data-[exiting]:animate-out data-[exiting]:fade-out-0 data-[exiting]:zoom-out-95"
332
+ ),
333
+ children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
334
+ import_react_aria_components.Menu,
335
+ {
336
+ "aria-label": "Toggle columns",
337
+ selectionMode: "multiple",
338
+ selectedKeys,
339
+ onSelectionChange: (keys) => {
340
+ if (keys === "all") return;
341
+ for (const col of toggleableColumns) {
342
+ col.toggleVisibility(keys.has(col.id));
343
+ }
344
+ },
345
+ className: "outline-hidden",
346
+ children: toggleableColumns.map((col) => {
347
+ const label = typeof col.header === "string" ? col.header : String(col.id).replace(/_/g, " ");
348
+ return /* @__PURE__ */ (0, import_jsx_runtime.jsx)(
349
+ import_react_aria_components.MenuItem,
350
+ {
351
+ id: col.id,
352
+ textValue: label,
353
+ className: cn(
354
+ "relative flex cursor-pointer select-none items-center",
355
+ "rounded-xs py-1.5 pl-8 pr-2 text-sm capitalize",
356
+ "outline-hidden transition-colors",
357
+ "data-[focused]:bg-accent data-[focused]:text-accent-foreground",
358
+ "data-[disabled]:pointer-events-none data-[disabled]:opacity-50"
359
+ ),
360
+ children: ({ isSelected }) => /* @__PURE__ */ (0, import_jsx_runtime.jsxs)(import_jsx_runtime.Fragment, { children: [
361
+ isSelected && /* @__PURE__ */ (0, import_jsx_runtime.jsx)("span", { className: "absolute left-2 flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime.jsx)(import_lucide_react.CheckIcon, { className: "h-3.5 w-3.5", "aria-hidden": "true" }) }),
362
+ label
363
+ ] })
364
+ },
365
+ col.id
366
+ );
367
+ })
368
+ }
369
+ )
370
+ }
371
+ )
372
+ ] });
373
+ }
374
+
375
+ // src/components/data-table/primitives/DataTableToolbar.tsx
376
+ var import_jsx_runtime2 = require("react/jsx-runtime");
377
+ function DataTableToolbar({
378
+ table,
379
+ classNames
380
+ }) {
381
+ const hasFilter = table.globalFilter.length > 0;
382
+ return /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
383
+ "div",
384
+ {
385
+ role: "toolbar",
386
+ "aria-label": "Table controls",
387
+ className: cn(
388
+ "flex flex-wrap items-center justify-between gap-2",
389
+ classNames?.toolbar
390
+ ),
391
+ children: [
392
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "relative flex-1 min-w-[200px] max-w-sm", children: [
393
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
394
+ import_lucide_react2.SearchIcon,
395
+ {
396
+ className: "absolute left-2.5 top-1/2 h-4 w-4 -translate-y-1/2 text-muted-foreground",
397
+ "aria-hidden": "true"
398
+ }
399
+ ),
400
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
401
+ "input",
402
+ {
403
+ type: "search",
404
+ role: "searchbox",
405
+ "aria-label": "Search all columns",
406
+ placeholder: "Search\u2026",
407
+ value: table.globalFilter,
408
+ onChange: (e) => table.setGlobalFilter(e.target.value),
409
+ className: cn(
410
+ "h-9 w-full rounded-md border border-input bg-background pl-8",
411
+ hasFilter ? "pr-8" : "pr-3",
412
+ "text-sm shadow-xs placeholder:text-muted-foreground",
413
+ "focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring",
414
+ "disabled:cursor-not-allowed disabled:opacity-50"
415
+ )
416
+ }
417
+ ),
418
+ hasFilter && /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(
419
+ "button",
420
+ {
421
+ type: "button",
422
+ onClick: () => table.setGlobalFilter(""),
423
+ "aria-label": "Clear search",
424
+ className: cn(
425
+ "absolute right-2 top-1/2 -translate-y-1/2 rounded-sm",
426
+ "text-muted-foreground hover:text-foreground",
427
+ "focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring"
428
+ ),
429
+ children: /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(import_lucide_react2.XIcon, { className: "h-3.5 w-3.5", "aria-hidden": "true" })
430
+ }
431
+ )
432
+ ] }),
433
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)("div", { className: "flex items-center gap-2", children: [
434
+ hasFilter && /* @__PURE__ */ (0, import_jsx_runtime2.jsxs)(
435
+ "p",
436
+ {
437
+ role: "status",
438
+ "aria-live": "polite",
439
+ "aria-atomic": "true",
440
+ className: "text-xs text-muted-foreground tabular-nums",
441
+ children: [
442
+ table.filteredRowCount.toLocaleString(),
443
+ " result",
444
+ table.filteredRowCount !== 1 ? "s" : ""
445
+ ]
446
+ }
447
+ ),
448
+ /* @__PURE__ */ (0, import_jsx_runtime2.jsx)(DataTableColumnToggle, { table })
449
+ ] })
450
+ ]
451
+ }
452
+ );
453
+ }
454
+
455
+ // src/components/data-table/primitives/DataTableHeader.tsx
456
+ var import_react_aria_components2 = require("react-aria-components");
457
+ var import_lucide_react3 = require("lucide-react");
458
+ var import_jsx_runtime3 = require("react/jsx-runtime");
459
+ var densityPaddingTh = {
460
+ compact: "h-8 px-2",
461
+ default: "h-10 px-3",
462
+ comfortable: "h-12 px-4"
463
+ };
464
+ function DataTableHeader({
465
+ table,
466
+ density,
467
+ classNames
468
+ }) {
469
+ const visibleColumns = table.columns.filter((col) => col.getIsVisible());
470
+ const hasSelection = table.columns.some((col) => col.id === "__select__");
471
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
472
+ "thead",
473
+ {
474
+ className: cn("border-b border-border bg-muted/50", classNames?.thead),
475
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
476
+ "tr",
477
+ {
478
+ role: "row",
479
+ className: cn(classNames?.theadRow),
480
+ children: [
481
+ hasSelection && /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
482
+ "th",
483
+ {
484
+ role: "columnheader",
485
+ scope: "col",
486
+ "aria-label": "Select all rows",
487
+ className: cn(
488
+ "w-10 text-center align-middle",
489
+ densityPaddingTh[density],
490
+ classNames?.th
491
+ ),
492
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SelectAllCheckbox, { table })
493
+ }
494
+ ),
495
+ visibleColumns.filter((col) => col.id !== "__select__").map((col) => {
496
+ const sortDir = col.getIsSorted();
497
+ const canSort = col.enableSorting ?? true;
498
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
499
+ "th",
500
+ {
501
+ role: "columnheader",
502
+ scope: "col",
503
+ "aria-sort": sortDir === "asc" ? "ascending" : sortDir === "desc" ? "descending" : canSort ? "none" : void 0,
504
+ style: { width: col.getSize() },
505
+ className: cn(
506
+ "text-left align-middle font-medium text-muted-foreground",
507
+ densityPaddingTh[density],
508
+ classNames?.th
509
+ ),
510
+ children: canSort ? /* @__PURE__ */ (0, import_jsx_runtime3.jsxs)(
511
+ "button",
512
+ {
513
+ type: "button",
514
+ onClick: () => col.toggleSort(),
515
+ className: cn(
516
+ "flex items-center gap-1.5 rounded-xs",
517
+ "transition-colors hover:text-foreground focus-visible:outline-hidden",
518
+ "focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-1",
519
+ sortDir && "text-foreground"
520
+ ),
521
+ "aria-label": `Sort by ${String(col.id)}${sortDir === "asc" ? ", currently ascending" : sortDir === "desc" ? ", currently descending" : ""}`,
522
+ children: [
523
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(HeaderContent, { col, table }),
524
+ /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(SortIcon, { direction: sortDir })
525
+ ]
526
+ }
527
+ ) : /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(HeaderContent, { col, table })
528
+ },
529
+ col.id
530
+ );
531
+ })
532
+ ]
533
+ }
534
+ )
535
+ }
536
+ );
537
+ }
538
+ function HeaderContent({
539
+ col,
540
+ table
541
+ }) {
542
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_jsx_runtime3.Fragment, { children: typeof col.header === "function" ? col.header({ column: col, table }) : col.header });
543
+ }
544
+ function SortIcon({ direction }) {
545
+ const cls = "h-3.5 w-3.5 shrink-0";
546
+ if (direction === "asc") return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.ChevronUpIcon, { className: cls, "aria-hidden": "true" });
547
+ if (direction === "desc") return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.ChevronDownIcon, { className: cls, "aria-hidden": "true" });
548
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.ChevronsUpDownIcon, { className: cn(cls, "opacity-40"), "aria-hidden": "true" });
549
+ }
550
+ function SelectAllCheckbox({ table }) {
551
+ const isAll = table.getIsAllRowsSelected();
552
+ const isSome = table.getIsSomeRowsSelected();
553
+ return /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(
554
+ import_react_aria_components2.Checkbox,
555
+ {
556
+ "aria-label": "Select all rows",
557
+ isSelected: isAll,
558
+ isIndeterminate: isSome && !isAll,
559
+ onChange: (checked) => table.toggleAllRowsSelected(checked),
560
+ className: "group mx-auto flex h-4 w-4 shrink-0 cursor-pointer items-center justify-center",
561
+ children: /* @__PURE__ */ (0, import_jsx_runtime3.jsx)("span", { className: checkboxBox, children: isSome && !isAll ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.MinusIcon, { className: "h-3 w-3", "aria-hidden": "true" }) : isAll ? /* @__PURE__ */ (0, import_jsx_runtime3.jsx)(import_lucide_react3.CheckIcon, { className: "h-3 w-3", "aria-hidden": "true" }) : null })
562
+ }
563
+ );
564
+ }
565
+ var checkboxBox = cn(
566
+ "flex h-4 w-4 items-center justify-center rounded-sm border border-primary shadow-sm",
567
+ "group-data-[focus-visible]:outline-hidden group-data-[focus-visible]:ring-2 group-data-[focus-visible]:ring-ring",
568
+ "group-data-[selected]:bg-primary group-data-[selected]:text-primary-foreground",
569
+ "group-data-[indeterminate]:bg-primary group-data-[indeterminate]:text-primary-foreground"
570
+ );
571
+
572
+ // src/components/data-table/primitives/DataTableBody.tsx
573
+ var import_react_aria_components3 = require("react-aria-components");
574
+ var import_lucide_react4 = require("lucide-react");
575
+ var import_jsx_runtime4 = require("react/jsx-runtime");
576
+ var densityPaddingTd = {
577
+ compact: "py-1 px-2",
578
+ default: "py-2 px-3",
579
+ comfortable: "py-3 px-4"
580
+ };
581
+ function DataTableBody({
582
+ table,
583
+ density,
584
+ isLoading,
585
+ renderEmpty,
586
+ renderRowWrapper,
587
+ classNames
588
+ }) {
589
+ const visibleColumns = table.columns.filter(
590
+ (col) => col.getIsVisible() && col.id !== "__select__"
591
+ );
592
+ const hasSelection = table.columns.some((col) => col.id === "__select__");
593
+ const colSpan = visibleColumns.length + (hasSelection ? 1 : 0);
594
+ if (!isLoading && table.rows.length === 0) {
595
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("tbody", { children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("tr", { role: "row", children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
596
+ "td",
597
+ {
598
+ colSpan,
599
+ className: "h-40 text-center align-middle text-muted-foreground",
600
+ children: renderEmpty ? renderEmpty() : /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(DefaultEmpty, {})
601
+ }
602
+ ) }) });
603
+ }
604
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("tbody", { className: cn("[&_tr:last-child]:border-0", classNames?.tbody), children: table.rows.map((row, rowIndex) => {
605
+ const isSelected = row.getIsSelected();
606
+ const ariaRowIndex = table.pagination.pageIndex * table.pagination.pageSize + rowIndex + 2;
607
+ const rowContent = /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)(
608
+ "tr",
609
+ {
610
+ role: "row",
611
+ "aria-rowindex": ariaRowIndex,
612
+ "aria-selected": hasSelection ? isSelected : void 0,
613
+ "data-state": isSelected ? "selected" : void 0,
614
+ className: cn(
615
+ "border-b border-border transition-colors",
616
+ "hover:bg-muted/50",
617
+ isSelected && "bg-muted",
618
+ classNames?.tr
619
+ ),
620
+ children: [
621
+ hasSelection && /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
622
+ "td",
623
+ {
624
+ role: "gridcell",
625
+ className: cn(
626
+ "w-10 text-center align-middle",
627
+ densityPaddingTd[density],
628
+ classNames?.td
629
+ ),
630
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(RowCheckbox, { row })
631
+ }
632
+ ),
633
+ visibleColumns.map((col) => {
634
+ const value = getCellValue(col, row.original);
635
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
636
+ "td",
637
+ {
638
+ role: "gridcell",
639
+ className: cn(
640
+ "align-middle",
641
+ densityPaddingTd[density],
642
+ classNames?.td
643
+ ),
644
+ children: col.cell ? col.cell({ row, column: col, value, table }) : renderValue(value)
645
+ },
646
+ col.id
647
+ );
648
+ })
649
+ ]
650
+ },
651
+ row.id
652
+ );
653
+ return renderRowWrapper ? renderRowWrapper(row, rowContent) : rowContent;
654
+ }) });
655
+ }
656
+ function RowCheckbox({ row }) {
657
+ const canSelect = row.getCanSelect();
658
+ const selected = row.getIsSelected();
659
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
660
+ import_react_aria_components3.Checkbox,
661
+ {
662
+ "aria-label": `Select row ${row.index + 1}`,
663
+ isSelected: selected,
664
+ isDisabled: !canSelect,
665
+ onChange: (checked) => row.toggleSelected(checked),
666
+ className: cn(
667
+ "group mx-auto flex h-4 w-4 shrink-0 items-center justify-center",
668
+ canSelect ? "cursor-pointer" : "cursor-not-allowed opacity-50"
669
+ ),
670
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("span", { className: checkboxBox, children: selected ? /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(import_lucide_react4.CheckIcon, { className: "h-3 w-3", "aria-hidden": "true" }) : null })
671
+ }
672
+ );
673
+ }
674
+ function DefaultEmpty() {
675
+ return /* @__PURE__ */ (0, import_jsx_runtime4.jsxs)("div", { className: "flex flex-col items-center gap-2 py-10", children: [
676
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
677
+ "svg",
678
+ {
679
+ className: "h-12 w-12 text-muted-foreground/40",
680
+ xmlns: "http://www.w3.org/2000/svg",
681
+ fill: "none",
682
+ viewBox: "0 0 24 24",
683
+ stroke: "currentColor",
684
+ "aria-hidden": "true",
685
+ children: /* @__PURE__ */ (0, import_jsx_runtime4.jsx)(
686
+ "path",
687
+ {
688
+ strokeLinecap: "round",
689
+ strokeLinejoin: "round",
690
+ strokeWidth: 1.5,
691
+ d: "M3 8l7.89 5.26a2 2 0 002.22 0L21 8M5 19h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v10a2 2 0 002 2z"
692
+ }
693
+ )
694
+ }
695
+ ),
696
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "text-sm font-medium", children: "No results found" }),
697
+ /* @__PURE__ */ (0, import_jsx_runtime4.jsx)("p", { className: "text-xs text-muted-foreground", children: "Try adjusting your search or filters." })
698
+ ] });
699
+ }
700
+ function renderValue(value) {
701
+ if (value === null || value === void 0) return null;
702
+ if (typeof value === "boolean") return value ? "Yes" : "No";
703
+ return String(value);
704
+ }
705
+ function getCellValue(col, row) {
706
+ if (col.accessorFn) return col.accessorFn(row);
707
+ if (col.accessorKey !== void 0) return row[col.accessorKey];
708
+ return null;
709
+ }
710
+
711
+ // src/components/data-table/primitives/DataTablePagination.tsx
712
+ var import_react_aria_components4 = require("react-aria-components");
713
+ var import_lucide_react5 = require("lucide-react");
714
+ var import_jsx_runtime5 = require("react/jsx-runtime");
715
+ function DataTablePagination({
716
+ table,
717
+ pageSizeOptions,
718
+ classNames
719
+ }) {
720
+ const { pageIndex, pageSize } = table.pagination;
721
+ const canPreviousPage = pageIndex > 0;
722
+ const canNextPage = pageIndex < table.pageCount - 1;
723
+ const selectedCount = Object.keys(table.rowSelection).length;
724
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
725
+ "div",
726
+ {
727
+ role: "navigation",
728
+ "aria-label": "Table pagination",
729
+ className: cn(
730
+ "flex flex-wrap items-center justify-between gap-4 text-sm",
731
+ classNames?.pagination
732
+ ),
733
+ children: [
734
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
735
+ "p",
736
+ {
737
+ "aria-live": "polite",
738
+ "aria-atomic": "true",
739
+ className: "text-muted-foreground tabular-nums",
740
+ children: selectedCount > 0 ? `${selectedCount.toLocaleString()} of ${table.filteredRowCount.toLocaleString()} row(s) selected` : `${table.filteredRowCount.toLocaleString()} row(s) total`
741
+ }
742
+ ),
743
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-4", children: [
744
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-2", children: [
745
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
746
+ "label",
747
+ {
748
+ id: "rows-per-page-label",
749
+ className: "text-muted-foreground whitespace-nowrap",
750
+ children: "Rows per page"
751
+ }
752
+ ),
753
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
754
+ PageSizeSelect,
755
+ {
756
+ value: pageSize,
757
+ options: pageSizeOptions,
758
+ onChange: (size) => table.setPagination((prev) => ({
759
+ ...prev,
760
+ pageSize: size,
761
+ pageIndex: 0
762
+ }))
763
+ }
764
+ )
765
+ ] }),
766
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
767
+ "p",
768
+ {
769
+ "aria-live": "polite",
770
+ "aria-atomic": "true",
771
+ className: "text-muted-foreground whitespace-nowrap tabular-nums",
772
+ children: [
773
+ "Page ",
774
+ (pageIndex + 1).toLocaleString(),
775
+ " of",
776
+ " ",
777
+ Math.max(1, table.pageCount).toLocaleString()
778
+ ]
779
+ }
780
+ ),
781
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)("div", { className: "flex items-center gap-1", role: "group", "aria-label": "Page navigation", children: [
782
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
783
+ NavButton,
784
+ {
785
+ onClick: () => table.setPagination((prev) => ({ ...prev, pageIndex: 0 })),
786
+ disabled: !canPreviousPage,
787
+ "aria-label": "Go to first page",
788
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react5.ChevronsLeftIcon, { className: "h-4 w-4", "aria-hidden": "true" })
789
+ }
790
+ ),
791
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
792
+ NavButton,
793
+ {
794
+ onClick: () => table.setPagination((prev) => ({
795
+ ...prev,
796
+ pageIndex: prev.pageIndex - 1
797
+ })),
798
+ disabled: !canPreviousPage,
799
+ "aria-label": "Go to previous page",
800
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react5.ChevronLeftIcon, { className: "h-4 w-4", "aria-hidden": "true" })
801
+ }
802
+ ),
803
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
804
+ NavButton,
805
+ {
806
+ onClick: () => table.setPagination((prev) => ({
807
+ ...prev,
808
+ pageIndex: prev.pageIndex + 1
809
+ })),
810
+ disabled: !canNextPage,
811
+ "aria-label": "Go to next page",
812
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react5.ChevronRightIcon, { className: "h-4 w-4", "aria-hidden": "true" })
813
+ }
814
+ ),
815
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
816
+ NavButton,
817
+ {
818
+ onClick: () => table.setPagination((prev) => ({
819
+ ...prev,
820
+ pageIndex: table.pageCount - 1
821
+ })),
822
+ disabled: !canNextPage,
823
+ "aria-label": "Go to last page",
824
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react5.ChevronsRightIcon, { className: "h-4 w-4", "aria-hidden": "true" })
825
+ }
826
+ )
827
+ ] })
828
+ ] })
829
+ ]
830
+ }
831
+ );
832
+ }
833
+ function NavButton({ children, disabled, ...props }) {
834
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
835
+ "button",
836
+ {
837
+ type: "button",
838
+ disabled,
839
+ ...props,
840
+ className: cn(
841
+ "inline-flex h-8 w-8 items-center justify-center rounded-md border border-input",
842
+ "bg-background text-sm shadow-xs transition-colors",
843
+ "hover:bg-accent hover:text-accent-foreground",
844
+ "focus-visible:outline-hidden focus-visible:ring-2 focus-visible:ring-ring",
845
+ "disabled:pointer-events-none disabled:opacity-50"
846
+ ),
847
+ children
848
+ }
849
+ );
850
+ }
851
+ function PageSizeSelect({ value, options, onChange }) {
852
+ return /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
853
+ import_react_aria_components4.Select,
854
+ {
855
+ "aria-label": "Rows per page",
856
+ selectedKey: String(value),
857
+ onSelectionChange: (key) => onChange(Number(key)),
858
+ children: [
859
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(
860
+ import_react_aria_components4.Button,
861
+ {
862
+ "aria-labelledby": "rows-per-page-label",
863
+ className: cn(
864
+ "inline-flex h-8 items-center justify-between gap-1 rounded-md border border-input",
865
+ "bg-background px-2.5 text-sm shadow-xs",
866
+ "transition-colors hover:bg-accent hover:text-accent-foreground",
867
+ "data-[focus-visible]:outline-hidden data-[focus-visible]:ring-2 data-[focus-visible]:ring-ring",
868
+ "data-[disabled]:cursor-not-allowed data-[disabled]:opacity-50"
869
+ ),
870
+ children: [
871
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_aria_components4.SelectValue, {}),
872
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react5.ChevronDownIcon, { className: "h-3.5 w-3.5 opacity-50", "aria-hidden": "true" })
873
+ ]
874
+ }
875
+ ),
876
+ /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
877
+ import_react_aria_components4.Popover,
878
+ {
879
+ offset: 4,
880
+ className: cn(
881
+ "z-50 overflow-hidden rounded-md border border-border",
882
+ "bg-popover text-popover-foreground shadow-md",
883
+ "data-[entering]:animate-in data-[entering]:fade-in-0 data-[entering]:zoom-in-95",
884
+ "data-[exiting]:animate-out data-[exiting]:fade-out-0 data-[exiting]:zoom-out-95"
885
+ ),
886
+ children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_react_aria_components4.ListBox, { className: "p-1 outline-hidden", children: options.map((opt) => /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(
887
+ import_react_aria_components4.ListBoxItem,
888
+ {
889
+ id: String(opt),
890
+ textValue: String(opt),
891
+ className: cn(
892
+ "relative flex cursor-pointer select-none items-center",
893
+ "rounded-xs py-1.5 pl-8 pr-2 text-sm",
894
+ "outline-hidden transition-colors",
895
+ "data-[focused]:bg-accent data-[focused]:text-accent-foreground",
896
+ "data-[disabled]:pointer-events-none data-[disabled]:opacity-50"
897
+ ),
898
+ children: ({ isSelected }) => /* @__PURE__ */ (0, import_jsx_runtime5.jsxs)(import_jsx_runtime5.Fragment, { children: [
899
+ isSelected && /* @__PURE__ */ (0, import_jsx_runtime5.jsx)("span", { className: "absolute left-2 flex items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime5.jsx)(import_lucide_react5.CheckIcon, { className: "h-3.5 w-3.5", "aria-hidden": "true" }) }),
900
+ opt
901
+ ] })
902
+ },
903
+ opt
904
+ )) })
905
+ }
906
+ )
907
+ ]
908
+ }
909
+ );
910
+ }
911
+
912
+ // src/components/data-table/core/DataTable.tsx
913
+ var import_jsx_runtime6 = require("react/jsx-runtime");
914
+ function DataTable({
915
+ renderToolbar,
916
+ renderEmpty,
917
+ renderLoading,
918
+ renderRowWrapper,
919
+ isLoading = false,
920
+ density = "default",
921
+ classNames,
922
+ style,
923
+ "aria-label": ariaLabel,
924
+ "aria-describedby": ariaDescribedBy,
925
+ ...rest
926
+ }) {
927
+ const table = useDataTable({ ...rest, renderToolbar, renderEmpty, renderLoading, renderRowWrapper });
928
+ return (
929
+ // Cast is safe: context consumers are co-located and always receive
930
+ // the same TData shape via the typed useDataTableContext<TData> hook.
931
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DataTableContext.Provider, { value: table, children: /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
932
+ "div",
933
+ {
934
+ className: cn("flex w-full flex-col gap-3", classNames?.root),
935
+ style,
936
+ children: [
937
+ renderToolbar ? renderToolbar(table) : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(DataTableToolbar, { table, classNames }),
938
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)("div", { className: "relative overflow-hidden rounded-md border border-border", children: [
939
+ isLoading && (renderLoading ? renderLoading() : /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
940
+ "div",
941
+ {
942
+ role: "status",
943
+ "aria-live": "polite",
944
+ "aria-label": "Loading table data",
945
+ className: "absolute inset-0 z-10 flex items-center justify-center\n bg-background/70 backdrop-blur-[2px]",
946
+ children: /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(LoadingSpinner, {})
947
+ }
948
+ )),
949
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
950
+ "table",
951
+ {
952
+ role: "grid",
953
+ "aria-label": ariaLabel,
954
+ "aria-describedby": ariaDescribedBy,
955
+ "aria-busy": isLoading,
956
+ "aria-rowcount": rest.rowCount ?? rest.data.length,
957
+ className: cn("w-full caption-bottom text-sm", classNames?.table),
958
+ children: [
959
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
960
+ DataTableHeader,
961
+ {
962
+ table,
963
+ density,
964
+ classNames
965
+ }
966
+ ),
967
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
968
+ DataTableBody,
969
+ {
970
+ table,
971
+ density,
972
+ isLoading,
973
+ renderEmpty,
974
+ renderRowWrapper,
975
+ classNames
976
+ }
977
+ )
978
+ ]
979
+ }
980
+ )
981
+ ] }),
982
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
983
+ DataTablePagination,
984
+ {
985
+ table,
986
+ pageSizeOptions: rest.pageSizeOptions ?? [10, 25, 50, 100],
987
+ classNames
988
+ }
989
+ )
990
+ ]
991
+ }
992
+ ) })
993
+ );
994
+ }
995
+ function LoadingSpinner() {
996
+ return /* @__PURE__ */ (0, import_jsx_runtime6.jsxs)(
997
+ "svg",
998
+ {
999
+ className: "h-8 w-8 animate-spin text-primary",
1000
+ xmlns: "http://www.w3.org/2000/svg",
1001
+ fill: "none",
1002
+ viewBox: "0 0 24 24",
1003
+ "aria-hidden": "true",
1004
+ children: [
1005
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1006
+ "circle",
1007
+ {
1008
+ className: "opacity-25",
1009
+ cx: "12",
1010
+ cy: "12",
1011
+ r: "10",
1012
+ stroke: "currentColor",
1013
+ strokeWidth: "4"
1014
+ }
1015
+ ),
1016
+ /* @__PURE__ */ (0, import_jsx_runtime6.jsx)(
1017
+ "path",
1018
+ {
1019
+ className: "opacity-75",
1020
+ fill: "currentColor",
1021
+ d: "M4 12a8 8 0 018-8v4a4 4 0 00-4 4H4z"
1022
+ }
1023
+ )
1024
+ ]
1025
+ }
1026
+ );
1027
+ }
1028
+
1029
+ // src/components/data-table/headless/useColumnSort.ts
1030
+ var import_react3 = require("react");
1031
+ function useColumnSort({
1032
+ enableMultiSort = false,
1033
+ onSortingChange
1034
+ } = {}) {
1035
+ const [sorting, setSortingInternal] = (0, import_react3.useState)([]);
1036
+ const setSorting = (0, import_react3.useCallback)(
1037
+ (updater) => {
1038
+ setSortingInternal((prev) => {
1039
+ const next = typeof updater === "function" ? updater(prev) : updater;
1040
+ onSortingChange?.(next);
1041
+ return next;
1042
+ });
1043
+ },
1044
+ [onSortingChange]
1045
+ );
1046
+ const toggleSort = (0, import_react3.useCallback)(
1047
+ (columnId, multiSort = false) => {
1048
+ setSorting((prev) => {
1049
+ const existing = prev.find((s) => s.id === columnId);
1050
+ const useMulti = multiSort && enableMultiSort;
1051
+ if (!existing) {
1052
+ return useMulti ? [...prev, { id: columnId, desc: false }] : [{ id: columnId, desc: false }];
1053
+ }
1054
+ if (!existing.desc) {
1055
+ return prev.map(
1056
+ (s) => s.id === columnId ? { ...s, desc: true } : s
1057
+ );
1058
+ }
1059
+ return prev.filter((s) => s.id !== columnId);
1060
+ });
1061
+ },
1062
+ [enableMultiSort, setSorting]
1063
+ );
1064
+ const clearSort = (0, import_react3.useCallback)(() => setSorting([]), [setSorting]);
1065
+ return { sorting, setSorting, toggleSort, clearSort };
1066
+ }
1067
+
1068
+ // src/components/data-table/headless/usePagination.ts
1069
+ var import_react4 = require("react");
1070
+ function usePagination({
1071
+ initialPageSize = 10,
1072
+ onPaginationChange
1073
+ } = {}) {
1074
+ const [pagination, setPaginationInternal] = (0, import_react4.useState)({
1075
+ pageIndex: 0,
1076
+ pageSize: initialPageSize
1077
+ });
1078
+ const setPagination = (0, import_react4.useCallback)(
1079
+ (updater) => {
1080
+ setPaginationInternal((prev) => {
1081
+ const next = typeof updater === "function" ? updater(prev) : updater;
1082
+ onPaginationChange?.(next);
1083
+ return next;
1084
+ });
1085
+ },
1086
+ [onPaginationChange]
1087
+ );
1088
+ return {
1089
+ pagination,
1090
+ setPagination,
1091
+ goToPage: (pageIndex) => setPagination((p) => ({ ...p, pageIndex })),
1092
+ goToFirstPage: () => setPagination((p) => ({ ...p, pageIndex: 0 })),
1093
+ goToLastPage: (pageCount) => setPagination((p) => ({ ...p, pageIndex: pageCount - 1 })),
1094
+ nextPage: () => setPagination((p) => ({ ...p, pageIndex: p.pageIndex + 1 })),
1095
+ previousPage: () => setPagination((p) => ({ ...p, pageIndex: Math.max(0, p.pageIndex - 1) })),
1096
+ setPageSize: (size) => setPagination((p) => ({ ...p, pageSize: size, pageIndex: 0 }))
1097
+ };
1098
+ }
1099
+
1100
+ // src/components/data-table/headless/useRowSelection.ts
1101
+ var import_react5 = require("react");
1102
+ function useRowSelection({
1103
+ enableMultiRowSelection = true,
1104
+ onRowSelectionChange
1105
+ } = {}) {
1106
+ const [rowSelection, setRowSelectionInternal] = (0, import_react5.useState)({});
1107
+ const setRowSelection = (0, import_react5.useCallback)(
1108
+ (next) => {
1109
+ setRowSelectionInternal(next);
1110
+ onRowSelectionChange?.(next);
1111
+ },
1112
+ [onRowSelectionChange]
1113
+ );
1114
+ const toggleRow = (0, import_react5.useCallback)(
1115
+ (rowId, value) => {
1116
+ setRowSelectionInternal((prev) => {
1117
+ const shouldSelect = value ?? !prev[rowId];
1118
+ let next;
1119
+ if (shouldSelect) {
1120
+ next = enableMultiRowSelection ? { ...prev, [rowId]: true } : { [rowId]: true };
1121
+ } else {
1122
+ const { [rowId]: _removed, ...rest } = prev;
1123
+ next = rest;
1124
+ }
1125
+ onRowSelectionChange?.(next);
1126
+ return next;
1127
+ });
1128
+ },
1129
+ [enableMultiRowSelection, onRowSelectionChange]
1130
+ );
1131
+ const toggleAllRows = (0, import_react5.useCallback)(
1132
+ (rows, value) => {
1133
+ const allSelected = rows.every((r) => r.getIsSelected());
1134
+ const next = value ?? !allSelected ? Object.fromEntries(rows.map((r) => [r.id, true])) : {};
1135
+ setRowSelection(next);
1136
+ },
1137
+ [setRowSelection]
1138
+ );
1139
+ return {
1140
+ rowSelection,
1141
+ setRowSelection,
1142
+ toggleRow,
1143
+ toggleAllRows,
1144
+ clearSelection: () => setRowSelection({}),
1145
+ isSelected: (rowId) => !!rowSelection[rowId],
1146
+ selectedCount: Object.keys(rowSelection).length
1147
+ };
1148
+ }
1149
+
1150
+ // src/components/data-table/headless/useColumnVisibility.ts
1151
+ var import_react6 = require("react");
1152
+ function useColumnVisibility(columns) {
1153
+ const [columnVisibility, setColumnVisibility] = (0, import_react6.useState)(
1154
+ () => Object.fromEntries(columns.map((c) => [c.id, true]))
1155
+ );
1156
+ const isVisible = (0, import_react6.useCallback)(
1157
+ (columnId) => columnVisibility[columnId] ?? true,
1158
+ [columnVisibility]
1159
+ );
1160
+ const toggleColumn = (0, import_react6.useCallback)((columnId, value) => {
1161
+ setColumnVisibility((prev) => ({
1162
+ ...prev,
1163
+ [columnId]: value ?? !(prev[columnId] ?? true)
1164
+ }));
1165
+ }, []);
1166
+ const showAll = (0, import_react6.useCallback)(() => {
1167
+ setColumnVisibility(
1168
+ (prev) => Object.fromEntries(Object.keys(prev).map((id) => [id, true]))
1169
+ );
1170
+ }, []);
1171
+ const hideAll = (0, import_react6.useCallback)((hideableIds) => {
1172
+ setColumnVisibility(
1173
+ (prev) => Object.fromEntries(Object.keys(prev).map((id) => [id, !hideableIds.includes(id)]))
1174
+ );
1175
+ }, []);
1176
+ return { columnVisibility, isVisible, toggleColumn, showAll, hideAll };
1177
+ }
1178
+
1179
+ // src/components/data-table/headless/useGlobalFilter.ts
1180
+ var import_react7 = require("react");
1181
+ function useGlobalFilter(columns, { onGlobalFilterChange } = {}) {
1182
+ const [globalFilter, setGlobalFilterInternal] = (0, import_react7.useState)("");
1183
+ const setGlobalFilter = (0, import_react7.useCallback)(
1184
+ (value) => {
1185
+ setGlobalFilterInternal(value);
1186
+ onGlobalFilterChange?.(value);
1187
+ },
1188
+ [onGlobalFilterChange]
1189
+ );
1190
+ const filterData = (0, import_react7.useCallback)(
1191
+ (data) => {
1192
+ if (!globalFilter.trim()) return data;
1193
+ const lower = globalFilter.toLowerCase();
1194
+ return data.filter(
1195
+ (row) => columns.some((col) => {
1196
+ const val = col.accessorFn ? col.accessorFn(row) : col.accessorKey !== void 0 ? row[col.accessorKey] : null;
1197
+ return String(val ?? "").toLowerCase().includes(lower);
1198
+ })
1199
+ );
1200
+ },
1201
+ [columns, globalFilter]
1202
+ );
1203
+ return { globalFilter, setGlobalFilter, filterData };
1204
+ }
1205
+ // Annotate the CommonJS export names for ESM import in node:
1206
+ 0 && (module.exports = {
1207
+ DataTable,
1208
+ DataTableBody,
1209
+ DataTableColumnToggle,
1210
+ DataTableHeader,
1211
+ DataTablePagination,
1212
+ DataTableToolbar,
1213
+ useColumnSort,
1214
+ useColumnVisibility,
1215
+ useDataTable,
1216
+ useDataTableContext,
1217
+ useGlobalFilter,
1218
+ usePagination,
1219
+ useRowSelection
1220
+ });