@rowsncolumns/rnc-engine 0.10.132
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/LICENSE.md +100 -0
- package/README.md +83 -0
- package/dist/esm/command-handlers.d.ts +169 -0
- package/dist/esm/command-handlers.d.ts.map +1 -0
- package/dist/esm/command-handlers.js +404 -0
- package/dist/esm/fold.d.ts +140 -0
- package/dist/esm/fold.d.ts.map +1 -0
- package/dist/esm/fold.js +539 -0
- package/dist/esm/index.d.ts +620 -0
- package/dist/esm/index.d.ts.map +1 -0
- package/dist/esm/index.js +425 -0
- package/dist/esm/io.d.ts +119 -0
- package/dist/esm/io.d.ts.map +1 -0
- package/dist/esm/io.js +318 -0
- package/dist/esm/keys.d.ts +20 -0
- package/dist/esm/keys.d.ts.map +1 -0
- package/dist/esm/keys.js +41 -0
- package/dist/esm/move-rescope.d.ts +18 -0
- package/dist/esm/move-rescope.d.ts.map +1 -0
- package/dist/esm/move-rescope.js +38 -0
- package/dist/esm/use-engine-core.d.ts +293 -0
- package/dist/esm/use-engine-core.d.ts.map +1 -0
- package/dist/esm/use-engine-core.js +2279 -0
- package/dist/esm/use-engine-pivot-editor.d.ts +78 -0
- package/dist/esm/use-engine-pivot-editor.d.ts.map +1 -0
- package/dist/esm/use-engine-pivot-editor.js +252 -0
- package/dist/esm/use-spreadsheet-engine.d.ts +7 -0
- package/dist/esm/use-spreadsheet-engine.d.ts.map +1 -0
- package/dist/esm/use-spreadsheet-engine.js +23 -0
- package/dist/esm/use-spreadsheet-ui.d.ts +39 -0
- package/dist/esm/use-spreadsheet-ui.d.ts.map +1 -0
- package/dist/esm/use-spreadsheet-ui.js +78 -0
- package/dist/esm/wasm/rnc_wasm.d.ts +394 -0
- package/dist/esm/wasm/rnc_wasm.js +1455 -0
- package/dist/esm/wasm/rnc_wasm_bg.wasm +0 -0
- package/dist/esm/wasm/rnc_wasm_bg.wasm.d.ts +81 -0
- package/package.json +51 -0
|
@@ -0,0 +1,404 @@
|
|
|
1
|
+
/** Two-digit zero-pad for the ISO date/time literals below. */
|
|
2
|
+
const pad2 = (n) => String(n).padStart(2, "0");
|
|
3
|
+
/** The Rust merge treats an absent patch key as "leave unchanged", and JSON.stringify drops a
|
|
4
|
+
* present-but-undefined key — so a toolbar toggle-off (`{ baselineOffset: undefined }`) would
|
|
5
|
+
* reach the engine as an empty patch and silently no-op. Present-but-nullish `baselineOffset` is
|
|
6
|
+
* the UI's clear intent; translate it to the engine's explicit-clear sentinel (`"none"`, which
|
|
7
|
+
* `TextFormat::merge` collapses back to unset) before the JSON boundary. */
|
|
8
|
+
const normalizeFormatPatch = (cellFormat) => {
|
|
9
|
+
if (!cellFormat || Array.isArray(cellFormat))
|
|
10
|
+
return cellFormat;
|
|
11
|
+
const tf = cellFormat.textFormat;
|
|
12
|
+
if (tf && "baselineOffset" in tf && tf.baselineOffset == null) {
|
|
13
|
+
return {
|
|
14
|
+
...cellFormat,
|
|
15
|
+
textFormat: { ...tf, baselineOffset: "none" },
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
return cellFormat;
|
|
19
|
+
};
|
|
20
|
+
/** Local calendar date as ISO `YYYY-MM-DD` — locale-independent, so the engine infers it as a date. */
|
|
21
|
+
const isoDate = (d) => `${d.getFullYear()}-${pad2(d.getMonth() + 1)}-${pad2(d.getDate())}`;
|
|
22
|
+
/** Local wall-clock as `HH:MM:SS` — the engine infers it as a time. */
|
|
23
|
+
const isoTime = (d) => `${pad2(d.getHours())}:${pad2(d.getMinutes())}:${pad2(d.getSeconds())}`;
|
|
24
|
+
/**
|
|
25
|
+
* Build the {@link EngineSpreadsheetHandlers} over a single `onCommand` sink. Pure (no React) —
|
|
26
|
+
* `useEngineCore` wraps this in a `useMemo([onCommand])`.
|
|
27
|
+
*/
|
|
28
|
+
export function createCommandHandlers(onCommand) {
|
|
29
|
+
return {
|
|
30
|
+
// Cell values
|
|
31
|
+
onChange: (sheetId, coords, value, textFormatRuns) => onCommand({ command: "set-value", sheetId, coords, value, textFormatRuns }),
|
|
32
|
+
onChangeBatch: (sheetId, ranges, values, formatting, citations, options) => onCommand({ command: "change-batch", sheetId, ranges, values, formatting, citations, options }),
|
|
33
|
+
onDelete: (sheetId, activeCell, selections, clearType) => onCommand({ command: "delete-cells", sheetId, activeCell, selections, clearType }),
|
|
34
|
+
// `SS["onFill"]` is async (returns `Promise<void>`); the command is emitted synchronously.
|
|
35
|
+
onFill: async (sheetId, activeCell, fillSelection, selections, fillableSelections, fillType) => {
|
|
36
|
+
onCommand({ command: "fill", sheetId, activeCell, fillSelection, selections, fillableSelections, fillType });
|
|
37
|
+
},
|
|
38
|
+
// Range fill (CanvasGrid's Cmd+D / Cmd+R shortcuts, plus UP/LEFT for callers that send them).
|
|
39
|
+
// Mirrors `useOnFillRange`: from the `direction`, derive the SOURCE range to repeat — the
|
|
40
|
+
// adjacent row/column beyond the selection for a single-line selection, else the selection's own
|
|
41
|
+
// leading (DOWN/RIGHT) or trailing (UP/LEFT) line — then emit the `fill` command with the
|
|
42
|
+
// selection as the target. `fillType` stays "FillCopy" (Excel's Ctrl+D copies; series detection
|
|
43
|
+
// is reserved for fill-handle drags, which arrive via `onFill` and auto-detect in the engine).
|
|
44
|
+
// `String(direction)` avoids the grid-vs-common-types `Direction` enum identity mismatch while
|
|
45
|
+
// comparing the shared "DOWN"/"UP"/"RIGHT"/"LEFT" values.
|
|
46
|
+
onFillRange: (sheetId, activeCell, selections, direction) => {
|
|
47
|
+
const fillSelections = selections.length
|
|
48
|
+
? selections
|
|
49
|
+
: [
|
|
50
|
+
{
|
|
51
|
+
range: {
|
|
52
|
+
startRowIndex: activeCell.rowIndex,
|
|
53
|
+
endRowIndex: activeCell.rowIndex,
|
|
54
|
+
startColumnIndex: activeCell.columnIndex,
|
|
55
|
+
endColumnIndex: activeCell.columnIndex,
|
|
56
|
+
},
|
|
57
|
+
},
|
|
58
|
+
];
|
|
59
|
+
const firstSel = fillSelections[0];
|
|
60
|
+
const dir = String(direction);
|
|
61
|
+
if (dir === "DOWN") {
|
|
62
|
+
const isSingleRow = firstSel.range.startRowIndex === firstSel.range.endRowIndex;
|
|
63
|
+
if (isSingleRow && firstSel.range.startRowIndex <= 1)
|
|
64
|
+
return;
|
|
65
|
+
const source = fillSelections.map((sel) => isSingleRow
|
|
66
|
+
? {
|
|
67
|
+
...sel,
|
|
68
|
+
range: {
|
|
69
|
+
...sel.range,
|
|
70
|
+
startRowIndex: sel.range.startRowIndex - 1,
|
|
71
|
+
endRowIndex: sel.range.endRowIndex - 1,
|
|
72
|
+
},
|
|
73
|
+
}
|
|
74
|
+
: {
|
|
75
|
+
...sel,
|
|
76
|
+
range: { ...sel.range, endRowIndex: sel.range.startRowIndex },
|
|
77
|
+
});
|
|
78
|
+
onCommand({
|
|
79
|
+
command: "fill",
|
|
80
|
+
sheetId,
|
|
81
|
+
activeCell,
|
|
82
|
+
fillSelection: fillSelections[0],
|
|
83
|
+
selections: source,
|
|
84
|
+
fillableSelections: selections,
|
|
85
|
+
fillType: "FillCopy",
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
else if (dir === "UP") {
|
|
89
|
+
const isSingleRow = firstSel.range.startRowIndex === firstSel.range.endRowIndex;
|
|
90
|
+
const source = fillSelections.map((sel) => isSingleRow
|
|
91
|
+
? {
|
|
92
|
+
...sel,
|
|
93
|
+
range: {
|
|
94
|
+
...sel.range,
|
|
95
|
+
startRowIndex: sel.range.startRowIndex + 1,
|
|
96
|
+
endRowIndex: sel.range.endRowIndex + 1,
|
|
97
|
+
},
|
|
98
|
+
}
|
|
99
|
+
: {
|
|
100
|
+
...sel,
|
|
101
|
+
range: { ...sel.range, startRowIndex: sel.range.endRowIndex },
|
|
102
|
+
});
|
|
103
|
+
onCommand({
|
|
104
|
+
command: "fill",
|
|
105
|
+
sheetId,
|
|
106
|
+
activeCell,
|
|
107
|
+
fillSelection: fillSelections[0],
|
|
108
|
+
selections: source,
|
|
109
|
+
fillableSelections: fillSelections,
|
|
110
|
+
fillType: "FillCopy",
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
else if (dir === "RIGHT") {
|
|
114
|
+
const isSingleCol = firstSel.range.startColumnIndex === firstSel.range.endColumnIndex;
|
|
115
|
+
if (isSingleCol && firstSel.range.startColumnIndex <= 1)
|
|
116
|
+
return;
|
|
117
|
+
const source = fillSelections.map((sel) => isSingleCol
|
|
118
|
+
? {
|
|
119
|
+
...sel,
|
|
120
|
+
range: {
|
|
121
|
+
...sel.range,
|
|
122
|
+
startColumnIndex: sel.range.startColumnIndex - 1,
|
|
123
|
+
endColumnIndex: sel.range.endColumnIndex - 1,
|
|
124
|
+
},
|
|
125
|
+
}
|
|
126
|
+
: {
|
|
127
|
+
...sel,
|
|
128
|
+
range: {
|
|
129
|
+
...sel.range,
|
|
130
|
+
endColumnIndex: sel.range.startColumnIndex,
|
|
131
|
+
},
|
|
132
|
+
});
|
|
133
|
+
onCommand({
|
|
134
|
+
command: "fill",
|
|
135
|
+
sheetId,
|
|
136
|
+
activeCell,
|
|
137
|
+
fillSelection: fillSelections[0],
|
|
138
|
+
selections: source,
|
|
139
|
+
fillableSelections: fillSelections,
|
|
140
|
+
fillType: "FillCopy",
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
else if (dir === "LEFT") {
|
|
144
|
+
const isSingleCol = firstSel.range.startColumnIndex === firstSel.range.endColumnIndex;
|
|
145
|
+
const source = fillSelections.map((sel) => isSingleCol
|
|
146
|
+
? {
|
|
147
|
+
...sel,
|
|
148
|
+
range: {
|
|
149
|
+
...sel.range,
|
|
150
|
+
startColumnIndex: sel.range.startColumnIndex + 1,
|
|
151
|
+
endColumnIndex: sel.range.endColumnIndex + 1,
|
|
152
|
+
},
|
|
153
|
+
}
|
|
154
|
+
: {
|
|
155
|
+
...sel,
|
|
156
|
+
range: {
|
|
157
|
+
...sel.range,
|
|
158
|
+
startColumnIndex: sel.range.endColumnIndex,
|
|
159
|
+
},
|
|
160
|
+
});
|
|
161
|
+
onCommand({
|
|
162
|
+
command: "fill",
|
|
163
|
+
sheetId,
|
|
164
|
+
activeCell,
|
|
165
|
+
fillSelection: fillSelections[0],
|
|
166
|
+
selections: source,
|
|
167
|
+
fillableSelections: fillSelections,
|
|
168
|
+
fillType: "FillCopy",
|
|
169
|
+
});
|
|
170
|
+
}
|
|
171
|
+
},
|
|
172
|
+
// `SS` types `selections`/`delimiter` as optional; default to a no-op-safe shape for the command.
|
|
173
|
+
onSplitTextToColumns: (sheetId, activeCell, selections, delimiter) => onCommand({
|
|
174
|
+
command: "split-text-to-columns",
|
|
175
|
+
sheetId,
|
|
176
|
+
activeCell,
|
|
177
|
+
selections: selections ?? [],
|
|
178
|
+
delimiter: delimiter ?? "",
|
|
179
|
+
}),
|
|
180
|
+
// `SS["onRemoveDuplicates"]` is `(sheetId, activeCell, selections?)` — no `columnIndices` (the
|
|
181
|
+
// engine dedups over the selection's columns). The reference returns `RemoveDuplicatesResult`
|
|
182
|
+
// (duplicate/unique row counts) computed inline; here the dedupe runs inside the wasm engine
|
|
183
|
+
// behind the fire-and-forget `onCommand` sink and `CommitResult` carries no counts channel, so
|
|
184
|
+
// the emitter reports `undefined` — the "counts unavailable" arm the SS contract allows.
|
|
185
|
+
onRemoveDuplicates: (sheetId, activeCell, selections) => {
|
|
186
|
+
onCommand({
|
|
187
|
+
command: "remove-duplicates",
|
|
188
|
+
sheetId,
|
|
189
|
+
activeCell,
|
|
190
|
+
selections: selections ?? [],
|
|
191
|
+
});
|
|
192
|
+
return undefined;
|
|
193
|
+
},
|
|
194
|
+
onFindReplace: (ranges, find, replace, matchCase, wholeWord, useRegex) => onCommand({ command: "find-replace", ranges, find, replace, matchCase, wholeWord, useRegex }),
|
|
195
|
+
// Formatting — overloaded (`SS["onChangeFormatting"]`): a string 4th arg is the UI `(type, value)`
|
|
196
|
+
// form with `options` 6th; otherwise it's a CellFormat (the toolbar form) with `options` 5th (in
|
|
197
|
+
// the `value` slot). `options` rides the command — `{ replace: true }` is the format-painter
|
|
198
|
+
// semantics (the pattern fully replaces the target's format instead of merging onto it).
|
|
199
|
+
onChangeFormatting: (sheetId, activeCell, selections, cellFormat, value, options) => onCommand({
|
|
200
|
+
command: "change-formatting",
|
|
201
|
+
sheetId,
|
|
202
|
+
activeCell,
|
|
203
|
+
selections,
|
|
204
|
+
cellFormat: normalizeFormatPatch(typeof cellFormat === "string"
|
|
205
|
+
? { [cellFormat]: value }
|
|
206
|
+
: cellFormat),
|
|
207
|
+
options: (typeof cellFormat === "string"
|
|
208
|
+
? options
|
|
209
|
+
: value),
|
|
210
|
+
}),
|
|
211
|
+
onChangeBorder: (sheetId, activeCell, selections, location, color, style) => onCommand({ command: "change-border", sheetId, activeCell, selections, location, color, style }),
|
|
212
|
+
onClearFormatting: (sheetId, activeCell, selections) => onCommand({ command: "clear-formatting", sheetId, activeCell, selections }),
|
|
213
|
+
onChangeDecimals: (sheetId, activeCell, selections, type) => onCommand({ command: "change-decimals", sheetId, activeCell, selections, type }),
|
|
214
|
+
onIncreaseIndent: (sheetId, activeCell, selections) => onCommand({ command: "increase-indent", sheetId, activeCell, selections }),
|
|
215
|
+
onDecreaseIndent: (sheetId, activeCell, selections) => onCommand({ command: "decrease-indent", sheetId, activeCell, selections }),
|
|
216
|
+
onChangeIndent: (sheetId, activeCell, selections, shouldIncrease) => onCommand({ command: "change-indent", sheetId, activeCell, selections, shouldIncrease }),
|
|
217
|
+
// Insert the current date/time: the HOST stamps the clock (the literal travels in the command, so
|
|
218
|
+
// it's deterministic for undo/replay); the ENGINE infers the serial + applies the date/time format.
|
|
219
|
+
onInsertDate: (sheetId, activeCell) => onCommand({
|
|
220
|
+
command: "set-value",
|
|
221
|
+
sheetId,
|
|
222
|
+
coords: activeCell,
|
|
223
|
+
value: isoDate(new Date()),
|
|
224
|
+
textFormatRuns: undefined,
|
|
225
|
+
}),
|
|
226
|
+
onInsertTime: (sheetId, activeCell) => onCommand({
|
|
227
|
+
command: "set-value",
|
|
228
|
+
sheetId,
|
|
229
|
+
coords: activeCell,
|
|
230
|
+
value: isoTime(new Date()),
|
|
231
|
+
textFormatRuns: undefined,
|
|
232
|
+
}),
|
|
233
|
+
onInsertDateTime: (sheetId, activeCell) => {
|
|
234
|
+
const d = new Date();
|
|
235
|
+
onCommand({
|
|
236
|
+
command: "set-value",
|
|
237
|
+
sheetId,
|
|
238
|
+
coords: activeCell,
|
|
239
|
+
value: `${isoDate(d)} ${isoTime(d)}`,
|
|
240
|
+
textFormatRuns: undefined,
|
|
241
|
+
});
|
|
242
|
+
},
|
|
243
|
+
// AutoSum: the engine scans the off-window numeric run and writes `=FN(range)` into the cell.
|
|
244
|
+
onInsertAutoSum: (sheetId, activeCell, functionName) => onCommand({ command: "insert-autosum", sheetId, activeCell, functionName }),
|
|
245
|
+
onMergeCells: (sheetId, activeCell, selections, direction) => onCommand({ command: "merge-cells", sheetId, activeCell, selections, direction }),
|
|
246
|
+
onUnMergeCells: (sheetId, activeCell, selections) => onCommand({ command: "unmerge-cells", sheetId, activeCell, selections }),
|
|
247
|
+
// Rows & Columns
|
|
248
|
+
onInsertRow: (sheetId, referenceRowIndex, numRows) => onCommand({ command: "insert-row", sheetId, referenceRowIndex, numRows }),
|
|
249
|
+
onInsertColumn: (sheetId, referenceColumnIndex, numColumns) => onCommand({ command: "insert-column", sheetId, referenceColumnIndex, numColumns }),
|
|
250
|
+
onDeleteRow: (sheetId, rowIndexes) => onCommand({ command: "delete-row", sheetId, rowIndexes }),
|
|
251
|
+
onDeleteColumn: (sheetId, columnIndexes) => onCommand({ command: "delete-column", sheetId, columnIndexes }),
|
|
252
|
+
onMoveRows: (sheetId, rows, referenceRowIndex) => onCommand({ command: "move-rows", sheetId, rows, referenceRowIndex }),
|
|
253
|
+
onMoveColumns: (sheetId, columns, referenceColumnIndex) => onCommand({ command: "move-columns", sheetId, columns, referenceColumnIndex }),
|
|
254
|
+
onMoveSelection: (sheetId, from, to) => onCommand({ command: "move-range", sheetId, from: from.range, to: to.range }),
|
|
255
|
+
onHideRow: (sheetId, rowIndexes) => onCommand({ command: "hide-row", sheetId, rowIndexes }),
|
|
256
|
+
onShowRow: (sheetId, rowIndexes) => onCommand({ command: "show-row", sheetId, rowIndexes }),
|
|
257
|
+
onHideColumn: (sheetId, columnIndexes) => onCommand({ command: "hide-column", sheetId, columnIndexes }),
|
|
258
|
+
onShowColumn: (sheetId, columnIndexes) => onCommand({ command: "show-column", sheetId, columnIndexes }),
|
|
259
|
+
onGroupRows: (sheetId, rowIndexes) => onCommand({ command: "group-rows", sheetId, rowIndexes }),
|
|
260
|
+
onUngroupRows: (sheetId, rowIndexes) => onCommand({ command: "ungroup-rows", sheetId, rowIndexes }),
|
|
261
|
+
onCollapseRowGroup: (sheetId, summaryRowIndex) => onCommand({ command: "collapse-row-group", sheetId, summaryRowIndex }),
|
|
262
|
+
onExpandRowGroup: (sheetId, summaryRowIndex) => onCommand({ command: "expand-row-group", sheetId, summaryRowIndex }),
|
|
263
|
+
onGroupColumns: (sheetId, columnIndexes) => onCommand({ command: "group-columns", sheetId, columnIndexes }),
|
|
264
|
+
onUngroupColumns: (sheetId, columnIndexes) => onCommand({ command: "ungroup-columns", sheetId, columnIndexes }),
|
|
265
|
+
onCollapseColumnGroup: (sheetId, summaryColumnIndex) => onCommand({ command: "collapse-column-group", sheetId, summaryColumnIndex }),
|
|
266
|
+
onExpandColumnGroup: (sheetId, summaryColumnIndex) => onCommand({ command: "expand-column-group", sheetId, summaryColumnIndex }),
|
|
267
|
+
onSetOutlineDepth: (sheetId, axis, depth) => onCommand({ command: "set-outline-depth", sheetId, axis, depth }),
|
|
268
|
+
onResize: (sheetId, indexes, dimension, axis, autoFit) => onCommand({ command: "resize", sheetId, indexes, dimension, axis, autoFit }),
|
|
269
|
+
onAutoResize: (sheetId, indexes, axis, sizes) => onCommand({ command: "auto-resize", sheetId, indexes, axis, sizes }),
|
|
270
|
+
onFreezeRow: (sheetId, rowIndex) => onCommand({ command: "freeze-row", sheetId, rowIndex }),
|
|
271
|
+
onFreezeColumn: (sheetId, columnIndex) => onCommand({ command: "freeze-column", sheetId, columnIndex }),
|
|
272
|
+
onInsertCellsShiftDown: (sheetId, activeCell, selections) => onCommand({ command: "insert-cells-shift-down", sheetId, activeCell, selections }),
|
|
273
|
+
onInsertCellsShiftRight: (sheetId, activeCell, selections) => onCommand({ command: "insert-cells-shift-right", sheetId, activeCell, selections }),
|
|
274
|
+
onDeleteCellsShiftUp: (sheetId, activeCell, selections) => onCommand({ command: "delete-cells-shift-up", sheetId, activeCell, selections }),
|
|
275
|
+
onDeleteCellsShiftLeft: (sheetId, activeCell, selections) => onCommand({ command: "delete-cells-shift-left", sheetId, activeCell, selections }),
|
|
276
|
+
// Sheets — grid form returns the created Sheet | undefined (engine creates async → undefined).
|
|
277
|
+
onCreateNewSheet: (sheetSpec) => {
|
|
278
|
+
onCommand({ command: "create-sheet", sheetSpec });
|
|
279
|
+
return undefined;
|
|
280
|
+
},
|
|
281
|
+
onDeleteSheet: (sheetId) => onCommand({ command: "delete-sheet", sheetId }),
|
|
282
|
+
onRenameSheet: (sheetId, name, oldName) => onCommand({ command: "rename-sheet", sheetId, name, oldName }),
|
|
283
|
+
onMoveSheet: (sheetId, currentPosition, newPosition) => onCommand({ command: "move-sheet", sheetId, currentPosition, newPosition }),
|
|
284
|
+
onDuplicateSheet: (sheetId) => onCommand({ command: "duplicate-sheet", sheetId }),
|
|
285
|
+
onHideSheet: (sheetId) => onCommand({ command: "hide-sheet", sheetId }),
|
|
286
|
+
onShowSheet: (sheetId) => onCommand({ command: "show-sheet", sheetId }),
|
|
287
|
+
onChangeSheetTabColor: (sheetId, color) => onCommand({ command: "change-sheet-tab-color", sheetId, color }),
|
|
288
|
+
// Sort & Filter
|
|
289
|
+
onSortColumn: (sheetId, sortedColumnIndex, sortOrder) => onCommand({ command: "sort-column", sheetId, sortedColumnIndex, sortOrder }),
|
|
290
|
+
onSortRange: (sheetId, selections, sortOrder) => onCommand({ command: "sort-range", sheetId, selections, sortOrder }),
|
|
291
|
+
onSortTable: (sheetId, filter, dimensionIndex, sortOrder) => onCommand({ command: "sort-table", sheetId, filter, dimensionIndex, sortOrder }),
|
|
292
|
+
onFilterTable: (sheetId, filter, columnIndex, conditionType, conditionValue, visibleValues, extras) => onCommand({
|
|
293
|
+
command: "filter-table",
|
|
294
|
+
sheetId,
|
|
295
|
+
filter,
|
|
296
|
+
columnIndex,
|
|
297
|
+
conditionType,
|
|
298
|
+
conditionValue,
|
|
299
|
+
visibleValues,
|
|
300
|
+
// `undefined` keys drop out of the JSON command (carry the existing criterion forward);
|
|
301
|
+
// `null` survives as the explicit clear marker. Mirrors `createFilterSpecs` extras.
|
|
302
|
+
colorFilter: extras?.colorFilter,
|
|
303
|
+
iconFilter: extras?.iconFilter,
|
|
304
|
+
}),
|
|
305
|
+
onDeleteFilter: (sheetId, filter, columnIndex) => onCommand({ command: "delete-filter", sheetId, filter, columnIndex }),
|
|
306
|
+
onCreateBasicFilter: (sheetId, activeCell, selections, basicFilterSpec) => onCommand({ command: "create-basic-filter", sheetId, activeCell, selections, basicFilterSpec }),
|
|
307
|
+
// Tables
|
|
308
|
+
onCreateTable: (sheetId, activeCell, selections, tableSpec, theme, bandedRange) => onCommand({ command: "create-table", sheetId, activeCell, selections, tableSpec, theme, bandedRange }),
|
|
309
|
+
onRemoveTable: (tableView) => onCommand({ command: "remove-table", tableView }),
|
|
310
|
+
onUpdateTable: (sheetId, tableId, updatedTable) => onCommand({ command: "update-table", sheetId, tableId, updatedTable }),
|
|
311
|
+
onResizeTable: (sheetId, table, range) => onCommand({ command: "resize-table", sheetId, table, range }),
|
|
312
|
+
onInsertTableRow: (tableView, relativeRowIndex, rowData) => onCommand({ command: "insert-table-row", tableView, relativeRowIndex, rowData }),
|
|
313
|
+
onDeleteTableRow: (tableView, rowIndexes) => onCommand({ command: "delete-table-row", tableView, rowIndexes }),
|
|
314
|
+
onInsertTableColumn: (tableView, dimensionIndex, direction) => onCommand({ command: "insert-table-column", tableView, dimensionIndex, direction }),
|
|
315
|
+
onDeleteTableColumn: (tableView, dimensionIndex) => onCommand({ command: "delete-table-column", tableView, dimensionIndex }),
|
|
316
|
+
onMoveTableColumns: (sheetId, tableId, columns, referenceColumnIndex) => onCommand({ command: "move-table-columns", sheetId, tableId, columns, referenceColumnIndex }),
|
|
317
|
+
onChangeTableColumn: (sheetId, tableId, columnIndex, name) => onCommand({ command: "change-table-column", sheetId, tableId, columnIndex, name }),
|
|
318
|
+
onCalculatedColumn: (sheetId, tableId, columnIndex, formula, fromRowIndex) => onCommand({ command: "calculated-column", sheetId, tableId, columnIndex, formula, fromRowIndex }),
|
|
319
|
+
// Named Ranges
|
|
320
|
+
onCreateNamedRange: (sheetId, namedRange) => onCommand({ command: "create-named-range", sheetId, namedRange }),
|
|
321
|
+
onUpdateNamedRange: (sheetId, namedRange, previousNamedRange) => onCommand({ command: "update-named-range", sheetId, namedRange, previousNamedRange }),
|
|
322
|
+
onDeleteNamedRange: (namedRangeId) => onCommand({ command: "delete-named-range", namedRangeId }),
|
|
323
|
+
// Notes & special inserts
|
|
324
|
+
onUpdateNote: (sheetId, activeCell, note) => onCommand({ command: "update-note", sheetId, activeCell, note }),
|
|
325
|
+
onInsertLink: (sheetId, activeCell, selections, url, title) => onCommand({ command: "insert-link", sheetId, activeCell, selections, url, title }),
|
|
326
|
+
onRemoveLink: (sheetId, activeCell, selections) => onCommand({ command: "remove-link", sheetId, activeCell, selections }),
|
|
327
|
+
onInsertImage: (sheetId, activeCell, imageUrl, width, height, insertOverCells, embedId) => onCommand({ command: "insert-image", sheetId, activeCell, imageUrl, width, height, insertOverCells, embedId }),
|
|
328
|
+
onInsertCheckbox: (sheetId, activeCell, selections) => onCommand({ command: "insert-checkbox", sheetId, activeCell, selections }),
|
|
329
|
+
// Comments (only the host-managed thread id rides on the cell; content is the host's)
|
|
330
|
+
onInsertComment: (sheetId, rowIndex, columnIndex, commentThreadId) => onCommand({ command: "insert-comment", sheetId, rowIndex, columnIndex, commentThreadId }),
|
|
331
|
+
onRemoveComment: (sheetId, rowIndex, columnIndex) => onCommand({ command: "remove-comment", sheetId, rowIndex, columnIndex }),
|
|
332
|
+
// Citations (collection add + per-cell `citationId` link; remove-from-cell keeps the collection)
|
|
333
|
+
onCreateCitation: (sheetId, rowIndex, columnIndex, citation) => onCommand({ command: "create-citation", sheetId, rowIndex, columnIndex, citation }),
|
|
334
|
+
onUpdateCitation: (citationId, updates) => onCommand({ command: "update-citation", citationId, updates }),
|
|
335
|
+
onDeleteCitation: (citationId) => onCommand({ command: "delete-citation", citationId }),
|
|
336
|
+
onRemoveCitationFromCell: (sheetId, rowIndex, columnIndex) => onCommand({ command: "remove-citation-from-cell", sheetId, rowIndex, columnIndex }),
|
|
337
|
+
// Conditional Formatting (onCreateConditionalFormattingRule is hook-exposed — reads active sheet).
|
|
338
|
+
// These take the SAME rule-object args as `useSpreadsheetState`; the engine pulls `id` off the rule
|
|
339
|
+
// and emits the id-based command (the wire shape stays id-based; only the call surface matches SS).
|
|
340
|
+
// `previousRule` isn't needed — the engine re-derives the affected ranges from the committed rule set.
|
|
341
|
+
onUpdateConditionalFormattingRule: (updatedRule) => onCommand({
|
|
342
|
+
command: "update-conditional-format",
|
|
343
|
+
ruleId: updatedRule.id,
|
|
344
|
+
rule: updatedRule,
|
|
345
|
+
}),
|
|
346
|
+
onDeleteConditionalFormattingRule: (rule) => onCommand({ command: "delete-conditional-format", ruleId: rule.id }),
|
|
347
|
+
// Live preview: overlay the candidate rule in the engine (no persist, no undo). `null` clears it.
|
|
348
|
+
onPreviewConditionalFormattingRule: (rule) => onCommand({ command: "preview-conditional-format", rule: rule ?? null }),
|
|
349
|
+
// Data Validations — rule-object API (matches SS). The rule carries its own `ranges`; derive the
|
|
350
|
+
// target sheet + range from `ranges[0]` (the editor always supplies at least one range). `options`
|
|
351
|
+
// / `previousRule` are unused — the engine re-derives validity for every covered cell.
|
|
352
|
+
onCreateDataValidationRule: (rule) => {
|
|
353
|
+
const range = rule.ranges?.[0];
|
|
354
|
+
if (!range)
|
|
355
|
+
return;
|
|
356
|
+
onCommand({
|
|
357
|
+
command: "create-data-validation",
|
|
358
|
+
sheetId: range.sheetId,
|
|
359
|
+
range,
|
|
360
|
+
rule,
|
|
361
|
+
});
|
|
362
|
+
},
|
|
363
|
+
onUpdateDataValidationRule: (rule) => onCommand({ command: "update-data-validation", ruleId: rule.id, rule }),
|
|
364
|
+
onDeleteDataValidationRule: (rule) => onCommand({ command: "delete-data-validation", ruleId: rule.id }),
|
|
365
|
+
// Banded Ranges
|
|
366
|
+
onAddBandedRange: (sheetId, bandedRange) => onCommand({ command: "add-banded-range", sheetId, bandedRange }),
|
|
367
|
+
onRemoveBandedRange: (sheetId, bandedRange) => onCommand({ command: "remove-banded-range", sheetId, bandedRange }),
|
|
368
|
+
// Protected Ranges
|
|
369
|
+
onProtectRange: (sheetId, activeCell, selections) => onCommand({ command: "protect-range", sheetId, activeCell, selections }),
|
|
370
|
+
onUnProtectRange: (sheetId, protectedRangeId) => onCommand({ command: "unprotect-range", sheetId, protectedRangeId }),
|
|
371
|
+
onProtectSheet: (sheetId) => onCommand({ command: "protect-sheet", sheetId }),
|
|
372
|
+
onUnProtectSheet: (sheetId) => onCommand({ command: "unprotect-sheet", sheetId }),
|
|
373
|
+
// Embeds
|
|
374
|
+
onCreateEmbed: (spec) => onCommand({ command: "create-embed", spec }),
|
|
375
|
+
onUpdateEmbed: (embed) => onCommand({ command: "update-embed", embed }),
|
|
376
|
+
onMoveEmbed: (embedId, sheetId, anchorCell, offsetXPixels, offsetYPixels) => onCommand({ command: "move-embed", embedId, sheetId, anchorCell, offsetXPixels, offsetYPixels }),
|
|
377
|
+
onResizeEmbed: (embedId, sheetId, anchorCell, offsetXPixels, offsetYPixels, width, height) => onCommand({ command: "resize-embed", embedId, sheetId, anchorCell, offsetXPixels, offsetYPixels, width, height }),
|
|
378
|
+
onDeleteEmbed: (embedId) => onCommand({ command: "delete-embed", embedId }),
|
|
379
|
+
// Slicers
|
|
380
|
+
onCreateSlicer: (slicer) => onCommand({ command: "create-slicer", slicer }),
|
|
381
|
+
onUpdateSlicer: (slicer) => onCommand({ command: "update-slicer", slicer }),
|
|
382
|
+
onMoveSlicer: (slicerId, sheetId, anchorCell, offsetXPixels, offsetYPixels) => onCommand({ command: "move-slicer", slicerId, sheetId, anchorCell, offsetXPixels, offsetYPixels }),
|
|
383
|
+
onResizeSlicer: (slicerId, sheetId, anchorCell, offsetXPixels, offsetYPixels, width, height) => onCommand({ command: "resize-slicer", slicerId, sheetId, anchorCell, offsetXPixels, offsetYPixels, width, height }),
|
|
384
|
+
onDeleteSlicer: (slicerId) => onCommand({ command: "delete-slicer", slicerId }),
|
|
385
|
+
// Charts (onMoveChart / onResizeChart are hook-exposed — they read the active sheet from view state)
|
|
386
|
+
onCreateChart: (spec) => onCommand({ command: "create-chart", spec }),
|
|
387
|
+
onUpdateChart: (chart) => onCommand({ command: "update-chart", chart }),
|
|
388
|
+
onDeleteChart: (chartId) => onCommand({ command: "delete-chart", chartId }),
|
|
389
|
+
// Pivot tables
|
|
390
|
+
onCreatePivotTable: (pivotTable) => onCommand({ command: "create-pivot-table", pivotTable }),
|
|
391
|
+
onUpdatePivotTable: (pivotId, pivotTable) => onCommand({ command: "update-pivot-table", pivotId, pivotTable }),
|
|
392
|
+
onDeletePivotTable: (pivotId) => onCommand({ command: "delete-pivot-table", pivotId }),
|
|
393
|
+
onRefreshPivot: (pivotId) => onCommand({ command: "refresh-pivot", pivotId }),
|
|
394
|
+
onExpandPivotRow: (pivotId, groupKeys) => onCommand({ command: "expand-pivot-row", pivotId, groupKeys }),
|
|
395
|
+
onCollapsePivotRow: (pivotId, groupKeys) => onCommand({ command: "collapse-pivot-row", pivotId, groupKeys }),
|
|
396
|
+
onExpandPivotColumn: (pivotId, path) => onCommand({ command: "expand-pivot-column", pivotId, path }),
|
|
397
|
+
onCollapsePivotColumn: (pivotId, path) => onCommand({ command: "collapse-pivot-column", pivotId, path }),
|
|
398
|
+
onDrillDownPivot: (pivotId, filters, cell) => onCommand({ command: "drill-down-pivot", pivotId, filters, cell }),
|
|
399
|
+
// Theme / Locale
|
|
400
|
+
onChangeSpreadsheetTheme: (theme) => onCommand({ command: "change-theme", theme }),
|
|
401
|
+
// The 2nd arg (`previousLocale`) is unnecessary: the engine re-derives it from current state.
|
|
402
|
+
onChangeLocale: (locale) => onCommand({ command: "change-locale", locale }),
|
|
403
|
+
};
|
|
404
|
+
}
|
|
@@ -0,0 +1,140 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Pure reducers that fold the engine's {@link CommitResult} channels into the minimal render state a
|
|
3
|
+
* thin "Option B" UI needs — no calc, no dependency graph, no immer. The engine is the authority; the
|
|
4
|
+
* UI just applies these deltas. {@link useEngineCore} wraps these in React state; they're kept
|
|
5
|
+
* pure (and exported) so they're unit-testable without a renderer or the wasm module.
|
|
6
|
+
*/
|
|
7
|
+
import type { CellData, CellFormat, SheetData } from "@rowsncolumns/common-types";
|
|
8
|
+
import type { EmbeddedChart, PivotTable, Sheet } from "@rowsncolumns/spreadsheet";
|
|
9
|
+
import type { CellChange, CommitResult, DimensionChange, EngineSheet, MergeChange, NoteEntry, StyleEntry, ValidationStatus, WindowCell, WindowSnapshot } from "./index";
|
|
10
|
+
/** Everything a thin renderer keeps in sync from the engine. Values live in `sheetData`; styles,
|
|
11
|
+
* CF-derived styles, validity, notes, and objects live in their own maps (the same split the engine
|
|
12
|
+
* streams on). The canonical `Sheet` carries the lifecycle/dimension metadata the reducers fold into. */
|
|
13
|
+
export interface EngineRenderState {
|
|
14
|
+
sheets: Sheet[];
|
|
15
|
+
sheetData: SheetData<CellData>;
|
|
16
|
+
/** `Map<"sheetId!A1", CellFormat>` — a cell's own consolidated style. */
|
|
17
|
+
cellStyles: Map<string, CellFormat>;
|
|
18
|
+
/** `Map<"sheetId!A1", CellFormat>` — render-only DERIVED formats: a formula cell's INHERITED
|
|
19
|
+
* number format (`=SUM($a,$b)` → currency), streamed by the engine and layered UNDER the user's
|
|
20
|
+
* own style in `getEffectiveFormat`. Never persisted (distinct from `cellStyles`); mirrors the JS
|
|
21
|
+
* reference's `getDerivedFormat`, but single-sourced in the engine so it survives a backend move. */
|
|
22
|
+
derivedFormats: Map<string, CellFormat>;
|
|
23
|
+
/** `Map<"sheetId!A1", note>`. */
|
|
24
|
+
notes: Map<string, string>;
|
|
25
|
+
/** Chart / pivot specs by id (the canonical EmbeddedChart / PivotTable, round-tripped by the engine). */
|
|
26
|
+
charts: Map<EmbeddedChart["chartId"], EmbeddedChart>;
|
|
27
|
+
pivotTables: Map<PivotTable["pivotId"], PivotTable>;
|
|
28
|
+
}
|
|
29
|
+
/** An empty render state (seed with your initial sheets). */
|
|
30
|
+
export declare function emptyRenderState(sheets?: Sheet[]): EngineRenderState;
|
|
31
|
+
/** Set one cell (1-indexed), creating the sparse row/column slots immutably. */
|
|
32
|
+
export declare function setCell(prev: SheetData<CellData>, sheetId: number, row: number, col: number, mut: (cell: CellData) => CellData): SheetData<CellData>;
|
|
33
|
+
/** Apply one cell change. The engine streams the cell VERBATIM (`ue` input, `ev` computed value incl.
|
|
34
|
+
* `ev.ev` = errorValue, `fv` formatted, …) — JS is view-only and reconstructs nothing. We merge it
|
|
35
|
+
* over the prior cell so the RESULT fields that OTHER channels own — `dataValidationResult` and
|
|
36
|
+
* `conditionalFormattingResultById`, which never ride on the value channel — survive a value edit
|
|
37
|
+
* (the value fields are always present on `ch.cell`, so they're replaced wholesale). `cell: null`
|
|
38
|
+
* clears the value (drop `ue`/`ev`/`fv`), leaving those channel-owned result fields for the
|
|
39
|
+
* validations channel to re-emit if the cleared cell's validity flips. */
|
|
40
|
+
export declare function applyChange(cell: CellData, ch: CellChange): CellData;
|
|
41
|
+
/** Stream the engine's cell change-set into `sheetData`. */
|
|
42
|
+
export declare function applyChanges(prev: SheetData<CellData>, changes: CellChange[]): SheetData<CellData>;
|
|
43
|
+
/** Fold a style change-set into a consolidated `Map<"sheetId!A1", CellFormat>` (`format: null` =
|
|
44
|
+
* cleared → drop). Used for both own styles (`styleChanges`) and CF-derived styles. */
|
|
45
|
+
export declare function foldStyles(prev: Map<string, CellFormat>, styles: StyleEntry[]): Map<string, CellFormat>;
|
|
46
|
+
/** Fold data-validation results ONTO each cell's `dataValidationResult` (the same place
|
|
47
|
+
* `useSpreadsheetState` keeps it) — `false` = the value violates a rule. No separate invalid set. */
|
|
48
|
+
export declare function foldDataValidationResults(prev: SheetData<CellData>, vals: ValidationStatus[]): SheetData<CellData>;
|
|
49
|
+
/** Fold per-cell note deltas (`note: null` = cleared). */
|
|
50
|
+
export declare function foldNotes(prev: Map<string, string>, notes: NoteEntry[]): Map<string, string>;
|
|
51
|
+
/** Fold id-keyed object spec deltas (charts / pivots; `null` spec = deleted). */
|
|
52
|
+
export declare function foldObjects<T>(prev: Map<string | number, T>, deltas: {
|
|
53
|
+
id: string | number;
|
|
54
|
+
spec: T | null;
|
|
55
|
+
}[]): Map<string | number, T>;
|
|
56
|
+
/** Fold row/column metadata deltas into each sheet's `rowMetadata`/`columnMetadata`. */
|
|
57
|
+
export declare function applyDimensions(prev: Sheet[], rowChanges?: DimensionChange[], columnChanges?: DimensionChange[]): Sheet[];
|
|
58
|
+
/** Fold merge-region deltas into each sheet's `merges` (added appended, `removed` dropped by bounds). */
|
|
59
|
+
export declare function applyMerges(prev: Sheet[], merges?: MergeChange[]): Sheet[];
|
|
60
|
+
/** Reconcile the render-state sheet list with the engine's AUTHORITATIVE sheet set + order + titles
|
|
61
|
+
* (from {@link RncEngine.sheets}). The engine owns sheet lifecycle — it generates ids for
|
|
62
|
+
* `create-sheet`, rewrites titles on rename, reorders on move, drops on delete — so the tab list is
|
|
63
|
+
* reconciled from it rather than guessed from the command (a `create-sheet` doesn't even carry the
|
|
64
|
+
* generated id). Per-sheet DIMENSION/MERGE metadata is preserved by reusing the existing `Sheet`
|
|
65
|
+
* object for a surviving id (those facets ride the commit channels, folded by {@link foldCommit});
|
|
66
|
+
* only the identity facets (`title`/`index`/`hidden`/`tabColor`) are taken from the engine.
|
|
67
|
+
*
|
|
68
|
+
* Returns the SAME `prev` array reference when nothing changed (every id, order, title, hidden,
|
|
69
|
+
* tabColor matches and each surviving sheet object is reused) — so a pure cell edit never churns the
|
|
70
|
+
* `sheets` prop. An empty `engineSheets` (a store that doesn't expose `sheets()`) leaves `prev` as-is. */
|
|
71
|
+
export declare function reconcileSheets(prev: Sheet[], engineSheets: EngineSheet[]): Sheet[];
|
|
72
|
+
/** Reconstruct a renderer-shaped {@link CellData} from a lean {@link WindowCell}. `readWindow` is a
|
|
73
|
+
* RENDER window — it carries what to paint (`formatted` text + `number` + `isFormula`), not the full
|
|
74
|
+
* cell. We surface:
|
|
75
|
+
* • `fv` — the formatted display string (what the grid draws),
|
|
76
|
+
* • `ev` — the effective value as a scalar (`nv` for numbers, else `sv` for text),
|
|
77
|
+
* • `ue` — a best-effort user-entered value reconstructed from the lean fields (no formula source
|
|
78
|
+
* here — literals carry their number/text).
|
|
79
|
+
* This LOSSY reconstruction is the FALLBACK for engines without `readWindowCommit`; the primary path
|
|
80
|
+
* (`RncEngine.readWindow` over `readWindowCommit`) carries each cell VERBATIM (`cell`), so `ue.fv`
|
|
81
|
+
* (formula source), hyperlink, note, and the full `ev` survive. See `windowToSheetData`. */
|
|
82
|
+
export declare function windowCellToCellData(c: WindowCell): CellData;
|
|
83
|
+
/** Replace a sheet's windowed cells with the snapshot's cells (1-indexed, sparse). Everything OUTSIDE
|
|
84
|
+
* the snapshot is evicted — only the window's rows/columns are retained, so JS memory stays bounded
|
|
85
|
+
* to the viewport regardless of sheet size. */
|
|
86
|
+
export declare function windowToSheetData(prev: SheetData<CellData>, snapshot: WindowSnapshot): SheetData<CellData>;
|
|
87
|
+
/** Rebuild the consolidated style map for a windowed sheet from the snapshot's `styles`. Entries for
|
|
88
|
+
* the snapshot's sheet are dropped first (eviction), then the window's are set — so a stale style
|
|
89
|
+
* outside the viewport can't linger. Styles for OTHER sheets are preserved. */
|
|
90
|
+
export declare function windowToStyles(prev: Map<string, CellFormat>, snapshot: WindowSnapshot): Map<string, CellFormat>;
|
|
91
|
+
/** Fold a {@link WindowSnapshot} into render state — the windowed analog of {@link foldCommit}. Holds
|
|
92
|
+
* ONLY the snapshot's cells + styles for its sheet (evicting the prior window), leaving the sheet
|
|
93
|
+
* lifecycle/dimension metadata (`sheets`) and other sheets untouched. The single entry point a
|
|
94
|
+
* windowed UI calls on scroll and after each commit. */
|
|
95
|
+
export declare function windowToDerivedFormats(prev: Map<string, CellFormat>, snapshot: WindowSnapshot): Map<string, CellFormat>;
|
|
96
|
+
export declare function foldWindow(prev: EngineRenderState, snapshot: WindowSnapshot): EngineRenderState;
|
|
97
|
+
/** Fold SEVERAL window snapshots of the SAME sheet in one pass — the viewport window plus its frozen
|
|
98
|
+
* top/left/corner bands (see {@link frozenBandRanges}). The sheet's prior cells/styles are evicted
|
|
99
|
+
* ONCE, then every snapshot's cells/styles are merged in — folding the snapshots one-by-one through
|
|
100
|
+
* {@link foldWindow} would instead evict each band as the next one lands.
|
|
101
|
+
*
|
|
102
|
+
* IDENTITY-PRESERVING: a windowed read rebuilds every cell/format as a fresh object even when its
|
|
103
|
+
* content is unchanged (the window is re-read after EVERY commit, including remote echoes and
|
|
104
|
+
* edits outside the viewport). Downstream, new identities defeat React memoization and trip the
|
|
105
|
+
* grid's renderer-identity cache invalidation — a full cold re-derive of the viewport on every
|
|
106
|
+
* commit. So the fold reuses the previous state's objects at every level where content matches:
|
|
107
|
+
* cell → row → whole sheet array → style maps → the state object itself. A fold whose window
|
|
108
|
+
* content is identical to the previous state returns `prev` unchanged, which lets React bail out
|
|
109
|
+
* of the setState entirely. */
|
|
110
|
+
export declare function foldWindows(prev: EngineRenderState, snapshots: WindowSnapshot[]): EngineRenderState;
|
|
111
|
+
/** A windowed read's buffered range — 1-indexed inclusive engine coordinates. */
|
|
112
|
+
export type WindowRange = {
|
|
113
|
+
sheetId: number;
|
|
114
|
+
startRow: number;
|
|
115
|
+
startCol: number;
|
|
116
|
+
endRow: number;
|
|
117
|
+
endCol: number;
|
|
118
|
+
};
|
|
119
|
+
/** The frozen-pane bands a windowed read must ALWAYS include alongside the viewport window: the top
|
|
120
|
+
* band (frozen rows × the window's columns), the left band (the window's rows × frozen columns), and
|
|
121
|
+
* the top-left corner (frozen rows × frozen columns). Frozen panes stay on screen at every scroll
|
|
122
|
+
* position, so their cells must never be evicted by the window fold — without these bands, scrolling
|
|
123
|
+
* past the frozen rows/columns blanks them. Bands already covered by the window (its edge sits at
|
|
124
|
+
* row/column 1) are omitted; a window starting INSIDE the frozen span only needs the uncovered part.
|
|
125
|
+
* Band spans are clamped to {@link MAX_FROZEN_BAND_ROWS}/{@link MAX_FROZEN_BAND_COLS}, and non-finite
|
|
126
|
+
* or fractional counts (a corrupted doc plane) are floored — `NaN` disables the band. */
|
|
127
|
+
export declare function frozenBandRanges(w: WindowRange, frozenRowCount: number, frozenColumnCount: number): WindowRange[];
|
|
128
|
+
/** Fold a {@link WindowSnapshot} companion {@link CommitResult}, keeping the channels a windowed
|
|
129
|
+
* sheet can't re-read from the window: structural metadata (dimensions / merges) AND the whole-document
|
|
130
|
+
* object channels (charts / pivots). In windowed mode a commit's CELL changes are intentionally NOT
|
|
131
|
+
* accumulated (the window is re-read instead); but row/column metadata + merges, and charts + pivots,
|
|
132
|
+
* are not part of the window read (and `documentFacets` excludes charts), so they must fold here or a
|
|
133
|
+
* windowed sheet silently drops them — e.g. charts/pivots an xlsx import adds. Cells/styles are left
|
|
134
|
+
* for {@link foldWindow} to refresh. */
|
|
135
|
+
export declare function foldCommitStructuralOnly(prev: EngineRenderState, commit: CommitResult): EngineRenderState;
|
|
136
|
+
/** Fold a whole {@link CommitResult} into the render state — the single entry point a thin UI calls
|
|
137
|
+
* for every engine change-set (edit, undo/redo, load). Returns a new state (referentially changed
|
|
138
|
+
* only where a channel had deltas). */
|
|
139
|
+
export declare function foldCommit(prev: EngineRenderState, commit: CommitResult): EngineRenderState;
|
|
140
|
+
//# sourceMappingURL=fold.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"fold.d.ts","sourceRoot":"","sources":["../../src/fold.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,OAAO,KAAK,EACV,QAAQ,EACR,UAAU,EAEV,SAAS,EACV,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EACV,aAAa,EACb,UAAU,EACV,KAAK,EACN,MAAM,2BAA2B,CAAC;AAEnC,OAAO,KAAK,EACV,UAAU,EACV,YAAY,EACZ,eAAe,EACf,WAAW,EACX,WAAW,EACX,SAAS,EACT,UAAU,EACV,gBAAgB,EAChB,UAAU,EACV,cAAc,EACf,MAAM,SAAS,CAAC;AAGjB;;yGAEyG;AACzG,MAAM,WAAW,iBAAiB;IAChC,MAAM,EAAE,KAAK,EAAE,CAAC;IAChB,SAAS,EAAE,SAAS,CAAC,QAAQ,CAAC,CAAC;IAC/B,yEAAyE;IACzE,UAAU,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IACpC;;;yGAGqG;IACrG,cAAc,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;IAKxC,iCAAiC;IACjC,KAAK,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAC3B,yGAAyG;IACzG,MAAM,EAAE,GAAG,CAAC,aAAa,CAAC,SAAS,CAAC,EAAE,aAAa,CAAC,CAAC;IACrD,WAAW,EAAE,GAAG,CAAC,UAAU,CAAC,SAAS,CAAC,EAAE,UAAU,CAAC,CAAC;CACrD;AAED,6DAA6D;AAC7D,wBAAgB,gBAAgB,CAAC,MAAM,GAAE,KAAK,EAAO,GAAG,iBAAiB,CAUxE;AAED,gFAAgF;AAChF,wBAAgB,OAAO,CACrB,IAAI,EAAE,SAAS,CAAC,QAAQ,CAAC,EACzB,OAAO,EAAE,MAAM,EACf,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,MAAM,EACX,GAAG,EAAE,CAAC,IAAI,EAAE,QAAQ,KAAK,QAAQ,GAChC,SAAS,CAAC,QAAQ,CAAC,CAQrB;AAED;;;;;;0EAM0E;AAC1E,wBAAgB,WAAW,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE,EAAE,UAAU,GAAG,QAAQ,CAiCpE;AAED,4DAA4D;AAC5D,wBAAgB,YAAY,CAC1B,IAAI,EAAE,SAAS,CAAC,QAAQ,CAAC,EACzB,OAAO,EAAE,UAAU,EAAE,GACpB,SAAS,CAAC,QAAQ,CAAC,CAQrB;AAED;uFACuF;AACvF,wBAAgB,UAAU,CACxB,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,EAC7B,MAAM,EAAE,UAAU,EAAE,GACnB,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CASzB;AAED;qGACqG;AACrG,wBAAgB,yBAAyB,CACvC,IAAI,EAAE,SAAS,CAAC,QAAQ,CAAC,EACzB,IAAI,EAAE,gBAAgB,EAAE,GACvB,SAAS,CAAC,QAAQ,CAAC,CAUrB;AAED,0DAA0D;AAC1D,wBAAgB,SAAS,CACvB,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,EACzB,KAAK,EAAE,SAAS,EAAE,GACjB,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,CASrB;AAED,iFAAiF;AACjF,wBAAgB,WAAW,CAAC,CAAC,EAC3B,IAAI,EAAE,GAAG,CAAC,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC,EAC7B,MAAM,EAAE;IAAE,EAAE,EAAE,MAAM,GAAG,MAAM,CAAC;IAAC,IAAI,EAAE,CAAC,GAAG,IAAI,CAAA;CAAE,EAAE,GAChD,GAAG,CAAC,MAAM,GAAG,MAAM,EAAE,CAAC,CAAC,CAQzB;AAED,wFAAwF;AACxF,wBAAgB,eAAe,CAC7B,IAAI,EAAE,KAAK,EAAE,EACb,UAAU,GAAE,eAAe,EAAO,EAClC,aAAa,GAAE,eAAe,EAAO,GACpC,KAAK,EAAE,CAoBT;AAED,yGAAyG;AACzG,wBAAgB,WAAW,CAAC,IAAI,EAAE,KAAK,EAAE,EAAE,MAAM,GAAE,WAAW,EAAO,GAAG,KAAK,EAAE,CAwB9E;AAED;;;;;;;;;;0GAU0G;AAC1G,wBAAgB,eAAe,CAC7B,IAAI,EAAE,KAAK,EAAE,EACb,YAAY,EAAE,WAAW,EAAE,GAC1B,KAAK,EAAE,CA4CT;AAUD;;;;;;;;;4FAS4F;AAC5F,wBAAgB,oBAAoB,CAAC,CAAC,EAAE,UAAU,GAAG,QAAQ,CAa5D;AAED;;+CAE+C;AAC/C,wBAAgB,iBAAiB,CAC/B,IAAI,EAAE,SAAS,CAAC,QAAQ,CAAC,EACzB,QAAQ,EAAE,cAAc,GACvB,SAAS,CAAC,QAAQ,CAAC,CAarB;AAED;;+EAE+E;AAC/E,wBAAgB,cAAc,CAC5B,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,EAC7B,QAAQ,EAAE,cAAc,GACvB,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAUzB;AAED;;;wDAGwD;AACxD,wBAAgB,sBAAsB,CACpC,IAAI,EAAE,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,EAC7B,QAAQ,EAAE,cAAc,GACvB,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,CAUzB;AAED,wBAAgB,UAAU,CACxB,IAAI,EAAE,iBAAiB,EACvB,QAAQ,EAAE,cAAc,GACvB,iBAAiB,CAEnB;AAsBD;;;;;;;;;;;;+BAY+B;AAC/B,wBAAgB,WAAW,CACzB,IAAI,EAAE,iBAAiB,EACvB,SAAS,EAAE,cAAc,EAAE,GAC1B,iBAAiB,CA2FnB;AAED,iFAAiF;AACjF,MAAM,MAAM,WAAW,GAAG;IACxB,OAAO,EAAE,MAAM,CAAC;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;CAChB,CAAC;AAaF;;;;;;;yFAOyF;AACzF,wBAAgB,gBAAgB,CAC9B,CAAC,EAAE,WAAW,EACd,cAAc,EAAE,MAAM,EACtB,iBAAiB,EAAE,MAAM,GACxB,WAAW,EAAE,CAqCf;AAED;;;;;;wCAMwC;AACxC,wBAAgB,wBAAwB,CACtC,IAAI,EAAE,iBAAiB,EACvB,MAAM,EAAE,YAAY,GACnB,iBAAiB,CA8BnB;AAED;;uCAEuC;AACvC,wBAAgB,UAAU,CACxB,IAAI,EAAE,iBAAiB,EACvB,MAAM,EAAE,YAAY,GACnB,iBAAiB,CA8BnB"}
|