@hexclave/dashboard-ui-components 1.0.30 → 1.0.33

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 (28) hide show
  1. package/dist/components/data-grid/data-grid-export-dialog.d.ts +23 -0
  2. package/dist/components/data-grid/data-grid-export-dialog.d.ts.map +1 -0
  3. package/dist/components/data-grid/data-grid-export-dialog.js +406 -0
  4. package/dist/components/data-grid/data-grid-export-dialog.js.map +1 -0
  5. package/dist/components/data-grid/data-grid.d.ts.map +1 -1
  6. package/dist/components/data-grid/data-grid.js +14 -14
  7. package/dist/components/data-grid/data-grid.js.map +1 -1
  8. package/dist/components/data-grid/index.d.ts +2 -2
  9. package/dist/components/data-grid/types.d.ts +31 -2
  10. package/dist/components/data-grid/types.d.ts.map +1 -1
  11. package/dist/dashboard-ui-components.global.js +904 -535
  12. package/dist/dashboard-ui-components.global.js.map +4 -4
  13. package/dist/esm/components/data-grid/data-grid-export-dialog.d.ts +23 -0
  14. package/dist/esm/components/data-grid/data-grid-export-dialog.d.ts.map +1 -0
  15. package/dist/esm/components/data-grid/data-grid-export-dialog.js +403 -0
  16. package/dist/esm/components/data-grid/data-grid-export-dialog.js.map +1 -0
  17. package/dist/esm/components/data-grid/data-grid.d.ts.map +1 -1
  18. package/dist/esm/components/data-grid/data-grid.js +16 -16
  19. package/dist/esm/components/data-grid/data-grid.js.map +1 -1
  20. package/dist/esm/components/data-grid/index.d.ts +2 -2
  21. package/dist/esm/components/data-grid/types.d.ts +31 -2
  22. package/dist/esm/components/data-grid/types.d.ts.map +1 -1
  23. package/dist/index.d.ts +2 -2
  24. package/package.json +3 -3
  25. package/src/components/data-grid/data-grid-export-dialog.tsx +479 -0
  26. package/src/components/data-grid/data-grid.tsx +192 -193
  27. package/src/components/data-grid/index.ts +5 -0
  28. package/src/components/data-grid/types.ts +35 -0
