@ai-matrx/design-system 0.22.0 → 0.25.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (93) hide show
  1. package/CHANGELOG.md +151 -0
  2. package/dist/data-table/cell-undo.cjs +98 -0
  3. package/dist/data-table/cell-undo.cjs.map +1 -0
  4. package/dist/data-table/cell-undo.d.cts +85 -0
  5. package/dist/data-table/cell-undo.d.ts +85 -0
  6. package/dist/data-table/cell-undo.js +78 -0
  7. package/dist/data-table/cell-undo.js.map +1 -0
  8. package/dist/data-table/copy-helpers.d.cts +6 -2
  9. package/dist/data-table/copy-helpers.d.ts +6 -2
  10. package/dist/data-table/coverage.cjs +116 -0
  11. package/dist/data-table/coverage.cjs.map +1 -0
  12. package/dist/data-table/coverage.d.cts +65 -0
  13. package/dist/data-table/coverage.d.ts +65 -0
  14. package/dist/data-table/coverage.js +96 -0
  15. package/dist/data-table/coverage.js.map +1 -0
  16. package/dist/data-table/fill.cjs +299 -0
  17. package/dist/data-table/fill.cjs.map +1 -0
  18. package/dist/data-table/fill.d.cts +129 -0
  19. package/dist/data-table/fill.d.ts +129 -0
  20. package/dist/data-table/fill.js +277 -0
  21. package/dist/data-table/fill.js.map +1 -0
  22. package/dist/data-table/filter-engine.d.cts +6 -2
  23. package/dist/data-table/filter-engine.d.ts +6 -2
  24. package/dist/data-table/grid-clipboard.cjs +165 -0
  25. package/dist/data-table/grid-clipboard.cjs.map +1 -0
  26. package/dist/data-table/grid-clipboard.d.cts +85 -0
  27. package/dist/data-table/grid-clipboard.d.ts +85 -0
  28. package/dist/data-table/grid-clipboard.js +145 -0
  29. package/dist/data-table/grid-clipboard.js.map +1 -0
  30. package/dist/data-table/grid-selection.cjs +258 -0
  31. package/dist/data-table/grid-selection.cjs.map +1 -0
  32. package/dist/data-table/grid-selection.d.cts +166 -0
  33. package/dist/data-table/grid-selection.d.ts +166 -0
  34. package/dist/data-table/grid-selection.js +238 -0
  35. package/dist/data-table/grid-selection.js.map +1 -0
  36. package/dist/data-table/grouping.cjs +302 -0
  37. package/dist/data-table/grouping.cjs.map +1 -0
  38. package/dist/data-table/grouping.d.cts +119 -0
  39. package/dist/data-table/grouping.d.ts +119 -0
  40. package/dist/data-table/grouping.js +282 -0
  41. package/dist/data-table/grouping.js.map +1 -0
  42. package/dist/data-table/host.d.cts +6 -2
  43. package/dist/data-table/host.d.ts +6 -2
  44. package/dist/data-table/index.cjs +2433 -246
  45. package/dist/data-table/index.cjs.map +1 -1
  46. package/dist/data-table/index.d.cts +11 -3
  47. package/dist/data-table/index.d.ts +11 -3
  48. package/dist/data-table/index.js +2425 -238
  49. package/dist/data-table/index.js.map +1 -1
  50. package/dist/data-table/infer-filter.d.cts +6 -2
  51. package/dist/data-table/infer-filter.d.ts +6 -2
  52. package/dist/data-table/layered-filters.d.cts +6 -2
  53. package/dist/data-table/layered-filters.d.ts +6 -2
  54. package/dist/data-table/query-control.d.cts +6 -2
  55. package/dist/data-table/query-control.d.ts +6 -2
  56. package/dist/data-table/spreadsheet.cjs +1014 -0
  57. package/dist/data-table/spreadsheet.cjs.map +1 -0
  58. package/dist/data-table/spreadsheet.d.cts +118 -0
  59. package/dist/data-table/spreadsheet.d.ts +118 -0
  60. package/dist/data-table/spreadsheet.js +994 -0
  61. package/dist/data-table/spreadsheet.js.map +1 -0
  62. package/dist/data-table/types.cjs.map +1 -1
  63. package/dist/data-table/types.d.cts +6 -2
  64. package/dist/data-table/types.d.ts +6 -2
  65. package/dist/data-table/url-state.d.cts +6 -2
  66. package/dist/data-table/url-state.d.ts +6 -2
  67. package/dist/data-table/validation.cjs +229 -0
  68. package/dist/data-table/validation.cjs.map +1 -0
  69. package/dist/data-table/validation.d.cts +125 -0
  70. package/dist/data-table/validation.d.ts +125 -0
  71. package/dist/data-table/validation.js +209 -0
  72. package/dist/data-table/validation.js.map +1 -0
  73. package/dist/data-table/virtual-window.cjs +203 -0
  74. package/dist/data-table/virtual-window.cjs.map +1 -0
  75. package/dist/data-table/virtual-window.d.cts +84 -0
  76. package/dist/data-table/virtual-window.d.ts +84 -0
  77. package/dist/data-table/virtual-window.js +183 -0
  78. package/dist/data-table/virtual-window.js.map +1 -0
  79. package/dist/data-table/xlsx.d.cts +6 -2
  80. package/dist/data-table/xlsx.d.ts +6 -2
  81. package/dist/field-formats/index.d.cts +2 -2
  82. package/dist/field-formats/index.d.ts +2 -2
  83. package/dist/formulas/index.cjs +1041 -0
  84. package/dist/formulas/index.cjs.map +1 -0
  85. package/dist/formulas/index.d.cts +389 -0
  86. package/dist/formulas/index.d.ts +389 -0
  87. package/dist/formulas/index.js +1019 -0
  88. package/dist/formulas/index.js.map +1 -0
  89. package/dist/{types-BdbvlF9-.d.cts → types-CJWU1Gae.d.cts} +1 -1
  90. package/dist/{types-BdbvlF9-.d.ts → types-CJWU1Gae.d.ts} +1 -1
  91. package/dist/{types-Sn249eoy.d.cts → types-CmQrOz-T.d.cts} +154 -2
  92. package/dist/{types-D-flTpK0.d.ts → types-nlLCMiqd.d.ts} +154 -2
  93. package/package.json +109 -2
