@svgrid/grid 3.0.7 → 3.0.8

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +1 -1
  2. package/dist/SvGrid.controller.svelte.js +16 -5
  3. package/dist/SvGrid.svelte +4 -1
  4. package/dist/cdn/{GridMenus-BgUS-W3o.js → GridMenus-B8-Gbfq2.js} +1 -1
  5. package/dist/cdn/{GridMenus-CcwU8I4t.js → GridMenus-D15bhpX5.js} +1 -1
  6. package/dist/cdn/{src-25JmJ_7g.js → src-CleI0g9l.js} +17 -12
  7. package/dist/cdn/{src-kE9B98r6.js → src-Dz625Fhx.js} +17 -12
  8. package/dist/cdn/svgrid.js +1 -1
  9. package/dist/cdn/svgrid.svelte-external.js +1 -1
  10. package/dist/cdn/validate-GEs4ZPSW.js +80 -0
  11. package/dist/core.js +7 -2
  12. package/dist/headless.d.ts +1 -1
  13. package/dist/headless.js +1 -1
  14. package/dist/index.d.ts +1 -1
  15. package/dist/index.js +1 -1
  16. package/dist/keyboard-handlers.js +5 -1
  17. package/dist/selection.js +14 -4
  18. package/dist/{subscribe.js → subscribe.svelte.js} +5 -1
  19. package/dist/validate.js +10 -3
  20. package/package.json +1 -1
  21. package/src/SvGrid.controller.svelte.ts +15 -9
  22. package/src/SvGrid.svelte +4 -1
  23. package/src/core.ts +5 -2
  24. package/src/headless.reactivity.svelte.test.ts +89 -0
  25. package/src/headless.ts +1 -1
  26. package/src/index.ts +1 -1
  27. package/src/keyboard-handlers.ts +5 -1
  28. package/src/selection.test.ts +37 -0
  29. package/src/selection.ts +16 -6
  30. package/src/{subscribe.test.ts → subscribe.svelte.test.ts} +1 -1
  31. package/src/{subscribe.ts → subscribe.svelte.ts} +5 -1
  32. package/src/svgrid.behavior.test.ts +29 -0
  33. package/src/validate.test.ts +16 -0
  34. package/src/validate.ts +10 -2
  35. package/dist/cdn/validate-AZoD8BoR.js +0 -80
  36. /package/dist/{subscribe.d.ts → subscribe.svelte.d.ts} +0 -0
package/README.md CHANGED
@@ -180,7 +180,7 @@ The pack sells in two editions, both covering unlimited production apps.
180
180
  merges, frozen panes, named ranges and charts surviving the round trip.
181
181
  - **SvGrid Studio.** The designer, the code generator and the SQL data sources.
182
182
 
183
- Both editions come with a 30-day evaluation period: every feature runs without a key, and under a
183
+ You can evaluate both editions without asking for a key: every feature runs without one, and under a
184
184
  key that does not cover it, with a watermark and a console message about the Enterprise package. `licenseCovers('spreadsheet')` tells you which
185
185
  side of the line the current key is on, if you would rather hide an entry point than let it nudge.
186
186
 
@@ -1386,11 +1386,22 @@ export function createSvGridController(rawProps, domIdBase) {
1386
1386
  return rawRows;
1387
1387
  let rows = rawRows;
1388
1388
  if (globalFilter.trim()) {
1389
- const needle = normalizeForFilter(globalFilter, (props.filterLocale ?? props.localization?.locale));
1390
- rows = rows.filter((row) => row
1391
- .getAllCells()
1392
- .some((cell) => normalizeForFilter(String(cell.getValue() ?? ""), (props.filterLocale ?? props.localization?.locale))
1393
- .includes(needle)));
1389
+ const locale = props.filterLocale ?? props.localization?.locale;
1390
+ const needle = normalizeForFilter(globalFilter, locale);
1391
+ // Read the values, not the cells. `getAllCells()` built a cell object
1392
+ // with two closures per column on EVERY row and kept them on the row:
1393
+ // ten million objects for one keystroke over a million rows, and a
1394
+ // search that froze the page for seconds. `cell.getValue()` and
1395
+ // `getCellValueByColumnId` read the same per-row values array, so the
1396
+ // answer is the same. The columns are the ones the rows were built with.
1397
+ const columnIds = grid.getAllColumns().map((c) => c.id);
1398
+ rows = rows.filter((row) => {
1399
+ for (const id of columnIds) {
1400
+ if (normalizeForFilter(String(row.getCellValueByColumnId(id) ?? ""), locale).includes(needle))
1401
+ return true;
1402
+ }
1403
+ return false;
1404
+ });
1394
1405
  }
1395
1406
  rows = applyColumnFilters(rows);
1396
1407
  // --- Advanced filter (Pro) ---------------------------------------------
@@ -1212,7 +1212,10 @@
1212
1212
  column: Column<TData>,
1213
1213
  cellValue: unknown,
1214
1214
  )}
