@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.
Files changed (201) hide show
  1. package/dist/cjs/api/anchored-objects.d.ts +66 -0
  2. package/dist/cjs/api/anchored-objects.d.ts.map +1 -0
  3. package/dist/cjs/api/anchored-objects.js +259 -0
  4. package/dist/cjs/api/dependency.d.ts +13 -2
  5. package/dist/cjs/api/dependency.d.ts.map +1 -1
  6. package/dist/cjs/api/dependency.js +11 -5
  7. package/dist/cjs/api/formula.d.ts +61 -4
  8. package/dist/cjs/api/formula.d.ts.map +1 -1
  9. package/dist/cjs/api/formula.js +250 -61
  10. package/dist/cjs/api/merges.d.ts +8 -0
  11. package/dist/cjs/api/merges.d.ts.map +1 -1
  12. package/dist/cjs/api/merges.js +64 -1
  13. package/dist/cjs/api/named-range-structural.d.ts +62 -0
  14. package/dist/cjs/api/named-range-structural.d.ts.map +1 -0
  15. package/dist/cjs/api/named-range-structural.js +197 -0
  16. package/dist/cjs/api/range-rule-helpers.d.ts +45 -1
  17. package/dist/cjs/api/range-rule-helpers.d.ts.map +1 -1
  18. package/dist/cjs/api/range-rule-helpers.js +159 -6
  19. package/dist/cjs/api/structural-formula-update.d.ts.map +1 -1
  20. package/dist/cjs/api/structural-formula-update.js +24 -6
  21. package/dist/cjs/api/structural-guards.d.ts +78 -0
  22. package/dist/cjs/api/structural-guards.d.ts.map +1 -0
  23. package/dist/cjs/api/structural-guards.js +202 -0
  24. package/dist/cjs/api/structural-table-refs.d.ts +94 -0
  25. package/dist/cjs/api/structural-table-refs.d.ts.map +1 -0
  26. package/dist/cjs/api/structural-table-refs.js +313 -0
  27. package/dist/cjs/constants.d.ts +25 -0
  28. package/dist/cjs/constants.d.ts.map +1 -0
  29. package/dist/cjs/constants.js +26 -0
  30. package/dist/cjs/hooks/use-calculation-worker.d.ts.map +1 -1
  31. package/dist/cjs/hooks/use-calculation-worker.js +64 -6
  32. package/dist/cjs/hooks/use-calculation.d.ts +7 -0
  33. package/dist/cjs/hooks/use-calculation.d.ts.map +1 -1
  34. package/dist/cjs/hooks/use-calculation.js +15 -0
  35. package/dist/cjs/hooks/use-on-calculation-success.d.ts +7 -0
  36. package/dist/cjs/hooks/use-on-calculation-success.d.ts.map +1 -1
  37. package/dist/cjs/hooks/use-on-calculation-success.js +7 -1
  38. package/dist/cjs/hooks/use-on-delete-cells-shift-left.d.ts +13 -3
  39. package/dist/cjs/hooks/use-on-delete-cells-shift-left.d.ts.map +1 -1
  40. package/dist/cjs/hooks/use-on-delete-cells-shift-left.js +27 -2
  41. package/dist/cjs/hooks/use-on-delete-cells-shift-up.d.ts +13 -3
  42. package/dist/cjs/hooks/use-on-delete-cells-shift-up.d.ts.map +1 -1
  43. package/dist/cjs/hooks/use-on-delete-cells-shift-up.js +27 -2
  44. package/dist/cjs/hooks/use-on-delete-column.d.ts +13 -4
  45. package/dist/cjs/hooks/use-on-delete-column.d.ts.map +1 -1
  46. package/dist/cjs/hooks/use-on-delete-column.js +14 -2
  47. package/dist/cjs/hooks/use-on-delete-row.d.ts +13 -4
  48. package/dist/cjs/hooks/use-on-delete-row.d.ts.map +1 -1
  49. package/dist/cjs/hooks/use-on-delete-row.js +14 -2
  50. package/dist/cjs/hooks/use-on-insert-cells-shift-down.d.ts +13 -3
  51. package/dist/cjs/hooks/use-on-insert-cells-shift-down.d.ts.map +1 -1
  52. package/dist/cjs/hooks/use-on-insert-cells-shift-down.js +27 -2
  53. package/dist/cjs/hooks/use-on-insert-cells-shift-right.d.ts +13 -3
  54. package/dist/cjs/hooks/use-on-insert-cells-shift-right.d.ts.map +1 -1
  55. package/dist/cjs/hooks/use-on-insert-cells-shift-right.js +27 -2
  56. package/dist/cjs/hooks/use-on-insert-column.d.ts +10 -3
  57. package/dist/cjs/hooks/use-on-insert-column.d.ts.map +1 -1
  58. package/dist/cjs/hooks/use-on-insert-column.js +33 -7
  59. package/dist/cjs/hooks/use-on-insert-row.d.ts +10 -3
  60. package/dist/cjs/hooks/use-on-insert-row.d.ts.map +1 -1
  61. package/dist/cjs/hooks/use-on-insert-row.js +32 -7
  62. package/dist/cjs/hooks/use-spreadsheet-state.d.ts +2 -2
  63. package/dist/cjs/hooks/use-spreadsheet-state.d.ts.map +1 -1
  64. package/dist/cjs/hooks/use-spreadsheet-state.js +61 -15
  65. package/dist/cjs/hooks/use-structured-reference.d.ts.map +1 -1
  66. package/dist/cjs/hooks/use-structured-reference.js +10 -6
  67. package/dist/cjs/index.d.ts +3 -0
  68. package/dist/cjs/index.d.ts.map +1 -1
  69. package/dist/cjs/index.js +3 -0
  70. package/dist/cjs/interface/calculation-interface.d.ts +9 -0
  71. package/dist/cjs/interface/calculation-interface.d.ts.map +1 -1
  72. package/dist/cjs/interface/calculation-interface.js +31 -2
  73. package/dist/cjs/interface/delete-cells-shift-left.d.ts +8 -1
  74. package/dist/cjs/interface/delete-cells-shift-left.d.ts.map +1 -1
  75. package/dist/cjs/interface/delete-cells-shift-left.js +158 -80
  76. package/dist/cjs/interface/delete-cells-shift-up.d.ts +9 -1
  77. package/dist/cjs/interface/delete-cells-shift-up.d.ts.map +1 -1
  78. package/dist/cjs/interface/delete-cells-shift-up.js +155 -73
  79. package/dist/cjs/interface/delete-column.d.ts +1 -1
  80. package/dist/cjs/interface/delete-column.d.ts.map +1 -1
  81. package/dist/cjs/interface/delete-column.js +90 -31
  82. package/dist/cjs/interface/delete-row.d.ts +1 -1
  83. package/dist/cjs/interface/delete-row.d.ts.map +1 -1
  84. package/dist/cjs/interface/delete-row.js +85 -26
  85. package/dist/cjs/interface/insert-cells-shift-down.d.ts +10 -1
  86. package/dist/cjs/interface/insert-cells-shift-down.d.ts.map +1 -1
  87. package/dist/cjs/interface/insert-cells-shift-down.js +147 -52
  88. package/dist/cjs/interface/insert-cells-shift-right.d.ts +10 -1
  89. package/dist/cjs/interface/insert-cells-shift-right.d.ts.map +1 -1
  90. package/dist/cjs/interface/insert-cells-shift-right.js +154 -58
  91. package/dist/cjs/interface/insert-column.d.ts +1 -1
  92. package/dist/cjs/interface/insert-column.d.ts.map +1 -1
  93. package/dist/cjs/interface/insert-column.js +46 -8
  94. package/dist/cjs/interface/insert-row.d.ts +1 -1
  95. package/dist/cjs/interface/insert-row.d.ts.map +1 -1
  96. package/dist/cjs/interface/insert-row.js +47 -10
  97. package/dist/cjs/interface/spreadsheet-interface.d.ts.map +1 -1
  98. package/dist/cjs/interface/spreadsheet-interface.js +33 -0
  99. package/dist/cjs/sheet-cell.d.ts.map +1 -1
  100. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  101. package/dist/esm/api/anchored-objects.d.ts +66 -0
  102. package/dist/esm/api/anchored-objects.d.ts.map +1 -0
  103. package/dist/esm/api/anchored-objects.js +212 -0
  104. package/dist/esm/api/dependency.d.ts +13 -2
  105. package/dist/esm/api/dependency.d.ts.map +1 -1
  106. package/dist/esm/api/dependency.js +10 -4
  107. package/dist/esm/api/formula.d.ts +61 -4
  108. package/dist/esm/api/formula.d.ts.map +1 -1
  109. package/dist/esm/api/formula.js +238 -60
  110. package/dist/esm/api/merges.d.ts +8 -0
  111. package/dist/esm/api/merges.d.ts.map +1 -1
  112. package/dist/esm/api/merges.js +51 -0
  113. package/dist/esm/api/named-range-structural.d.ts +62 -0
  114. package/dist/esm/api/named-range-structural.d.ts.map +1 -0
  115. package/dist/esm/api/named-range-structural.js +176 -0
  116. package/dist/esm/api/range-rule-helpers.d.ts +45 -1
  117. package/dist/esm/api/range-rule-helpers.d.ts.map +1 -1
  118. package/dist/esm/api/range-rule-helpers.js +153 -1
  119. package/dist/esm/api/structural-formula-update.d.ts.map +1 -1
  120. package/dist/esm/api/structural-formula-update.js +24 -6
  121. package/dist/esm/api/structural-guards.d.ts +78 -0
  122. package/dist/esm/api/structural-guards.d.ts.map +1 -0
  123. package/dist/esm/api/structural-guards.js +186 -0
  124. package/dist/esm/api/structural-table-refs.d.ts +94 -0
  125. package/dist/esm/api/structural-table-refs.d.ts.map +1 -0
  126. package/dist/esm/api/structural-table-refs.js +219 -0
  127. package/dist/esm/constants.d.ts +25 -0
  128. package/dist/esm/constants.d.ts.map +1 -0
  129. package/dist/esm/constants.js +26 -0
  130. package/dist/esm/hooks/use-calculation-worker.d.ts.map +1 -1
  131. package/dist/esm/hooks/use-calculation-worker.js +45 -0
  132. package/dist/esm/hooks/use-calculation.d.ts +7 -0
  133. package/dist/esm/hooks/use-calculation.d.ts.map +1 -1
  134. package/dist/esm/hooks/use-calculation.js +15 -0
  135. package/dist/esm/hooks/use-on-calculation-success.d.ts +7 -0
  136. package/dist/esm/hooks/use-on-calculation-success.d.ts.map +1 -1
  137. package/dist/esm/hooks/use-on-calculation-success.js +7 -1
  138. package/dist/esm/hooks/use-on-delete-cells-shift-left.d.ts +13 -3
  139. package/dist/esm/hooks/use-on-delete-cells-shift-left.d.ts.map +1 -1
  140. package/dist/esm/hooks/use-on-delete-cells-shift-left.js +26 -2
  141. package/dist/esm/hooks/use-on-delete-cells-shift-up.d.ts +13 -3
  142. package/dist/esm/hooks/use-on-delete-cells-shift-up.d.ts.map +1 -1
  143. package/dist/esm/hooks/use-on-delete-cells-shift-up.js +26 -2
  144. package/dist/esm/hooks/use-on-delete-column.d.ts +13 -4
  145. package/dist/esm/hooks/use-on-delete-column.d.ts.map +1 -1
  146. package/dist/esm/hooks/use-on-delete-column.js +14 -2
  147. package/dist/esm/hooks/use-on-delete-row.d.ts +13 -4
  148. package/dist/esm/hooks/use-on-delete-row.d.ts.map +1 -1
  149. package/dist/esm/hooks/use-on-delete-row.js +14 -2
  150. package/dist/esm/hooks/use-on-insert-cells-shift-down.d.ts +13 -3
  151. package/dist/esm/hooks/use-on-insert-cells-shift-down.d.ts.map +1 -1
  152. package/dist/esm/hooks/use-on-insert-cells-shift-down.js +26 -2
  153. package/dist/esm/hooks/use-on-insert-cells-shift-right.d.ts +13 -3
  154. package/dist/esm/hooks/use-on-insert-cells-shift-right.d.ts.map +1 -1
  155. package/dist/esm/hooks/use-on-insert-cells-shift-right.js +26 -2
  156. package/dist/esm/hooks/use-on-insert-column.d.ts +10 -3
  157. package/dist/esm/hooks/use-on-insert-column.d.ts.map +1 -1
  158. package/dist/esm/hooks/use-on-insert-column.js +33 -7
  159. package/dist/esm/hooks/use-on-insert-row.d.ts +10 -3
  160. package/dist/esm/hooks/use-on-insert-row.d.ts.map +1 -1
  161. package/dist/esm/hooks/use-on-insert-row.js +32 -7
  162. package/dist/esm/hooks/use-spreadsheet-state.d.ts +2 -2
  163. package/dist/esm/hooks/use-spreadsheet-state.d.ts.map +1 -1
  164. package/dist/esm/hooks/use-spreadsheet-state.js +61 -15
  165. package/dist/esm/hooks/use-structured-reference.d.ts.map +1 -1
  166. package/dist/esm/hooks/use-structured-reference.js +10 -6
  167. package/dist/esm/index.d.ts +3 -0
  168. package/dist/esm/index.d.ts.map +1 -1
  169. package/dist/esm/index.js +3 -0
  170. package/dist/esm/interface/calculation-interface.d.ts +9 -0
  171. package/dist/esm/interface/calculation-interface.d.ts.map +1 -1
  172. package/dist/esm/interface/calculation-interface.js +33 -2
  173. package/dist/esm/interface/delete-cells-shift-left.d.ts +8 -1
  174. package/dist/esm/interface/delete-cells-shift-left.d.ts.map +1 -1
  175. package/dist/esm/interface/delete-cells-shift-left.js +135 -59
  176. package/dist/esm/interface/delete-cells-shift-up.d.ts +9 -1
  177. package/dist/esm/interface/delete-cells-shift-up.d.ts.map +1 -1
  178. package/dist/esm/interface/delete-cells-shift-up.js +133 -53
  179. package/dist/esm/interface/delete-column.d.ts +1 -1
  180. package/dist/esm/interface/delete-column.d.ts.map +1 -1
  181. package/dist/esm/interface/delete-column.js +80 -11
  182. package/dist/esm/interface/delete-row.d.ts +1 -1
  183. package/dist/esm/interface/delete-row.d.ts.map +1 -1
  184. package/dist/esm/interface/delete-row.js +81 -11
  185. package/dist/esm/interface/insert-cells-shift-down.d.ts +10 -1
  186. package/dist/esm/interface/insert-cells-shift-down.d.ts.map +1 -1
  187. package/dist/esm/interface/insert-cells-shift-down.js +116 -47
  188. package/dist/esm/interface/insert-cells-shift-right.d.ts +10 -1
  189. package/dist/esm/interface/insert-cells-shift-right.d.ts.map +1 -1
  190. package/dist/esm/interface/insert-cells-shift-right.js +120 -51
  191. package/dist/esm/interface/insert-column.d.ts +1 -1
  192. package/dist/esm/interface/insert-column.d.ts.map +1 -1
  193. package/dist/esm/interface/insert-column.js +46 -8
  194. package/dist/esm/interface/insert-row.d.ts +1 -1
  195. package/dist/esm/interface/insert-row.d.ts.map +1 -1
  196. package/dist/esm/interface/insert-row.js +47 -10
  197. package/dist/esm/interface/spreadsheet-interface.d.ts.map +1 -1
  198. package/dist/esm/interface/spreadsheet-interface.js +29 -0
  199. package/dist/esm/sheet-cell.d.ts.map +1 -1
  200. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  201. 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 Active sheet name to scope range updates
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 Active sheet name to scope range updates
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":"AAMA,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;;;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;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,GAChC,SAAS,MAAM,GAAG,SAAS,EAC3B,gBAAgB,MAAM,EACtB,OAAO,MAAM,EACb,WAAW,KAAK,GAAG,QAAQ,EAC3B,iBAAiB,MAAM,KACtB,MAAM,GAAG,SAmKX,CAAC;AAEF;;;;;;;;GAQG;AACH,eAAO,MAAM,uBAAuB,GAClC,SAAS,MAAM,GAAG,SAAS,EAC3B,eAAe,MAAM,EACrB,OAAO,MAAM,EACb,WAAW,KAAK,GAAG,QAAQ,EAC3B,iBAAiB,MAAM,KACtB,MAAM,GAAG,SA6NX,CAAC"}
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"}
@@ -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 Active sheet name to scope range updates
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 && sheetName.toLowerCase() !== normalizedActiveSheetName;
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 && endCell) {
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 Active sheet name to scope range updates
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 && sheetName.toLowerCase() !== normalizedActiveSheetName;
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 && endCell) {
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
- // Check if deletion overlaps with range
299
- if (deletionIndex <= rangeEnd && deletionEnd >= rangeStart) {
300
- // Calculate how many deleted rows are within the range
301
- const overlapStart = Math.max(deletionIndex, rangeStart);
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
- // Check if deletion overlaps with range
324
- if (deletionIndex <= rangeEnd && deletionEnd >= rangeStart) {
325
- // Calculate how many deleted columns are within the range
326
- const overlapStart = Math.max(deletionIndex, rangeStart);
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
- // Check if deletion overlaps with range
365
- if (deletionIndex <= endColumnIndex &&
366
- deletionEnd >= startColumnIndex) {
367
- // Calculate how many deleted columns are within the range
368
- const overlapStart = Math.max(deletionIndex, startColumnIndex);
369
- const overlapEnd = Math.min(deletionEnd, endColumnIndex);
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
- // Check if deletion overlaps with range
401
- if (deletionIndex <= endRowIndex && deletionEnd >= startRowIndex) {
402
- // Calculate how many deleted rows are within the range
403
- const overlapStart = Math.max(deletionIndex, startRowIndex);
404
- const overlapEnd = Math.min(deletionEnd, endRowIndex);
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;
@@ -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"}
@@ -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
+ };