@@ -35,8 +35,11 @@ __export(data_table_exports, {
35
35
  CELL_TINT_CLASS: () => CELL_TINT_CLASS,
36
36
  COLOR_RULE_OPS: () => COLOR_RULE_OPS,
37
37
  COLOR_RULE_OP_LABELS: () => COLOR_RULE_OP_LABELS,
38
+ DEFAULT_OVERSCAN: () => DEFAULT_OVERSCAN,
39
+ DEFAULT_UNDO_DEPTH: () => DEFAULT_UNDO_DEPTH,
38
40
  DataRowInspector: () => DataRowInspector,
39
41
  DataRowWindow: () => DataRowWindow,
42
+ EMPTY_GROUP_KEY: () => EMPTY_GROUP_KEY,
40
43
  EMPTY_TABLE_STYLE: () => EMPTY_TABLE_STYLE,
41
44
  FORMAT_MISMATCH_CLASS: () => FORMAT_MISMATCH_CLASS,
42
45
  FormattedCell: () => FormattedCell,
@@ -47,6 +50,7 @@ __export(data_table_exports, {
47
50
  MatrxUuidCell: () => MatrxUuidCell,
48
51
  NO_FROZEN_COLUMNS: () => NO_FROZEN_COLUMNS,
49
52
  OPS_WITH_VALUE: () => OPS_WITH_VALUE,
53
+ PARTIAL_BADGE: () => PARTIAL_BADGE,
50
54
  ROW_TINT_CLASS: () => ROW_TINT_CLASS,
51
55
  STYLE_COLORS: () => STYLE_COLORS,
52
56
  STYLE_COLOR_LABELS: () => STYLE_COLOR_LABELS,
@@ -56,22 +60,62 @@ __export(data_table_exports, {
56
60
  TABLE_STYLE_VERSION: () => TABLE_STYLE_VERSION,
57
61
  TableTitleRow: () => TableTitleRow,
58
62
  TableViewTabs: () => TableViewTabs,
63
+ UNKNOWN_TOTAL_BADGE: () => UNKNOWN_TOTAL_BADGE,
59
64
  alignForFormat: () => alignForFormat,
65
+ allRange: () => allRange,
60
66
  applyStylePath: () => applyStylePath,
67
+ boundsContain: () => boundsContain,
61
68
  buildRowAgentInput: () => buildRowAgentInput,
62
69
  buildViewAgentInput: () => buildViewAgentInput,
63
70
  buildViewHuman: () => buildViewHuman,
71
+ canRedo: () => canRedo,
72
+ canUndo: () => canUndo,
73
+ cellClipboardText: () => cellClipboardText,
74
+ cellDomKey: () => cellDomKey,
75
+ cellsInRange: () => cellsInRange,
76
+ classifyGridKey: () => classifyGridKey,
77
+ coerceGroupNumber: () => coerceGroupNumber,
64
78
  collectSelectOptions: () => collectSelectOptions,
65
79
  colorForChoice: () => colorForChoice,
80
+ columnRange: () => columnRange,
81
+ computeVariableWindow: () => computeVariableWindow,
82
+ computeWindow: () => computeWindow,
83
+ createUndoStack: () => createUndoStack,
84
+ describeCoverage: () => describeCoverage,
85
+ detectSeries: () => detectSeries,
86
+ directClickKinds: () => directClickKinds,
66
87
  downloadTableAsXlsx: () => downloadTableAsXlsx,
67
88
  evaluateRule: () => evaluateRule,
89
+ fillDown: () => fillDown,
90
+ fillRange: () => fillRange,
91
+ fillTargetFor: () => fillTargetFor,
68
92
  filterKindLabel: () => filterKindLabel,
93
+ flattenGroups: () => flattenGroups,
69
94
  formatColumnValue: () => formatColumnValue,
95
+ formatGroupNumber: () => formatGroupNumber,
96
+ gridToTsv: () => gridToTsv,
97
+ groupKeyOf: () => groupKeyOf,
98
+ groupRows: () => groupRows,
99
+ groupSummary: () => groupSummary,
70
100
  inferTableSearchRole: () => inferTableSearchRole,
101
+ isBlankGroupValue: () => isBlankGroupValue,
102
+ isDirectClickEditor: () => isDirectClickEditor,
103
+ isGroupCollapsed: () => isGroupCollapsed,
104
+ isSingleCell: () => isSingleCell,
105
+ isSingleCellRange: () => isSingleCellRange,
71
106
  isStyleColor: () => isStyleColor,
107
+ moveSelection: () => moveSelection,
108
+ parseClipboardGrid: () => parseClipboardGrid,
72
109
  parseTableStyle: () => parseTableStyle,
73
110
  parseTableViewSnapshot: () => parseTableViewSnapshot,
74
111
  pixelWidth: () => pixelWidth,
112
+ planPaste: () => planPaste,
113
+ popRedo: () => popRedo,
114
+ popUndo: () => popUndo,
115
+ pushUndo: () => pushUndo,
116
+ rangeBounds: () => rangeBounds,
117
+ rangeRows: () => rangeRows,
118
+ rangeSize: () => rangeSize,
75
119
  rankTableSearchRows: () => rankTableSearchRows,
76
120
  resolveCellColor: () => resolveCellColor,
77
121
  resolveColumnMarker: () => resolveColumnMarker,
@@ -79,23 +123,31 @@ __export(data_table_exports, {
79
123
  resolveFrozenColumns: () => resolveFrozenColumns,
80
124
  resolveRowColor: () => resolveRowColor,
81
125
  resolveTableSearchScopes: () => resolveTableSearchScopes,
126
+ rowRange: () => rowRange,
82
127
  rowsToCsvFromColumns: () => rowsToCsvFromColumns,
83
128
  rowsToMarkdownTable: () => rowsToMarkdownTable,
84
129
  rowsToRecordsFromColumns: () => rowsToRecordsFromColumns,
130
+ sameCell: () => sameCell,
131
+ scrollOffsetForIndex: () => scrollOffsetForIndex,
85
132
  shortUuid: () => shortUuid,
133
+ storedValuesEqual: () => storedValuesEqual,
86
134
  stylePath: () => stylePath,
87
135
  tableStyleFromMetadata: () => tableStyleFromMetadata,
88
136
  tableStyleIsEmpty: () => tableStyleIsEmpty,
89
137
  tableUrlParamPrefix: () => tableUrlParamPrefix,
138
+ tileIndex: () => tileIndex,
139
+ toggleGroupCollapsed: () => toggleGroupCollapsed,
140
+ undoEntryFor: () => undoEntryFor,
90
141
  useMatrxDataTableHost: () => useMatrxDataTableHost,
91
142
  useMatrxTableRowAlchemy: () => useMatrxTableRowAlchemy,
92
143
  useScrollPagination: () => useScrollPagination,
144
+ useSpreadsheetGrid: () => useSpreadsheetGrid,
93
145
  useTableUrlState: () => useTableUrlState
94
146
  });
95
147
  module.exports = __toCommonJS(data_table_exports);
96
148
 
97
149
  // src/data-table/MatrxDataTable.tsx
98
- var import_react20 = require("react");
150
+ var import_react21 = require("react");
99
151
  var import_core3 = require("@dnd-kit/core");
100
152
 
101
153
  // src/data-table/icons.tsx
@@ -5512,11 +5564,11 @@ var import_react11 = require("react");
5512
5564
  var import_url_state = require("@ai-matrx/kit/url-state");
5513
5565
  function parseSort(raw) {
5514
5566
  if (!raw) return null;
5515
- const at = raw.lastIndexOf(".");
5516
- if (at <= 0) return null;
5517
- const direction = raw.slice(at + 1);
5567
+ const at2 = raw.lastIndexOf(".");
5568
+ if (at2 <= 0) return null;
5569
+ const direction = raw.slice(at2 + 1);
5518
5570
  if (direction !== "asc" && direction !== "desc") return null;
5519
- return { id: raw.slice(0, at), direction };
5571
+ return { id: raw.slice(0, at2), direction };
5520
5572
  }
5521
5573
  function parseObject(raw, fallback) {
5522
5574
  if (!raw) return fallback;
@@ -6790,6 +6842,1611 @@ function projectColumnDragOrder(orderedIds, preview2) {
6790
6842
  return order;
6791
6843
  }
6792
6844
 
6845
+ // src/data-table/grouping.ts
6846
+ var EMPTY_GROUP_KEY = "empty";
6847
+ var DEFAULT_EMPTY_LABEL = "(empty)";
6848
+ function isBlankGroupValue(value) {
6849
+ return value === null || value === void 0 || value === "";
6850
+ }
6851
+ function groupKeyOf(value) {
6852
+ if (isBlankGroupValue(value)) return EMPTY_GROUP_KEY;
6853
+ switch (typeof value) {
6854
+ case "string":
6855
+ return `s:${value}`;
6856
+ case "number":
6857
+ return `n:${String(value)}`;
6858
+ case "boolean":
6859
+ return `b:${value ? "true" : "false"}`;
6860
+ case "bigint":
6861
+ return `i:${String(value)}`;
6862
+ case "symbol":
6863
+ return `y:${String(value)}`;
6864
+ case "function":
6865
+ return `f:${safeString(value)}`;
6866
+ default:
6867
+ break;
6868
+ }
6869
+ if (value instanceof Date) {
6870
+ const time = value.getTime();
6871
+ return `d:${Number.isFinite(time) ? value.toISOString() : "invalid"}`;
6872
+ }
6873
+ return `o:${safeJson(value)}`;
6874
+ }
6875
+ function safeString(value) {
6876
+ try {
6877
+ return String(value);
6878
+ } catch {
6879
+ return "[unreadable]";
6880
+ }
6881
+ }
6882
+ function safeJson(value) {
6883
+ try {
6884
+ const json = JSON.stringify(value);
6885
+ return typeof json === "string" ? json : safeString(value);
6886
+ } catch {
6887
+ return safeString(value);
6888
+ }
6889
+ }
6890
+ var CURRENCY_AND_SPACE = /[\s,$£€¥]/g;
6891
+ function coerceGroupNumber(value) {
6892
+ if (isBlankGroupValue(value)) return null;
6893
+ if (typeof value === "number") return Number.isFinite(value) ? value : null;
6894
+ if (typeof value === "boolean") return value ? 1 : 0;
6895
+ if (typeof value === "bigint") {
6896
+ const asNumber = Number(value);
6897
+ return Number.isFinite(asNumber) ? asNumber : null;
6898
+ }
6899
+ if (typeof value !== "string") return null;
6900
+ const cleaned = value.replace(CURRENCY_AND_SPACE, "");
6901
+ if (cleaned === "" || cleaned === "-" || cleaned === "+") return null;
6902
+ const parsed = Number(cleaned);
6903
+ return Number.isFinite(parsed) ? parsed : null;
6904
+ }
6905
+ var NUMBER_FORMAT = new Intl.NumberFormat("en-US", { maximumFractionDigits: 2 });
6906
+ function formatGroupNumber(value) {
6907
+ if (!Number.isFinite(value)) return "\u2014";
6908
+ try {
6909
+ return NUMBER_FORMAT.format(value);
6910
+ } catch {
6911
+ return String(value);
6912
+ }
6913
+ }
6914
+ function defaultLabelOf(value, emptyLabel) {
6915
+ if (isBlankGroupValue(value)) return emptyLabel;
6916
+ if (typeof value === "string") return value;
6917
+ if (typeof value === "number") return formatGroupNumber(value);
6918
+ if (typeof value === "boolean") return value ? "true" : "false";
6919
+ if (value instanceof Date) {
6920
+ const time = value.getTime();
6921
+ return Number.isFinite(time) ? value.toISOString() : "Invalid Date";
6922
+ }
6923
+ if (typeof value === "object") return safeJson(value);
6924
+ return safeString(value);
6925
+ }
6926
+ var AGGREGATE_NAMES = {
6927
+ count: { name: "Count", position: "before" },
6928
+ sum: { name: "Sum", position: "before" },
6929
+ average: { name: "Average", position: "before" },
6930
+ min: { name: "Min", position: "before" },
6931
+ max: { name: "Max", position: "before" },
6932
+ empty: { name: "empty", position: "after" },
6933
+ filled: { name: "filled", position: "after" },
6934
+ unique: { name: "unique", position: "after" },
6935
+ checked: { name: "checked", position: "after" },
6936
+ unchecked: { name: "unchecked", position: "after" },
6937
+ percentFilled: { name: "filled", position: "after" }
6938
+ };
6939
+ function aggregateLabel(kind, value, override) {
6940
+ const naming = AGGREGATE_NAMES[kind] ?? { name: kind, position: "before" };
6941
+ const name = override && override.trim() !== "" ? override : naming.name;
6942
+ const shown = value === null ? "\u2014" : kind === "percentFilled" ? `${formatGroupNumber(value)}%` : formatGroupNumber(value);
6943
+ return naming.position === "before" ? `${name} ${shown}` : `${shown} ${name}`;
6944
+ }
6945
+ function readValue(row, reader) {
6946
+ try {
6947
+ return reader(row);
6948
+ } catch {
6949
+ return null;
6950
+ }
6951
+ }
6952
+ function computeAggregate(rows, column) {
6953
+ const kind = column.spec.kind;
6954
+ const raw = rows.map((row) => readValue(row, column.readCell));
6955
+ const total = raw.length;
6956
+ const filled = raw.filter((value2) => !isBlankGroupValue(value2));
6957
+ const numbers = filled.map((value2) => coerceGroupNumber(value2)).filter((value2) => value2 !== null);
6958
+ let value;
6959
+ switch (kind) {
6960
+ case "count":
6961
+ value = total;
6962
+ break;
6963
+ case "sum":
6964
+ value = numbers.length === 0 ? null : numbers.reduce((acc, n) => acc + n, 0);
6965
+ break;
6966
+ case "average":
6967
+ value = numbers.length === 0 ? null : numbers.reduce((acc, n) => acc + n, 0) / numbers.length;
6968
+ break;
6969
+ case "min":
6970
+ value = numbers.length === 0 ? null : numbers.reduce((acc, n) => n < acc ? n : acc);
6971
+ break;
6972
+ case "max":
6973
+ value = numbers.length === 0 ? null : numbers.reduce((acc, n) => n > acc ? n : acc);
6974
+ break;
6975
+ case "empty":
6976
+ value = total - filled.length;
6977
+ break;
6978
+ case "filled":
6979
+ value = filled.length;
6980
+ break;
6981
+ case "unique": {
6982
+ const seen = /* @__PURE__ */ new Set();
6983
+ for (const entry of filled) seen.add(groupKeyOf(entry));
6984
+ value = seen.size;
6985
+ break;
6986
+ }
6987
+ case "checked":
6988
+ value = filled.filter((entry) => entry === true).length;
6989
+ break;
6990
+ case "unchecked":
6991
+ value = filled.filter((entry) => entry === false).length;
6992
+ break;
6993
+ case "percentFilled":
6994
+ value = total === 0 ? null : filled.length / total * 100;
6995
+ break;
6996
+ default:
6997
+ value = null;
6998
+ break;
6999
+ }
7000
+ return { kind, value, label: aggregateLabel(kind, value, column.spec.label) };
7001
+ }
7002
+ function compareLabels(a, b) {
7003
+ if (a === b) return 0;
7004
+ return a < b ? -1 : 1;
7005
+ }
7006
+ function compareValues2(a, b) {
7007
+ const aNumber = typeof a.value === "number" ? a.value : null;
7008
+ const bNumber = typeof b.value === "number" ? b.value : null;
7009
+ if (aNumber !== null && bNumber !== null && aNumber !== bNumber) return aNumber < bNumber ? -1 : 1;
7010
+ if (typeof a.value === "boolean" && typeof b.value === "boolean" && a.value !== b.value) {
7011
+ return a.value ? 1 : -1;
7012
+ }
7013
+ return compareLabels(a.label, b.label);
7014
+ }
7015
+ function groupRows(options) {
7016
+ const rows = Array.isArray(options.rows) ? options.rows : [];
7017
+ const emptyLabel = options.emptyLabel ?? DEFAULT_EMPTY_LABEL;
7018
+ const order = options.order ?? "value-asc";
7019
+ const aggregates = options.aggregates ?? {};
7020
+ const labelFor = (value) => {
7021
+ if (!options.labelOf) return defaultLabelOf(value, emptyLabel);
7022
+ if (isBlankGroupValue(value)) {
7023
+ try {
7024
+ const custom = options.labelOf(value);
7025
+ return typeof custom === "string" && custom !== "" ? custom : emptyLabel;
7026
+ } catch {
7027
+ return emptyLabel;
7028
+ }
7029
+ }
7030
+ try {
7031
+ const custom = options.labelOf(value);
7032
+ return typeof custom === "string" ? custom : defaultLabelOf(value, emptyLabel);
7033
+ } catch {
7034
+ return defaultLabelOf(value, emptyLabel);
7035
+ }
7036
+ };
7037
+ const buckets = /* @__PURE__ */ new Map();
7038
+ for (const row of rows) {
7039
+ const raw = readValue(row, options.readCell);
7040
+ const blank = isBlankGroupValue(raw);
7041
+ const value = blank ? null : raw;
7042
+ const key = groupKeyOf(value);
7043
+ let bucket = buckets.get(key);
7044
+ if (!bucket) {
7045
+ bucket = { key, value, label: labelFor(value), rows: [], seen: buckets.size };
7046
+ buckets.set(key, bucket);
7047
+ }
7048
+ bucket.rows.push(row);
7049
+ }
7050
+ const ordered = [...buckets.values()].sort((a, b) => {
7051
+ const aBlank = a.key === EMPTY_GROUP_KEY;
7052
+ const bBlank = b.key === EMPTY_GROUP_KEY;
7053
+ if (aBlank !== bBlank) return aBlank ? 1 : -1;
7054
+ if (aBlank && bBlank) return 0;
7055
+ switch (order) {
7056
+ case "value-asc":
7057
+ return compareValues2(a, b) || a.seen - b.seen;
7058
+ case "value-desc":
7059
+ return -compareValues2(a, b) || a.seen - b.seen;
7060
+ case "count-desc":
7061
+ return b.rows.length - a.rows.length || a.seen - b.seen;
7062
+ case "first-seen":
7063
+ default:
7064
+ return a.seen - b.seen;
7065
+ }
7066
+ });
7067
+ const aggregateEntries = Object.entries(aggregates);
7068
+ return ordered.map((bucket, index) => {
7069
+ const results = {};
7070
+ for (const [columnId2, column] of aggregateEntries) {
7071
+ if (!column || typeof column.readCell !== "function" || !column.spec) continue;
7072
+ results[columnId2] = computeAggregate(bucket.rows, column);
7073
+ }
7074
+ return {
7075
+ key: bucket.key,
7076
+ value: bucket.value,
7077
+ label: bucket.label,
7078
+ rows: bucket.rows,
7079
+ aggregates: results,
7080
+ index
7081
+ };
7082
+ });
7083
+ }
7084
+ function toggleGroupCollapsed(collapsed, key) {
7085
+ const list = Array.isArray(collapsed) ? collapsed : [];
7086
+ return list.includes(key) ? list.filter((entry) => entry !== key) : [...list, key];
7087
+ }
7088
+ function isGroupCollapsed(collapsed, key) {
7089
+ return Array.isArray(collapsed) ? collapsed.includes(key) : false;
7090
+ }
7091
+ function flattenGroups(groups, collapsed) {
7092
+ const out = [];
7093
+ let dataIndex = 0;
7094
+ for (const group of Array.isArray(groups) ? groups : []) {
7095
+ out.push({ kind: "group", group });
7096
+ if (isGroupCollapsed(collapsed, group.key)) continue;
7097
+ for (const row of group.rows) {
7098
+ out.push({ kind: "row", group, row, dataIndex });
7099
+ dataIndex += 1;
7100
+ }
7101
+ }
7102
+ return out;
7103
+ }
7104
+ function groupSummary(group, rowNoun = "row") {
7105
+ const count = group?.rows?.length ?? 0;
7106
+ const noun = count === 1 ? rowNoun : `${rowNoun}s`;
7107
+ const parts = [`${formatGroupNumber(count)} ${noun}`];
7108
+ for (const result of Object.values(group?.aggregates ?? {})) {
7109
+ if (result?.label) parts.push(result.label);
7110
+ }
7111
+ return parts.join(" \xB7 ");
7112
+ }
7113
+
7114
+ // src/data-table/virtual-window.ts
7115
+ var DEFAULT_OVERSCAN = 6;
7116
+ function isNum(value) {
7117
+ return typeof value === "number" && Number.isFinite(value);
7118
+ }
7119
+ function toCount(value) {
7120
+ return isNum(value) && value > 0 ? Math.floor(value) : 0;
7121
+ }
7122
+ function toLength(value) {
7123
+ return isNum(value) && value > 0 ? value : 0;
7124
+ }
7125
+ function toOverscan(value) {
7126
+ if (!isNum(value)) return DEFAULT_OVERSCAN;
7127
+ return value > 0 ? Math.floor(value) : 0;
7128
+ }
7129
+ function toPinned(value, count) {
7130
+ if (!isNum(value) || value <= 0) return 0;
7131
+ return Math.min(Math.floor(value), count);
7132
+ }
7133
+ function assemble(args) {
7134
+ const { pinnedCount, start, end, padStart, body, total } = args;
7135
+ let padEnd = total - (padStart + body);
7136
+ if (!Number.isFinite(padEnd)) padEnd = 0;
7137
+ const sum = padStart + body + padEnd;
7138
+ if (sum !== total) padEnd = padEnd + (total - sum);
7139
+ const pinned = [];
7140
+ for (let i = 0; i < pinnedCount; i += 1) pinned.push(i);
7141
+ return { start, end, padStart, padEnd, total, pinned };
7142
+ }
7143
+ function emptyWindow(pinnedCount) {
7144
+ const pinned = [];
7145
+ for (let i = 0; i < pinnedCount; i += 1) pinned.push(i);
7146
+ return { start: pinnedCount, end: pinnedCount, padStart: 0, padEnd: 0, total: 0, pinned };
7147
+ }
7148
+ function band(firstVisible, lastVisible, scrollCount, overscan) {
7149
+ let first = firstVisible;
7150
+ if (first > scrollCount - 1) first = scrollCount - 1;
7151
+ if (first < 0) first = 0;
7152
+ let last = lastVisible;
7153
+ if (last < first + 1) last = first + 1;
7154
+ if (last > scrollCount) last = scrollCount;
7155
+ const start = Math.max(0, first - overscan);
7156
+ let end = Math.min(scrollCount, last + overscan);
7157
+ if (end < start) end = start;
7158
+ return { start, end };
7159
+ }
7160
+ function computeWindow(input) {
7161
+ const count = toCount(input?.count);
7162
+ const pinnedCount = toPinned(input?.pinnedStart, count);
7163
+ if (count <= 0) return emptyWindow(0);
7164
+ const itemSize = toLength(input?.itemSize);
7165
+ const viewport = toLength(input?.viewport);
7166
+ const overscan = toOverscan(input?.overscan);
7167
+ const scrollCount = count - pinnedCount;
7168
+ if (scrollCount <= 0) return emptyWindow(pinnedCount);
7169
+ if (itemSize <= 0) {
7170
+ return assemble({ pinnedCount, start: pinnedCount, end: count, padStart: 0, body: 0, total: 0 });
7171
+ }
7172
+ const total = scrollCount * itemSize;
7173
+ const maxOffset = Math.max(0, total - viewport);
7174
+ const offset = Math.min(toLength(input?.offset), maxOffset);
7175
+ const { start: relStart, end: relEnd } = band(
7176
+ Math.floor(offset / itemSize),
7177
+ Math.ceil((offset + viewport) / itemSize),
7178
+ scrollCount,
7179
+ overscan
7180
+ );
7181
+ return assemble({
7182
+ pinnedCount,
7183
+ start: pinnedCount + relStart,
7184
+ end: pinnedCount + relEnd,
7185
+ padStart: relStart * itemSize,
7186
+ body: (relEnd - relStart) * itemSize,
7187
+ total
7188
+ });
7189
+ }
7190
+ var PREFIX_CACHE = /* @__PURE__ */ new WeakMap();
7191
+ function prefixFor(sizeAt, count) {
7192
+ let byCount = PREFIX_CACHE.get(sizeAt);
7193
+ if (!byCount) {
7194
+ byCount = /* @__PURE__ */ new Map();
7195
+ PREFIX_CACHE.set(sizeAt, byCount);
7196
+ }
7197
+ const cached = byCount.get(count);
7198
+ if (cached) return cached;
7199
+ const prefix = new Float64Array(count + 1);
7200
+ let running = 0;
7201
+ for (let i = 0; i < count; i += 1) {
7202
+ let size = 0;
7203
+ try {
7204
+ size = sizeAt(i);
7205
+ } catch {
7206
+ size = 0;
7207
+ }
7208
+ running += isNum(size) && size > 0 ? size : 0;
7209
+ prefix[i + 1] = running;
7210
+ }
7211
+ byCount.set(count, prefix);
7212
+ return prefix;
7213
+ }
7214
+ function at(prefix, index) {
7215
+ return prefix[index] ?? 0;
7216
+ }
7217
+ function lastAtOrBefore(prefix, lo, hi, target) {
7218
+ let a = lo;
7219
+ let b = hi;
7220
+ while (a < b) {
7221
+ const mid = a + Math.ceil((b - a) / 2);
7222
+ if (at(prefix, mid) <= target) a = mid;
7223
+ else b = mid - 1;
7224
+ }
7225
+ return a;
7226
+ }
7227
+ function firstAtOrAfter(prefix, lo, hi, target) {
7228
+ let a = lo;
7229
+ let b = hi;
7230
+ while (a < b) {
7231
+ const mid = a + Math.floor((b - a) / 2);
7232
+ if (at(prefix, mid) >= target) b = mid;
7233
+ else a = mid + 1;
7234
+ }
7235
+ return a;
7236
+ }
7237
+ function computeVariableWindow(input) {
7238
+ const count = toCount(input?.count);
7239
+ const pinnedCount = toPinned(input?.pinnedStart, count);
7240
+ if (count <= 0) return emptyWindow(0);
7241
+ const sizeAt = typeof input?.sizeAt === "function" ? input.sizeAt : () => 0;
7242
+ const viewport = toLength(input?.viewport);
7243
+ const overscan = toOverscan(input?.overscan);
7244
+ const scrollCount = count - pinnedCount;
7245
+ if (scrollCount <= 0) return emptyWindow(pinnedCount);
7246
+ const prefix = prefixFor(sizeAt, count);
7247
+ const pinnedLength = at(prefix, pinnedCount);
7248
+ const total = at(prefix, count) - pinnedLength;
7249
+ if (total <= 0) {
7250
+ return assemble({ pinnedCount, start: pinnedCount, end: count, padStart: 0, body: 0, total: total > 0 ? total : 0 });
7251
+ }
7252
+ const maxOffset = Math.max(0, total - viewport);
7253
+ const offset = Math.min(toLength(input?.offset), maxOffset);
7254
+ const firstVisible = lastAtOrBefore(prefix, pinnedCount, count, pinnedLength + offset) - pinnedCount;
7255
+ const lastVisible = firstAtOrAfter(prefix, pinnedCount, count, pinnedLength + offset + viewport) - pinnedCount;
7256
+ const { start: relStart, end: relEnd } = band(firstVisible, lastVisible, scrollCount, overscan);
7257
+ const absStart = pinnedCount + relStart;
7258
+ const absEnd = pinnedCount + relEnd;
7259
+ return assemble({
7260
+ pinnedCount,
7261
+ start: absStart,
7262
+ end: absEnd,
7263
+ padStart: at(prefix, absStart) - pinnedLength,
7264
+ body: at(prefix, absEnd) - at(prefix, absStart),
7265
+ total
7266
+ });
7267
+ }
7268
+ function scrollOffsetForIndex(input) {
7269
+ const count = toCount(input?.count);
7270
+ if (count <= 0) return null;
7271
+ const pinnedCount = toPinned(input?.pinnedStart, count);
7272
+ const itemSize = toLength(input?.itemSize);
7273
+ const viewport = toLength(input?.viewport);
7274
+ const scrollCount = count - pinnedCount;
7275
+ if (scrollCount <= 0 || itemSize <= 0) return null;
7276
+ const rawIndex = isNum(input?.index) ? Math.floor(input.index) : 0;
7277
+ const index = Math.min(Math.max(rawIndex, 0), count - 1);
7278
+ if (index < pinnedCount) return null;
7279
+ const total = scrollCount * itemSize;
7280
+ const maxOffset = Math.max(0, total - viewport);
7281
+ const offset = Math.min(toLength(input?.offset), maxOffset);
7282
+ const itemStart = (index - pinnedCount) * itemSize;
7283
+ const itemEnd = itemStart + itemSize;
7284
+ if (itemStart >= offset && itemEnd <= offset + viewport) return null;
7285
+ const next = itemStart < offset ? itemStart : itemEnd - viewport;
7286
+ const clamped = Math.min(Math.max(next, 0), maxOffset);
7287
+ return clamped === offset ? null : clamped;
7288
+ }
7289
+
7290
+ // src/data-table/useSpreadsheetGrid.ts
7291
+ var import_react15 = require("react");
7292
+
7293
+ // src/data-table/grid-selection.ts
7294
+ function rangeBounds(range, rowIds, fieldNames) {
7295
+ const ra = rowIds.indexOf(range.anchor.rowId);
7296
+ const rf = rowIds.indexOf(range.focus.rowId);
7297
+ const ca = fieldNames.indexOf(range.anchor.fieldName);
7298
+ const cf = fieldNames.indexOf(range.focus.fieldName);
7299
+ if (ra === -1 || rf === -1 || ca === -1 || cf === -1) return null;
7300
+ return {
7301
+ r0: Math.min(ra, rf),
7302
+ r1: Math.max(ra, rf),
7303
+ c0: Math.min(ca, cf),
7304
+ c1: Math.max(ca, cf)
7305
+ };
7306
+ }
7307
+ function rangeSize(bounds) {
7308
+ const rows = bounds.r1 - bounds.r0 + 1;
7309
+ const cols = bounds.c1 - bounds.c0 + 1;
7310
+ return { rows, cols, cells: rows * cols };
7311
+ }
7312
+ function boundsContain(bounds, rowIndex, colIndex) {
7313
+ return rowIndex >= bounds.r0 && rowIndex <= bounds.r1 && colIndex >= bounds.c0 && colIndex <= bounds.c1;
7314
+ }
7315
+ function cellsInRange(range, rowIds, fieldNames) {
7316
+ const b = rangeBounds(range, rowIds, fieldNames);
7317
+ if (!b) return [];
7318
+ const out = [];
7319
+ for (let r = b.r0; r <= b.r1; r += 1) {
7320
+ const rowId = rowIds[r];
7321
+ if (rowId === void 0) continue;
7322
+ for (let c = b.c0; c <= b.c1; c += 1) {
7323
+ const fieldName = fieldNames[c];
7324
+ if (fieldName === void 0) continue;
7325
+ out.push({ rowId, fieldName });
7326
+ }
7327
+ }
7328
+ return out;
7329
+ }
7330
+ function rangeRows(range, rowIds, fieldNames) {
7331
+ const b = rangeBounds(range, rowIds, fieldNames);
7332
+ if (!b) return [];
7333
+ const rows = [];
7334
+ for (let r = b.r0; r <= b.r1; r += 1) {
7335
+ const rowId = rowIds[r];
7336
+ if (rowId === void 0) continue;
7337
+ const row = [];
7338
+ for (let c = b.c0; c <= b.c1; c += 1) {
7339
+ const fieldName = fieldNames[c];
7340
+ if (fieldName === void 0) continue;
7341
+ row.push({ rowId, fieldName });
7342
+ }
7343
+ rows.push(row);
7344
+ }
7345
+ return rows;
7346
+ }
7347
+ function isSingleCellRange(range) {
7348
+ return sameCell(range.anchor, range.focus);
7349
+ }
7350
+ function rowRange(rowId, fieldNames) {
7351
+ const firstField = fieldNames[0];
7352
+ const lastField = fieldNames[fieldNames.length - 1];
7353
+ if (firstField === void 0 || lastField === void 0) return null;
7354
+ return {
7355
+ anchor: { rowId, fieldName: firstField },
7356
+ focus: { rowId, fieldName: lastField }
7357
+ };
7358
+ }
7359
+ function columnRange(fieldName, rowIds) {
7360
+ const firstRow = rowIds[0];
7361
+ const lastRow = rowIds[rowIds.length - 1];
7362
+ if (firstRow === void 0 || lastRow === void 0) return null;
7363
+ return {
7364
+ anchor: { rowId: firstRow, fieldName },
7365
+ focus: { rowId: lastRow, fieldName }
7366
+ };
7367
+ }
7368
+ function allRange(rowIds, fieldNames) {
7369
+ const firstRow = rowIds[0];
7370
+ const lastRow = rowIds[rowIds.length - 1];
7371
+ const firstField = fieldNames[0];
7372
+ const lastField = fieldNames[fieldNames.length - 1];
7373
+ if (firstRow === void 0 || lastRow === void 0 || firstField === void 0 || lastField === void 0) {
7374
+ return null;
7375
+ }
7376
+ return {
7377
+ anchor: { rowId: firstRow, fieldName: firstField },
7378
+ focus: { rowId: lastRow, fieldName: lastField }
7379
+ };
7380
+ }
7381
+ function sameCell(a, b) {
7382
+ if (!a || !b) return a === b;
7383
+ return a.rowId === b.rowId && a.fieldName === b.fieldName;
7384
+ }
7385
+ function moveSelection(current, move, rowIds, fieldNames) {
7386
+ if (rowIds.length === 0 || fieldNames.length === 0) return null;
7387
+ const firstRowId = rowIds[0];
7388
+ const firstFieldName = fieldNames[0];
7389
+ if (firstRowId === void 0 || firstFieldName === void 0) return null;
7390
+ const first = { rowId: firstRowId, fieldName: firstFieldName };
7391
+ if (!current) return first;
7392
+ let r = rowIds.indexOf(current.rowId);
7393
+ let c = fieldNames.indexOf(current.fieldName);
7394
+ if (r === -1 || c === -1) return first;
7395
+ const lastRow = rowIds.length - 1;
7396
+ const lastCol = fieldNames.length - 1;
7397
+ const clamp = (n, max) => Math.min(Math.max(n, 0), max);
7398
+ switch (move) {
7399
+ case "up":
7400
+ r = clamp(r - 1, lastRow);
7401
+ break;
7402
+ case "down":
7403
+ r = clamp(r + 1, lastRow);
7404
+ break;
7405
+ case "left":
7406
+ c = clamp(c - 1, lastCol);
7407
+ break;
7408
+ case "right":
7409
+ c = clamp(c + 1, lastCol);
7410
+ break;
7411
+ case "rowStart":
7412
+ c = 0;
7413
+ break;
7414
+ case "rowEnd":
7415
+ c = lastCol;
7416
+ break;
7417
+ case "gridStart":
7418
+ r = 0;
7419
+ c = 0;
7420
+ break;
7421
+ case "gridEnd":
7422
+ r = lastRow;
7423
+ c = lastCol;
7424
+ break;
7425
+ case "nextCell":
7426
+ if (c < lastCol) c += 1;
7427
+ else if (r < lastRow) {
7428
+ r += 1;
7429
+ c = 0;
7430
+ }
7431
+ break;
7432
+ case "prevCell":
7433
+ if (c > 0) c -= 1;
7434
+ else if (r > 0) {
7435
+ r -= 1;
7436
+ c = lastCol;
7437
+ }
7438
+ break;
7439
+ }
7440
+ const rowId = rowIds[r];
7441
+ const fieldName = fieldNames[c];
7442
+ if (rowId === void 0 || fieldName === void 0) return first;
7443
+ return { rowId, fieldName };
7444
+ }
7445
+ function isTypingKey(e) {
7446
+ if (e.ctrlKey || e.metaKey || e.altKey) return false;
7447
+ return e.key.length === 1 && e.key !== " ";
7448
+ }
7449
+ function classifyGridKey(e) {
7450
+ const mod = e.ctrlKey || e.metaKey;
7451
+ const arrowMove = {
7452
+ ArrowUp: mod ? "gridStart" : "up",
7453
+ ArrowDown: mod ? "gridEnd" : "down",
7454
+ ArrowLeft: mod ? "rowStart" : "left",
7455
+ ArrowRight: mod ? "rowEnd" : "right",
7456
+ Home: mod ? "gridStart" : "rowStart",
7457
+ End: mod ? "gridEnd" : "rowEnd"
7458
+ };
7459
+ const arrow = arrowMove[e.key];
7460
+ if (arrow !== void 0) {
7461
+ return { kind: e.shiftKey ? "extend" : "move", move: arrow };
7462
+ }
7463
+ switch (e.key) {
7464
+ case "Tab":
7465
+ return { kind: "move", move: e.shiftKey ? "prevCell" : "nextCell" };
7466
+ case "Enter":
7467
+ case "F2":
7468
+ return { kind: "edit" };
7469
+ case " ":
7470
+ if (e.shiftKey) return { kind: "selectRow" };
7471
+ if (mod) return { kind: "selectColumn" };
7472
+ return { kind: "edit" };
7473
+ case "a":
7474
+ case "A":
7475
+ if (mod) return { kind: "selectAll" };
7476
+ break;
7477
+ case "d":
7478
+ case "D":
7479
+ if (mod) return { kind: "fillDown" };
7480
+ break;
7481
+ case "Escape":
7482
+ return { kind: "escape" };
7483
+ case "Delete":
7484
+ case "Backspace":
7485
+ return { kind: "clearCell" };
7486
+ case "c":
7487
+ case "C":
7488
+ if (mod) return { kind: "copy" };
7489
+ break;
7490
+ case "x":
7491
+ case "X":
7492
+ if (mod) return { kind: "cut" };
7493
+ break;
7494
+ case "v":
7495
+ case "V":
7496
+ if (mod) return { kind: "paste" };
7497
+ break;
7498
+ }
7499
+ if (isTypingKey(e)) return { kind: "editSeeded", seed: e.key };
7500
+ return null;
7501
+ }
7502
+ function directClickKinds() {
7503
+ return ["checkbox", "rating", "select", "multiselect"];
7504
+ }
7505
+ function isDirectClickEditor(editorKind) {
7506
+ return editorKind !== void 0 && directClickKinds().includes(editorKind);
7507
+ }
7508
+ function cellDomKey(address) {
7509
+ return `${address.rowId}::${address.fieldName}`;
7510
+ }
7511
+
7512
+ // src/data-table/grid-clipboard.ts
7513
+ function cellClipboardText(value) {
7514
+ if (value === null || value === void 0) return "";
7515
+ if (typeof value === "object") return JSON.stringify(value);
7516
+ return String(value);
7517
+ }
7518
+ function tsvCell(text) {
7519
+ return /[\t\r\n"]/.test(text) ? `"${text.replace(/"/g, '""')}"` : text;
7520
+ }
7521
+ function gridToTsv(block) {
7522
+ return block.map((row) => row.map((cell) => tsvCell(cellClipboardText(cell))).join(" ")).join("\n");
7523
+ }
7524
+ function parseClipboardGrid(text) {
7525
+ const rows = [];
7526
+ let row = [];
7527
+ let cell = "";
7528
+ let i = 0;
7529
+ const n = text.length;
7530
+ while (i < n) {
7531
+ const ch = text[i];
7532
+ if (cell === "" && ch === '"') {
7533
+ let j = i + 1;
7534
+ let out = "";
7535
+ let closed = false;
7536
+ while (j < n) {
7537
+ const c = text[j];
7538
+ if (c === '"') {
7539
+ if (text[j + 1] === '"') {
7540
+ out += '"';
7541
+ j += 2;
7542
+ continue;
7543
+ }
7544
+ closed = true;
7545
+ j += 1;
7546
+ break;
7547
+ }
7548
+ out += c;
7549
+ j += 1;
7550
+ }
7551
+ if (closed && (j >= n || text[j] === " " || text[j] === "\n" || text[j] === "\r")) {
7552
+ cell = out;
7553
+ i = j;
7554
+ if (i >= n) break;
7555
+ const d = text[i];
7556
+ if (d === " ") {
7557
+ row.push(cell);
7558
+ cell = "";
7559
+ i += 1;
7560
+ continue;
7561
+ }
7562
+ row.push(cell);
7563
+ rows.push(row);
7564
+ row = [];
7565
+ cell = "";
7566
+ i += d === "\r" && text[i + 1] === "\n" ? 2 : 1;
7567
+ continue;
7568
+ }
7569
+ cell += ch;
7570
+ i += 1;
7571
+ continue;
7572
+ }
7573
+ if (ch === " ") {
7574
+ row.push(cell);
7575
+ cell = "";
7576
+ i += 1;
7577
+ continue;
7578
+ }
7579
+ if (ch === "\n" || ch === "\r") {
7580
+ row.push(cell);
7581
+ rows.push(row);
7582
+ row = [];
7583
+ cell = "";
7584
+ i += ch === "\r" && text[i + 1] === "\n" ? 2 : 1;
7585
+ continue;
7586
+ }
7587
+ cell += ch;
7588
+ i += 1;
7589
+ }
7590
+ const endedOnBreak = n > 0 && (text[n - 1] === "\n" || text[n - 1] === "\r");
7591
+ if (!endedOnBreak) {
7592
+ row.push(cell);
7593
+ rows.push(row);
7594
+ } else if (row.length > 0) {
7595
+ row.push(cell);
7596
+ rows.push(row);
7597
+ }
7598
+ return rows.length === 0 ? [[""]] : rows;
7599
+ }
7600
+ function planPaste(anchor, block, rowIds, fieldNames) {
7601
+ const r0 = rowIds.indexOf(anchor.rowId);
7602
+ const c0 = fieldNames.indexOf(anchor.fieldName);
7603
+ if (r0 === -1 || c0 === -1) return null;
7604
+ const width = block.reduce((w, row) => Math.max(w, row.length), 0);
7605
+ const colsAvailable = fieldNames.length - c0;
7606
+ const colsUsed = Math.min(width, colsAvailable);
7607
+ const targetFields = fieldNames.slice(c0, c0 + colsUsed);
7608
+ const cells = [];
7609
+ const overflowRows = [];
7610
+ block.forEach((blockRow, dr) => {
7611
+ const rowId = rowIds[r0 + dr];
7612
+ if (rowId === void 0) {
7613
+ overflowRows.push(targetFields.map((_, dc) => blockRow[dc] ?? ""));
7614
+ return;
7615
+ }
7616
+ targetFields.forEach((fieldName, dc) => {
7617
+ if (dc >= blockRow.length) return;
7618
+ const raw = blockRow[dc];
7619
+ if (raw === void 0) return;
7620
+ cells.push({ rowId, fieldName, raw });
7621
+ });
7622
+ });
7623
+ return {
7624
+ cells,
7625
+ overflowRows,
7626
+ clippedColumns: Math.max(0, width - colsAvailable),
7627
+ fieldNames: targetFields
7628
+ };
7629
+ }
7630
+ function isSingleCell(block) {
7631
+ return block.length === 1 && block[0]?.length === 1;
7632
+ }
7633
+ function storedValuesEqual(a, b) {
7634
+ if (a === b) return true;
7635
+ if (a === null || b === null || a === void 0 || b === void 0) {
7636
+ return (a ?? null) === (b ?? null);
7637
+ }
7638
+ if (typeof a === "object" || typeof b === "object") {
7639
+ try {
7640
+ return JSON.stringify(a) === JSON.stringify(b);
7641
+ } catch {
7642
+ return false;
7643
+ }
7644
+ }
7645
+ return false;
7646
+ }
7647
+
7648
+ // src/data-table/fill.ts
7649
+ var NO_SERIES = { kind: "none", step: 0 };
7650
+ var MS_PER_DAY = 864e5;
7651
+ function plural(count, one, many) {
7652
+ return `${count} ${count === 1 ? one : many}`;
7653
+ }
7654
+ function isValidRect(rect) {
7655
+ return Number.isInteger(rect.top) && Number.isInteger(rect.left) && Number.isInteger(rect.bottom) && Number.isInteger(rect.right) && rect.top <= rect.bottom && rect.left <= rect.right;
7656
+ }
7657
+ function asBounds(rect) {
7658
+ return { r0: rect.top, r1: rect.bottom, c0: rect.left, c1: rect.right };
7659
+ }
7660
+ function directionOf(source, target) {
7661
+ if (!isValidRect(source) || !isValidRect(target)) return null;
7662
+ if (target.top === source.bottom + 1) return "down";
7663
+ if (target.bottom === source.top - 1) return "up";
7664
+ if (target.left === source.right + 1) return "right";
7665
+ if (target.right === source.left - 1) return "left";
7666
+ return null;
7667
+ }
7668
+ function tileIndex(i, a, b) {
7669
+ const n = b - a + 1;
7670
+ if (n <= 0) return a;
7671
+ if (i >= a && i <= b) return i;
7672
+ if (i > b) return a + (i - a) % n;
7673
+ return b - (a - 1 - i) % n;
7674
+ }
7675
+ function isFiniteNumber(v) {
7676
+ return typeof v === "number" && Number.isFinite(v);
7677
+ }
7678
+ var DATE_ONLY = /^\d{4}-\d{2}-\d{2}$/;
7679
+ var DATE_ISH = /^\d{4}-\d{2}-\d{2}([T ]|$)/;
7680
+ function dateMs(v) {
7681
+ if (v instanceof Date) {
7682
+ const t = v.getTime();
7683
+ return Number.isFinite(t) ? t : null;
7684
+ }
7685
+ if (typeof v === "string" && DATE_ISH.test(v)) {
7686
+ const t = Date.parse(v);
7687
+ return Number.isFinite(t) ? t : null;
7688
+ }
7689
+ return null;
7690
+ }
7691
+ function detectSeries(values) {
7692
+ if (!Array.isArray(values) || values.length < 2) return NO_SERIES;
7693
+ if (values.every(isFiniteNumber)) {
7694
+ const nums = values;
7695
+ const first = nums[0];
7696
+ const second = nums[1];
7697
+ if (first === void 0 || second === void 0) return NO_SERIES;
7698
+ const step = second - first;
7699
+ for (let i = 1; i < nums.length; i += 1) {
7700
+ const prev = nums[i - 1];
7701
+ const cur = nums[i];
7702
+ if (prev === void 0 || cur === void 0) return NO_SERIES;
7703
+ if (cur - prev !== step) return NO_SERIES;
7704
+ }
7705
+ return { kind: "number", step };
7706
+ }
7707
+ const stamps = values.map(dateMs);
7708
+ if (stamps.every((t) => t !== null)) {
7709
+ const first = stamps[0];
7710
+ const second = stamps[1];
7711
+ if (first === void 0 || second === void 0) return NO_SERIES;
7712
+ const deltaMs = second - first;
7713
+ if (deltaMs % MS_PER_DAY !== 0) return NO_SERIES;
7714
+ for (let i = 1; i < stamps.length; i += 1) {
7715
+ const prev = stamps[i - 1];
7716
+ const cur = stamps[i];
7717
+ if (prev === void 0 || cur === void 0) return NO_SERIES;
7718
+ if (cur - prev !== deltaMs) return NO_SERIES;
7719
+ }
7720
+ return { kind: "date", step: deltaMs / MS_PER_DAY };
7721
+ }
7722
+ return NO_SERIES;
7723
+ }
7724
+ function formatDateLike(ms, sample) {
7725
+ const iso = new Date(ms).toISOString();
7726
+ if (typeof sample === "string" && DATE_ONLY.test(sample)) return iso.slice(0, 10);
7727
+ return iso;
7728
+ }
7729
+ function extrapolate(line, series, k) {
7730
+ const last = line[line.length - 1];
7731
+ if (series.kind === "number" && isFiniteNumber(last)) {
7732
+ return last + k * series.step;
7733
+ }
7734
+ if (series.kind === "date") {
7735
+ const base = dateMs(last);
7736
+ if (base === null) return jsonable(last);
7737
+ return formatDateLike(base + k * series.step * MS_PER_DAY, last);
7738
+ }
7739
+ return jsonable(last);
7740
+ }
7741
+ function jsonable(value) {
7742
+ if (value instanceof Date) {
7743
+ const t = value.getTime();
7744
+ return Number.isFinite(t) ? value.toISOString() : null;
7745
+ }
7746
+ if (value === void 0) return null;
7747
+ return value;
7748
+ }
7749
+ function emptyResult(summary) {
7750
+ return { patches: [], skipped: [], summary };
7751
+ }
7752
+ function fillRange(request) {
7753
+ const { source, target, columnIds, rowIds } = request;
7754
+ const mode = request.mode ?? "series";
7755
+ const isWritable = request.isColumnWritable ?? (() => true);
7756
+ const direction = directionOf(source, target);
7757
+ if (!direction) {
7758
+ return emptyResult(
7759
+ "Filled 0 cells because the fill area does not touch the selection."
7760
+ );
7761
+ }
7762
+ if (rowIds.length === 0 || columnIds.length === 0) {
7763
+ return emptyResult("Filled 0 cells because the table is empty.");
7764
+ }
7765
+ const read = (r, c) => {
7766
+ try {
7767
+ return { ok: true, value: request.valueAt(r, c) };
7768
+ } catch {
7769
+ return { ok: false, value: void 0 };
7770
+ }
7771
+ };
7772
+ const vertical = direction === "down" || direction === "up";
7773
+ const forward = direction === "down" || direction === "right";
7774
+ const fillStart = vertical ? source.top : source.left;
7775
+ const fillEnd = vertical ? source.bottom : source.right;
7776
+ const crossStart = vertical ? source.left : source.top;
7777
+ const crossEnd = vertical ? source.right : source.bottom;
7778
+ const maxFill = (vertical ? rowIds.length : columnIds.length) - 1;
7779
+ const maxCross = (vertical ? columnIds.length : rowIds.length) - 1;
7780
+ const rawFillFrom = vertical ? target.top : target.left;
7781
+ const rawFillTo = vertical ? target.bottom : target.right;
7782
+ const rawCrossFrom = vertical ? target.left : target.top;
7783
+ const rawCrossTo = vertical ? target.right : target.bottom;
7784
+ const fillFrom = Math.max(rawFillFrom, 0);
7785
+ const fillTo = Math.min(rawFillTo, maxFill);
7786
+ const crossFrom = Math.max(rawCrossFrom, 0);
7787
+ const crossTo = Math.min(rawCrossTo, maxCross);
7788
+ const requested = Math.max(rawFillTo - rawFillFrom + 1, 0) * Math.max(rawCrossTo - rawCrossFrom + 1, 0);
7789
+ const inside = Math.max(fillTo - fillFrom + 1, 0) * Math.max(crossTo - crossFrom + 1, 0);
7790
+ const outside = Math.max(requested - inside, 0);
7791
+ const sourceBounds = asBounds(source);
7792
+ const patches = [];
7793
+ const skipped = [];
7794
+ const touchedColumns = /* @__PURE__ */ new Set();
7795
+ const lines = /* @__PURE__ */ new Map();
7796
+ const lineFor = (crossIndex) => {
7797
+ const srcCross = tileIndex(crossIndex, crossStart, crossEnd);
7798
+ const cached = lines.get(srcCross);
7799
+ if (cached) return cached;
7800
+ const values = [];
7801
+ const reads = [];
7802
+ for (let f = fillStart; f <= fillEnd; f += 1) {
7803
+ const r = vertical ? f : srcCross;
7804
+ const c = vertical ? srcCross : f;
7805
+ const got = read(r, c);
7806
+ values.push(got.value);
7807
+ reads.push(got.ok);
7808
+ }
7809
+ const series = mode === "series" ? detectSeries(values) : NO_SERIES;
7810
+ const line = { values, reads, series };
7811
+ lines.set(srcCross, line);
7812
+ return line;
7813
+ };
7814
+ for (let f = fillFrom; f <= fillTo; f += 1) {
7815
+ for (let x = crossFrom; x <= crossTo; x += 1) {
7816
+ const rowIndex = vertical ? f : x;
7817
+ const colIndex = vertical ? x : f;
7818
+ const rowId = rowIds[rowIndex];
7819
+ const columnId2 = columnIds[colIndex];
7820
+ if (rowId === void 0 || columnId2 === void 0) continue;
7821
+ if (boundsContain(sourceBounds, rowIndex, colIndex)) continue;
7822
+ if (!isWritable(columnId2)) {
7823
+ skipped.push({
7824
+ rowId,
7825
+ columnId: columnId2,
7826
+ reason: `${columnId2} cannot be filled because that column is read-only.`
7827
+ });
7828
+ continue;
7829
+ }
7830
+ const line = lineFor(x);
7831
+ if (line.series.kind !== "none") {
7832
+ const k = forward ? f - fillEnd : fillStart - f;
7833
+ patches.push({ rowId, columnId: columnId2, value: jsonable(extrapolate(line.values, line.series, k)) });
7834
+ touchedColumns.add(columnId2);
7835
+ continue;
7836
+ }
7837
+ const srcFill = tileIndex(f, fillStart, fillEnd);
7838
+ const patternIndex = srcFill - fillStart;
7839
+ if (line.reads[patternIndex] !== true) {
7840
+ skipped.push({
7841
+ rowId,
7842
+ columnId: columnId2,
7843
+ reason: `The value to copy into ${columnId2} could not be read, so that cell was left alone.`
7844
+ });
7845
+ continue;
7846
+ }
7847
+ patches.push({ rowId, columnId: columnId2, value: jsonable(line.values[patternIndex]) });
7848
+ touchedColumns.add(columnId2);
7849
+ }
7850
+ }
7851
+ return { patches, skipped, summary: summarize(direction, patches, skipped, touchedColumns, outside) };
7852
+ }
7853
+ function summarize(direction, patches, skipped, touchedColumns, outside) {
7854
+ const parts = [];
7855
+ if (patches.length === 0) {
7856
+ parts.push("Filled 0 cells because there was nothing to fill.");
7857
+ } else if (direction === "down" || direction === "up") {
7858
+ const word = direction === "down" ? "down" : "up";
7859
+ parts.push(
7860
+ `Filled ${plural(patches.length, "cell", "cells")} ${word} ${plural(touchedColumns.size, "column", "columns")}.`
7861
+ );
7862
+ } else {
7863
+ const word = direction === "right" ? "right" : "left";
7864
+ parts.push(
7865
+ `Filled ${plural(patches.length, "cell", "cells")} ${word} across ${plural(touchedColumns.size, "column", "columns")}.`
7866
+ );
7867
+ }
7868
+ if (skipped.length > 0) {
7869
+ parts.push(`Skipped ${plural(skipped.length, "cell", "cells")} that could not be filled.`);
7870
+ }
7871
+ if (outside > 0) {
7872
+ parts.push(`Ignored ${plural(outside, "cell", "cells")} outside the table.`);
7873
+ }
7874
+ return parts.join(" ");
7875
+ }
7876
+ function fillDown(request) {
7877
+ const { range, ...rest } = request;
7878
+ if (!isValidRect(range)) {
7879
+ return emptyResult("Filled 0 cells because the selection is not a rectangle.");
7880
+ }
7881
+ if (range.top === range.bottom) {
7882
+ return emptyResult("Filled 0 cells because the selection is a single row.");
7883
+ }
7884
+ return fillRange({
7885
+ ...rest,
7886
+ source: { top: range.top, left: range.left, bottom: range.top, right: range.right },
7887
+ target: { top: range.top + 1, left: range.left, bottom: range.bottom, right: range.right }
7888
+ });
7889
+ }
7890
+ function fillTargetFor(source, toRow, toColumn) {
7891
+ if (!isValidRect(source)) return null;
7892
+ if (!Number.isInteger(toRow) || !Number.isInteger(toColumn)) return null;
7893
+ const below = toRow - source.bottom;
7894
+ const above = source.top - toRow;
7895
+ const rightOf = toColumn - source.right;
7896
+ const leftOf = source.left - toColumn;
7897
+ const vertical = Math.max(below, above, 0);
7898
+ const horizontal = Math.max(rightOf, leftOf, 0);
7899
+ if (vertical === 0 && horizontal === 0) return null;
7900
+ if (vertical >= horizontal) {
7901
+ if (below > 0) {
7902
+ return { top: source.bottom + 1, bottom: toRow, left: source.left, right: source.right };
7903
+ }
7904
+ return { top: toRow, bottom: source.top - 1, left: source.left, right: source.right };
7905
+ }
7906
+ if (rightOf > 0) {
7907
+ return { top: source.top, bottom: source.bottom, left: source.right + 1, right: toColumn };
7908
+ }
7909
+ return { top: source.top, bottom: source.bottom, left: toColumn, right: source.left - 1 };
7910
+ }
7911
+
7912
+ // src/data-table/cell-undo.ts
7913
+ var DEFAULT_UNDO_DEPTH = 100;
7914
+ function createUndoStack(depth = DEFAULT_UNDO_DEPTH) {
7915
+ const bounded = Number.isFinite(depth) && depth > 0 ? Math.floor(depth) : 0;
7916
+ return { past: [], future: [], depth: bounded };
7917
+ }
7918
+ function pushUndo(stack, entry) {
7919
+ if (stack.depth <= 0) {
7920
+ return { past: [], future: [], depth: stack.depth };
7921
+ }
7922
+ const past = [...stack.past, entry];
7923
+ while (past.length > stack.depth) past.shift();
7924
+ return { past, future: [], depth: stack.depth };
7925
+ }
7926
+ function popUndo(stack) {
7927
+ const entry = stack.past[stack.past.length - 1];
7928
+ if (entry === void 0) return { stack, entry: null };
7929
+ return {
7930
+ stack: {
7931
+ past: stack.past.slice(0, -1),
7932
+ future: [...stack.future, entry],
7933
+ depth: stack.depth
7934
+ },
7935
+ entry
7936
+ };
7937
+ }
7938
+ function popRedo(stack) {
7939
+ const entry = stack.future[stack.future.length - 1];
7940
+ if (entry === void 0) return { stack, entry: null };
7941
+ const past = [...stack.past, entry];
7942
+ while (past.length > stack.depth) past.shift();
7943
+ return {
7944
+ stack: { past, future: stack.future.slice(0, -1), depth: stack.depth },
7945
+ entry
7946
+ };
7947
+ }
7948
+ function keyOf(patch) {
7949
+ return JSON.stringify([patch.rowId, patch.columnId]);
7950
+ }
7951
+ function undoEntryFor(before, after, label) {
7952
+ const priors = /* @__PURE__ */ new Map();
7953
+ for (const patch of before) priors.set(keyOf(patch), patch);
7954
+ const undo = after.map((patch) => {
7955
+ const prior = priors.get(keyOf(patch));
7956
+ return {
7957
+ rowId: patch.rowId,
7958
+ columnId: patch.columnId,
7959
+ value: prior ? prior.value : null
7960
+ };
7961
+ });
7962
+ return {
7963
+ label,
7964
+ undo,
7965
+ redo: after.map((patch) => ({
7966
+ rowId: patch.rowId,
7967
+ columnId: patch.columnId,
7968
+ value: patch.value
7969
+ }))
7970
+ };
7971
+ }
7972
+ function canUndo(stack) {
7973
+ return stack.depth > 0 && stack.past.length > 0;
7974
+ }
7975
+ function canRedo(stack) {
7976
+ return stack.depth > 0 && stack.future.length > 0;
7977
+ }
7978
+
7979
+ // src/data-table/useSpreadsheetGrid.ts
7980
+ function targetsAnEditor(target) {
7981
+ const element = target instanceof Element ? target : null;
7982
+ if (!element) return false;
7983
+ return element.closest("input, textarea, select, [contenteditable='true']") !== null;
7984
+ }
7985
+ function useSpreadsheetGrid(options) {
7986
+ const [range, setRange] = (0, import_react15.useState)(null);
7987
+ const [fillTarget, setFillTarget] = (0, import_react15.useState)(null);
7988
+ const [stack, setStack] = (0, import_react15.useState)(() => createUndoStack(options?.undoDepth ?? 100));
7989
+ const draggingRef = (0, import_react15.useRef)(null);
7990
+ const optionsRef = (0, import_react15.useRef)(options);
7991
+ optionsRef.current = options;
7992
+ const rowIds = options?.rowIds ?? [];
7993
+ const columnIds = (0, import_react15.useMemo)(
7994
+ () => options ? options.columnIds.filter((id) => id !== "") : [],
7995
+ [options]
7996
+ );
7997
+ (0, import_react15.useEffect)(() => {
7998
+ if (!range) return;
7999
+ const rowSet = new Set(rowIds);
8000
+ const colSet = new Set(columnIds);
8001
+ const alive = rowSet.has(range.anchor.rowId) && rowSet.has(range.focus.rowId) && colSet.has(range.anchor.fieldName) && colSet.has(range.focus.fieldName);
8002
+ if (!alive) setRange(null);
8003
+ }, [range, rowIds, columnIds]);
8004
+ const bounds = (0, import_react15.useMemo)(
8005
+ () => range ? rangeBounds(range, rowIds, columnIds) : null,
8006
+ [range, rowIds, columnIds]
8007
+ );
8008
+ const rowIndexById = (0, import_react15.useMemo)(() => {
8009
+ const map = /* @__PURE__ */ new Map();
8010
+ for (let i = 0; i < rowIds.length; i += 1) map.set(rowIds[i], i);
8011
+ return map;
8012
+ }, [rowIds]);
8013
+ const columnIndexById = (0, import_react15.useMemo)(() => {
8014
+ const map = /* @__PURE__ */ new Map();
8015
+ for (let i = 0; i < columnIds.length; i += 1) map.set(columnIds[i], i);
8016
+ return map;
8017
+ }, [columnIds]);
8018
+ const applyPatches = (0, import_react15.useCallback)(
8019
+ async (patches, intent, label) => {
8020
+ const current = optionsRef.current;
8021
+ if (!current || patches.length === 0) return;
8022
+ const depth = current.undoDepth ?? 100;
8023
+ if (depth > 0 && intent !== "undo" && intent !== "redo") {
8024
+ const before = patches.map((patch) => ({
8025
+ rowId: patch.rowId,
8026
+ columnId: patch.columnId,
8027
+ value: current.valueAt(patch.rowId, patch.columnId) ?? null
8028
+ }));
8029
+ setStack((previous) => pushUndo(previous, undoEntryFor(before, patches, label)));
8030
+ }
8031
+ await current.onPatch(patches, intent);
8032
+ },
8033
+ []
8034
+ );
8035
+ const readValueAt = (0, import_react15.useCallback)((rowIndex, columnIndex) => {
8036
+ const current = optionsRef.current;
8037
+ const rowId = current?.rowIds[rowIndex];
8038
+ const columnId2 = current?.columnIds[columnIndex];
8039
+ if (!current || rowId === void 0 || columnId2 === void 0) return null;
8040
+ return current.valueAt(rowId, columnId2);
8041
+ }, []);
8042
+ const fillRequestBase = (0, import_react15.useCallback)(() => {
8043
+ const current = optionsRef.current;
8044
+ if (!current) return null;
8045
+ return {
8046
+ valueAt: readValueAt,
8047
+ columnIds: [...current.columnIds],
8048
+ rowIds: [...current.rowIds],
8049
+ isColumnWritable: current.isColumnWritable
8050
+ };
8051
+ }, [readValueAt]);
8052
+ const announceSkips = (0, import_react15.useCallback)(
8053
+ (skipped, summary) => {
8054
+ const current = optionsRef.current;
8055
+ if (!current?.notify) return;
8056
+ if (skipped.length === 0) {
8057
+ current.notify(summary);
8058
+ return;
8059
+ }
8060
+ const first = skipped[0];
8061
+ const reason = current.columnRefusal?.(first.columnId) ?? first.reason;
8062
+ const rest = skipped.length > 1 ? ` ${skipped.length - 1} more were left alone.` : "";
8063
+ current.notify(`${summary} ${reason}${rest}`);
8064
+ },
8065
+ []
8066
+ );
8067
+ const doFillDown = (0, import_react15.useCallback)(async () => {
8068
+ const base = fillRequestBase();
8069
+ if (!base || !bounds) return;
8070
+ const result = fillDown({
8071
+ ...base,
8072
+ range: { top: bounds.r0, left: bounds.c0, bottom: bounds.r1, right: bounds.c1 }
8073
+ });
8074
+ await applyPatches(result.patches, "fill-down", "Filled down");
8075
+ announceSkips(result.skipped, result.summary);
8076
+ }, [fillRequestBase, bounds, applyPatches, announceSkips]);
8077
+ const doClear = (0, import_react15.useCallback)(async () => {
8078
+ const current = optionsRef.current;
8079
+ if (!current || !bounds) return;
8080
+ const patches = [];
8081
+ const skipped = [];
8082
+ for (let r = bounds.r0; r <= bounds.r1; r += 1) {
8083
+ for (let c = bounds.c0; c <= bounds.c1; c += 1) {
8084
+ const rowId = current.rowIds[r];
8085
+ const columnId2 = current.columnIds[c];
8086
+ if (rowId === void 0 || columnId2 === void 0) continue;
8087
+ if (!current.isColumnWritable(columnId2)) {
8088
+ skipped.push({
8089
+ columnId: columnId2,
8090
+ reason: current.columnRefusal?.(columnId2) ?? `${columnId2} cannot be cleared.`
8091
+ });
8092
+ continue;
8093
+ }
8094
+ patches.push({ rowId, columnId: columnId2, value: null });
8095
+ }
8096
+ }
8097
+ await applyPatches(patches, "clear", "Cleared");
8098
+ announceSkips(skipped, `Cleared ${patches.length} cells.`);
8099
+ }, [bounds, applyPatches, announceSkips]);
8100
+ const doUndo = (0, import_react15.useCallback)(async () => {
8101
+ const taken = popUndo(stack);
8102
+ if (!taken.entry) return;
8103
+ setStack(taken.stack);
8104
+ await applyPatches(taken.entry.undo, "undo", taken.entry.label);
8105
+ optionsRef.current?.notify?.(taken.entry.label);
8106
+ }, [stack, applyPatches]);
8107
+ const doRedo = (0, import_react15.useCallback)(async () => {
8108
+ const taken = popRedo(stack);
8109
+ if (!taken.entry) return;
8110
+ setStack(taken.stack);
8111
+ await applyPatches(taken.entry.redo, "redo", taken.entry.label);
8112
+ }, [stack, applyPatches]);
8113
+ const move = (0, import_react15.useCallback)(
8114
+ (direction, extend) => {
8115
+ setRange((previous) => {
8116
+ const from = previous ? previous.focus : null;
8117
+ const next = moveSelection(from, direction, rowIds, columnIds);
8118
+ if (!next) return previous;
8119
+ if (extend && previous) return { anchor: previous.anchor, focus: next };
8120
+ return { anchor: next, focus: next };
8121
+ });
8122
+ },
8123
+ [rowIds, columnIds]
8124
+ );
8125
+ const onKeyDown = (0, import_react15.useCallback)(
8126
+ (event) => {
8127
+ const current = optionsRef.current;
8128
+ if (!current) return;
8129
+ if (targetsAnEditor(event.target)) return;
8130
+ const accel = event.metaKey || event.ctrlKey;
8131
+ if (accel && (event.key === "z" || event.key === "Z")) {
8132
+ event.preventDefault();
8133
+ if (event.shiftKey) void doRedo();
8134
+ else void doUndo();
8135
+ return;
8136
+ }
8137
+ if (accel && (event.key === "y" || event.key === "Y")) {
8138
+ event.preventDefault();
8139
+ void doRedo();
8140
+ return;
8141
+ }
8142
+ const action = classifyGridKey(event);
8143
+ if (!action) return;
8144
+ switch (action.kind) {
8145
+ case "move":
8146
+ event.preventDefault();
8147
+ move(action.move, false);
8148
+ return;
8149
+ case "extend":
8150
+ event.preventDefault();
8151
+ move(action.move, true);
8152
+ return;
8153
+ case "selectAll": {
8154
+ event.preventDefault();
8155
+ const all = allRange(rowIds, columnIds);
8156
+ if (all) setRange(all);
8157
+ return;
8158
+ }
8159
+ case "selectRow": {
8160
+ event.preventDefault();
8161
+ const focused = range?.focus;
8162
+ const next = focused ? rowRange(focused.rowId, columnIds) : null;
8163
+ if (next) setRange(next);
8164
+ return;
8165
+ }
8166
+ case "selectColumn": {
8167
+ event.preventDefault();
8168
+ const focused = range?.focus;
8169
+ const next = focused ? columnRange(focused.fieldName, rowIds) : null;
8170
+ if (next) setRange(next);
8171
+ return;
8172
+ }
8173
+ case "clearCell":
8174
+ event.preventDefault();
8175
+ void doClear();
8176
+ return;
8177
+ case "fillDown":
8178
+ event.preventDefault();
8179
+ void doFillDown();
8180
+ return;
8181
+ case "escape":
8182
+ setRange(null);
8183
+ return;
8184
+ default:
8185
+ return;
8186
+ }
8187
+ },
8188
+ [move, range, rowIds, columnIds, doClear, doFillDown, doUndo, doRedo]
8189
+ );
8190
+ const selectionAsTsv = (0, import_react15.useCallback)(() => {
8191
+ const current = optionsRef.current;
8192
+ if (!current || !range) return "";
8193
+ const grid = rangeRows(range, rowIds, columnIds).map(
8194
+ (row) => row.map((address) => current.valueAt(address.rowId, address.fieldName))
8195
+ );
8196
+ return gridToTsv(grid);
8197
+ }, [range, rowIds, columnIds]);
8198
+ const onCopy = (0, import_react15.useCallback)(
8199
+ (event) => {
8200
+ if (!optionsRef.current || !range || targetsAnEditor(event.target)) return;
8201
+ const text = selectionAsTsv();
8202
+ if (!text) return;
8203
+ event.preventDefault();
8204
+ event.clipboardData?.setData("text/plain", text);
8205
+ },
8206
+ [range, selectionAsTsv]
8207
+ );
8208
+ const onCut = (0, import_react15.useCallback)(
8209
+ (event) => {
8210
+ if (!optionsRef.current || !range || targetsAnEditor(event.target)) return;
8211
+ const text = selectionAsTsv();
8212
+ if (!text) return;
8213
+ event.preventDefault();
8214
+ event.clipboardData?.setData("text/plain", text);
8215
+ void doClear();
8216
+ },
8217
+ [range, selectionAsTsv, doClear]
8218
+ );
8219
+ const onPaste = (0, import_react15.useCallback)(
8220
+ (event) => {
8221
+ const current = optionsRef.current;
8222
+ if (!current || current.paste === false || !range || targetsAnEditor(event.target)) return;
8223
+ const text = event.clipboardData?.getData("text/plain") ?? "";
8224
+ if (!text) return;
8225
+ event.preventDefault();
8226
+ const block = parseClipboardGrid(text);
8227
+ const start = range.focus;
8228
+ const startRow = rowIds.indexOf(start.rowId);
8229
+ const startCol = columnIds.indexOf(start.fieldName);
8230
+ if (startRow === -1 || startCol === -1) return;
8231
+ const patches = [];
8232
+ const skipped = [];
8233
+ let outside = 0;
8234
+ for (let r = 0; r < block.length; r += 1) {
8235
+ const line = block[r] ?? [];
8236
+ for (let c = 0; c < line.length; c += 1) {
8237
+ const rowId = rowIds[startRow + r];
8238
+ const columnId2 = columnIds[startCol + c];
8239
+ if (rowId === void 0 || columnId2 === void 0) {
8240
+ outside += 1;
8241
+ continue;
8242
+ }
8243
+ if (!current.isColumnWritable(columnId2)) {
8244
+ skipped.push({
8245
+ columnId: columnId2,
8246
+ reason: current.columnRefusal?.(columnId2) ?? `${columnId2} cannot take a pasted value.`
8247
+ });
8248
+ continue;
8249
+ }
8250
+ patches.push({ rowId, columnId: columnId2, value: line[c] ?? null });
8251
+ }
8252
+ }
8253
+ void applyPatches(patches, "paste", "Pasted");
8254
+ const tail = outside > 0 ? ` ${outside} landed past the edge of the table and were ignored.` : "";
8255
+ announceSkips(skipped, `Pasted ${patches.length} cells.${tail}`);
8256
+ },
8257
+ [range, rowIds, columnIds, applyPatches, announceSkips]
8258
+ );
8259
+ const cellProps = (0, import_react15.useCallback)(
8260
+ (rowId, columnId2) => {
8261
+ const address = { rowId, fieldName: columnId2 };
8262
+ const rowIndex = rowIndexById.get(rowId) ?? -1;
8263
+ const colIndex = columnIndexById.get(columnId2) ?? -1;
8264
+ const selected = bounds !== null && rowIndex >= bounds.r0 && rowIndex <= bounds.r1 && colIndex >= bounds.c0 && colIndex <= bounds.c1;
8265
+ const active = range !== null && sameCell(range.focus, address);
8266
+ const inFillPreview = fillTarget !== null && rowIndex >= fillTarget.top && rowIndex <= fillTarget.bottom && colIndex >= fillTarget.left && colIndex <= fillTarget.right;
8267
+ return {
8268
+ "data-matrx-cell-row": rowId,
8269
+ "data-matrx-cell-col": columnId2,
8270
+ ...selected ? { "data-matrx-cell-selected": "true" } : {},
8271
+ ...active ? { "data-matrx-cell-active": "true" } : {},
8272
+ ...inFillPreview ? { "data-matrx-cell-fill-preview": "true" } : {},
8273
+ onMouseDown: (event) => {
8274
+ if (event.button !== 0) return;
8275
+ event.preventDefault();
8276
+ setRange(
8277
+ (previous) => event.shiftKey && previous ? { anchor: previous.anchor, focus: address } : { anchor: address, focus: address }
8278
+ );
8279
+ draggingRef.current = "select";
8280
+ },
8281
+ onMouseEnter: () => {
8282
+ if (draggingRef.current === "select") {
8283
+ setRange((previous) => previous ? { anchor: previous.anchor, focus: address } : previous);
8284
+ return;
8285
+ }
8286
+ if (draggingRef.current === "fill" && bounds) {
8287
+ setFillTarget(
8288
+ fillTargetFor(
8289
+ { top: bounds.r0, left: bounds.c0, bottom: bounds.r1, right: bounds.c1 },
8290
+ rowIndex,
8291
+ colIndex
8292
+ )
8293
+ );
8294
+ }
8295
+ }
8296
+ };
8297
+ },
8298
+ [rowIndexById, columnIndexById, bounds, range, fillTarget]
8299
+ );
8300
+ const isFillHandleCell = (0, import_react15.useCallback)(
8301
+ (rowId, columnId2) => {
8302
+ const current = optionsRef.current;
8303
+ if (!current || current.fillHandle === false || !bounds) return false;
8304
+ return rowIds[bounds.r1] === rowId && columnIds[bounds.c1] === columnId2;
8305
+ },
8306
+ [bounds, rowIds, columnIds]
8307
+ );
8308
+ const startFillDrag = (0, import_react15.useCallback)(
8309
+ (event) => {
8310
+ event.preventDefault();
8311
+ event.stopPropagation();
8312
+ draggingRef.current = "fill";
8313
+ },
8314
+ []
8315
+ );
8316
+ const fillTargetRef = (0, import_react15.useRef)(null);
8317
+ fillTargetRef.current = fillTarget;
8318
+ const boundsRef = (0, import_react15.useRef)(null);
8319
+ boundsRef.current = bounds;
8320
+ (0, import_react15.useEffect)(() => {
8321
+ if (!options) return;
8322
+ const commit = () => {
8323
+ const wasDragging = draggingRef.current;
8324
+ draggingRef.current = null;
8325
+ if (wasDragging !== "fill") return;
8326
+ const target = fillTargetRef.current;
8327
+ const source = boundsRef.current;
8328
+ setFillTarget(null);
8329
+ if (!target || !source) return;
8330
+ const base = fillRequestBase();
8331
+ if (!base) return;
8332
+ const result = fillRange({
8333
+ ...base,
8334
+ source: { top: source.r0, left: source.c0, bottom: source.r1, right: source.c1 },
8335
+ target
8336
+ });
8337
+ void applyPatches(result.patches, "fill", "Filled");
8338
+ announceSkips(result.skipped, result.summary);
8339
+ };
8340
+ window.addEventListener("mouseup", commit);
8341
+ return () => window.removeEventListener("mouseup", commit);
8342
+ }, [options, fillRequestBase, applyPatches, announceSkips]);
8343
+ return {
8344
+ range,
8345
+ bounds,
8346
+ enabled: options !== null,
8347
+ containerProps: {
8348
+ tabIndex: options ? 0 : -1,
8349
+ onKeyDown,
8350
+ onCopy,
8351
+ onCut,
8352
+ onPaste
8353
+ },
8354
+ cellProps,
8355
+ isFillHandleCell,
8356
+ startFillDrag,
8357
+ canUndo: stack.past.length > 0,
8358
+ canRedo: stack.future.length > 0
8359
+ };
8360
+ }
8361
+
8362
+ // src/data-table/coverage.ts
8363
+ var PARTIAL_BADGE = "Partial";
8364
+ var UNKNOWN_TOTAL_BADGE = "Unknown total";
8365
+ var NUMBERS = new Intl.NumberFormat("en-US");
8366
+ function formatNumber(value) {
8367
+ try {
8368
+ return NUMBERS.format(value);
8369
+ } catch {
8370
+ return String(value);
8371
+ }
8372
+ }
8373
+ function pluralize(noun) {
8374
+ const word = noun.trim();
8375
+ if (!word) return "rows";
8376
+ const lower = word.toLowerCase();
8377
+ if (/[^aeiou]y$/.test(lower)) return `${word.slice(0, -1)}ies`;
8378
+ if (/(s|x|z|ch|sh)$/.test(lower)) return `${word}es`;
8379
+ return `${word}s`;
8380
+ }
8381
+ function quantity(value, noun) {
8382
+ return `${formatNumber(value)} ${value === 1 ? noun.trim() || "row" : pluralize(noun)}`;
8383
+ }
8384
+ function toWhole(value) {
8385
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? Math.floor(value) : 0;
8386
+ }
8387
+ function toOptionalWhole(value) {
8388
+ if (typeof value !== "number" || !Number.isFinite(value) || value < 0) return void 0;
8389
+ return Math.floor(value);
8390
+ }
8391
+ function describeCoverage(input) {
8392
+ const safe = input && typeof input === "object" ? input : { loaded: 0, answeredBy: "client" };
8393
+ const loaded = toWhole(safe.loaded);
8394
+ const matched = toOptionalWhole(safe.matched);
8395
+ const total = toOptionalWhole(safe.total);
8396
+ const cap = toOptionalWhole(safe.cap);
8397
+ const answeredBy = safe.answeredBy === "source" ? "source" : "client";
8398
+ const noun = typeof safe.noun === "string" && safe.noun.trim() ? safe.noun.trim() : "row";
8399
+ const reference = matched ?? total;
8400
+ const capReached = cap !== void 0 && cap > 0 && loaded >= cap;
8401
+ if (reference !== void 0 && loaded >= reference) {
8402
+ return {
8403
+ scope: "all",
8404
+ complete: true,
8405
+ count: { value: matched ?? reference, exact: true },
8406
+ notice: "",
8407
+ badge: ""
8408
+ };
8409
+ }
8410
+ if (answeredBy === "source" && matched !== void 0 && !capReached) {
8411
+ return { scope: "all", complete: true, count: { value: matched, exact: true }, notice: "", badge: "" };
8412
+ }
8413
+ if (capReached && cap !== void 0) {
8414
+ const rest = reference !== void 0 ? `${quantity(reference, noun)} in this table; the rest were not looked at.` : "Anything past that was not looked at.";
8415
+ return {
8416
+ scope: "capped",
8417
+ complete: false,
8418
+ count: { value: loaded, exact: false },
8419
+ notice: `This filter searched the first ${quantity(cap, noun)} only. ${rest}`,
8420
+ badge: PARTIAL_BADGE
8421
+ };
8422
+ }
8423
+ if (answeredBy === "client" && reference !== void 0) {
8424
+ return {
8425
+ scope: "loaded",
8426
+ complete: false,
8427
+ count: { value: loaded, exact: false },
8428
+ notice: `Filtering the ${quantity(loaded, noun)} loaded so far, not all ${quantity(reference, noun)} in this table.`,
8429
+ badge: PARTIAL_BADGE
8430
+ };
8431
+ }
8432
+ if (answeredBy === "source" && total !== void 0) {
8433
+ return {
8434
+ scope: "all",
8435
+ complete: false,
8436
+ count: { value: loaded, exact: false },
8437
+ notice: `Showing the ${quantity(loaded, noun)} loaded so far out of ${quantity(total, noun)} in this table.`,
8438
+ badge: PARTIAL_BADGE
8439
+ };
8440
+ }
8441
+ return {
8442
+ scope: answeredBy === "source" ? "all" : "loaded",
8443
+ complete: false,
8444
+ count: { value: loaded, exact: false },
8445
+ notice: `Showing the ${quantity(loaded, noun)} loaded so far. The total number of ${pluralize(noun)} in this table is not known.`,
8446
+ badge: UNKNOWN_TOTAL_BADGE
8447
+ };
8448
+ }
8449
+
6793
8450
  // src/data-table/ranked-search.ts
6794
8451
  function inferTableSearchRole(field) {
6795
8452
  const key = field.replace(/([a-z])([A-Z])/g, "$1_$2").toLowerCase().replace(/[\s-]+/g, "_");
@@ -6850,7 +8507,7 @@ function rankTableSearchRows(rows, columns, query, options = {}, extraText) {
6850
8507
  }
6851
8508
 
6852
8509
  // src/data-table/ColumnManager.tsx
6853
- var import_react15 = require("react");
8510
+ var import_react16 = require("react");
6854
8511
  var import_core2 = require("@dnd-kit/core");
6855
8512
  var import_jsx_runtime33 = require("react/jsx-runtime");
6856
8513
  function ColumnManager({
@@ -6861,15 +8518,15 @@ function ColumnManager({
6861
8518
  hidden,
6862
8519
  onApply
6863
8520
  }) {
6864
- const byId = (0, import_react15.useMemo)(
8521
+ const byId = (0, import_react16.useMemo)(
6865
8522
  () => new Map(columns.map((column) => [columnId(column), column])),
6866
8523
  [columns]
6867
8524
  );
6868
- const [draftOrder, setDraftOrder] = (0, import_react15.useState)(order);
6869
- const [draftHidden, setDraftHidden] = (0, import_react15.useState)(() => new Set(hidden));
6870
- const [draggedColumnId, setDraggedColumnId] = (0, import_react15.useState)(null);
6871
- const [dropTarget, setDropTarget] = (0, import_react15.useState)(null);
6872
- (0, import_react15.useEffect)(() => {
8525
+ const [draftOrder, setDraftOrder] = (0, import_react16.useState)(order);
8526
+ const [draftHidden, setDraftHidden] = (0, import_react16.useState)(() => new Set(hidden));
8527
+ const [draggedColumnId, setDraggedColumnId] = (0, import_react16.useState)(null);
8528
+ const [dropTarget, setDropTarget] = (0, import_react16.useState)(null);
8529
+ (0, import_react16.useEffect)(() => {
6873
8530
  if (!open) return;
6874
8531
  setDraftOrder(order);
6875
8532
  setDraftHidden(new Set(hidden));
@@ -7399,7 +9056,7 @@ function parseTableViewSnapshot(raw) {
7399
9056
  }
7400
9057
 
7401
9058
  // src/data-table/MatrxUuidCell.tsx
7402
- var import_react16 = require("react");
9059
+ var import_react17 = require("react");
7403
9060
  var import_jsx_runtime34 = require("react/jsx-runtime");
7404
9061
  var SHORT_LEN = 8;
7405
9062
  function shortUuid(id, len = SHORT_LEN) {
@@ -7439,11 +9096,11 @@ function MatrxUuidCell({
7439
9096
  }) {
7440
9097
  const { resolveEntityDoors, ResourcePeek } = useMatrxDataTableHost();
7441
9098
  const { notify, notice } = useTableNotice();
7442
- const [copied, setCopied] = (0, import_react16.useState)(false);
7443
- const [opening, setOpening] = (0, import_react16.useState)(false);
7444
- const [peekOpen, setPeekOpen] = (0, import_react16.useState)(false);
7445
- const timer = (0, import_react16.useRef)(null);
7446
- (0, import_react16.useEffect)(
9099
+ const [copied, setCopied] = (0, import_react17.useState)(false);
9100
+ const [opening, setOpening] = (0, import_react17.useState)(false);
9101
+ const [peekOpen, setPeekOpen] = (0, import_react17.useState)(false);
9102
+ const timer = (0, import_react17.useRef)(null);
9103
+ (0, import_react17.useEffect)(
7447
9104
  () => () => {
7448
9105
  if (timer.current) clearTimeout(timer.current);
7449
9106
  },
@@ -7738,7 +9395,7 @@ function DataRowInspector({
7738
9395
  }
7739
9396
 
7740
9397
  // src/data-table/DataRowWindow.tsx
7741
- var import_react17 = require("react");
9398
+ var import_react18 = require("react");
7742
9399
  var import_jsx_runtime36 = require("react/jsx-runtime");
7743
9400
  function DataRowWindow({
7744
9401
  isOpen,
@@ -7759,8 +9416,8 @@ function DataRowWindow({
7759
9416
  const hasEdit = editContent != null;
7760
9417
  const hasTabs = hasEdit && children == null;
7761
9418
  const initialTab = defaultTab ?? (hasEdit ? "edit" : "view");
7762
- const [tab, setTab] = (0, import_react17.useState)(initialTab);
7763
- (0, import_react17.useEffect)(() => {
9419
+ const [tab, setTab] = (0, import_react18.useState)(initialTab);
9420
+ (0, import_react18.useEffect)(() => {
7764
9421
  if (!isOpen) return;
7765
9422
  setTab(defaultTab ?? (hasEdit ? "edit" : "view"));
7766
9423
  }, [isOpen, windowId, hasEdit, defaultTab]);
@@ -8639,7 +10296,7 @@ function DirtySavePill({
8639
10296
  }
8640
10297
 
8641
10298
  // src/data-table/EditableTableCell.tsx
8642
- var import_react18 = require("react");
10299
+ var import_react19 = require("react");
8643
10300
  var import_jsx_runtime39 = require("react/jsx-runtime");
8644
10301
  function CellEditShell({
8645
10302
  href,
@@ -8689,8 +10346,8 @@ function EditableTableCell({
8689
10346
  editTrigger = "click",
8690
10347
  editRequest
8691
10348
  }) {
8692
- const [open, setOpen] = (0, import_react18.useState)(false);
8693
- (0, import_react18.useEffect)(() => {
10349
+ const [open, setOpen] = (0, import_react19.useState)(false);
10350
+ (0, import_react19.useEffect)(() => {
8694
10351
  if (editRequest !== void 0) setOpen(true);
8695
10352
  }, [editRequest]);
8696
10353
  const usePencil = Boolean(href) || editTrigger === "pencil";
@@ -8783,10 +10440,10 @@ function InlineStringEditor({
8783
10440
  usePencil,
8784
10441
  editRequest
8785
10442
  }) {
8786
- const [editing, setEditing] = (0, import_react18.useState)(false);
8787
- const [draft, setDraft] = (0, import_react18.useState)(value);
8788
- const handledEditRequest = (0, import_react18.useRef)(void 0);
8789
- (0, import_react18.useEffect)(() => {
10443
+ const [editing, setEditing] = (0, import_react19.useState)(false);
10444
+ const [draft, setDraft] = (0, import_react19.useState)(value);
10445
+ const handledEditRequest = (0, import_react19.useRef)(void 0);
10446
+ (0, import_react19.useEffect)(() => {
8790
10447
  if (editRequest === void 0 || handledEditRequest.current === editRequest) return;
8791
10448
  handledEditRequest.current = editRequest;
8792
10449
  setDraft(value);
@@ -8942,8 +10599,8 @@ function TagsEditor({
8942
10599
  onCommit,
8943
10600
  onCancel
8944
10601
  }) {
8945
- const [tags, setTags] = (0, import_react18.useState)(value);
8946
- const [draft, setDraft] = (0, import_react18.useState)("");
10602
+ const [tags, setTags] = (0, import_react19.useState)(value);
10603
+ const [draft, setDraft] = (0, import_react19.useState)("");
8947
10604
  const add = (raw) => {
8948
10605
  const next = raw.trim();
8949
10606
  if (!next || tags.includes(next)) return;
@@ -9040,7 +10697,7 @@ function NumberEditor({
9040
10697
  onCommit,
9041
10698
  onCancel
9042
10699
  }) {
9043
- const [draft, setDraft] = (0, import_react18.useState)(String(value));
10700
+ const [draft, setDraft] = (0, import_react19.useState)(String(value));
9044
10701
  return /* @__PURE__ */ (0, import_jsx_runtime39.jsxs)("div", { className: "flex flex-col gap-2", children: [
9045
10702
  /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(
9046
10703
  Input,
@@ -9170,7 +10827,7 @@ function resetToolbarFacets(facets) {
9170
10827
  }
9171
10828
 
9172
10829
  // src/data-table/LayeredFilterBuilder.tsx
9173
- var import_react19 = require("react");
10830
+ var import_react20 = require("react");
9174
10831
  var import_jsx_runtime41 = require("react/jsx-runtime");
9175
10832
  function moveRule(rules, from, to) {
9176
10833
  if (from === to || to < 0 || to >= rules.length) return [...rules];
@@ -9187,8 +10844,8 @@ function LayeredFilterBuilder({
9187
10844
  maxRules = 20,
9188
10845
  label = "Advanced filters"
9189
10846
  }) {
9190
- const [open, setOpen] = (0, import_react19.useState)(false);
9191
- const [draggedIndex, setDraggedIndex] = (0, import_react19.useState)(null);
10847
+ const [open, setOpen] = (0, import_react20.useState)(false);
10848
+ const [draggedIndex, setDraggedIndex] = (0, import_react20.useState)(null);
9192
10849
  if (fields.length === 0) return null;
9193
10850
  const updateRule = (index, patch) => {
9194
10851
  onChange(
@@ -9669,7 +11326,7 @@ function TableToolbarAction({
9669
11326
  "aria-busy": busy || void 0,
9670
11327
  disabled,
9671
11328
  onClick,
9672
- children: (0, import_react20.isValidElement)(children) ? (0, import_react20.cloneElement)(children, { className: cn("h-5 w-5", children.props.className) }) : children
11329
+ children: (0, import_react21.isValidElement)(children) ? (0, import_react21.cloneElement)(children, { className: cn("h-5 w-5", children.props.className) }) : children
9673
11330
  }
9674
11331
  ) }) }),
9675
11332
  /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(TooltipContent, { children: tooltip ?? ariaLabel })
@@ -9732,7 +11389,7 @@ function MatrxDataTableWithUrlState({
9732
11389
  ...urlState.history === void 0 ? {} : { history: urlState.history },
9733
11390
  ...urlState.textHistory === void 0 ? {} : { textHistory: urlState.textHistory }
9734
11391
  });
9735
- (0, import_react20.useEffect)(() => {
11392
+ (0, import_react21.useEffect)(() => {
9736
11393
  if (activeUrlTableIds.has(urlState.id)) {
9737
11394
  throw new Error(
9738
11395
  `Duplicate MatrxDataTable urlState.id "${urlState.id}" on one page. Give every table a unique stable id.`
@@ -9823,6 +11480,11 @@ function opensRowHref(event) {
9823
11480
  function TableContextPassthrough({ children }) {
9824
11481
  return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(import_jsx_runtime42.Fragment, { children });
9825
11482
  }
11483
+ function sameWindow(a, b) {
11484
+ if (a === null || b === null) return a === b;
11485
+ return a.start === b.start && a.end === b.end && a.padStart === b.padStart && a.padEnd === b.padEnd && a.total === b.total && a.pinned.length === b.pinned.length;
11486
+ }
11487
+ var NO_GROUPING_VALUE = "__matrx_no_grouping__";
9826
11488
  function MatrxDataTableCore({
9827
11489
  data: suppliedData,
9828
11490
  columns,
@@ -9879,11 +11541,16 @@ function MatrxDataTableCore({
9879
11541
  mobileCardsBreakpoint = "sm",
9880
11542
  getRowHref,
9881
11543
  onRowOpen,
9882
- density
11544
+ density,
11545
+ grouping,
11546
+ virtualize,
11547
+ coverage,
11548
+ spreadsheet,
11549
+ rowHeight: explicitRowHeight
9883
11550
  }) {
9884
11551
  const appendQuery = query?.mode === "controlled-append" ? query : null;
9885
11552
  const appendQueryKey = appendQuery?.pagination.queryKey ?? null;
9886
- const committedAppendQueryKey = (0, import_react20.useRef)(appendQueryKey);
11553
+ const committedAppendQueryKey = (0, import_react21.useRef)(appendQueryKey);
9887
11554
  const appendSourceChanged = appendQuery !== null && committedAppendQueryKey.current !== appendQueryKey;
9888
11555
  const data = (appendQuery ? appendQuery.pagination.rows : suppliedData) ?? [];
9889
11556
  const isLoading = appendQuery ? appendQuery.pagination.loading : suppliedLoading;
@@ -9895,10 +11562,10 @@ function MatrxDataTableCore({
9895
11562
  const Link = host.Link ?? DefaultTableLink;
9896
11563
  const SidePanelSurface = host.SidePanelSurface ?? DefaultTableSidePanel;
9897
11564
  const { notify: toast, notice } = useTableNotice();
9898
- const [savedViewError, setSavedViewError] = (0, import_react20.useState)(null);
9899
- const [toolbarActionError, setToolbarActionError] = (0, import_react20.useState)(null);
9900
- const [toolbarActionPending, setToolbarActionPending] = (0, import_react20.useState)({ refresh: false, add: false });
9901
- const toolbarActionPendingRef = (0, import_react20.useRef)({ refresh: false, add: false });
11565
+ const [savedViewError, setSavedViewError] = (0, import_react21.useState)(null);
11566
+ const [toolbarActionError, setToolbarActionError] = (0, import_react21.useState)(null);
11567
+ const [toolbarActionPending, setToolbarActionPending] = (0, import_react21.useState)({ refresh: false, add: false });
11568
+ const toolbarActionPendingRef = (0, import_react21.useRef)({ refresh: false, add: false });
9902
11569
  const CHECKBOX_TAP_AREA = "h-4 w-4 touch-manipulation";
9903
11570
  const mobileCardsClass = mobileCardsBreakpoint === "lg" ? "lg:hidden" : "sm:hidden";
9904
11571
  const tableWithCardsClass = mobileCardsBreakpoint === "lg" ? "max-lg:hidden" : "max-sm:hidden";
@@ -9911,7 +11578,7 @@ function MatrxDataTableCore({
9911
11578
  const sourceOwnsIntelligentSearch = sourceProcessing?.intelligentSearch === "source";
9912
11579
  const sourceOwnsSearch = remoteQuery?.mode === "controlled" || appendQuery !== null && sourceProcessing === void 0 || sourceProcessing?.search === "source" || sourceOwnsIntelligentSearch;
9913
11580
  const intelligentSearchEnabled = toolbar?.intelligentSearch !== void 0 && (sourceOwnsIntelligentSearch || remoteQuery === null && !sourceOwnsSearch);
9914
- const emitControlledQueryChange = (0, import_react20.useCallback)(
11581
+ const emitControlledQueryChange = (0, import_react21.useCallback)(
9915
11582
  (patch, options) => {
9916
11583
  if (!controlledQuery) return;
9917
11584
  controlledQuery.onStateChange(
@@ -9920,8 +11587,8 @@ function MatrxDataTableCore({
9920
11587
  },
9921
11588
  [controlledQuery]
9922
11589
  );
9923
- const [internalSearch, setInternalSearch] = (0, import_react20.useState)("");
9924
- const [draggedRowId, setDraggedRowId] = (0, import_react20.useState)(null);
11590
+ const [internalSearch, setInternalSearch] = (0, import_react21.useState)("");
11591
+ const [draggedRowId, setDraggedRowId] = (0, import_react21.useState)(null);
9925
11592
  const hierarchySensors = (0, import_core3.useSensors)(
9926
11593
  (0, import_core3.useSensor)(import_core3.PointerSensor, {
9927
11594
  activationConstraint: { distance: COLUMN_REORDER_ACTIVATION_DISTANCE }
@@ -9937,7 +11604,7 @@ function MatrxDataTableCore({
9937
11604
  }
9938
11605
  toolbar?.onSearchChange?.(v);
9939
11606
  };
9940
- const [internalSearchMatchMode, setInternalSearchMatchMode] = (0, import_react20.useState)(
11607
+ const [internalSearchMatchMode, setInternalSearchMatchMode] = (0, import_react21.useState)(
9941
11608
  toolbar?.searchMatch?.defaultMode ?? "contains"
9942
11609
  );
9943
11610
  const searchMatchMode = controlledQuery ? controlledQuery.state.searchMatchMode ?? toolbar?.searchMatch?.defaultMode ?? "contains" : internalSearchMatchMode;
@@ -9948,7 +11615,7 @@ function MatrxDataTableCore({
9948
11615
  }
9949
11616
  setInternalSearchMatchMode(mode);
9950
11617
  };
9951
- const [internalSearchScope, setInternalSearchScope] = (0, import_react20.useState)("all");
11618
+ const [internalSearchScope, setInternalSearchScope] = (0, import_react21.useState)("all");
9952
11619
  const searchScope = controlledQuery ? controlledQuery.state.searchScope ?? "all" : internalSearchScope;
9953
11620
  const setSearchScope = (scope) => {
9954
11621
  if (controlledQuery) {
@@ -9957,7 +11624,7 @@ function MatrxDataTableCore({
9957
11624
  }
9958
11625
  setInternalSearchScope(scope);
9959
11626
  };
9960
- const [internalAnyOf, setInternalAnyOf] = (0, import_react20.useState)("");
11627
+ const [internalAnyOf, setInternalAnyOf] = (0, import_react21.useState)("");
9961
11628
  const toolbarAnyOfControlled = toolbar?.anyOf?.value !== void 0;
9962
11629
  const anyOfValue = controlledQuery ? controlledQuery.state.anyOf : toolbarAnyOfControlled ? toolbar?.anyOf?.value ?? "" : internalAnyOf;
9963
11630
  const setAnyOfValue = (v) => {
@@ -9968,7 +11635,7 @@ function MatrxDataTableCore({
9968
11635
  }
9969
11636
  toolbar?.anyOf?.onChange?.(v);
9970
11637
  };
9971
- const [internalLayeredFilters, setInternalLayeredFilters] = (0, import_react20.useState)([]);
11638
+ const [internalLayeredFilters, setInternalLayeredFilters] = (0, import_react21.useState)([]);
9972
11639
  const layeredFilters = controlledQuery ? controlledQuery.state.layeredFilters ?? [] : internalLayeredFilters;
9973
11640
  const setLayeredFilters = (next) => {
9974
11641
  if (controlledQuery) {
@@ -9977,7 +11644,7 @@ function MatrxDataTableCore({
9977
11644
  }
9978
11645
  setInternalLayeredFilters(next);
9979
11646
  };
9980
- const [internalColumnFilters, setInternalColumnFilters] = (0, import_react20.useState)({});
11647
+ const [internalColumnFilters, setInternalColumnFilters] = (0, import_react21.useState)({});
9981
11648
  const columnFilters = controlledQuery ? controlledQuery.state.columnFilters : internalColumnFilters;
9982
11649
  const setColumnFilter = (id, next) => {
9983
11650
  const nextFilters = { ...columnFilters, [id]: next };
@@ -9990,7 +11657,7 @@ function MatrxDataTableCore({
9990
11657
  }
9991
11658
  setInternalColumnFilters(nextFilters);
9992
11659
  };
9993
- const [internalSort, setInternalSort] = (0, import_react20.useState)(defaultSort);
11660
+ const [internalSort, setInternalSort] = (0, import_react21.useState)(defaultSort);
9994
11661
  const sort = controlledQuery ? controlledQuery.state.sort : internalSort;
9995
11662
  const setSort = (next) => {
9996
11663
  if (controlledQuery) {
@@ -9999,7 +11666,7 @@ function MatrxDataTableCore({
9999
11666
  }
10000
11667
  setInternalSort(next);
10001
11668
  };
10002
- const [internalPage, setInternalPage] = (0, import_react20.useState)(1);
11669
+ const [internalPage, setInternalPage] = (0, import_react21.useState)(1);
10003
11670
  const page = controlledQuery ? controlledQuery.state.page : internalPage;
10004
11671
  const setPage = (next) => {
10005
11672
  if (controlledQuery) {
@@ -10008,7 +11675,7 @@ function MatrxDataTableCore({
10008
11675
  }
10009
11676
  setInternalPage(next);
10010
11677
  };
10011
- const [internalPageSize, setInternalPageSize] = (0, import_react20.useState)(
11678
+ const [internalPageSize, setInternalPageSize] = (0, import_react21.useState)(
10012
11679
  defaultPageSize
10013
11680
  );
10014
11681
  const pageSize = controlledQuery ? controlledQuery.state.pageSize : internalPageSize;
@@ -10019,28 +11686,28 @@ function MatrxDataTableCore({
10019
11686
  }
10020
11687
  setInternalPageSize(next);
10021
11688
  };
10022
- const [uncontrolledSelectedId, setUncontrolledSelectedId] = (0, import_react20.useState)(null);
11689
+ const [uncontrolledSelectedId, setUncontrolledSelectedId] = (0, import_react21.useState)(null);
10023
11690
  const rawSelectedId = controlledSelectedId !== void 0 ? controlledSelectedId : uncontrolledSelectedId;
10024
11691
  const selectedId = appendSourceChanged ? null : rawSelectedId;
10025
11692
  const setSelectedId = (id) => {
10026
11693
  if (controlledSelectedId === void 0) setUncontrolledSelectedId(id);
10027
11694
  onSelectedIdChange?.(id);
10028
11695
  };
10029
- const [uncontrolledWindowRowId, setUncontrolledWindowRowId] = (0, import_react20.useState)(null);
11696
+ const [uncontrolledWindowRowId, setUncontrolledWindowRowId] = (0, import_react21.useState)(null);
10030
11697
  const rawWindowRowId = controlledWindowRowId !== void 0 ? controlledWindowRowId : uncontrolledWindowRowId;
10031
11698
  const windowRowId = appendSourceChanged ? null : rawWindowRowId;
10032
11699
  const setWindowRowId = (id) => {
10033
11700
  if (controlledWindowRowId === void 0) setUncontrolledWindowRowId(id);
10034
11701
  onWindowRowIdChange?.(id);
10035
11702
  };
10036
- const [windowRowSnapshot, setWindowRowSnapshot] = (0, import_react20.useState)(null);
11703
+ const [windowRowSnapshot, setWindowRowSnapshot] = (0, import_react21.useState)(null);
10037
11704
  const editScopeKey = appendQueryKey === null ? "\0local" : `append:${appendQueryKey}`;
10038
- const [editsByScope, setEditsByScope] = (0, import_react20.useState)({});
10039
- const editRevisionRef = (0, import_react20.useRef)({});
10040
- const [savingByScope, setSavingByScope] = (0, import_react20.useState)({});
10041
- const savingScopesRef = (0, import_react20.useRef)(/* @__PURE__ */ new Set());
10042
- const [editRequest, setEditRequest] = (0, import_react20.useState)(null);
10043
- const [confirmForeignDraftDiscard, setConfirmForeignDraftDiscard] = (0, import_react20.useState)(false);
11705
+ const [editsByScope, setEditsByScope] = (0, import_react21.useState)({});
11706
+ const editRevisionRef = (0, import_react21.useRef)({});
11707
+ const [savingByScope, setSavingByScope] = (0, import_react21.useState)({});
11708
+ const savingScopesRef = (0, import_react21.useRef)(/* @__PURE__ */ new Set());
11709
+ const [editRequest, setEditRequest] = (0, import_react21.useState)(null);
11710
+ const [confirmForeignDraftDiscard, setConfirmForeignDraftDiscard] = (0, import_react21.useState)(false);
10044
11711
  const edits = editsByScope[editScopeKey]?.values ?? {};
10045
11712
  const saving = (savingByScope[editScopeKey] ?? 0) > 0;
10046
11713
  const setEdits = (next) => {
@@ -10107,7 +11774,7 @@ function MatrxDataTableCore({
10107
11774
  rows: Object.entries(draft.values).map(([rowId, columns2]) => ({ rowId, columns: columns2 }))
10108
11775
  }));
10109
11776
  const selection = appendSourceChanged && selectionConfig ? { ...selectionConfig, selectedIds: [] } : selectionConfig;
10110
- (0, import_react20.useEffect)(() => {
11777
+ (0, import_react21.useEffect)(() => {
10111
11778
  const previous = committedAppendQueryKey.current;
10112
11779
  committedAppendQueryKey.current = appendQueryKey;
10113
11780
  if (previous === null || appendQueryKey === null || previous === appendQueryKey) {
@@ -10125,14 +11792,14 @@ function MatrxDataTableCore({
10125
11792
  onWindowRowIdChange,
10126
11793
  selectionConfig
10127
11794
  ]);
10128
- const scrollRef = (0, import_react20.useRef)(null);
10129
- const tableRef = (0, import_react20.useRef)(null);
10130
- const tableHeaderRef = (0, import_react20.useRef)(null);
10131
- const cardsScrollRef = (0, import_react20.useRef)(null);
10132
- const [documentStickyHeader, setDocumentStickyHeader] = (0, import_react20.useState)(false);
10133
- const documentStickyHeaderRef = (0, import_react20.useRef)(false);
10134
- const previousAppendKeyForScrollReset = (0, import_react20.useRef)(appendQueryKey);
10135
- (0, import_react20.useLayoutEffect)(() => {
11795
+ const scrollRef = (0, import_react21.useRef)(null);
11796
+ const tableRef = (0, import_react21.useRef)(null);
11797
+ const tableHeaderRef = (0, import_react21.useRef)(null);
11798
+ const cardsScrollRef = (0, import_react21.useRef)(null);
11799
+ const [documentStickyHeader, setDocumentStickyHeader] = (0, import_react21.useState)(false);
11800
+ const documentStickyHeaderRef = (0, import_react21.useRef)(false);
11801
+ const previousAppendKeyForScrollReset = (0, import_react21.useRef)(appendQueryKey);
11802
+ (0, import_react21.useLayoutEffect)(() => {
10136
11803
  const previous = previousAppendKeyForScrollReset.current;
10137
11804
  previousAppendKeyForScrollReset.current = appendQueryKey;
10138
11805
  if (previous === null || appendQueryKey === null || previous === appendQueryKey) {
@@ -10157,13 +11824,13 @@ function MatrxDataTableCore({
10157
11824
  ...appendScrollPolicy?.mode === "scroll" && appendScrollPolicy.thresholdPx !== void 0 ? { thresholdPx: appendScrollPolicy.thresholdPx } : {},
10158
11825
  ...appendScrollPolicy?.mode === "scroll" && appendScrollPolicy.intentTimeoutMs !== void 0 ? { intentTimeoutMs: appendScrollPolicy.intentTimeoutMs } : {}
10159
11826
  });
10160
- const rootRef = (0, import_react20.useRef)(null);
11827
+ const rootRef = (0, import_react21.useRef)(null);
10161
11828
  useClippedContentGuard(rootRef, {
10162
11829
  label: typeof copy === "object" && copy.listLabel ? `Table: ${copy.listLabel}` : "MatrxDataTable"
10163
11830
  });
10164
- const [scrollHintRight, setScrollHintRight] = (0, import_react20.useState)(false);
10165
- const [scrollHintLeft, setScrollHintLeft] = (0, import_react20.useState)(false);
10166
- const updateScrollHint = (0, import_react20.useCallback)(() => {
11831
+ const [scrollHintRight, setScrollHintRight] = (0, import_react21.useState)(false);
11832
+ const [scrollHintLeft, setScrollHintLeft] = (0, import_react21.useState)(false);
11833
+ const updateScrollHint = (0, import_react21.useCallback)(() => {
10167
11834
  const el = scrollRef.current;
10168
11835
  if (!el) return;
10169
11836
  setScrollHintRight(el.scrollLeft + el.clientWidth < el.scrollWidth - 2);
@@ -10177,7 +11844,7 @@ function MatrxDataTableCore({
10177
11844
  behavior: "smooth"
10178
11845
  });
10179
11846
  };
10180
- (0, import_react20.useEffect)(() => {
11847
+ (0, import_react21.useEffect)(() => {
10181
11848
  updateScrollHint();
10182
11849
  window.addEventListener("resize", updateScrollHint);
10183
11850
  const el = scrollRef.current;
@@ -10192,7 +11859,7 @@ function MatrxDataTableCore({
10192
11859
  observer?.disconnect();
10193
11860
  };
10194
11861
  }, [updateScrollHint, data, columns]);
10195
- (0, import_react20.useLayoutEffect)(() => {
11862
+ (0, import_react21.useLayoutEffect)(() => {
10196
11863
  const scroll = scrollRef.current;
10197
11864
  const table = tableRef.current;
10198
11865
  const header = tableHeaderRef.current;
@@ -10251,30 +11918,30 @@ function MatrxDataTableCore({
10251
11918
  window.removeEventListener("resize", requestUpdate);
10252
11919
  };
10253
11920
  }, [stickyHeader, data, columns]);
10254
- const [columnOrder, setColumnOrder] = (0, import_react20.useState)(() => columnState?.order ?? columns.map(columnId));
10255
- const [hiddenColumnIds, setHiddenColumnIds] = (0, import_react20.useState)(
11921
+ const [columnOrder, setColumnOrder] = (0, import_react21.useState)(() => columnState?.order ?? columns.map(columnId));
11922
+ const [hiddenColumnIds, setHiddenColumnIds] = (0, import_react21.useState)(
10256
11923
  () => new Set(columnState?.hidden ?? columns.filter((column) => column.hidden).map(columnId))
10257
11924
  );
10258
- const [columnWidths, setColumnWidths] = (0, import_react20.useState)(
11925
+ const [columnWidths, setColumnWidths] = (0, import_react21.useState)(
10259
11926
  () => normalizeColumnWidthsForColumns(columnState?.widths, columns)
10260
11927
  );
10261
- const [fixedTableGeometry, setFixedTableGeometry] = (0, import_react20.useState)(null);
10262
- const knownColumnIds = (0, import_react20.useRef)(new Set(columns.map(columnId)));
10263
- const draggedColumnId = (0, import_react20.useRef)(null);
10264
- const columnDragActive = (0, import_react20.useRef)(false);
10265
- const columnDragRaf = (0, import_react20.useRef)(null);
10266
- const columnDragScrollDirection = (0, import_react20.useRef)(0);
10267
- const [columnDragPreview, setColumnDragPreview] = (0, import_react20.useState)(null);
10268
- const stopColumnDragScroll = (0, import_react20.useCallback)(() => {
11928
+ const [fixedTableGeometry, setFixedTableGeometry] = (0, import_react21.useState)(null);
11929
+ const knownColumnIds = (0, import_react21.useRef)(new Set(columns.map(columnId)));
11930
+ const draggedColumnId = (0, import_react21.useRef)(null);
11931
+ const columnDragActive = (0, import_react21.useRef)(false);
11932
+ const columnDragRaf = (0, import_react21.useRef)(null);
11933
+ const columnDragScrollDirection = (0, import_react21.useRef)(0);
11934
+ const [columnDragPreview, setColumnDragPreview] = (0, import_react21.useState)(null);
11935
+ const stopColumnDragScroll = (0, import_react21.useCallback)(() => {
10269
11936
  if (columnDragRaf.current !== null) cancelAnimationFrame(columnDragRaf.current);
10270
11937
  columnDragRaf.current = null;
10271
11938
  columnDragScrollDirection.current = 0;
10272
11939
  }, []);
10273
- (0, import_react20.useEffect)(() => () => {
11940
+ (0, import_react21.useEffect)(() => () => {
10274
11941
  columnDragActive.current = false;
10275
11942
  stopColumnDragScroll();
10276
11943
  }, [stopColumnDragScroll]);
10277
- const startColumnDragScroll = (0, import_react20.useCallback)(() => {
11944
+ const startColumnDragScroll = (0, import_react21.useCallback)(() => {
10278
11945
  if (!columnDragActive.current || columnDragRaf.current !== null) return;
10279
11946
  const scrollStep = () => {
10280
11947
  const scroll = scrollRef.current;
@@ -10301,25 +11968,25 @@ function MatrxDataTableCore({
10301
11968
  };
10302
11969
  columnDragRaf.current = requestAnimationFrame(scrollStep);
10303
11970
  }, []);
10304
- const commitColumnState = (0, import_react20.useCallback)((order, hidden) => {
11971
+ const commitColumnState = (0, import_react21.useCallback)((order, hidden) => {
10305
11972
  if (columnState) columnState.onChange({ order, hidden: [...hidden] });
10306
11973
  else {
10307
11974
  setColumnOrder(order);
10308
11975
  setHiddenColumnIds(hidden);
10309
11976
  }
10310
11977
  }, [columnState]);
10311
- const [internalColumnManagerOpen, setInternalColumnManagerOpen] = (0, import_react20.useState)(false);
11978
+ const [internalColumnManagerOpen, setInternalColumnManagerOpen] = (0, import_react21.useState)(false);
10312
11979
  const columnManagerOpen = controlledColumnManager?.open ?? internalColumnManagerOpen;
10313
11980
  const setColumnManagerOpen = controlledColumnManager?.onOpenChange ?? setInternalColumnManagerOpen;
10314
- const columnIds = (0, import_react20.useMemo)(() => columns.map(columnId), [columns]);
10315
- const effectiveColumnOrder = (0, import_react20.useMemo)(() => columnState ? [...columnState.order.filter((id, index, values) => columnIds.includes(id) && values.indexOf(id) === index), ...columnIds.filter((id) => !columnState.order.includes(id))] : columnOrder, [columnIds, columnOrder, columnState]);
10316
- const effectiveHiddenColumnIds = (0, import_react20.useMemo)(() => columnState ? new Set(columnState.hidden.filter((id) => columnIds.includes(id))) : hiddenColumnIds, [columnIds, columnState, hiddenColumnIds]);
10317
- const effectiveColumnWidths = (0, import_react20.useMemo)(
11981
+ const columnIds = (0, import_react21.useMemo)(() => columns.map(columnId), [columns]);
11982
+ const effectiveColumnOrder = (0, import_react21.useMemo)(() => columnState ? [...columnState.order.filter((id, index, values) => columnIds.includes(id) && values.indexOf(id) === index), ...columnIds.filter((id) => !columnState.order.includes(id))] : columnOrder, [columnIds, columnOrder, columnState]);
11983
+ const effectiveHiddenColumnIds = (0, import_react21.useMemo)(() => columnState ? new Set(columnState.hidden.filter((id) => columnIds.includes(id))) : hiddenColumnIds, [columnIds, columnState, hiddenColumnIds]);
11984
+ const effectiveColumnWidths = (0, import_react21.useMemo)(
10318
11985
  () => normalizeColumnWidthsForColumns(columnState?.widths ?? columnWidths, columns),
10319
11986
  [columns, columnState?.widths, columnWidths]
10320
11987
  );
10321
- const resizeGestureSnapshotRef = (0, import_react20.useRef)(null);
10322
- const measureVisibleColumnWidths = (0, import_react20.useCallback)(() => {
11988
+ const resizeGestureSnapshotRef = (0, import_react21.useRef)(null);
11989
+ const measureVisibleColumnWidths = (0, import_react21.useCallback)(() => {
10323
11990
  const measured = {};
10324
11991
  const headers = tableRef.current ? Array.from(tableRef.current.querySelectorAll(
10325
11992
  "thead th[data-matrx-table-column-id]"
@@ -10333,7 +12000,7 @@ function MatrxDataTableCore({
10333
12000
  }
10334
12001
  return normalizeColumnWidthsForColumns(measured, columns);
10335
12002
  }, [columns]);
10336
- const measureFixedTableGeometry = (0, import_react20.useCallback)(() => {
12003
+ const measureFixedTableGeometry = (0, import_react21.useCallback)(() => {
10337
12004
  const table = tableRef.current;
10338
12005
  const headers = table ? Array.from(table.querySelectorAll("thead th")) : [];
10339
12006
  const widthFor = (selector) => {
@@ -10368,16 +12035,16 @@ function MatrxDataTableCore({
10368
12035
  ...actionsWidth === void 0 ? {} : { actionsWidth }
10369
12036
  };
10370
12037
  }, [resolvedDensity]);
10371
- const captureResizeGestureSnapshot = (0, import_react20.useCallback)(() => {
12038
+ const captureResizeGestureSnapshot = (0, import_react21.useCallback)(() => {
10372
12039
  resizeGestureSnapshotRef.current = {
10373
12040
  widths: measureVisibleColumnWidths(),
10374
12041
  geometry: measureFixedTableGeometry()
10375
12042
  };
10376
12043
  }, [measureFixedTableGeometry, measureVisibleColumnWidths]);
10377
- const clearResizeGestureSnapshot = (0, import_react20.useCallback)(() => {
12044
+ const clearResizeGestureSnapshot = (0, import_react21.useCallback)(() => {
10378
12045
  resizeGestureSnapshotRef.current = null;
10379
12046
  }, []);
10380
- const commitColumnWidths = (0, import_react20.useCallback)((next, captureInitialWidths = false) => {
12047
+ const commitColumnWidths = (0, import_react21.useCallback)((next, captureInitialWidths = false) => {
10381
12048
  const gestureSnapshot = captureInitialWidths ? resizeGestureSnapshotRef.current : null;
10382
12049
  resizeGestureSnapshotRef.current = null;
10383
12050
  const normalized = normalizeColumnWidthsForColumns(
@@ -10396,7 +12063,7 @@ function MatrxDataTableCore({
10396
12063
  }
10397
12064
  setColumnWidths(normalized);
10398
12065
  }, [columnIds, columnState, columns, measureFixedTableGeometry, measureVisibleColumnWidths]);
10399
- (0, import_react20.useEffect)(() => {
12066
+ (0, import_react21.useEffect)(() => {
10400
12067
  const ids = columns.map(columnId);
10401
12068
  if (columnState) return;
10402
12069
  const previousIds = knownColumnIds.current;
@@ -10415,31 +12082,31 @@ function MatrxDataTableCore({
10415
12082
  setColumnWidths((current) => normalizeColumnWidthsForColumns(current, columns));
10416
12083
  knownColumnIds.current = new Set(ids);
10417
12084
  }, [columns, columnState]);
10418
- (0, import_react20.useLayoutEffect)(() => {
12085
+ (0, import_react21.useLayoutEffect)(() => {
10419
12086
  for (const id of columnIds) {
10420
12087
  tableRef.current?.style.removeProperty(columnResizeCssVar(id));
10421
12088
  }
10422
12089
  });
10423
- const previewColumnOrder = (0, import_react20.useMemo)(
12090
+ const previewColumnOrder = (0, import_react21.useMemo)(
10424
12091
  () => projectColumnDragOrder(effectiveColumnOrder, columnDragPreview),
10425
12092
  [columnDragPreview, effectiveColumnOrder]
10426
12093
  );
10427
12094
  useColumnReorderAnimation(tableRef, previewColumnOrder.join("\0"), columnDragPreview !== null);
10428
- const visibleColumns = (0, import_react20.useMemo)(() => {
12095
+ const visibleColumns = (0, import_react21.useMemo)(() => {
10429
12096
  const byId = new Map(columns.map((column) => [columnId(column), column]));
10430
12097
  return previewColumnOrder.map((id) => byId.get(id)).filter(
10431
12098
  (column) => column !== void 0 && !effectiveHiddenColumnIds.has(columnId(column))
10432
12099
  );
10433
12100
  }, [columns, effectiveHiddenColumnIds, previewColumnOrder]);
10434
12101
  const usesMeasuredFixedLayout = visibleColumns.length > 0 && visibleColumns.every((column) => effectiveColumnWidths[columnId(column)] !== void 0);
10435
- (0, import_react20.useLayoutEffect)(() => {
12102
+ (0, import_react21.useLayoutEffect)(() => {
10436
12103
  if (!usesMeasuredFixedLayout) {
10437
12104
  if (fixedTableGeometry !== null) setFixedTableGeometry(null);
10438
12105
  return;
10439
12106
  }
10440
12107
  if (fixedTableGeometry === null) setFixedTableGeometry(measureFixedTableGeometry());
10441
12108
  }, [fixedTableGeometry, measureFixedTableGeometry, usesMeasuredFixedLayout]);
10442
- (0, import_react20.useLayoutEffect)(() => {
12109
+ (0, import_react21.useLayoutEffect)(() => {
10443
12110
  if (!usesMeasuredFixedLayout) return;
10444
12111
  const table = tableRef.current;
10445
12112
  if (!table) return;
@@ -10461,7 +12128,7 @@ function MatrxDataTableCore({
10461
12128
  setFixedTableGeometry(geometry);
10462
12129
  }, [resolvedDensity, measureFixedTableGeometry, usesMeasuredFixedLayout]);
10463
12130
  const fixedLayoutActive = usesMeasuredFixedLayout && fixedTableGeometry !== null;
10464
- const frozenColumns = (0, import_react20.useMemo)(() => {
12131
+ const frozenColumns = (0, import_react21.useMemo)(() => {
10465
12132
  if (!visibleColumns.some((column) => column.frozen)) return NO_FROZEN_COLUMNS;
10466
12133
  const ids = visibleColumns.map(columnId);
10467
12134
  const byId = new Map(visibleColumns.map((column) => [columnId(column), column]));
@@ -10503,15 +12170,15 @@ function MatrxDataTableCore({
10503
12170
  );
10504
12171
  return color ? CELL_TINT_CLASS[color] : void 0;
10505
12172
  };
10506
- const declaredVisibleCopyColumns = (0, import_react20.useMemo)(
12173
+ const declaredVisibleCopyColumns = (0, import_react21.useMemo)(
10507
12174
  () => visibleColumns.filter((column) => column.accessorFn !== void 0 || column.accessorKey !== void 0),
10508
12175
  [visibleColumns]
10509
12176
  );
10510
- const tableCopySourceNamespace = (0, import_react20.useId)();
10511
- const tableContextMenuInstanceId = (0, import_react20.useId)();
10512
- const tableDndContextId = (0, import_react20.useId)();
12177
+ const tableCopySourceNamespace = (0, import_react21.useId)();
12178
+ const tableContextMenuInstanceId = (0, import_react21.useId)();
12179
+ const tableDndContextId = (0, import_react21.useId)();
10513
12180
  const defaultCopyTitle = toolbar?.title || "Table";
10514
- const defaultCopy = (0, import_react20.useMemo)(() => ({
12181
+ const defaultCopy = (0, import_react21.useMemo)(() => ({
10515
12182
  label: defaultCopyTitle,
10516
12183
  listLabel: defaultCopyTitle,
10517
12184
  location: defaultCopyTitle,
@@ -10546,7 +12213,7 @@ function MatrxDataTableCore({
10546
12213
  serializer: (shaped) => buildViewAgentInput(effectiveCopy, shaped, data, { scope: "custom" })
10547
12214
  }))
10548
12215
  ] : [];
10549
- const filterMeta = (0, import_react20.useMemo)(() => {
12216
+ const filterMeta = (0, import_react21.useMemo)(() => {
10550
12217
  const meta = /* @__PURE__ */ new Map();
10551
12218
  for (const col of visibleColumns) {
10552
12219
  const id = columnId(col);
@@ -10554,14 +12221,14 @@ function MatrxDataTableCore({
10554
12221
  }
10555
12222
  return meta;
10556
12223
  }, [visibleColumns, data, remoteQuery]);
10557
- const intelligentSearchScopes = (0, import_react20.useMemo)(
12224
+ const intelligentSearchScopes = (0, import_react21.useMemo)(
10558
12225
  () => resolveTableSearchScopes(data, columns, {
10559
12226
  ...toolbar?.intelligentSearch?.roles === void 0 ? {} : { roles: toolbar.intelligentSearch.roles },
10560
12227
  ...toolbar?.intelligentSearch?.scopes === void 0 ? {} : { scopes: toolbar.intelligentSearch.scopes }
10561
12228
  }),
10562
12229
  [columns, data, toolbar?.intelligentSearch?.roles, toolbar?.intelligentSearch?.scopes]
10563
12230
  );
10564
- const processed = (0, import_react20.useMemo)(() => {
12231
+ const processed = (0, import_react21.useMemo)(() => {
10565
12232
  if (remoteQuery && !appendUsesLocalProcessing) return data;
10566
12233
  if (processLocalRows) {
10567
12234
  return processLocalRows(data, {
@@ -10624,8 +12291,8 @@ function MatrxDataTableCore({
10624
12291
  intelligentSearchEnabled,
10625
12292
  searchScope
10626
12293
  ]);
10627
- const lastNotifiedViewRows = (0, import_react20.useRef)(null);
10628
- (0, import_react20.useEffect)(() => {
12294
+ const lastNotifiedViewRows = (0, import_react21.useRef)(null);
12295
+ (0, import_react21.useEffect)(() => {
10629
12296
  if (!onViewChange) {
10630
12297
  lastNotifiedViewRows.current = null;
10631
12298
  return;
@@ -10634,7 +12301,7 @@ function MatrxDataTableCore({
10634
12301
  lastNotifiedViewRows.current = processed;
10635
12302
  if (changed) onViewChange(processed);
10636
12303
  }, [processed, onViewChange]);
10637
- (0, import_react20.useEffect)(() => {
12304
+ (0, import_react21.useEffect)(() => {
10638
12305
  if (controlledQuery) return;
10639
12306
  setInternalPage(1);
10640
12307
  }, [
@@ -10678,7 +12345,7 @@ function MatrxDataTableCore({
10678
12345
  numberedPageHasMore: !hidePagination && !isLoading && !isFetching && processed.length > 0 && safePage < pageCount && (remoteQuery?.mode === "controlled" || !remoteQuery && localPagination?.mode === "numbered"),
10679
12346
  onPageAdvance: setPage
10680
12347
  });
10681
- const paginated = (0, import_react20.useMemo)(() => {
12348
+ const paginated = (0, import_react21.useMemo)(() => {
10682
12349
  if (remoteQuery) return processed;
10683
12350
  if (hidePagination) return processed;
10684
12351
  if (localProgressiveEnabled) return [...progressiveRows.rows];
@@ -10686,15 +12353,268 @@ function MatrxDataTableCore({
10686
12353
  const start = (safePage - 1) * effectivePageSize;
10687
12354
  return processed.slice(start, start + effectivePageSize);
10688
12355
  }, [processed, safePage, effectivePageSize, pageSize, remoteQuery, hidePagination, localProgressiveEnabled, progressiveRows.rows]);
10689
- (0, import_react20.useEffect)(() => {
12356
+ const groupingColumnId = grouping?.columnId ?? null;
12357
+ const [uncontrolledCollapsed, setUncontrolledCollapsed] = (0, import_react21.useState)([]);
12358
+ const collapsedGroups = grouping?.collapsed ?? uncontrolledCollapsed;
12359
+ const setCollapsedGroups = (0, import_react21.useCallback)(
12360
+ (next) => {
12361
+ if (grouping?.onCollapsedChange) grouping.onCollapsedChange(next);
12362
+ else setUncontrolledCollapsed(next);
12363
+ },
12364
+ [grouping]
12365
+ );
12366
+ const groups = (0, import_react21.useMemo)(() => {
12367
+ if (!grouping || !groupingColumnId) return null;
12368
+ const groupColumn = columns.find((col) => columnId(col) === groupingColumnId);
12369
+ if (!groupColumn) return null;
12370
+ const aggregateColumns = {};
12371
+ for (const [aggColumnId, spec] of Object.entries(grouping.aggregates ?? {})) {
12372
+ const col = columns.find((c) => columnId(c) === aggColumnId);
12373
+ if (!col) continue;
12374
+ aggregateColumns[aggColumnId] = { spec, readCell: (row) => getCellValue(row, col) };
12375
+ }
12376
+ return groupRows({
12377
+ rows: paginated,
12378
+ columnId: groupingColumnId,
12379
+ readCell: (row) => grouping.readCell ? grouping.readCell(row, groupingColumnId) : getCellValue(row, groupColumn),
12380
+ aggregates: aggregateColumns,
12381
+ ...grouping.order ? { order: grouping.order } : {},
12382
+ ...grouping.emptyLabel ? { emptyLabel: grouping.emptyLabel } : {}
12383
+ });
12384
+ }, [grouping, groupingColumnId, columns, paginated]);
12385
+ const groupableColumns = (0, import_react21.useMemo)(() => {
12386
+ const allowed = grouping?.groupableColumnIds;
12387
+ return visibleColumns.filter((col) => {
12388
+ const id = columnId(col);
12389
+ if (allowed) return allowed.includes(id);
12390
+ return col.accessorKey !== void 0 || col.accessorFn !== void 0;
12391
+ });
12392
+ }, [visibleColumns, grouping?.groupableColumnIds]);
12393
+ const groupedRenderRows = (0, import_react21.useMemo)(
12394
+ () => groups ? flattenGroups(groups, collapsedGroups) : null,
12395
+ [groups, collapsedGroups]
12396
+ );
12397
+ const virtualizeConfig = virtualize === void 0 || virtualize === false ? null : virtualize === true ? {} : virtualize;
12398
+ const virtualRowHeight = explicitRowHeight ?? virtualizeConfig?.rowHeight ?? (resolvedDensity === "condensed" ? 32 : resolvedDensity === "spacious" ? 52 : 40);
12399
+ const virtualThreshold = virtualizeConfig?.threshold ?? 150;
12400
+ const bodyEntryCount = groupedRenderRows ? groupedRenderRows.length : paginated.length;
12401
+ const virtualizeActive = virtualizeConfig !== null && virtualizeConfig.enabled !== false && bodyEntryCount > virtualThreshold && !hierarchy && expandedDetail === void 0;
12402
+ const columnWindowWidth = virtualizeConfig?.columnWidth ?? 160;
12403
+ const frozenRunLength = (0, import_react21.useMemo)(() => {
12404
+ let run = 0;
12405
+ for (const col of visibleColumns) {
12406
+ if (frozenColumns.offsets[columnId(col)] === void 0) break;
12407
+ run += 1;
12408
+ }
12409
+ return run;
12410
+ }, [visibleColumns, frozenColumns]);
12411
+ const columnVirtualizeActive = virtualizeConfig !== null && virtualizeConfig.enabled !== false && virtualizeConfig.columns === true && visibleColumns.length - frozenRunLength > 30 && !hierarchy && mobileCards === void 0;
12412
+ const [rowWindow, setRowWindow] = (0, import_react21.useState)(null);
12413
+ const [columnWindow, setColumnWindow] = (0, import_react21.useState)(null);
12414
+ const windowInputs = (0, import_react21.useRef)({
12415
+ virtualizeActive: false,
12416
+ bodyEntryCount: 0,
12417
+ virtualRowHeight: 40,
12418
+ overscan: 8,
12419
+ columnVirtualizeActive: false,
12420
+ columnCount: 0,
12421
+ columnWindowWidth: 160,
12422
+ frozenRunLength: 0
12423
+ });
12424
+ windowInputs.current = {
12425
+ virtualizeActive,
12426
+ bodyEntryCount,
12427
+ virtualRowHeight,
12428
+ overscan: virtualizeConfig?.overscan ?? 8,
12429
+ columnVirtualizeActive,
12430
+ columnCount: visibleColumns.length,
12431
+ columnWindowWidth,
12432
+ frozenRunLength
12433
+ };
12434
+ const recomputeWindows = (0, import_react21.useCallback)(() => {
12435
+ const el = scrollRef.current;
12436
+ const input = windowInputs.current;
12437
+ const nextRow = input.virtualizeActive ? computeWindow({
12438
+ count: input.bodyEntryCount,
12439
+ itemSize: input.virtualRowHeight,
12440
+ viewport: el?.clientHeight || 600,
12441
+ offset: el?.scrollTop ?? 0,
12442
+ overscan: input.overscan
12443
+ }) : null;
12444
+ const nextColumn = input.columnVirtualizeActive ? computeWindow({
12445
+ count: input.columnCount,
12446
+ itemSize: input.columnWindowWidth,
12447
+ viewport: el?.clientWidth || 1200,
12448
+ offset: el?.scrollLeft ?? 0,
12449
+ overscan: Math.max(2, Math.round(input.overscan / 2)),
12450
+ pinnedStart: input.frozenRunLength
12451
+ }) : null;
12452
+ setRowWindow((prev) => sameWindow(prev, nextRow) ? prev : nextRow);
12453
+ setColumnWindow((prev) => sameWindow(prev, nextColumn) ? prev : nextColumn);
12454
+ }, []);
12455
+ const effectiveRowWindow = (0, import_react21.useMemo)(() => {
12456
+ if (!virtualizeActive) return null;
12457
+ if (rowWindow && rowWindow.end <= bodyEntryCount) return rowWindow;
12458
+ return computeWindow({
12459
+ count: bodyEntryCount,
12460
+ itemSize: virtualRowHeight,
12461
+ viewport: scrollRef.current?.clientHeight || 600,
12462
+ offset: scrollRef.current?.scrollTop ?? 0,
12463
+ overscan: virtualizeConfig?.overscan ?? 8
12464
+ });
12465
+ }, [virtualizeActive, rowWindow, bodyEntryCount, virtualRowHeight, virtualizeConfig?.overscan]);
12466
+ const effectiveColumnWindow = (0, import_react21.useMemo)(() => {
12467
+ if (!columnVirtualizeActive) return null;
12468
+ if (columnWindow && columnWindow.end <= visibleColumns.length) return columnWindow;
12469
+ return computeWindow({
12470
+ count: visibleColumns.length,
12471
+ itemSize: columnWindowWidth,
12472
+ viewport: scrollRef.current?.clientWidth || 1200,
12473
+ offset: scrollRef.current?.scrollLeft ?? 0,
12474
+ overscan: Math.max(2, Math.round((virtualizeConfig?.overscan ?? 8) / 2)),
12475
+ pinnedStart: frozenRunLength
12476
+ });
12477
+ }, [
12478
+ columnVirtualizeActive,
12479
+ columnWindow,
12480
+ visibleColumns.length,
12481
+ columnWindowWidth,
12482
+ virtualizeConfig?.overscan,
12483
+ frozenRunLength
12484
+ ]);
12485
+ const metricsWanted = virtualizeActive || columnVirtualizeActive;
12486
+ (0, import_react21.useEffect)(() => {
12487
+ recomputeWindows();
12488
+ }, [
12489
+ recomputeWindows,
12490
+ virtualizeActive,
12491
+ bodyEntryCount,
12492
+ virtualRowHeight,
12493
+ columnVirtualizeActive,
12494
+ visibleColumns.length,
12495
+ columnWindowWidth,
12496
+ frozenRunLength
12497
+ ]);
12498
+ (0, import_react21.useEffect)(() => {
12499
+ if (!metricsWanted) return;
12500
+ const el = scrollRef.current;
12501
+ if (!el) return;
12502
+ let frame = 0;
12503
+ const onScroll = () => {
12504
+ if (frame) return;
12505
+ frame = requestAnimationFrame(() => {
12506
+ frame = 0;
12507
+ recomputeWindows();
12508
+ });
12509
+ };
12510
+ el.addEventListener("scroll", onScroll, { passive: true });
12511
+ const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(onScroll);
12512
+ observer?.observe(el);
12513
+ return () => {
12514
+ if (frame) cancelAnimationFrame(frame);
12515
+ el.removeEventListener("scroll", onScroll);
12516
+ observer?.disconnect();
12517
+ };
12518
+ }, [metricsWanted, recomputeWindows]);
12519
+ const renderedColumns = (0, import_react21.useMemo)(
12520
+ () => effectiveColumnWindow ? [
12521
+ ...visibleColumns.slice(0, frozenRunLength),
12522
+ ...visibleColumns.slice(effectiveColumnWindow.start, effectiveColumnWindow.end)
12523
+ ] : visibleColumns,
12524
+ [effectiveColumnWindow, visibleColumns, frozenRunLength]
12525
+ );
12526
+ const columnSpacers = effectiveColumnWindow ? { start: effectiveColumnWindow.padStart, end: effectiveColumnWindow.padEnd } : null;
12527
+ const windowedWidthFor = (0, import_react21.useCallback)(
12528
+ (id, declared) => {
12529
+ const resized = effectiveColumnWidths[id];
12530
+ if (!effectiveColumnWindow) return resized;
12531
+ if (frozenColumns.offsets[id] !== void 0) return resized;
12532
+ return resized ?? (typeof declared === "number" ? declared : columnWindowWidth);
12533
+ },
12534
+ [effectiveColumnWindow, effectiveColumnWidths, frozenColumns, columnWindowWidth]
12535
+ );
12536
+ const coverageResult = (0, import_react21.useMemo)(() => {
12537
+ if (!coverage) return null;
12538
+ return describeCoverage({
12539
+ loaded: processed.length,
12540
+ ...coverage.matched === void 0 ? {} : { matched: coverage.matched },
12541
+ ...coverage.total === void 0 ? {} : { total: coverage.total },
12542
+ ...coverage.cap === void 0 ? {} : { cap: coverage.cap },
12543
+ answeredBy: coverage.answeredBy ?? "client",
12544
+ ...coverage.noun === void 0 ? {} : { noun: coverage.noun }
12545
+ });
12546
+ }, [coverage, processed.length]);
12547
+ const bodyEntries = (0, import_react21.useMemo)(() => {
12548
+ const from = effectiveRowWindow ? effectiveRowWindow.start : 0;
12549
+ const to = effectiveRowWindow ? effectiveRowWindow.end : void 0;
12550
+ const windowed = groupedRenderRows ? groupedRenderRows.slice(from, to).map(
12551
+ (entry) => entry.kind === "group" ? {
12552
+ kind: "group",
12553
+ group: entry.group,
12554
+ collapsed: isGroupCollapsed(collapsedGroups, entry.group.key)
12555
+ } : { kind: "row", row: entry.row, index: entry.dataIndex ?? 0 }
12556
+ ) : paginated.slice(from, to).map((row, offset) => ({ kind: "row", row, index: from + offset }));
12557
+ if (!effectiveRowWindow) return windowed;
12558
+ if (effectiveRowWindow.padStart > 0)
12559
+ windowed.unshift({ kind: "spacer", height: effectiveRowWindow.padStart, place: "start" });
12560
+ if (effectiveRowWindow.padEnd > 0)
12561
+ windowed.push({ kind: "spacer", height: effectiveRowWindow.padEnd, place: "end" });
12562
+ return windowed;
12563
+ }, [groupedRenderRows, collapsedGroups, paginated, effectiveRowWindow]);
12564
+ const getRowIdRef = (0, import_react21.useRef)(getRowId);
12565
+ getRowIdRef.current = getRowId;
12566
+ const spreadsheetRowIds = (0, import_react21.useMemo)(
12567
+ () => paginated.map((row) => getRowIdRef.current(row)),
12568
+ [paginated]
12569
+ );
12570
+ const spreadsheetColumnIds = (0, import_react21.useMemo)(
12571
+ () => visibleColumns.map((col) => columnId(col)),
12572
+ [visibleColumns]
12573
+ );
12574
+ const spreadsheetRowById = (0, import_react21.useMemo)(() => {
12575
+ const map = /* @__PURE__ */ new Map();
12576
+ for (const row of paginated) map.set(getRowIdRef.current(row), row);
12577
+ return map;
12578
+ }, [paginated]);
12579
+ const spreadsheetColumnById = (0, import_react21.useMemo)(() => {
12580
+ const map = /* @__PURE__ */ new Map();
12581
+ for (const col of visibleColumns) map.set(columnId(col), col);
12582
+ return map;
12583
+ }, [visibleColumns]);
12584
+ const spreadsheetEnabled = Boolean(spreadsheet && spreadsheet.enabled !== false);
12585
+ const sheet = useSpreadsheetGrid(
12586
+ spreadsheetEnabled && spreadsheet ? {
12587
+ rowIds: spreadsheetRowIds,
12588
+ columnIds: spreadsheetColumnIds,
12589
+ valueAt: (rowId, colId) => {
12590
+ const row = spreadsheetRowById.get(rowId);
12591
+ const col = spreadsheetColumnById.get(colId);
12592
+ if (row === void 0 || col === void 0) return null;
12593
+ if (spreadsheet.readCell) return spreadsheet.readCell(row, colId);
12594
+ return getCellValue(applyRowEdits(row, edits[rowId]), col);
12595
+ },
12596
+ isColumnWritable: (colId) => {
12597
+ if (spreadsheet.writableColumnIds) return spreadsheet.writableColumnIds.includes(colId);
12598
+ const col = spreadsheetColumnById.get(colId);
12599
+ return Boolean(col && col.editable);
12600
+ },
12601
+ ...spreadsheet.columnRefusal ? { columnRefusal: spreadsheet.columnRefusal } : {},
12602
+ onPatch: spreadsheet.onPatch,
12603
+ ...spreadsheet.undoDepth === void 0 ? {} : { undoDepth: spreadsheet.undoDepth },
12604
+ ...spreadsheet.fillHandle === void 0 ? {} : { fillHandle: spreadsheet.fillHandle },
12605
+ ...spreadsheet.paste === void 0 ? {} : { paste: spreadsheet.paste },
12606
+ notify: spreadsheet.notify ?? ((message) => toast.success(message))
12607
+ } : null
12608
+ );
12609
+ (0, import_react21.useEffect)(() => {
10690
12610
  if (!controlledQuery || controlledQuery.state.page === safePage) return;
10691
12611
  emitControlledQueryChange({ page: safePage });
10692
12612
  }, [controlledQuery, emitControlledQueryChange, safePage]);
10693
- const selectedRow = (0, import_react20.useMemo)(() => {
12613
+ const selectedRow = (0, import_react21.useMemo)(() => {
10694
12614
  if (!selectedId) return null;
10695
12615
  return data.find((r) => getRowId(r) === selectedId) ?? null;
10696
12616
  }, [data, selectedId, getRowId]);
10697
- const windowRow = (0, import_react20.useMemo)(() => {
12617
+ const windowRow = (0, import_react21.useMemo)(() => {
10698
12618
  if (!windowRowId) return null;
10699
12619
  return data.find((r) => getRowId(r) === windowRowId) ?? (windowRowSnapshot && windowRowSnapshot.queryKey === appendQueryKey && getRowId(windowRowSnapshot.row) === windowRowId ? windowRowSnapshot.row : null);
10700
12620
  }, [data, windowRowId, windowRowSnapshot, getRowId, appendQueryKey]);
@@ -10729,7 +12649,7 @@ function MatrxDataTableCore({
10729
12649
  return def !== void 0 && f.value !== def;
10730
12650
  });
10731
12651
  const hasActiveFilters = activeFilterCount > 0 || Boolean(searchValue.trim()) || intelligentSearchEnabled && searchScope !== "all" || Boolean(anyOfValue.trim()) || completeLayeredFilterRules(layeredFilters).length > 0 || facetActive;
10732
- const changeCount = (0, import_react20.useMemo)(() => {
12652
+ const changeCount = (0, import_react21.useMemo)(() => {
10733
12653
  let n = 0;
10734
12654
  for (const fields of Object.values(edits)) {
10735
12655
  n += Object.keys(fields).length;
@@ -10885,7 +12805,7 @@ function MatrxDataTableCore({
10885
12805
  };
10886
12806
  const hierarchyRows = hierarchy?.rows ?? data;
10887
12807
  const hierarchyGetParentId = hierarchy?.getParentId;
10888
- const hierarchyIndex = (0, import_react20.useMemo)(() => {
12808
+ const hierarchyIndex = (0, import_react21.useMemo)(() => {
10889
12809
  if (!hierarchyGetParentId) return null;
10890
12810
  const byId = /* @__PURE__ */ new Map();
10891
12811
  const parentById = /* @__PURE__ */ new Map();
@@ -10917,7 +12837,7 @@ function MatrxDataTableCore({
10917
12837
  return { byId, parentById, depthById };
10918
12838
  }, [hierarchyGetParentId, hierarchyRows, getRowId]);
10919
12839
  const draggedRow = draggedRowId && hierarchyIndex ? hierarchyIndex.byId.get(draggedRowId) : void 0;
10920
- const hierarchyBlockedIds = (0, import_react20.useMemo)(() => {
12840
+ const hierarchyBlockedIds = (0, import_react21.useMemo)(() => {
10921
12841
  if (!draggedRowId || !hierarchyIndex) return null;
10922
12842
  const childrenById = /* @__PURE__ */ new Map();
10923
12843
  for (const [id, parentId] of hierarchyIndex.parentById) {
@@ -11100,7 +13020,7 @@ function MatrxDataTableCore({
11100
13020
  setEdits({});
11101
13021
  edit?.onCancel?.();
11102
13022
  };
11103
- (0, import_react20.useEffect)(() => {
13023
+ (0, import_react21.useEffect)(() => {
11104
13024
  if (!host.rowContextRegistry || !contextMenu && !host.createDefaultRowContext) return;
11105
13025
  return host.rowContextRegistry.register(tableContextMenuInstanceId, (rowId) => {
11106
13026
  const row = data.find((candidate) => getRowId(candidate) === rowId);
@@ -11112,7 +13032,7 @@ function MatrxDataTableCore({
11112
13032
  });
11113
13033
  }, [buildRecordControls, contextMenu, data, getRowId, host.createDefaultRowContext, host.rowContextRegistry, tableContextMenuInstanceId]);
11114
13034
  const showActionsCol = windowEnabled || Boolean(rowActions) || showInlineRowCopy;
11115
- const selectedIdSet = (0, import_react20.useMemo)(
13035
+ const selectedIdSet = (0, import_react21.useMemo)(
11116
13036
  () => new Set(selection?.selectedIds ?? []),
11117
13037
  [selection?.selectedIds]
11118
13038
  );
@@ -11124,11 +13044,11 @@ function MatrxDataTableCore({
11124
13044
  );
11125
13045
  const allOnPageSelected = selectableRows.length > 0 && selectedOnPage.length === selectableRows.length;
11126
13046
  const someOnPageSelected = selectedOnPage.length > 0 && !allOnPageSelected;
11127
- const selectedRows = (0, import_react20.useMemo)(
13047
+ const selectedRows = (0, import_react21.useMemo)(
11128
13048
  () => data.filter((row) => selectedIdSet.has(getRowId(row))),
11129
13049
  [data, selectedIdSet, getRowId]
11130
13050
  );
11131
- const lastToggledIndex = (0, import_react20.useRef)(null);
13051
+ const lastToggledIndex = (0, import_react21.useRef)(null);
11132
13052
  const setSelectedIds = (next) => selection?.onSelectedIdsChange([...next]);
11133
13053
  const toggleRowSelected = (index, shiftKey) => {
11134
13054
  if (!selection) return;
@@ -11165,6 +13085,7 @@ function MatrxDataTableCore({
11165
13085
  const selectionNoun = selection?.noun ?? "row";
11166
13086
  const selectionCount = selection?.selectedIds.length ?? 0;
11167
13087
  const leadingCols = selection ? 1 : 0;
13088
+ const renderedColSpan = renderedColumns.length + leadingCols + (showActionsCol ? 1 : 0) + (columnSpacers ? (columnSpacers.start > 0 ? 1 : 0) + (columnSpacers.end > 0 ? 1 : 0) : 0);
11168
13089
  const renderedFacets = controlledQuery ? toolbar?.facets?.map(
11169
13090
  (facet) => facet.type === "button-group" ? {
11170
13091
  ...facet,
@@ -11198,7 +13119,7 @@ function MatrxDataTableCore({
11198
13119
  }
11199
13120
  return href ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Link, { href, onClick: (event) => event.stopPropagation(), className: "block w-full min-w-0 rounded outline-none focus-visible:ring-2 focus-visible:ring-ring", children: display }) : display;
11200
13121
  };
11201
- const currentViewSnapshot = (0, import_react20.useMemo)(
13122
+ const currentViewSnapshot = (0, import_react21.useMemo)(
11202
13123
  () => ({
11203
13124
  __kind: "matrx-table-view",
11204
13125
  version: 1,
@@ -11233,7 +13154,7 @@ function MatrxDataTableCore({
11233
13154
  sort
11234
13155
  ]
11235
13156
  );
11236
- const declaredDefaultViewSnapshot = (0, import_react20.useMemo)(
13157
+ const declaredDefaultViewSnapshot = (0, import_react21.useMemo)(
11237
13158
  () => defaultViewSnapshot ?? {
11238
13159
  __kind: "matrx-table-view",
11239
13160
  version: 1,
@@ -11252,7 +13173,7 @@ function MatrxDataTableCore({
11252
13173
  },
11253
13174
  [columns, defaultPageSize, defaultSort, defaultViewSnapshot]
11254
13175
  );
11255
- const applySavedView = (0, import_react20.useCallback)(
13176
+ const applySavedView = (0, import_react21.useCallback)(
11256
13177
  (rawSnapshot) => {
11257
13178
  const snapshot = parseTableViewSnapshot(rawSnapshot);
11258
13179
  if (!snapshot) {
@@ -11604,6 +13525,28 @@ function MatrxDataTableCore({
11604
13525
  }
11605
13526
  }
11606
13527
  ) : null,
13528
+ grouping?.onColumnIdChange ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
13529
+ Select,
13530
+ {
13531
+ value: groupingColumnId ?? NO_GROUPING_VALUE,
13532
+ onValueChange: (value) => grouping.onColumnIdChange?.(value === NO_GROUPING_VALUE ? null : value),
13533
+ children: [
13534
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
13535
+ SelectTrigger,
13536
+ {
13537
+ "data-matrx-table-group-by": true,
13538
+ "aria-label": "Group rows by a column",
13539
+ className: "h-8 w-auto min-w-[9rem] gap-1 text-xs",
13540
+ children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(SelectValue, { placeholder: "No groups" })
13541
+ }
13542
+ ),
13543
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(SelectContent, { children: [
13544
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(SelectItem, { value: NO_GROUPING_VALUE, children: "No groups" }),
13545
+ groupableColumns.map((col) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(SelectItem, { value: columnId(col), children: `Group by ${col.label ?? (typeof col.header === "string" ? col.header : columnId(col))}` }, columnId(col)))
13546
+ ] })
13547
+ ]
13548
+ }
13549
+ ) : null,
11607
13550
  columns.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
11608
13551
  TableToolbarAction,
11609
13552
  {
@@ -11671,65 +13614,6 @@ function MatrxDataTableCore({
11671
13614
  ]
11672
13615
  }
11673
13616
  ),
11674
- selection && selectionCount > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex shrink-0 flex-wrap items-center gap-2 rounded-md border border-primary/30 bg-primary/5 px-2 py-1.5", children: [
11675
- /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "text-xs font-medium text-foreground", children: [
11676
- (0, import_format5.formatCount)(selectionCount),
11677
- " ",
11678
- selectionNoun,
11679
- selectionCount === 1 ? "" : "s",
11680
- " selected"
11681
- ] }),
11682
- /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
11683
- Button,
11684
- {
11685
- type: "button",
11686
- variant: "ghost",
11687
- size: "sm",
11688
- className: "h-7 gap-1 px-2 text-xs",
11689
- onClick: () => {
11690
- lastToggledIndex.current = null;
11691
- selection.onSelectedIdsChange([]);
11692
- },
11693
- children: [
11694
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(X, { className: "h-3.5 w-3.5" }),
11695
- "Clear"
11696
- ]
11697
- }
11698
- ),
11699
- /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "ml-auto flex flex-wrap items-center gap-1.5", children: [
11700
- effectiveCopy && selectedRows.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
11701
- CopyButtons,
11702
- {
11703
- size: "icon",
11704
- "data-matrx-table-icon-action": true,
11705
- sourceId: `${tableCopySourceNamespace}:selection`,
11706
- ...effectiveCopy.references ? { references: effectiveCopy.references(selectedRows, data) } : {},
11707
- ...effectiveCopy.triggerVariant ? { triggerVariant: effectiveCopy.triggerVariant } : {},
11708
- label: `${selectedRows.length} selected ${selectionNoun}${selectedRows.length === 1 ? "" : "s"}`,
11709
- ...copy === void 0 ? {} : {
11710
- human: () => buildViewHuman(effectiveCopy, selectedRows, effectiveCopyColumns),
11711
- json: () => selectedRows.map((row) => effectiveCopy.agentRow ? effectiveCopy.agentRow(row) : row)
11712
- },
11713
- agent: () => copy === void 0 ? buildDefaultTableViewAgentInput(effectiveCopy, selectedRows, data, declaredVisibleCopyColumns, { scope: "selected" }) : buildViewAgentInput(effectiveCopy, selectedRows, data, { scope: "selected" }),
11714
- aiVariants: copySubsetVariantFor(
11715
- () => selectedRows,
11716
- "selected"
11717
- ),
11718
- export: {
11719
- items: [],
11720
- sheetRows: () => selectedRows.map(
11721
- (row) => projectDefaultTableCopyRow(row, declaredVisibleCopyColumns)
11722
- ),
11723
- sheetColumns: () => declaredVisibleCopyColumns.map((column) => ({
11724
- id: columnId(column),
11725
- label: column.label ?? (typeof column.header === "string" ? column.header : columnId(column))
11726
- }))
11727
- }
11728
- }
11729
- ) : null,
11730
- selection.actions?.(selectedRows, selection.selectedIds)
11731
- ] })
11732
- ] }) : null,
11733
13617
  /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { "data-matrx-table-body-region": true, className: "flex min-h-0 flex-1 flex-col gap-2", children: [
11734
13618
  /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "relative min-h-0 flex-1", children: [
11735
13619
  mobileCards ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
@@ -11837,12 +13721,33 @@ function MatrxDataTableCore({
11837
13721
  ]
11838
13722
  }
11839
13723
  ) : null,
13724
+ coverageResult && coverageResult.notice ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
13725
+ "div",
13726
+ {
13727
+ "data-matrx-table-coverage": true,
13728
+ "data-matrx-table-coverage-scope": coverageResult.scope,
13729
+ role: "status",
13730
+ className: "mb-1 flex items-start gap-2 rounded-md border border-amber-300/70 bg-amber-50 px-3 py-2 text-xs text-amber-900 dark:border-amber-500/40 dark:bg-amber-950/40 dark:text-amber-200",
13731
+ children: [
13732
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "font-semibold", children: coverageResult.badge }),
13733
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { children: coverageResult.notice })
13734
+ ]
13735
+ }
13736
+ ) : null,
11840
13737
  /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
11841
13738
  "div",
11842
13739
  {
11843
13740
  ref: scrollRef,
11844
13741
  "data-matrx-table-scroll": true,
11845
13742
  onScroll: updateScrollHint,
13743
+ ...spreadsheetEnabled ? {
13744
+ tabIndex: sheet.containerProps.tabIndex,
13745
+ onKeyDown: sheet.containerProps.onKeyDown,
13746
+ onCopy: sheet.containerProps.onCopy,
13747
+ onCut: sheet.containerProps.onCut,
13748
+ onPaste: sheet.containerProps.onPaste,
13749
+ "data-matrx-table-spreadsheet": "true"
13750
+ } : {},
11846
13751
  "aria-busy": isLoading || isFetching,
11847
13752
  style: { containerType: "inline-size" },
11848
13753
  className: cn(
@@ -11900,7 +13805,14 @@ function MatrxDataTableCore({
11900
13805
  ref: tableHeaderRef,
11901
13806
  "data-matrx-table-sticky-header": stickyHeader ? "true" : void 0,
11902
13807
  className: cn(
11903
- "z-10 border-b border-border bg-muted/90 shadow-[0_1px_0_0_var(--border)] backdrop-blur-sm",
13808
+ // 🚨 ABOVE THE FROZEN COLUMN, NOT UNDER IT. A frozen body
13809
+ // cell is `sm:z-20` so it can slide under the columns to
13810
+ // its right; the header was `z-10`, so the frozen column's
13811
+ // cells scrolled straight OVER the header they belong to
13812
+ // and the top data row printed through the column names.
13813
+ // Only a table long enough to scroll shows it, which is why
13814
+ // it survived until a hundred-thousand-row screenshot.
13815
+ "z-30 border-b border-border bg-muted/90 shadow-[0_1px_0_0_var(--border)] backdrop-blur-sm",
11904
13816
  stickyHeader && (documentStickyHeader ? "relative translate-y-[var(--matrx-document-sticky-offset)]" : "sticky top-0")
11905
13817
  ),
11906
13818
  children: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("tr", { children: [
@@ -11933,7 +13845,17 @@ function MatrxDataTableCore({
11933
13845
  )
11934
13846
  }
11935
13847
  ) : null,
11936
- visibleColumns.map((col) => {
13848
+ columnSpacers && columnSpacers.start > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
13849
+ "th",
13850
+ {
13851
+ "aria-hidden": "true",
13852
+ "data-matrx-table-column-spacer": "start",
13853
+ className: "border-0 p-0",
13854
+ style: { width: `${columnSpacers.start}px`, minWidth: `${columnSpacers.start}px` }
13855
+ },
13856
+ "matrx-column-spacer-start"
13857
+ ) : null,
13858
+ renderedColumns.map((col) => {
11937
13859
  const id = columnId(col);
11938
13860
  const meta = filterMeta.get(id);
11939
13861
  const isSorted = sort?.id === id;
@@ -11976,7 +13898,7 @@ function MatrxDataTableCore({
11976
13898
  frozenEdgeId === id && FROZEN_EDGE_CLASS
11977
13899
  ),
11978
13900
  style: {
11979
- ...columnWidthVar(width, effectiveColumnWidths[id], id, columnResizable),
13901
+ ...columnWidthVar(width, windowedWidthFor(id, width), id, columnResizable),
11980
13902
  ...frozenStyle(id)
11981
13903
  },
11982
13904
  children: [
@@ -12040,6 +13962,16 @@ function MatrxDataTableCore({
12040
13962
  id
12041
13963
  );
12042
13964
  }),
13965
+ columnSpacers && columnSpacers.end > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
13966
+ "th",
13967
+ {
13968
+ "aria-hidden": "true",
13969
+ "data-matrx-table-column-spacer": "end",
13970
+ className: "border-0 p-0",
13971
+ style: { width: `${columnSpacers.end}px`, minWidth: `${columnSpacers.end}px` }
13972
+ },
13973
+ "matrx-column-spacer-end"
13974
+ ) : null,
12043
13975
  showActionsCol && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
12044
13976
  "th",
12045
13977
  {
@@ -12055,7 +13987,17 @@ function MatrxDataTableCore({
12055
13987
  /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("tbody", { children: [
12056
13988
  isLoading ? Array.from({ length: 6 }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("tr", { className: "border-b border-border/60", children: [
12057
13989
  selection ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("td", { className: "px-2 py-2", style: fixedUtilityWidth(fixedTableGeometry?.selectionWidth), children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Skeleton, { className: "h-3.5 w-3.5" }) }) : null,
12058
- visibleColumns.map((col) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
13990
+ columnSpacers && columnSpacers.start > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
13991
+ "td",
13992
+ {
13993
+ "aria-hidden": "true",
13994
+ "data-matrx-table-column-spacer": "start",
13995
+ className: "border-0 p-0",
13996
+ style: { width: `${columnSpacers.start}px`, minWidth: `${columnSpacers.start}px` }
13997
+ },
13998
+ "matrx-column-spacer-start"
13999
+ ) : null,
14000
+ renderedColumns.map((col) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
12059
14001
  "td",
12060
14002
  {
12061
14003
  className: cn(
@@ -12067,11 +14009,21 @@ function MatrxDataTableCore({
12067
14009
  },
12068
14010
  columnId(col)
12069
14011
  )),
12070
- showActionsCol && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("td", { className: "text-right align-middle", style: fixedUtilityWidth(fixedTableGeometry?.actionsWidth), children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Skeleton, { className: "ml-auto h-5 w-10" }) })
14012
+ showActionsCol && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("td", { className: "text-right align-middle", style: fixedUtilityWidth(fixedTableGeometry?.actionsWidth), children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(Skeleton, { className: "ml-auto h-5 w-10" }) }),
14013
+ columnSpacers && columnSpacers.end > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
14014
+ "td",
14015
+ {
14016
+ "aria-hidden": "true",
14017
+ "data-matrx-table-column-spacer": "end",
14018
+ className: "border-0 p-0",
14019
+ style: { width: `${columnSpacers.end}px`, minWidth: `${columnSpacers.end}px` }
14020
+ },
14021
+ "matrx-column-spacer-end"
14022
+ ) : null
12071
14023
  ] }, `sk-${i}`)) : paginated.length === 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("tr", { children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
12072
14024
  "td",
12073
14025
  {
12074
- colSpan: visibleColumns.length + leadingCols + (showActionsCol ? 1 : 0),
14026
+ colSpan: renderedColSpan,
12075
14027
  className: "px-0 py-12 text-center",
12076
14028
  children: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { "data-matrx-table-empty-state": true, className: "sticky left-0 flex flex-col items-center gap-2 whitespace-normal px-4", style: { width: "100cqw", maxWidth: "100%" }, children: [
12077
14029
  renderedEmptyState?.icon,
@@ -12080,7 +14032,43 @@ function MatrxDataTableCore({
12080
14032
  renderedEmptyState?.action
12081
14033
  ] })
12082
14034
  }
12083
- ) }) : paginated.map((row, index) => {
14035
+ ) }) : bodyEntries.map((entry) => {
14036
+ if (entry.kind === "spacer") {
14037
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
14038
+ "tr",
14039
+ {
14040
+ "aria-hidden": "true",
14041
+ "data-matrx-table-virtual-spacer": entry.place,
14042
+ style: { height: `${entry.height}px` },
14043
+ children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
14044
+ "td",
14045
+ {
14046
+ className: "border-0 p-0",
14047
+ colSpan: renderedColSpan
14048
+ }
14049
+ )
14050
+ },
14051
+ `matrx-virtual-spacer-${entry.place}`
14052
+ );
14053
+ }
14054
+ if (entry.kind === "group") {
14055
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
14056
+ TableGroupHeaderRow,
14057
+ {
14058
+ group: entry.group,
14059
+ collapsed: entry.collapsed,
14060
+ columns: renderedColumns,
14061
+ spacers: columnSpacers,
14062
+ leadingCols,
14063
+ showActionsCol,
14064
+ rowNoun: grouping?.rowNoun ?? "row",
14065
+ onToggle: () => setCollapsedGroups(toggleGroupCollapsed(collapsedGroups, entry.group.key))
14066
+ },
14067
+ `matrx-group-${entry.group.key}`
14068
+ );
14069
+ }
14070
+ const row = entry.row;
14071
+ const index = entry.index;
12084
14072
  const id = getRowId(row);
12085
14073
  const isSelected = selectedId === id;
12086
14074
  const rowEdits = edits[id];
@@ -12091,6 +14079,7 @@ function MatrxDataTableCore({
12091
14079
  "tr",
12092
14080
  {
12093
14081
  "data-row-id": id,
14082
+ style: explicitRowHeight !== void 0 ? { height: `${explicitRowHeight}px` } : virtualizeActive ? { height: `${virtualRowHeight}px` } : void 0,
12094
14083
  "data-state": isSelected ? "selected" : void 0,
12095
14084
  onClick: (e) => {
12096
14085
  if (rowEventTargetsInteractiveDescendant(e.target)) return;
@@ -12163,15 +14152,40 @@ function MatrxDataTableCore({
12163
14152
  ) : null
12164
14153
  }
12165
14154
  ) : null,
12166
- visibleColumns.map((col, colIdx) => {
14155
+ columnSpacers && columnSpacers.start > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
14156
+ "td",
14157
+ {
14158
+ "aria-hidden": "true",
14159
+ "data-matrx-table-column-spacer": "start",
14160
+ className: "border-0 p-0",
14161
+ style: { width: `${columnSpacers.start}px`, minWidth: `${columnSpacers.start}px` }
14162
+ },
14163
+ "matrx-column-spacer-start"
14164
+ ) : null,
14165
+ renderedColumns.map((col, colIdx) => {
12167
14166
  const isColumnDragShadow = columnDragPreview?.draggedId === columnId(col) && columnDragPreview.targetId !== null;
12168
- return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
14167
+ const sheetCell = spreadsheetEnabled ? sheet.cellProps(id, columnId(col)) : null;
14168
+ const carriesFillHandle = sheetCell !== null && sheet.isFillHandleCell(id, columnId(col));
14169
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
12169
14170
  "td",
12170
14171
  {
12171
14172
  "data-matrx-table-column-id": columnId(col),
12172
14173
  "data-matrx-column-drag-shadow": isColumnDragShadow ? "true" : void 0,
14174
+ ...sheetCell ? {
14175
+ "data-matrx-cell-row": sheetCell["data-matrx-cell-row"],
14176
+ "data-matrx-cell-col": sheetCell["data-matrx-cell-col"],
14177
+ ...sheetCell["data-matrx-cell-selected"] ? { "data-matrx-cell-selected": "true" } : {},
14178
+ ...sheetCell["data-matrx-cell-active"] ? { "data-matrx-cell-active": "true" } : {},
14179
+ ...sheetCell["data-matrx-cell-fill-preview"] ? { "data-matrx-cell-fill-preview": "true" } : {},
14180
+ onMouseDown: sheetCell.onMouseDown,
14181
+ onMouseEnter: sheetCell.onMouseEnter
14182
+ } : {},
12173
14183
  className: cn(
12174
14184
  "py-1.5 align-middle lg:py-0.5",
14185
+ sheetCell?.["data-matrx-cell-selected"] && "bg-primary/10",
14186
+ sheetCell?.["data-matrx-cell-fill-preview"] && "ring-1 ring-inset ring-primary/40",
14187
+ sheetCell?.["data-matrx-cell-active"] && "relative z-10 ring-2 ring-inset ring-primary",
14188
+ carriesFillHandle && "relative",
12175
14189
  col.compact || resolveColumnMarker(col) !== void 0 ? "px-1" : "px-2",
12176
14190
  // nowrap (NOT truncate — truncate clips the cell and
12177
14191
  // defeats w-max, killing the horizontal scroll).
@@ -12193,42 +14207,64 @@ function MatrxDataTableCore({
12193
14207
  style: {
12194
14208
  ...columnWidthVar(
12195
14209
  resolveColumnMarker(col) && col.width === void 0 ? 40 : col.width,
12196
- effectiveColumnWidths[columnId(col)],
14210
+ windowedWidthFor(columnId(col), col.width),
12197
14211
  columnId(col),
12198
14212
  resizableColumns && col.resizable !== false
12199
14213
  ),
12200
14214
  ...frozenStyle(columnId(col))
12201
14215
  },
12202
- children: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
12203
- "div",
12204
- {
12205
- className: cn(
12206
- colIdx === 0 && hierarchy && "flex min-w-0 items-center"
12207
- ),
12208
- children: [
12209
- colIdx === 0 && hierarchy ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
12210
- HierarchyDragHandle,
12211
- {
12212
- id,
12213
- label: hierarchy.itemLabel?.(row) ?? "row",
12214
- disabled: !(hierarchy.canReparent?.(row) ?? true)
12215
- }
12216
- ) : null,
12217
- /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
12218
- "div",
12219
- {
12220
- "data-matrx-cell-content": true,
12221
- className: "min-w-0 max-w-full flex-1 whitespace-normal break-words [overflow-wrap:anywhere] sm:[&_*]:min-w-0 sm:[&_*]:whitespace-normal sm:[&_*]:[overflow-wrap:anywhere] sm:[&_.truncate]:whitespace-nowrap sm:[&_[data-slot=badge]]:whitespace-nowrap sm:[&_[data-slot=badge]]:[overflow-wrap:normal] sm:[&_[data-matrx-cell-control]]:w-max sm:[&_[data-matrx-cell-control]]:min-w-max sm:[&_[data-matrx-cell-control]]:max-w-none sm:[&_[data-matrx-cell-control]]:whitespace-nowrap sm:[&_[data-matrx-cell-control]]:[overflow-wrap:normal] sm:[&_[data-matrx-cell-control]_*]:whitespace-nowrap sm:[&_[data-matrx-cell-control]_*]:[overflow-wrap:normal]",
12222
- children: renderRecordCell(row, col, index)
12223
- }
12224
- )
12225
- ]
12226
- }
12227
- )
14216
+ children: [
14217
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
14218
+ "div",
14219
+ {
14220
+ className: cn(
14221
+ colIdx === 0 && hierarchy && "flex min-w-0 items-center"
14222
+ ),
14223
+ children: [
14224
+ colIdx === 0 && hierarchy ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
14225
+ HierarchyDragHandle,
14226
+ {
14227
+ id,
14228
+ label: hierarchy.itemLabel?.(row) ?? "row",
14229
+ disabled: !(hierarchy.canReparent?.(row) ?? true)
14230
+ }
14231
+ ) : null,
14232
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
14233
+ "div",
14234
+ {
14235
+ "data-matrx-cell-content": true,
14236
+ className: "min-w-0 max-w-full flex-1 whitespace-normal break-words [overflow-wrap:anywhere] sm:[&_*]:min-w-0 sm:[&_*]:whitespace-normal sm:[&_*]:[overflow-wrap:anywhere] sm:[&_.truncate]:whitespace-nowrap sm:[&_[data-slot=badge]]:whitespace-nowrap sm:[&_[data-slot=badge]]:[overflow-wrap:normal] sm:[&_[data-matrx-cell-control]]:w-max sm:[&_[data-matrx-cell-control]]:min-w-max sm:[&_[data-matrx-cell-control]]:max-w-none sm:[&_[data-matrx-cell-control]]:whitespace-nowrap sm:[&_[data-matrx-cell-control]]:[overflow-wrap:normal] sm:[&_[data-matrx-cell-control]_*]:whitespace-nowrap sm:[&_[data-matrx-cell-control]_*]:[overflow-wrap:normal]",
14237
+ children: renderRecordCell(row, col, index)
14238
+ }
14239
+ )
14240
+ ]
14241
+ }
14242
+ ),
14243
+ carriesFillHandle ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
14244
+ "button",
14245
+ {
14246
+ type: "button",
14247
+ "data-matrx-table-fill-handle": true,
14248
+ "aria-label": "Drag to fill the cells below or beside this selection",
14249
+ onMouseDown: sheet.startFillDrag,
14250
+ className: "absolute -bottom-[3px] -right-[3px] z-20 h-2 w-2 cursor-crosshair rounded-[1px] border border-background bg-primary"
14251
+ }
14252
+ ) : null
14253
+ ]
12228
14254
  },
12229
14255
  columnId(col)
12230
14256
  );
12231
14257
  }),
14258
+ columnSpacers && columnSpacers.end > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
14259
+ "td",
14260
+ {
14261
+ "aria-hidden": "true",
14262
+ "data-matrx-table-column-spacer": "end",
14263
+ className: "border-0 p-0",
14264
+ style: { width: `${columnSpacers.end}px`, minWidth: `${columnSpacers.end}px` }
14265
+ },
14266
+ "matrx-column-spacer-end"
14267
+ ) : null,
12232
14268
  showActionsCol && /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
12233
14269
  "td",
12234
14270
  {
@@ -12291,7 +14327,7 @@ function MatrxDataTableCore({
12291
14327
  children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
12292
14328
  "td",
12293
14329
  {
12294
- colSpan: visibleColumns.length + leadingCols + (showActionsCol ? 1 : 0),
14330
+ colSpan: renderedColSpan,
12295
14331
  className: "p-4",
12296
14332
  children: expandedDetail.render(displayRow, controls)
12297
14333
  }
@@ -12299,7 +14335,7 @@ function MatrxDataTableCore({
12299
14335
  },
12300
14336
  `${id}:expanded-detail`
12301
14337
  ) : null;
12302
- return hierarchy ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_react20.Fragment, { children: [
14338
+ return hierarchy ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_react21.Fragment, { children: [
12303
14339
  /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
12304
14340
  HierarchyDroppableRow,
12305
14341
  {
@@ -12309,7 +14345,7 @@ function MatrxDataTableCore({
12309
14345
  }
12310
14346
  ),
12311
14347
  inlineDetail
12312
- ] }, id) : /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_react20.Fragment, { children: [
14348
+ ] }, id) : /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(import_react21.Fragment, { children: [
12313
14349
  wrappedRow,
12314
14350
  inlineDetail
12315
14351
  ] }, id);
@@ -12317,7 +14353,7 @@ function MatrxDataTableCore({
12317
14353
  appendQuery !== null && isFetching && !isLoading ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("tr", { "data-matrx-table-append-loading-row": true, children: /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
12318
14354
  "td",
12319
14355
  {
12320
- colSpan: visibleColumns.length + leadingCols + (showActionsCol ? 1 : 0),
14356
+ colSpan: renderedColSpan,
12321
14357
  className: "sticky bottom-0 z-20 h-16 border-y border-border bg-card/95 px-0 py-0 text-left shadow-sm backdrop-blur-sm",
12322
14358
  children: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
12323
14359
  "div",
@@ -12342,7 +14378,17 @@ function MatrxDataTableCore({
12342
14378
  ] }),
12343
14379
  footerRows.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("tfoot", { className: "border-t-2 border-border bg-muted/50 font-semibold", children: footerRows.map((row, index) => /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("tr", { children: [
12344
14380
  selection ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("td", { className: "px-2 py-2" }) : null,
12345
- visibleColumns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
14381
+ columnSpacers && columnSpacers.start > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
14382
+ "td",
14383
+ {
14384
+ "aria-hidden": "true",
14385
+ "data-matrx-table-column-spacer": "start",
14386
+ className: "border-0 p-0",
14387
+ style: { width: `${columnSpacers.start}px`, minWidth: `${columnSpacers.start}px` }
14388
+ },
14389
+ "matrx-column-spacer-start"
14390
+ ) : null,
14391
+ renderedColumns.map((column) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
12346
14392
  "td",
12347
14393
  {
12348
14394
  className: cn(
@@ -12354,7 +14400,7 @@ function MatrxDataTableCore({
12354
14400
  ),
12355
14401
  style: columnWidthVar(
12356
14402
  resolveColumnMarker(column) && column.width === void 0 ? 40 : column.width,
12357
- effectiveColumnWidths[columnId(column)],
14403
+ windowedWidthFor(columnId(column), column.width),
12358
14404
  columnId(column),
12359
14405
  resizableColumns && column.resizable !== false
12360
14406
  ),
@@ -12362,6 +14408,16 @@ function MatrxDataTableCore({
12362
14408
  },
12363
14409
  columnId(column)
12364
14410
  )),
14411
+ columnSpacers && columnSpacers.end > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
14412
+ "td",
14413
+ {
14414
+ "aria-hidden": "true",
14415
+ "data-matrx-table-column-spacer": "end",
14416
+ className: "border-0 p-0",
14417
+ style: { width: `${columnSpacers.end}px`, minWidth: `${columnSpacers.end}px` }
14418
+ },
14419
+ "matrx-column-spacer-end"
14420
+ ) : null,
12365
14421
  showActionsCol ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("td", {}) : null
12366
14422
  ] }, `footer-${index}`)) }) : null
12367
14423
  ]
@@ -12536,7 +14592,73 @@ function MatrxDataTableCore({
12536
14592
  compact: true,
12537
14593
  hideEntriesInfo: false
12538
14594
  }
12539
- ) }) : null
14595
+ ) }) : null,
14596
+ selection && selectionCount > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
14597
+ "div",
14598
+ {
14599
+ "data-matrx-table-bulk-bar": true,
14600
+ className: "mt-2 flex shrink-0 flex-wrap items-center gap-2 rounded-md border border-primary/30 bg-primary/5 px-2 py-1.5",
14601
+ children: [
14602
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("span", { className: "text-xs font-medium text-foreground", children: [
14603
+ (0, import_format5.formatCount)(selectionCount),
14604
+ " ",
14605
+ selectionNoun,
14606
+ selectionCount === 1 ? "" : "s",
14607
+ " selected"
14608
+ ] }),
14609
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
14610
+ Button,
14611
+ {
14612
+ type: "button",
14613
+ variant: "ghost",
14614
+ size: "sm",
14615
+ className: "h-7 gap-1 px-2 text-xs",
14616
+ onClick: () => {
14617
+ lastToggledIndex.current = null;
14618
+ selection.onSelectedIdsChange([]);
14619
+ },
14620
+ children: [
14621
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(X, { className: "h-3.5 w-3.5" }),
14622
+ "Clear"
14623
+ ]
14624
+ }
14625
+ ),
14626
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "ml-auto flex flex-wrap items-center gap-1.5", children: [
14627
+ effectiveCopy && selectedRows.length > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
14628
+ CopyButtons,
14629
+ {
14630
+ size: "icon",
14631
+ "data-matrx-table-icon-action": true,
14632
+ sourceId: `${tableCopySourceNamespace}:selection`,
14633
+ ...effectiveCopy.references ? { references: effectiveCopy.references(selectedRows, data) } : {},
14634
+ ...effectiveCopy.triggerVariant ? { triggerVariant: effectiveCopy.triggerVariant } : {},
14635
+ label: `${selectedRows.length} selected ${selectionNoun}${selectedRows.length === 1 ? "" : "s"}`,
14636
+ ...copy === void 0 ? {} : {
14637
+ human: () => buildViewHuman(effectiveCopy, selectedRows, effectiveCopyColumns),
14638
+ json: () => selectedRows.map((row) => effectiveCopy.agentRow ? effectiveCopy.agentRow(row) : row)
14639
+ },
14640
+ agent: () => copy === void 0 ? buildDefaultTableViewAgentInput(effectiveCopy, selectedRows, data, declaredVisibleCopyColumns, { scope: "selected" }) : buildViewAgentInput(effectiveCopy, selectedRows, data, { scope: "selected" }),
14641
+ aiVariants: copySubsetVariantFor(
14642
+ () => selectedRows,
14643
+ "selected"
14644
+ ),
14645
+ export: {
14646
+ items: [],
14647
+ sheetRows: () => selectedRows.map(
14648
+ (row) => projectDefaultTableCopyRow(row, declaredVisibleCopyColumns)
14649
+ ),
14650
+ sheetColumns: () => declaredVisibleCopyColumns.map((column) => ({
14651
+ id: columnId(column),
14652
+ label: column.label ?? (typeof column.header === "string" ? column.header : columnId(column))
14653
+ }))
14654
+ }
14655
+ }
14656
+ ) : null,
14657
+ selection.actions?.(selectedRows, selection.selectedIds)
14658
+ ] })
14659
+ ]
14660
+ }
14661
+ ) : null
12540
14662
  ] }),
12541
14663
  /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
12542
14664
  ColumnManager,
@@ -12818,7 +14940,7 @@ function HierarchyDragIndicatorLayer({
12818
14940
  scrollRef,
12819
14941
  resolve: resolve2
12820
14942
  }) {
12821
- const [indicator, setIndicator] = (0, import_react20.useState)(
14943
+ const [indicator, setIndicator] = (0, import_react21.useState)(
12822
14944
  null
12823
14945
  );
12824
14946
  (0, import_core3.useDndMonitor)({
@@ -12922,9 +15044,9 @@ function HierarchyDroppableRow({
12922
15044
  children
12923
15045
  }) {
12924
15046
  const { setNodeRef } = (0, import_core3.useDroppable)({ id, disabled });
12925
- if (!(0, import_react20.isValidElement)(children) || children.type !== "tr") return children;
15047
+ if (!(0, import_react21.isValidElement)(children) || children.type !== "tr") return children;
12926
15048
  const row = children;
12927
- return (0, import_react20.cloneElement)(row, {
15049
+ return (0, import_react21.cloneElement)(row, {
12928
15050
  ref: setNodeRef,
12929
15051
  className: row.props.className
12930
15052
  });
@@ -12959,6 +15081,71 @@ function normalizeColumnWidthsForColumns(widths, columns) {
12959
15081
  })
12960
15082
  );
12961
15083
  }
15084
+ function TableGroupHeaderRow({
15085
+ group,
15086
+ collapsed,
15087
+ columns,
15088
+ spacers,
15089
+ leadingCols,
15090
+ showActionsCol,
15091
+ rowNoun,
15092
+ onToggle
15093
+ }) {
15094
+ const count = group.rows.length;
15095
+ const summary = `${count.toLocaleString()} ${count === 1 ? rowNoun : `${rowNoun}s`}`;
15096
+ const firstId = columns[0] ? columnId(columns[0]) : null;
15097
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)(
15098
+ "tr",
15099
+ {
15100
+ "data-matrx-table-group-row": group.key,
15101
+ "data-matrx-table-group-collapsed": collapsed ? "true" : "false",
15102
+ className: "border-y border-border bg-muted/40",
15103
+ children: [
15104
+ Array.from({ length: leadingCols }).map((_, i) => /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("td", { className: "px-2 py-1.5" }, `group-lead-${i}`)),
15105
+ spacers && spacers.start > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("td", { "aria-hidden": "true", className: "border-0 p-0", style: { width: `${spacers.start}px` } }) : null,
15106
+ columns.map((col) => {
15107
+ const id = columnId(col);
15108
+ const aggregate = group.aggregates[id];
15109
+ if (id === firstId) {
15110
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("td", { className: cn("px-2 py-1.5 align-middle", col.mobileHidden && "max-sm:hidden"), children: /* @__PURE__ */ (0, import_jsx_runtime42.jsxs)("div", { className: "flex items-center gap-1.5", children: [
15111
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
15112
+ "button",
15113
+ {
15114
+ type: "button",
15115
+ onClick: onToggle,
15116
+ "aria-expanded": !collapsed,
15117
+ "aria-label": `${collapsed ? "Show" : "Hide"} ${group.label}`,
15118
+ className: "inline-flex h-5 w-5 shrink-0 items-center justify-center rounded text-muted-foreground hover:bg-accent hover:text-foreground",
15119
+ children: collapsed ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ChevronRight, { className: "h-4 w-4" }) : /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(ChevronDown, { className: "h-4 w-4" })
15120
+ }
15121
+ ),
15122
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "truncate text-sm font-semibold text-foreground", children: group.label }),
15123
+ /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { className: "whitespace-nowrap text-xs text-muted-foreground", children: summary })
15124
+ ] }) }, id);
15125
+ }
15126
+ return /* @__PURE__ */ (0, import_jsx_runtime42.jsx)(
15127
+ "td",
15128
+ {
15129
+ className: cn(
15130
+ "px-2 py-1.5 align-middle text-xs font-medium text-muted-foreground",
15131
+ // A subtotal lines up with the numbers it is a subtotal OF.
15132
+ // Left-aligned under a right-aligned money column it reads as a
15133
+ // different column's value.
15134
+ alignForFormat(col) === "center" && "text-center",
15135
+ alignForFormat(col) === "right" && "text-right",
15136
+ col.mobileHidden && "max-sm:hidden"
15137
+ ),
15138
+ children: aggregate ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("span", { "data-matrx-table-group-aggregate": id, className: "whitespace-nowrap", children: aggregate.label }) : null
15139
+ },
15140
+ id
15141
+ );
15142
+ }),
15143
+ spacers && spacers.end > 0 ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("td", { "aria-hidden": "true", className: "border-0 p-0", style: { width: `${spacers.end}px` } }) : null,
15144
+ showActionsCol ? /* @__PURE__ */ (0, import_jsx_runtime42.jsx)("td", { className: "px-2 py-1.5" }) : null
15145
+ ]
15146
+ }
15147
+ );
15148
+ }
12962
15149
  function columnWidthVar(width, resizedWidth, id, resizable) {
12963
15150
  if (width === void 0 && resizedWidth === void 0 && !resizable) return void 0;
12964
15151
  const inferredWidth = resizable && inferTableSearchRole(id) === "name" ? "12rem" : "auto";
@@ -12968,7 +15155,7 @@ function columnWidthVar(width, resizedWidth, id, resizable) {
12968
15155
  };
12969
15156
  }
12970
15157
  function DefaultJsonCell({ value }) {
12971
- const [open, setOpen] = (0, import_react20.useState)(false);
15158
+ const [open, setOpen] = (0, import_react21.useState)(false);
12972
15159
  const host = useMatrxDataTableHost();
12973
15160
  const Viewer = host.JsonViewer;
12974
15161
  const text = JSON.stringify(value, null, 2);
@@ -13051,7 +15238,7 @@ function resolveStringTitle(title, fallback) {
13051
15238
  }
13052
15239
 
13053
15240
  // src/data-table/SavedViewsControl.tsx
13054
- var import_react21 = require("react");
15241
+ var import_react22 = require("react");
13055
15242
 
13056
15243
  // src/dropdown-menu.tsx
13057
15244
  var DropdownMenuPrimitive = __toESM(require("@radix-ui/react-dropdown-menu"), 1);
@@ -13194,11 +15381,11 @@ function SavedViewsControl({
13194
15381
  onUpdate,
13195
15382
  onReload
13196
15383
  }) {
13197
- const unsavedChangesId = (0, import_react21.useId)();
13198
- const [saveOpen, setSaveOpen] = (0, import_react21.useState)(false);
13199
- const [name, setName] = (0, import_react21.useState)("");
13200
- const [saving, setSaving] = (0, import_react21.useState)(false);
13201
- const [actionError, setActionError] = (0, import_react21.useState)(null);
15384
+ const unsavedChangesId = (0, import_react22.useId)();
15385
+ const [saveOpen, setSaveOpen] = (0, import_react22.useState)(false);
15386
+ const [name, setName] = (0, import_react22.useState)("");
15387
+ const [saving, setSaving] = (0, import_react22.useState)(false);
15388
+ const [actionError, setActionError] = (0, import_react22.useState)(null);
13202
15389
  const visibleError = actionError ?? error;
13203
15390
  const save = async () => {
13204
15391
  const next = name.trim();