@rowsncolumns/spreadsheet-state 28.0.75 → 28.0.76
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/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/merges.d.ts +8 -0
- package/dist/cjs/api/merges.d.ts.map +1 -1
- package/dist/cjs/api/merges.js +64 -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/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-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-spreadsheet-state.d.ts +2 -2
- package/dist/cjs/hooks/use-spreadsheet-state.d.ts.map +1 -1
- package/dist/cjs/hooks/use-spreadsheet-state.js +61 -15
- 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 +3 -0
- package/dist/cjs/index.d.ts.map +1 -1
- package/dist/cjs/index.js +3 -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 +90 -31
- 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 +85 -26
- 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 +46 -8
- 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 +47 -10
- 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/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/merges.d.ts +8 -0
- package/dist/esm/api/merges.d.ts.map +1 -1
- package/dist/esm/api/merges.js +51 -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/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-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-spreadsheet-state.d.ts +2 -2
- package/dist/esm/hooks/use-spreadsheet-state.d.ts.map +1 -1
- package/dist/esm/hooks/use-spreadsheet-state.js +61 -15
- 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 +3 -0
- package/dist/esm/index.d.ts.map +1 -1
- package/dist/esm/index.js +3 -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 +80 -11
- 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 +81 -11
- 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 +46 -8
- 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 +47 -10
- 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 +16 -16
|
@@ -22,6 +22,23 @@ export declare const updateNamedRangeName: (formulaValue: string, newName: strin
|
|
|
22
22
|
* @param newName
|
|
23
23
|
*/
|
|
24
24
|
export declare const updateColumnName: (formulaValue: string, newName: string, oldName: string) => string;
|
|
25
|
+
/** The column specifier Excel writes for a deleted table column (`Table1[@#REF!]`). */
|
|
26
|
+
export declare const REF_ERROR_COLUMN = "#REF!";
|
|
27
|
+
/**
|
|
28
|
+
* Rewrite every structured reference into `tableName` to the `#REF!` token —
|
|
29
|
+
* Excel's text for a formula whose table was deleted (`=SUM(Table1[Price])`
|
|
30
|
+
* → `=SUM(#REF!)`), so the cell evaluates to `#REF!` rather than `#NAME?`.
|
|
31
|
+
* Returns the input unchanged when the formula never names the table.
|
|
32
|
+
*/
|
|
33
|
+
export declare const breakTableReferencesInFormula: (formulaValue: string, tableName: string) => string;
|
|
34
|
+
/**
|
|
35
|
+
* Rewrite the column specifiers of `columnNames` inside structured references
|
|
36
|
+
* to `tableName` (or, when `tableName` is undefined, the bare this-row form
|
|
37
|
+
* `[@Col]` whose table is implied by the formula's own cell) to `#REF!` —
|
|
38
|
+
* Excel's text for a deleted table column (`=Table1[@Qty]*2` →
|
|
39
|
+
* `=Table1[@#REF!]*2`). Keeps every other character of the reference as typed.
|
|
40
|
+
*/
|
|
41
|
+
export declare const breakTableColumnsInFormula: (formulaValue: string, tableName: string | undefined, columnNames: string[]) => string;
|
|
25
42
|
/**
|
|
26
43
|
* Converts tokens to string
|
|
27
44
|
* @param tokens
|
|
@@ -46,24 +63,64 @@ export declare const isAbsoluteColumnReference: (formula: string | undefined) =>
|
|
|
46
63
|
* @param formula
|
|
47
64
|
*/
|
|
48
65
|
export declare const isAbsoluteRowReference: (formula: string | undefined) => boolean;
|
|
66
|
+
export type StructuralRangeRewriteOptions = {
|
|
67
|
+
/**
|
|
68
|
+
* Whether references WITHOUT a sheet qualifier resolve to the edited sheet.
|
|
69
|
+
* True for a formula that lives on the edited sheet; false for a formula on
|
|
70
|
+
* another sheet, whose bare `A1:A3` is its own sheet's range and must not
|
|
71
|
+
* move when the edited sheet changes shape.
|
|
72
|
+
*/
|
|
73
|
+
includeUnqualifiedReferences?: boolean;
|
|
74
|
+
/**
|
|
75
|
+
* Restrict the rewrite to ranges whose PERPENDICULAR span lies inside this
|
|
76
|
+
* band — an insert/delete-cells shift of columns `B:C` only touches ranges
|
|
77
|
+
* that live entirely inside columns B..C (SHF-08); a wider range keeps its
|
|
78
|
+
* address (SHF-07). Whole-column and whole-row references span the entire
|
|
79
|
+
* sheet, so they never fall inside a band.
|
|
80
|
+
*/
|
|
81
|
+
bandSpan?: RangeBandSpan;
|
|
82
|
+
};
|
|
83
|
+
/** Alias kept for callers that import the scope options under this name. */
|
|
84
|
+
export type StructuralFormulaScopeOptions = StructuralRangeRewriteOptions;
|
|
85
|
+
/**
|
|
86
|
+
* Restricts `expandRangesInFormula` / `contractRangesInFormula` to ranges whose PERPENDICULAR span
|
|
87
|
+
* lies within a band — an insert/delete-cells shift of columns `B:C` only expands ranges that live
|
|
88
|
+
* entirely inside columns B..C (SHF-08); a wider range keeps its address (SHF-07). Whole-column and
|
|
89
|
+
* whole-row references span the entire sheet, so they never fall inside a band.
|
|
90
|
+
*/
|
|
91
|
+
export type RangeBandSpan = {
|
|
92
|
+
startIndex: number;
|
|
93
|
+
endIndex: number;
|
|
94
|
+
};
|
|
49
95
|
/**
|
|
50
96
|
* Expands ranges in a formula when rows or columns are inserted within the range
|
|
51
97
|
* @param formula The formula to process
|
|
52
98
|
* @param insertionIndex The row or column index where insertion happens
|
|
53
99
|
* @param count Number of rows or columns being inserted
|
|
54
100
|
* @param dimension Whether inserting 'row' or 'column'
|
|
55
|
-
* @param activeSheetName
|
|
101
|
+
* @param activeSheetName Name of the EDITED sheet: only references that resolve
|
|
102
|
+
* to it are touched
|
|
103
|
+
* @param options `includeUnqualifiedReferences` — whether references without a
|
|
104
|
+
* sheet qualifier resolve to the edited sheet (true when the formula lives on
|
|
105
|
+
* it; false for a formula on another sheet, whose bare `A1:A3` is its own)
|
|
56
106
|
* @returns The formula with expanded ranges, or undefined if no changes
|
|
57
107
|
*/
|
|
58
|
-
export declare const expandRangesInFormula: (formula: string | undefined, insertionIndex: number, count: number, dimension: "row" | "column", activeSheetName: string) => string | undefined;
|
|
108
|
+
export declare const expandRangesInFormula: (formula: string | undefined, insertionIndex: number, count: number, dimension: "row" | "column", activeSheetName: string, options?: StructuralRangeRewriteOptions) => string | undefined;
|
|
59
109
|
/**
|
|
60
110
|
* Contracts ranges in a formula when rows or columns are deleted within the range
|
|
61
111
|
* @param formula The formula to process
|
|
62
112
|
* @param deletionIndex The row or column index where deletion starts
|
|
63
113
|
* @param count Number of rows or columns being deleted
|
|
64
114
|
* @param dimension Whether deleting 'row' or 'column'
|
|
65
|
-
* @param activeSheetName
|
|
115
|
+
* @param activeSheetName Name of the EDITED sheet: only references that resolve
|
|
116
|
+
* to it are touched
|
|
117
|
+
* @param options `includeUnqualifiedReferences` — see `expandRangesInFormula`
|
|
66
118
|
* @returns The formula with contracted ranges, or undefined if no changes
|
|
119
|
+
*
|
|
120
|
+
* Excel semantics for the deleted band: a range whose rows/columns are ALL
|
|
121
|
+
* deleted becomes the `#REF!` token (`=SUM(A2:A3)` → `=SUM(#REF!)`); a single
|
|
122
|
+
* cell reference into the band becomes `#REF!` (`=A3+1` → `=#REF!+1`, a spill
|
|
123
|
+
* reference `=C1#` → `=#REF!#`); a partially covered range shrinks.
|
|
67
124
|
*/
|
|
68
|
-
export declare const contractRangesInFormula: (formula: string | undefined, deletionIndex: number, count: number, dimension: "row" | "column", activeSheetName: string) => string | undefined;
|
|
125
|
+
export declare const contractRangesInFormula: (formula: string | undefined, deletionIndex: number, count: number, dimension: "row" | "column", activeSheetName: string, options?: StructuralRangeRewriteOptions) => string | undefined;
|
|
69
126
|
//# sourceMappingURL=formula.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"formula.d.ts","sourceRoot":"","sources":["../../../api/formula.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"formula.d.ts","sourceRoot":"","sources":["../../../api/formula.ts"],"names":[],"mappings":"AASA,OAAO,KAAK,EAAE,WAAW,EAAS,MAAM,2BAA2B,CAAC;AAUpE,OAAO,EACL,yBAAyB,EACzB,eAAe,EACf,0BAA0B,GAC3B,MAAM,0BAA0B,CAAC;AAClC,YAAY,EAAE,iCAAiC,EAAE,MAAM,0BAA0B,CAAC;AAElF;;;;;GAKG;AACH,eAAO,MAAM,eAAe,GAC1B,cAAc,MAAM,EACpB,SAAS,MAAM,EACf,SAAS,MAAM,WA4BhB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,oBAAoB,GAC/B,cAAc,MAAM,EACpB,SAAS,MAAM,EACf,SAAS,MAAM,WAehB,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,gBAAgB,GAC3B,cAAc,MAAM,EACpB,SAAS,MAAM,EACf,SAAS,MAAM,WAYhB,CAAC;AAEF,uFAAuF;AACvF,eAAO,MAAM,gBAAgB,UAAU,CAAC;AAgDxC;;;;;GAKG;AACH,eAAO,MAAM,6BAA6B,GACxC,cAAc,MAAM,EACpB,WAAW,MAAM,KAChB,MA4BF,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,0BAA0B,GACrC,cAAc,MAAM,EACpB,WAAW,MAAM,GAAG,SAAS,EAC7B,aAAa,MAAM,EAAE,KACpB,MA+BF,CAAC;AAaF;;;GAGG;AACH,eAAO,MAAM,UAAU,GAAI,QAAQ,WAAW,EAAE,WAU/C,CAAC;AAiBF;;;;;GAKG;AACH,eAAO,MAAM,yBAAyB,GACpC,SAAS,MAAM,EACf,UAAU,MAAM,EAChB,aAAa,MAAM,WAGpB,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,GAAI,SAAS,MAAM,GAAG,SAAS,YAGpE,CAAC;AAEF;;;;GAIG;AACH,eAAO,MAAM,sBAAsB,GAAI,SAAS,MAAM,GAAG,SAAS,YAGjE,CAAC;AAEF,MAAM,MAAM,6BAA6B,GAAG;IAC1C;;;;;OAKG;IACH,4BAA4B,CAAC,EAAE,OAAO,CAAC;IACvC;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,aAAa,CAAC;CAC1B,CAAC;AAEF,4EAA4E;AAC5E,MAAM,MAAM,6BAA6B,GAAG,6BAA6B,CAAC;AAE1E;;;;;GAKG;AACH,MAAM,MAAM,aAAa,GAAG;IAAE,UAAU,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,CAAC;AAmBrE;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,qBAAqB,GAChC,SAAS,MAAM,GAAG,SAAS,EAC3B,gBAAgB,MAAM,EACtB,OAAO,MAAM,EACb,WAAW,KAAK,GAAG,QAAQ,EAC3B,iBAAiB,MAAM,EACvB,UAAS,6BAAkC,KAC1C,MAAM,GAAG,SAyKX,CAAC;AAEF;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,uBAAuB,GAClC,SAAS,MAAM,GAAG,SAAS,EAC3B,eAAe,MAAM,EACrB,OAAO,MAAM,EACb,WAAW,KAAK,GAAG,QAAQ,EAC3B,iBAAiB,MAAM,EACvB,UAAS,6BAAkC,KAC1C,MAAM,GAAG,SAkPX,CAAC"}
|
package/dist/esm/api/formula.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { isRangeToken, lexify, stripPropertyFromCellRef, tokenVocabulary, } from "@rowsncolumns/calculator";
|
|
1
|
+
import { cleanupColumnName, cleanupTableName, isRangeToken, lexify, stripPropertyFromCellRef, tokenVocabulary, } from "@rowsncolumns/calculator";
|
|
2
2
|
import { addressToCell, alpha2number, cellToAddress, desanitizeSheetName, number2Alpha, sanitizeSheetName, } from "@rowsncolumns/utils";
|
|
3
3
|
export { ERROR_CIRCULAR_DEPENDENCY, ERROR_REFERENCE, formulaToRelativeReference, } from "@rowsncolumns/calculator";
|
|
4
4
|
/**
|
|
@@ -65,6 +65,138 @@ export const updateColumnName = (formulaValue, newName, oldName) => {
|
|
|
65
65
|
}
|
|
66
66
|
return detokenize(newTokens);
|
|
67
67
|
};
|
|
68
|
+
/** The column specifier Excel writes for a deleted table column (`Table1[@#REF!]`). */
|
|
69
|
+
export const REF_ERROR_COLUMN = "#REF!";
|
|
70
|
+
const REF_ERROR_TOKEN = "#REF!";
|
|
71
|
+
/**
|
|
72
|
+
* One `#REF!` token standing in for the run `start … end` (a range's start,
|
|
73
|
+
* colon and end): it keeps `start`'s position and stretches to `end` so
|
|
74
|
+
* `detokenize` (which re-inserts a space wherever adjacent tokens' offsets
|
|
75
|
+
* disagree) prints `SUM(#REF!)`, not `SUM(#REF! )`.
|
|
76
|
+
*/
|
|
77
|
+
const collapseToRefError = (start, end) => ({
|
|
78
|
+
...start,
|
|
79
|
+
image: REF_ERROR_TOKEN,
|
|
80
|
+
tokenType: tokenVocabulary.RefError,
|
|
81
|
+
endOffset: end?.endOffset ?? start.endOffset,
|
|
82
|
+
endColumn: end?.endColumn ?? start.endColumn,
|
|
83
|
+
});
|
|
84
|
+
const isTableNameToken = (token) => token?.tokenType.name === tokenVocabulary.TableName.name;
|
|
85
|
+
const isTableColumnToken = (token) => token?.tokenType.name === tokenVocabulary.TableColumnName.name;
|
|
86
|
+
/** Bracket depth change contributed by one token's image (`Table1[` opens, `Qty]` closes). */
|
|
87
|
+
const bracketDelta = (image) => {
|
|
88
|
+
let depth = 0;
|
|
89
|
+
for (const ch of image) {
|
|
90
|
+
if (ch === "[")
|
|
91
|
+
depth++;
|
|
92
|
+
else if (ch === "]")
|
|
93
|
+
depth--;
|
|
94
|
+
}
|
|
95
|
+
return depth;
|
|
96
|
+
};
|
|
97
|
+
/**
|
|
98
|
+
* Index just past the last token of the structured reference that starts at
|
|
99
|
+
* `start` (a TableName token): consume tokens until the bracket the table name
|
|
100
|
+
* opened is closed again.
|
|
101
|
+
*/
|
|
102
|
+
const structuredReferenceEnd = (tokens, start) => {
|
|
103
|
+
let depth = 0;
|
|
104
|
+
let i = start;
|
|
105
|
+
while (i < tokens.length) {
|
|
106
|
+
depth += bracketDelta(tokens[i].image);
|
|
107
|
+
i++;
|
|
108
|
+
if (depth <= 0)
|
|
109
|
+
break;
|
|
110
|
+
}
|
|
111
|
+
return i;
|
|
112
|
+
};
|
|
113
|
+
/**
|
|
114
|
+
* Rewrite every structured reference into `tableName` to the `#REF!` token —
|
|
115
|
+
* Excel's text for a formula whose table was deleted (`=SUM(Table1[Price])`
|
|
116
|
+
* → `=SUM(#REF!)`), so the cell evaluates to `#REF!` rather than `#NAME?`.
|
|
117
|
+
* Returns the input unchanged when the formula never names the table.
|
|
118
|
+
*/
|
|
119
|
+
export const breakTableReferencesInFormula = (formulaValue, tableName) => {
|
|
120
|
+
if (!formulaValue.includes("["))
|
|
121
|
+
return formulaValue;
|
|
122
|
+
const tokens = cloneTokens(lexify(formulaValue).tokens);
|
|
123
|
+
const newTokens = [];
|
|
124
|
+
const target = tableName.toLowerCase();
|
|
125
|
+
let hasChanges = false;
|
|
126
|
+
for (let i = 0; i < tokens.length; i++) {
|
|
127
|
+
const token = tokens[i];
|
|
128
|
+
if (isTableNameToken(token) &&
|
|
129
|
+
cleanupTableName(token.image).trim().toLowerCase() === target) {
|
|
130
|
+
const end = structuredReferenceEnd(tokens, i);
|
|
131
|
+
const last = tokens[end - 1] ?? token;
|
|
132
|
+
newTokens.push({
|
|
133
|
+
...token,
|
|
134
|
+
image: REF_ERROR_TOKEN,
|
|
135
|
+
endOffset: last.endOffset,
|
|
136
|
+
endColumn: last.endColumn,
|
|
137
|
+
tokenType: tokenVocabulary.RefError,
|
|
138
|
+
});
|
|
139
|
+
i = end - 1;
|
|
140
|
+
hasChanges = true;
|
|
141
|
+
continue;
|
|
142
|
+
}
|
|
143
|
+
newTokens.push(token);
|
|
144
|
+
}
|
|
145
|
+
return hasChanges ? detokenize(newTokens) : formulaValue;
|
|
146
|
+
};
|
|
147
|
+
/**
|
|
148
|
+
* Rewrite the column specifiers of `columnNames` inside structured references
|
|
149
|
+
* to `tableName` (or, when `tableName` is undefined, the bare this-row form
|
|
150
|
+
* `[@Col]` whose table is implied by the formula's own cell) to `#REF!` —
|
|
151
|
+
* Excel's text for a deleted table column (`=Table1[@Qty]*2` →
|
|
152
|
+
* `=Table1[@#REF!]*2`). Keeps every other character of the reference as typed.
|
|
153
|
+
*/
|
|
154
|
+
export const breakTableColumnsInFormula = (formulaValue, tableName, columnNames) => {
|
|
155
|
+
if (!formulaValue.includes("[") || columnNames.length === 0) {
|
|
156
|
+
return formulaValue;
|
|
157
|
+
}
|
|
158
|
+
const targets = new Set(columnNames.map((name) => name.toLowerCase()));
|
|
159
|
+
const target = tableName?.toLowerCase();
|
|
160
|
+
const tokens = cloneTokens(lexify(formulaValue).tokens);
|
|
161
|
+
let hasChanges = false;
|
|
162
|
+
let i = 0;
|
|
163
|
+
while (i < tokens.length) {
|
|
164
|
+
const token = tokens[i];
|
|
165
|
+
if (isTableNameToken(token)) {
|
|
166
|
+
const end = structuredReferenceEnd(tokens, i);
|
|
167
|
+
const matches = target !== undefined &&
|
|
168
|
+
cleanupTableName(token.image).trim().toLowerCase() === target;
|
|
169
|
+
if (matches) {
|
|
170
|
+
for (let j = i + 1; j < end; j++) {
|
|
171
|
+
if (breakColumnToken(tokens[j], targets))
|
|
172
|
+
hasChanges = true;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
i = end;
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
// A bare `[@Col]` / `[Col]` names the table the formula cell sits in.
|
|
179
|
+
if (target === undefined && isTableColumnToken(token)) {
|
|
180
|
+
if (breakColumnToken(token, targets))
|
|
181
|
+
hasChanges = true;
|
|
182
|
+
}
|
|
183
|
+
i++;
|
|
184
|
+
}
|
|
185
|
+
return hasChanges ? detokenize(tokens) : formulaValue;
|
|
186
|
+
};
|
|
187
|
+
const breakColumnToken = (token, targets) => {
|
|
188
|
+
if (!token || !isTableColumnToken(token))
|
|
189
|
+
return false;
|
|
190
|
+
const name = cleanupColumnName(token.image);
|
|
191
|
+
if (!name || !targets.has(name.toLowerCase()))
|
|
192
|
+
return false;
|
|
193
|
+
const at = token.image.indexOf(name);
|
|
194
|
+
if (at === -1)
|
|
195
|
+
return false;
|
|
196
|
+
token.image =
|
|
197
|
+
token.image.slice(0, at) + REF_ERROR_COLUMN + token.image.slice(at + name.length);
|
|
198
|
+
return true;
|
|
199
|
+
};
|
|
68
200
|
/**
|
|
69
201
|
* Converts tokens to string
|
|
70
202
|
* @param tokens
|
|
@@ -122,19 +254,33 @@ export const isAbsoluteRowReference = (formula) => {
|
|
|
122
254
|
return false;
|
|
123
255
|
return /\$\d+/gi.test(formula);
|
|
124
256
|
};
|
|
257
|
+
const isRangeWithinBandSpan = (startCell, endCell, dimension, bandSpan) => {
|
|
258
|
+
if (!bandSpan)
|
|
259
|
+
return true;
|
|
260
|
+
const [start, end] = dimension === "row"
|
|
261
|
+
? [startCell.columnIndex, endCell.columnIndex]
|
|
262
|
+
: [startCell.rowIndex, endCell.rowIndex];
|
|
263
|
+
return (Math.min(start, end) >= bandSpan.startIndex &&
|
|
264
|
+
Math.max(start, end) <= bandSpan.endIndex);
|
|
265
|
+
};
|
|
125
266
|
/**
|
|
126
267
|
* Expands ranges in a formula when rows or columns are inserted within the range
|
|
127
268
|
* @param formula The formula to process
|
|
128
269
|
* @param insertionIndex The row or column index where insertion happens
|
|
129
270
|
* @param count Number of rows or columns being inserted
|
|
130
271
|
* @param dimension Whether inserting 'row' or 'column'
|
|
131
|
-
* @param activeSheetName
|
|
272
|
+
* @param activeSheetName Name of the EDITED sheet: only references that resolve
|
|
273
|
+
* to it are touched
|
|
274
|
+
* @param options `includeUnqualifiedReferences` — whether references without a
|
|
275
|
+
* sheet qualifier resolve to the edited sheet (true when the formula lives on
|
|
276
|
+
* it; false for a formula on another sheet, whose bare `A1:A3` is its own)
|
|
132
277
|
* @returns The formula with expanded ranges, or undefined if no changes
|
|
133
278
|
*/
|
|
134
|
-
export const expandRangesInFormula = (formula, insertionIndex, count, dimension, activeSheetName) => {
|
|
279
|
+
export const expandRangesInFormula = (formula, insertionIndex, count, dimension, activeSheetName, options = {}) => {
|
|
135
280
|
if (!formula || count <= 0) {
|
|
136
281
|
return formula;
|
|
137
282
|
}
|
|
283
|
+
const { includeUnqualifiedReferences = true, bandSpan } = options;
|
|
138
284
|
const normalizedActiveSheetName = activeSheetName.toLowerCase();
|
|
139
285
|
const tokens = cloneTokens(lexify(formula).tokens);
|
|
140
286
|
const newTokens = [];
|
|
@@ -142,7 +288,9 @@ export const expandRangesInFormula = (formula, insertionIndex, count, dimension,
|
|
|
142
288
|
for (let i = 0; i < tokens.length; i++) {
|
|
143
289
|
const token = tokens[i];
|
|
144
290
|
const sheetName = getSheetNameFromToken(tokens[i - 1]);
|
|
145
|
-
const skipSheet = sheetName
|
|
291
|
+
const skipSheet = sheetName
|
|
292
|
+
? sheetName.toLowerCase() !== normalizedActiveSheetName
|
|
293
|
+
: !includeUnqualifiedReferences;
|
|
146
294
|
// Handle cell ranges (e.g., A1:A10)
|
|
147
295
|
if (token.tokenType.name === tokenVocabulary.Cell.name) {
|
|
148
296
|
const isRange = isRangeToken(tokens[i + 1]);
|
|
@@ -157,7 +305,9 @@ export const expandRangesInFormula = (formula, insertionIndex, count, dimension,
|
|
|
157
305
|
if (isRange && tokens[i + 2]) {
|
|
158
306
|
const startCell = addressToCell(stripPropertyFromCellRef(token.image));
|
|
159
307
|
const endCell = addressToCell(stripPropertyFromCellRef(tokens[i + 2].image));
|
|
160
|
-
if (startCell &&
|
|
308
|
+
if (startCell &&
|
|
309
|
+
endCell &&
|
|
310
|
+
isRangeWithinBandSpan(startCell, endCell, dimension, bandSpan)) {
|
|
161
311
|
const isAbsoluteEndRow = isAbsoluteRowReference(tokens[i + 2].image);
|
|
162
312
|
const isAbsoluteEndCol = isAbsoluteColumnReference(tokens[i + 2].image);
|
|
163
313
|
if (dimension === "row") {
|
|
@@ -197,7 +347,7 @@ export const expandRangesInFormula = (formula, insertionIndex, count, dimension,
|
|
|
197
347
|
if (dimension === "column" &&
|
|
198
348
|
token.tokenType.name === tokenVocabulary.Column.name) {
|
|
199
349
|
const isRange = isRangeToken(tokens[i + 1]);
|
|
200
|
-
if (skipSheet) {
|
|
350
|
+
if (skipSheet || bandSpan) {
|
|
201
351
|
if (isRange) {
|
|
202
352
|
newTokens.push(token, tokens[i + 1], tokens[i + 2]);
|
|
203
353
|
i += 2;
|
|
@@ -224,7 +374,7 @@ export const expandRangesInFormula = (formula, insertionIndex, count, dimension,
|
|
|
224
374
|
if (dimension === "row" &&
|
|
225
375
|
token.tokenType.name === tokenVocabulary.Number.name) {
|
|
226
376
|
const isRange = isRangeToken(tokens[i + 1]);
|
|
227
|
-
if (skipSheet) {
|
|
377
|
+
if (skipSheet || bandSpan) {
|
|
228
378
|
if (isRange) {
|
|
229
379
|
newTokens.push(token, tokens[i + 1], tokens[i + 2]);
|
|
230
380
|
i += 2;
|
|
@@ -257,22 +407,45 @@ export const expandRangesInFormula = (formula, insertionIndex, count, dimension,
|
|
|
257
407
|
* @param deletionIndex The row or column index where deletion starts
|
|
258
408
|
* @param count Number of rows or columns being deleted
|
|
259
409
|
* @param dimension Whether deleting 'row' or 'column'
|
|
260
|
-
* @param activeSheetName
|
|
410
|
+
* @param activeSheetName Name of the EDITED sheet: only references that resolve
|
|
411
|
+
* to it are touched
|
|
412
|
+
* @param options `includeUnqualifiedReferences` — see `expandRangesInFormula`
|
|
261
413
|
* @returns The formula with contracted ranges, or undefined if no changes
|
|
414
|
+
*
|
|
415
|
+
* Excel semantics for the deleted band: a range whose rows/columns are ALL
|
|
416
|
+
* deleted becomes the `#REF!` token (`=SUM(A2:A3)` → `=SUM(#REF!)`); a single
|
|
417
|
+
* cell reference into the band becomes `#REF!` (`=A3+1` → `=#REF!+1`, a spill
|
|
418
|
+
* reference `=C1#` → `=#REF!#`); a partially covered range shrinks.
|
|
262
419
|
*/
|
|
263
|
-
export const contractRangesInFormula = (formula, deletionIndex, count, dimension, activeSheetName) => {
|
|
420
|
+
export const contractRangesInFormula = (formula, deletionIndex, count, dimension, activeSheetName, options = {}) => {
|
|
264
421
|
if (!formula || count <= 0) {
|
|
265
422
|
return formula;
|
|
266
423
|
}
|
|
424
|
+
const { includeUnqualifiedReferences = true, bandSpan } = options;
|
|
267
425
|
const normalizedActiveSheetName = activeSheetName.toLowerCase();
|
|
268
426
|
const tokens = cloneTokens(lexify(formula).tokens);
|
|
269
427
|
const newTokens = [];
|
|
270
428
|
let hasChanges = false;
|
|
271
429
|
const deletionEnd = deletionIndex + count - 1;
|
|
430
|
+
const inDeletedBand = (cell) => {
|
|
431
|
+
const index = dimension === "row" ? cell.rowIndex : cell.columnIndex;
|
|
432
|
+
return index >= deletionIndex && index <= deletionEnd;
|
|
433
|
+
};
|
|
434
|
+
const overlapsBand = (start, end) => deletionIndex <= end && deletionEnd >= start;
|
|
435
|
+
// The survivors of a span that overlaps (but is not swallowed by) the band:
|
|
436
|
+
// whatever lay before the band keeps its start, whatever lay inside starts at
|
|
437
|
+
// the deletion point; an end past the band moves up by the full count, an
|
|
438
|
+
// end inside the band stops just before it.
|
|
439
|
+
const contractSpan = (start, end) => ({
|
|
440
|
+
start: Math.min(start, deletionIndex),
|
|
441
|
+
end: end > deletionEnd ? end - count : deletionIndex - 1,
|
|
442
|
+
});
|
|
272
443
|
for (let i = 0; i < tokens.length; i++) {
|
|
273
444
|
const token = tokens[i];
|
|
274
445
|
const sheetName = getSheetNameFromToken(tokens[i - 1]);
|
|
275
|
-
const skipSheet = sheetName
|
|
446
|
+
const skipSheet = sheetName
|
|
447
|
+
? sheetName.toLowerCase() !== normalizedActiveSheetName
|
|
448
|
+
: !includeUnqualifiedReferences;
|
|
276
449
|
// Handle cell ranges (e.g., A1:A10)
|
|
277
450
|
if (token.tokenType.name === tokenVocabulary.Cell.name) {
|
|
278
451
|
const isRange = isRangeToken(tokens[i + 1]);
|
|
@@ -284,10 +457,33 @@ export const contractRangesInFormula = (formula, deletionIndex, count, dimension
|
|
|
284
457
|
}
|
|
285
458
|
continue;
|
|
286
459
|
}
|
|
460
|
+
if (!isRange) {
|
|
461
|
+
// Single cell: broken when it sits in the deleted band. The trailing
|
|
462
|
+
// spilled-range operator (`C1#`) stays on the token (`#REF!#`).
|
|
463
|
+
const spill = token.image.endsWith("#") ? "#" : "";
|
|
464
|
+
const cell = addressToCell(stripPropertyFromCellRef(spill ? token.image.slice(0, -1) : token.image));
|
|
465
|
+
if (cell &&
|
|
466
|
+
inDeletedBand(cell) &&
|
|
467
|
+
isRangeWithinBandSpan(cell, cell, dimension, bandSpan)) {
|
|
468
|
+
token.image = `${REF_ERROR_TOKEN}${spill}`;
|
|
469
|
+
hasChanges = true;
|
|
470
|
+
}
|
|
471
|
+
newTokens.push(token);
|
|
472
|
+
continue;
|
|
473
|
+
}
|
|
287
474
|
if (isRange && tokens[i + 2]) {
|
|
288
475
|
const startCell = addressToCell(stripPropertyFromCellRef(token.image));
|
|
289
476
|
const endCell = addressToCell(stripPropertyFromCellRef(tokens[i + 2].image));
|
|
290
|
-
if (startCell &&
|
|
477
|
+
if (startCell &&
|
|
478
|
+
endCell &&
|
|
479
|
+
isRangeWithinBandSpan(startCell, endCell, dimension, bandSpan)) {
|
|
480
|
+
// Every row/column of the range deleted → the whole range is #REF!.
|
|
481
|
+
if (inDeletedBand(startCell) && inDeletedBand(endCell)) {
|
|
482
|
+
newTokens.push(collapseToRefError(token, tokens[i + 2]));
|
|
483
|
+
i += 2;
|
|
484
|
+
hasChanges = true;
|
|
485
|
+
continue;
|
|
486
|
+
}
|
|
291
487
|
const isAbsoluteStartRow = isAbsoluteRowReference(token.image);
|
|
292
488
|
const isAbsoluteEndRow = isAbsoluteRowReference(tokens[i + 2].image);
|
|
293
489
|
const isAbsoluteStartCol = isAbsoluteColumnReference(token.image);
|
|
@@ -295,18 +491,10 @@ export const contractRangesInFormula = (formula, deletionIndex, count, dimension
|
|
|
295
491
|
if (dimension === "row") {
|
|
296
492
|
const rangeStart = startCell.rowIndex;
|
|
297
493
|
const rangeEnd = endCell.rowIndex;
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
const overlapEnd = Math.min(deletionEnd, rangeEnd);
|
|
303
|
-
const rowsDeletedInRange = overlapEnd - overlapStart + 1;
|
|
304
|
-
// Contract the end of the range
|
|
305
|
-
endCell.rowIndex -= rowsDeletedInRange;
|
|
306
|
-
// If entire range is deleted, result in #REF! (but we'll just contract to minimum)
|
|
307
|
-
if (endCell.rowIndex < startCell.rowIndex) {
|
|
308
|
-
endCell.rowIndex = startCell.rowIndex;
|
|
309
|
-
}
|
|
494
|
+
if (overlapsBand(rangeStart, rangeEnd)) {
|
|
495
|
+
const survivors = contractSpan(rangeStart, rangeEnd);
|
|
496
|
+
startCell.rowIndex = survivors.start;
|
|
497
|
+
endCell.rowIndex = survivors.end;
|
|
310
498
|
const newStartAddress = cellToAddress(startCell, isAbsoluteStartCol, isAbsoluteStartRow);
|
|
311
499
|
const newEndAddress = cellToAddress(endCell, isAbsoluteEndCol, isAbsoluteEndRow);
|
|
312
500
|
if (newStartAddress && newEndAddress) {
|
|
@@ -320,18 +508,10 @@ export const contractRangesInFormula = (formula, deletionIndex, count, dimension
|
|
|
320
508
|
// dimension === 'column'
|
|
321
509
|
const rangeStart = startCell.columnIndex;
|
|
322
510
|
const rangeEnd = endCell.columnIndex;
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
const overlapEnd = Math.min(deletionEnd, rangeEnd);
|
|
328
|
-
const colsDeletedInRange = overlapEnd - overlapStart + 1;
|
|
329
|
-
// Contract the end of the range
|
|
330
|
-
endCell.columnIndex -= colsDeletedInRange;
|
|
331
|
-
// If entire range is deleted, result in #REF! (but we'll just contract to minimum)
|
|
332
|
-
if (endCell.columnIndex < startCell.columnIndex) {
|
|
333
|
-
endCell.columnIndex = startCell.columnIndex;
|
|
334
|
-
}
|
|
511
|
+
if (overlapsBand(rangeStart, rangeEnd)) {
|
|
512
|
+
const survivors = contractSpan(rangeStart, rangeEnd);
|
|
513
|
+
startCell.columnIndex = survivors.start;
|
|
514
|
+
endCell.columnIndex = survivors.end;
|
|
335
515
|
const newStartAddress = cellToAddress(startCell, isAbsoluteStartCol, isAbsoluteStartRow);
|
|
336
516
|
const newEndAddress = cellToAddress(endCell, isAbsoluteEndCol, isAbsoluteEndRow);
|
|
337
517
|
if (newStartAddress && newEndAddress) {
|
|
@@ -351,7 +531,7 @@ export const contractRangesInFormula = (formula, deletionIndex, count, dimension
|
|
|
351
531
|
if (dimension === "column" &&
|
|
352
532
|
token.tokenType.name === tokenVocabulary.Column.name) {
|
|
353
533
|
const isRange = isRangeToken(tokens[i + 1]);
|
|
354
|
-
if (skipSheet) {
|
|
534
|
+
if (skipSheet || bandSpan) {
|
|
355
535
|
if (isRange) {
|
|
356
536
|
newTokens.push(token, tokens[i + 1], tokens[i + 2]);
|
|
357
537
|
i += 2;
|
|
@@ -361,20 +541,19 @@ export const contractRangesInFormula = (formula, deletionIndex, count, dimension
|
|
|
361
541
|
if (isRange && tokens[i + 2]) {
|
|
362
542
|
const startColumnIndex = alpha2number(token.image);
|
|
363
543
|
const endColumnIndex = alpha2number(tokens[i + 2].image);
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
const colsDeletedInRange = overlapEnd - overlapStart + 1;
|
|
371
|
-
// Contract the end column
|
|
372
|
-
const newEndColumnIndex = endColumnIndex - colsDeletedInRange;
|
|
373
|
-
// If entire range is deleted, keep at minimum
|
|
374
|
-
if (newEndColumnIndex >= startColumnIndex) {
|
|
375
|
-
tokens[i + 2].image = number2Alpha(newEndColumnIndex - 1);
|
|
544
|
+
if (overlapsBand(startColumnIndex, endColumnIndex)) {
|
|
545
|
+
// Every column deleted → #REF!; otherwise the survivors close up.
|
|
546
|
+
if (startColumnIndex >= deletionIndex &&
|
|
547
|
+
endColumnIndex <= deletionEnd) {
|
|
548
|
+
newTokens.push(collapseToRefError(token, tokens[i + 2]));
|
|
549
|
+
i += 2;
|
|
376
550
|
hasChanges = true;
|
|
551
|
+
continue;
|
|
377
552
|
}
|
|
553
|
+
const survivors = contractSpan(startColumnIndex, endColumnIndex);
|
|
554
|
+
token.image = number2Alpha(survivors.start - 1);
|
|
555
|
+
tokens[i + 2].image = number2Alpha(survivors.end - 1);
|
|
556
|
+
hasChanges = true;
|
|
378
557
|
}
|
|
379
558
|
newTokens.push(token, tokens[i + 1], tokens[i + 2]);
|
|
380
559
|
i += 2;
|
|
@@ -385,7 +564,7 @@ export const contractRangesInFormula = (formula, deletionIndex, count, dimension
|
|
|
385
564
|
if (dimension === "row" &&
|
|
386
565
|
token.tokenType.name === tokenVocabulary.Number.name) {
|
|
387
566
|
const isRange = isRangeToken(tokens[i + 1]);
|
|
388
|
-
if (skipSheet) {
|
|
567
|
+
if (skipSheet || bandSpan) {
|
|
389
568
|
if (isRange) {
|
|
390
569
|
newTokens.push(token, tokens[i + 1], tokens[i + 2]);
|
|
391
570
|
i += 2;
|
|
@@ -397,19 +576,18 @@ export const contractRangesInFormula = (formula, deletionIndex, count, dimension
|
|
|
397
576
|
tokens[i + 2].tokenType.name === tokenVocabulary.Number.name) {
|
|
398
577
|
const startRowIndex = Number(token.image);
|
|
399
578
|
const endRowIndex = Number(tokens[i + 2].image);
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
const rowsDeletedInRange = overlapEnd - overlapStart + 1;
|
|
406
|
-
// Contract the end row
|
|
407
|
-
const newEndRowIndex = endRowIndex - rowsDeletedInRange;
|
|
408
|
-
// If entire range is deleted, keep at minimum
|
|
409
|
-
if (newEndRowIndex >= startRowIndex) {
|
|
410
|
-
tokens[i + 2].image = String(newEndRowIndex);
|
|
579
|
+
if (overlapsBand(startRowIndex, endRowIndex)) {
|
|
580
|
+
// Every row deleted → #REF!; otherwise the survivors close up.
|
|
581
|
+
if (startRowIndex >= deletionIndex && endRowIndex <= deletionEnd) {
|
|
582
|
+
newTokens.push(collapseToRefError(token, tokens[i + 2]));
|
|
583
|
+
i += 2;
|
|
411
584
|
hasChanges = true;
|
|
585
|
+
continue;
|
|
412
586
|
}
|
|
587
|
+
const survivors = contractSpan(startRowIndex, endRowIndex);
|
|
588
|
+
token.image = String(survivors.start);
|
|
589
|
+
tokens[i + 2].image = String(survivors.end);
|
|
590
|
+
hasChanges = true;
|
|
413
591
|
}
|
|
414
592
|
newTokens.push(token, tokens[i + 1], tokens[i + 2]);
|
|
415
593
|
i += 2;
|
package/dist/esm/api/merges.d.ts
CHANGED
|
@@ -4,4 +4,12 @@ export declare const shrinkMergesOnRowDelete: (merges: GridRange[], deleteStart:
|
|
|
4
4
|
export declare const shrinkMergesOnColumnDelete: (merges: GridRange[], deleteStart: number, deleteCount: number) => GridRange[];
|
|
5
5
|
export declare const expandMergesOnRowInsert: (merges: GridRange[], insertIndex: number, count: number) => GridRange[];
|
|
6
6
|
export declare const expandMergesOnColumnInsert: (merges: GridRange[], insertIndex: number, count: number) => GridRange[];
|
|
7
|
+
/**
|
|
8
|
+
* Merges that travel with an insert/delete-cells shift: a merge wholly inside the shifted band
|
|
9
|
+
* (rows `>= start` in the band's columns for a vertical shift; columns `>= start` in its rows for a
|
|
10
|
+
* horizontal one) moves by `count`; on a delete, a merge wholly inside the deleted block is dropped
|
|
11
|
+
* with it. Merges the band only partially covers are refused upstream (`findTornMerge`) — this
|
|
12
|
+
* helper never tears one, it leaves anything outside the band untouched.
|
|
13
|
+
*/
|
|
14
|
+
export declare const shiftMergesInBand: (merges: GridRange[] | undefined | null, axis: "row" | "column", start: number, count: number, insert: boolean, spanStart: number, spanEnd: number) => GridRange[] | null | undefined;
|
|
7
15
|
//# sourceMappingURL=merges.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"merges.d.ts","sourceRoot":"","sources":["../../../api/merges.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAY5D,eAAO,MAAM,eAAe,GAC1B,aAAa,SAAS,EAAE,GAAG,SAAS,GAAG,IAAI,EAC3C,IAAI,eAAe,GAAG,YAAY,GAAG,eAAe,GAAG,YAAY,EACnE,gBAAgB,MAAM,EACtB,cAAS,EACT,YAAY,CAAC,IAAI,EAAE,SAAS,KAAK,OAAO,mCA2DzC,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAClC,QAAQ,SAAS,EAAE,EACnB,aAAa,MAAM,EACnB,aAAa,MAAM,gBA8CpB,CAAC;AAEF,eAAO,MAAM,0BAA0B,GACrC,QAAQ,SAAS,EAAE,EACnB,aAAa,MAAM,EACnB,aAAa,MAAM,gBA8CpB,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAClC,QAAQ,SAAS,EAAE,EACnB,aAAa,MAAM,EACnB,OAAO,MAAM,gBAyBd,CAAC;AAEF,eAAO,MAAM,0BAA0B,GACrC,QAAQ,SAAS,EAAE,EACnB,aAAa,MAAM,EACnB,OAAO,MAAM,gBAyBd,CAAC"}
|
|
1
|
+
{"version":3,"file":"merges.d.ts","sourceRoot":"","sources":["../../../api/merges.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAY5D,eAAO,MAAM,eAAe,GAC1B,aAAa,SAAS,EAAE,GAAG,SAAS,GAAG,IAAI,EAC3C,IAAI,eAAe,GAAG,YAAY,GAAG,eAAe,GAAG,YAAY,EACnE,gBAAgB,MAAM,EACtB,cAAS,EACT,YAAY,CAAC,IAAI,EAAE,SAAS,KAAK,OAAO,mCA2DzC,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAClC,QAAQ,SAAS,EAAE,EACnB,aAAa,MAAM,EACnB,aAAa,MAAM,gBA8CpB,CAAC;AAEF,eAAO,MAAM,0BAA0B,GACrC,QAAQ,SAAS,EAAE,EACnB,aAAa,MAAM,EACnB,aAAa,MAAM,gBA8CpB,CAAC;AAEF,eAAO,MAAM,uBAAuB,GAClC,QAAQ,SAAS,EAAE,EACnB,aAAa,MAAM,EACnB,OAAO,MAAM,gBAyBd,CAAC;AAEF,eAAO,MAAM,0BAA0B,GACrC,QAAQ,SAAS,EAAE,EACnB,aAAa,MAAM,EACnB,OAAO,MAAM,gBAyBd,CAAC;AAEF;;;;;;GAMG;AACH,eAAO,MAAM,iBAAiB,GAC5B,QAAQ,SAAS,EAAE,GAAG,SAAS,GAAG,IAAI,EACtC,MAAM,KAAK,GAAG,QAAQ,EACtB,OAAO,MAAM,EACb,OAAO,MAAM,EACb,QAAQ,OAAO,EACf,WAAW,MAAM,EACjB,SAAS,MAAM,mCAkDhB,CAAC"}
|
package/dist/esm/api/merges.js
CHANGED
|
@@ -172,3 +172,54 @@ export const expandMergesOnColumnInsert = (merges, insertIndex, count) => {
|
|
|
172
172
|
})
|
|
173
173
|
.filter((merge) => !isSingleCellMerge(merge));
|
|
174
174
|
};
|
|
175
|
+
/**
|
|
176
|
+
* Merges that travel with an insert/delete-cells shift: a merge wholly inside the shifted band
|
|
177
|
+
* (rows `>= start` in the band's columns for a vertical shift; columns `>= start` in its rows for a
|
|
178
|
+
* horizontal one) moves by `count`; on a delete, a merge wholly inside the deleted block is dropped
|
|
179
|
+
* with it. Merges the band only partially covers are refused upstream (`findTornMerge`) — this
|
|
180
|
+
* helper never tears one, it leaves anything outside the band untouched.
|
|
181
|
+
*/
|
|
182
|
+
export const shiftMergesInBand = (merges, axis, start, count, insert, spanStart, spanEnd) => {
|
|
183
|
+
if (!merges || count <= 0) {
|
|
184
|
+
return merges;
|
|
185
|
+
}
|
|
186
|
+
const deletedEnd = start + count - 1;
|
|
187
|
+
const inSpan = axis === "row"
|
|
188
|
+
? (merge) => merge.startColumnIndex >= spanStart && merge.endColumnIndex <= spanEnd
|
|
189
|
+
: (merge) => merge.startRowIndex >= spanStart && merge.endRowIndex <= spanEnd;
|
|
190
|
+
const lineStart = (merge) => axis === "row" ? merge.startRowIndex : merge.startColumnIndex;
|
|
191
|
+
const lineEnd = (merge) => axis === "row" ? merge.endRowIndex : merge.endColumnIndex;
|
|
192
|
+
const moved = (merge, delta) => axis === "row"
|
|
193
|
+
? {
|
|
194
|
+
...merge,
|
|
195
|
+
startRowIndex: merge.startRowIndex + delta,
|
|
196
|
+
endRowIndex: merge.endRowIndex + delta,
|
|
197
|
+
}
|
|
198
|
+
: {
|
|
199
|
+
...merge,
|
|
200
|
+
startColumnIndex: merge.startColumnIndex + delta,
|
|
201
|
+
endColumnIndex: merge.endColumnIndex + delta,
|
|
202
|
+
};
|
|
203
|
+
const next = [];
|
|
204
|
+
for (const merge of merges) {
|
|
205
|
+
if (!inSpan(merge) || lineStart(merge) < start) {
|
|
206
|
+
next.push(merge);
|
|
207
|
+
continue;
|
|
208
|
+
}
|
|
209
|
+
if (insert) {
|
|
210
|
+
next.push(moved(merge, count));
|
|
211
|
+
continue;
|
|
212
|
+
}
|
|
213
|
+
if (lineEnd(merge) <= deletedEnd) {
|
|
214
|
+
// Wholly inside the deleted block — removed with it.
|
|
215
|
+
continue;
|
|
216
|
+
}
|
|
217
|
+
if (lineStart(merge) > deletedEnd) {
|
|
218
|
+
next.push(moved(merge, -count));
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
// Straddles the deleted block's edge: refused upstream; never tear it here.
|
|
222
|
+
next.push(merge);
|
|
223
|
+
}
|
|
224
|
+
return next;
|
|
225
|
+
};
|