shadcn-datagrid 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs ADDED
@@ -0,0 +1,1599 @@
1
+ 'use strict';
2
+
3
+ var react = require('react');
4
+ var lucideReact = require('lucide-react');
5
+ var radixUi = require('radix-ui');
6
+ var clsx = require('clsx');
7
+ var tailwindMerge = require('tailwind-merge');
8
+ var jsxRuntime = require('react/jsx-runtime');
9
+ var TooltipPrimitive = require('@radix-ui/react-tooltip');
10
+ var reactSlot = require('@radix-ui/react-slot');
11
+ var classVarianceAuthority = require('class-variance-authority');
12
+ var DropdownMenuPrimitive = require('@radix-ui/react-dropdown-menu');
13
+ var dateFns = require('date-fns');
14
+ var server = require('react-dom/server');
15
+
16
+ function _interopNamespace(e) {
17
+ if (e && e.__esModule) return e;
18
+ var n = Object.create(null);
19
+ if (e) {
20
+ Object.keys(e).forEach(function (k) {
21
+ if (k !== 'default') {
22
+ var d = Object.getOwnPropertyDescriptor(e, k);
23
+ Object.defineProperty(n, k, d.get ? d : {
24
+ enumerable: true,
25
+ get: function () { return e[k]; }
26
+ });
27
+ }
28
+ });
29
+ }
30
+ n.default = e;
31
+ return Object.freeze(n);
32
+ }
33
+
34
+ var TooltipPrimitive__namespace = /*#__PURE__*/_interopNamespace(TooltipPrimitive);
35
+ var DropdownMenuPrimitive__namespace = /*#__PURE__*/_interopNamespace(DropdownMenuPrimitive);
36
+
37
+ // src/components/DataGrid/index.tsx
38
+ function cn(...inputs) {
39
+ return tailwindMerge.twMerge(clsx.clsx(inputs));
40
+ }
41
+ function Checkbox({
42
+ className,
43
+ ...props
44
+ }) {
45
+ return /* @__PURE__ */ jsxRuntime.jsx(
46
+ radixUi.Checkbox.Root,
47
+ {
48
+ "data-slot": "checkbox",
49
+ className: cn(
50
+ "peer border-input dark:bg-input/30 data-[state=checked]:bg-primary data-[state=checked]:text-primary-foreground dark:data-[state=checked]:bg-primary data-[state=checked]:border-primary focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive size-4 shrink-0 rounded-[4px] border shadow-xs transition-shadow outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50",
51
+ className
52
+ ),
53
+ ...props,
54
+ children: /* @__PURE__ */ jsxRuntime.jsx(
55
+ radixUi.Checkbox.Indicator,
56
+ {
57
+ "data-slot": "checkbox-indicator",
58
+ className: "grid place-content-center text-current transition-none",
59
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckIcon, { className: "size-3.5" })
60
+ }
61
+ )
62
+ }
63
+ );
64
+ }
65
+ function Table({ className, ...props }) {
66
+ return /* @__PURE__ */ jsxRuntime.jsx("div", { "data-slot": "table-container", className: "relative w-full overflow-x-auto", children: /* @__PURE__ */ jsxRuntime.jsx("table", { "data-slot": "table", className: cn("w-full caption-bottom text-sm", className), ...props }) });
67
+ }
68
+ function TableHeader({ className, ...props }) {
69
+ return /* @__PURE__ */ jsxRuntime.jsx("thead", { "data-slot": "table-header", className: cn("[&_tr]:border-b", className), ...props });
70
+ }
71
+ function TableBody({ className, ...props }) {
72
+ return /* @__PURE__ */ jsxRuntime.jsx(
73
+ "tbody",
74
+ {
75
+ "data-slot": "table-body",
76
+ className: cn("[&_tr:last-child]:border-0", className),
77
+ ...props
78
+ }
79
+ );
80
+ }
81
+ function TableRow({ className, ...props }) {
82
+ return /* @__PURE__ */ jsxRuntime.jsx(
83
+ "tr",
84
+ {
85
+ "data-slot": "table-row",
86
+ className: cn(
87
+ "hover:bg-muted/50 data-[state=selected]:bg-muted border-b transition-colors",
88
+ className
89
+ ),
90
+ ...props
91
+ }
92
+ );
93
+ }
94
+ function TableHead({ className, ...props }) {
95
+ return /* @__PURE__ */ jsxRuntime.jsx(
96
+ "th",
97
+ {
98
+ "data-slot": "table-head",
99
+ className: cn(
100
+ "text-foreground h-10 px-2 text-left align-middle font-medium whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
101
+ className
102
+ ),
103
+ ...props
104
+ }
105
+ );
106
+ }
107
+ function TableCell({ className, ...props }) {
108
+ return /* @__PURE__ */ jsxRuntime.jsx(
109
+ "td",
110
+ {
111
+ "data-slot": "table-cell",
112
+ className: cn(
113
+ "p-2 align-middle whitespace-nowrap [&:has([role=checkbox])]:pr-0 [&>[role=checkbox]]:translate-y-[2px]",
114
+ className
115
+ ),
116
+ ...props
117
+ }
118
+ );
119
+ }
120
+ function TooltipProvider({
121
+ delayDuration = 0,
122
+ ...props
123
+ }) {
124
+ return /* @__PURE__ */ jsxRuntime.jsx(
125
+ TooltipPrimitive__namespace.Provider,
126
+ {
127
+ "data-slot": "tooltip-provider",
128
+ delayDuration,
129
+ ...props
130
+ }
131
+ );
132
+ }
133
+ function Tooltip({ ...props }) {
134
+ return /* @__PURE__ */ jsxRuntime.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntime.jsx(TooltipPrimitive__namespace.Root, { "data-slot": "tooltip", ...props }) });
135
+ }
136
+ function TooltipTrigger({ ...props }) {
137
+ return /* @__PURE__ */ jsxRuntime.jsx(TooltipPrimitive__namespace.Trigger, { "data-slot": "tooltip-trigger", ...props });
138
+ }
139
+ function TooltipContent({
140
+ className,
141
+ sideOffset = 0,
142
+ children,
143
+ ...props
144
+ }) {
145
+ return /* @__PURE__ */ jsxRuntime.jsx(TooltipPrimitive__namespace.Portal, { children: /* @__PURE__ */ jsxRuntime.jsxs(
146
+ TooltipPrimitive__namespace.Content,
147
+ {
148
+ "data-slot": "tooltip-content",
149
+ sideOffset,
150
+ className: cn(
151
+ "bg-foreground text-background animate-in fade-in-0 zoom-in-95 data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=closed]:zoom-out-95 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 z-50 w-fit origin-(--radix-tooltip-content-transform-origin) rounded-md px-3 py-1.5 text-xs text-balance",
152
+ className
153
+ ),
154
+ ...props,
155
+ children: [
156
+ children,
157
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipPrimitive__namespace.Arrow, { className: "bg-foreground fill-foreground z-50 size-2.5 translate-y-[calc(-50%_-_2px)] rotate-45 rounded-[2px]" })
158
+ ]
159
+ }
160
+ ) });
161
+ }
162
+ var buttonVariants = classVarianceAuthority.cva(
163
+ "inline-flex items-center justify-center gap-2 whitespace-nowrap rounded-md text-sm font-medium transition-all disabled:pointer-events-none disabled:opacity-50 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4 shrink-0 [&_svg]:shrink-0 outline-none focus-visible:border-ring focus-visible:ring-ring/50 focus-visible:ring-[3px] aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive",
164
+ {
165
+ variants: {
166
+ variant: {
167
+ default: "bg-primary text-primary-foreground hover:bg-primary/90",
168
+ destructive: "bg-destructive text-white hover:bg-destructive/90 focus-visible:ring-destructive/20 dark:focus-visible:ring-destructive/40 dark:bg-destructive/60",
169
+ outline: "border bg-background shadow-xs hover:bg-accent hover:text-accent-foreground dark:bg-input/30 dark:border-input dark:hover:bg-input/50",
170
+ secondary: "bg-secondary text-secondary-foreground hover:bg-secondary/80",
171
+ ghost: "hover:bg-accent hover:text-accent-foreground dark:hover:bg-accent/50",
172
+ link: "text-primary underline-offset-4 hover:underline"
173
+ },
174
+ size: {
175
+ default: "h-9 px-4 py-2 has-[>svg]:px-3",
176
+ xs: "h-6 gap-1 rounded-md px-2 text-xs has-[>svg]:px-1.5 [&_svg:not([class*='size-'])]:size-3",
177
+ sm: "h-8 rounded-md gap-1.5 px-3 has-[>svg]:px-2.5",
178
+ lg: "h-10 rounded-md px-6 has-[>svg]:px-4",
179
+ icon: "size-9",
180
+ "icon-xs": "size-6 rounded-md [&_svg:not([class*='size-'])]:size-3",
181
+ "icon-sm": "size-8",
182
+ "icon-lg": "size-10"
183
+ }
184
+ },
185
+ defaultVariants: {
186
+ variant: "default",
187
+ size: "default"
188
+ }
189
+ }
190
+ );
191
+ function Button({
192
+ className,
193
+ variant = "default",
194
+ size = "default",
195
+ asChild = false,
196
+ ...props
197
+ }) {
198
+ const Comp = asChild ? reactSlot.Slot : "button";
199
+ return /* @__PURE__ */ jsxRuntime.jsx(
200
+ Comp,
201
+ {
202
+ "data-slot": "button",
203
+ "data-variant": variant,
204
+ "data-size": size,
205
+ className: cn(buttonVariants({ variant, size, className })),
206
+ ...props
207
+ }
208
+ );
209
+ }
210
+ function DropdownMenu({ ...props }) {
211
+ return /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuPrimitive__namespace.Root, { "data-slot": "dropdown-menu", ...props });
212
+ }
213
+ function DropdownMenuTrigger({
214
+ ...props
215
+ }) {
216
+ return /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuPrimitive__namespace.Trigger, { "data-slot": "dropdown-menu-trigger", ...props });
217
+ }
218
+ function DropdownMenuContent({
219
+ className,
220
+ sideOffset = 4,
221
+ ...props
222
+ }) {
223
+ return /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuPrimitive__namespace.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(
224
+ DropdownMenuPrimitive__namespace.Content,
225
+ {
226
+ "data-slot": "dropdown-menu-content",
227
+ sideOffset,
228
+ className: cn(
229
+ "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 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 z-50 max-h-(--radix-dropdown-menu-content-available-height) min-w-[8rem] origin-(--radix-dropdown-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
230
+ className
231
+ ),
232
+ ...props
233
+ }
234
+ ) });
235
+ }
236
+ function DropdownMenuItem({
237
+ className,
238
+ inset,
239
+ variant = "default",
240
+ ...props
241
+ }) {
242
+ return /* @__PURE__ */ jsxRuntime.jsx(
243
+ DropdownMenuPrimitive__namespace.Item,
244
+ {
245
+ "data-slot": "dropdown-menu-item",
246
+ "data-inset": inset,
247
+ "data-variant": variant,
248
+ className: cn(
249
+ "focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
250
+ className
251
+ ),
252
+ ...props
253
+ }
254
+ );
255
+ }
256
+ function DropdownMenuSeparator({
257
+ className,
258
+ ...props
259
+ }) {
260
+ return /* @__PURE__ */ jsxRuntime.jsx(
261
+ DropdownMenuPrimitive__namespace.Separator,
262
+ {
263
+ "data-slot": "dropdown-menu-separator",
264
+ className: cn("bg-border -mx-1 my-1 h-px", className),
265
+ ...props
266
+ }
267
+ );
268
+ }
269
+ function ActionIconButton({ action, data }) {
270
+ const isDisabled = action.disabled?.(data) ?? false;
271
+ return /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
272
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(
273
+ Button,
274
+ {
275
+ variant: "ghost",
276
+ size: "icon",
277
+ disabled: isDisabled,
278
+ className: `dg-action-btn h-7 w-7 ${action.variant === "destructive" ? "hover:text-destructive" : ""}`,
279
+ onClick: (e) => {
280
+ e.stopPropagation();
281
+ action.onClick(data);
282
+ },
283
+ children: action.icon
284
+ }
285
+ ) }),
286
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { children: /* @__PURE__ */ jsxRuntime.jsx("p", { children: action.label }) })
287
+ ] });
288
+ }
289
+ function DataGridActions({ config, data, isHovered = true }) {
290
+ const { mode, showOnHover, actions } = config;
291
+ const visibleActions = actions.filter((action) => !action.hidden || !action.hidden(data));
292
+ if (visibleActions.length === 0) return null;
293
+ if (mode === "dropdown") {
294
+ const shouldShow = !showOnHover || isHovered;
295
+ return /* @__PURE__ */ jsxRuntime.jsx(
296
+ "div",
297
+ {
298
+ className: "dg-actions dg-actions--dropdown flex justify-end",
299
+ style: { opacity: shouldShow ? 1 : 0, transition: "opacity 0.15s" },
300
+ children: /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenu, { children: [
301
+ /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(Button, { variant: "ghost", size: "icon", className: "dg-actions__trigger h-8 w-8 p-0", children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.MoreHorizontal, { className: "h-4 w-4" }) }) }),
302
+ /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuContent, { align: "end", className: "dg-actions__content", children: visibleActions.map((action, index) => {
303
+ const isDisabled = action.disabled?.(data) ?? false;
304
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { children: [
305
+ index > 0 && visibleActions[index - 1]?.variant !== action.variant && action.variant === "destructive" && /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuSeparator, {}),
306
+ /* @__PURE__ */ jsxRuntime.jsxs(
307
+ DropdownMenuItem,
308
+ {
309
+ disabled: isDisabled,
310
+ onClick: () => action.onClick(data),
311
+ className: action.variant === "destructive" ? "text-destructive focus:text-destructive" : "",
312
+ children: [
313
+ action.icon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mr-2", children: action.icon }),
314
+ action.label
315
+ ]
316
+ }
317
+ )
318
+ ] }, action.label);
319
+ }) })
320
+ ] })
321
+ }
322
+ );
323
+ }
324
+ return /* @__PURE__ */ jsxRuntime.jsx(TooltipProvider, { children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "dg-actions dg-actions--icons flex justify-end gap-1", children: visibleActions.map((action) => /* @__PURE__ */ jsxRuntime.jsx(ActionIconButton, { action, data }, action.label)) }) });
325
+ }
326
+ function formatCurrency(value, locale = "en-US", currency = "USD") {
327
+ const num = typeof value === "number" ? value : parseFloat(String(value));
328
+ if (Number.isNaN(num)) return "-";
329
+ return new Intl.NumberFormat(locale, {
330
+ style: "currency",
331
+ currency,
332
+ minimumFractionDigits: 2,
333
+ maximumFractionDigits: 2
334
+ }).format(num);
335
+ }
336
+ function formatNumber(value, locale = "en-US") {
337
+ const num = typeof value === "number" ? value : parseFloat(String(value));
338
+ if (Number.isNaN(num)) return "-";
339
+ return new Intl.NumberFormat(locale, {
340
+ minimumFractionDigits: 2,
341
+ maximumFractionDigits: 2
342
+ }).format(num);
343
+ }
344
+ function formatDateValue(value, dateFormat = "dd MMM yyyy") {
345
+ if (!value) return "-";
346
+ try {
347
+ const date = typeof value === "string" ? dateFns.parseISO(value) : value;
348
+ return dateFns.format(date, dateFormat);
349
+ } catch {
350
+ return String(value);
351
+ }
352
+ }
353
+ function formatValue(value, formatType, type) {
354
+ if (value === null || value === void 0) return "-";
355
+ if (type === "date" || formatType?.includes("MMM") || formatType?.includes("yyyy")) {
356
+ return formatDateValue(value, formatType || "dd MMM yyyy");
357
+ }
358
+ if (formatType === "C2") return formatCurrency(value);
359
+ if (formatType === "N2") return formatNumber(value);
360
+ if (type === "boolean") return value ? "Yes" : "No";
361
+ return String(value);
362
+ }
363
+ function asyncReducer(_state, action) {
364
+ switch (action.type) {
365
+ case "FETCH_START":
366
+ return { data: null, isLoading: true, error: null };
367
+ case "FETCH_SUCCESS":
368
+ return { data: action.data, isLoading: false, error: null };
369
+ case "FETCH_ERROR":
370
+ return { data: null, isLoading: false, error: action.error };
371
+ }
372
+ }
373
+ function DataGridChildRow({
374
+ parentRow,
375
+ config,
376
+ parentColSpan,
377
+ className
378
+ }) {
379
+ const [asyncState, dispatch] = react.useReducer(asyncReducer, {
380
+ data: null,
381
+ isLoading: false,
382
+ error: null
383
+ });
384
+ const { childColumns, getChildData, fetchChildData, childKeyField = "id" } = config;
385
+ const inlineData = getChildData?.(parentRow);
386
+ react.useEffect(() => {
387
+ if (!inlineData && fetchChildData) {
388
+ dispatch({ type: "FETCH_START" });
389
+ fetchChildData(parentRow).then((result) => dispatch({ type: "FETCH_SUCCESS", data: result })).catch(() => dispatch({ type: "FETCH_ERROR", error: "Failed to load child data" }));
390
+ }
391
+ }, [parentRow, fetchChildData, inlineData]);
392
+ const childData = inlineData ?? asyncState.data;
393
+ const columns = childColumns ?? [];
394
+ if (asyncState.isLoading) {
395
+ return /* @__PURE__ */ jsxRuntime.jsx(TableRow, { className: cn("dg-child-row dg-child-row--loading hover:bg-transparent", className), children: /* @__PURE__ */ jsxRuntime.jsx(TableCell, { colSpan: parentColSpan, children: /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-2 py-4 px-8 text-muted-foreground", children: [
396
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Loader2, { className: "h-4 w-4 animate-spin" }),
397
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm", children: "Loading..." })
398
+ ] }) }) });
399
+ }
400
+ if (asyncState.error) {
401
+ return /* @__PURE__ */ jsxRuntime.jsx(TableRow, { className: cn("dg-child-row dg-child-row--error hover:bg-transparent", className), children: /* @__PURE__ */ jsxRuntime.jsx(TableCell, { colSpan: parentColSpan, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-4 px-8 text-sm text-destructive", children: asyncState.error }) }) });
402
+ }
403
+ if (!childData || childData.length === 0) {
404
+ return /* @__PURE__ */ jsxRuntime.jsx(TableRow, { className: cn("dg-child-row dg-child-row--empty hover:bg-transparent", className), children: /* @__PURE__ */ jsxRuntime.jsx(TableCell, { colSpan: parentColSpan, children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "py-4 px-8 text-sm text-muted-foreground italic", children: "No child records" }) }) });
405
+ }
406
+ return /* @__PURE__ */ jsxRuntime.jsx(TableRow, { className: cn("dg-child-row hover:bg-transparent", className), children: /* @__PURE__ */ jsxRuntime.jsx(TableCell, { colSpan: parentColSpan, className: "p-0", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "dg-child-row__container ml-10 mr-4 my-2", children: /* @__PURE__ */ jsxRuntime.jsx("div", { className: "rounded-md border border-border/50 overflow-hidden shadow-[0_1px_3px_rgba(0,0,0,0.04)] bg-muted/30", children: /* @__PURE__ */ jsxRuntime.jsxs(Table, { className: "dg-child-row__table", children: [
407
+ /* @__PURE__ */ jsxRuntime.jsx(TableHeader, { children: /* @__PURE__ */ jsxRuntime.jsx(TableRow, { className: "dg-child-row__header-row bg-muted/80 hover:bg-muted/80 border-b border-border", children: columns.map((col) => /* @__PURE__ */ jsxRuntime.jsx(
408
+ TableHead,
409
+ {
410
+ className: "dg-child-row__header-cell text-[11px] font-bold text-muted-foreground/90 uppercase tracking-wider h-8",
411
+ style: {
412
+ width: typeof col.width === "number" ? `${col.width}px` : col.width,
413
+ minWidth: col.minWidth,
414
+ maxWidth: col.maxWidth
415
+ },
416
+ children: col.headerText
417
+ },
418
+ String(col.field)
419
+ )) }) }),
420
+ /* @__PURE__ */ jsxRuntime.jsx(TableBody, { children: childData.map(
421
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
422
+ (childRow, childIndex) => /* @__PURE__ */ jsxRuntime.jsx(
423
+ TableRow,
424
+ {
425
+ className: cn("dg-child-row__row", childIndex % 2 === 1 && "bg-muted/20"),
426
+ children: columns.map((col) => {
427
+ const value = childRow[col.field];
428
+ return /* @__PURE__ */ jsxRuntime.jsx(
429
+ TableCell,
430
+ {
431
+ className: "dg-child-row__cell text-xs py-2",
432
+ style: {
433
+ width: typeof col.width === "number" ? `${col.width}px` : col.width,
434
+ minWidth: col.minWidth,
435
+ maxWidth: col.maxWidth
436
+ },
437
+ children: col.template ? col.template(childRow, childIndex) : formatValue(value, col.format, col.type)
438
+ },
439
+ String(col.field)
440
+ );
441
+ })
442
+ },
443
+ childRow[childKeyField] != null ? String(childRow[childKeyField]) : childIndex
444
+ )
445
+ ) })
446
+ ] }) }) }) }) });
447
+ }
448
+ function DataGridExpander({ isExpanded, onToggle, className }) {
449
+ const handleClick = react.useCallback(
450
+ (e) => {
451
+ e.stopPropagation();
452
+ onToggle();
453
+ },
454
+ [onToggle]
455
+ );
456
+ return /* @__PURE__ */ jsxRuntime.jsx(
457
+ "button",
458
+ {
459
+ type: "button",
460
+ className: cn(
461
+ "dg-expander inline-flex items-center justify-center h-6 w-6 rounded-sm hover:bg-muted transition-transform duration-200",
462
+ className
463
+ ),
464
+ onClick: handleClick,
465
+ "aria-label": isExpanded ? "Collapse row" : "Expand row",
466
+ children: /* @__PURE__ */ jsxRuntime.jsx(
467
+ lucideReact.ChevronRight,
468
+ {
469
+ className: cn("h-4 w-4 transition-transform duration-200", isExpanded && "rotate-90")
470
+ }
471
+ )
472
+ }
473
+ );
474
+ }
475
+ function ContextMenu({ ...props }) {
476
+ return /* @__PURE__ */ jsxRuntime.jsx(radixUi.ContextMenu.Root, { "data-slot": "context-menu", ...props });
477
+ }
478
+ function ContextMenuTrigger({
479
+ ...props
480
+ }) {
481
+ return /* @__PURE__ */ jsxRuntime.jsx(radixUi.ContextMenu.Trigger, { "data-slot": "context-menu-trigger", ...props });
482
+ }
483
+ function ContextMenuContent({
484
+ className,
485
+ ...props
486
+ }) {
487
+ return /* @__PURE__ */ jsxRuntime.jsx(radixUi.ContextMenu.Portal, { children: /* @__PURE__ */ jsxRuntime.jsx(
488
+ radixUi.ContextMenu.Content,
489
+ {
490
+ "data-slot": "context-menu-content",
491
+ className: cn(
492
+ "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 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 z-50 max-h-(--radix-context-menu-content-available-height) min-w-[8rem] origin-(--radix-context-menu-content-transform-origin) overflow-x-hidden overflow-y-auto rounded-md border p-1 shadow-md",
493
+ className
494
+ ),
495
+ ...props
496
+ }
497
+ ) });
498
+ }
499
+ function ContextMenuItem({
500
+ className,
501
+ inset,
502
+ variant = "default",
503
+ ...props
504
+ }) {
505
+ return /* @__PURE__ */ jsxRuntime.jsx(
506
+ radixUi.ContextMenu.Item,
507
+ {
508
+ "data-slot": "context-menu-item",
509
+ "data-inset": inset,
510
+ "data-variant": variant,
511
+ className: cn(
512
+ "focus:bg-accent focus:text-accent-foreground data-[variant=destructive]:text-destructive data-[variant=destructive]:focus:bg-destructive/10 dark:data-[variant=destructive]:focus:bg-destructive/20 data-[variant=destructive]:focus:text-destructive data-[variant=destructive]:*:[svg]:!text-destructive [&_svg:not([class*='text-'])]:text-muted-foreground relative flex cursor-default items-center gap-2 rounded-sm px-2 py-1.5 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 data-[inset]:pl-8 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
513
+ className
514
+ ),
515
+ ...props
516
+ }
517
+ );
518
+ }
519
+ function ContextMenuLabel({
520
+ className,
521
+ inset,
522
+ ...props
523
+ }) {
524
+ return /* @__PURE__ */ jsxRuntime.jsx(
525
+ radixUi.ContextMenu.Label,
526
+ {
527
+ "data-slot": "context-menu-label",
528
+ "data-inset": inset,
529
+ className: cn("text-foreground px-2 py-1.5 text-sm font-medium data-[inset]:pl-8", className),
530
+ ...props
531
+ }
532
+ );
533
+ }
534
+ function ContextMenuSeparator({
535
+ className,
536
+ ...props
537
+ }) {
538
+ return /* @__PURE__ */ jsxRuntime.jsx(
539
+ radixUi.ContextMenu.Separator,
540
+ {
541
+ "data-slot": "context-menu-separator",
542
+ className: cn("bg-border -mx-1 my-1 h-px", className),
543
+ ...props
544
+ }
545
+ );
546
+ }
547
+ function DataGridContextMenu({
548
+ items,
549
+ data,
550
+ children,
551
+ className
552
+ }) {
553
+ if (items.length === 0) return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children });
554
+ const processedItems = items.map((item, index) => {
555
+ if (item.type === "separator" || item.type === "label") {
556
+ return { item, index, isVisible: true };
557
+ }
558
+ const isHidden = item.hidden?.(data) ?? false;
559
+ return { item, index, isVisible: !isHidden };
560
+ });
561
+ const visibleItems = processedItems.filter((processedItem, idx) => {
562
+ const { item, isVisible } = processedItem;
563
+ if (item.type !== "separator") return isVisible;
564
+ const hasVisibleItemsAfter = processedItems.slice(idx + 1).some((pi) => pi.item.type !== "separator" && pi.isVisible);
565
+ const hasVisibleItemsBefore = processedItems.slice(0, idx).some((pi) => pi.item.type !== "separator" && pi.isVisible);
566
+ return hasVisibleItemsAfter && hasVisibleItemsBefore;
567
+ });
568
+ return /* @__PURE__ */ jsxRuntime.jsxs(ContextMenu, { children: [
569
+ /* @__PURE__ */ jsxRuntime.jsx(ContextMenuTrigger, { asChild: true, children }),
570
+ /* @__PURE__ */ jsxRuntime.jsx(ContextMenuContent, { className: cn("dg-context-menu w-56", className), children: visibleItems.map(({ item, index }) => {
571
+ if (item.type === "separator") {
572
+ return /* @__PURE__ */ jsxRuntime.jsx(
573
+ ContextMenuSeparator,
574
+ {
575
+ className: "dg-context-menu__separator"
576
+ },
577
+ `sep-${String(index)}`
578
+ );
579
+ }
580
+ if (item.type === "label") {
581
+ return /* @__PURE__ */ jsxRuntime.jsx(ContextMenuLabel, { className: "dg-context-menu__label", children: item.label }, `label-${String(index)}`);
582
+ }
583
+ const isDisabled = item.disabled?.(data) ?? false;
584
+ return /* @__PURE__ */ jsxRuntime.jsxs(
585
+ ContextMenuItem,
586
+ {
587
+ disabled: isDisabled,
588
+ onClick: () => item.onClick(data),
589
+ className: cn(
590
+ "dg-context-menu__item",
591
+ item.variant === "destructive" && "text-destructive focus:text-destructive"
592
+ ),
593
+ children: [
594
+ item.icon && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "dg-context-menu__item-icon mr-2", children: item.icon }),
595
+ item.label
596
+ ]
597
+ },
598
+ `item-${item.label}`
599
+ );
600
+ }) })
601
+ ] });
602
+ }
603
+ function Select({ ...props }) {
604
+ return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Select.Root, { "data-slot": "select", ...props });
605
+ }
606
+ function SelectValue({ ...props }) {
607
+ return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Select.Value, { "data-slot": "select-value", ...props });
608
+ }
609
+ function SelectTrigger({
610
+ className,
611
+ size = "default",
612
+ children,
613
+ ...props
614
+ }) {
615
+ return /* @__PURE__ */ jsxRuntime.jsxs(
616
+ radixUi.Select.Trigger,
617
+ {
618
+ "data-slot": "select-trigger",
619
+ "data-size": size,
620
+ className: cn(
621
+ "border-input data-[placeholder]:text-muted-foreground [&_svg:not([class*='text-'])]:text-muted-foreground focus-visible:border-ring focus-visible:ring-ring/50 aria-invalid:ring-destructive/20 dark:aria-invalid:ring-destructive/40 aria-invalid:border-destructive dark:bg-input/30 dark:hover:bg-input/50 flex w-fit items-center justify-between gap-2 rounded-md border bg-transparent px-3 py-2 text-sm whitespace-nowrap shadow-xs transition-[color,box-shadow] outline-none focus-visible:ring-[3px] disabled:cursor-not-allowed disabled:opacity-50 data-[size=default]:h-9 data-[size=sm]:h-8 *:data-[slot=select-value]:line-clamp-1 *:data-[slot=select-value]:flex *:data-[slot=select-value]:items-center *:data-[slot=select-value]:gap-2 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
622
+ className
623
+ ),
624
+ ...props,
625
+ children: [
626
+ children,
627
+ /* @__PURE__ */ jsxRuntime.jsx(radixUi.Select.Icon, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDownIcon, { className: "size-4 opacity-50" }) })
628
+ ]
629
+ }
630
+ );
631
+ }
632
+ function SelectScrollUpButton({
633
+ className,
634
+ ...props
635
+ }) {
636
+ return /* @__PURE__ */ jsxRuntime.jsx(
637
+ radixUi.Select.ScrollUpButton,
638
+ {
639
+ "data-slot": "select-scroll-up-button",
640
+ className: cn("flex cursor-default items-center justify-center py-1", className),
641
+ ...props,
642
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronUpIcon, { className: "size-4" })
643
+ }
644
+ );
645
+ }
646
+ function SelectScrollDownButton({
647
+ className,
648
+ ...props
649
+ }) {
650
+ return /* @__PURE__ */ jsxRuntime.jsx(
651
+ radixUi.Select.ScrollDownButton,
652
+ {
653
+ "data-slot": "select-scroll-down-button",
654
+ className: cn("flex cursor-default items-center justify-center py-1", className),
655
+ ...props,
656
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDownIcon, { className: "size-4" })
657
+ }
658
+ );
659
+ }
660
+ function SelectContent({
661
+ className,
662
+ children,
663
+ position = "item-aligned",
664
+ align = "center",
665
+ ...props
666
+ }) {
667
+ return /* @__PURE__ */ jsxRuntime.jsx(radixUi.Select.Portal, { children: /* @__PURE__ */ jsxRuntime.jsxs(
668
+ radixUi.Select.Content,
669
+ {
670
+ "data-slot": "select-content",
671
+ className: cn(
672
+ "bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 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 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-md border shadow-md",
673
+ position === "popper" && "data-[side=bottom]:translate-y-1 data-[side=left]:-translate-x-1 data-[side=right]:translate-x-1 data-[side=top]:-translate-y-1",
674
+ className
675
+ ),
676
+ position,
677
+ align,
678
+ ...props,
679
+ children: [
680
+ /* @__PURE__ */ jsxRuntime.jsx(SelectScrollUpButton, {}),
681
+ /* @__PURE__ */ jsxRuntime.jsx(
682
+ radixUi.Select.Viewport,
683
+ {
684
+ className: cn(
685
+ "p-1",
686
+ position === "popper" && "h-[var(--radix-select-trigger-height)] w-full min-w-[var(--radix-select-trigger-width)] scroll-my-1"
687
+ ),
688
+ children
689
+ }
690
+ ),
691
+ /* @__PURE__ */ jsxRuntime.jsx(SelectScrollDownButton, {})
692
+ ]
693
+ }
694
+ ) });
695
+ }
696
+ function SelectItem({
697
+ className,
698
+ children,
699
+ ...props
700
+ }) {
701
+ return /* @__PURE__ */ jsxRuntime.jsxs(
702
+ radixUi.Select.Item,
703
+ {
704
+ "data-slot": "select-item",
705
+ className: cn(
706
+ "focus:bg-accent focus:text-accent-foreground [&_svg:not([class*='text-'])]:text-muted-foreground relative flex w-full cursor-default items-center gap-2 rounded-sm py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 *:[span]:last:flex *:[span]:last:items-center *:[span]:last:gap-2",
707
+ className
708
+ ),
709
+ ...props,
710
+ children: [
711
+ /* @__PURE__ */ jsxRuntime.jsx(
712
+ "span",
713
+ {
714
+ "data-slot": "select-item-indicator",
715
+ className: "absolute right-2 flex size-3.5 items-center justify-center",
716
+ children: /* @__PURE__ */ jsxRuntime.jsx(radixUi.Select.ItemIndicator, { children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckIcon, { className: "size-4" }) })
717
+ }
718
+ ),
719
+ /* @__PURE__ */ jsxRuntime.jsx(radixUi.Select.ItemText, { children })
720
+ ]
721
+ }
722
+ );
723
+ }
724
+ function getLayout(width) {
725
+ if (width >= 600) return "full";
726
+ if (width >= 400) return "compact";
727
+ return "minimal";
728
+ }
729
+ function DataGridPagination({ config, className }) {
730
+ const {
731
+ page,
732
+ pageSize,
733
+ total,
734
+ pageSizeOptions = [5, 10, 15, 20, 25],
735
+ onPageChange,
736
+ onPageSizeChange
737
+ } = config;
738
+ const totalPages = Math.ceil(total / pageSize) || 1;
739
+ const containerRef = react.useRef(null);
740
+ const [layout, setLayout] = react.useState("full");
741
+ const updateLayout = react.useCallback(() => {
742
+ if (containerRef.current) {
743
+ setLayout(getLayout(containerRef.current.offsetWidth));
744
+ }
745
+ }, []);
746
+ react.useEffect(() => {
747
+ updateLayout();
748
+ const observer = new ResizeObserver(updateLayout);
749
+ if (containerRef.current) observer.observe(containerRef.current);
750
+ return () => observer.disconnect();
751
+ }, [updateLayout]);
752
+ return /* @__PURE__ */ jsxRuntime.jsxs(
753
+ "div",
754
+ {
755
+ ref: containerRef,
756
+ className: cn(
757
+ "dg-pagination flex flex-wrap items-center gap-4 mt-4 pb-2 px-2",
758
+ layout === "minimal" ? "justify-center" : "justify-between",
759
+ className
760
+ ),
761
+ children: [
762
+ layout !== "minimal" && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "dg-pagination__page-size flex items-center gap-2", children: [
763
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-sm text-muted-foreground whitespace-nowrap", children: layout === "full" ? "Rows per page:" : "Rows:" }),
764
+ /* @__PURE__ */ jsxRuntime.jsxs(
765
+ Select,
766
+ {
767
+ value: pageSize.toString(),
768
+ onValueChange: (value) => {
769
+ onPageSizeChange(Number(value));
770
+ onPageChange(1);
771
+ },
772
+ children: [
773
+ /* @__PURE__ */ jsxRuntime.jsx(SelectTrigger, { className: "dg-pagination__page-size-trigger w-[80px] h-8", children: /* @__PURE__ */ jsxRuntime.jsx(SelectValue, {}) }),
774
+ /* @__PURE__ */ jsxRuntime.jsx(SelectContent, { children: pageSizeOptions.map((size) => /* @__PURE__ */ jsxRuntime.jsx(SelectItem, { value: size.toString(), children: size }, size)) })
775
+ ]
776
+ }
777
+ )
778
+ ] }),
779
+ layout !== "minimal" && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "dg-pagination__info flex items-center gap-2", children: /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm text-muted-foreground whitespace-nowrap", children: [
780
+ (page - 1) * pageSize + 1,
781
+ "\u2013",
782
+ Math.min(page * pageSize, total),
783
+ " of ",
784
+ total
785
+ ] }) }),
786
+ /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "dg-pagination__nav flex items-center gap-1", children: [
787
+ layout === "full" && /* @__PURE__ */ jsxRuntime.jsx(
788
+ Button,
789
+ {
790
+ variant: "outline",
791
+ size: "icon",
792
+ className: "dg-pagination__btn h-8 w-8",
793
+ onClick: () => onPageChange(1),
794
+ disabled: page === 1,
795
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronsLeft, { className: "h-4 w-4" })
796
+ }
797
+ ),
798
+ /* @__PURE__ */ jsxRuntime.jsx(
799
+ Button,
800
+ {
801
+ variant: "outline",
802
+ size: "icon",
803
+ className: "dg-pagination__btn h-8 w-8",
804
+ onClick: () => onPageChange(Math.max(page - 1, 1)),
805
+ disabled: page === 1,
806
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronLeft, { className: "h-4 w-4" })
807
+ }
808
+ ),
809
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "dg-pagination__page-info text-sm px-2 whitespace-nowrap", children: layout === "full" ? `Page ${String(page)} of ${String(totalPages)}` : `${String(page)}/${String(totalPages)}` }),
810
+ /* @__PURE__ */ jsxRuntime.jsx(
811
+ Button,
812
+ {
813
+ variant: "outline",
814
+ size: "icon",
815
+ className: "dg-pagination__btn h-8 w-8",
816
+ onClick: () => onPageChange(Math.min(page + 1, totalPages)),
817
+ disabled: page === totalPages,
818
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronRight, { className: "h-4 w-4" })
819
+ }
820
+ ),
821
+ layout === "full" && /* @__PURE__ */ jsxRuntime.jsx(
822
+ Button,
823
+ {
824
+ variant: "outline",
825
+ size: "icon",
826
+ className: "dg-pagination__btn h-8 w-8",
827
+ onClick: () => onPageChange(totalPages),
828
+ disabled: page === totalPages,
829
+ children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronsRight, { className: "h-4 w-4" })
830
+ }
831
+ )
832
+ ] })
833
+ ]
834
+ }
835
+ );
836
+ }
837
+ function Skeleton({ className, ...props }) {
838
+ return /* @__PURE__ */ jsxRuntime.jsx(
839
+ "div",
840
+ {
841
+ "data-slot": "skeleton",
842
+ className: cn("bg-accent animate-pulse rounded-md", className),
843
+ ...props
844
+ }
845
+ );
846
+ }
847
+ function DataGridSkeleton({
848
+ columns,
849
+ rowCount = 5,
850
+ showCheckbox = false,
851
+ showActions = false,
852
+ showExpander = false
853
+ }) {
854
+ const visibleColumns = columns.filter((col) => col.visible !== false);
855
+ const skeletonWidths = react.useMemo(
856
+ () => Array.from(
857
+ { length: rowCount },
858
+ (_, rowIdx) => visibleColumns.map((_2, colIdx) => `${((rowIdx + 1) * 37 + (colIdx + 1) * 53) % 40 + 50}%`)
859
+ ),
860
+ [rowCount, visibleColumns]
861
+ );
862
+ return /* @__PURE__ */ jsxRuntime.jsx(jsxRuntime.Fragment, { children: Array.from({ length: rowCount }).map((_, rowIndex) => /* @__PURE__ */ jsxRuntime.jsxs(
863
+ TableRow,
864
+ {
865
+ className: "dg-row dg-row--skeleton hover:bg-transparent",
866
+ children: [
867
+ showExpander && /* @__PURE__ */ jsxRuntime.jsx(TableCell, { className: "dg-cell dg-cell--expander w-[40px]", children: /* @__PURE__ */ jsxRuntime.jsx(Skeleton, { className: "h-4 w-4" }) }),
868
+ showCheckbox && /* @__PURE__ */ jsxRuntime.jsx(TableCell, { className: "dg-cell dg-cell--checkbox w-[40px]", children: /* @__PURE__ */ jsxRuntime.jsx(Skeleton, { className: "h-4 w-4" }) }),
869
+ visibleColumns.map((column, colIndex) => {
870
+ const width = typeof column.width === "number" ? `${column.width}px` : column.width;
871
+ return /* @__PURE__ */ jsxRuntime.jsx(
872
+ TableCell,
873
+ {
874
+ className: "dg-cell",
875
+ style: { width, minWidth: column.minWidth, maxWidth: column.maxWidth },
876
+ children: /* @__PURE__ */ jsxRuntime.jsx(
877
+ Skeleton,
878
+ {
879
+ className: "h-4",
880
+ style: { width: skeletonWidths[rowIndex]?.[colIndex] ?? "60%" }
881
+ }
882
+ )
883
+ },
884
+ `skeleton-cell-${String(column.field)}`
885
+ );
886
+ }),
887
+ showActions && /* @__PURE__ */ jsxRuntime.jsx(TableCell, { className: "dg-cell dg-cell--actions w-[80px]", children: /* @__PURE__ */ jsxRuntime.jsx(Skeleton, { className: "h-6 w-6 ml-auto" }) })
888
+ ]
889
+ },
890
+ `skeleton-row-${String(rowIndex)}`
891
+ )) });
892
+ }
893
+ function stripHtml(html) {
894
+ const doc = new DOMParser().parseFromString(html, "text/html");
895
+ return doc.body.textContent?.trim() ?? "";
896
+ }
897
+ function reactNodeToText(node) {
898
+ if (node === null || node === void 0) return "";
899
+ if (typeof node === "string") return node;
900
+ if (typeof node === "number" || typeof node === "boolean") return String(node);
901
+ if (react.isValidElement(node)) {
902
+ try {
903
+ return stripHtml(server.renderToStaticMarkup(node));
904
+ } catch {
905
+ return "";
906
+ }
907
+ }
908
+ return String(node);
909
+ }
910
+ function getCellExportValue(row, col, rowIndex) {
911
+ if (col.exportFormatter) return col.exportFormatter(row);
912
+ if (col.template) {
913
+ const rendered = col.template(row, rowIndex);
914
+ return reactNodeToText(rendered);
915
+ }
916
+ const value = row[col.field];
917
+ return String(formatValue(value, col.format, col.type));
918
+ }
919
+ function exportToCsv(data, columns, fileName) {
920
+ const visibleColumns = columns.filter((col) => col.visible !== false);
921
+ const headerRow = visibleColumns.map((col) => `"${col.headerText}"`).join(",");
922
+ const dataRows = data.map(
923
+ (row, rowIndex) => visibleColumns.map((col) => {
924
+ const formatted = getCellExportValue(row, col, rowIndex);
925
+ return `"${formatted.replace(/"/g, '""')}"`;
926
+ }).join(",")
927
+ );
928
+ const csv = [headerRow, ...dataRows].join("\n");
929
+ const blob = new Blob([csv], { type: "text/csv;charset=utf-8;" });
930
+ const url = URL.createObjectURL(blob);
931
+ const link = document.createElement("a");
932
+ link.href = url;
933
+ link.download = `${fileName}.csv`;
934
+ link.click();
935
+ URL.revokeObjectURL(url);
936
+ }
937
+ function exportToPrint(data, columns) {
938
+ const visibleColumns = columns.filter((col) => col.visible !== false);
939
+ const tableHtml = `
940
+ <!DOCTYPE html>
941
+ <html>
942
+ <head>
943
+ <title>Print</title>
944
+ <style>
945
+ body { font-family: Inter, Arial, sans-serif; padding: 20px; }
946
+ table { border-collapse: collapse; width: 100%; font-size: 12px; }
947
+ th, td { border: 1px solid #ddd; padding: 8px 12px; text-align: left; }
948
+ th { background-color: #f5f5f5; font-weight: 600; }
949
+ tr:nth-child(even) { background-color: #fafafa; }
950
+ @media print {
951
+ body { padding: 0; }
952
+ th { background-color: #f5f5f5 !important; -webkit-print-color-adjust: exact; }
953
+ }
954
+ </style>
955
+ </head>
956
+ <body>
957
+ <table>
958
+ <thead>
959
+ <tr>${visibleColumns.map((col) => `<th>${col.headerText}</th>`).join("")}</tr>
960
+ </thead>
961
+ <tbody>
962
+ ${data.map(
963
+ (row, rowIndex) => `
964
+ <tr>
965
+ ${visibleColumns.map((col) => `<td>${getCellExportValue(row, col, rowIndex)}</td>`).join("")}
966
+ </tr>
967
+ `
968
+ ).join("")}
969
+ </tbody>
970
+ </table>
971
+ </body>
972
+ </html>
973
+ `;
974
+ const iframe = document.createElement("iframe");
975
+ iframe.style.cssText = "position:fixed;width:0;height:0;border:none;opacity:0";
976
+ document.body.appendChild(iframe);
977
+ const iframeDoc = iframe.contentDocument ?? iframe.contentWindow?.document;
978
+ if (!iframeDoc) {
979
+ document.body.removeChild(iframe);
980
+ return;
981
+ }
982
+ iframeDoc.open();
983
+ iframeDoc.write(tableHtml);
984
+ iframeDoc.close();
985
+ iframe.onload = () => {
986
+ iframe.contentWindow?.print();
987
+ setTimeout(() => document.body.removeChild(iframe), 1e3);
988
+ };
989
+ }
990
+ function getExportIcon(format) {
991
+ switch (format) {
992
+ case "csv":
993
+ return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.FileSpreadsheet, { className: "h-4 w-4" });
994
+ case "print":
995
+ return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Printer, { className: "h-4 w-4" });
996
+ default:
997
+ return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Download, { className: "h-4 w-4" });
998
+ }
999
+ }
1000
+ function getExportLabel(format) {
1001
+ switch (format) {
1002
+ case "csv":
1003
+ return "Export as CSV";
1004
+ case "print":
1005
+ return "Print";
1006
+ default:
1007
+ return `Export as ${format.toUpperCase()}`;
1008
+ }
1009
+ }
1010
+ function DataGridExport({
1011
+ config,
1012
+ data,
1013
+ columns,
1014
+ className
1015
+ }) {
1016
+ const { formats = ["csv", "print"], fileName = "export" } = config;
1017
+ const handleExport = react.useCallback(
1018
+ (format) => {
1019
+ switch (format) {
1020
+ case "csv":
1021
+ exportToCsv(data, columns, fileName);
1022
+ break;
1023
+ case "print":
1024
+ exportToPrint(data, columns);
1025
+ break;
1026
+ default:
1027
+ config.onCustomExport?.(format, data, columns);
1028
+ break;
1029
+ }
1030
+ },
1031
+ [data, columns, fileName, config]
1032
+ );
1033
+ if (formats.length === 1) {
1034
+ const format = formats[0];
1035
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1036
+ Button,
1037
+ {
1038
+ variant: "outline",
1039
+ size: "sm",
1040
+ className: cn("dg-export__btn gap-2", className),
1041
+ onClick: () => handleExport(format),
1042
+ children: [
1043
+ getExportIcon(format),
1044
+ getExportLabel(format)
1045
+ ]
1046
+ }
1047
+ );
1048
+ }
1049
+ return /* @__PURE__ */ jsxRuntime.jsxs(DropdownMenu, { children: [
1050
+ /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsxs(Button, { variant: "outline", size: "sm", className: cn("dg-export__btn gap-2", className), children: [
1051
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.Download, { className: "h-4 w-4" }),
1052
+ "Export"
1053
+ ] }) }),
1054
+ /* @__PURE__ */ jsxRuntime.jsx(DropdownMenuContent, { align: "end", className: "dg-export__content", children: formats.map((format) => /* @__PURE__ */ jsxRuntime.jsxs(
1055
+ DropdownMenuItem,
1056
+ {
1057
+ onClick: () => handleExport(format),
1058
+ className: "dg-export__item gap-2",
1059
+ children: [
1060
+ getExportIcon(format),
1061
+ getExportLabel(format)
1062
+ ]
1063
+ },
1064
+ format
1065
+ )) })
1066
+ ] });
1067
+ }
1068
+ function DataGridToolbar({
1069
+ showExport,
1070
+ showBulkActions,
1071
+ selectedCount,
1072
+ bulkActionConfig,
1073
+ exportConfig,
1074
+ data,
1075
+ columns,
1076
+ getSelectedData,
1077
+ classNames = {}
1078
+ }) {
1079
+ if (!showExport && !(showBulkActions && selectedCount > 0)) return null;
1080
+ return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: cn("dg-toolbar flex items-center justify-between gap-2", classNames.toolbar), children: [
1081
+ showBulkActions && selectedCount > 0 && bulkActionConfig && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "dg-toolbar__bulk flex items-center gap-2 p-2 bg-muted/50 rounded-md flex-1", children: [
1082
+ /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "text-sm text-muted-foreground", children: [
1083
+ selectedCount,
1084
+ " item(s) selected"
1085
+ ] }),
1086
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: "flex gap-2 ml-auto", children: bulkActionConfig.actions.map((action) => /* @__PURE__ */ jsxRuntime.jsxs(
1087
+ "button",
1088
+ {
1089
+ type: "button",
1090
+ className: cn(
1091
+ "dg-toolbar__bulk-btn inline-flex items-center gap-1 px-3 py-1.5 text-sm rounded-md transition-colors",
1092
+ action.variant === "destructive" ? "bg-destructive text-destructive-foreground hover:bg-destructive/90" : "bg-primary text-primary-foreground hover:bg-primary/90"
1093
+ ),
1094
+ onClick: () => action.onClick(getSelectedData()),
1095
+ children: [
1096
+ action.icon,
1097
+ action.label
1098
+ ]
1099
+ },
1100
+ action.label
1101
+ )) })
1102
+ ] }),
1103
+ showExport && exportConfig && /* @__PURE__ */ jsxRuntime.jsx("div", { className: cn("dg-toolbar__export ml-auto", classNames.exportToolbar), children: /* @__PURE__ */ jsxRuntime.jsx(DataGridExport, { config: exportConfig, data, columns }) })
1104
+ ] });
1105
+ }
1106
+ function getTextAlign(align) {
1107
+ switch (align) {
1108
+ case "Right":
1109
+ return "right";
1110
+ case "Center":
1111
+ return "center";
1112
+ default:
1113
+ return "left";
1114
+ }
1115
+ }
1116
+ function getJustifyContent(align) {
1117
+ switch (align) {
1118
+ case "Right":
1119
+ return "flex-end";
1120
+ case "Center":
1121
+ return "center";
1122
+ default:
1123
+ return "flex-start";
1124
+ }
1125
+ }
1126
+ function DataGridCellContentInner({
1127
+ row,
1128
+ column,
1129
+ children
1130
+ }) {
1131
+ const tooltipCfg = column.tooltip;
1132
+ if (!tooltipCfg || tooltipCfg === true) {
1133
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block truncate", children });
1134
+ }
1135
+ const config = tooltipCfg;
1136
+ return /* @__PURE__ */ jsxRuntime.jsxs(Tooltip, { children: [
1137
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipTrigger, { asChild: true, children: /* @__PURE__ */ jsxRuntime.jsx("span", { className: "block truncate", children }) }),
1138
+ /* @__PURE__ */ jsxRuntime.jsx(TooltipContent, { side: config.side ?? "top", className: config.className, children: config.content ? config.content(row) : children })
1139
+ ] });
1140
+ }
1141
+ function DataGrid({
1142
+ columns,
1143
+ data,
1144
+ isLoading = false,
1145
+ skeletonRowCount = 5,
1146
+ pagination,
1147
+ actionConfig,
1148
+ bulkActionConfig,
1149
+ contextMenuItems,
1150
+ exportConfig,
1151
+ childRowConfig,
1152
+ emptyMessage = "No data found",
1153
+ emptyComponent,
1154
+ onRowClick,
1155
+ className,
1156
+ classNames = {},
1157
+ keyField = "id",
1158
+ sortConfig: externalSortConfig,
1159
+ onSort,
1160
+ tableContainerRef: externalContainerRef,
1161
+ stickyHeader = false,
1162
+ rowHeight,
1163
+ maxBodyHeight,
1164
+ alternatingRows = true,
1165
+ enableColumnResize = false,
1166
+ showHeaderBorder = false
1167
+ }) {
1168
+ const [internalSortConfig, setInternalSortConfig] = react.useState({
1169
+ field: "",
1170
+ direction: null
1171
+ });
1172
+ const sortConfig = externalSortConfig ?? internalSortConfig;
1173
+ const [selectedRows, setSelectedRows] = react.useState(/* @__PURE__ */ new Set());
1174
+ const [hoveredRow, setHoveredRow] = react.useState(null);
1175
+ const [expandedRows, setExpandedRows] = react.useState(/* @__PURE__ */ new Set());
1176
+ const [columnWidths, setColumnWidths] = react.useState({});
1177
+ const [resizingColumn, setResizingColumn] = react.useState(null);
1178
+ const resizeStartX = react.useRef(0);
1179
+ const resizeStartWidth = react.useRef(0);
1180
+ const justResized = react.useRef(false);
1181
+ const internalContainerRef = react.useRef(null);
1182
+ const tableContainerRef = externalContainerRef ?? internalContainerRef;
1183
+ const sortedData = react.useMemo(() => {
1184
+ if (onSort || !sortConfig.field || !sortConfig.direction) return data;
1185
+ return [...data].sort((a, b) => {
1186
+ const field = sortConfig.field;
1187
+ const aVal = a[field];
1188
+ const bVal = b[field];
1189
+ if (aVal == null || bVal == null) return 0;
1190
+ let cmp = 0;
1191
+ if (aVal < bVal) cmp = -1;
1192
+ else if (aVal > bVal) cmp = 1;
1193
+ return sortConfig.direction === "desc" ? -cmp : cmp;
1194
+ });
1195
+ }, [data, sortConfig, onSort]);
1196
+ const visibleColumns = react.useMemo(() => columns.filter((col) => col.visible !== false), [columns]);
1197
+ const showBulkActions = bulkActionConfig?.enabled ?? false;
1198
+ const showActionColumn = !!actionConfig && actionConfig.actions.length > 0;
1199
+ const showExpanderColumn = childRowConfig?.enabled ?? false;
1200
+ const showContextMenu = !!contextMenuItems && contextMenuItems.length > 0;
1201
+ const showExport = exportConfig?.enabled ?? false;
1202
+ const totalColSpan = visibleColumns.length + (showBulkActions ? 1 : 0) + (showActionColumn ? 1 : 0) + (showExpanderColumn ? 1 : 0);
1203
+ const columnsToRender = visibleColumns.map((column, index) => ({ column, index }));
1204
+ const handleSort = react.useCallback(
1205
+ (field, allowSorting) => {
1206
+ if (allowSorting === false) return;
1207
+ setExpandedRows(/* @__PURE__ */ new Set());
1208
+ let nextSortConfig;
1209
+ if (sortConfig.field !== field) {
1210
+ nextSortConfig = { field, direction: "asc" };
1211
+ } else if (sortConfig.direction === "asc") {
1212
+ nextSortConfig = { field, direction: "desc" };
1213
+ } else {
1214
+ nextSortConfig = { field: "", direction: null };
1215
+ }
1216
+ if (onSort) {
1217
+ if (!nextSortConfig.field || !nextSortConfig.direction) {
1218
+ onSort(null);
1219
+ } else {
1220
+ const ordering = nextSortConfig.direction === "desc" ? `-${nextSortConfig.field}` : nextSortConfig.field;
1221
+ onSort(ordering);
1222
+ }
1223
+ } else {
1224
+ setInternalSortConfig(nextSortConfig);
1225
+ }
1226
+ },
1227
+ [sortConfig, onSort]
1228
+ );
1229
+ const handleSelectAll = react.useCallback(() => {
1230
+ if (selectedRows.size === data.length) {
1231
+ setSelectedRows(/* @__PURE__ */ new Set());
1232
+ } else {
1233
+ const allKeys = data.map((row) => row[keyField]);
1234
+ setSelectedRows(new Set(allKeys));
1235
+ }
1236
+ }, [data, keyField, selectedRows.size]);
1237
+ const handleSelectRow = react.useCallback((key) => {
1238
+ setSelectedRows((prev) => {
1239
+ const next = new Set(prev);
1240
+ if (next.has(key)) next.delete(key);
1241
+ else next.add(key);
1242
+ return next;
1243
+ });
1244
+ }, []);
1245
+ const handleToggleExpand = react.useCallback((key) => {
1246
+ setExpandedRows((prev) => {
1247
+ const next = new Set(prev);
1248
+ if (next.has(key)) next.delete(key);
1249
+ else next.add(key);
1250
+ return next;
1251
+ });
1252
+ }, []);
1253
+ const getSelectedData = react.useCallback(
1254
+ () => data.filter((row) => selectedRows.has(row[keyField])),
1255
+ [data, keyField, selectedRows]
1256
+ );
1257
+ const getColumnWidth = react.useCallback(
1258
+ (column) => {
1259
+ const field = String(column.field);
1260
+ if (columnWidths[field] !== void 0) return columnWidths[field];
1261
+ if (typeof column.width === "number") return column.width;
1262
+ if (typeof column.width === "string") {
1263
+ const parsed = parseInt(column.width, 10);
1264
+ return isNaN(parsed) ? 150 : parsed;
1265
+ }
1266
+ return 150;
1267
+ },
1268
+ [columnWidths]
1269
+ );
1270
+ const handleResizeStart = react.useCallback(
1271
+ (e, column) => {
1272
+ e.preventDefault();
1273
+ e.stopPropagation();
1274
+ const field = String(column.field);
1275
+ const currentWidth = getColumnWidth(column);
1276
+ resizeStartX.current = e.clientX;
1277
+ resizeStartWidth.current = currentWidth;
1278
+ setResizingColumn(field);
1279
+ const handleMouseMove = (moveEvent) => {
1280
+ const diff = moveEvent.clientX - resizeStartX.current;
1281
+ const minW = column.minWidth ?? 20;
1282
+ const newWidth = Math.max(minW, resizeStartWidth.current + diff);
1283
+ setColumnWidths((prev) => ({ ...prev, [field]: newWidth }));
1284
+ };
1285
+ const handleMouseUp = () => {
1286
+ setResizingColumn(null);
1287
+ justResized.current = true;
1288
+ requestAnimationFrame(() => {
1289
+ justResized.current = false;
1290
+ });
1291
+ document.removeEventListener("mousemove", handleMouseMove);
1292
+ document.removeEventListener("mouseup", handleMouseUp);
1293
+ };
1294
+ document.addEventListener("mousemove", handleMouseMove);
1295
+ document.addEventListener("mouseup", handleMouseUp);
1296
+ },
1297
+ [getColumnWidth]
1298
+ );
1299
+ react.useEffect(() => {
1300
+ const root = document.documentElement;
1301
+ if (resizingColumn) root.classList.add("dg-resizing");
1302
+ else root.classList.remove("dg-resizing");
1303
+ return () => root.classList.remove("dg-resizing");
1304
+ }, [resizingColumn]);
1305
+ const getColumnWidthStr = (column) => {
1306
+ if (enableColumnResize) return `${getColumnWidth(column)}px`;
1307
+ if (typeof column.width === "number") return `${column.width}px`;
1308
+ if (typeof column.width === "string") return column.width;
1309
+ return void 0;
1310
+ };
1311
+ const renderSortIcon = (column) => {
1312
+ if (column.allowSorting === false) return null;
1313
+ const field = String(column.field);
1314
+ if (sortConfig.field !== field) {
1315
+ return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronsUpDown, { className: "dg-sort-icon ml-1 h-3.5 w-3.5 opacity-30", strokeWidth: 2.5 });
1316
+ }
1317
+ if (sortConfig.direction === "asc") {
1318
+ return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronUp, { className: "dg-sort-icon ml-1 h-3.5 w-3.5 text-primary", strokeWidth: 2.5 });
1319
+ }
1320
+ if (sortConfig.direction === "desc") {
1321
+ return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: "dg-sort-icon ml-1 h-3.5 w-3.5 text-primary", strokeWidth: 2.5 });
1322
+ }
1323
+ return /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronsUpDown, { className: "dg-sort-icon ml-1 h-3.5 w-3.5 opacity-30", strokeWidth: 2.5 });
1324
+ };
1325
+ const renderCellValue = (row, column, rowIndex) => {
1326
+ if (column.template) return column.template(row, rowIndex);
1327
+ const value = row[column.field];
1328
+ const formatted = formatValue(value, column.format, column.type);
1329
+ return /* @__PURE__ */ jsxRuntime.jsx(DataGridCellContentInner, { row, column, children: formatted });
1330
+ };
1331
+ const renderRow = (row, rowIndex) => {
1332
+ const rowKey = row[keyField] ?? rowIndex;
1333
+ const isSelected = selectedRows.has(rowKey);
1334
+ const isHovered = hoveredRow === rowKey;
1335
+ const isExpanded = expandedRows.has(rowKey);
1336
+ const tableRowElement = /* @__PURE__ */ jsxRuntime.jsxs(
1337
+ TableRow,
1338
+ {
1339
+ className: cn(
1340
+ "dg-row transition-colors",
1341
+ isSelected && "dg-row--selected bg-muted",
1342
+ isExpanded && "dg-row--expanded",
1343
+ onRowClick && "cursor-pointer",
1344
+ classNames.row,
1345
+ isSelected && classNames.rowSelected,
1346
+ isExpanded && classNames.rowExpanded
1347
+ ),
1348
+ "data-state": isSelected ? "selected" : void 0,
1349
+ onClick: () => onRowClick?.(row),
1350
+ onMouseEnter: () => setHoveredRow(rowKey),
1351
+ onMouseLeave: () => setHoveredRow(null),
1352
+ style: rowHeight ? { height: rowHeight } : void 0,
1353
+ children: [
1354
+ showExpanderColumn && /* @__PURE__ */ jsxRuntime.jsx(TableCell, { className: cn("dg-cell dg-cell--expander w-[40px]", classNames.cellExpander), children: /* @__PURE__ */ jsxRuntime.jsx(DataGridExpander, { isExpanded, onToggle: () => handleToggleExpand(rowKey) }) }),
1355
+ showBulkActions && /* @__PURE__ */ jsxRuntime.jsx(TableCell, { className: cn("dg-cell dg-cell--checkbox w-[40px]", classNames.cellCheckbox), children: /* @__PURE__ */ jsxRuntime.jsx(
1356
+ Checkbox,
1357
+ {
1358
+ checked: isSelected,
1359
+ onCheckedChange: () => handleSelectRow(rowKey),
1360
+ onClick: (e) => e.stopPropagation(),
1361
+ "aria-label": `Select row ${rowIndex + 1}`
1362
+ }
1363
+ ) }),
1364
+ columnsToRender.map(({ column, index: colIndex }) => {
1365
+ const widthStr = getColumnWidthStr(column);
1366
+ return /* @__PURE__ */ jsxRuntime.jsx(
1367
+ TableCell,
1368
+ {
1369
+ className: cn("dg-cell", column.className, classNames.cell),
1370
+ style: {
1371
+ width: widthStr,
1372
+ minWidth: column.minWidth,
1373
+ maxWidth: column.maxWidth,
1374
+ textAlign: getTextAlign(column.textAlign)
1375
+ },
1376
+ "data-column": String(column.field),
1377
+ "data-col-index": colIndex,
1378
+ children: renderCellValue(row, column, rowIndex)
1379
+ },
1380
+ String(column.field)
1381
+ );
1382
+ }),
1383
+ showActionColumn && actionConfig && /* @__PURE__ */ jsxRuntime.jsx(
1384
+ TableCell,
1385
+ {
1386
+ className: cn("dg-cell dg-cell--actions", classNames.cellActions),
1387
+ style: { width: actionConfig.width || 80 },
1388
+ onClick: (e) => e.stopPropagation(),
1389
+ children: /* @__PURE__ */ jsxRuntime.jsx(DataGridActions, { config: actionConfig, data: row, isHovered })
1390
+ }
1391
+ )
1392
+ ]
1393
+ },
1394
+ rowKey
1395
+ );
1396
+ const wrappedRow = showContextMenu && contextMenuItems ? /* @__PURE__ */ jsxRuntime.jsx(
1397
+ DataGridContextMenu,
1398
+ {
1399
+ items: contextMenuItems,
1400
+ data: row,
1401
+ className: classNames.contextMenu,
1402
+ children: tableRowElement
1403
+ },
1404
+ rowKey
1405
+ ) : tableRowElement;
1406
+ return /* @__PURE__ */ jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [
1407
+ wrappedRow,
1408
+ showExpanderColumn && isExpanded && childRowConfig && /* @__PURE__ */ jsxRuntime.jsx(
1409
+ DataGridChildRow,
1410
+ {
1411
+ parentRow: row,
1412
+ config: childRowConfig,
1413
+ parentColSpan: totalColSpan,
1414
+ className: classNames.childRow
1415
+ }
1416
+ )
1417
+ ] });
1418
+ };
1419
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1420
+ "div",
1421
+ {
1422
+ className: cn(
1423
+ "dg-root h-full flex flex-col gap-4 w-full min-w-0 overflow-hidden",
1424
+ classNames.root,
1425
+ className
1426
+ ),
1427
+ children: [
1428
+ /* @__PURE__ */ jsxRuntime.jsx(
1429
+ DataGridToolbar,
1430
+ {
1431
+ showExport,
1432
+ showBulkActions,
1433
+ selectedCount: selectedRows.size,
1434
+ bulkActionConfig,
1435
+ exportConfig,
1436
+ data,
1437
+ columns,
1438
+ getSelectedData,
1439
+ classNames
1440
+ }
1441
+ ),
1442
+ /* @__PURE__ */ jsxRuntime.jsx(
1443
+ "div",
1444
+ {
1445
+ ref: tableContainerRef,
1446
+ className: cn(
1447
+ "dg-table-container custom-scrollbar relative overflow-auto rounded-md border flex-1 min-h-0 [&_[data-slot=table-container]]:overflow-visible",
1448
+ classNames.tableContainer
1449
+ ),
1450
+ style: {
1451
+ width: 0,
1452
+ minWidth: "100%",
1453
+ ...maxBodyHeight ? { maxHeight: maxBodyHeight } : {}
1454
+ },
1455
+ children: /* @__PURE__ */ jsxRuntime.jsxs(Table, { className: cn("dg-table", classNames.table), children: [
1456
+ /* @__PURE__ */ jsxRuntime.jsx(
1457
+ TableHeader,
1458
+ {
1459
+ className: cn(
1460
+ "dg-header",
1461
+ stickyHeader && "sticky top-0 z-10 bg-background",
1462
+ classNames.header
1463
+ ),
1464
+ children: /* @__PURE__ */ jsxRuntime.jsxs(
1465
+ TableRow,
1466
+ {
1467
+ className: cn(
1468
+ "dg-header-row bg-muted/50",
1469
+ showHeaderBorder && "dg-header-bordered",
1470
+ classNames.headerRow
1471
+ ),
1472
+ children: [
1473
+ showExpanderColumn && /* @__PURE__ */ jsxRuntime.jsx(TableHead, { className: "dg-header-cell dg-header-cell--expander w-[40px]" }),
1474
+ showBulkActions && /* @__PURE__ */ jsxRuntime.jsx(TableHead, { className: "dg-header-cell dg-header-cell--checkbox w-[40px]", children: /* @__PURE__ */ jsxRuntime.jsx(
1475
+ Checkbox,
1476
+ {
1477
+ checked: data.length > 0 && selectedRows.size === data.length,
1478
+ onCheckedChange: handleSelectAll,
1479
+ "aria-label": "Select all"
1480
+ }
1481
+ ) }),
1482
+ columnsToRender.map(({ column }) => {
1483
+ const widthStr = getColumnWidthStr(column);
1484
+ const canResize = enableColumnResize && column.allowResizing !== false;
1485
+ return /* @__PURE__ */ jsxRuntime.jsxs(
1486
+ TableHead,
1487
+ {
1488
+ className: cn(
1489
+ "dg-header-cell select-none",
1490
+ column.headerClassName,
1491
+ classNames.headerCell
1492
+ ),
1493
+ style: {
1494
+ width: widthStr,
1495
+ minWidth: column.minWidth,
1496
+ maxWidth: column.maxWidth,
1497
+ textAlign: getTextAlign(column.textAlign)
1498
+ },
1499
+ "data-column": String(column.field),
1500
+ children: [
1501
+ column.headerTemplate ? column.headerTemplate() : /* @__PURE__ */ jsxRuntime.jsxs(
1502
+ "div",
1503
+ {
1504
+ className: cn(
1505
+ "flex items-center",
1506
+ column.allowSorting !== false && "cursor-pointer"
1507
+ ),
1508
+ style: { justifyContent: getJustifyContent(column.textAlign) },
1509
+ role: "button",
1510
+ tabIndex: 0,
1511
+ onClick: () => handleSort(String(column.field), column.allowSorting),
1512
+ onKeyDown: (e) => {
1513
+ if (e.key === "Enter" || e.key === " ") {
1514
+ e.preventDefault();
1515
+ handleSort(String(column.field), column.allowSorting);
1516
+ }
1517
+ },
1518
+ children: [
1519
+ column.headerText,
1520
+ renderSortIcon(column)
1521
+ ]
1522
+ }
1523
+ ),
1524
+ canResize && /* @__PURE__ */ jsxRuntime.jsx(
1525
+ "div",
1526
+ {
1527
+ className: cn(
1528
+ "dg-resize-handle",
1529
+ resizingColumn === String(column.field) && "dg-resize-handle--active"
1530
+ ),
1531
+ onMouseDown: (e) => handleResizeStart(e, column),
1532
+ role: "separator",
1533
+ "aria-orientation": "vertical"
1534
+ }
1535
+ )
1536
+ ]
1537
+ },
1538
+ String(column.field)
1539
+ );
1540
+ }),
1541
+ showActionColumn && actionConfig && /* @__PURE__ */ jsxRuntime.jsx(
1542
+ TableHead,
1543
+ {
1544
+ className: "dg-header-cell dg-header-cell--actions text-right",
1545
+ style: { width: actionConfig.width || 80 },
1546
+ children: actionConfig.showHeader !== false ? actionConfig.headerText || "Actions" : ""
1547
+ }
1548
+ )
1549
+ ]
1550
+ }
1551
+ )
1552
+ }
1553
+ ),
1554
+ /* @__PURE__ */ jsxRuntime.jsxs(
1555
+ TableBody,
1556
+ {
1557
+ className: cn("dg-body", alternatingRows && "dg-alternating", classNames.body),
1558
+ children: [
1559
+ isLoading && /* @__PURE__ */ jsxRuntime.jsx(
1560
+ DataGridSkeleton,
1561
+ {
1562
+ columns,
1563
+ rowCount: skeletonRowCount,
1564
+ showCheckbox: showBulkActions,
1565
+ showActions: showActionColumn,
1566
+ showExpander: showExpanderColumn
1567
+ }
1568
+ ),
1569
+ !isLoading && data.length === 0 && /* @__PURE__ */ jsxRuntime.jsx(TableRow, { className: "dg-row dg-row--empty", children: /* @__PURE__ */ jsxRuntime.jsx(
1570
+ TableCell,
1571
+ {
1572
+ colSpan: totalColSpan,
1573
+ className: cn("dg-cell dg-cell--empty h-24 text-center", classNames.empty),
1574
+ children: emptyComponent || /* @__PURE__ */ jsxRuntime.jsx("span", { className: "text-muted-foreground", children: emptyMessage })
1575
+ }
1576
+ ) }),
1577
+ !isLoading && sortedData.map((row, rowIndex) => renderRow(row, rowIndex))
1578
+ ]
1579
+ }
1580
+ )
1581
+ ] })
1582
+ }
1583
+ ),
1584
+ pagination && /* @__PURE__ */ jsxRuntime.jsx(DataGridPagination, { config: pagination, className: classNames.pagination })
1585
+ ]
1586
+ }
1587
+ );
1588
+ }
1589
+
1590
+ exports.DataGrid = DataGrid;
1591
+ exports.exportToCsv = exportToCsv;
1592
+ exports.exportToPrint = exportToPrint;
1593
+ exports.formatCurrency = formatCurrency;
1594
+ exports.formatDateValue = formatDateValue;
1595
+ exports.formatNumber = formatNumber;
1596
+ exports.formatValue = formatValue;
1597
+ exports.getCellExportValue = getCellExportValue;
1598
+ //# sourceMappingURL=index.cjs.map
1599
+ //# sourceMappingURL=index.cjs.map