@svgrid/grid 3.0.4 → 3.0.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.
- package/README.md +32 -11
- package/dist/GridMenus.svelte +34 -4
- package/dist/SvGrid.controller.svelte.d.ts +33 -5
- package/dist/SvGrid.controller.svelte.js +397 -58
- package/dist/SvGrid.css +200 -2
- package/dist/SvGrid.svelte +350 -40
- package/dist/SvGrid.types.d.ts +433 -7
- package/dist/SvGridDropdown.svelte +20 -0
- package/dist/SvGridDropdown.svelte.d.ts +12 -0
- package/dist/SvModal.svelte +22 -0
- package/dist/SvModal.svelte.d.ts +7 -0
- package/dist/a11y/dismissable.d.ts +10 -0
- package/dist/a11y/dismissable.js +24 -2
- package/dist/build-api.js +92 -37
- package/dist/cdn/{GridMenus-BL7ZgQvU.js → GridMenus-C-NtjlrJ.js} +155 -146
- package/dist/cdn/GridMenus-TssHej3q.js +644 -0
- package/dist/cdn/{SvChartMenu-FBSMINA6.js → SvChartMenu-BFSTpvB-.js} +1 -1
- package/dist/cdn/{SvChartMenu-Bl6PBbkT.js → SvChartMenu-BZ1Iwv0y.js} +1 -1
- package/dist/cdn/{SvDateTimePicker-DQwt4UAs.js → SvDateTimePicker-D9u_cOf8.js} +1 -1
- package/dist/cdn/{SvDateTimePicker-vNU6bZ-q.js → SvDateTimePicker-s_6IUpI7.js} +1 -1
- package/dist/cdn/{SvGridCellEditor-B1p-vCK5.js → SvGridCellEditor-BlyCSp0q.js} +1 -1
- package/dist/cdn/{SvGridCellEditor-D_0q4xAu.js → SvGridCellEditor-ClRRA9lW.js} +1 -1
- package/dist/cdn/{SvGridChart-DzLkSwCH.js → SvGridChart-B4EV8U6t.js} +4 -4
- package/dist/cdn/{SvGridChart-C3EWAZaM.js → SvGridChart-DsmwTyZF.js} +4 -4
- package/dist/cdn/{SvGridChartBuilder-CfII62sZ.js → SvGridChartBuilder-cHloHobN.js} +4 -4
- package/dist/cdn/{SvGridChartBuilder-BE2T1ykB.js → SvGridChartBuilder-vOl7C0Za.js} +4 -4
- package/dist/cdn/{SvGridChartPanel-xf4sWVTo.js → SvGridChartPanel-AcqF-Ll5.js} +5 -5
- package/dist/cdn/{SvGridChartPanel-CCX5_Wgd.js → SvGridChartPanel-biMtSBPA.js} +5 -5
- package/dist/cdn/{SvGridChartView-DYwabQWj.js → SvGridChartView-Dx_GmZuL.js} +2 -2
- package/dist/cdn/{SvGridChartView-CfuXmY5I.js → SvGridChartView-I3rDHXAq.js} +2 -2
- package/dist/cdn/{SvGridDropdown-D0VdjeR8.js → SvGridDropdown-BWeM3lMe.js} +46 -46
- package/dist/cdn/{SvGridDropdown-D13MtJ2j.js → SvGridDropdown-CJ6QqTxu.js} +67 -67
- package/dist/cdn/{SvModal-CIZWCcad.js → SvModal-ClxGmpAc.js} +187 -186
- package/dist/cdn/{SvModal-BAE-pjZX.js → SvModal-DhB_VMFR.js} +148 -147
- package/dist/cdn/{cell-formatting-C2Hf5gqd.js → cell-formatting-DVVKOSI9.js} +1 -1
- package/dist/cdn/{chart-Dz7SqMXH.js → chart-DEZxwC0i.js} +1 -1
- package/dist/cdn/{chart-panel-messages-CUbf2R4i.js → chart-panel-messages-D12XcTZJ.js} +1 -1
- package/dist/cdn/{chart-panel-messages-CmNrMdsr.js → chart-panel-messages-DguS7gC5.js} +1 -1
- package/dist/cdn/{chart-summary-BJW_tg_X.js → chart-summary-CMAXBGY-.js} +1 -1
- package/dist/cdn/{chart-trend-CaN9mDEV.js → chart-trend-BkEp61Gj.js} +1 -1
- package/dist/cdn/{dismissable-DAHetSNk.js → dismissable-Brc4EZU1.js} +5 -1
- package/dist/cdn/{export-format-CUDLy2yn.js → export-format-YzdwBww2.js} +1 -1
- package/dist/cdn/{row-resize-niQCp040.js → row-resize-DtOS1KG6.js} +41 -32
- package/dist/cdn/server-block-cache-DIqb3VZn.js +241 -0
- package/dist/cdn/{src-V1uu8iE9.js → src-CgoeSM8k.js} +5339 -4753
- package/dist/cdn/{src-BKhZ6eXd.js → src-DfwxB1TH.js} +9817 -9231
- package/dist/cdn/svgrid.js +28 -27
- package/dist/cdn/svgrid.svelte-external.js +28 -27
- package/dist/cdn/validate-AZoD8BoR.js +80 -0
- package/dist/cell-formatting.js +18 -1
- package/dist/cell-render.js +25 -6
- package/dist/clipboard.d.ts +2 -2
- package/dist/clipboard.js +72 -23
- package/dist/columns.js +7 -2
- package/dist/conditional-formatting.d.ts +2 -0
- package/dist/conditional-formatting.js +2 -0
- package/dist/core.d.ts +22 -6
- package/dist/core.js +401 -38
- package/dist/editing.js +34 -0
- package/dist/gantt-view.svelte.d.ts +24 -0
- package/dist/gantt-view.svelte.js +13 -0
- package/dist/grid-icons.d.ts +2 -2
- package/dist/grid-icons.js +2 -2
- package/dist/grid-messages.d.ts +10 -0
- package/dist/grid-messages.js +9 -0
- package/dist/headless.d.ts +5 -0
- package/dist/headless.js +12 -0
- package/dist/history.d.ts +21 -0
- package/dist/history.js +34 -4
- package/dist/index.d.ts +5 -4
- package/dist/index.js +3 -3
- package/dist/keyboard-handlers.js +24 -1
- package/dist/keyboard.js +4 -0
- package/dist/row-drag-touch.d.ts +1 -1
- package/dist/row-drag.js +35 -1
- package/dist/row-model.d.ts +139 -0
- package/dist/row-model.js +16 -0
- package/dist/row-resize.js +22 -1
- package/dist/selection.d.ts +2 -1
- package/dist/selection.js +31 -3
- package/dist/server-block-cache.d.ts +219 -0
- package/dist/server-block-cache.js +539 -0
- package/dist/server-data-source.d.ts +274 -8
- package/dist/server-data-source.js +229 -20
- package/dist/server.d.ts +13 -0
- package/dist/server.js +13 -0
- package/dist/sparkline.js +7 -5
- package/dist/svgrid-wrapper.types.d.ts +33 -0
- package/dist/validate.d.ts +4 -0
- package/dist/validate.js +29 -3
- package/package.json +11 -1
- package/src/GridMenus.svelte +34 -4
- package/src/SvGrid.controller.svelte.ts +397 -61
- package/src/SvGrid.css +200 -2
- package/src/SvGrid.svelte +350 -40
- package/src/SvGrid.types.ts +453 -7
- package/src/SvGridDropdown.svelte +20 -0
- package/src/SvModal.svelte +22 -0
- package/src/SvModal.test.ts +30 -1
- package/src/a11y/dismissable.test.ts +10 -1
- package/src/a11y/dismissable.ts +22 -2
- package/src/build-api.ts +96 -31
- package/src/cell-formatting.test.ts +17 -0
- package/src/cell-formatting.ts +18 -1
- package/src/cell-render.test.ts +24 -2
- package/src/cell-render.ts +24 -8
- package/src/clipboard.test.ts +107 -0
- package/src/clipboard.ts +62 -23
- package/src/columns.test.ts +4 -4
- package/src/columns.ts +7 -2
- package/src/conditional-formatting.test.ts +8 -0
- package/src/conditional-formatting.ts +3 -0
- package/src/core.rowmodel-cache.test.ts +51 -0
- package/src/core.tick-repair.test.ts +287 -0
- package/src/core.ts +407 -38
- package/src/editing.test.ts +52 -0
- package/src/editing.ts +33 -0
- package/src/gantt-stub.test.svelte +38 -0
- package/src/gantt-view.svelte.ts +35 -0
- package/src/grid-icons.ts +2 -2
- package/src/grid-messages.test.ts +7 -0
- package/src/grid-messages.ts +22 -0
- package/src/headless.ts +24 -0
- package/src/history.test.ts +49 -1
- package/src/history.ts +48 -2
- package/src/icon-seam.test.ts +2 -4
- package/src/index.ts +38 -10
- package/src/keyboard-handlers.coverage.test.ts +65 -0
- package/src/keyboard-handlers.ts +26 -1
- package/src/keyboard.ts +3 -0
- package/src/row-drag-touch.ts +2 -2
- package/src/row-drag.test.ts +51 -0
- package/src/row-drag.ts +35 -2
- package/src/row-model.ts +152 -0
- package/src/row-resize.ts +24 -1
- package/src/selection.test.ts +3 -0
- package/src/selection.ts +33 -3
- package/src/server-block-cache.test.ts +672 -0
- package/src/server-block-cache.ts +690 -0
- package/src/server-data-source.infinite.test.ts +405 -0
- package/src/server-data-source.test.ts +19 -0
- package/src/server-data-source.ts +496 -29
- package/src/server.ts +49 -0
- package/src/sparkline.test.ts +7 -0
- package/src/sparkline.ts +6 -4
- package/src/svgrid-wrapper.types.ts +32 -0
- package/src/svgrid.api.test.ts +21 -0
- package/src/svgrid.charting.test.ts +12 -4
- package/src/svgrid.context-menu.test.ts +12 -0
- package/src/svgrid.detail-rows.svelte.test.ts +187 -0
- package/src/svgrid.filter-menu-scroll.test.ts +9 -0
- package/src/svgrid.gantt-seam.test.ts +199 -0
- package/src/svgrid.live-update-paths.svelte.test.ts +184 -0
- package/src/svgrid.menu-scroll-close.test.ts +10 -0
- package/src/svgrid.row-model-prop.svelte.test.ts +352 -0
- package/src/svgrid.row-model-seam.svelte.test.ts +305 -0
- package/src/svgrid.row-pinning.test.ts +15 -1
- package/src/svgrid.selection-bar-seam.test.ts +1 -1
- package/src/svgrid.sticky-groups.svelte.test.ts +145 -0
- package/src/svgrid.upsell-license.test.ts +8 -5
- package/src/test-setup.ts +12 -0
- package/src/transaction.test.ts +58 -0
- package/src/validate.test.ts +29 -0
- package/src/validate.ts +34 -3
- package/dist/SvGroupCell.svelte +0 -141
- package/dist/SvGroupCell.svelte.d.ts +0 -49
- package/dist/SvRowGroupPanel.svelte +0 -186
- package/dist/SvRowGroupPanel.svelte.d.ts +0 -25
- package/dist/cdn/GridMenus-7kbpnnBW.js +0 -635
- package/dist/cdn/validate-_CDJzgIo.js +0 -75
- package/dist/server-group-model.d.ts +0 -98
- package/dist/server-group-model.js +0 -263
- package/src/SvGroupCell.svelte +0 -141
- package/src/SvRowGroupPanel.svelte +0 -186
- package/src/server-group-model.test.ts +0 -294
- package/src/server-group-model.ts +0 -370
package/dist/core.js
CHANGED
|
@@ -165,6 +165,19 @@ export const filterFns = {
|
|
|
165
165
|
const ROW_CTX = Symbol('svgrid.row.ctx');
|
|
166
166
|
const ROW_VALUES = Symbol('svgrid.row.values');
|
|
167
167
|
const ROW_CELLS = Symbol('svgrid.row.cells');
|
|
168
|
+
// Where the filtered stage last put this row: the generation of that full
|
|
169
|
+
// filter and the row's index in its output. Lets a tick swap a replaced row
|
|
170
|
+
// into place without re-filtering everything (see createFilteredRowModel).
|
|
171
|
+
const ROW_FILTER_GEN = Symbol('svgrid.row.filterGen');
|
|
172
|
+
const ROW_FILTER_POS = Symbol('svgrid.row.filterPos');
|
|
173
|
+
// Stamped on the rows a sort repair drops, so the merge skips them with one
|
|
174
|
+
// property read per row instead of a Set lookup (3 ms per 100k rows).
|
|
175
|
+
const ROW_SORT_DROPPED = Symbol('svgrid.row.sortDropped');
|
|
176
|
+
// The key(s) the last full sort computed for a row: the key itself for a
|
|
177
|
+
// one-clause sort, an array for several. Read by the repair path so a tick
|
|
178
|
+
// never recomputes a kept row's key.
|
|
179
|
+
const ROW_SORT_KEY = Symbol('svgrid.row.sortKey');
|
|
180
|
+
let sortDropGeneration = 0;
|
|
168
181
|
/**
|
|
169
182
|
* The methods every base row carries, defined ONCE and assigned by reference.
|
|
170
183
|
*
|
|
@@ -276,17 +289,32 @@ export function createCoreRowModel() {
|
|
|
276
289
|
* `columnFilteringFeature`; without it there are no filters to apply.
|
|
277
290
|
*/
|
|
278
291
|
export function createFilteredRowModel() {
|
|
292
|
+
/**
|
|
293
|
+
* The last full filter, so a tick can repair it. When `getRowModel`
|
|
294
|
+
* reports which rows were replaced (`replacedRowsOf`) and the filter state
|
|
295
|
+
* and columns are the same objects as last time, each replacement is tested
|
|
296
|
+
* on its own: a row that stays in keeps its slot, a row that stays out
|
|
297
|
+
* stays out, and a row whose membership flips sends the whole thing back
|
|
298
|
+
* through the full pass, because an insertion has to land in data order.
|
|
299
|
+
* Positions come from stamps on the rows (ROW_FILTER_GEN / ROW_FILTER_POS)
|
|
300
|
+
* written by the full pass; no map is built.
|
|
301
|
+
*/
|
|
302
|
+
let cache = null;
|
|
303
|
+
let generation = 0;
|
|
279
304
|
return ({ table, rows }) => {
|
|
280
305
|
const filters = table.getState().columnFilters ?? [];
|
|
281
|
-
if (!filters.length)
|
|
306
|
+
if (!filters.length) {
|
|
307
|
+
cache = null;
|
|
282
308
|
return rows;
|
|
309
|
+
}
|
|
310
|
+
const columns = table.getAllColumns();
|
|
283
311
|
// Resolve each filter's match function once, outside the row loop.
|
|
284
312
|
const compiled = filters.map((filter) => ({
|
|
285
313
|
id: filter.id,
|
|
286
314
|
value: filter.value,
|
|
287
315
|
fn: filter.fn ? filterFns[filter.fn] : filterFns.includesString,
|
|
288
316
|
}));
|
|
289
|
-
|
|
317
|
+
const passes = (row) => {
|
|
290
318
|
for (let i = 0; i < compiled.length; i++) {
|
|
291
319
|
const filter = compiled[i];
|
|
292
320
|
// `getCellValueByColumnId` rather than `getAllCells().find(...)`.
|
|
@@ -299,7 +327,56 @@ export function createFilteredRowModel() {
|
|
|
299
327
|
return false;
|
|
300
328
|
}
|
|
301
329
|
return true;
|
|
302
|
-
}
|
|
330
|
+
};
|
|
331
|
+
if (cache && cache.filters === filters && cache.columns === columns) {
|
|
332
|
+
if (cache.input === rows)
|
|
333
|
+
return cache.output;
|
|
334
|
+
const replaced = replacedRowsOf(table);
|
|
335
|
+
if (replaced && rows.length === cache.input.length) {
|
|
336
|
+
let out = null;
|
|
337
|
+
let ok = true;
|
|
338
|
+
for (let i = 0; i < rows.length && ok; i++) {
|
|
339
|
+
const prev = cache.input[i];
|
|
340
|
+
const next = rows[i];
|
|
341
|
+
if (next === prev)
|
|
342
|
+
continue;
|
|
343
|
+
if (replaced.get(prev) !== next) {
|
|
344
|
+
ok = false;
|
|
345
|
+
break;
|
|
346
|
+
}
|
|
347
|
+
const wasIn = prev[ROW_FILTER_GEN] === cache.gen;
|
|
348
|
+
const isIn = passes(next);
|
|
349
|
+
if (wasIn !== isIn) {
|
|
350
|
+
ok = false;
|
|
351
|
+
break;
|
|
352
|
+
}
|
|
353
|
+
if (!isIn)
|
|
354
|
+
continue;
|
|
355
|
+
out ?? (out = cache.output.slice());
|
|
356
|
+
const pos = prev[ROW_FILTER_POS];
|
|
357
|
+
out[pos] = next;
|
|
358
|
+
next[ROW_FILTER_GEN] = cache.gen;
|
|
359
|
+
next[ROW_FILTER_POS] = pos;
|
|
360
|
+
}
|
|
361
|
+
if (ok) {
|
|
362
|
+
const output = out ?? cache.output;
|
|
363
|
+
cache = { ...cache, input: rows, output };
|
|
364
|
+
return output;
|
|
365
|
+
}
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
const gen = ++generation;
|
|
369
|
+
const output = [];
|
|
370
|
+
for (let i = 0; i < rows.length; i++) {
|
|
371
|
+
const row = rows[i];
|
|
372
|
+
if (!passes(row))
|
|
373
|
+
continue;
|
|
374
|
+
row[ROW_FILTER_GEN] = gen;
|
|
375
|
+
row[ROW_FILTER_POS] = output.length;
|
|
376
|
+
output.push(row);
|
|
377
|
+
}
|
|
378
|
+
cache = { input: rows, output, filters, columns, gen };
|
|
379
|
+
return output;
|
|
303
380
|
};
|
|
304
381
|
}
|
|
305
382
|
/**
|
|
@@ -542,6 +619,19 @@ export function flattenTreeData(data, options) {
|
|
|
542
619
|
export function createExpandedRowModel() {
|
|
543
620
|
return ({ table, rows }) => {
|
|
544
621
|
const expanded = table.getState().expanded ?? {};
|
|
622
|
+
// Nothing expanded means every collapsed row hides its children and the
|
|
623
|
+
// output is the input, element for element. Returning it as is keeps the
|
|
624
|
+
// array identity the stages downstream and the render window key on, and
|
|
625
|
+
// skips a 100k-element copy per data change on a flat grid.
|
|
626
|
+
let anyExpanded = false;
|
|
627
|
+
for (const id in expanded) {
|
|
628
|
+
if (expanded[id]) {
|
|
629
|
+
anyExpanded = true;
|
|
630
|
+
break;
|
|
631
|
+
}
|
|
632
|
+
}
|
|
633
|
+
if (!anyExpanded)
|
|
634
|
+
return rows;
|
|
545
635
|
const flattened = [];
|
|
546
636
|
const visit = (row) => {
|
|
547
637
|
flattened.push(row);
|
|
@@ -560,11 +650,58 @@ export function createExpandedRowModel() {
|
|
|
560
650
|
* override the built-in {@link sortFns} - useful for locale-aware or
|
|
561
651
|
* domain-specific ordering.
|
|
562
652
|
*/
|
|
653
|
+
/**
|
|
654
|
+
* Which rows the last `getRowModel` rebuild replaced, old row -> new row, for
|
|
655
|
+
* the duration of the pipeline run that follows it.
|
|
656
|
+
*
|
|
657
|
+
* A live feed replaces a few hundred row objects in a hundred-thousand-row
|
|
658
|
+
* array and hands the grid the new array. The base rows are rebuilt reusing
|
|
659
|
+
* every row whose data object is the same (see `getRowModel`), so the rebuild
|
|
660
|
+
* knows exactly which rows are new. The filtered and sorted stages read that
|
|
661
|
+
* here and repair their previous output instead of recomputing it, when the
|
|
662
|
+
* replacements are few and nothing else changed. Cleared after the run; a
|
|
663
|
+
* stage that reads it outside a run sees null and does the full work.
|
|
664
|
+
*
|
|
665
|
+
* Keyed on the grid object in a WeakMap rather than carried on the public
|
|
666
|
+
* `SvGrid` type, so the stage signature stays `({ table, rows }) => rows`.
|
|
667
|
+
*/
|
|
668
|
+
const replacedRowsByGrid = new WeakMap();
|
|
669
|
+
/** The rows `getRowModel` replaced in the rebuild this pipeline run follows, or null. */
|
|
670
|
+
export function replacedRowsOf(table) {
|
|
671
|
+
return replacedRowsByGrid.get(table) ?? null;
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* The sorting stage of the row pipeline: orders the rows by the grid's
|
|
675
|
+
* `sorting` state with the built-in comparators (or the `localSortFns` map
|
|
676
|
+
* handed in), stable, equal keys keeping their data order. A tick that
|
|
677
|
+
* replaces a few rows out of many is repaired incrementally rather than
|
|
678
|
+
* re-sorted (see the note inside). Registered by `rowSortingFeature`; an app
|
|
679
|
+
* only calls this to install its own comparator set.
|
|
680
|
+
*/
|
|
563
681
|
export function createSortedRowModel(localSortFns = sortFns) {
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
682
|
+
/**
|
|
683
|
+
* What the last full sort produced, kept so a tick can repair it.
|
|
684
|
+
*
|
|
685
|
+
* A live feed replaces a few hundred row objects out of a hundred thousand
|
|
686
|
+
* and hands the grid a new array. Re-sorting all of it costs the same as
|
|
687
|
+
* the first sort did (115-149 ms in the browser for 100k rows, see
|
|
688
|
+
* docs/help/benchmarks.md); the order it would produce differs from the
|
|
689
|
+
* previous one only where the replaced rows land. So the previous output
|
|
690
|
+
* is kept, with its sort keys aligned to it, and when `getRowModel` says
|
|
691
|
+
* which rows were replaced (see `replacedRowsOf`) the stage drops those,
|
|
692
|
+
* sorts the replacements among themselves and merges them back in one
|
|
693
|
+
* linear pass: O(n + k log k) instead of O(n log n) key builds and
|
|
694
|
+
* comparisons.
|
|
695
|
+
*
|
|
696
|
+
* The repair is only attempted when everything else is the same object as
|
|
697
|
+
* last time - the sorting state, the columns, and every input row except
|
|
698
|
+
* the replaced ones - and it produces exactly what the full sort would:
|
|
699
|
+
* equal keys fall back to the data index, which is the order a stable sort
|
|
700
|
+
* over data-ordered input keeps.
|
|
701
|
+
*/
|
|
702
|
+
let cache = null;
|
|
703
|
+
const MISSING_KEY = Symbol('svgrid.sort.missing-key');
|
|
704
|
+
function fullSort(rows, sorting, allColumns) {
|
|
568
705
|
// Resolve every clause ONCE, before sorting.
|
|
569
706
|
//
|
|
570
707
|
// This used to live inside the comparator, so `getAllColumns().find(...)`
|
|
@@ -572,7 +709,6 @@ export function createSortedRowModel(localSortFns = sortFns) {
|
|
|
572
709
|
// 1,528,947 array scans, and a three-clause sort made 3,933,751 (measured;
|
|
573
710
|
// `pnpm bench --case=sort-1col`). The comparator is called O(n log n)
|
|
574
711
|
// times, so anything inside it that is not O(1) sets the cost of the sort.
|
|
575
|
-
const allColumns = table.getAllColumns();
|
|
576
712
|
const clauses = [];
|
|
577
713
|
for (const clause of sorting) {
|
|
578
714
|
const column = allColumns.find((col) => col.id === clause.id);
|
|
@@ -599,15 +735,17 @@ export function createSortedRowModel(localSortFns = sortFns) {
|
|
|
599
735
|
const n = rows.length;
|
|
600
736
|
let keys = new Array(n);
|
|
601
737
|
let compare;
|
|
738
|
+
let keyOf;
|
|
602
739
|
if (comparator === sortFns.number) {
|
|
740
|
+
keyOf = (row) => Number(row.getCellValueByColumnId(columnId) ?? 0);
|
|
603
741
|
for (let i = 0; i < n; i++)
|
|
604
|
-
keys[i] =
|
|
742
|
+
keys[i] = keyOf(rows[i]);
|
|
605
743
|
compare = compareNumericKeys;
|
|
606
744
|
}
|
|
607
745
|
else if (comparator === sortFns.date) {
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
746
|
+
keyOf = (row) => new Date(row.getCellValueByColumnId(columnId)).getTime();
|
|
747
|
+
for (let i = 0; i < n; i++)
|
|
748
|
+
keys[i] = keyOf(rows[i]);
|
|
611
749
|
compare = compareNumericKeys;
|
|
612
750
|
}
|
|
613
751
|
else if (comparator === sortFns.auto) {
|
|
@@ -669,21 +807,26 @@ export function createSortedRowModel(localSortFns = sortFns) {
|
|
|
669
807
|
for (let i = 0; i < n; i++)
|
|
670
808
|
keys[i] = rankOf.get(strings[i]);
|
|
671
809
|
compare = compareNumericKeys;
|
|
810
|
+
// A replacement whose text is not among the ranked values has no
|
|
811
|
+
// rank; the repair path treats that as "sort everything again".
|
|
812
|
+
keyOf = (row) => rankOf.get(String(row.getCellValueByColumnId(columnId))) ?? MISSING_KEY;
|
|
672
813
|
}
|
|
673
814
|
else {
|
|
674
815
|
keys = strings;
|
|
675
816
|
compare = compareCollatedKeys;
|
|
817
|
+
keyOf = (row) => String(row.getCellValueByColumnId(columnId));
|
|
676
818
|
}
|
|
677
819
|
}
|
|
678
820
|
else {
|
|
821
|
+
keyOf = (row) => row.getCellValueByColumnId(columnId);
|
|
679
822
|
for (let i = 0; i < n; i++)
|
|
680
|
-
keys[i] = rows[i]
|
|
823
|
+
keys[i] = keyOf(rows[i]);
|
|
681
824
|
compare = comparator;
|
|
682
825
|
}
|
|
683
|
-
clauses.push({ keys, desc: clause.desc, compare });
|
|
826
|
+
clauses.push({ keys, desc: clause.desc, compare, keyOf });
|
|
684
827
|
}
|
|
685
828
|
if (!clauses.length)
|
|
686
|
-
return
|
|
829
|
+
return null;
|
|
687
830
|
// Sort an index array, then materialise. `Array.prototype.sort` is stable,
|
|
688
831
|
// so equal keys keep their original relative order exactly as the previous
|
|
689
832
|
// `[...rows].sort(...)` did.
|
|
@@ -730,7 +873,167 @@ export function createSortedRowModel(localSortFns = sortFns) {
|
|
|
730
873
|
const sorted = new Array(rows.length);
|
|
731
874
|
for (let i = 0; i < order.length; i++)
|
|
732
875
|
sorted[i] = rows[order[i]];
|
|
733
|
-
|
|
876
|
+
// Each row keeps its key(s), so a later tick can place a replacement
|
|
877
|
+
// among the kept rows without reading any of them again. One property
|
|
878
|
+
// write per row; an array per row only when there are several clauses.
|
|
879
|
+
if (clauses.length === 1) {
|
|
880
|
+
const keys = clauses[0].keys;
|
|
881
|
+
for (let i = 0; i < rows.length; i++)
|
|
882
|
+
rows[i][ROW_SORT_KEY] = keys[i];
|
|
883
|
+
}
|
|
884
|
+
else {
|
|
885
|
+
for (let i = 0; i < rows.length; i++) {
|
|
886
|
+
const per = new Array(clauses.length);
|
|
887
|
+
for (let k = 0; k < clauses.length; k++)
|
|
888
|
+
per[k] = clauses[k].keys[i];
|
|
889
|
+
rows[i][ROW_SORT_KEY] = per;
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
return { sorted, clauses: clauses.map(({ desc, compare, keyOf }) => ({ desc, compare, keyOf })) };
|
|
893
|
+
}
|
|
894
|
+
/**
|
|
895
|
+
* The previous output with the replaced rows swapped for their
|
|
896
|
+
* replacements, in sorted position. Null when the input is not the cached
|
|
897
|
+
* input plus replacements, or a replacement has no incremental key.
|
|
898
|
+
*
|
|
899
|
+
* Every row carries the key(s) the last full sort computed for it
|
|
900
|
+
* (ROW_SORT_KEY: the key itself for one clause, an array for several), so
|
|
901
|
+
* the merge moves row references and nothing else. At 100k rows the loop
|
|
902
|
+
* that also copied an aligned key array cost 3.7 ms; this one is the
|
|
903
|
+
* reference copy alone.
|
|
904
|
+
*/
|
|
905
|
+
function repair(rows, replaced) {
|
|
906
|
+
const c = cache;
|
|
907
|
+
if (rows.length !== c.input.length)
|
|
908
|
+
return null;
|
|
909
|
+
// Which rows of THIS stage's input changed. The map may name rows a
|
|
910
|
+
// stage upstream filtered out; only the ones that reach here matter.
|
|
911
|
+
const dropGen = ++sortDropGeneration;
|
|
912
|
+
const added = [];
|
|
913
|
+
for (let i = 0; i < rows.length; i++) {
|
|
914
|
+
const prev = c.input[i];
|
|
915
|
+
const next = rows[i];
|
|
916
|
+
if (next === prev)
|
|
917
|
+
continue;
|
|
918
|
+
if (replaced.get(prev) !== next)
|
|
919
|
+
return null;
|
|
920
|
+
prev[ROW_SORT_DROPPED] = dropGen;
|
|
921
|
+
added.push(next);
|
|
922
|
+
}
|
|
923
|
+
if (!added.length)
|
|
924
|
+
return c.output;
|
|
925
|
+
const clauses = c.clauses;
|
|
926
|
+
const single = clauses.length === 1;
|
|
927
|
+
for (let j = 0; j < added.length; j++) {
|
|
928
|
+
const row = added[j];
|
|
929
|
+
if (single) {
|
|
930
|
+
const key = clauses[0].keyOf(row);
|
|
931
|
+
if (key === MISSING_KEY)
|
|
932
|
+
return null;
|
|
933
|
+
row[ROW_SORT_KEY] = key;
|
|
934
|
+
}
|
|
935
|
+
else {
|
|
936
|
+
const keys = new Array(clauses.length);
|
|
937
|
+
for (let k = 0; k < clauses.length; k++) {
|
|
938
|
+
const key = clauses[k].keyOf(row);
|
|
939
|
+
if (key === MISSING_KEY)
|
|
940
|
+
return null;
|
|
941
|
+
keys[k] = key;
|
|
942
|
+
}
|
|
943
|
+
row[ROW_SORT_KEY] = keys;
|
|
944
|
+
}
|
|
945
|
+
}
|
|
946
|
+
// Keys first, then the data index: the tie-break a stable sort over
|
|
947
|
+
// data-ordered input applies, so the merge lands where a full sort would.
|
|
948
|
+
const prevOut = c.output;
|
|
949
|
+
const compareRows = single
|
|
950
|
+
? (() => {
|
|
951
|
+
const { compare, desc } = clauses[0];
|
|
952
|
+
return (a, b) => {
|
|
953
|
+
const r = compare(a[ROW_SORT_KEY], b[ROW_SORT_KEY]);
|
|
954
|
+
if (r !== 0)
|
|
955
|
+
return desc ? -r : r;
|
|
956
|
+
return a.index - b.index;
|
|
957
|
+
};
|
|
958
|
+
})()
|
|
959
|
+
: (a, b) => {
|
|
960
|
+
const ka = a[ROW_SORT_KEY];
|
|
961
|
+
const kb = b[ROW_SORT_KEY];
|
|
962
|
+
for (let k = 0; k < clauses.length; k++) {
|
|
963
|
+
const clause = clauses[k];
|
|
964
|
+
const r = clause.compare(ka[k], kb[k]);
|
|
965
|
+
if (r !== 0)
|
|
966
|
+
return clause.desc ? -r : r;
|
|
967
|
+
}
|
|
968
|
+
return a.index - b.index;
|
|
969
|
+
};
|
|
970
|
+
// Sort the replacements among themselves (k log k)...
|
|
971
|
+
added.sort(compareRows);
|
|
972
|
+
// ...find where each one goes in the previous output by binary search
|
|
973
|
+
// (k log n comparisons, against the dropped rows too - they are skipped
|
|
974
|
+
// below, which does not move an insertion point)...
|
|
975
|
+
const n = prevOut.length;
|
|
976
|
+
const insertAt = new Array(added.length);
|
|
977
|
+
let lo = 0;
|
|
978
|
+
for (let j = 0; j < added.length; j++) {
|
|
979
|
+
// Monotone: the replacements are sorted, so each search starts where
|
|
980
|
+
// the previous one landed.
|
|
981
|
+
const row = added[j];
|
|
982
|
+
let hi = n;
|
|
983
|
+
while (lo < hi) {
|
|
984
|
+
const mid = (lo + hi) >>> 1;
|
|
985
|
+
if (compareRows(row, prevOut[mid]) < 0)
|
|
986
|
+
hi = mid;
|
|
987
|
+
else
|
|
988
|
+
lo = mid + 1;
|
|
989
|
+
}
|
|
990
|
+
insertAt[j] = lo;
|
|
991
|
+
}
|
|
992
|
+
// ...then one linear pass that only moves references.
|
|
993
|
+
const out = new Array(rows.length);
|
|
994
|
+
let w = 0;
|
|
995
|
+
let j = 0;
|
|
996
|
+
let nextInsert = insertAt[0] ?? -1;
|
|
997
|
+
for (let i = 0; i < n; i++) {
|
|
998
|
+
while (nextInsert === i) {
|
|
999
|
+
out[w++] = added[j++];
|
|
1000
|
+
nextInsert = j < added.length ? insertAt[j] : -1;
|
|
1001
|
+
}
|
|
1002
|
+
const prev = prevOut[i];
|
|
1003
|
+
if (prev[ROW_SORT_DROPPED] === dropGen)
|
|
1004
|
+
continue;
|
|
1005
|
+
out[w++] = prev;
|
|
1006
|
+
}
|
|
1007
|
+
while (j < added.length)
|
|
1008
|
+
out[w++] = added[j++];
|
|
1009
|
+
return out;
|
|
1010
|
+
}
|
|
1011
|
+
return function sortedRowModelStage({ table, rows }) {
|
|
1012
|
+
const sorting = table.getState().sorting ?? [];
|
|
1013
|
+
if (!sorting.length) {
|
|
1014
|
+
cache = null;
|
|
1015
|
+
return rows;
|
|
1016
|
+
}
|
|
1017
|
+
const columns = table.getAllColumns();
|
|
1018
|
+
if (cache && cache.sorting === sorting && cache.columns === columns) {
|
|
1019
|
+
if (cache.input === rows)
|
|
1020
|
+
return cache.output;
|
|
1021
|
+
const replaced = replacedRowsOf(table);
|
|
1022
|
+
if (replaced) {
|
|
1023
|
+
const repaired = repair(rows, replaced);
|
|
1024
|
+
if (repaired) {
|
|
1025
|
+
cache = { ...cache, input: rows, output: repaired };
|
|
1026
|
+
return repaired;
|
|
1027
|
+
}
|
|
1028
|
+
}
|
|
1029
|
+
}
|
|
1030
|
+
const full = fullSort(rows, sorting, columns);
|
|
1031
|
+
if (!full) {
|
|
1032
|
+
cache = null;
|
|
1033
|
+
return rows;
|
|
1034
|
+
}
|
|
1035
|
+
cache = { input: rows, output: full.sorted, sorting, columns, clauses: full.clauses };
|
|
1036
|
+
return full.sorted;
|
|
734
1037
|
};
|
|
735
1038
|
}
|
|
736
1039
|
/**
|
|
@@ -788,6 +1091,9 @@ export function createSvGridCore(options) {
|
|
|
788
1091
|
let cachedBaseRowsInput = null;
|
|
789
1092
|
let cachedBaseRowsColumns = null;
|
|
790
1093
|
let cachedBaseRows = [];
|
|
1094
|
+
/** Old row -> new row for the rebuild the next pipeline run follows; see `replacedRowsOf`. */
|
|
1095
|
+
let replacedRows = null;
|
|
1096
|
+
let cachedRowCtx = null;
|
|
791
1097
|
let cachedRowModel = null;
|
|
792
1098
|
let cachedRowModelBaseRows = null;
|
|
793
1099
|
let cachedPipeline = options._rowModels;
|
|
@@ -967,29 +1273,63 @@ export function createSvGridCore(options) {
|
|
|
967
1273
|
},
|
|
968
1274
|
getRowModel() {
|
|
969
1275
|
const columns = grid.getAllColumns();
|
|
970
|
-
|
|
971
|
-
|
|
1276
|
+
// Read once. `options.data` is a getter when <SvGrid> drives the engine
|
|
1277
|
+
// (a $state.raw signal), and the rebuild below touched it twice per row:
|
|
1278
|
+
// 200k tracked reads per 100k-row tick, 11 ms of a 65 ms tick.
|
|
1279
|
+
const data = options.data;
|
|
1280
|
+
if (cachedBaseRowsInput !== data || cachedBaseRowsColumns !== columns) {
|
|
1281
|
+
// Same columns as last time: the shared context still describes them,
|
|
1282
|
+
// and a row whose data object sits at the same index can keep its
|
|
1283
|
+
// row object. A row model that streams blocks hands the grid a new
|
|
1284
|
+
// array of 60k entries per block where 100 changed; building 60k
|
|
1285
|
+
// fresh row objects each time was most of the cost of a block
|
|
1286
|
+
// landing. The reused row drops its memoised values and cells, since
|
|
1287
|
+
// an app may have changed the object in place before passing a new
|
|
1288
|
+
// array - that is the case the old rebuild covered by accident.
|
|
1289
|
+
const previous = cachedBaseRowsColumns === columns && cachedRowCtx ? cachedBaseRows : null;
|
|
1290
|
+
// Same length as last time: a replacement, not an add or remove, so
|
|
1291
|
+
// each new row stands in for the old row at its index and the stages
|
|
1292
|
+
// can repair rather than recompute. Anything else is structural.
|
|
1293
|
+
replacedRows = previous && previous.length === data.length ? new Map() : null;
|
|
1294
|
+
cachedBaseRowsInput = data;
|
|
972
1295
|
cachedBaseRowsColumns = columns;
|
|
973
1296
|
// O(1) column-id → index lookup so getCellValueByColumnId doesn't do
|
|
974
1297
|
// a linear `findIndex` on every cell read (was O(rows × cells × cols)).
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
1298
|
+
let rowCtx;
|
|
1299
|
+
if (previous && cachedRowCtx) {
|
|
1300
|
+
rowCtx = cachedRowCtx;
|
|
1301
|
+
}
|
|
1302
|
+
else {
|
|
1303
|
+
const columnIndexById = new Map();
|
|
1304
|
+
for (let i = 0; i < columns.length; i++)
|
|
1305
|
+
columnIndexById.set(columns[i].id, i);
|
|
1306
|
+
// One shared context for every row in this table, so a row carries a
|
|
1307
|
+
// pointer rather than a closure scope. See BASE_ROW_METHODS.
|
|
1308
|
+
rowCtx = {
|
|
1309
|
+
grid: grid,
|
|
1310
|
+
store,
|
|
1311
|
+
columns,
|
|
1312
|
+
columnCount: columns.length,
|
|
1313
|
+
columnIndexById,
|
|
1314
|
+
};
|
|
1315
|
+
cachedRowCtx = rowCtx;
|
|
1316
|
+
}
|
|
1317
|
+
cachedBaseRows = new Array(data.length);
|
|
989
1318
|
const getRowId = options.getRowId;
|
|
990
1319
|
const m = BASE_ROW_METHODS;
|
|
991
|
-
for (let index = 0; index <
|
|
992
|
-
const original =
|
|
1320
|
+
for (let index = 0; index < data.length; index++) {
|
|
1321
|
+
const original = data[index];
|
|
1322
|
+
const kept = previous?.[index];
|
|
1323
|
+
if (kept && kept.original === original) {
|
|
1324
|
+
kept[ROW_VALUES] = null;
|
|
1325
|
+
kept[ROW_CELLS] = null;
|
|
1326
|
+
cachedBaseRows[index] = kept;
|
|
1327
|
+
continue;
|
|
1328
|
+
}
|
|
1329
|
+
// Past a quarter of the rows the merge stops being cheaper than a
|
|
1330
|
+
// sort with the ranking pass; let the stages do the full work.
|
|
1331
|
+
if (replacedRows && replacedRows.size * 4 >= data.length)
|
|
1332
|
+
replacedRows = null;
|
|
993
1333
|
// `_values` and `_cells` stay null until something reads them - a
|
|
994
1334
|
// 100k-row grid showing twenty rows must not materialise every row's
|
|
995
1335
|
// values or cell objects to paint.
|
|
@@ -1001,6 +1341,10 @@ export function createSvGridCore(options) {
|
|
|
1001
1341
|
[ROW_CTX]: rowCtx,
|
|
1002
1342
|
[ROW_VALUES]: null,
|
|
1003
1343
|
[ROW_CELLS]: null,
|
|
1344
|
+
[ROW_FILTER_GEN]: 0,
|
|
1345
|
+
[ROW_FILTER_POS]: -1,
|
|
1346
|
+
[ROW_SORT_DROPPED]: 0,
|
|
1347
|
+
[ROW_SORT_KEY]: undefined,
|
|
1004
1348
|
getCanExpand: m.getCanExpand,
|
|
1005
1349
|
getIsExpanded: m.getIsExpanded,
|
|
1006
1350
|
toggleExpanded: m.toggleExpanded,
|
|
@@ -1010,7 +1354,17 @@ export function createSvGridCore(options) {
|
|
|
1010
1354
|
getCellValueByColumnId: m.getCellValueByColumnId,
|
|
1011
1355
|
};
|
|
1012
1356
|
cachedBaseRows[index] = row;
|
|
1357
|
+
if (replacedRows && kept)
|
|
1358
|
+
replacedRows.set(kept, row);
|
|
1013
1359
|
}
|
|
1360
|
+
// A new array with every object the same is the documented way to
|
|
1361
|
+
// say "I changed rows in place, re-read them" (docs/help/cells/
|
|
1362
|
+
// view-refresh.md). Nothing was replaced, so there is nothing to
|
|
1363
|
+
// repair around: the stages recompute from the current values. A
|
|
1364
|
+
// repair assumes the rows it keeps are unchanged in value as well as
|
|
1365
|
+
// identity, which is the immutable-update contract a tick follows.
|
|
1366
|
+
if (replacedRows && replacedRows.size === 0)
|
|
1367
|
+
replacedRows = null;
|
|
1014
1368
|
}
|
|
1015
1369
|
// Only the slices a pipeline stage actually READS belong in this key.
|
|
1016
1370
|
//
|
|
@@ -1052,10 +1406,19 @@ export function createSvGridCore(options) {
|
|
|
1052
1406
|
pipeline.expandedRowModel,
|
|
1053
1407
|
pipeline.paginatedRowModel,
|
|
1054
1408
|
];
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1409
|
+
// The stages read the replacements through `replacedRowsOf` while the
|
|
1410
|
+
// run lasts; a stage that runs outside one sees null.
|
|
1411
|
+
replacedRowsByGrid.set(grid, replacedRows);
|
|
1412
|
+
try {
|
|
1413
|
+
ordered.forEach((fn) => {
|
|
1414
|
+
if (fn)
|
|
1415
|
+
rows = fn({ table: grid, rows });
|
|
1416
|
+
});
|
|
1417
|
+
}
|
|
1418
|
+
finally {
|
|
1419
|
+
replacedRowsByGrid.set(grid, null);
|
|
1420
|
+
replacedRows = null;
|
|
1421
|
+
}
|
|
1059
1422
|
cachedPipeline = options._rowModels;
|
|
1060
1423
|
cachedSlices = currentSlices;
|
|
1061
1424
|
cachedRowModelBaseRows = cachedBaseRows;
|
package/dist/editing.js
CHANGED
|
@@ -28,6 +28,16 @@ export function createEditing(ctx) {
|
|
|
28
28
|
const editable = column.columnDef.editable;
|
|
29
29
|
if (editable === false)
|
|
30
30
|
return false;
|
|
31
|
+
if (row) {
|
|
32
|
+
// A server-side group row is a key with aggregates under the leaf
|
|
33
|
+
// columns, and a placeholder row has no data yet: neither takes an
|
|
34
|
+
// edit, whatever the column says.
|
|
35
|
+
const serverGroup = ctx.props.serverGroup;
|
|
36
|
+
if (serverGroup && row.original !== undefined && serverGroup.isGroup(row.original))
|
|
37
|
+
return false;
|
|
38
|
+
if (ctx.placeholderStateOf?.(row))
|
|
39
|
+
return false;
|
|
40
|
+
}
|
|
31
41
|
if (typeof editable !== "function")
|
|
32
42
|
return true;
|
|
33
43
|
if (!row)
|
|
@@ -123,6 +133,22 @@ export function createEditing(ctx) {
|
|
|
123
133
|
// belongs to the same Ctrl+Z as the value.
|
|
124
134
|
runHistoryGroup(ctx, () => commitEditingCell());
|
|
125
135
|
}
|
|
136
|
+
/**
|
|
137
|
+
* True when a column with `rejectInvalid` refuses `value`: its `validate`
|
|
138
|
+
* hook returns `false` or a message. Without the flag nothing is refused.
|
|
139
|
+
*/
|
|
140
|
+
function editRejected(column, value, rowData) {
|
|
141
|
+
const def = column?.columnDef;
|
|
142
|
+
if (!def?.rejectInvalid || typeof def.validate !== "function")
|
|
143
|
+
return false;
|
|
144
|
+
const verdict = def.validate({
|
|
145
|
+
value,
|
|
146
|
+
row: rowData,
|
|
147
|
+
rowIndex: ctx.internalData.indexOf(rowData),
|
|
148
|
+
column,
|
|
149
|
+
});
|
|
150
|
+
return verdict === false || (typeof verdict === "string" && verdict.length > 0);
|
|
151
|
+
}
|
|
126
152
|
function commitEditingCell() {
|
|
127
153
|
if (!ctx.editingCell)
|
|
128
154
|
return;
|
|
@@ -155,6 +181,12 @@ export function createEditing(ctx) {
|
|
|
155
181
|
columnId: ctx.editingCell.columnId,
|
|
156
182
|
});
|
|
157
183
|
}
|
|
184
|
+
// A refused edit closes the editor and leaves the row, the overlay,
|
|
185
|
+
// the history and the consumer untouched.
|
|
186
|
+
if (editRejected(column, finalValue, rowData)) {
|
|
187
|
+
ctx.editingCell = null;
|
|
188
|
+
return;
|
|
189
|
+
}
|
|
158
190
|
rowData[column.columnDef.field] = finalValue;
|
|
159
191
|
}
|
|
160
192
|
const key = getCellKey(ctx.editingCell.rowId, ctx.editingCell.columnId);
|
|
@@ -754,6 +786,8 @@ export function createEditing(ctx) {
|
|
|
754
786
|
columnId: column.id,
|
|
755
787
|
})
|
|
756
788
|
: parsed;
|
|
789
|
+
if (editRejected(column, finalValue, row.original))
|
|
790
|
+
continue;
|
|
757
791
|
if (oldValue !== finalValue) {
|
|
758
792
|
row.original[field] = finalValue;
|
|
759
793
|
const key = getCellKey(fr.rowId, column.id);
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* gantt-view registry - the injection seam for the *Gantt view of the grid*.
|
|
3
|
+
* Setting the `gantt` prop on `<SvGrid>` switches the table for a task table
|
|
4
|
+
* beside a time chart (bars by start / end, a work-breakdown tree, dependency
|
|
5
|
+
* arrows), but the renderer itself ships in `@svgrid/enterprise` (a paid
|
|
6
|
+
* feature). This tiny reactive holder lets enterprise register that renderer at
|
|
7
|
+
* import time; the grid looks it up and mounts it, or shows an upsell
|
|
8
|
+
* placeholder when it is absent. Mirrors the `scheduler-view` / `board-view`
|
|
9
|
+
* pattern.
|
|
10
|
+
*
|
|
11
|
+
* ```ts
|
|
12
|
+
* // in @svgrid/enterprise, at module load:
|
|
13
|
+
* import { registerGanttView } from '@svgrid/grid'
|
|
14
|
+
* import SvGridGantt from './gantt/SvGridGantt.svelte'
|
|
15
|
+
* registerGanttView(SvGridGantt)
|
|
16
|
+
* ```
|
|
17
|
+
*/
|
|
18
|
+
import type { Component } from 'svelte';
|
|
19
|
+
/** Register the component that renders `gantt` mode. Enterprise calls this. */
|
|
20
|
+
export declare function registerGanttView(component: Component<any>): void;
|
|
21
|
+
/** The registered Gantt renderer, or null when enterprise is not installed. */
|
|
22
|
+
export declare function getGanttView(): Component<any> | null;
|
|
23
|
+
/** Whether a Gantt renderer has been registered. */
|
|
24
|
+
export declare function hasGanttView(): boolean;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
let renderer = $state(null);
|
|
2
|
+
/** Register the component that renders `gantt` mode. Enterprise calls this. */
|
|
3
|
+
export function registerGanttView(component) {
|
|
4
|
+
renderer = component;
|
|
5
|
+
}
|
|
6
|
+
/** The registered Gantt renderer, or null when enterprise is not installed. */
|
|
7
|
+
export function getGanttView() {
|
|
8
|
+
return renderer;
|
|
9
|
+
}
|
|
10
|
+
/** Whether a Gantt renderer has been registered. */
|
|
11
|
+
export function hasGanttView() {
|
|
12
|
+
return renderer != null;
|
|
13
|
+
}
|