@dynostack/react-grid 0.1.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,2689 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var reactTable = require('@tanstack/react-table');
5
+ var lucideReact = require('lucide-react');
6
+ var clsx = require('clsx');
7
+ var tailwindMerge = require('tailwind-merge');
8
+ var radixUi = require('radix-ui');
9
+ var jsxRuntime = require('react/jsx-runtime');
10
+ var classVarianceAuthority = require('class-variance-authority');
11
+
12
+ function cn(...inputs) {
13
+ return tailwindMerge.twMerge(clsx.clsx(inputs));
14
+ }
15
+ function Checkbox({
16
+ className,
17
+ ...props
18
+ }) {
19
+ return /* @__PURE__ */ jsxRuntime.jsx(
20
+ radixUi.Checkbox.Root,
21
+ {
22
+ "data-slot": "checkbox",
23
+ className: cn(
24
+ "peer inline-flex size-4 shrink-0 items-center justify-center rounded-[5px] border border-input bg-background shadow-xs outline-none transition-colors focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/40 disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:border-primary data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground data-[state=indeterminate]:border-primary data-[state=indeterminate]:bg-primary data-[state=indeterminate]:text-primary-foreground",
25
+ className
26
+ ),
27
+ ...props,
28
+ children: /* @__PURE__ */ jsxRuntime.jsx(
29
+ radixUi.Checkbox.Indicator,
30
+ {
31
+ "data-slot": "checkbox-indicator",
32
+ className: "flex items-center justify-center text-current",
33
+ children: props.checked === "indeterminate" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MinusIcon, { className: "size-3" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckIcon, { className: "size-3" })
34
+ }
35
+ )
36
+ }
37
+ );
38
+ }
39
+ function Skeleton({ className, ...props }) {
40
+ return /* @__PURE__ */ jsxRuntime.jsx(
41
+ "div",
42
+ {
43
+ "data-slot": "skeleton",
44
+ className: cn("animate-pulse rounded-md bg-muted", className),
45
+ ...props
46
+ }
47
+ );
48
+ }
49
+ var buttonVariants = classVarianceAuthority.cva(
50
+ "group/button inline-flex shrink-0 items-center justify-center rounded-lg border border-transparent bg-clip-padding text-sm font-medium whitespace-nowrap transition-all outline-none select-none focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 active:not-aria-[haspopup]:translate-y-px disabled:pointer-events-none disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
51
+ {
52
+ variants: {
53
+ variant: {
54
+ default: "bg-primary text-primary-foreground [a]:hover:bg-primary/80",
55
+ outline: "border-border bg-background hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:border-input dark:bg-input/30 dark:hover:bg-input/50",
56
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80 aria-expanded:bg-secondary aria-expanded:text-secondary-foreground",
57
+ ghost: "hover:bg-muted hover:text-foreground aria-expanded:bg-muted aria-expanded:text-foreground dark:hover:bg-muted/50 data-[size^=icon]:hover:bg-primary/10 data-[size^=icon]:hover:text-primary data-[size^=icon]:hover:[&_svg]:text-primary data-[size^=icon]:aria-expanded:bg-primary/10 data-[size^=icon]:aria-expanded:text-primary data-[size^=icon]:aria-expanded:border data-[size^=icon]:aria-expanded:border-primary/30",
58
+ destructive: "bg-destructive/10 text-destructive hover:bg-destructive/20 focus-visible:border-destructive/40 focus-visible:ring-destructive/20 dark:bg-destructive/20 dark:hover:bg-destructive/30 dark:focus-visible:ring-destructive/40",
59
+ link: "text-primary underline-offset-4 hover:underline"
60
+ },
61
+ size: {
62
+ default: "h-8 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
63
+ xs: "h-6 gap-1 rounded-[min(var(--radius-md),10px)] px-2 text-xs in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3",
64
+ sm: "h-7 gap-1 rounded-[min(var(--radius-md),12px)] px-2.5 text-[0.8rem] in-data-[slot=button-group]:rounded-lg has-data-[icon=inline-end]:pr-1.5 has-data-[icon=inline-start]:pl-1.5 [&_svg:not([class*='size-'])]:size-3.5",
65
+ lg: "h-9 gap-1.5 px-2.5 has-data-[icon=inline-end]:pr-2 has-data-[icon=inline-start]:pl-2",
66
+ icon: "size-8",
67
+ "icon-xs": "size-6 rounded-[min(var(--radius-md),10px)] in-data-[slot=button-group]:rounded-lg [&_svg:not([class*='size-'])]:size-3",
68
+ "icon-sm": "size-7 rounded-[min(var(--radius-md),12px)] in-data-[slot=button-group]:rounded-lg",
69
+ "icon-lg": "size-9"
70
+ }
71
+ },
72
+ defaultVariants: {
73
+ variant: "default",
74
+ size: "default"
75
+ }
76
+ }
77
+ );
78
+ function Button({
79
+ className,
80
+ variant = "default",
81
+ size = "default",
82
+ asChild = false,
83
+ ...props
84
+ }) {
85
+ const Comp = asChild ? radixUi.Slot.Root : "button";
86
+ return /* @__PURE__ */ jsxRuntime.jsx(
87
+ Comp,
88
+ {
89
+ "data-slot": "button",
90
+ "data-variant": variant,
91
+ "data-size": size,
92
+ className: cn(buttonVariants({ variant, size, className })),
93
+ ...props
94
+ }
95
+ );
96
+ }
97
+ function Input({ className, type, ...props }) {
98
+ return /* @__PURE__ */ jsxRuntime.jsx(
99
+ "input",
100
+ {
101
+ type,
102
+ "data-slot": "input",
103
+ className: cn(
104
+ "h-8 w-full min-w-0 rounded-lg border border-input bg-transparent px-2.5 py-1 text-base transition-colors outline-none file:inline-flex file:h-6 file:border-0 file:bg-transparent file:text-sm file:font-medium file:text-foreground placeholder:text-muted-foreground focus-visible:border-ring focus-visible:ring-3 focus-visible:ring-ring/50 disabled:pointer-events-none disabled:cursor-not-allowed disabled:bg-input/50 disabled:opacity-50 aria-invalid:border-destructive aria-invalid:ring-3 aria-invalid:ring-destructive/20 md:text-sm dark:bg-input/30 dark:disabled:bg-input/80 dark:aria-invalid:border-destructive/50 dark:aria-invalid:ring-destructive/40",
105
+ className
106
+ ),
107
+ ...props
108
+ }
109
+ );
110
+ }
111
+ var badgeVariants = classVarianceAuthority.cva(
112
+ "inline-flex items-center gap-1 rounded-md border px-1.5 py-0.5 text-xs font-medium whitespace-nowrap transition-colors [&_svg]:pointer-events-none [&_svg]:size-3",
113
+ {
114
+ variants: {
115
+ variant: {
116
+ default: "border-transparent bg-primary/10 text-primary [a&]:hover:bg-primary/20",
117
+ secondary: "border-transparent bg-secondary text-secondary-foreground [a&]:hover:bg-secondary/80",
118
+ destructive: "border-transparent bg-destructive/10 text-destructive [a&]:hover:bg-destructive/20",
119
+ success: "border-transparent bg-emerald-500/10 text-emerald-600 dark:text-emerald-400 [a&]:hover:bg-emerald-500/20",
120
+ warning: "border-transparent bg-amber-500/15 text-amber-700 dark:text-amber-400 [a&]:hover:bg-amber-500/25",
121
+ outline: "border-border text-foreground"
122
+ }
123
+ },
124
+ defaultVariants: {
125
+ variant: "default"
126
+ }
127
+ }
128
+ );
129
+ function Badge({
130
+ className,
131
+ variant,
132
+ ...props
133
+ }) {
134
+ return /* @__PURE__ */ jsxRuntime.jsx(
135
+ "span",
136
+ {
137
+ "data-slot": "badge",
138
+ className: cn(badgeVariants({ variant }), className),
139
+ ...props
140
+ }
141
+ );
142
+ }
143
+ function Separator({
144
+ className,
145
+ orientation = "horizontal",
146
+ decorative = true,
147
+ ...props
148
+ }) {
149
+ return /* @__PURE__ */ jsxRuntime.jsx(
150
+ radixUi.Separator.Root,
151
+ {
152
+ "data-slot": "separator",
153
+ decorative,
154
+ orientation,
155
+ className: cn(
156
+ "shrink-0 bg-border data-horizontal:h-px data-horizontal:w-full data-vertical:w-px data-vertical:self-stretch",
157
+ className
158
+ ),
159
+ ...props
160
+ }
161
+ );
162
+ }
163
+ function DropdownMenu({
164
+ ...props
165
+ }) {
166
+ return /* @__PURE__ */ jsxRuntime.jsx(radixUi.DropdownMenu.Root, { "data-slot": "dropdown-menu", ...props });
167
+ }
168
+ function DropdownMenuTrigger({
169
+ ...props
170
+ }) {
171
+ return /* @__PURE__ */ jsxRuntime.jsx(
172
+ radixUi.DropdownMenu.Trigger,
173
+ {
174
+ "data-slot": "dropdown-menu-trigger",
175
+ ...props
176
+ }
177
+ );
178
+ }
179
+ function DropdownMenuContent({
180
+ className,
181
+ align = "start",
182
+ sideOffset = 4,
183
+ ...props
184
+ }) {
185
+ return /* @__PURE__ */ jsxRuntime.jsx(radixUi.DropdownMenu.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(
186
+ radixUi.DropdownMenu.Content,
187
+ {
188
+ "data-slot": "dropdown-menu-content",
189
+ sideOffset,
190
+ align,
191
+ className: cn("z-50 max-h-(--radix-dropdown-menu-content-available-height) w-(--radix-dropdown-menu-trigger-width) min-w-32 origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-lg p-1 text-popover-foreground shadow-md ring-1 ring-foreground/10 duration-100 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 data-[state=closed]:overflow-hidden data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95 animate-none! relative bg-popover/70 before:pointer-events-none before:absolute before:inset-0 before:-z-1 before:rounded-[inherit] before:backdrop-blur-2xl before:backdrop-saturate-150 **:data-[slot$=-item]:focus:bg-foreground/10 **:data-[slot$=-item]:data-highlighted:bg-foreground/10 **:data-[slot$=-separator]:bg-foreground/5 **:data-[slot$=-trigger]:focus:bg-foreground/10 **:data-[slot$=-trigger]:aria-expanded:bg-foreground/10! **:data-[variant=destructive]:focus:bg-foreground/10! **:data-[variant=destructive]:text-accent-foreground! **:data-[variant=destructive]:**:text-accent-foreground!", className),
192
+ ...props
193
+ }
194
+ ) });
195
+ }
196
+ function DropdownMenuItem({
197
+ className,
198
+ inset,
199
+ variant = "default",
200
+ ...props
201
+ }) {
202
+ return /* @__PURE__ */ jsxRuntime.jsx(
203
+ radixUi.DropdownMenu.Item,
204
+ {
205
+ "data-slot": "dropdown-menu-item",
206
+ "data-inset": inset,
207
+ "data-variant": variant,
208
+ className: cn(
209
+ "group/dropdown-menu-item relative flex cursor-default items-center gap-1.5 rounded-md px-1.5 py-1 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground not-data-[variant=destructive]:focus:**:text-accent-foreground data-inset:pl-7 data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 data-[variant=destructive]:focus:text-destructive dark:data-[variant=destructive]:focus:bg-destructive/20 data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-[variant=destructive]:*:[svg]:text-destructive",
210
+ className
211
+ ),
212
+ ...props
213
+ }
214
+ );
215
+ }
216
+ function DropdownMenuLabel({
217
+ className,
218
+ inset,
219
+ ...props
220
+ }) {
221
+ return /* @__PURE__ */ jsxRuntime.jsx(
222
+ radixUi.DropdownMenu.Label,
223
+ {
224
+ "data-slot": "dropdown-menu-label",
225
+ "data-inset": inset,
226
+ className: cn(
227
+ "px-1.5 py-1 text-xs font-medium text-muted-foreground data-inset:pl-7",
228
+ className
229
+ ),
230
+ ...props
231
+ }
232
+ );
233
+ }
234
+ function DropdownMenuSeparator({
235
+ className,
236
+ ...props
237
+ }) {
238
+ return /* @__PURE__ */ jsxRuntime.jsx(
239
+ radixUi.DropdownMenu.Separator,
240
+ {
241
+ "data-slot": "dropdown-menu-separator",
242
+ className: cn("-mx-1 my-1 h-px bg-border", className),
243
+ ...props
244
+ }
245
+ );
246
+ }
247
+ function Popover({
248
+ ...props
249
+ }) {
250
+ return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Popover.Root, { "data-slot": "popover", ...props });
251
+ }
252
+ function PopoverTrigger({
253
+ ...props
254
+ }) {
255
+ return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Popover.Trigger, { "data-slot": "popover-trigger", ...props });
256
+ }
257
+ function PopoverContent({
258
+ className,
259
+ align = "center",
260
+ sideOffset = 6,
261
+ ...props
262
+ }) {
263
+ return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Popover.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(
264
+ radixUi.Popover.Content,
265
+ {
266
+ "data-slot": "popover-content",
267
+ align,
268
+ sideOffset,
269
+ className: cn(
270
+ "z-50 w-72 origin-(--radix-popover-content-transform-origin) rounded-lg border bg-popover p-3 text-popover-foreground shadow-md outline-none data-[side=bottom]:slide-in-from-top-1 data-[side=left]:slide-in-from-right-1 data-[side=right]:slide-in-from-left-1 data-[side=top]:slide-in-from-bottom-1 data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
271
+ className
272
+ ),
273
+ ...props
274
+ }
275
+ ) });
276
+ }
277
+
278
+ // src/export.ts
279
+ function escapeCsv(value) {
280
+ if (value === null || value === void 0) return "";
281
+ const s = String(value);
282
+ if (/[",\n\r]/.test(s)) return `"${s.replace(/"/g, '""')}"`;
283
+ return s;
284
+ }
285
+ function escapeHtml(value) {
286
+ if (value === null || value === void 0) return "";
287
+ return String(value).replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
288
+ }
289
+ function getExportRows(table) {
290
+ const visible = table.getVisibleLeafColumns().filter((c) => {
291
+ if (c.id === "__select" || c.id === "__expand" || c.id === "__actions")
292
+ return false;
293
+ return c.columnDef.meta?.exportable !== false;
294
+ });
295
+ const headers = visible.map(
296
+ (c) => c.columnDef.meta?.label ?? (typeof c.columnDef.header === "string" ? c.columnDef.header : c.id)
297
+ );
298
+ const selected = table.getSelectedRowModel().rows;
299
+ const rows = selected.length > 0 ? selected : table.getFilteredRowModel().rows;
300
+ const data = rows.map(
301
+ (row) => visible.map((col) => {
302
+ const value = row.getValue(col.id);
303
+ if (value instanceof Date) return value.toISOString().slice(0, 10);
304
+ if (typeof value === "object" && value !== null) return JSON.stringify(value);
305
+ return value;
306
+ })
307
+ );
308
+ return { headers, data };
309
+ }
310
+ function triggerDownload(content, mime, filename) {
311
+ const bom = "\uFEFF";
312
+ const blob = new Blob([bom + content], { type: mime });
313
+ const url = URL.createObjectURL(blob);
314
+ const a = document.createElement("a");
315
+ a.href = url;
316
+ a.download = filename;
317
+ document.body.appendChild(a);
318
+ a.click();
319
+ document.body.removeChild(a);
320
+ setTimeout(() => URL.revokeObjectURL(url), 100);
321
+ }
322
+ function exportToCsv(table, fileName = "export") {
323
+ const { headers, data } = getExportRows(table);
324
+ const lines = [headers.map(escapeCsv).join(","), ...data.map((r) => r.map(escapeCsv).join(","))];
325
+ triggerDownload(lines.join("\r\n"), "text/csv;charset=utf-8;", `${fileName}.csv`);
326
+ }
327
+ function exportToExcel(table, fileName = "export") {
328
+ const { headers, data } = getExportRows(table);
329
+ const headerHtml = headers.map((h) => `<th style="background:#f4f4f5;text-align:left;padding:6px 10px;border:1px solid #d4d4d8">${escapeHtml(h)}</th>`).join("");
330
+ const bodyHtml = data.map(
331
+ (row) => `<tr>${row.map((c) => `<td style="padding:6px 10px;border:1px solid #d4d4d8">${escapeHtml(c)}</td>`).join("")}</tr>`
332
+ ).join("");
333
+ const html = `<!DOCTYPE html><html xmlns:o="urn:schemas-microsoft-com:office:office" xmlns:x="urn:schemas-microsoft-com:office:excel" xmlns="http://www.w3.org/TR/REC-html40"><head><meta charset="utf-8"/><!--[if gte mso 9]><xml><x:ExcelWorkbook><x:ExcelWorksheets><x:ExcelWorksheet><x:Name>Sheet1</x:Name><x:WorksheetOptions><x:DisplayGridlines/></x:WorksheetOptions></x:ExcelWorksheet></x:ExcelWorksheets></x:ExcelWorkbook></xml><![endif]--></head><body><table>${`<thead><tr>${headerHtml}</tr></thead><tbody>${bodyHtml}</tbody>`}</table></body></html>`;
334
+ triggerDownload(html, "application/vnd.ms-excel;charset=utf-8;", `${fileName}.xls`);
335
+ }
336
+ var TOOLBAR_DEFAULT_LABELS = {
337
+ search: "Search...",
338
+ addRow: "Add row",
339
+ delete: "Delete",
340
+ clear: "Clear",
341
+ selected: "selected",
342
+ refresh: "Refresh",
343
+ columns: "Columns",
344
+ export: "Export",
345
+ csv: "CSV",
346
+ excel: "Excel",
347
+ total: "Total"
348
+ };
349
+ function DataTableToolbar({
350
+ table,
351
+ totalRecords,
352
+ isFetching,
353
+ onRefresh,
354
+ onAddRow,
355
+ onBulkDelete,
356
+ exportFileName = "export",
357
+ globalFilter,
358
+ onGlobalFilterChange,
359
+ toolbarSlot,
360
+ features,
361
+ labels: labelsProp
362
+ }) {
363
+ const labels = {
364
+ ...TOOLBAR_DEFAULT_LABELS,
365
+ ...labelsProp
366
+ };
367
+ const showSearch = features?.search ?? true;
368
+ const showColumnVisibility = features?.columnVisibility ?? true;
369
+ const showExport = features?.export ?? true;
370
+ const [searchOpen, setSearchOpen] = react.useState(Boolean(globalFilter));
371
+ const searchInputRef = react.useRef(null);
372
+ const selectedRows = table.getSelectedRowModel().rows;
373
+ const selectedCount = selectedRows.length;
374
+ const hideableColumns = table.getAllLeafColumns().filter((c) => c.getCanHide());
375
+ const visibleCount = hideableColumns.filter((c) => c.getIsVisible()).length;
376
+ react.useEffect(() => {
377
+ if (globalFilter) setSearchOpen(true);
378
+ }, [globalFilter]);
379
+ react.useEffect(() => {
380
+ if (searchOpen) searchInputRef.current?.focus();
381
+ }, [searchOpen]);
382
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-wrap items-center gap-2 border-b p-2", children: [
383
+ toolbarSlot,
384
+ selectedCount > 0 && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
385
+ /* @__PURE__ */ jsxRuntime.jsxs(
386
+ Badge,
387
+ {
388
+ variant: "secondary",
389
+ className: "flex h-8 items-center gap-1 rounded-md px-2 text-xs",
390
+ children: [
391
+ selectedCount,
392
+ " ",
393
+ labels.selected
394
+ ]
395
+ }
396
+ ),
397
+ onBulkDelete && /* @__PURE__ */ jsxRuntime.jsxs(
398
+ Button,
399
+ {
400
+ variant: "destructive",
401
+ size: "sm",
402
+ className: "h-8",
403
+ onClick: () => onBulkDelete(selectedRows.map((r) => r.original)),
404
+ children: [
405
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2Icon, {}),
406
+ " ",
407
+ labels.delete
408
+ ]
409
+ }
410
+ ),
411
+ /* @__PURE__ */ jsxRuntime.jsxs(
412
+ Button,
413
+ {
414
+ variant: "ghost",
415
+ size: "sm",
416
+ className: "h-8",
417
+ onClick: () => table.resetRowSelection(),
418
+ children: [
419
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.XIcon, {}),
420
+ " ",
421
+ labels.clear
422
+ ]
423
+ }
424
+ ),
425
+ /* @__PURE__ */ jsxRuntime.jsx(Separator, { orientation: "vertical", className: "mx-2 !h-6 !self-center" })
426
+ ] }),
427
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "ml-auto flex h-8 items-center gap-1", children: [
428
+ showSearch && /* @__PURE__ */ jsxRuntime.jsx(
429
+ "div",
430
+ {
431
+ className: cn(
432
+ "relative h-8 overflow-hidden transition-[width] duration-200 ease-out",
433
+ searchOpen ? "w-56" : "w-8"
434
+ ),
435
+ children: searchOpen ? /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
436
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.SearchIcon, { className: "pointer-events-none absolute top-1/2 left-2 size-3.5 -translate-y-1/2 opacity-60" }),
437
+ /* @__PURE__ */ jsxRuntime.jsx(
438
+ Input,
439
+ {
440
+ ref: searchInputRef,
441
+ placeholder: labels.search,
442
+ value: globalFilter,
443
+ onChange: (e) => onGlobalFilterChange(e.target.value),
444
+ onKeyDown: (e) => {
445
+ if (e.key === "Escape" && !globalFilter) setSearchOpen(false);
446
+ },
447
+ onBlur: () => {
448
+ if (!globalFilter) setSearchOpen(false);
449
+ },
450
+ className: "h-8 w-56 border-input bg-transparent pl-7 pr-7 shadow-none focus-visible:bg-transparent focus-visible:ring-0"
451
+ }
452
+ ),
453
+ /* @__PURE__ */ jsxRuntime.jsx(
454
+ "button",
455
+ {
456
+ type: "button",
457
+ onMouseDown: (e) => e.preventDefault(),
458
+ onClick: () => {
459
+ if (globalFilter) {
460
+ onGlobalFilterChange("");
461
+ searchInputRef.current?.focus();
462
+ return;
463
+ }
464
+ setSearchOpen(false);
465
+ },
466
+ className: "absolute top-1/2 right-1.5 -translate-y-1/2 rounded p-0.5 hover:bg-muted",
467
+ "aria-label": globalFilter ? "Clear search" : "Close search",
468
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.XIcon, { className: "size-3" })
469
+ }
470
+ )
471
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(
472
+ Button,
473
+ {
474
+ type: "button",
475
+ variant: "outline",
476
+ size: "icon-sm",
477
+ onClick: () => setSearchOpen(true),
478
+ "aria-label": "Open search",
479
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.SearchIcon, {})
480
+ }
481
+ )
482
+ }
483
+ ),
484
+ onRefresh && /* @__PURE__ */ jsxRuntime.jsx(
485
+ Button,
486
+ {
487
+ variant: "ghost",
488
+ size: "icon-sm",
489
+ onClick: onRefresh,
490
+ disabled: isFetching,
491
+ "aria-label": labels.refresh,
492
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.RefreshCwIcon, { className: isFetching ? "animate-spin" : void 0 })
493
+ }
494
+ ),
495
+ showColumnVisibility && /* @__PURE__ */ jsxRuntime.jsxs(Popover, { children: [
496
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "outline", size: "sm", children: [
497
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ColumnsIcon, {}),
498
+ " ",
499
+ labels.columns,
500
+ /* @__PURE__ */ jsxRuntime.jsxs(Badge, { variant: "secondary", className: "ml-1", children: [
501
+ visibleCount,
502
+ "/",
503
+ hideableColumns.length
504
+ ] })
505
+ ] }) }),
506
+ /* @__PURE__ */ jsxRuntime.jsxs(PopoverContent, { align: "end", className: "w-56 p-2", children: [
507
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-1 flex items-center justify-between text-xs text-muted-foreground", children: [
508
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Toggle columns" }),
509
+ /* @__PURE__ */ jsxRuntime.jsx(
510
+ "button",
511
+ {
512
+ className: "hover:text-foreground",
513
+ onClick: () => table.resetColumnVisibility(),
514
+ children: "Reset"
515
+ }
516
+ )
517
+ ] }),
518
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex max-h-72 flex-col gap-0.5 overflow-auto", children: hideableColumns.map((col) => /* @__PURE__ */ jsxRuntime.jsxs(
519
+ "label",
520
+ {
521
+ className: "flex cursor-pointer items-center gap-2 rounded px-2 py-1 text-sm hover:bg-accent",
522
+ children: [
523
+ /* @__PURE__ */ jsxRuntime.jsx(
524
+ Checkbox,
525
+ {
526
+ checked: col.getIsVisible(),
527
+ onCheckedChange: (c) => col.toggleVisibility(!!c)
528
+ }
529
+ ),
530
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "capitalize", children: col.columnDef.meta?.label ?? (typeof col.columnDef.header === "string" ? col.columnDef.header : col.id) })
531
+ ]
532
+ },
533
+ col.id
534
+ )) })
535
+ ] })
536
+ ] }),
537
+ showExport && /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenu, { children: [
538
+ /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "outline", size: "sm", children: [
539
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.DownloadIcon, {}),
540
+ " ",
541
+ labels.export
542
+ ] }) }),
543
+ /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenuContent, { align: "end", className: "w-44", children: [
544
+ /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuLabel, { children: selectedCount > 0 ? `${labels.export} ${selectedCount} ${labels.selected}` : `${labels.export} all rows` }),
545
+ /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuSeparator, {}),
546
+ /* @__PURE__ */ jsxRuntime.jsxs(
547
+ DropdownMenuItem,
548
+ {
549
+ onSelect: () => exportToCsv(table, exportFileName),
550
+ children: [
551
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileTextIcon, {}),
552
+ " ",
553
+ labels.csv
554
+ ]
555
+ }
556
+ ),
557
+ /* @__PURE__ */ jsxRuntime.jsxs(
558
+ DropdownMenuItem,
559
+ {
560
+ onSelect: () => exportToExcel(table, exportFileName),
561
+ children: [
562
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileSpreadsheetIcon, {}),
563
+ " ",
564
+ labels.excel
565
+ ]
566
+ }
567
+ )
568
+ ] })
569
+ ] }),
570
+ onAddRow && /* @__PURE__ */ jsxRuntime.jsxs(Button, { size: "sm", onClick: onAddRow, children: [
571
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PlusIcon, {}),
572
+ " ",
573
+ labels.addRow
574
+ ] }),
575
+ /* @__PURE__ */ jsxRuntime.jsx(Separator, { orientation: "vertical", className: "mx-2 !h-6 !self-center" }),
576
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex h-8 items-center gap-1.5 rounded-md border bg-muted/40 px-2 text-xs", children: [
577
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: labels.total }),
578
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-semibold tabular-nums", children: new Intl.NumberFormat().format(totalRecords) })
579
+ ] })
580
+ ] })
581
+ ] });
582
+ }
583
+ function Select(props) {
584
+ return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Select.Root, { "data-slot": "select", ...props });
585
+ }
586
+ function SelectValue(props) {
587
+ return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Select.Value, { "data-slot": "select-value", ...props });
588
+ }
589
+ function SelectTrigger({
590
+ className,
591
+ size = "default",
592
+ children,
593
+ ...props
594
+ }) {
595
+ return /* @__PURE__ */ jsxRuntime.jsxs(
596
+ radixUi.Select.Trigger,
597
+ {
598
+ "data-slot": "select-trigger",
599
+ "data-size": size,
600
+ className: cn(
601
+ "flex w-fit items-center justify-between gap-1.5 rounded-md border border-input bg-background px-2.5 text-sm whitespace-nowrap shadow-xs outline-none transition-colors focus-visible:border-ring focus-visible:ring-2 focus-visible:ring-ring/40 disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-8 data-[size=sm]:h-7 data-[placeholder]:text-muted-foreground [&_svg]:pointer-events-none [&_svg]:size-3.5 [&_svg]:shrink-0",
602
+ className
603
+ ),
604
+ ...props,
605
+ children: [
606
+ children,
607
+ /* @__PURE__ */ jsxRuntime.jsx(radixUi.Select.Icon, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDownIcon, { className: "opacity-60" }) })
608
+ ]
609
+ }
610
+ );
611
+ }
612
+ function SelectContent({
613
+ className,
614
+ children,
615
+ position = "popper",
616
+ ...props
617
+ }) {
618
+ return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Select.Portal, { children: /* @__PURE__ */ jsxRuntime.jsxs(
619
+ radixUi.Select.Content,
620
+ {
621
+ "data-slot": "select-content",
622
+ position,
623
+ className: cn(
624
+ "relative z-50 max-h-(--radix-select-content-available-height) min-w-[8rem] origin-(--radix-select-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-lg border bg-popover text-popover-foreground shadow-md data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
625
+ position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=top]:-translate-y-1",
626
+ className
627
+ ),
628
+ ...props,
629
+ children: [
630
+ /* @__PURE__ */ jsxRuntime.jsx(SelectScrollUpButton, {}),
631
+ /* @__PURE__ */ jsxRuntime.jsx(
632
+ radixUi.Select.Viewport,
633
+ {
634
+ className: cn(
635
+ "p-1",
636
+ position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
637
+ ),
638
+ children
639
+ }
640
+ ),
641
+ /* @__PURE__ */ jsxRuntime.jsx(SelectScrollDownButton, {})
642
+ ]
643
+ }
644
+ ) });
645
+ }
646
+ function SelectItem({
647
+ className,
648
+ children,
649
+ ...props
650
+ }) {
651
+ return /* @__PURE__ */ jsxRuntime.jsxs(
652
+ radixUi.Select.Item,
653
+ {
654
+ "data-slot": "select-item",
655
+ className: cn(
656
+ "relative flex w-full cursor-default items-center gap-1.5 rounded-md py-1 pr-8 pl-2 text-sm outline-hidden select-none focus:bg-accent focus:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50",
657
+ className
658
+ ),
659
+ ...props,
660
+ children: [
661
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "absolute right-2 flex size-3.5 items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(radixUi.Select.ItemIndicator, { children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckIcon, { className: "size-3.5" }) }) }),
662
+ /* @__PURE__ */ jsxRuntime.jsx(radixUi.Select.ItemText, { children })
663
+ ]
664
+ }
665
+ );
666
+ }
667
+ function SelectScrollUpButton({
668
+ className,
669
+ ...props
670
+ }) {
671
+ return /* @__PURE__ */ jsxRuntime.jsx(
672
+ radixUi.Select.ScrollUpButton,
673
+ {
674
+ "data-slot": "select-scroll-up-button",
675
+ className: cn(
676
+ "flex cursor-default items-center justify-center py-1",
677
+ className
678
+ ),
679
+ ...props,
680
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronUpIcon, { className: "size-3.5" })
681
+ }
682
+ );
683
+ }
684
+ function SelectScrollDownButton({
685
+ className,
686
+ ...props
687
+ }) {
688
+ return /* @__PURE__ */ jsxRuntime.jsx(
689
+ radixUi.Select.ScrollDownButton,
690
+ {
691
+ "data-slot": "select-scroll-down-button",
692
+ className: cn(
693
+ "flex cursor-default items-center justify-center py-1",
694
+ className
695
+ ),
696
+ ...props,
697
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDownIcon, { className: "size-3.5" })
698
+ }
699
+ );
700
+ }
701
+ function getPageRange(current, total) {
702
+ const pages = [];
703
+ if (total <= 7) {
704
+ for (let i = 0; i < total; i++) pages.push(i);
705
+ return pages;
706
+ }
707
+ pages.push(0);
708
+ if (current > 3) pages.push("\u2026");
709
+ const start = Math.max(1, current - 1);
710
+ const end = Math.min(total - 2, current + 1);
711
+ for (let i = start; i <= end; i++) pages.push(i);
712
+ if (current < total - 4) pages.push("\u2026");
713
+ pages.push(total - 1);
714
+ return pages;
715
+ }
716
+ function DataTablePagination({
717
+ table,
718
+ pageSizeOptions = [10, 25, 50, 100]
719
+ }) {
720
+ const pageIndex = table.getState().pagination.pageIndex;
721
+ const pageSize = table.getState().pagination.pageSize;
722
+ const pageCount = table.getPageCount();
723
+ const totalRows = table.getFilteredRowModel().rows.length;
724
+ const fromRow = totalRows === 0 ? 0 : pageIndex * pageSize + 1;
725
+ const toRow = Math.min(totalRows, (pageIndex + 1) * pageSize);
726
+ const selected = table.getSelectedRowModel().rows.length;
727
+ const range = getPageRange(pageIndex, pageCount);
728
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "grid grid-cols-1 items-center gap-2 border-t p-2 text-xs md:grid-cols-3", children: [
729
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 text-muted-foreground md:justify-self-start", children: [
730
+ selected > 0 ? /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
731
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-foreground", children: selected }),
732
+ " ",
733
+ "selected \xB7",
734
+ " "
735
+ ] }) : null,
736
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { children: [
737
+ "Showing",
738
+ " ",
739
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-foreground", children: fromRow }),
740
+ "\u2013",
741
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-foreground", children: toRow }),
742
+ " of",
743
+ " ",
744
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "font-medium text-foreground", children: new Intl.NumberFormat().format(totalRows) })
745
+ ] })
746
+ ] }),
747
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 md:justify-self-center", children: [
748
+ /* @__PURE__ */ jsxRuntime.jsx(
749
+ Button,
750
+ {
751
+ variant: "ghost",
752
+ size: "icon-sm",
753
+ onClick: () => table.setPageIndex(0),
754
+ disabled: !table.getCanPreviousPage(),
755
+ "aria-label": "First page",
756
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronsLeftIcon, {})
757
+ }
758
+ ),
759
+ /* @__PURE__ */ jsxRuntime.jsx(
760
+ Button,
761
+ {
762
+ variant: "ghost",
763
+ size: "icon-sm",
764
+ onClick: () => table.previousPage(),
765
+ disabled: !table.getCanPreviousPage(),
766
+ "aria-label": "Previous page",
767
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeftIcon, {})
768
+ }
769
+ ),
770
+ range.map(
771
+ (p, idx) => p === "\u2026" ? /* @__PURE__ */ jsxRuntime.jsx("span", { className: "px-1 text-muted-foreground", children: "\u2026" }, `e-${idx}`) : /* @__PURE__ */ jsxRuntime.jsx(
772
+ "button",
773
+ {
774
+ type: "button",
775
+ onClick: () => table.setPageIndex(p),
776
+ className: cn(
777
+ "h-7 min-w-7 rounded-md border border-transparent px-2 text-xs font-medium transition-colors hover:bg-accent",
778
+ p === pageIndex ? "border-border bg-primary/10 text-primary" : "text-muted-foreground"
779
+ ),
780
+ children: p + 1
781
+ },
782
+ p
783
+ )
784
+ ),
785
+ /* @__PURE__ */ jsxRuntime.jsx(
786
+ Button,
787
+ {
788
+ variant: "ghost",
789
+ size: "icon-sm",
790
+ onClick: () => table.nextPage(),
791
+ disabled: !table.getCanNextPage(),
792
+ "aria-label": "Next page",
793
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRightIcon, {})
794
+ }
795
+ ),
796
+ /* @__PURE__ */ jsxRuntime.jsx(
797
+ Button,
798
+ {
799
+ variant: "ghost",
800
+ size: "icon-sm",
801
+ onClick: () => table.setPageIndex(pageCount - 1),
802
+ disabled: !table.getCanNextPage(),
803
+ "aria-label": "Last page",
804
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronsRightIcon, {})
805
+ }
806
+ )
807
+ ] }),
808
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5 text-muted-foreground md:justify-self-end", children: [
809
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: "Rows per page" }),
810
+ /* @__PURE__ */ jsxRuntime.jsxs(
811
+ Select,
812
+ {
813
+ value: String(pageSize),
814
+ onValueChange: (v) => table.setPageSize(Number(v)),
815
+ children: [
816
+ /* @__PURE__ */ jsxRuntime.jsx(SelectTrigger, { size: "sm", className: "h-7 w-16", children: /* @__PURE__ */ jsxRuntime.jsx(SelectValue, {}) }),
817
+ /* @__PURE__ */ jsxRuntime.jsx(SelectContent, { children: pageSizeOptions.map((opt) => /* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: String(opt), children: opt }, opt)) })
818
+ ]
819
+ }
820
+ )
821
+ ] })
822
+ ] });
823
+ }
824
+ var TEXT_OPS = [
825
+ { v: "contains", l: "Contains" },
826
+ { v: "notContains", l: "Does not contain" },
827
+ { v: "equals", l: "Equals" },
828
+ { v: "notEqual", l: "Does not equal" },
829
+ { v: "startsWith", l: "Starts with" },
830
+ { v: "endsWith", l: "Ends with" },
831
+ { v: "blank", l: "Blank" },
832
+ { v: "notBlank", l: "Not blank" }
833
+ ];
834
+ var NUMBER_OPS = [
835
+ { v: "equals", l: "Equals" },
836
+ { v: "notEqual", l: "Does not equal" },
837
+ { v: "lessThan", l: "Less than" },
838
+ { v: "lessThanOrEqual", l: "Less than or equal" },
839
+ { v: "greaterThan", l: "Greater than" },
840
+ { v: "greaterThanOrEqual", l: "Greater than or equal" },
841
+ { v: "inRange", l: "In range" },
842
+ { v: "blank", l: "Blank" },
843
+ { v: "notBlank", l: "Not blank" }
844
+ ];
845
+ var DATE_OPS = [
846
+ { v: "equals", l: "Equals" },
847
+ { v: "notEqual", l: "Does not equal" },
848
+ { v: "before", l: "Before" },
849
+ { v: "after", l: "After" },
850
+ { v: "inRange", l: "In range" },
851
+ { v: "blank", l: "Blank" },
852
+ { v: "notBlank", l: "Not blank" }
853
+ ];
854
+ function isUnary(op) {
855
+ return op === "blank" || op === "notBlank";
856
+ }
857
+ function isRange(op) {
858
+ return op === "inRange";
859
+ }
860
+ function evalText(op, cell, q) {
861
+ const c = (cell ?? "").toString().toLowerCase();
862
+ const v = (q ?? "").toString().toLowerCase();
863
+ switch (op) {
864
+ case "contains":
865
+ return v === "" ? true : c.includes(v);
866
+ case "notContains":
867
+ return v === "" ? true : !c.includes(v);
868
+ case "equals":
869
+ return c === v;
870
+ case "notEqual":
871
+ return c !== v;
872
+ case "startsWith":
873
+ return c.startsWith(v);
874
+ case "endsWith":
875
+ return c.endsWith(v);
876
+ case "blank":
877
+ return c === "";
878
+ case "notBlank":
879
+ return c !== "";
880
+ }
881
+ }
882
+ function evalNumber(op, cell, a, b) {
883
+ const n = cell == null || cell === "" ? null : Number(cell);
884
+ switch (op) {
885
+ case "blank":
886
+ return n === null;
887
+ case "notBlank":
888
+ return n !== null;
889
+ }
890
+ if (n === null || Number.isNaN(n)) return false;
891
+ if (a == null) return true;
892
+ switch (op) {
893
+ case "equals":
894
+ return n === a;
895
+ case "notEqual":
896
+ return n !== a;
897
+ case "lessThan":
898
+ return n < a;
899
+ case "lessThanOrEqual":
900
+ return n <= a;
901
+ case "greaterThan":
902
+ return n > a;
903
+ case "greaterThanOrEqual":
904
+ return n >= a;
905
+ case "inRange":
906
+ if (b == null) return n >= a;
907
+ return n >= a && n <= b;
908
+ }
909
+ return true;
910
+ }
911
+ function evalDate(op, cell, a, b) {
912
+ const c = cell ? String(cell) : "";
913
+ switch (op) {
914
+ case "blank":
915
+ return c === "";
916
+ case "notBlank":
917
+ return c !== "";
918
+ }
919
+ if (!c) return false;
920
+ if (!a) return true;
921
+ switch (op) {
922
+ case "equals":
923
+ return c === a;
924
+ case "notEqual":
925
+ return c !== a;
926
+ case "before":
927
+ return c < a;
928
+ case "after":
929
+ return c > a;
930
+ case "inRange":
931
+ if (!b) return c >= a;
932
+ return c >= a && c <= b;
933
+ }
934
+ return true;
935
+ }
936
+ function combineResults(a, b, mode) {
937
+ if (mode === "OR") return a || b;
938
+ return a && b;
939
+ }
940
+ function isAdvActive(f) {
941
+ if (!f?.c1) return false;
942
+ const c1 = f.c1;
943
+ if (isUnary(c1.op)) return true;
944
+ if (c1.v == null || c1.v === "") {
945
+ if (!f.c2) return false;
946
+ const c2 = f.c2;
947
+ if (isUnary(c2.op)) return true;
948
+ return c2.v != null && c2.v !== "";
949
+ }
950
+ return true;
951
+ }
952
+ var textFilterFn = (row, columnId, value) => {
953
+ const f = value;
954
+ if (!isAdvActive(f)) return true;
955
+ const cell = row.getValue(columnId);
956
+ const c1 = f.c1;
957
+ const aActive = isUnary(c1.op) || c1.v != null && c1.v !== "";
958
+ const aRes = aActive ? evalText(c1.op, cell, c1.v) : true;
959
+ if (!f.c2) return aRes;
960
+ const c2 = f.c2;
961
+ const bActive = isUnary(c2.op) || c2.v != null && c2.v !== "";
962
+ if (!bActive) return aRes;
963
+ const bRes = evalText(c2.op, cell, c2.v);
964
+ if (!aActive) return bRes;
965
+ return combineResults(aRes, bRes, f.combine);
966
+ };
967
+ var numberFilterFn = (row, columnId, value) => {
968
+ const f = value;
969
+ if (!isAdvActive(f)) return true;
970
+ const cell = row.getValue(columnId);
971
+ const c1 = f.c1;
972
+ const aActive = isUnary(c1.op) || c1.v != null;
973
+ const aRes = aActive ? evalNumber(c1.op, cell, c1.v, c1.v2) : true;
974
+ if (!f.c2) return aRes;
975
+ const c2 = f.c2;
976
+ const bActive = isUnary(c2.op) || c2.v != null;
977
+ if (!bActive) return aRes;
978
+ const bRes = evalNumber(c2.op, cell, c2.v, c2.v2);
979
+ if (!aActive) return bRes;
980
+ return combineResults(aRes, bRes, f.combine);
981
+ };
982
+ var dateFilterFn = (row, columnId, value) => {
983
+ const f = value;
984
+ if (!isAdvActive(f)) return true;
985
+ const cell = row.getValue(columnId);
986
+ const c1 = f.c1;
987
+ const aActive = isUnary(c1.op) || c1.v != null && c1.v !== "";
988
+ const aRes = aActive ? evalDate(c1.op, cell, c1.v, c1.v2) : true;
989
+ if (!f.c2) return aRes;
990
+ const c2 = f.c2;
991
+ const bActive = isUnary(c2.op) || c2.v != null && c2.v !== "";
992
+ if (!bActive) return aRes;
993
+ const bRes = evalDate(c2.op, cell, c2.v, c2.v2);
994
+ if (!aActive) return bRes;
995
+ return combineResults(aRes, bRes, f.combine);
996
+ };
997
+ var setFilterFn = (row, columnId, value) => {
998
+ const f = value;
999
+ if (!f || !Array.isArray(f.selected)) return true;
1000
+ if (f.selected.length === 0) return false;
1001
+ const cell = row.getValue(columnId);
1002
+ if (Array.isArray(cell)) {
1003
+ return cell.some((c) => f.selected.includes(String(c)));
1004
+ }
1005
+ return f.selected.includes(String(cell ?? ""));
1006
+ };
1007
+ var booleanFilterFn = (row, columnId, value) => {
1008
+ if (value === void 0 || value === null || value === "") return true;
1009
+ const cell = row.getValue(columnId);
1010
+ return Boolean(cell) === Boolean(value);
1011
+ };
1012
+ function isFilterActive(filterType, value) {
1013
+ if (value === void 0 || value === null || value === "") return false;
1014
+ if (filterType === "multi-select" || filterType === "select") {
1015
+ const f = value;
1016
+ return Array.isArray(f.selected);
1017
+ }
1018
+ if (filterType === "text" || filterType === "number" || filterType === "date") {
1019
+ return isAdvActive(value);
1020
+ }
1021
+ if (filterType === "boolean") return value !== void 0;
1022
+ return true;
1023
+ }
1024
+ function ColumnFilterPanel(props) {
1025
+ const filterType = props.column.columnDef.meta?.filterType;
1026
+ switch (filterType) {
1027
+ case "text":
1028
+ return /* @__PURE__ */ jsxRuntime.jsx(TextFilterPanel, { ...props });
1029
+ case "number":
1030
+ return /* @__PURE__ */ jsxRuntime.jsx(NumberFilterPanel, { ...props });
1031
+ case "date":
1032
+ return /* @__PURE__ */ jsxRuntime.jsx(DateFilterPanel, { ...props });
1033
+ case "select":
1034
+ case "multi-select":
1035
+ return /* @__PURE__ */ jsxRuntime.jsx(SetFilterPanel, { ...props });
1036
+ case "boolean":
1037
+ return /* @__PURE__ */ jsxRuntime.jsx(BooleanFilterPanel, { ...props });
1038
+ default:
1039
+ return null;
1040
+ }
1041
+ }
1042
+ function OpSelect({
1043
+ ops,
1044
+ value,
1045
+ onChange
1046
+ }) {
1047
+ return /* @__PURE__ */ jsxRuntime.jsx(
1048
+ "select",
1049
+ {
1050
+ value,
1051
+ onChange: (e) => onChange(e.target.value),
1052
+ className: "h-8 w-full rounded-md border border-input bg-background px-2 text-xs outline-none focus-visible:border-ring",
1053
+ children: ops.map((o) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: o.v, children: o.l }, o.v))
1054
+ }
1055
+ );
1056
+ }
1057
+ function CombineToggle({
1058
+ value,
1059
+ onChange
1060
+ }) {
1061
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "inline-flex h-7 overflow-hidden rounded-md border bg-muted/30 text-[11px]", children: ["AND", "OR"].map((m) => /* @__PURE__ */ jsxRuntime.jsx(
1062
+ "button",
1063
+ {
1064
+ type: "button",
1065
+ className: cn(
1066
+ "px-2 transition-colors hover:bg-muted",
1067
+ value === m && "bg-primary/10 text-primary"
1068
+ ),
1069
+ onClick: () => onChange(m),
1070
+ children: m
1071
+ },
1072
+ m
1073
+ )) });
1074
+ }
1075
+ function FooterBar({
1076
+ onApply,
1077
+ onClear,
1078
+ onClose,
1079
+ applyDisabled,
1080
+ clearDisabled
1081
+ }) {
1082
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2 flex items-center justify-between gap-1.5", children: [
1083
+ /* @__PURE__ */ jsxRuntime.jsx(
1084
+ Button,
1085
+ {
1086
+ size: "xs",
1087
+ variant: "ghost",
1088
+ onClick: onClear,
1089
+ disabled: clearDisabled,
1090
+ children: "Clear"
1091
+ }
1092
+ ),
1093
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1.5", children: [
1094
+ /* @__PURE__ */ jsxRuntime.jsx(Button, { size: "xs", variant: "ghost", onClick: onClose, children: "Close" }),
1095
+ onApply && /* @__PURE__ */ jsxRuntime.jsx(Button, { size: "xs", onClick: onApply, disabled: applyDisabled, children: "Apply" })
1096
+ ] })
1097
+ ] });
1098
+ }
1099
+ function TextFilterPanel({
1100
+ column,
1101
+ onClose
1102
+ }) {
1103
+ const value = column.getFilterValue();
1104
+ const [draft, setDraft] = react.useState(
1105
+ value ?? { c1: { op: "contains", v: "" } }
1106
+ );
1107
+ react.useEffect(() => {
1108
+ setDraft(value ?? { c1: { op: "contains", v: "" } });
1109
+ }, [value]);
1110
+ const apply = () => {
1111
+ column.setFilterValue(
1112
+ isAdvActive(draft) ? draft : void 0
1113
+ );
1114
+ onClose();
1115
+ };
1116
+ const clear = () => {
1117
+ column.setFilterValue(void 0);
1118
+ setDraft({ c1: { op: "contains", v: "" } });
1119
+ };
1120
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
1121
+ /* @__PURE__ */ jsxRuntime.jsx(
1122
+ ConditionRow,
1123
+ {
1124
+ ops: TEXT_OPS,
1125
+ c: draft.c1,
1126
+ onChange: (c1) => setDraft((d) => ({ ...d, c1 })),
1127
+ renderInput: (c, set) => /* @__PURE__ */ jsxRuntime.jsx(
1128
+ Input,
1129
+ {
1130
+ autoFocus: true,
1131
+ className: "h-8 text-sm",
1132
+ placeholder: "Value",
1133
+ value: c.v ?? "",
1134
+ onChange: (e) => set({ ...c, v: e.target.value }),
1135
+ onKeyDown: (e) => e.key === "Enter" && apply()
1136
+ }
1137
+ )
1138
+ }
1139
+ ),
1140
+ /* @__PURE__ */ jsxRuntime.jsx(Separator, {}),
1141
+ /* @__PURE__ */ jsxRuntime.jsx(
1142
+ SecondCondition,
1143
+ {
1144
+ draft,
1145
+ setDraft,
1146
+ ops: TEXT_OPS,
1147
+ renderInput: (c, set) => /* @__PURE__ */ jsxRuntime.jsx(
1148
+ Input,
1149
+ {
1150
+ className: "h-8 text-sm",
1151
+ placeholder: "Value",
1152
+ value: c.v ?? "",
1153
+ onChange: (e) => set({ ...c, v: e.target.value }),
1154
+ onKeyDown: (e) => e.key === "Enter" && apply()
1155
+ }
1156
+ )
1157
+ }
1158
+ ),
1159
+ /* @__PURE__ */ jsxRuntime.jsx(FooterBar, { onApply: apply, onClear: clear, onClose, clearDisabled: !value })
1160
+ ] });
1161
+ }
1162
+ function NumberFilterPanel({
1163
+ column,
1164
+ onClose
1165
+ }) {
1166
+ const value = column.getFilterValue();
1167
+ const [draft, setDraft] = react.useState(
1168
+ value ?? { c1: { op: "equals" } }
1169
+ );
1170
+ react.useEffect(() => {
1171
+ setDraft(value ?? { c1: { op: "equals" } });
1172
+ }, [value]);
1173
+ const apply = () => {
1174
+ column.setFilterValue(
1175
+ isAdvActive(draft) ? draft : void 0
1176
+ );
1177
+ onClose();
1178
+ };
1179
+ const clear = () => {
1180
+ column.setFilterValue(void 0);
1181
+ setDraft({ c1: { op: "equals" } });
1182
+ };
1183
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
1184
+ /* @__PURE__ */ jsxRuntime.jsx(
1185
+ ConditionRow,
1186
+ {
1187
+ ops: NUMBER_OPS,
1188
+ c: draft.c1,
1189
+ onChange: (c1) => setDraft((d) => ({ ...d, c1 })),
1190
+ renderInput: (c, set) => isRange(c.op) ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-1.5", children: [
1191
+ /* @__PURE__ */ jsxRuntime.jsx(
1192
+ Input,
1193
+ {
1194
+ autoFocus: true,
1195
+ type: "number",
1196
+ className: "h-8 text-sm",
1197
+ placeholder: "From",
1198
+ value: c.v ?? "",
1199
+ onChange: (e) => set({
1200
+ ...c,
1201
+ v: e.target.value === "" ? void 0 : Number(e.target.value)
1202
+ })
1203
+ }
1204
+ ),
1205
+ /* @__PURE__ */ jsxRuntime.jsx(
1206
+ Input,
1207
+ {
1208
+ type: "number",
1209
+ className: "h-8 text-sm",
1210
+ placeholder: "To",
1211
+ value: c.v2 ?? "",
1212
+ onChange: (e) => set({
1213
+ ...c,
1214
+ v2: e.target.value === "" ? void 0 : Number(e.target.value)
1215
+ })
1216
+ }
1217
+ )
1218
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(
1219
+ Input,
1220
+ {
1221
+ autoFocus: true,
1222
+ type: "number",
1223
+ className: "h-8 text-sm",
1224
+ placeholder: "Value",
1225
+ value: c.v ?? "",
1226
+ onChange: (e) => set({
1227
+ ...c,
1228
+ v: e.target.value === "" ? void 0 : Number(e.target.value)
1229
+ }),
1230
+ onKeyDown: (e) => e.key === "Enter" && apply()
1231
+ }
1232
+ )
1233
+ }
1234
+ ),
1235
+ /* @__PURE__ */ jsxRuntime.jsx(Separator, {}),
1236
+ /* @__PURE__ */ jsxRuntime.jsx(
1237
+ SecondCondition,
1238
+ {
1239
+ draft,
1240
+ setDraft,
1241
+ ops: NUMBER_OPS,
1242
+ renderInput: (c, set) => isRange(c.op) ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-1.5", children: [
1243
+ /* @__PURE__ */ jsxRuntime.jsx(
1244
+ Input,
1245
+ {
1246
+ type: "number",
1247
+ className: "h-8 text-sm",
1248
+ placeholder: "From",
1249
+ value: c.v ?? "",
1250
+ onChange: (e) => set({
1251
+ ...c,
1252
+ v: e.target.value === "" ? void 0 : Number(e.target.value)
1253
+ })
1254
+ }
1255
+ ),
1256
+ /* @__PURE__ */ jsxRuntime.jsx(
1257
+ Input,
1258
+ {
1259
+ type: "number",
1260
+ className: "h-8 text-sm",
1261
+ placeholder: "To",
1262
+ value: c.v2 ?? "",
1263
+ onChange: (e) => set({
1264
+ ...c,
1265
+ v2: e.target.value === "" ? void 0 : Number(e.target.value)
1266
+ })
1267
+ }
1268
+ )
1269
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(
1270
+ Input,
1271
+ {
1272
+ type: "number",
1273
+ className: "h-8 text-sm",
1274
+ placeholder: "Value",
1275
+ value: c.v ?? "",
1276
+ onChange: (e) => set({
1277
+ ...c,
1278
+ v: e.target.value === "" ? void 0 : Number(e.target.value)
1279
+ })
1280
+ }
1281
+ )
1282
+ }
1283
+ ),
1284
+ /* @__PURE__ */ jsxRuntime.jsx(FooterBar, { onApply: apply, onClear: clear, onClose, clearDisabled: !value })
1285
+ ] });
1286
+ }
1287
+ function DateFilterPanel({
1288
+ column,
1289
+ onClose
1290
+ }) {
1291
+ const value = column.getFilterValue();
1292
+ const [draft, setDraft] = react.useState(
1293
+ value ?? { c1: { op: "equals" } }
1294
+ );
1295
+ react.useEffect(() => {
1296
+ setDraft(value ?? { c1: { op: "equals" } });
1297
+ }, [value]);
1298
+ const apply = () => {
1299
+ column.setFilterValue(
1300
+ isAdvActive(draft) ? draft : void 0
1301
+ );
1302
+ onClose();
1303
+ };
1304
+ const clear = () => {
1305
+ column.setFilterValue(void 0);
1306
+ setDraft({ c1: { op: "equals" } });
1307
+ };
1308
+ const renderInput = (c, set) => isRange(c.op) ? /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex gap-1.5", children: [
1309
+ /* @__PURE__ */ jsxRuntime.jsx(
1310
+ Input,
1311
+ {
1312
+ type: "date",
1313
+ className: "h-8 text-sm",
1314
+ value: c.v ?? "",
1315
+ onChange: (e) => set({ ...c, v: e.target.value || void 0 })
1316
+ }
1317
+ ),
1318
+ /* @__PURE__ */ jsxRuntime.jsx(
1319
+ Input,
1320
+ {
1321
+ type: "date",
1322
+ className: "h-8 text-sm",
1323
+ value: c.v2 ?? "",
1324
+ onChange: (e) => set({ ...c, v2: e.target.value || void 0 })
1325
+ }
1326
+ )
1327
+ ] }) : /* @__PURE__ */ jsxRuntime.jsx(
1328
+ Input,
1329
+ {
1330
+ type: "date",
1331
+ className: "h-8 text-sm",
1332
+ value: c.v ?? "",
1333
+ onChange: (e) => set({ ...c, v: e.target.value || void 0 })
1334
+ }
1335
+ );
1336
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
1337
+ /* @__PURE__ */ jsxRuntime.jsx(
1338
+ ConditionRow,
1339
+ {
1340
+ ops: DATE_OPS,
1341
+ c: draft.c1,
1342
+ onChange: (c1) => setDraft((d) => ({ ...d, c1 })),
1343
+ renderInput
1344
+ }
1345
+ ),
1346
+ /* @__PURE__ */ jsxRuntime.jsx(Separator, {}),
1347
+ /* @__PURE__ */ jsxRuntime.jsx(
1348
+ SecondCondition,
1349
+ {
1350
+ draft,
1351
+ setDraft,
1352
+ ops: DATE_OPS,
1353
+ renderInput
1354
+ }
1355
+ ),
1356
+ /* @__PURE__ */ jsxRuntime.jsx(FooterBar, { onApply: apply, onClear: clear, onClose, clearDisabled: !value })
1357
+ ] });
1358
+ }
1359
+ function SetFilterPanel({
1360
+ column,
1361
+ onClose
1362
+ }) {
1363
+ const value = column.getFilterValue();
1364
+ const meta = column.columnDef.meta;
1365
+ const declared = meta?.selectOptions ?? [];
1366
+ const options = react.useMemo(() => {
1367
+ if (declared.length) return declared;
1368
+ const rows = column.getFacetedRowModel?.().rows ?? [];
1369
+ const seen = /* @__PURE__ */ new Set();
1370
+ const out = [];
1371
+ for (const r of rows) {
1372
+ const v = r.getValue(column.id);
1373
+ const arr = Array.isArray(v) ? v : [v];
1374
+ for (const item of arr) {
1375
+ const k = item == null ? "" : String(item);
1376
+ if (!seen.has(k)) {
1377
+ seen.add(k);
1378
+ out.push({ value: k, label: k === "" ? "(Blank)" : k });
1379
+ }
1380
+ }
1381
+ }
1382
+ return out.sort((a, b) => a.label.localeCompare(b.label));
1383
+ }, [declared, column]);
1384
+ const [search, setSearch] = react.useState("");
1385
+ const [draft, setDraft] = react.useState(() => {
1386
+ if (value && Array.isArray(value.selected)) return value.selected;
1387
+ return options.map((o) => o.value);
1388
+ });
1389
+ react.useEffect(() => {
1390
+ if (!value) setDraft(options.map((o) => o.value));
1391
+ else if (Array.isArray(value.selected)) setDraft(value.selected);
1392
+ }, [value, options]);
1393
+ const visible = react.useMemo(() => {
1394
+ const q = search.trim().toLowerCase();
1395
+ if (!q) return options;
1396
+ return options.filter((o) => o.label.toLowerCase().includes(q));
1397
+ }, [options, search]);
1398
+ const visibleAllSelected = visible.length > 0 && visible.every((o) => draft.includes(o.value));
1399
+ const visibleSomeSelected = visible.some((o) => draft.includes(o.value));
1400
+ const toggleAll = (checked) => {
1401
+ const visibleVals = visible.map((o) => o.value);
1402
+ if (checked) {
1403
+ const set = /* @__PURE__ */ new Set([...draft, ...visibleVals]);
1404
+ setDraft(Array.from(set));
1405
+ } else {
1406
+ setDraft(draft.filter((v) => !visibleVals.includes(v)));
1407
+ }
1408
+ };
1409
+ const toggle = (v) => {
1410
+ setDraft((d) => d.includes(v) ? d.filter((x) => x !== v) : [...d, v]);
1411
+ };
1412
+ const apply = () => {
1413
+ if (draft.length === options.length) {
1414
+ column.setFilterValue(void 0);
1415
+ } else {
1416
+ column.setFilterValue({ selected: draft });
1417
+ }
1418
+ onClose();
1419
+ };
1420
+ const clear = () => {
1421
+ column.setFilterValue(void 0);
1422
+ setDraft(options.map((o) => o.value));
1423
+ setSearch("");
1424
+ };
1425
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-2", children: [
1426
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative", children: [
1427
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.SearchIcon, { className: "pointer-events-none absolute top-1/2 left-2 size-3.5 -translate-y-1/2 opacity-60" }),
1428
+ /* @__PURE__ */ jsxRuntime.jsx(
1429
+ Input,
1430
+ {
1431
+ autoFocus: true,
1432
+ placeholder: "Search...",
1433
+ value: search,
1434
+ onChange: (e) => setSearch(e.target.value),
1435
+ className: "h-8 pl-7 pr-7 text-sm"
1436
+ }
1437
+ ),
1438
+ search && /* @__PURE__ */ jsxRuntime.jsx(
1439
+ "button",
1440
+ {
1441
+ type: "button",
1442
+ onClick: () => setSearch(""),
1443
+ className: "absolute top-1/2 right-1.5 -translate-y-1/2 rounded p-0.5 hover:bg-muted",
1444
+ "aria-label": "Clear search",
1445
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.XIcon, { className: "size-3" })
1446
+ }
1447
+ )
1448
+ ] }),
1449
+ /* @__PURE__ */ jsxRuntime.jsxs("label", { className: "flex cursor-pointer items-center gap-2 rounded px-1 py-1 text-sm hover:bg-accent", children: [
1450
+ /* @__PURE__ */ jsxRuntime.jsx(
1451
+ Checkbox,
1452
+ {
1453
+ checked: visibleAllSelected ? true : visibleSomeSelected ? "indeterminate" : false,
1454
+ onCheckedChange: (c) => toggleAll(!!c)
1455
+ }
1456
+ ),
1457
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-xs font-medium", children: [
1458
+ "Select all ",
1459
+ search ? "(filtered)" : ""
1460
+ ] }),
1461
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "ml-auto text-[10px] text-muted-foreground", children: [
1462
+ draft.length,
1463
+ "/",
1464
+ options.length
1465
+ ] })
1466
+ ] }),
1467
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex max-h-60 flex-col gap-0.5 overflow-auto pr-0.5", children: visible.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-2 py-3 text-xs text-muted-foreground", children: "No matches" }) : visible.map((o) => {
1468
+ const checked = draft.includes(o.value);
1469
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1470
+ "label",
1471
+ {
1472
+ className: "flex cursor-pointer items-center gap-2 rounded px-1 py-1 text-sm hover:bg-accent",
1473
+ children: [
1474
+ /* @__PURE__ */ jsxRuntime.jsx(
1475
+ Checkbox,
1476
+ {
1477
+ checked,
1478
+ onCheckedChange: () => toggle(o.value)
1479
+ }
1480
+ ),
1481
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "truncate", children: o.label })
1482
+ ]
1483
+ },
1484
+ o.value
1485
+ );
1486
+ }) }),
1487
+ /* @__PURE__ */ jsxRuntime.jsx(
1488
+ FooterBar,
1489
+ {
1490
+ onApply: apply,
1491
+ onClear: clear,
1492
+ onClose,
1493
+ clearDisabled: !value
1494
+ }
1495
+ )
1496
+ ] });
1497
+ }
1498
+ function BooleanFilterPanel({
1499
+ column,
1500
+ onClose
1501
+ }) {
1502
+ const value = column.getFilterValue();
1503
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex flex-col gap-1", children: [
1504
+ { v: void 0, l: "All" },
1505
+ { v: true, l: "True" },
1506
+ { v: false, l: "False" }
1507
+ ].map((o) => /* @__PURE__ */ jsxRuntime.jsx(
1508
+ "button",
1509
+ {
1510
+ type: "button",
1511
+ onClick: () => {
1512
+ column.setFilterValue(o.v);
1513
+ onClose();
1514
+ },
1515
+ className: cn(
1516
+ "rounded px-2 py-1 text-left text-sm hover:bg-accent",
1517
+ value === o.v && "bg-accent text-accent-foreground"
1518
+ ),
1519
+ children: o.l
1520
+ },
1521
+ String(o.l)
1522
+ )) });
1523
+ }
1524
+ function ConditionRow({
1525
+ ops,
1526
+ c,
1527
+ onChange,
1528
+ renderInput
1529
+ }) {
1530
+ const showInput = !isUnary(c.op);
1531
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1.5", children: [
1532
+ /* @__PURE__ */ jsxRuntime.jsx(OpSelect, { ops, value: c.op, onChange: (op) => onChange({ ...c, op }) }),
1533
+ showInput && renderInput(c, onChange)
1534
+ ] });
1535
+ }
1536
+ function SecondCondition({
1537
+ draft,
1538
+ setDraft,
1539
+ ops,
1540
+ renderInput
1541
+ }) {
1542
+ const enabled = !!draft.c2;
1543
+ const c2 = draft.c2;
1544
+ const combine = draft.combine ?? "AND";
1545
+ if (!enabled) {
1546
+ return /* @__PURE__ */ jsxRuntime.jsx(
1547
+ "button",
1548
+ {
1549
+ type: "button",
1550
+ onClick: () => setDraft((d) => ({
1551
+ ...d,
1552
+ combine: d.combine ?? "AND",
1553
+ c2: { op: ops[0].v }
1554
+ })),
1555
+ className: "self-start text-[11px] font-medium text-primary hover:underline",
1556
+ children: "+ Add condition"
1557
+ }
1558
+ );
1559
+ }
1560
+ const setC2 = (n) => setDraft((d) => ({ ...d, c2: n }));
1561
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col gap-1.5", children: [
1562
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between", children: [
1563
+ /* @__PURE__ */ jsxRuntime.jsx(
1564
+ CombineToggle,
1565
+ {
1566
+ value: combine,
1567
+ onChange: (combine2) => setDraft((d) => ({ ...d, combine: combine2 }))
1568
+ }
1569
+ ),
1570
+ /* @__PURE__ */ jsxRuntime.jsx(
1571
+ "button",
1572
+ {
1573
+ type: "button",
1574
+ onClick: () => setDraft((d) => ({ ...d, combine: void 0, c2: void 0 })),
1575
+ className: "rounded p-0.5 text-muted-foreground hover:bg-muted hover:text-foreground",
1576
+ "aria-label": "Remove second condition",
1577
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.XIcon, { className: "size-3.5" })
1578
+ }
1579
+ )
1580
+ ] }),
1581
+ /* @__PURE__ */ jsxRuntime.jsx(
1582
+ ConditionRow,
1583
+ {
1584
+ ops,
1585
+ c: c2,
1586
+ onChange: setC2,
1587
+ renderInput
1588
+ }
1589
+ )
1590
+ ] });
1591
+ }
1592
+ function DataTableColumnHeader({
1593
+ column,
1594
+ title
1595
+ }) {
1596
+ const filterType = column.columnDef.meta?.filterType;
1597
+ const canSort = column.getCanSort();
1598
+ const canFilter = column.getCanFilter() && !!filterType;
1599
+ const canHide = column.getCanHide();
1600
+ const canPin = column.getCanPin();
1601
+ const sorted = column.getIsSorted();
1602
+ const isPinned = column.getIsPinned();
1603
+ const filterValue = column.getFilterValue();
1604
+ const isFiltered = isFilterActive(filterType, filterValue);
1605
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-1", children: [
1606
+ /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenu, { children: [
1607
+ /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(
1608
+ Button,
1609
+ {
1610
+ variant: "ghost",
1611
+ size: "xs",
1612
+ className: "-ml-2 h-7 px-1.5 text-xs font-medium data-[state=open]:bg-accent",
1613
+ children: [
1614
+ /* @__PURE__ */ jsxRuntime.jsx("span", { children: title }),
1615
+ canSort ? sorted === "desc" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDownIcon, { className: "opacity-70" }) : sorted === "asc" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUpIcon, { className: "opacity-70" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronsUpDownIcon, { className: "opacity-50" }) : null
1616
+ ]
1617
+ }
1618
+ ) }),
1619
+ /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenuContent, { align: "start", className: "w-44", children: [
1620
+ canSort && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1621
+ /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenuItem, { onSelect: () => column.toggleSorting(false), children: [
1622
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowUpIcon, {}),
1623
+ " Ascending"
1624
+ ] }),
1625
+ /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenuItem, { onSelect: () => column.toggleSorting(true), children: [
1626
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ArrowDownIcon, {}),
1627
+ " Descending"
1628
+ ] }),
1629
+ sorted && /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenuItem, { onSelect: () => column.clearSorting(), children: [
1630
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronsUpDownIcon, {}),
1631
+ " Clear sort"
1632
+ ] }),
1633
+ /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuSeparator, {})
1634
+ ] }),
1635
+ canPin && /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1636
+ /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuLabel, { children: "Pin column" }),
1637
+ /* @__PURE__ */ jsxRuntime.jsxs(
1638
+ DropdownMenuItem,
1639
+ {
1640
+ onSelect: () => column.pin(isPinned === "left" ? false : "left"),
1641
+ children: [
1642
+ isPinned === "left" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PinOffIcon, {}) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PinIcon, {}),
1643
+ " ",
1644
+ isPinned === "left" ? "Unpin from left" : "Pin to left"
1645
+ ]
1646
+ }
1647
+ ),
1648
+ /* @__PURE__ */ jsxRuntime.jsxs(
1649
+ DropdownMenuItem,
1650
+ {
1651
+ onSelect: () => column.pin(isPinned === "right" ? false : "right"),
1652
+ children: [
1653
+ isPinned === "right" ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PinOffIcon, {}) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PinIcon, {}),
1654
+ " ",
1655
+ isPinned === "right" ? "Unpin from right" : "Pin to right"
1656
+ ]
1657
+ }
1658
+ ),
1659
+ /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuSeparator, {})
1660
+ ] }),
1661
+ canHide && /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenuItem, { onSelect: () => column.toggleVisibility(false), children: [
1662
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.EyeOffIcon, {}),
1663
+ " Hide column"
1664
+ ] })
1665
+ ] })
1666
+ ] }),
1667
+ canFilter && /* @__PURE__ */ jsxRuntime.jsx(ColumnFilter, { column, isFiltered })
1668
+ ] });
1669
+ }
1670
+ function ColumnFilter({
1671
+ column,
1672
+ isFiltered
1673
+ }) {
1674
+ const [open, setOpen] = react.useState(false);
1675
+ return /* @__PURE__ */ jsxRuntime.jsxs(Popover, { open, onOpenChange: setOpen, children: [
1676
+ /* @__PURE__ */ jsxRuntime.jsx(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
1677
+ Button,
1678
+ {
1679
+ variant: "ghost",
1680
+ size: "icon-xs",
1681
+ className: cn(
1682
+ "size-6",
1683
+ isFiltered && "bg-primary/10 text-primary"
1684
+ ),
1685
+ "aria-label": "Filter column",
1686
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FilterIcon, {})
1687
+ }
1688
+ ) }),
1689
+ /* @__PURE__ */ jsxRuntime.jsxs(PopoverContent, { align: "end", className: "w-72 p-2", children: [
1690
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mb-2 flex items-center justify-between", children: [
1691
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-xs font-medium text-muted-foreground", children: "Filter" }),
1692
+ isFiltered && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "rounded-full bg-primary/10 px-1.5 py-0.5 text-[10px] font-medium text-primary", children: "Active" })
1693
+ ] }),
1694
+ /* @__PURE__ */ jsxRuntime.jsx(ColumnFilterPanel, { column, onClose: () => setOpen(false) })
1695
+ ] })
1696
+ ] });
1697
+ }
1698
+ var builtinIcons = {
1699
+ view: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.EyeIcon, {}),
1700
+ edit: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.PencilIcon, {}),
1701
+ duplicate: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CopyIcon, {}),
1702
+ delete: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Trash2Icon, {})
1703
+ };
1704
+ var builtinLabels = {
1705
+ view: "View",
1706
+ edit: "Edit",
1707
+ duplicate: "Duplicate",
1708
+ delete: "Delete"
1709
+ };
1710
+ function DataTableRowActions({
1711
+ row,
1712
+ actions = ["view", "edit", "duplicate", "delete"],
1713
+ customActions = [],
1714
+ onAction,
1715
+ isEditing,
1716
+ onSaveEdit,
1717
+ onCancelEdit
1718
+ }) {
1719
+ const [open, setOpen] = react.useState(false);
1720
+ const menuRef = react.useRef(null);
1721
+ react.useEffect(() => {
1722
+ if (!open) return;
1723
+ const onPointerDown = (event) => {
1724
+ if (!menuRef.current?.contains(event.target)) setOpen(false);
1725
+ };
1726
+ document.addEventListener("pointerdown", onPointerDown);
1727
+ return () => document.removeEventListener("pointerdown", onPointerDown);
1728
+ }, [open]);
1729
+ if (isEditing) {
1730
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-end gap-1", children: [
1731
+ /* @__PURE__ */ jsxRuntime.jsx(
1732
+ Button,
1733
+ {
1734
+ size: "icon-xs",
1735
+ variant: "ghost",
1736
+ onClick: onSaveEdit,
1737
+ "aria-label": "Save row",
1738
+ className: "text-emerald-600 hover:text-emerald-600",
1739
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckIcon, {})
1740
+ }
1741
+ ),
1742
+ /* @__PURE__ */ jsxRuntime.jsx(
1743
+ Button,
1744
+ {
1745
+ size: "icon-xs",
1746
+ variant: "ghost",
1747
+ onClick: onCancelEdit,
1748
+ "aria-label": "Cancel edit",
1749
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.XIcon, {})
1750
+ }
1751
+ )
1752
+ ] });
1753
+ }
1754
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { ref: menuRef, className: "relative flex justify-end", children: [
1755
+ /* @__PURE__ */ jsxRuntime.jsx(
1756
+ Button,
1757
+ {
1758
+ variant: "ghost",
1759
+ size: "icon-xs",
1760
+ "aria-label": "Open row actions",
1761
+ "aria-expanded": open,
1762
+ onClick: () => setOpen((value) => !value),
1763
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MoreHorizontalIcon, {})
1764
+ }
1765
+ ),
1766
+ open && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "absolute top-7 right-0 z-50 w-40 rounded-md border bg-popover p-1 text-popover-foreground shadow-md", children: [
1767
+ actions.map((a) => /* @__PURE__ */ jsxRuntime.jsxs(
1768
+ RowActionButton,
1769
+ {
1770
+ danger: a === "delete",
1771
+ onClick: () => {
1772
+ setOpen(false);
1773
+ onAction?.(a, row);
1774
+ },
1775
+ children: [
1776
+ builtinIcons[a],
1777
+ " ",
1778
+ builtinLabels[a]
1779
+ ]
1780
+ },
1781
+ a
1782
+ )),
1783
+ customActions.length > 0 && actions.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "-mx-1 my-1 h-px bg-border" }),
1784
+ customActions.filter((c) => c.show ? c.show(row) : true).map((c) => /* @__PURE__ */ jsxRuntime.jsxs(
1785
+ RowActionButton,
1786
+ {
1787
+ danger: c.danger,
1788
+ onClick: () => {
1789
+ setOpen(false);
1790
+ onAction?.(c.id, row);
1791
+ },
1792
+ children: [
1793
+ c.icon,
1794
+ " ",
1795
+ c.label
1796
+ ]
1797
+ },
1798
+ c.id
1799
+ ))
1800
+ ] })
1801
+ ] });
1802
+ }
1803
+ function RowActionButton({
1804
+ children,
1805
+ danger,
1806
+ onClick
1807
+ }) {
1808
+ return /* @__PURE__ */ jsxRuntime.jsx(
1809
+ "button",
1810
+ {
1811
+ type: "button",
1812
+ onClick,
1813
+ className: cn(
1814
+ "flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm outline-none hover:bg-accent hover:text-accent-foreground focus-visible:bg-accent",
1815
+ danger && "text-destructive hover:bg-destructive/10 hover:text-destructive"
1816
+ ),
1817
+ children
1818
+ }
1819
+ );
1820
+ }
1821
+ function EditableCell({
1822
+ getValue,
1823
+ column,
1824
+ row,
1825
+ isEditing,
1826
+ autoOpen,
1827
+ onCommit,
1828
+ onCancel,
1829
+ onStartEdit
1830
+ }) {
1831
+ const meta = column.columnDef.meta;
1832
+ const editor = meta?.editor ?? "text";
1833
+ const align = meta?.align ?? "left";
1834
+ const initial = getValue();
1835
+ const [local, setLocal] = react.useState(initial);
1836
+ const inputRef = react.useRef(null);
1837
+ react.useEffect(() => {
1838
+ setLocal(initial);
1839
+ }, [initial, isEditing]);
1840
+ react.useEffect(() => {
1841
+ if (isEditing && inputRef.current) inputRef.current.focus();
1842
+ }, [isEditing]);
1843
+ const isEditable = (() => {
1844
+ const flag = meta?.isEditable;
1845
+ if (flag === void 0) return true;
1846
+ if (typeof flag === "function") return flag(row.original);
1847
+ return flag;
1848
+ })();
1849
+ if (!isEditing) {
1850
+ const display = renderDisplay(initial, editor, meta);
1851
+ return /* @__PURE__ */ jsxRuntime.jsx(
1852
+ "div",
1853
+ {
1854
+ className: cn(
1855
+ "flex h-7 items-center gap-1.5 rounded px-1.5 text-sm",
1856
+ isEditable && "cursor-text outline-1 outline-transparent transition-colors hover:bg-muted/60 hover:outline-border",
1857
+ align === "right" && "justify-end",
1858
+ align === "center" && "justify-center"
1859
+ ),
1860
+ onDoubleClick: () => isEditable && onStartEdit(),
1861
+ title: isEditable ? "Double-click to edit" : void 0,
1862
+ children: display
1863
+ }
1864
+ );
1865
+ }
1866
+ const commit = (value) => onCommit(value);
1867
+ const handleKey = (e) => {
1868
+ if (e.key === "Enter") commit(local);
1869
+ else if (e.key === "Escape") onCancel();
1870
+ };
1871
+ if (editor === "select") {
1872
+ const opts = meta?.selectOptions ?? [];
1873
+ if (!autoOpen) {
1874
+ return /* @__PURE__ */ jsxRuntime.jsx(
1875
+ "select",
1876
+ {
1877
+ value: String(local ?? ""),
1878
+ onChange: (e) => {
1879
+ setLocal(e.target.value);
1880
+ commit(e.target.value);
1881
+ },
1882
+ className: cn(
1883
+ "h-7 w-full rounded-md border border-input bg-background px-1.5 text-xs outline-none focus-visible:border-ring",
1884
+ align === "right" && "text-right",
1885
+ align === "center" && "text-center"
1886
+ ),
1887
+ children: opts.map((o) => /* @__PURE__ */ jsxRuntime.jsx("option", { value: o.value, children: o.label }, o.value))
1888
+ }
1889
+ );
1890
+ }
1891
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1892
+ Select,
1893
+ {
1894
+ defaultOpen: autoOpen,
1895
+ value: String(local ?? ""),
1896
+ onValueChange: (v) => commit(v),
1897
+ children: [
1898
+ /* @__PURE__ */ jsxRuntime.jsx(SelectTrigger, { size: "sm", className: "h-7 w-full text-xs", children: /* @__PURE__ */ jsxRuntime.jsx(SelectValue, {}) }),
1899
+ /* @__PURE__ */ jsxRuntime.jsx(SelectContent, { children: opts.map((o) => /* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: o.value, children: o.label }, o.value)) })
1900
+ ]
1901
+ }
1902
+ );
1903
+ }
1904
+ if (editor === "checkbox" || editor === "switch") {
1905
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex h-7 items-center justify-center", children: /* @__PURE__ */ jsxRuntime.jsx(
1906
+ Checkbox,
1907
+ {
1908
+ checked: !!local,
1909
+ onCheckedChange: (c) => commit(!!c),
1910
+ autoFocus: true
1911
+ }
1912
+ ) });
1913
+ }
1914
+ return /* @__PURE__ */ jsxRuntime.jsx(
1915
+ Input,
1916
+ {
1917
+ ref: inputRef,
1918
+ type: editor === "number" || editor === "currency" ? "number" : editor === "date" ? "date" : "text",
1919
+ value: local == null ? "" : String(local),
1920
+ onChange: (e) => {
1921
+ if (editor === "number" || editor === "currency") {
1922
+ setLocal(e.target.value === "" ? null : Number(e.target.value));
1923
+ } else {
1924
+ setLocal(e.target.value);
1925
+ }
1926
+ },
1927
+ onBlur: () => commit(local),
1928
+ onKeyDown: handleKey,
1929
+ className: cn(
1930
+ "h-7 px-1.5 text-sm",
1931
+ align === "right" && "text-right",
1932
+ align === "center" && "text-center"
1933
+ )
1934
+ }
1935
+ );
1936
+ }
1937
+ function renderDisplay(value, editor, meta) {
1938
+ if (value === null || value === void 0 || value === "") {
1939
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground/60", children: "\u2014" });
1940
+ }
1941
+ if (editor === "currency") {
1942
+ const n = typeof value === "number" ? value : Number(value);
1943
+ return Number.isFinite(n) ? new Intl.NumberFormat(void 0, {
1944
+ style: "currency",
1945
+ currency: "USD",
1946
+ maximumFractionDigits: 2
1947
+ }).format(n) : String(value);
1948
+ }
1949
+ if (editor === "number") {
1950
+ const n = typeof value === "number" ? value : Number(value);
1951
+ return Number.isFinite(n) ? new Intl.NumberFormat().format(n) : String(value);
1952
+ }
1953
+ if (editor === "checkbox" || editor === "switch") {
1954
+ return /* @__PURE__ */ jsxRuntime.jsx(Checkbox, { checked: !!value, disabled: true, "aria-label": "value" });
1955
+ }
1956
+ if (editor === "select") {
1957
+ const opt = meta?.selectOptions?.find((o) => o.value === value);
1958
+ const label = opt?.label ?? String(value);
1959
+ if (meta?.badgeMap) {
1960
+ const variant = meta.badgeMap[String(value)] ?? "default";
1961
+ return /* @__PURE__ */ jsxRuntime.jsx(Badge, { variant, children: label });
1962
+ }
1963
+ return label;
1964
+ }
1965
+ if (editor === "date") {
1966
+ if (value instanceof Date) return value.toLocaleDateString();
1967
+ return String(value);
1968
+ }
1969
+ return String(value);
1970
+ }
1971
+
1972
+ // src/theme.ts
1973
+ var TOKEN_TO_VAR = {
1974
+ background: "--background",
1975
+ foreground: "--foreground",
1976
+ card: "--card",
1977
+ cardForeground: "--card-foreground",
1978
+ popover: "--popover",
1979
+ popoverForeground: "--popover-foreground",
1980
+ primary: "--primary",
1981
+ primaryForeground: "--primary-foreground",
1982
+ secondary: "--secondary",
1983
+ secondaryForeground: "--secondary-foreground",
1984
+ muted: "--muted",
1985
+ mutedForeground: "--muted-foreground",
1986
+ accent: "--accent",
1987
+ accentForeground: "--accent-foreground",
1988
+ destructive: "--destructive",
1989
+ destructiveForeground: "--destructive-foreground",
1990
+ border: "--border",
1991
+ input: "--input",
1992
+ ring: "--ring",
1993
+ radius: "--radius",
1994
+ fontFamily: "--font-family"
1995
+ };
1996
+ function themeToStyle(theme) {
1997
+ if (!theme) return {};
1998
+ const out = {};
1999
+ for (const [k, v] of Object.entries(theme)) {
2000
+ if (v == null) continue;
2001
+ const cssVar = TOKEN_TO_VAR[k];
2002
+ if (cssVar) out[cssVar] = v;
2003
+ }
2004
+ return out;
2005
+ }
2006
+ var themePresets = {
2007
+ light: {
2008
+ background: "oklch(1 0 0)",
2009
+ foreground: "oklch(0.145 0 0)",
2010
+ card: "oklch(1 0 0)",
2011
+ cardForeground: "oklch(0.145 0 0)",
2012
+ primary: "oklch(0.205 0 0)",
2013
+ primaryForeground: "oklch(0.985 0 0)",
2014
+ border: "oklch(0.922 0 0)"
2015
+ },
2016
+ dark: {
2017
+ background: "oklch(0.145 0 0)",
2018
+ foreground: "oklch(0.985 0 0)",
2019
+ card: "oklch(0.205 0 0)",
2020
+ cardForeground: "oklch(0.985 0 0)",
2021
+ primary: "oklch(0.985 0 0)",
2022
+ primaryForeground: "oklch(0.205 0 0)",
2023
+ border: "oklch(0.269 0 0)"
2024
+ },
2025
+ emerald: {
2026
+ primary: "oklch(0.66 0.15 162)",
2027
+ primaryForeground: "oklch(1 0 0)",
2028
+ accent: "oklch(0.94 0.04 162)",
2029
+ accentForeground: "oklch(0.3 0.1 162)",
2030
+ ring: "oklch(0.66 0.15 162 / 0.5)"
2031
+ },
2032
+ violet: {
2033
+ primary: "oklch(0.55 0.22 285)",
2034
+ primaryForeground: "oklch(1 0 0)",
2035
+ accent: "oklch(0.94 0.04 285)",
2036
+ accentForeground: "oklch(0.3 0.12 285)",
2037
+ ring: "oklch(0.55 0.22 285 / 0.5)"
2038
+ },
2039
+ amber: {
2040
+ primary: "oklch(0.7 0.18 65)",
2041
+ primaryForeground: "oklch(0.2 0 0)",
2042
+ accent: "oklch(0.94 0.05 65)",
2043
+ accentForeground: "oklch(0.3 0.12 65)",
2044
+ ring: "oklch(0.7 0.18 65 / 0.5)"
2045
+ }
2046
+ };
2047
+ function getPinningStyles(column) {
2048
+ const isPinned = column.getIsPinned();
2049
+ if (!isPinned) return {};
2050
+ const isLast = isPinned === "left" && column.getIsLastColumn("left");
2051
+ const isFirst = isPinned === "right" && column.getIsFirstColumn("right");
2052
+ return {
2053
+ position: "sticky",
2054
+ left: isPinned === "left" ? `${column.getStart("left")}px` : void 0,
2055
+ right: isPinned === "right" ? `${column.getAfter("right")}px` : void 0,
2056
+ zIndex: 1,
2057
+ boxShadow: isLast ? "1px 0 0 0 var(--border)" : isFirst ? "-1px 0 0 0 var(--border)" : void 0
2058
+ };
2059
+ }
2060
+ var DataTableEditingContext = react.createContext(
2061
+ null
2062
+ );
2063
+ function useEditingContext() {
2064
+ const ctx = react.useContext(DataTableEditingContext);
2065
+ if (!ctx) throw new Error("DataTable editing context missing");
2066
+ return ctx;
2067
+ }
2068
+ function EditableCellHost(ctx) {
2069
+ const {
2070
+ editingCell,
2071
+ editingRowId,
2072
+ setEditingCell,
2073
+ setRowDraft,
2074
+ onCellEdit
2075
+ } = useEditingContext();
2076
+ const colId = ctx.column.id;
2077
+ const rowId = ctx.row.id;
2078
+ const isCellEdit = editingCell?.rowId === rowId && editingCell.colId === colId;
2079
+ const isRowEdit = editingRowId === rowId;
2080
+ const isEditing = isCellEdit || isRowEdit;
2081
+ return /* @__PURE__ */ jsxRuntime.jsx(
2082
+ EditableCell,
2083
+ {
2084
+ ...ctx,
2085
+ isEditing,
2086
+ autoOpen: isCellEdit,
2087
+ onStartEdit: () => setEditingCell({ rowId, colId }),
2088
+ onCancel: () => {
2089
+ if (isCellEdit) setEditingCell(null);
2090
+ },
2091
+ onCommit: (value) => {
2092
+ if (isRowEdit) {
2093
+ setRowDraft((d) => ({
2094
+ ...d,
2095
+ [rowId]: {
2096
+ ...d[rowId],
2097
+ [colId]: value
2098
+ }
2099
+ }));
2100
+ return;
2101
+ }
2102
+ setEditingCell(null);
2103
+ if (value !== ctx.getValue()) {
2104
+ onCellEdit?.(ctx.row.original, colId, value);
2105
+ }
2106
+ }
2107
+ }
2108
+ );
2109
+ }
2110
+ function ActionsCellHost({ row }) {
2111
+ const {
2112
+ editingRowId,
2113
+ setEditingRowId,
2114
+ rowDraft,
2115
+ setRowDraft,
2116
+ rowActions,
2117
+ customRowActions,
2118
+ onRowAction,
2119
+ onRowSave
2120
+ } = useEditingContext();
2121
+ const rowId = row.id;
2122
+ const isRowEdit = editingRowId === rowId;
2123
+ return /* @__PURE__ */ jsxRuntime.jsx(
2124
+ DataTableRowActions,
2125
+ {
2126
+ row: row.original,
2127
+ actions: rowActions,
2128
+ customActions: customRowActions,
2129
+ isEditing: isRowEdit,
2130
+ onSaveEdit: () => {
2131
+ onRowSave?.(row.original, rowDraft[rowId] ?? {});
2132
+ setRowDraft((d) => {
2133
+ const { [rowId]: _, ...rest } = d;
2134
+ return rest;
2135
+ });
2136
+ setEditingRowId(null);
2137
+ },
2138
+ onCancelEdit: () => {
2139
+ setRowDraft((d) => {
2140
+ const { [rowId]: _, ...rest } = d;
2141
+ return rest;
2142
+ });
2143
+ setEditingRowId(null);
2144
+ },
2145
+ onAction: (a) => {
2146
+ if (a === "edit") {
2147
+ setEditingRowId(rowId);
2148
+ return;
2149
+ }
2150
+ onRowAction?.(a, row.original);
2151
+ }
2152
+ }
2153
+ );
2154
+ }
2155
+ var DEFAULT_LABELS = {
2156
+ search: "Search...",
2157
+ addRow: "Add row",
2158
+ delete: "Delete",
2159
+ clear: "Clear",
2160
+ selected: "selected",
2161
+ refresh: "Refresh",
2162
+ columns: "Columns",
2163
+ export: "Export",
2164
+ csv: "CSV",
2165
+ excel: "Excel",
2166
+ total: "Total",
2167
+ noData: "No data.",
2168
+ noResults: "No rows match the current filters.",
2169
+ refreshing: "Refreshing",
2170
+ rowsPerPage: "Rows per page",
2171
+ page: "Page",
2172
+ of: "of"
2173
+ };
2174
+ var DENSITY_CLASS = {
2175
+ compact: "[&_tbody_td]:py-0.5 [&_tbody_td]:text-xs",
2176
+ default: "",
2177
+ comfortable: "[&_tbody_td]:py-2"
2178
+ };
2179
+ function DataTable({
2180
+ data,
2181
+ columns,
2182
+ isLoading,
2183
+ isFetching,
2184
+ onRefresh,
2185
+ totalRecords,
2186
+ exportFileName,
2187
+ enableSelection = true,
2188
+ renderSubRow,
2189
+ getSubRows,
2190
+ rowActions = ["view", "edit", "duplicate", "delete"],
2191
+ customRowActions,
2192
+ onRowAction,
2193
+ onCellEdit,
2194
+ onRowSave,
2195
+ onAddRow,
2196
+ onBulkDelete,
2197
+ initialPageSize = 10,
2198
+ pageSizeOptions,
2199
+ initialColumnPinning,
2200
+ initialColumnVisibility,
2201
+ globalFilter: globalFilterProp,
2202
+ onGlobalFilterChange,
2203
+ className,
2204
+ toolbarSlot,
2205
+ features,
2206
+ labels: labelsProp,
2207
+ density = "default",
2208
+ theme
2209
+ }) {
2210
+ const labels = { ...DEFAULT_LABELS, ...labelsProp };
2211
+ const f = {
2212
+ search: features?.search ?? true,
2213
+ refresh: features?.refresh ?? !!onRefresh,
2214
+ columnVisibility: features?.columnVisibility ?? true,
2215
+ export: features?.export ?? true,
2216
+ addRow: features?.addRow ?? !!onAddRow,
2217
+ pagination: features?.pagination ?? true,
2218
+ sorting: features?.sorting ?? true,
2219
+ filtering: features?.filtering ?? true,
2220
+ resizing: features?.resizing ?? true,
2221
+ reordering: features?.reordering ?? true,
2222
+ pinning: features?.pinning ?? true
2223
+ };
2224
+ const themeStyle = themeToStyle(theme);
2225
+ const [sorting, setSorting] = react.useState([]);
2226
+ const [columnFilters, setColumnFilters] = react.useState([]);
2227
+ const [internalGlobalFilter, setInternalGlobalFilter] = react.useState("");
2228
+ const isControlledFilter = globalFilterProp !== void 0;
2229
+ const globalFilter = isControlledFilter ? globalFilterProp : internalGlobalFilter;
2230
+ const setGlobalFilter = (v) => {
2231
+ if (isControlledFilter) onGlobalFilterChange?.(v);
2232
+ else setInternalGlobalFilter(v);
2233
+ };
2234
+ const [columnVisibility, setColumnVisibility] = react.useState(
2235
+ initialColumnVisibility ?? {}
2236
+ );
2237
+ const [columnPinning, setColumnPinning] = react.useState(
2238
+ () => {
2239
+ const base = initialColumnPinning ?? { left: [], right: [] };
2240
+ const left = [...base.left ?? []];
2241
+ const right = [...base.right ?? []];
2242
+ const hasExpandColumn = !!(renderSubRow || getSubRows);
2243
+ if (hasExpandColumn && left.includes("__select") && !left.includes("__expand")) {
2244
+ const idx = left.indexOf("__select");
2245
+ left.splice(idx + 1, 0, "__expand");
2246
+ }
2247
+ return { left, right };
2248
+ }
2249
+ );
2250
+ const [columnSizing, setColumnSizing] = react.useState({});
2251
+ const [rowSelection, setRowSelection] = react.useState({});
2252
+ const [expanded, setExpanded] = react.useState({});
2253
+ const [pagination, setPagination] = react.useState({
2254
+ pageIndex: 0,
2255
+ pageSize: initialPageSize
2256
+ });
2257
+ const [editingCell, setEditingCell] = react.useState(null);
2258
+ const [editingRowId, setEditingRowId] = react.useState(null);
2259
+ const [rowDraft, setRowDraft] = react.useState({});
2260
+ const [extraRows, setExtraRows] = react.useState([]);
2261
+ const mergedData = react.useMemo(
2262
+ () => extraRows.length ? [...extraRows, ...data] : data,
2263
+ [extraRows, data]
2264
+ );
2265
+ const augmentedColumns = react.useMemo(() => {
2266
+ const wrap = (col) => {
2267
+ const meta = col.meta;
2268
+ const hasEditor = !!meta?.editor;
2269
+ const id = col.id ?? col.accessorKey;
2270
+ const original = col.cell;
2271
+ const headerLabel = meta?.label ?? (typeof col.header === "string" ? col.header : id ?? "");
2272
+ const defaultFilterFn = (() => {
2273
+ switch (meta?.filterType) {
2274
+ case "text":
2275
+ return textFilterFn;
2276
+ case "number":
2277
+ return numberFilterFn;
2278
+ case "date":
2279
+ return dateFilterFn;
2280
+ case "select":
2281
+ case "multi-select":
2282
+ return setFilterFn;
2283
+ case "boolean":
2284
+ return booleanFilterFn;
2285
+ default:
2286
+ return void 0;
2287
+ }
2288
+ })();
2289
+ return {
2290
+ ...col,
2291
+ filterFn: col.filterFn ?? defaultFilterFn,
2292
+ header: typeof col.header === "function" ? col.header : (ctx) => /* @__PURE__ */ jsxRuntime.jsx(
2293
+ DataTableColumnHeader,
2294
+ {
2295
+ column: ctx.column,
2296
+ title: headerLabel
2297
+ }
2298
+ ),
2299
+ cell: hasEditor ? EditableCellHost : original
2300
+ };
2301
+ };
2302
+ const built = columns.map(wrap);
2303
+ if (renderSubRow || getSubRows) {
2304
+ built.unshift({
2305
+ id: "__expand",
2306
+ size: 32,
2307
+ enableSorting: false,
2308
+ enableHiding: false,
2309
+ enableResizing: false,
2310
+ header: () => null,
2311
+ cell: ({ row }) => row.getCanExpand() ? /* @__PURE__ */ jsxRuntime.jsx(
2312
+ "button",
2313
+ {
2314
+ type: "button",
2315
+ onClick: row.getToggleExpandedHandler(),
2316
+ className: "flex size-6 items-center justify-center rounded text-muted-foreground hover:bg-muted hover:text-foreground",
2317
+ "aria-label": row.getIsExpanded() ? "Collapse row" : "Expand row",
2318
+ children: row.getIsExpanded() ? /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDownIcon, { className: "size-3.5" }) : /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRightIcon, { className: "size-3.5" })
2319
+ }
2320
+ ) : null
2321
+ });
2322
+ }
2323
+ if (enableSelection) {
2324
+ built.unshift({
2325
+ id: "__select",
2326
+ size: 36,
2327
+ enableSorting: false,
2328
+ enableHiding: false,
2329
+ enableResizing: false,
2330
+ header: ({ table: table2 }) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center px-1", children: /* @__PURE__ */ jsxRuntime.jsx(
2331
+ Checkbox,
2332
+ {
2333
+ checked: table2.getIsAllPageRowsSelected() ? true : table2.getIsSomePageRowsSelected() ? "indeterminate" : false,
2334
+ onCheckedChange: (c) => table2.toggleAllPageRowsSelected(!!c),
2335
+ "aria-label": "Select all rows on this page"
2336
+ }
2337
+ ) }),
2338
+ cell: ({ row }) => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex items-center justify-center px-1", children: /* @__PURE__ */ jsxRuntime.jsx(
2339
+ Checkbox,
2340
+ {
2341
+ checked: row.getIsSelected(),
2342
+ onCheckedChange: (c) => row.toggleSelected(!!c),
2343
+ "aria-label": "Select row"
2344
+ }
2345
+ ) })
2346
+ });
2347
+ }
2348
+ if (rowActions.length || customRowActions?.length) {
2349
+ built.push({
2350
+ id: "__actions",
2351
+ size: 56,
2352
+ enableSorting: false,
2353
+ enableHiding: false,
2354
+ enableResizing: false,
2355
+ header: () => /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-right text-xs", children: "Actions" }),
2356
+ cell: ActionsCellHost
2357
+ });
2358
+ }
2359
+ return built;
2360
+ }, [
2361
+ columns,
2362
+ enableSelection,
2363
+ renderSubRow,
2364
+ getSubRows,
2365
+ rowActions.length,
2366
+ customRowActions?.length
2367
+ ]);
2368
+ const [columnOrder, setColumnOrder] = react.useState(
2369
+ () => augmentedColumns.map((c) => c.id ?? c.accessorKey ?? "")
2370
+ );
2371
+ react.useEffect(() => {
2372
+ const next = augmentedColumns.map(
2373
+ (c) => c.id ?? c.accessorKey ?? ""
2374
+ );
2375
+ setColumnOrder((prev) => {
2376
+ const existing = prev.filter((id) => next.includes(id));
2377
+ const added = next.filter((id) => !prev.includes(id));
2378
+ const final = [...existing, ...added];
2379
+ return final.length === prev.length && final.every((v, i) => prev[i] === v) ? prev : final;
2380
+ });
2381
+ }, [augmentedColumns]);
2382
+ const table = reactTable.useReactTable({
2383
+ data: mergedData,
2384
+ columns: augmentedColumns,
2385
+ state: {
2386
+ sorting,
2387
+ columnFilters,
2388
+ globalFilter,
2389
+ columnVisibility,
2390
+ columnPinning,
2391
+ columnSizing,
2392
+ columnOrder,
2393
+ rowSelection,
2394
+ expanded,
2395
+ pagination
2396
+ },
2397
+ onSortingChange: setSorting,
2398
+ onColumnFiltersChange: setColumnFilters,
2399
+ onGlobalFilterChange: setGlobalFilter,
2400
+ onColumnVisibilityChange: setColumnVisibility,
2401
+ onColumnPinningChange: setColumnPinning,
2402
+ onColumnSizingChange: setColumnSizing,
2403
+ onColumnOrderChange: setColumnOrder,
2404
+ onRowSelectionChange: setRowSelection,
2405
+ onExpandedChange: setExpanded,
2406
+ onPaginationChange: setPagination,
2407
+ enableSorting: f.sorting,
2408
+ enableColumnFilters: f.filtering,
2409
+ enableColumnResizing: f.resizing,
2410
+ columnResizeMode: "onChange",
2411
+ enableRowSelection: true,
2412
+ enableExpanding: !!(renderSubRow || getSubRows),
2413
+ enablePinning: f.pinning,
2414
+ getRowId: (row) => String(row.id),
2415
+ getSubRows,
2416
+ getRowCanExpand: (row) => {
2417
+ if (renderSubRow) return true;
2418
+ const subs = getSubRows?.(row.original);
2419
+ return !!subs && subs.length > 0;
2420
+ },
2421
+ globalFilterFn: (row, _columnId, value) => {
2422
+ const q = String(value ?? "").toLowerCase().trim();
2423
+ if (!q) return true;
2424
+ return row.getAllCells().some((cell) => {
2425
+ const v = cell.getValue();
2426
+ return v != null && String(v).toLowerCase().includes(q);
2427
+ });
2428
+ },
2429
+ filterFns: {
2430
+ // attached via columnDef.filterFn below where needed
2431
+ },
2432
+ getCoreRowModel: reactTable.getCoreRowModel(),
2433
+ getFilteredRowModel: reactTable.getFilteredRowModel(),
2434
+ getSortedRowModel: reactTable.getSortedRowModel(),
2435
+ getExpandedRowModel: reactTable.getExpandedRowModel(),
2436
+ getPaginationRowModel: reactTable.getPaginationRowModel()
2437
+ });
2438
+ const dragState = react.useRef({ src: null });
2439
+ const onHeaderDragStart = (id, e) => {
2440
+ dragState.current.src = id;
2441
+ e.dataTransfer.effectAllowed = "move";
2442
+ e.dataTransfer.setData("text/plain", id);
2443
+ };
2444
+ const onHeaderDragOver = (e) => {
2445
+ e.preventDefault();
2446
+ e.dataTransfer.dropEffect = "move";
2447
+ };
2448
+ const onHeaderDrop = (targetId) => {
2449
+ const src = dragState.current.src;
2450
+ dragState.current.src = null;
2451
+ if (!src || src === targetId) return;
2452
+ setColumnOrder((order) => {
2453
+ const next = [...order];
2454
+ const from = next.indexOf(src);
2455
+ const to = next.indexOf(targetId);
2456
+ if (from === -1 || to === -1) return order;
2457
+ next.splice(from, 1);
2458
+ next.splice(to, 0, src);
2459
+ return next;
2460
+ });
2461
+ };
2462
+ const handleAddRow = onAddRow ? () => {
2463
+ const draft = onAddRow();
2464
+ const tempId = `__new_${Date.now()}`;
2465
+ const newRow = { id: tempId, ...draft };
2466
+ setExtraRows((r) => [newRow, ...r]);
2467
+ setPagination((p) => ({ ...p, pageIndex: 0 }));
2468
+ setEditingRowId(tempId);
2469
+ } : void 0;
2470
+ const rows = table.getRowModel().rows;
2471
+ const visibleLeafCount = table.getVisibleLeafColumns().length;
2472
+ const editingContextValue = react.useMemo(
2473
+ () => ({
2474
+ editingCell,
2475
+ editingRowId,
2476
+ rowDraft,
2477
+ setEditingCell,
2478
+ setEditingRowId,
2479
+ setRowDraft,
2480
+ rowActions,
2481
+ customRowActions,
2482
+ onRowAction,
2483
+ onCellEdit,
2484
+ onRowSave
2485
+ }),
2486
+ [
2487
+ editingCell,
2488
+ editingRowId,
2489
+ rowDraft,
2490
+ rowActions,
2491
+ customRowActions,
2492
+ onRowAction,
2493
+ onCellEdit,
2494
+ onRowSave
2495
+ ]
2496
+ );
2497
+ return /* @__PURE__ */ jsxRuntime.jsx(
2498
+ DataTableEditingContext.Provider,
2499
+ {
2500
+ value: editingContextValue,
2501
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
2502
+ "div",
2503
+ {
2504
+ className: cn(
2505
+ "flex flex-col rounded-lg border bg-card text-card-foreground shadow-sm",
2506
+ DENSITY_CLASS[density],
2507
+ className
2508
+ ),
2509
+ style: themeStyle,
2510
+ children: [
2511
+ /* @__PURE__ */ jsxRuntime.jsx(
2512
+ DataTableToolbar,
2513
+ {
2514
+ table,
2515
+ totalRecords: totalRecords ?? mergedData.length,
2516
+ isFetching,
2517
+ onRefresh: f.refresh ? onRefresh : void 0,
2518
+ onAddRow: f.addRow ? handleAddRow : void 0,
2519
+ onBulkDelete,
2520
+ exportFileName,
2521
+ globalFilter,
2522
+ onGlobalFilterChange: (v) => {
2523
+ setGlobalFilter(v);
2524
+ table.setPageIndex(0);
2525
+ },
2526
+ toolbarSlot,
2527
+ features: f,
2528
+ labels
2529
+ }
2530
+ ),
2531
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "relative w-full overflow-auto", children: [
2532
+ /* @__PURE__ */ jsxRuntime.jsxs(
2533
+ "table",
2534
+ {
2535
+ className: "w-full caption-bottom text-sm",
2536
+ style: {
2537
+ width: table.getCenterTotalSize() ? void 0 : "100%",
2538
+ minWidth: "100%"
2539
+ },
2540
+ children: [
2541
+ /* @__PURE__ */ jsxRuntime.jsx("thead", { className: "sticky top-0 z-10 bg-card/95 backdrop-blur supports-[backdrop-filter]:bg-card/70", children: table.getHeaderGroups().map((hg) => /* @__PURE__ */ jsxRuntime.jsx("tr", { className: "border-b", children: hg.headers.map((header) => {
2542
+ const meta = header.column.columnDef.meta;
2543
+ const isUtility = header.column.id === "__select" || header.column.id === "__expand" || header.column.id === "__actions";
2544
+ const isPinned = !!header.column.getIsPinned();
2545
+ return /* @__PURE__ */ jsxRuntime.jsxs(
2546
+ "th",
2547
+ {
2548
+ colSpan: header.colSpan,
2549
+ style: {
2550
+ width: header.getSize(),
2551
+ ...getPinningStyles(header.column),
2552
+ ...isPinned ? { zIndex: 2 } : {}
2553
+ },
2554
+ className: cn(
2555
+ "group/th relative h-9 px-2 text-left align-middle text-xs font-medium text-muted-foreground",
2556
+ isPinned && "bg-card",
2557
+ meta?.headerClassName
2558
+ ),
2559
+ children: [
2560
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1", children: [
2561
+ !isUtility && f.reordering && /* @__PURE__ */ jsxRuntime.jsx(
2562
+ "span",
2563
+ {
2564
+ draggable: true,
2565
+ onDragStart: (e) => onHeaderDragStart(header.column.id, e),
2566
+ onDragOver: onHeaderDragOver,
2567
+ onDrop: () => onHeaderDrop(header.column.id),
2568
+ className: "-ml-1 cursor-grab rounded p-0.5 text-muted-foreground/40 opacity-0 transition group-hover/th:opacity-100 hover:bg-muted hover:text-foreground active:cursor-grabbing",
2569
+ title: "Drag to reorder",
2570
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.GripVerticalIcon, { className: "size-3" })
2571
+ }
2572
+ ),
2573
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "min-w-0 flex-1", children: header.isPlaceholder ? null : reactTable.flexRender(header.column.columnDef.header, header.getContext()) })
2574
+ ] }),
2575
+ header.column.getCanResize() && /* @__PURE__ */ jsxRuntime.jsx(
2576
+ "span",
2577
+ {
2578
+ onMouseDown: header.getResizeHandler(),
2579
+ onTouchStart: header.getResizeHandler(),
2580
+ onDoubleClick: () => header.column.resetSize(),
2581
+ className: cn(
2582
+ "absolute top-0 right-0 h-full w-1 cursor-col-resize touch-none select-none bg-transparent transition-colors hover:bg-primary/40",
2583
+ header.column.getIsResizing() && "bg-primary"
2584
+ )
2585
+ }
2586
+ )
2587
+ ]
2588
+ },
2589
+ header.id
2590
+ );
2591
+ }) }, hg.id)) }),
2592
+ /* @__PURE__ */ jsxRuntime.jsx("tbody", { children: isLoading ? renderSkeleton(visibleLeafCount, pagination.pageSize) : rows.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("tr", { children: /* @__PURE__ */ jsxRuntime.jsx(
2593
+ "td",
2594
+ {
2595
+ colSpan: visibleLeafCount,
2596
+ className: "h-32 text-center text-sm text-muted-foreground",
2597
+ children: globalFilter || columnFilters.length ? labels.noResults : labels.noData
2598
+ }
2599
+ ) }) : rows.map((row) => /* @__PURE__ */ jsxRuntime.jsx(
2600
+ DataTableRow,
2601
+ {
2602
+ row,
2603
+ renderSubRow,
2604
+ isRowEditing: editingRowId === row.id
2605
+ },
2606
+ row.id
2607
+ )) })
2608
+ ]
2609
+ }
2610
+ ),
2611
+ isFetching && !isLoading && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "pointer-events-none absolute top-1.5 right-2 flex items-center gap-1.5 rounded-md bg-background/80 px-2 py-1 text-xs text-muted-foreground shadow-sm backdrop-blur", children: [
2612
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2Icon, { className: "size-3 animate-spin" }),
2613
+ " ",
2614
+ labels.refreshing
2615
+ ] })
2616
+ ] }),
2617
+ f.pagination && /* @__PURE__ */ jsxRuntime.jsx(DataTablePagination, { table, pageSizeOptions })
2618
+ ]
2619
+ }
2620
+ )
2621
+ }
2622
+ );
2623
+ }
2624
+ function DataTableRow({
2625
+ row,
2626
+ renderSubRow,
2627
+ isRowEditing
2628
+ }) {
2629
+ const expanded = row.getIsExpanded();
2630
+ const isSelected = row.getIsSelected();
2631
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
2632
+ /* @__PURE__ */ jsxRuntime.jsx(
2633
+ "tr",
2634
+ {
2635
+ "data-state": isSelected ? "selected" : void 0,
2636
+ className: cn(
2637
+ "group/row border-b transition-colors hover:bg-muted/40 data-[state=selected]:bg-primary/5",
2638
+ isRowEditing && "bg-amber-500/5",
2639
+ row.depth > 0 && "bg-muted/20"
2640
+ ),
2641
+ children: row.getVisibleCells().map((cell) => {
2642
+ const meta = cell.column.columnDef.meta;
2643
+ const isPinned = !!cell.column.getIsPinned();
2644
+ return /* @__PURE__ */ jsxRuntime.jsx(
2645
+ "td",
2646
+ {
2647
+ style: {
2648
+ width: cell.column.getSize(),
2649
+ ...getPinningStyles(cell.column)
2650
+ },
2651
+ className: cn(
2652
+ "px-2 py-1 align-middle text-sm",
2653
+ isPinned && "bg-card group-hover/row:bg-[color-mix(in_oklab,var(--card),var(--muted)_40%)] group-data-[state=selected]/row:bg-[color-mix(in_oklab,var(--card),var(--primary)_5%)]",
2654
+ isPinned && isRowEditing && "bg-[color-mix(in_oklab,var(--card),#f59e0b_5%)]",
2655
+ meta?.cellClassName
2656
+ ),
2657
+ children: reactTable.flexRender(cell.column.columnDef.cell, cell.getContext())
2658
+ },
2659
+ cell.id
2660
+ );
2661
+ })
2662
+ }
2663
+ ),
2664
+ expanded && renderSubRow && /* @__PURE__ */ jsxRuntime.jsx("tr", { className: "border-b bg-muted/30", children: /* @__PURE__ */ jsxRuntime.jsx("td", { colSpan: row.getVisibleCells().length, className: "p-3", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-md border bg-background p-3 shadow-inner", children: renderSubRow(row.original) }) }) })
2665
+ ] });
2666
+ }
2667
+ function renderSkeleton(cols, rows) {
2668
+ return Array.from({ length: rows }).map((_, r) => /* @__PURE__ */ jsxRuntime.jsx("tr", { className: "border-b", children: Array.from({ length: cols }).map((__, c) => /* @__PURE__ */ jsxRuntime.jsx("td", { className: "px-2 py-2", children: /* @__PURE__ */ jsxRuntime.jsx(Skeleton, { className: "h-4 w-full" }) }, c)) }, r));
2669
+ }
2670
+
2671
+ exports.ColumnFilterPanel = ColumnFilterPanel;
2672
+ exports.DataTable = DataTable;
2673
+ exports.DataTableColumnHeader = DataTableColumnHeader;
2674
+ exports.DataTablePagination = DataTablePagination;
2675
+ exports.DataTableRowActions = DataTableRowActions;
2676
+ exports.DataTableToolbar = DataTableToolbar;
2677
+ exports.EditableCell = EditableCell;
2678
+ exports.booleanFilterFn = booleanFilterFn;
2679
+ exports.dateFilterFn = dateFilterFn;
2680
+ exports.exportToCsv = exportToCsv;
2681
+ exports.exportToExcel = exportToExcel;
2682
+ exports.isFilterActive = isFilterActive;
2683
+ exports.numberFilterFn = numberFilterFn;
2684
+ exports.setFilterFn = setFilterFn;
2685
+ exports.textFilterFn = textFilterFn;
2686
+ exports.themePresets = themePresets;
2687
+ exports.themeToStyle = themeToStyle;
2688
+ //# sourceMappingURL=index.cjs.map
2689
+ //# sourceMappingURL=index.cjs.map