@svgrid/enterprise 3.1.4 → 3.1.6

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 (35) hide show
  1. package/dist/cdn/svgrid-enterprise.svelte-external.js +8956 -8606
  2. package/dist/designer/assets/GridMenus-CvcASlKv.js +8 -0
  3. package/dist/designer/assets/SvListBox-BlmmzYcU.js +110 -0
  4. package/dist/designer/assets/{index-BgVTSVoK.js → index-Dknv-Eu8.js} +432 -370
  5. package/dist/designer/assets/index-KmSEY7Lb.css +1 -0
  6. package/dist/designer/assets/src-D4PRZJYA.js +58 -0
  7. package/dist/designer/assets/{validate-YxSrBF1H.js → validate-B71dE1Cs.js} +1 -1
  8. package/dist/designer/index.html +4 -4
  9. package/dist/node/studio.js +402 -81
  10. package/dist/server/server-row-model.js +199 -26
  11. package/dist/studio/emit-project.js +86 -56
  12. package/dist/studio/emit-schema.d.ts +3 -0
  13. package/dist/studio/emit-schema.js +128 -29
  14. package/dist/studio/index.d.ts +2 -1
  15. package/dist/studio/index.js +2 -1
  16. package/dist/studio/init-flow.d.ts +3 -0
  17. package/dist/studio/init-flow.js +5 -2
  18. package/dist/studio/project.d.ts +10 -1
  19. package/dist/studio/project.js +28 -15
  20. package/dist/studio/samples/restaurant.js +1 -1
  21. package/dist/studio/sveltekit3.d.ts +38 -0
  22. package/dist/studio/sveltekit3.js +194 -0
  23. package/dist/studio/ui-components.generated.js +15 -4
  24. package/dist/version.d.ts +2 -2
  25. package/dist/version.js +2 -2
  26. package/dist/watermark.js +31 -1
  27. package/dist/wc/{GridMenus-6_fF5jIm.js → GridMenus-fWbBUiRH.js} +121 -121
  28. package/dist/wc/sv-sheet-element.js +10979 -10522
  29. package/dist/wc/{svelte-virtualizer.svelte-Du4tjrIP.js → svelte-virtualizer.svelte-Dntdh8Ms.js} +103 -56
  30. package/dist/wc/{validate-JAuMsk6Z.js → validate-43C03DLP.js} +1 -1
  31. package/package.json +3 -3
  32. package/dist/designer/assets/GridMenus-DGZJKFvX.js +0 -8
  33. package/dist/designer/assets/SvListBox-v2jm0-vf.js +0 -110
  34. package/dist/designer/assets/index-l21iGkpF.css +0 -1
  35. package/dist/designer/assets/src-BqEXoOog.js +0 -58
