@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
@@ -0,0 +1,1014 @@
1
+ "use client";
2
+ "use strict";
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
7
+ var __export = (target, all) => {
8
+ for (var name in all)
9
+ __defProp(target, name, { get: all[name], enumerable: true });
10
+ };
11
+ var __copyProps = (to, from, except, desc) => {
12
+ if (from && typeof from === "object" || typeof from === "function") {
13
+ for (let key of __getOwnPropNames(from))
14
+ if (!__hasOwnProp.call(to, key) && key !== except)
15
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
16
+ }
17
+ return to;
18
+ };
19
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
20
+
21
+ // src/data-table/useSpreadsheetGrid.ts
22
+ var useSpreadsheetGrid_exports = {};
23
+ __export(useSpreadsheetGrid_exports, {
24
+ NO_PATCHES: () => NOTHING,
25
+ useSpreadsheetGrid: () => useSpreadsheetGrid
26
+ });
27
+ module.exports = __toCommonJS(useSpreadsheetGrid_exports);
28
+ var import_react = require("react");
29
+
30
+ // src/data-table/grid-selection.ts
31
+ function rangeBounds(range, rowIds, fieldNames) {
32
+ const ra = rowIds.indexOf(range.anchor.rowId);
33
+ const rf = rowIds.indexOf(range.focus.rowId);
34
+ const ca = fieldNames.indexOf(range.anchor.fieldName);
35
+ const cf = fieldNames.indexOf(range.focus.fieldName);
36
+ if (ra === -1 || rf === -1 || ca === -1 || cf === -1) return null;
37
+ return {
38
+ r0: Math.min(ra, rf),
39
+ r1: Math.max(ra, rf),
40
+ c0: Math.min(ca, cf),
41
+ c1: Math.max(ca, cf)
42
+ };
43
+ }
44
+ function boundsContain(bounds, rowIndex, colIndex) {
45
+ return rowIndex >= bounds.r0 && rowIndex <= bounds.r1 && colIndex >= bounds.c0 && colIndex <= bounds.c1;
46
+ }
47
+ function rangeRows(range, rowIds, fieldNames) {
48
+ const b = rangeBounds(range, rowIds, fieldNames);
49
+ if (!b) return [];
50
+ const rows = [];
51
+ for (let r = b.r0; r <= b.r1; r += 1) {
52
+ const rowId = rowIds[r];
53
+ if (rowId === void 0) continue;
54
+ const row = [];
55
+ for (let c = b.c0; c <= b.c1; c += 1) {
56
+ const fieldName = fieldNames[c];
57
+ if (fieldName === void 0) continue;
58
+ row.push({ rowId, fieldName });
59
+ }
60
+ rows.push(row);
61
+ }
62
+ return rows;
63
+ }
64
+ function rowRange(rowId, fieldNames) {
65
+ const firstField = fieldNames[0];
66
+ const lastField = fieldNames[fieldNames.length - 1];
67
+ if (firstField === void 0 || lastField === void 0) return null;
68
+ return {
69
+ anchor: { rowId, fieldName: firstField },
70
+ focus: { rowId, fieldName: lastField }
71
+ };
72
+ }
73
+ function columnRange(fieldName, rowIds) {
74
+ const firstRow = rowIds[0];
75
+ const lastRow = rowIds[rowIds.length - 1];
76
+ if (firstRow === void 0 || lastRow === void 0) return null;
77
+ return {
78
+ anchor: { rowId: firstRow, fieldName },
79
+ focus: { rowId: lastRow, fieldName }
80
+ };
81
+ }
82
+ function allRange(rowIds, fieldNames) {
83
+ const firstRow = rowIds[0];
84
+ const lastRow = rowIds[rowIds.length - 1];
85
+ const firstField = fieldNames[0];
86
+ const lastField = fieldNames[fieldNames.length - 1];
87
+ if (firstRow === void 0 || lastRow === void 0 || firstField === void 0 || lastField === void 0) {
88
+ return null;
89
+ }
90
+ return {
91
+ anchor: { rowId: firstRow, fieldName: firstField },
92
+ focus: { rowId: lastRow, fieldName: lastField }
93
+ };
94
+ }
95
+ function sameCell(a, b) {
96
+ if (!a || !b) return a === b;
97
+ return a.rowId === b.rowId && a.fieldName === b.fieldName;
98
+ }
99
+ function moveSelection(current, move, rowIds, fieldNames) {
100
+ if (rowIds.length === 0 || fieldNames.length === 0) return null;
101
+ const firstRowId = rowIds[0];
102
+ const firstFieldName = fieldNames[0];
103
+ if (firstRowId === void 0 || firstFieldName === void 0) return null;
104
+ const first = { rowId: firstRowId, fieldName: firstFieldName };
105
+ if (!current) return first;
106
+ let r = rowIds.indexOf(current.rowId);
107
+ let c = fieldNames.indexOf(current.fieldName);
108
+ if (r === -1 || c === -1) return first;
109
+ const lastRow = rowIds.length - 1;
110
+ const lastCol = fieldNames.length - 1;
111
+ const clamp = (n, max) => Math.min(Math.max(n, 0), max);
112
+ switch (move) {
113
+ case "up":
114
+ r = clamp(r - 1, lastRow);
115
+ break;
116
+ case "down":
117
+ r = clamp(r + 1, lastRow);
118
+ break;
119
+ case "left":
120
+ c = clamp(c - 1, lastCol);
121
+ break;
122
+ case "right":
123
+ c = clamp(c + 1, lastCol);
124
+ break;
125
+ case "rowStart":
126
+ c = 0;
127
+ break;
128
+ case "rowEnd":
129
+ c = lastCol;
130
+ break;
131
+ case "gridStart":
132
+ r = 0;
133
+ c = 0;
134
+ break;
135
+ case "gridEnd":
136
+ r = lastRow;
137
+ c = lastCol;
138
+ break;
139
+ case "nextCell":
140
+ if (c < lastCol) c += 1;
141
+ else if (r < lastRow) {
142
+ r += 1;
143
+ c = 0;
144
+ }
145
+ break;
146
+ case "prevCell":
147
+ if (c > 0) c -= 1;
148
+ else if (r > 0) {
149
+ r -= 1;
150
+ c = lastCol;
151
+ }
152
+ break;
153
+ }
154
+ const rowId = rowIds[r];
155
+ const fieldName = fieldNames[c];
156
+ if (rowId === void 0 || fieldName === void 0) return first;
157
+ return { rowId, fieldName };
158
+ }
159
+ function isTypingKey(e) {
160
+ if (e.ctrlKey || e.metaKey || e.altKey) return false;
161
+ return e.key.length === 1 && e.key !== " ";
162
+ }
163
+ function classifyGridKey(e) {
164
+ const mod = e.ctrlKey || e.metaKey;
165
+ const arrowMove = {
166
+ ArrowUp: mod ? "gridStart" : "up",
167
+ ArrowDown: mod ? "gridEnd" : "down",
168
+ ArrowLeft: mod ? "rowStart" : "left",
169
+ ArrowRight: mod ? "rowEnd" : "right",
170
+ Home: mod ? "gridStart" : "rowStart",
171
+ End: mod ? "gridEnd" : "rowEnd"
172
+ };
173
+ const arrow = arrowMove[e.key];
174
+ if (arrow !== void 0) {
175
+ return { kind: e.shiftKey ? "extend" : "move", move: arrow };
176
+ }
177
+ switch (e.key) {
178
+ case "Tab":
179
+ return { kind: "move", move: e.shiftKey ? "prevCell" : "nextCell" };
180
+ case "Enter":
181
+ case "F2":
182
+ return { kind: "edit" };
183
+ case " ":
184
+ if (e.shiftKey) return { kind: "selectRow" };
185
+ if (mod) return { kind: "selectColumn" };
186
+ return { kind: "edit" };
187
+ case "a":
188
+ case "A":
189
+ if (mod) return { kind: "selectAll" };
190
+ break;
191
+ case "d":
192
+ case "D":
193
+ if (mod) return { kind: "fillDown" };
194
+ break;
195
+ case "Escape":
196
+ return { kind: "escape" };
197
+ case "Delete":
198
+ case "Backspace":
199
+ return { kind: "clearCell" };
200
+ case "c":
201
+ case "C":
202
+ if (mod) return { kind: "copy" };
203
+ break;
204
+ case "x":
205
+ case "X":
206
+ if (mod) return { kind: "cut" };
207
+ break;
208
+ case "v":
209
+ case "V":
210
+ if (mod) return { kind: "paste" };
211
+ break;
212
+ }
213
+ if (isTypingKey(e)) return { kind: "editSeeded", seed: e.key };
214
+ return null;
215
+ }
216
+
217
+ // src/data-table/grid-clipboard.ts
218
+ function cellClipboardText(value) {
219
+ if (value === null || value === void 0) return "";
220
+ if (typeof value === "object") return JSON.stringify(value);
221
+ return String(value);
222
+ }
223
+ function tsvCell(text) {
224
+ return /[\t\r\n"]/.test(text) ? `"${text.replace(/"/g, '""')}"` : text;
225
+ }
226
+ function gridToTsv(block) {
227
+ return block.map((row) => row.map((cell) => tsvCell(cellClipboardText(cell))).join(" ")).join("\n");
228
+ }
229
+ function parseClipboardGrid(text) {
230
+ const rows = [];
231
+ let row = [];
232
+ let cell = "";
233
+ let i = 0;
234
+ const n = text.length;
235
+ while (i < n) {
236
+ const ch = text[i];
237
+ if (cell === "" && ch === '"') {
238
+ let j = i + 1;
239
+ let out = "";
240
+ let closed = false;
241
+ while (j < n) {
242
+ const c = text[j];
243
+ if (c === '"') {
244
+ if (text[j + 1] === '"') {
245
+ out += '"';
246
+ j += 2;
247
+ continue;
248
+ }
249
+ closed = true;
250
+ j += 1;
251
+ break;
252
+ }
253
+ out += c;
254
+ j += 1;
255
+ }
256
+ if (closed && (j >= n || text[j] === " " || text[j] === "\n" || text[j] === "\r")) {
257
+ cell = out;
258
+ i = j;
259
+ if (i >= n) break;
260
+ const d = text[i];
261
+ if (d === " ") {
262
+ row.push(cell);
263
+ cell = "";
264
+ i += 1;
265
+ continue;
266
+ }
267
+ row.push(cell);
268
+ rows.push(row);
269
+ row = [];
270
+ cell = "";
271
+ i += d === "\r" && text[i + 1] === "\n" ? 2 : 1;
272
+ continue;
273
+ }
274
+ cell += ch;
275
+ i += 1;
276
+ continue;
277
+ }
278
+ if (ch === " ") {
279
+ row.push(cell);
280
+ cell = "";
281
+ i += 1;
282
+ continue;
283
+ }
284
+ if (ch === "\n" || ch === "\r") {
285
+ row.push(cell);
286
+ rows.push(row);
287
+ row = [];
288
+ cell = "";
289
+ i += ch === "\r" && text[i + 1] === "\n" ? 2 : 1;
290
+ continue;
291
+ }
292
+ cell += ch;
293
+ i += 1;
294
+ }
295
+ const endedOnBreak = n > 0 && (text[n - 1] === "\n" || text[n - 1] === "\r");
296
+ if (!endedOnBreak) {
297
+ row.push(cell);
298
+ rows.push(row);
299
+ } else if (row.length > 0) {
300
+ row.push(cell);
301
+ rows.push(row);
302
+ }
303
+ return rows.length === 0 ? [[""]] : rows;
304
+ }
305
+
306
+ // src/data-table/fill.ts
307
+ var NO_SERIES = { kind: "none", step: 0 };
308
+ var MS_PER_DAY = 864e5;
309
+ function plural(count, one, many) {
310
+ return `${count} ${count === 1 ? one : many}`;
311
+ }
312
+ function isValidRect(rect) {
313
+ 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;
314
+ }
315
+ function asBounds(rect) {
316
+ return { r0: rect.top, r1: rect.bottom, c0: rect.left, c1: rect.right };
317
+ }
318
+ function directionOf(source, target) {
319
+ if (!isValidRect(source) || !isValidRect(target)) return null;
320
+ if (target.top === source.bottom + 1) return "down";
321
+ if (target.bottom === source.top - 1) return "up";
322
+ if (target.left === source.right + 1) return "right";
323
+ if (target.right === source.left - 1) return "left";
324
+ return null;
325
+ }
326
+ function tileIndex(i, a, b) {
327
+ const n = b - a + 1;
328
+ if (n <= 0) return a;
329
+ if (i >= a && i <= b) return i;
330
+ if (i > b) return a + (i - a) % n;
331
+ return b - (a - 1 - i) % n;
332
+ }
333
+ function isFiniteNumber(v) {
334
+ return typeof v === "number" && Number.isFinite(v);
335
+ }
336
+ var DATE_ONLY = /^\d{4}-\d{2}-\d{2}$/;
337
+ var DATE_ISH = /^\d{4}-\d{2}-\d{2}([T ]|$)/;
338
+ function dateMs(v) {
339
+ if (v instanceof Date) {
340
+ const t = v.getTime();
341
+ return Number.isFinite(t) ? t : null;
342
+ }
343
+ if (typeof v === "string" && DATE_ISH.test(v)) {
344
+ const t = Date.parse(v);
345
+ return Number.isFinite(t) ? t : null;
346
+ }
347
+ return null;
348
+ }
349
+ function detectSeries(values) {
350
+ if (!Array.isArray(values) || values.length < 2) return NO_SERIES;
351
+ if (values.every(isFiniteNumber)) {
352
+ const nums = values;
353
+ const first = nums[0];
354
+ const second = nums[1];
355
+ if (first === void 0 || second === void 0) return NO_SERIES;
356
+ const step = second - first;
357
+ for (let i = 1; i < nums.length; i += 1) {
358
+ const prev = nums[i - 1];
359
+ const cur = nums[i];
360
+ if (prev === void 0 || cur === void 0) return NO_SERIES;
361
+ if (cur - prev !== step) return NO_SERIES;
362
+ }
363
+ return { kind: "number", step };
364
+ }
365
+ const stamps = values.map(dateMs);
366
+ if (stamps.every((t) => t !== null)) {
367
+ const first = stamps[0];
368
+ const second = stamps[1];
369
+ if (first === void 0 || second === void 0) return NO_SERIES;
370
+ const deltaMs = second - first;
371
+ if (deltaMs % MS_PER_DAY !== 0) return NO_SERIES;
372
+ for (let i = 1; i < stamps.length; i += 1) {
373
+ const prev = stamps[i - 1];
374
+ const cur = stamps[i];
375
+ if (prev === void 0 || cur === void 0) return NO_SERIES;
376
+ if (cur - prev !== deltaMs) return NO_SERIES;
377
+ }
378
+ return { kind: "date", step: deltaMs / MS_PER_DAY };
379
+ }
380
+ return NO_SERIES;
381
+ }
382
+ function formatDateLike(ms, sample) {
383
+ const iso = new Date(ms).toISOString();
384
+ if (typeof sample === "string" && DATE_ONLY.test(sample)) return iso.slice(0, 10);
385
+ return iso;
386
+ }
387
+ function extrapolate(line, series, k) {
388
+ const last = line[line.length - 1];
389
+ if (series.kind === "number" && isFiniteNumber(last)) {
390
+ return last + k * series.step;
391
+ }
392
+ if (series.kind === "date") {
393
+ const base = dateMs(last);
394
+ if (base === null) return jsonable(last);
395
+ return formatDateLike(base + k * series.step * MS_PER_DAY, last);
396
+ }
397
+ return jsonable(last);
398
+ }
399
+ function jsonable(value) {
400
+ if (value instanceof Date) {
401
+ const t = value.getTime();
402
+ return Number.isFinite(t) ? value.toISOString() : null;
403
+ }
404
+ if (value === void 0) return null;
405
+ return value;
406
+ }
407
+ function emptyResult(summary) {
408
+ return { patches: [], skipped: [], summary };
409
+ }
410
+ function fillRange(request) {
411
+ const { source, target, columnIds, rowIds } = request;
412
+ const mode = request.mode ?? "series";
413
+ const isWritable = request.isColumnWritable ?? (() => true);
414
+ const direction = directionOf(source, target);
415
+ if (!direction) {
416
+ return emptyResult(
417
+ "Filled 0 cells because the fill area does not touch the selection."
418
+ );
419
+ }
420
+ if (rowIds.length === 0 || columnIds.length === 0) {
421
+ return emptyResult("Filled 0 cells because the table is empty.");
422
+ }
423
+ const read = (r, c) => {
424
+ try {
425
+ return { ok: true, value: request.valueAt(r, c) };
426
+ } catch {
427
+ return { ok: false, value: void 0 };
428
+ }
429
+ };
430
+ const vertical = direction === "down" || direction === "up";
431
+ const forward = direction === "down" || direction === "right";
432
+ const fillStart = vertical ? source.top : source.left;
433
+ const fillEnd = vertical ? source.bottom : source.right;
434
+ const crossStart = vertical ? source.left : source.top;
435
+ const crossEnd = vertical ? source.right : source.bottom;
436
+ const maxFill = (vertical ? rowIds.length : columnIds.length) - 1;
437
+ const maxCross = (vertical ? columnIds.length : rowIds.length) - 1;
438
+ const rawFillFrom = vertical ? target.top : target.left;
439
+ const rawFillTo = vertical ? target.bottom : target.right;
440
+ const rawCrossFrom = vertical ? target.left : target.top;
441
+ const rawCrossTo = vertical ? target.right : target.bottom;
442
+ const fillFrom = Math.max(rawFillFrom, 0);
443
+ const fillTo = Math.min(rawFillTo, maxFill);
444
+ const crossFrom = Math.max(rawCrossFrom, 0);
445
+ const crossTo = Math.min(rawCrossTo, maxCross);
446
+ const requested = Math.max(rawFillTo - rawFillFrom + 1, 0) * Math.max(rawCrossTo - rawCrossFrom + 1, 0);
447
+ const inside = Math.max(fillTo - fillFrom + 1, 0) * Math.max(crossTo - crossFrom + 1, 0);
448
+ const outside = Math.max(requested - inside, 0);
449
+ const sourceBounds = asBounds(source);
450
+ const patches = [];
451
+ const skipped = [];
452
+ const touchedColumns = /* @__PURE__ */ new Set();
453
+ const lines = /* @__PURE__ */ new Map();
454
+ const lineFor = (crossIndex) => {
455
+ const srcCross = tileIndex(crossIndex, crossStart, crossEnd);
456
+ const cached = lines.get(srcCross);
457
+ if (cached) return cached;
458
+ const values = [];
459
+ const reads = [];
460
+ for (let f = fillStart; f <= fillEnd; f += 1) {
461
+ const r = vertical ? f : srcCross;
462
+ const c = vertical ? srcCross : f;
463
+ const got = read(r, c);
464
+ values.push(got.value);
465
+ reads.push(got.ok);
466
+ }
467
+ const series = mode === "series" ? detectSeries(values) : NO_SERIES;
468
+ const line = { values, reads, series };
469
+ lines.set(srcCross, line);
470
+ return line;
471
+ };
472
+ for (let f = fillFrom; f <= fillTo; f += 1) {
473
+ for (let x = crossFrom; x <= crossTo; x += 1) {
474
+ const rowIndex = vertical ? f : x;
475
+ const colIndex = vertical ? x : f;
476
+ const rowId = rowIds[rowIndex];
477
+ const columnId = columnIds[colIndex];
478
+ if (rowId === void 0 || columnId === void 0) continue;
479
+ if (boundsContain(sourceBounds, rowIndex, colIndex)) continue;
480
+ if (!isWritable(columnId)) {
481
+ skipped.push({
482
+ rowId,
483
+ columnId,
484
+ reason: `${columnId} cannot be filled because that column is read-only.`
485
+ });
486
+ continue;
487
+ }
488
+ const line = lineFor(x);
489
+ if (line.series.kind !== "none") {
490
+ const k = forward ? f - fillEnd : fillStart - f;
491
+ patches.push({ rowId, columnId, value: jsonable(extrapolate(line.values, line.series, k)) });
492
+ touchedColumns.add(columnId);
493
+ continue;
494
+ }
495
+ const srcFill = tileIndex(f, fillStart, fillEnd);
496
+ const patternIndex = srcFill - fillStart;
497
+ if (line.reads[patternIndex] !== true) {
498
+ skipped.push({
499
+ rowId,
500
+ columnId,
501
+ reason: `The value to copy into ${columnId} could not be read, so that cell was left alone.`
502
+ });
503
+ continue;
504
+ }
505
+ patches.push({ rowId, columnId, value: jsonable(line.values[patternIndex]) });
506
+ touchedColumns.add(columnId);
507
+ }
508
+ }
509
+ return { patches, skipped, summary: summarize(direction, patches, skipped, touchedColumns, outside) };
510
+ }
511
+ function summarize(direction, patches, skipped, touchedColumns, outside) {
512
+ const parts = [];
513
+ if (patches.length === 0) {
514
+ parts.push("Filled 0 cells because there was nothing to fill.");
515
+ } else if (direction === "down" || direction === "up") {
516
+ const word = direction === "down" ? "down" : "up";
517
+ parts.push(
518
+ `Filled ${plural(patches.length, "cell", "cells")} ${word} ${plural(touchedColumns.size, "column", "columns")}.`
519
+ );
520
+ } else {
521
+ const word = direction === "right" ? "right" : "left";
522
+ parts.push(
523
+ `Filled ${plural(patches.length, "cell", "cells")} ${word} across ${plural(touchedColumns.size, "column", "columns")}.`
524
+ );
525
+ }
526
+ if (skipped.length > 0) {
527
+ parts.push(`Skipped ${plural(skipped.length, "cell", "cells")} that could not be filled.`);
528
+ }
529
+ if (outside > 0) {
530
+ parts.push(`Ignored ${plural(outside, "cell", "cells")} outside the table.`);
531
+ }
532
+ return parts.join(" ");
533
+ }
534
+ function fillDown(request) {
535
+ const { range, ...rest } = request;
536
+ if (!isValidRect(range)) {
537
+ return emptyResult("Filled 0 cells because the selection is not a rectangle.");
538
+ }
539
+ if (range.top === range.bottom) {
540
+ return emptyResult("Filled 0 cells because the selection is a single row.");
541
+ }
542
+ return fillRange({
543
+ ...rest,
544
+ source: { top: range.top, left: range.left, bottom: range.top, right: range.right },
545
+ target: { top: range.top + 1, left: range.left, bottom: range.bottom, right: range.right }
546
+ });
547
+ }
548
+ function fillTargetFor(source, toRow, toColumn) {
549
+ if (!isValidRect(source)) return null;
550
+ if (!Number.isInteger(toRow) || !Number.isInteger(toColumn)) return null;
551
+ const below = toRow - source.bottom;
552
+ const above = source.top - toRow;
553
+ const rightOf = toColumn - source.right;
554
+ const leftOf = source.left - toColumn;
555
+ const vertical = Math.max(below, above, 0);
556
+ const horizontal = Math.max(rightOf, leftOf, 0);
557
+ if (vertical === 0 && horizontal === 0) return null;
558
+ if (vertical >= horizontal) {
559
+ if (below > 0) {
560
+ return { top: source.bottom + 1, bottom: toRow, left: source.left, right: source.right };
561
+ }
562
+ return { top: toRow, bottom: source.top - 1, left: source.left, right: source.right };
563
+ }
564
+ if (rightOf > 0) {
565
+ return { top: source.top, bottom: source.bottom, left: source.right + 1, right: toColumn };
566
+ }
567
+ return { top: source.top, bottom: source.bottom, left: toColumn, right: source.left - 1 };
568
+ }
569
+
570
+ // src/data-table/cell-undo.ts
571
+ var DEFAULT_UNDO_DEPTH = 100;
572
+ function createUndoStack(depth = DEFAULT_UNDO_DEPTH) {
573
+ const bounded = Number.isFinite(depth) && depth > 0 ? Math.floor(depth) : 0;
574
+ return { past: [], future: [], depth: bounded };
575
+ }
576
+ function pushUndo(stack, entry) {
577
+ if (stack.depth <= 0) {
578
+ return { past: [], future: [], depth: stack.depth };
579
+ }
580
+ const past = [...stack.past, entry];
581
+ while (past.length > stack.depth) past.shift();
582
+ return { past, future: [], depth: stack.depth };
583
+ }
584
+ function popUndo(stack) {
585
+ const entry = stack.past[stack.past.length - 1];
586
+ if (entry === void 0) return { stack, entry: null };
587
+ return {
588
+ stack: {
589
+ past: stack.past.slice(0, -1),
590
+ future: [...stack.future, entry],
591
+ depth: stack.depth
592
+ },
593
+ entry
594
+ };
595
+ }
596
+ function popRedo(stack) {
597
+ const entry = stack.future[stack.future.length - 1];
598
+ if (entry === void 0) return { stack, entry: null };
599
+ const past = [...stack.past, entry];
600
+ while (past.length > stack.depth) past.shift();
601
+ return {
602
+ stack: { past, future: stack.future.slice(0, -1), depth: stack.depth },
603
+ entry
604
+ };
605
+ }
606
+ function keyOf(patch) {
607
+ return JSON.stringify([patch.rowId, patch.columnId]);
608
+ }
609
+ function undoEntryFor(before, after, label) {
610
+ const priors = /* @__PURE__ */ new Map();
611
+ for (const patch of before) priors.set(keyOf(patch), patch);
612
+ const undo = after.map((patch) => {
613
+ const prior = priors.get(keyOf(patch));
614
+ return {
615
+ rowId: patch.rowId,
616
+ columnId: patch.columnId,
617
+ value: prior ? prior.value : null
618
+ };
619
+ });
620
+ return {
621
+ label,
622
+ undo,
623
+ redo: after.map((patch) => ({
624
+ rowId: patch.rowId,
625
+ columnId: patch.columnId,
626
+ value: patch.value
627
+ }))
628
+ };
629
+ }
630
+
631
+ // src/data-table/useSpreadsheetGrid.ts
632
+ function targetsAnEditor(target) {
633
+ const element = target instanceof Element ? target : null;
634
+ if (!element) return false;
635
+ return element.closest("input, textarea, select, [contenteditable='true']") !== null;
636
+ }
637
+ var NOTHING = [];
638
+ function useSpreadsheetGrid(options) {
639
+ const [range, setRange] = (0, import_react.useState)(null);
640
+ const [fillTarget, setFillTarget] = (0, import_react.useState)(null);
641
+ const [stack, setStack] = (0, import_react.useState)(() => createUndoStack(options?.undoDepth ?? 100));
642
+ const draggingRef = (0, import_react.useRef)(null);
643
+ const optionsRef = (0, import_react.useRef)(options);
644
+ optionsRef.current = options;
645
+ const rowIds = options?.rowIds ?? [];
646
+ const columnIds = (0, import_react.useMemo)(
647
+ () => options ? options.columnIds.filter((id) => id !== "") : [],
648
+ [options]
649
+ );
650
+ (0, import_react.useEffect)(() => {
651
+ if (!range) return;
652
+ const rowSet = new Set(rowIds);
653
+ const colSet = new Set(columnIds);
654
+ const alive = rowSet.has(range.anchor.rowId) && rowSet.has(range.focus.rowId) && colSet.has(range.anchor.fieldName) && colSet.has(range.focus.fieldName);
655
+ if (!alive) setRange(null);
656
+ }, [range, rowIds, columnIds]);
657
+ const bounds = (0, import_react.useMemo)(
658
+ () => range ? rangeBounds(range, rowIds, columnIds) : null,
659
+ [range, rowIds, columnIds]
660
+ );
661
+ const rowIndexById = (0, import_react.useMemo)(() => {
662
+ const map = /* @__PURE__ */ new Map();
663
+ for (let i = 0; i < rowIds.length; i += 1) map.set(rowIds[i], i);
664
+ return map;
665
+ }, [rowIds]);
666
+ const columnIndexById = (0, import_react.useMemo)(() => {
667
+ const map = /* @__PURE__ */ new Map();
668
+ for (let i = 0; i < columnIds.length; i += 1) map.set(columnIds[i], i);
669
+ return map;
670
+ }, [columnIds]);
671
+ const applyPatches = (0, import_react.useCallback)(
672
+ async (patches, intent, label) => {
673
+ const current = optionsRef.current;
674
+ if (!current || patches.length === 0) return;
675
+ const depth = current.undoDepth ?? 100;
676
+ if (depth > 0 && intent !== "undo" && intent !== "redo") {
677
+ const before = patches.map((patch) => ({
678
+ rowId: patch.rowId,
679
+ columnId: patch.columnId,
680
+ value: current.valueAt(patch.rowId, patch.columnId) ?? null
681
+ }));
682
+ setStack((previous) => pushUndo(previous, undoEntryFor(before, patches, label)));
683
+ }
684
+ await current.onPatch(patches, intent);
685
+ },
686
+ []
687
+ );
688
+ const readValueAt = (0, import_react.useCallback)((rowIndex, columnIndex) => {
689
+ const current = optionsRef.current;
690
+ const rowId = current?.rowIds[rowIndex];
691
+ const columnId = current?.columnIds[columnIndex];
692
+ if (!current || rowId === void 0 || columnId === void 0) return null;
693
+ return current.valueAt(rowId, columnId);
694
+ }, []);
695
+ const fillRequestBase = (0, import_react.useCallback)(() => {
696
+ const current = optionsRef.current;
697
+ if (!current) return null;
698
+ return {
699
+ valueAt: readValueAt,
700
+ columnIds: [...current.columnIds],
701
+ rowIds: [...current.rowIds],
702
+ isColumnWritable: current.isColumnWritable
703
+ };
704
+ }, [readValueAt]);
705
+ const announceSkips = (0, import_react.useCallback)(
706
+ (skipped, summary) => {
707
+ const current = optionsRef.current;
708
+ if (!current?.notify) return;
709
+ if (skipped.length === 0) {
710
+ current.notify(summary);
711
+ return;
712
+ }
713
+ const first = skipped[0];
714
+ const reason = current.columnRefusal?.(first.columnId) ?? first.reason;
715
+ const rest = skipped.length > 1 ? ` ${skipped.length - 1} more were left alone.` : "";
716
+ current.notify(`${summary} ${reason}${rest}`);
717
+ },
718
+ []
719
+ );
720
+ const doFillDown = (0, import_react.useCallback)(async () => {
721
+ const base = fillRequestBase();
722
+ if (!base || !bounds) return;
723
+ const result = fillDown({
724
+ ...base,
725
+ range: { top: bounds.r0, left: bounds.c0, bottom: bounds.r1, right: bounds.c1 }
726
+ });
727
+ await applyPatches(result.patches, "fill-down", "Filled down");
728
+ announceSkips(result.skipped, result.summary);
729
+ }, [fillRequestBase, bounds, applyPatches, announceSkips]);
730
+ const doClear = (0, import_react.useCallback)(async () => {
731
+ const current = optionsRef.current;
732
+ if (!current || !bounds) return;
733
+ const patches = [];
734
+ const skipped = [];
735
+ for (let r = bounds.r0; r <= bounds.r1; r += 1) {
736
+ for (let c = bounds.c0; c <= bounds.c1; c += 1) {
737
+ const rowId = current.rowIds[r];
738
+ const columnId = current.columnIds[c];
739
+ if (rowId === void 0 || columnId === void 0) continue;
740
+ if (!current.isColumnWritable(columnId)) {
741
+ skipped.push({
742
+ columnId,
743
+ reason: current.columnRefusal?.(columnId) ?? `${columnId} cannot be cleared.`
744
+ });
745
+ continue;
746
+ }
747
+ patches.push({ rowId, columnId, value: null });
748
+ }
749
+ }
750
+ await applyPatches(patches, "clear", "Cleared");
751
+ announceSkips(skipped, `Cleared ${patches.length} cells.`);
752
+ }, [bounds, applyPatches, announceSkips]);
753
+ const doUndo = (0, import_react.useCallback)(async () => {
754
+ const taken = popUndo(stack);
755
+ if (!taken.entry) return;
756
+ setStack(taken.stack);
757
+ await applyPatches(taken.entry.undo, "undo", taken.entry.label);
758
+ optionsRef.current?.notify?.(taken.entry.label);
759
+ }, [stack, applyPatches]);
760
+ const doRedo = (0, import_react.useCallback)(async () => {
761
+ const taken = popRedo(stack);
762
+ if (!taken.entry) return;
763
+ setStack(taken.stack);
764
+ await applyPatches(taken.entry.redo, "redo", taken.entry.label);
765
+ }, [stack, applyPatches]);
766
+ const move = (0, import_react.useCallback)(
767
+ (direction, extend) => {
768
+ setRange((previous) => {
769
+ const from = previous ? previous.focus : null;
770
+ const next = moveSelection(from, direction, rowIds, columnIds);
771
+ if (!next) return previous;
772
+ if (extend && previous) return { anchor: previous.anchor, focus: next };
773
+ return { anchor: next, focus: next };
774
+ });
775
+ },
776
+ [rowIds, columnIds]
777
+ );
778
+ const onKeyDown = (0, import_react.useCallback)(
779
+ (event) => {
780
+ const current = optionsRef.current;
781
+ if (!current) return;
782
+ if (targetsAnEditor(event.target)) return;
783
+ const accel = event.metaKey || event.ctrlKey;
784
+ if (accel && (event.key === "z" || event.key === "Z")) {
785
+ event.preventDefault();
786
+ if (event.shiftKey) void doRedo();
787
+ else void doUndo();
788
+ return;
789
+ }
790
+ if (accel && (event.key === "y" || event.key === "Y")) {
791
+ event.preventDefault();
792
+ void doRedo();
793
+ return;
794
+ }
795
+ const action = classifyGridKey(event);
796
+ if (!action) return;
797
+ switch (action.kind) {
798
+ case "move":
799
+ event.preventDefault();
800
+ move(action.move, false);
801
+ return;
802
+ case "extend":
803
+ event.preventDefault();
804
+ move(action.move, true);
805
+ return;
806
+ case "selectAll": {
807
+ event.preventDefault();
808
+ const all = allRange(rowIds, columnIds);
809
+ if (all) setRange(all);
810
+ return;
811
+ }
812
+ case "selectRow": {
813
+ event.preventDefault();
814
+ const focused = range?.focus;
815
+ const next = focused ? rowRange(focused.rowId, columnIds) : null;
816
+ if (next) setRange(next);
817
+ return;
818
+ }
819
+ case "selectColumn": {
820
+ event.preventDefault();
821
+ const focused = range?.focus;
822
+ const next = focused ? columnRange(focused.fieldName, rowIds) : null;
823
+ if (next) setRange(next);
824
+ return;
825
+ }
826
+ case "clearCell":
827
+ event.preventDefault();
828
+ void doClear();
829
+ return;
830
+ case "fillDown":
831
+ event.preventDefault();
832
+ void doFillDown();
833
+ return;
834
+ case "escape":
835
+ setRange(null);
836
+ return;
837
+ default:
838
+ return;
839
+ }
840
+ },
841
+ [move, range, rowIds, columnIds, doClear, doFillDown, doUndo, doRedo]
842
+ );
843
+ const selectionAsTsv = (0, import_react.useCallback)(() => {
844
+ const current = optionsRef.current;
845
+ if (!current || !range) return "";
846
+ const grid = rangeRows(range, rowIds, columnIds).map(
847
+ (row) => row.map((address) => current.valueAt(address.rowId, address.fieldName))
848
+ );
849
+ return gridToTsv(grid);
850
+ }, [range, rowIds, columnIds]);
851
+ const onCopy = (0, import_react.useCallback)(
852
+ (event) => {
853
+ if (!optionsRef.current || !range || targetsAnEditor(event.target)) return;
854
+ const text = selectionAsTsv();
855
+ if (!text) return;
856
+ event.preventDefault();
857
+ event.clipboardData?.setData("text/plain", text);
858
+ },
859
+ [range, selectionAsTsv]
860
+ );
861
+ const onCut = (0, import_react.useCallback)(
862
+ (event) => {
863
+ if (!optionsRef.current || !range || targetsAnEditor(event.target)) return;
864
+ const text = selectionAsTsv();
865
+ if (!text) return;
866
+ event.preventDefault();
867
+ event.clipboardData?.setData("text/plain", text);
868
+ void doClear();
869
+ },
870
+ [range, selectionAsTsv, doClear]
871
+ );
872
+ const onPaste = (0, import_react.useCallback)(
873
+ (event) => {
874
+ const current = optionsRef.current;
875
+ if (!current || current.paste === false || !range || targetsAnEditor(event.target)) return;
876
+ const text = event.clipboardData?.getData("text/plain") ?? "";
877
+ if (!text) return;
878
+ event.preventDefault();
879
+ const block = parseClipboardGrid(text);
880
+ const start = range.focus;
881
+ const startRow = rowIds.indexOf(start.rowId);
882
+ const startCol = columnIds.indexOf(start.fieldName);
883
+ if (startRow === -1 || startCol === -1) return;
884
+ const patches = [];
885
+ const skipped = [];
886
+ let outside = 0;
887
+ for (let r = 0; r < block.length; r += 1) {
888
+ const line = block[r] ?? [];
889
+ for (let c = 0; c < line.length; c += 1) {
890
+ const rowId = rowIds[startRow + r];
891
+ const columnId = columnIds[startCol + c];
892
+ if (rowId === void 0 || columnId === void 0) {
893
+ outside += 1;
894
+ continue;
895
+ }
896
+ if (!current.isColumnWritable(columnId)) {
897
+ skipped.push({
898
+ columnId,
899
+ reason: current.columnRefusal?.(columnId) ?? `${columnId} cannot take a pasted value.`
900
+ });
901
+ continue;
902
+ }
903
+ patches.push({ rowId, columnId, value: line[c] ?? null });
904
+ }
905
+ }
906
+ void applyPatches(patches, "paste", "Pasted");
907
+ const tail = outside > 0 ? ` ${outside} landed past the edge of the table and were ignored.` : "";
908
+ announceSkips(skipped, `Pasted ${patches.length} cells.${tail}`);
909
+ },
910
+ [range, rowIds, columnIds, applyPatches, announceSkips]
911
+ );
912
+ const cellProps = (0, import_react.useCallback)(
913
+ (rowId, columnId) => {
914
+ const address = { rowId, fieldName: columnId };
915
+ const rowIndex = rowIndexById.get(rowId) ?? -1;
916
+ const colIndex = columnIndexById.get(columnId) ?? -1;
917
+ const selected = bounds !== null && rowIndex >= bounds.r0 && rowIndex <= bounds.r1 && colIndex >= bounds.c0 && colIndex <= bounds.c1;
918
+ const active = range !== null && sameCell(range.focus, address);
919
+ const inFillPreview = fillTarget !== null && rowIndex >= fillTarget.top && rowIndex <= fillTarget.bottom && colIndex >= fillTarget.left && colIndex <= fillTarget.right;
920
+ return {
921
+ "data-matrx-cell-row": rowId,
922
+ "data-matrx-cell-col": columnId,
923
+ ...selected ? { "data-matrx-cell-selected": "true" } : {},
924
+ ...active ? { "data-matrx-cell-active": "true" } : {},
925
+ ...inFillPreview ? { "data-matrx-cell-fill-preview": "true" } : {},
926
+ onMouseDown: (event) => {
927
+ if (event.button !== 0) return;
928
+ event.preventDefault();
929
+ setRange(
930
+ (previous) => event.shiftKey && previous ? { anchor: previous.anchor, focus: address } : { anchor: address, focus: address }
931
+ );
932
+ draggingRef.current = "select";
933
+ },
934
+ onMouseEnter: () => {
935
+ if (draggingRef.current === "select") {
936
+ setRange((previous) => previous ? { anchor: previous.anchor, focus: address } : previous);
937
+ return;
938
+ }
939
+ if (draggingRef.current === "fill" && bounds) {
940
+ setFillTarget(
941
+ fillTargetFor(
942
+ { top: bounds.r0, left: bounds.c0, bottom: bounds.r1, right: bounds.c1 },
943
+ rowIndex,
944
+ colIndex
945
+ )
946
+ );
947
+ }
948
+ }
949
+ };
950
+ },
951
+ [rowIndexById, columnIndexById, bounds, range, fillTarget]
952
+ );
953
+ const isFillHandleCell = (0, import_react.useCallback)(
954
+ (rowId, columnId) => {
955
+ const current = optionsRef.current;
956
+ if (!current || current.fillHandle === false || !bounds) return false;
957
+ return rowIds[bounds.r1] === rowId && columnIds[bounds.c1] === columnId;
958
+ },
959
+ [bounds, rowIds, columnIds]
960
+ );
961
+ const startFillDrag = (0, import_react.useCallback)(
962
+ (event) => {
963
+ event.preventDefault();
964
+ event.stopPropagation();
965
+ draggingRef.current = "fill";
966
+ },
967
+ []
968
+ );
969
+ const fillTargetRef = (0, import_react.useRef)(null);
970
+ fillTargetRef.current = fillTarget;
971
+ const boundsRef = (0, import_react.useRef)(null);
972
+ boundsRef.current = bounds;
973
+ (0, import_react.useEffect)(() => {
974
+ if (!options) return;
975
+ const commit = () => {
976
+ const wasDragging = draggingRef.current;
977
+ draggingRef.current = null;
978
+ if (wasDragging !== "fill") return;
979
+ const target = fillTargetRef.current;
980
+ const source = boundsRef.current;
981
+ setFillTarget(null);
982
+ if (!target || !source) return;
983
+ const base = fillRequestBase();
984
+ if (!base) return;
985
+ const result = fillRange({
986
+ ...base,
987
+ source: { top: source.r0, left: source.c0, bottom: source.r1, right: source.c1 },
988
+ target
989
+ });
990
+ void applyPatches(result.patches, "fill", "Filled");
991
+ announceSkips(result.skipped, result.summary);
992
+ };
993
+ window.addEventListener("mouseup", commit);
994
+ return () => window.removeEventListener("mouseup", commit);
995
+ }, [options, fillRequestBase, applyPatches, announceSkips]);
996
+ return {
997
+ range,
998
+ bounds,
999
+ enabled: options !== null,
1000
+ containerProps: {
1001
+ tabIndex: options ? 0 : -1,
1002
+ onKeyDown,
1003
+ onCopy,
1004
+ onCut,
1005
+ onPaste
1006
+ },
1007
+ cellProps,
1008
+ isFillHandleCell,
1009
+ startFillDrag,
1010
+ canUndo: stack.past.length > 0,
1011
+ canRedo: stack.future.length > 0
1012
+ };
1013
+ }
1014
+ //# sourceMappingURL=spreadsheet.cjs.map