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