@@ -21,7 +21,7 @@
21
21
  * `createServerGroupModel` (the previous model, block-append with a "Load N
22
22
  * more" button) is still exported for anyone on it; this one supersedes it.
23
23
  */
24
- import { createBlockCache, createRowPlaceholder, rowPlaceholderState, toServerFilterColumns,
24
+ import { createBlockCache, createRowPlaceholder, createWindowedData, isWindowedData, rowPlaceholderState, toServerFilterColumns,
25
25
  // The subpath, not the root: the root re-exports SvGrid.svelte, which
26
26
  // would land in every bundle and test that touches this model.
27
27
  } from '@svgrid/grid/server';
@@ -412,17 +412,80 @@ export function createServerRowModel(source, options) {
412
412
  leafRuns.set(store, run);
413
413
  return run;
414
414
  }
415
+ /**
416
+ * A level made only of data rows maps one to one: its i-th display row is
417
+ * its i-th child. Such a level is not walked or copied - the flatten records
418
+ * it as one lazy piece and `lazyLeafAt` builds a row when the grid reads it,
419
+ * so a 100M-row level costs the rows on screen, not 100M. Groups, tree
420
+ * data, pivot, load-more, detail rows and child-row paging still walk.
421
+ */
422
+ function isLazyLeafLevel(store) {
423
+ return (!treeData &&
424
+ !pivotMode &&
425
+ store.level >= groupBy.length &&
426
+ !store.loadMore &&
427
+ openDetails.size === 0 &&
428
+ !paging?.paginateChildRows);
429
+ }
430
+ // Replaced when a write settles (see `saving`): the grid writes a cell edit
431
+ // into the row object it was handed, so a row the server refused would keep
432
+ // showing the refused value while its child, which nothing changed, still
433
+ // matched the memo.
434
+ let lazyMemo = new WeakMap();
435
+ const LAZY_MEMO_CAP = 20000;
436
+ /** Row `index` of a lazy leaf level, built once per child object it holds. */
437
+ function lazyLeafAt(store, index) {
438
+ const child = store.cache.getRow(index);
439
+ const ph = rowPlaceholderState(child);
440
+ if (ph) {
441
+ const d = ph === 'failed' ? FAILED_ROW : LOADING_ROW;
442
+ return { child, d, g: toGridRow(d) };
443
+ }
444
+ let memo = lazyMemo.get(store);
445
+ if (!memo) {
446
+ memo = new Map();
447
+ lazyMemo.set(store, memo);
448
+ }
449
+ const hit = memo.get(index);
450
+ if (hit && hit.child === child)
451
+ return hit;
452
+ const leaf = child;
453
+ const d = { kind: 'leaf', id: leaf.id, level: store.level, route: store.route, data: leaf.data };
454
+ const entry = { child, d, g: toGridRow(d, leaf.data) };
455
+ memo.set(index, entry);
456
+ if (memo.size > LAZY_MEMO_CAP) {
457
+ let drop = LAZY_MEMO_CAP / 4;
458
+ for (const key of memo.keys()) {
459
+ if (drop-- <= 0)
460
+ break;
461
+ memo.delete(key);
462
+ }
463
+ }
464
+ return entry;
465
+ }
415
466
  function flatten() {
416
- let display = [];
417
- let grid = [];
467
+ const pieces = [];
468
+ let total = 0;
469
+ let open = null;
418
470
  segments = [];
419
471
  const pushRow = (d, data) => {
420
- display.push(d);
421
- grid.push(toGridRow(d, data));
472
+ if (!open) {
473
+ open = { start: total, length: 0, display: [], grid: [] };
474
+ pieces.push(open);
475
+ }
476
+ open.display.push(d);
477
+ open.grid.push(toGridRow(d, data));
478
+ open.length += 1;
479
+ total += 1;
480
+ };
481
+ const pushPiece = (piece) => {
482
+ open = null;
483
+ pieces.push(piece);
484
+ total += piece.length;
422
485
  };
423
486
  if (grandTotalRow === 'top' && grandTotal)
424
487
  pushRow(grandTotalDisplay(grandTotal), grandTotal);
425
- const bodyStart = display.length;
488
+ const bodyStart = total;
426
489
  /**
427
490
  * A `loadMore` level shows its loaded prefix and a "more" row; rows
428
491
  * past the first placeholder are not the grid's to scroll into.
@@ -434,32 +497,34 @@ export function createServerRowModel(source, options) {
434
497
  return n;
435
498
  };
436
499
  const walk = (store, from = 0, to = Number.POSITIVE_INFINITY) => {
500
+ if (isLazyLeafLevel(store)) {
501
+ // A pure leaf level: one lazy piece, nothing copied.
502
+ const end = Math.min(store.cache.windowedRows().length, to);
503
+ if (end > from) {
504
+ segments.push({ store, displayStart: total, storeStart: from, length: end - from });
505
+ pushPiece({ start: total, length: end - from, store, from });
506
+ }
507
+ return;
508
+ }
437
509
  const rows = store.cache.rows();
438
510
  const run = store.loadMore ? null : leafRun(store, rows);
439
511
  if (run) {
440
512
  // A level of leaves maps one to one: copy the range out of the run.
441
513
  const end = Math.min(rows.length, to);
442
514
  if (end > from) {
443
- segments.push({ store, displayStart: display.length, storeStart: from, length: end - from });
444
- const { display: d, grid: g } = run;
445
- if (display.length === 0) {
446
- // The common flat case: the whole list is this run. A slice is a
447
- // block copy; a push per row was the rest of the flatten's cost.
448
- display = d.slice(from, end);
449
- grid = g.slice(from, end);
450
- }
451
- else {
452
- for (let i = from; i < end; i += 1) {
453
- display.push(d[i]);
454
- grid.push(g[i]);
455
- }
456
- }
515
+ segments.push({ store, displayStart: total, storeStart: from, length: end - from });
516
+ pushPiece({
517
+ start: total,
518
+ length: end - from,
519
+ display: run.display.slice(from, end),
520
+ grid: run.grid.slice(from, end),
521
+ });
457
522
  }
458
523
  return;
459
524
  }
460
525
  const end = Math.min(rows.length, to, store.loadMore ? loadedPrefix(rows) : Number.POSITIVE_INFINITY);
461
526
  let segStart = from;
462
- let dispStart = display.length;
527
+ let dispStart = total;
463
528
  const flushSegment = (upTo) => {
464
529
  if (upTo > segStart) {
465
530
  segments.push({ store, displayStart: dispStart, storeStart: segStart, length: upTo - segStart });
@@ -523,7 +588,7 @@ export function createServerRowModel(source, options) {
523
588
  }, child.data);
524
589
  }
525
590
  segStart = i + 1;
526
- dispStart = display.length;
591
+ dispStart = total;
527
592
  }
528
593
  }
529
594
  flushSegment(end);
@@ -548,7 +613,7 @@ export function createServerRowModel(source, options) {
548
613
  // A page of top-level rows. The root rows array is already sized to
549
614
  // the count (placeholders for what has not loaded), so the page
550
615
  // exists before its block does.
551
- const count = root.cache.rowCount() ?? root.cache.rows().length;
616
+ const count = root.cache.rowCount() ?? root.cache.windowedRows().length;
552
617
  pagerRowCount = count;
553
618
  const start = Math.min(pageIndex * pageSize, Math.max(0, count - 1));
554
619
  walk(root, start, start + pageSize);
@@ -557,7 +622,32 @@ export function createServerRowModel(source, options) {
557
622
  walk(root);
558
623
  }
559
624
  }
625
+ /** Every piece as plain arrays. Lazy pieces read through `lazyLeafAt`. */
626
+ const materialize = () => {
627
+ if (pieces.length === 1 && pieces[0].display)
628
+ return { display: pieces[0].display, grid: pieces[0].grid };
629
+ const display = [];
630
+ const grid = [];
631
+ for (const p of pieces) {
632
+ if (p.display) {
633
+ for (let i = 0; i < p.length; i += 1) {
634
+ display.push(p.display[i]);
635
+ grid.push(p.grid[i]);
636
+ }
637
+ continue;
638
+ }
639
+ for (let i = 0; i < p.length; i += 1) {
640
+ const e = lazyLeafAt(p.store, p.from + i);
641
+ display.push(e.d);
642
+ grid.push(e.g);
643
+ }
644
+ }
645
+ return { display, grid };
646
+ };
560
647
  if (paging?.paginateChildRows) {
648
+ // Child-row paging cuts the flattened body, so it works on plain
649
+ // arrays; no level is lazy in this mode (see isLazyLeafLevel).
650
+ const { display, grid } = materialize();
561
651
  // A page of the flattened tree. Cut the body and re-base the segments
562
652
  // that cross into the page; the grand total stays on every page.
563
653
  const bodyLength = display.length - bodyStart;
@@ -582,10 +672,47 @@ export function createServerRowModel(source, options) {
582
672
  });
583
673
  }
584
674
  segments = cut;
675
+ if (grandTotalRow === 'bottom' && grandTotal) {
676
+ const d = grandTotalDisplay(grandTotal);
677
+ display.push(d);
678
+ grid.push(toGridRow(d, grandTotal));
679
+ }
680
+ return { display, grid };
585
681
  }
586
682
  if (grandTotalRow === 'bottom' && grandTotal)
587
683
  pushRow(grandTotalDisplay(grandTotal), grandTotal);
588
- return { display, grid };
684
+ if (!pieces.some((p) => p.store))
685
+ return materialize();
686
+ // At least one lazy piece: hand out windowed arrays that find the piece
687
+ // holding an index by binary search on the piece starts.
688
+ const pieceAt = (index) => {
689
+ let lo = 0;
690
+ let hi = pieces.length - 1;
691
+ while (lo <= hi) {
692
+ const mid = (lo + hi) >> 1;
693
+ const p = pieces[mid];
694
+ if (index < p.start)
695
+ hi = mid - 1;
696
+ else if (index >= p.start + p.length)
697
+ lo = mid + 1;
698
+ else
699
+ return p;
700
+ }
701
+ return undefined;
702
+ };
703
+ const entryAt = (index) => {
704
+ const p = pieceAt(index);
705
+ if (!p)
706
+ return undefined;
707
+ const offset = index - p.start;
708
+ if (p.display)
709
+ return { d: p.display[offset], g: p.grid[offset] };
710
+ return lazyLeafAt(p.store, p.from + offset);
711
+ };
712
+ return {
713
+ display: createWindowedData(total, (i) => entryAt(i)?.d),
714
+ grid: createWindowedData(total, (i) => entryAt(i)?.g),
715
+ };
589
716
  }
