@svgrid/grid 3.0.2 → 3.0.4
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -1
- package/dist/GridMenus.svelte +44 -2
- package/dist/SvChartMenu.svelte +69 -0
- package/dist/SvChartMenu.svelte.d.ts +11 -0
- package/dist/SvChartPanes.svelte +113 -0
- package/dist/SvChartPanes.svelte.d.ts +20 -0
- package/dist/SvGrid.controller.svelte.d.ts +110 -39
- package/dist/SvGrid.controller.svelte.js +655 -200
- package/dist/SvGrid.css +105 -2
- package/dist/SvGrid.svelte +386 -514
- package/dist/SvGrid.types.d.ts +210 -9
- package/dist/SvGridCellEditor.svelte +17 -0
- package/dist/SvGridChart.svelte +2257 -329
- package/dist/SvGridChart.svelte.d.ts +6 -106
- package/dist/SvGridChart.types.d.ts +338 -0
- package/dist/SvGridChart.types.js +1 -0
- package/dist/SvGridChartBuilder.svelte +323 -0
- package/dist/SvGridChartBuilder.svelte.d.ts +34 -0
- package/dist/SvGridChartPanel.svelte +221 -275
- package/dist/SvGridChartPickers.svelte +417 -0
- package/dist/SvGridChartPickers.svelte.d.ts +70 -0
- package/dist/SvGridChartView.svelte +13 -14
- package/dist/SvModal.svelte +7 -1
- package/dist/SvModal.svelte.d.ts +5 -0
- package/dist/ai.d.ts +40 -8
- package/dist/ai.js +135 -24
- package/dist/aria.d.ts +11 -0
- package/dist/build-api.js +108 -22
- package/dist/cdn/GridMenus-7kbpnnBW.js +635 -0
- package/dist/cdn/GridMenus-BL7ZgQvU.js +636 -0
- package/dist/cdn/SvChartMenu-Bl6PBbkT.js +58 -0
- package/dist/cdn/SvChartMenu-FBSMINA6.js +59 -0
- package/dist/cdn/{SvDateRangeInput-DMLKmGEc.js → SvDateRangeInput-BeU_TkbQ.js} +15 -14
- package/dist/cdn/{SvDateRangeInput-CaOuMs8O.js → SvDateRangeInput-yX8vzleW.js} +38 -37
- package/dist/cdn/{SvDateTimePicker-sonaH0oh.js → SvDateTimePicker-DQwt4UAs.js} +139 -180
- package/dist/cdn/SvDateTimePicker-vNU6bZ-q.js +775 -0
- package/dist/cdn/{SvGridCellEditor-CjEJMJKc.js → SvGridCellEditor-B1p-vCK5.js} +188 -178
- package/dist/cdn/{SvGridCellEditor-BWnTi2N7.js → SvGridCellEditor-D_0q4xAu.js} +98 -88
- package/dist/cdn/SvGridChart-C3EWAZaM.js +3574 -0
- package/dist/cdn/SvGridChart-DzLkSwCH.js +3572 -0
- package/dist/cdn/SvGridChartBuilder-BE2T1ykB.js +783 -0
- package/dist/cdn/SvGridChartBuilder-CfII62sZ.js +784 -0
- package/dist/cdn/SvGridChartPanel-CCX5_Wgd.js +675 -0
- package/dist/cdn/SvGridChartPanel-xf4sWVTo.js +699 -0
- package/dist/cdn/SvGridChartView-CfuXmY5I.js +48 -0
- package/dist/cdn/SvGridChartView-DYwabQWj.js +47 -0
- package/dist/cdn/SvMenuList-CLN8OuIK.js +452 -0
- package/dist/cdn/SvMenuList-DkGhNKjf.js +451 -0
- package/dist/cdn/SvModal-BAE-pjZX.js +396 -0
- package/dist/cdn/SvModal-CIZWCcad.js +397 -0
- package/dist/cdn/chart-Dz7SqMXH.js +4341 -0
- package/dist/cdn/chart-export-BXqueeWJ.js +135 -0
- package/dist/cdn/chart-export-pdf-DONFUnNY.js +110 -0
- package/dist/cdn/chart-panel-messages-CUbf2R4i.js +1052 -0
- package/dist/cdn/chart-panel-messages-CmNrMdsr.js +1053 -0
- package/dist/cdn/chart-summary-BJW_tg_X.js +138 -0
- package/dist/cdn/chart-trend-CaN9mDEV.js +50 -0
- package/dist/cdn/chart-validate-HcOVFAfJ.js +188 -0
- package/dist/cdn/{column-resize-DsfNXMom.js → column-resize-DpmOLfRp.js} +3 -3
- package/dist/cdn/{date-format-BnnHlqGw.js → date-format-CtquV-p3.js} +583 -603
- package/dist/cdn/{date-format-BNii4zeD.js → date-format-D6KFzU_W.js} +347 -367
- package/dist/cdn/dismissable-DAHetSNk.js +44 -0
- package/dist/cdn/editor-contract-LXgQJAgd.js +22 -0
- package/dist/cdn/focus-trap-BBOQYBma.js +65 -0
- package/dist/cdn/{row-resize-BRcimkUT.js → row-resize-niQCp040.js} +44 -36
- package/dist/cdn/{src-DNbz94Ct.js → src-BKhZ6eXd.js} +10227 -9787
- package/dist/cdn/{src-BJ2opd0j.js → src-V1uu8iE9.js} +6568 -6128
- package/dist/cdn/svgrid.js +26 -15
- package/dist/cdn/svgrid.svelte-external.js +26 -15
- package/dist/chart-axes.d.ts +53 -0
- package/dist/chart-axes.js +351 -0
- package/dist/chart-cartesian.d.ts +88 -0
- package/dist/chart-cartesian.js +1862 -0
- package/dist/chart-decimate.d.ts +51 -0
- package/dist/chart-decimate.js +199 -0
- package/dist/chart-export-pdf.d.ts +47 -0
- package/dist/chart-export-pdf.js +187 -0
- package/dist/chart-export.d.ts +2 -1
- package/dist/chart-export.js +18 -3
- package/dist/chart-financial.d.ts +91 -0
- package/dist/chart-financial.js +175 -0
- package/dist/chart-flow.d.ts +10 -0
- package/dist/chart-flow.js +298 -0
- package/dist/chart-format.d.ts +18 -0
- package/dist/chart-format.js +65 -0
- package/dist/chart-grid.d.ts +8 -0
- package/dist/chart-grid.js +190 -0
- package/dist/chart-hierarchy.d.ts +17 -0
- package/dist/chart-hierarchy.js +146 -0
- package/dist/chart-indicators.d.ts +115 -0
- package/dist/chart-indicators.js +389 -0
- package/dist/chart-messages.d.ts +95 -0
- package/dist/chart-messages.js +92 -0
- package/dist/chart-motion.d.ts +16 -0
- package/dist/chart-motion.js +141 -0
- package/dist/chart-panel-messages.d.ts +265 -0
- package/dist/chart-panel-messages.js +285 -0
- package/dist/chart-pivot.d.ts +87 -0
- package/dist/chart-pivot.js +156 -0
- package/dist/chart-polar.d.ts +25 -0
- package/dist/chart-polar.js +700 -0
- package/dist/chart-samples.d.ts +16 -0
- package/dist/chart-samples.js +153 -0
- package/dist/chart-scale.d.ts +113 -0
- package/dist/chart-scale.js +405 -0
- package/dist/chart-stats.d.ts +111 -0
- package/dist/chart-stats.js +419 -0
- package/dist/chart-stream.d.ts +45 -0
- package/dist/chart-stream.js +91 -0
- package/dist/chart-summary.d.ts +27 -0
- package/dist/chart-summary.js +205 -0
- package/dist/chart-sync.svelte.d.ts +32 -0
- package/dist/chart-sync.svelte.js +23 -0
- package/dist/chart-table.d.ts +35 -0
- package/dist/chart-table.js +165 -0
- package/dist/chart-trend.d.ts +19 -0
- package/dist/chart-trend.js +61 -0
- package/dist/chart-types.d.ts +1456 -0
- package/dist/chart-types.js +6 -0
- package/dist/chart-validate.d.ts +43 -0
- package/dist/chart-validate.js +272 -0
- package/dist/chart-zoom.d.ts +40 -0
- package/dist/chart-zoom.js +145 -0
- package/dist/chart.d.ts +186 -865
- package/dist/chart.js +460 -2204
- package/dist/clipboard.d.ts +1 -0
- package/dist/clipboard.js +137 -18
- package/dist/column-resize.d.ts +3 -0
- package/dist/column-resize.js +4 -1
- package/dist/columns.js +3 -0
- package/dist/command-context.d.ts +19 -0
- package/dist/command-context.js +116 -0
- package/dist/core.d.ts +7 -0
- package/dist/createPopoverSelect.svelte.d.ts +2 -2
- package/dist/editing.d.ts +2 -8
- package/dist/editing.js +262 -33
- package/dist/fill-patterns.d.ts +0 -5
- package/dist/fill-patterns.js +56 -2
- package/dist/grid-messages.d.ts +10 -3
- package/dist/grid-messages.js +5 -0
- package/dist/history.d.ts +83 -0
- package/dist/history.js +133 -0
- package/dist/index.d.ts +24 -6
- package/dist/index.js +20 -2
- package/dist/keyboard-handlers.js +88 -31
- package/dist/keyboard.d.ts +55 -0
- package/dist/keyboard.js +132 -0
- package/dist/menus.d.ts +3 -1
- package/dist/menus.js +7 -2
- package/dist/merges.d.ts +97 -0
- package/dist/merges.js +147 -0
- package/dist/row-resize.d.ts +3 -0
- package/dist/row-resize.js +14 -0
- package/dist/scroll-sync.d.ts +1 -0
- package/dist/scroll-sync.js +24 -0
- package/dist/selection.d.ts +16 -0
- package/dist/selection.js +108 -16
- package/dist/shortcut-registry.d.ts +145 -0
- package/dist/shortcut-registry.js +44 -0
- package/dist/svgrid-wrapper.types.d.ts +213 -22
- package/dist/virtualization/virtualizer.js +38 -17
- package/package.json +6 -1
- package/src/GridMenus.svelte +44 -2
- package/src/SvChartMenu.svelte +69 -0
- package/src/SvChartPanes.svelte +113 -0
- package/src/SvChartPanes.test.ts +66 -0
- package/src/SvGrid.controller.svelte.ts +629 -172
- package/src/SvGrid.css +105 -2
- package/src/SvGrid.svelte +386 -514
- package/src/SvGrid.types.ts +188 -8
- package/src/SvGridCellEditor.svelte +17 -0
- package/src/SvGridChart.svelte +2257 -329
- package/src/SvGridChart.test.ts +1419 -1
- package/src/SvGridChart.types.ts +336 -0
- package/src/SvGridChartBuilder.svelte +323 -0
- package/src/SvGridChartPanel.svelte +221 -275
- package/src/SvGridChartPickers.svelte +417 -0
- package/src/SvGridChartView.svelte +13 -14
- package/src/SvModal.svelte +7 -1
- package/src/SvModal.test.ts +9 -0
- package/src/ai.test.ts +57 -2
- package/src/ai.ts +166 -22
- package/src/aria.d.ts +11 -0
- package/src/build-api.coverage.test.ts +184 -0
- package/src/build-api.ts +107 -18
- package/src/chart-axes.test.ts +573 -0
- package/src/chart-axes.ts +342 -0
- package/src/chart-cartesian.ts +1863 -0
- package/src/chart-decimate.test.ts +249 -0
- package/src/chart-decimate.ts +186 -0
- package/src/chart-export-pdf.test.ts +91 -0
- package/src/chart-export-pdf.ts +207 -0
- package/src/chart-export.test.ts +14 -0
- package/src/chart-export.ts +13 -3
- package/src/chart-financial.ts +208 -0
- package/src/chart-flow.ts +286 -0
- package/src/chart-format.ts +67 -0
- package/src/chart-grid.ts +179 -0
- package/src/chart-hierarchy.ts +135 -0
- package/src/chart-indicators.test.ts +443 -0
- package/src/chart-indicators.ts +384 -0
- package/src/chart-messages.test.ts +21 -0
- package/src/chart-messages.ts +184 -0
- package/src/chart-motion.test.ts +71 -0
- package/src/chart-motion.ts +148 -0
- package/src/chart-panel-messages.test.ts +29 -0
- package/src/chart-panel-messages.ts +550 -0
- package/src/chart-pivot.test.ts +54 -0
- package/src/chart-pivot.ts +225 -0
- package/src/chart-polar.ts +674 -0
- package/src/chart-samples.ts +106 -0
- package/src/chart-scale.ts +394 -0
- package/src/chart-series-types.test.ts +714 -0
- package/src/chart-stats.ts +409 -0
- package/src/chart-stream.test.ts +75 -0
- package/src/chart-stream.ts +114 -0
- package/src/chart-summary.test.ts +90 -0
- package/src/chart-summary.ts +197 -0
- package/src/chart-sync.svelte.ts +41 -0
- package/src/chart-table.test.ts +76 -0
- package/src/chart-table.ts +178 -0
- package/src/chart-trend.ts +47 -0
- package/src/chart-types.ts +1309 -0
- package/src/chart-validate.test.ts +100 -0
- package/src/chart-validate.ts +247 -0
- package/src/chart-zoom.test.ts +137 -0
- package/src/chart-zoom.ts +124 -0
- package/src/chart.coverage.test.ts +81 -0
- package/src/chart.test.ts +56 -3
- package/src/chart.ts +557 -2784
- package/src/clipboard.test.ts +151 -0
- package/src/clipboard.ts +145 -16
- package/src/column-resize.ts +7 -1
- package/src/columns.ts +2 -0
- package/src/command-context.test.ts +200 -0
- package/src/command-context.ts +137 -0
- package/src/core.ts +7 -0
- package/src/editing.test.ts +281 -3
- package/src/editing.ts +256 -38
- package/src/fill-patterns.test.ts +35 -0
- package/src/fill-patterns.ts +56 -2
- package/src/grid-messages.ts +16 -3
- package/src/history.test.ts +196 -0
- package/src/history.ts +162 -0
- package/src/icon-seam.test.ts +3 -2
- package/src/index.ts +150 -2
- package/src/keyboard-handlers.coverage.test.ts +126 -6
- package/src/keyboard-handlers.ts +91 -30
- package/src/keyboard-shortcuts.seam.test.ts +200 -0
- package/src/keyboard.test.ts +124 -1
- package/src/keyboard.ts +142 -0
- package/src/menus.test.ts +26 -0
- package/src/menus.ts +10 -3
- package/src/merges.test.ts +112 -0
- package/src/merges.ts +168 -0
- package/src/pivot.grid.test.ts +40 -5
- package/src/row-resize.test.ts +44 -0
- package/src/row-resize.ts +17 -0
- package/src/scroll-sync.test.ts +42 -1
- package/src/scroll-sync.ts +25 -0
- package/src/selection.multi-range.test.ts +34 -0
- package/src/selection.test.ts +30 -2
- package/src/selection.ts +111 -19
- package/src/shortcut-registry.test.ts +97 -0
- package/src/shortcut-registry.ts +181 -0
- package/src/svgrid-wrapper.types.ts +184 -13
- package/src/svgrid.behavior.test.ts +135 -0
- package/src/svgrid.charting.test.ts +513 -3
- package/src/svgrid.context-menu.test.ts +31 -0
- package/src/svgrid.new-features.wrapper.test.ts +10 -3
- package/src/virtualization/virtualizer.ts +39 -17
- package/dist/cdn/GridMenus-DyFA-QxA.js +0 -602
- package/dist/cdn/GridMenus-M2XP9t0_.js +0 -603
- package/dist/cdn/SvDateTimePicker-CCbDZNZB.js +0 -816
- package/dist/cdn/SvGridChart-DAb9RbRs.js +0 -1480
- package/dist/cdn/SvGridChart-Yl-IXZFO.js +0 -1481
- package/dist/cdn/SvGridChartPanel-DayQpDsR.js +0 -834
- package/dist/cdn/SvGridChartPanel-aqf8n-vJ.js +0 -810
- package/dist/cdn/SvGridChartView-CxfakxR0.js +0 -56
- package/dist/cdn/SvGridChartView-Dm_YEx9L.js +0 -55
- package/dist/cdn/chart-CKJRCPOk.js +0 -1652
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* shortcut-registry - the injection seam for keyboard commands the free grid
|
|
3
|
+
* does not implement. `@svgrid/grid` owns cell navigation, copy/paste, undo and
|
|
4
|
+
* find; the Excel command set (Ctrl+Arrow to the edge of a data region, Ctrl+D
|
|
5
|
+
* fill down, Ctrl+Shift+V paste special, Alt+= autosum) is a paid feature in
|
|
6
|
+
* `@svgrid/enterprise`. This holder lets enterprise register a handler at
|
|
7
|
+
* import time; the grid runs the chain before it interprets a key itself.
|
|
8
|
+
*
|
|
9
|
+
* Same shape as `selection-bar-view`, `scheduler-view` and `board-view`, with
|
|
10
|
+
* one difference: those hold a single renderer, this holds a prioritised chain,
|
|
11
|
+
* because several packages may want a key and the first claim has to win
|
|
12
|
+
* deterministically.
|
|
13
|
+
*
|
|
14
|
+
* The chain runs BEFORE `getKeyboardIntent`, which is the whole point. The
|
|
15
|
+
* intent union in `keyboard.ts` is public API and closed, so there is no member
|
|
16
|
+
* that could mean "jump to the edge of the data region" and no way to add one
|
|
17
|
+
* without breaking a consumer that switches on it exhaustively. Running first
|
|
18
|
+
* sidesteps that: a registered handler claims `Ctrl+ArrowDown` and returns
|
|
19
|
+
* true, and the grid never computes an intent for that event at all.
|
|
20
|
+
*
|
|
21
|
+
* ```ts
|
|
22
|
+
* // in @svgrid/enterprise, at module load:
|
|
23
|
+
* import { registerGridShortcuts } from '@svgrid/grid'
|
|
24
|
+
* registerGridShortcuts(handleSheetKey, { id: 'sheet', priority: 100 })
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
import type { SvGridApi } from './index.js';
|
|
28
|
+
/** What a handler gets to read and write. A getter object over controller
|
|
29
|
+
* state, built only when at least one handler is registered - a free grid
|
|
30
|
+
* never pays to construct it. Indices are DISPLAY indices, matching
|
|
31
|
+
* `SelectionPoint`, so they follow sort, filter and pagination. */
|
|
32
|
+
export type GridCommandContext = {
|
|
33
|
+
/** The grid's public api. Identity is stable for the life of the grid, so
|
|
34
|
+
* a handler can key per-grid state on it in a WeakMap. */
|
|
35
|
+
readonly api: SvGridApi<any, any>;
|
|
36
|
+
/** True when the event came from the cell editor rather than the grid root.
|
|
37
|
+
* Handlers that only make sense mid-edit (Alt+Enter) check this. */
|
|
38
|
+
readonly editing: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* The open inline editor's element, or null. A binding registered for
|
|
41
|
+
* `editing: true` reads and rewrites the text through it: F4 in a
|
|
42
|
+
* spreadsheet pins the reference under the caret.
|
|
43
|
+
*/
|
|
44
|
+
readonly editor: HTMLInputElement | HTMLTextAreaElement | null;
|
|
45
|
+
readonly activeCell: {
|
|
46
|
+
rowIndex: number;
|
|
47
|
+
colIndex: number;
|
|
48
|
+
columnId: string | null;
|
|
49
|
+
} | null;
|
|
50
|
+
/**
|
|
51
|
+
* The corner the active range grows from: the far end of a Shift+Arrow,
|
|
52
|
+
* Shift+click or drag, which need not be the active cell. Null while
|
|
53
|
+
* nothing is anchored. A command that extends the selection steps from
|
|
54
|
+
* here, not from the active cell, or Ctrl+Shift+Down twice would land
|
|
55
|
+
* on the same edge twice.
|
|
56
|
+
*/
|
|
57
|
+
readonly selectionFocus: {
|
|
58
|
+
rowIndex: number;
|
|
59
|
+
colIndex: number;
|
|
60
|
+
} | null;
|
|
61
|
+
readonly rowCount: number;
|
|
62
|
+
readonly colCount: number;
|
|
63
|
+
/** Every selected rectangle as [minRow, minCol, maxRow, maxCol], active last. */
|
|
64
|
+
readonly ranges: ReadonlyArray<readonly [number, number, number, number]>;
|
|
65
|
+
columnIdAt(colIndex: number): string | null;
|
|
66
|
+
getCellValue(rowIndex: number, colIndex: number): unknown;
|
|
67
|
+
/** Write a cell and record it in the grid's undo history. Inside `batch`
|
|
68
|
+
* every write joins one step, so a fill or a replace-all is one Ctrl+Z. */
|
|
69
|
+
setCellValue(rowIndex: number, colIndex: number, value: unknown): void;
|
|
70
|
+
setActiveCell(rowIndex: number, colIndex: number): void;
|
|
71
|
+
setSelection(rowIndex: number, colIndex: number): void;
|
|
72
|
+
extendSelection(rowIndex: number, colIndex: number): void;
|
|
73
|
+
scrollIntoView(rowIndex: number, colIndex: number): void;
|
|
74
|
+
startEditing(rowIndex: number, colIndex: number, seed?: string): boolean;
|
|
75
|
+
/** Run every write inside `fn` as ONE undo step. Returns what `fn` returns. */
|
|
76
|
+
batch<T>(fn: () => T): T;
|
|
77
|
+
/**
|
|
78
|
+
* Put something that is not a cell write into the grid's undo history:
|
|
79
|
+
* a format kept in the caller's own store, a structural change the grid
|
|
80
|
+
* cannot see. `undo` and `redo` are what Ctrl+Z and Ctrl+Y will call.
|
|
81
|
+
* Inside `batch` the step joins the batch, so a command that writes cells
|
|
82
|
+
* AND formats is still one press.
|
|
83
|
+
*/
|
|
84
|
+
recordUndo(undo: () => void, redo: () => void): void;
|
|
85
|
+
/**
|
|
86
|
+
* Put keyboard focus back on the grid, without scrolling.
|
|
87
|
+
*
|
|
88
|
+
* A toolbar button takes focus when it is clicked and a dialog takes it
|
|
89
|
+
* when it opens, so the keystroke after either - Ctrl+Z to undo what the
|
|
90
|
+
* button did, typing into the cell the dialog changed - lands on the button
|
|
91
|
+
* or on `<body>` rather than on the grid. Excel returns focus to the sheet
|
|
92
|
+
* after every ribbon command and every dialog; a command that owns chrome
|
|
93
|
+
* calls this once it is done.
|
|
94
|
+
*/
|
|
95
|
+
focus(): void;
|
|
96
|
+
/**
|
|
97
|
+
* Paste the system clipboard at the active cell, the way Ctrl+V does:
|
|
98
|
+
* tab-separated text becomes a block of cells, one undo step. Resolves
|
|
99
|
+
* once the paste has been applied, or without doing anything when the
|
|
100
|
+
* clipboard cannot be read (an insecure context, or permission refused),
|
|
101
|
+
* which is the same silence Ctrl+V keeps there.
|
|
102
|
+
*/
|
|
103
|
+
paste(): Promise<void>;
|
|
104
|
+
/**
|
|
105
|
+
* Copy the selection the way Ctrl+C does: every selected rectangle as
|
|
106
|
+
* tab-separated text, each cell through `processCellForClipboard`. The
|
|
107
|
+
* api's `copyToClipboard` is the export: the displayed rows with their
|
|
108
|
+
* headers, whatever the selection.
|
|
109
|
+
*/
|
|
110
|
+
copy(): void;
|
|
111
|
+
/**
|
|
112
|
+
* Cut the selection the way Ctrl+X does: the copy above, then the cells
|
|
113
|
+
* cleared as one undo step.
|
|
114
|
+
*/
|
|
115
|
+
cut(): Promise<void>;
|
|
116
|
+
/**
|
|
117
|
+
* Whether a cell may be written: the column's `editable` predicate as
|
|
118
|
+
* the grid's own editor, fill handle and paste read it. `setCellValue`
|
|
119
|
+
* does not consult it (a command is trusted to know what it writes), so
|
|
120
|
+
* a command that fills or stamps a range asks here and skips the cells
|
|
121
|
+
* that say no. Optional for a context built by hand.
|
|
122
|
+
*/
|
|
123
|
+
canEdit?(rowIndex: number, colIndex: number): boolean;
|
|
124
|
+
};
|
|
125
|
+
/** Return true to consume the event. The grid stops there and does not
|
|
126
|
+
* interpret the key itself. Return false to let it fall through. */
|
|
127
|
+
export type GridShortcutHandler = (event: KeyboardEvent, cmd: GridCommandContext) => boolean;
|
|
128
|
+
/**
|
|
129
|
+
* Register a keyboard handler. Higher `priority` runs first; ties keep
|
|
130
|
+
* registration order. Passing an `id` that is already registered replaces that
|
|
131
|
+
* entry rather than stacking a second copy, so an enable* function stays
|
|
132
|
+
* idempotent. Returns an unregister function.
|
|
133
|
+
*/
|
|
134
|
+
export declare function registerGridShortcuts(handler: GridShortcutHandler, opts?: {
|
|
135
|
+
id?: string;
|
|
136
|
+
priority?: number;
|
|
137
|
+
}): () => void;
|
|
138
|
+
/** Run the chain. Returns true when a handler consumed the event. */
|
|
139
|
+
export declare function runGridShortcuts(event: KeyboardEvent, cmd: GridCommandContext): boolean;
|
|
140
|
+
/** Whether anything is registered. The grid checks this before building a
|
|
141
|
+
* command context, so an unregistered grid pays one array-length read. */
|
|
142
|
+
export declare function hasGridShortcuts(): boolean;
|
|
143
|
+
/** Drop every handler. Tests use this; production code should keep the
|
|
144
|
+
* unregister function `registerGridShortcuts` returns. */
|
|
145
|
+
export declare function clearGridShortcuts(): void;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
// Module-level, not $state: the chain is written at import time by whoever
|
|
2
|
+
// installs a feature pack and read inside an event handler. Nothing renders
|
|
3
|
+
// from it, so reactivity would cost a proxy for no benefit.
|
|
4
|
+
let entries = [];
|
|
5
|
+
/**
|
|
6
|
+
* Register a keyboard handler. Higher `priority` runs first; ties keep
|
|
7
|
+
* registration order. Passing an `id` that is already registered replaces that
|
|
8
|
+
* entry rather than stacking a second copy, so an enable* function stays
|
|
9
|
+
* idempotent. Returns an unregister function.
|
|
10
|
+
*/
|
|
11
|
+
export function registerGridShortcuts(handler, opts) {
|
|
12
|
+
const entry = {
|
|
13
|
+
handler,
|
|
14
|
+
id: opts?.id,
|
|
15
|
+
priority: opts?.priority ?? 0,
|
|
16
|
+
};
|
|
17
|
+
const next = entry.id ? entries.filter((e) => e.id !== entry.id) : entries.slice();
|
|
18
|
+
next.push(entry);
|
|
19
|
+
// Stable sort by descending priority: Array.prototype.sort is stable per
|
|
20
|
+
// spec, so equal priorities keep the order they were pushed in.
|
|
21
|
+
next.sort((a, b) => b.priority - a.priority);
|
|
22
|
+
entries = next;
|
|
23
|
+
return () => {
|
|
24
|
+
entries = entries.filter((e) => e !== entry);
|
|
25
|
+
};
|
|
26
|
+
}
|
|
27
|
+
/** Run the chain. Returns true when a handler consumed the event. */
|
|
28
|
+
export function runGridShortcuts(event, cmd) {
|
|
29
|
+
for (const entry of entries) {
|
|
30
|
+
if (entry.handler(event, cmd))
|
|
31
|
+
return true;
|
|
32
|
+
}
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
/** Whether anything is registered. The grid checks this before building a
|
|
36
|
+
* command context, so an unregistered grid pays one array-length read. */
|
|
37
|
+
export function hasGridShortcuts() {
|
|
38
|
+
return entries.length > 0;
|
|
39
|
+
}
|
|
40
|
+
/** Drop every handler. Tests use this; production code should keep the
|
|
41
|
+
* unregister function `registerGridShortcuts` returns. */
|
|
42
|
+
export function clearGridShortcuts() {
|
|
43
|
+
entries = [];
|
|
44
|
+
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { CellFormatConfig, ColumnDef, RowData, SvGridOptions, TableFeatures } from './core.js';
|
|
2
2
|
import type { FilterOperator, Props } from './SvGrid.types.js';
|
|
3
3
|
import type { GridExportOptions, GridClipboardOptions } from './export-format.js';
|
|
4
|
-
import type { ChartSpec, ChartType } from './chart.js';
|
|
4
|
+
import type { ChartFormatState, ChartReducer, ChartSpec, ChartTimeBucket, ChartType, ChartZoomWindow } from './chart.js';
|
|
5
5
|
import type { GridPredicateExpr } from './filtering/predicate-expr.js';
|
|
6
|
+
import type { GridCommandContext } from './shortcut-registry.js';
|
|
6
7
|
/** The comparisons a column filter can use, as shown in the filter menu. */
|
|
7
8
|
export type SvGridFilterOperator = FilterOperator;
|
|
8
9
|
/**
|
|
@@ -56,21 +57,78 @@ export type SvGridViewState = {
|
|
|
56
57
|
reduce: 'sum' | 'avg' | 'count';
|
|
57
58
|
stacked: boolean;
|
|
58
59
|
};
|
|
59
|
-
/** All charts in the tab strip (multi-chart), plus the active index.
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
dimension: string | null;
|
|
64
|
-
series: string | null;
|
|
65
|
-
measure: string | null;
|
|
66
|
-
reduce: 'sum' | 'avg' | 'count';
|
|
67
|
-
stacked: boolean | null;
|
|
68
|
-
dataLabels: boolean | null;
|
|
69
|
-
logScale: boolean | null;
|
|
70
|
-
timeAxis: boolean | null;
|
|
71
|
-
valueFormat: 'number' | 'currency' | 'percent' | 'compact' | null;
|
|
72
|
-
}>;
|
|
60
|
+
/** All charts in the tab strip (multi-chart), plus the active index.
|
|
61
|
+
* Every field the panel can change is here so a saved view restores the
|
|
62
|
+
* chart exactly; `null` inherits the `charting` config. */
|
|
63
|
+
charts?: ChartTabSnapshot[];
|
|
73
64
|
chartActive?: number;
|
|
65
|
+
/** The named charts kept with `api.saveChart`; present only when there is one. */
|
|
66
|
+
savedCharts?: SavedChart[];
|
|
67
|
+
};
|
|
68
|
+
/** One chart tab as the saved view carries it (`GridState.charts[i]`). */
|
|
69
|
+
export type ChartTabSnapshot = {
|
|
70
|
+
title: string;
|
|
71
|
+
type: ChartType;
|
|
72
|
+
dimension: string | null;
|
|
73
|
+
series: string | null;
|
|
74
|
+
measure: string | null;
|
|
75
|
+
/** Scatter's y measure, a range's high, a bullet's target. */
|
|
76
|
+
measure2?: string | null;
|
|
77
|
+
reduce: ChartReducer;
|
|
78
|
+
stacked: boolean | null;
|
|
79
|
+
stacked100?: boolean | null;
|
|
80
|
+
orientation?: 'vertical' | 'horizontal' | null;
|
|
81
|
+
donut?: boolean | null;
|
|
82
|
+
palette?: string[] | null;
|
|
83
|
+
dataLabels: boolean | null;
|
|
84
|
+
logScale: boolean | null;
|
|
85
|
+
timeAxis: boolean | null;
|
|
86
|
+
valueFormat: 'number' | 'currency' | 'percent' | 'compact' | null;
|
|
87
|
+
bucket?: ChartTimeBucket | null;
|
|
88
|
+
bins?: number | null;
|
|
89
|
+
funnelShape?: 'trapezoid' | 'pyramid' | 'cone' | null;
|
|
90
|
+
candleStyle?: 'classic' | 'hollow' | 'heikin-ashi' | null;
|
|
91
|
+
/** The zoom window as category indices, or null for the whole chart. */
|
|
92
|
+
zoom?: ChartZoomWindow | null;
|
|
93
|
+
ohlc?: {
|
|
94
|
+
open: string | null;
|
|
95
|
+
high: string | null;
|
|
96
|
+
low: string | null;
|
|
97
|
+
close: string | null;
|
|
98
|
+
volume: string | null;
|
|
99
|
+
} | null;
|
|
100
|
+
indicators?: string[];
|
|
101
|
+
format?: ChartFormatState | null;
|
|
102
|
+
/** An unlinked chart carries the spec it was frozen with. */
|
|
103
|
+
frozen?: {
|
|
104
|
+
spec: ChartSpec;
|
|
105
|
+
at: string;
|
|
106
|
+
} | null;
|
|
107
|
+
};
|
|
108
|
+
/** A chart configuration kept under a name (`api.saveChart`). */
|
|
109
|
+
export type SavedChart = {
|
|
110
|
+
name: string;
|
|
111
|
+
/** ISO timestamp of the save. */
|
|
112
|
+
savedAt: string;
|
|
113
|
+
/** The tab's state at the time; `applySavedChart` puts it on the active tab. */
|
|
114
|
+
tab: ChartTabSnapshot;
|
|
115
|
+
};
|
|
116
|
+
/** One cell, in the 0-based display coordinates every selection method uses. */
|
|
117
|
+
export type SvGridCellCoords = {
|
|
118
|
+
row: number;
|
|
119
|
+
col: number;
|
|
120
|
+
};
|
|
121
|
+
/**
|
|
122
|
+
* A selected rectangle with its orientation kept: `from` is where the
|
|
123
|
+
* selection was started (the anchor) and `to` where it ended (the focus),
|
|
124
|
+
* so a range dragged upwards has `from.row > to.row`. `highlight` is the
|
|
125
|
+
* active cell inside it. The same three fields as Handsontable's CellRange,
|
|
126
|
+
* so code written against `getSelectedRange()` there reads the same here.
|
|
127
|
+
*/
|
|
128
|
+
export type SvGridSelectedRange = {
|
|
129
|
+
from: SvGridCellCoords;
|
|
130
|
+
to: SvGridCellCoords;
|
|
131
|
+
highlight: SvGridCellCoords;
|
|
74
132
|
};
|
|
75
133
|
/**
|
|
76
134
|
* A batch of row mutations for `api.applyTransaction`. `update` / `remove`
|
|
@@ -112,17 +170,44 @@ export type SvGridApi<TFeatures extends TableFeatures, TData extends RowData> =
|
|
|
112
170
|
* range is `[rowStart, colStart, rowEnd, colEnd]` in 0-indexed grid
|
|
113
171
|
* coordinates. Pass an empty array to clear the selection.
|
|
114
172
|
*
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
*
|
|
118
|
-
* corner.
|
|
173
|
+
* Every range is honoured: the last one becomes the active range and
|
|
174
|
+
* the others stay highlighted beside it, as after a Ctrl+drag. The
|
|
175
|
+
* active cell goes to the last range's start corner.
|
|
119
176
|
*/
|
|
120
177
|
selectCells(ranges: ReadonlyArray<readonly [number, number, number, number]>): void;
|
|
121
178
|
/**
|
|
122
|
-
*
|
|
123
|
-
*
|
|
179
|
+
* The current cell-selection rectangles in the same shape `selectCells`
|
|
180
|
+
* accepts, each normalised to `[minRow, minCol, maxRow, maxCol]`, in the
|
|
181
|
+
* order they were selected with the active one last. Empty array when no
|
|
182
|
+
* range is active.
|
|
183
|
+
*
|
|
184
|
+
* Handsontable's method of the same name. Two differences, both on
|
|
185
|
+
* purpose: an empty array rather than `undefined`, so `getSelected()[0]`
|
|
186
|
+
* never throws; and normalised corners, so a loop from the first row to
|
|
187
|
+
* the last cannot run zero times because the range was dragged upwards.
|
|
188
|
+
* For the orientation, and the active cell inside a range, use
|
|
189
|
+
* `getSelectedRange()`.
|
|
190
|
+
*
|
|
191
|
+
* With cell selection off (`selectable={false}`) no rectangle is ever
|
|
192
|
+
* recorded and this stays `[]`; the focused cell is `getActiveCell()`.
|
|
124
193
|
*/
|
|
125
194
|
getSelected(): Array<[number, number, number, number]>;
|
|
195
|
+
/**
|
|
196
|
+
* The most recently selected rectangle as `[startRow, startCol, endRow,
|
|
197
|
+
* endCol]`, start being where the selection began, or `undefined` when
|
|
198
|
+
* nothing is selected. Handsontable's `getSelectedLast()`, orientation
|
|
199
|
+
* included: a range dragged upwards has `startRow > endRow`.
|
|
200
|
+
*/
|
|
201
|
+
getSelectedLast(): [number, number, number, number] | undefined;
|
|
202
|
+
/**
|
|
203
|
+
* Every selected rectangle with its orientation and active cell, oldest
|
|
204
|
+
* first, or `undefined` when nothing is selected. Handsontable's
|
|
205
|
+
* `getSelectedRange()`.
|
|
206
|
+
*/
|
|
207
|
+
getSelectedRange(): SvGridSelectedRange[] | undefined;
|
|
208
|
+
/** The most recently selected rectangle, or `undefined`. Handsontable's
|
|
209
|
+
* `getSelectedRangeLast()`. */
|
|
210
|
+
getSelectedRangeLast(): SvGridSelectedRange | undefined;
|
|
126
211
|
/** Open the built-in chart panel. */
|
|
127
212
|
openChart(): void;
|
|
128
213
|
/** Close the built-in chart panel. */
|
|
@@ -141,19 +226,69 @@ export type SvGridApi<TFeatures extends TableFeatures, TData extends RowData> =
|
|
|
141
226
|
dimension?: string | null;
|
|
142
227
|
series?: string | null;
|
|
143
228
|
measure?: string | null;
|
|
144
|
-
reduce?:
|
|
229
|
+
reduce?: ChartReducer;
|
|
145
230
|
stacked?: boolean;
|
|
146
231
|
dataLabels?: boolean;
|
|
147
232
|
logScale?: boolean;
|
|
148
233
|
timeAxis?: boolean;
|
|
149
234
|
valueFormat?: 'number' | 'currency' | 'percent' | 'compact';
|
|
235
|
+
/** Group a date dimension by calendar unit; `null` returns to exact values. */
|
|
236
|
+
bucket?: ChartTimeBucket | null;
|
|
237
|
+
/** Second measure: a scatter's Y, a range's high end, a bullet's target. */
|
|
238
|
+
measure2?: string | null;
|
|
239
|
+
/** Histogram bin count; `null` for Sturges' rule. */
|
|
240
|
+
bins?: number | null;
|
|
241
|
+
funnelShape?: 'trapezoid' | 'pyramid' | 'cone' | null;
|
|
242
|
+
candleStyle?: 'classic' | 'hollow' | 'heikin-ashi' | null;
|
|
243
|
+
/** Candlestick / OHLC: the price columns (ids or fields); `null` goes
|
|
244
|
+
* back to guessing them from the column names. */
|
|
245
|
+
ohlc?: {
|
|
246
|
+
open?: string | null;
|
|
247
|
+
high?: string | null;
|
|
248
|
+
low?: string | null;
|
|
249
|
+
close?: string | null;
|
|
250
|
+
volume?: string | null;
|
|
251
|
+
} | null;
|
|
252
|
+
/** Indicator panes under a price chart (`volume`, `rsi`, `macd`,
|
|
253
|
+
* `stochastic`, `atr`, `obv`) and overlays on it (`sma`, `ema`,
|
|
254
|
+
* `bb`, `vwap`). */
|
|
255
|
+
indicators?: Array<'volume' | 'rsi' | 'macd' | 'stochastic' | 'atr' | 'obv' | 'sma' | 'ema' | 'bb' | 'vwap'>;
|
|
256
|
+
/** The builder's format state (titles, axes, legend, per-series style);
|
|
257
|
+
* `null` resets it. */
|
|
258
|
+
format?: ChartFormatState | null;
|
|
259
|
+
/** `true` unlinks the chart from the grid (it keeps the spec it has),
|
|
260
|
+
* `false` links it back. */
|
|
261
|
+
frozen?: boolean;
|
|
262
|
+
/** Apply a saved chart (by name) to the active tab before the other keys. */
|
|
263
|
+
saved?: string;
|
|
150
264
|
}): void;
|
|
265
|
+
/**
|
|
266
|
+
* Keep the active chart's whole configuration (type, columns, aggregate,
|
|
267
|
+
* switches, indicators, format, zoom) under a name. A same-named entry is
|
|
268
|
+
* replaced. Saved charts travel with `getState()` / `setState()` and show
|
|
269
|
+
* in the panel's Saved charts popover.
|
|
270
|
+
*/
|
|
271
|
+
saveChart(name: string): void;
|
|
272
|
+
/** Apply a saved chart to the active tab, which keeps its title. `false` when no chart has that name. */
|
|
273
|
+
applySavedChart(name: string): boolean;
|
|
274
|
+
removeSavedChart(name: string): void;
|
|
275
|
+
getSavedCharts(): SavedChart[];
|
|
151
276
|
/**
|
|
152
277
|
* Register a natural-language "chart this" handler. When set, the chart
|
|
153
278
|
* panel shows an AI button. `@svgrid/enterprise`'s `enableAiCharting(api)`
|
|
154
279
|
* fills this. Pass `null` to remove it.
|
|
155
280
|
*/
|
|
156
281
|
setChartAiHandler(handler: ((prompt: string) => Promise<Record<string, unknown> | null>) | null): void;
|
|
282
|
+
/**
|
|
283
|
+
* Register an "explain this chart" handler. When set, the chart panel's AI
|
|
284
|
+
* row shows an Explain button that reads the active chart and shows the
|
|
285
|
+
* summary and insights it returns. `enableAiCharting(api)` fills this with
|
|
286
|
+
* `aiExplainChart`. Pass `null` to remove it.
|
|
287
|
+
*/
|
|
288
|
+
setChartExplainHandler(handler: (() => Promise<{
|
|
289
|
+
summary: string;
|
|
290
|
+
insights: string[];
|
|
291
|
+
} | null>) | null): void;
|
|
157
292
|
/** Add one row. `position` defaults to `'bottom'`. */
|
|
158
293
|
addRow(row: TData, position?: 'top' | 'bottom' | number): void;
|
|
159
294
|
addRows(rows: ReadonlyArray<TData>, position?: 'top' | 'bottom' | number): void;
|
|
@@ -227,6 +362,13 @@ export type SvGridApi<TFeatures extends TableFeatures, TData extends RowData> =
|
|
|
227
362
|
* `editorOptions` returns a Promise - static lists are never refetched.
|
|
228
363
|
*/
|
|
229
364
|
refreshEditorOptions(columnId?: string): void;
|
|
365
|
+
/**
|
|
366
|
+
* Open the `contextMenu` at the pointer, for the cell at (rowIndex,
|
|
367
|
+
* colIndex), from an element of your own: a spreadsheet's column letters
|
|
368
|
+
* and row numbers offer the cell menu for the column or row they select.
|
|
369
|
+
* Does nothing without a `contextMenu` prop.
|
|
370
|
+
*/
|
|
371
|
+
openContextMenu(event: MouseEvent, rowIndex: number, colIndex: number): void;
|
|
230
372
|
/**
|
|
231
373
|
* Clear every active column filter (menu, filter-row, set-list, global, and
|
|
232
374
|
* the advanced filter). Resets the grid to "no filtering" in a single call.
|
|
@@ -331,6 +473,43 @@ export type SvGridApi<TFeatures extends TableFeatures, TData extends RowData> =
|
|
|
331
473
|
* "Autosize" item.
|
|
332
474
|
*/
|
|
333
475
|
autosizeColumn(columnId: string): void;
|
|
476
|
+
/**
|
|
477
|
+
* The height of a row in px: the one it was dragged or set to, else the
|
|
478
|
+
* declared `rowHeight` (30 when none). `rowIndex` is a display index.
|
|
479
|
+
*/
|
|
480
|
+
getRowHeight(rowIndex: number): number;
|
|
481
|
+
/**
|
|
482
|
+
* Give a row its own height, the way dragging its grip does with
|
|
483
|
+
* `rowResize`; `null` takes it back to the declared height. The height
|
|
484
|
+
* belongs to the row (its id), so it follows the row through a sort and
|
|
485
|
+
* survives the data being replaced. Ignored under `autoRowHeight`, where
|
|
486
|
+
* the content decides.
|
|
487
|
+
*/
|
|
488
|
+
setRowHeight(rowIndex: number, height: number | null): void;
|
|
489
|
+
/**
|
|
490
|
+
* Fold a column to nothing, the way a spreadsheet hides one: it keeps
|
|
491
|
+
* its index, its cells and its width, and takes no room until it is
|
|
492
|
+
* unfolded. Unlike `setColumnVisible(id, false)`, which takes the
|
|
493
|
+
* column out of the model and shifts every index after it, references
|
|
494
|
+
* by index stay valid, so a sheet's formulas and formats are untouched.
|
|
495
|
+
* Arrow keys, Tab and Enter step over a collapsed column.
|
|
496
|
+
*/
|
|
497
|
+
setColumnCollapsed(columnId: string, collapsed: boolean): void;
|
|
498
|
+
isColumnCollapsed(columnId: string): boolean;
|
|
499
|
+
/**
|
|
500
|
+
* Fold a row to nothing, the row-side twin of `setColumnCollapsed`.
|
|
501
|
+
* `rowIndex` is a display index; the fold belongs to the row (its id)
|
|
502
|
+
* and follows it through a sort.
|
|
503
|
+
*/
|
|
504
|
+
setRowCollapsed(rowIndex: number, collapsed: boolean): void;
|
|
505
|
+
isRowCollapsed(rowIndex: number): boolean;
|
|
506
|
+
/** The `mergedCells` in force, copied: origins with their spans. */
|
|
507
|
+
getMergedCells(): Array<{
|
|
508
|
+
rowIndex: number;
|
|
509
|
+
colIndex: number;
|
|
510
|
+
rowSpan: number;
|
|
511
|
+
colSpan: number;
|
|
512
|
+
}>;
|
|
334
513
|
/** Run `autosizeColumn` on every column. */
|
|
335
514
|
autosizeAllColumns(): void;
|
|
336
515
|
/**
|
|
@@ -381,6 +560,18 @@ export type SvGridApi<TFeatures extends TableFeatures, TData extends RowData> =
|
|
|
381
560
|
canRedo(): boolean;
|
|
382
561
|
/** Wipe both stacks (e.g. after a server save commits the buffer). */
|
|
383
562
|
clearHistory(): void;
|
|
563
|
+
/**
|
|
564
|
+
* The same `GridCommandContext` a registered shortcut handler receives.
|
|
565
|
+
*
|
|
566
|
+
* Keyboard commands are handed one on every keystroke, but a button in a
|
|
567
|
+
* ribbon or a toolbar has no keystroke to ride in on, and re-implementing
|
|
568
|
+
* "bold the selection" against the api would give the button and the key
|
|
569
|
+
* two code paths that drift. Both go through this instead.
|
|
570
|
+
*
|
|
571
|
+
* Live, not a snapshot: the object reads through to the grid, so one built
|
|
572
|
+
* once at mount still reports the current selection.
|
|
573
|
+
*/
|
|
574
|
+
getCommandContext(): GridCommandContext;
|
|
384
575
|
/** Open the built-in find overlay (Ctrl+F also opens it). */
|
|
385
576
|
openFind(): void;
|
|
386
577
|
/** Close the find overlay and clear the query. */
|
|
@@ -23,25 +23,41 @@ function sameState(a, b) {
|
|
|
23
23
|
for (let i = 0; i < a.items.length; i += 1) {
|
|
24
24
|
const ai = a.items[i];
|
|
25
25
|
const bi = b.items[i];
|
|
26
|
-
if (ai.index !== bi.index || ai.start !== bi.start || ai.size !== bi.size)
|
|
26
|
+
if (ai.index !== bi.index || ai.start !== bi.start || ai.size !== bi.size || ai.key !== bi.key)
|
|
27
27
|
return false;
|
|
28
28
|
}
|
|
29
29
|
return true;
|
|
30
30
|
}
|
|
31
|
-
// Slot-based keying: the key is
|
|
32
|
-
//
|
|
33
|
-
//
|
|
34
|
-
//
|
|
35
|
-
// the
|
|
31
|
+
// Slot-based keying: the key is a SLOT number, not the data index, so the
|
|
32
|
+
// same N <tr> nodes stay alive for the lifetime of the grid and scrolling
|
|
33
|
+
// never mounts or unmounts a row. With data-index keys, scrolling down one
|
|
34
|
+
// row meant the old topmost key disappeared and a new bottommost key
|
|
35
|
+
// appeared, so Svelte unmounted the top <tr> and mounted a fresh one at the
|
|
36
|
+
// bottom on every scroll tick.
|
|
36
37
|
//
|
|
37
|
-
//
|
|
38
|
-
// scroll
|
|
39
|
-
//
|
|
40
|
-
//
|
|
41
|
-
//
|
|
42
|
-
//
|
|
38
|
+
// The slot for data index i is `i mod N`, N being the window length. That
|
|
39
|
+
// matters for what a one-row scroll costs: while N holds, a row that stays
|
|
40
|
+
// inside the window keeps its slot, so Svelte keeps its <tr> and touches
|
|
41
|
+
// nothing in it, and only the row that entered gets new content - the keyed
|
|
42
|
+
// each moves that one <tr> to the other end with a single `insertBefore`.
|
|
43
|
+
// The earlier `i - startIndex` slot scheme handed EVERY rendered row a new
|
|
44
|
+
// index on every one-row scroll, so an arrow key re-rendered all ~30 rows x
|
|
45
|
+
// every rendered column to shift the data up by one.
|
|
46
|
+
//
|
|
47
|
+
// The modulus has to be EXACTLY N, not merely >= N: under a larger modulus
|
|
48
|
+
// the window's residues are a proper subset, so each one-row scroll drops
|
|
49
|
+
// one residue and picks up another, and Svelte unmounts a <tr> and mounts a
|
|
50
|
+
// fresh one instead of moving it (measured: mount + unmount per arrow key,
|
|
51
|
+
// zero moves). When N changes - the top and bottom overscan clamping on the
|
|
52
|
+
// first and last rows, a resize, variable row heights - every slot remaps
|
|
53
|
+
// and the whole window re-renders once, which is what the old scheme did on
|
|
54
|
+
// every tick.
|
|
55
|
+
function slotKey(index, windowLength) {
|
|
56
|
+
return `virtual_slot_${index % windowLength}`;
|
|
57
|
+
}
|
|
43
58
|
function buildUniformItems(startIndex, endIndex, estimateSize) {
|
|
44
59
|
const items = [];
|
|
60
|
+
const windowLength = endIndex - startIndex + 1;
|
|
45
61
|
for (let index = startIndex; index <= endIndex; index += 1) {
|
|
46
62
|
const start = index * estimateSize;
|
|
47
63
|
items.push({
|
|
@@ -49,7 +65,7 @@ function buildUniformItems(startIndex, endIndex, estimateSize) {
|
|
|
49
65
|
start,
|
|
50
66
|
size: estimateSize,
|
|
51
67
|
end: start + estimateSize,
|
|
52
|
-
key:
|
|
68
|
+
key: slotKey(index, windowLength),
|
|
53
69
|
});
|
|
54
70
|
}
|
|
55
71
|
return items;
|
|
@@ -72,11 +88,12 @@ export function buildPreMeasureItems(count, estimateSize, viewportHeight, oversc
|
|
|
72
88
|
return [];
|
|
73
89
|
const size = Math.max(estimateSize, 1);
|
|
74
90
|
const visible = Math.ceil(Math.max(viewportHeight, 0) / size);
|
|
75
|
-
const endIndex = Math.min(visible + Math.max(overscan, 0), count - 1);
|
|
76
|
-
return buildUniformItems(0,
|
|
91
|
+
const endIndex = Math.max(Math.min(visible + Math.max(overscan, 0), count - 1), 0);
|
|
92
|
+
return buildUniformItems(0, endIndex, size);
|
|
77
93
|
}
|
|
78
94
|
function buildVariableItems(startIndex, endIndex, offsets) {
|
|
79
95
|
const items = [];
|
|
96
|
+
const windowLength = endIndex - startIndex + 1;
|
|
80
97
|
for (let index = startIndex; index <= endIndex; index += 1) {
|
|
81
98
|
const start = offsets[index] ?? 0;
|
|
82
99
|
const end = offsets[index + 1] ?? start;
|
|
@@ -85,7 +102,7 @@ function buildVariableItems(startIndex, endIndex, offsets) {
|
|
|
85
102
|
start,
|
|
86
103
|
size: end - start,
|
|
87
104
|
end,
|
|
88
|
-
key:
|
|
105
|
+
key: slotKey(index, windowLength),
|
|
89
106
|
});
|
|
90
107
|
}
|
|
91
108
|
return items;
|
|
@@ -168,8 +185,12 @@ export function createVirtualizer(initial) {
|
|
|
168
185
|
const sizeFn = options.estimateSize;
|
|
169
186
|
const offsets = new Array(count + 1);
|
|
170
187
|
offsets[0] = 0;
|
|
188
|
+
// A size of 0 is a real size: a collapsed row or column takes no room
|
|
189
|
+
// and both searches below stay monotonic over equal offsets. Only a
|
|
190
|
+
// negative size is nonsense. The floor used to be 1, which left every
|
|
191
|
+
// hidden column a one-pixel sliver.
|
|
171
192
|
for (let i = 0; i < count; i += 1) {
|
|
172
|
-
offsets[i + 1] = offsets[i] + Math.max(sizeFn(i),
|
|
193
|
+
offsets[i + 1] = offsets[i] + Math.max(sizeFn(i), 0);
|
|
173
194
|
}
|
|
174
195
|
offsetCache = { fn: sizeFn, count, offsets };
|
|
175
196
|
return offsets;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@svgrid/grid",
|
|
3
|
-
"version": "3.0.
|
|
3
|
+
"version": "3.0.4",
|
|
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",
|
|
@@ -94,6 +94,11 @@
|
|
|
94
94
|
"svelte": "./dist/filtering/excel-filters.js",
|
|
95
95
|
"import": "./dist/filtering/excel-filters.js"
|
|
96
96
|
},
|
|
97
|
+
"./shortcuts": {
|
|
98
|
+
"types": "./dist/shortcut-registry.d.ts",
|
|
99
|
+
"svelte": "./dist/shortcut-registry.js",
|
|
100
|
+
"import": "./dist/shortcut-registry.js"
|
|
101
|
+
},
|
|
97
102
|
"./flex-render": {
|
|
98
103
|
"types": "./dist/flex-render.d.ts",
|
|
99
104
|
"svelte": "./dist/flex-render.js",
|