@freegridjs/core 1.9.5 → 1.11.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.
- package/README.md +2 -2
- package/dist/cdn/freegrid.css +35 -25
- package/dist/cdn/freegrid.esm.js +24 -25
- package/dist/cdn/freegrid.min.js +24 -25
- package/dist/index.d.ts +3 -3
- package/dist/index.js +538 -536
- package/dist/locales/de.d.ts +1 -1
- package/dist/locales/de.js +2 -1
- package/dist/locales/en.d.ts +1 -1
- package/dist/locales/en.js +2 -1
- package/dist/locales/es.d.ts +1 -1
- package/dist/locales/es.js +2 -1
- package/dist/locales/fr.d.ts +1 -1
- package/dist/locales/fr.js +2 -1
- package/dist/locales/id.d.ts +1 -1
- package/dist/locales/id.js +2 -1
- package/dist/locales/it.d.ts +1 -1
- package/dist/locales/it.js +2 -1
- package/dist/locales/ja.d.ts +1 -1
- package/dist/locales/ja.js +2 -1
- package/dist/locales/ko.d.ts +1 -1
- package/dist/locales/ko.js +2 -1
- package/dist/locales/pt-BR.d.ts +1 -1
- package/dist/locales/pt-BR.js +2 -1
- package/dist/locales/ru.d.ts +1 -1
- package/dist/locales/ru.js +2 -1
- package/dist/locales/th.d.ts +1 -1
- package/dist/locales/th.js +2 -1
- package/dist/locales/vi.d.ts +1 -1
- package/dist/locales/vi.js +2 -1
- package/dist/locales/zh-CN.d.ts +1 -1
- package/dist/locales/zh-CN.js +2 -1
- package/dist/locales/zh-TW.d.ts +1 -1
- package/dist/locales/zh-TW.js +2 -1
- package/dist/styles.css +35 -25
- package/dist/{types-CIOv3MoN.d.ts → types-8dux3w2p.d.ts} +28 -142
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -432,9 +432,10 @@ var ColumnModel = class {
|
|
|
432
432
|
setContainerWidth(width) {
|
|
433
433
|
if (width === this.containerWidth) return false;
|
|
434
434
|
this.containerWidth = width;
|
|
435
|
-
const
|
|
435
|
+
const sig = () => this.displayed().map((c) => `${c.width}${c.region}`).join();
|
|
436
|
+
const before = sig();
|
|
436
437
|
this.relayout();
|
|
437
|
-
return before !==
|
|
438
|
+
return before !== sig();
|
|
438
439
|
}
|
|
439
440
|
roots() {
|
|
440
441
|
return this.roots_;
|
|
@@ -608,7 +609,19 @@ var ColumnModel = class {
|
|
|
608
609
|
return excess > 0 ? d > 0 : excess < 0 ? d < 0 : false;
|
|
609
610
|
});
|
|
610
611
|
if (!clamped.length) {
|
|
611
|
-
|
|
612
|
+
let rest = free;
|
|
613
|
+
for (const n of pending) {
|
|
614
|
+
const w = clampWidth(n.def, share(n));
|
|
615
|
+
flexWidth.set(n, w);
|
|
616
|
+
rest -= w;
|
|
617
|
+
}
|
|
618
|
+
for (const n of pending) {
|
|
619
|
+
if (rest < 1) break;
|
|
620
|
+
const w = flexWidth.get(n);
|
|
621
|
+
if (clampWidth(n.def, w + 1) !== w + 1) continue;
|
|
622
|
+
flexWidth.set(n, w + 1);
|
|
623
|
+
rest--;
|
|
624
|
+
}
|
|
612
625
|
break;
|
|
613
626
|
}
|
|
614
627
|
let used = 0;
|
|
@@ -624,13 +637,16 @@ var ColumnModel = class {
|
|
|
624
637
|
const next = { left: [], center: [], right: [] };
|
|
625
638
|
this.leafById = /* @__PURE__ */ new Map();
|
|
626
639
|
let index = 0;
|
|
640
|
+
const pinnedWidth = visible.reduce((s, n) => s + (n.pinned ? widthOf(n) : 0), 0);
|
|
641
|
+
const unpin = this.containerWidth > 0 && pinnedWidth > this.containerWidth * 0.75;
|
|
642
|
+
const x = { left: 0, center: 0, right: 0 };
|
|
627
643
|
for (const r of REGIONS) {
|
|
628
|
-
|
|
644
|
+
const to = unpin ? "center" : r;
|
|
629
645
|
for (const n of visible) {
|
|
630
646
|
if ((n.pinned ?? "center") !== r) continue;
|
|
631
|
-
const col = { id: n.id, def: n.def, node: n, width: widthOf(n), region:
|
|
632
|
-
x += col.width;
|
|
633
|
-
next[
|
|
647
|
+
const col = { id: n.id, def: n.def, node: n, width: widthOf(n), region: to, left: x[to], index: index++ };
|
|
648
|
+
x[to] += col.width;
|
|
649
|
+
next[to].push(col);
|
|
634
650
|
this.leafById.set(n.id, col);
|
|
635
651
|
}
|
|
636
652
|
}
|
|
@@ -638,6 +654,39 @@ var ColumnModel = class {
|
|
|
638
654
|
}
|
|
639
655
|
};
|
|
640
656
|
|
|
657
|
+
// src/keyboard.ts
|
|
658
|
+
var imeKey = (e) => e.isComposing || e.keyCode === 229;
|
|
659
|
+
var NAV_KEYS = /* @__PURE__ */ new Set(["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Tab", "Home", "End", "PageUp", "PageDown"]);
|
|
660
|
+
function nextFocus(key, mods, p, s) {
|
|
661
|
+
const r = (i) => Math.max(0, Math.min(s.rows - 1, i));
|
|
662
|
+
const c = (i) => Math.max(0, Math.min(s.cols - 1, i));
|
|
663
|
+
switch (key) {
|
|
664
|
+
case "ArrowUp":
|
|
665
|
+
return { rowIndex: r(p.rowIndex - 1), colIndex: p.colIndex };
|
|
666
|
+
case "ArrowDown":
|
|
667
|
+
return { rowIndex: r(p.rowIndex + 1), colIndex: p.colIndex };
|
|
668
|
+
case "ArrowLeft":
|
|
669
|
+
return { rowIndex: p.rowIndex, colIndex: c(p.colIndex - 1) };
|
|
670
|
+
case "ArrowRight":
|
|
671
|
+
return { rowIndex: p.rowIndex, colIndex: c(p.colIndex + 1) };
|
|
672
|
+
case "PageUp":
|
|
673
|
+
return { rowIndex: r(p.rowIndex - s.pageRows), colIndex: p.colIndex };
|
|
674
|
+
case "PageDown":
|
|
675
|
+
return { rowIndex: r(p.rowIndex + s.pageRows), colIndex: p.colIndex };
|
|
676
|
+
case "Home":
|
|
677
|
+
return { rowIndex: mods.ctrl ? 0 : p.rowIndex, colIndex: 0 };
|
|
678
|
+
case "End":
|
|
679
|
+
return { rowIndex: mods.ctrl ? s.rows - 1 : p.rowIndex, colIndex: s.cols - 1 };
|
|
680
|
+
case "Tab": {
|
|
681
|
+
const flat2 = p.rowIndex * s.cols + p.colIndex + (mods.shift ? -1 : 1);
|
|
682
|
+
if (flat2 < 0 || flat2 >= s.rows * s.cols) return null;
|
|
683
|
+
return { rowIndex: Math.floor(flat2 / s.cols), colIndex: flat2 % s.cols };
|
|
684
|
+
}
|
|
685
|
+
default:
|
|
686
|
+
return null;
|
|
687
|
+
}
|
|
688
|
+
}
|
|
689
|
+
|
|
641
690
|
// src/formula.ts
|
|
642
691
|
var FormulaError = class extends Error {
|
|
643
692
|
constructor(code) {
|
|
@@ -1320,25 +1369,6 @@ function columnFormulaValue(def, row) {
|
|
|
1320
1369
|
}
|
|
1321
1370
|
var formulasOff = (def) => !!scopes.get(def)?.off?.();
|
|
1322
1371
|
var isFormula = (v) => typeof v === "string" && v.length > 1 && v.startsWith("=");
|
|
1323
|
-
function formulaRefs(formula) {
|
|
1324
|
-
let n;
|
|
1325
|
-
try {
|
|
1326
|
-
n = compile(formula);
|
|
1327
|
-
} catch {
|
|
1328
|
-
return null;
|
|
1329
|
-
}
|
|
1330
|
-
const out = /* @__PURE__ */ new Set();
|
|
1331
|
-
const walk = (x) => {
|
|
1332
|
-
if (x.k === "ref") out.add(x.name);
|
|
1333
|
-
else if (x.k === "un") walk(x.a);
|
|
1334
|
-
else if (x.k === "bin") {
|
|
1335
|
-
walk(x.a);
|
|
1336
|
-
walk(x.b);
|
|
1337
|
-
} else if (x.k === "call") x.args.forEach(walk);
|
|
1338
|
-
};
|
|
1339
|
-
walk(n);
|
|
1340
|
-
return [...out];
|
|
1341
|
-
}
|
|
1342
1372
|
|
|
1343
1373
|
// src/rows/values.ts
|
|
1344
1374
|
function getCellValue(def, row) {
|
|
@@ -1361,6 +1391,7 @@ var REGIONS2 = ["left", "center", "right"];
|
|
|
1361
1391
|
var NUM_OPS = [[">=", "gte"], ["<=", "lte"], ["<>", "ne"], ["!=", "ne"], [">", "gt"], ["<", "lt"], ["=", "eq"]];
|
|
1362
1392
|
var DATE_OPS = [[">", "after"], ["<", "before"], ["=", "equals"]];
|
|
1363
1393
|
var RANGE = /^(.+?)\s*(?:~|\.\.)\s*(.+)$/;
|
|
1394
|
+
var MULTI = "~many";
|
|
1364
1395
|
var toNumber = (s) => {
|
|
1365
1396
|
const n = Number(s.replace(/[,\s₩$€¥]/g, ""));
|
|
1366
1397
|
return s.trim() !== "" && Number.isFinite(n) ? n : null;
|
|
@@ -1460,8 +1491,21 @@ var FilterRow = class {
|
|
|
1460
1491
|
box.closest(".fg-filter-cell").classList.toggle("fg-filter-active", !!model);
|
|
1461
1492
|
if (box === box.ownerDocument.activeElement || box.getRootNode().activeElement === box) continue;
|
|
1462
1493
|
if (box instanceof HTMLSelectElement) {
|
|
1463
|
-
const
|
|
1464
|
-
|
|
1494
|
+
const many = model?.type === "set" && model.values.length > 1 ? model.values.length : 0;
|
|
1495
|
+
let multi = box.querySelector(`option[value="${MULTI}"]`);
|
|
1496
|
+
if (many) {
|
|
1497
|
+
if (!multi) {
|
|
1498
|
+
multi = el("option", "", { value: MULTI });
|
|
1499
|
+
multi.disabled = true;
|
|
1500
|
+
box.options[0].after(multi);
|
|
1501
|
+
}
|
|
1502
|
+
multi.textContent = this.grid.store.state.locale.setSelected.replace("{n}", String(many));
|
|
1503
|
+
box.value = MULTI;
|
|
1504
|
+
} else {
|
|
1505
|
+
multi?.remove();
|
|
1506
|
+
const v = model?.type === "set" && model.values.length === 1 ? JSON.stringify(model.values[0]) : "";
|
|
1507
|
+
if ([...box.options].some((o) => o.value === v)) box.value = v;
|
|
1508
|
+
}
|
|
1465
1509
|
} else box.value = filterText(model);
|
|
1466
1510
|
box.classList.remove("fg-filter-bad");
|
|
1467
1511
|
}
|
|
@@ -1475,6 +1519,10 @@ var FilterRow = class {
|
|
|
1475
1519
|
if (!def) continue;
|
|
1476
1520
|
const v = select.value;
|
|
1477
1521
|
this.fillOptions(select, def);
|
|
1522
|
+
if (v === MULTI) {
|
|
1523
|
+
this.rebuilt = true;
|
|
1524
|
+
continue;
|
|
1525
|
+
}
|
|
1478
1526
|
if (v && ![...select.options].some((o) => o.value === v)) {
|
|
1479
1527
|
const o = el("option", "", { value: v });
|
|
1480
1528
|
o.textContent = String(JSON.parse(v));
|
|
@@ -1495,7 +1543,7 @@ var FilterRow = class {
|
|
|
1495
1543
|
select.replaceChildren(all2, ...opts);
|
|
1496
1544
|
}
|
|
1497
1545
|
cell(c, h) {
|
|
1498
|
-
const cell = el("div", "fg-filter-cell");
|
|
1546
|
+
const cell = el("div", "fg-filter-cell", { role: "gridcell" });
|
|
1499
1547
|
cell.style.left = `${c.left}px`;
|
|
1500
1548
|
cell.style.width = `${c.width}px`;
|
|
1501
1549
|
cell.style.height = `${h}px`;
|
|
@@ -1529,6 +1577,7 @@ var FilterRow = class {
|
|
|
1529
1577
|
});
|
|
1530
1578
|
input.addEventListener("keydown", (e) => {
|
|
1531
1579
|
e.stopPropagation();
|
|
1580
|
+
if (imeKey(e)) return;
|
|
1532
1581
|
if (e.key === "Enter") {
|
|
1533
1582
|
clearTimeout(this.timers.get(c.id));
|
|
1534
1583
|
apply();
|
|
@@ -1818,8 +1867,10 @@ function extreme(values, pickMax) {
|
|
|
1818
1867
|
function aggregate(agg, values, rows) {
|
|
1819
1868
|
if (typeof agg === "function") return agg(values, rows);
|
|
1820
1869
|
switch (agg) {
|
|
1821
|
-
case "sum":
|
|
1822
|
-
|
|
1870
|
+
case "sum": {
|
|
1871
|
+
const nums = numbers(values);
|
|
1872
|
+
return nums.length || !values.length ? nums.reduce((s, n) => s + n, 0) : null;
|
|
1873
|
+
}
|
|
1823
1874
|
case "avg": {
|
|
1824
1875
|
const nums = numbers(values);
|
|
1825
1876
|
return nums.length ? nums.reduce((s, n) => s + n, 0) / nums.length : null;
|
|
@@ -2055,6 +2106,7 @@ var Renderer = class {
|
|
|
2055
2106
|
};
|
|
2056
2107
|
this.overlayKey = "";
|
|
2057
2108
|
this.heightFn = null;
|
|
2109
|
+
this.freeBadge = null;
|
|
2058
2110
|
this.onCardClick = (e) => {
|
|
2059
2111
|
const target = e.target;
|
|
2060
2112
|
const card = target.closest(".fg-card");
|
|
@@ -2203,7 +2255,8 @@ var Renderer = class {
|
|
|
2203
2255
|
}
|
|
2204
2256
|
if (options.autoHeight && !this.useCards()) {
|
|
2205
2257
|
this.root.classList.add("fg-auto-height");
|
|
2206
|
-
|
|
2258
|
+
const rowsH = count > 0 ? this.rowsHeight(count) : Math.max(this.rowsHeight(count), 3 * rh);
|
|
2259
|
+
this.bodyEl.style.height = `${rowsH + this.topOffset + bottomExtra + Math.max(0, this.scroller.offsetHeight - this.scroller.clientHeight)}px`;
|
|
2207
2260
|
}
|
|
2208
2261
|
const sig = `${headerHeight}|${REGIONS3.map((r) => columns.regionWidth(r)).join(",")}|${this.rowsHeight(count) + this.topOffset + bottomExtra}|${summary && sumPinned ? sumPos : ""}|${sumRows}|${options.autoHeight ? 1 : 0}`;
|
|
2209
2262
|
if (sig !== this.layoutSig) {
|
|
@@ -2262,6 +2315,7 @@ var Renderer = class {
|
|
|
2262
2315
|
if (ref.nextElementSibling !== b) ref.after(b);
|
|
2263
2316
|
ref = b;
|
|
2264
2317
|
}
|
|
2318
|
+
this.placeFreeBadge();
|
|
2265
2319
|
}
|
|
2266
2320
|
/** "No rows" / "Loading" over the body, or the noRowsOverlay / loadingOverlay given in the options. */
|
|
2267
2321
|
paintOverlay(count) {
|
|
@@ -2352,7 +2406,7 @@ var Renderer = class {
|
|
|
2352
2406
|
const { grid } = this;
|
|
2353
2407
|
const state = grid.store.state;
|
|
2354
2408
|
const total = grid.rowModel.server ? count : grid.viewRows().length;
|
|
2355
|
-
const key = `${this.version}.${grid.valuesVersion}|${count}|${total}|${state.selected.size}|${JSON.stringify(state.range)}|${state.locale.code}`;
|
|
2409
|
+
const key = `${this.version}.${grid.valuesVersion}|${count}|${total}|${state.selected.size}|${JSON.stringify(state.range)}|${JSON.stringify(state.extraRanges)}|${state.locale.code}`;
|
|
2356
2410
|
if (this.statusEl && key === this.statusKey) return;
|
|
2357
2411
|
this.statusKey = key;
|
|
2358
2412
|
this.statusEl ?? (this.statusEl = el("div", "fg-statusbar", { role: "status", "aria-live": "polite" }));
|
|
@@ -3224,12 +3278,21 @@ var Renderer = class {
|
|
|
3224
3278
|
/** Small "free version" mark in the bottom-right corner (unlicensed company domains flagged on freegrid.app). */
|
|
3225
3279
|
showFreeBadge() {
|
|
3226
3280
|
if (this.root.querySelector(".fg-free-badge")) return;
|
|
3227
|
-
const a = el("
|
|
3281
|
+
const a = el("div", "fg-free-badge", { "aria-hidden": "true" });
|
|
3228
3282
|
a.innerHTML = '<svg viewBox="0 0 32 32" width="14" height="14" aria-hidden="true"><rect width="32" height="32" rx="7" fill="#0E1726"/><path d="M7 11.5h18M7 16.5h18M7 21.5h18M13 7v18" stroke="#fff" stroke-width="2" stroke-linecap="round"/><rect x="14" y="12.5" width="11" height="3" rx="1" fill="#F2A900"/></svg>';
|
|
3229
3283
|
const text2 = el("span");
|
|
3230
3284
|
text2.textContent = `FreeGrid \xB7 ${this.grid.store.state.locale.freeBadge}`;
|
|
3231
3285
|
a.append(text2);
|
|
3232
3286
|
this.root.append(a);
|
|
3287
|
+
this.freeBadge = a;
|
|
3288
|
+
this.placeFreeBadge();
|
|
3289
|
+
}
|
|
3290
|
+
/** The free badge sits above the status bar and pager, not on them. */
|
|
3291
|
+
placeFreeBadge() {
|
|
3292
|
+
if (!this.freeBadge) return;
|
|
3293
|
+
const below = (this.statusEl?.isConnected ? this.statusEl.offsetHeight : 0) + (this.pagerEl?.isConnected ? this.pagerEl.offsetHeight : 0);
|
|
3294
|
+
const bottom = below ? `${below + 22}px` : "";
|
|
3295
|
+
if (this.freeBadge.style.bottom !== bottom) this.freeBadge.style.bottom = bottom;
|
|
3233
3296
|
}
|
|
3234
3297
|
/** Card view applies when enabled and the grid is narrower than the breakpoint. */
|
|
3235
3298
|
useCards() {
|
|
@@ -3549,6 +3612,8 @@ var en = {
|
|
|
3549
3612
|
apply: "Apply",
|
|
3550
3613
|
reset: "Reset",
|
|
3551
3614
|
selectAll: "(Select all)",
|
|
3615
|
+
setSelected: "({n} selected)",
|
|
3616
|
+
noAggregate: "No aggregate",
|
|
3552
3617
|
search: "Search\u2026",
|
|
3553
3618
|
blanks: "(Blanks)",
|
|
3554
3619
|
value: "Value",
|
|
@@ -3635,7 +3700,6 @@ var en = {
|
|
|
3635
3700
|
freeBadge: "Free version",
|
|
3636
3701
|
exportPdf: "Export PDF",
|
|
3637
3702
|
exportJson: "Export JSON",
|
|
3638
|
-
ai: { title: "AI assistant", placeholder: "Ask about the data or tell the grid what to do", send: "Send", thinking: "Working\u2026", undo: "Undo", undone: "Undone", failed: "The assistant did not answer", skipped: "Skipped", done: "Done" },
|
|
3639
3703
|
cf: {
|
|
3640
3704
|
title: "Conditional formatting",
|
|
3641
3705
|
add: "Add rule",
|
|
@@ -3732,38 +3796,6 @@ function eventInside(e, ...els) {
|
|
|
3732
3796
|
return els.some((el2) => !!el2 && el2.contains(e.target));
|
|
3733
3797
|
}
|
|
3734
3798
|
|
|
3735
|
-
// src/keyboard.ts
|
|
3736
|
-
var NAV_KEYS = /* @__PURE__ */ new Set(["ArrowUp", "ArrowDown", "ArrowLeft", "ArrowRight", "Tab", "Home", "End", "PageUp", "PageDown"]);
|
|
3737
|
-
function nextFocus(key, mods, p, s) {
|
|
3738
|
-
const r = (i) => Math.max(0, Math.min(s.rows - 1, i));
|
|
3739
|
-
const c = (i) => Math.max(0, Math.min(s.cols - 1, i));
|
|
3740
|
-
switch (key) {
|
|
3741
|
-
case "ArrowUp":
|
|
3742
|
-
return { rowIndex: r(p.rowIndex - 1), colIndex: p.colIndex };
|
|
3743
|
-
case "ArrowDown":
|
|
3744
|
-
return { rowIndex: r(p.rowIndex + 1), colIndex: p.colIndex };
|
|
3745
|
-
case "ArrowLeft":
|
|
3746
|
-
return { rowIndex: p.rowIndex, colIndex: c(p.colIndex - 1) };
|
|
3747
|
-
case "ArrowRight":
|
|
3748
|
-
return { rowIndex: p.rowIndex, colIndex: c(p.colIndex + 1) };
|
|
3749
|
-
case "PageUp":
|
|
3750
|
-
return { rowIndex: r(p.rowIndex - s.pageRows), colIndex: p.colIndex };
|
|
3751
|
-
case "PageDown":
|
|
3752
|
-
return { rowIndex: r(p.rowIndex + s.pageRows), colIndex: p.colIndex };
|
|
3753
|
-
case "Home":
|
|
3754
|
-
return { rowIndex: mods.ctrl ? 0 : p.rowIndex, colIndex: 0 };
|
|
3755
|
-
case "End":
|
|
3756
|
-
return { rowIndex: mods.ctrl ? s.rows - 1 : p.rowIndex, colIndex: s.cols - 1 };
|
|
3757
|
-
case "Tab": {
|
|
3758
|
-
const flat2 = p.rowIndex * s.cols + p.colIndex + (mods.shift ? -1 : 1);
|
|
3759
|
-
if (flat2 < 0 || flat2 >= s.rows * s.cols) return null;
|
|
3760
|
-
return { rowIndex: Math.floor(flat2 / s.cols), colIndex: flat2 % s.cols };
|
|
3761
|
-
}
|
|
3762
|
-
default:
|
|
3763
|
-
return null;
|
|
3764
|
-
}
|
|
3765
|
-
}
|
|
3766
|
-
|
|
3767
3799
|
// src/registry.ts
|
|
3768
3800
|
var liveGrids = /* @__PURE__ */ new Map();
|
|
3769
3801
|
var apiGrids = /* @__PURE__ */ new WeakMap();
|
|
@@ -3779,7 +3811,8 @@ var DEFAULT_KEYMAP = {
|
|
|
3779
3811
|
"Ctrl+Z": "undo",
|
|
3780
3812
|
"Ctrl+Y": "redo",
|
|
3781
3813
|
"Ctrl+Shift+Z": "redo",
|
|
3782
|
-
"Ctrl+F": "find"
|
|
3814
|
+
"Ctrl+F": "find",
|
|
3815
|
+
"Alt+ArrowDown": "columnMenu"
|
|
3783
3816
|
};
|
|
3784
3817
|
function comboOf(e) {
|
|
3785
3818
|
const parts = [];
|
|
@@ -3928,6 +3961,8 @@ function attachBody(grid) {
|
|
|
3928
3961
|
case "paste":
|
|
3929
3962
|
navigator.clipboard?.readText?.().then((t) => grid.pasteText(t), () => void 0);
|
|
3930
3963
|
return true;
|
|
3964
|
+
case "columnMenu":
|
|
3965
|
+
return grid.menu.openAtFocus();
|
|
3931
3966
|
}
|
|
3932
3967
|
if (!f) return false;
|
|
3933
3968
|
if (!grid.editor.canEdit(f.rowIndex, f.colId)) return false;
|
|
@@ -3943,6 +3978,13 @@ function attachBody(grid) {
|
|
|
3943
3978
|
}
|
|
3944
3979
|
return false;
|
|
3945
3980
|
};
|
|
3981
|
+
const onImeKey = (e) => {
|
|
3982
|
+
const f = grid.store.state.focus;
|
|
3983
|
+
if (e.isComposing || e.key !== "Process" || e.ctrlKey || e.metaKey || e.altKey) return;
|
|
3984
|
+
if (!f || !grid.editor.canEdit(f.rowIndex, f.colId)) return;
|
|
3985
|
+
if (grid.columns.getColumn(f.colId).def.type === "boolean") return;
|
|
3986
|
+
grid.editor.start(f.rowIndex, f.colId, "");
|
|
3987
|
+
};
|
|
3946
3988
|
const onExpandKey = (e) => {
|
|
3947
3989
|
const f = grid.store.state.focus;
|
|
3948
3990
|
if (!f || e.ctrlKey || e.metaKey || e.altKey || e.shiftKey) return false;
|
|
@@ -4092,6 +4134,10 @@ function attachBody(grid) {
|
|
|
4092
4134
|
const cols = grid.columns.displayed();
|
|
4093
4135
|
const count = grid.rowModel.getRowCount();
|
|
4094
4136
|
if (!cols.length || !count) return;
|
|
4137
|
+
if (imeKey(e)) {
|
|
4138
|
+
onImeKey(e);
|
|
4139
|
+
return;
|
|
4140
|
+
}
|
|
4095
4141
|
if (onExpandKey(e)) {
|
|
4096
4142
|
e.preventDefault();
|
|
4097
4143
|
return;
|
|
@@ -4156,14 +4202,14 @@ function attachBody(grid) {
|
|
|
4156
4202
|
const nested = (e) => e.target?.closest?.(".fg-root") !== root;
|
|
4157
4203
|
const onCopy = (e) => {
|
|
4158
4204
|
if (fromField(e) || nested(e)) return;
|
|
4159
|
-
const text2 = grid.
|
|
4205
|
+
const text2 = grid.copyText(false);
|
|
4160
4206
|
if (!text2 || !e.clipboardData) return;
|
|
4161
4207
|
e.clipboardData.setData("text/plain", text2);
|
|
4162
4208
|
e.preventDefault();
|
|
4163
4209
|
};
|
|
4164
4210
|
const onCut = (e) => {
|
|
4165
4211
|
if (fromField(e) || nested(e)) return;
|
|
4166
|
-
const text2 = grid.
|
|
4212
|
+
const text2 = grid.copyText(false);
|
|
4167
4213
|
if (!text2 || !e.clipboardData) return;
|
|
4168
4214
|
e.clipboardData.setData("text/plain", text2);
|
|
4169
4215
|
e.preventDefault();
|
|
@@ -4530,7 +4576,9 @@ var ColumnChooserDialog = class {
|
|
|
4530
4576
|
if (!eventInside(e, popup)) this.close();
|
|
4531
4577
|
};
|
|
4532
4578
|
const onKey = (e) => {
|
|
4533
|
-
if (e.key
|
|
4579
|
+
if (e.key !== "Escape") return;
|
|
4580
|
+
this.close();
|
|
4581
|
+
this.grid.renderer.gridEl.focus();
|
|
4534
4582
|
};
|
|
4535
4583
|
document.addEventListener("pointerdown", onDown, true);
|
|
4536
4584
|
document.addEventListener("keydown", onKey, true);
|
|
@@ -4669,12 +4717,13 @@ var ContextMenu = class {
|
|
|
4669
4717
|
const node = grid.columns.getNode(colId);
|
|
4670
4718
|
const def = node?.def;
|
|
4671
4719
|
const dataCol = !!def && !isSystemCol(colId);
|
|
4720
|
+
const checked = grid.store.state.selected.size ? grid.shownSelectedRows() : [];
|
|
4672
4721
|
const items = [
|
|
4673
4722
|
{ label: loc.copy, shortcut: shortcut("C"), action: () => grid.copyToClipboard(false) },
|
|
4674
4723
|
{ label: loc.copyWithHeaders, action: () => grid.copyToClipboard(true) },
|
|
4675
4724
|
// after the copy items, so defaultItems indexes from earlier versions still point at the same entries
|
|
4676
4725
|
{ label: loc.cut, shortcut: shortcut("X"), action: () => grid.cutToClipboard() },
|
|
4677
|
-
...
|
|
4726
|
+
...checked.length ? [{ label: loc.copyRows, action: () => grid.copyToClipboard(false, true) }] : [],
|
|
4678
4727
|
{ label: loc.paste, shortcut: shortcut("V"), action: () => this.pasteFromClipboard() }
|
|
4679
4728
|
];
|
|
4680
4729
|
if (grid.history.canUndo() || grid.history.canRedo()) {
|
|
@@ -4684,8 +4733,8 @@ var ContextMenu = class {
|
|
|
4684
4733
|
{ label: loc.redo, shortcut: shortcut("Y"), action: () => grid.redo(), disabled: !grid.history.canRedo() }
|
|
4685
4734
|
);
|
|
4686
4735
|
}
|
|
4687
|
-
if (grid.options.rowSelection &&
|
|
4688
|
-
items.push({ label: loc.deleteRows, action: () => grid.
|
|
4736
|
+
if (grid.options.rowSelection && checked.length) {
|
|
4737
|
+
items.push({ label: loc.deleteRows, action: () => grid.removeRows(checked) });
|
|
4689
4738
|
}
|
|
4690
4739
|
if (dataCol) {
|
|
4691
4740
|
items.push("separator", { label: loc.autosizeThis, action: () => grid.autoSize([colId]) });
|
|
@@ -4713,7 +4762,6 @@ var ContextMenu = class {
|
|
|
4713
4762
|
{ label: loc.exportCsv, action: () => grid.api.exportCsv() },
|
|
4714
4763
|
...grid.paid ? [{ label: loc.exportExcel, action: () => grid.api.exportXlsx() }] : [],
|
|
4715
4764
|
{ label: loc.exportJson, action: () => grid.api.exportJson() },
|
|
4716
|
-
...grid.options.assistant?.url && grid.paid ? [{ label: `${loc.ai.title}\u2026`, action: () => grid.api.openAssistant() }] : [],
|
|
4717
4765
|
...grid.paid ? [{ label: loc.exportPdf, action: () => grid.api.exportPdf() }] : []
|
|
4718
4766
|
);
|
|
4719
4767
|
if (grid.options.advancedFilter !== false && !grid.options.pivot && grid.paid) items.push("separator", { label: `${loc.advancedFilter}\u2026`, action: () => grid.filterBuilder.open() });
|
|
@@ -4780,7 +4828,9 @@ var ContextMenu = class {
|
|
|
4780
4828
|
popupParent(this.grid.renderer.root).append(popup);
|
|
4781
4829
|
const w = popup.offsetWidth || 220, h = popup.offsetHeight || 300;
|
|
4782
4830
|
popup.style.left = `${Math.max(4, Math.min(x, window.innerWidth - w - 4))}px`;
|
|
4783
|
-
|
|
4831
|
+
const top = Math.max(4, Math.min(y, window.innerHeight - h - 4));
|
|
4832
|
+
popup.style.top = `${top}px`;
|
|
4833
|
+
popup.style.maxHeight = `${Math.max(120, window.innerHeight - top - 8)}px`;
|
|
4784
4834
|
this.popup = popup;
|
|
4785
4835
|
const onDown = (e) => {
|
|
4786
4836
|
if (!eventInside(e, popup)) this.close();
|
|
@@ -5061,6 +5111,7 @@ var Finder = class {
|
|
|
5061
5111
|
input.addEventListener("input", () => this.find(input.value));
|
|
5062
5112
|
bar.addEventListener("keydown", (e) => {
|
|
5063
5113
|
e.stopPropagation();
|
|
5114
|
+
if (imeKey(e)) return;
|
|
5064
5115
|
if (e.key === "Escape") {
|
|
5065
5116
|
e.preventDefault();
|
|
5066
5117
|
this.closeBar();
|
|
@@ -5070,10 +5121,28 @@ var Finder = class {
|
|
|
5070
5121
|
else this.next();
|
|
5071
5122
|
}
|
|
5072
5123
|
});
|
|
5073
|
-
this.grid.renderer
|
|
5124
|
+
const { root, headerEl, main } = this.grid.renderer;
|
|
5125
|
+
root.append(bar);
|
|
5126
|
+
const box = root.getBoundingClientRect(), head = headerEl.getBoundingClientRect(), area = main.getBoundingClientRect();
|
|
5127
|
+
if (head.height) bar.style.top = `${Math.round(head.bottom - box.top) + 6}px`;
|
|
5128
|
+
if (area.width) bar.style.right = `${Math.max(0, Math.round(box.right - area.right)) + 18}px`;
|
|
5074
5129
|
this.bar = bar;
|
|
5075
5130
|
input.focus();
|
|
5076
5131
|
}
|
|
5132
|
+
/** setLocale: the open bar's placeholder, button labels and "no matches" in the new language. */
|
|
5133
|
+
relabel() {
|
|
5134
|
+
if (!this.bar) return;
|
|
5135
|
+
const loc = this.grid.store.state.locale;
|
|
5136
|
+
const input = this.bar.querySelector("input");
|
|
5137
|
+
input.placeholder = loc.findPlaceholder;
|
|
5138
|
+
input.setAttribute("aria-label", loc.findPlaceholder);
|
|
5139
|
+
const labels = [loc.findPrevious, loc.findNext, loc.close];
|
|
5140
|
+
this.bar.querySelectorAll(".fg-findbar-btn").forEach((b, i) => {
|
|
5141
|
+
b.setAttribute("aria-label", labels[i]);
|
|
5142
|
+
b.title = labels[i];
|
|
5143
|
+
});
|
|
5144
|
+
this.updateCount();
|
|
5145
|
+
}
|
|
5077
5146
|
closeBar() {
|
|
5078
5147
|
this.bar?.remove();
|
|
5079
5148
|
this.bar = null;
|
|
@@ -5326,7 +5395,7 @@ var CellEditor = class {
|
|
|
5326
5395
|
const col = this.grid.columns.getColumn(colId);
|
|
5327
5396
|
if (!col?.def.field || rowIndex < 0 || rowIndex >= this.grid.rowModel.getRowCount()) return false;
|
|
5328
5397
|
const node = this.grid.rowModel.getRow(rowIndex);
|
|
5329
|
-
return !node.kind && cellEditable(col.def, node.data, rowIndex);
|
|
5398
|
+
return !node.kind && this.grid.cellEditable(col.def, node.data, rowIndex);
|
|
5330
5399
|
}
|
|
5331
5400
|
start(rowIndex, colId, initialText) {
|
|
5332
5401
|
if (this.cur && !this.commit(null)) return false;
|
|
@@ -5399,6 +5468,7 @@ var CellEditor = class {
|
|
|
5399
5468
|
wrap2.append(msg);
|
|
5400
5469
|
const onKey = (e) => {
|
|
5401
5470
|
e.stopPropagation();
|
|
5471
|
+
if (imeKey(e)) return;
|
|
5402
5472
|
if (e.key === "Escape") {
|
|
5403
5473
|
e.preventDefault();
|
|
5404
5474
|
this.cancel();
|
|
@@ -5552,6 +5622,7 @@ var CellEditor = class {
|
|
|
5552
5622
|
i = cols.length - 1;
|
|
5553
5623
|
if (!this.session) r--;
|
|
5554
5624
|
}
|
|
5625
|
+
while (r !== rowIndex && r >= 0 && r < count && grid.rowModel.getRow(r)?.kind) r += step;
|
|
5555
5626
|
if (r < 0 || r >= count) return;
|
|
5556
5627
|
this.start(r, cols[i].id);
|
|
5557
5628
|
}
|
|
@@ -6661,9 +6732,9 @@ function exportColumns(grid, ids) {
|
|
|
6661
6732
|
return grid.columns.displayed().filter((c) => !isSystemCol(c.id)).map((c) => ({ id: c.id, def: c.def, node: c.node, width: c.width }));
|
|
6662
6733
|
}
|
|
6663
6734
|
function exportRows(grid, mode) {
|
|
6664
|
-
if (mode === "selected") return grid.
|
|
6735
|
+
if (mode === "selected") return grid.shownSelectedRows();
|
|
6665
6736
|
if (mode === "all") return grid.rowModel.server ? grid.rowModel.dataRows().map((n) => n.data) : grid.viewRows();
|
|
6666
|
-
return grid.
|
|
6737
|
+
return grid.orderedDataRows().map((n) => n.data);
|
|
6667
6738
|
}
|
|
6668
6739
|
var header = (def, id) => def.header ?? def.field ?? id;
|
|
6669
6740
|
var ALIGNS = ["left", "center", "right"];
|
|
@@ -7118,8 +7189,10 @@ function buildXlsxSheet(grid, opts = {}) {
|
|
|
7118
7189
|
return d ? { v: d, fmt } : String(v);
|
|
7119
7190
|
}
|
|
7120
7191
|
case "boolean":
|
|
7192
|
+
if (typeof v === "number" && Number.isFinite(v)) return v;
|
|
7121
7193
|
return typeof v === "boolean" ? v : String(v);
|
|
7122
7194
|
default:
|
|
7195
|
+
if (typeof v === "number" && Number.isFinite(v)) return v;
|
|
7123
7196
|
return formatters[i](v);
|
|
7124
7197
|
}
|
|
7125
7198
|
};
|
|
@@ -7754,6 +7827,44 @@ function buildPivot(source, config, defOf, labels, locale) {
|
|
|
7754
7827
|
});
|
|
7755
7828
|
return { columns: [...rowColumns, ...valueColumns], rows, groupBy: cfg.rows.slice(0, -1) };
|
|
7756
7829
|
}
|
|
7830
|
+
var ROLLUP = /* @__PURE__ */ new WeakMap();
|
|
7831
|
+
function rollupSource(row) {
|
|
7832
|
+
return ROLLUP.get(row);
|
|
7833
|
+
}
|
|
7834
|
+
function buildRollup(source, cols, by) {
|
|
7835
|
+
const isKey = (c) => by ? by.includes(c.id) : !c.def.aggFunc;
|
|
7836
|
+
const keys = cols.filter(isKey).map((c) => c.def);
|
|
7837
|
+
const rest = cols.filter((c) => !isKey(c) && c.def.field && !c.def.valueGetter).map((c) => c.def);
|
|
7838
|
+
const values = rest.filter((d) => d.aggFunc);
|
|
7839
|
+
const others = rest.filter((d) => !d.aggFunc);
|
|
7840
|
+
const buckets = /* @__PURE__ */ new Map();
|
|
7841
|
+
for (const r of source) {
|
|
7842
|
+
const k = keys.map((d) => setKey(getCellValue(d, r))).join("");
|
|
7843
|
+
let list = buckets.get(k);
|
|
7844
|
+
if (!list) buckets.set(k, list = []);
|
|
7845
|
+
list.push(r);
|
|
7846
|
+
}
|
|
7847
|
+
const rows = [];
|
|
7848
|
+
for (const src of buckets.values()) {
|
|
7849
|
+
const row = { ...src[0] };
|
|
7850
|
+
for (const d of values) {
|
|
7851
|
+
try {
|
|
7852
|
+
row[d.field] = aggregate(d.aggFunc, src.map((r) => getCellValue(d, r)), src);
|
|
7853
|
+
} catch {
|
|
7854
|
+
row[d.field] = null;
|
|
7855
|
+
}
|
|
7856
|
+
}
|
|
7857
|
+
if (src.length > 1) {
|
|
7858
|
+
for (const d of others) {
|
|
7859
|
+
const first = setKey(getCellValue(d, src[0]));
|
|
7860
|
+
if (src.some((r) => setKey(getCellValue(d, r)) !== first)) row[d.field] = null;
|
|
7861
|
+
}
|
|
7862
|
+
}
|
|
7863
|
+
ROLLUP.set(row, src);
|
|
7864
|
+
rows.push(row);
|
|
7865
|
+
}
|
|
7866
|
+
return rows;
|
|
7867
|
+
}
|
|
7757
7868
|
|
|
7758
7869
|
// src/features.ts
|
|
7759
7870
|
var PAID_FEATURES = {
|
|
@@ -7773,13 +7884,12 @@ var PAID_FEATURES = {
|
|
|
7773
7884
|
formulas: { en: "Formulas", ko: "\uC218\uC2DD" },
|
|
7774
7885
|
find: { en: "Find", ko: "\uCC3E\uAE30" },
|
|
7775
7886
|
conditional: { en: "Conditional formatting", ko: "\uC870\uAC74\uBD80 \uC11C\uC2DD" },
|
|
7776
|
-
assistant: { en: "AI assistant", ko: "AI \uC5B4\uC2DC\uC2A4\uD134\uD2B8" },
|
|
7777
7887
|
merge: { en: "Merged cells", ko: "\uC140 \uBCD1\uD569" },
|
|
7778
7888
|
workbook: { en: "Workbook", ko: "\uC6CC\uD06C\uBD81" }
|
|
7779
7889
|
};
|
|
7780
7890
|
|
|
7781
7891
|
// src/version.ts
|
|
7782
|
-
var version = "1.
|
|
7892
|
+
var version = "1.11.0";
|
|
7783
7893
|
|
|
7784
7894
|
// src/license.ts
|
|
7785
7895
|
var PUBLIC_KEY = { kty: "EC", crv: "P-256", x: "sUTKkglZmk-HMKPNub8p7AeLBKesHCU8irIkw9rJlbE", y: "j0U_aKRDR7iApY9UpaD3hUemhBLZJwDv9-QiT_hUyw8" };
|
|
@@ -8078,6 +8188,12 @@ var ColumnMenu = class {
|
|
|
8078
8188
|
if (grouped.length) g.push([loc.expandAll, () => this.grid.expandAll(true)], [loc.collapseAll, () => this.grid.expandAll(false)]);
|
|
8079
8189
|
popup.append(this.section(g));
|
|
8080
8190
|
}
|
|
8191
|
+
if (this.grid.getRollup() && !this.grid.isRollupKey(colId)) {
|
|
8192
|
+
const cur = def.aggFunc ?? null;
|
|
8193
|
+
const mark = (on, label) => `${on ? "\u2713 " : ""}${label}`;
|
|
8194
|
+
const aggs = ["sum", "avg", "min", "max", "count"].map((a) => [mark(cur === a, loc[a]), () => this.grid.setColumnAggFunc(colId, a)]);
|
|
8195
|
+
popup.append(this.section([[mark(!cur, loc.noAggregate), () => this.grid.setColumnAggFunc(colId, null)], ...aggs]));
|
|
8196
|
+
}
|
|
8081
8197
|
const items = [
|
|
8082
8198
|
[loc.autosizeThis, () => this.grid.autoSize([colId])],
|
|
8083
8199
|
[loc.autosizeAll, () => this.grid.autoSize()]
|
|
@@ -8105,14 +8221,30 @@ var ColumnMenu = class {
|
|
|
8105
8221
|
this.close();
|
|
8106
8222
|
this.grid.renderer.gridEl.focus();
|
|
8107
8223
|
};
|
|
8224
|
+
const onScroll = () => this.close();
|
|
8225
|
+
const scroller = this.grid.renderer.scroller;
|
|
8108
8226
|
document.addEventListener("pointerdown", onDown, true);
|
|
8109
8227
|
document.addEventListener("keydown", onKey, true);
|
|
8228
|
+
scroller.addEventListener("scroll", onScroll, { passive: true });
|
|
8110
8229
|
this.off.push(
|
|
8111
8230
|
() => document.removeEventListener("pointerdown", onDown, true),
|
|
8112
|
-
() => document.removeEventListener("keydown", onKey, true)
|
|
8231
|
+
() => document.removeEventListener("keydown", onKey, true),
|
|
8232
|
+
() => scroller.removeEventListener("scroll", onScroll)
|
|
8113
8233
|
);
|
|
8114
8234
|
popup.querySelector(".fg-filter-value:not([hidden]), .fg-set-search, .fg-menu-item")?.focus();
|
|
8115
8235
|
}
|
|
8236
|
+
/** Keyboard (Alt+ArrowDown): the menu of the focused cell's column (else the first column), under its header. */
|
|
8237
|
+
openAtFocus() {
|
|
8238
|
+
const { grid } = this;
|
|
8239
|
+
const f = grid.store.state.focus;
|
|
8240
|
+
const colId = f && !isSystemCol(f.colId) ? f.colId : grid.columns.displayed().find((c) => !isSystemCol(c.id))?.id;
|
|
8241
|
+
if (!colId) return false;
|
|
8242
|
+
const root = grid.renderer.root;
|
|
8243
|
+
const id = CSS.escape(colId);
|
|
8244
|
+
const anchor = root.querySelector(`.fg-header-cell:not(.fg-header-group)[data-col="${id}"]`) ?? (f ? root.querySelector(`.fg-row[data-row="${f.rowIndex}"] .fg-cell[data-col="${id}"]`) : null) ?? grid.renderer.gridEl;
|
|
8245
|
+
this.open(colId, anchor);
|
|
8246
|
+
return true;
|
|
8247
|
+
}
|
|
8116
8248
|
/** The filter UI of a column (used by the menu and the filters panel); `done` runs after apply/reset. */
|
|
8117
8249
|
filterSection(colId, done) {
|
|
8118
8250
|
const node = this.grid.columns.getNode(colId);
|
|
@@ -8137,6 +8269,8 @@ var ColumnMenu = class {
|
|
|
8137
8269
|
b.addEventListener("click", () => {
|
|
8138
8270
|
run2();
|
|
8139
8271
|
this.close();
|
|
8272
|
+
const a = activeElement(this.grid.renderer.root);
|
|
8273
|
+
if (!a || a === document.body || !a.isConnected) this.grid.renderer.gridEl.focus({ preventScroll: true });
|
|
8140
8274
|
});
|
|
8141
8275
|
s.append(b);
|
|
8142
8276
|
}
|
|
@@ -8191,7 +8325,7 @@ var ColumnMenu = class {
|
|
|
8191
8325
|
};
|
|
8192
8326
|
select.addEventListener("change", sync);
|
|
8193
8327
|
sync();
|
|
8194
|
-
for (const input of [v1, v2]) input.addEventListener("keydown", (e) => e.key === "Enter" && apply());
|
|
8328
|
+
for (const input of [v1, v2]) input.addEventListener("keydown", (e) => e.key === "Enter" && !imeKey(e) && apply());
|
|
8195
8329
|
row.append(select, v1, v2);
|
|
8196
8330
|
const read = () => {
|
|
8197
8331
|
const op = select.value;
|
|
@@ -8303,7 +8437,7 @@ var ColumnMenu = class {
|
|
|
8303
8437
|
grid.setFilter(colId, checked.size === distinct.size ? null : { type: "set", values: entries.filter(([k]) => checked.has(k)).map(([, v]) => v) });
|
|
8304
8438
|
done();
|
|
8305
8439
|
};
|
|
8306
|
-
search.addEventListener("keydown", (e) => e.key === "Enter" && apply());
|
|
8440
|
+
search.addEventListener("keydown", (e) => e.key === "Enter" && !imeKey(e) && apply());
|
|
8307
8441
|
draw();
|
|
8308
8442
|
box.append(this.title(loc), search, allLabel, list, this.actions(colId, loc, apply, done));
|
|
8309
8443
|
return box;
|
|
@@ -8333,6 +8467,7 @@ var RowModel = class {
|
|
|
8333
8467
|
this.sorted = [];
|
|
8334
8468
|
this.display = [];
|
|
8335
8469
|
this.indexById = null;
|
|
8470
|
+
this.orderedCache = null;
|
|
8336
8471
|
this.layoutCache = null;
|
|
8337
8472
|
/** Searchable text of each row, kept between quick filter keystrokes. */
|
|
8338
8473
|
this.quickCache = null;
|
|
@@ -8391,6 +8526,13 @@ var RowModel = class {
|
|
|
8391
8526
|
fullCount() {
|
|
8392
8527
|
return this.full.length;
|
|
8393
8528
|
}
|
|
8529
|
+
dataRowsBefore(index) {
|
|
8530
|
+
const end = Math.min(index, this.full.length);
|
|
8531
|
+
if (this.full === this.sorted) return end;
|
|
8532
|
+
let n = 0;
|
|
8533
|
+
for (let k = 0; k < end; k++) if (!this.full[k].kind) n++;
|
|
8534
|
+
return n;
|
|
8535
|
+
}
|
|
8394
8536
|
dataCount() {
|
|
8395
8537
|
return this.sorted.length;
|
|
8396
8538
|
}
|
|
@@ -8411,6 +8553,19 @@ var RowModel = class {
|
|
|
8411
8553
|
dataRows() {
|
|
8412
8554
|
return this.sorted;
|
|
8413
8555
|
}
|
|
8556
|
+
/** Filtered data rows in display order, as if every group and tree row were expanded. */
|
|
8557
|
+
orderedDataRows() {
|
|
8558
|
+
const c = this.orderedCache;
|
|
8559
|
+
if (c && c.full === this.full) return c.rows;
|
|
8560
|
+
const d = this.last.display;
|
|
8561
|
+
let rows = this.sorted;
|
|
8562
|
+
if (d && (d.getParentId || d.groupBy.length)) {
|
|
8563
|
+
const open = { ...d, expanded: /* @__PURE__ */ new Map(), defaultExpanded: -1, detailOpen: /* @__PURE__ */ new Set(), lazyChildren: void 0 };
|
|
8564
|
+
rows = this.buildDisplay(open, this.last.sort ?? [], this.last.collator ?? this.deps.collator()).filter((n) => !n.kind);
|
|
8565
|
+
}
|
|
8566
|
+
this.orderedCache = { full: this.full, rows };
|
|
8567
|
+
return rows;
|
|
8568
|
+
}
|
|
8414
8569
|
allNodes() {
|
|
8415
8570
|
return this.nodes;
|
|
8416
8571
|
}
|
|
@@ -8813,6 +8968,7 @@ var ServerRowModel = class {
|
|
|
8813
8968
|
/** Server-side grouping, while groupBy has columns. */
|
|
8814
8969
|
this.groups = null;
|
|
8815
8970
|
this.displayIn = null;
|
|
8971
|
+
this.quiet = false;
|
|
8816
8972
|
}
|
|
8817
8973
|
get bs() {
|
|
8818
8974
|
return this.page ? this.page.size : this.deps.blockSize;
|
|
@@ -8911,9 +9067,19 @@ var ServerRowModel = class {
|
|
|
8911
9067
|
const abs = this.base + index;
|
|
8912
9068
|
const hit = this.peek(abs);
|
|
8913
9069
|
if (hit) return hit;
|
|
8914
|
-
if (index >= 0 && index < this.getRowCount()) this.ensure(Math.floor(abs / this.bs));
|
|
9070
|
+
if (!this.quiet && index >= 0 && index < this.getRowCount()) this.ensure(Math.floor(abs / this.bs));
|
|
8915
9071
|
return this.placeholder(abs);
|
|
8916
9072
|
}
|
|
9073
|
+
/** Run `fn` with getRow answering from loaded blocks only (rows not loaded stay placeholders, nothing is requested). */
|
|
9074
|
+
withoutLoading(fn) {
|
|
9075
|
+
const was = this.quiet;
|
|
9076
|
+
this.quiet = true;
|
|
9077
|
+
try {
|
|
9078
|
+
return fn();
|
|
9079
|
+
} finally {
|
|
9080
|
+
this.quiet = was;
|
|
9081
|
+
}
|
|
9082
|
+
}
|
|
8917
9083
|
ensure(k) {
|
|
8918
9084
|
const cur = this.blocks.get(k);
|
|
8919
9085
|
if (cur && (cur.state !== "failed" || Date.now() < (cur.retryAt ?? 0))) return;
|
|
@@ -9335,17 +9501,23 @@ var FilterBuilder = class {
|
|
|
9335
9501
|
actions.append(reset, ok);
|
|
9336
9502
|
popup.append(head, join, list, addBtn, actions);
|
|
9337
9503
|
popupParent(grid.renderer.root).append(popup);
|
|
9504
|
+
if (!rows.length) addBtn.click();
|
|
9338
9505
|
const r = grid.renderer.root.getBoundingClientRect();
|
|
9339
9506
|
const w = popup.offsetWidth || 460;
|
|
9340
9507
|
popup.style.left = `${Math.max(4, Math.min(r.left + 24, (window.innerWidth || 1024) - w - 4))}px`;
|
|
9341
|
-
|
|
9508
|
+
const header2 = grid.renderer.headerEl.getBoundingClientRect();
|
|
9509
|
+
const vh = window.innerHeight || 768;
|
|
9510
|
+
const top = Math.max(4, Math.min((header2.height ? header2.bottom : r.top) + 8, vh - (popup.offsetHeight || 0) - 4));
|
|
9511
|
+
popup.style.top = `${top}px`;
|
|
9512
|
+
popup.style.maxHeight = `${Math.max(160, vh - top - 8)}px`;
|
|
9342
9513
|
this.popup = popup;
|
|
9343
|
-
if (!rows.length) addBtn.click();
|
|
9344
9514
|
const onDown = (e) => {
|
|
9345
9515
|
if (!eventInside(e, popup)) this.close();
|
|
9346
9516
|
};
|
|
9347
9517
|
const onKey = (e) => {
|
|
9348
|
-
if (e.key
|
|
9518
|
+
if (e.key !== "Escape") return;
|
|
9519
|
+
this.close();
|
|
9520
|
+
grid.renderer.gridEl.focus();
|
|
9349
9521
|
};
|
|
9350
9522
|
document.addEventListener("pointerdown", onDown, true);
|
|
9351
9523
|
document.addEventListener("keydown", onKey, true);
|
|
@@ -9483,8 +9655,8 @@ var FormatDialog = class {
|
|
|
9483
9655
|
CF_PRESETS.forEach((p, k) => {
|
|
9484
9656
|
const b = el("button", "fg-cf-swatch", { type: "button", "aria-label": cf.styles[p.key], title: cf.styles[p.key], "aria-pressed": String(k === preset) });
|
|
9485
9657
|
b.textContent = p.key === "bold" ? "B" : "Aa";
|
|
9486
|
-
|
|
9487
|
-
|
|
9658
|
+
b.style.background = p.style.background ?? "#ffffff";
|
|
9659
|
+
b.style.color = p.style.color ?? "#1f2430";
|
|
9488
9660
|
b.addEventListener("click", () => {
|
|
9489
9661
|
preset = k;
|
|
9490
9662
|
for (const s of swatches.children) s.setAttribute("aria-pressed", String(s === b));
|
|
@@ -9583,348 +9755,9 @@ var FormatDialog = class {
|
|
|
9583
9755
|
if (!eventInside(e, popup)) this.close();
|
|
9584
9756
|
};
|
|
9585
9757
|
const onKey = (e) => {
|
|
9586
|
-
if (e.key
|
|
9587
|
-
|
|
9588
|
-
|
|
9589
|
-
document.addEventListener("keydown", onKey, true);
|
|
9590
|
-
this.off.push(
|
|
9591
|
-
() => document.removeEventListener("pointerdown", onDown, true),
|
|
9592
|
-
() => document.removeEventListener("keydown", onKey, true)
|
|
9593
|
-
);
|
|
9594
|
-
}
|
|
9595
|
-
close() {
|
|
9596
|
-
for (const f of this.off) f();
|
|
9597
|
-
this.off = [];
|
|
9598
|
-
this.popup?.remove();
|
|
9599
|
-
this.popup = null;
|
|
9600
|
-
}
|
|
9601
|
-
};
|
|
9602
|
-
|
|
9603
|
-
// src/assistant.ts
|
|
9604
|
-
var FILTER_TYPES = ["text", "number", "date", "set", "combined"];
|
|
9605
|
-
var CHART_TYPES = ["bar", "stackedBar", "line", "area", "pie", "donut", "combo", "scatter"];
|
|
9606
|
-
var MAX_ACTIONS = 30;
|
|
9607
|
-
function describeColumn(grid, id, def, rows) {
|
|
9608
|
-
const col = { id, header: def.header ?? def.field ?? id };
|
|
9609
|
-
if (def.type) col.type = def.type;
|
|
9610
|
-
if (def.formula) col.formula = def.formula;
|
|
9611
|
-
if (def.aggFunc && typeof def.aggFunc === "string") col.aggFunc = def.aggFunc;
|
|
9612
|
-
let blanks = 0;
|
|
9613
|
-
const counts = /* @__PURE__ */ new Map();
|
|
9614
|
-
let min = Infinity, max = -Infinity, sum = 0, nums = 0;
|
|
9615
|
-
let minText = "", maxText = "";
|
|
9616
|
-
for (const r of rows) {
|
|
9617
|
-
const v = getCellValue(def, r);
|
|
9618
|
-
if (isBlank(v)) {
|
|
9619
|
-
blanks++;
|
|
9620
|
-
continue;
|
|
9621
|
-
}
|
|
9622
|
-
if (typeof v === "number" && Number.isFinite(v)) {
|
|
9623
|
-
nums++;
|
|
9624
|
-
sum += v;
|
|
9625
|
-
if (v < min) min = v;
|
|
9626
|
-
if (v > max) max = v;
|
|
9627
|
-
} else if (def.type === "date") {
|
|
9628
|
-
const t = v instanceof Date ? v.toISOString().slice(0, 10) : String(v);
|
|
9629
|
-
if (!minText || t < minText) minText = t;
|
|
9630
|
-
if (!maxText || t > maxText) maxText = t;
|
|
9631
|
-
}
|
|
9632
|
-
if (counts.size < 5e3) {
|
|
9633
|
-
const k = setKey(v);
|
|
9634
|
-
const c = counts.get(k);
|
|
9635
|
-
if (c) c.n++;
|
|
9636
|
-
else counts.set(k, { value: v, n: 1 });
|
|
9637
|
-
}
|
|
9638
|
-
}
|
|
9639
|
-
col.blanks = blanks;
|
|
9640
|
-
col.distinct = counts.size;
|
|
9641
|
-
if (nums) Object.assign(col, { min, max, avg: Math.round(sum / nums * 1e3) / 1e3 });
|
|
9642
|
-
if (minText) Object.assign(col, { min: minText, max: maxText });
|
|
9643
|
-
if (!nums && counts.size) {
|
|
9644
|
-
col.top = [...counts.values()].sort((a, b) => b.n - a.n).slice(0, 12).map((c) => ({ value: String(c.value).slice(0, 80), count: c.n }));
|
|
9645
|
-
}
|
|
9646
|
-
return col;
|
|
9647
|
-
}
|
|
9648
|
-
function assistantRequest(grid, prompt, options) {
|
|
9649
|
-
const rows = grid.rowModel.dataRows().map((n) => n.data);
|
|
9650
|
-
const all2 = grid.columns.getState().map((s2) => grid.columns.getNode(s2.colId)).filter((n) => n && !n.children && !isSystemCol(n.id));
|
|
9651
|
-
const columns = all2.map((n) => ({ ...describeColumn(grid, n.id, n.def, rows), hidden: grid.columns.getState().find((s2) => s2.colId === n.id)?.hide || void 0 }));
|
|
9652
|
-
const sampleN = typeof options.sampleRows === "number" && options.sampleRows >= 0 ? Math.min(50, options.sampleRows) : 5;
|
|
9653
|
-
const sample = rows.slice(0, sampleN).map((r) => Object.fromEntries(all2.map((n) => [n.id, getCellValue(n.def, r)])));
|
|
9654
|
-
const s = grid.store.state;
|
|
9655
|
-
return {
|
|
9656
|
-
prompt: String(prompt).slice(0, 2e3),
|
|
9657
|
-
locale: s.locale.code,
|
|
9658
|
-
rowCount: rows.length,
|
|
9659
|
-
totalRows: grid.rowModel.server ? rows.length : grid.store.state.data.length,
|
|
9660
|
-
columns,
|
|
9661
|
-
sample,
|
|
9662
|
-
state: {
|
|
9663
|
-
sort: s.sort,
|
|
9664
|
-
filters: s.filters,
|
|
9665
|
-
quickFilter: s.quickFilter,
|
|
9666
|
-
advancedFilter: s.advancedFilter,
|
|
9667
|
-
groupBy: grid.getGroupBy(),
|
|
9668
|
-
pivot: grid.getPivot(),
|
|
9669
|
-
conditionalFormats: grid.getConditionalFormats()
|
|
9670
|
-
},
|
|
9671
|
-
...options.context ? { context: options.context() } : {}
|
|
9672
|
-
};
|
|
9673
|
-
}
|
|
9674
|
-
function applyAssistantActions(grid, actions) {
|
|
9675
|
-
const before = { state: grid.api.getState(), defs: grid.columnDefs(), summary: grid.summaryOptions() };
|
|
9676
|
-
const applied = [];
|
|
9677
|
-
const skipped = [];
|
|
9678
|
-
const charts = [];
|
|
9679
|
-
const list = Array.isArray(actions) ? actions.slice(0, MAX_ACTIONS) : [];
|
|
9680
|
-
const known = (id) => typeof id === "string" && !!grid.columns.getNode(id) && !grid.columns.getNode(id).children && !isSystemCol(id);
|
|
9681
|
-
const ok = (a) => applied.push(a);
|
|
9682
|
-
const skip = (a, reason) => skipped.push({ action: a, reason });
|
|
9683
|
-
for (const raw of list) {
|
|
9684
|
-
const a = raw;
|
|
9685
|
-
if (!a || typeof a !== "object" || typeof a.type !== "string") {
|
|
9686
|
-
skip(raw, "not an action");
|
|
9687
|
-
continue;
|
|
9688
|
-
}
|
|
9689
|
-
try {
|
|
9690
|
-
switch (a.type) {
|
|
9691
|
-
case "sort": {
|
|
9692
|
-
const sort = (Array.isArray(a.sort) ? a.sort : []).filter((x) => !!x && known(x.colId) && (x.dir === "asc" || x.dir === "desc"));
|
|
9693
|
-
if (Array.isArray(a.sort) && sort.length !== a.sort.length) skip(a, "unknown column in sort");
|
|
9694
|
-
grid.setSort(sort.map((x) => ({ colId: x.colId, dir: x.dir })));
|
|
9695
|
-
ok(a);
|
|
9696
|
-
break;
|
|
9697
|
-
}
|
|
9698
|
-
case "filter": {
|
|
9699
|
-
if (!known(a.colId)) {
|
|
9700
|
-
skip(a, "unknown column");
|
|
9701
|
-
break;
|
|
9702
|
-
}
|
|
9703
|
-
const m = a.model;
|
|
9704
|
-
if (m !== null && (!m || typeof m !== "object" || !FILTER_TYPES.includes(m.type))) {
|
|
9705
|
-
skip(a, "bad filter model");
|
|
9706
|
-
break;
|
|
9707
|
-
}
|
|
9708
|
-
grid.setFilter(a.colId, m);
|
|
9709
|
-
ok(a);
|
|
9710
|
-
break;
|
|
9711
|
-
}
|
|
9712
|
-
case "clearFilters":
|
|
9713
|
-
for (const id of Object.keys(grid.store.state.filters)) grid.setFilter(id, null);
|
|
9714
|
-
grid.setAdvancedFilter(null);
|
|
9715
|
-
grid.api.setQuickFilter("");
|
|
9716
|
-
ok(a);
|
|
9717
|
-
break;
|
|
9718
|
-
case "quickFilter":
|
|
9719
|
-
grid.api.setQuickFilter(typeof a.text === "string" ? a.text.slice(0, 200) : "");
|
|
9720
|
-
ok(a);
|
|
9721
|
-
break;
|
|
9722
|
-
case "advancedFilter": {
|
|
9723
|
-
const f = a.filter;
|
|
9724
|
-
const ids = [];
|
|
9725
|
-
const walk = (x) => {
|
|
9726
|
-
const o = x;
|
|
9727
|
-
if (Array.isArray(o?.conditions)) o.conditions.forEach(walk);
|
|
9728
|
-
else ids.push(o?.colId);
|
|
9729
|
-
};
|
|
9730
|
-
if (f) walk(f);
|
|
9731
|
-
if (f && (!Array.isArray(f.conditions) || !ids.every(known))) {
|
|
9732
|
-
skip(a, "unknown column in advanced filter");
|
|
9733
|
-
break;
|
|
9734
|
-
}
|
|
9735
|
-
grid.setAdvancedFilter(f ?? null);
|
|
9736
|
-
ok(a);
|
|
9737
|
-
break;
|
|
9738
|
-
}
|
|
9739
|
-
case "groupBy": {
|
|
9740
|
-
const ids = Array.isArray(a.colIds) ? a.colIds.filter(known) : [];
|
|
9741
|
-
grid.setGroupBy(ids);
|
|
9742
|
-
if (a.expand) grid.expandAll(true);
|
|
9743
|
-
ok(a);
|
|
9744
|
-
break;
|
|
9745
|
-
}
|
|
9746
|
-
case "pivot": {
|
|
9747
|
-
const p = a.pivot;
|
|
9748
|
-
if (p && (!Array.isArray(p.rows) || !Array.isArray(p.values) || !p.rows.every(known) || !(p.columns ?? []).every(known) || !p.values.every((v) => v && known(v.colId)))) {
|
|
9749
|
-
skip(a, "unknown column in pivot");
|
|
9750
|
-
break;
|
|
9751
|
-
}
|
|
9752
|
-
grid.setPivot(p ?? null);
|
|
9753
|
-
ok(a);
|
|
9754
|
-
break;
|
|
9755
|
-
}
|
|
9756
|
-
case "showColumns":
|
|
9757
|
-
case "hideColumns": {
|
|
9758
|
-
const ids = Array.isArray(a.colIds) ? a.colIds.filter(known) : [];
|
|
9759
|
-
if (ids.length) grid.setColumnsVisible(ids, a.type === "showColumns");
|
|
9760
|
-
ok(a);
|
|
9761
|
-
break;
|
|
9762
|
-
}
|
|
9763
|
-
case "conditionalFormat": {
|
|
9764
|
-
const f = a.format;
|
|
9765
|
-
if (!f || !Array.isArray(f.columns) || !f.columns.length || !f.columns.every(known)) {
|
|
9766
|
-
skip(a, "unknown column in format");
|
|
9767
|
-
break;
|
|
9768
|
-
}
|
|
9769
|
-
grid.setConditionalFormats([...grid.getConditionalFormats(), f]);
|
|
9770
|
-
ok(a);
|
|
9771
|
-
break;
|
|
9772
|
-
}
|
|
9773
|
-
case "clearConditionalFormats":
|
|
9774
|
-
grid.setConditionalFormats([]);
|
|
9775
|
-
ok(a);
|
|
9776
|
-
break;
|
|
9777
|
-
case "addColumn": {
|
|
9778
|
-
const c = a.column;
|
|
9779
|
-
if (!c || typeof c.id !== "string" || !/^[\w.-]{1,40}$/.test(c.id) || grid.columns.getNode(c.id)) {
|
|
9780
|
-
skip(a, "bad or taken column id");
|
|
9781
|
-
break;
|
|
9782
|
-
}
|
|
9783
|
-
const f = typeof c.formula === "string" ? c.formula.startsWith("=") ? c.formula : `=${c.formula}` : "";
|
|
9784
|
-
const refs = f ? formulaRefs(f) : null;
|
|
9785
|
-
if (!refs) {
|
|
9786
|
-
skip(a, "formula does not parse");
|
|
9787
|
-
break;
|
|
9788
|
-
}
|
|
9789
|
-
const def = { id: c.id, header: typeof c.header === "string" ? c.header.slice(0, 80) : c.id, formula: f };
|
|
9790
|
-
if (c.type === "number" || c.type === "date" || c.type === "text") def.type = c.type;
|
|
9791
|
-
grid.api.setColumns([...grid.columnDefs(), def]);
|
|
9792
|
-
ok(a);
|
|
9793
|
-
break;
|
|
9794
|
-
}
|
|
9795
|
-
case "summary": {
|
|
9796
|
-
const s = a.summary;
|
|
9797
|
-
if (s && (!Array.isArray(s.rows) || !s.rows.every((r) => r && Object.keys(r.columns ?? {}).every(known)))) {
|
|
9798
|
-
skip(a, "unknown column in summary");
|
|
9799
|
-
break;
|
|
9800
|
-
}
|
|
9801
|
-
grid.setSummary(s ?? null);
|
|
9802
|
-
ok(a);
|
|
9803
|
-
break;
|
|
9804
|
-
}
|
|
9805
|
-
case "chart": {
|
|
9806
|
-
const ids = Array.isArray(a.colIds) ? a.colIds.filter(known) : [];
|
|
9807
|
-
const displayed = grid.columns.displayed().map((c) => c.id);
|
|
9808
|
-
const idx = ids.map((id) => displayed.indexOf(id)).filter((i) => i >= 0);
|
|
9809
|
-
const count = grid.rowModel.getRowCount();
|
|
9810
|
-
if (!idx.length || !count) {
|
|
9811
|
-
skip(a, "nothing to chart");
|
|
9812
|
-
break;
|
|
9813
|
-
}
|
|
9814
|
-
const range = { start: { rowIndex: 0, colId: displayed[Math.min(...idx)] }, end: { rowIndex: Math.min(count, 200) - 1, colId: displayed[Math.max(...idx)] } };
|
|
9815
|
-
charts.push(grid.api.createChart({ type: CHART_TYPES.includes(a.chartType) ? a.chartType : "bar", range, title: typeof a.title === "string" ? a.title.slice(0, 80) : void 0 }));
|
|
9816
|
-
ok(a);
|
|
9817
|
-
break;
|
|
9818
|
-
}
|
|
9819
|
-
case "message":
|
|
9820
|
-
ok(a);
|
|
9821
|
-
break;
|
|
9822
|
-
default:
|
|
9823
|
-
skip(a, "unknown action");
|
|
9824
|
-
}
|
|
9825
|
-
} catch {
|
|
9826
|
-
skip(a, "failed");
|
|
9827
|
-
}
|
|
9828
|
-
}
|
|
9829
|
-
const undo = () => {
|
|
9830
|
-
for (const c of charts) c.destroy();
|
|
9831
|
-
if (grid.columnDefs().length !== before.defs.length || grid.columnDefs().some((d, i) => d !== before.defs[i])) grid.api.setColumns(before.defs);
|
|
9832
|
-
grid.setSummary(before.summary);
|
|
9833
|
-
grid.api.setState(before.state);
|
|
9834
|
-
if (!before.state.conditionalFormats) grid.setConditionalFormats([]);
|
|
9835
|
-
};
|
|
9836
|
-
return { applied, skipped, undo };
|
|
9837
|
-
}
|
|
9838
|
-
async function askAssistant(grid, prompt, options, signal) {
|
|
9839
|
-
if (!options?.url) throw new Error("assistant.url is not set");
|
|
9840
|
-
const headers = typeof options.headers === "function" ? options.headers() : options.headers ?? {};
|
|
9841
|
-
const res = await fetch(options.url, {
|
|
9842
|
-
method: "POST",
|
|
9843
|
-
credentials: options.credentials ?? "same-origin",
|
|
9844
|
-
headers: { "Content-Type": "application/json", Accept: "application/json", ...headers },
|
|
9845
|
-
body: JSON.stringify(assistantRequest(grid, prompt, options)),
|
|
9846
|
-
signal
|
|
9847
|
-
});
|
|
9848
|
-
if (!res.ok) throw new Error(`HTTP ${res.status} from the assistant`);
|
|
9849
|
-
const json = await res.json();
|
|
9850
|
-
const result = applyAssistantActions(grid, Array.isArray(json?.actions) ? json.actions : []);
|
|
9851
|
-
const say = typeof json?.message === "string" ? json.message : result.applied.filter((a) => a.type === "message").map((a) => a.text ?? "").join("\n");
|
|
9852
|
-
return { ...result, message: say.slice(0, 4e3) };
|
|
9853
|
-
}
|
|
9854
|
-
|
|
9855
|
-
// src/interactions/assistantPanel.ts
|
|
9856
|
-
var AssistantPanel = class {
|
|
9857
|
-
constructor(grid) {
|
|
9858
|
-
this.grid = grid;
|
|
9859
|
-
this.popup = null;
|
|
9860
|
-
this.log = null;
|
|
9861
|
-
this.off = [];
|
|
9862
|
-
this.busy = null;
|
|
9863
|
-
}
|
|
9864
|
-
get isOpen() {
|
|
9865
|
-
return !!this.popup;
|
|
9866
|
-
}
|
|
9867
|
-
open() {
|
|
9868
|
-
if (this.popup) {
|
|
9869
|
-
this.popup.querySelector(".fg-ai-input")?.focus();
|
|
9870
|
-
return;
|
|
9871
|
-
}
|
|
9872
|
-
const { grid } = this;
|
|
9873
|
-
const loc = grid.store.state.locale;
|
|
9874
|
-
const opts = grid.options.assistant;
|
|
9875
|
-
const popup = el("div", `fg-menu fg-ai-panel fg-theme-${grid.store.state.theme}`, { role: "dialog", "aria-label": loc.ai.title });
|
|
9876
|
-
grid.applyFontTo(popup);
|
|
9877
|
-
const head = el("div", "fg-chooser-head");
|
|
9878
|
-
const title = el("span", "fg-menu-title");
|
|
9879
|
-
title.textContent = loc.ai.title;
|
|
9880
|
-
const x = el("button", "fg-chooser-close", { type: "button", "aria-label": loc.close });
|
|
9881
|
-
x.textContent = "\xD7";
|
|
9882
|
-
x.addEventListener("click", () => this.close());
|
|
9883
|
-
head.append(title, x);
|
|
9884
|
-
const log = el("div", "fg-ai-log", { role: "log", "aria-live": "polite" });
|
|
9885
|
-
this.log = log;
|
|
9886
|
-
const form = el("form", "fg-ai-form");
|
|
9887
|
-
const input = el("textarea", "fg-input fg-ai-input", { rows: "2", placeholder: loc.ai.placeholder, "aria-label": loc.ai.placeholder, maxlength: "2000" });
|
|
9888
|
-
const send2 = el("button", "fg-btn fg-btn-primary fg-ai-send", { type: "submit" });
|
|
9889
|
-
send2.textContent = loc.ai.send;
|
|
9890
|
-
form.append(input, send2);
|
|
9891
|
-
const tips = el("div", "fg-ai-tips");
|
|
9892
|
-
for (const s of (opts?.suggestions ?? []).slice(0, 6)) {
|
|
9893
|
-
const b = el("button", "fg-ai-tip", { type: "button" });
|
|
9894
|
-
b.textContent = s;
|
|
9895
|
-
b.addEventListener("click", () => {
|
|
9896
|
-
input.value = s;
|
|
9897
|
-
input.focus();
|
|
9898
|
-
});
|
|
9899
|
-
tips.append(b);
|
|
9900
|
-
}
|
|
9901
|
-
input.addEventListener("keydown", (e) => {
|
|
9902
|
-
if (e.key === "Enter" && !e.shiftKey && !e.isComposing) {
|
|
9903
|
-
e.preventDefault();
|
|
9904
|
-
form.requestSubmit?.() ?? form.dispatchEvent(new Event("submit", { cancelable: true }));
|
|
9905
|
-
}
|
|
9906
|
-
});
|
|
9907
|
-
form.addEventListener("submit", (e) => {
|
|
9908
|
-
e.preventDefault();
|
|
9909
|
-
const prompt = input.value.trim();
|
|
9910
|
-
if (!prompt || this.busy) return;
|
|
9911
|
-
input.value = "";
|
|
9912
|
-
void this.ask(prompt, send2);
|
|
9913
|
-
});
|
|
9914
|
-
popup.append(head, log, form);
|
|
9915
|
-
if (tips.childElementCount) popup.append(tips);
|
|
9916
|
-
popupParent(grid.renderer.root).append(popup);
|
|
9917
|
-
const r = grid.renderer.root.getBoundingClientRect();
|
|
9918
|
-
const w = popup.offsetWidth || 360;
|
|
9919
|
-
popup.style.left = `${Math.max(4, Math.min(r.right - w - 16, (window.innerWidth || 1024) - w - 4))}px`;
|
|
9920
|
-
popup.style.top = `${Math.max(4, r.top + 16)}px`;
|
|
9921
|
-
this.popup = popup;
|
|
9922
|
-
input.focus();
|
|
9923
|
-
const onDown = (e) => {
|
|
9924
|
-
if (!eventInside(e, popup) && !this.busy) this.close();
|
|
9925
|
-
};
|
|
9926
|
-
const onKey = (e) => {
|
|
9927
|
-
if (e.key === "Escape") this.close();
|
|
9758
|
+
if (e.key !== "Escape") return;
|
|
9759
|
+
this.close();
|
|
9760
|
+
grid.renderer.gridEl.focus();
|
|
9928
9761
|
};
|
|
9929
9762
|
document.addEventListener("pointerdown", onDown, true);
|
|
9930
9763
|
document.addEventListener("keydown", onKey, true);
|
|
@@ -9933,58 +9766,11 @@ var AssistantPanel = class {
|
|
|
9933
9766
|
() => document.removeEventListener("keydown", onKey, true)
|
|
9934
9767
|
);
|
|
9935
9768
|
}
|
|
9936
|
-
say(who, text2) {
|
|
9937
|
-
const msg = el("div", `fg-ai-msg fg-ai-${who}`);
|
|
9938
|
-
msg.textContent = text2;
|
|
9939
|
-
this.log?.append(msg);
|
|
9940
|
-
if (this.log) this.log.scrollTop = this.log.scrollHeight;
|
|
9941
|
-
return msg;
|
|
9942
|
-
}
|
|
9943
|
-
async ask(prompt, send2) {
|
|
9944
|
-
const loc = this.grid.store.state.locale;
|
|
9945
|
-
this.say("user", prompt);
|
|
9946
|
-
const wait = this.say("ai", loc.ai.thinking);
|
|
9947
|
-
wait.classList.add("fg-ai-wait");
|
|
9948
|
-
this.busy = new AbortController();
|
|
9949
|
-
send2.disabled = true;
|
|
9950
|
-
try {
|
|
9951
|
-
const res = await askAssistant(this.grid, prompt, this.grid.options.assistant, this.busy.signal);
|
|
9952
|
-
wait.classList.remove("fg-ai-wait");
|
|
9953
|
-
const changed = res.applied.filter((a) => a.type !== "message").length;
|
|
9954
|
-
wait.textContent = res.message || (changed ? loc.ai.done : "");
|
|
9955
|
-
if (res.skipped.length) {
|
|
9956
|
-
const note = el("div", "fg-ai-note");
|
|
9957
|
-
note.textContent = `${loc.ai.skipped}: ${res.skipped.map((s) => s.reason).join(", ")}`;
|
|
9958
|
-
wait.append(note);
|
|
9959
|
-
}
|
|
9960
|
-
if (changed) {
|
|
9961
|
-
const undo = el("button", "fg-btn fg-ai-undo", { type: "button" });
|
|
9962
|
-
undo.textContent = loc.ai.undo;
|
|
9963
|
-
undo.addEventListener("click", () => {
|
|
9964
|
-
res.undo();
|
|
9965
|
-
undo.disabled = true;
|
|
9966
|
-
undo.textContent = loc.ai.undone;
|
|
9967
|
-
});
|
|
9968
|
-
wait.append(undo);
|
|
9969
|
-
}
|
|
9970
|
-
this.grid.events.emit("assistantAnswer", { prompt, message: res.message, applied: res.applied, skipped: res.skipped });
|
|
9971
|
-
} catch (e) {
|
|
9972
|
-
if (e?.name === "AbortError") return;
|
|
9973
|
-
wait.remove();
|
|
9974
|
-
this.say("error", loc.ai.failed);
|
|
9975
|
-
} finally {
|
|
9976
|
-
this.busy = null;
|
|
9977
|
-
send2.disabled = false;
|
|
9978
|
-
}
|
|
9979
|
-
}
|
|
9980
9769
|
close() {
|
|
9981
|
-
this.busy?.abort();
|
|
9982
|
-
this.busy = null;
|
|
9983
9770
|
for (const f of this.off) f();
|
|
9984
9771
|
this.off = [];
|
|
9985
9772
|
this.popup?.remove();
|
|
9986
9773
|
this.popup = null;
|
|
9987
|
-
this.log = null;
|
|
9988
9774
|
}
|
|
9989
9775
|
};
|
|
9990
9776
|
|
|
@@ -10027,6 +9813,12 @@ var Grid = class {
|
|
|
10027
9813
|
this.sourceDefs = [];
|
|
10028
9814
|
this.pivotCfg = null;
|
|
10029
9815
|
this.pivotData = null;
|
|
9816
|
+
/** rollup option / setRollup: rows merged by the shown columns without an aggFunc. */
|
|
9817
|
+
this.rollupOn = false;
|
|
9818
|
+
/** rollup { by }: the columns the rows merge by (only the shown ones count); null: every shown column without an aggFunc. */
|
|
9819
|
+
this.rollupBy = null;
|
|
9820
|
+
/** The merged rows while rollup applies (not while pivoting), else null. */
|
|
9821
|
+
this.rollupData = null;
|
|
10030
9822
|
/** Grouping state from before pivot mode, restored when it ends. */
|
|
10031
9823
|
this.savedGroup = null;
|
|
10032
9824
|
/** 0-based page and rows per page (pagination only). */
|
|
@@ -10067,7 +9859,8 @@ var Grid = class {
|
|
|
10067
9859
|
formula: (f, row) => evaluateFormula(f, row, /* @__PURE__ */ new Set(), 0, /* @__PURE__ */ new Map(), this.formulaScope)
|
|
10068
9860
|
});
|
|
10069
9861
|
this.formatDialog = new FormatDialog(this);
|
|
10070
|
-
|
|
9862
|
+
/** Selection waiting for the row model to hold the newly merged rows (set by updateRows). */
|
|
9863
|
+
this.rollupSelection = null;
|
|
10071
9864
|
// ── pagination and server rows ──────────────────────────────
|
|
10072
9865
|
/** Cells flashing after a transaction: `${rowId}\u0000${colId}` → time the flash ends. */
|
|
10073
9866
|
/** showOverlay: an overlay the app asked for, shown whatever the row count. */
|
|
@@ -10093,6 +9886,8 @@ var Grid = class {
|
|
|
10093
9886
|
}
|
|
10094
9887
|
};
|
|
10095
9888
|
this.density = "normal";
|
|
9889
|
+
/** Set by the cellPadding option or setCellPadding; null: the theme / density value. */
|
|
9890
|
+
this.cellPadding = null;
|
|
10096
9891
|
// ── merge, aggregates, fill, pinned rows ────────────────────
|
|
10097
9892
|
/** `sortWithinMerges` is on. */
|
|
10098
9893
|
this.sortWithin = false;
|
|
@@ -10173,6 +9968,7 @@ var Grid = class {
|
|
|
10173
9968
|
this.renderer.applyTheme();
|
|
10174
9969
|
if (options.flashDuration) this.renderer.root.style.setProperty("--fg-flash-duration", `${this.flashMs()}ms`);
|
|
10175
9970
|
this.renderer.applyFont(this.font.size, this.font.family);
|
|
9971
|
+
if (options.cellPadding != null) this.applyCellPadding(options.cellPadding);
|
|
10176
9972
|
this.menu = new ColumnMenu(this);
|
|
10177
9973
|
this.chooser = new ColumnChooserDialog(this);
|
|
10178
9974
|
this.contextMenu = new ContextMenu(this);
|
|
@@ -10199,7 +9995,6 @@ var Grid = class {
|
|
|
10199
9995
|
() => this.charts.destroy(),
|
|
10200
9996
|
() => this.filterBuilder.close(),
|
|
10201
9997
|
() => this.formatDialog.close(),
|
|
10202
|
-
() => this.assistantPanel.close(),
|
|
10203
9998
|
attachTooltips(this)
|
|
10204
9999
|
);
|
|
10205
10000
|
if (typeof ResizeObserver !== "undefined") {
|
|
@@ -10208,6 +10003,7 @@ var Grid = class {
|
|
|
10208
10003
|
this.disposers.push(() => ro.disconnect());
|
|
10209
10004
|
}
|
|
10210
10005
|
if (options.pivot) this.setPivot(options.pivot);
|
|
10006
|
+
if (options.rollup) this.setRollup(options.rollup);
|
|
10211
10007
|
void needsFreeBadge(options.licenseKey).then((show) => {
|
|
10212
10008
|
if (show && !this.destroyed) this.renderer.showFreeBadge();
|
|
10213
10009
|
});
|
|
@@ -10286,6 +10082,7 @@ var Grid = class {
|
|
|
10286
10082
|
}
|
|
10287
10083
|
/** Column definitions or visibility changed; filters/quick filter may depend on them. */
|
|
10288
10084
|
columnsChanged() {
|
|
10085
|
+
this.rebuildRollup();
|
|
10289
10086
|
this.rowModel.invalidate();
|
|
10290
10087
|
this.updateRows();
|
|
10291
10088
|
this.refreshSummary();
|
|
@@ -10308,17 +10105,102 @@ var Grid = class {
|
|
|
10308
10105
|
}
|
|
10309
10106
|
updateRows() {
|
|
10310
10107
|
const paged = !!this.options.pagination;
|
|
10311
|
-
const data = this.
|
|
10108
|
+
const data = this.viewRows();
|
|
10312
10109
|
this.rowModel.update({ ...this.store.state, data, display: this.display(), page: paged ? { index: this.page, size: this.pageSize } : null });
|
|
10110
|
+
const remapped = this.rollupSelection;
|
|
10111
|
+
if (remapped) {
|
|
10112
|
+
this.rollupSelection = null;
|
|
10113
|
+
this.setSelected(remapped);
|
|
10114
|
+
}
|
|
10313
10115
|
if (paged && this.page >= this.getPageCount()) {
|
|
10314
10116
|
this.page = this.getPageCount() - 1;
|
|
10315
10117
|
this.rowModel.update({ ...this.store.state, data, display: this.display(), page: { index: this.page, size: this.pageSize } });
|
|
10316
10118
|
}
|
|
10317
10119
|
}
|
|
10318
10120
|
// ── pivot ───────────────────────────────────────────────────
|
|
10319
|
-
/** Rows behind the view: the pivot table while pivoting, else the source data. */
|
|
10121
|
+
/** Rows behind the view: the pivot table while pivoting, the merged rows with rollup, else the source data. */
|
|
10320
10122
|
viewRows() {
|
|
10321
|
-
return this.pivotData ?? this.store.state.data;
|
|
10123
|
+
return this.pivotData ?? this.rollupData ?? this.store.state.data;
|
|
10124
|
+
}
|
|
10125
|
+
// ── rollup ──────────────────────────────────────────────────
|
|
10126
|
+
getRollup() {
|
|
10127
|
+
return this.rollupBy ? { by: [...this.rollupBy] } : this.rollupOn;
|
|
10128
|
+
}
|
|
10129
|
+
/** true: merge by every shown column without an aggFunc; { by }: merge by the shown `by` columns; false: off. */
|
|
10130
|
+
setRollup(cfg) {
|
|
10131
|
+
const by = cfg && typeof cfg === "object" ? Array.isArray(cfg.by) ? cfg.by.filter((id) => typeof id === "string") : null : null;
|
|
10132
|
+
if (cfg && typeof cfg === "object" && !by) {
|
|
10133
|
+
warn("setRollup: expected true, false or { by: string[] }");
|
|
10134
|
+
return;
|
|
10135
|
+
}
|
|
10136
|
+
const on = !!cfg;
|
|
10137
|
+
if (on && !this.allow("pivot")) return;
|
|
10138
|
+
if (on && this.clientOnly("rollup")) return;
|
|
10139
|
+
if (on && this.options.treeData) {
|
|
10140
|
+
warn("rollup is not available with treeData");
|
|
10141
|
+
return;
|
|
10142
|
+
}
|
|
10143
|
+
if (on === this.rollupOn && (by ?? []).join("") === (this.rollupBy ?? []).join("") && !by === !this.rollupBy) return;
|
|
10144
|
+
this.editor.cancel();
|
|
10145
|
+
this.rollupOn = on;
|
|
10146
|
+
this.rollupBy = on ? by : null;
|
|
10147
|
+
this.store.set({ range: null, focus: null });
|
|
10148
|
+
this.columnsChanged();
|
|
10149
|
+
}
|
|
10150
|
+
/** Merge the source rows again (rollup on, not pivoting); the view is updated by the caller. */
|
|
10151
|
+
rebuildRollup() {
|
|
10152
|
+
const old = this.rollupData;
|
|
10153
|
+
this.rollupData = this.rollupOn && !this.pivotCfg ? buildRollup(this.store.state.data, this.dataColumns().map((c) => ({ id: c.id, def: c.def })), this.rollupBy) : null;
|
|
10154
|
+
if (old || this.rollupData) this.remapRollupSelection(old);
|
|
10155
|
+
}
|
|
10156
|
+
/**
|
|
10157
|
+
* Keep the selection across a re-merge: a selected merged row stands for its source rows, and a newly merged
|
|
10158
|
+
* row is selected when every one of its source rows was (so a delete never reaches a row nobody checked).
|
|
10159
|
+
*/
|
|
10160
|
+
remapRollupSelection(old) {
|
|
10161
|
+
const sel = this.store.state.selected;
|
|
10162
|
+
if (!sel.size) return;
|
|
10163
|
+
const idOf = (r) => this.rowModel.idOf(r);
|
|
10164
|
+
const picked = old ? new Set(old.filter((r) => sel.has(idOf(r))).flatMap((r) => rollupSource(r) ?? [r]).map(idOf)) : sel;
|
|
10165
|
+
const now = this.rollupData;
|
|
10166
|
+
const next = now ? now.filter((r) => (rollupSource(r) ?? [r]).every((s) => picked.has(idOf(s)))).map(idOf) : this.store.state.data.map(idOf).filter((id) => picked.has(id));
|
|
10167
|
+
if (next.length !== sel.size || next.some((id) => !sel.has(id))) this.rollupSelection = new Set(next);
|
|
10168
|
+
}
|
|
10169
|
+
/** With rollup { by }: whether the rows merge by this column (it is one of `by`). */
|
|
10170
|
+
isRollupKey(colId) {
|
|
10171
|
+
return !!this.rollupBy?.includes(colId);
|
|
10172
|
+
}
|
|
10173
|
+
/** The source rows behind `rows`: merged rollup rows stand for the rows they merge (for group and summary values). */
|
|
10174
|
+
sourceRowsOf(rows) {
|
|
10175
|
+
return this.rollupData ? rows.flatMap((r) => rollupSource(r) ?? [r]) : rows;
|
|
10176
|
+
}
|
|
10177
|
+
/** Whether a cell can be changed: never on merged rollup rows. */
|
|
10178
|
+
cellEditable(def, row, rowIndex) {
|
|
10179
|
+
return !this.rollupData && cellEditable(def, row, rowIndex);
|
|
10180
|
+
}
|
|
10181
|
+
/** Set or clear a column's aggFunc (null: no aggregate, so with rollup it becomes a column the rows merge by). */
|
|
10182
|
+
setColumnAggFunc(colId, aggFunc) {
|
|
10183
|
+
if (aggFunc !== null && typeof aggFunc !== "function" && !["sum", "avg", "min", "max", "count"].includes(aggFunc)) {
|
|
10184
|
+
warn(`setColumnAggFunc: unknown aggFunc "${String(aggFunc)}"`);
|
|
10185
|
+
return;
|
|
10186
|
+
}
|
|
10187
|
+
let found = false;
|
|
10188
|
+
const map = (defs2) => defs2.map((d) => {
|
|
10189
|
+
if (!d || typeof d !== "object") return d;
|
|
10190
|
+
if (Array.isArray(d.children)) return { ...d, children: map(d.children) };
|
|
10191
|
+
if ((d.id ?? d.field) !== colId) return d;
|
|
10192
|
+
found = true;
|
|
10193
|
+
const next = { ...d };
|
|
10194
|
+
if (aggFunc) next.aggFunc = aggFunc;
|
|
10195
|
+
else delete next.aggFunc;
|
|
10196
|
+
return next;
|
|
10197
|
+
});
|
|
10198
|
+
const defs = map(this.sourceDefs);
|
|
10199
|
+
if (!found) {
|
|
10200
|
+
warn(`setColumnAggFunc: no column "${colId}"`);
|
|
10201
|
+
return;
|
|
10202
|
+
}
|
|
10203
|
+
this.setColumns(defs);
|
|
10322
10204
|
}
|
|
10323
10205
|
/** Leaf columns of the user's definitions (not the pivot columns). */
|
|
10324
10206
|
sourceLeaves() {
|
|
@@ -10376,6 +10258,7 @@ var Grid = class {
|
|
|
10376
10258
|
afterPivotSwitch() {
|
|
10377
10259
|
this.page = 0;
|
|
10378
10260
|
this.store.set({ sort: [], filters: {}, range: null, focus: null });
|
|
10261
|
+
this.rebuildRollup();
|
|
10379
10262
|
this.rowModel.invalidate();
|
|
10380
10263
|
this.updateRows();
|
|
10381
10264
|
this.displayVersion++;
|
|
@@ -10484,12 +10367,31 @@ var Grid = class {
|
|
|
10484
10367
|
} finally {
|
|
10485
10368
|
this.keepRange = false;
|
|
10486
10369
|
}
|
|
10370
|
+
this.clampRanges();
|
|
10487
10371
|
}
|
|
10488
10372
|
this.editor.sync();
|
|
10489
10373
|
if (flashKeys.size && this.options.flashCells !== false) this.flash(flashKeys);
|
|
10490
10374
|
this.events.emit("transactionApplied", result);
|
|
10491
10375
|
return result;
|
|
10492
10376
|
}
|
|
10377
|
+
/** Kept ranges and focus after the row count shrank: cut to the last row, dropped when wholly past it. */
|
|
10378
|
+
clampRanges() {
|
|
10379
|
+
const count = this.rowModel.getRowCount();
|
|
10380
|
+
const { range, extraRanges, focus } = this.store.state;
|
|
10381
|
+
const clamp = (r) => {
|
|
10382
|
+
if (!r || r.start.rowIndex < count && r.end.rowIndex < count) return r;
|
|
10383
|
+
if (Math.min(r.start.rowIndex, r.end.rowIndex) >= count) return null;
|
|
10384
|
+
return { start: { ...r.start, rowIndex: Math.min(r.start.rowIndex, count - 1) }, end: { ...r.end, rowIndex: Math.min(r.end.rowIndex, count - 1) } };
|
|
10385
|
+
};
|
|
10386
|
+
const extras = extraRanges.map(clamp).filter((r) => !!r);
|
|
10387
|
+
if (extras.length !== extraRanges.length || extras.some((r, i) => r !== extraRanges[i])) this.store.set({ extraRanges: extras });
|
|
10388
|
+
if (focus && focus.rowIndex >= count) this.store.set({ focus: count ? { ...focus, rowIndex: count - 1 } : null });
|
|
10389
|
+
const next = clamp(range);
|
|
10390
|
+
if (next !== range) {
|
|
10391
|
+
this.store.set({ range: next });
|
|
10392
|
+
this.events.emit("rangeChange", { range: next && { start: { ...next.start }, end: { ...next.end } } });
|
|
10393
|
+
}
|
|
10394
|
+
}
|
|
10493
10395
|
/** One transaction against `data` (remove → update → add); returns the new array. */
|
|
10494
10396
|
/** Whether rows changed in `fields` could filter, sort, group or move differently. */
|
|
10495
10397
|
viewReads(fields) {
|
|
@@ -10644,14 +10546,24 @@ var Grid = class {
|
|
|
10644
10546
|
}
|
|
10645
10547
|
if (want("groupBy") && Array.isArray(state.groupBy)) this.setGroupBy(state.groupBy.filter(known));
|
|
10646
10548
|
if (want("pivot")) this.setPivot(state.pivot ?? null);
|
|
10647
|
-
|
|
10648
|
-
if (want("
|
|
10649
|
-
const
|
|
10650
|
-
|
|
10651
|
-
this.events.emit("filterChange", { filters: { ...filters } });
|
|
10549
|
+
const query = {};
|
|
10550
|
+
if (want("sort") && Array.isArray(state.sort)) {
|
|
10551
|
+
const sort = this.checkSort(state.sort.filter((x) => x && known(x.colId)));
|
|
10552
|
+
if (sort) query.sort = sort;
|
|
10652
10553
|
}
|
|
10653
|
-
if (want("
|
|
10654
|
-
|
|
10554
|
+
if (want("filters") && state.filters && typeof state.filters === "object") {
|
|
10555
|
+
query.filters = Object.fromEntries(Object.entries(state.filters).filter(([id]) => known(id)));
|
|
10556
|
+
}
|
|
10557
|
+
if (want("advancedFilter")) {
|
|
10558
|
+
const f = state.advancedFilter;
|
|
10559
|
+
const next = f && typeof f === "object" && Array.isArray(f.conditions) && f.conditions.length ? f : null;
|
|
10560
|
+
if (next !== this.store.state.advancedFilter && (!next || this.allow("advancedFilter"))) query.advancedFilter = next;
|
|
10561
|
+
}
|
|
10562
|
+
if (want("quickFilter") && typeof state.quickFilter === "string") query.quickFilter = state.quickFilter;
|
|
10563
|
+
this.store.set(query);
|
|
10564
|
+
if (query.sort) this.events.emit("sortChange", { sort: [...query.sort] });
|
|
10565
|
+
if (query.filters) this.events.emit("filterChange", { filters: { ...query.filters } });
|
|
10566
|
+
if ("advancedFilter" in query) this.events.emit("advancedFilterChange", { advancedFilter: query.advancedFilter ?? null });
|
|
10655
10567
|
if (want("expanded") && state.expanded && typeof state.expanded === "object") {
|
|
10656
10568
|
const expanded = new Map(Object.entries(state.expanded).filter(([, v]) => typeof v === "boolean"));
|
|
10657
10569
|
if (this.savedGroup) this.savedGroup.expanded = expanded;
|
|
@@ -10677,11 +10589,13 @@ var Grid = class {
|
|
|
10677
10589
|
rows.forEach((r, k) => {
|
|
10678
10590
|
nums[k] = r.kind ? 0 : ++n;
|
|
10679
10591
|
});
|
|
10680
|
-
|
|
10592
|
+
const first = this.page * this.pageSize;
|
|
10593
|
+
const offset = this.options.pagination && !this.rowModel.server ? this.rowModel.dataRowsBefore?.(first) ?? first : 0;
|
|
10594
|
+
this.rowNumberCache = { rows, nums, offset };
|
|
10681
10595
|
}
|
|
10682
10596
|
const v = this.rowNumberCache.nums[i];
|
|
10683
10597
|
if (!v) return null;
|
|
10684
|
-
return v +
|
|
10598
|
+
return v + this.rowNumberCache.offset;
|
|
10685
10599
|
}
|
|
10686
10600
|
getPage() {
|
|
10687
10601
|
return this.options.pagination ? this.page : 0;
|
|
@@ -10733,6 +10647,7 @@ var Grid = class {
|
|
|
10733
10647
|
/** Server rows: drop the cache and load again. */
|
|
10734
10648
|
refreshRows() {
|
|
10735
10649
|
if (this.rowModel instanceof ServerRowModel) {
|
|
10650
|
+
this.saveEditBeforeReload();
|
|
10736
10651
|
this.rowModel.purge();
|
|
10737
10652
|
this.renderer.invalidate({ rows: true });
|
|
10738
10653
|
}
|
|
@@ -10742,6 +10657,12 @@ var Grid = class {
|
|
|
10742
10657
|
this.renderer.invalidate({ header: true, rows: true });
|
|
10743
10658
|
this.events.emit("rowsLoaded", total === null ? { start, end } : { start, end, total });
|
|
10744
10659
|
}
|
|
10660
|
+
/** Server rows are about to be dropped: save an open editor to its row first, else it is left over another row. */
|
|
10661
|
+
saveEditBeforeReload() {
|
|
10662
|
+
if (!this.rowModel.server) return;
|
|
10663
|
+
this.editor.endRow(true);
|
|
10664
|
+
if (this.editor.active && !this.editor.commit(null)) this.editor.cancel();
|
|
10665
|
+
}
|
|
10745
10666
|
serverFailed(start, end, error) {
|
|
10746
10667
|
if (this.destroyed) return;
|
|
10747
10668
|
warn(`datasource.getRows failed for rows ${start}\u2013${end}`);
|
|
@@ -10890,7 +10811,7 @@ var Grid = class {
|
|
|
10890
10811
|
let m = this.aggCache.map.get(node.group);
|
|
10891
10812
|
if (!m) this.aggCache.map.set(node.group, m = /* @__PURE__ */ new Map());
|
|
10892
10813
|
if (!m.has(colId)) {
|
|
10893
|
-
const rows = node.group.rows;
|
|
10814
|
+
const rows = this.sourceRowsOf(node.group.rows);
|
|
10894
10815
|
try {
|
|
10895
10816
|
m.set(colId, aggregate(def.aggFunc, rows.map((r) => getCellValue(def, r)), rows));
|
|
10896
10817
|
} catch {
|
|
@@ -11083,10 +11004,33 @@ var Grid = class {
|
|
|
11083
11004
|
this.renderer.invalidateHeights();
|
|
11084
11005
|
this.layoutChanged();
|
|
11085
11006
|
}
|
|
11007
|
+
/** Store and apply the padding; false when the value is not valid. */
|
|
11008
|
+
applyCellPadding(px) {
|
|
11009
|
+
if (px != null && !(typeof px === "number" && Number.isFinite(px) && px >= 0)) {
|
|
11010
|
+
warn("cellPadding must be a number of px, 0 or more");
|
|
11011
|
+
return false;
|
|
11012
|
+
}
|
|
11013
|
+
this.cellPadding = px;
|
|
11014
|
+
if (px == null) this.renderer.root.style.removeProperty("--fg-cell-px");
|
|
11015
|
+
else this.renderer.root.style.setProperty("--fg-cell-px", `${px}px`);
|
|
11016
|
+
return true;
|
|
11017
|
+
}
|
|
11018
|
+
setCellPadding(px) {
|
|
11019
|
+
if (px === this.cellPadding || !this.applyCellPadding(px)) return;
|
|
11020
|
+
this.menu.close();
|
|
11021
|
+
this.layoutChanged();
|
|
11022
|
+
}
|
|
11023
|
+
/** The padding in use: the set value, else the theme / density CSS variable. */
|
|
11024
|
+
getCellPadding() {
|
|
11025
|
+
if (this.cellPadding != null) return this.cellPadding;
|
|
11026
|
+
const v = parseFloat(getComputedStyle(this.renderer.root).getPropertyValue("--fg-cell-px"));
|
|
11027
|
+
return Number.isFinite(v) ? v : 12;
|
|
11028
|
+
}
|
|
11086
11029
|
/** Copy the grid font variables onto an element rendered outside the root (popups, drag ghost). */
|
|
11087
11030
|
applyFontTo(target) {
|
|
11088
11031
|
if (this.font.size) target.style.setProperty("--fg-font-size", `${this.font.size}px`);
|
|
11089
11032
|
if (this.font.family) target.style.setProperty("--fg-font", this.font.family);
|
|
11033
|
+
if (this.cellPadding != null) target.style.setProperty("--fg-cell-px", `${this.cellPadding}px`);
|
|
11090
11034
|
}
|
|
11091
11035
|
/** Current summary configuration and values, or null when there are no summary rows. */
|
|
11092
11036
|
summary() {
|
|
@@ -11115,7 +11059,7 @@ var Grid = class {
|
|
|
11115
11059
|
}
|
|
11116
11060
|
const scope = opts.scope ?? "filtered";
|
|
11117
11061
|
const rows = scope === "all" ? this.rowModel.allNodes().map((n) => n.data) : scope === "selected" ? this.selectedRows() : this.rowModel.dataRows().map((n) => n.data);
|
|
11118
|
-
this.summaryValues = computeSummary(opts.rows, rows, (id) => this.columns.getNode(id)?.def);
|
|
11062
|
+
this.summaryValues = computeSummary(opts.rows, this.sourceRowsOf(rows), (id) => this.columns.getNode(id)?.def);
|
|
11119
11063
|
}
|
|
11120
11064
|
// ── cell ranges ─────────────────────────────────────────────
|
|
11121
11065
|
dataColumns() {
|
|
@@ -11208,12 +11152,11 @@ var Grid = class {
|
|
|
11208
11152
|
out = [];
|
|
11209
11153
|
for (let i = b.r1; i <= b.r2; i++) {
|
|
11210
11154
|
const node = this.rowModel.getRow(i);
|
|
11211
|
-
if (!node.kind) out.push(node.data);
|
|
11155
|
+
if (node && !node.kind) out.push(node.data);
|
|
11212
11156
|
}
|
|
11213
11157
|
cols = displayed.slice(b.c1, b.c2 + 1).filter((c) => !isSystemCol(c.id));
|
|
11214
11158
|
} else if (checkedRows) {
|
|
11215
|
-
|
|
11216
|
-
out = this.rowModel.dataRows().filter((n) => sel.has(n.id)).map((n) => n.data);
|
|
11159
|
+
out = this.shownSelectedRows();
|
|
11217
11160
|
cols = this.dataColumns();
|
|
11218
11161
|
} else {
|
|
11219
11162
|
const f = this.store.state.focus;
|
|
@@ -11268,8 +11211,17 @@ var Grid = class {
|
|
|
11268
11211
|
if (text2) this.clearCells(true);
|
|
11269
11212
|
return text2;
|
|
11270
11213
|
}
|
|
11214
|
+
/** Text a copy puts on the clipboard. Server rows: only the loaded rows, with a warning about the rest. */
|
|
11215
|
+
copyText(headers, rows = false) {
|
|
11216
|
+
const m = this.rowModel;
|
|
11217
|
+
if (!(m instanceof ServerRowModel)) return this.selectionTSV(headers, rows);
|
|
11218
|
+
const text2 = m.withoutLoading(() => this.selectionTSV(headers, rows));
|
|
11219
|
+
const missing = rows ? 0 : m.withoutLoading(() => this.unloadedInRanges());
|
|
11220
|
+
if (missing) warn(`copy: ${missing} row${missing > 1 ? "s" : ""} of the range ${missing > 1 ? "are" : "is"} not loaded and ${missing > 1 ? "were" : "was"} not copied`);
|
|
11221
|
+
return text2;
|
|
11222
|
+
}
|
|
11271
11223
|
copyToClipboard(headers, rows = false) {
|
|
11272
|
-
const text2 = this.
|
|
11224
|
+
const text2 = this.copyText(headers, rows);
|
|
11273
11225
|
if (text2 && typeof navigator !== "undefined" && navigator.clipboard?.writeText) {
|
|
11274
11226
|
try {
|
|
11275
11227
|
navigator.clipboard.writeText(text2)?.catch?.(() => void 0);
|
|
@@ -11278,6 +11230,14 @@ var Grid = class {
|
|
|
11278
11230
|
}
|
|
11279
11231
|
return text2;
|
|
11280
11232
|
}
|
|
11233
|
+
/** Server rows: how many rows the ranges cover that are not loaded (placeholders). */
|
|
11234
|
+
unloadedInRanges() {
|
|
11235
|
+
const seen2 = /* @__PURE__ */ new Set();
|
|
11236
|
+
for (const b of this.allRangeBounds()) for (let i = b.r1; i <= b.r2; i++) {
|
|
11237
|
+
if (this.rowModel.getRow(i)?.kind === "loading") seen2.add(i);
|
|
11238
|
+
}
|
|
11239
|
+
return seen2.size;
|
|
11240
|
+
}
|
|
11281
11241
|
pasteText(text2) {
|
|
11282
11242
|
const matrix = parseTSV(text2);
|
|
11283
11243
|
const result = { changed: 0, skipped: 0 };
|
|
@@ -11300,19 +11260,21 @@ var Grid = class {
|
|
|
11300
11260
|
const single = matrix.length === 1 && matrix[0].length === 1;
|
|
11301
11261
|
const rows = single && b ? b.r2 - b.r1 + 1 : matrix.length;
|
|
11302
11262
|
const width = single && b ? b.c2 - b.c1 + 1 : Math.max(...matrix.map((r) => r.length));
|
|
11303
|
-
for (let dr = 0; dr < rows;
|
|
11304
|
-
const ri = r0 + dr;
|
|
11305
|
-
if (ri >= count) break;
|
|
11263
|
+
for (let ri = r0, dr = 0; ri < count && dr < rows; ri++) {
|
|
11306
11264
|
const node = this.rowModel.getRow(ri);
|
|
11307
|
-
if (node.kind)
|
|
11265
|
+
if (node.kind) {
|
|
11266
|
+
if (single && b) dr++;
|
|
11267
|
+
continue;
|
|
11268
|
+
}
|
|
11269
|
+
const line2 = dr++;
|
|
11308
11270
|
for (let dc = 0; dc < width; dc++) {
|
|
11309
11271
|
const col = displayed[c0 + dc];
|
|
11310
11272
|
if (!col) break;
|
|
11311
11273
|
if (isSystemCol(col.id)) continue;
|
|
11312
|
-
const raw = single && b ? matrix[0][0] : matrix[
|
|
11274
|
+
const raw = single && b ? matrix[0][0] : matrix[line2]?.[dc];
|
|
11313
11275
|
if (raw === void 0) continue;
|
|
11314
11276
|
const def = col.def;
|
|
11315
|
-
if (!def.field || !cellEditable(def, node.data, ri)) {
|
|
11277
|
+
if (!def.field || !this.cellEditable(def, node.data, ri)) {
|
|
11316
11278
|
result.skipped++;
|
|
11317
11279
|
continue;
|
|
11318
11280
|
}
|
|
@@ -11380,7 +11342,7 @@ var Grid = class {
|
|
|
11380
11342
|
const id = this.rowModel.idOf(row);
|
|
11381
11343
|
const rowIndex = this.rowModel.indexOfId(id);
|
|
11382
11344
|
for (const c of cols) {
|
|
11383
|
-
if (!cellEditable(c.def, row, rowIndex < 0 ? i : rowIndex)) continue;
|
|
11345
|
+
if (!this.cellEditable(c.def, row, rowIndex < 0 ? i : rowIndex)) continue;
|
|
11384
11346
|
const message = this.cellError(c.def, row, row[c.def.field]);
|
|
11385
11347
|
if (message !== null) out.push({ row, rowIndex, colId: c.id, message });
|
|
11386
11348
|
}
|
|
@@ -11645,26 +11607,36 @@ var Grid = class {
|
|
|
11645
11607
|
const col = displayed[c];
|
|
11646
11608
|
const node = this.rowModel.getRow(r);
|
|
11647
11609
|
const def = col?.def;
|
|
11648
|
-
if (!node || node.kind || !def?.field || isSystemCol(col.id) || !cellEditable(def, node.data, r)) return;
|
|
11610
|
+
if (!node || node.kind || !def?.field || isSystemCol(col.id) || !this.cellEditable(def, node.data, r)) return;
|
|
11649
11611
|
if (this.cellError(def, node.data, value2) !== null) return;
|
|
11650
11612
|
this.writeCell(changes, node.data, def.field, col.id, r, value2);
|
|
11651
11613
|
};
|
|
11652
11614
|
const value = (r, c) => getCellValue(displayed[c].def, this.rowModel.getRow(r).data);
|
|
11615
|
+
const dataRow = (r) => {
|
|
11616
|
+
const node = this.rowModel.getRow(r);
|
|
11617
|
+
return !!node && !node.kind;
|
|
11618
|
+
};
|
|
11619
|
+
const keep = (from, to, ok) => {
|
|
11620
|
+
const out = [];
|
|
11621
|
+
for (let i = from; i <= to; i++) if (ok(i)) out.push(i);
|
|
11622
|
+
return out;
|
|
11623
|
+
};
|
|
11624
|
+
const dataCol = (c) => !!displayed[c] && !isSystemCol(displayed[c].id);
|
|
11653
11625
|
if (t.r1 < src.r1 || t.r2 > src.r2) {
|
|
11626
|
+
const down = keep(src.r2 + 1, t.r2, dataRow), up = keep(t.r1, src.r1 - 1, dataRow).reverse();
|
|
11627
|
+
const rows = keep(src.r1, src.r2, dataRow);
|
|
11654
11628
|
for (let c = src.c1; c <= src.c2; c++) {
|
|
11655
|
-
const
|
|
11656
|
-
|
|
11657
|
-
|
|
11658
|
-
fillSeries(down, t.r2 - src.r2).forEach((v, k) => write(src.r2 + 1 + k, c, v));
|
|
11659
|
-
fillSeries(up, src.r1 - t.r1).forEach((v, k) => write(src.r1 - 1 - k, c, v));
|
|
11629
|
+
const seed = rows.map((r) => value(r, c));
|
|
11630
|
+
fillSeries(seed, down.length).forEach((v, k) => write(down[k], c, v));
|
|
11631
|
+
fillSeries([...seed].reverse(), up.length).forEach((v, k) => write(up[k], c, v));
|
|
11660
11632
|
}
|
|
11661
11633
|
} else if (t.c1 < src.c1 || t.c2 > src.c2) {
|
|
11662
|
-
|
|
11663
|
-
|
|
11664
|
-
|
|
11665
|
-
|
|
11666
|
-
fillSeries(
|
|
11667
|
-
fillSeries(
|
|
11634
|
+
const right = keep(src.c2 + 1, t.c2, dataCol), left = keep(t.c1, src.c1 - 1, dataCol).reverse();
|
|
11635
|
+
const cols = keep(src.c1, src.c2, dataCol);
|
|
11636
|
+
for (const r of keep(src.r1, src.r2, dataRow)) {
|
|
11637
|
+
const seed = cols.map((c) => value(r, c));
|
|
11638
|
+
fillSeries(seed, right.length).forEach((v, k) => write(r, right[k], v));
|
|
11639
|
+
fillSeries([...seed].reverse(), left.length).forEach((v, k) => write(r, left[k], v));
|
|
11668
11640
|
}
|
|
11669
11641
|
} else return;
|
|
11670
11642
|
this.endCellTx(changes);
|
|
@@ -11733,7 +11705,7 @@ var Grid = class {
|
|
|
11733
11705
|
const node = this.rowModel.getRow(r);
|
|
11734
11706
|
for (let c = box.c1; c <= box.c2; c++) {
|
|
11735
11707
|
const def = displayed[c]?.def;
|
|
11736
|
-
if (node && !node.kind && def?.field && cellEditable(def, node.data, r) && (!cut || this.copyAllowed(def, node.data))) this.writeCell(changes, node.data, def.field, displayed[c].id, r, null);
|
|
11708
|
+
if (node && !node.kind && def?.field && this.cellEditable(def, node.data, r) && (!cut || this.copyAllowed(def, node.data))) this.writeCell(changes, node.data, def.field, displayed[c].id, r, null);
|
|
11737
11709
|
}
|
|
11738
11710
|
}
|
|
11739
11711
|
this.endCellTx(changes);
|
|
@@ -11764,7 +11736,7 @@ var Grid = class {
|
|
|
11764
11736
|
removeRows(target, record = true) {
|
|
11765
11737
|
if (this.clientOnly("removeRows")) return [];
|
|
11766
11738
|
const data = this.store.state.data;
|
|
11767
|
-
const match = typeof target === "function" ? target : /* @__PURE__ */ ((set) => (r) => set.has(r))(new Set(target));
|
|
11739
|
+
const match = typeof target === "function" ? target : /* @__PURE__ */ ((set) => (r) => set.has(r))(new Set(Array.isArray(target) ? target.flatMap((r) => rollupSource(r) ?? [r]) : []));
|
|
11768
11740
|
const removed = [];
|
|
11769
11741
|
data.forEach((row, index) => {
|
|
11770
11742
|
let hit = false;
|
|
@@ -11887,10 +11859,13 @@ var Grid = class {
|
|
|
11887
11859
|
const cols = (ids ? ids.map((id) => this.columns.getColumn(id)) : this.columns.displayed()).filter((c) => !!c && isResizable(c.def));
|
|
11888
11860
|
const { first, last } = this.renderer.renderedRange();
|
|
11889
11861
|
const code = this.store.state.locale.code;
|
|
11890
|
-
const
|
|
11891
|
-
const
|
|
11862
|
+
const pad4 = this.getCellPadding() * 2;
|
|
11863
|
+
const HEADER_EXTRA = pad4 + 6 + 14;
|
|
11864
|
+
const FILTER_BTN = 6 + 22;
|
|
11865
|
+
const filters = this.store.state.filters;
|
|
11866
|
+
const CELL_EXTRA = pad4;
|
|
11892
11867
|
for (const col of cols) {
|
|
11893
|
-
let w = this.renderer.measureText(col.def.header ?? col.def.field ?? "", true) + HEADER_EXTRA;
|
|
11868
|
+
let w = this.renderer.measureText(col.def.header ?? col.def.field ?? "", true) + HEADER_EXTRA + (filters[col.id] ? FILTER_BTN : 0);
|
|
11894
11869
|
const fmt = getFormatter(col.def, code);
|
|
11895
11870
|
for (let i = first; i <= last; i++) {
|
|
11896
11871
|
const node = this.rowModel.getRow(i);
|
|
@@ -11906,6 +11881,13 @@ var Grid = class {
|
|
|
11906
11881
|
this.columnsChanged();
|
|
11907
11882
|
}
|
|
11908
11883
|
setSort(sort) {
|
|
11884
|
+
const valid = this.checkSort(sort);
|
|
11885
|
+
if (!valid) return;
|
|
11886
|
+
this.store.set({ sort: valid });
|
|
11887
|
+
this.events.emit("sortChange", { sort: [...valid] });
|
|
11888
|
+
}
|
|
11889
|
+
/** The known, well-formed sort items, or null when a beforeSort handler refused them. */
|
|
11890
|
+
checkSort(sort) {
|
|
11909
11891
|
const valid = sort.filter((s) => {
|
|
11910
11892
|
const node = this.columns.getNode(s.colId);
|
|
11911
11893
|
if (!node || node.children) {
|
|
@@ -11914,9 +11896,7 @@ var Grid = class {
|
|
|
11914
11896
|
}
|
|
11915
11897
|
return s.dir === "asc" || s.dir === "desc";
|
|
11916
11898
|
});
|
|
11917
|
-
|
|
11918
|
-
this.store.set({ sort: valid });
|
|
11919
|
-
this.events.emit("sortChange", { sort: [...valid] });
|
|
11899
|
+
return this.veto("beforeSort", { sort: valid.map((s) => ({ ...s })) }) ? null : valid;
|
|
11920
11900
|
}
|
|
11921
11901
|
toggleSort(colId, additive) {
|
|
11922
11902
|
const cur = this.store.state.sort;
|
|
@@ -12082,9 +12062,19 @@ var Grid = class {
|
|
|
12082
12062
|
}
|
|
12083
12063
|
this.setSelected(next);
|
|
12084
12064
|
}
|
|
12065
|
+
/** The checked rows' data; a merged rollup row stands for its source rows, so these are always rows of the data. */
|
|
12085
12066
|
selectedRows() {
|
|
12086
12067
|
const sel = this.store.state.selected;
|
|
12087
|
-
return sel.size ? this.rowModel.allNodes().filter((n) => sel.has(n.id)).map((n) => n.data) : [];
|
|
12068
|
+
return sel.size ? this.sourceRowsOf(this.rowModel.allNodes().filter((n) => sel.has(n.id)).map((n) => n.data)) : [];
|
|
12069
|
+
}
|
|
12070
|
+
/** Filtered data rows in display order (group buckets, parents before children), collapsed ones included. */
|
|
12071
|
+
orderedDataRows() {
|
|
12072
|
+
return this.rowModel.orderedDataRows?.() ?? this.rowModel.dataRows();
|
|
12073
|
+
}
|
|
12074
|
+
/** Checked rows that pass the filters, in display order: what copy, export and delete of the checked rows act on. */
|
|
12075
|
+
shownSelectedRows() {
|
|
12076
|
+
const sel = this.store.state.selected;
|
|
12077
|
+
return sel.size ? this.orderedDataRows().filter((n) => sel.has(n.id)).map((n) => n.data) : [];
|
|
12088
12078
|
}
|
|
12089
12079
|
withSelect(defs) {
|
|
12090
12080
|
const sys = [];
|
|
@@ -12157,12 +12147,25 @@ var Grid = class {
|
|
|
12157
12147
|
});
|
|
12158
12148
|
this.columnsChanged();
|
|
12159
12149
|
}
|
|
12150
|
+
/** Close every popup of this grid (menus and dialogs); focus that was in one goes back to the grid. */
|
|
12151
|
+
closePopups() {
|
|
12152
|
+
const r = this.renderer.root.getRootNode();
|
|
12153
|
+
const inPopup = !!(r.activeElement ?? document.activeElement)?.closest(".fg-menu");
|
|
12154
|
+
this.menu.close();
|
|
12155
|
+
this.chooser.close();
|
|
12156
|
+
this.contextMenu.close();
|
|
12157
|
+
this.filterBuilder.close();
|
|
12158
|
+
this.formatDialog.close();
|
|
12159
|
+
if (inPopup) this.renderer.gridEl.focus({ preventScroll: true });
|
|
12160
|
+
}
|
|
12160
12161
|
onChange(keys) {
|
|
12161
12162
|
const has = (...k) => k.some((x) => keys.includes(x));
|
|
12163
|
+
if (has("sort", "filters", "quickFilter", "advancedFilter")) this.saveEditBeforeReload();
|
|
12162
12164
|
if (this.pivotCfg && has("data", "locale")) this.rebuildPivot(false);
|
|
12165
|
+
if (this.rollupOn && has("data")) this.rebuildRollup();
|
|
12166
|
+
if (has("locale", "theme")) this.closePopups();
|
|
12163
12167
|
if (has("locale")) {
|
|
12164
|
-
this.
|
|
12165
|
-
this.chooser.close();
|
|
12168
|
+
this.finder.relabel();
|
|
12166
12169
|
this.sideBar?.refresh();
|
|
12167
12170
|
this.rowModel.invalidate();
|
|
12168
12171
|
}
|
|
@@ -12253,12 +12256,6 @@ var Grid = class {
|
|
|
12253
12256
|
if (!this.allow("conditional")) return;
|
|
12254
12257
|
this.formatDialog.open(colId);
|
|
12255
12258
|
}),
|
|
12256
|
-
// closed: as if `assistant` were not set
|
|
12257
|
-
askAssistant: guard((prompt) => askAssistant(this, prompt, this.allow("assistant") ? this.options.assistant : void 0)),
|
|
12258
|
-
openAssistant: guard(() => {
|
|
12259
|
-
if (!this.allow("assistant")) return;
|
|
12260
|
-
this.assistantPanel.open();
|
|
12261
|
-
}),
|
|
12262
12259
|
getState: guard(() => this.getState()),
|
|
12263
12260
|
setState: guard((s, o) => this.setState(s, o)),
|
|
12264
12261
|
getScrollPosition: guard(() => this.renderer.getScroll()),
|
|
@@ -12267,6 +12264,11 @@ var Grid = class {
|
|
|
12267
12264
|
setTheme: guard((t) => this.store.set({ theme: t })),
|
|
12268
12265
|
setLocale: guard((l) => this.store.set({ locale: resolveLocale(l) })),
|
|
12269
12266
|
setFont: guard((f) => this.setFont(f)),
|
|
12267
|
+
setCellPadding: guard((px) => this.setCellPadding(px)),
|
|
12268
|
+
getCellPadding: guard(() => this.getCellPadding()),
|
|
12269
|
+
setRollup: guard((cfg) => this.setRollup(cfg)),
|
|
12270
|
+
getRollup: guard(() => this.getRollup()),
|
|
12271
|
+
setColumnAggFunc: guard((id, agg) => this.setColumnAggFunc(id, agg)),
|
|
12270
12272
|
setColumnVisible: guard((id, v) => this.setColumnsVisible([id], v)),
|
|
12271
12273
|
autoSizeColumns: guard((ids) => this.autoSize(ids)),
|
|
12272
12274
|
resetColumnState: guard(() => this.resetColumns()),
|