590
717
  function pagination() {
591
718
  if (!paging)
@@ -679,8 +806,38 @@ export function createServerRowModel(source, options) {
679
806
  let held = false;
680
807
  function holdRows() {
681
808
  const ms = options.keepRowsWhileLoading ?? 0;
682
- if (ms > 0 && current.displayRows.length > 0)
809
+ if (ms > 0 && current.displayRows.length > 0) {
683
810
  holdUntil = Date.now() + ms;
811
+ freezeCurrent();
812
+ }
813
+ }
814
+ /**
815
+ * Held rows must not change under the user, but a lazy list reads the live
816
+ * caches, which the sort or filter that started the hold is about to purge.
817
+ * Copy the rows around the last viewport into a snapshot and serve the held
818
+ * lists from it. Only the grid's subscription is told (so it re-reads);
819
+ * `onChange` still fires once, when the hold ends.
820
+ */
821
+ function freezeCurrent() {
822
+ if (!isWindowedData(current.gridRows))
823
+ return; // plain arrays are a snapshot already
824
+ const n = current.gridRows.length;
825
+ const MARGIN = 200;
826
+ const from = Math.max(0, lastViewport[0] - MARGIN);
827
+ const to = Math.min(n - 1, lastViewport[1] + MARGIN);
828
+ const display = new Map();
829
+ const grid = new Map();
830
+ for (let i = from; i <= to; i += 1) {
831
+ display.set(i, current.displayRows[i]);
832
+ grid.set(i, current.gridRows[i]);
833
+ }
834
+ current = {
835
+ ...current,
836
+ displayRows: createWindowedData(n, (i) => display.get(i) ?? LOADING_ROW),
837
+ gridRows: createWindowedData(n, (i) => grid.get(i) ?? toGridRow(LOADING_ROW)),
838
+ };
839
+ for (const notify of subscribers)
840
+ notify();
684
841
  }
685
842
  /** Whether the rows on screen should stay a little longer. */
686
843
  function holding() {
@@ -1012,7 +1169,7 @@ export function createServerRowModel(source, options) {
1012
1169
  }
1013
1170
  if (tx.add && tx.add.length) {
1014
1171
  const total = store.cache.rowCount();
1015
- const at = tx.addIndex ?? (total ?? store.cache.rows().length);
1172
+ const at = tx.addIndex ?? (total ?? store.cache.windowedRows().length);
1016
1173
  const children = toChildren(route, at, tx.add);
1017
1174
  const placed = store.cache.insert(at, children);
1018
1175
  if (placed)
@@ -1090,6 +1247,9 @@ export function createServerRowModel(source, options) {
1090
1247
  }
1091
1248
  finally {
1092
1249
  writesInFlight -= 1;
1250
+ // Rows are rebuilt from the cache on the next read, so an edit the
1251
+ // server refused (and the model never applied) is gone from the grid.
1252
+ lazyMemo = new WeakMap();
1093
1253
  scheduleEmit();
1094
1254
  }
1095
1255
  }
@@ -1358,6 +1518,10 @@ export function createServerRowModel(source, options) {
1358
1518
  expandAll(opts) {
1359
1519
  autoExpand = !!opts?.includeUnloaded;
1360
1520
  for (const store of [...stores.values()]) {
1521
+ // A pure leaf level holds no groups to open; reading it would walk
1522
+ // every row it has.
1523
+ if (isLazyLeafLevel(store))
1524
+ continue;
1361
1525
  for (const child of store.cache.rows()) {
1362
1526
  if (rowPlaceholderState(child) || child.kind !== 'group')
1363
1527
  continue;
@@ -1478,6 +1642,15 @@ export function createServerRowModel(source, options) {
1478
1642
  },
1479
1643
  setViewport(startIndex, endIndex) {
1480
1644
  lastViewport = [Math.max(0, startIndex), Math.max(startIndex, endIndex)];
1645
+ // The first viewport starts the model. Its first load used to come from
1646
+ // the grid reporting its starting sort on mount (setSort -> rebuild);
1647
+ // the grid no longer reports a mount as a sort change, and a model that
1648
+ // nothing else touched (no sort, no filter, a layout equal to the one
1649
+ // it was built with) then never asked for a single block.
1650
+ if (!disposed && !stores.has(routeKey([]))) {
1651
+ rebuild();
1652
+ return;
1653
+ }
1481
1654
  applyViewport();
1482
1655
  },
1483
1656
  setPage(next) {