@@ -163,7 +163,7 @@ var DashboardUI = (() => {
163
163
  var snakeCase2 = (str) => join2(str, "_");
164
164
  var kebabCase = (str) => join2(str, "-");
165
165
  var sentenceCase = (str) => upperFirst(join2(str, " "));
166
- var titleCase = (str) => words(str).map(upperFirst).join(" ");
166
+ var titleCase2 = (str) => words(str).map(upperFirst).join(" ");
167
167
  module.exports = {
168
168
  words,
169
169
  upperFirst,
@@ -172,7 +172,7 @@ var DashboardUI = (() => {
172
172
  snakeCase: snakeCase2,
173
173
  kebabCase,
174
174
  sentenceCase,
175
- titleCase
175
+ titleCase: titleCase2
176
176
  };
177
177
  }
178
178
  });
@@ -22120,7 +22120,7 @@ ${colorConfig.map(([key, itemConfig]) => {
22120
22120
  }
22121
22121
 
22122
22122
  // src/components/data-grid/data-grid.tsx
22123
- var import_react33 = __toESM(require_react());
22123
+ var import_react35 = __toESM(require_react());
22124
22124
 
22125
22125
  // src/components/data-grid/data-grid-sizing.ts
22126
22126
  var MIN_COL_WIDTH = 20;
@@ -22162,12 +22162,520 @@ ${colorConfig.map(([key, itemConfig]) => {
22162
22162
  return Math.max(getEffectiveMinWidth(col), Math.min(getEffectiveMaxWidth(col), width));
22163
22163
  }
22164
22164
 
22165
- // src/components/data-grid/data-grid-toolbar.tsx
22165
+ // src/components/data-grid/data-grid-export-dialog.tsx
22166
22166
  var import_react31 = __toESM(require_react());
22167
+
22168
+ // src/components/data-grid/state.ts
22169
+ function createDefaultDataGridState(columns) {
22170
+ const columnWidths = {};
22171
+ const columnOrder = [];
22172
+ for (const col of columns) {
22173
+ columnWidths[col.id] = clampColumnWidth(col, col.width ?? DEFAULT_COL_WIDTH);
22174
+ columnOrder.push(col.id);
22175
+ }
22176
+ return {
22177
+ sorting: [],
22178
+ columnVisibility: {},
22179
+ columnWidths,
22180
+ columnPinning: { left: [], right: [] },
22181
+ columnOrder,
22182
+ pagination: { pageIndex: 0, pageSize: 50 },
22183
+ selection: { selectedIds: /* @__PURE__ */ new Set(), anchorId: null },
22184
+ dateDisplay: "relative",
22185
+ quickSearch: ""
22186
+ };
22187
+ }
22188
+ function resolveColumnValue(col, row) {
22189
+ if (typeof col.accessor === "function") return col.accessor(row);
22190
+ const key = col.accessor ?? col.id;
22191
+ return row[key];
22192
+ }
22193
+ function defaultComparator(a26, b) {
22194
+ if (a26 == null && b == null) return 0;
22195
+ if (a26 == null) return -1;
22196
+ if (b == null) return 1;
22197
+ if (typeof a26 === "number" && typeof b === "number") return a26 - b;
22198
+ if (a26 instanceof Date && b instanceof Date) return a26.getTime() - b.getTime();
22199
+ return stringCompare(String(a26), String(b));
22200
+ }
22201
+ function buildRowComparator(sortModel, columns) {
22202
+ if (sortModel.length === 0) return null;
22203
+ const colMap = new Map(columns.map((c4) => [c4.id, c4]));
22204
+ return (a26, b) => {
22205
+ for (const { columnId, direction } of sortModel) {
22206
+ const col = colMap.get(columnId);
22207
+ if (!col) continue;
22208
+ const va = resolveColumnValue(col, a26);
22209
+ const vb = resolveColumnValue(col, b);
22210
+ const cmp = col.sortComparator ? col.sortComparator(va, vb) : defaultComparator(va, vb);
22211
+ if (cmp !== 0) return direction === "asc" ? cmp : -cmp;
22212
+ }
22213
+ return 0;
22214
+ };
22215
+ }
22216
+ function paginateRows(rows, pagination) {
22217
+ const start = pagination.pageIndex * pagination.pageSize;
22218
+ return rows.slice(start, start + pagination.pageSize);
22219
+ }
22220
+ function defaultMatchRow(row, query, columns) {
22221
+ for (const col of columns) {
22222
+ const v = resolveColumnValue(col, row);
22223
+ if (v == null) continue;
22224
+ if (String(v).toLowerCase().includes(query)) return true;
22225
+ }
22226
+ return false;
22227
+ }
22228
+ function applyQuickSearch(rows, query, columns, matchRow = defaultMatchRow) {
22229
+ const trimmed = query.trim().toLowerCase();
22230
+ if (!trimmed) return rows;
22231
+ return rows.filter((r7) => matchRow(r7, trimmed, columns));
22232
+ }
22233
+ function defaultParseDate(value) {
22234
+ if (value == null) return null;
22235
+ if (value instanceof Date) return isNaN(value.getTime()) ? null : value;
22236
+ if (typeof value === "number" || typeof value === "string") {
22237
+ const d = new Date(value);
22238
+ return isNaN(d.getTime()) ? null : d;
22239
+ }
22240
+ return null;
22241
+ }
22242
+ var DIVISIONS = [
22243
+ { amount: 60, unit: "second" },
22244
+ { amount: 60, unit: "minute" },
22245
+ { amount: 24, unit: "hour" },
22246
+ { amount: 7, unit: "day" },
22247
+ { amount: 4.34524, unit: "week" },
22248
+ { amount: 12, unit: "month" },
22249
+ { amount: Number.POSITIVE_INFINITY, unit: "year" }
22250
+ ];
22251
+ var relativeTimeFormatterCache = /* @__PURE__ */ new Map();
22252
+ function getRelativeTimeFormatter(locale2) {
22253
+ const key = locale2 ?? "__default__";
22254
+ let cached = relativeTimeFormatterCache.get(key);
22255
+ if (cached == null) {
22256
+ cached = new Intl.RelativeTimeFormat(locale2, { numeric: "auto" });
22257
+ relativeTimeFormatterCache.set(key, cached);
22258
+ }
22259
+ return cached;
22260
+ }
22261
+ function defaultFormatRelative(date2) {
22262
+ const rtf = getRelativeTimeFormatter();
22263
+ let duration = (date2.getTime() - Date.now()) / 1e3;
22264
+ for (const div of DIVISIONS) {
22265
+ if (Math.abs(duration) < div.amount) return rtf.format(Math.round(duration), div.unit);
22266
+ duration /= div.amount;
22267
+ }
22268
+ return rtf.format(Math.round(duration), "year");
22269
+ }
22270
+ function defaultFormatAbsolute(date2) {
22271
+ return date2.toLocaleString();
22272
+ }
22273
+ function formatGridDate(value, mode, opts) {
22274
+ const parse2 = opts?.parseValue ?? defaultParseDate;
22275
+ const date2 = parse2(value);
22276
+ if (!date2) return { display: null, tooltip: null };
22277
+ const relative = opts?.dateFormat?.relative ?? defaultFormatRelative;
22278
+ const absolute = opts?.dateFormat?.absolute ?? defaultFormatAbsolute;
22279
+ const tooltip = absolute(date2);
22280
+ const display = mode === "relative" ? relative(date2) : tooltip;
22281
+ return { display, tooltip };
22282
+ }
22283
+ function exportToCsv(rows, columns, filename) {
22284
+ const header = columns.map(
22285
+ (col) => typeof col.header === "string" ? col.header : col.id
22286
+ );
22287
+ const csvRows = rows.map(
22288
+ (row) => columns.map((col) => {
22289
+ const val = resolveColumnValue(col, row);
22290
+ const formatted = col.formatValue ? String(col.formatValue(val, row) ?? "") : String(val ?? "");
22291
+ if (formatted.includes(",") || formatted.includes('"') || formatted.includes("\n")) {
22292
+ return `"${formatted.replace(/"/g, '""')}"`;
22293
+ }
22294
+ return formatted;
22295
+ })
22296
+ );
22297
+ const csvContent = "\uFEFF" + [
22298
+ header.join(","),
22299
+ ...csvRows.map((row) => row.join(","))
22300
+ ].join("\n");
22301
+ const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" });
22302
+ const url = URL.createObjectURL(blob);
22303
+ const link = document.createElement("a");
22304
+ link.href = url;
22305
+ link.download = `${filename}.csv`;
22306
+ document.body.appendChild(link);
22307
+ try {
22308
+ link.click();
22309
+ } finally {
22310
+ link.remove();
22311
+ URL.revokeObjectURL(url);
22312
+ }
22313
+ }
22314
+
22315
+ // src/components/data-grid/data-grid-export-dialog.tsx
22316
+ var idleExportProgress = {
22317
+ phase: "idle",
22318
+ fetched: 0
22319
+ };
22320
+ var exportCompletionDisplayMs = 800;
22321
+ function DataGridExportDialog({
22322
+ open,
22323
+ onOpenChange,
22324
+ rows,
22325
+ columns,
22326
+ exportFilename,
22327
+ exportOptions
22328
+ }) {
22329
+ const hasServerExport = exportOptions?.fetchRows != null;
22330
+ const resolvedFields = (0, import_react31.useMemo)(
22331
+ () => exportOptions?.fields ?? buildColumnExportFields(columns),
22332
+ [exportOptions?.fields, columns]
22333
+ );
22334
+ const [format, setFormat] = (0, import_react31.useState)("csv");
22335
+ const [scope, setScope] = (0, import_react31.useState)("all");
22336
+ const [fields, setFields] = (0, import_react31.useState)(resolvedFields);
22337
+ const [isExporting, setIsExporting] = (0, import_react31.useState)(false);
22338
+ const [progress, setProgress] = (0, import_react31.useState)(idleExportProgress);
22339
+ const [errorMessage, setErrorMessage] = (0, import_react31.useState)(null);
22340
+ (0, import_react31.useEffect)(() => {
22341
+ if (!isExporting) {
22342
+ setFields(resolvedFields);
22343
+ }
22344
+ }, [isExporting, resolvedFields]);
22345
+ const entityName = exportOptions?.entityName ?? "row";
22346
+ const entityNamePlural = exportOptions?.entityNamePlural ?? "rows";
22347
+ const filenamePrefix = exportOptions?.filenamePrefix ?? exportFilename;
22348
+ const title = exportOptions?.title ?? "Export data";
22349
+ const description = exportOptions?.description ?? (hasServerExport ? "Configure and download data from this table" : "Configure and download the rows currently loaded in this table");
22350
+ const allScopeLabel = exportOptions?.allScopeLabel ?? `Export all ${entityNamePlural} in the project`;
22351
+ const filteredScopeLabel = exportOptions?.filteredScopeLabel ?? `Export only filtered/searched ${entityNamePlural}`;
22352
+ const progressSubjectLabel = exportOptions?.progressSubjectLabel ?? entityNamePlural;
22353
+ const progressTitle = progress.phase === "complete" ? "Export complete" : `Exporting ${progressSubjectLabel}`;
22354
+ const fetchExportRows = exportOptions?.fetchRows;
22355
+ const closeDialog = (0, import_react31.useCallback)(() => {
22356
+ onOpenChange(false);
22357
+ setErrorMessage(null);
22358
+ }, [onOpenChange]);
22359
+ const handleOpenChange = (0, import_react31.useCallback)((nextOpen) => {
22360
+ if (isExporting && !nextOpen) {
22361
+ return;
22362
+ }
22363
+ if (nextOpen) {
22364
+ onOpenChange(true);
22365
+ } else {
22366
+ closeDialog();
22367
+ }
22368
+ }, [closeDialog, isExporting, onOpenChange]);
22369
+ const toggleField = (0, import_react31.useCallback)((key) => {
22370
+ setFields(
22371
+ (prev) => prev.map(
22372
+ (field) => field.key === key ? { ...field, enabled: !field.enabled } : field
22373
+ )
22374
+ );
22375
+ }, []);
22376
+ const selectAllFields = (0, import_react31.useCallback)(() => {
22377
+ setFields((prev) => prev.map((field) => ({ ...field, enabled: true })));
22378
+ }, []);
22379
+ const deselectAllFields = (0, import_react31.useCallback)(() => {
22380
+ setFields((prev) => prev.map((field) => ({ ...field, enabled: false })));
22381
+ }, []);
22382
+ const fetchRows = (0, import_react31.useCallback)(async () => {
22383
+ if (fetchExportRows != null) {
22384
+ return await fetchExportRows({
22385
+ scope,
22386
+ onProgress: (fetched) => setProgress({ phase: "fetching", fetched })
22387
+ });
22388
+ }
22389
+ setProgress({ phase: "fetching", fetched: rows.length });
22390
+ return rows;
22391
+ }, [fetchExportRows, rows, scope]);
22392
+ const handleExport = async () => {
22393
+ const enabledFields = fields.filter((field) => field.enabled);
22394
+ if (enabledFields.length === 0) {
22395
+ setErrorMessage("Select at least one field to export.");
22396
+ return;
22397
+ }
22398
+ setErrorMessage(null);
22399
+ setIsExporting(true);
22400
+ setProgress({ phase: "fetching", fetched: 0 });
22401
+ try {
22402
+ const exportRows = await fetchRows();
22403
+ if (exportRows.length === 0) {
22404
+ setErrorMessage(
22405
+ exportOptions?.emptyExportDescription ?? `There are no ${entityNamePlural} to export.`
22406
+ );
22407
+ setIsExporting(false);
22408
+ setProgress(idleExportProgress);
22409
+ return;
22410
+ }
22411
+ setProgress({ phase: "generating", fetched: exportRows.length });
22412
+ const transformedData = buildExportTable(exportRows, enabledFields);
22413
+ if (format === "csv") {
22414
+ exportToCsv2(transformedData, filenamePrefix);
22415
+ } else {
22416
+ exportToJson(transformedData, filenamePrefix);
22417
+ }
22418
+ setProgress({ phase: "complete", fetched: exportRows.length });
22419
+ await new Promise((resolve) => setTimeout(resolve, exportCompletionDisplayMs));
22420
+ closeDialog();
22421
+ } catch {
22422
+ setErrorMessage("Something went wrong while exporting. Please try again.");
22423
+ } finally {
22424
+ setIsExporting(false);
22425
+ setProgress(idleExportProgress);
22426
+ }
22427
+ };
22428
+ return /* @__PURE__ */ jsx(
22429
+ DesignDialog,
22430
+ {
22431
+ open,
22432
+ onOpenChange: handleOpenChange,
22433
+ title: isExporting ? progressTitle : title,
22434
+ description: isExporting ? `Preparing export for ${progressSubjectLabel}.` : description,
22435
+ size: "2xl",
22436
+ variant: "plain",
22437
+ headerClassName: isExporting ? "sr-only" : void 0,
22438
+ hideTopCloseButton: isExporting,
22439
+ children: isExporting ? /* @__PURE__ */ jsx(
22440
+ ExportProgressContent,
22441
+ {
22442
+ progress,
22443
+ format,
22444
+ subjectLabel: progressSubjectLabel
22445
+ }
22446
+ ) : /* @__PURE__ */ jsxs("div", { className: "space-y-6", children: [
22447
+ /* @__PURE__ */ jsxs("div", { className: "space-y-2", children: [
22448
+ /* @__PURE__ */ jsx("label", { className: "text-sm font-medium", htmlFor: `${filenamePrefix}-export-format`, children: "Export Format" }),
22449
+ /* @__PURE__ */ jsxs(
22450
+ "select",
22451
+ {
22452
+ id: `${filenamePrefix}-export-format`,
22453
+ value: format,
22454
+ onChange: (event) => setFormat(event.currentTarget.value === "json" ? "json" : "csv"),
22455
+ className: "h-9 w-full rounded-md border border-input bg-white px-3 text-sm dark:bg-background",
22456
+ children: [
22457
+ /* @__PURE__ */ jsx("option", { value: "csv", children: "CSV (Comma-separated values)" }),
22458
+ /* @__PURE__ */ jsx("option", { value: "json", children: "JSON (JavaScript Object Notation)" })
22459
+ ]
22460
+ }
22461
+ )
22462
+ ] }),
22463
+ hasServerExport ? /* @__PURE__ */ jsxs("fieldset", { className: "space-y-2", children: [
22464
+ /* @__PURE__ */ jsx("legend", { className: "text-sm font-medium", children: "Export Scope" }),
22465
+ /* @__PURE__ */ jsxs("label", { className: "flex cursor-pointer items-center gap-2 text-sm", children: [
22466
+ /* @__PURE__ */ jsx(
22467
+ "input",
22468
+ {
22469
+ type: "radio",
22470
+ name: `${filenamePrefix}-export-scope`,
22471
+ value: "all",
22472
+ checked: scope === "all",
22473
+ onChange: () => setScope("all")
22474
+ }
22475
+ ),
22476
+ /* @__PURE__ */ jsx("span", { children: allScopeLabel })
22477
+ ] }),
22478
+ /* @__PURE__ */ jsxs("label", { className: "flex cursor-pointer items-center gap-2 text-sm", children: [
22479
+ /* @__PURE__ */ jsx(
22480
+ "input",
22481
+ {
22482
+ type: "radio",
22483
+ name: `${filenamePrefix}-export-scope`,
22484
+ value: "filtered",
22485
+ checked: scope === "filtered",
22486
+ onChange: () => setScope("filtered")
22487
+ }
22488
+ ),
22489
+ /* @__PURE__ */ jsx("span", { children: filteredScopeLabel })
22490
+ ] })
22491
+ ] }) : null,
22492
+ /* @__PURE__ */ jsxs("div", { className: "space-y-3", children: [
22493
+ /* @__PURE__ */ jsxs("div", { className: "flex items-center justify-between gap-3", children: [
22494
+ /* @__PURE__ */ jsx("label", { className: "text-sm font-medium", children: "Fields to Export" }),
22495
+ /* @__PURE__ */ jsxs("div", { className: "flex gap-2", children: [
22496
+ /* @__PURE__ */ jsx(DesignButton, { type: "button", variant: "ghost", size: "sm", onClick: selectAllFields, className: "h-7 text-xs", children: "Select All" }),
22497
+ /* @__PURE__ */ jsx(DesignButton, { type: "button", variant: "ghost", size: "sm", onClick: deselectAllFields, className: "h-7 text-xs", children: "Deselect All" })
22498
+ ] })
22499
+ ] }),
22500
+ /* @__PURE__ */ jsx("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", children: fields.map((field) => /* @__PURE__ */ jsxs("label", { className: "flex cursor-pointer items-center gap-2 text-sm font-normal", children: [
22501
+ /* @__PURE__ */ jsx(
22502
+ "input",
22503
+ {
22504
+ type: "checkbox",
22505
+ checked: field.enabled,
22506
+ onChange: () => toggleField(field.key)
22507
+ }
22508
+ ),
22509
+ /* @__PURE__ */ jsx("span", { children: field.label })
22510
+ ] }, field.key)) })
22511
+ ] }),
22512
+ errorMessage != null ? /* @__PURE__ */ jsxs("div", { className: "rounded-md border border-destructive/30 bg-destructive/10 px-3 py-2 text-sm text-destructive", children: [
22513
+ /* @__PURE__ */ jsx("div", { className: "font-medium", children: exportOptions?.emptyExportTitle ?? "Export unavailable" }),
22514
+ /* @__PURE__ */ jsx("div", { children: errorMessage })
22515
+ ] }) : null,
22516
+ /* @__PURE__ */ jsxs("div", { className: "flex justify-end gap-3 pt-2", children: [
22517
+ /* @__PURE__ */ jsx(DesignButton, { variant: "outline", onClick: closeDialog, children: "Cancel" }),
22518
+ /* @__PURE__ */ jsxs(DesignButton, { onClick: handleExport, children: [
22519
+ /* @__PURE__ */ jsx(e29, { className: "mr-2 h-4 w-4" }),
22520
+ "Export ",
22521
+ titleCase(entityNamePlural)
22522
+ ] })
22523
+ ] })
22524
+ ] })
22525
+ }
22526
+ );
22527
+ }
22528
+ function ExportProgressContent(props) {
22529
+ const { progress, format, subjectLabel } = props;
22530
+ const fileLabel = format.toUpperCase();
22531
+ const isComplete = progress.phase === "complete";
22532
+ const title = isComplete ? "Export complete" : `Exporting ${subjectLabel}`;
22533
+ const description = isComplete ? `Your ${fileLabel} is ready and the download should begin automatically.` : `Your ${fileLabel} is being prepared from matching ${subjectLabel}.`;
22534
+ const statusLabel = progress.phase === "complete" ? "Download ready" : progress.phase === "generating" ? `Preparing ${fileLabel}` : `Fetching ${subjectLabel}`;
22535
+ const countLabel = `${progress.fetched.toLocaleString()} ${isComplete ? "exported" : "fetched"}`;
22536
+ return /* @__PURE__ */ jsxs("div", { className: "space-y-4", children: [
22537
+ /* @__PURE__ */ jsxs("div", { className: "space-y-1", children: [
22538
+ /* @__PURE__ */ jsx("h2", { className: "text-base font-semibold leading-snug", children: title }),
22539
+ /* @__PURE__ */ jsx("p", { className: "text-sm text-muted-foreground", children: description })
22540
+ ] }),
22541
+ /* @__PURE__ */ jsxs("div", { className: "rounded-xl border border-border bg-muted/35 p-4", children: [
22542
+ /* @__PURE__ */ jsxs("div", { className: "mb-3 flex items-center justify-between gap-4 text-sm", children: [
22543
+ /* @__PURE__ */ jsx("span", { className: "font-medium text-foreground", children: statusLabel }),
22544
+ /* @__PURE__ */ jsx("span", { className: "shrink-0 tabular-nums text-muted-foreground", children: countLabel })
22545
+ ] }),
22546
+ /* @__PURE__ */ jsx("div", { className: "relative h-2 overflow-hidden rounded-full bg-foreground/10", children: isComplete ? /* @__PURE__ */ jsx("div", { className: "h-full w-full rounded-full bg-emerald-500/80" }) : /* @__PURE__ */ 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" }) })
22547
+ ] }),
22548
+ /* @__PURE__ */ jsx("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", children: "Do not reload this page until the export finishes. The download will start automatically." }),
22549
+ /* @__PURE__ */ jsx("div", { className: "flex justify-end gap-3 pt-2", children: /* @__PURE__ */ jsx(DesignButton, { variant: "outline", disabled: true, children: "Cancel" }) })
22550
+ ] });
22551
+ }
22552
+ function buildColumnExportFields(columns) {
22553
+ const fields = [];
22554
+ for (const column of columns) {
22555
+ const label = typeof column.header === "string" ? column.header.trim() : column.id;
22556
+ if (label.length === 0) {
22557
+ continue;
22558
+ }
22559
+ fields.push({
22560
+ key: column.id,
22561
+ label,
22562
+ enabled: true,
22563
+ getValue: (row) => formatColumnExportValue(column, row)
22564
+ });
22565
+ }
22566
+ return fields;
22567
+ }
22568
+ function formatColumnExportValue(column, row) {
22569
+ const value = resolveColumnValue(column, row);
22570
+ if (column.formatValue != null) {
22571
+ return column.formatValue(value, row);
22572
+ }
22573
+ if (column.type === "date" || column.type === "dateTime") {
22574
+ return formatGridDate(value, "absolute", {
22575
+ parseValue: column.parseValue,
22576
+ dateFormat: column.dateFormat
22577
+ }).display ?? "";
22578
+ }
22579
+ return value;
22580
+ }
22581
+ function buildExportTable(rows, enabledFields) {
22582
+ return {
22583
+ csvHeaders: enabledFields.map((field) => field.label),
22584
+ jsonKeys: buildJsonKeys(enabledFields),
22585
+ rows: rows.map((row) => enabledFields.map((field) => toExportCellValue(field.getValue(row))))
22586
+ };
22587
+ }
22588
+ function buildJsonKeys(fields) {
22589
+ const labelCounts = /* @__PURE__ */ new Map();
22590
+ for (const field of fields) {
22591
+ labelCounts.set(field.label, (labelCounts.get(field.label) ?? 0) + 1);
22592
+ }
22593
+ const usedKeys = /* @__PURE__ */ new Map();
22594
+ const keys = [];
22595
+ for (const field of fields) {
22596
+ const baseKey = labelCounts.get(field.label) === 1 ? field.label : `${field.label} (${field.key})`;
22597
+ let key = baseKey;
22598
+ let suffix = 2;
22599
+ while (usedKeys.has(key)) {
22600
+ key = `${baseKey} ${suffix}`;
22601
+ suffix++;
22602
+ }
22603
+ usedKeys.set(key, true);
22604
+ keys.push(key);
22605
+ }
22606
+ return keys;
22607
+ }
22608
+ function toExportCellValue(value) {
22609
+ if (value == null) {
22610
+ return "";
22611
+ }
22612
+ if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
22613
+ return value;
22614
+ }
22615
+ if (typeof value === "bigint") {
22616
+ return value.toString();
22617
+ }
22618
+ if (value instanceof Date) {
22619
+ return value.toISOString();
22620
+ }
22621
+ if (typeof value === "object") {
22622
+ return JSON.stringify(value);
22623
+ }
22624
+ return String(value);
22625
+ }
22626
+ function exportToCsv2(data, filenamePrefix) {
22627
+ const csvContent = "\uFEFF" + [
22628
+ data.csvHeaders.map(escapeCsvCell).join(","),
22629
+ ...data.rows.map((row) => row.map(escapeCsvCell).join(","))
22630
+ ].join("\n");
22631
+ downloadFile(csvContent, `${buildExportFilename(filenamePrefix)}.csv`, "text/csv;charset=utf-8;");
22632
+ }
22633
+ function escapeCsvCell(value) {
22634
+ const rawText = String(value ?? "");
22635
+ const text2 = typeof value === "string" && /^[=+\-@\t\r]/.test(rawText.trimStart()) ? `'${rawText}` : rawText;
22636
+ if (text2.includes(",") || text2.includes('"') || text2.includes("\n") || text2.includes("\r")) {
22637
+ return `"${text2.replace(/"/g, '""')}"`;
22638
+ }
22639
+ return text2;
22640
+ }
22641
+ function exportToJson(data, filenamePrefix) {
22642
+ const rows = data.rows.map((row) => {
22643
+ const jsonRow = {};
22644
+ for (let i = 0; i < data.jsonKeys.length; i++) {
22645
+ jsonRow[data.jsonKeys[i]] = row[i] ?? "";
22646
+ }
22647
+ return jsonRow;
22648
+ });
22649
+ const jsonString = JSON.stringify(rows, null, 2);
22650
+ downloadFile(jsonString, `${buildExportFilename(filenamePrefix)}.json`, "application/json");
22651
+ }
22652
+ function downloadFile(content, filename, type) {
22653
+ const blob = new Blob([content], { type });
22654
+ const url = URL.createObjectURL(blob);
22655
+ const link = document.createElement("a");
22656
+ link.href = url;
22657
+ link.download = filename;
22658
+ document.body.appendChild(link);
22659
+ try {
22660
+ link.click();
22661
+ } finally {
22662
+ link.remove();
22663
+ URL.revokeObjectURL(url);
22664
+ }
22665
+ }
22666
+ function buildExportFilename(prefix) {
22667
+ return `${prefix}-${(/* @__PURE__ */ new Date()).toISOString().split("T")[0]}`;
22668
+ }
22669
+ function titleCase(value) {
22670
+ return value.charAt(0).toUpperCase() + value.slice(1);
22671
+ }
22672
+
22673
+ // src/components/data-grid/data-grid-toolbar.tsx
22674
+ var import_react33 = __toESM(require_react());
22167
22675
  function usePopover() {
22168
- const [open, setOpen] = (0, import_react31.useState)(false);
22169
- const ref = (0, import_react31.useRef)(null);
22170
- import_react31.default.useEffect(() => {
22676
+ const [open, setOpen] = (0, import_react33.useState)(false);
22677
+ const ref = (0, import_react33.useRef)(null);
22678
+ import_react33.default.useEffect(() => {
22171
22679
  if (!open) return;
22172
22680
  const handler = (e40) => {
22173
22681
  if (ref.current && !ref.current.contains(e40.target)) {
@@ -22177,7 +22685,7 @@ ${colorConfig.map(([key, itemConfig]) => {
22177
22685
  document.addEventListener("mousedown", handler);
22178
22686
  return () => document.removeEventListener("mousedown", handler);
22179
22687
  }, [open]);
22180
- import_react31.default.useEffect(() => {
22688
+ import_react33.default.useEffect(() => {
22181
22689
  if (!open) return;
22182
22690
  const handler = (e40) => {
22183
22691
  if (e40.key === "Escape") setOpen(false);
@@ -22274,7 +22782,7 @@ ${colorConfig.map(([key, itemConfig]) => {
22274
22782
  onDateDisplayChange,
22275
22783
  hasDateColumns
22276
22784
  }) {
22277
- const hideableColumns = (0, import_react31.useMemo)(
22785
+ const hideableColumns = (0, import_react33.useMemo)(
22278
22786
  () => columns.filter((c4) => c4.hideable !== false),
22279
22787
  [columns]
22280
22788
  );
@@ -22322,255 +22830,108 @@ ${colorConfig.map(([key, itemConfig]) => {
22322
22830
  /* @__PURE__ */ jsx("span", { className: "text-[10px] font-medium uppercase tracking-wider text-muted-foreground", children: strings.dateFormat }),
22323
22831
  /* @__PURE__ */ jsxs("div", { className: "inline-flex items-center gap-0.5 rounded-lg bg-zinc-100/90 p-0.5 ring-1 ring-black/[0.06] dark:bg-foreground/[0.04] dark:ring-white/[0.06]", children: [
22324
22832
  /* @__PURE__ */ jsx(
22325
- "button",
22326
- {
22327
- className: cn(
22328
- "px-2 py-0.5 rounded-md text-[11px] font-medium transition-colors duration-150 hover:transition-none",
22329
- dateDisplay === "relative" ? "bg-white text-foreground shadow-sm ring-1 ring-black/[0.12] dark:bg-background dark:ring-white/[0.06]" : "text-muted-foreground hover:text-foreground hover:bg-white/50 dark:hover:bg-white/[0.06]"
22330
- ),
22331
- onClick: () => onDateDisplayChange("relative"),
22332
- children: strings.dateFormatRelative
22333
- }
22334
- ),
22335
- /* @__PURE__ */ jsx(
22336
- "button",
22337
- {
22338
- className: cn(
22339
- "px-2 py-0.5 rounded-md text-[11px] font-medium transition-colors duration-150 hover:transition-none",
22340
- dateDisplay === "absolute" ? "bg-white text-foreground shadow-sm ring-1 ring-black/[0.12] dark:bg-background dark:ring-white/[0.06]" : "text-muted-foreground hover:text-foreground hover:bg-white/50 dark:hover:bg-white/[0.06]"
22341
- ),
22342
- onClick: () => onDateDisplayChange("absolute"),
22343
- children: strings.dateFormatAbsolute
22344
- }
22345
- )
22346
- ] })
22347
- ] }) })
22348
- ] });
22349
- }
22350
- function DataGridToolbar({
22351
- ctx,
22352
- extra,
22353
- extraLeading,
22354
- hideQuickSearch
22355
- }) {
22356
- const { state, onChange, columns, strings, exportCsv } = ctx;
22357
- const columnPopover = usePopover();
22358
- const updateVisibility = (0, import_react31.useCallback)(
22359
- (visibility) => {
22360
- onChange((s8) => ({ ...s8, columnVisibility: visibility }));
22361
- },
22362
- [onChange]
22363
- );
22364
- const updateDateDisplay = (0, import_react31.useCallback)(
22365
- (mode) => {
22366
- onChange((s8) => ({ ...s8, dateDisplay: mode }));
22367
- },
22368
- [onChange]
22369
- );
22370
- const updateQuickSearch = (0, import_react31.useCallback)(
22371
- (value) => {
22372
- onChange((s8) => ({
22373
- ...s8,
22374
- quickSearch: value,
22375
- // Reset to first page whenever the search text changes,
22376
- // otherwise you can end up on a page index that no longer
22377
- // exists in the filtered / refetched result set.
22378
- pagination: { ...s8.pagination, pageIndex: 0 }
22379
- }));
22380
- },
22381
- [onChange]
22382
- );
22383
- const hasDateColumns = (0, import_react31.useMemo)(
22384
- () => columns.some((c4) => c4.type === "date" || c4.type === "dateTime"),
22385
- [columns]
22386
- );
22387
- return /* @__PURE__ */ jsxs("div", { className: "flex w-full min-w-0 flex-col gap-2 px-2.5 py-2.5 border-b border-foreground/[0.06] sm:flex-row sm:items-center sm:gap-2", children: [
22388
- /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-wrap items-center gap-2", children: [
22389
- !hideQuickSearch && /* @__PURE__ */ jsx(
22390
- QuickSearch,
22391
- {
22392
- value: state.quickSearch,
22393
- onChange: updateQuickSearch,
22394
- placeholder: strings.searchPlaceholder
22395
- }
22396
- ),
22397
- extraLeading,
22398
- extra
22399
- ] }),
22400
- /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center justify-end gap-2", children: [
22401
- /* @__PURE__ */ jsxs("div", { className: "relative shrink-0", ref: columnPopover.ref, children: [
22402
- /* @__PURE__ */ jsx(
22403
- ToolbarButton,
22404
- {
22405
- onClick: () => columnPopover.setOpen(!columnPopover.open),
22406
- active: columnPopover.open,
22407
- title: strings.columns,
22408
- children: /* @__PURE__ */ jsx(n3, { className: "h-3.5 w-3.5" })
22409
- }
22410
- ),
22411
- columnPopover.open && /* @__PURE__ */ jsx(PopoverPanel, { popoverRef: columnPopover.ref, className: "right-0 left-auto", children: /* @__PURE__ */ jsx(
22412
- ColumnManager,
22413
- {
22414
- columns,
22415
- visibility: state.columnVisibility,
22416
- onChange: updateVisibility,
22417
- strings,
22418
- dateDisplay: state.dateDisplay,
22419
- onDateDisplayChange: updateDateDisplay,
22420
- hasDateColumns
22421
- }
22422
- ) })
22423
- ] }),
22424
- /* @__PURE__ */ jsx(ToolbarButton, { onClick: exportCsv, title: strings.export, children: /* @__PURE__ */ jsx(l2, { className: "h-3.5 w-3.5" }) })
22425
- ] })
22426
- ] });
22427
- }
22428
-
22429
- // src/components/data-grid/state.ts
22430
- function createDefaultDataGridState(columns) {
22431
- const columnWidths = {};
22432
- const columnOrder = [];
22433
- for (const col of columns) {
22434
- columnWidths[col.id] = clampColumnWidth(col, col.width ?? DEFAULT_COL_WIDTH);
22435
- columnOrder.push(col.id);
22436
- }
22437
- return {
22438
- sorting: [],
22439
- columnVisibility: {},
22440
- columnWidths,
22441
- columnPinning: { left: [], right: [] },
22442
- columnOrder,
22443
- pagination: { pageIndex: 0, pageSize: 50 },
22444
- selection: { selectedIds: /* @__PURE__ */ new Set(), anchorId: null },
22445
- dateDisplay: "relative",
22446
- quickSearch: ""
22447
- };
22448
- }
22449
- function resolveColumnValue(col, row) {
22450
- if (typeof col.accessor === "function") return col.accessor(row);
22451
- const key = col.accessor ?? col.id;
22452
- return row[key];
22453
- }
22454
- function defaultComparator(a26, b) {
22455
- if (a26 == null && b == null) return 0;
22456
- if (a26 == null) return -1;
22457
- if (b == null) return 1;
22458
- if (typeof a26 === "number" && typeof b === "number") return a26 - b;
22459
- if (a26 instanceof Date && b instanceof Date) return a26.getTime() - b.getTime();
22460
- return stringCompare(String(a26), String(b));
22461
- }
22462
- function buildRowComparator(sortModel, columns) {
22463
- if (sortModel.length === 0) return null;
22464
- const colMap = new Map(columns.map((c4) => [c4.id, c4]));
22465
- return (a26, b) => {
22466
- for (const { columnId, direction } of sortModel) {
22467
- const col = colMap.get(columnId);
22468
- if (!col) continue;
22469
- const va = resolveColumnValue(col, a26);
22470
- const vb = resolveColumnValue(col, b);
22471
- const cmp = col.sortComparator ? col.sortComparator(va, vb) : defaultComparator(va, vb);
22472
- if (cmp !== 0) return direction === "asc" ? cmp : -cmp;
22473
- }
22474
- return 0;
22475
- };
22476
- }
22477
- function paginateRows(rows, pagination) {
22478
- const start = pagination.pageIndex * pagination.pageSize;
22479
- return rows.slice(start, start + pagination.pageSize);
22480
- }
22481
- function defaultMatchRow(row, query, columns) {
22482
- for (const col of columns) {
22483
- const v = resolveColumnValue(col, row);
22484
- if (v == null) continue;
22485
- if (String(v).toLowerCase().includes(query)) return true;
22486
- }
22487
- return false;
22488
- }
22489
- function applyQuickSearch(rows, query, columns, matchRow = defaultMatchRow) {
22490
- const trimmed = query.trim().toLowerCase();
22491
- if (!trimmed) return rows;
22492
- return rows.filter((r7) => matchRow(r7, trimmed, columns));
22493
- }
22494
- function defaultParseDate(value) {
22495
- if (value == null) return null;
22496
- if (value instanceof Date) return isNaN(value.getTime()) ? null : value;
22497
- if (typeof value === "number" || typeof value === "string") {
22498
- const d = new Date(value);
22499
- return isNaN(d.getTime()) ? null : d;
22500
- }
22501
- return null;
22502
- }
22503
- var DIVISIONS = [
22504
- { amount: 60, unit: "second" },
22505
- { amount: 60, unit: "minute" },
22506
- { amount: 24, unit: "hour" },
22507
- { amount: 7, unit: "day" },
22508
- { amount: 4.34524, unit: "week" },
22509
- { amount: 12, unit: "month" },
22510
- { amount: Number.POSITIVE_INFINITY, unit: "year" }
22511
- ];
22512
- var relativeTimeFormatterCache = /* @__PURE__ */ new Map();
22513
- function getRelativeTimeFormatter(locale2) {
22514
- const key = locale2 ?? "__default__";
22515
- let cached = relativeTimeFormatterCache.get(key);
22516
- if (cached == null) {
22517
- cached = new Intl.RelativeTimeFormat(locale2, { numeric: "auto" });
22518
- relativeTimeFormatterCache.set(key, cached);
22519
- }
22520
- return cached;
22521
- }
22522
- function defaultFormatRelative(date2) {
22523
- const rtf = getRelativeTimeFormatter();
22524
- let duration = (date2.getTime() - Date.now()) / 1e3;
22525
- for (const div of DIVISIONS) {
22526
- if (Math.abs(duration) < div.amount) return rtf.format(Math.round(duration), div.unit);
22527
- duration /= div.amount;
22528
- }
22529
- return rtf.format(Math.round(duration), "year");
22530
- }
22531
- function defaultFormatAbsolute(date2) {
22532
- return date2.toLocaleString();
22533
- }
22534
- function formatGridDate(value, mode, opts) {
22535
- const parse2 = opts?.parseValue ?? defaultParseDate;
22536
- const date2 = parse2(value);
22537
- if (!date2) return { display: null, tooltip: null };
22538
- const relative = opts?.dateFormat?.relative ?? defaultFormatRelative;
22539
- const absolute = opts?.dateFormat?.absolute ?? defaultFormatAbsolute;
22540
- const tooltip = absolute(date2);
22541
- const display = mode === "relative" ? relative(date2) : tooltip;
22542
- return { display, tooltip };
22833
+ "button",
22834
+ {
22835
+ className: cn(
22836
+ "px-2 py-0.5 rounded-md text-[11px] font-medium transition-colors duration-150 hover:transition-none",
22837
+ dateDisplay === "relative" ? "bg-white text-foreground shadow-sm ring-1 ring-black/[0.12] dark:bg-background dark:ring-white/[0.06]" : "text-muted-foreground hover:text-foreground hover:bg-white/50 dark:hover:bg-white/[0.06]"
22838
+ ),
22839
+ onClick: () => onDateDisplayChange("relative"),
22840
+ children: strings.dateFormatRelative
22841
+ }
22842
+ ),
22843
+ /* @__PURE__ */ jsx(
22844
+ "button",
22845
+ {
22846
+ className: cn(
22847
+ "px-2 py-0.5 rounded-md text-[11px] font-medium transition-colors duration-150 hover:transition-none",
22848
+ dateDisplay === "absolute" ? "bg-white text-foreground shadow-sm ring-1 ring-black/[0.12] dark:bg-background dark:ring-white/[0.06]" : "text-muted-foreground hover:text-foreground hover:bg-white/50 dark:hover:bg-white/[0.06]"
22849
+ ),
22850
+ onClick: () => onDateDisplayChange("absolute"),
22851
+ children: strings.dateFormatAbsolute
22852
+ }
22853
+ )
22854
+ ] })
22855
+ ] }) })
22856
+ ] });
22543
22857
  }
