@hexclave/dashboard-ui-components 1.0.29 → 1.0.32

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.
Files changed (32) hide show
  1. package/dist/components/button.d.ts +2 -2
  2. package/dist/components/chart-legend.d.ts +1 -1
  3. package/dist/components/data-grid/data-grid-export-dialog.d.ts +23 -0
  4. package/dist/components/data-grid/data-grid-export-dialog.d.ts.map +1 -0
  5. package/dist/components/data-grid/data-grid-export-dialog.js +406 -0
  6. package/dist/components/data-grid/data-grid-export-dialog.js.map +1 -0
  7. package/dist/components/data-grid/data-grid.d.ts.map +1 -1
  8. package/dist/components/data-grid/data-grid.js +14 -14
  9. package/dist/components/data-grid/data-grid.js.map +1 -1
  10. package/dist/components/data-grid/index.d.ts +2 -2
  11. package/dist/components/data-grid/types.d.ts +31 -2
  12. package/dist/components/data-grid/types.d.ts.map +1 -1
  13. package/dist/dashboard-ui-components.global.js +904 -535
  14. package/dist/dashboard-ui-components.global.js.map +4 -4
  15. package/dist/esm/components/button.d.ts +2 -2
  16. package/dist/esm/components/chart-legend.d.ts +1 -1
  17. package/dist/esm/components/data-grid/data-grid-export-dialog.d.ts +23 -0
  18. package/dist/esm/components/data-grid/data-grid-export-dialog.d.ts.map +1 -0
  19. package/dist/esm/components/data-grid/data-grid-export-dialog.js +403 -0
  20. package/dist/esm/components/data-grid/data-grid-export-dialog.js.map +1 -0
  21. package/dist/esm/components/data-grid/data-grid.d.ts.map +1 -1
  22. package/dist/esm/components/data-grid/data-grid.js +16 -16
  23. package/dist/esm/components/data-grid/data-grid.js.map +1 -1
  24. package/dist/esm/components/data-grid/index.d.ts +2 -2
  25. package/dist/esm/components/data-grid/types.d.ts +31 -2
  26. package/dist/esm/components/data-grid/types.d.ts.map +1 -1
  27. package/dist/index.d.ts +2 -2
  28. package/package.json +3 -3
  29. package/src/components/data-grid/data-grid-export-dialog.tsx +479 -0
  30. package/src/components/data-grid/data-grid.tsx +192 -193
  31. package/src/components/data-grid/index.ts +5 -0
  32. package/src/components/data-grid/types.ts +35 -0
@@ -4,7 +4,7 @@ import { VariantProps } from "class-variance-authority";
4
4
 
5
5
  //#region src/components/button.d.ts
6
6
  declare const designButtonVariants: (props?: ({
7
- variant?: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link" | "plain" | null | undefined;
7
+ variant?: "link" | "plain" | "default" | "destructive" | "outline" | "secondary" | "ghost" | null | undefined;
8
8
  size?: "default" | "sm" | "lg" | "icon" | null | undefined;
9
9
  } & class_variance_authority_types0.ClassProp) | undefined) => string;
