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