@vueye-table/core 3.0.0-alpha.1 → 3.0.0-alpha.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +138 -60
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +512 -96
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -8,13 +8,34 @@ function humanize(id) {
|
|
|
8
8
|
return sentence.charAt(0).toUpperCase() + sentence.slice(1);
|
|
9
9
|
}
|
|
10
10
|
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region src/issues.ts
|
|
13
|
+
function issue(code, message, details = {}) {
|
|
14
|
+
return Object.freeze({
|
|
15
|
+
code,
|
|
16
|
+
message,
|
|
17
|
+
...details
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
|
|
11
21
|
//#endregion
|
|
12
22
|
//#region src/path.ts
|
|
23
|
+
/**
|
|
24
|
+
* Dotted property paths.
|
|
25
|
+
*
|
|
26
|
+
* A column addresses nested data with a path such as `"address.city"`. The types below give every
|
|
27
|
+
* such path its value type, and the functions read and write through a path without mutating.
|
|
28
|
+
*/
|
|
13
29
|
function isRecord$1(value) {
|
|
14
30
|
return typeof value === "object" && value !== null;
|
|
15
31
|
}
|
|
32
|
+
/** Whether every path segment can be read or written without addressing a prototype. */
|
|
33
|
+
function isSafePath(path) {
|
|
34
|
+
return path.split(".").every((segment) => segment !== "__proto__" && segment !== "constructor" && segment !== "prototype");
|
|
35
|
+
}
|
|
16
36
|
/** Read the value at a dotted path, or `undefined` when a step is missing. */
|
|
17
37
|
function getPath(source, path) {
|
|
38
|
+
if (!isSafePath(path)) return;
|
|
18
39
|
let current = source;
|
|
19
40
|
for (const segment of path.split(".")) {
|
|
20
41
|
if (!isRecord$1(current)) return;
|
|
@@ -24,13 +45,18 @@ function getPath(source, path) {
|
|
|
24
45
|
}
|
|
25
46
|
/**
|
|
26
47
|
* Return a copy of `source` with `value` at `path`. Only the objects along the path are copied;
|
|
27
|
-
* everything else is shared. Missing steps are created as plain objects.
|
|
48
|
+
* everything else is shared. Missing steps are created as plain objects. Unsafe paths return
|
|
49
|
+
* `source` unchanged and report an `unsafe_path` issue through `onIssue`, when supplied.
|
|
28
50
|
*/
|
|
29
|
-
function setPath(source, path, value) {
|
|
51
|
+
function setPath(source, path, value, onIssue) {
|
|
52
|
+
if (!isSafePath(path)) {
|
|
53
|
+
onIssue?.(issue("unsafe_path", `The path "${path}" addresses a prototype and cannot be written.`, { column: path }));
|
|
54
|
+
return source;
|
|
55
|
+
}
|
|
30
56
|
const [head, ...rest] = path.split(".");
|
|
31
57
|
const base = isRecord$1(source) ? { ...source } : {};
|
|
32
58
|
if (head === void 0) return source;
|
|
33
|
-
base[head] = rest.length === 0 ? value : setPath(base[head], rest.join("."), value);
|
|
59
|
+
base[head] = rest.length === 0 ? value : setPath(Object.hasOwn(base, head) ? base[head] : void 0, rest.join("."), value);
|
|
34
60
|
return base;
|
|
35
61
|
}
|
|
36
62
|
|
|
@@ -197,14 +223,15 @@ function parseAs(input, type) {
|
|
|
197
223
|
function resolveColumn(definition) {
|
|
198
224
|
const def = definition;
|
|
199
225
|
const { accessor } = def;
|
|
200
|
-
const
|
|
226
|
+
const safe = isSafePath(def.id);
|
|
227
|
+
const getValue = safe ? accessor ?? ((row) => getPath(row, def.id)) : () => void 0;
|
|
201
228
|
const format = def.format ?? ((value) => formatValue(value));
|
|
202
229
|
const compare = def.compare ?? compareValues;
|
|
203
230
|
const customFilter = def.filter;
|
|
204
231
|
const customParse = def.parse;
|
|
205
232
|
const editable = def.editable ?? false;
|
|
206
233
|
const customSetValue = def.setValue;
|
|
207
|
-
const canWrite = accessor === void 0 || customSetValue !== void 0;
|
|
234
|
+
const canWrite = safe && (accessor === void 0 || customSetValue !== void 0);
|
|
208
235
|
return Object.freeze({
|
|
209
236
|
id: def.id,
|
|
210
237
|
header: def.header ?? humanize(def.id),
|
|
@@ -241,6 +268,7 @@ function resolveColumn(definition) {
|
|
|
241
268
|
return parseAs(input, def.type ?? typeOf(getValue(row)) ?? typeOf(sample) ?? "text");
|
|
242
269
|
},
|
|
243
270
|
setValue(row, value) {
|
|
271
|
+
if (!safe) return;
|
|
244
272
|
if (customSetValue) return customSetValue(row, value);
|
|
245
273
|
return accessor === void 0 ? setPath(row, def.id, value) : void 0;
|
|
246
274
|
}
|
|
@@ -258,6 +286,10 @@ function resolveColumn(definition) {
|
|
|
258
286
|
function quote(field, delimiter) {
|
|
259
287
|
return field.includes(delimiter) || field.includes("\"") || field.includes("\n") || field.includes("\r") ? `"${field.replaceAll("\"", "\"\"")}"` : field;
|
|
260
288
|
}
|
|
289
|
+
/** Escape formula-like display text; a finite number formatted as a number stays numeric. */
|
|
290
|
+
function escapeFormula(text, value) {
|
|
291
|
+
return !(typeof value === "number" && Number.isFinite(value) && /^[+-]?(?:\d+(?:\.\d*)?|\.\d+)(?:[eE][+-]?\d+)?$/u.test(text)) && /^[=+\-@\t\r\n=+-@]/u.test(text) ? "'" + text : text;
|
|
292
|
+
}
|
|
261
293
|
/** Write a matrix of text as delimited lines joined with `\n`. */
|
|
262
294
|
function toDelimited(matrix, delimiter = " ") {
|
|
263
295
|
return matrix.map((line) => line.map((field) => quote(field, delimiter)).join(delimiter)).join("\n");
|
|
@@ -267,39 +299,92 @@ function toDelimited(matrix, delimiter = " ") {
|
|
|
267
299
|
* and embedded line breaks, and ignores one trailing line break.
|
|
268
300
|
*/
|
|
269
301
|
function parseDelimited(text, delimiter = " ") {
|
|
302
|
+
return readDelimited(text, {
|
|
303
|
+
maxRows: Infinity,
|
|
304
|
+
maxColumns: Infinity,
|
|
305
|
+
maxCells: Infinity,
|
|
306
|
+
maxLength: Infinity
|
|
307
|
+
}, delimiter).rows;
|
|
308
|
+
}
|
|
309
|
+
/** Read complete fields within a grid and work budget, without materializing discarded fields. */
|
|
310
|
+
function readDelimited(text, limits, delimiter = " ") {
|
|
270
311
|
const rows = [];
|
|
312
|
+
if (limits.maxRows === 0 || limits.maxColumns === 0) return {
|
|
313
|
+
rows,
|
|
314
|
+
truncated: text.length > 0
|
|
315
|
+
};
|
|
271
316
|
let row = [];
|
|
272
317
|
let field = "";
|
|
273
318
|
let quoted = false;
|
|
319
|
+
let started = false;
|
|
274
320
|
let index = 0;
|
|
321
|
+
let column = 0;
|
|
322
|
+
let cells = 0;
|
|
323
|
+
let truncated = false;
|
|
324
|
+
const end = Math.min(text.length, limits.maxLength);
|
|
275
325
|
const endField = () => {
|
|
276
|
-
row.push(field);
|
|
326
|
+
if (column < limits.maxColumns) row.push(field);
|
|
327
|
+
else truncated = true;
|
|
328
|
+
column += 1;
|
|
329
|
+
cells += 1;
|
|
277
330
|
field = "";
|
|
331
|
+
started = false;
|
|
278
332
|
};
|
|
279
|
-
const
|
|
280
|
-
|
|
281
|
-
rows.push(row);
|
|
282
|
-
row = [];
|
|
333
|
+
const append = (char) => {
|
|
334
|
+
if (column < limits.maxColumns) field += char;
|
|
283
335
|
};
|
|
284
|
-
while (index <
|
|
336
|
+
while (index < end) {
|
|
285
337
|
const char = text.charAt(index);
|
|
286
338
|
if (quoted) {
|
|
287
339
|
if (char === "\"") {
|
|
288
|
-
if (text.charAt(index + 1) === "\"") {
|
|
289
|
-
|
|
340
|
+
if (index + 1 < end && text.charAt(index + 1) === "\"") {
|
|
341
|
+
append("\"");
|
|
290
342
|
index += 1;
|
|
291
343
|
} else quoted = false;
|
|
292
|
-
} else
|
|
293
|
-
} else if (char === "\"" &&
|
|
294
|
-
else if (char === delimiter)
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
344
|
+
} else append(char);
|
|
345
|
+
} else if (char === "\"" && !started) quoted = true;
|
|
346
|
+
else if (char === delimiter) {
|
|
347
|
+
endField();
|
|
348
|
+
index += 1;
|
|
349
|
+
if (cells >= limits.maxCells || rows.length + 1 >= limits.maxRows && column >= limits.maxColumns) {
|
|
350
|
+
rows.push(row);
|
|
351
|
+
return {
|
|
352
|
+
rows,
|
|
353
|
+
truncated: true
|
|
354
|
+
};
|
|
355
|
+
}
|
|
356
|
+
continue;
|
|
357
|
+
} else if (char === "\n" || char === "\r") {
|
|
358
|
+
if (char === "\r" && index + 1 < end && text.charAt(index + 1) === "\n") index += 1;
|
|
359
|
+
endField();
|
|
360
|
+
rows.push(row);
|
|
361
|
+
row = [];
|
|
362
|
+
column = 0;
|
|
363
|
+
index += 1;
|
|
364
|
+
if (cells >= limits.maxCells || rows.length >= limits.maxRows) return {
|
|
365
|
+
rows,
|
|
366
|
+
truncated: truncated || index < text.length
|
|
367
|
+
};
|
|
368
|
+
continue;
|
|
369
|
+
} else append(char);
|
|
370
|
+
started = true;
|
|
299
371
|
index += 1;
|
|
300
372
|
}
|
|
301
|
-
if (
|
|
302
|
-
|
|
373
|
+
if (end < text.length) {
|
|
374
|
+
if (row.length > 0) rows.push(row);
|
|
375
|
+
return {
|
|
376
|
+
rows,
|
|
377
|
+
truncated: true
|
|
378
|
+
};
|
|
379
|
+
}
|
|
380
|
+
if (started || column > 0) {
|
|
381
|
+
endField();
|
|
382
|
+
rows.push(row);
|
|
383
|
+
}
|
|
384
|
+
return {
|
|
385
|
+
rows,
|
|
386
|
+
truncated
|
|
387
|
+
};
|
|
303
388
|
}
|
|
304
389
|
|
|
305
390
|
//#endregion
|
|
@@ -466,6 +551,196 @@ function createUndoStack(limit = 100) {
|
|
|
466
551
|
};
|
|
467
552
|
}
|
|
468
553
|
|
|
554
|
+
//#endregion
|
|
555
|
+
//#region src/virtualizer.ts
|
|
556
|
+
const FALLBACK_SIZE = 40;
|
|
557
|
+
/** Layout arithmetic for either axis. All viewport values and measurements come from the caller. */
|
|
558
|
+
function createVirtualizer(initialOptions) {
|
|
559
|
+
let options = initialOptions;
|
|
560
|
+
let count = 0;
|
|
561
|
+
let overscan = 5;
|
|
562
|
+
let fixedSize;
|
|
563
|
+
let keys = [];
|
|
564
|
+
let byKey = /* @__PURE__ */ new Map();
|
|
565
|
+
let sizes = [];
|
|
566
|
+
let prefix = [0];
|
|
567
|
+
let offset = 0;
|
|
568
|
+
let viewportSize = 0;
|
|
569
|
+
let cached;
|
|
570
|
+
const measured = /* @__PURE__ */ new Map();
|
|
571
|
+
const problems = /* @__PURE__ */ new Map();
|
|
572
|
+
const listeners = /* @__PURE__ */ new Set();
|
|
573
|
+
function recover(code, message) {
|
|
574
|
+
if (!problems.has(code)) {
|
|
575
|
+
const problem = issue(code, message);
|
|
576
|
+
problems.set(code, problem);
|
|
577
|
+
options.onIssue?.(problem);
|
|
578
|
+
cached = void 0;
|
|
579
|
+
}
|
|
580
|
+
}
|
|
581
|
+
function validSize(value) {
|
|
582
|
+
if (Number.isFinite(value) && value > 0) return value;
|
|
583
|
+
recover("invalid_virtual_size", `Item size ${String(value)} is not positive and finite; 40 is used.`);
|
|
584
|
+
return FALLBACK_SIZE;
|
|
585
|
+
}
|
|
586
|
+
function configure(next) {
|
|
587
|
+
options = next;
|
|
588
|
+
problems.clear();
|
|
589
|
+
count = Number.isSafeInteger(next.count) && next.count >= 0 ? next.count : 0;
|
|
590
|
+
if (count !== next.count) recover("invalid_virtual_option", "Virtual item count must be a non-negative safe integer; 0 is used.");
|
|
591
|
+
const requestedOverscan = next.overscan ?? 5;
|
|
592
|
+
overscan = Number.isSafeInteger(requestedOverscan) && requestedOverscan >= 0 ? requestedOverscan : 5;
|
|
593
|
+
if (overscan !== requestedOverscan) recover("invalid_virtual_option", "Overscan must be a non-negative safe integer; 5 is used.");
|
|
594
|
+
const estimate = typeof next.estimateSize === "number" ? validSize(next.estimateSize) : void 0;
|
|
595
|
+
const nextKeys = [];
|
|
596
|
+
const nextSizes = [];
|
|
597
|
+
const nextByKey = /* @__PURE__ */ new Map();
|
|
598
|
+
let firstChanged = count;
|
|
599
|
+
let uniform = estimate !== void 0;
|
|
600
|
+
for (let index = 0; index < count; index += 1) {
|
|
601
|
+
let key = next.getKey ? next.getKey(index) : index;
|
|
602
|
+
if (!(typeof key === "string" || typeof key === "number" && Number.isFinite(key))) {
|
|
603
|
+
recover("invalid_virtual_option", `Item ${index} has an invalid key; its index is used.`);
|
|
604
|
+
key = index;
|
|
605
|
+
}
|
|
606
|
+
if (nextByKey.has(key)) {
|
|
607
|
+
recover("duplicate_virtual_key", `Item ${index} repeats a key; a distinct fallback key is used.`);
|
|
608
|
+
key = JSON.stringify(["virtual", index]);
|
|
609
|
+
while (nextByKey.has(key)) key += ":";
|
|
610
|
+
}
|
|
611
|
+
const size = measured.get(key) ?? estimate ?? validSize(next.estimateSize(index));
|
|
612
|
+
uniform &&= size === estimate;
|
|
613
|
+
if (keys[index] !== key || sizes[index] !== size) firstChanged = Math.min(firstChanged, index);
|
|
614
|
+
nextKeys.push(key);
|
|
615
|
+
nextSizes.push(size);
|
|
616
|
+
nextByKey.set(key, index);
|
|
617
|
+
}
|
|
618
|
+
if (fixedSize !== void 0 || uniform) prefix = [0];
|
|
619
|
+
else prefix.length = Math.min(prefix.length, firstChanged + 1);
|
|
620
|
+
keys = nextKeys;
|
|
621
|
+
sizes = nextSizes;
|
|
622
|
+
byKey = nextByKey;
|
|
623
|
+
fixedSize = uniform ? estimate : void 0;
|
|
624
|
+
cached = void 0;
|
|
625
|
+
}
|
|
626
|
+
function startOf(index) {
|
|
627
|
+
if (fixedSize !== void 0) return index * fixedSize;
|
|
628
|
+
while (prefix.length <= index) {
|
|
629
|
+
const at = prefix.length - 1;
|
|
630
|
+
prefix.push(prefix[at] + sizes[at]);
|
|
631
|
+
}
|
|
632
|
+
return prefix[index];
|
|
633
|
+
}
|
|
634
|
+
function totalSize() {
|
|
635
|
+
return startOf(count);
|
|
636
|
+
}
|
|
637
|
+
function indexAt(position) {
|
|
638
|
+
if (fixedSize !== void 0) return Math.min(count - 1, Math.floor(position / fixedSize));
|
|
639
|
+
let low = 0;
|
|
640
|
+
let high = count - 1;
|
|
641
|
+
while (low < high) {
|
|
642
|
+
const middle = Math.floor((low + high) / 2);
|
|
643
|
+
if (startOf(middle + 1) <= position) low = middle + 1;
|
|
644
|
+
else high = middle;
|
|
645
|
+
}
|
|
646
|
+
return low;
|
|
647
|
+
}
|
|
648
|
+
function getWindow() {
|
|
649
|
+
if (cached) return cached;
|
|
650
|
+
const total = totalSize();
|
|
651
|
+
const clampedOffset = Math.min(offset, Math.max(0, total - viewportSize));
|
|
652
|
+
let first = 0;
|
|
653
|
+
let last = -1;
|
|
654
|
+
if (count > 0 && viewportSize > 0) {
|
|
655
|
+
first = Math.max(0, indexAt(clampedOffset) - overscan);
|
|
656
|
+
const edge = Math.min(total, clampedOffset + viewportSize);
|
|
657
|
+
let visibleEnd = indexAt(edge);
|
|
658
|
+
if (startOf(visibleEnd) >= edge) visibleEnd -= 1;
|
|
659
|
+
last = Math.min(count - 1, visibleEnd + overscan);
|
|
660
|
+
}
|
|
661
|
+
const items = [];
|
|
662
|
+
for (let index = first; index <= last; index += 1) items.push(Object.freeze({
|
|
663
|
+
index,
|
|
664
|
+
key: keys[index],
|
|
665
|
+
start: startOf(index),
|
|
666
|
+
size: sizes[index]
|
|
667
|
+
}));
|
|
668
|
+
const paddingStart = items.length > 0 ? startOf(first) : 0;
|
|
669
|
+
const paddingEnd = items.length > 0 ? Math.max(0, total - startOf(last + 1)) : total;
|
|
670
|
+
cached = Object.freeze({
|
|
671
|
+
items: Object.freeze(items),
|
|
672
|
+
totalSize: total,
|
|
673
|
+
paddingStart,
|
|
674
|
+
paddingEnd,
|
|
675
|
+
startIndex: first,
|
|
676
|
+
endIndex: last,
|
|
677
|
+
offset: clampedOffset,
|
|
678
|
+
viewportSize,
|
|
679
|
+
issues: Object.freeze([...problems.values()])
|
|
680
|
+
});
|
|
681
|
+
return cached;
|
|
682
|
+
}
|
|
683
|
+
function notify() {
|
|
684
|
+
cached = void 0;
|
|
685
|
+
if (listeners.size > 0) {
|
|
686
|
+
const view = getWindow();
|
|
687
|
+
for (const listener of listeners) listener(view);
|
|
688
|
+
}
|
|
689
|
+
}
|
|
690
|
+
configure(initialOptions);
|
|
691
|
+
return {
|
|
692
|
+
setOptions(next) {
|
|
693
|
+
configure(next);
|
|
694
|
+
notify();
|
|
695
|
+
},
|
|
696
|
+
setViewport(nextOffset, nextSize) {
|
|
697
|
+
const issueCount = problems.size;
|
|
698
|
+
const safeOffset = Number.isFinite(nextOffset) && nextOffset >= 0 ? nextOffset : 0;
|
|
699
|
+
const safeSize = Number.isFinite(nextSize) && nextSize >= 0 ? nextSize : 0;
|
|
700
|
+
if (safeOffset !== nextOffset || safeSize !== nextSize) recover("invalid_virtual_viewport", "Viewport offset and size must be non-negative and finite; invalid values use 0.");
|
|
701
|
+
if (offset === safeOffset && viewportSize === safeSize && problems.size === issueCount) return;
|
|
702
|
+
offset = safeOffset;
|
|
703
|
+
viewportSize = safeSize;
|
|
704
|
+
notify();
|
|
705
|
+
},
|
|
706
|
+
measure(key, size) {
|
|
707
|
+
const index = byKey.get(key);
|
|
708
|
+
if (index === void 0) return;
|
|
709
|
+
if (!Number.isFinite(size) || size <= 0) {
|
|
710
|
+
recover("invalid_virtual_size", "Measured size must be positive and finite; the previous size is retained.");
|
|
711
|
+
notify();
|
|
712
|
+
return;
|
|
713
|
+
}
|
|
714
|
+
measured.set(key, size);
|
|
715
|
+
if (sizes[index] === size) return;
|
|
716
|
+
if (fixedSize !== void 0) {
|
|
717
|
+
fixedSize = void 0;
|
|
718
|
+
prefix = [0];
|
|
719
|
+
} else prefix.length = Math.min(prefix.length, index + 1);
|
|
720
|
+
sizes[index] = size;
|
|
721
|
+
notify();
|
|
722
|
+
},
|
|
723
|
+
getWindow,
|
|
724
|
+
getOffsetForIndex(index, align = "auto") {
|
|
725
|
+
if (count === 0) return 0;
|
|
726
|
+
if (!Number.isFinite(index) || !Number.isInteger(index)) recover("invalid_virtual_option", "Scroll index must be a finite integer; it is clamped to a valid item.");
|
|
727
|
+
const target = Math.min(count - 1, Math.max(0, Number.isFinite(index) ? Math.trunc(index) : 0));
|
|
728
|
+
const start = startOf(target);
|
|
729
|
+
const end = start + sizes[target];
|
|
730
|
+
const current = getWindow().offset;
|
|
731
|
+
let result = start;
|
|
732
|
+
if (align === "center") result = start - (viewportSize - sizes[target]) / 2;
|
|
733
|
+
else if (align === "end") result = end - viewportSize;
|
|
734
|
+
else if (align === "auto") result = start < current ? start : end > current + viewportSize ? end - viewportSize : current;
|
|
735
|
+
return Math.min(Math.max(0, totalSize() - viewportSize), Math.max(0, result));
|
|
736
|
+
},
|
|
737
|
+
subscribe(listener) {
|
|
738
|
+
listeners.add(listener);
|
|
739
|
+
return () => listeners.delete(listener);
|
|
740
|
+
}
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
|
|
469
744
|
//#endregion
|
|
470
745
|
//#region src/infer.ts
|
|
471
746
|
function isPlainObject(value) {
|
|
@@ -593,10 +868,47 @@ function paginationItems(page, pageCount, siblings = 1) {
|
|
|
593
868
|
return items;
|
|
594
869
|
}
|
|
595
870
|
|
|
871
|
+
//#endregion
|
|
872
|
+
//#region src/row.ts
|
|
873
|
+
/** Disjoint namespaces and typed keys avoid collisions between 1, "1", and detail-like ids. */
|
|
874
|
+
function getRowItemKey(key, kind = "row") {
|
|
875
|
+
return JSON.stringify([
|
|
876
|
+
kind,
|
|
877
|
+
typeof key,
|
|
878
|
+
key
|
|
879
|
+
]);
|
|
880
|
+
}
|
|
881
|
+
function createRow(original, key, index, columns, canExpand = false) {
|
|
882
|
+
const values = /* @__PURE__ */ new Map();
|
|
883
|
+
const displays = /* @__PURE__ */ new Map();
|
|
884
|
+
const getValue = (columnId) => {
|
|
885
|
+
if (values.has(columnId)) return values.get(columnId);
|
|
886
|
+
const value = columns.get(columnId)?.getValue(original);
|
|
887
|
+
values.set(columnId, value);
|
|
888
|
+
return value;
|
|
889
|
+
};
|
|
890
|
+
return Object.freeze({
|
|
891
|
+
key,
|
|
892
|
+
index,
|
|
893
|
+
original,
|
|
894
|
+
canExpand,
|
|
895
|
+
isExpanded: false,
|
|
896
|
+
getValue,
|
|
897
|
+
getDisplay(columnId) {
|
|
898
|
+
const cached = displays.get(columnId);
|
|
899
|
+
if (cached !== void 0) return cached;
|
|
900
|
+
const column = columns.get(columnId);
|
|
901
|
+
const display = column ? column.format(getValue(columnId), original) : "";
|
|
902
|
+
displays.set(columnId, display);
|
|
903
|
+
return display;
|
|
904
|
+
}
|
|
905
|
+
});
|
|
906
|
+
}
|
|
907
|
+
|
|
596
908
|
//#endregion
|
|
597
909
|
//#region src/state.ts
|
|
598
910
|
const DEFAULT_PAGE_SIZE = 10;
|
|
599
|
-
function createInitialState(patch = {}) {
|
|
911
|
+
function createInitialState(patch = {}, onIssue) {
|
|
600
912
|
return freezeState({
|
|
601
913
|
sorting: patch.sorting ?? [],
|
|
602
914
|
search: patch.search ?? "",
|
|
@@ -606,70 +918,46 @@ function createInitialState(patch = {}) {
|
|
|
606
918
|
pageSize: 10
|
|
607
919
|
},
|
|
608
920
|
selection: patch.selection ?? [],
|
|
921
|
+
expanded: normalizeExpanded(patch.expanded === void 0 ? [] : patch.expanded, onIssue),
|
|
609
922
|
hiddenColumns: patch.hiddenColumns ?? [],
|
|
610
923
|
columnOrder: patch.columnOrder ?? []
|
|
611
924
|
});
|
|
612
925
|
}
|
|
613
|
-
function mergeState(state, patch) {
|
|
926
|
+
function mergeState(state, patch, onIssue) {
|
|
614
927
|
let changed = false;
|
|
615
928
|
const next = { ...state };
|
|
616
|
-
for (const [key,
|
|
929
|
+
for (const [key, input] of Object.entries(patch)) {
|
|
930
|
+
const value = key === "expanded" && input !== void 0 ? normalizeExpanded(input, onIssue) : input;
|
|
617
931
|
if (value === void 0 || Object.is(next[key], value)) continue;
|
|
618
932
|
if (key === "pagination" && samePagination(next[key], value)) continue;
|
|
933
|
+
if (key === "expanded" && sameExpanded(state.expanded, value)) continue;
|
|
619
934
|
next[key] = value;
|
|
620
935
|
changed = true;
|
|
621
936
|
}
|
|
622
|
-
return changed ? freezeState(next) : state;
|
|
937
|
+
return changed ? freezeState(next, state) : state;
|
|
938
|
+
}
|
|
939
|
+
function normalizeExpanded(value, onIssue) {
|
|
940
|
+
if (value === true) return true;
|
|
941
|
+
if (Array.isArray(value) && value.every((key) => typeof key === "string" || typeof key === "number" && Number.isFinite(key))) return [...new Set(value)];
|
|
942
|
+
onIssue?.(issue("invalid_expanded", "Expanded state must be true or an array of string/finite number keys; an empty array is used."));
|
|
943
|
+
return [];
|
|
944
|
+
}
|
|
945
|
+
function sameExpanded(left, right) {
|
|
946
|
+
return left === right || left !== true && right !== true && left.length === right.length && left.every((key, index) => key === right[index]);
|
|
623
947
|
}
|
|
624
948
|
function samePagination(left, right) {
|
|
625
949
|
return left.page === right.page && left.pageSize === right.pageSize;
|
|
626
950
|
}
|
|
627
|
-
function freezeState(state) {
|
|
951
|
+
function freezeState(state, previous) {
|
|
628
952
|
return Object.freeze({
|
|
629
|
-
sorting: Object.freeze(state.sorting.map((rule) => Object.freeze({ ...rule }))),
|
|
953
|
+
sorting: state.sorting === previous?.sorting ? previous.sorting : Object.freeze(state.sorting.map((rule) => Object.freeze({ ...rule }))),
|
|
630
954
|
search: state.search,
|
|
631
|
-
filters: Object.freeze({ ...state.filters }),
|
|
632
|
-
pagination: Object.freeze({ ...state.pagination }),
|
|
633
|
-
selection: Object.freeze([...state.selection]),
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
}
|
|
638
|
-
|
|
639
|
-
//#endregion
|
|
640
|
-
//#region src/issues.ts
|
|
641
|
-
function issue(code, message, details = {}) {
|
|
642
|
-
return Object.freeze({
|
|
643
|
-
code,
|
|
644
|
-
message,
|
|
645
|
-
...details
|
|
646
|
-
});
|
|
647
|
-
}
|
|
648
|
-
|
|
649
|
-
//#endregion
|
|
650
|
-
//#region src/row.ts
|
|
651
|
-
function createRow(original, key, index, columns) {
|
|
652
|
-
const values = /* @__PURE__ */ new Map();
|
|
653
|
-
const displays = /* @__PURE__ */ new Map();
|
|
654
|
-
const getValue = (columnId) => {
|
|
655
|
-
if (values.has(columnId)) return values.get(columnId);
|
|
656
|
-
const value = columns.get(columnId)?.getValue(original);
|
|
657
|
-
values.set(columnId, value);
|
|
658
|
-
return value;
|
|
659
|
-
};
|
|
660
|
-
return Object.freeze({
|
|
661
|
-
key,
|
|
662
|
-
index,
|
|
663
|
-
original,
|
|
664
|
-
getValue,
|
|
665
|
-
getDisplay(columnId) {
|
|
666
|
-
const cached = displays.get(columnId);
|
|
667
|
-
if (cached !== void 0) return cached;
|
|
668
|
-
const column = columns.get(columnId);
|
|
669
|
-
const display = column ? column.format(getValue(columnId), original) : "";
|
|
670
|
-
displays.set(columnId, display);
|
|
671
|
-
return display;
|
|
672
|
-
}
|
|
955
|
+
filters: state.filters === previous?.filters ? previous.filters : Object.freeze({ ...state.filters }),
|
|
956
|
+
pagination: state.pagination === previous?.pagination ? previous.pagination : Object.freeze({ ...state.pagination }),
|
|
957
|
+
selection: state.selection === previous?.selection ? previous.selection : Object.freeze([...state.selection]),
|
|
958
|
+
expanded: state.expanded === previous?.expanded ? previous.expanded : state.expanded === true ? true : Object.freeze([...state.expanded]),
|
|
959
|
+
hiddenColumns: state.hiddenColumns === previous?.hiddenColumns ? previous.hiddenColumns : Object.freeze([...state.hiddenColumns]),
|
|
960
|
+
columnOrder: state.columnOrder === previous?.columnOrder ? previous.columnOrder : Object.freeze([...state.columnOrder])
|
|
673
961
|
});
|
|
674
962
|
}
|
|
675
963
|
|
|
@@ -697,19 +985,33 @@ function coverage(selected, total) {
|
|
|
697
985
|
if (selected === 0 || total === 0) return "none";
|
|
698
986
|
return selected === total ? "all" : "some";
|
|
699
987
|
}
|
|
988
|
+
function pasteLimit(value, fallback, name, issues) {
|
|
989
|
+
if (value === void 0) return fallback;
|
|
990
|
+
if (Number.isSafeInteger(value) && value > 0) return value;
|
|
991
|
+
issues.push(issue("invalid_paste_limit", `Paste ${name} ${String(value)} is not a positive safe integer; ${fallback} is used.`));
|
|
992
|
+
return fallback;
|
|
993
|
+
}
|
|
700
994
|
/** Create a data table. */
|
|
701
995
|
function createTable(options) {
|
|
996
|
+
const paginate = options.paginate ?? true;
|
|
997
|
+
const expandMode = options.expandMode ?? "multiple";
|
|
702
998
|
const selectionMode = options.selectionMode ?? "multiple";
|
|
703
999
|
const selectScope = options.selectScope ?? "all";
|
|
704
1000
|
const manual = options.manual ?? false;
|
|
705
1001
|
const undoStack = createUndoStack(options.historyLimit);
|
|
706
1002
|
const listeners = /* @__PURE__ */ new Set();
|
|
1003
|
+
const optionIssues = [];
|
|
1004
|
+
const maxPasteCells = pasteLimit(options.pasteLimit?.maxCells, 1e5, "maxCells", optionIssues);
|
|
1005
|
+
const maxPasteLength = pasteLimit(options.pasteLimit?.maxLength, 5e6, "maxLength", optionIssues);
|
|
707
1006
|
let data = options.data;
|
|
708
1007
|
let definitions = options.columns;
|
|
709
1008
|
let rowCount = options.rowCount;
|
|
710
|
-
|
|
1009
|
+
let expandedIssue;
|
|
1010
|
+
let initialState = createInitialState({
|
|
711
1011
|
...options.initialState,
|
|
712
1012
|
hiddenColumns: options.initialState?.hiddenColumns ?? options.columns.filter((column) => column.hidden === true).map((column) => column.id)
|
|
1013
|
+
}, (problem) => {
|
|
1014
|
+
expandedIssue = problem;
|
|
713
1015
|
});
|
|
714
1016
|
let state = initialState;
|
|
715
1017
|
let snapshot;
|
|
@@ -721,10 +1023,18 @@ function createTable(options) {
|
|
|
721
1023
|
return rowKey === void 0 || !isRecord(row) ? index : String(value);
|
|
722
1024
|
};
|
|
723
1025
|
const resolveColumns = memo((defs) => {
|
|
724
|
-
const
|
|
1026
|
+
const issues = [];
|
|
1027
|
+
const columns = defs.flatMap((definition) => {
|
|
1028
|
+
if (!isSafePath(definition.id)) {
|
|
1029
|
+
issues.push(issue("unsafe_path", `The column "${definition.id}" addresses a prototype and is ignored.`, { column: definition.id }));
|
|
1030
|
+
return [];
|
|
1031
|
+
}
|
|
1032
|
+
return [resolveColumn(definition)];
|
|
1033
|
+
});
|
|
725
1034
|
return {
|
|
726
1035
|
columns,
|
|
727
|
-
byId: new Map(columns.map((column) => [column.id, column]))
|
|
1036
|
+
byId: new Map(columns.map((column) => [column.id, column])),
|
|
1037
|
+
issues
|
|
728
1038
|
};
|
|
729
1039
|
});
|
|
730
1040
|
const indexRows = memo((rows, byId) => {
|
|
@@ -737,7 +1047,7 @@ function createTable(options) {
|
|
|
737
1047
|
issues.push(issue("duplicate_row_key", `Row ${index} repeats the key "${String(key)}"; it is keyed "${String(key)}#${index}" instead.`, { rowKey: key }));
|
|
738
1048
|
key = `${String(key)}#${index}`;
|
|
739
1049
|
}
|
|
740
|
-
const row = createRow(original, key, index, byId);
|
|
1050
|
+
const row = createRow(original, key, index, byId, options.getRowCanExpand?.(original) ?? false);
|
|
741
1051
|
byKey.set(key, row);
|
|
742
1052
|
return row;
|
|
743
1053
|
}),
|
|
@@ -762,8 +1072,21 @@ function createTable(options) {
|
|
|
762
1072
|
const filterStage = memo(filterRows);
|
|
763
1073
|
const sortStage = memo(sortRows);
|
|
764
1074
|
const selectedSet = memo((selection) => new Set(selection));
|
|
1075
|
+
const expansionStage = memo((indexed, expanded) => {
|
|
1076
|
+
const keys = expanded === true ? void 0 : new Set(expanded);
|
|
1077
|
+
const rows = indexed.rows.map((row) => row.canExpand && (expanded === true || keys?.has(row.key)) ? Object.freeze({
|
|
1078
|
+
...row,
|
|
1079
|
+
isExpanded: true
|
|
1080
|
+
}) : row);
|
|
1081
|
+
return {
|
|
1082
|
+
rows,
|
|
1083
|
+
byKey: new Map(rows.map((row) => [row.key, row]))
|
|
1084
|
+
};
|
|
1085
|
+
});
|
|
1086
|
+
const expandedOrder = memo((rows, byKey) => rows.map((row) => byKey.get(row.key)));
|
|
765
1087
|
function stateIssues(byId) {
|
|
766
1088
|
const issues = [];
|
|
1089
|
+
if (expandedIssue) issues.push(expandedIssue);
|
|
767
1090
|
const referenced = /* @__PURE__ */ new Set([
|
|
768
1091
|
...state.sorting.map((rule) => rule.column),
|
|
769
1092
|
...Object.keys(state.filters),
|
|
@@ -772,30 +1095,36 @@ function createTable(options) {
|
|
|
772
1095
|
]);
|
|
773
1096
|
for (const id of referenced) if (!byId.has(id)) issues.push(issue("unknown_column", `The state refers to a column "${id}" that is not defined.`, { column: id }));
|
|
774
1097
|
const { pageSize } = state.pagination;
|
|
775
|
-
if (!Number.isInteger(pageSize) || pageSize < 1) issues.push(issue("invalid_page_size", `Page size ${String(pageSize)} is not a positive whole number; ${10} is used.`));
|
|
1098
|
+
if (paginate && (!Number.isInteger(pageSize) || pageSize < 1)) issues.push(issue("invalid_page_size", `Page size ${String(pageSize)} is not a positive whole number; ${10} is used.`));
|
|
776
1099
|
return issues;
|
|
777
1100
|
}
|
|
778
1101
|
function computeSnapshot() {
|
|
779
|
-
const { columns, byId } = resolveColumns(definitions);
|
|
1102
|
+
const { columns, byId, issues: columnIssues } = resolveColumns(definitions);
|
|
780
1103
|
const indexed = indexRows(data, byId);
|
|
1104
|
+
const expanded = expansionStage(indexed, state.expanded);
|
|
781
1105
|
const ordered = orderColumns(columns, state.columnOrder, state.hiddenColumns);
|
|
782
|
-
const issues = [
|
|
1106
|
+
const issues = [
|
|
1107
|
+
...optionIssues,
|
|
1108
|
+
...columnIssues,
|
|
1109
|
+
...indexed.issues,
|
|
1110
|
+
...stateIssues(byId)
|
|
1111
|
+
];
|
|
783
1112
|
const requestedSize = state.pagination.pageSize;
|
|
784
|
-
|
|
1113
|
+
let pageSize = Number.isInteger(requestedSize) && requestedSize >= 1 ? requestedSize : 10;
|
|
785
1114
|
let processedRows;
|
|
786
1115
|
let rows;
|
|
787
1116
|
let filteredCount;
|
|
788
1117
|
let page;
|
|
789
1118
|
let pageCount;
|
|
790
1119
|
if (manual) {
|
|
791
|
-
processedRows =
|
|
792
|
-
rows =
|
|
1120
|
+
processedRows = expanded.rows;
|
|
1121
|
+
rows = expanded.rows;
|
|
793
1122
|
filteredCount = rowCount ?? indexed.rows.length;
|
|
794
1123
|
pageCount = countPages(filteredCount, pageSize);
|
|
795
1124
|
page = clampPage(state.pagination.page, pageCount);
|
|
796
1125
|
} else {
|
|
797
1126
|
const filtered = filterStage(indexed.rows, columns, state.search, state.filters);
|
|
798
|
-
processedRows = sortStage(filtered, columns, state.sorting);
|
|
1127
|
+
processedRows = expandedOrder(sortStage(filtered, columns, state.sorting), expanded.byKey);
|
|
799
1128
|
filteredCount = processedRows.length;
|
|
800
1129
|
pageCount = countPages(filteredCount, pageSize);
|
|
801
1130
|
page = clampPage(state.pagination.page, pageCount);
|
|
@@ -804,6 +1133,12 @@ function createTable(options) {
|
|
|
804
1133
|
pageSize
|
|
805
1134
|
});
|
|
806
1135
|
}
|
|
1136
|
+
if (!paginate) {
|
|
1137
|
+
rows = processedRows;
|
|
1138
|
+
page = 1;
|
|
1139
|
+
pageCount = 1;
|
|
1140
|
+
pageSize = Math.max(1, rows.length);
|
|
1141
|
+
}
|
|
807
1142
|
const selected = selectedSet(state.selection);
|
|
808
1143
|
const pageSelected = rows.filter((row) => selected.has(row.key)).length;
|
|
809
1144
|
const scopeRows = selectScope === "page" || manual ? rows : processedRows;
|
|
@@ -818,6 +1153,21 @@ function createTable(options) {
|
|
|
818
1153
|
columns: ordered.visible,
|
|
819
1154
|
allColumns: ordered.all,
|
|
820
1155
|
rows,
|
|
1156
|
+
renderItems: Object.freeze(rows.flatMap((row, rowIndex) => {
|
|
1157
|
+
const item = Object.freeze({
|
|
1158
|
+
kind: "row",
|
|
1159
|
+
key: getRowItemKey(row.key),
|
|
1160
|
+
row,
|
|
1161
|
+
rowIndex
|
|
1162
|
+
});
|
|
1163
|
+
return row.isExpanded ? [item, Object.freeze({
|
|
1164
|
+
kind: "detail",
|
|
1165
|
+
key: getRowItemKey(row.key, "detail"),
|
|
1166
|
+
row,
|
|
1167
|
+
rowIndex
|
|
1168
|
+
})] : [item];
|
|
1169
|
+
})),
|
|
1170
|
+
paginate,
|
|
821
1171
|
processedRows,
|
|
822
1172
|
page,
|
|
823
1173
|
pageSize,
|
|
@@ -839,7 +1189,7 @@ function createTable(options) {
|
|
|
839
1189
|
isSelected: (key) => selected.has(key),
|
|
840
1190
|
getSort: (columnId) => sortIndex.get(columnId),
|
|
841
1191
|
getColumn: (columnId) => byId.get(columnId),
|
|
842
|
-
getRow: (key) =>
|
|
1192
|
+
getRow: (key) => expanded.byKey.get(key)
|
|
843
1193
|
});
|
|
844
1194
|
}
|
|
845
1195
|
function getSnapshot() {
|
|
@@ -855,12 +1205,31 @@ function createTable(options) {
|
|
|
855
1205
|
/** Apply a change made by a table operation. */
|
|
856
1206
|
function commit(patch) {
|
|
857
1207
|
const previous = state;
|
|
858
|
-
const
|
|
859
|
-
|
|
1208
|
+
const previousIssue = expandedIssue;
|
|
1209
|
+
const next = mergePatch(patch);
|
|
1210
|
+
if (next === previous && previousIssue === expandedIssue) return;
|
|
860
1211
|
state = next;
|
|
861
|
-
options.onStateChange?.(state, previous);
|
|
1212
|
+
if (next !== previous) options.onStateChange?.(state, previous);
|
|
862
1213
|
notify();
|
|
863
1214
|
}
|
|
1215
|
+
function canonicalExpanded(expanded, candidate = state) {
|
|
1216
|
+
if (expandMode === "multiple") return expanded;
|
|
1217
|
+
if (expanded !== true) return expanded.slice(-1);
|
|
1218
|
+
const { columns, byId } = resolveColumns(definitions);
|
|
1219
|
+
const indexed = indexRows(data, byId);
|
|
1220
|
+
const first = (manual ? indexed.rows : sortStage(filterStage(indexed.rows, columns, candidate.search, candidate.filters), columns, candidate.sorting)).find((row) => row.canExpand);
|
|
1221
|
+
return first ? [first.key] : [];
|
|
1222
|
+
}
|
|
1223
|
+
function mergePatch(patch) {
|
|
1224
|
+
if (patch.expanded !== void 0) expandedIssue = void 0;
|
|
1225
|
+
const next = mergeState(state, patch, (problem) => {
|
|
1226
|
+
expandedIssue = problem;
|
|
1227
|
+
});
|
|
1228
|
+
return expandMode === "single" && patch.expanded !== void 0 ? mergeState(state, {
|
|
1229
|
+
...patch,
|
|
1230
|
+
expanded: canonicalExpanded(next.expanded, next)
|
|
1231
|
+
}) : next;
|
|
1232
|
+
}
|
|
864
1233
|
const firstPage = () => ({
|
|
865
1234
|
page: 1,
|
|
866
1235
|
pageSize: state.pagination.pageSize
|
|
@@ -890,10 +1259,10 @@ function createTable(options) {
|
|
|
890
1259
|
if (selection.length === current.length && selection.every((key, i) => key === current[i])) return;
|
|
891
1260
|
commit({ selection });
|
|
892
1261
|
}
|
|
893
|
-
function applyChanges(edits, record) {
|
|
1262
|
+
function applyChanges(edits, record, recoveredIssues = []) {
|
|
894
1263
|
const current = getSnapshot();
|
|
895
1264
|
const working = [...data];
|
|
896
|
-
const issues = [];
|
|
1265
|
+
const issues = [...recoveredIssues];
|
|
897
1266
|
const changes = [];
|
|
898
1267
|
const samples = /* @__PURE__ */ new Map();
|
|
899
1268
|
const sampleOf = (columnId) => {
|
|
@@ -916,6 +1285,13 @@ function createTable(options) {
|
|
|
916
1285
|
}));
|
|
917
1286
|
continue;
|
|
918
1287
|
}
|
|
1288
|
+
if (!isSafePath(edit.column)) {
|
|
1289
|
+
issues.push(issue("unsafe_path", `The path "${edit.column}" addresses a prototype and cannot be written.`, {
|
|
1290
|
+
rowKey: edit.rowKey,
|
|
1291
|
+
column: edit.column
|
|
1292
|
+
}));
|
|
1293
|
+
continue;
|
|
1294
|
+
}
|
|
919
1295
|
if (!column) {
|
|
920
1296
|
issues.push(issue("unknown_column", `No column has the id "${edit.column}".`, {
|
|
921
1297
|
rowKey: edit.rowKey,
|
|
@@ -1002,6 +1378,11 @@ function createTable(options) {
|
|
|
1002
1378
|
value: direction === "undo" ? change.previous : change.value
|
|
1003
1379
|
})), false);
|
|
1004
1380
|
}
|
|
1381
|
+
if (expandMode === "single") {
|
|
1382
|
+
initialState = mergeState(initialState, { expanded: canonicalExpanded(initialState.expanded) });
|
|
1383
|
+
state = initialState;
|
|
1384
|
+
snapshot = void 0;
|
|
1385
|
+
}
|
|
1005
1386
|
const table = {
|
|
1006
1387
|
getSnapshot,
|
|
1007
1388
|
getState: () => state,
|
|
@@ -1012,8 +1393,9 @@ function createTable(options) {
|
|
|
1012
1393
|
};
|
|
1013
1394
|
},
|
|
1014
1395
|
setState(patch) {
|
|
1015
|
-
const
|
|
1016
|
-
|
|
1396
|
+
const previousIssue = expandedIssue;
|
|
1397
|
+
const next = mergePatch(patch);
|
|
1398
|
+
if (next !== state || previousIssue !== expandedIssue) {
|
|
1017
1399
|
state = next;
|
|
1018
1400
|
notify();
|
|
1019
1401
|
}
|
|
@@ -1069,6 +1451,7 @@ function createTable(options) {
|
|
|
1069
1451
|
});
|
|
1070
1452
|
},
|
|
1071
1453
|
goToPage(page) {
|
|
1454
|
+
if (!paginate) return;
|
|
1072
1455
|
const { pageCount, page: currentPage } = getSnapshot();
|
|
1073
1456
|
const next = clampPage(page, pageCount);
|
|
1074
1457
|
if (next !== currentPage || next !== state.pagination.page) commit({ pagination: {
|
|
@@ -1083,6 +1466,7 @@ function createTable(options) {
|
|
|
1083
1466
|
table.goToPage(getSnapshot().page - 1);
|
|
1084
1467
|
},
|
|
1085
1468
|
setPageSize(pageSize) {
|
|
1469
|
+
if (!paginate) return;
|
|
1086
1470
|
if (!Number.isInteger(pageSize) || pageSize < 1 || pageSize === state.pagination.pageSize) return;
|
|
1087
1471
|
const { pageStart } = getSnapshot();
|
|
1088
1472
|
commit({ pagination: {
|
|
@@ -1121,6 +1505,23 @@ function createTable(options) {
|
|
|
1121
1505
|
return row ? [row] : [];
|
|
1122
1506
|
});
|
|
1123
1507
|
},
|
|
1508
|
+
toggleExpanded(key, expanded) {
|
|
1509
|
+
const row = getSnapshot().getRow(key);
|
|
1510
|
+
if (!row?.canExpand) return;
|
|
1511
|
+
const open = expanded ?? !row.isExpanded;
|
|
1512
|
+
if (open === row.isExpanded) return;
|
|
1513
|
+
if (expandMode === "single") commit({ expanded: open ? [key] : [] });
|
|
1514
|
+
else if (state.expanded === true) {
|
|
1515
|
+
const { byId } = resolveColumns(definitions);
|
|
1516
|
+
commit({ expanded: indexRows(data, byId).rows.filter((candidate) => candidate.canExpand && candidate.key !== key).map((candidate) => candidate.key) });
|
|
1517
|
+
} else commit({ expanded: open ? [...state.expanded, key] : state.expanded.filter((candidate) => candidate !== key) });
|
|
1518
|
+
},
|
|
1519
|
+
expandAll() {
|
|
1520
|
+
commit({ expanded: true });
|
|
1521
|
+
},
|
|
1522
|
+
collapseAll() {
|
|
1523
|
+
commit({ expanded: [] });
|
|
1524
|
+
},
|
|
1124
1525
|
toggleColumn(columnId, visible) {
|
|
1125
1526
|
if (!getSnapshot().getColumn(columnId)) return;
|
|
1126
1527
|
const hidden = state.hiddenColumns.includes(columnId);
|
|
@@ -1153,7 +1554,7 @@ function createTable(options) {
|
|
|
1153
1554
|
replay(changes, "redo");
|
|
1154
1555
|
return true;
|
|
1155
1556
|
},
|
|
1156
|
-
copy(range) {
|
|
1557
|
+
copy(range, { escapeFormulas = false } = {}) {
|
|
1157
1558
|
const { rows, columns } = getSnapshot();
|
|
1158
1559
|
const matrix = [];
|
|
1159
1560
|
rows.forEach((row, rowIndex) => {
|
|
@@ -1162,14 +1563,26 @@ function createTable(options) {
|
|
|
1162
1563
|
if (rangeContains(range, {
|
|
1163
1564
|
row: rowIndex,
|
|
1164
1565
|
column: columnIndex
|
|
1165
|
-
}))
|
|
1566
|
+
})) {
|
|
1567
|
+
const display = row.getDisplay(column.id);
|
|
1568
|
+
line.push(escapeFormulas ? escapeFormula(display, row.getValue(column.id)) : display);
|
|
1569
|
+
}
|
|
1166
1570
|
});
|
|
1167
1571
|
if (line.length > 0) matrix.push(line);
|
|
1168
1572
|
});
|
|
1169
1573
|
return toDelimited(matrix);
|
|
1170
1574
|
},
|
|
1171
1575
|
paste(origin, text) {
|
|
1172
|
-
|
|
1576
|
+
if (!Number.isInteger(origin.row) || !Number.isInteger(origin.column) || origin.row < 0 || origin.column < 0) return applyChanges([], true, [issue("invalid_value", "The paste origin must use non-negative whole row and column indices.")]);
|
|
1577
|
+
const { rows, columns } = getSnapshot();
|
|
1578
|
+
const parsed = readDelimited(text, {
|
|
1579
|
+
maxRows: Math.max(0, rows.length - origin.row),
|
|
1580
|
+
maxColumns: Math.max(0, columns.length - origin.column),
|
|
1581
|
+
maxCells: maxPasteCells,
|
|
1582
|
+
maxLength: maxPasteLength
|
|
1583
|
+
});
|
|
1584
|
+
const issues = parsed.truncated ? [issue("paste_truncated", "The paste exceeds the visible grid or parsing limits; only complete cells within those limits are applied.")] : [];
|
|
1585
|
+
return applyChanges(rangeEdits(origin, parsed.rows), true, issues);
|
|
1173
1586
|
},
|
|
1174
1587
|
clear(range) {
|
|
1175
1588
|
const matrix = Array.from({ length: range.bottom - range.top + 1 }, () => Array.from({ length: range.right - range.left + 1 }, () => ""));
|
|
@@ -1182,14 +1595,17 @@ function createTable(options) {
|
|
|
1182
1595
|
return row !== void 0 && current.getColumn(edit.column)?.isEditable(row.original) === true;
|
|
1183
1596
|
}), true);
|
|
1184
1597
|
},
|
|
1185
|
-
exportRows({ format = "csv", columns: ids, pageOnly = false, headers = true } = {}) {
|
|
1598
|
+
exportRows({ format = "csv", columns: ids, pageOnly = false, headers = true, escapeFormulas = true } = {}) {
|
|
1186
1599
|
const current = getSnapshot();
|
|
1187
1600
|
const columns = ids ? ids.flatMap((id) => {
|
|
1188
1601
|
const column = current.getColumn(id);
|
|
1189
1602
|
return column ? [column] : [];
|
|
1190
1603
|
}) : current.columns;
|
|
1191
|
-
const matrix = (pageOnly ? current.rows : current.processedRows).map((row) => columns.map((column) =>
|
|
1192
|
-
|
|
1604
|
+
const matrix = (pageOnly ? current.rows : current.processedRows).map((row) => columns.map((column) => {
|
|
1605
|
+
const display = row.getDisplay(column.id);
|
|
1606
|
+
return escapeFormulas ? escapeFormula(display, row.getValue(column.id)) : display;
|
|
1607
|
+
}));
|
|
1608
|
+
if (headers) matrix.unshift(columns.map((column) => escapeFormulas ? escapeFormula(column.header) : column.header));
|
|
1193
1609
|
return toDelimited(matrix, format === "csv" ? "," : " ");
|
|
1194
1610
|
},
|
|
1195
1611
|
reset() {
|
|
@@ -1203,5 +1619,5 @@ function createTable(options) {
|
|
|
1203
1619
|
}
|
|
1204
1620
|
|
|
1205
1621
|
//#endregion
|
|
1206
|
-
export { DEFAULT_PAGE_SIZE, clampPage, clampPosition, columnLabel, compareValues, countPages, createTable, createUndoStack, defineColumns, filterRows, formatValue, fromA1, getPath, gridCommand, humanize, inferColumns, matchesFilter, moveSelection, paginateRows, paginationItems, parseAs, parseDelimited, rangeContains, resolveColumn, selectCell, selectionRange, setPath, sortRows, toA1, toDelimited, typeOf };
|
|
1622
|
+
export { DEFAULT_PAGE_SIZE, clampPage, clampPosition, columnLabel, compareValues, countPages, createTable, createUndoStack, createVirtualizer, defineColumns, filterRows, formatValue, fromA1, getPath, getRowItemKey, gridCommand, humanize, inferColumns, matchesFilter, moveSelection, paginateRows, paginationItems, parseAs, parseDelimited, rangeContains, resolveColumn, selectCell, selectionRange, setPath, sortRows, toA1, toDelimited, typeOf };
|
|
1207
1623
|
//# sourceMappingURL=index.js.map
|