10
10
  type DesignOriginalButtonProps = {
@@ -36,7 +36,7 @@ declare const DesignButton: React.FC<{
36
36
  } & {
37
37
  asChild?: boolean;
38
38
  } & React.ButtonHTMLAttributes<HTMLButtonElement> & VariantProps<(props?: ({
39
- variant?: "default" | "destructive" | "outline" | "secondary" | "ghost" | "link" | "plain" | null | undefined;
39
+ variant?: "link" | "plain" | "default" | "destructive" | "outline" | "secondary" | "ghost" | null | undefined;
40
40
  size?: "default" | "sm" | "lg" | "icon" | null | undefined;
41
41
  } & class_variance_authority_types0.ClassProp) | undefined) => string> & {
42
42
  ref?: React.Ref<HTMLButtonElement> | undefined;
@@ -2,7 +2,7 @@ import * as React$1 from "react";
2
2
  import * as RechartsPrimitive from "recharts";
3
3
 
4
4
  //#region src/components/chart-legend.d.ts
5
- declare const DesignChartLegendContent: React$1.ForwardRefExoticComponent<Omit<React$1.ClassAttributes<HTMLDivElement> & React$1.HTMLAttributes<HTMLDivElement> & Pick<RechartsPrimitive.LegendProps, "verticalAlign" | "payload"> & {
5
+ declare const DesignChartLegendContent: React$1.ForwardRefExoticComponent<Omit<React$1.ClassAttributes<HTMLDivElement> & React$1.HTMLAttributes<HTMLDivElement> & Pick<RechartsPrimitive.LegendProps, "payload" | "verticalAlign"> & {
6
6
  hideIcon?: boolean;
7
7
  nameKey?: string;
8
8
  }, "ref"> & React$1.RefAttributes<HTMLDivElement>>;
@@ -0,0 +1,23 @@
1
+ import { DataGridColumnDef, DataGridExportOptions } from "./types.js";
2
+ import * as react_jsx_runtime0 from "react/jsx-runtime";
3
+
4
+ //#region src/components/data-grid/data-grid-export-dialog.d.ts
5
+ type DataGridExportDialogProps<TRow> = {
6
+ open: boolean;
7
+ onOpenChange: (open: boolean) => void;
8
+ rows: readonly TRow[];
9
+ columns: readonly DataGridColumnDef<TRow>[];
10
+ exportFilename: string;
11
+ exportOptions?: DataGridExportOptions<TRow>;
12
+ };
13
+ declare function DataGridExportDialog<TRow>({
14
+ open,
15
+ onOpenChange,
16
+ rows,
17
+ columns,
18
+ exportFilename,
19
+ exportOptions
20
+ }: DataGridExportDialogProps<TRow>): react_jsx_runtime0.JSX.Element;
21
+ //#endregion
22
+ export { DataGridExportDialog };
23
+ //# sourceMappingURL=data-grid-export-dialog.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-grid-export-dialog.d.ts","names":[],"sources":["../../../src/components/data-grid/data-grid-export-dialog.tsx"],"mappings":";;;;KA4BK,yBAAA;EACH,IAAA;EACA,YAAA,GAAe,IAAA;EACf,IAAA,WAAe,IAAA;EACf,OAAA,WAAkB,iBAAA,CAAkB,IAAA;EACpC,cAAA;EACA,aAAA,GAAgB,qBAAA,CAAsB,IAAA;AAAA;AAAA,iBASxB,oBAAA,MAAA,CAAA;EACd,IAAA;EACA,YAAA;EACA,IAAA;EACA,OAAA;EACA,cAAA;EACA;AAAA,GACC,yBAAA,CAA0B,IAAA,IAAK,kBAAA,CAAA,GAAA,CAAA,OAAA"}
@@ -0,0 +1,406 @@
1
+ "use client";
2
+
3
+ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
4
+ const require_chunk = require('../../chunk-BE-pF4vm.js');
5
+ let _phosphor_icons_react = require("@phosphor-icons/react");
6
+ let react_jsx_runtime = require("react/jsx-runtime");
7
+ let react = require("react");
8
+ react = require_chunk.__toESM(react);
9
+ let ___button_js = require("../button.js");
10
+ let __state_js = require("./state.js");
11
+ let ___dialog_js = require("../dialog.js");
12
+
13
+ //#region src/components/data-grid/data-grid-export-dialog.tsx
14
+ const idleExportProgress = {
15
+ phase: "idle",
16
+ fetched: 0
17
+ };
18
+ const exportCompletionDisplayMs = 800;
19
+ function DataGridExportDialog({ open, onOpenChange, rows, columns, exportFilename, exportOptions }) {
20
+ const hasServerExport = exportOptions?.fetchRows != null;
21
+ const resolvedFields = (0, react.useMemo)(() => exportOptions?.fields ?? buildColumnExportFields(columns), [exportOptions?.fields, columns]);
22
+ const [format, setFormat] = (0, react.useState)("csv");
23
+ const [scope, setScope] = (0, react.useState)("all");
24
+ const [fields, setFields] = (0, react.useState)(resolvedFields);
25
+ const [isExporting, setIsExporting] = (0, react.useState)(false);
26
+ const [progress, setProgress] = (0, react.useState)(idleExportProgress);
27
+ const [errorMessage, setErrorMessage] = (0, react.useState)(null);
28
+ (0, react.useEffect)(() => {
29
+ if (!isExporting) setFields(resolvedFields);
30
+ }, [isExporting, resolvedFields]);
31
+ exportOptions?.entityName;
32
+ const entityNamePlural = exportOptions?.entityNamePlural ?? "rows";
33
+ const filenamePrefix = exportOptions?.filenamePrefix ?? exportFilename;
34
+ const title = exportOptions?.title ?? "Export data";
35
+ const description = exportOptions?.description ?? (hasServerExport ? "Configure and download data from this table" : "Configure and download the rows currently loaded in this table");
36
+ const allScopeLabel = exportOptions?.allScopeLabel ?? `Export all ${entityNamePlural} in the project`;
37
+ const filteredScopeLabel = exportOptions?.filteredScopeLabel ?? `Export only filtered/searched ${entityNamePlural}`;
38
+ const progressSubjectLabel = exportOptions?.progressSubjectLabel ?? entityNamePlural;
39
+ const progressTitle = progress.phase === "complete" ? "Export complete" : `Exporting ${progressSubjectLabel}`;
40
+ const fetchExportRows = exportOptions?.fetchRows;
41
+ const closeDialog = (0, react.useCallback)(() => {
42
+ onOpenChange(false);
43
+ setErrorMessage(null);
44
+ }, [onOpenChange]);
45
+ const handleOpenChange = (0, react.useCallback)((nextOpen) => {
46
+ if (isExporting && !nextOpen) return;
47
+ if (nextOpen) onOpenChange(true);
48
+ else closeDialog();
49
+ }, [
50
+ closeDialog,
51
+ isExporting,
52
+ onOpenChange
53
+ ]);
54
+ const toggleField = (0, react.useCallback)((key) => {
55
+ setFields((prev) => prev.map((field) => field.key === key ? {
56
+ ...field,
57
+ enabled: !field.enabled
58
+ } : field));
59
+ }, []);
60
+ const selectAllFields = (0, react.useCallback)(() => {
61
+ setFields((prev) => prev.map((field) => ({
62
+ ...field,
63
+ enabled: true
64
+ })));
65
+ }, []);
66
+ const deselectAllFields = (0, react.useCallback)(() => {
67
+ setFields((prev) => prev.map((field) => ({
68
+ ...field,
69
+ enabled: false
70
+ })));
71
+ }, []);
72
+ const fetchRows = (0, react.useCallback)(async () => {
73
+ if (fetchExportRows != null) return await fetchExportRows({
74
+ scope,
75
+ onProgress: (fetched) => setProgress({
76
+ phase: "fetching",
77
+ fetched
78
+ })
79
+ });
80
+ setProgress({
81
+ phase: "fetching",
82
+ fetched: rows.length
83
+ });
84
+ return rows;
85
+ }, [
86
+ fetchExportRows,
87
+ rows,
88
+ scope
89
+ ]);
90
+ const handleExport = async () => {
91
+ const enabledFields = fields.filter((field) => field.enabled);
92
+ if (enabledFields.length === 0) {
93
+ setErrorMessage("Select at least one field to export.");
94
+ return;
95
+ }
96
+ setErrorMessage(null);
97
+ setIsExporting(true);
98
+ setProgress({
99
+ phase: "fetching",
100
+ fetched: 0
101
+ });
102
+ try {
103
+ const exportRows = await fetchRows();
104
+ if (exportRows.length === 0) {
105
+ setErrorMessage(exportOptions?.emptyExportDescription ?? `There are no ${entityNamePlural} to export.`);
106
+ setIsExporting(false);
107
+ setProgress(idleExportProgress);
108
+ return;
109
+ }
110
+ setProgress({
111
+ phase: "generating",
112
+ fetched: exportRows.length
113
+ });
114
+ const transformedData = buildExportTable(exportRows, enabledFields);
115
+ if (format === "csv") exportToCsv(transformedData, filenamePrefix);
116
+ else exportToJson(transformedData, filenamePrefix);
117
+ setProgress({
118
+ phase: "complete",
119
+ fetched: exportRows.length
120
+ });
121
+ await new Promise((resolve) => setTimeout(resolve, exportCompletionDisplayMs));
122
+ closeDialog();
123
+ } catch {
124
+ setErrorMessage("Something went wrong while exporting. Please try again.");
125
+ } finally {
126
+ setIsExporting(false);
127
+ setProgress(idleExportProgress);
128
+ }
129
+ };
130
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsx)(___dialog_js.DesignDialog, {
131
+ open,
132
+ onOpenChange: handleOpenChange,
133
+ title: isExporting ? progressTitle : title,
134
+ description: isExporting ? `Preparing export for ${progressSubjectLabel}.` : description,
135
+ size: "2xl",
136
+ variant: "plain",
137
+ headerClassName: isExporting ? "sr-only" : void 0,
138
+ hideTopCloseButton: isExporting,
139
+ children: isExporting ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)(ExportProgressContent, {
140
+ progress,
141
+ format,
142
+ subjectLabel: progressSubjectLabel
143
+ }) : /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
144
+ className: "space-y-6",
145
+ children: [
146
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
147
+ className: "space-y-2",
148
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
149
+ className: "text-sm font-medium",
150
+ htmlFor: `${filenamePrefix}-export-format`,
151
+ children: "Export Format"
152
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("select", {
153
+ id: `${filenamePrefix}-export-format`,
154
+ value: format,
155
+ onChange: (event) => setFormat(event.currentTarget.value === "json" ? "json" : "csv"),
156
+ className: "h-9 w-full rounded-md border border-input bg-white px-3 text-sm dark:bg-background",
157
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
158
+ value: "csv",
159
+ children: "CSV (Comma-separated values)"
160
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("option", {
161
+ value: "json",
162
+ children: "JSON (JavaScript Object Notation)"
163
+ })]
164
+ })]
165
+ }),
166
+ hasServerExport ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("fieldset", {
167
+ className: "space-y-2",
168
+ children: [
169
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("legend", {
170
+ className: "text-sm font-medium",
171
+ children: "Export Scope"
172
+ }),
173
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
174
+ className: "flex cursor-pointer items-center gap-2 text-sm",
175
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
176
+ type: "radio",
177
+ name: `${filenamePrefix}-export-scope`,
178
+ value: "all",
179
+ checked: scope === "all",
180
+ onChange: () => setScope("all")
181
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: allScopeLabel })]
182
+ }),
183
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
184
+ className: "flex cursor-pointer items-center gap-2 text-sm",
185
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
186
+ type: "radio",
187
+ name: `${filenamePrefix}-export-scope`,
188
+ value: "filtered",
189
+ checked: scope === "filtered",
190
+ onChange: () => setScope("filtered")
191
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: filteredScopeLabel })]
192
+ })
193
+ ]
194
+ }) : null,
195
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
196
+ className: "space-y-3",
197
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
198
+ className: "flex items-center justify-between gap-3",
199
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("label", {
200
+ className: "text-sm font-medium",
201
+ children: "Fields to Export"
202
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
203
+ className: "flex gap-2",
204
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(___button_js.DesignButton, {
205
+ type: "button",
206
+ variant: "ghost",
207
+ size: "sm",
208
+ onClick: selectAllFields,
209
+ className: "h-7 text-xs",
210
+ children: "Select All"
211
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)(___button_js.DesignButton, {
212
+ type: "button",
213
+ variant: "ghost",
214
+ size: "sm",
215
+ onClick: deselectAllFields,
216
+ className: "h-7 text-xs",
217
+ children: "Deselect All"
218
+ })]
219
+ })]
220
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
221
+ className: "grid max-h-[300px] grid-cols-1 gap-3 overflow-y-auto rounded-lg border border-border p-4 sm:grid-cols-2",
222
+ children: fields.map((field) => /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("label", {
223
+ className: "flex cursor-pointer items-center gap-2 text-sm font-normal",
224
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("input", {
225
+ type: "checkbox",
226
+ checked: field.enabled,
227
+ onChange: () => toggleField(field.key)
228
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", { children: field.label })]
229
+ }, field.key))
230
+ })]
231
+ }),
232
+ errorMessage != null ? /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
233
+ className: "rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive",
234
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
235
+ className: "font-medium",
236
+ children: exportOptions?.emptyExportTitle ?? "Export unavailable"
237
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { children: errorMessage })]
238
+ }) : null,
239
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
240
+ className: "flex justify-end gap-3 pt-2",
241
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(___button_js.DesignButton, {
242
+ variant: "outline",
243
+ onClick: closeDialog,
244
+ children: "Cancel"
245
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(___button_js.DesignButton, {
246
+ onClick: handleExport,
247
+ children: [
248
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)(_phosphor_icons_react.DownloadSimpleIcon, { className: "mr-2 h-4 w-4" }),
249
+ "Export ",
250
+ titleCase(entityNamePlural)
251
+ ]
252
+ })]
253
+ })
254
+ ]
255
+ })
256
+ });
257
+ }
258
+ function ExportProgressContent(props) {
259
+ const { progress, format, subjectLabel } = props;
260
+ const fileLabel = format.toUpperCase();
261
+ const isComplete = progress.phase === "complete";
262
+ const title = isComplete ? "Export complete" : `Exporting ${subjectLabel}`;
263
+ const description = isComplete ? `Your ${fileLabel} is ready and the download should begin automatically.` : `Your ${fileLabel} is being prepared from matching ${subjectLabel}.`;
264
+ const statusLabel = progress.phase === "complete" ? "Download ready" : progress.phase === "generating" ? `Preparing ${fileLabel}` : `Fetching ${subjectLabel}`;
265
+ const countLabel = `${progress.fetched.toLocaleString()} ${isComplete ? "exported" : "fetched"}`;
266
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
267
+ className: "space-y-4",
268
+ children: [
269
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
270
+ className: "space-y-1",
271
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("h2", {
272
+ className: "text-base font-semibold leading-snug",
273
+ children: title
274
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("p", {
275
+ className: "text-sm text-muted-foreground",
276
+ children: description
277
+ })]
278
+ }),
279
+ /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
280
+ className: "rounded-xl border border-border bg-muted/35 p-4",
281
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
282
+ className: "mb-3 flex items-center justify-between gap-4 text-sm",
283
+ children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
284
+ className: "font-medium text-foreground",
285
+ children: statusLabel
286
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("span", {
287
+ className: "shrink-0 tabular-nums text-muted-foreground",
288
+ children: countLabel
289
+ })]
290
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
291
+ className: "relative h-2 overflow-hidden rounded-full bg-foreground/10",
292
+ children: isComplete ? /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "h-full w-full rounded-full bg-emerald-500/80" }) : /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", { className: "data-grid-export-progress-shimmer absolute inset-y-0 left-0 w-2/5 rounded-full bg-gradient-to-r from-transparent via-foreground/65 to-transparent" })
293
+ })]
294
+ }),
295
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
296
+ className: "rounded-lg border border-amber-200 bg-amber-50 px-3 py-2 text-sm text-amber-900 dark:border-amber-500/30 dark:bg-amber-500/10 dark:text-amber-200",
297
+ children: "Do not reload this page until the export finishes. The download will start automatically."
298
+ }),
299
+ /* @__PURE__ */ (0, react_jsx_runtime.jsx)("div", {
300
+ className: "flex justify-end gap-3 pt-2",
301
+ children: /* @__PURE__ */ (0, react_jsx_runtime.jsx)(___button_js.DesignButton, {
302
+ variant: "outline",
303
+ disabled: true,
304
+ children: "Cancel"
305
+ })
306
+ })
307
+ ]
308
+ });
309
+ }
310
+ function buildColumnExportFields(columns) {
311
+ const fields = [];
312
+ for (const column of columns) {
313
+ const label = typeof column.header === "string" ? column.header.trim() : column.id;
314
+ if (label.length === 0) continue;
315
+ fields.push({
316
+ key: column.id,
317
+ label,
318
+ enabled: true,
319
+ getValue: (row) => formatColumnExportValue(column, row)
320
+ });
321
+ }
322
+ return fields;
323
+ }
324
+ function formatColumnExportValue(column, row) {
325
+ const value = (0, __state_js.resolveColumnValue)(column, row);
326
+ if (column.formatValue != null) return column.formatValue(value, row);
327
+ if (column.type === "date" || column.type === "dateTime") return (0, __state_js.formatGridDate)(value, "absolute", {
328
+ parseValue: column.parseValue,
329
+ dateFormat: column.dateFormat
330
+ }).display ?? "";
331
+ return value;
332
+ }
333
+ function buildExportTable(rows, enabledFields) {
334
+ return {
335
+ csvHeaders: enabledFields.map((field) => field.label),
336
+ jsonKeys: buildJsonKeys(enabledFields),
337
+ rows: rows.map((row) => enabledFields.map((field) => toExportCellValue(field.getValue(row))))
338
+ };
339
+ }
340
+ function buildJsonKeys(fields) {
341
+ const labelCounts = /* @__PURE__ */ new Map();
342
+ for (const field of fields) labelCounts.set(field.label, (labelCounts.get(field.label) ?? 0) + 1);
343
+ const usedKeys = /* @__PURE__ */ new Map();
344
+ const keys = [];
345
+ for (const field of fields) {
346
+ const baseKey = labelCounts.get(field.label) === 1 ? field.label : `${field.label} (${field.key})`;
347
+ let key = baseKey;
348
+ let suffix = 2;
349
+ while (usedKeys.has(key)) {
350
+ key = `${baseKey} ${suffix}`;
351
+ suffix++;
352
+ }
353
+ usedKeys.set(key, true);
354
+ keys.push(key);
355
+ }
356
+ return keys;
357
+ }
358
+ function toExportCellValue(value) {
359
+ if (value == null) return "";
360
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") return value;
361
+ if (typeof value === "bigint") return value.toString();
362
+ if (value instanceof Date) return value.toISOString();
363
+ if (typeof value === "object") return JSON.stringify(value);
364
+ return String(value);
365
+ }
366
+ function exportToCsv(data, filenamePrefix) {
367
+ downloadFile("" + [data.csvHeaders.map(escapeCsvCell).join(","), ...data.rows.map((row) => row.map(escapeCsvCell).join(","))].join("\n"), `${buildExportFilename(filenamePrefix)}.csv`, "text/csv;charset=utf-8;");
368
+ }
369
+ function escapeCsvCell(value) {
370
+ const rawText = String(value ?? "");
371
+ const text = typeof value === "string" && /^[=+\-@\t\r]/.test(rawText.trimStart()) ? `'${rawText}` : rawText;
372
+ if (text.includes(",") || text.includes("\"") || text.includes("\n") || text.includes("\r")) return `"${text.replace(/"/g, "\"\"")}"`;
373
+ return text;
374
+ }
375
+ function exportToJson(data, filenamePrefix) {
376
+ const rows = data.rows.map((row) => {
377
+ const jsonRow = {};
378
+ for (let i = 0; i < data.jsonKeys.length; i++) jsonRow[data.jsonKeys[i]] = row[i] ?? "";
379
+ return jsonRow;
380
+ });
381
+ downloadFile(JSON.stringify(rows, null, 2), `${buildExportFilename(filenamePrefix)}.json`, "application/json");
382
+ }
383
+ function downloadFile(content, filename, type) {
384
+ const blob = new Blob([content], { type });
385
+ const url = URL.createObjectURL(blob);
386
+ const link = document.createElement("a");
387
+ link.href = url;
388
+ link.download = filename;
389
+ document.body.appendChild(link);
390
+ try {
391
+ link.click();
392
+ } finally {
393
+ link.remove();
394
+ URL.revokeObjectURL(url);
395
+ }
396
+ }
397
+ function buildExportFilename(prefix) {
398
+ return `${prefix}-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}`;
399
+ }
400
+ function titleCase(value) {
401
+ return value.charAt(0).toUpperCase() + value.slice(1);
402
+ }
403
+
404
+ //#endregion
405
+ exports.DataGridExportDialog = DataGridExportDialog;
406
+ //# sourceMappingURL=data-grid-export-dialog.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"data-grid-export-dialog.js","names":["DesignDialog","DesignButton","DownloadSimpleIcon"],"sources":["../../../src/components/data-grid/data-grid-export-dialog.tsx"],"sourcesContent":["\"use client\";\n\nimport { DownloadSimpleIcon } from \"@phosphor-icons/react\";\nimport React, { useCallback, useEffect, useMemo, useState } from \"react\";\n\nimport { DesignButton } from \"../button\";\nimport { DesignDialog } from \"../dialog\";\nimport { formatGridDate, resolveColumnValue } from \"./state\";\nimport type {\n DataGridColumnDef,\n DataGridExportField,\n DataGridExportFormat,\n DataGridExportOptions,\n DataGridExportScope,\n} from \"./types\";\n\ntype ExportProgress = {\n phase: \"idle\" | \"fetching\" | \"generating\" | \"complete\";\n fetched: number;\n};\n\ntype ExportCellValue = string | number | boolean | null | undefined;\ntype ExportTable = {\n csvHeaders: string[];\n jsonKeys: string[];\n rows: ExportCellValue[][];\n};\n\ntype DataGridExportDialogProps<TRow> = {\n open: boolean;\n onOpenChange: (open: boolean) => void;\n rows: readonly TRow[];\n columns: readonly DataGridColumnDef<TRow>[];\n exportFilename: string;\n exportOptions?: DataGridExportOptions<TRow>;\n};\n\nconst idleExportProgress: ExportProgress = {\n phase: \"idle\",\n fetched: 0,\n};\nconst exportCompletionDisplayMs = 800;\n\nexport function DataGridExportDialog<TRow>({\n open,\n onOpenChange,\n rows,\n columns,\n exportFilename,\n exportOptions,\n}: DataGridExportDialogProps<TRow>) {\n const hasServerExport = exportOptions?.fetchRows != null;\n const resolvedFields = useMemo(\n () => exportOptions?.fields ?? buildColumnExportFields(columns),\n [exportOptions?.fields, columns],\n );\n const [format, setFormat] = useState<DataGridExportFormat>(\"csv\");\n const [scope, setScope] = useState<DataGridExportScope>(\"all\");\n const [fields, setFields] = useState<readonly DataGridExportField<TRow>[]>(resolvedFields);\n const [isExporting, setIsExporting] = useState(false);\n const [progress, setProgress] = useState<ExportProgress>(idleExportProgress);\n const [errorMessage, setErrorMessage] = useState<string | null>(null);\n\n useEffect(() => {\n if (!isExporting) {\n setFields(resolvedFields);\n }\n }, [isExporting, resolvedFields]);\n\n const entityName = exportOptions?.entityName ?? \"row\";\n const entityNamePlural = exportOptions?.entityNamePlural ?? \"rows\";\n const filenamePrefix = exportOptions?.filenamePrefix ?? exportFilename;\n const title = exportOptions?.title ?? \"Export data\";\n const description = exportOptions?.description ?? (\n hasServerExport\n ? \"Configure and download data from this table\"\n : \"Configure and download the rows currently loaded in this table\"\n );\n const allScopeLabel = exportOptions?.allScopeLabel ?? `Export all ${entityNamePlural} in the project`;\n const filteredScopeLabel = exportOptions?.filteredScopeLabel ?? `Export only filtered/searched ${entityNamePlural}`;\n const progressSubjectLabel = exportOptions?.progressSubjectLabel ?? entityNamePlural;\n const progressTitle = progress.phase === \"complete\" ? \"Export complete\" : `Exporting ${progressSubjectLabel}`;\n const fetchExportRows = exportOptions?.fetchRows;\n\n const closeDialog = useCallback(() => {\n onOpenChange(false);\n setErrorMessage(null);\n }, [onOpenChange]);\n\n const handleOpenChange = useCallback((nextOpen: boolean) => {\n if (isExporting && !nextOpen) {\n return;\n }\n if (nextOpen) {\n onOpenChange(true);\n } else {\n closeDialog();\n }\n }, [closeDialog, isExporting, onOpenChange]);\n\n const toggleField = useCallback((key: string) => {\n setFields((prev) =>\n prev.map((field) =>\n field.key === key ? { ...field, enabled: !field.enabled } : field\n )\n );\n }, []);\n\n const selectAllFields = useCallback(() => {\n setFields((prev) => prev.map((field) => ({ ...field, enabled: true })));\n }, []);\n\n const deselectAllFields = useCallback(() => {\n setFields((prev) => prev.map((field) => ({ ...field, enabled: false })));\n }, []);\n\n const fetchRows = useCallback(async () => {\n if (fetchExportRows != null) {\n return await fetchExportRows({\n scope,\n onProgress: (fetched) => setProgress({ phase: \"fetching\", fetched }),\n });\n }\n\n setProgress({ phase: \"fetching\", fetched: rows.length });\n return rows;\n }, [fetchExportRows, rows, scope]);\n\n const handleExport = async () => {\n const enabledFields = fields.filter((field) => field.enabled);\n if (enabledFields.length === 0) {\n setErrorMessage(\"Select at least one field to export.\");\n return;\n }\n\n setErrorMessage(null);\n setIsExporting(true);\n setProgress({ phase: \"fetching\", fetched: 0 });\n try {\n const exportRows = await fetchRows();\n\n if (exportRows.length === 0) {\n setErrorMessage(\n exportOptions?.emptyExportDescription\n ?? `There are no ${entityNamePlural} to export.`,\n );\n setIsExporting(false);\n setProgress(idleExportProgress);\n return;\n }\n\n setProgress({ phase: \"generating\", fetched: exportRows.length });\n const transformedData = buildExportTable(exportRows, enabledFields);\n\n if (format === \"csv\") {\n exportToCsv(transformedData, filenamePrefix);\n } else {\n exportToJson(transformedData, filenamePrefix);\n }\n\n setProgress({ phase: \"complete\", fetched: exportRows.length });\n await new Promise<void>((resolve) => setTimeout(resolve, exportCompletionDisplayMs));\n closeDialog();\n } catch {\n setErrorMessage(\"Something went wrong while exporting. Please try again.\");\n } finally {\n setIsExporting(false);\n setProgress(idleExportProgress);\n }\n };\n\n return (\n <DesignDialog\n open={open}\n onOpenChange={handleOpenChange}\n title={isExporting ? progressTitle : title}\n description={isExporting ? `Preparing export for ${progressSubjectLabel}.` : description}\n size=\"2xl\"\n variant=\"plain\"\n headerClassName={isExporting ? \"sr-only\" : undefined}\n hideTopCloseButton={isExporting}\n >\n {isExporting ? (\n <ExportProgressContent\n progress={progress}\n format={format}\n subjectLabel={progressSubjectLabel}\n />\n ) : (\n <div className=\"space-y-6\">\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium\" htmlFor={`${filenamePrefix}-export-format`}>\n Export Format\n </label>\n <select\n id={`${filenamePrefix}-export-format`}\n value={format}\n onChange={(event) => setFormat(event.currentTarget.value === \"json\" ? \"json\" : \"csv\")}\n className=\"h-9 w-full rounded-md border border-input bg-white px-3 text-sm dark:bg-background\"\n >\n <option value=\"csv\">CSV (Comma-separated values)</option>\n <option value=\"json\">JSON (JavaScript Object Notation)</option>\n </select>\n </div>\n\n {hasServerExport ? (\n <fieldset className=\"space-y-2\">\n <legend className=\"text-sm font-medium\">Export Scope</legend>\n <label className=\"flex cursor-pointer items-center gap-2 text-sm\">\n <input\n type=\"radio\"\n name={`${filenamePrefix}-export-scope`}\n value=\"all\"\n checked={scope === \"all\"}\n onChange={() => setScope(\"all\")}\n />\n <span>{allScopeLabel}</span>\n </label>\n <label className=\"flex cursor-pointer items-center gap-2 text-sm\">\n <input\n type=\"radio\"\n name={`${filenamePrefix}-export-scope`}\n value=\"filtered\"\n checked={scope === \"filtered\"}\n onChange={() => setScope(\"filtered\")}\n />\n <span>{filteredScopeLabel}</span>\n </label>\n </fieldset>\n ) : null}\n\n <div className=\"space-y-3\">\n <div className=\"flex items-center justify-between gap-3\">\n <label className=\"text-sm font-medium\">Fields to Export</label>\n <div className=\"flex gap-2\">\n <DesignButton type=\"button\" variant=\"ghost\" size=\"sm\" onClick={selectAllFields} className=\"h-7 text-xs\">\n Select All\n </DesignButton>\n <DesignButton type=\"button\" variant=\"ghost\" size=\"sm\" onClick={deselectAllFields} className=\"h-7 text-xs\">\n Deselect All\n </DesignButton>\n </div>\n </div>\n <div className=\"grid max-h-[300px] grid-cols-1 gap-3 overflow-y-auto rounded-lg border border-border p-4 sm:grid-cols-2\">\n {fields.map((field) => (\n <label key={field.key} className=\"flex cursor-pointer items-center gap-2 text-sm font-normal\">\n <input\n type=\"checkbox\"\n checked={field.enabled}\n onChange={() => toggleField(field.key)}\n />\n <span>{field.label}</span>\n </label>\n ))}\n </div>\n </div>\n\n {errorMessage != null ? (\n <div className=\"rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive\">\n <div className=\"font-medium\">{exportOptions?.emptyExportTitle ?? \"Export unavailable\"}</div>\n <div>{errorMessage}</div>\n </div>\n ) : null}\n\n <div className=\"flex justify-end gap-3 pt-2\">\n <DesignButton variant=\"outline\" onClick={closeDialog}>\n Cancel\n </DesignButton>\n <DesignButton onClick={handleExport}>\n <DownloadSimpleIcon className=\"mr-2 h-4 w-4\" />\n Export {titleCase(entityNamePlural)}\n </DesignButton>\n </div>\n </div>\n )}\n </DesignDialog>\n );\n}\n\nfunction ExportProgressContent(props: {\n progress: ExportProgress;\n format: DataGridExportFormat;\n subjectLabel: string;\n}) {\n const { progress, format, subjectLabel } = props;\n const fileLabel = format.toUpperCase();\n const isComplete = progress.phase === \"complete\";\n const title = isComplete ? \"Export complete\" : `Exporting ${subjectLabel}`;\n const description = isComplete\n ? `Your ${fileLabel} is ready and the download should begin automatically.`\n : `Your ${fileLabel} is being prepared from matching ${subjectLabel}.`;\n const statusLabel = progress.phase === \"complete\"\n ? \"Download ready\"\n : progress.phase === \"generating\"\n ? `Preparing ${fileLabel}`\n : `Fetching ${subjectLabel}`;\n const countLabel = `${progress.fetched.toLocaleString()} ${isComplete ? \"exported\" : \"fetched\"}`;\n\n return (\n <div className=\"space-y-4\">\n <div className=\"space-y-1\">\n <h2 className=\"text-base font-semibold leading-snug\">{title}</h2>\n <p className=\"text-sm text-muted-foreground\">{description}</p>\n </div>\n\n <div className=\"rounded-xl border border-border bg-muted/35 p-4\">\n <div className=\"mb-3 flex items-center justify-between gap-4 text-sm\">\n <span className=\"font-medium text-foreground\">{statusLabel}</span>\n <span className=\"shrink-0 tabular-nums text-muted-foreground\">\n {countLabel}\n </span>\n </div>\n <div className=\"relative h-2 overflow-hidden rounded-full bg-foreground/10\">\n {isComplete ? (\n <div className=\"h-full w-full rounded-full bg-emerald-500/80\" />\n ) : (\n <div className=\"data-grid-export-progress-shimmer absolute inset-y-0 left-0 w-2/5 rounded-full bg-gradient-to-r from-transparent via-foreground/65 to-transparent\" />\n )}\n </div>\n </div>\n\n <div className=\"rounded-lg border border-amber-200 bg-amber-50 px-3 py-2 text-sm text-amber-900 dark:border-amber-500/30 dark:bg-amber-500/10 dark:text-amber-200\">\n Do not reload this page until the export finishes. The download will start automatically.\n </div>\n\n <div className=\"flex justify-end gap-3 pt-2\">\n <DesignButton variant=\"outline\" disabled>\n Cancel\n </DesignButton>\n </div>\n </div>\n );\n}\n\nfunction buildColumnExportFields<TRow>(\n columns: readonly DataGridColumnDef<TRow>[],\n): readonly DataGridExportField<TRow>[] {\n const fields: DataGridExportField<TRow>[] = [];\n\n for (const column of columns) {\n const label = typeof column.header === \"string\" ? column.header.trim() : column.id;\n if (label.length === 0) {\n continue;\n }\n\n fields.push({\n key: column.id,\n label,\n enabled: true,\n getValue: (row) => formatColumnExportValue(column, row),\n });\n }\n\n return fields;\n}\n\nfunction formatColumnExportValue<TRow>(\n column: DataGridColumnDef<TRow>,\n row: TRow,\n): unknown {\n const value = resolveColumnValue(column, row);\n if (column.formatValue != null) {\n return column.formatValue(value, row);\n }\n if (column.type === \"date\" || column.type === \"dateTime\") {\n return formatGridDate(value, \"absolute\", {\n parseValue: column.parseValue,\n dateFormat: column.dateFormat,\n }).display ?? \"\";\n }\n return value;\n}\n\nfunction buildExportTable<TRow>(\n rows: readonly TRow[],\n enabledFields: readonly DataGridExportField<TRow>[],\n): ExportTable {\n return {\n csvHeaders: enabledFields.map((field) => field.label),\n jsonKeys: buildJsonKeys(enabledFields),\n rows: rows.map((row) => enabledFields.map((field) => toExportCellValue(field.getValue(row)))),\n };\n}\n\nfunction buildJsonKeys<TRow>(\n fields: readonly DataGridExportField<TRow>[],\n): string[] {\n const labelCounts = new Map<string, number>();\n for (const field of fields) {\n labelCounts.set(field.label, (labelCounts.get(field.label) ?? 0) + 1);\n }\n\n const usedKeys = new Map<string, true>();\n const keys: string[] = [];\n for (const field of fields) {\n const baseKey = labelCounts.get(field.label) === 1 ? field.label : `${field.label} (${field.key})`;\n let key = baseKey;\n let suffix = 2;\n while (usedKeys.has(key)) {\n key = `${baseKey} ${suffix}`;\n suffix++;\n }\n usedKeys.set(key, true);\n keys.push(key);\n }\n\n return keys;\n}\n\nfunction toExportCellValue(value: unknown): ExportCellValue {\n if (value == null) {\n return \"\";\n }\n if (typeof value === \"string\" || typeof value === \"number\" || typeof value === \"boolean\") {\n return value;\n }\n if (typeof value === \"bigint\") {\n return value.toString();\n }\n if (value instanceof Date) {\n return value.toISOString();\n }\n if (typeof value === \"object\") {\n return JSON.stringify(value);\n }\n return String(value);\n}\n\nfunction exportToCsv(data: ExportTable, filenamePrefix: string) {\n const csvContent = \"\\uFEFF\" + [\n data.csvHeaders.map(escapeCsvCell).join(\",\"),\n ...data.rows.map((row) => row.map(escapeCsvCell).join(\",\")),\n ].join(\"\\n\");\n downloadFile(csvContent, `${buildExportFilename(filenamePrefix)}.csv`, \"text/csv;charset=utf-8;\");\n}\n\nfunction escapeCsvCell(value: ExportCellValue): string {\n const rawText = String(value ?? \"\");\n const text = typeof value === \"string\" && /^[=+\\-@\\t\\r]/.test(rawText.trimStart()) ? `'${rawText}` : rawText;\n if (text.includes(\",\") || text.includes('\"') || text.includes(\"\\n\") || text.includes(\"\\r\")) {\n return `\"${text.replace(/\"/g, '\"\"')}\"`;\n }\n return text;\n}\n\nfunction exportToJson(data: ExportTable, filenamePrefix: string) {\n const rows = data.rows.map((row) => {\n const jsonRow: Record<string, ExportCellValue> = {};\n for (let i = 0; i < data.jsonKeys.length; i++) {\n jsonRow[data.jsonKeys[i]] = row[i] ?? \"\";\n }\n return jsonRow;\n });\n const jsonString = JSON.stringify(rows, null, 2);\n downloadFile(jsonString, `${buildExportFilename(filenamePrefix)}.json`, \"application/json\");\n}\n\nfunction downloadFile(content: string, filename: string, type: string) {\n const blob = new Blob([content], { type });\n const url = URL.createObjectURL(blob);\n const link = document.createElement(\"a\");\n link.href = url;\n link.download = filename;\n document.body.appendChild(link);\n try {\n link.click();\n } finally {\n link.remove();\n URL.revokeObjectURL(url);\n }\n}\n\nfunction buildExportFilename(prefix: string) {\n return `${prefix}-${new Date().toISOString().split(\"T\")[0]}`;\n}\n\nfunction titleCase(value: string) {\n return value.charAt(0).toUpperCase() + value.slice(1);\n}\n"],"mappings":";;;;;;;;;;;;;AAqCA,MAAM,qBAAqC;CACzC,OAAO;CACP,SAAS;CACV;AACD,MAAM,4BAA4B;AAElC,SAAgB,qBAA2B,EACzC,MACA,cACA,MACA,SACA,gBACA,iBACkC;CAClC,MAAM,kBAAkB,eAAe,aAAa;CACpD,MAAM,0CACE,eAAe,UAAU,wBAAwB,QAAQ,EAC/D,CAAC,eAAe,QAAQ,QAAQ,CACjC;CACD,MAAM,CAAC,QAAQ,iCAA4C,MAAM;CACjE,MAAM,CAAC,OAAO,gCAA0C,MAAM;CAC9D,MAAM,CAAC,QAAQ,iCAA4D,eAAe;CAC1F,MAAM,CAAC,aAAa,sCAA2B,MAAM;CACrD,MAAM,CAAC,UAAU,mCAAwC,mBAAmB;CAC5E,MAAM,CAAC,cAAc,uCAA2C,KAAK;AAErE,4BAAgB;AACd,MAAI,CAAC,YACH,WAAU,eAAe;IAE1B,CAAC,aAAa,eAAe,CAAC;AAEd,gBAAe;CAClC,MAAM,mBAAmB,eAAe,oBAAoB;CAC5D,MAAM,iBAAiB,eAAe,kBAAkB;CACxD,MAAM,QAAQ,eAAe,SAAS;CACtC,MAAM,cAAc,eAAe,gBACjC,kBACI,gDACA;CAEN,MAAM,gBAAgB,eAAe,iBAAiB,cAAc,iBAAiB;CACrF,MAAM,qBAAqB,eAAe,sBAAsB,iCAAiC;CACjG,MAAM,uBAAuB,eAAe,wBAAwB;CACpE,MAAM,gBAAgB,SAAS,UAAU,aAAa,oBAAoB,aAAa;CACvF,MAAM,kBAAkB,eAAe;CAEvC,MAAM,2CAAgC;AACpC,eAAa,MAAM;AACnB,kBAAgB,KAAK;IACpB,CAAC,aAAa,CAAC;CAElB,MAAM,2CAAgC,aAAsB;AAC1D,MAAI,eAAe,CAAC,SAClB;AAEF,MAAI,SACF,cAAa,KAAK;MAElB,cAAa;IAEd;EAAC;EAAa;EAAa;EAAa,CAAC;CAE5C,MAAM,sCAA2B,QAAgB;AAC/C,aAAW,SACT,KAAK,KAAK,UACR,MAAM,QAAQ,MAAM;GAAE,GAAG;GAAO,SAAS,CAAC,MAAM;GAAS,GAAG,MAC7D,CACF;IACA,EAAE,CAAC;CAEN,MAAM,+CAAoC;AACxC,aAAW,SAAS,KAAK,KAAK,WAAW;GAAE,GAAG;GAAO,SAAS;GAAM,EAAE,CAAC;IACtE,EAAE,CAAC;CAEN,MAAM,iDAAsC;AAC1C,aAAW,SAAS,KAAK,KAAK,WAAW;GAAE,GAAG;GAAO,SAAS;GAAO,EAAE,CAAC;IACvE,EAAE,CAAC;CAEN,MAAM,mCAAwB,YAAY;AACxC,MAAI,mBAAmB,KACrB,QAAO,MAAM,gBAAgB;GAC3B;GACA,aAAa,YAAY,YAAY;IAAE,OAAO;IAAY;IAAS,CAAC;GACrE,CAAC;AAGJ,cAAY;GAAE,OAAO;GAAY,SAAS,KAAK;GAAQ,CAAC;AACxD,SAAO;IACN;EAAC;EAAiB;EAAM;EAAM,CAAC;CAElC,MAAM,eAAe,YAAY;EAC/B,MAAM,gBAAgB,OAAO,QAAQ,UAAU,MAAM,QAAQ;AAC7D,MAAI,cAAc,WAAW,GAAG;AAC9B,mBAAgB,uCAAuC;AACvD;;AAGF,kBAAgB,KAAK;AACrB,iBAAe,KAAK;AACpB,cAAY;GAAE,OAAO;GAAY,SAAS;GAAG,CAAC;AAC9C,MAAI;GACF,MAAM,aAAa,MAAM,WAAW;AAEpC,OAAI,WAAW,WAAW,GAAG;AAC3B,oBACE,eAAe,0BACZ,gBAAgB,iBAAiB,aACrC;AACD,mBAAe,MAAM;AACrB,gBAAY,mBAAmB;AAC/B;;AAGF,eAAY;IAAE,OAAO;IAAc,SAAS,WAAW;IAAQ,CAAC;GAChE,MAAM,kBAAkB,iBAAiB,YAAY,cAAc;AAEnE,OAAI,WAAW,MACb,aAAY,iBAAiB,eAAe;OAE5C,cAAa,iBAAiB,eAAe;AAG/C,eAAY;IAAE,OAAO;IAAY,SAAS,WAAW;IAAQ,CAAC;AAC9D,SAAM,IAAI,SAAe,YAAY,WAAW,SAAS,0BAA0B,CAAC;AACpF,gBAAa;UACP;AACN,mBAAgB,0DAA0D;YAClE;AACR,kBAAe,MAAM;AACrB,eAAY,mBAAmB;;;AAInC,QACE,2CAACA;EACO;EACN,cAAc;EACd,OAAO,cAAc,gBAAgB;EACrC,aAAa,cAAc,wBAAwB,qBAAqB,KAAK;EAC7E,MAAK;EACL,SAAQ;EACR,iBAAiB,cAAc,YAAY;EAC3C,oBAAoB;YAEnB,cACC,2CAAC;GACW;GACF;GACR,cAAc;IACd,GAEF,4CAAC;GAAI,WAAU;;IACb,4CAAC;KAAI,WAAU;gBACb,2CAAC;MAAM,WAAU;MAAsB,SAAS,GAAG,eAAe;gBAAiB;OAE3E,EACR,4CAAC;MACC,IAAI,GAAG,eAAe;MACtB,OAAO;MACP,WAAW,UAAU,UAAU,MAAM,cAAc,UAAU,SAAS,SAAS,MAAM;MACrF,WAAU;iBAEV,2CAAC;OAAO,OAAM;iBAAM;QAAqC,EACzD,2CAAC;OAAO,OAAM;iBAAO;QAA0C;OACxD;MACL;IAEL,kBACC,4CAAC;KAAS,WAAU;;MAClB,2CAAC;OAAO,WAAU;iBAAsB;QAAqB;MAC7D,4CAAC;OAAM,WAAU;kBACf,2CAAC;QACC,MAAK;QACL,MAAM,GAAG,eAAe;QACxB,OAAM;QACN,SAAS,UAAU;QACnB,gBAAgB,SAAS,MAAM;SAC/B,EACF,2CAAC,oBAAM,gBAAqB;QACtB;MACR,4CAAC;OAAM,WAAU;kBACf,2CAAC;QACC,MAAK;QACL,MAAM,GAAG,eAAe;QACxB,OAAM;QACN,SAAS,UAAU;QACnB,gBAAgB,SAAS,WAAW;SACpC,EACF,2CAAC,oBAAM,qBAA0B;QAC3B;;MACC,GACT;IAEJ,4CAAC;KAAI,WAAU;gBACb,4CAAC;MAAI,WAAU;iBACb,2CAAC;OAAM,WAAU;iBAAsB;QAAwB,EAC/D,4CAAC;OAAI,WAAU;kBACb,2CAACC;QAAa,MAAK;QAAS,SAAQ;QAAQ,MAAK;QAAK,SAAS;QAAiB,WAAU;kBAAc;SAEzF,EACf,2CAACA;QAAa,MAAK;QAAS,SAAQ;QAAQ,MAAK;QAAK,SAAS;QAAmB,WAAU;kBAAc;SAE3F;QACX;OACF,EACN,2CAAC;MAAI,WAAU;gBACZ,OAAO,KAAK,UACX,4CAAC;OAAsB,WAAU;kBAC/B,2CAAC;QACC,MAAK;QACL,SAAS,MAAM;QACf,gBAAgB,YAAY,MAAM,IAAI;SACtC,EACF,2CAAC,oBAAM,MAAM,QAAa;SANhB,MAAM,IAOV,CACR;OACE;MACF;IAEL,gBAAgB,OACf,4CAAC;KAAI,WAAU;gBACb,2CAAC;MAAI,WAAU;gBAAe,eAAe,oBAAoB;OAA2B,EAC5F,2CAAC,mBAAK,eAAmB;MACrB,GACJ;IAEJ,4CAAC;KAAI,WAAU;gBACb,2CAACA;MAAa,SAAQ;MAAU,SAAS;gBAAa;OAEvC,EACf,4CAACA;MAAa,SAAS;;OACrB,2CAACC,4CAAmB,WAAU,iBAAiB;;OACvC,UAAU,iBAAiB;;OACtB;MACX;;IACF;GAEK;;AAInB,SAAS,sBAAsB,OAI5B;CACD,MAAM,EAAE,UAAU,QAAQ,iBAAiB;CAC3C,MAAM,YAAY,OAAO,aAAa;CACtC,MAAM,aAAa,SAAS,UAAU;CACtC,MAAM,QAAQ,aAAa,oBAAoB,aAAa;CAC5D,MAAM,cAAc,aAChB,QAAQ,UAAU,0DAClB,QAAQ,UAAU,mCAAmC,aAAa;CACtE,MAAM,cAAc,SAAS,UAAU,aACnC,mBACA,SAAS,UAAU,eACjB,aAAa,cACb,YAAY;CAClB,MAAM,aAAa,GAAG,SAAS,QAAQ,gBAAgB,CAAC,GAAG,aAAa,aAAa;AAErF,QACE,4CAAC;EAAI,WAAU;;GACb,4CAAC;IAAI,WAAU;eACb,2CAAC;KAAG,WAAU;eAAwC;MAAW,EACjE,2CAAC;KAAE,WAAU;eAAiC;MAAgB;KAC1D;GAEN,4CAAC;IAAI,WAAU;eACb,4CAAC;KAAI,WAAU;gBACb,2CAAC;MAAK,WAAU;gBAA+B;OAAmB,EAClE,2CAAC;MAAK,WAAU;gBACb;OACI;MACH,EACN,2CAAC;KAAI,WAAU;eACZ,aACC,2CAAC,SAAI,WAAU,iDAAiD,GAEhE,2CAAC,SAAI,WAAU,sJAAsJ;MAEnK;KACF;GAEN,2CAAC;IAAI,WAAU;cAAoJ;KAE7J;GAEN,2CAAC;IAAI,WAAU;cACb,2CAACD;KAAa,SAAQ;KAAU;eAAS;MAE1B;KACX;;GACF;;AAIV,SAAS,wBACP,SACsC;CACtC,MAAM,SAAsC,EAAE;AAE9C,MAAK,MAAM,UAAU,SAAS;EAC5B,MAAM,QAAQ,OAAO,OAAO,WAAW,WAAW,OAAO,OAAO,MAAM,GAAG,OAAO;AAChF,MAAI,MAAM,WAAW,EACnB;AAGF,SAAO,KAAK;GACV,KAAK,OAAO;GACZ;GACA,SAAS;GACT,WAAW,QAAQ,wBAAwB,QAAQ,IAAI;GACxD,CAAC;;AAGJ,QAAO;;AAGT,SAAS,wBACP,QACA,KACS;CACT,MAAM,2CAA2B,QAAQ,IAAI;AAC7C,KAAI,OAAO,eAAe,KACxB,QAAO,OAAO,YAAY,OAAO,IAAI;AAEvC,KAAI,OAAO,SAAS,UAAU,OAAO,SAAS,WAC5C,uCAAsB,OAAO,YAAY;EACvC,YAAY,OAAO;EACnB,YAAY,OAAO;EACpB,CAAC,CAAC,WAAW;AAEhB,QAAO;;AAGT,SAAS,iBACP,MACA,eACa;AACb,QAAO;EACL,YAAY,cAAc,KAAK,UAAU,MAAM,MAAM;EACrD,UAAU,cAAc,cAAc;EACtC,MAAM,KAAK,KAAK,QAAQ,cAAc,KAAK,UAAU,kBAAkB,MAAM,SAAS,IAAI,CAAC,CAAC,CAAC;EAC9F;;AAGH,SAAS,cACP,QACU;CACV,MAAM,8BAAc,IAAI,KAAqB;AAC7C,MAAK,MAAM,SAAS,OAClB,aAAY,IAAI,MAAM,QAAQ,YAAY,IAAI,MAAM,MAAM,IAAI,KAAK,EAAE;CAGvE,MAAM,2BAAW,IAAI,KAAmB;CACxC,MAAM,OAAiB,EAAE;AACzB,MAAK,MAAM,SAAS,QAAQ;EAC1B,MAAM,UAAU,YAAY,IAAI,MAAM,MAAM,KAAK,IAAI,MAAM,QAAQ,GAAG,MAAM,MAAM,IAAI,MAAM,IAAI;EAChG,IAAI,MAAM;EACV,IAAI,SAAS;AACb,SAAO,SAAS,IAAI,IAAI,EAAE;AACxB,SAAM,GAAG,QAAQ,GAAG;AACpB;;AAEF,WAAS,IAAI,KAAK,KAAK;AACvB,OAAK,KAAK,IAAI;;AAGhB,QAAO;;AAGT,SAAS,kBAAkB,OAAiC;AAC1D,KAAI,SAAS,KACX,QAAO;AAET,KAAI,OAAO,UAAU,YAAY,OAAO,UAAU,YAAY,OAAO,UAAU,UAC7E,QAAO;AAET,KAAI,OAAO,UAAU,SACnB,QAAO,MAAM,UAAU;AAEzB,KAAI,iBAAiB,KACnB,QAAO,MAAM,aAAa;AAE5B,KAAI,OAAO,UAAU,SACnB,QAAO,KAAK,UAAU,MAAM;AAE9B,QAAO,OAAO,MAAM;;AAGtB,SAAS,YAAY,MAAmB,gBAAwB;AAK9D,cAJmB,MAAW,CAC5B,KAAK,WAAW,IAAI,cAAc,CAAC,KAAK,IAAI,EAC5C,GAAG,KAAK,KAAK,KAAK,QAAQ,IAAI,IAAI,cAAc,CAAC,KAAK,IAAI,CAAC,CAC5D,CAAC,KAAK,KAAK,EACa,GAAG,oBAAoB,eAAe,CAAC,OAAO,0BAA0B;;AAGnG,SAAS,cAAc,OAAgC;CACrD,MAAM,UAAU,OAAO,SAAS,GAAG;CACnC,MAAM,OAAO,OAAO,UAAU,YAAY,eAAe,KAAK,QAAQ,WAAW,CAAC,GAAG,IAAI,YAAY;AACrG,KAAI,KAAK,SAAS,IAAI,IAAI,KAAK,SAAS,KAAI,IAAI,KAAK,SAAS,KAAK,IAAI,KAAK,SAAS,KAAK,CACxF,QAAO,IAAI,KAAK,QAAQ,MAAM,OAAK,CAAC;AAEtC,QAAO;;AAGT,SAAS,aAAa,MAAmB,gBAAwB;CAC/D,MAAM,OAAO,KAAK,KAAK,KAAK,QAAQ;EAClC,MAAM,UAA2C,EAAE;AACnD,OAAK,IAAI,IAAI,GAAG,IAAI,KAAK,SAAS,QAAQ,IACxC,SAAQ,KAAK,SAAS,MAAM,IAAI,MAAM;AAExC,SAAO;GACP;AAEF,cADmB,KAAK,UAAU,MAAM,MAAM,EAAE,EACvB,GAAG,oBAAoB,eAAe,CAAC,QAAQ,mBAAmB;;AAG7F,SAAS,aAAa,SAAiB,UAAkB,MAAc;CACrE,MAAM,OAAO,IAAI,KAAK,CAAC,QAAQ,EAAE,EAAE,MAAM,CAAC;CAC1C,MAAM,MAAM,IAAI,gBAAgB,KAAK;CACrC,MAAM,OAAO,SAAS,cAAc,IAAI;AACxC,MAAK,OAAO;AACZ,MAAK,WAAW;AAChB,UAAS,KAAK,YAAY,KAAK;AAC/B,KAAI;AACF,OAAK,OAAO;WACJ;AACR,OAAK,QAAQ;AACb,MAAI,gBAAgB,IAAI;;;AAI5B,SAAS,oBAAoB,QAAgB;AAC3C,QAAO,GAAG,OAAO,oBAAG,IAAI,MAAM,EAAC,aAAa,CAAC,MAAM,IAAI,CAAC;;AAG1D,SAAS,UAAU,OAAe;AAChC,QAAO,MAAM,OAAO,EAAE,CAAC,aAAa,GAAG,MAAM,MAAM,EAAE"}
@@ -1 +1 @@
1
- {"version":3,"file":"data-grid.d.ts","names":[],"sources":["../../../src/components/data-grid/data-grid.tsx"],"mappings":";;;;iBAmEgB,mCAAA,CAAoC,MAAA,EAAQ,WAAA;;;AAA5D;;;;;AAqhBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAgB,QAAA,MAAA,CAAe,KAAA,EAAO,aAAA,CAAc,IAAA,IAAK,kBAAA,CAAA,GAAA,CAAA,OAAA"}
1
+ {"version":3,"file":"data-grid.d.ts","names":[],"sources":["../../../src/components/data-grid/data-grid.tsx"],"mappings":";;;;iBAoEgB,mCAAA,CAAoC,MAAA,EAAQ,WAAA;;;AAA5D;;;;;AAqhBA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;iBAAgB,QAAA,MAAA,CAAe,KAAA,EAAO,aAAA,CAAc,IAAA,IAAK,kBAAA,CAAA,GAAA,CAAA,OAAA"}
@@ -14,6 +14,7 @@ let _tanstack_react_table = require("@tanstack/react-table");
14
14
  let _tanstack_react_virtual = require("@tanstack/react-virtual");
