@svgrid/grid 3.0.3 → 3.0.5
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 +6 -5
- 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 +122 -41
- package/dist/SvGrid.controller.svelte.js +846 -207
- package/dist/SvGrid.css +178 -2
- package/dist/SvGrid.svelte +717 -720
- package/dist/SvGrid.types.d.ts +566 -12
- 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 +119 -28
- package/dist/cdn/GridMenus-CID62rlI.js +636 -0
- package/dist/cdn/GridMenus-DLL-qZeu.js +635 -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/server-block-cache-CGoWz-87.js +237 -0
- package/dist/cdn/{src-D6XvA1Ij.js → src-Bklofsad.js} +10615 -10155
- package/dist/cdn/{src-Bugf5XlI.js → src-jcFMkBnN.js} +7111 -6651
- package/dist/cdn/svgrid.js +27 -15
- package/dist/cdn/svgrid.svelte-external.js +27 -15
- package/dist/cdn/validate-C7Rn_Bns.js +76 -0
- 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/core.js +36 -13
- package/dist/createPopoverSelect.svelte.d.ts +2 -2
- package/dist/editing.d.ts +2 -8
- package/dist/editing.js +272 -33
- package/dist/fill-patterns.d.ts +0 -5
- package/dist/fill-patterns.js +56 -2
- 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 +16 -3
- package/dist/grid-messages.js +10 -0
- package/dist/history.d.ts +83 -0
- package/dist/history.js +133 -0
- package/dist/index.d.ts +29 -10
- package/dist/index.js +23 -5
- 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-model.d.ts +133 -0
- package/dist/row-model.js +16 -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 +18 -1
- package/dist/selection.js +138 -18
- package/dist/server-block-cache.d.ts +214 -0
- package/dist/server-block-cache.js +531 -0
- package/dist/server-data-source.d.ts +240 -6
- package/dist/server-data-source.js +220 -20
- package/dist/server.d.ts +13 -0
- package/dist/server.js +13 -0
- 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/validate.d.ts +4 -0
- package/dist/validate.js +21 -3
- package/dist/virtualization/virtualizer.js +38 -17
- package/package.json +11 -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 +819 -179
- package/src/SvGrid.css +178 -2
- package/src/SvGrid.svelte +717 -720
- package/src/SvGrid.types.ts +564 -11
- 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 +118 -22
- 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.rowmodel-cache.test.ts +51 -0
- package/src/core.ts +40 -12
- package/src/editing.test.ts +281 -3
- package/src/editing.ts +264 -38
- package/src/fill-patterns.test.ts +35 -0
- package/src/fill-patterns.ts +56 -2
- 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.ts +28 -3
- package/src/history.test.ts +196 -0
- package/src/history.ts +162 -0
- package/src/icon-seam.test.ts +5 -6
- package/src/index.ts +186 -12
- 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-model.ts +146 -0
- 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 +33 -2
- package/src/selection.ts +143 -21
- package/src/server-block-cache.test.ts +645 -0
- package/src/server-block-cache.ts +677 -0
- package/src/server-data-source.infinite.test.ts +343 -0
- package/src/server-data-source.ts +450 -27
- package/src/server.ts +47 -0
- 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.gantt-seam.test.ts +199 -0
- package/src/svgrid.new-features.wrapper.test.ts +10 -3
- package/src/svgrid.row-model-prop.svelte.test.ts +352 -0
- package/src/svgrid.row-model-seam.svelte.test.ts +289 -0
- package/src/svgrid.upsell-license.test.ts +7 -4
- package/src/validate.test.ts +29 -0
- package/src/validate.ts +28 -3
- package/src/virtualization/virtualizer.ts +39 -17
- 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-CHOMnW-c.js +0 -602
- package/dist/cdn/GridMenus-DhdPF700.js +0 -603
- package/dist/cdn/SvDateTimePicker-CCbDZNZB.js +0 -816
- package/dist/cdn/SvGridChart-1PMSp2LZ.js +0 -1481
- package/dist/cdn/SvGridChart-D02avhyr.js +0 -1480
- package/dist/cdn/SvGridChartPanel-DJnViOv4.js +0 -834
- package/dist/cdn/SvGridChartPanel-vPTNerFZ.js +0 -810
- package/dist/cdn/SvGridChartView-C--HhdUo.js +0 -56
- package/dist/cdn/SvGridChartView-KyyFG-wY.js +0 -55
- package/dist/cdn/chart-DLg3_zTQ.js +0 -1652
- 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/editing.js
CHANGED
|
@@ -3,9 +3,12 @@
|
|
|
3
3
|
// ($state/$derived/$effect) stays in the controller.
|
|
4
4
|
import { parseEditorValue, } from "./index.js";
|
|
5
5
|
import "./sv-grid-scrollbar.js";
|
|
6
|
-
import {
|
|
6
|
+
import { getEntryStep } from "./keyboard.js";
|
|
7
7
|
import { getCellKey, toValueArray, } from "./SvGrid.helpers.js";
|
|
8
8
|
import { createDataIndexLookup, getColumnBaseValue, isGroupRow, } from "./cell-values.js";
|
|
9
|
+
import { pushHistory, nextGroupId, runHistoryGroup } from "./history.js";
|
|
10
|
+
import { hasGridShortcuts, runGridShortcuts } from "./shortcut-registry.js";
|
|
11
|
+
import { buildCommandContext } from "./command-context.js";
|
|
9
12
|
export function createEditing(ctx) {
|
|
10
13
|
/**
|
|
11
14
|
* Resolve `columnDef.editable` for one row × column pair.
|
|
@@ -25,6 +28,16 @@ export function createEditing(ctx) {
|
|
|
25
28
|
const editable = column.columnDef.editable;
|
|
26
29
|
if (editable === false)
|
|
27
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
|
+
}
|
|
28
41
|
if (typeof editable !== "function")
|
|
29
42
|
return true;
|
|
30
43
|
if (!row)
|
|
@@ -100,10 +113,27 @@ export function createEditing(ctx) {
|
|
|
100
113
|
rowRef: row.original,
|
|
101
114
|
};
|
|
102
115
|
ctx.setActiveCell(rowIndex, colIndex);
|
|
103
|
-
|
|
116
|
+
selectForEdit(rowIndex, colIndex);
|
|
104
117
|
return true;
|
|
105
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* The selection an edit starts with. A cell already inside the selection
|
|
121
|
+
* leaves it standing: typing into the active cell of a selected block
|
|
122
|
+
* used to collapse the block to that one cell, so Enter could never walk
|
|
123
|
+
* on inside it. A cell outside the selection becomes the selection.
|
|
124
|
+
*/
|
|
125
|
+
function selectForEdit(rowIndex, colIndex) {
|
|
126
|
+
if (ctx.isCellInSelectedRange(rowIndex, colIndex))
|
|
127
|
+
return;
|
|
128
|
+
ctx.setSelection(rowIndex, colIndex);
|
|
129
|
+
}
|
|
106
130
|
function saveEditingCell() {
|
|
131
|
+
// One group: a consumer's onCellValueChange may record a step of its own
|
|
132
|
+
// (a spreadsheet gives "12%" a percent format as it lands), and that
|
|
133
|
+
// belongs to the same Ctrl+Z as the value.
|
|
134
|
+
runHistoryGroup(ctx, () => commitEditingCell());
|
|
135
|
+
}
|
|
136
|
+
function commitEditingCell() {
|
|
107
137
|
if (!ctx.editingCell)
|
|
108
138
|
return;
|
|
109
139
|
const editing = ctx.editingCell;
|
|
@@ -154,20 +184,7 @@ export function createEditing(ctx) {
|
|
|
154
184
|
before: oldValue,
|
|
155
185
|
after: finalValue,
|
|
156
186
|
};
|
|
157
|
-
|
|
158
|
-
truncated.push(step);
|
|
159
|
-
// Cap the buffer at UNDO_LIMIT; drop the OLDEST entries so the
|
|
160
|
-
// pointer stays valid relative to the newest steps.
|
|
161
|
-
if (truncated.length > ctx.UNDO_LIMIT) {
|
|
162
|
-
const drop = truncated.length - ctx.UNDO_LIMIT;
|
|
163
|
-
ctx.history = truncated.slice(drop);
|
|
164
|
-
ctx.historyPtr = ctx.history.length - 1;
|
|
165
|
-
}
|
|
166
|
-
else {
|
|
167
|
-
ctx.history = truncated;
|
|
168
|
-
ctx.historyPtr = ctx.history.length - 1;
|
|
169
|
-
}
|
|
170
|
-
ctx.historyVersion += 1;
|
|
187
|
+
pushHistory(ctx, [step]);
|
|
171
188
|
}
|
|
172
189
|
// Notify the consumer AFTER the row has been updated so any callback-
|
|
173
190
|
// driven recompute (cascade totals, server save, undo stack) sees the
|
|
@@ -187,6 +204,13 @@ export function createEditing(ctx) {
|
|
|
187
204
|
/** Apply an undo / redo step directly to the underlying row, bypassing
|
|
188
205
|
* the editor pipeline so we don't accidentally re-push to the stack. */
|
|
189
206
|
function applyHistoryStep(step, direction) {
|
|
207
|
+
if (step.custom) {
|
|
208
|
+
if (direction === 'undo')
|
|
209
|
+
step.custom.undo();
|
|
210
|
+
else
|
|
211
|
+
step.custom.redo();
|
|
212
|
+
return;
|
|
213
|
+
}
|
|
190
214
|
const row = ctx.allRows.find((r) => r.id === step.rowId);
|
|
191
215
|
const col = ctx.allColumns.find((c) => c.id === step.columnId);
|
|
192
216
|
if (!row?.original || !col)
|
|
@@ -217,26 +241,115 @@ export function createEditing(ctx) {
|
|
|
217
241
|
* move, Tab committed the edit and then let the browser walk focus out of the
|
|
218
242
|
* grid entirely (#48). Shared by every editor type, including the textarea.
|
|
219
243
|
*/
|
|
220
|
-
|
|
244
|
+
/**
|
|
245
|
+
* Commit the edit and step the cursor the way data entry expects: Tab a
|
|
246
|
+
* column to the right (wrapping at the row's end), Enter a row down, Shift
|
|
247
|
+
* reversing either. Enter used to commit and stay, so a column of numbers
|
|
248
|
+
* took a click or an arrow between every value; Excel and every other
|
|
249
|
+
* sheet move down, and the accessibility page has always said this grid
|
|
250
|
+
* does too. At the last row or column the cursor stays put.
|
|
251
|
+
*/
|
|
252
|
+
function commitAndMove(intent) {
|
|
221
253
|
const active = ctx.activeCell;
|
|
222
254
|
saveEditingCell();
|
|
223
255
|
ctx.gridRootEl?.focus({ preventScroll: true });
|
|
224
256
|
if (!active)
|
|
225
257
|
return;
|
|
226
|
-
|
|
258
|
+
// The same stepping as Enter and Tab on the grid root: a Tab run's Enter
|
|
259
|
+
// goes back to the run's first column, and a selected block keeps the
|
|
260
|
+
// cursor inside it.
|
|
261
|
+
const step = getEntryStep(active, intent, {
|
|
227
262
|
maxRow: ctx.allRows.length - 1,
|
|
228
263
|
maxCol: ctx.allColumns.length - 1,
|
|
264
|
+
tabOrigin: ctx.tabRunOrigin,
|
|
265
|
+
range: ctx.activeRangeRect(),
|
|
266
|
+
collapsed: {
|
|
267
|
+
isRowCollapsed: (i) => ctx.isRowCollapsed(i),
|
|
268
|
+
isColumnCollapsed: (i) => !!ctx.collapsedColumns[ctx.allColumns[i]?.id],
|
|
269
|
+
},
|
|
229
270
|
});
|
|
271
|
+
const next = step.cell;
|
|
230
272
|
ctx.setActiveCell(next.rowIndex, next.colIndex);
|
|
231
|
-
ctx.
|
|
273
|
+
ctx.tabRunOrigin = step.tabOrigin;
|
|
274
|
+
if (!step.withinRange)
|
|
275
|
+
ctx.setSelection(next.rowIndex, next.colIndex);
|
|
232
276
|
ctx.scrollActiveCellIntoView(next.rowIndex, next.colIndex);
|
|
233
277
|
}
|
|
278
|
+
function commitAndMoveByTab(shiftKey) {
|
|
279
|
+
commitAndMove(shiftKey ? "tabPrev" : "tabNext");
|
|
280
|
+
}
|
|
281
|
+
/**
|
|
282
|
+
* Ctrl+Enter: the entry goes into every cell of the selected block and the
|
|
283
|
+
* cursor stays where it is, as in Excel, where it is how a block is filled
|
|
284
|
+
* with one value in one go. Each cell takes the same raw entry through the
|
|
285
|
+
* ordinary commit, so its column's parser, the history and
|
|
286
|
+
* `onCellValueChange` all see it, and the whole block is one undo. A cell
|
|
287
|
+
* that cannot be edited is left alone. With nothing but the active cell
|
|
288
|
+
* selected it is a commit that does not move.
|
|
289
|
+
*/
|
|
290
|
+
function commitToSelection() {
|
|
291
|
+
const editing = ctx.editingCell;
|
|
292
|
+
if (!editing)
|
|
293
|
+
return;
|
|
294
|
+
const raw = editing.value;
|
|
295
|
+
const active = ctx.activeCell;
|
|
296
|
+
const range = ctx.activeRangeRect();
|
|
297
|
+
runHistoryGroup(ctx, () => {
|
|
298
|
+
commitEditingCell();
|
|
299
|
+
if (!active || !range)
|
|
300
|
+
return;
|
|
301
|
+
for (let r = range.minRow; r <= range.maxRow; r += 1) {
|
|
302
|
+
for (let c = range.minCol; c <= range.maxCol; c += 1) {
|
|
303
|
+
if (r === active.rowIndex && c === active.colIndex)
|
|
304
|
+
continue;
|
|
305
|
+
const row = ctx.allRows[r];
|
|
306
|
+
const column = ctx.allColumns[c];
|
|
307
|
+
if (!row || !column || isGroupRow(row) || !isCellEditable(column, row))
|
|
308
|
+
continue;
|
|
309
|
+
ctx.editingCell = {
|
|
310
|
+
rowId: row.id,
|
|
311
|
+
columnId: column.id,
|
|
312
|
+
editorType: (column.columnDef.editorType ?? "text"),
|
|
313
|
+
value: raw,
|
|
314
|
+
rowRef: row.original,
|
|
315
|
+
};
|
|
316
|
+
commitEditingCell();
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
});
|
|
320
|
+
ctx.gridRootEl?.focus({ preventScroll: true });
|
|
321
|
+
}
|
|
234
322
|
function onEditorKeyDown(event) {
|
|
235
323
|
event.stopPropagation();
|
|
236
|
-
|
|
324
|
+
// Registered commands get the key first here too, with `editing: true`, so
|
|
325
|
+
// a handler can claim a combination that only means something mid-edit
|
|
326
|
+
// (Alt+Enter for a newline, F4 to cycle a reference between relative and
|
|
327
|
+
// absolute) before the editor's own Enter / Tab / Escape handling runs.
|
|
328
|
+
if (hasGridShortcuts() && runGridShortcuts(event, buildCommandContext(ctx, true))) {
|
|
329
|
+
return;
|
|
330
|
+
}
|
|
331
|
+
if (event.key === "Enter" && event.altKey) {
|
|
332
|
+
// A line break, in an editor that can hold one (`editorMultiline`).
|
|
333
|
+
// In a single-line editor Alt+Enter is just Enter.
|
|
334
|
+
const el = event.currentTarget;
|
|
335
|
+
if (el instanceof HTMLTextAreaElement) {
|
|
336
|
+
event.preventDefault();
|
|
337
|
+
const start = el.selectionStart ?? el.value.length;
|
|
338
|
+
const end = el.selectionEnd ?? start;
|
|
339
|
+
el.value = el.value.slice(0, start) + "\n" + el.value.slice(end);
|
|
340
|
+
el.selectionStart = el.selectionEnd = start + 1;
|
|
341
|
+
updateEditingCellValue(el.value);
|
|
342
|
+
el.dispatchEvent(new Event("input", { bubbles: true }));
|
|
343
|
+
return;
|
|
344
|
+
}
|
|
345
|
+
}
|
|
346
|
+
if (event.key === "Enter" && (event.ctrlKey || event.metaKey)) {
|
|
237
347
|
event.preventDefault();
|
|
238
|
-
|
|
239
|
-
|
|
348
|
+
commitToSelection();
|
|
349
|
+
}
|
|
350
|
+
else if (event.key === "Enter") {
|
|
351
|
+
event.preventDefault();
|
|
352
|
+
commitAndMove(event.shiftKey ? "moveUp" : "moveDown");
|
|
240
353
|
}
|
|
241
354
|
else if (event.key === "Tab") {
|
|
242
355
|
event.preventDefault();
|
|
@@ -316,7 +429,7 @@ export function createEditing(ctx) {
|
|
|
316
429
|
rowRef: row.original,
|
|
317
430
|
};
|
|
318
431
|
ctx.setActiveCell(rowIndex, colIndex);
|
|
319
|
-
|
|
432
|
+
selectForEdit(rowIndex, colIndex);
|
|
320
433
|
}
|
|
321
434
|
async function pasteFromClipboard() {
|
|
322
435
|
// Preferred path: the async Clipboard API. Requires a secure context
|
|
@@ -325,6 +438,34 @@ export function createEditing(ctx) {
|
|
|
325
438
|
// On plain HTTP (a XAMPP/Apache LAN host) `navigator.clipboard` is
|
|
326
439
|
// undefined - there the Ctrl+V keydown handler skips preventDefault and
|
|
327
440
|
// lets the browser deliver a native `paste` event to `onGridPaste`.
|
|
441
|
+
if (ctx.props.onPasteClipboard && navigator.clipboard?.read) {
|
|
442
|
+
// Both types, for a handler that reads the HTML.
|
|
443
|
+
try {
|
|
444
|
+
const items = await navigator.clipboard.read();
|
|
445
|
+
let text = "";
|
|
446
|
+
let html = null;
|
|
447
|
+
for (const item of items) {
|
|
448
|
+
if (item.types.includes("text/html"))
|
|
449
|
+
html = await (await item.getType("text/html")).text();
|
|
450
|
+
if (item.types.includes("text/plain"))
|
|
451
|
+
text = await (await item.getType("text/plain")).text();
|
|
452
|
+
}
|
|
453
|
+
if (text || html)
|
|
454
|
+
applyPastedPayload({ text, html, source: "async" });
|
|
455
|
+
}
|
|
456
|
+
catch {
|
|
457
|
+
// No permission or nothing readable: the text-only read below may still work.
|
|
458
|
+
if (!navigator.clipboard.readText)
|
|
459
|
+
return;
|
|
460
|
+
try {
|
|
461
|
+
applyPastedPayload({ text: await navigator.clipboard.readText(), html: null, source: "async" });
|
|
462
|
+
}
|
|
463
|
+
catch {
|
|
464
|
+
return;
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
return;
|
|
468
|
+
}
|
|
328
469
|
if (!navigator.clipboard?.readText)
|
|
329
470
|
return;
|
|
330
471
|
let text;
|
|
@@ -336,6 +477,27 @@ export function createEditing(ctx) {
|
|
|
336
477
|
}
|
|
337
478
|
applyPastedText(text);
|
|
338
479
|
}
|
|
480
|
+
/**
|
|
481
|
+
* With `onPasteClipboard` set, Ctrl+V leaves the key to the browser so the
|
|
482
|
+
* `paste` event arrives with the HTML. A browser that does not deliver
|
|
483
|
+
* that event to the grid root (Firefox, a non-editable element) would
|
|
484
|
+
* paste nothing, so the async read is armed to run shortly after unless
|
|
485
|
+
* the event lands first and disarms it.
|
|
486
|
+
*/
|
|
487
|
+
let pasteFallback = null;
|
|
488
|
+
function armPasteFallback() {
|
|
489
|
+
if (pasteFallback)
|
|
490
|
+
clearTimeout(pasteFallback);
|
|
491
|
+
pasteFallback = setTimeout(() => {
|
|
492
|
+
pasteFallback = null;
|
|
493
|
+
void pasteFromClipboard();
|
|
494
|
+
}, 80);
|
|
495
|
+
}
|
|
496
|
+
function disarmPasteFallback() {
|
|
497
|
+
if (pasteFallback)
|
|
498
|
+
clearTimeout(pasteFallback);
|
|
499
|
+
pasteFallback = null;
|
|
500
|
+
}
|
|
339
501
|
/**
|
|
340
502
|
* Native `paste` ClipboardEvent fallback (insecure-context path). A real
|
|
341
503
|
* Ctrl/Cmd+V on the focused grid root fires this with `clipboardData`
|
|
@@ -351,6 +513,18 @@ export function createEditing(ctx) {
|
|
|
351
513
|
// so the edit "wouldn't update".
|
|
352
514
|
if (ctx.editingCell)
|
|
353
515
|
return;
|
|
516
|
+
if (ctx.props.onPasteClipboard) {
|
|
517
|
+
// The event is the preferred path for a handler: it carries the HTML
|
|
518
|
+
// as the source wrote it, needs no permission, and works over HTTP.
|
|
519
|
+
disarmPasteFallback();
|
|
520
|
+
const text = event.clipboardData?.getData("text/plain") ?? "";
|
|
521
|
+
const html = event.clipboardData?.getData("text/html") || null;
|
|
522
|
+
if (!text && !html)
|
|
523
|
+
return;
|
|
524
|
+
event.preventDefault();
|
|
525
|
+
applyPastedPayload({ text, html, source: "event" });
|
|
526
|
+
return;
|
|
527
|
+
}
|
|
354
528
|
// When the async API is available the keydown handler already pasted via
|
|
355
529
|
// pasteFromClipboard(); ignore the (suppressed) native event so we don't
|
|
356
530
|
// paste twice.
|
|
@@ -363,6 +537,22 @@ export function createEditing(ctx) {
|
|
|
363
537
|
applyPastedText(text);
|
|
364
538
|
}
|
|
365
539
|
function applyPastedText(text) {
|
|
540
|
+
// One history group for the whole paste, so whatever a consumer's
|
|
541
|
+
// processCellFromClipboard records alongside the values (a format, via
|
|
542
|
+
// recordUndo) comes back with them on a single Ctrl+Z.
|
|
543
|
+
runHistoryGroup(ctx, () => applyPastedCells(text));
|
|
544
|
+
}
|
|
545
|
+
/** A paste with both types: the handler first, the plain text otherwise. */
|
|
546
|
+
function applyPastedPayload(payload) {
|
|
547
|
+
runHistoryGroup(ctx, () => {
|
|
548
|
+
const handled = ctx.props.onPasteClipboard?.(payload);
|
|
549
|
+
if (handled === true)
|
|
550
|
+
return;
|
|
551
|
+
if (payload.text)
|
|
552
|
+
applyPastedCells(payload.text);
|
|
553
|
+
});
|
|
554
|
+
}
|
|
555
|
+
function applyPastedCells(text) {
|
|
366
556
|
const anchor = ctx.selectionRange.anchor ?? ctx.grid.getState().activeCell;
|
|
367
557
|
if (!anchor)
|
|
368
558
|
return;
|
|
@@ -390,6 +580,12 @@ export function createEditing(ctx) {
|
|
|
390
580
|
: Math.max(...lines.map((l) => l.split("\t").length));
|
|
391
581
|
const next = ctx.internalData.slice();
|
|
392
582
|
const dataIndexOf = createDataIndexLookup(next);
|
|
583
|
+
// Every cell the paste changes: one grouped history entry for the lot,
|
|
584
|
+
// so Ctrl+Z takes the whole paste back, and one onCellValueChange each,
|
|
585
|
+
// so a consumer keeping its own model behind the grid (a formula engine)
|
|
586
|
+
// learns the values. The loop used to swap the rows in and stop, which
|
|
587
|
+
// left the undo stack blind to a paste and the consumer never told.
|
|
588
|
+
const changed = [];
|
|
393
589
|
for (let i = 0; i < rowSpan; i += 1) {
|
|
394
590
|
const targetRowIndex = startRow + i;
|
|
395
591
|
const row = ctx.allRows[targetRowIndex];
|
|
@@ -409,23 +605,69 @@ export function createEditing(ctx) {
|
|
|
409
605
|
const updated = {
|
|
410
606
|
...originalRow,
|
|
411
607
|
};
|
|
608
|
+
const rowChanges = [];
|
|
609
|
+
const record = (column, before, after) => {
|
|
610
|
+
if (before === after)
|
|
611
|
+
return;
|
|
612
|
+
rowChanges.push({
|
|
613
|
+
row: updated, rowId: row.id, rowIndex: dataIndex, columnId: column.id,
|
|
614
|
+
field: column.columnDef.field, before, after,
|
|
615
|
+
});
|
|
616
|
+
};
|
|
412
617
|
for (let j = 0; j < colSpan; j += 1) {
|
|
413
618
|
const column = ctx.allColumns[startCol + j];
|
|
414
619
|
if (!column?.columnDef.field)
|
|
415
620
|
continue;
|
|
416
621
|
if (!isCellEditableAt(targetRowIndex, startCol + j))
|
|
417
622
|
continue;
|
|
623
|
+
const before = updated[column.columnDef.field];
|
|
418
624
|
const editorType = (column.columnDef.editorType ??
|
|
419
625
|
"text");
|
|
420
626
|
const raw = fillRange ? lines[0] : sourceCells?.[j] ?? "";
|
|
421
|
-
|
|
627
|
+
const parsedValue = parseEditorValue(editorType, raw, {
|
|
422
628
|
dateOnly: column.columnDef.cellDataType === "dateString",
|
|
423
629
|
});
|
|
630
|
+
// The inbound half of the clipboard pair. A consumer (or a feature
|
|
631
|
+
// pack doing paste-special) gets the raw text AND what the grid would
|
|
632
|
+
// have written, and can return either. `undefined` leaves the cell
|
|
633
|
+
// alone, which is how "paste values only" skips a formula column.
|
|
634
|
+
const hook = ctx.props.processCellFromClipboard;
|
|
635
|
+
if (hook) {
|
|
636
|
+
const decided = hook({
|
|
637
|
+
text: raw,
|
|
638
|
+
parsedValue,
|
|
639
|
+
row: originalRow,
|
|
640
|
+
rowIndex: targetRowIndex,
|
|
641
|
+
columnId: column.id,
|
|
642
|
+
});
|
|
643
|
+
if (decided === undefined)
|
|
644
|
+
continue;
|
|
645
|
+
updated[column.columnDef.field] = decided;
|
|
646
|
+
record(column, before, decided);
|
|
647
|
+
continue;
|
|
648
|
+
}
|
|
649
|
+
updated[column.columnDef.field] = parsedValue;
|
|
650
|
+
record(column, before, parsedValue);
|
|
424
651
|
}
|
|
652
|
+
if (!rowChanges.length)
|
|
653
|
+
continue;
|
|
425
654
|
next[dataIndex] = updated;
|
|
655
|
+
changed.push(...rowChanges);
|
|
426
656
|
}
|
|
657
|
+
if (!changed.length)
|
|
658
|
+
return;
|
|
427
659
|
ctx.internalData = next;
|
|
428
660
|
ctx.grid.store.setState((prev) => ({ ...prev }));
|
|
661
|
+
pushHistory(ctx, changed.map((c) => ({
|
|
662
|
+
rowId: c.rowId, columnId: c.columnId, field: c.field, before: c.before, after: c.after,
|
|
663
|
+
})), nextGroupId());
|
|
664
|
+
if (ctx.props.onCellValueChange) {
|
|
665
|
+
for (const c of changed) {
|
|
666
|
+
ctx.props.onCellValueChange({
|
|
667
|
+
rowIndex: c.rowIndex, columnId: c.columnId, oldValue: c.before, newValue: c.after, row: c.row,
|
|
668
|
+
});
|
|
669
|
+
}
|
|
670
|
+
}
|
|
429
671
|
}
|
|
430
672
|
/** Programmatically begin editing a cell (mirrors a double-click). */
|
|
431
673
|
function startEditing(rowIndex, columnId) {
|
|
@@ -531,15 +773,11 @@ export function createEditing(ctx) {
|
|
|
531
773
|
}
|
|
532
774
|
// Record the whole-row change as consecutive history steps.
|
|
533
775
|
if (changed.length) {
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
hist = hist.slice(hist.length - ctx.UNDO_LIMIT);
|
|
540
|
-
ctx.history = hist;
|
|
541
|
-
ctx.historyPtr = ctx.history.length - 1;
|
|
542
|
-
ctx.historyVersion += 1;
|
|
776
|
+
// One full-row commit is ONE undo, not one per changed column.
|
|
777
|
+
pushHistory(ctx, changed.map((c) => ({
|
|
778
|
+
rowId: fr.rowId, columnId: c.columnId, field: c.field,
|
|
779
|
+
before: c.before, after: c.after,
|
|
780
|
+
})), nextGroupId());
|
|
543
781
|
}
|
|
544
782
|
ctx.grid.store.setState((prev) => ({ ...prev }));
|
|
545
783
|
if (ctx.props.onCellValueChange && changed.length) {
|
|
@@ -581,5 +819,6 @@ export function createEditing(ctx) {
|
|
|
581
819
|
onCellDoubleClick,
|
|
582
820
|
pasteFromClipboard,
|
|
583
821
|
onGridPaste,
|
|
822
|
+
armPasteFallback,
|
|
584
823
|
};
|
|
585
824
|
}
|
package/dist/fill-patterns.d.ts
CHANGED
|
@@ -22,9 +22,4 @@
|
|
|
22
22
|
* A single source value falls through to the cycle case (repeats), which
|
|
23
23
|
* matches Excel's default fill behavior.
|
|
24
24
|
*/
|
|
25
|
-
/**
|
|
26
|
-
* Compute the values to write into `targetCount` new cells given the
|
|
27
|
-
* `sourceValues` already in the selection. See module header for the
|
|
28
|
-
* pattern-detection rules.
|
|
29
|
-
*/
|
|
30
25
|
export declare function buildFillPattern(sourceValues: ReadonlyArray<unknown>, targetCount: number): unknown[];
|
package/dist/fill-patterns.js
CHANGED
|
@@ -149,10 +149,48 @@ function arithmeticStep(nums) {
|
|
|
149
149
|
* `sourceValues` already in the selection. See module header for the
|
|
150
150
|
* pattern-detection rules.
|
|
151
151
|
*/
|
|
152
|
+
const ISO_DATE = /^\d{4}-\d{2}-\d{2}$/;
|
|
153
|
+
/**
|
|
154
|
+
* The sources as day stamps when every one is a date: `Date` objects, or
|
|
155
|
+
* `YYYY-MM-DD` text (read as UTC so a fill never crosses a day boundary
|
|
156
|
+
* with the time zone). Anything else means "not a date series".
|
|
157
|
+
*/
|
|
158
|
+
function datesOf(values) {
|
|
159
|
+
if (values.every((v) => v instanceof Date && Number.isFinite(v.getTime()))) {
|
|
160
|
+
return { stamps: values.map((v) => v.getTime()), iso: false };
|
|
161
|
+
}
|
|
162
|
+
if (values.every((v) => typeof v === 'string' && ISO_DATE.test(v))) {
|
|
163
|
+
const stamps = values.map((v) => Date.parse(`${v}T00:00:00Z`));
|
|
164
|
+
return stamps.every((t) => Number.isFinite(t)) ? { stamps, iso: true } : null;
|
|
165
|
+
}
|
|
166
|
+
return null;
|
|
167
|
+
}
|
|
152
168
|
export function buildFillPattern(sourceValues, targetCount) {
|
|
153
169
|
if (sourceValues.length === 0 || targetCount <= 0)
|
|
154
170
|
return [];
|
|
155
|
-
// --------
|
|
171
|
+
// -------- 0) Dates step by the day ---------------------------------
|
|
172
|
+
// Before the numeric rule, since a Date coerces to a finite number of
|
|
173
|
+
// milliseconds, and before the prefix rule, which read 2024-01-15 as the
|
|
174
|
+
// number 2024 with a suffix and filled 2025-01-15. Two dates set the step
|
|
175
|
+
// (a week apart fills weekly); one date steps by a day, as Excel's does.
|
|
176
|
+
const dates = datesOf(sourceValues);
|
|
177
|
+
if (dates) {
|
|
178
|
+
const DAY = 86400000;
|
|
179
|
+
const days = dates.stamps.map((t) => Math.round(t / DAY));
|
|
180
|
+
const step = days.length >= 2 ? arithmeticStep(days) : 1;
|
|
181
|
+
if (step !== null) {
|
|
182
|
+
const last = days[days.length - 1];
|
|
183
|
+
return Array.from({ length: targetCount }, (_, i) => {
|
|
184
|
+
const stamp = (last + step * (i + 1)) * DAY;
|
|
185
|
+
return dates.iso ? new Date(stamp).toISOString().slice(0, 10) : new Date(stamp);
|
|
186
|
+
});
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
// -------- 1) Numbers: the linear trend (2+ values) -----------------
|
|
190
|
+
// Excel's AutoFill of two or more numbers continues their least-squares
|
|
191
|
+
// line: 1, 2, 3 goes on 4, 5, 6, and 1, 2, 4 goes on 5.33, 6.83, ...
|
|
192
|
+
// rather than repeating. An even step is the same line, so the common
|
|
193
|
+
// case is unchanged.
|
|
156
194
|
const nums = sourceValues.map((v) => Number(v));
|
|
157
195
|
const allNumericLike = sourceValues.every((v) => v !== null && v !== '' && v !== undefined) &&
|
|
158
196
|
nums.every((n) => Number.isFinite(n));
|
|
@@ -162,6 +200,20 @@ export function buildFillPattern(sourceValues, targetCount) {
|
|
|
162
200
|
const last = nums[nums.length - 1];
|
|
163
201
|
return Array.from({ length: targetCount }, (_, i) => last + step * (i + 1));
|
|
164
202
|
}
|
|
203
|
+
const n = nums.length;
|
|
204
|
+
const meanX = (n - 1) / 2;
|
|
205
|
+
const meanY = nums.reduce((a, b) => a + b, 0) / n;
|
|
206
|
+
let sxy = 0;
|
|
207
|
+
let sxx = 0;
|
|
208
|
+
for (let i = 0; i < n; i += 1) {
|
|
209
|
+
sxy += (i - meanX) * (nums[i] - meanY);
|
|
210
|
+
sxx += (i - meanX) ** 2;
|
|
211
|
+
}
|
|
212
|
+
const slope = sxx === 0 ? 0 : sxy / sxx;
|
|
213
|
+
const intercept = meanY - slope * meanX;
|
|
214
|
+
// Rounded to the precision floating point gives 5.333333333333333, so
|
|
215
|
+
// 10, 20, 40 fills 50, 60 and not 50.00000000000001.
|
|
216
|
+
return Array.from({ length: targetCount }, (_, i) => Number((intercept + slope * (n + i)).toPrecision(15)));
|
|
165
217
|
}
|
|
166
218
|
// -------- 2) Known string sequence (days / months / quarters) -----
|
|
167
219
|
const allStrings = sourceValues.every((v) => typeof v === 'string');
|
|
@@ -179,7 +231,9 @@ export function buildFillPattern(sourceValues, targetCount) {
|
|
|
179
231
|
if (allStrings && sourceValues.length >= 1) {
|
|
180
232
|
const stringSources = sourceValues;
|
|
181
233
|
const split = findPrefixNumberSuffix(stringSources);
|
|
182
|
-
|
|
234
|
+
// A bare number as text ('5') is a number, not 'Item 5': one of them
|
|
235
|
+
// copies, as a numeric 5 does, and two or more were a progression above.
|
|
236
|
+
if (split && (split.prefix !== '' || split.suffix !== '')) {
|
|
183
237
|
const step = split.numbers.length >= 2
|
|
184
238
|
? arithmeticStep(split.numbers) ?? 1
|
|
185
239
|
: 1;
|
|
@@ -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
|
+
}
|
package/dist/grid-icons.d.ts
CHANGED
|
@@ -35,8 +35,8 @@ export type GridIconName = 'sort' | 'sort-asc' | 'sort-desc' | 'filter' | 'menu'
|
|
|
35
35
|
export type GridIcons = Partial<Record<GridIconName, Snippet>>;
|
|
36
36
|
/**
|
|
37
37
|
* Icons whose built-in form is a character rather than an SVG path. Kept as
|
|
38
|
-
* data so `<SvGrid>`, its footer and the
|
|
39
|
-
* the same defaults instead of three copies drifting apart.
|
|
38
|
+
* data so `<SvGrid>`, its footer and the row-group panel in @svgrid/enterprise
|
|
39
|
+
* render the same defaults instead of three copies drifting apart.
|
|
40
40
|
*
|
|
41
41
|
* Each is the exact character that shipped inline before, so a grid that sets
|
|
42
42
|
* no `icons` renders byte-for-byte what it always did.
|
package/dist/grid-icons.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* Icons whose built-in form is a character rather than an SVG path. Kept as
|
|
3
|
-
* data so `<SvGrid>`, its footer and the
|
|
4
|
-
* the same defaults instead of three copies drifting apart.
|
|
3
|
+
* data so `<SvGrid>`, its footer and the row-group panel in @svgrid/enterprise
|
|
4
|
+
* render the same defaults instead of three copies drifting apart.
|
|
5
5
|
*
|
|
6
6
|
* Each is the exact character that shipped inline before, so a grid that sets
|
|
7
7
|
* no `icons` renders byte-for-byte what it always did.
|
package/dist/grid-messages.d.ts
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
|
+
import type { GridChartPanelMessages } from './chart-panel-messages.js';
|
|
2
|
+
/**
|
|
3
|
+
* Every string `localization.text` accepts: the grid chrome's own
|
|
4
|
+
* ({@link GridChromeMessages}) and the chart panel's
|
|
5
|
+
* ({@link GridChartPanelMessages}).
|
|
6
|
+
*/
|
|
7
|
+
export type GridMessages = GridChromeMessages & GridChartPanelMessages;
|
|
1
8
|
/** Every localizable chrome string, grouped by area in the comments. */
|
|
2
|
-
export type
|
|
9
|
+
export type GridChromeMessages = {
|
|
3
10
|
noRows: string;
|
|
4
11
|
loading: string;
|
|
5
12
|
columns: string;
|
|
@@ -67,17 +74,23 @@ export type GridMessages = {
|
|
|
67
74
|
bulkEditCancel: string;
|
|
68
75
|
bulkEditNoFields: string;
|
|
69
76
|
chartRange: string;
|
|
77
|
+
rowLoadFailed: string;
|
|
78
|
+
rowLoadRetry: string;
|
|
70
79
|
pivotUpsellTitle: string;
|
|
71
80
|
pivotUpsellBody: string;
|
|
81
|
+
ganttUpsellTitle: string;
|
|
82
|
+
ganttUpsellBody: string;
|
|
83
|
+
/** Placeholder in the Gantt view's search box. */
|
|
84
|
+
ganttSearchPlaceholder: string;
|
|
72
85
|
/** Shared licensing line under every Enterprise upsell note. */
|
|
73
86
|
upsellLicense: string;
|
|
74
87
|
upsellLicenseLink: string;
|
|
75
88
|
};
|
|
76
89
|
/** English defaults - the literal strings the grid shipped before localization. */
|
|
77
|
-
export declare const defaultGridMessages:
|
|
90
|
+
export declare const defaultGridMessages: GridChromeMessages;
|
|
78
91
|
/**
|
|
79
92
|
* Merge a consumer's `localeText` over the English defaults. Undefined/empty
|
|
80
93
|
* values fall back to the default (via `resolveMessages`), so a partial map only
|
|
81
94
|
* replaces the keys it sets.
|
|
82
95
|
*/
|
|
83
|
-
export declare function resolveGridMessages(overrides?: Partial<GridMessages> | null):
|
|
96
|
+
export declare function resolveGridMessages(overrides?: Partial<GridMessages> | null): GridChromeMessages;
|
package/dist/grid-messages.js
CHANGED
|
@@ -7,6 +7,11 @@
|
|
|
7
7
|
*
|
|
8
8
|
* This mirrors the editor kit's `resolveMessages` pattern (editor-contract.ts) so
|
|
9
9
|
* chrome and editors share one localization idiom.
|
|
10
|
+
*
|
|
11
|
+
* The chart panel's strings are the other half of `GridMessages`
|
|
12
|
+
* (`GridChartPanelMessages` in chart-panel-messages.ts): same map, same
|
|
13
|
+
* `localization.text`, but their English defaults load with the panel, not
|
|
14
|
+
* with every grid.
|
|
10
15
|
*/
|
|
11
16
|
import { resolveMessages } from './editor-contract.js';
|
|
12
17
|
/** English defaults - the literal strings the grid shipped before localization. */
|
|
@@ -79,8 +84,13 @@ export const defaultGridMessages = {
|
|
|
79
84
|
opBefore: 'Before',
|
|
80
85
|
opAfter: 'After',
|
|
81
86
|
chartRange: 'Chart selected range',
|
|
87
|
+
rowLoadFailed: 'Could not load these rows.',
|
|
88
|
+
rowLoadRetry: 'Retry',
|
|
82
89
|
pivotUpsellTitle: 'Pivot mode',
|
|
83
90
|
pivotUpsellBody: 'Pivot mode is an Enterprise feature. Install @svgrid/enterprise and call enablePivot() to use it.',
|
|
91
|
+
ganttUpsellTitle: 'Gantt view',
|
|
92
|
+
ganttUpsellBody: 'The Gantt view is an Enterprise feature. Install @svgrid/enterprise and call enableGanttView() to render it.',
|
|
93
|
+
ganttSearchPlaceholder: 'Search tasks...',
|
|
84
94
|
upsellLicense: 'A license key is required for production use. Without one the feature still works, but the grid renders an unlicensed watermark.',
|
|
85
95
|
upsellLicenseLink: 'View licensing',
|
|
86
96
|
};
|