22544
- function exportToCsv(rows, columns, filename) {
22545
- const header = columns.map(
22546
- (col) => typeof col.header === "string" ? col.header : col.id
22858
+ function DataGridToolbar({
22859
+ ctx,
22860
+ extra,
22861
+ extraLeading,
22862
+ hideQuickSearch
22863
+ }) {
22864
+ const { state, onChange, columns, strings, exportCsv } = ctx;
22865
+ const columnPopover = usePopover();
22866
+ const updateVisibility = (0, import_react33.useCallback)(
22867
+ (visibility) => {
22868
+ onChange((s8) => ({ ...s8, columnVisibility: visibility }));
22869
+ },
22870
+ [onChange]
22547
22871
  );
22548
- const csvRows = rows.map(
22549
- (row) => columns.map((col) => {
22550
- const val = resolveColumnValue(col, row);
22551
- const formatted = col.formatValue ? String(col.formatValue(val, row) ?? "") : String(val ?? "");
22552
- if (formatted.includes(",") || formatted.includes('"') || formatted.includes("\n")) {
22553
- return `"${formatted.replace(/"/g, '""')}"`;
22554
- }
22555
- return formatted;
22556
- })
22872
+ const updateDateDisplay = (0, import_react33.useCallback)(
22873
+ (mode) => {
22874
+ onChange((s8) => ({ ...s8, dateDisplay: mode }));
22875
+ },
22876
+ [onChange]
22557
22877
  );
22558
- const csvContent = "\uFEFF" + [
22559
- header.join(","),
22560
- ...csvRows.map((row) => row.join(","))
22561
- ].join("\n");
22562
- const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" });
22563
- const url = URL.createObjectURL(blob);
22564
- const link = document.createElement("a");
22565
- link.href = url;
22566
- link.download = `${filename}.csv`;
22567
- document.body.appendChild(link);
22568
- try {
22569
- link.click();
22570
- } finally {
22571
- link.remove();
22572
- URL.revokeObjectURL(url);
22573
- }
22878
+ const updateQuickSearch = (0, import_react33.useCallback)(
22879
+ (value) => {
22880
+ onChange((s8) => ({
22881
+ ...s8,
22882
+ quickSearch: value,
22883
+ // Reset to first page whenever the search text changes,
22884
+ // otherwise you can end up on a page index that no longer
22885
+ // exists in the filtered / refetched result set.
22886
+ pagination: { ...s8.pagination, pageIndex: 0 }
22887
+ }));
22888
+ },
22889
+ [onChange]
22890
+ );
22891
+ const hasDateColumns = (0, import_react33.useMemo)(
22892
+ () => columns.some((c4) => c4.type === "date" || c4.type === "dateTime"),
22893
+ [columns]
22894
+ );
22895
+ return /* @__PURE__ */ jsxs("div", { className: "flex w-full min-w-0 flex-col gap-2 px-2.5 py-2.5 border-b border-foreground/[0.06] sm:flex-row sm:items-center sm:gap-2", children: [
22896
+ /* @__PURE__ */ jsxs("div", { className: "flex min-w-0 flex-1 flex-wrap items-center gap-2", children: [
22897
+ !hideQuickSearch && /* @__PURE__ */ jsx(
22898
+ QuickSearch,
22899
+ {
22900
+ value: state.quickSearch,
22901
+ onChange: updateQuickSearch,
22902
+ placeholder: strings.searchPlaceholder
22903
+ }
22904
+ ),
22905
+ extraLeading,
22906
+ extra
22907
+ ] }),
22908
+ /* @__PURE__ */ jsxs("div", { className: "flex shrink-0 items-center justify-end gap-2", children: [
22909
+ /* @__PURE__ */ jsxs("div", { className: "relative shrink-0", ref: columnPopover.ref, children: [
22910
+ /* @__PURE__ */ jsx(
22911
+ ToolbarButton,
22912
+ {
22913
+ onClick: () => columnPopover.setOpen(!columnPopover.open),
22914
+ active: columnPopover.open,
22915
+ title: strings.columns,
22916
+ children: /* @__PURE__ */ jsx(n3, { className: "h-3.5 w-3.5" })
22917
+ }
22918
+ ),
22919
+ columnPopover.open && /* @__PURE__ */ jsx(PopoverPanel, { popoverRef: columnPopover.ref, className: "right-0 left-auto", children: /* @__PURE__ */ jsx(
22920
+ ColumnManager,
22921
+ {
22922
+ columns,
22923
+ visibility: state.columnVisibility,
22924
+ onChange: updateVisibility,
22925
+ strings,
22926
+ dateDisplay: state.dateDisplay,
22927
+ onDateDisplayChange: updateDateDisplay,
22928
+ hasDateColumns
22929
+ }
22930
+ ) })
22931
+ ] }),
22932
+ /* @__PURE__ */ jsx(ToolbarButton, { onClick: exportCsv, title: strings.export, children: /* @__PURE__ */ jsx(l2, { className: "h-3.5 w-3.5" }) })
22933
+ ] })
22934
+ ] });
22574
22935
  }
22575
22936
 
22576
22937
  // src/components/data-grid/strings.ts
@@ -22896,8 +23257,8 @@ ${colorConfig.map(([key, itemConfig]) => {
22896
23257
  rootRef,
22897
23258
  strings
22898
23259
  }) {
22899
- const ref = (0, import_react33.useRef)(null);
22900
- (0, import_react33.useEffect)(() => {
23260
+ const ref = (0, import_react35.useRef)(null);
23261
+ (0, import_react35.useEffect)(() => {
22901
23262
  const el = ref.current;
22902
23263
  if (!el) return;
22903
23264
  const observer = new IntersectionObserver(
@@ -23015,6 +23376,7 @@ ${colorConfig.map(([key, itemConfig]) => {
23015
23376
  footer,
23016
23377
  footerExtra,
23017
23378
  exportFilename = "export",
23379
+ exportOptions,
23018
23380
  strings: stringsOverride,
23019
23381
  className,
23020
23382
  onRowClick,
@@ -23027,8 +23389,8 @@ ${colorConfig.map(([key, itemConfig]) => {
23027
23389
  const isDynamicRowHeight = rowHeightProp === "auto";
23028
23390
  const fixedRowHeight = isDynamicRowHeight ? void 0 : rowHeightProp;
23029
23391
  const estimatedRowHeight = estimatedRowHeightProp ?? (fixedRowHeight ?? 44);
23030
- const strings = (0, import_react33.useMemo)(() => resolveDataGridStrings(stringsOverride), [stringsOverride]);
23031
- const tableColumns = (0, import_react33.useMemo)(
23392
+ const strings = (0, import_react35.useMemo)(() => resolveDataGridStrings(stringsOverride), [stringsOverride]);
23393
+ const tableColumns = (0, import_react35.useMemo)(
23032
23394
  () => allColumns.map((col) => ({
23033
23395
  id: col.id,
23034
23396
  accessorFn: (row) => resolveColumnValue(col, row),
@@ -23043,22 +23405,22 @@ ${colorConfig.map(([key, itemConfig]) => {
23043
23405
  })),
23044
23406
  [allColumns]
23045
23407
  );
23046
- const tanstackSorting = (0, import_react33.useMemo)(() => toTanstackSorting(state.sorting), [state.sorting]);
23047
- const tanstackRowSelection = (0, import_react33.useMemo)(
23408
+ const tanstackSorting = (0, import_react35.useMemo)(() => toTanstackSorting(state.sorting), [state.sorting]);
23409
+ const tanstackRowSelection = (0, import_react35.useMemo)(
23048
23410
  () => toTanstackRowSelection(state.selection.selectedIds),
23049
23411
  [state.selection.selectedIds]
23050
23412
  );
23051
- const tanstackColumnPinning = (0, import_react33.useMemo)(
23413
+ const tanstackColumnPinning = (0, import_react35.useMemo)(
23052
23414
  () => ({ left: [...state.columnPinning.left], right: [...state.columnPinning.right] }),
23053
23415
  [state.columnPinning]
23054
23416
  );
23055
- const tanstackColumnOrder = (0, import_react33.useMemo)(
23417
+ const tanstackColumnOrder = (0, import_react35.useMemo)(
23056
23418
  () => [...state.columnOrder],
23057
23419
  [state.columnOrder]
23058
23420
  );
23059
- const allColumnsRef = (0, import_react33.useRef)(allColumns);
23421
+ const allColumnsRef = (0, import_react35.useRef)(allColumns);
23060
23422
  allColumnsRef.current = allColumns;
23061
- const handleSortingChange = (0, import_react33.useCallback)(
23423
+ const handleSortingChange = (0, import_react35.useCallback)(
23062
23424
  (updater) => {
23063
23425
  const next = resolveUpdater(updater, toTanstackSorting(state.sorting));
23064
23426
  const ours = fromTanstackSorting(next).map((s8) => ({ ...s8 }));
@@ -23071,7 +23433,7 @@ ${colorConfig.map(([key, itemConfig]) => {
23071
23433
  },
23072
23434
  [onChange, onSortChange, state.sorting]
23073
23435
  );
23074
- const handleColumnSizingChange = (0, import_react33.useCallback)(
23436
+ const handleColumnSizingChange = (0, import_react35.useCallback)(
23075
23437
  (updater) => {
23076
23438
  const next = resolveUpdater(updater, state.columnWidths);
23077
23439
  const clamped = {};
@@ -23088,7 +23450,7 @@ ${colorConfig.map(([key, itemConfig]) => {
23088
23450
  },
23089
23451
  [onChange, onColumnResize, state.columnWidths]
23090
23452
  );
23091
- const handleVisibilityChange = (0, import_react33.useCallback)(
23453
+ const handleVisibilityChange = (0, import_react35.useCallback)(
23092
23454
  (updater) => {
23093
23455
  const next = resolveUpdater(updater, state.columnVisibility);
23094
23456
  onChange((s8) => ({ ...s8, columnVisibility: next }));
@@ -23096,14 +23458,14 @@ ${colorConfig.map(([key, itemConfig]) => {
23096
23458
  },
23097
23459
  [onChange, onColumnVisibilityChange, state.columnVisibility]
23098
23460
  );
23099
- const handleColumnOrderChange = (0, import_react33.useCallback)(
23461
+ const handleColumnOrderChange = (0, import_react35.useCallback)(
23100
23462
  (updater) => {
23101
23463
  const next = resolveUpdater(updater, [...state.columnOrder]);
23102
23464
  onChange((s8) => ({ ...s8, columnOrder: next }));
23103
23465
  },
23104
23466
  [onChange, state.columnOrder]
23105
23467
  );
23106
- const handleColumnPinningChange = (0, import_react33.useCallback)(
23468
+ const handleColumnPinningChange = (0, import_react35.useCallback)(
23107
23469
  (updater) => {
23108
23470
  const current = {
23109
23471
  left: [...state.columnPinning.left],
@@ -23143,13 +23505,13 @@ ${colorConfig.map(([key, itemConfig]) => {
23143
23505
  manualPagination: true,
23144
23506
  manualFiltering: true
23145
23507
  });
23146
- const visibleColumns = (0, import_react33.useMemo)(() => {
23508
+ const visibleColumns = (0, import_react35.useMemo)(() => {
23147
23509
  const colMap = new Map(allColumns.map((c4) => [c4.id, c4]));
23148
23510
  return table.getVisibleLeafColumns().map((c4) => colMap.get(c4.id)).filter(Boolean);
23149
23511
  }, [allColumns, table, state.columnOrder, state.columnVisibility]);
23150
- const rowIds = (0, import_react33.useMemo)(() => rows.map(getRowId), [rows, getRowId]);
23151
- const [containerWidth, setContainerWidth] = (0, import_react33.useState)(0);
23152
- (0, import_react33.useLayoutEffect)(() => {
23512
+ const rowIds = (0, import_react35.useMemo)(() => rows.map(getRowId), [rows, getRowId]);
23513
+ const [containerWidth, setContainerWidth] = (0, import_react35.useState)(0);
23514
+ (0, import_react35.useLayoutEffect)(() => {
23153
23515
  const grid = gridRef.current;
23154
23516
  const scroller = scrollContainerRef.current;
23155
23517
  if (!grid) return;
@@ -23174,7 +23536,7 @@ ${colorConfig.map(([key, itemConfig]) => {
23174
23536
  return () => observer.disconnect();
23175
23537
  }, []);
23176
23538
  const columnSizingInfo = table.getState().columnSizingInfo;
23177
- const columnSizes = (0, import_react33.useMemo)(() => {
23539
+ const columnSizes = (0, import_react35.useMemo)(() => {
23178
23540
  const sizes = {};
23179
23541
  let baseTotal = selectionMode !== "none" ? 44 : 0;
23180
23542
  const resizingId = columnSizingInfo.isResizingColumn || null;
@@ -23189,19 +23551,19 @@ ${colorConfig.map(([key, itemConfig]) => {
23189
23551
  distributeFlexWidths(sizes, visibleColumns, containerWidth - baseTotal);
23190
23552
  return sizes;
23191
23553
  }, [visibleColumns, table, columnSizingInfo, state.columnWidths, containerWidth, selectionMode]);
23192
- const totalContentWidth = (0, import_react33.useMemo)(() => {
23554
+ const totalContentWidth = (0, import_react35.useMemo)(() => {
23193
23555
  let total = selectionMode !== "none" ? 44 : 0;
23194
23556
  for (const col of visibleColumns) total += columnSizes[col.id] ?? 0;
23195
23557
  return total;
23196
23558
  }, [visibleColumns, columnSizes, selectionMode]);
23197
- const cssVars = (0, import_react33.useMemo)(() => {
23559
+ const cssVars = (0, import_react35.useMemo)(() => {
23198
23560
  const vars = { "--grid-total-w": `${totalContentWidth}px` };
23199
23561
  for (const col of visibleColumns) {
23200
23562
  vars[`--col-${col.id}-size`] = columnSizes[col.id];
23201
23563
  }
23202
23564
  return vars;
23203
23565
  }, [visibleColumns, columnSizes, totalContentWidth]);
23204
- const fireSelection = (0, import_react33.useCallback)(
23566
+ const fireSelection = (0, import_react35.useCallback)(
23205
23567
  (next) => {
23206
23568
  onChange((s8) => ({ ...s8, selection: next }));
23207
23569
  if (onSelectionChange) {
@@ -23212,7 +23574,7 @@ ${colorConfig.map(([key, itemConfig]) => {
23212
23574
  },
23213
23575
  [onChange, onSelectionChange, rows, getRowId]
23214
23576
  );
23215
- const handleRowClick = (0, import_react33.useCallback)(
23577
+ const handleRowClick = (0, import_react35.useCallback)(
23216
23578
  (row, rowId, event) => {
23217
23579
  if (selectionMode !== "none") {
23218
23580
  const next = nextSelection({
@@ -23228,28 +23590,22 @@ ${colorConfig.map(([key, itemConfig]) => {
23228
23590
  },
23229
23591
  [selectionMode, state.selection, rowIds, fireSelection, onRowClick]
23230
23592
  );
23231
- const handleSelectAll = (0, import_react33.useCallback)(() => {
23593
+ const handleSelectAll = (0, import_react35.useCallback)(() => {
23232
23594
  const allSelectedNow = rowIds.length > 0 && rowIds.every((id) => state.selection.selectedIds.has(id));
23233
23595
  fireSelection(
23234
23596
  allSelectedNow ? { selectedIds: /* @__PURE__ */ new Set(), anchorId: null } : { selectedIds: new Set(rowIds), anchorId: null }
23235
23597
  );
23236
23598
  }, [rowIds, state.selection.selectedIds, fireSelection]);
23237
- const handleExportCsv = (0, import_react33.useCallback)(() => {
23238
- if (typeof window !== "undefined" && rows.length > 0) {
23239
- const totalSuffix = totalRowCount != null && totalRowCount > rows.length ? ` of ${totalRowCount} total \u2014 load more rows first to include them` : "";
23240
- const confirmed = window.confirm(
23241
- `Export ${rows.length.toLocaleString()} loaded row${rows.length === 1 ? "" : "s"}${totalSuffix}?`
23242
- );
23243
- if (!confirmed) return;
23244
- }
23245
- exportToCsv(rows, visibleColumns, exportFilename);
23246
- }, [rows, visibleColumns, exportFilename, totalRowCount]);
23247
- const scrollContainerRef = (0, import_react33.useRef)(null);
23248
- const headerScrollRef = (0, import_react33.useRef)(null);
23249
- const stickyChromeRef = (0, import_react33.useRef)(null);
23250
- const rowsClipRef = (0, import_react33.useRef)(null);
23251
- const gridRef = (0, import_react33.useRef)(null);
23252
- const measureElementFn = (0, import_react33.useCallback)((el) => el.getBoundingClientRect().height, []);
23599
+ const [exportDialogOpen, setExportDialogOpen] = (0, import_react35.useState)(false);
23600
+ const handleExportCsv = (0, import_react35.useCallback)(() => {
23601
+ setExportDialogOpen(true);
23602
+ }, []);
23603
+ const scrollContainerRef = (0, import_react35.useRef)(null);
23604
+ const headerScrollRef = (0, import_react35.useRef)(null);
23605
+ const stickyChromeRef = (0, import_react35.useRef)(null);
23606
+ const rowsClipRef = (0, import_react35.useRef)(null);
23607
+ const gridRef = (0, import_react35.useRef)(null);
23608
+ const measureElementFn = (0, import_react35.useCallback)((el) => el.getBoundingClientRect().height, []);
23253
23609
  const rowVirtualizer = useVirtualizer({
23254
23610
  count: rows.length,
23255
23611
  getScrollElement: () => scrollContainerRef.current,
@@ -23261,7 +23617,7 @@ ${colorConfig.map(([key, itemConfig]) => {
23261
23617
  },
23262
23618
  ...isDynamicRowHeight ? { measureElement: measureElementFn } : {}
23263
23619
  });
23264
- (0, import_react33.useLayoutEffect)(() => {
23620
+ (0, import_react35.useLayoutEffect)(() => {
23265
23621
  const gridEl = gridRef.current;
23266
23622
  const stickyEl = stickyChromeRef.current;
23267
23623
  const bodyEl = scrollContainerRef.current;
@@ -23288,12 +23644,12 @@ ${colorConfig.map(([key, itemConfig]) => {
23288
23644
  observer.disconnect();
23289
23645
  };
23290
23646
  }, []);
23291
- const handleBodyScroll = (0, import_react33.useCallback)(() => {
23647
+ const handleBodyScroll = (0, import_react35.useCallback)(() => {
23292
23648
  const body = scrollContainerRef.current;
23293
23649
  const header = headerScrollRef.current;
23294
23650
  if (body && header) header.scrollLeft = body.scrollLeft;
23295
23651
  }, []);
23296
- const toolbarCtx = (0, import_react33.useMemo)(
23652
+ const toolbarCtx = (0, import_react35.useMemo)(
23297
23653
  () => ({
23298
23654
  state,
23299
23655
  onChange,
@@ -23306,7 +23662,7 @@ ${colorConfig.map(([key, itemConfig]) => {
23306
23662
  }),
23307
23663
  [state, onChange, allColumns, visibleColumns, totalRowCount, strings, handleExportCsv]
23308
23664
  );
23309
- const footerCtx = (0, import_react33.useMemo)(
23665
+ const footerCtx = (0, import_react35.useMemo)(
23310
23666
  () => ({
23311
23667
  state,
23312
23668
  totalRowCount,
@@ -23320,238 +23676,251 @@ ${colorConfig.map(([key, itemConfig]) => {
23320
23676
  const allSelected = rowIds.length > 0 && rowIds.every((id) => state.selection.selectedIds.has(id));
23321
23677
  const someSelected = !allSelected && rowIds.some((id) => state.selection.selectedIds.has(id));
23322
23678
  const infiniteScrollRootRef = paginationMode === "infinite" && (fillHeight || maxHeight != null) ? scrollContainerRef : void 0;
23323
- const headers = (0, import_react33.useMemo)(
23679
+ const headers = (0, import_react35.useMemo)(
23324
23680
  () => table.getHeaderGroups()[0]?.headers.filter((h) => visibleColumns.some((c4) => c4.id === h.column.id)) ?? [],
23325
23681
  [table, visibleColumns]
23326
23682
  );
23327
- const headerByColId = (0, import_react33.useMemo)(() => {
23683
+ const headerByColId = (0, import_react35.useMemo)(() => {
23328
23684
  const m5 = /* @__PURE__ */ new Map();
23329
23685
  for (const h of headers) m5.set(h.column.id, h);
23330
23686
  return m5;
23331
23687
  }, [headers]);
23332
23688
  const isBounded = fillHeight || maxHeight != null;
23333
- return /* @__PURE__ */ jsxs(
23334
- "div",
23335
- {
23336
- ref: gridRef,
23337
- className: cn(
23338
- "isolate flex w-full min-w-0 max-w-full flex-col bg-transparent rounded-[calc(var(--radius)*2)]",
23339
- fillHeight ? "min-h-0 h-full" : "min-h-0 h-auto",
23340
- isBounded && "overflow-hidden",
23341
- className
23342
- ),
23343
- style: maxHeight != null ? { ...cssVars, maxHeight } : cssVars,
23344
- role: "grid",
23345
- "aria-rowcount": totalRowCount ?? rows.length,
23346
- "aria-colcount": visibleColumns.length,
23347
- children: [
23348
- /* @__PURE__ */ jsxs(
23349
- "div",
23350
- {
23351
- ref: stickyChromeRef,
23352
- className: "sticky z-30 w-full min-w-0 shrink-0 overflow-visible rounded-t-[calc(var(--radius)*2)] bg-white/90 dark:bg-background/60 backdrop-blur-xl",
23353
- style: { top: stickyTop ?? (maxHeight != null ? 0 : "var(--data-grid-sticky-top, 0px)") },
23354
- children: [
23355
- toolbar !== false && /* @__PURE__ */ jsx("div", { className: "relative bg-transparent", children: toolbar ? toolbar(toolbarCtx) : /* @__PURE__ */ jsx(
23356
- DataGridToolbar,
23357
- {
23358
- ctx: toolbarCtx,
23359
- extra: typeof toolbarExtra === "function" ? toolbarExtra(toolbarCtx) : toolbarExtra
23360
- }
23361
- ) }),
23362
- /* @__PURE__ */ jsxs("div", { className: "relative", children: [
23363
- isRefetching && /* @__PURE__ */ jsx("div", { className: "absolute top-0 left-0 right-0 h-0.5 z-30 bg-foreground/[0.04] overflow-hidden", children: /* @__PURE__ */ jsx("div", { className: "h-full w-1/3 bg-blue-500/60 rounded-full animate-pulse" }) }),
23364
- /* @__PURE__ */ jsx(
23365
- "div",
23689
+ return /* @__PURE__ */ jsxs(Fragment24, { children: [
23690
+ /* @__PURE__ */ jsx(
23691
+ DataGridExportDialog,
23692
+ {
23693
+ open: exportDialogOpen,
23694
+ onOpenChange: setExportDialogOpen,
23695
+ rows,
23696
+ columns: visibleColumns,
23697
+ exportFilename,
23698
+ exportOptions
23699
+ }
23700
+ ),
23701
+ /* @__PURE__ */ jsxs(
23702
+ "div",
23703
+ {
23704
+ ref: gridRef,
23705
+ className: cn(
23706
+ "isolate flex w-full min-w-0 max-w-full flex-col bg-transparent rounded-[calc(var(--radius)*2)]",
23707
+ fillHeight ? "min-h-0 h-full" : "min-h-0 h-auto",
23708
+ isBounded && "overflow-hidden",
23709
+ className
23710
+ ),
23711
+ style: maxHeight != null ? { ...cssVars, maxHeight } : cssVars,
23712
+ role: "grid",
23713
+ "aria-rowcount": totalRowCount ?? rows.length,
23714
+ "aria-colcount": visibleColumns.length,
23715
+ children: [
23716
+ /* @__PURE__ */ jsxs(
23717
+ "div",
23718
+ {
23719
+ ref: stickyChromeRef,
23720
+ className: "sticky z-30 w-full min-w-0 shrink-0 overflow-visible rounded-t-[calc(var(--radius)*2)] bg-white/90 dark:bg-background/60 backdrop-blur-xl",
23721
+ style: { top: stickyTop ?? (maxHeight != null ? 0 : "var(--data-grid-sticky-top, 0px)") },
23722
+ children: [
23723
+ toolbar !== false && /* @__PURE__ */ jsx("div", { className: "relative bg-transparent", children: toolbar ? toolbar(toolbarCtx) : /* @__PURE__ */ jsx(
23724
+ DataGridToolbar,
23366
23725
  {
23367
- ref: headerScrollRef,
23368
- className: "w-full min-w-0 shrink-0 overflow-hidden border-b border-foreground/[0.06]",
23369
- children: /* @__PURE__ */ jsxs(
23370
- "div",
23371
- {
23372
- className: "flex",
23373
- style: { height: headerHeight, minWidth: totalContentWidth },
23374
- role: "row",
23375
- children: [
23376
- selectionMode !== "none" && /* @__PURE__ */ jsx(
23377
- "div",
23378
- {
23379
- className: "flex items-center justify-center border-r border-foreground/[0.04]",
23380
- style: { width: 44 },
23381
- children: selectionMode === "multiple" && /* @__PURE__ */ jsx(
23382
- SelectionCheckbox,
23383
- {
23384
- checked: allSelected,
23385
- indeterminate: someSelected,
23386
- onChange: handleSelectAll,
23387
- ariaLabel: "Select all rows on this page",
23388
- title: "Select all rows on this page"
23389
- }
23390
- )
23391
- }
23392
- ),
23393
- visibleColumns.map((col) => {
23394
- const header = headerByColId.get(col.id);
23395
- if (!header) return null;
23396
- return /* @__PURE__ */ jsx(HeaderCell, { header, col, resizable }, col.id);
23397
- })
23398
- ]
23399
- }
23400
- )
23726
+ ctx: toolbarCtx,
23727
+ extra: typeof toolbarExtra === "function" ? toolbarExtra(toolbarCtx) : toolbarExtra
23401
23728
  }
23402
- )
23403
- ] })
23404
- ]
23405
- }
23406
- ),
23407
- /* @__PURE__ */ jsx(
23408
- "div",
23409
- {
23410
- ref: scrollContainerRef,
23411
- className: cn(
23412
- "relative z-0 w-full min-w-0 overflow-auto bg-transparent",
23413
- isBounded ? "min-h-0 flex-1" : "flex-none",
23414
- "[&::-webkit-scrollbar]:w-1.5 [&::-webkit-scrollbar]:h-1.5",
23415
- "[&::-webkit-scrollbar-track]:bg-transparent",
23416
- "[&::-webkit-scrollbar-thumb]:bg-foreground/[0.08] [&::-webkit-scrollbar-thumb]:rounded-full",
23417
- "[&::-webkit-scrollbar-thumb]:hover:bg-foreground/[0.15]"
23418
- ),
23419
- onScroll: handleBodyScroll,
23420
- children: /* @__PURE__ */ jsxs(
23421
- "div",
23422
- {
23423
- ref: rowsClipRef,
23424
- "data-data-grid-rows-clip": "",
23425
- className: "relative z-0",
23426
- style: {
23427
- minWidth: totalContentWidth,
23428
- clipPath: "inset(var(--data-grid-sticky-overlap, 0px) 0 0 0)"
23429
- },
23430
- children: [
23431
- isLoading && /* @__PURE__ */ jsx("div", { style: { minWidth: totalContentWidth }, children: loadingState ?? Array.from({ length: 8 }).map((_, i) => /* @__PURE__ */ jsx(
23432
- SkeletonRow,
23433
- {
23434
- columns: visibleColumns,
23435
- height: estimatedRowHeight,
23436
- showCheckbox: selectionMode !== "none"
23437
- },
23438
- i
23439
- )) }),
23440
- !isLoading && rows.length === 0 && /* @__PURE__ */ jsx(
23441
- "div",
23442
- {
23443
- className: "flex items-center justify-center py-16 text-sm text-muted-foreground",
23444
- style: { minWidth: totalContentWidth },
23445
- children: emptyState ?? strings.noData
23446
- }
23447
- ),
23448
- !isLoading && rows.length > 0 && /* @__PURE__ */ jsx(
23729
+ ) }),
23730
+ /* @__PURE__ */ jsxs("div", { className: "relative", children: [
23731
+ isRefetching && /* @__PURE__ */ jsx("div", { className: "absolute top-0 left-0 right-0 h-0.5 z-30 bg-foreground/[0.04] overflow-hidden", children: /* @__PURE__ */ jsx("div", { className: "h-full w-1/3 bg-blue-500/60 rounded-full animate-pulse" }) }),
23732
+ /* @__PURE__ */ jsx(
23449
23733
  "div",
23450
23734
  {
23451
- style: {
23452
- height: rowVirtualizer.getTotalSize(),
23453
- width: "100%",
23454
- minWidth: totalContentWidth,
23455
- position: "relative"
23456
- },
23457
- children: rowVirtualizer.getVirtualItems().map((virtualRow) => {
23458
- const row = rows[virtualRow.index] ?? throwErr(
23459
- `DataGrid: virtualized row index ${virtualRow.index} out of range (rows.length=${rows.length})`
23460
- );
23461
- const rowId = getRowId(row);
23462
- const isSelected = state.selection.selectedIds.has(rowId);
23463
- const isOddRow = virtualRow.index % 2 === 1;
23464
- return /* @__PURE__ */ jsxs(
23465
- "div",
23466
- {
23467
- ref: isDynamicRowHeight ? rowVirtualizer.measureElement : void 0,
23468
- "data-index": virtualRow.index,
23469
- className: cn(
23470
- "absolute left-0 w-full flex",
23471
- "border-b border-black/[0.03] dark:border-white/[0.03]",
23472
- "transition-colors duration-75",
23473
- isSelected ? "bg-blue-500/[0.06] dark:bg-blue-400/[0.08] hover:bg-blue-500/[0.08] dark:hover:bg-blue-400/[0.1]" : isOddRow ? "bg-foreground/[0.02] dark:bg-foreground/[0.03] hover:bg-foreground/[0.04] dark:hover:bg-foreground/[0.06]" : "hover:bg-foreground/[0.025] dark:hover:bg-foreground/[0.04]",
23474
- (selectionMode !== "none" || onRowClick) && "cursor-pointer"
23735
+ ref: headerScrollRef,
23736
+ className: "w-full min-w-0 shrink-0 overflow-hidden border-b border-foreground/[0.06]",
23737
+ children: /* @__PURE__ */ jsxs(
23738
+ "div",
23739
+ {
23740
+ className: "flex",
23741
+ style: { height: headerHeight, minWidth: totalContentWidth },
23742
+ role: "row",
23743
+ children: [
23744
+ selectionMode !== "none" && /* @__PURE__ */ jsx(
23745
+ "div",
23746
+ {
23747
+ className: "flex items-center justify-center border-r border-foreground/[0.04]",
23748
+ style: { width: 44 },
23749
+ children: selectionMode === "multiple" && /* @__PURE__ */ jsx(
23750
+ SelectionCheckbox,
23751
+ {
23752
+ checked: allSelected,
23753
+ indeterminate: someSelected,
23754
+ onChange: handleSelectAll,
23755
+ ariaLabel: "Select all rows on this page",
23756
+ title: "Select all rows on this page"
23757
+ }
23758
+ )
23759
+ }
23475
23760
  ),
23476
- style: {
23477
- ...isDynamicRowHeight ? { minHeight: estimatedRowHeight } : { height: fixedRowHeight },
23478
- transform: `translateY(${virtualRow.start}px)`
23479
- },
23480
- onClick: (e40) => {
23481
- if (!shouldIgnoreRowClick(e40)) handleRowClick(row, rowId, e40);
23482
- },
23483
- onDoubleClick: (e40) => {
23484
- if (!shouldIgnoreRowClick(e40)) onRowDoubleClick?.(row, rowId, e40);
23485
- },
23486
- role: "row",
23487
- "aria-rowindex": virtualRow.index + 2,
23488
- "aria-selected": isSelected,
23489
- "data-row-id": rowId,
23490
- "data-state": isSelected ? "selected" : void 0,
23491
- children: [
23492
- selectionMode !== "none" && /* @__PURE__ */ jsx(
23493
- "div",
23494
- {
23495
- className: "flex items-center justify-center border-r border-black/[0.04] dark:border-white/[0.04]",
23496
- style: { width: 44 },
23497
- children: /* @__PURE__ */ jsx(
23498
- SelectionCheckbox,
23499
- {
23500
- checked: isSelected,
23501
- onChange: (event) => handleRowClick(row, rowId, event),
23502
- ariaLabel: `Select row ${rowId}`
23503
- }
23504
- )
23505
- }
23506
- ),
23507
- visibleColumns.map((col) => /* @__PURE__ */ jsx(
23508
- DataCell,
23509
- {
23510
- col,
23511
- row,
23512
- rowId,
23513
- rowIndex: virtualRow.index,
23514
- isSelected,
23515
- dateDisplay: state.dateDisplay
23516
- },
23517
- col.id
23518
- ))
23519
- ]
23520
- },
23521
- rowId
23522
- );
23523
- })
23524
- }
23525
- ),
23526
- paginationMode === "infinite" && hasMore && !isLoading && /* @__PURE__ */ jsx(
23527
- InfiniteScrollSentinel,
23528
- {
23529
- onIntersect: onLoadMore ?? NOOP,
23530
- isLoading: isLoadingMore,
23531
- rootRef: infiniteScrollRootRef,
23532
- strings
23761
+ visibleColumns.map((col) => {
23762
+ const header = headerByColId.get(col.id);
23763
+ if (!header) return null;
23764
+ return /* @__PURE__ */ jsx(HeaderCell, { header, col, resizable }, col.id);
23765
+ })
23766
+ ]
23767
+ }
23768
+ )
23533
23769
  }
23534
23770
  )
23535
- ]
23536
- }
23537
- )
23538
- }
23539
- ),
23540
- footer !== false && /* @__PURE__ */ jsxs("div", { className: "sticky bottom-0 z-30 shrink-0 overflow-hidden rounded-b-[calc(var(--radius)*2)] bg-white/90 dark:bg-background/60 backdrop-blur-xl", children: [
23541
- footer ? footer(footerCtx) : /* @__PURE__ */ jsx(DefaultFooter, { ctx: footerCtx, pagination: paginationMode, onChange }),
23542
- footerExtra && (typeof footerExtra === "function" ? footerExtra(footerCtx) : footerExtra)
23543
- ] })
23544
- ]
23545
- }
23546
- );
23771
+ ] })
23772
+ ]
23773
+ }
23774
+ ),
23775
+ /* @__PURE__ */ jsx(
23776
+ "div",
23777
+ {
23778
+ ref: scrollContainerRef,
23779
+ className: cn(
23780
+ "relative z-0 w-full min-w-0 overflow-auto bg-transparent",
23781
+ isBounded ? "min-h-0 flex-1" : "flex-none",
23782
+ "[&::-webkit-scrollbar]:w-1.5 [&::-webkit-scrollbar]:h-1.5",
23783
+ "[&::-webkit-scrollbar-track]:bg-transparent",
23784
+ "[&::-webkit-scrollbar-thumb]:bg-foreground/[0.08] [&::-webkit-scrollbar-thumb]:rounded-full",
23785
+ "[&::-webkit-scrollbar-thumb]:hover:bg-foreground/[0.15]"
23786
+ ),
23787
+ onScroll: handleBodyScroll,
23788
+ children: /* @__PURE__ */ jsxs(
23789
+ "div",
23790
+ {
23791
+ ref: rowsClipRef,
23792
+ "data-data-grid-rows-clip": "",
23793
+ className: "relative z-0",
23794
+ style: {
23795
+ minWidth: totalContentWidth,
23796
+ clipPath: "inset(var(--data-grid-sticky-overlap, 0px) 0 0 0)"
23797
+ },
23798
+ children: [
23799
+ isLoading && /* @__PURE__ */ jsx("div", { style: { minWidth: totalContentWidth }, children: loadingState ?? Array.from({ length: 8 }).map((_, i) => /* @__PURE__ */ jsx(
23800
+ SkeletonRow,
23801
+ {
23802
+ columns: visibleColumns,
23803
+ height: estimatedRowHeight,
23804
+ showCheckbox: selectionMode !== "none"
23805
+ },
23806
+ i
23807
+ )) }),
23808
+ !isLoading && rows.length === 0 && /* @__PURE__ */ jsx(
23809
+ "div",
23810
+ {
23811
+ className: "flex items-center justify-center py-16 text-sm text-muted-foreground",
23812
+ style: { minWidth: totalContentWidth },
23813
+ children: emptyState ?? strings.noData
23814
+ }
23815
+ ),
23816
+ !isLoading && rows.length > 0 && /* @__PURE__ */ jsx(
23817
+ "div",
23818
+ {
23819
+ style: {
23820
+ height: rowVirtualizer.getTotalSize(),
23821
+ width: "100%",
23822
+ minWidth: totalContentWidth,
23823
+ position: "relative"
23824
+ },
23825
+ children: rowVirtualizer.getVirtualItems().map((virtualRow) => {
23826
+ const row = rows[virtualRow.index] ?? throwErr(
23827
+ `DataGrid: virtualized row index ${virtualRow.index} out of range (rows.length=${rows.length})`
23828
+ );
23829
+ const rowId = getRowId(row);
23830
+ const isSelected = state.selection.selectedIds.has(rowId);
23831
+ const isOddRow = virtualRow.index % 2 === 1;
23832
+ return /* @__PURE__ */ jsxs(
23833
+ "div",
23834
+ {
23835
+ ref: isDynamicRowHeight ? rowVirtualizer.measureElement : void 0,
23836
+ "data-index": virtualRow.index,
23837
+ className: cn(
23838
+ "absolute left-0 w-full flex",
23839
+ "border-b border-black/[0.03] dark:border-white/[0.03]",
23840
+ "transition-colors duration-75",
23841
+ isSelected ? "bg-blue-500/[0.06] dark:bg-blue-400/[0.08] hover:bg-blue-500/[0.08] dark:hover:bg-blue-400/[0.1]" : isOddRow ? "bg-foreground/[0.02] dark:bg-foreground/[0.03] hover:bg-foreground/[0.04] dark:hover:bg-foreground/[0.06]" : "hover:bg-foreground/[0.025] dark:hover:bg-foreground/[0.04]",
23842
+ (selectionMode !== "none" || onRowClick) && "cursor-pointer"
23843
+ ),
23844
+ style: {
23845
+ ...isDynamicRowHeight ? { minHeight: estimatedRowHeight } : { height: fixedRowHeight },
23846
+ transform: `translateY(${virtualRow.start}px)`
23847
+ },
23848
+ onClick: (e40) => {
23849
+ if (!shouldIgnoreRowClick(e40)) handleRowClick(row, rowId, e40);
23850
+ },
23851
+ onDoubleClick: (e40) => {
23852
+ if (!shouldIgnoreRowClick(e40)) onRowDoubleClick?.(row, rowId, e40);
23853
+ },
23854
+ role: "row",
23855
+ "aria-rowindex": virtualRow.index + 2,
23856
+ "aria-selected": isSelected,
23857
+ "data-row-id": rowId,
23858
+ "data-state": isSelected ? "selected" : void 0,
23859
+ children: [
23860
+ selectionMode !== "none" && /* @__PURE__ */ jsx(
23861
+ "div",
23862
+ {
23863
+ className: "flex items-center justify-center border-r border-black/[0.04] dark:border-white/[0.04]",
23864
+ style: { width: 44 },
23865
+ children: /* @__PURE__ */ jsx(
23866
+ SelectionCheckbox,
23867
+ {
23868
+ checked: isSelected,
23869
+ onChange: (event) => handleRowClick(row, rowId, event),
23870
+ ariaLabel: `Select row ${rowId}`
23871
+ }
23872
+ )
23873
+ }
23874
+ ),
23875
+ visibleColumns.map((col) => /* @__PURE__ */ jsx(
23876
+ DataCell,
23877
+ {
23878
+ col,
23879
+ row,
23880
+ rowId,
23881
+ rowIndex: virtualRow.index,
23882
+ isSelected,
23883
+ dateDisplay: state.dateDisplay
23884
+ },
23885
+ col.id
23886
+ ))
23887
+ ]
23888
+ },
23889
+ rowId
23890
+ );
23891
+ })
23892
+ }
23893
+ ),
23894
+ paginationMode === "infinite" && hasMore && !isLoading && /* @__PURE__ */ jsx(
23895
+ InfiniteScrollSentinel,
23896
+ {
23897
+ onIntersect: onLoadMore ?? NOOP,
23898
+ isLoading: isLoadingMore,
23899
+ rootRef: infiniteScrollRootRef,
23900
+ strings
23901
+ }
23902
+ )
23903
+ ]
23904
+ }
23905
+ )
23906
+ }
23907
+ ),
23908
+ footer !== false && /* @__PURE__ */ jsxs("div", { className: "sticky bottom-0 z-30 shrink-0 overflow-hidden rounded-b-[calc(var(--radius)*2)] bg-white/90 dark:bg-background/60 backdrop-blur-xl", children: [
23909
+ footer ? footer(footerCtx) : /* @__PURE__ */ jsx(DefaultFooter, { ctx: footerCtx, pagination: paginationMode, onChange }),
23910
+ footerExtra && (typeof footerExtra === "function" ? footerExtra(footerCtx) : footerExtra)
23911
+ ] })
23912
+ ]
23913
+ }
23914
+ )
23915
+ ] });
23547
23916
  }
23548
23917
 
23549
23918
  // src/components/data-grid/use-data-source.ts
23550
- var import_react34 = __toESM(require_react());
23919
+ var import_react36 = __toESM(require_react());
23551
23920
  function useClientDataSource(opts) {
23552
23921
  const { data, columns, sorting, quickSearch, matchRow, pagination, paginationMode } = opts;
23553
23922
  const sortingKey = JSON.stringify(sorting);
23554
- const processed = (0, import_react34.useMemo)(() => {
23923
+ const processed = (0, import_react36.useMemo)(() => {
23555
23924
  const searched = applyQuickSearch(data, quickSearch, columns, matchRow);
23556
23925
  const comparator = buildRowComparator(sorting, columns);
23557
23926
  const sorted = comparator ? [...searched].sort(comparator) : searched;
@@ -23559,7 +23928,7 @@ ${colorConfig.map(([key, itemConfig]) => {
23559
23928
  const paged = paginationMode === "client" ? paginateRows(sorted, pagination) : sorted;
23560
23929
  return { rows: paged, totalRowCount };
23561
23930
  }, [data, sortingKey, quickSearch, matchRow, pagination.pageIndex, pagination.pageSize, paginationMode, columns]);
23562
- return (0, import_react34.useMemo)(() => ({
23931
+ return (0, import_react36.useMemo)(() => ({
23563
23932
  rows: processed.rows,
23564
23933
  totalRowCount: processed.totalRowCount,
23565
23934
  isLoading: false,
@@ -23582,19 +23951,19 @@ ${colorConfig.map(([key, itemConfig]) => {
23582
23951
  pagination,
23583
23952
  paginationMode
23584
23953
  } = opts;
23585
- const [rows, setRows] = (0, import_react34.useState)([]);
23586
- const [totalRowCount, setTotalRowCount] = (0, import_react34.useState)(void 0);
23587
- const [isLoading, setIsLoading] = (0, import_react34.useState)(true);
23588
- const [isRefetching, setIsRefetching] = (0, import_react34.useState)(false);
23589
- const [isLoadingMore, setIsLoadingMore] = (0, import_react34.useState)(false);
23590
- const [hasMore, setHasMore] = (0, import_react34.useState)(true);
23591
- const [error, setError] = (0, import_react34.useState)(null);
23592
- const cursorRef = (0, import_react34.useRef)(void 0);
23593
- const abortRef = (0, import_react34.useRef)(null);
23594
- const pageIndexRef = (0, import_react34.useRef)(0);
23595
- const hasDataRef = (0, import_react34.useRef)(false);
23596
- const hasMountedServerPaginationRef = (0, import_react34.useRef)(false);
23597
- const latestArgsRef = (0, import_react34.useRef)({
23954
+ const [rows, setRows] = (0, import_react36.useState)([]);
23955
+ const [totalRowCount, setTotalRowCount] = (0, import_react36.useState)(void 0);
23956
+ const [isLoading, setIsLoading] = (0, import_react36.useState)(true);
23957
+ const [isRefetching, setIsRefetching] = (0, import_react36.useState)(false);
23958
+ const [isLoadingMore, setIsLoadingMore] = (0, import_react36.useState)(false);
23959
+ const [hasMore, setHasMore] = (0, import_react36.useState)(true);
23960
+ const [error, setError] = (0, import_react36.useState)(null);
23961
+ const cursorRef = (0, import_react36.useRef)(void 0);
23962
+ const abortRef = (0, import_react36.useRef)(null);
23963
+ const pageIndexRef = (0, import_react36.useRef)(0);
23964
+ const hasDataRef = (0, import_react36.useRef)(false);
23965
+ const hasMountedServerPaginationRef = (0, import_react36.useRef)(false);
23966
+ const latestArgsRef = (0, import_react36.useRef)({
23598
23967
  dataSource,
23599
23968
  getRowId,
23600
23969
  sorting,
@@ -23604,7 +23973,7 @@ ${colorConfig.map(([key, itemConfig]) => {
23604
23973
  latestArgsRef.current = { dataSource, getRowId, sorting, quickSearch, pagination };
23605
23974
  const sortingKey = JSON.stringify(sorting);
23606
23975
  const quickSearchKey = quickSearch;
23607
- const fetchPage = (0, import_react34.useCallback)(
23976
+ const fetchPage = (0, import_react36.useCallback)(
23608
23977
  async (append) => {
23609
23978
  const {
23610
23979
  dataSource: currentDataSource,
@@ -23673,12 +24042,12 @@ ${colorConfig.map(([key, itemConfig]) => {
23673
24042
  },
23674
24043
  []
23675
24044
  );
23676
- (0, import_react34.useEffect)(() => {
24045
+ (0, import_react36.useEffect)(() => {
23677
24046
  fetchPage(false).catch(() => {
23678
24047
  });
23679
24048
  return () => abortRef.current?.abort();
23680
24049
  }, [fetchPage, dataSource, sortingKey, quickSearchKey, pagination.pageSize]);
23681
- (0, import_react34.useEffect)(() => {
24050
+ (0, import_react36.useEffect)(() => {
23682
24051
  if (paginationMode !== "server") {
23683
24052
  hasMountedServerPaginationRef.current = false;
23684
24053
  return;
@@ -23690,13 +24059,13 @@ ${colorConfig.map(([key, itemConfig]) => {
23690
24059
  fetchPage(false).catch(() => {
23691
24060
  });
23692
24061
  }, [fetchPage, paginationMode, pagination.pageIndex]);
23693
- const loadMore = (0, import_react34.useCallback)(() => {
24062
+ const loadMore = (0, import_react36.useCallback)(() => {
23694
24063
  if (!isLoadingMore && hasMore && paginationMode === "infinite") {
23695
24064
  fetchPage(true).catch(() => {
23696
24065
  });
23697
24066
  }
23698
24067
  }, [isLoadingMore, hasMore, paginationMode, fetchPage]);
23699
- const reload = (0, import_react34.useCallback)(() => {
24068
+ const reload = (0, import_react36.useCallback)(() => {
23700
24069
  fetchPage(false).catch(() => {
23701
24070
  });
23702
24071
  }, [fetchPage]);
@@ -23759,7 +24128,7 @@ ${colorConfig.map(([key, itemConfig]) => {
23759
24128
  }
23760
24129
 
23761
24130
  // src/components/data-grid/use-url-state.ts
23762
- var import_react35 = __toESM(require_react());
24131
+ var import_react37 = __toESM(require_react());
23763
24132
  function serializeWidths(widths, columns) {
23764
24133
  const parts = [];
23765
24134
  for (const col of columns) {
@@ -23855,10 +24224,10 @@ ${colorConfig.map(([key, itemConfig]) => {
23855
24224
  const hiddenKey = `${prefix}_h`;
23856
24225
  const sortKey = `${prefix}_s`;
23857
24226
  const searchKey = `${prefix}_q`;
23858
- const columnsRef = (0, import_react35.useRef)(columns);
24227
+ const columnsRef = (0, import_react37.useRef)(columns);
23859
24228
  columnsRef.current = columns;
23860
- const initialRef = (0, import_react35.useRef)(opts?.initial);
23861
- const [state, setState] = (0, import_react35.useState)(() => {
24229
+ const initialRef = (0, import_react37.useRef)(opts?.initial);
24230
+ const [state, setState] = (0, import_react37.useState)(() => {
23862
24231
  const base = createDefaultDataGridState(columns);
23863
24232
  const initial = initialRef.current ?? {};
23864
24233
  const baseWithInitial = {
@@ -23877,7 +24246,7 @@ ${colorConfig.map(([key, itemConfig]) => {
23877
24246
  quickSearch: parseQuickSearch(params.get(searchKey), baseWithInitial.quickSearch)
23878
24247
  };
23879
24248
  });
23880
- (0, import_react35.useEffect)(() => {
24249
+ (0, import_react37.useEffect)(() => {
23881
24250
  if (typeof window === "undefined") return;
23882
24251
  const timer = setTimeout(() => {
23883
24252
  const params = new URLSearchParams(window.location.search);
@@ -23914,7 +24283,7 @@ ${colorConfig.map(([key, itemConfig]) => {
23914
24283
  sortKey,
23915
24284
  searchKey
23916
24285
  ]);
23917
- (0, import_react35.useEffect)(() => {
24286
+ (0, import_react37.useEffect)(() => {
23918
24287
  if (typeof window === "undefined") return;
23919
24288
  const onPop = () => {
23920
24289
  const params = new URLSearchParams(window.location.search);