15
15
  let ___skeleton_js = require("../skeleton.js");
16
16
  let __data_grid_sizing_js = require("./data-grid-sizing.js");
17
+ let __data_grid_export_dialog_js = require("./data-grid-export-dialog.js");
17
18
  let __data_grid_toolbar_js = require("./data-grid-toolbar.js");
18
19
 
19
20
  //#region src/components/data-grid/data-grid.tsx
@@ -431,7 +432,7 @@ function DefaultFooter({ ctx, pagination, onChange }) {
431
432
  * - `renderCell` must be a pure function — no React hooks inside.
432
433
  */
433
434
  function DataGrid(props) {
434
- const { columns: allColumns, rows, getRowId, totalRowCount, isLoading = false, isRefetching = false, hasMore = false, isLoadingMore = false, onLoadMore, state, onChange, paginationMode = "paginated", selectionMode = "none", resizable = true, rowHeight: rowHeightProp = 44, estimatedRowHeight: estimatedRowHeightProp, headerHeight = 44, overscan = 5, maxHeight, fillHeight = true, stickyTop, toolbar, toolbarExtra, emptyState, loadingState, footer, footerExtra, exportFilename = "export", strings: stringsOverride, className, onRowClick, onRowDoubleClick, onSelectionChange, onSortChange, onColumnResize, onColumnVisibilityChange } = props;
435
+ const { columns: allColumns, rows, getRowId, totalRowCount, isLoading = false, isRefetching = false, hasMore = false, isLoadingMore = false, onLoadMore, state, onChange, paginationMode = "paginated", selectionMode = "none", resizable = true, rowHeight: rowHeightProp = 44, estimatedRowHeight: estimatedRowHeightProp, headerHeight = 44, overscan = 5, maxHeight, fillHeight = true, stickyTop, toolbar, toolbarExtra, emptyState, loadingState, footer, footerExtra, exportFilename = "export", exportOptions, strings: stringsOverride, className, onRowClick, onRowDoubleClick, onSelectionChange, onSortChange, onColumnResize, onColumnVisibilityChange } = props;
435
436
  const isDynamicRowHeight = rowHeightProp === "auto";
436
437
  const fixedRowHeight = isDynamicRowHeight ? void 0 : rowHeightProp;
437
438
  const estimatedRowHeight = estimatedRowHeightProp ?? fixedRowHeight ?? 44;
@@ -668,18 +669,10 @@ function DataGrid(props) {
668
669
  state.selection.selectedIds,
669
670
  fireSelection
670
671
  ]);
672
+ const [exportDialogOpen, setExportDialogOpen] = (0, react.useState)(false);
671
673
  const handleExportCsv = (0, react.useCallback)(() => {
672
- if (typeof window !== "undefined" && rows.length > 0) {
673
- const totalSuffix = totalRowCount != null && totalRowCount > rows.length ? ` of ${totalRowCount} total — load more rows first to include them` : "";
674
- if (!window.confirm(`Export ${rows.length.toLocaleString()} loaded row${rows.length === 1 ? "" : "s"}${totalSuffix}?`)) return;
675
- }
676
- (0, __state_js.exportToCsv)(rows, visibleColumns, exportFilename);
677
- }, [
678
- rows,
679
- visibleColumns,
680
- exportFilename,
681
- totalRowCount
682
- ]);
674
+ setExportDialogOpen(true);
675
+ }, []);
683
676
  const scrollContainerRef = (0, react.useRef)(null);