1215
- {#if column.columnDef.editorType === "checkbox" || typeof cellValue === "boolean"}
1215
+ <!-- A column's own `cell` renderer wins over every built-in display, the
1216
+ checkbox included: a boolean field drawn as a flag button or a status
1217
+ pill used to come out as a read-only checkbox, with `cell` never called. -->
1218
+ {#if (column.columnDef.editorType === "checkbox" || typeof cellValue === "boolean") && column.columnDef.cell == null}
1216
1219
  <div
1217
1220
  class="sv-grid-checkbox sv-grid-checkbox-readonly"
1218
1221
  role="checkbox"
@@ -1,4 +1,4 @@
1
- import { $a as e, Fn as t, Qa as n } from "./src-kE9B98r6.js";
1
+ import { $a as e, Fn as t, Qa as n } from "./src-Dz625Fhx.js";
2
2
  import { S as r, _ as i, j as a, k as o } from "./editor-registry-BVeHvlZR.js";
3
3
  import { r as s } from "./dismissable-Brc4EZU1.js";
4
4
  import * as c from "svelte/internal/client";
@@ -1,4 +1,4 @@
1
- import { $a as e, Fn as t, Qa as n } from "./src-25JmJ_7g.js";
1
+ import { $a as e, Fn as t, Qa as n } from "./src-CleI0g9l.js";
2
2
  import { $ as r, D as i, E as a, F as o, G as s, H as c, I as l, L as u, N as d, Q as f, R as p, U as m, V as h, X as g, Y as _, Z as v, _ as y, at as b, b as x, d as S, et as C, f as w, ft as T, g as E, h as D, it as O, j as k, k as A, lt as ee, m as j, mt as te, nt as M, o as ne, pt as N, st as P, t as re, tt as F, u as I, ut as L, v as R, w as ie } from "./disclose-version-Dr7rEVv-.js";
3
3
  import { S as ae, _ as oe, j as se, k as ce } from "./editor-registry-BVeHvlZR.js";
4
4
  import { r as le } from "./dismissable-Brc4EZU1.js";
@@ -943,7 +943,7 @@ function kn(e) {
943
943
  for (let e = 0; e < r.length; e++) {
944
944
  let t = r[e], n = i?.[e];
945
945
  if (n && n.original === t) {
946
- n[an] = null, n[on] = null, c[e] = n;
946
+ n[an] !== null && (n[an] = null), n[on] !== null && (n[on] = null), c[e] = n;
947
947
  continue;
948
948
  }
949
949
  l && l.size * 4 >= r.length && (l = null);
@@ -1036,12 +1036,12 @@ var Pn = Nn, Fn = (e, t) => {
1036
1036
  return i.length === a.length && i.every((e) => Object.is(n[e], r[e]));
1037
1037
  };
1038
1038
  function In(e, t) {
1039
- let n = t(e.store.state);
1039
+ let n = j(ue(t(e.store.state)));
1040
1040
  return e.store.subscribe(() => {
1041
1041
  let r = t(e.store.state);
1042
- Fn(n, r) || (n = r);
1042
+ Fn(u(n), r) || B(n, r, !0);
1043
1043
  }), { get current() {
1044
- return n;
1044
+ return u(n);
1045
1045
  } };
1046
1046
  }
1047
1047
  var Ln = In, Rn = 16, zn = 280, Bn = 50, Vn = "http://www.w3.org/2000/svg";
@@ -2441,7 +2441,7 @@ function Ci(e) {
2441
2441
  }
2442
2442
  }
2443
2443
  if (i?.columnDef.editorType === "checkbox") {
2444
- e.toggleBooleanCell(n.rowIndex, n.colIndex);
2444
+ (e.isCellEditableAt?.(n.rowIndex, n.colIndex) ?? !0) && e.toggleBooleanCell(n.rowIndex, n.colIndex);
2445
2445
  return;
2446
2446
  }
2447
2447
  e.onCellDoubleClick(n.rowIndex, n.colIndex);
@@ -4117,7 +4117,7 @@ function zi(e) {
4117
4117
  return;
4118
4118
  }
4119
4119
  let o = wi(r, i), s = e.getCellDisplayValue(r.id, i.id, o);
4120
- if (e.props.onCellClick?.({
4120
+ e.props.onCellClick?.({
4121
4121
  rowIndex: t,
4122
4122
  colIndex: n,
4123
4123
  columnId: i.id,
@@ -4127,8 +4127,10 @@ function zi(e) {
4127
4127
  rowIndex: t,
4128
4128
  columnId: i.id,
4129
4129
  row: r.original
4130
- }), i.columnDef.editorType === "checkbox" || typeof s == "boolean") {
4131
- e.toggleBooleanCell(t, n), a(t, n), p(t, n), e.editingCell = null;
4130
+ });
4131
+ let c = i.columnDef.editorType === "checkbox", l = !c && typeof s == "boolean" && i.columnDef.cell == null;
4132
+ if (c || l) {
4133
+ (e.isCellEditableAt?.(t, n) ?? !0) && (c || e.editingEnabled) && e.toggleBooleanCell(t, n), a(t, n), p(t, n), e.editingCell = null;
4132
4134
  return;
4133
4135
  }
4134
4136
  if (e.activeAtPointerDown !== null && e.activeAtPointerDown.rowIndex === t && e.activeAtPointerDown.colIndex === n && e.editingEnabled && e.props.editOnSecondClick !== !1) {
@@ -6869,8 +6871,11 @@ function qa(e, t) {
6869
6871
  if (pt) return e;
6870
6872
  let t = e;
6871
6873
  if (u(d).trim()) {
6872
- let e = Ar(u(d), o.filterLocale ?? o.localization?.locale);
6873
- t = t.filter((t) => t.getAllCells().some((t) => Ar(String(t.getValue() ?? ""), o.filterLocale ?? o.localization?.locale).includes(e)));
6874
+ let e = o.filterLocale ?? o.localization?.locale, n = Ar(u(d), e), r = _t.getAllColumns().map((e) => e.id);
6875
+ t = t.filter((t) => {
6876
+ for (let i of r) if (Ar(String(t.getCellValueByColumnId(i) ?? ""), e).includes(n)) return !0;
6877
+ return !1;
6878
+ });
6874
6879
  }
6875
6880
  if (t = Yt(t), u(m)) {
6876
6881
  let e = ia();
@@ -10188,7 +10193,7 @@ function ql(e, t) {
10188
10193
  E(() => {
10189
10194
  u(oe) || (m.editingEnabled || m.editingCell || m.fullRowEdit) && import("./SvGridCellEditor-D7-ql1Hd.js").then((e) => B(oe, e.default, !0));
10190
10195
  }), E(() => {
10191
- (m.tooltip || m.columnMenuFor || m.contextMenuFor || m.filterMenuFor || m.operatorMenuFor || m.inSuggestFor || m.chooseColumnsPos) && !u(T) && import("./GridMenus-CcwU8I4t.js").then((e) => B(T, e.default, !0));
10196
+ (m.tooltip || m.columnMenuFor || m.contextMenuFor || m.filterMenuFor || m.operatorMenuFor || m.inSuggestFor || m.chooseColumnsPos) && !u(T) && import("./GridMenus-D15bhpX5.js").then((e) => B(T, e.default, !0));
10192
10197
  });
10193
10198
  let ce = J(() => !!m.pivotConfig && m.pivotModeOn), ue = J(() => m.pivotResult), Y = J(() => m.allRowsBeforePagination.filter((e) => !m.isGroupRow(e)).map((e) => e.original)), Q = J(() => m.filterRowValues), ge = J(() => m.filterMenuValues), ve = J(() => m.showTooltipFor), ye = J(() => m.hideTooltip), Se = J(() => m.findHits), we = J(() => m.headerHeight), Te = J(() => h.columnResize === !0), Ee = J(() => m.selectionColumnWidth), De = J(() => m.rowNumberColumnWidth), je = J(() => m.detailToggleColumnWidth), Me = J(() => m.detailToggleColumnLeft), Ne = J(() => m.showDetailToggleEffective), Fe = J(() => m.detailToggleOf), Ie = J(() => m.showRowNumbersEffective), Le = J(() => m.columnMenuFor), Re = J(() => m.filterMenuFor), ze = J(() => m.operatorMenuFor), Be = J(() => m.viewportWidth), Ve = J(() => m.viewportHeight), He = J(() => m.scrollMetrics), Ue = J(() => m.hasVerticalOverflow), We = J(() => m.showGlobalFilterEffective), Ge = J(() => m.showFilterRowEffective), Ke = J(() => m.showInlineColumnFilterEffective), qe = J(() => m.showRowSelectionEffective), Je = J(() => m.rowSummariesEnabled), Ye = J(() => m.grid), Xe = J(() => m.allColumns), Ze = J(() => m.headerGroups), Qe = J(() => m.groupHeaderRowsWindowed), $e = J(() => m.cellPinStyle), et = J(() => m.isColumnPinned), tt = J(() => m.colDragId), nt = J(() => m.colDropOnId), rt = J(() => m.colDropSide), it = J(() => m.onColumnHeaderDragStart), at = J(() => m.onColumnHeaderDragOver), ot = J(() => m.onColumnHeaderDragLeave), st = J(() => m.onColumnHeaderDrop), ct = J(() => m.onColumnHeaderDragEnd), lt = J(() => h.rowDragManaged === !0), ut = J(() => m.rowDropIndex), dt = J(() => m.rowDropSide), ft = J(() => m.onRowDragStart), pt = J(() => m.onRowDragOver), mt = J(() => m.onRowDragLeave), ht = J(() => m.onRowDrop), gt = J(() => m.onRowsContainerDragOver), _t = J(() => m.onRowsContainerDrop), vt = J(() => m.onRowDragEnd), yt = J(() => m.onRowPointerDown);
10194
10199
  function bt(e) {
@@ -10737,7 +10742,7 @@ function ql(e, t) {
10737
10742
  }), l(e, o);
10738
10743
  };
10739
10744
  H(o, (e) => {
10740
- n().columnDef.editorType === "checkbox" || typeof r() == "boolean" ? e(d) : (n().columnDef.editorType === "list" || n().columnDef.editorType === "chips") && n().columnDef.cell == null ? e(p, 1) : n().columnDef.sparkline && n().columnDef.cell == null ? e(m, 2) : e(h, -1);
10745
+ (n().columnDef.editorType === "checkbox" || typeof r() == "boolean") && n().columnDef.cell == null ? e(d) : (n().columnDef.editorType === "list" || n().columnDef.editorType === "chips") && n().columnDef.cell == null ? e(p, 1) : n().columnDef.sparkline && n().columnDef.cell == null ? e(m, 2) : e(h, -1);
10741
10746
  }), l(e, i);
10742
10747
  }, n = (e, t = G) => {
10743
10748
  let n = J(() => t().getCanExpand?.() ?? !1);
@@ -945,7 +945,7 @@ function kt(e) {
945
945
  for (let e = 0; e < r.length; e++) {
946
946
  let t = r[e], n = i?.[e];
947
947
  if (n && n.original === t) {
948
- n[at] = null, n[ot] = null, c[e] = n;
948
+ n[at] !== null && (n[at] = null), n[ot] !== null && (n[ot] = null), c[e] = n;
949
949
  continue;
950
950
  }
951
951
  l && l.size * 4 >= r.length && (l = null);
@@ -1038,12 +1038,12 @@ var Pt = Nt, Ft = (e, t) => {
1038
1038
  return i.length === a.length && i.every((e) => Object.is(n[e], r[e]));
1039
1039
  };
1040
1040
  function It(e, t) {
1041
- let n = t(e.store.state);
1041
+ let n = K.state(K.proxy(t(e.store.state)));
1042
1042
  return e.store.subscribe(() => {
1043
1043
  let r = t(e.store.state);
1044
- Ft(n, r) || (n = r);
1044
+ Ft(K.get(n), r) || K.set(n, r, !0);
1045
1045
  }), { get current() {
1046
- return n;
1046
+ return K.get(n);
1047
1047
  } };
1048
1048
  }
1049
1049
  var Lt = It, Rt = 16, zt = 280, Bt = 50, Vt = "http://www.w3.org/2000/svg";
@@ -2443,7 +2443,7 @@ function Sr(e) {
2443
2443
  }
2444
2444
  }
2445
2445
  if (i?.columnDef.editorType === "checkbox") {
2446
- e.toggleBooleanCell(n.rowIndex, n.colIndex);
2446
+ (e.isCellEditableAt?.(n.rowIndex, n.colIndex) ?? !0) && e.toggleBooleanCell(n.rowIndex, n.colIndex);
2447
2447
  return;
2448
2448
  }
2449
2449
  e.onCellDoubleClick(n.rowIndex, n.colIndex);
@@ -4119,7 +4119,7 @@ function Rr(e) {
4119
4119
  return;
4120
4120
  }
4121
4121
  let o = Cr(r, i), s = e.getCellDisplayValue(r.id, i.id, o);
4122
- if (e.props.onCellClick?.({
4122
+ e.props.onCellClick?.({
4123
4123
  rowIndex: t,
4124
4124
  colIndex: n,
4125
4125
  columnId: i.id,
@@ -4129,8 +4129,10 @@ function Rr(e) {
4129
4129
  rowIndex: t,
4130
4130
  columnId: i.id,
4131
4131
  row: r.original
4132
- }), i.columnDef.editorType === "checkbox" || typeof s == "boolean") {
4133
- e.toggleBooleanCell(t, n), a(t, n), p(t, n), e.editingCell = null;
4132
+ });
4133
+ let c = i.columnDef.editorType === "checkbox", l = !c && typeof s == "boolean" && i.columnDef.cell == null;
4134
+ if (c || l) {
4135
+ (e.isCellEditableAt?.(t, n) ?? !0) && (c || e.editingEnabled) && e.toggleBooleanCell(t, n), a(t, n), p(t, n), e.editingCell = null;
4134
4136
  return;
4135
4137
  }
4136
4138
  if (e.activeAtPointerDown !== null && e.activeAtPointerDown.rowIndex === t && e.activeAtPointerDown.colIndex === n && e.editingEnabled && e.props.editOnSecondClick !== !1) {
@@ -6871,8 +6873,11 @@ function Ui(e, t) {
6871
6873
  if (Tt) return e;
6872
6874
  let t = e;
6873
6875
  if (K.get(d).trim()) {
6874
- let e = An(K.get(d), s.filterLocale ?? s.localization?.locale);
6875
- t = t.filter((t) => t.getAllCells().some((t) => An(String(t.getValue() ?? ""), s.filterLocale ?? s.localization?.locale).includes(e)));
6876
+ let e = s.filterLocale ?? s.localization?.locale, n = An(K.get(d), e), r = kt.getAllColumns().map((e) => e.id);
6877
+ t = t.filter((t) => {
6878
+ for (let i of r) if (An(String(t.getCellValueByColumnId(i) ?? ""), e).includes(n)) return !0;
6879
+ return !1;
6880
+ });
6876
6881
  }
6877
6882
  if (t = rn(t), K.get(m)) {
6878
6883
  let e = ei();
@@ -10190,7 +10195,7 @@ function Uc(e, t) {
10190
10195
  K.user_effect(() => {
10191
10196
  K.get(D) || (l.editingEnabled || l.editingCell || l.fullRowEdit) && import("./SvGridCellEditor-9yz-SbdZ.js").then((e) => K.set(D, e.default, !0));
10192
10197
  }), K.user_effect(() => {
10193
- (l.tooltip || l.columnMenuFor || l.contextMenuFor || l.filterMenuFor || l.operatorMenuFor || l.inSuggestFor || l.chooseColumnsPos) && !K.get(C) && import("./GridMenus-BgUS-W3o.js").then((e) => K.set(C, e.default, !0));
10198
+ (l.tooltip || l.columnMenuFor || l.contextMenuFor || l.filterMenuFor || l.operatorMenuFor || l.inSuggestFor || l.chooseColumnsPos) && !K.get(C) && import("./GridMenus-B8-Gbfq2.js").then((e) => K.set(C, e.default, !0));
10194
10199
  });
10195
10200
  let O = K.derived(() => !!l.pivotConfig && l.pivotModeOn), k = K.derived(() => l.pivotResult), A = K.derived(() => l.allRowsBeforePagination.filter((e) => !l.isGroupRow(e)).map((e) => e.original)), j = K.derived(() => l.filterRowValues), M = K.derived(() => l.filterMenuValues), N = K.derived(() => l.showTooltipFor), P = K.derived(() => l.hideTooltip), F = K.derived(() => l.findHits), I = K.derived(() => l.headerHeight), L = K.derived(() => d.columnResize === !0), R = K.derived(() => l.selectionColumnWidth), z = K.derived(() => l.rowNumberColumnWidth), B = K.derived(() => l.detailToggleColumnWidth), V = K.derived(() => l.detailToggleColumnLeft), H = K.derived(() => l.showDetailToggleEffective), U = K.derived(() => l.detailToggleOf), W = K.derived(() => l.showRowNumbersEffective), ee = K.derived(() => l.columnMenuFor), G = K.derived(() => l.filterMenuFor), te = K.derived(() => l.operatorMenuFor), ne = K.derived(() => l.viewportWidth), re = K.derived(() => l.viewportHeight), ie = K.derived(() => l.scrollMetrics), ae = K.derived(() => l.hasVerticalOverflow), oe = K.derived(() => l.showGlobalFilterEffective), se = K.derived(() => l.showFilterRowEffective), ce = K.derived(() => l.showInlineColumnFilterEffective), le = K.derived(() => l.showRowSelectionEffective), ue = K.derived(() => l.rowSummariesEnabled), de = K.derived(() => l.grid), fe = K.derived(() => l.allColumns), pe = K.derived(() => l.headerGroups), me = K.derived(() => l.groupHeaderRowsWindowed), he = K.derived(() => l.cellPinStyle), ge = K.derived(() => l.isColumnPinned), _e = K.derived(() => l.colDragId), ve = K.derived(() => l.colDropOnId), ye = K.derived(() => l.colDropSide), be = K.derived(() => l.onColumnHeaderDragStart), xe = K.derived(() => l.onColumnHeaderDragOver), Se = K.derived(() => l.onColumnHeaderDragLeave), Ce = K.derived(() => l.onColumnHeaderDrop), we = K.derived(() => l.onColumnHeaderDragEnd), Te = K.derived(() => d.rowDragManaged === !0), q = K.derived(() => l.rowDropIndex), Ee = K.derived(() => l.rowDropSide), De = K.derived(() => l.onRowDragStart), Oe = K.derived(() => l.onRowDragOver), ke = K.derived(() => l.onRowDragLeave), Ae = K.derived(() => l.onRowDrop), je = K.derived(() => l.onRowsContainerDragOver), Me = K.derived(() => l.onRowsContainerDrop), Ne = K.derived(() => l.onRowDragEnd), Pe = K.derived(() => l.onRowPointerDown);
10196
10201
  function Fe(e) {
@@ -10739,7 +10744,7 @@ function Uc(e, t) {
10739
10744
  }), K.append(e, a);
10740
10745
  };
10741
10746
  K.if(o, (e) => {
10742
- n().columnDef.editorType === "checkbox" || typeof r() == "boolean" ? e(s) : (n().columnDef.editorType === "list" || n().columnDef.editorType === "chips") && n().columnDef.cell == null ? e(c, 1) : n().columnDef.sparkline && n().columnDef.cell == null ? e(l, 2) : e(d, -1);
10747
+ (n().columnDef.editorType === "checkbox" || typeof r() == "boolean") && n().columnDef.cell == null ? e(s) : (n().columnDef.editorType === "list" || n().columnDef.editorType === "chips") && n().columnDef.cell == null ? e(c, 1) : n().columnDef.sparkline && n().columnDef.cell == null ? e(l, 2) : e(d, -1);
10743
10748
  }), K.append(e, a);
10744
10749
  }, n = (e, t = K.noop) => {
10745
10750
  let n = K.derived(() => t().getCanExpand?.() ?? !1);
@@ -1,5 +1,5 @@
1
1
  import { a as ee, t as te } from "./cell-formatting-Mnv84zoE.js";
2
- import { $ as e, $a as ne, $i as re, $n as t, $r as n, $t as r, A as i, Aa as a, Ai as o, An as s, Ao as c, Ar as l, At as u, B as d, Ba as f, Bi as p, Bn as m, Bo as h, Br as g, Bt as _, C as v, Ca as y, Ci as b, Cn as x, Co as S, Cr as C, Ct as w, D as T, Da as E, Di as D, Dn as O, Do as k, Dr as A, Dt as j, E as M, Ea as N, Ei as P, En as F, Eo as I, Er as L, Et as R, F as z, Fa as B, Fi as V, Fn as H, Fo as U, Fr as W, Ft as G, G as K, Ga as q, Gi as J, Gn as Y, Go as X, Gr as ie, Gt as ae, H as oe, Ha as se, Hi as ce, Hn as le, Ho as ue, Hr as de, Ht as fe, I as pe, Ia as me, Ii as he, In as ge, Io as _e, Ir as ve, It as ye, J as be, Ja as xe, Ji as Se, Jn as Ce, Jo as we, Jr as Te, Jt as Ee, K as De, Ka as Oe, Ki as ke, Kn as Ae, Ko as je, Kr as Me, Kt as Ne, L as Pe, La as Fe, Li as Ie, Ln as Le, Lo as Re, Lr as ze, Lt as Be, M as Ve, Ma as He, Mi as Ue, Mn as We, Mo as Ge, Mr as Ke, Mt as qe, N as Je, Na as Ye, Ni as Xe, Nn as Ze, No as Qe, Nr as $e, Nt as et, O as tt, Oa as nt, Oi as rt, On as it, Oo as at, Or as ot, Ot as st, P as ct, Pa as lt, Pi as ut, Pn as dt, Po as ft, Pr as pt, Pt as mt, Q as ht, Qa as gt, Qi as _t, Qn as vt, Qr as yt, Qt as bt, R as xt, Ra as St, Ri as Ct, Rn as wt, Ro as Tt, Rr as Et, Rt as Dt, S as Ot, Sa as kt, Si as At, Sn as jt, So as Mt, Sr as Nt, St as Pt, T as Ft, Ta as It, Ti as Lt, Tn as Rt, To as zt, Tr as Bt, Tt as Vt, U as Ht, Ua as Ut, Ui as Wt, Un as Gt, Uo as Kt, Ur as qt, Ut as Jt, V as Yt, Va as Xt, Vi as Zt, Vn as Qt, Vo as $t, Vr as en, Vt as tn, W as nn, Wa as rn, Wi as an, Wn as on, Wo as sn, Wr as cn, Wt as ln, X as un, Xa as dn, Xi as fn, Xn as pn, Xo as mn, Xr as hn, Xt as gn, Y as _n, Ya as vn, Yi as yn, Yn as bn, Yo as xn, Yr as Sn, Yt as Cn, Z as wn, Za as Tn, Zi as En, Zn as Dn, Zo as On, Zr as kn, Zt as An, _ as jn, _a as Mn, _i as Nn, _n as Pn, _o as Fn, _r as In, _t as Ln, a as Rn, aa as zn, ai as Bn, an as Vn, ao as Hn, ar as Un, at as Wn, b as Gn, ba as Kn, bi as qn, bn as Jn, bo as Yn, br as Xn, bt as Zn, c as Qn, ca as $n, ci as er, cn as tr, co as nr, cr as rr, ct as ir, d as ar, da as or, di as sr, dn as cr, do as lr, dr as ur, dt as dr, ea as fr, ei as pr, en as mr, eo as hr, er as gr, et as _r, f as vr, fa as yr, fi as br, fn as xr, fo as Sr, fr as Cr, ft as wr, g as Tr, ga as Er, gi as Dr, gn as Or, go as kr, gr as Ar, gt as jr, h as Mr, ha as Nr, hi as Pr, hn as Fr, ho as Ir, hr as Lr, ht as Rr, i as zr, ia as Br, ii as Vr, in as Hr, io as Ur, ir as Wr, it as Gr, j as Kr, ja as qr, ji as Jr, jn as Yr, jo as Xr, jr as Zr, jt as Qr, k as $r, ka as ei, ki as ti, kn as ni, ko as ri, kr as ii, kt as ai, l as oi, la as si, li as ci, ln as li, lo as ui, lr as di, lt as fi, m as pi, ma as mi, mi as hi, mn as gi, mo as _i, mr as vi, mt as yi, n as bi, na as xi, ni as Si, nn as Ci, no as wi, nr as Ti, nt as Ei, o as Di, oa as Oi, oi as ki, on as Ai, oo as ji, or as Mi, ot as Ni, p as Pi, pa as Fi, pi as Ii, pn as Li, po as Ri, pr as zi, pt as Bi, q as Vi, qa as Hi, qi as Ui, qn as Wi, qo as Gi, qr as Ki, qt as qi, r as Ji, ra as Yi, ri as Xi, rn as Zi, ro as Qi, rr as $i, rt as ea, s as ta, sa as na, si as ra, sn as ia, so as aa, sr as oa, st as sa, t as ca, ta as la, ti as ua, tn as da, to as Z, tr as fa, tt as pa, u as ma, ua as ha, ui as ga, un as _a, uo as va, ur as ya, ut as ba, v as xa, va as Sa, vi as Ca, vn as wa, vo as Ta, vr as Ea, vt as Da, w as Oa, wa as ka, wi as Aa, wn as ja, wo as Ma, wr as Na, wt as Pa, x as Fa, xa as Ia, xi as La, xn as Ra, xo as za, xr as Ba, xt as Va, y as Ha, ya as Ua, yi as Wa, yn as Ga, yo as Ka, yr as qa, yt as Ja, z as Ya, za as Xa, zi as Za, zn as Qa, zo as $a, zr as eo, zt as to } from "./src-25JmJ_7g.js";
2
+ import { $ as e, $a as ne, $i as re, $n as t, $r as n, $t as r, A as i, Aa as a, Ai as o, An as s, Ao as c, Ar as l, At as u, B as d, Ba as f, Bi as p, Bn as m, Bo as h, Br as g, Bt as _, C as v, Ca as y, Ci as b, Cn as x, Co as S, Cr as C, Ct as w, D as T, Da as E, Di as D, Dn as O, Do as k, Dr as A, Dt as j, E as M, Ea as N, Ei as P, En as F, Eo as I, Er as L, Et as R, F as z, Fa as B, Fi as V, Fn as H, Fo as U, Fr as W, Ft as G, G as K, Ga as q, Gi as J, Gn as Y, Go as X, Gr as ie, Gt as ae, H as oe, Ha as se, Hi as ce, Hn as le, Ho as ue, Hr as de, Ht as fe, I as pe, Ia as me, Ii as he, In as ge, Io as _e, Ir as ve, It as ye, J as be, Ja as xe, Ji as Se, Jn as Ce, Jo as we, Jr as Te, Jt as Ee, K as De, Ka as Oe, Ki as ke, Kn as Ae, Ko as je, Kr as Me, Kt as Ne, L as Pe, La as Fe, Li as Ie, Ln as Le, Lo as Re, Lr as ze, Lt as Be, M as Ve, Ma as He, Mi as Ue, Mn as We, Mo as Ge, Mr as Ke, Mt as qe, N as Je, Na as Ye, Ni as Xe, Nn as Ze, No as Qe, Nr as $e, Nt as et, O as tt, Oa as nt, Oi as rt, On as it, Oo as at, Or as ot, Ot as st, P as ct, Pa as lt, Pi as ut, Pn as dt, Po as ft, Pr as pt, Pt as mt, Q as ht, Qa as gt, Qi as _t, Qn as vt, Qr as yt, Qt as bt, R as xt, Ra as St, Ri as Ct, Rn as wt, Ro as Tt, Rr as Et, Rt as Dt, S as Ot, Sa as kt, Si as At, Sn as jt, So as Mt, Sr as Nt, St as Pt, T as Ft, Ta as It, Ti as Lt, Tn as Rt, To as zt, Tr as Bt, Tt as Vt, U as Ht, Ua as Ut, Ui as Wt, Un as Gt, Uo as Kt, Ur as qt, Ut as Jt, V as Yt, Va as Xt, Vi as Zt, Vn as Qt, Vo as $t, Vr as en, Vt as tn, W as nn, Wa as rn, Wi as an, Wn as on, Wo as sn, Wr as cn, Wt as ln, X as un, Xa as dn, Xi as fn, Xn as pn, Xo as mn, Xr as hn, Xt as gn, Y as _n, Ya as vn, Yi as yn, Yn as bn, Yo as xn, Yr as Sn, Yt as Cn, Z as wn, Za as Tn, Zi as En, Zn as Dn, Zo as On, Zr as kn, Zt as An, _ as jn, _a as Mn, _i as Nn, _n as Pn, _o as Fn, _r as In, _t as Ln, a as Rn, aa as zn, ai as Bn, an as Vn, ao as Hn, ar as Un, at as Wn, b as Gn, ba as Kn, bi as qn, bn as Jn, bo as Yn, br as Xn, bt as Zn, c as Qn, ca as $n, ci as er, cn as tr, co as nr, cr as rr, ct as ir, d as ar, da as or, di as sr, dn as cr, do as lr, dr as ur, dt as dr, ea as fr, ei as pr, en as mr, eo as hr, er as gr, et as _r, f as vr, fa as yr, fi as br, fn as xr, fo as Sr, fr as Cr, ft as wr, g as Tr, ga as Er, gi as Dr, gn as Or, go as kr, gr as Ar, gt as jr, h as Mr, ha as Nr, hi as Pr, hn as Fr, ho as Ir, hr as Lr, ht as Rr, i as zr, ia as Br, ii as Vr, in as Hr, io as Ur, ir as Wr, it as Gr, j as Kr, ja as qr, ji as Jr, jn as Yr, jo as Xr, jr as Zr, jt as Qr, k as $r, ka as ei, ki as ti, kn as ni, ko as ri, kr as ii, kt as ai, l as oi, la as si, li as ci, ln as li, lo as ui, lr as di, lt as fi, m as pi, ma as mi, mi as hi, mn as gi, mo as _i, mr as vi, mt as yi, n as bi, na as xi, ni as Si, nn as Ci, no as wi, nr as Ti, nt as Ei, o as Di, oa as Oi, oi as ki, on as Ai, oo as ji, or as Mi, ot as Ni, p as Pi, pa as Fi, pi as Ii, pn as Li, po as Ri, pr as zi, pt as Bi, q as Vi, qa as Hi, qi as Ui, qn as Wi, qo as Gi, qr as Ki, qt as qi, r as Ji, ra as Yi, ri as Xi, rn as Zi, ro as Qi, rr as $i, rt as ea, s as ta, sa as na, si as ra, sn as ia, so as aa, sr as oa, st as sa, t as ca, ta as la, ti as ua, tn as da, to as Z, tr as fa, tt as pa, u as ma, ua as ha, ui as ga, un as _a, uo as va, ur as ya, ut as ba, v as xa, va as Sa, vi as Ca, vn as wa, vo as Ta, vr as Ea, vt as Da, w as Oa, wa as ka, wi as Aa, wn as ja, wo as Ma, wr as Na, wt as Pa, x as Fa, xa as Ia, xi as La, xn as Ra, xo as za, xr as Ba, xt as Va, y as Ha, ya as Ua, yi as Wa, yn as Ga, yo as Ka, yr as qa, yt as Ja, z as Ya, za as Xa, zi as Za, zn as Qa, zo as $a, zr as eo, zt as to } from "./src-CleI0g9l.js";
3
3
  import { c as no, d as ro, f as io, i as ao, l as oo, m as so, n as co, o as lo, p as uo, r as fo, s as po, t as mo, u as ho } from "./export-format-y1wdyCcv.js";
4
4
  import { a as go, i as _o, n as vo, o as yo, r as bo, s as xo, t as So } from "./editor-registry-BVeHvlZR.js";
5
5
  import { a as Co, i as wo, n as To, r as Eo, t as Do } from "./editor-contract-LXgQJAgd.js";
@@ -1,5 +1,5 @@
1
1
  import { a as ee, t as te } from "./cell-formatting-Mnv84zoE.js";
2
- import { $ as e, $a as ne, $i as re, $n as t, $r as n, $t as r, A as i, Aa as a, Ai as o, An as s, Ao as c, Ar as l, At as u, B as d, Ba as f, Bi as p, Bn as m, Bo as h, Br as g, Bt as _, C as v, Ca as y, Ci as b, Cn as x, Co as S, Cr as C, Ct as w, D as T, Da as E, Di as D, Dn as O, Do as k, Dr as A, Dt as j, E as M, Ea as N, Ei as P, En as F, Eo as I, Er as L, Et as R, F as z, Fa as B, Fi as V, Fn as H, Fo as U, Fr as W, Ft as G, G as K, Ga as q, Gi as J, Gn as Y, Go as X, Gr as ie, Gt as ae, H as oe, Ha as se, Hi as ce, Hn as le, Ho as ue, Hr as de, Ht as fe, I as pe, Ia as me, Ii as he, In as ge, Io as _e, Ir as ve, It as ye, J as be, Ja as xe, Ji as Se, Jn as Ce, Jo as we, Jr as Te, Jt as Ee, K as De, Ka as Oe, Ki as ke, Kn as Ae, Ko as je, Kr as Me, Kt as Ne, L as Pe, La as Fe, Li as Ie, Ln as Le, Lo as Re, Lr as ze, Lt as Be, M as Ve, Ma as He, Mi as Ue, Mn as We, Mo as Ge, Mr as Ke, Mt as qe, N as Je, Na as Ye, Ni as Xe, Nn as Ze, No as Qe, Nr as $e, Nt as et, O as tt, Oa as nt, Oi as rt, On as it, Oo as at, Or as ot, Ot as st, P as ct, Pa as lt, Pi as ut, Pn as dt, Po as ft, Pr as pt, Pt as mt, Q as ht, Qa as gt, Qi as _t, Qn as vt, Qr as yt, Qt as bt, R as xt, Ra as St, Ri as Ct, Rn as wt, Ro as Tt, Rr as Et, Rt as Dt, S as Ot, Sa as kt, Si as At, Sn as jt, So as Mt, Sr as Nt, St as Pt, T as Ft, Ta as It, Ti as Lt, Tn as Rt, To as zt, Tr as Bt, Tt as Vt, U as Ht, Ua as Ut, Ui as Wt, Un as Gt, Uo as Kt, Ur as qt, Ut as Jt, V as Yt, Va as Xt, Vi as Zt, Vn as Qt, Vo as $t, Vr as en, Vt as tn, W as nn, Wa as rn, Wi as an, Wn as on, Wo as sn, Wr as cn, Wt as ln, X as un, Xa as dn, Xi as fn, Xn as pn, Xo as mn, Xr as hn, Xt as gn, Y as _n, Ya as vn, Yi as yn, Yn as bn, Yo as xn, Yr as Sn, Yt as Cn, Z as wn, Za as Tn, Zi as En, Zn as Dn, Zo as On, Zr as kn, Zt as An, _ as jn, _a as Mn, _i as Nn, _n as Pn, _o as Fn, _r as In, _t as Ln, a as Rn, aa as zn, ai as Bn, an as Vn, ao as Hn, ar as Un, at as Wn, b as Gn, ba as Kn, bi as qn, bn as Jn, bo as Yn, br as Xn, bt as Zn, c as Qn, ca as $n, ci as er, cn as tr, co as nr, cr as rr, ct as ir, d as ar, da as or, di as sr, dn as cr, do as lr, dr as ur, dt as dr, ea as fr, ei as pr, en as mr, eo as hr, er as gr, et as _r, f as vr, fa as yr, fi as br, fn as xr, fo as Sr, fr as Cr, ft as wr, g as Tr, ga as Er, gi as Dr, gn as Or, go as kr, gr as Ar, gt as jr, h as Mr, ha as Nr, hi as Pr, hn as Fr, ho as Ir, hr as Lr, ht as Rr, i as zr, ia as Br, ii as Vr, in as Hr, io as Ur, ir as Wr, it as Gr, j as Kr, ja as qr, ji as Jr, jn as Yr, jo as Xr, jr as Zr, jt as Qr, k as $r, ka as ei, ki as ti, kn as ni, ko as ri, kr as ii, kt as ai, l as oi, la as si, li as ci, ln as li, lo as ui, lr as di, lt as fi, m as pi, ma as mi, mi as hi, mn as gi, mo as _i, mr as vi, mt as yi, n as bi, na as xi, ni as Si, nn as Ci, no as wi, nr as Ti, nt as Ei, o as Di, oa as Oi, oi as ki, on as Ai, oo as ji, or as Mi, ot as Ni, p as Pi, pa as Fi, pi as Ii, pn as Li, po as Ri, pr as zi, pt as Bi, q as Vi, qa as Hi, qi as Ui, qn as Wi, qo as Gi, qr as Ki, qt as qi, r as Ji, ra as Yi, ri as Xi, rn as Zi, ro as Qi, rr as $i, rt as ea, s as ta, sa as na, si as ra, sn as ia, so as aa, sr as oa, st as sa, t as ca, ta as la, ti as ua, tn as da, to as Z, tr as fa, tt as pa, u as ma, ua as ha, ui as ga, un as _a, uo as va, ur as ya, ut as ba, v as xa, va as Sa, vi as Ca, vn as wa, vo as Ta, vr as Ea, vt as Da, w as Oa, wa as ka, wi as Aa, wn as ja, wo as Ma, wr as Na, wt as Pa, x as Fa, xa as Ia, xi as La, xn as Ra, xo as za, xr as Ba, xt as Va, y as Ha, ya as Ua, yi as Wa, yn as Ga, yo as Ka, yr as qa, yt as Ja, z as Ya, za as Xa, zi as Za, zn as Qa, zo as $a, zr as eo, zt as to } from "./src-kE9B98r6.js";
2
+ import { $ as e, $a as ne, $i as re, $n as t, $r as n, $t as r, A as i, Aa as a, Ai as o, An as s, Ao as c, Ar as l, At as u, B as d, Ba as f, Bi as p, Bn as m, Bo as h, Br as g, Bt as _, C as v, Ca as y, Ci as b, Cn as x, Co as S, Cr as C, Ct as w, D as T, Da as E, Di as D, Dn as O, Do as k, Dr as A, Dt as j, E as M, Ea as N, Ei as P, En as F, Eo as I, Er as L, Et as R, F as z, Fa as B, Fi as V, Fn as H, Fo as U, Fr as W, Ft as G, G as K, Ga as q, Gi as J, Gn as Y, Go as X, Gr as ie, Gt as ae, H as oe, Ha as se, Hi as ce, Hn as le, Ho as ue, Hr as de, Ht as fe, I as pe, Ia as me, Ii as he, In as ge, Io as _e, Ir as ve, It as ye, J as be, Ja as xe, Ji as Se, Jn as Ce, Jo as we, Jr as Te, Jt as Ee, K as De, Ka as Oe, Ki as ke, Kn as Ae, Ko as je, Kr as Me, Kt as Ne, L as Pe, La as Fe, Li as Ie, Ln as Le, Lo as Re, Lr as ze, Lt as Be, M as Ve, Ma as He, Mi as Ue, Mn as We, Mo as Ge, Mr as Ke, Mt as qe, N as Je, Na as Ye, Ni as Xe, Nn as Ze, No as Qe, Nr as $e, Nt as et, O as tt, Oa as nt, Oi as rt, On as it, Oo as at, Or as ot, Ot as st, P as ct, Pa as lt, Pi as ut, Pn as dt, Po as ft, Pr as pt, Pt as mt, Q as ht, Qa as gt, Qi as _t, Qn as vt, Qr as yt, Qt as bt, R as xt, Ra as St, Ri as Ct, Rn as wt, Ro as Tt, Rr as Et, Rt as Dt, S as Ot, Sa as kt, Si as At, Sn as jt, So as Mt, Sr as Nt, St as Pt, T as Ft, Ta as It, Ti as Lt, Tn as Rt, To as zt, Tr as Bt, Tt as Vt, U as Ht, Ua as Ut, Ui as Wt, Un as Gt, Uo as Kt, Ur as qt, Ut as Jt, V as Yt, Va as Xt, Vi as Zt, Vn as Qt, Vo as $t, Vr as en, Vt as tn, W as nn, Wa as rn, Wi as an, Wn as on, Wo as sn, Wr as cn, Wt as ln, X as un, Xa as dn, Xi as fn, Xn as pn, Xo as mn, Xr as hn, Xt as gn, Y as _n, Ya as vn, Yi as yn, Yn as bn, Yo as xn, Yr as Sn, Yt as Cn, Z as wn, Za as Tn, Zi as En, Zn as Dn, Zo as On, Zr as kn, Zt as An, _ as jn, _a as Mn, _i as Nn, _n as Pn, _o as Fn, _r as In, _t as Ln, a as Rn, aa as zn, ai as Bn, an as Vn, ao as Hn, ar as Un, at as Wn, b as Gn, ba as Kn, bi as qn, bn as Jn, bo as Yn, br as Xn, bt as Zn, c as Qn, ca as $n, ci as er, cn as tr, co as nr, cr as rr, ct as ir, d as ar, da as or, di as sr, dn as cr, do as lr, dr as ur, dt as dr, ea as fr, ei as pr, en as mr, eo as hr, er as gr, et as _r, f as vr, fa as yr, fi as br, fn as xr, fo as Sr, fr as Cr, ft as wr, g as Tr, ga as Er, gi as Dr, gn as Or, go as kr, gr as Ar, gt as jr, h as Mr, ha as Nr, hi as Pr, hn as Fr, ho as Ir, hr as Lr, ht as Rr, i as zr, ia as Br, ii as Vr, in as Hr, io as Ur, ir as Wr, it as Gr, j as Kr, ja as qr, ji as Jr, jn as Yr, jo as Xr, jr as Zr, jt as Qr, k as $r, ka as ei, ki as ti, kn as ni, ko as ri, kr as ii, kt as ai, l as oi, la as si, li as ci, ln as li, lo as ui, lr as di, lt as fi, m as pi, ma as mi, mi as hi, mn as gi, mo as _i, mr as vi, mt as yi, n as bi, na as xi, ni as Si, nn as Ci, no as wi, nr as Ti, nt as Ei, o as Di, oa as Oi, oi as ki, on as Ai, oo as ji, or as Mi, ot as Ni, p as Pi, pa as Fi, pi as Ii, pn as Li, po as Ri, pr as zi, pt as Bi, q as Vi, qa as Hi, qi as Ui, qn as Wi, qo as Gi, qr as Ki, qt as qi, r as Ji, ra as Yi, ri as Xi, rn as Zi, ro as Qi, rr as $i, rt as ea, s as ta, sa as na, si as ra, sn as ia, so as aa, sr as oa, st as sa, t as ca, ta as la, ti as ua, tn as da, to as Z, tr as fa, tt as pa, u as ma, ua as ha, ui as ga, un as _a, uo as va, ur as ya, ut as ba, v as xa, va as Sa, vi as Ca, vn as wa, vo as Ta, vr as Ea, vt as Da, w as Oa, wa as ka, wi as Aa, wn as ja, wo as Ma, wr as Na, wt as Pa, x as Fa, xa as Ia, xi as La, xn as Ra, xo as za, xr as Ba, xt as Va, y as Ha, ya as Ua, yi as Wa, yn as Ga, yo as Ka, yr as qa, yt as Ja, z as Ya, za as Xa, zi as Za, zn as Qa, zo as $a, zr as eo, zt as to } from "./src-Dz625Fhx.js";
3
3
  import { c as no, d as ro, f as io, i as ao, l as oo, m as so, n as co, o as lo, p as uo, r as fo, s as po, t as mo, u as ho } from "./export-format-y1wdyCcv.js";
4
4
  import { a as go, i as _o, n as vo, o as yo, r as bo, s as xo, t as So } from "./editor-registry-BVeHvlZR.js";
5
5
  import { a as Co, i as wo, n as To, r as Eo, t as Do } from "./editor-contract-LXgQJAgd.js";
@@ -0,0 +1,80 @@
1
+ import { r as e } from "./server-block-cache-DIqb3VZn.js";
2
+ //#region src/validate.ts
3
+ var t = 10, n = 1e3, r = "https://svgrid.com/docs/getting-started/3-data-and-columns/";
4
+ function i(e, t = []) {
5
+ for (let n of e) n.columns?.length ? i(n.columns, t) : t.push(n);
6
+ return t;
7
+ }
8
+ function a(a) {
9
+ let s = [], c = i(a.columns ?? []), l = [], u = a.data ?? [], d = Math.min(u.length, n);
10
+ for (let n = 0; n < d && l.length < t; n += 1) {
11
+ let t = u[n];
12
+ e(t) || l.push(t);
13
+ }
14
+ if (l.length && !a.serverGroup) {
15
+ let e = /* @__PURE__ */ new Set();
16
+ for (let t of l) if (t && typeof t == "object") for (let n of Object.keys(t)) e.add(n);
17
+ for (let t of c) {
18
+ let n = t.field;
19
+ if (!n || t.fieldFn || e.has(n)) continue;
20
+ let i = o(n, [...e]);
21
+ s.push(`[svgrid] Column field "${n}" does not exist on your row data, so that column renders empty.${i ? ` Did you mean "${i}"?` : ""} Available keys: ${[...e].slice(0, 12).join(", ")}. See ${r}`);
22
+ }
23
+ }
24
+ let f = /* @__PURE__ */ new Map();
25
+ for (let e of c) {
26
+ let t = e.id ?? e.field;
27
+ t && f.set(t, (f.get(t) ?? 0) + 1);
28
+ }
29
+ for (let [e, t] of f) t > 1 && s.push(`[svgrid] ${t} columns share the id "${e}". Column state (sorting, filtering, pinning, width) is keyed by id, so they will act as one. Give each an explicit \`id\`. See ${r}`);
30
+ let p = a.pageable === void 0 && a.showPagination === void 0;
31
+ a.pageSize !== void 0 && p && s.push("[svgrid] `pageSize` is set but pagination was never turned on, so it has no effect and every row renders. Add `pageable` to switch the pager on."), !(a.sortable === !0 || a.features?.rowSortingFeature) && c.some((e) => e.sortable === !0) && s.push("[svgrid] A column sets `sortable: true`, but sorting is not enabled on the grid, so its header does nothing. Add `sortable` to <SvGrid> (it registers the sorting feature for you).");
32
+ let m = /* @__PURE__ */ new Set();
33
+ for (let e of c) {
34
+ let t = e.id ?? e.field;
35
+ t && m.add(t);
36
+ }
37
+ for (let e of a.groupBy ?? []) {
38
+ if (m.has(e)) continue;
39
+ let t = o(e, [...m]);
40
+ s.push(`[svgrid] \`groupBy\` refers to column "${e}", which does not exist, so that grouping level is ignored.${t ? ` Did you mean "${t}"?` : ""} Column ids: ${[...m].slice(0, 12).join(", ")}`);
41
+ }
42
+ let h = a.treeData?.column;
43
+ if (h && !m.has(h) && s.push(`[svgrid] \`treeData.column\` refers to column "${h}", which does not exist, so the expander falls back to the first visible column.`), l.length && a.treeData) {
44
+ let e = /* @__PURE__ */ new Set();
45
+ for (let t of l) if (t && typeof t == "object") for (let n of Object.keys(t)) e.add(n);
46
+ let t = a.treeData.parentField;
47
+ if (t && !e.has(t)) {
48
+ let n = o(t, [...e]);
49
+ s.push(`[svgrid] \`treeData.parentField\` is "${t}", which is not on your row data, so every row becomes a root and no hierarchy appears.${n ? ` Did you mean "${n}"?` : ""}`);
50
+ }
51
+ let n = a.treeData.idField;
52
+ n && !e.has(n) && s.push(`[svgrid] \`treeData.idField\` is "${n}", which is not on your row data, so parent lookups cannot match and the tree stays flat.`);
53
+ }
54
+ return a.gantt && a.treeData && s.push("[svgrid] `gantt` and `treeData` are both set. The Gantt builds its own work-breakdown tree from `gantt.parentField` and needs every descendant row, but `treeData` hides collapsed children from the view, so summary bars will under-report. Remove `treeData` and set `gantt.parentField` instead."), (a.initialColumnPinning?.left?.length ?? 0) + (a.initialColumnPinning?.right?.length ?? 0) > 0 && a.columnVirtualization !== !1 && s.push("[svgrid] `initialColumnPinning` is set while column virtualization is on (its default), so the pinned columns will not stick - the virtualizer recycles column nodes. Add `columnVirtualization={false}`."), a.externalPagination === !0 && a.rowCount === void 0 && s.push("[svgrid] `externalPagination` is on but `rowCount` is not set, so the pager cannot know how many pages exist. Pass the server total as `rowCount`."), a.externalSort === !0 && !a.onSortingChange && s.push("[svgrid] `externalSort` is on but there is no `onSortingChange` handler, so clicking a header changes nothing - the grid stopped sorting and nobody is listening. Add `onSortingChange` and re-fetch in your handler."), a.externalFilter === !0 && !a.onFiltersChange && s.push("[svgrid] `externalFilter` is on but there is no `onFiltersChange` handler, so filtering the grid changes nothing. Add `onFiltersChange` and re-fetch in your handler."), s;
55
+ }
56
+ function o(e, t) {
57
+ let n, r = Infinity;
58
+ for (let i of t) {
59
+ let t = s(e.toLowerCase(), i.toLowerCase());
60
+ t < r && (r = t, n = i);
61
+ }
62
+ let i = Math.min(3, Math.floor(e.length / 3) + 1);
63
+ return r <= i ? n : void 0;
64
+ }
65
+ function s(e, t) {
66
+ if (e === t) return 0;
67
+ let n = Array(t.length + 1);
68
+ for (let e = 0; e <= t.length; e++) n[e] = e;
69
+ for (let r = 1; r <= e.length; r++) {
70
+ let i = n[0];
71
+ n[0] = r;
72
+ for (let a = 1; a <= t.length; a++) {
73
+ let o = Math.min(n[a] + 1, n[a - 1] + 1, i + (e[r - 1] === t[a - 1] ? 0 : 1));
74
+ i = n[a], n[a] = o;
75
+ }
76
+ }
77
+ return n[t.length];
78
+ }
79
+ //#endregion
80
+ export { a as validateGridConfig };
package/dist/core.js CHANGED
@@ -1419,8 +1419,13 @@ export function createSvGridCore(options) {
1419
1419
  const original = data[index];
1420
1420
  const kept = previous?.[index];
1421
1421
  if (kept && kept.original === original) {
1422
- kept[ROW_VALUES] = null;
1423
- kept[ROW_CELLS] = null;
1422
+ // Read before writing: a row nobody rendered (a million off-screen
1423
+ // placeholders under a row model) has nothing memoised, and a
1424
+ // store per row was a measurable share of each block landing.
1425
+ if (kept[ROW_VALUES] !== null)
1426
+ kept[ROW_VALUES] = null;
1427
+ if (kept[ROW_CELLS] !== null)
1428
+ kept[ROW_CELLS] = null;
1424
1429
  cachedBaseRows[index] = kept;
1425
1430
  continue;
1426
1431
  }
@@ -26,7 +26,7 @@
26
26
  export { columnFilteringFeature, columnGroupingFeature, createCoreRowModel, createExpandedRowModel, createFilteredRowModel, createGroupedRowModel, createPaginatedRowModel, createSortedRowModel, createTreeRowModel, createSvGridCore, flattenTreeData, applyGroupAggregate, filterFns, rowExpandingFeature, rowPaginationFeature, rowSelectionFeature, rowSortingFeature, sortFns, tableFeatures, type Cell, type CellContext, type CellSpanParams, type ValueParserParams, type EditorContext, type CellData, type ActiveCellState, type CellFormatConfig, type CellFormatter, type Column, type ColumnDef, type ColumnDefTemplate, type ColumnFilter, type ColumnFiltersState, type ExpandedState, type GridColumnDef, type GridColumns, type GroupAggregator, type GroupingState, type Header, type HeaderContext, type HeaderGroup, type PaginationState, type Row, type RowData, type RowModel, type RowModelFactory, type RowSelectionState, type SortingState, type Store, type SvGrid as SvGridInstance, type SvGridOptions, type TableFeatures, type TreeRowModelOptions, type FlattenTreeOptions, type Updater, } from './core.js';
27
27
  export { createGrid, createSvGrid, type SvelteGrid } from './createGrid.svelte.js';
28
28
  export { createGridState, createSvGridState } from './createGridState.svelte.js';
29
- export { subscribeGrid, subscribeSvGrid } from './subscribe.js';
29
+ export { subscribeGrid, subscribeSvGrid } from './subscribe.svelte.js';
30
30
  export { createVirtualizer } from './virtualization/virtualizer.js';
31
31
  export { createSvelteVirtualizer } from './virtualization/svelte-virtualizer.svelte.js';
32
32
  export { createColumnVirtualizer } from './virtualization/column-virtualizer.js';
package/dist/headless.js CHANGED
@@ -26,7 +26,7 @@
26
26
  export { columnFilteringFeature, columnGroupingFeature, createCoreRowModel, createExpandedRowModel, createFilteredRowModel, createGroupedRowModel, createPaginatedRowModel, createSortedRowModel, createTreeRowModel, createSvGridCore, flattenTreeData, applyGroupAggregate, filterFns, rowExpandingFeature, rowPaginationFeature, rowSelectionFeature, rowSortingFeature, sortFns, tableFeatures, } from './core.js';
27
27
  export { createGrid, createSvGrid } from './createGrid.svelte.js';
28
28
  export { createGridState, createSvGridState } from './createGridState.svelte.js';
29
- export { subscribeGrid, subscribeSvGrid } from './subscribe.js';
29
+ export { subscribeGrid, subscribeSvGrid } from './subscribe.svelte.js';
30
30
  // The virtualizers are pure window arithmetic (scroll offset in, visible
31
31
  // range out) with no DOM of their own, and a custom renderer over 50k rows
32
32
  // needs one. They were only on the barrel, so the Headless demos imported
package/dist/index.d.ts CHANGED
@@ -3,7 +3,7 @@ export { coerceExportDate, formatValueForExport, projectGridRows, toExcelNumFmt,
3
3
  export { columnFilteringFeature, columnGroupingFeature, createCoreRowModel, createExpandedRowModel, createFilteredRowModel, createGroupedRowModel, createPaginatedRowModel, createSortedRowModel, createTreeRowModel, flattenTreeData, applyGroupAggregate, filterFns, rowExpandingFeature, rowPaginationFeature, rowSelectionFeature, rowSortingFeature, sortFns, tableFeatures, type Cell, type CellContext, type CellSpanParams, type ValueParserParams, type EditorContext, type CellData, type ActiveCellState, type CellFormatConfig, type CellFormatter, type Column, type ColumnDef, type ColumnDefTemplate, type GridColumnDef, type GridColumns, type GroupAggregator, type Header, type HeaderContext, type HeaderGroup, type Row, type RowData, type SortingState, type SvGrid as SvGridInstance, type SvGridOptions, type TableFeatures, type TreeRowModelOptions, type FlattenTreeOptions, type Updater, } from './core.js';
4
4
  export { createGrid, createSvGrid, type SvelteGrid } from './createGrid.svelte.js';
5
5
  export { createGridState, createSvGridState } from './createGridState.svelte.js';
6
- export { subscribeGrid, subscribeSvGrid } from './subscribe.js';
6
+ export { subscribeGrid, subscribeSvGrid } from './subscribe.svelte.js';
7
7
  export { default as SvGrid } from './SvGrid.svelte';
8
8
  export type { BoardConfig, BoardLane, BoardCardMoveEvent, BoardCardCommitEvent, BoardLayout, BoardSubtask, BoardComment, BoardMenuContext, BoardLaneMenuContext, BoardDrawerConfig } from './SvGrid.types.js';
9
9
  export { registerBoardView, getBoardView, hasBoardView } from './board-view.svelte.js';
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ export { coerceExportDate, formatValueForExport, projectGridRows, toExcelNumFmt,
3
3
  export { columnFilteringFeature, columnGroupingFeature, createCoreRowModel, createExpandedRowModel, createFilteredRowModel, createGroupedRowModel, createPaginatedRowModel, createSortedRowModel, createTreeRowModel, flattenTreeData, applyGroupAggregate, filterFns, rowExpandingFeature, rowPaginationFeature, rowSelectionFeature, rowSortingFeature, sortFns, tableFeatures, } from './core.js';
4
4
  export { createGrid, createSvGrid } from './createGrid.svelte.js';
5
5
  export { createGridState, createSvGridState } from './createGridState.svelte.js';
6
- export { subscribeGrid, subscribeSvGrid } from './subscribe.js';
6
+ export { subscribeGrid, subscribeSvGrid } from './subscribe.svelte.js';
7
7
  export { default as SvGrid } from './SvGrid.svelte';
8
8
  export { registerBoardView, getBoardView, hasBoardView } from './board-view.svelte.js';
9
9
  // Built-in AI helpers (free): natural-language filter, smart-fill, summarize,
@@ -189,7 +189,11 @@ export function createKeyboard(ctx) {
189
189
  }
190
190
  }
191
191
  if (column?.columnDef.editorType === "checkbox") {
192
- ctx.toggleBooleanCell(current.rowIndex, current.colIndex);
192
+ // Same rule as a click: `editable: false` (or an editable callback
193
+ // saying no) keeps the checkbox read-only.
194
+ if (ctx.isCellEditableAt?.(current.rowIndex, current.colIndex) ?? true) {
195
+ ctx.toggleBooleanCell(current.rowIndex, current.colIndex);
196
+ }
193
197
  return;
194
198
  }
195
199
  ctx.onCellDoubleClick(current.rowIndex, current.colIndex);
package/dist/selection.js CHANGED
@@ -622,10 +622,20 @@ export function createSelection(ctx) {
622
622
  columnId: column.id,
623
623
  row: row.original,
624
624
  });
625
- const isCheckboxColumn = column.columnDef.editorType === "checkbox" ||
626
- typeof cellValue === "boolean";
627
- if (isCheckboxColumn) {
628
- ctx.toggleBooleanCell(rowIndex, colIndex);
625
+ // A declared checkbox editor toggles on click unless the cell is not
626
+ // editable. A value that only HAPPENS to be boolean is drawn as a read-only
627
+ // checkbox, so it toggles only when inline editing is on - it used to flip
628
+ // and write the row on any click, in grids with editing switched off. A
629
+ // column with its own `cell` renderer draws (and handles) the value itself.
630
+ const declaredCheckbox = column.columnDef.editorType === "checkbox";
631
+ const inferredCheckbox = !declaredCheckbox &&
632
+ typeof cellValue === "boolean" &&
633
+ column.columnDef.cell == null;
634
+ if (declaredCheckbox || inferredCheckbox) {
635
+ const editable = ctx.isCellEditableAt?.(rowIndex, colIndex) ?? true;
636
+ if (editable && (declaredCheckbox || ctx.editingEnabled)) {
637
+ ctx.toggleBooleanCell(rowIndex, colIndex);
638
+ }
629
639
  setActiveCell(rowIndex, colIndex);
630
640
  setSelection(rowIndex, colIndex);
631
641
  ctx.editingCell = null;
@@ -16,7 +16,11 @@ const shallowCompare = (a, b) => {
16
16
  * comparison. Call from `<script>` so the selector is contextually typed.
17
17
  */
18
18
  export function subscribeGrid(grid, selector) {
19
- let current = selector(grid.store.state);
19
+ // $state, not a plain variable: core.ts keeps a deliberately framework-free
20
+ // store, so this Svelte adapter is where reactivity belongs. Without it a
21
+ // `$derived` reading .current never re-runs and the docs' headless examples
22
+ // render their seeded state forever.
23
+ let current = $state(selector(grid.store.state));
20
24
  grid.store.subscribe(() => {
21
25
  const next = selector(grid.store.state);
22
26
  if (!shallowCompare(current, next))
package/dist/validate.js CHANGED
@@ -1,6 +1,8 @@
1
1
  import { rowPlaceholderState } from './server-block-cache.js';
2
2
  /** How many rows to sample when deciding whether a `field` exists. */
3
3
  const FIELD_SAMPLE_ROWS = 10;
4
+ /** How many rows to look through for that sample, placeholders included. */
5
+ const FIELD_SCAN_ROWS = 1000;
4
6
  const DOCS = 'https://svgrid.com/docs/getting-started/3-data-and-columns/';
5
7
  /** Every leaf column, flattened through group columns. */
6
8
  function leaves(columns, out = []) {
@@ -23,10 +25,15 @@ export function validateGridConfig(input) {
23
25
  // no data keys at all and say nothing about the columns.
24
26
  // Stops at the sample size: this runs on every data change, and a filter
25
27
  // over all the rows first was O(n) per tick on a live feed.
28
+ // The scan is bounded too: a row model scrolled to the middle of a million
29
+ // rows, or just purged by a sort, hands over a list that is placeholders
30
+ // up to the loaded block, and walking all of them for ten real rows was
31
+ // O(n) per block landing. Finding none means nothing to judge by.
26
32
  const sample = [];
27
- for (const row of input.data ?? []) {
28
- if (sample.length >= FIELD_SAMPLE_ROWS)
29
- break;
33
+ const data = input.data ?? [];
34
+ const scan = Math.min(data.length, FIELD_SCAN_ROWS);
35
+ for (let i = 0; i < scan && sample.length < FIELD_SAMPLE_ROWS; i += 1) {
36
+ const row = data[i];
30
37
  if (!rowPlaceholderState(row))
31
38
  sample.push(row);
32
39
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@svgrid/grid",
3
- "version": "3.0.7",
3
+ "version": "3.0.8",
4
4
  "description": "Svelte 5-native data grid and data table. Headless-first engine with virtual scrolling for 100k+ rows, Excel-style filtering, inline editing, grouping, tree data, pivot, and server-side data. Drop-in <SvGrid> component. TypeScript, SSR-ready.",
5
5
  "author": "jQWidgets <boikom@jqwidgets.com>",
6
6
  "license": "MIT",
@@ -1718,15 +1718,21 @@ export function createSvGridController<
1718
1718
 
1719
1719
  let rows = rawRows;
1720
1720
  if (globalFilter.trim()) {
1721
- const needle = normalizeForFilter(globalFilter, (props.filterLocale ?? props.localization?.locale));
1722
- rows = rows.filter((row) =>
1723
- row
1724
- .getAllCells()
1725
- .some((cell) =>
1726
- normalizeForFilter(String(cell.getValue() ?? ""), (props.filterLocale ?? props.localization?.locale))
1727
- .includes(needle),
1728
- ),
1729
- );
1721
+ const locale = props.filterLocale ?? props.localization?.locale;
1722
+ const needle = normalizeForFilter(globalFilter, locale);
1723
+ // Read the values, not the cells. `getAllCells()` built a cell object
1724
+ // with two closures per column on EVERY row and kept them on the row:
1725
+ // ten million objects for one keystroke over a million rows, and a
1726
+ // search that froze the page for seconds. `cell.getValue()` and
1727
+ // `getCellValueByColumnId` read the same per-row values array, so the
1728
+ // answer is the same. The columns are the ones the rows were built with.
1729
+ const columnIds = grid.getAllColumns().map((c) => c.id);
1730
+ rows = rows.filter((row) => {
1731
+ for (const id of columnIds) {
1732
+ if (normalizeForFilter(String(row.getCellValueByColumnId(id) ?? ""), locale).includes(needle)) return true;
1733
+ }
1734
+ return false;
1735
+ });
1730
1736
  }
1731
1737
 
1732
1738
  rows = applyColumnFilters(rows);
package/src/SvGrid.svelte CHANGED
@@ -1212,7 +1212,10 @@
1212
1212
  column: Column<TData>,
1213
1213
  cellValue: unknown,
1214
1214
  )}
1215
- {#if column.columnDef.editorType === "checkbox" || typeof cellValue === "boolean"}
1215
+ <!-- A column's own `cell` renderer wins over every built-in display, the
1216
+ checkbox included: a boolean field drawn as a flag button or a status
1217
+ pill used to come out as a read-only checkbox, with `cell` never called. -->
1218
+ {#if (column.columnDef.editorType === "checkbox" || typeof cellValue === "boolean") && column.columnDef.cell == null}
1216
1219
  <div
1217
1220
  class="sv-grid-checkbox sv-grid-checkbox-readonly"
1218
1221
  role="checkbox"
package/src/core.ts CHANGED
@@ -2216,8 +2216,11 @@ export function createSvGridCore<TFeatures extends TableFeatures, TData extends
2216
2216
  const original = data[index]!
2217
2217
  const kept = previous?.[index] as BaseRowState<TData> | undefined
2218
2218
  if (kept && kept.original === original) {
2219
- kept[ROW_VALUES] = null
2220
- kept[ROW_CELLS] = null
2219
+ // Read before writing: a row nobody rendered (a million off-screen
2220
+ // placeholders under a row model) has nothing memoised, and a
2221
+ // store per row was a measurable share of each block landing.
2222
+ if (kept[ROW_VALUES] !== null) kept[ROW_VALUES] = null
2223
+ if (kept[ROW_CELLS] !== null) kept[ROW_CELLS] = null
2221
2224
  cachedBaseRows[index] = kept
2222
2225
  continue
2223
2226
  }
@@ -0,0 +1,89 @@
1
+ /**
2
+ * The headless docs tell you to write:
3
+ *
4
+ * const table = createSvGrid({ ..., state: { sorting }, onSortingChange })
5
+ * const rows = $derived(table.getRowModel().rows)
6
+ *
7
+ * `createStore` (core.ts) is a plain closure + listener set, NOT runes, and
8
+ * `options.state` is spread into the initial store once at creation. So that
9
+ * `$derived` has no reactive dependency on the engine's state: clicking a
10
+ * header updates the store and the component's own `sorting` rune, the sort
11
+ * indicator moves, and the ROWS never recompute.
12
+ *
13
+ * Caught while recording the headless video: the arrow cleared and the order
14
+ * did not change. Lives in a `.svelte.test.ts` so `$state` is available.
15
+ */
16
+ import { describe, expect, it } from 'vitest'
17
+ import { flushSync } from 'svelte'
18
+ import {
19
+ createSvGrid,
20
+ createCoreRowModel,
21
+ createSortedRowModel,
22
+ tableFeatures,
23
+ rowSortingFeature,
24
+ } from './index'
25
+
26
+ type Repo = { name: string; stars: number }
27
+
28
+ // Deliberately NOT in stars order: clearing the sort has to produce a
29
+ // visibly different list, or the assertions below prove nothing.
30
+ const data: Repo[] = [
31
+ { name: 'sv-grid', stars: 172 },
32
+ { name: 'svelte', stars: 78000 },
33
+ { name: 'vite', stars: 68000 },
34
+ ]
35
+
36
+ const features = tableFeatures({ rowSortingFeature })
37
+ const columns = [
38
+ { field: 'name', header: 'Repo' },
39
+ { field: 'stars', header: 'Stars' },
40
+ ] as never
41
+
42
+ function build(onSortingChange: (next: unknown) => void, seed: Array<{ id: string; desc: boolean }>) {
43
+ return createSvGrid({
44
+ _features: features,
45
+ _rowModels: {
46
+ coreRowModel: createCoreRowModel<Repo>(),
47
+ sortedRowModel: createSortedRowModel<Repo>(),
48
+ },
49
+ data,
50
+ columns,
51
+ state: { sorting: seed },
52
+ onSortingChange,
53
+ } as never)
54
+ }
55
+
56
+ describe('headless state reactivity', () => {
57
+ it('seeds the sort from options.state', () => {
58
+ const table = build(() => {}, [{ id: 'stars', desc: true }])
59
+ const names = table.getRowModel().rows.map((r) => (r.original as Repo).name)
60
+ expect(names).toEqual(['svelte', 'vite', 'sv-grid'])
61
+ })
62
+
63
+ it('re-sorts when the toggle handler runs', () => {
64
+ let sorting = $state([{ id: 'stars', desc: true }])
65
+ const table = build((next) => (sorting = next as typeof sorting), sorting)
66
+
67
+ // The pattern the headless docs now teach: touch the state rune the
68
+ // component owns so the derived re-runs. A plain
69
+ // `$derived(table.getRowModel().rows)` reads only the framework-free
70
+ // engine and never recomputes, which is the bug this guards.
71
+ const rows = $derived.by(() => {
72
+ sorting
73
+ return table.getRowModel().rows
74
+ })
75
+ expect(rows.map((r) => (r.original as Repo).name)).toEqual(['svelte', 'vite', 'sv-grid'])
76
+
77
+ const stars = table.getAllColumns().find((c) => c.id === 'stars')!
78
+ stars.getToggleSortingHandler()()
79
+ flushSync()
80
+
81
+ // The engine's own state really did change...
82
+ expect(table.getState().sorting).toEqual([])
83
+ // ...and the component's rune did too, which is why the arrow clears.
84
+ expect(sorting).toEqual([])
85
+
86
+ // The rows must follow the cleared sort, i.e. fall back to data order.
87
+ expect(rows.map((r) => (r.original as Repo).name)).toEqual(['sv-grid', 'svelte', 'vite'])
88
+ })
89
+ })
package/src/headless.ts CHANGED
@@ -84,7 +84,7 @@ export {
84
84
 
85
85
  export { createGrid, createSvGrid, type SvelteGrid } from './createGrid.svelte'
86
86
  export { createGridState, createSvGridState } from './createGridState.svelte'
87
- export { subscribeGrid, subscribeSvGrid } from './subscribe'
87
+ export { subscribeGrid, subscribeSvGrid } from './subscribe.svelte'
88
88
 
89
89
  // The virtualizers are pure window arithmetic (scroll offset in, visible
90
90
  // range out) with no DOM of their own, and a custom renderer over 50k rows
package/src/index.ts CHANGED
@@ -73,7 +73,7 @@ export {
73
73
 
74
74
  export { createGrid, createSvGrid, type SvelteGrid } from './createGrid.svelte'
75
75
  export { createGridState, createSvGridState } from './createGridState.svelte'
76
- export { subscribeGrid, subscribeSvGrid } from './subscribe'
76
+ export { subscribeGrid, subscribeSvGrid } from './subscribe.svelte'
77
77
  export { default as SvGrid } from './SvGrid.svelte'
78
78
  // Kanban board view. The `board` prop + its config types live in the free grid,
79
79
  // but the RENDERER (SvGridBoard) ships in @svgrid/enterprise and registers itself
@@ -216,7 +216,11 @@ export function createKeyboard<
216
216
  }
217
217
  }
218
218
  if (column?.columnDef.editorType === "checkbox") {
219
- ctx.toggleBooleanCell(current.rowIndex, current.colIndex);
219
+ // Same rule as a click: `editable: false` (or an editable callback
220
+ // saying no) keeps the checkbox read-only.
221
+ if (ctx.isCellEditableAt?.(current.rowIndex, current.colIndex) ?? true) {
222
+ ctx.toggleBooleanCell(current.rowIndex, current.colIndex);
223
+ }
220
224
  return;
221
225
  }
222
226
  ctx.onCellDoubleClick(current.rowIndex, current.colIndex);
@@ -657,6 +657,43 @@ describe('createSelection - onCellClick', () => {
657
657
  expect(ctx.selectionRange.anchor).toEqual({ rowIndex: 0, colIndex: 0 })
658
658
  })
659
659
 
660
+ describe('boolean cells that are not declared checkboxes', () => {
661
+ const boolCtx = (over: any = {}, columnDef: any = {}) =>
662
+ clickCtx({
663
+ allRows: [makeDataRow('r0', { done: true })],
664
+ allColumns: [makeColumn('done', { field: 'done', ...columnDef })],
665
+ ...over,
666
+ })
667
+
668
+ it('do not toggle (or write the row) while inline editing is off', () => {
669
+ const ctx = boolCtx({ editingEnabled: false })
670
+ createSelection(ctx).onCellClick(0, 0)
671
+ expect(ctx.toggleBooleanCell).not.toHaveBeenCalled()
672
+ expect(ctx.selectionRange.anchor).toEqual({ rowIndex: 0, colIndex: 0 })
673
+ })
674
+
675
+ it('toggle when inline editing is on and the cell is editable', () => {
676
+ const ctx = boolCtx({ editingEnabled: true, isCellEditableAt: () => true })
677
+ createSelection(ctx).onCellClick(0, 0)
678
+ expect(ctx.toggleBooleanCell).toHaveBeenCalledWith(0, 0)
679
+ })
680
+
681
+ it('do not toggle when the column has its own cell renderer', () => {
682
+ const ctx = boolCtx({ editingEnabled: true, isCellEditableAt: () => true }, { cell: () => 'flag' })
683
+ createSelection(ctx).onCellClick(0, 0)
684
+ expect(ctx.toggleBooleanCell).not.toHaveBeenCalled()
685
+ })
686
+ })
687
+
688
+ it('a declared checkbox that is not editable stays put', () => {
689
+ const ctx = clickCtx({
690
+ allColumns: [makeColumn('c0', { editorType: 'checkbox', editable: false })],
691
+ isCellEditableAt: () => false,
692
+ })
693
+ createSelection(ctx).onCellClick(0, 0)
694
+ expect(ctx.toggleBooleanCell).not.toHaveBeenCalled()
695
+ })
696
+
660
697
  it('enters edit mode when clicking the previously-active editable cell', () => {
661
698
  const ctx = clickCtx({
662
699
  editingEnabled: true,
package/src/selection.ts CHANGED
@@ -655,12 +655,22 @@ export function createSelection<
655
655
  row: row.original as TData,
656
656
  });
657
657
 
658
- const isCheckboxColumn =
659
- column.columnDef.editorType === "checkbox" ||
660
- typeof cellValue === "boolean";
661
-
662
- if (isCheckboxColumn) {
663
- ctx.toggleBooleanCell(rowIndex, colIndex);
658
+ // A declared checkbox editor toggles on click unless the cell is not
659
+ // editable. A value that only HAPPENS to be boolean is drawn as a read-only
660
+ // checkbox, so it toggles only when inline editing is on - it used to flip
661
+ // and write the row on any click, in grids with editing switched off. A
662
+ // column with its own `cell` renderer draws (and handles) the value itself.
663
+ const declaredCheckbox = column.columnDef.editorType === "checkbox";
664
+ const inferredCheckbox =
665
+ !declaredCheckbox &&
666
+ typeof cellValue === "boolean" &&
667
+ column.columnDef.cell == null;
668
+
669
+ if (declaredCheckbox || inferredCheckbox) {
670
+ const editable = ctx.isCellEditableAt?.(rowIndex, colIndex) ?? true;
671
+ if (editable && (declaredCheckbox || ctx.editingEnabled)) {
672
+ ctx.toggleBooleanCell(rowIndex, colIndex);
673
+ }
664
674
  setActiveCell(rowIndex, colIndex);
665
675
  setSelection(rowIndex, colIndex);
666
676
  ctx.editingCell = null;
@@ -6,7 +6,7 @@
6
6
  * selection actually changed.
7
7
  */
8
8
  import { describe, expect, it } from 'vitest'
9
- import { subscribeGrid, subscribeSvGrid } from './subscribe'
9
+ import { subscribeGrid, subscribeSvGrid } from './subscribe.svelte'
10
10
 
11
11
  type Listener = () => void
12
12
 
@@ -23,7 +23,11 @@ export function subscribeGrid<
23
23
  grid: SvGrid<TData>,
24
24
  selector: (state: Record<string, any>) => TSelected,
25
25
  ) {
26
- let current = selector(grid.store.state)
26
+ // $state, not a plain variable: core.ts keeps a deliberately framework-free
27
+ // store, so this Svelte adapter is where reactivity belongs. Without it a
28
+ // `$derived` reading .current never re-runs and the docs' headless examples
29
+ // render their seeded state forever.
30
+ let current = $state(selector(grid.store.state))
27
31
  grid.store.subscribe(() => {
28
32
  const next = selector(grid.store.state)
29
33
  if (!shallowCompare(current, next)) current = next
@@ -620,6 +620,35 @@ describe('SvGrid - custom snippet cell', () => {
620
620
  destroy()
621
621
  }
622
622
  })
623
+
624
+ it('a boolean column with its own cell renderer draws the renderer, not the built-in checkbox', async () => {
625
+ // The checkbox branch used to run before the `cell` branch, so any boolean
626
+ // value came out as a read-only checkbox and the renderer was never called.
627
+ const flagCols: ColumnDef<typeof fullFeatures, Person>[] = personColumns.map((c) =>
628
+ c.field === 'active'
629
+ ? { ...c, cell: (ctx: any) => ((ctx.row.original as Person).active ? 'ON' : 'OFF') }
630
+ : c,
631
+ )
632
+ const { target, destroy } = await mountGrid({ columns: flagCols, showRowSelection: false })
633
+ try {
634
+ await tick()
635
+ expect(target.querySelectorAll('.sv-grid-checkbox-readonly').length).toBe(0)
636
+ expect(target.textContent).toContain('ON')
637
+ expect(target.textContent).toContain('OFF')
638
+ } finally {
639
+ destroy()
640
+ }
641
+ })
642
+
643
+ it('a boolean column without a renderer still draws the read-only checkbox', async () => {
644
+ const { target, destroy } = await mountGrid({ showRowSelection: false })
645
+ try {
646
+ await tick()
647
+ expect(target.querySelectorAll('.sv-grid-checkbox-readonly').length).toBeGreaterThan(0)
648
+ } finally {
649
+ destroy()
650
+ }
651
+ })
623
652
  })
624
653
 
625
654
  describe('SvGrid - filterMode shortcuts', () => {
@@ -58,6 +58,22 @@ describe('unknown field', () => {
58
58
  expect(run({ data: [...loading, ...data], columns: [{ field: 'naem' }] })).toHaveLength(1)
59
59
  })
60
60
 
61
+ it('does not walk a million placeholders looking for the sample', () => {
62
+ // A row model scrolled to its middle: placeholders up to the loaded block.
63
+ const placeholder = createRowPlaceholder('loading', { index: 0 })
64
+ const big = new Array(1_000_000).fill(placeholder)
65
+ big.push({ name: 'x' })
66
+ let reads = 0
67
+ const counted = new Proxy(big, {
68
+ get(target, key, receiver) {
69
+ if (typeof key === 'string' && /^\d+$/.test(key)) reads += 1
70
+ return Reflect.get(target, key, receiver)
71
+ },
72
+ })
73
+ expect(run({ data: counted, columns: [{ field: 'naem' }] })).toEqual([])
74
+ expect(reads).toBeLessThanOrEqual(1000)
75
+ })
76
+
61
77
  it('looks through group columns', () => {
62
78
  const msgs = run({ columns: [{ header: 'Group', columns: [{ field: 'nope' }] }] })
63
79
  expect(msgs[0]).toContain('"nope" does not exist')
package/src/validate.ts CHANGED
@@ -24,6 +24,8 @@ import { rowPlaceholderState } from './server-block-cache'
24
24
 
25
25
  /** How many rows to sample when deciding whether a `field` exists. */
26
26
  const FIELD_SAMPLE_ROWS = 10
27
+ /** How many rows to look through for that sample, placeholders included. */
28
+ const FIELD_SCAN_ROWS = 1000
27
29
 
28
30
  const DOCS = 'https://svgrid.com/docs/getting-started/3-data-and-columns/'
29
31
 
@@ -79,9 +81,15 @@ export function validateGridConfig<
79
81
  // no data keys at all and say nothing about the columns.
80
82
  // Stops at the sample size: this runs on every data change, and a filter
81
83
  // over all the rows first was O(n) per tick on a live feed.
84
+ // The scan is bounded too: a row model scrolled to the middle of a million
85
+ // rows, or just purged by a sort, hands over a list that is placeholders
86
+ // up to the loaded block, and walking all of them for ten real rows was
87
+ // O(n) per block landing. Finding none means nothing to judge by.
82
88
  const sample: TData[] = []
83
- for (const row of input.data ?? []) {
84
- if (sample.length >= FIELD_SAMPLE_ROWS) break
89
+ const data = input.data ?? []
90
+ const scan = Math.min(data.length, FIELD_SCAN_ROWS)
91
+ for (let i = 0; i < scan && sample.length < FIELD_SAMPLE_ROWS; i += 1) {
92
+ const row = data[i]!
85
93
  if (!rowPlaceholderState(row)) sample.push(row)
86
94
  }
87
95
  // Under server-side grouping the first rows are group rows, which carry
@@ -1,80 +0,0 @@
1
- import { r as e } from "./server-block-cache-DIqb3VZn.js";
2
- //#region src/validate.ts
3
- var t = 10, n = "https://svgrid.com/docs/getting-started/3-data-and-columns/";
4
- function r(e, t = []) {
5
- for (let n of e) n.columns?.length ? r(n.columns, t) : t.push(n);
6
- return t;
7
- }
8
- function i(i) {
9
- let o = [], s = r(i.columns ?? []), c = [];
10
- for (let n of i.data ?? []) {
11
- if (c.length >= t) break;
12
- e(n) || c.push(n);
13
- }
14
- if (c.length && !i.serverGroup) {
15
- let e = /* @__PURE__ */ new Set();
16
- for (let t of c) if (t && typeof t == "object") for (let n of Object.keys(t)) e.add(n);
17
- for (let t of s) {
18
- let r = t.field;
19
- if (!r || t.fieldFn || e.has(r)) continue;
20
- let i = a(r, [...e]);
21
- o.push(`[svgrid] Column field "${r}" does not exist on your row data, so that column renders empty.${i ? ` Did you mean "${i}"?` : ""} Available keys: ${[...e].slice(0, 12).join(", ")}. See ${n}`);
22
- }
23
- }
24
- let l = /* @__PURE__ */ new Map();
25
- for (let e of s) {
26
- let t = e.id ?? e.field;
27
- t && l.set(t, (l.get(t) ?? 0) + 1);
28
- }
29
- for (let [e, t] of l) t > 1 && o.push(`[svgrid] ${t} columns share the id "${e}". Column state (sorting, filtering, pinning, width) is keyed by id, so they will act as one. Give each an explicit \`id\`. See ${n}`);
30
- let u = i.pageable === void 0 && i.showPagination === void 0;
31
- i.pageSize !== void 0 && u && o.push("[svgrid] `pageSize` is set but pagination was never turned on, so it has no effect and every row renders. Add `pageable` to switch the pager on."), !(i.sortable === !0 || i.features?.rowSortingFeature) && s.some((e) => e.sortable === !0) && o.push("[svgrid] A column sets `sortable: true`, but sorting is not enabled on the grid, so its header does nothing. Add `sortable` to <SvGrid> (it registers the sorting feature for you).");
32
- let d = /* @__PURE__ */ new Set();
33
- for (let e of s) {
34
- let t = e.id ?? e.field;
35
- t && d.add(t);
36
- }
37
- for (let e of i.groupBy ?? []) {
38
- if (d.has(e)) continue;
39
- let t = a(e, [...d]);
40
- o.push(`[svgrid] \`groupBy\` refers to column "${e}", which does not exist, so that grouping level is ignored.${t ? ` Did you mean "${t}"?` : ""} Column ids: ${[...d].slice(0, 12).join(", ")}`);
41
- }
42
- let f = i.treeData?.column;
43
- if (f && !d.has(f) && o.push(`[svgrid] \`treeData.column\` refers to column "${f}", which does not exist, so the expander falls back to the first visible column.`), c.length && i.treeData) {
44
- let e = /* @__PURE__ */ new Set();
45
- for (let t of c) if (t && typeof t == "object") for (let n of Object.keys(t)) e.add(n);
46
- let t = i.treeData.parentField;
47
- if (t && !e.has(t)) {
48
- let n = a(t, [...e]);
49
- o.push(`[svgrid] \`treeData.parentField\` is "${t}", which is not on your row data, so every row becomes a root and no hierarchy appears.${n ? ` Did you mean "${n}"?` : ""}`);
50
- }
51
- let n = i.treeData.idField;
52
- n && !e.has(n) && o.push(`[svgrid] \`treeData.idField\` is "${n}", which is not on your row data, so parent lookups cannot match and the tree stays flat.`);
53
- }
54
- return i.gantt && i.treeData && o.push("[svgrid] `gantt` and `treeData` are both set. The Gantt builds its own work-breakdown tree from `gantt.parentField` and needs every descendant row, but `treeData` hides collapsed children from the view, so summary bars will under-report. Remove `treeData` and set `gantt.parentField` instead."), (i.initialColumnPinning?.left?.length ?? 0) + (i.initialColumnPinning?.right?.length ?? 0) > 0 && i.columnVirtualization !== !1 && o.push("[svgrid] `initialColumnPinning` is set while column virtualization is on (its default), so the pinned columns will not stick - the virtualizer recycles column nodes. Add `columnVirtualization={false}`."), i.externalPagination === !0 && i.rowCount === void 0 && o.push("[svgrid] `externalPagination` is on but `rowCount` is not set, so the pager cannot know how many pages exist. Pass the server total as `rowCount`."), i.externalSort === !0 && !i.onSortingChange && o.push("[svgrid] `externalSort` is on but there is no `onSortingChange` handler, so clicking a header changes nothing - the grid stopped sorting and nobody is listening. Add `onSortingChange` and re-fetch in your handler."), i.externalFilter === !0 && !i.onFiltersChange && o.push("[svgrid] `externalFilter` is on but there is no `onFiltersChange` handler, so filtering the grid changes nothing. Add `onFiltersChange` and re-fetch in your handler."), o;
55
- }
56
- function a(e, t) {
57
- let n, r = Infinity;
58
- for (let i of t) {
59
- let t = o(e.toLowerCase(), i.toLowerCase());
60
- t < r && (r = t, n = i);
61
- }
62
- let i = Math.min(3, Math.floor(e.length / 3) + 1);
63
- return r <= i ? n : void 0;
64
- }
65
- function o(e, t) {
66
- if (e === t) return 0;
67
- let n = Array(t.length + 1);
68
- for (let e = 0; e <= t.length; e++) n[e] = e;
69
- for (let r = 1; r <= e.length; r++) {
70
- let i = n[0];
71
- n[0] = r;
72
- for (let a = 1; a <= t.length; a++) {
73
- let o = Math.min(n[a] + 1, n[a - 1] + 1, i + (e[r - 1] === t[a - 1] ? 0 : 1));
74
- i = n[a], n[a] = o;
75
- }
76
- }
77
- return n[t.length];
78
- }
79
- //#endregion
80
- export { i as validateGridConfig };
File without changes