@rowsncolumns/spreadsheet-state 28.0.75 → 29.0.0
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/dist/cjs/api/anchored-objects.d.ts +66 -0
- package/dist/cjs/api/anchored-objects.d.ts.map +1 -0
- package/dist/cjs/api/anchored-objects.js +259 -0
- package/dist/cjs/api/copy-paste.d.ts +8 -1
- package/dist/cjs/api/copy-paste.d.ts.map +1 -1
- package/dist/cjs/api/copy-paste.js +4 -2
- package/dist/cjs/api/dependency.d.ts +13 -2
- package/dist/cjs/api/dependency.d.ts.map +1 -1
- package/dist/cjs/api/dependency.js +11 -5
- package/dist/cjs/api/formula.d.ts +61 -4
- package/dist/cjs/api/formula.d.ts.map +1 -1
- package/dist/cjs/api/formula.js +250 -61
- package/dist/cjs/api/immer-utils.d.ts +11 -1
- package/dist/cjs/api/immer-utils.d.ts.map +1 -1
- package/dist/cjs/api/immer-utils.js +17 -1
- package/dist/cjs/api/insert-cut-cells.d.ts +35 -0
- package/dist/cjs/api/insert-cut-cells.d.ts.map +1 -0
- package/dist/cjs/api/insert-cut-cells.js +60 -0
- package/dist/cjs/api/merges.d.ts +16 -0
- package/dist/cjs/api/merges.d.ts.map +1 -1
- package/dist/cjs/api/merges.js +75 -1
- package/dist/cjs/api/named-range-structural.d.ts +62 -0
- package/dist/cjs/api/named-range-structural.d.ts.map +1 -0
- package/dist/cjs/api/named-range-structural.js +197 -0
- package/dist/cjs/api/range-rule-helpers.d.ts +45 -1
- package/dist/cjs/api/range-rule-helpers.d.ts.map +1 -1
- package/dist/cjs/api/range-rule-helpers.js +159 -6
- package/dist/cjs/api/structural-formula-update.d.ts.map +1 -1
- package/dist/cjs/api/structural-formula-update.js +24 -6
- package/dist/cjs/api/structural-guards.d.ts +78 -0
- package/dist/cjs/api/structural-guards.d.ts.map +1 -0
- package/dist/cjs/api/structural-guards.js +202 -0
- package/dist/cjs/api/structural-table-refs.d.ts +94 -0
- package/dist/cjs/api/structural-table-refs.d.ts.map +1 -0
- package/dist/cjs/api/structural-table-refs.js +313 -0
- package/dist/cjs/api/subtotal.d.ts +20 -13
- package/dist/cjs/api/subtotal.d.ts.map +1 -1
- package/dist/cjs/api/subtotal.js +26 -19
- package/dist/cjs/commands.d.ts +9 -0
- package/dist/cjs/commands.d.ts.map +1 -1
- package/dist/cjs/constants.d.ts +25 -0
- package/dist/cjs/constants.d.ts.map +1 -0
- package/dist/cjs/constants.js +26 -0
- package/dist/cjs/hooks/use-calculation-worker.d.ts.map +1 -1
- package/dist/cjs/hooks/use-calculation-worker.js +64 -6
- package/dist/cjs/hooks/use-calculation.d.ts +7 -0
- package/dist/cjs/hooks/use-calculation.d.ts.map +1 -1
- package/dist/cjs/hooks/use-calculation.js +15 -0
- package/dist/cjs/hooks/use-on-calculation-success.d.ts +7 -0
- package/dist/cjs/hooks/use-on-calculation-success.d.ts.map +1 -1
- package/dist/cjs/hooks/use-on-calculation-success.js +7 -1
- package/dist/cjs/hooks/use-on-copy.d.ts +1 -1
- package/dist/cjs/hooks/use-on-copy.d.ts.map +1 -1
- package/dist/cjs/hooks/use-on-copy.js +2 -3
- package/dist/cjs/hooks/use-on-delete-cells-shift-left.d.ts +13 -3
- package/dist/cjs/hooks/use-on-delete-cells-shift-left.d.ts.map +1 -1
- package/dist/cjs/hooks/use-on-delete-cells-shift-left.js +27 -2
- package/dist/cjs/hooks/use-on-delete-cells-shift-up.d.ts +13 -3
- package/dist/cjs/hooks/use-on-delete-cells-shift-up.d.ts.map +1 -1
- package/dist/cjs/hooks/use-on-delete-cells-shift-up.js +27 -2
- package/dist/cjs/hooks/use-on-delete-column.d.ts +13 -4
- package/dist/cjs/hooks/use-on-delete-column.d.ts.map +1 -1
- package/dist/cjs/hooks/use-on-delete-column.js +14 -2
- package/dist/cjs/hooks/use-on-delete-row.d.ts +13 -4
- package/dist/cjs/hooks/use-on-delete-row.d.ts.map +1 -1
- package/dist/cjs/hooks/use-on-delete-row.js +14 -2
- package/dist/cjs/hooks/use-on-insert-cells-shift-down.d.ts +13 -3
- package/dist/cjs/hooks/use-on-insert-cells-shift-down.d.ts.map +1 -1
- package/dist/cjs/hooks/use-on-insert-cells-shift-down.js +27 -2
- package/dist/cjs/hooks/use-on-insert-cells-shift-right.d.ts +13 -3
- package/dist/cjs/hooks/use-on-insert-cells-shift-right.d.ts.map +1 -1
- package/dist/cjs/hooks/use-on-insert-cells-shift-right.js +27 -2
- package/dist/cjs/hooks/use-on-insert-column.d.ts +10 -3
- package/dist/cjs/hooks/use-on-insert-column.d.ts.map +1 -1
- package/dist/cjs/hooks/use-on-insert-column.js +33 -7
- package/dist/cjs/hooks/use-on-insert-row.d.ts +10 -3
- package/dist/cjs/hooks/use-on-insert-row.d.ts.map +1 -1
- package/dist/cjs/hooks/use-on-insert-row.js +32 -7
- package/dist/cjs/hooks/use-on-move-columns.d.ts +2 -1
- package/dist/cjs/hooks/use-on-move-columns.d.ts.map +1 -1
- package/dist/cjs/hooks/use-on-move-columns.js +14 -5
- package/dist/cjs/hooks/use-on-move-rows.d.ts +16 -1
- package/dist/cjs/hooks/use-on-move-rows.d.ts.map +1 -1
- package/dist/cjs/hooks/use-on-move-rows.js +30 -5
- package/dist/cjs/hooks/use-on-move-selection.d.ts +7 -2
- package/dist/cjs/hooks/use-on-move-selection.d.ts.map +1 -1
- package/dist/cjs/hooks/use-on-move-selection.js +14 -1
- package/dist/cjs/hooks/use-on-paste.d.ts +12 -2
- package/dist/cjs/hooks/use-on-paste.d.ts.map +1 -1
- package/dist/cjs/hooks/use-on-paste.js +56 -8
- package/dist/cjs/hooks/use-spreadsheet-state.d.ts +16 -5
- package/dist/cjs/hooks/use-spreadsheet-state.d.ts.map +1 -1
- package/dist/cjs/hooks/use-spreadsheet-state.js +141 -18
- package/dist/cjs/hooks/use-structured-reference.d.ts.map +1 -1
- package/dist/cjs/hooks/use-structured-reference.js +10 -6
- package/dist/cjs/index.d.ts +4 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +4 -0
- package/dist/cjs/interface/calculation-interface.d.ts +9 -0
- package/dist/cjs/interface/calculation-interface.d.ts.map +1 -1
- package/dist/cjs/interface/calculation-interface.js +31 -2
- package/dist/cjs/interface/delete-cells-shift-left.d.ts +8 -1
- package/dist/cjs/interface/delete-cells-shift-left.d.ts.map +1 -1
- package/dist/cjs/interface/delete-cells-shift-left.js +158 -80
- package/dist/cjs/interface/delete-cells-shift-up.d.ts +9 -1
- package/dist/cjs/interface/delete-cells-shift-up.d.ts.map +1 -1
- package/dist/cjs/interface/delete-cells-shift-up.js +155 -73
- package/dist/cjs/interface/delete-column.d.ts +1 -1
- package/dist/cjs/interface/delete-column.d.ts.map +1 -1
- package/dist/cjs/interface/delete-column.js +109 -50
- package/dist/cjs/interface/delete-row.d.ts +1 -1
- package/dist/cjs/interface/delete-row.d.ts.map +1 -1
- package/dist/cjs/interface/delete-row.js +105 -46
- package/dist/cjs/interface/insert-cells-shift-down.d.ts +10 -1
- package/dist/cjs/interface/insert-cells-shift-down.d.ts.map +1 -1
- package/dist/cjs/interface/insert-cells-shift-down.js +147 -52
- package/dist/cjs/interface/insert-cells-shift-right.d.ts +10 -1
- package/dist/cjs/interface/insert-cells-shift-right.d.ts.map +1 -1
- package/dist/cjs/interface/insert-cells-shift-right.js +154 -58
- package/dist/cjs/interface/insert-column.d.ts +1 -1
- package/dist/cjs/interface/insert-column.d.ts.map +1 -1
- package/dist/cjs/interface/insert-column.js +80 -26
- package/dist/cjs/interface/insert-row.d.ts +1 -1
- package/dist/cjs/interface/insert-row.d.ts.map +1 -1
- package/dist/cjs/interface/insert-row.js +63 -15
- package/dist/cjs/interface/spreadsheet-interface.d.ts.map +1 -1
- package/dist/cjs/interface/spreadsheet-interface.js +33 -0
- package/dist/cjs/sheet-cell.d.ts.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/api/anchored-objects.d.ts +66 -0
- package/dist/esm/api/anchored-objects.d.ts.map +1 -0
- package/dist/esm/api/anchored-objects.js +212 -0
- package/dist/esm/api/copy-paste.d.ts +8 -1
- package/dist/esm/api/copy-paste.d.ts.map +1 -1
- package/dist/esm/api/copy-paste.js +4 -2
- package/dist/esm/api/dependency.d.ts +13 -2
- package/dist/esm/api/dependency.d.ts.map +1 -1
- package/dist/esm/api/dependency.js +10 -4
- package/dist/esm/api/formula.d.ts +61 -4
- package/dist/esm/api/formula.d.ts.map +1 -1
- package/dist/esm/api/formula.js +238 -60
- package/dist/esm/api/immer-utils.d.ts +11 -1
- package/dist/esm/api/immer-utils.d.ts.map +1 -1
- package/dist/esm/api/immer-utils.js +16 -0
- package/dist/esm/api/insert-cut-cells.d.ts +35 -0
- package/dist/esm/api/insert-cut-cells.d.ts.map +1 -0
- package/dist/esm/api/insert-cut-cells.js +67 -0
- package/dist/esm/api/merges.d.ts +16 -0
- package/dist/esm/api/merges.d.ts.map +1 -1
- package/dist/esm/api/merges.js +60 -0
- package/dist/esm/api/named-range-structural.d.ts +62 -0
- package/dist/esm/api/named-range-structural.d.ts.map +1 -0
- package/dist/esm/api/named-range-structural.js +176 -0
- package/dist/esm/api/range-rule-helpers.d.ts +45 -1
- package/dist/esm/api/range-rule-helpers.d.ts.map +1 -1
- package/dist/esm/api/range-rule-helpers.js +153 -1
- package/dist/esm/api/structural-formula-update.d.ts.map +1 -1
- package/dist/esm/api/structural-formula-update.js +24 -6
- package/dist/esm/api/structural-guards.d.ts +78 -0
- package/dist/esm/api/structural-guards.d.ts.map +1 -0
- package/dist/esm/api/structural-guards.js +186 -0
- package/dist/esm/api/structural-table-refs.d.ts +94 -0
- package/dist/esm/api/structural-table-refs.d.ts.map +1 -0
- package/dist/esm/api/structural-table-refs.js +219 -0
- package/dist/esm/api/subtotal.d.ts +20 -13
- package/dist/esm/api/subtotal.d.ts.map +1 -1
- package/dist/esm/api/subtotal.js +15 -8
- package/dist/esm/commands.d.ts +9 -0
- package/dist/esm/commands.d.ts.map +1 -1
- package/dist/esm/constants.d.ts +25 -0
- package/dist/esm/constants.d.ts.map +1 -0
- package/dist/esm/constants.js +26 -0
- package/dist/esm/hooks/use-calculation-worker.d.ts.map +1 -1
- package/dist/esm/hooks/use-calculation-worker.js +45 -0
- package/dist/esm/hooks/use-calculation.d.ts +7 -0
- package/dist/esm/hooks/use-calculation.d.ts.map +1 -1
- package/dist/esm/hooks/use-calculation.js +15 -0
- package/dist/esm/hooks/use-on-calculation-success.d.ts +7 -0
- package/dist/esm/hooks/use-on-calculation-success.d.ts.map +1 -1
- package/dist/esm/hooks/use-on-calculation-success.js +7 -1
- package/dist/esm/hooks/use-on-copy.d.ts +1 -1
- package/dist/esm/hooks/use-on-copy.d.ts.map +1 -1
- package/dist/esm/hooks/use-on-copy.js +2 -3
- package/dist/esm/hooks/use-on-delete-cells-shift-left.d.ts +13 -3
- package/dist/esm/hooks/use-on-delete-cells-shift-left.d.ts.map +1 -1
- package/dist/esm/hooks/use-on-delete-cells-shift-left.js +26 -2
- package/dist/esm/hooks/use-on-delete-cells-shift-up.d.ts +13 -3
- package/dist/esm/hooks/use-on-delete-cells-shift-up.d.ts.map +1 -1
- package/dist/esm/hooks/use-on-delete-cells-shift-up.js +26 -2
- package/dist/esm/hooks/use-on-delete-column.d.ts +13 -4
- package/dist/esm/hooks/use-on-delete-column.d.ts.map +1 -1
- package/dist/esm/hooks/use-on-delete-column.js +14 -2
- package/dist/esm/hooks/use-on-delete-row.d.ts +13 -4
- package/dist/esm/hooks/use-on-delete-row.d.ts.map +1 -1
- package/dist/esm/hooks/use-on-delete-row.js +14 -2
- package/dist/esm/hooks/use-on-insert-cells-shift-down.d.ts +13 -3
- package/dist/esm/hooks/use-on-insert-cells-shift-down.d.ts.map +1 -1
- package/dist/esm/hooks/use-on-insert-cells-shift-down.js +26 -2
- package/dist/esm/hooks/use-on-insert-cells-shift-right.d.ts +13 -3
- package/dist/esm/hooks/use-on-insert-cells-shift-right.d.ts.map +1 -1
- package/dist/esm/hooks/use-on-insert-cells-shift-right.js +26 -2
- package/dist/esm/hooks/use-on-insert-column.d.ts +10 -3
- package/dist/esm/hooks/use-on-insert-column.d.ts.map +1 -1
- package/dist/esm/hooks/use-on-insert-column.js +33 -7
- package/dist/esm/hooks/use-on-insert-row.d.ts +10 -3
- package/dist/esm/hooks/use-on-insert-row.d.ts.map +1 -1
- package/dist/esm/hooks/use-on-insert-row.js +32 -7
- package/dist/esm/hooks/use-on-move-columns.d.ts +2 -1
- package/dist/esm/hooks/use-on-move-columns.d.ts.map +1 -1
- package/dist/esm/hooks/use-on-move-columns.js +14 -5
- package/dist/esm/hooks/use-on-move-rows.d.ts +16 -1
- package/dist/esm/hooks/use-on-move-rows.d.ts.map +1 -1
- package/dist/esm/hooks/use-on-move-rows.js +30 -5
- package/dist/esm/hooks/use-on-move-selection.d.ts +7 -2
- package/dist/esm/hooks/use-on-move-selection.d.ts.map +1 -1
- package/dist/esm/hooks/use-on-move-selection.js +14 -1
- package/dist/esm/hooks/use-on-paste.d.ts +12 -2
- package/dist/esm/hooks/use-on-paste.d.ts.map +1 -1
- package/dist/esm/hooks/use-on-paste.js +41 -2
- package/dist/esm/hooks/use-spreadsheet-state.d.ts +16 -5
- package/dist/esm/hooks/use-spreadsheet-state.d.ts.map +1 -1
- package/dist/esm/hooks/use-spreadsheet-state.js +141 -17
- package/dist/esm/hooks/use-structured-reference.d.ts.map +1 -1
- package/dist/esm/hooks/use-structured-reference.js +10 -6
- package/dist/esm/index.d.ts +4 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +4 -0
- package/dist/esm/interface/calculation-interface.d.ts +9 -0
- package/dist/esm/interface/calculation-interface.d.ts.map +1 -1
- package/dist/esm/interface/calculation-interface.js +33 -2
- package/dist/esm/interface/delete-cells-shift-left.d.ts +8 -1
- package/dist/esm/interface/delete-cells-shift-left.d.ts.map +1 -1
- package/dist/esm/interface/delete-cells-shift-left.js +135 -59
- package/dist/esm/interface/delete-cells-shift-up.d.ts +9 -1
- package/dist/esm/interface/delete-cells-shift-up.d.ts.map +1 -1
- package/dist/esm/interface/delete-cells-shift-up.js +133 -53
- package/dist/esm/interface/delete-column.d.ts +1 -1
- package/dist/esm/interface/delete-column.d.ts.map +1 -1
- package/dist/esm/interface/delete-column.js +96 -38
- package/dist/esm/interface/delete-row.d.ts +1 -1
- package/dist/esm/interface/delete-row.d.ts.map +1 -1
- package/dist/esm/interface/delete-row.js +98 -39
- package/dist/esm/interface/insert-cells-shift-down.d.ts +10 -1
- package/dist/esm/interface/insert-cells-shift-down.d.ts.map +1 -1
- package/dist/esm/interface/insert-cells-shift-down.js +116 -47
- package/dist/esm/interface/insert-cells-shift-right.d.ts +10 -1
- package/dist/esm/interface/insert-cells-shift-right.d.ts.map +1 -1
- package/dist/esm/interface/insert-cells-shift-right.js +120 -51
- package/dist/esm/interface/insert-column.d.ts +1 -1
- package/dist/esm/interface/insert-column.d.ts.map +1 -1
- package/dist/esm/interface/insert-column.js +68 -18
- package/dist/esm/interface/insert-row.d.ts +1 -1
- package/dist/esm/interface/insert-row.d.ts.map +1 -1
- package/dist/esm/interface/insert-row.js +60 -14
- package/dist/esm/interface/spreadsheet-interface.d.ts.map +1 -1
- package/dist/esm/interface/spreadsheet-interface.js +29 -0
- package/dist/esm/sheet-cell.d.ts.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/package.json +18 -18
package/dist/esm/api/merges.js
CHANGED
|
@@ -1,3 +1,12 @@
|
|
|
1
|
+
import { areaInsideArea, areaIntersects } from "@rowsncolumns/grid";
|
|
2
|
+
/** Excel's refusal when a move / cut-paste / shift would tear a merged cell. */
|
|
3
|
+
export const MERGED_CELL_MOVE_ERROR = "We can't do that to a merged cell.";
|
|
4
|
+
/**
|
|
5
|
+
* The first merge that `range` covers only PARTIALLY (it intersects the range without being
|
|
6
|
+
* wholly inside it). Excel refuses any move, cut + paste or cell shift whose source or destination
|
|
7
|
+
* does that ("We can't do that to a merged cell."); merges wholly inside the range travel intact.
|
|
8
|
+
*/
|
|
9
|
+
export const findPartiallyCoveredMerge = (range, merges) => merges?.find((merge) => areaIntersects(merge, range) && !areaInsideArea(merge, range));
|
|
1
10
|
/**
|
|
2
11
|
* Move merged cells when user is inserts, deletes row and columns
|
|
3
12
|
* @param mergedCells
|
|
@@ -172,3 +181,54 @@ export const expandMergesOnColumnInsert = (merges, insertIndex, count) => {
|
|
|
172
181
|
})
|
|
173
182
|
.filter((merge) => !isSingleCellMerge(merge));
|
|
174
183
|
};
|
|
184
|
+
/**
|
|
185
|
+
* Merges that travel with an insert/delete-cells shift: a merge wholly inside the shifted band
|
|
186
|
+
* (rows `>= start` in the band's columns for a vertical shift; columns `>= start` in its rows for a
|
|
187
|
+
* horizontal one) moves by `count`; on a delete, a merge wholly inside the deleted block is dropped
|
|
188
|
+
* with it. Merges the band only partially covers are refused upstream (`findTornMerge`) — this
|
|
189
|
+
* helper never tears one, it leaves anything outside the band untouched.
|
|
190
|
+
*/
|
|
191
|
+
export const shiftMergesInBand = (merges, axis, start, count, insert, spanStart, spanEnd) => {
|
|
192
|
+
if (!merges || count <= 0) {
|
|
193
|
+
return merges;
|
|
194
|
+
}
|
|
195
|
+
const deletedEnd = start + count - 1;
|
|
196
|
+
const inSpan = axis === "row"
|
|
197
|
+
? (merge) => merge.startColumnIndex >= spanStart && merge.endColumnIndex <= spanEnd
|
|
198
|
+
: (merge) => merge.startRowIndex >= spanStart && merge.endRowIndex <= spanEnd;
|
|
199
|
+
const lineStart = (merge) => axis === "row" ? merge.startRowIndex : merge.startColumnIndex;
|
|
200
|
+
const lineEnd = (merge) => axis === "row" ? merge.endRowIndex : merge.endColumnIndex;
|
|
201
|
+
const moved = (merge, delta) => axis === "row"
|
|
202
|
+
? {
|
|
203
|
+
...merge,
|
|
204
|
+
startRowIndex: merge.startRowIndex + delta,
|
|
205
|
+
endRowIndex: merge.endRowIndex + delta,
|
|
206
|
+
}
|
|
207
|
+
: {
|
|
208
|
+
...merge,
|
|
209
|
+
startColumnIndex: merge.startColumnIndex + delta,
|
|
210
|
+
endColumnIndex: merge.endColumnIndex + delta,
|
|
211
|
+
};
|
|
212
|
+
const next = [];
|
|
213
|
+
for (const merge of merges) {
|
|
214
|
+
if (!inSpan(merge) || lineStart(merge) < start) {
|
|
215
|
+
next.push(merge);
|
|
216
|
+
continue;
|
|
217
|
+
}
|
|
218
|
+
if (insert) {
|
|
219
|
+
next.push(moved(merge, count));
|
|
220
|
+
continue;
|
|
221
|
+
}
|
|
222
|
+
if (lineEnd(merge) <= deletedEnd) {
|
|
223
|
+
// Wholly inside the deleted block — removed with it.
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
if (lineStart(merge) > deletedEnd) {
|
|
227
|
+
next.push(moved(merge, -count));
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
// Straddles the deleted block's edge: refused upstream; never tear it here.
|
|
231
|
+
next.push(merge);
|
|
232
|
+
}
|
|
233
|
+
return next;
|
|
234
|
+
};
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import type { NamedRange, SheetPrintSettings } from "@rowsncolumns/spreadsheet";
|
|
2
|
+
import type { WritableDraft } from "immer";
|
|
3
|
+
export type StructuralAxis = "row" | "column";
|
|
4
|
+
export type StructuralOp = "insert" | "delete";
|
|
5
|
+
/**
|
|
6
|
+
* The `value` a defined name carries once every cell of its range has been
|
|
7
|
+
* deleted. Excel keeps the name in the Name Manager as `=Sheet1!#REF!` and
|
|
8
|
+
* every formula that uses it evaluates to `#REF!` (not `#NAME?`), so the
|
|
9
|
+
* name survives with no range and this error marker as its value; the
|
|
10
|
+
* calculation worker's name resolver turns it into `FormulaError.REF`.
|
|
11
|
+
*/
|
|
12
|
+
export declare const NAMED_RANGE_REF_ERROR = "#REF!";
|
|
13
|
+
export declare const isRefErrorNamedRange: (namedRange: Pick<NamedRange, "range" | "value">) => boolean;
|
|
14
|
+
/**
|
|
15
|
+
* Shift the inclusive `[start, end]` band of a defined name / print band for
|
|
16
|
+
* an insert or delete of `count` rows or columns at `index` (1-based).
|
|
17
|
+
* Excel's rule for a name's RefersTo, identical to the Rust engine's
|
|
18
|
+
* `shift_named_ranges`:
|
|
19
|
+
*
|
|
20
|
+
* - insert at or above the first index → the whole band shifts;
|
|
21
|
+
* - insert strictly inside → the band grows;
|
|
22
|
+
* - insert at the boundary directly below → unchanged;
|
|
23
|
+
* - delete wholly above → shifts back; delete overlapping → shrinks by the
|
|
24
|
+
* deleted indices inside the band; delete covering the band → `null`.
|
|
25
|
+
*/
|
|
26
|
+
export declare const shiftNamedBand: (start: number, end: number, op: StructuralOp, index: number, count: number) => [number, number] | null;
|
|
27
|
+
export type NamedRangeStructuralUpdate<N extends NamedRange> = {
|
|
28
|
+
/** Names whose range moved, grew, or shrank. */
|
|
29
|
+
changed: N[];
|
|
30
|
+
/** Names whose whole range was deleted — kept, now `=#REF!`. */
|
|
31
|
+
refErrored: N[];
|
|
32
|
+
};
|
|
33
|
+
/**
|
|
34
|
+
* Adjust defined names for a sheet-level insert/delete on `sheetId`,
|
|
35
|
+
* mutating the immer `draft` in place. Only names whose `range.sheetId` is
|
|
36
|
+
* the edited sheet move — a name's scope (`scopeSheetId`) governs how it is
|
|
37
|
+
* resolved, not which sheet's edits shift it (a Sheet2-scoped name pointing
|
|
38
|
+
* at Sheet2!$B$4 shifts on Sheet2 edits, never on Sheet1 edits). Value
|
|
39
|
+
* names (`=SUM(...)*2`, `0.1`) have no grid range and pass through.
|
|
40
|
+
*
|
|
41
|
+
* A name whose entire range is deleted is NOT dropped: it keeps its entry
|
|
42
|
+
* with `range` cleared and `value: "#REF!"`, so `=SUM(Data)` evaluates to
|
|
43
|
+
* `#REF!` like Excel rather than `#NAME?`.
|
|
44
|
+
*/
|
|
45
|
+
export declare const updateNamedRangesOnStructuralChange: (draft: WritableDraft<NamedRange>[], axis: StructuralAxis, op: StructuralOp, index: number, count: number, sheetId: number) => NamedRangeStructuralUpdate<WritableDraft<NamedRange>>;
|
|
46
|
+
/**
|
|
47
|
+
* Pure variant of {@link updateNamedRangesOnStructuralChange}: returns the
|
|
48
|
+
* adjusted list (sharing untouched entries) plus the names that changed.
|
|
49
|
+
*/
|
|
50
|
+
export declare const shiftNamedRangesOnStructuralChange: <N extends NamedRange>(namedRanges: readonly N[], axis: StructuralAxis, op: StructuralOp, index: number, count: number, sheetId: number) => {
|
|
51
|
+
next: N[];
|
|
52
|
+
} & NamedRangeStructuralUpdate<N>;
|
|
53
|
+
/**
|
|
54
|
+
* `_xlnm.Print_Area` / `_xlnm.Print_Titles` are sheet-scoped defined names
|
|
55
|
+
* in Excel and follow the same RefersTo rule as any other name. Mutates the
|
|
56
|
+
* sheet's `printSettings` draft: each print-area rectangle shifts / grows /
|
|
57
|
+
* shrinks (a rectangle wholly deleted is removed; an emptied print area is
|
|
58
|
+
* cleared), and the repeated title rows / columns shift as a band along the
|
|
59
|
+
* edited axis (cleared when every title row / column is deleted).
|
|
60
|
+
*/
|
|
61
|
+
export declare const updatePrintSettingsOnStructuralChange: (printSettings: WritableDraft<SheetPrintSettings>, axis: StructuralAxis, op: StructuralOp, index: number, count: number) => boolean;
|
|
62
|
+
//# sourceMappingURL=named-range-structural.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"named-range-structural.d.ts","sourceRoot":"","sources":["../../../api/named-range-structural.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EACV,UAAU,EAEV,kBAAkB,EACnB,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAE3C,MAAM,MAAM,cAAc,GAAG,KAAK,GAAG,QAAQ,CAAC;AAC9C,MAAM,MAAM,YAAY,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAE/C;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,UAAU,CAAC;AAE7C,eAAO,MAAM,oBAAoB,GAC/B,YAAY,IAAI,CAAC,UAAU,EAAE,OAAO,GAAG,OAAO,CAAC,YACmB,CAAC;AAErE;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,cAAc,GACzB,OAAO,MAAM,EACb,KAAK,MAAM,EACX,IAAI,YAAY,EAChB,OAAO,MAAM,EACb,OAAO,MAAM,KACZ,CAAC,MAAM,EAAE,MAAM,CAAC,GAAG,IAgBrB,CAAC;AAmBF,MAAM,MAAM,0BAA0B,CAAC,CAAC,SAAS,UAAU,IAAI;IAC7D,gDAAgD;IAChD,OAAO,EAAE,CAAC,EAAE,CAAC;IACb,gEAAgE;IAChE,UAAU,EAAE,CAAC,EAAE,CAAC;CACjB,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,mCAAmC,GAC9C,OAAO,aAAa,CAAC,UAAU,CAAC,EAAE,EAClC,MAAM,cAAc,EACpB,IAAI,YAAY,EAChB,OAAO,MAAM,EACb,OAAO,MAAM,EACb,SAAS,MAAM,KACd,0BAA0B,CAAC,aAAa,CAAC,UAAU,CAAC,CAmBtD,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,kCAAkC,GAAI,CAAC,SAAS,UAAU,EACrE,aAAa,SAAS,CAAC,EAAE,EACzB,MAAM,cAAc,EACpB,IAAI,YAAY,EAChB,OAAO,MAAM,EACb,OAAO,MAAM,EACb,SAAS,MAAM,KACd;IAAE,IAAI,EAAE,CAAC,EAAE,CAAA;CAAE,GAAG,0BAA0B,CAAC,CAAC,CAsB9C,CAAC;AAEF;;;;;;;GAOG;AACH,eAAO,MAAM,qCAAqC,GAChD,eAAe,aAAa,CAAC,kBAAkB,CAAC,EAChD,MAAM,cAAc,EACpB,IAAI,YAAY,EAChB,OAAO,MAAM,EACb,OAAO,MAAM,KACZ,OAiDF,CAAC"}
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The `value` a defined name carries once every cell of its range has been
|
|
3
|
+
* deleted. Excel keeps the name in the Name Manager as `=Sheet1!#REF!` and
|
|
4
|
+
* every formula that uses it evaluates to `#REF!` (not `#NAME?`), so the
|
|
5
|
+
* name survives with no range and this error marker as its value; the
|
|
6
|
+
* calculation worker's name resolver turns it into `FormulaError.REF`.
|
|
7
|
+
*/
|
|
8
|
+
export const NAMED_RANGE_REF_ERROR = "#REF!";
|
|
9
|
+
export const isRefErrorNamedRange = (namedRange) => !namedRange.range && namedRange.value === NAMED_RANGE_REF_ERROR;
|
|
10
|
+
/**
|
|
11
|
+
* Shift the inclusive `[start, end]` band of a defined name / print band for
|
|
12
|
+
* an insert or delete of `count` rows or columns at `index` (1-based).
|
|
13
|
+
* Excel's rule for a name's RefersTo, identical to the Rust engine's
|
|
14
|
+
* `shift_named_ranges`:
|
|
15
|
+
*
|
|
16
|
+
* - insert at or above the first index → the whole band shifts;
|
|
17
|
+
* - insert strictly inside → the band grows;
|
|
18
|
+
* - insert at the boundary directly below → unchanged;
|
|
19
|
+
* - delete wholly above → shifts back; delete overlapping → shrinks by the
|
|
20
|
+
* deleted indices inside the band; delete covering the band → `null`.
|
|
21
|
+
*/
|
|
22
|
+
export const shiftNamedBand = (start, end, op, index, count) => {
|
|
23
|
+
if (count <= 0)
|
|
24
|
+
return [start, end];
|
|
25
|
+
const s = Math.max(1, start);
|
|
26
|
+
const e = Math.max(s, end);
|
|
27
|
+
if (op === "insert") {
|
|
28
|
+
if (index <= s)
|
|
29
|
+
return [s + count, e + count];
|
|
30
|
+
if (index <= e)
|
|
31
|
+
return [s, e + count];
|
|
32
|
+
return [s, e];
|
|
33
|
+
}
|
|
34
|
+
const deleteEnd = index + count - 1;
|
|
35
|
+
if (deleteEnd < s)
|
|
36
|
+
return [s - count, e - count];
|
|
37
|
+
if (index > e)
|
|
38
|
+
return [s, e];
|
|
39
|
+
if (index <= s && deleteEnd >= e)
|
|
40
|
+
return null;
|
|
41
|
+
const before = Math.max(s - index, 0);
|
|
42
|
+
const inside = Math.min(deleteEnd, e) - Math.max(index, s) + 1;
|
|
43
|
+
return [s - before, e - before - inside];
|
|
44
|
+
};
|
|
45
|
+
const shiftGridRange = (range, axis, op, index, count) => {
|
|
46
|
+
const [startKey, endKey] = axis === "row"
|
|
47
|
+
? ["startRowIndex", "endRowIndex"]
|
|
48
|
+
: ["startColumnIndex", "endColumnIndex"];
|
|
49
|
+
const next = shiftNamedBand(range[startKey], range[endKey], op, index, count);
|
|
50
|
+
if (!next)
|
|
51
|
+
return null;
|
|
52
|
+
if (next[0] === range[startKey] && next[1] === range[endKey])
|
|
53
|
+
return range;
|
|
54
|
+
return { ...range, [startKey]: next[0], [endKey]: next[1] };
|
|
55
|
+
};
|
|
56
|
+
/**
|
|
57
|
+
* Adjust defined names for a sheet-level insert/delete on `sheetId`,
|
|
58
|
+
* mutating the immer `draft` in place. Only names whose `range.sheetId` is
|
|
59
|
+
* the edited sheet move — a name's scope (`scopeSheetId`) governs how it is
|
|
60
|
+
* resolved, not which sheet's edits shift it (a Sheet2-scoped name pointing
|
|
61
|
+
* at Sheet2!$B$4 shifts on Sheet2 edits, never on Sheet1 edits). Value
|
|
62
|
+
* names (`=SUM(...)*2`, `0.1`) have no grid range and pass through.
|
|
63
|
+
*
|
|
64
|
+
* A name whose entire range is deleted is NOT dropped: it keeps its entry
|
|
65
|
+
* with `range` cleared and `value: "#REF!"`, so `=SUM(Data)` evaluates to
|
|
66
|
+
* `#REF!` like Excel rather than `#NAME?`.
|
|
67
|
+
*/
|
|
68
|
+
export const updateNamedRangesOnStructuralChange = (draft, axis, op, index, count, sheetId) => {
|
|
69
|
+
const changed = [];
|
|
70
|
+
const refErrored = [];
|
|
71
|
+
if (count <= 0)
|
|
72
|
+
return { changed, refErrored };
|
|
73
|
+
for (const namedRange of draft) {
|
|
74
|
+
const range = namedRange.range;
|
|
75
|
+
if (!range || range.sheetId !== sheetId)
|
|
76
|
+
continue;
|
|
77
|
+
const next = shiftGridRange(range, axis, op, index, count);
|
|
78
|
+
if (next === range)
|
|
79
|
+
continue;
|
|
80
|
+
if (next === null) {
|
|
81
|
+
namedRange.range = undefined;
|
|
82
|
+
namedRange.value = NAMED_RANGE_REF_ERROR;
|
|
83
|
+
refErrored.push(namedRange);
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
namedRange.range = next;
|
|
87
|
+
changed.push(namedRange);
|
|
88
|
+
}
|
|
89
|
+
return { changed, refErrored };
|
|
90
|
+
};
|
|
91
|
+
/**
|
|
92
|
+
* Pure variant of {@link updateNamedRangesOnStructuralChange}: returns the
|
|
93
|
+
* adjusted list (sharing untouched entries) plus the names that changed.
|
|
94
|
+
*/
|
|
95
|
+
export const shiftNamedRangesOnStructuralChange = (namedRanges, axis, op, index, count, sheetId) => {
|
|
96
|
+
const changed = [];
|
|
97
|
+
const refErrored = [];
|
|
98
|
+
const next = namedRanges.map((namedRange) => {
|
|
99
|
+
const range = namedRange.range;
|
|
100
|
+
if (!range || range.sheetId !== sheetId || count <= 0)
|
|
101
|
+
return namedRange;
|
|
102
|
+
const shifted = shiftGridRange(range, axis, op, index, count);
|
|
103
|
+
if (shifted === range)
|
|
104
|
+
return namedRange;
|
|
105
|
+
if (shifted === null) {
|
|
106
|
+
const broken = {
|
|
107
|
+
...namedRange,
|
|
108
|
+
range: undefined,
|
|
109
|
+
value: NAMED_RANGE_REF_ERROR,
|
|
110
|
+
};
|
|
111
|
+
refErrored.push(broken);
|
|
112
|
+
return broken;
|
|
113
|
+
}
|
|
114
|
+
const moved = { ...namedRange, range: shifted };
|
|
115
|
+
changed.push(moved);
|
|
116
|
+
return moved;
|
|
117
|
+
});
|
|
118
|
+
return { next, changed, refErrored };
|
|
119
|
+
};
|
|
120
|
+
/**
|
|
121
|
+
* `_xlnm.Print_Area` / `_xlnm.Print_Titles` are sheet-scoped defined names
|
|
122
|
+
* in Excel and follow the same RefersTo rule as any other name. Mutates the
|
|
123
|
+
* sheet's `printSettings` draft: each print-area rectangle shifts / grows /
|
|
124
|
+
* shrinks (a rectangle wholly deleted is removed; an emptied print area is
|
|
125
|
+
* cleared), and the repeated title rows / columns shift as a band along the
|
|
126
|
+
* edited axis (cleared when every title row / column is deleted).
|
|
127
|
+
*/
|
|
128
|
+
export const updatePrintSettingsOnStructuralChange = (printSettings, axis, op, index, count) => {
|
|
129
|
+
if (count <= 0)
|
|
130
|
+
return false;
|
|
131
|
+
let changed = false;
|
|
132
|
+
if (printSettings.printArea?.length) {
|
|
133
|
+
const nextAreas = [];
|
|
134
|
+
for (const area of printSettings.printArea) {
|
|
135
|
+
const next = shiftGridRange(area, axis, op, index, count);
|
|
136
|
+
if (next !== area)
|
|
137
|
+
changed = true;
|
|
138
|
+
if (next)
|
|
139
|
+
nextAreas.push(next);
|
|
140
|
+
}
|
|
141
|
+
if (changed) {
|
|
142
|
+
if (nextAreas.length) {
|
|
143
|
+
printSettings.printArea = nextAreas;
|
|
144
|
+
}
|
|
145
|
+
else {
|
|
146
|
+
delete printSettings.printArea;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
const titles = printSettings.printTitles;
|
|
151
|
+
if (titles) {
|
|
152
|
+
const [startKey, endKey] = axis === "row" ? ["rowStart", "rowEnd"] : ["columnStart", "columnEnd"];
|
|
153
|
+
const start = titles[startKey];
|
|
154
|
+
const end = titles[endKey];
|
|
155
|
+
if (start != null && end != null) {
|
|
156
|
+
const next = shiftNamedBand(start, end, op, index, count);
|
|
157
|
+
if (!next) {
|
|
158
|
+
delete titles[startKey];
|
|
159
|
+
delete titles[endKey];
|
|
160
|
+
changed = true;
|
|
161
|
+
if (titles.rowStart == null &&
|
|
162
|
+
titles.rowEnd == null &&
|
|
163
|
+
titles.columnStart == null &&
|
|
164
|
+
titles.columnEnd == null) {
|
|
165
|
+
delete printSettings.printTitles;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
else if (next[0] !== start || next[1] !== end) {
|
|
169
|
+
titles[startKey] = next[0];
|
|
170
|
+
titles[endKey] = next[1];
|
|
171
|
+
changed = true;
|
|
172
|
+
}
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
return changed;
|
|
176
|
+
};
|
|
@@ -1,17 +1,61 @@
|
|
|
1
1
|
import type { GridRange, SheetRange } from "@rowsncolumns/common-types";
|
|
2
2
|
import type { WritableDraft } from "immer";
|
|
3
3
|
type RangedRuleAxis = "row" | "column";
|
|
4
|
+
/**
|
|
5
|
+
* Sheet-name lookup the rule-formula rewrite needs to scope references:
|
|
6
|
+
* unqualified references in a CF/DV formula resolve on the sheet the rule's
|
|
7
|
+
* applies-to range lives on, so only when that is the edited sheet do they
|
|
8
|
+
* move; `EditedSheet!`-qualified references move from any sheet.
|
|
9
|
+
*/
|
|
10
|
+
export type RuleFormulaScope = {
|
|
11
|
+
getSheetName(sheetId: number): string;
|
|
12
|
+
};
|
|
13
|
+
export type RuleFormulaStructuralParams = {
|
|
14
|
+
op: "insert" | "delete";
|
|
15
|
+
axis: RangedRuleAxis;
|
|
16
|
+
/** 1-based index the rows/columns are inserted at / deleted from. */
|
|
17
|
+
index: number;
|
|
18
|
+
count: number;
|
|
19
|
+
/** Name of the sheet being edited. */
|
|
20
|
+
editedSheetName: string;
|
|
21
|
+
/** Name of the sheet the rule's applies-to range lives on. */
|
|
22
|
+
ruleSheetName: string;
|
|
23
|
+
/**
|
|
24
|
+
* How many rows/columns (along `axis`) the rule's top-left cell moved
|
|
25
|
+
* WITHIN the pre-edit grid because the previous top-left was deleted —
|
|
26
|
+
* the surviving top-left's instance formula becomes the rule text, so
|
|
27
|
+
* relative references are re-anchored by this delta first. 0 otherwise.
|
|
28
|
+
*/
|
|
29
|
+
anchorRebase?: number;
|
|
30
|
+
};
|
|
31
|
+
/**
|
|
32
|
+
* Rewrite one CF / DV formula (`=…`) for a sheet-level insert or delete,
|
|
33
|
+
* exactly as Excel rewrites the rule text: the formula is read as if it
|
|
34
|
+
* were entered in the rule's top-left cell, so relative and absolute
|
|
35
|
+
* references alike are grid positions that shift when they sit at/after
|
|
36
|
+
* the edit (`=$A1>$C$10` + insert row 5 → `=$A1>$C$11`), ranges spanning
|
|
37
|
+
* the edit grow / shrink, and a reference inside a deleted band becomes
|
|
38
|
+
* `#REF!`. Returns the input unchanged when it is not a formula or when
|
|
39
|
+
* no reference moved.
|
|
40
|
+
*/
|
|
41
|
+
export declare const shiftRuleFormulaForStructuralChange: (formula: string, { op, axis, index, count, editedSheetName, ruleSheetName, anchorRebase, }: RuleFormulaStructuralParams) => string;
|
|
4
42
|
/**
|
|
5
43
|
* Apply an insert or delete range adjustment to a list of rules whose
|
|
6
44
|
* `ranges: SheetRange[]` field references cells. Used for tables, conditional
|
|
7
45
|
* formats, data validations, etc. Mutates the immer draft in place; rules
|
|
8
46
|
* whose ranges all collapse to nothing are spliced out.
|
|
9
47
|
*
|
|
48
|
+
* When `formulaScope` is given, each rule's condition formulas
|
|
49
|
+
* (`booleanRule.condition.values[].userEnteredValue` for CF,
|
|
50
|
+
* `condition.values[].userEnteredValue` for DV — those starting with `=`)
|
|
51
|
+
* are rewritten as if entered in the rule's applies-to top-left cell, see
|
|
52
|
+
* {@link shiftRuleFormulaForStructuralChange}.
|
|
53
|
+
*
|
|
10
54
|
* Walks `draft` from the end so splice indices don't shift mid-iteration.
|
|
11
55
|
*/
|
|
12
56
|
export declare const updateRangedRulesOnStructuralChange: <R extends {
|
|
13
57
|
ranges?: SheetRange[] | null;
|
|
14
|
-
}>(draft: WritableDraft<R>[], op: "insert" | "delete", axis: RangedRuleAxis, index: number, count: number, sheetId: number) => void;
|
|
58
|
+
}>(draft: WritableDraft<R>[], op: "insert" | "delete", axis: RangedRuleAxis, index: number, count: number, sheetId: number, formulaScope?: RuleFormulaScope) => void;
|
|
15
59
|
/**
|
|
16
60
|
* Returns true when a sheet range has any cells in the given grid bounds.
|
|
17
61
|
* Cheap pre-check used by callers to skip patch generation for sheets that
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"range-rule-helpers.d.ts","sourceRoot":"","sources":["../../../api/range-rule-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"range-rule-helpers.d.ts","sourceRoot":"","sources":["../../../api/range-rule-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAGV,SAAS,EACT,UAAU,EACX,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,OAAO,CAAC;AAS3C,KAAK,cAAc,GAAG,KAAK,GAAG,QAAQ,CAAC;AAMvC;;;;;GAKG;AACH,MAAM,MAAM,gBAAgB,GAAG;IAC7B,YAAY,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAAC;CACvC,CAAC;AAEF,MAAM,MAAM,2BAA2B,GAAG;IACxC,EAAE,EAAE,QAAQ,GAAG,QAAQ,CAAC;IACxB,IAAI,EAAE,cAAc,CAAC;IACrB,qEAAqE;IACrE,KAAK,EAAE,MAAM,CAAC;IACd,KAAK,EAAE,MAAM,CAAC;IACd,sCAAsC;IACtC,eAAe,EAAE,MAAM,CAAC;IACxB,8DAA8D;IAC9D,aAAa,EAAE,MAAM,CAAC;IACtB;;;;;OAKG;IACH,YAAY,CAAC,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF;;;;;;;;;GASG;AACH,eAAO,MAAM,mCAAmC,GAC9C,SAAS,MAAM,EACf,2EAQG,2BAA2B,KAC7B,MAoHF,CAAC;AAsBF;;;;;;;;;;;;;GAaG;AACH,eAAO,MAAM,mCAAmC,GAC9C,CAAC,SAAS;IAAE,MAAM,CAAC,EAAE,UAAU,EAAE,GAAG,IAAI,CAAA;CAAE,EAE1C,OAAO,aAAa,CAAC,CAAC,CAAC,EAAE,EACzB,IAAI,QAAQ,GAAG,QAAQ,EACvB,MAAM,cAAc,EACpB,OAAO,MAAM,EACb,OAAO,MAAM,EACb,SAAS,MAAM,EACf,eAAe,gBAAgB,SA8ChC,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,iBAAiB,GAC5B,OAAO,UAAU,EACjB,SAAS,MAAM,EACf,QAAQ,SAAS,YASlB,CAAC;AAEF;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,aAAa,GACxB,OAAO,MAAM,EACb,YAAY,MAAM,EAClB,WAAW,MAAM,EACjB,gBAAgB,MAAM,KACrB,MAiBF,CAAC;AAEF,KAAK,WAAW,GAAG,KAAK,GAAG,QAAQ,CAAC;AAEpC;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,0BAA0B,GACrC,CAAC,SAAS;IAAE,MAAM,CAAC,EAAE,UAAU,EAAE,GAAG,IAAI,CAAA;CAAE,EAE1C,OAAO,aAAa,CAAC,CAAC,CAAC,EAAE,EACzB,MAAM,WAAW,EACjB,YAAY,MAAM,EAClB,WAAW,MAAM,EACjB,gBAAgB,MAAM,EACtB,SAAS,MAAM;AACf;;;;;;;;;GASG;AACH,qBAAqB;IAAE,KAAK,EAAE,MAAM,CAAC;IAAC,GAAG,EAAE,MAAM,CAAA;CAAE,SAkEpD,CAAC"}
|
|
@@ -1,16 +1,143 @@
|
|
|
1
|
+
import { contractRangesInFormula, expandRangesInFormula, formulaToRelativeReference, } from "./formula";
|
|
1
2
|
import { updateSheetRangeOnDelete, updateSheetRangeOnInsert } from "./range";
|
|
3
|
+
/** Excel's grid bounds — the shift region for "every reference at/after the edit". */
|
|
4
|
+
const MAX_ROW_INDEX = 1_048_576;
|
|
5
|
+
const MAX_COLUMN_INDEX = 16_384;
|
|
6
|
+
/**
|
|
7
|
+
* Rewrite one CF / DV formula (`=…`) for a sheet-level insert or delete,
|
|
8
|
+
* exactly as Excel rewrites the rule text: the formula is read as if it
|
|
9
|
+
* were entered in the rule's top-left cell, so relative and absolute
|
|
10
|
+
* references alike are grid positions that shift when they sit at/after
|
|
11
|
+
* the edit (`=$A1>$C$10` + insert row 5 → `=$A1>$C$11`), ranges spanning
|
|
12
|
+
* the edit grow / shrink, and a reference inside a deleted band becomes
|
|
13
|
+
* `#REF!`. Returns the input unchanged when it is not a formula or when
|
|
14
|
+
* no reference moved.
|
|
15
|
+
*/
|
|
16
|
+
export const shiftRuleFormulaForStructuralChange = (formula, { op, axis, index, count, editedSheetName, ruleSheetName, anchorRebase = 0, }) => {
|
|
17
|
+
if (!formula.startsWith("=") || count <= 0)
|
|
18
|
+
return formula;
|
|
19
|
+
const sameSheet = ruleSheetName.toLowerCase() === editedSheetName.toLowerCase();
|
|
20
|
+
const isRow = axis === "row";
|
|
21
|
+
const gridBounds = { rowCount: MAX_ROW_INDEX, columnCount: MAX_COLUMN_INDEX };
|
|
22
|
+
let next = formula;
|
|
23
|
+
// 1. The rule's previous top-left cell was deleted: the rule text is now
|
|
24
|
+
// the instance formula of the first surviving cell — relative
|
|
25
|
+
// references move with the anchor, absolutes stay.
|
|
26
|
+
if (anchorRebase > 0 && sameSheet) {
|
|
27
|
+
const source = { rowIndex: 1, columnIndex: 1 };
|
|
28
|
+
const destination = {
|
|
29
|
+
rowIndex: 1 + (isRow ? anchorRebase : 0),
|
|
30
|
+
columnIndex: 1 + (isRow ? 0 : anchorRebase),
|
|
31
|
+
};
|
|
32
|
+
next =
|
|
33
|
+
formulaToRelativeReference(next, source, destination, ruleSheetName, {
|
|
34
|
+
preserveAbsolutes: true,
|
|
35
|
+
ignoreCircular: true,
|
|
36
|
+
ignoreReferenceError: true,
|
|
37
|
+
...gridBounds,
|
|
38
|
+
}) ?? next;
|
|
39
|
+
}
|
|
40
|
+
// 2. Delete: references wholly inside the deleted band are gone → `#REF!`.
|
|
41
|
+
// Shifting the band far off-grid lets `formulaToRelativeReference`'s
|
|
42
|
+
// reference-error path emit the token.
|
|
43
|
+
if (op === "delete") {
|
|
44
|
+
const band = isRow
|
|
45
|
+
? {
|
|
46
|
+
startRowIndex: index,
|
|
47
|
+
endRowIndex: index + count - 1,
|
|
48
|
+
startColumnIndex: 1,
|
|
49
|
+
endColumnIndex: MAX_COLUMN_INDEX,
|
|
50
|
+
}
|
|
51
|
+
: {
|
|
52
|
+
startRowIndex: 1,
|
|
53
|
+
endRowIndex: MAX_ROW_INDEX,
|
|
54
|
+
startColumnIndex: index,
|
|
55
|
+
endColumnIndex: index + count - 1,
|
|
56
|
+
};
|
|
57
|
+
const offGrid = -(index + count);
|
|
58
|
+
next =
|
|
59
|
+
formulaToRelativeReference(next, { rowIndex: index, columnIndex: index }, {
|
|
60
|
+
rowIndex: isRow ? offGrid : index,
|
|
61
|
+
columnIndex: isRow ? index : offGrid,
|
|
62
|
+
}, editedSheetName, {
|
|
63
|
+
exclusionRange: band,
|
|
64
|
+
ignoreCircular: true,
|
|
65
|
+
ignoreReferenceError: true,
|
|
66
|
+
referenceSheetName: editedSheetName,
|
|
67
|
+
includeUnqualifiedReferences: sameSheet,
|
|
68
|
+
// Deliberately NOT the grid bounds: a whole-row / whole-column
|
|
69
|
+
// reference inside the band would otherwise be pushed to a
|
|
70
|
+
// negative index instead of `#REF!`; those stay put and are
|
|
71
|
+
// handled by the range contraction below.
|
|
72
|
+
rowCount: MAX_ROW_INDEX + 1,
|
|
73
|
+
columnCount: MAX_COLUMN_INDEX + 1,
|
|
74
|
+
}) ?? next;
|
|
75
|
+
}
|
|
76
|
+
// 3. Ranges that span the edit grow (insert) / shrink (delete).
|
|
77
|
+
next =
|
|
78
|
+
(op === "insert" ? expandRangesInFormula : contractRangesInFormula)(next, index, count, axis, editedSheetName, { includeUnqualifiedReferences: sameSheet }) ?? next;
|
|
79
|
+
// 4. Everything at/after the edit shifts by ±count along the axis.
|
|
80
|
+
const delta = op === "insert" ? count : -count;
|
|
81
|
+
const regionStart = op === "insert" ? index : index + count;
|
|
82
|
+
const region = isRow
|
|
83
|
+
? {
|
|
84
|
+
startRowIndex: regionStart,
|
|
85
|
+
endRowIndex: MAX_ROW_INDEX,
|
|
86
|
+
startColumnIndex: 1,
|
|
87
|
+
endColumnIndex: MAX_COLUMN_INDEX,
|
|
88
|
+
}
|
|
89
|
+
: {
|
|
90
|
+
startRowIndex: 1,
|
|
91
|
+
endRowIndex: MAX_ROW_INDEX,
|
|
92
|
+
startColumnIndex: regionStart,
|
|
93
|
+
endColumnIndex: MAX_COLUMN_INDEX,
|
|
94
|
+
};
|
|
95
|
+
next =
|
|
96
|
+
formulaToRelativeReference(next, { rowIndex: regionStart, columnIndex: regionStart }, {
|
|
97
|
+
rowIndex: regionStart + (isRow ? delta : 0),
|
|
98
|
+
columnIndex: regionStart + (isRow ? 0 : delta),
|
|
99
|
+
}, editedSheetName, {
|
|
100
|
+
exclusionRange: region,
|
|
101
|
+
ignoreCircular: true,
|
|
102
|
+
ignoreReferenceError: true,
|
|
103
|
+
referenceSheetName: editedSheetName,
|
|
104
|
+
includeUnqualifiedReferences: sameSheet,
|
|
105
|
+
...gridBounds,
|
|
106
|
+
}) ?? next;
|
|
107
|
+
return next;
|
|
108
|
+
};
|
|
109
|
+
const rewriteConditionFormulas = (condition, params) => {
|
|
110
|
+
if (!condition?.values)
|
|
111
|
+
return;
|
|
112
|
+
for (const value of condition.values) {
|
|
113
|
+
const text = value.userEnteredValue;
|
|
114
|
+
if (typeof text !== "string" || !text.startsWith("="))
|
|
115
|
+
continue;
|
|
116
|
+
const next = shiftRuleFormulaForStructuralChange(text, params);
|
|
117
|
+
if (next !== text)
|
|
118
|
+
value.userEnteredValue = next;
|
|
119
|
+
}
|
|
120
|
+
};
|
|
2
121
|
/**
|
|
3
122
|
* Apply an insert or delete range adjustment to a list of rules whose
|
|
4
123
|
* `ranges: SheetRange[]` field references cells. Used for tables, conditional
|
|
5
124
|
* formats, data validations, etc. Mutates the immer draft in place; rules
|
|
6
125
|
* whose ranges all collapse to nothing are spliced out.
|
|
7
126
|
*
|
|
127
|
+
* When `formulaScope` is given, each rule's condition formulas
|
|
128
|
+
* (`booleanRule.condition.values[].userEnteredValue` for CF,
|
|
129
|
+
* `condition.values[].userEnteredValue` for DV — those starting with `=`)
|
|
130
|
+
* are rewritten as if entered in the rule's applies-to top-left cell, see
|
|
131
|
+
* {@link shiftRuleFormulaForStructuralChange}.
|
|
132
|
+
*
|
|
8
133
|
* Walks `draft` from the end so splice indices don't shift mid-iteration.
|
|
9
134
|
*/
|
|
10
|
-
export const updateRangedRulesOnStructuralChange = (draft, op, axis, index, count, sheetId) => {
|
|
135
|
+
export const updateRangedRulesOnStructuralChange = (draft, op, axis, index, count, sheetId, formulaScope) => {
|
|
11
136
|
const updater = op === "insert" ? updateSheetRangeOnInsert : updateSheetRangeOnDelete;
|
|
137
|
+
const isRow = axis === "row";
|
|
12
138
|
for (let i = draft.length - 1; i >= 0; i--) {
|
|
13
139
|
const rule = draft[i];
|
|
140
|
+
const anchorRange = rule.ranges?.[0];
|
|
14
141
|
const ranges = rule.ranges
|
|
15
142
|
?.map((range) => updater(range, axis, index, count, sheetId))
|
|
16
143
|
.filter((range) => !!range) ?? [];
|
|
@@ -18,6 +145,31 @@ export const updateRangedRulesOnStructuralChange = (draft, op, axis, index, coun
|
|
|
18
145
|
// immer's typing for nested-mutation assignment is flaky here; cast
|
|
19
146
|
// through `unknown` since the runtime shape matches the rule type.
|
|
20
147
|
rule.ranges = ranges;
|
|
148
|
+
if (formulaScope && anchorRange) {
|
|
149
|
+
// Excel anchors a rule's formula at its applies-to top-left. When
|
|
150
|
+
// that cell is deleted, the first surviving cell (right after the
|
|
151
|
+
// deleted band) becomes the anchor and its instance formula the
|
|
152
|
+
// rule text.
|
|
153
|
+
const anchorPosition = isRow
|
|
154
|
+
? anchorRange.startRowIndex
|
|
155
|
+
: anchorRange.startColumnIndex;
|
|
156
|
+
const anchorDeleted = op === "delete" &&
|
|
157
|
+
anchorRange.sheetId === sheetId &&
|
|
158
|
+
anchorPosition >= index &&
|
|
159
|
+
anchorPosition <= index + count - 1;
|
|
160
|
+
const params = {
|
|
161
|
+
op,
|
|
162
|
+
axis,
|
|
163
|
+
index,
|
|
164
|
+
count,
|
|
165
|
+
editedSheetName: formulaScope.getSheetName(sheetId),
|
|
166
|
+
ruleSheetName: formulaScope.getSheetName(anchorRange.sheetId),
|
|
167
|
+
anchorRebase: anchorDeleted ? index + count - anchorPosition : 0,
|
|
168
|
+
};
|
|
169
|
+
const carrier = rule;
|
|
170
|
+
rewriteConditionFormulas(carrier.booleanRule?.condition, params);
|
|
171
|
+
rewriteConditionFormulas(carrier.condition, params);
|
|
172
|
+
}
|
|
21
173
|
}
|
|
22
174
|
else {
|
|
23
175
|
draft.splice(i, 1);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"structural-formula-update.d.ts","sourceRoot":"","sources":["../../../api/structural-formula-update.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAK1D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAEnC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAChE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAErE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAarD,KAAK,cAAc,GAAG,KAAK,GAAG,QAAQ,CAAC;AACvC,KAAK,YAAY,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAExC,MAAM,MAAM,6BAA6B,CAAC,CAAC,SAAS,QAAQ,IAAI;IAC9D,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3B,IAAI,EAAE,cAAc,CAAC;IACrB,EAAE,EAAE,YAAY,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB,wDAAwD;IACxD,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IAEpB,aAAa,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC,eAAe,CAAC,CAAC;IAClE;;;;OAIG;IACH,aAAa,CAAC,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC,eAAe,CAAC,CAAC;IACnE;;;;;OAKG;IACH,oBAAoB,CAAC,EAAE,UAAU,CAC/B,OAAO,cAAc,CACtB,CAAC,sBAAsB,CAAC,CAAC;IAC1B,YAAY,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;IAC1C,kBAAkB,CAAC,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC,oBAAoB,CAAC,CAAC;IAC7E,qBAAqB,CAAC,EAAE,UAAU,CAChC,OAAO,cAAc,CACtB,CAAC,uBAAuB,CAAC,CAAC;IAE3B,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;IAE/C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;CAC7C,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,iCAAiC,GAAI,CAAC,SAAS,QAAQ,EAClE,QAAQ,6BAA6B,CAAC,CAAC,CAAC,
|
|
1
|
+
{"version":3,"file":"structural-formula-update.d.ts","sourceRoot":"","sources":["../../../api/structural-formula-update.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,2BAA2B,CAAC;AAK1D,OAAO,KAAK,EAAE,KAAK,EAAE,MAAM,OAAO,CAAC;AAEnC,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC/D,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,+BAA+B,CAAC;AACrE,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,gCAAgC,CAAC;AAChE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,0BAA0B,CAAC;AAErE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,UAAU,CAAC;AAarD,KAAK,cAAc,GAAG,KAAK,GAAG,QAAQ,CAAC;AACvC,KAAK,YAAY,GAAG,QAAQ,GAAG,QAAQ,CAAC;AAExC,MAAM,MAAM,6BAA6B,CAAC,CAAC,SAAS,QAAQ,IAAI;IAC9D,KAAK,EAAE,KAAK,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC,CAAC;IAC3B,IAAI,EAAE,cAAc,CAAC;IACrB,EAAE,EAAE,YAAY,CAAC;IACjB,OAAO,EAAE,MAAM,CAAC;IAChB;;;OAGG;IACH,cAAc,EAAE,MAAM,CAAC;IACvB,wDAAwD;IACxD,KAAK,EAAE,MAAM,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,WAAW,EAAE,MAAM,CAAC;IAEpB,aAAa,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC,eAAe,CAAC,CAAC;IAClE;;;;OAIG;IACH,aAAa,CAAC,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC,eAAe,CAAC,CAAC;IACnE;;;;;OAKG;IACH,oBAAoB,CAAC,EAAE,UAAU,CAC/B,OAAO,cAAc,CACtB,CAAC,sBAAsB,CAAC,CAAC;IAC1B,YAAY,EAAE,CAAC,OAAO,EAAE,MAAM,KAAK,MAAM,CAAC;IAC1C,kBAAkB,CAAC,EAAE,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC,oBAAoB,CAAC,CAAC;IAC7E,qBAAqB,CAAC,EAAE,UAAU,CAChC,OAAO,cAAc,CACtB,CAAC,uBAAuB,CAAC,CAAC;IAE3B,WAAW,EAAE,GAAG,CAAC,MAAM,EAAE,oBAAoB,CAAC,CAAC;IAE/C,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,eAAe,CAAC,EAAE,eAAe,CAAC;IAClC,oBAAoB,CAAC,EAAE,oBAAoB,CAAC;CAC7C,CAAC;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;GA0BG;AACH,eAAO,MAAM,iCAAiC,GAAI,CAAC,SAAS,QAAQ,EAClE,QAAQ,6BAA6B,CAAC,CAAC,CAAC,SA2WzC,CAAC"}
|