684
677
  const headerScrollRef = (0, react.useRef)(null);
685
678
  const stickyChromeRef = (0, react.useRef)(null);
@@ -771,7 +764,14 @@ function DataGrid(props) {
771
764
  return m;
772
765
  }, [headers]);
773
766
  const isBounded = fillHeight || maxHeight != null;
774
- return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
767
+ return /* @__PURE__ */ (0, react_jsx_runtime.jsxs)(react_jsx_runtime.Fragment, { children: [/* @__PURE__ */ (0, react_jsx_runtime.jsx)(__data_grid_export_dialog_js.DataGridExportDialog, {
768
+ open: exportDialogOpen,
769
+ onOpenChange: setExportDialogOpen,
770
+ rows,
771
+ columns: visibleColumns,
772
+ exportFilename,
773
+ exportOptions
774
+ }), /* @__PURE__ */ (0, react_jsx_runtime.jsxs)("div", {
775
775
  ref: gridRef,
776
776
  className: (0, _hexclave_ui.cn)("isolate flex w-full min-w-0 max-w-full flex-col bg-transparent rounded-[calc(var(--radius)*2)]", fillHeight ? "min-h-0 h-full" : "min-h-0 h-auto", isBounded && "overflow-hidden", className),
777
777
  style: maxHeight != null ? {
@@ -924,7 +924,7 @@ function DataGrid(props) {
924
924
  }), footerExtra && (typeof footerExtra === "function" ? footerExtra(footerCtx) : footerExtra)]
925
925
  })
926
926
  ]
927
- });
927
+ })] });
928
928
  }
929
929
 
930
930
  //#endregion