@freegridjs/core 1.7.1 → 1.9.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/LICENSE +63 -29
- package/README.md +19 -4
- package/dist/cdn/freegrid.esm.js +16 -14
- package/dist/cdn/freegrid.min.js +21 -19
- package/dist/index.d.ts +8 -4
- package/dist/index.js +239 -73
- package/dist/locales/de.d.ts +1 -1
- package/dist/locales/en.d.ts +1 -1
- package/dist/locales/es.d.ts +1 -1
- package/dist/locales/fr.d.ts +1 -1
- package/dist/locales/id.d.ts +1 -1
- package/dist/locales/it.d.ts +1 -1
- package/dist/locales/ja.d.ts +1 -1
- package/dist/locales/ko.d.ts +1 -1
- package/dist/locales/pt-BR.d.ts +1 -1
- package/dist/locales/ru.d.ts +1 -1
- package/dist/locales/th.d.ts +1 -1
- package/dist/locales/vi.d.ts +1 -1
- package/dist/locales/zh-CN.d.ts +1 -1
- package/dist/locales/zh-TW.d.ts +1 -1
- package/dist/{types-D8F0nn1a.d.ts → types-Du3F8mqD.d.ts} +3 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -1296,6 +1296,7 @@ function columnFormulaValue(def, row) {
|
|
|
1296
1296
|
if (scope) return valueIn(scope, cacheOf(scope), row, def.field);
|
|
1297
1297
|
return run(row[def.field], { row, scope, seen: /* @__PURE__ */ new Set([def.field]), depth: 0, memo: /* @__PURE__ */ new Map() });
|
|
1298
1298
|
}
|
|
1299
|
+
var formulasOff = (def) => !!scopes.get(def)?.off?.();
|
|
1299
1300
|
var isFormula = (v) => typeof v === "string" && v.length > 1 && v.startsWith("=");
|
|
1300
1301
|
function formulaRefs(formula) {
|
|
1301
1302
|
let n;
|
|
@@ -1327,10 +1328,10 @@ function getCellValue(def, row) {
|
|
|
1327
1328
|
return void 0;
|
|
1328
1329
|
}
|
|
1329
1330
|
}
|
|
1330
|
-
if (typeof def.formula === "string" && isFormula(def.formula)) return columnFormulaValue(def, row);
|
|
1331
|
+
if (typeof def.formula === "string" && isFormula(def.formula) && !formulasOff(def)) return columnFormulaValue(def, row);
|
|
1331
1332
|
if (def.field == null) return void 0;
|
|
1332
1333
|
const raw = row[def.field];
|
|
1333
|
-
return def.formulas && isFormula(raw) ? columnFormulaValue(def, row) : raw;
|
|
1334
|
+
return def.formulas && isFormula(raw) && !formulasOff(def) ? columnFormulaValue(def, row) : raw;
|
|
1334
1335
|
}
|
|
1335
1336
|
|
|
1336
1337
|
// src/interactions/filterRow.ts
|
|
@@ -3416,6 +3417,8 @@ function nextFocus(key, mods, p, s) {
|
|
|
3416
3417
|
|
|
3417
3418
|
// src/registry.ts
|
|
3418
3419
|
var liveGrids = /* @__PURE__ */ new Map();
|
|
3420
|
+
var apiGrids = /* @__PURE__ */ new WeakMap();
|
|
3421
|
+
var gridOfApi = (api) => api ? apiGrids.get(api) : void 0;
|
|
3419
3422
|
var gridOfRoot = (root) => root ? liveGrids.get(root) : void 0;
|
|
3420
3423
|
|
|
3421
3424
|
// src/interactions/body.ts
|
|
@@ -3492,7 +3495,7 @@ function attachBody(grid) {
|
|
|
3492
3495
|
const data = grid.columns.displayed().filter((c) => !isSystemCol(c.id));
|
|
3493
3496
|
if (data.length) {
|
|
3494
3497
|
grid.setFocus(ev.rowIndex, data[0].id);
|
|
3495
|
-
grid.setRange({ start: { rowIndex: ev.rowIndex, colId: data[0].id }, end: { rowIndex: ev.rowIndex, colId: data[data.length - 1].id } });
|
|
3498
|
+
if (grid.rangesOn()) grid.setRange({ start: { rowIndex: ev.rowIndex, colId: data[0].id }, end: { rowIndex: ev.rowIndex, colId: data[data.length - 1].id } });
|
|
3496
3499
|
}
|
|
3497
3500
|
return;
|
|
3498
3501
|
}
|
|
@@ -3501,9 +3504,9 @@ function attachBody(grid) {
|
|
|
3501
3504
|
const prev = grid.store.state.focus;
|
|
3502
3505
|
const onBox = ev.colId === SELECT_COL_ID;
|
|
3503
3506
|
if (dragged) {
|
|
3504
|
-
} else if (e.shiftKey && prev && !onBox && !isSystemCol(prev.colId) && grid.
|
|
3507
|
+
} else if (e.shiftKey && prev && !onBox && !isSystemCol(prev.colId) && grid.rangesOn()) {
|
|
3505
3508
|
grid.setRange({ start: prev, end: { rowIndex: ev.rowIndex, colId: ev.colId } }, true);
|
|
3506
|
-
} else if ((e.ctrlKey || e.metaKey) && !onBox && !isSystemCol(ev.colId) && grid.
|
|
3509
|
+
} else if ((e.ctrlKey || e.metaKey) && !onBox && !isSystemCol(ev.colId) && grid.rangesOn()) {
|
|
3507
3510
|
const at = { rowIndex: ev.rowIndex, colId: ev.colId };
|
|
3508
3511
|
grid.addRange({ start: at, end: at });
|
|
3509
3512
|
grid.setFocus(ev.rowIndex, ev.colId);
|
|
@@ -3511,7 +3514,7 @@ function attachBody(grid) {
|
|
|
3511
3514
|
grid.setFocus(ev.rowIndex, ev.colId);
|
|
3512
3515
|
grid.setRange(null);
|
|
3513
3516
|
}
|
|
3514
|
-
if (grid.options.rowSelection && !dragged) {
|
|
3517
|
+
if (grid.options.rowSelection && !dragged && (onBox || grid.options.rowClickSelection !== false)) {
|
|
3515
3518
|
grid.selectFromClick(ev.rowIndex, onBox || e.ctrlKey || e.metaKey, e.shiftKey);
|
|
3516
3519
|
}
|
|
3517
3520
|
grid.events.emit("cellClick", ev);
|
|
@@ -3569,7 +3572,7 @@ function attachBody(grid) {
|
|
|
3569
3572
|
grid.selectAllCells();
|
|
3570
3573
|
return true;
|
|
3571
3574
|
case "find":
|
|
3572
|
-
if (grid.options.findBar === false) return false;
|
|
3575
|
+
if (grid.options.findBar === false || !grid.paid) return false;
|
|
3573
3576
|
grid.finder.openBar();
|
|
3574
3577
|
return true;
|
|
3575
3578
|
case "paste":
|
|
@@ -3679,7 +3682,7 @@ function attachBody(grid) {
|
|
|
3679
3682
|
clearTimeout(pressTimer);
|
|
3680
3683
|
pressTimer = window.setTimeout(() => {
|
|
3681
3684
|
cancel();
|
|
3682
|
-
if (grid.
|
|
3685
|
+
if (!grid.contextMenu.enabled()) return;
|
|
3683
3686
|
suppressClick = true;
|
|
3684
3687
|
if (!isSystemCol(hit.colId)) {
|
|
3685
3688
|
grid.setFocus(hit.rowIndex, hit.colId);
|
|
@@ -3698,7 +3701,7 @@ function attachBody(grid) {
|
|
|
3698
3701
|
return;
|
|
3699
3702
|
}
|
|
3700
3703
|
if (e.button === 0 && onHandleDown(e)) return;
|
|
3701
|
-
if (grid.
|
|
3704
|
+
if (!grid.rangesOn()) return;
|
|
3702
3705
|
if (e.button !== 0 || e.shiftKey || e.target.closest("input, a, button")) return;
|
|
3703
3706
|
const start = renderer.cellAt(e.target);
|
|
3704
3707
|
if (!start || isSystemCol(start.colId)) return;
|
|
@@ -3746,7 +3749,7 @@ function attachBody(grid) {
|
|
|
3746
3749
|
grid.setRange(null);
|
|
3747
3750
|
return;
|
|
3748
3751
|
}
|
|
3749
|
-
if (ctrl && e.key.toLowerCase() === "a" && grid.
|
|
3752
|
+
if (ctrl && e.key.toLowerCase() === "a" && grid.rangesOn()) {
|
|
3750
3753
|
e.preventDefault();
|
|
3751
3754
|
grid.selectAllCells();
|
|
3752
3755
|
return;
|
|
@@ -3763,7 +3766,7 @@ function attachBody(grid) {
|
|
|
3763
3766
|
}
|
|
3764
3767
|
const size = { rows: count, cols: cols.length, pageRows: renderer.pageRows() };
|
|
3765
3768
|
const firstData = cols.findIndex((c) => !isSystemCol(c.id));
|
|
3766
|
-
const ranges = grid.
|
|
3769
|
+
const ranges = grid.rangesOn();
|
|
3767
3770
|
if (ranges && e.shiftKey && e.key !== "Tab" && NAV_KEYS.has(e.key)) {
|
|
3768
3771
|
const anchor = f ?? { rowIndex: 0, colId: cols[firstData].id };
|
|
3769
3772
|
const range = grid.store.state.range;
|
|
@@ -4264,6 +4267,10 @@ var ContextMenu = class {
|
|
|
4264
4267
|
this.close();
|
|
4265
4268
|
this.detach();
|
|
4266
4269
|
}
|
|
4270
|
+
/** The cell menu is on: not switched off and the paid feature allowed (closed: the browser's menu shows). */
|
|
4271
|
+
enabled() {
|
|
4272
|
+
return this.grid.options.contextMenu !== false && this.grid.allow("contextMenu");
|
|
4273
|
+
}
|
|
4267
4274
|
onContextMenu(e) {
|
|
4268
4275
|
const { grid } = this;
|
|
4269
4276
|
if (grid.options.contextMenu === false) return;
|
|
@@ -4278,7 +4285,7 @@ var ContextMenu = class {
|
|
|
4278
4285
|
return;
|
|
4279
4286
|
}
|
|
4280
4287
|
const hit = grid.renderer.cellAt(target);
|
|
4281
|
-
if (!hit) return;
|
|
4288
|
+
if (!hit || !this.enabled()) return;
|
|
4282
4289
|
e.preventDefault();
|
|
4283
4290
|
grid.renderer.gridEl.focus({ preventScroll: true });
|
|
4284
4291
|
if (!isSystemCol(hit.colId) && !grid.rangeContains(hit.rowIndex, hit.colId)) {
|
|
@@ -4290,7 +4297,7 @@ var ContextMenu = class {
|
|
|
4290
4297
|
/** Keyboard: Shift+F10 or the ContextMenu key. */
|
|
4291
4298
|
openAtFocus() {
|
|
4292
4299
|
const f = this.grid.store.state.focus;
|
|
4293
|
-
if (!f || this.
|
|
4300
|
+
if (!f || !this.enabled()) return;
|
|
4294
4301
|
const r = this.grid.renderer.root.getBoundingClientRect();
|
|
4295
4302
|
const cell = this.grid.renderer.root.querySelector(`.fg-row[data-row="${f.rowIndex}"] .fg-cell[data-col="${CSS.escape(f.colId)}"]`);
|
|
4296
4303
|
const box = cell?.getBoundingClientRect() ?? r;
|
|
@@ -4333,7 +4340,7 @@ var ContextMenu = class {
|
|
|
4333
4340
|
if (grid.options.rowSelection === "multiple") items.push({ label: loc.selectAllRows, action: () => grid.api.selectAll() });
|
|
4334
4341
|
items.push({ label: loc.chooseColumns, action: () => grid.chooser.open() });
|
|
4335
4342
|
const b = grid.rangeBounds();
|
|
4336
|
-
if (b && (b.r2 > b.r1 || b.c2 > b.c1)) {
|
|
4343
|
+
if (b && (b.r2 > b.r1 || b.c2 > b.c1) && grid.paid) {
|
|
4337
4344
|
items.push(
|
|
4338
4345
|
"separator",
|
|
4339
4346
|
{ label: loc.chartBar, action: () => grid.api.createChart({ type: "bar" }) },
|
|
@@ -4344,12 +4351,12 @@ var ContextMenu = class {
|
|
|
4344
4351
|
items.push(
|
|
4345
4352
|
"separator",
|
|
4346
4353
|
{ label: loc.exportCsv, action: () => grid.api.exportCsv() },
|
|
4347
|
-
{ label: loc.exportExcel, action: () => grid.api.exportXlsx() },
|
|
4354
|
+
...grid.paid ? [{ label: loc.exportExcel, action: () => grid.api.exportXlsx() }] : [],
|
|
4348
4355
|
{ label: loc.exportJson, action: () => grid.api.exportJson() },
|
|
4349
|
-
...grid.options.assistant?.url ? [{ label: `${loc.ai.title}\u2026`, action: () => grid.api.openAssistant() }] : [],
|
|
4350
|
-
{ label: loc.exportPdf, action: () => grid.api.exportPdf() }
|
|
4356
|
+
...grid.options.assistant?.url && grid.paid ? [{ label: `${loc.ai.title}\u2026`, action: () => grid.api.openAssistant() }] : [],
|
|
4357
|
+
...grid.paid ? [{ label: loc.exportPdf, action: () => grid.api.exportPdf() }] : []
|
|
4351
4358
|
);
|
|
4352
|
-
if (grid.options.advancedFilter !== false && !grid.options.pivot) items.push("separator", { label: `${loc.advancedFilter}\u2026`, action: () => grid.filterBuilder.open() });
|
|
4359
|
+
if (grid.options.advancedFilter !== false && !grid.options.pivot && grid.paid) items.push("separator", { label: `${loc.advancedFilter}\u2026`, action: () => grid.filterBuilder.open() });
|
|
4353
4360
|
items.push("separator", { label: loc.print, action: () => grid.api.print() });
|
|
4354
4361
|
return items;
|
|
4355
4362
|
}
|
|
@@ -4492,8 +4499,7 @@ var FillHandle = class {
|
|
|
4492
4499
|
this.handle.addEventListener("pointerdown", this.onDown);
|
|
4493
4500
|
}
|
|
4494
4501
|
enabled() {
|
|
4495
|
-
|
|
4496
|
-
return o.fillHandle !== false && o.rangeSelection !== false;
|
|
4502
|
+
return this.grid.options.fillHandle !== false && this.grid.rangesOn();
|
|
4497
4503
|
}
|
|
4498
4504
|
/** Current source block: the range, or the focused cell. */
|
|
4499
4505
|
source() {
|
|
@@ -4670,7 +4676,7 @@ var Finder = class {
|
|
|
4670
4676
|
this.countEl.textContent = !this.query ? "" : this.matches.length ? `${this.index + 1}/${this.matches.length}` : loc.noMatches;
|
|
4671
4677
|
}
|
|
4672
4678
|
openBar() {
|
|
4673
|
-
if (this.grid.options.findBar === false) return;
|
|
4679
|
+
if (this.grid.options.findBar === false || !this.grid.paid) return;
|
|
4674
4680
|
if (this.bar) {
|
|
4675
4681
|
this.bar.querySelector("input").focus();
|
|
4676
4682
|
return;
|
|
@@ -7603,12 +7609,36 @@ function buildPivot(source, config, defOf, labels, locale) {
|
|
|
7603
7609
|
return { columns: [...rowColumns, ...valueColumns], rows, groupBy: cfg.rows.slice(0, -1) };
|
|
7604
7610
|
}
|
|
7605
7611
|
|
|
7612
|
+
// src/features.ts
|
|
7613
|
+
var PAID_FEATURES = {
|
|
7614
|
+
grouping: { en: "Row grouping and aggregation", ko: "\uD589 \uADF8\uB8F9\xB7\uC9D1\uACC4" },
|
|
7615
|
+
pivot: { en: "Pivot", ko: "\uD53C\uBC97" },
|
|
7616
|
+
charts: { en: "Charts", ko: "\uCC28\uD2B8" },
|
|
7617
|
+
range: { en: "Cell range selection", ko: "\uC140 \uBC94\uC704 \uC120\uD0DD" },
|
|
7618
|
+
clipboard: { en: "Range copy and paste", ko: "\uBC94\uC704 \uBCF5\uC0AC\xB7\uBD99\uC5EC\uB123\uAE30" },
|
|
7619
|
+
fill: { en: "Fill handle", ko: "\uCC44\uC6B0\uAE30 \uD578\uB4E4" },
|
|
7620
|
+
excel: { en: "Excel export", ko: "\uC5D1\uC140 \uB0B4\uBCF4\uB0B4\uAE30" },
|
|
7621
|
+
pdf: { en: "PDF export", ko: "PDF \uB0B4\uBCF4\uB0B4\uAE30" },
|
|
7622
|
+
serverGroups: { en: "Server-side groups", ko: "\uC11C\uBC84 \uADF8\uB8F9" },
|
|
7623
|
+
advancedFilter: { en: "Advanced filter", ko: "\uACE0\uAE09 \uD544\uD130" },
|
|
7624
|
+
sideBar: { en: "Side bar panels", ko: "\uC0AC\uC774\uB4DC\uBC14 \uD328\uB110" },
|
|
7625
|
+
contextMenu: { en: "Context menu", ko: "\uCEE8\uD14D\uC2A4\uD2B8 \uBA54\uB274" },
|
|
7626
|
+
summary: { en: "Summary rows", ko: "\uD569\uACC4 \uD589" },
|
|
7627
|
+
formulas: { en: "Formulas", ko: "\uC218\uC2DD" },
|
|
7628
|
+
find: { en: "Find", ko: "\uCC3E\uAE30" },
|
|
7629
|
+
conditional: { en: "Conditional formatting", ko: "\uC870\uAC74\uBD80 \uC11C\uC2DD" },
|
|
7630
|
+
assistant: { en: "AI assistant", ko: "AI \uC5B4\uC2DC\uC2A4\uD134\uD2B8" },
|
|
7631
|
+
merge: { en: "Merged cells", ko: "\uC140 \uBCD1\uD569" },
|
|
7632
|
+
workbook: { en: "Workbook", ko: "\uC6CC\uD06C\uBD81" }
|
|
7633
|
+
};
|
|
7634
|
+
|
|
7606
7635
|
// src/version.ts
|
|
7607
|
-
var version = "1.
|
|
7636
|
+
var version = "1.9.0";
|
|
7608
7637
|
|
|
7609
7638
|
// src/license.ts
|
|
7610
7639
|
var PUBLIC_KEY = { kty: "EC", crv: "P-256", x: "sUTKkglZmk-HMKPNub8p7AeLBKesHCU8irIkw9rJlbE", y: "j0U_aKRDR7iApY9UpaD3hUemhBLZJwDv9-QiT_hUyw8" };
|
|
7611
7640
|
var PRICING_URL = "https://freegrid.app/pricing/";
|
|
7641
|
+
var PERSONAL_URL = "https://freegrid.app/license/personal/";
|
|
7612
7642
|
var USAGE_URL = "https://freegrid.app/api/usage";
|
|
7613
7643
|
var STATUS_URL = "https://freegrid.app/api/domain-status";
|
|
7614
7644
|
var majorOf = (v) => Number.parseInt(String(v).split(".")[0], 10);
|
|
@@ -7624,11 +7654,9 @@ function domainMatches(host, domains) {
|
|
|
7624
7654
|
const h = host.toLowerCase().replace(/\.$/, "");
|
|
7625
7655
|
return domains.some((d) => {
|
|
7626
7656
|
const raw = d.toLowerCase().replace(/\.$/, "");
|
|
7627
|
-
const
|
|
7628
|
-
const dom = wildcard ? raw.slice(2) : raw;
|
|
7657
|
+
const dom = raw.startsWith("*.") ? raw.slice(2) : raw;
|
|
7629
7658
|
if (!dom) return false;
|
|
7630
|
-
|
|
7631
|
-
return wildcard && h.endsWith(`.${dom}`);
|
|
7659
|
+
return h === dom || h.endsWith(`.${dom}`);
|
|
7632
7660
|
});
|
|
7633
7661
|
}
|
|
7634
7662
|
var isDay = (s) => {
|
|
@@ -7646,6 +7674,7 @@ async function verifyLicense(key, host, opts = {}) {
|
|
|
7646
7674
|
info = JSON.parse(new TextDecoder().decode(b64urlDecode(parts[1])));
|
|
7647
7675
|
if (!Array.isArray(info.domains) || !isDay(info.expires)) return { ok: false, reason: "format" };
|
|
7648
7676
|
if (info.major !== void 0 && !(Number.isInteger(info.major) && info.major > 0)) return { ok: false, reason: "format" };
|
|
7677
|
+
if (info.plan !== void 0 && info.plan !== "company" && info.plan !== "personal") return { ok: false, reason: "format" };
|
|
7649
7678
|
} catch {
|
|
7650
7679
|
return { ok: false, reason: "format" };
|
|
7651
7680
|
}
|
|
@@ -7666,13 +7695,18 @@ async function verifyLicense(key, host, opts = {}) {
|
|
|
7666
7695
|
}
|
|
7667
7696
|
var globalKey;
|
|
7668
7697
|
var statuses = /* @__PURE__ */ new Map();
|
|
7669
|
-
var
|
|
7698
|
+
var statusChecks = /* @__PURE__ */ new Map();
|
|
7699
|
+
var gateNotices = /* @__PURE__ */ new Set();
|
|
7700
|
+
var blockedWarned = /* @__PURE__ */ new Set();
|
|
7670
7701
|
function setLicenseKey(key) {
|
|
7671
7702
|
globalKey = key;
|
|
7672
7703
|
}
|
|
7673
7704
|
var NOTICE = {
|
|
7674
|
-
missing: `FreeGrid is free for
|
|
7675
|
-
FreeGrid
|
|
7705
|
+
missing: `FreeGrid: every feature is free for 30 days on a domain. After that, paid features need a free personal key (${PERSONAL_URL}) or a company key: ${PRICING_URL}
|
|
7706
|
+
FreeGrid: \uB3C4\uBA54\uC778\uB9C8\uB2E4 30\uC77C \uB3D9\uC548 \uBAA8\uB4E0 \uAE30\uB2A5\uC744 \uBB34\uB8CC\uB85C \uC4F8 \uC218 \uC788\uC2B5\uB2C8\uB2E4. \uADF8 \uB4A4 \uC720\uB8CC \uAE30\uB2A5\uC5D0\uB294 \uBB34\uB8CC \uAC1C\uC778 \uD0A4(${PERSONAL_URL}) \uB610\uB294 \uD68C\uC0AC \uD0A4\uAC00 \uD544\uC694\uD569\uB2C8\uB2E4: ${PRICING_URL}`,
|
|
7707
|
+
trial: (ends) => `FreeGrid: the 30-day trial of paid features on this domain ends ${ends}. / \uC774 \uB3C4\uBA54\uC778\uC758 \uC720\uB8CC \uAE30\uB2A5 30\uC77C \uCCB4\uD5D8\uC740 ${ends}\uC5D0 \uB05D\uB0A9\uB2C8\uB2E4.`,
|
|
7708
|
+
ended: `FreeGrid: the 30-day trial on this domain has ended, so paid features are now off. Get a free personal key (${PERSONAL_URL}) or a company key: ${PRICING_URL}
|
|
7709
|
+
FreeGrid: \uC774 \uB3C4\uBA54\uC778\uC758 30\uC77C \uCCB4\uD5D8\uC774 \uB05D\uB098 \uC720\uB8CC \uAE30\uB2A5\uC774 \uAEBC\uC84C\uC2B5\uB2C8\uB2E4. \uBB34\uB8CC \uAC1C\uC778 \uD0A4(${PERSONAL_URL}) \uB610\uB294 \uD68C\uC0AC \uD0A4\uB97C \uBC1B\uC73C\uC138\uC694: ${PRICING_URL}`,
|
|
7676
7710
|
format: "FreeGrid: the license key is not in the right format. / \uB77C\uC774\uC120\uC2A4 \uD0A4 \uD615\uC2DD\uC774 \uC62C\uBC14\uB974\uC9C0 \uC54A\uC2B5\uB2C8\uB2E4.",
|
|
7677
7711
|
signature: "FreeGrid: the license key could not be verified. / \uB77C\uC774\uC120\uC2A4 \uD0A4\uB97C \uD655\uC778\uD560 \uC218 \uC5C6\uC2B5\uB2C8\uB2E4.",
|
|
7678
7712
|
expired: "FreeGrid: the license key has expired. / \uB77C\uC774\uC120\uC2A4 \uAE30\uAC04\uC774 \uB05D\uB0AC\uC2B5\uB2C8\uB2E4.",
|
|
@@ -7716,31 +7750,98 @@ ${PRICING_URL}`);
|
|
|
7716
7750
|
statuses.set(id, status);
|
|
7717
7751
|
return status;
|
|
7718
7752
|
}
|
|
7719
|
-
|
|
7720
|
-
|
|
7721
|
-
|
|
7753
|
+
var gateHost;
|
|
7754
|
+
var currentHost = () => gateHost ?? (typeof location !== "undefined" ? location.hostname : "");
|
|
7755
|
+
var parseStatus = (d) => {
|
|
7756
|
+
if (!d || typeof d !== "object") return null;
|
|
7757
|
+
const { badge, trial } = d;
|
|
7758
|
+
const valid = !!trial && typeof trial === "object" && isDay(trial.started) && isDay(trial.ends) && typeof trial.active === "boolean";
|
|
7759
|
+
return { badge: badge === true, trial: valid ? { started: trial.started, ends: trial.ends, active: trial.active } : null };
|
|
7760
|
+
};
|
|
7761
|
+
function fetchDomainStatus(host = currentHost()) {
|
|
7762
|
+
if (isDevHost(host) || typeof fetch !== "function") return Promise.resolve(null);
|
|
7763
|
+
const pending = statusChecks.get(host);
|
|
7764
|
+
if (pending) return pending;
|
|
7722
7765
|
const day = (/* @__PURE__ */ new Date()).toISOString().slice(0, 10);
|
|
7723
7766
|
let store = null;
|
|
7724
7767
|
try {
|
|
7725
7768
|
store = typeof localStorage !== "undefined" ? localStorage : null;
|
|
7726
|
-
const cached = JSON.parse(store?.getItem("freegrid:
|
|
7727
|
-
|
|
7769
|
+
const cached = JSON.parse(store?.getItem("freegrid:status") ?? "null");
|
|
7770
|
+
const status = cached && cached.host === host && cached.day === day ? parseStatus(cached.status) : null;
|
|
7771
|
+
if (status) {
|
|
7772
|
+
const hit = Promise.resolve(status);
|
|
7773
|
+
statusChecks.set(host, hit);
|
|
7774
|
+
return hit;
|
|
7775
|
+
}
|
|
7728
7776
|
} catch {
|
|
7729
7777
|
}
|
|
7730
|
-
|
|
7731
|
-
const
|
|
7732
|
-
|
|
7733
|
-
|
|
7734
|
-
|
|
7778
|
+
const check = fetch(STATUS_URL, { mode: "cors", credentials: "omit" }).then((r) => r.ok ? r.json() : null).then((d) => {
|
|
7779
|
+
const status = parseStatus(d);
|
|
7780
|
+
if (status) {
|
|
7781
|
+
try {
|
|
7782
|
+
store?.setItem("freegrid:status", JSON.stringify({ host, day, status }));
|
|
7783
|
+
} catch {
|
|
7784
|
+
}
|
|
7735
7785
|
}
|
|
7736
|
-
return
|
|
7737
|
-
}
|
|
7738
|
-
|
|
7786
|
+
return status;
|
|
7787
|
+
}).catch(() => null);
|
|
7788
|
+
statusChecks.set(host, check);
|
|
7789
|
+
return check;
|
|
7790
|
+
}
|
|
7791
|
+
function badgeRequested(host = currentHost()) {
|
|
7792
|
+
return fetchDomainStatus(host).then((s) => s?.badge === true);
|
|
7739
7793
|
}
|
|
7740
|
-
async function needsFreeBadge(key, host =
|
|
7794
|
+
async function needsFreeBadge(key, host = currentHost()) {
|
|
7741
7795
|
if (await checkLicense(key, host)) return false;
|
|
7742
7796
|
return badgeRequested(host);
|
|
7743
7797
|
}
|
|
7798
|
+
var GATE_KEY = "freegrid:gate";
|
|
7799
|
+
var keyIdOf = (key) => {
|
|
7800
|
+
if (!key) return "";
|
|
7801
|
+
const parts = key.trim().split(".");
|
|
7802
|
+
return parts[parts.length - 1].slice(-16);
|
|
7803
|
+
};
|
|
7804
|
+
function paidAllowed(key, host = currentHost()) {
|
|
7805
|
+
if (isDevHost(host)) return true;
|
|
7806
|
+
try {
|
|
7807
|
+
const cache2 = JSON.parse(localStorage.getItem(GATE_KEY) ?? "null");
|
|
7808
|
+
if (!cache2 || cache2.host !== host || cache2.keyId !== keyIdOf(key ?? globalKey)) return true;
|
|
7809
|
+
return cache2.paid !== false;
|
|
7810
|
+
} catch {
|
|
7811
|
+
return true;
|
|
7812
|
+
}
|
|
7813
|
+
}
|
|
7814
|
+
var writeGate = (cache2) => {
|
|
7815
|
+
try {
|
|
7816
|
+
localStorage.setItem(GATE_KEY, JSON.stringify(cache2));
|
|
7817
|
+
} catch {
|
|
7818
|
+
}
|
|
7819
|
+
};
|
|
7820
|
+
async function resolveGate(key, host = currentHost()) {
|
|
7821
|
+
const k = key ?? globalKey;
|
|
7822
|
+
const keyId = keyIdOf(k);
|
|
7823
|
+
if (await checkLicense(k, host)) {
|
|
7824
|
+
writeGate({ host, keyId, paid: true });
|
|
7825
|
+
return true;
|
|
7826
|
+
}
|
|
7827
|
+
const trial = (await fetchDomainStatus(host))?.trial;
|
|
7828
|
+
if (!trial) return true;
|
|
7829
|
+
writeGate({ host, keyId, paid: trial.active, trialEnds: trial.ends });
|
|
7830
|
+
const notice = `${trial.active ? "trial" : "ended"}@${host}`;
|
|
7831
|
+
if (!gateNotices.has(notice)) {
|
|
7832
|
+
gateNotices.add(notice);
|
|
7833
|
+
if (trial.active) console.info(NOTICE.trial(trial.ends));
|
|
7834
|
+
else console.warn(NOTICE.ended);
|
|
7835
|
+
}
|
|
7836
|
+
return trial.active;
|
|
7837
|
+
}
|
|
7838
|
+
function paidBlocked(feature) {
|
|
7839
|
+
if (blockedWarned.has(feature)) return;
|
|
7840
|
+
blockedWarned.add(feature);
|
|
7841
|
+
const name = PAID_FEATURES[feature] ?? { en: feature, ko: feature };
|
|
7842
|
+
console.warn(`FreeGrid: ${name.en} is a paid feature and is off on this domain. Get a free personal key (${PERSONAL_URL}) or a company key: ${PRICING_URL}
|
|
7843
|
+
FreeGrid: ${name.ko}\uC740(\uB294) \uC720\uB8CC \uAE30\uB2A5\uC774\uB77C \uC774 \uB3C4\uBA54\uC778\uC5D0\uC11C \uAEBC\uC838 \uC788\uC2B5\uB2C8\uB2E4. \uBB34\uB8CC \uAC1C\uC778 \uD0A4(${PERSONAL_URL}) \uB610\uB294 \uD68C\uC0AC \uD0A4\uB97C \uBC1B\uC73C\uC138\uC694: ${PRICING_URL}`);
|
|
7844
|
+
}
|
|
7744
7845
|
|
|
7745
7846
|
// src/interactions/menu.ts
|
|
7746
7847
|
var OPS = {
|
|
@@ -7785,7 +7886,7 @@ var ColumnMenu = class {
|
|
|
7785
7886
|
[loc.unpin, () => this.grid.setPinned(colId, null)]
|
|
7786
7887
|
]));
|
|
7787
7888
|
const grouped = this.grid.getGroupBy();
|
|
7788
|
-
if (!this.grid.options.treeData && (this.grid.options.groupPanel || grouped.length)) {
|
|
7889
|
+
if (!this.grid.options.treeData && this.grid.paid && (this.grid.options.groupPanel || grouped.length)) {
|
|
7789
7890
|
const g = grouped.includes(colId) ? [[loc.ungroup, () => this.grid.setGroupBy(grouped.filter((x) => x !== colId))]] : [[loc.groupByThis, () => this.grid.setGroupBy([...grouped, colId])]];
|
|
7790
7891
|
if (grouped.length) g.push([loc.expandAll, () => this.grid.expandAll(true)], [loc.collapseAll, () => this.grid.expandAll(false)]);
|
|
7791
7892
|
popup.append(this.section(g));
|
|
@@ -7796,7 +7897,7 @@ var ColumnMenu = class {
|
|
|
7796
7897
|
];
|
|
7797
7898
|
if (!def.lockVisible) items.push([loc.hideColumn, () => this.grid.setColumnsVisible([colId], false)]);
|
|
7798
7899
|
items.push([loc.chooseColumns, () => this.grid.chooser.open()], [loc.resetColumns, () => this.grid.resetColumns()]);
|
|
7799
|
-
if (this.grid.options.conditionalFormatMenu !== false) items.push([`${loc.cf.title}\u2026`, () => this.grid.formatDialog.open(colId)]);
|
|
7900
|
+
if (this.grid.options.conditionalFormatMenu !== false && this.grid.paid) items.push([`${loc.cf.title}\u2026`, () => this.grid.formatDialog.open(colId)]);
|
|
7800
7901
|
popup.append(this.section(items));
|
|
7801
7902
|
const filter = this.filterSection(colId, () => this.close());
|
|
7802
7903
|
if (filter) popup.append(filter);
|
|
@@ -9704,6 +9805,7 @@ var Grid = class {
|
|
|
9704
9805
|
this.pageSize = DEFAULT_PAGE_SIZE;
|
|
9705
9806
|
/** What formulas see: every source row, and computed columns by id. */
|
|
9706
9807
|
this.formulaScope = {
|
|
9808
|
+
off: () => !this.allow("formulas"),
|
|
9707
9809
|
rows: () => this.store.state.data,
|
|
9708
9810
|
version: () => this.editVersion,
|
|
9709
9811
|
computed: (name) => {
|
|
@@ -9768,6 +9870,7 @@ var Grid = class {
|
|
|
9768
9870
|
this.selectionVersion = 0;
|
|
9769
9871
|
/** Rows select-all acts on: displayed data rows that are selectable. */
|
|
9770
9872
|
this.selectableCache = null;
|
|
9873
|
+
this.paid = paidAllowed(options.licenseKey);
|
|
9771
9874
|
if (!Array.isArray(options.columns)) warn("options.columns must be an array");
|
|
9772
9875
|
if (options.data != null && !Array.isArray(options.data)) warn("options.data must be an array");
|
|
9773
9876
|
this.store = new Store({
|
|
@@ -9809,12 +9912,12 @@ var Grid = class {
|
|
|
9809
9912
|
collator: () => this.collator(),
|
|
9810
9913
|
textVersion: () => `${this.store.state.locale.code}|${this.editVersion}`
|
|
9811
9914
|
});
|
|
9812
|
-
this.groupBy = this.validGroupBy(options.groupBy
|
|
9813
|
-
if (Array.isArray(options.conditionalFormats)) this.cfRules = options.conditionalFormats.filter((r) => r && typeof r === "object" && Array.isArray(r.columns)).map((r) => ({ ...r, columns: [...r.columns] }));
|
|
9814
|
-
this.defaultExpanded = options.groupDefaultExpanded ?? 0;
|
|
9915
|
+
if (options.groupBy?.length && this.allow(this.rowModel.server ? "serverGroups" : "grouping")) this.groupBy = this.validGroupBy(options.groupBy);
|
|
9916
|
+
if (Array.isArray(options.conditionalFormats) && options.conditionalFormats.length && this.allow("conditional")) this.cfRules = options.conditionalFormats.filter((r) => r && typeof r === "object" && Array.isArray(r.columns)).map((r) => ({ ...r, columns: [...r.columns] }));
|
|
9917
|
+
this.defaultExpanded = options.treeData || this.paid ? options.groupDefaultExpanded ?? 0 : 0;
|
|
9815
9918
|
if (options.treeData && !options.getRowId) warn("treeData needs getRowId so parent ids can be matched");
|
|
9816
9919
|
this.updateRows();
|
|
9817
|
-
this.summaryOpts = options.summary
|
|
9920
|
+
this.summaryOpts = options.summary && this.allow("summary") ? options.summary : null;
|
|
9818
9921
|
this.summarySnapshot = snapshot(this.summaryOpts);
|
|
9819
9922
|
this.refreshSummary();
|
|
9820
9923
|
const explicit = options.fontSize != null && options.fontSize > 0 ? options.fontSize : null;
|
|
@@ -9835,9 +9938,9 @@ var Grid = class {
|
|
|
9835
9938
|
this.editor = new CellEditor(this);
|
|
9836
9939
|
this.finder = new Finder(this);
|
|
9837
9940
|
this.fillHandle = new FillHandle(this);
|
|
9838
|
-
this.groupPanel = options.groupPanel ? new GroupPanel(this) : null;
|
|
9941
|
+
this.groupPanel = options.groupPanel && this.allow("grouping") ? new GroupPanel(this) : null;
|
|
9839
9942
|
this.pinnedRows = { top: cleanRows(options.pinnedTopRows ?? []), bottom: cleanRows(options.pinnedBottomRows ?? []) };
|
|
9840
|
-
this.sideBar = options.sideBar ? new SideBar(
|
|
9943
|
+
this.sideBar = options.sideBar && this.allow("sideBar") ? new SideBar(
|
|
9841
9944
|
this,
|
|
9842
9945
|
typeof options.sideBar === "object" && !!options.sideBar.open,
|
|
9843
9946
|
(typeof options.sideBar === "object" ? options.sideBar.panels : void 0) ?? (options.pivot || options.pivotPanel ? ["columns", "filters", "pivot"] : void 0)
|
|
@@ -9870,11 +9973,23 @@ var Grid = class {
|
|
|
9870
9973
|
this.renderer.render();
|
|
9871
9974
|
this.api = this.createApi();
|
|
9872
9975
|
liveGrids.set(this.renderer.root, this);
|
|
9976
|
+
apiGrids.set(this.api, this);
|
|
9977
|
+
void resolveGate(options.licenseKey);
|
|
9873
9978
|
}
|
|
9874
9979
|
/** Bumped whenever cell values change (edits, transactions, undo). */
|
|
9875
9980
|
get valuesVersion() {
|
|
9876
9981
|
return this.editVersion;
|
|
9877
9982
|
}
|
|
9983
|
+
/** Cell ranges are on: not switched off and paid features allowed. Closed behaves exactly like `rangeSelection: false`. */
|
|
9984
|
+
rangesOn() {
|
|
9985
|
+
return this.options.rangeSelection !== false && this.paid;
|
|
9986
|
+
}
|
|
9987
|
+
/** Whether a paid feature may run; warns once per feature when it may not. */
|
|
9988
|
+
allow(feature) {
|
|
9989
|
+
if (this.paid) return true;
|
|
9990
|
+
paidBlocked(feature);
|
|
9991
|
+
return false;
|
|
9992
|
+
}
|
|
9878
9993
|
/** The column definitions as given (setColumns), without the system columns. */
|
|
9879
9994
|
columnDefs() {
|
|
9880
9995
|
return [...this.sourceDefs];
|
|
@@ -9887,6 +10002,7 @@ var Grid = class {
|
|
|
9887
10002
|
warn("setConditionalFormats: formats must be an array");
|
|
9888
10003
|
return;
|
|
9889
10004
|
}
|
|
10005
|
+
if (list.length && !this.allow("conditional")) return;
|
|
9890
10006
|
this.cfRules = list.filter((r) => r && typeof r === "object" && Array.isArray(r.columns)).map((r) => ({ ...r, columns: [...r.columns] }));
|
|
9891
10007
|
this.renderer.invalidate({ rows: true });
|
|
9892
10008
|
this.events.emit("conditionalFormatsChange", { formats: this.getConditionalFormats() });
|
|
@@ -9984,6 +10100,7 @@ var Grid = class {
|
|
|
9984
10100
|
return out;
|
|
9985
10101
|
}
|
|
9986
10102
|
setPivot(cfg) {
|
|
10103
|
+
if (cfg && !this.allow("pivot")) return;
|
|
9987
10104
|
if (cfg && this.clientOnly("pivot")) return;
|
|
9988
10105
|
if (cfg && (typeof cfg !== "object" || !Array.isArray(cfg.rows) || !Array.isArray(cfg.values))) {
|
|
9989
10106
|
warn("setPivot: expected { rows: string[], values: [...] }");
|
|
@@ -10173,6 +10290,15 @@ var Grid = class {
|
|
|
10173
10290
|
}
|
|
10174
10291
|
const update = (r, k, row) => {
|
|
10175
10292
|
const orig = this.tracker.originals.get(row);
|
|
10293
|
+
if (r === row) {
|
|
10294
|
+
for (const field of Object.keys(r)) {
|
|
10295
|
+
if (orig) orig[field] = r[field];
|
|
10296
|
+
changed.fields.add(field);
|
|
10297
|
+
}
|
|
10298
|
+
changed.ids.add(k);
|
|
10299
|
+
result.updated.push(row);
|
|
10300
|
+
return;
|
|
10301
|
+
}
|
|
10176
10302
|
for (const [field, value] of Object.entries(r)) {
|
|
10177
10303
|
if (orig) orig[field] = value;
|
|
10178
10304
|
const old = row[field];
|
|
@@ -10230,6 +10356,7 @@ var Grid = class {
|
|
|
10230
10356
|
}
|
|
10231
10357
|
setAdvancedFilter(filter) {
|
|
10232
10358
|
const next = filter && typeof filter === "object" && Array.isArray(filter.conditions) && filter.conditions.length ? filter : null;
|
|
10359
|
+
if (next && !this.allow("advancedFilter")) return;
|
|
10233
10360
|
if (next === this.store.state.advancedFilter) return;
|
|
10234
10361
|
this.store.set({ advancedFilter: next });
|
|
10235
10362
|
this.events.emit("advancedFilterChange", { advancedFilter: next });
|
|
@@ -10441,6 +10568,7 @@ var Grid = class {
|
|
|
10441
10568
|
return [...this.groupBy];
|
|
10442
10569
|
}
|
|
10443
10570
|
setGroupBy(ids) {
|
|
10571
|
+
if (Array.isArray(ids) && ids.length && !this.allow(this.rowModel.server ? "serverGroups" : "grouping")) return;
|
|
10444
10572
|
const next = this.validGroupBy(ids);
|
|
10445
10573
|
if (next.join("") === this.groupBy.join("")) return;
|
|
10446
10574
|
this.groupBy = next;
|
|
@@ -10726,6 +10854,7 @@ var Grid = class {
|
|
|
10726
10854
|
warn("setSummary: summary.rows must be an array");
|
|
10727
10855
|
return;
|
|
10728
10856
|
}
|
|
10857
|
+
if (opts && !this.allow("summary")) return;
|
|
10729
10858
|
if (same2(opts, this.summarySnapshot)) {
|
|
10730
10859
|
this.summaryOpts = opts;
|
|
10731
10860
|
return;
|
|
@@ -10750,13 +10879,14 @@ var Grid = class {
|
|
|
10750
10879
|
return this.columns.displayed().filter((c) => !isSystemCol(c.id));
|
|
10751
10880
|
}
|
|
10752
10881
|
validRange(range) {
|
|
10753
|
-
if (!range || this.
|
|
10882
|
+
if (!range || !this.rangesOn()) return null;
|
|
10754
10883
|
const count = this.rowModel.getRowCount();
|
|
10755
10884
|
const ok = (p) => !!p && !isSystemCol(p.colId) && !!this.columns.getColumn(p.colId) && p.rowIndex >= 0 && p.rowIndex < count;
|
|
10756
10885
|
return ok(range.start) && ok(range.end) ? { start: { ...range.start }, end: { ...range.end } } : null;
|
|
10757
10886
|
}
|
|
10758
10887
|
/** Set the active range. Earlier Ctrl ranges are dropped unless `keepOthers`. */
|
|
10759
10888
|
setRange(range, keepOthers = false) {
|
|
10889
|
+
if (range && (range.start?.rowIndex !== range.end?.rowIndex || range.start?.colId !== range.end?.colId) && !this.allow("range")) return;
|
|
10760
10890
|
const next = this.validRange(range);
|
|
10761
10891
|
const extras = keepOthers || !this.store.state.extraRanges.length ? this.store.state.extraRanges : [];
|
|
10762
10892
|
const cur = this.store.state.range;
|
|
@@ -10766,6 +10896,7 @@ var Grid = class {
|
|
|
10766
10896
|
}
|
|
10767
10897
|
/** Ctrl+click / Ctrl+drag: keep what is selected (the focused cell when there is no range) and start a new range. */
|
|
10768
10898
|
addRange(range) {
|
|
10899
|
+
if (!this.allow("range")) return;
|
|
10769
10900
|
const { range: active, focus } = this.store.state;
|
|
10770
10901
|
const keep = this.validRange(active ?? (focus ? { start: focus, end: focus } : null));
|
|
10771
10902
|
if (keep) this.store.set({ extraRanges: [...this.store.state.extraRanges, keep] });
|
|
@@ -10777,6 +10908,7 @@ var Grid = class {
|
|
|
10777
10908
|
return all2.map((r) => ({ start: { ...r.start }, end: { ...r.end } }));
|
|
10778
10909
|
}
|
|
10779
10910
|
setRanges(ranges) {
|
|
10911
|
+
if (Array.isArray(ranges) && ranges.length && !this.allow("range")) return;
|
|
10780
10912
|
const list = (Array.isArray(ranges) ? ranges : []).map((r) => this.validRange(r)).filter((r) => !!r);
|
|
10781
10913
|
this.store.set({ extraRanges: list.slice(0, -1) });
|
|
10782
10914
|
this.setRange(list[list.length - 1] ?? null, true);
|
|
@@ -10800,6 +10932,7 @@ var Grid = class {
|
|
|
10800
10932
|
return !!col && this.allRangeBounds().some((b) => rangeContains(b, rowIndex, col.index));
|
|
10801
10933
|
}
|
|
10802
10934
|
selectAllCells() {
|
|
10935
|
+
if (!this.allow("range")) return;
|
|
10803
10936
|
const cols = this.dataColumns();
|
|
10804
10937
|
const count = this.rowModel.getRowCount();
|
|
10805
10938
|
if (!cols.length || !count) return;
|
|
@@ -10809,6 +10942,7 @@ var Grid = class {
|
|
|
10809
10942
|
// ── clipboard ───────────────────────────────────────────────
|
|
10810
10943
|
/** TSV for the range, else the focused cell; `rows` copies the checked rows instead. */
|
|
10811
10944
|
selectionTSV(headers, rows = false) {
|
|
10945
|
+
if (!rows && this.multiCell() && !this.allow("clipboard")) return "";
|
|
10812
10946
|
const code = this.store.state.locale.code;
|
|
10813
10947
|
const displayed = this.columns.displayed();
|
|
10814
10948
|
const b = rows ? null : this.rangeBounds();
|
|
@@ -10850,6 +10984,11 @@ var Grid = class {
|
|
|
10850
10984
|
if (headers) matrix.unshift(cols.map((c) => c.def.header ?? c.def.field ?? c.id));
|
|
10851
10985
|
return toTSV(matrix);
|
|
10852
10986
|
}
|
|
10987
|
+
/** The selection covers more than one cell (several ranges, or one range wider than a cell). */
|
|
10988
|
+
multiCell() {
|
|
10989
|
+
const all2 = this.allRangeBounds();
|
|
10990
|
+
return all2.length > 1 || all2.some((b) => b.r1 !== b.r2 || b.c1 !== b.c2);
|
|
10991
|
+
}
|
|
10853
10992
|
/** Like Excel: ranges stack when they share columns or rows; otherwise only the active one is copied. */
|
|
10854
10993
|
rangesStack(all2) {
|
|
10855
10994
|
return all2.length > 1 && (all2.every((x) => x.c1 === all2[0].c1 && x.c2 === all2[0].c2) || all2.every((x) => x.r1 === all2[0].r1 && x.r2 === all2[0].r2));
|
|
@@ -10900,6 +11039,7 @@ var Grid = class {
|
|
|
10900
11039
|
const matrix = parseTSV(text2);
|
|
10901
11040
|
const result = { changed: 0, skipped: 0 };
|
|
10902
11041
|
if (!matrix.length) return result;
|
|
11042
|
+
if ((matrix.length > 1 || matrix[0].length > 1) && !this.allow("clipboard")) return result;
|
|
10903
11043
|
const displayed = this.columns.displayed();
|
|
10904
11044
|
const count = this.rowModel.getRowCount();
|
|
10905
11045
|
const b = this.rangeBounds();
|
|
@@ -11008,6 +11148,7 @@ var Grid = class {
|
|
|
11008
11148
|
/** Merge blocks for every `merge` column over the displayed rows (memoized). */
|
|
11009
11149
|
mergeSpans() {
|
|
11010
11150
|
if (this.rowModel.server || !this.columns.displayed().some((c) => c.def.merge || c.def.rowSpan)) return NO_SPANS;
|
|
11151
|
+
if (!this.allow("merge")) return NO_SPANS;
|
|
11011
11152
|
const rows = this.rowModel.rows();
|
|
11012
11153
|
const ver = this.editVersion * 1e6 + this.columns.displayed().length;
|
|
11013
11154
|
if (this.spanCache && this.spanCache.rows === rows && this.spanCache.ver === ver) return this.spanCache.spans;
|
|
@@ -11022,7 +11163,7 @@ var Grid = class {
|
|
|
11022
11163
|
}
|
|
11023
11164
|
rangeAggregates() {
|
|
11024
11165
|
const all2 = this.allRangeBounds();
|
|
11025
|
-
if (!all2.length) return null;
|
|
11166
|
+
if (!all2.length || !this.paid) return null;
|
|
11026
11167
|
const displayed = this.columns.displayed();
|
|
11027
11168
|
const key = [JSON.stringify(all2), this.rowModel.rows(), this.editVersion, ...displayed.map((c) => c.def)];
|
|
11028
11169
|
const memo = this.aggMemo;
|
|
@@ -11061,6 +11202,7 @@ var Grid = class {
|
|
|
11061
11202
|
}
|
|
11062
11203
|
/** Fill from the current selection into `target`, Excel-style (one undo step). */
|
|
11063
11204
|
fillRange(target) {
|
|
11205
|
+
if (!this.allow("fill")) return;
|
|
11064
11206
|
const src = this.rangeBounds() ?? (() => {
|
|
11065
11207
|
const f = this.store.state.focus;
|
|
11066
11208
|
const col = f && this.columns.getColumn(f.colId);
|
|
@@ -11109,30 +11251,31 @@ var Grid = class {
|
|
|
11109
11251
|
this.renderer.invalidate({ rows: true });
|
|
11110
11252
|
}
|
|
11111
11253
|
// ── edits, change tracking, undo ────────────────────────────
|
|
11112
|
-
/** Write one cell
|
|
11254
|
+
/** Write one cell and record it for undo; endCellTx emits cellValueChanged. Returns false when unchanged. */
|
|
11113
11255
|
writeCell(changes, row, field, colId, rowIndex, value) {
|
|
11114
11256
|
const oldValue = row[field];
|
|
11115
11257
|
if (Object.is(oldValue, value)) return false;
|
|
11116
11258
|
if (this.veto("beforeCellChange", { row, rowIndex, colId, oldValue, newValue: value })) return false;
|
|
11117
11259
|
this.tracker.beforeChange(row);
|
|
11118
11260
|
row[field] = value;
|
|
11119
|
-
changes.push({ row, field, colId, oldValue, newValue: value });
|
|
11120
|
-
this.events.emit("cellValueChanged", { row, rowIndex, colId, oldValue, newValue: value });
|
|
11261
|
+
changes.push({ row, field, colId, rowIndex, oldValue, newValue: value });
|
|
11121
11262
|
return true;
|
|
11122
11263
|
}
|
|
11123
|
-
/** Finish a batch of cell writes: one undo step, refresh summary and repaint. */
|
|
11264
|
+
/** Finish a batch of cell writes: one undo step, refresh summary and repaint, then emit cellValueChanged. */
|
|
11124
11265
|
endCellTx(changes) {
|
|
11125
11266
|
if (!changes.length) return;
|
|
11126
11267
|
const apply = (list, key) => {
|
|
11127
|
-
|
|
11268
|
+
const done = list.map((c) => {
|
|
11128
11269
|
const from = c.row[c.field];
|
|
11129
11270
|
c.row[c.field] = c[key];
|
|
11130
|
-
|
|
11131
|
-
}
|
|
11271
|
+
return { row: c.row, rowIndex: this.rowModel.indexOfId(this.rowModel.idOf(c.row)), colId: c.colId, oldValue: from, newValue: c[key] };
|
|
11272
|
+
});
|
|
11132
11273
|
this.afterDataEdit();
|
|
11274
|
+
for (const e of done) this.events.emit("cellValueChanged", e);
|
|
11133
11275
|
};
|
|
11134
11276
|
this.history.push({ undo: () => apply([...changes].reverse(), "oldValue"), redo: () => apply(changes, "newValue") });
|
|
11135
11277
|
this.afterDataEdit();
|
|
11278
|
+
for (const c of changes) this.events.emit("cellValueChanged", { row: c.row, rowIndex: c.rowIndex, colId: c.colId, oldValue: c.oldValue, newValue: c.newValue });
|
|
11136
11279
|
}
|
|
11137
11280
|
afterDataEdit() {
|
|
11138
11281
|
this.renderer.invalidateHeights();
|
|
@@ -11536,7 +11679,8 @@ var Grid = class {
|
|
|
11536
11679
|
this.treeLoaded.clear();
|
|
11537
11680
|
this.treeLoading.clear();
|
|
11538
11681
|
this.renderer.dropDetail();
|
|
11539
|
-
|
|
11682
|
+
const clean = cleanRows(rows);
|
|
11683
|
+
this.store.set({ data: clean === this.store.state.data ? [...clean] : clean });
|
|
11540
11684
|
if (options?.scrollToTop) this.renderer.setScroll({ top: 0, left: 0 });
|
|
11541
11685
|
const sel = this.store.state.selected;
|
|
11542
11686
|
if (sel.size) {
|
|
@@ -11644,11 +11788,13 @@ var Grid = class {
|
|
|
11644
11788
|
this.forcedOverlay = kind === "loading" || kind === "noRows" ? kind : null;
|
|
11645
11789
|
this.renderer.invalidate();
|
|
11646
11790
|
}),
|
|
11647
|
-
|
|
11791
|
+
// closed: an empty sheet, so writeXlsx([api.getXlsxSheet()]) is not an Excel export
|
|
11792
|
+
getXlsxSheet: guard((o) => this.allow("excel") ? buildXlsxSheet(this, o) : { name: o?.sheetName ?? "Sheet1", rows: [] }),
|
|
11648
11793
|
print: guard((o) => printHtml(buildPrintHtml(this, o))),
|
|
11649
11794
|
getPrintHtml: guard((o) => buildPrintHtml(this, o)),
|
|
11650
|
-
getPdf: guard((o) => renderPdf(printModel(this, o), browserSurface, { scale: o?.resolution, quality: o?.quality })),
|
|
11795
|
+
getPdf: guard((o) => this.allow("pdf") ? renderPdf(printModel(this, o), browserSurface, { scale: o?.resolution, quality: o?.quality }) : null),
|
|
11651
11796
|
exportPdf: guard((name = "export.pdf", o) => {
|
|
11797
|
+
if (!this.allow("pdf")) return;
|
|
11652
11798
|
const bytes = renderPdf(printModel(this, o), browserSurface, { scale: o?.resolution, quality: o?.quality });
|
|
11653
11799
|
if (bytes) download(name, [bytes], "application/pdf");
|
|
11654
11800
|
else warn("exportPdf: this browser cannot draw on a canvas");
|
|
@@ -11656,16 +11802,26 @@ var Grid = class {
|
|
|
11656
11802
|
setColumnGroupOpened: guard((id, open) => this.setColumnGroupOpened(id, open)),
|
|
11657
11803
|
setAdvancedFilter: guard((f) => this.setAdvancedFilter(f)),
|
|
11658
11804
|
getAdvancedFilter: guard(() => this.store.state.advancedFilter ? JSON.parse(JSON.stringify(this.store.state.advancedFilter)) : null),
|
|
11659
|
-
openFilterBuilder: guard(() =>
|
|
11805
|
+
openFilterBuilder: guard(() => {
|
|
11806
|
+
if (!this.allow("advancedFilter")) return;
|
|
11807
|
+
this.filterBuilder.open();
|
|
11808
|
+
}),
|
|
11660
11809
|
isColumnGroupOpened: guard((id) => !!this.columns.getNode(id)?.open),
|
|
11661
11810
|
applyTransaction: guard((tx) => this.applyTransaction(tx)),
|
|
11662
11811
|
applyTransactionAsync: guard((tx) => this.applyTransactionAsync(tx)),
|
|
11663
11812
|
flushAsyncTransactions: guard(() => this.flushAsyncTransactions()),
|
|
11664
11813
|
setConditionalFormats: guard((f) => this.setConditionalFormats(f)),
|
|
11665
11814
|
getConditionalFormats: guard(() => this.getConditionalFormats()),
|
|
11666
|
-
openConditionalFormats: guard((colId) =>
|
|
11667
|
-
|
|
11668
|
-
|
|
11815
|
+
openConditionalFormats: guard((colId) => {
|
|
11816
|
+
if (!this.allow("conditional")) return;
|
|
11817
|
+
this.formatDialog.open(colId);
|
|
11818
|
+
}),
|
|
11819
|
+
// closed: as if `assistant` were not set
|
|
11820
|
+
askAssistant: guard((prompt) => askAssistant(this, prompt, this.allow("assistant") ? this.options.assistant : void 0)),
|
|
11821
|
+
openAssistant: guard(() => {
|
|
11822
|
+
if (!this.allow("assistant")) return;
|
|
11823
|
+
this.assistantPanel.open();
|
|
11824
|
+
}),
|
|
11669
11825
|
getState: guard(() => this.getState()),
|
|
11670
11826
|
setState: guard((s, o) => this.setState(s, o)),
|
|
11671
11827
|
getScrollPosition: guard(() => this.renderer.getScroll()),
|
|
@@ -11679,7 +11835,10 @@ var Grid = class {
|
|
|
11679
11835
|
resetColumnState: guard(() => this.resetColumns()),
|
|
11680
11836
|
openColumnChooser: guard(() => this.chooser.open()),
|
|
11681
11837
|
closeColumnChooser: guard(() => this.chooser.close()),
|
|
11682
|
-
toggleSideBar: guard((open, panel) =>
|
|
11838
|
+
toggleSideBar: guard((open, panel) => {
|
|
11839
|
+
if (!this.allow("sideBar")) return;
|
|
11840
|
+
this.sideBar?.toggle(open, panel);
|
|
11841
|
+
}),
|
|
11683
11842
|
setSummary: guard((s) => this.setSummary(s)),
|
|
11684
11843
|
getSummaryValues: guard(() => this.summaryValues.map((v) => ({ ...v }))),
|
|
11685
11844
|
getRange: guard(() => {
|
|
@@ -11709,7 +11868,7 @@ var Grid = class {
|
|
|
11709
11868
|
canRedo: guard(() => this.history.canRedo()),
|
|
11710
11869
|
fillRange: guard((t) => this.fillRange(t)),
|
|
11711
11870
|
setPinnedRows: guard((r) => this.setPinnedRows(r)),
|
|
11712
|
-
find: guard((text2) => this.finder.find(text2)),
|
|
11871
|
+
find: guard((text2) => this.allow("find") ? this.finder.find(text2) : 0),
|
|
11713
11872
|
findNext: guard(() => this.finder.next()),
|
|
11714
11873
|
findPrevious: guard(() => this.finder.previous()),
|
|
11715
11874
|
clearFind: guard(() => this.finder.clear()),
|
|
@@ -11730,13 +11889,17 @@ var Grid = class {
|
|
|
11730
11889
|
refreshRows: guard(() => this.refreshRows()),
|
|
11731
11890
|
setPivot: guard((p) => this.setPivot(p)),
|
|
11732
11891
|
getPivot: guard(() => this.getPivot()),
|
|
11733
|
-
|
|
11892
|
+
// closed: an inert handle whose methods do nothing
|
|
11893
|
+
createChart: guard((o) => this.allow("charts") ? this.charts.create(o) : { el: el("div", "fg-chart-body"), setType: () => void 0, update: () => void 0, destroy: () => void 0 }),
|
|
11734
11894
|
getCsv: guard((o) => buildCsv(this, o)),
|
|
11735
11895
|
getJson: guard((o) => buildJson(this, o)),
|
|
11736
11896
|
exportJson: guard((name = "export.json", o) => download(name, [buildJson(this, o)], "application/json;charset=utf-8")),
|
|
11737
11897
|
exportCsv: guard((name = "export.csv", o) => download(name, ["\uFEFF", buildCsv(this, o)], "text/csv;charset=utf-8")),
|
|
11738
|
-
getXlsx: guard((o) => buildXlsx(this, o)),
|
|
11739
|
-
exportXlsx: guard((name = "export.xlsx", o) =>
|
|
11898
|
+
getXlsx: guard((o) => this.allow("excel") ? buildXlsx(this, o) : new Uint8Array(0)),
|
|
11899
|
+
exportXlsx: guard((name = "export.xlsx", o) => {
|
|
11900
|
+
if (!this.allow("excel")) return;
|
|
11901
|
+
download(name, [buildXlsx(this, o)], "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
|
11902
|
+
}),
|
|
11740
11903
|
importFile: guard((file, o) => importInto(this, file, o)),
|
|
11741
11904
|
on: guard((type, fn) => this.events.on(type, fn)),
|
|
11742
11905
|
destroy: () => this.destroy()
|
|
@@ -11764,10 +11927,13 @@ function buildWorkbook(sheets) {
|
|
|
11764
11927
|
warn("buildWorkbook: no grids given; the file has one empty sheet");
|
|
11765
11928
|
return writeXlsx([{ name: "Sheet1", rows: [] }]);
|
|
11766
11929
|
}
|
|
11930
|
+
if (list.some((s) => gridOfApi(s.grid)?.allow("workbook") === false)) return new Uint8Array(0);
|
|
11767
11931
|
return writeXlsx(list.map((s, i) => s.grid.getXlsxSheet({ ...s.options, sheetName: s.sheetName ?? s.options?.sheetName ?? `Sheet${i + 1}` })));
|
|
11768
11932
|
}
|
|
11769
11933
|
function exportWorkbook(filename, sheets) {
|
|
11770
|
-
|
|
11934
|
+
const bytes = buildWorkbook(sheets);
|
|
11935
|
+
if (!bytes.length) return;
|
|
11936
|
+
download(filename || "export.xlsx", [bytes], "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
|
|
11771
11937
|
}
|
|
11772
11938
|
|
|
11773
11939
|
// src/rest.ts
|