@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
@@ -1,12 +1,15 @@
1
1
  import { Direction } from "@rowsncolumns/common-types";
2
2
  import { produceWithPatches } from "immer";
3
+ import { shiftChartsOnStructuralChange, shiftEmbedsOnStructuralChange, shiftProtectedRangesOnStructuralChange, } from "../api/anchored-objects";
3
4
  import { shrinkMergesOnColumnDelete } from "../api/merges";
5
+ import { updateNamedRangesOnStructuralChange, updatePrintSettingsOnStructuralChange, } from "../api/named-range-structural";
4
6
  import { shiftPivotsOnDelete } from "../api/pivot-range";
5
- import { updateRangedRulesOnStructuralChange } from "../api/range-rule-helpers";
6
7
  import { updateSheetRangeOnDelete } from "../api/range";
8
+ import { updateRangedRulesOnStructuralChange } from "../api/range-rule-helpers";
7
9
  import { moveFilterView } from "../api/selection";
8
10
  import { updateFormulasForStructuralChange } from "../api/structural-formula-update";
9
- export const deleteColumn = ({ rowCount, columnCount, locale, cellXfsRegistry, sharedStringRegistry, getSheetName, getActiveCell, getSelections, getDependents, getGraphNodes, getGraphNodesInSheet, createHistory, enqueueGraphOperation, enqueueCalculation, enqueueStructureChange, onChangeSheetData, onChangeSheets, onChangeTables, onChangeActiveCell, onChangeSelections, onChangeConditionalFormats, onChangeDataValidations, onChangeCitations, onChangePivotTables, }) => {
11
+ import { breakStructuredRefsForRemovedTableParts, collectRemovedTableParts, removeSpillsOfDeletedAnchors, } from "../api/structural-table-refs";
12
+ export const deleteColumn = ({ rowCount, columnCount, locale, cellXfsRegistry, sharedStringRegistry, getSheetName, getActiveCell, getSelections, getDependents, getGraphNodes, getGraphNodesInSheet, createHistory, enqueueGraphOperation, enqueueCalculation, enqueueStructureChange, onChangeSheetData, onChangeSheets, onChangeTables, onChangeActiveCell, onChangeSelections, onChangeConditionalFormats, onChangeDataValidations, onChangeCitations, onChangePivotTables, tables, getArrayRange, onChangeNamedRanges, onChangeEmbeds, onChangeCharts, onChangeProtectedRanges, }) => {
10
13
  return (sheetId, columnIndexes) => {
11
14
  if (!columnIndexes.length)
12
15
  return;
@@ -118,7 +121,7 @@ export const deleteColumn = ({ rowCount, columnCount, locale, cellXfsRegistry, s
118
121
  // Data validations
119
122
  onChangeDataValidations?.((prevValidations) => {
120
123
  const [nextState, patches, inversePatches] = produceWithPatches(prevValidations, (draft) => {
121
- updateRangedRulesOnStructuralChange(draft, "delete", "column", minColumnIndex, count, sheetId);
124
+ updateRangedRulesOnStructuralChange(draft, "delete", "column", minColumnIndex, count, sheetId, { getSheetName });
122
125
  });
123
126
  saveHistory({ dataValidations: { patches, inversePatches } });
124
127
  return nextState;
@@ -126,11 +129,20 @@ export const deleteColumn = ({ rowCount, columnCount, locale, cellXfsRegistry, s
126
129
  // Conditional formats
127
130
  onChangeConditionalFormats?.((prevConditionalFormats) => {
128
131
  const [nextState, patches, inversePatches] = produceWithPatches(prevConditionalFormats, (draft) => {
129
- updateRangedRulesOnStructuralChange(draft, "delete", "column", minColumnIndex, count, sheetId);
132
+ updateRangedRulesOnStructuralChange(draft, "delete", "column", minColumnIndex, count, sheetId, { getSheetName });
130
133
  });
131
134
  saveHistory({ conditionalFormats: { patches, inversePatches } });
132
135
  return nextState;
133
136
  });
137
+ // Defined names: shift back / shrink; a name whose whole range is
138
+ // deleted is kept as `#REF!` so its users evaluate `#REF!`, like Excel.
139
+ onChangeNamedRanges?.((prevNamedRanges) => {
140
+ const [nextState, patches, inversePatches] = produceWithPatches(prevNamedRanges, (draft) => {
141
+ updateNamedRangesOnStructuralChange(draft, "column", "delete", minColumnIndex, count, sheetId);
142
+ });
143
+ saveHistory({ namedRanges: { patches, inversePatches } });
144
+ return nextState;
145
+ });
134
146
  // Shift pivot tables.
135
147
  onChangePivotTables?.((prevPivots) => {
136
148
  const [nextState, patches, inversePatches] = produceWithPatches(prevPivots, (draft) => {
@@ -161,6 +173,31 @@ export const deleteColumn = ({ rowCount, columnCount, locale, cellXfsRegistry, s
161
173
  saveHistory({ citations: { patches, inversePatches } });
162
174
  return nextState;
163
175
  });
176
+ // Anchored objects (Excel ANC rules): floating charts / embeds move with
177
+ // their anchor cell, chart series ranges and AllowEditRange rectangles
178
+ // shift like references. Frozen panes are a positional viewport split
179
+ // and are deliberately NOT touched here.
180
+ onChangeEmbeds?.((prevEmbeds) => {
181
+ const [nextState, patches, inversePatches] = produceWithPatches(prevEmbeds, (draft) => {
182
+ shiftEmbedsOnStructuralChange(draft, sheetId, "column", "delete", minColumnIndex, count);
183
+ });
184
+ saveHistory({ embeds: { patches, inversePatches } });
185
+ return nextState;
186
+ });
187
+ onChangeCharts?.((prevCharts) => {
188
+ const [nextState, patches, inversePatches] = produceWithPatches(prevCharts, (draft) => {
189
+ shiftChartsOnStructuralChange(draft, sheetId, "column", "delete", minColumnIndex, count);
190
+ });
191
+ saveHistory({ charts: { patches, inversePatches } });
192
+ return nextState;
193
+ });
194
+ onChangeProtectedRanges?.((prevProtectedRanges) => {
195
+ const [nextState, patches, inversePatches] = produceWithPatches(prevProtectedRanges, (draft) => {
196
+ shiftProtectedRangesOnStructuralChange(draft, sheetId, "column", "delete", minColumnIndex, count);
197
+ });
198
+ saveHistory({ protectedRanges: { patches, inversePatches } });
199
+ return nextState;
200
+ });
164
201
  // Sheet metadata
165
202
  onChangeSheets?.((prevSheets) => {
166
203
  const [nextState, patches, inversePatches] = produceWithPatches(prevSheets, (draft) => {
@@ -195,24 +232,56 @@ export const deleteColumn = ({ rowCount, columnCount, locale, cellXfsRegistry, s
195
232
  sheet.basicFilter.range.endColumnIndex -= delta;
196
233
  }
197
234
  }
198
- if (sheet.frozenColumnCount) {
199
- for (const columnIndex of sortedColumnIndexes) {
200
- if (columnIndex <= sheet.frozenColumnCount) {
201
- sheet.frozenColumnCount--;
202
- }
203
- }
204
- }
205
235
  if (sheet.merges) {
206
236
  sheet.merges = shrinkMergesOnColumnDelete(sheet.merges, minColumnIndex, count);
207
237
  }
238
+ // `_xlnm.Print_Area` / `_xlnm.Print_Titles` follow the defined-name rule.
239
+ if (sheet.printSettings) {
240
+ updatePrintSettingsOnStructuralChange(sheet.printSettings, "column", "delete", minColumnIndex, count);
241
+ }
208
242
  sheet.columnCount = Math.max(1, (sheet.columnCount ?? columnCount) - count);
243
+ // Frozen panes are positional: the split stays where it is, only
244
+ // clamped so the freeze never exceeds the shrunk sheet.
245
+ if (sheet.frozenColumnCount &&
246
+ sheet.frozenColumnCount > sheet.columnCount) {
247
+ sheet.frozenColumnCount = sheet.columnCount;
248
+ }
209
249
  });
210
250
  saveHistory({ sheets: { patches, inversePatches } });
211
251
  return nextState;
212
252
  });
253
+ // Table columns spliced out above (and tables dropped whole): their
254
+ // structured refs break to `#REF!`, computed from the PRE-edit definitions.
255
+ const removedTableParts = collectRemovedTableParts(tables, sheetId, "column", minColumnIndex, maxColumnIndex);
213
256
  // Sheet data: three-pass formula update, then per-row column splice.
214
257
  onChangeSheetData?.((prevSheetData) => {
215
258
  const [nextState, patches, inversePatches] = produceWithPatches(prevSheetData, (draft) => {
259
+ removeSpillsOfDeletedAnchors({
260
+ draft,
261
+ sheetId,
262
+ axis: "column",
263
+ minIndex: minColumnIndex,
264
+ maxIndex: maxColumnIndex,
265
+ getArrayRange,
266
+ locale,
267
+ cellXfsRegistry,
268
+ sharedStringRegistry,
269
+ });
270
+ breakStructuredRefsForRemovedTableParts({
271
+ draft,
272
+ sheetId,
273
+ axis: "column",
274
+ referenceIndex: minColumnIndex,
275
+ count,
276
+ removed: removedTableParts,
277
+ getGraphNodes,
278
+ enqueueCalculation,
279
+ enqueueGraphOperation,
280
+ recalcCells,
281
+ locale,
282
+ cellXfsRegistry,
283
+ sharedStringRegistry,
284
+ });
216
285
  updateFormulasForStructuralChange({
217
286
  draft,
218
287
  axis: "column",
@@ -1,4 +1,4 @@
1
1
  import type { CellData, Sheet } from "@rowsncolumns/spreadsheet";
2
2
  import { type UseOnDeleteRowProps } from "../hooks/use-on-delete-row";
3
- export declare const deleteRow: <T extends CellData, S extends Sheet>({ rowCount, columnCount, locale, cellXfsRegistry, sharedStringRegistry, getSheetName, getActiveCell, getSelections, getDependents, getGraphNodes, getGraphNodesInSheet, createHistory, enqueueGraphOperation, enqueueCalculation, enqueueStructureChange, onChangeSheetData, onChangeSheets, onChangeTables, onChangeActiveCell, onChangeSelections, onChangeConditionalFormats, onChangeDataValidations, onChangeCitations, onChangePivotTables, }: UseOnDeleteRowProps<T, S>) => (sheetId: number, rowIndexes: number[]) => void;
3
+ export declare const deleteRow: <T extends CellData, S extends Sheet>({ rowCount, columnCount, locale, cellXfsRegistry, sharedStringRegistry, getSheetName, getActiveCell, getSelections, getDependents, getGraphNodes, getGraphNodesInSheet, createHistory, enqueueGraphOperation, enqueueCalculation, enqueueStructureChange, onChangeSheetData, onChangeSheets, onChangeTables, onChangeActiveCell, onChangeSelections, onChangeConditionalFormats, onChangeDataValidations, onChangeCitations, onChangePivotTables, tables, getArrayRange, onChangeNamedRanges, onChangeEmbeds, onChangeCharts, onChangeProtectedRanges, }: UseOnDeleteRowProps<T, S>) => (sheetId: number, rowIndexes: number[]) => void;
4
4
  //# sourceMappingURL=delete-row.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"delete-row.d.ts","sourceRoot":"","sources":["../../../interface/delete-row.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AASjE,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAGtE,eAAO,MAAM,SAAS,GAAI,CAAC,SAAS,QAAQ,EAAE,CAAC,SAAS,KAAK,EAAE,qbAyB5D,mBAAmB,CAAC,CAAC,EAAE,CAAC,CAAC,MAClB,SAAS,MAAM,EAAE,YAAY,MAAM,EAAE,SAqR9C,CAAC"}
1
+ {"version":3,"file":"delete-row.d.ts","sourceRoot":"","sources":["../../../interface/delete-row.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AAuBjE,OAAO,EAAE,KAAK,mBAAmB,EAAE,MAAM,4BAA4B,CAAC;AAGtE,eAAO,MAAM,SAAS,GAAI,CAAC,SAAS,QAAQ,EAAE,CAAC,SAAS,KAAK,EAAE,0hBA+B5D,mBAAmB,CAAC,CAAC,EAAE,CAAC,CAAC,MAClB,SAAS,MAAM,EAAE,YAAY,MAAM,EAAE,SAmZ9C,CAAC"}
@@ -1,12 +1,15 @@
1
1
  import { Direction } from "@rowsncolumns/common-types";
2
2
  import { produceWithPatches } from "immer";
3
+ import { shiftChartsOnStructuralChange, shiftEmbedsOnStructuralChange, shiftProtectedRangesOnStructuralChange, } from "../api/anchored-objects";
3
4
  import { shrinkMergesOnRowDelete } from "../api/merges";
5
+ import { updateNamedRangesOnStructuralChange, updatePrintSettingsOnStructuralChange, } from "../api/named-range-structural";
4
6
  import { shiftPivotsOnDelete } from "../api/pivot-range";
5
- import { updateRangedRulesOnStructuralChange } from "../api/range-rule-helpers";
6
7
  import { updateSheetRangeOnDelete } from "../api/range";
8
+ import { updateRangedRulesOnStructuralChange } from "../api/range-rule-helpers";
7
9
  import { moveFilterView } from "../api/selection";
8
10
  import { updateFormulasForStructuralChange } from "../api/structural-formula-update";
9
- export const deleteRow = ({ rowCount, columnCount, locale, cellXfsRegistry, sharedStringRegistry, getSheetName, getActiveCell, getSelections, getDependents, getGraphNodes, getGraphNodesInSheet, createHistory, enqueueGraphOperation, enqueueCalculation, enqueueStructureChange, onChangeSheetData, onChangeSheets, onChangeTables, onChangeActiveCell, onChangeSelections, onChangeConditionalFormats, onChangeDataValidations, onChangeCitations, onChangePivotTables, }) => {
11
+ import { breakStructuredRefsForRemovedTableParts, collectRemovedTableParts, removeSpillsOfDeletedAnchors, } from "../api/structural-table-refs";
12
+ export const deleteRow = ({ rowCount, columnCount, locale, cellXfsRegistry, sharedStringRegistry, getSheetName, getActiveCell, getSelections, getDependents, getGraphNodes, getGraphNodesInSheet, createHistory, enqueueGraphOperation, enqueueCalculation, enqueueStructureChange, onChangeSheetData, onChangeSheets, onChangeTables, onChangeActiveCell, onChangeSelections, onChangeConditionalFormats, onChangeDataValidations, onChangeCitations, onChangePivotTables, tables, getArrayRange, onChangeNamedRanges, onChangeEmbeds, onChangeCharts, onChangeProtectedRanges, }) => {
10
13
  return (sheetId, rowIndexes) => {
11
14
  if (!rowIndexes.length)
12
15
  return;
@@ -96,7 +99,7 @@ export const deleteRow = ({ rowCount, columnCount, locale, cellXfsRegistry, shar
96
99
  // Data validations
97
100
  onChangeDataValidations?.((prevValidations) => {
98
101
  const [nextState, patches, inversePatches] = produceWithPatches(prevValidations, (draft) => {
99
- updateRangedRulesOnStructuralChange(draft, "delete", "row", minRowIndex, count, sheetId);
102
+ updateRangedRulesOnStructuralChange(draft, "delete", "row", minRowIndex, count, sheetId, { getSheetName });
100
103
  });
101
104
  saveHistory({ dataValidations: { patches, inversePatches } });
102
105
  return nextState;
@@ -104,11 +107,20 @@ export const deleteRow = ({ rowCount, columnCount, locale, cellXfsRegistry, shar
104
107
  // Conditional formats
105
108
  onChangeConditionalFormats?.((prevConditionalFormats) => {
106
109
  const [nextState, patches, inversePatches] = produceWithPatches(prevConditionalFormats, (draft) => {
107
- updateRangedRulesOnStructuralChange(draft, "delete", "row", minRowIndex, count, sheetId);
110
+ updateRangedRulesOnStructuralChange(draft, "delete", "row", minRowIndex, count, sheetId, { getSheetName });
108
111
  });
109
112
  saveHistory({ conditionalFormats: { patches, inversePatches } });
110
113
  return nextState;
111
114
  });
115
+ // Defined names: shift back / shrink; a name whose whole range is
116
+ // deleted is kept as `#REF!` so its users evaluate `#REF!`, like Excel.
117
+ onChangeNamedRanges?.((prevNamedRanges) => {
118
+ const [nextState, patches, inversePatches] = produceWithPatches(prevNamedRanges, (draft) => {
119
+ updateNamedRangesOnStructuralChange(draft, "row", "delete", minRowIndex, count, sheetId);
120
+ });
121
+ saveHistory({ namedRanges: { patches, inversePatches } });
122
+ return nextState;
123
+ });
112
124
  // Shift pivot tables: ranges shrink/shift; targetPosition anchors to
113
125
  // the start of the deletion when its row falls inside the deletion.
114
126
  onChangePivotTables?.((prevPivots) => {
@@ -141,6 +153,31 @@ export const deleteRow = ({ rowCount, columnCount, locale, cellXfsRegistry, shar
141
153
  saveHistory({ citations: { patches, inversePatches } });
142
154
  return nextState;
143
155
  });
156
+ // Anchored objects (Excel ANC rules): floating charts / embeds move with
157
+ // their anchor cell, chart series ranges and AllowEditRange rectangles
158
+ // shift like references. Frozen panes are a positional viewport split
159
+ // and are deliberately NOT touched here.
160
+ onChangeEmbeds?.((prevEmbeds) => {
161
+ const [nextState, patches, inversePatches] = produceWithPatches(prevEmbeds, (draft) => {
162
+ shiftEmbedsOnStructuralChange(draft, sheetId, "row", "delete", minRowIndex, count);
163
+ });
164
+ saveHistory({ embeds: { patches, inversePatches } });
165
+ return nextState;
166
+ });
167
+ onChangeCharts?.((prevCharts) => {
168
+ const [nextState, patches, inversePatches] = produceWithPatches(prevCharts, (draft) => {
169
+ shiftChartsOnStructuralChange(draft, sheetId, "row", "delete", minRowIndex, count);
170
+ });
171
+ saveHistory({ charts: { patches, inversePatches } });
172
+ return nextState;
173
+ });
174
+ onChangeProtectedRanges?.((prevProtectedRanges) => {
175
+ const [nextState, patches, inversePatches] = produceWithPatches(prevProtectedRanges, (draft) => {
176
+ shiftProtectedRangesOnStructuralChange(draft, sheetId, "row", "delete", minRowIndex, count);
177
+ });
178
+ saveHistory({ protectedRanges: { patches, inversePatches } });
179
+ return nextState;
180
+ });
144
181
  // Sheet metadata
145
182
  onChangeSheets?.((prevSheets) => {
146
183
  const [nextState, patches, inversePatches] = produceWithPatches(prevSheets, (draft) => {
@@ -164,24 +201,57 @@ export const deleteRow = ({ rowCount, columnCount, locale, cellXfsRegistry, shar
164
201
  delete sheet.basicFilter;
165
202
  }
166
203
  }
167
- if (sheet.frozenRowCount) {
168
- for (const rowIndex of sortedRowIndexes) {
169
- if (rowIndex <= sheet.frozenRowCount) {
170
- sheet.frozenRowCount--;
171
- }
172
- }
173
- }
174
204
  if (sheet.merges) {
175
205
  sheet.merges = shrinkMergesOnRowDelete(sheet.merges, minRowIndex, count);
176
206
  }
207
+ // `_xlnm.Print_Area` / `_xlnm.Print_Titles` follow the defined-name rule.
208
+ if (sheet.printSettings) {
209
+ updatePrintSettingsOnStructuralChange(sheet.printSettings, "row", "delete", minRowIndex, count);
210
+ }
177
211
  sheet.rowCount = Math.max(1, (sheet.rowCount ?? rowCount) - count);
212
+ // Frozen panes are positional: the split stays where it is, only
213
+ // clamped so the freeze never exceeds the shrunk sheet.
214
+ if (sheet.frozenRowCount && sheet.frozenRowCount > sheet.rowCount) {
215
+ sheet.frozenRowCount = sheet.rowCount;
216
+ }
178
217
  });
179
218
  saveHistory({ sheets: { patches, inversePatches } });
180
219
  return nextState;
181
220
  });
221
+ // Tables whose header row goes (dropped above): their structured refs
222
+ // break to `#REF!`, computed from the PRE-edit definitions.
223
+ const removedTableParts = collectRemovedTableParts(tables, sheetId, "row", minRowIndex, maxRowIndex);
182
224
  // Sheet data: three-pass formula update, then splice the rows out.
183
225
  onChangeSheetData?.((prevSheetData) => {
184
226
  const [nextState, patches, inversePatches] = produceWithPatches(prevSheetData, (draft) => {
227
+ // A dynamic array whose anchor row is deleted takes its spill with
228
+ // it — drop the projected cells before the rows close up.
229
+ removeSpillsOfDeletedAnchors({
230
+ draft,
231
+ sheetId,
232
+ axis: "row",
233
+ minIndex: minRowIndex,
234
+ maxIndex: maxRowIndex,
235
+ getArrayRange,
236
+ locale,
237
+ cellXfsRegistry,
238
+ sharedStringRegistry,
239
+ });
240
+ breakStructuredRefsForRemovedTableParts({
241
+ draft,
242
+ sheetId,
243
+ axis: "row",
244
+ referenceIndex: minRowIndex,
245
+ count,
246
+ removed: removedTableParts,
247
+ getGraphNodes,
248
+ enqueueCalculation,
249
+ enqueueGraphOperation,
250
+ recalcCells,
251
+ locale,
252
+ cellXfsRegistry,
253
+ sharedStringRegistry,
254
+ });
185
255
  updateFormulasForStructuralChange({
186
256
  draft,
187
257
  axis: "row",
@@ -1,5 +1,14 @@
1
1
  import type { CellData, CellInterface, SelectionArea } from "@rowsncolumns/common-types";
2
2
  import type { SelectionAttributes, Sheet } from "@rowsncolumns/spreadsheet";
3
3
  import type { UseOnInsertCellsShiftDown } from "../hooks/use-on-insert-cells-shift-down";
4
- export declare const insertCellsShiftDown: <T extends CellData, S extends Sheet>({ rowCount, columnCount, locale, cellXfsRegistry, sharedStringRegistry, getActiveCell, getSelections, getDependents, createHistory, enqueueGraphOperation, enqueueCalculation, enqueueStructureChange, onChangeSheetData, onChangeSheets, onChangeCitations, }: UseOnInsertCellsShiftDown<T, S>) => (sheetId: number, activeCell: CellInterface, selections: SelectionArea<SelectionAttributes>[]) => void;
4
+ /**
5
+ * Insert cells ▸ Shift cells down (SHF-01, SHF-06, SHF-07, SHF-08, FMT-03). Only the selection's
6
+ * columns move: every cell at or below the selection's first row slides down by the selection's
7
+ * height. References that target a shifted cell follow it; a range that lies entirely inside the
8
+ * shifted columns and straddles the insertion row grows by the inserted height (SHF-08); a wider
9
+ * range keeps its address (SHF-07). The vacated cells take the formatting of the cell above the
10
+ * insertion (FMT-03). Refusals (torn merge/table/array, off-sheet push) are decided by the hook via
11
+ * `getShiftCellsRefusal` before this runs.
12
+ */
13
+ export declare const insertCellsShiftDown: <T extends CellData, S extends Sheet>({ rowCount, columnCount, locale, cellXfsRegistry, sharedStringRegistry, getActiveCell, getSelections, getDependents, getSheetName, createHistory, enqueueGraphOperation, enqueueCalculation, enqueueStructureChange, onChangeSheetData, onChangeSheets, onChangeTables, onChangeCitations, }: UseOnInsertCellsShiftDown<T, S>) => (sheetId: number, activeCell: CellInterface, selections: SelectionArea<SelectionAttributes>[]) => void;
5
14
  //# sourceMappingURL=insert-cells-shift-down.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"insert-cells-shift-down.d.ts","sourceRoot":"","sources":["../../../interface/insert-cells-shift-down.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,QAAQ,EACR,aAAa,EAEb,aAAa,EACd,MAAM,4BAA4B,CAAC;AAEpC,OAAO,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AAM5E,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,yCAAyC,CAAC;AAGzF,eAAO,MAAM,oBAAoB,GAAI,CAAC,SAAS,QAAQ,EAAE,CAAC,SAAS,KAAK,EAAE,gQAgBvE,yBAAyB,CAAC,CAAC,EAAE,CAAC,CAAC,MAE9B,SAAS,MAAM,EACf,YAAY,aAAa,EACzB,YAAY,aAAa,CAAC,mBAAmB,CAAC,EAAE,SAoMnD,CAAC"}
1
+ {"version":3,"file":"insert-cells-shift-down.d.ts","sourceRoot":"","sources":["../../../interface/insert-cells-shift-down.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,QAAQ,EACR,aAAa,EAEb,aAAa,EACd,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AAe5E,OAAO,KAAK,EAAE,yBAAyB,EAAE,MAAM,yCAAyC,CAAC;AAIzF;;;;;;;;GAQG;AACH,eAAO,MAAM,oBAAoB,GAAI,CAAC,SAAS,QAAQ,EAAE,CAAC,SAAS,KAAK,EAAE,8RAkBvE,yBAAyB,CAAC,CAAC,EAAE,CAAC,CAAC,MAE9B,SAAS,MAAM,EACf,YAAY,aAAa,EACzB,YAAY,aAAa,CAAC,mBAAmB,CAAC,EAAE,SAoQnD,CAAC"}
@@ -1,37 +1,44 @@
1
- import { selectionFromActiveCell } from "@rowsncolumns/utils";
2
1
  import { produceWithPatches } from "immer";
3
- import { processSideEffectDependentCells } from "../api/dependency";
2
+ import { collectUniqueDependentsForRange, processSideEffectDependentCells, } from "../api/dependency";
3
+ import { createSheetCellIdentifier, expandRangesInFormula } from "../api/formula";
4
4
  import { cloneCellWithFormatting, createSheetDraft } from "../api/immer-utils";
5
- import { moveMergedCells } from "../api/merges";
6
- export const insertCellsShiftDown = ({ rowCount, columnCount, locale, cellXfsRegistry, sharedStringRegistry, getActiveCell, getSelections, getDependents, createHistory, enqueueGraphOperation, enqueueCalculation, enqueueStructureChange, onChangeSheetData, onChangeSheets, onChangeCitations, }) => {
5
+ import { shiftMergesInBand } from "../api/merges";
6
+ import { isRangeInsideBand, resolveShiftSelection, shiftBand, } from "../api/structural-guards";
7
+ import { SheetCell } from "../sheet-cell";
8
+ /**
9
+ * Insert cells ▸ Shift cells down (SHF-01, SHF-06, SHF-07, SHF-08, FMT-03). Only the selection's
10
+ * columns move: every cell at or below the selection's first row slides down by the selection's
11
+ * height. References that target a shifted cell follow it; a range that lies entirely inside the
12
+ * shifted columns and straddles the insertion row grows by the inserted height (SHF-08); a wider
13
+ * range keeps its address (SHF-07). The vacated cells take the formatting of the cell above the
14
+ * insertion (FMT-03). Refusals (torn merge/table/array, off-sheet push) are decided by the hook via
15
+ * `getShiftCellsRefusal` before this runs.
16
+ */
17
+ export const insertCellsShiftDown = ({ rowCount, columnCount, locale, cellXfsRegistry, sharedStringRegistry, getActiveCell, getSelections, getDependents, getSheetName, createHistory, enqueueGraphOperation, enqueueCalculation, enqueueStructureChange, onChangeSheetData, onChangeSheets, onChangeTables, onChangeCitations, }) => {
7
18
  return (sheetId, activeCell, selections) => {
8
19
  const saveHistory = createHistory();
9
20
  // We need to track this for history. Precedent cells
10
21
  const recalcCells = new Map();
11
- // Count
12
- const finalSelections = selections.length
13
- ? selections
14
- : selectionFromActiveCell(activeCell);
15
- // Last selection
16
- const selection = finalSelections[finalSelections.length - 1];
17
- const count = selection.range.endRowIndex - selection.range.startRowIndex + 1;
22
+ const selection = resolveShiftSelection(activeCell, selections);
23
+ const { startRowIndex: r1, endRowIndex: r2, startColumnIndex: c1, endColumnIndex: c2, } = selection.range;
24
+ const count = r2 - r1 + 1;
25
+ const band = shiftBand("down", selection.range);
26
+ const sheetName = getSheetName?.(sheetId) ?? "";
18
27
  enqueueStructureChange?.({
19
28
  kind: "insert-cells-shift-down",
20
29
  sheetId,
21
- startRowIndex: selection.range.startRowIndex,
22
- endRowIndex: selection.range.endRowIndex,
23
- startColumnIndex: selection.range.startColumnIndex,
24
- endColumnIndex: selection.range.endColumnIndex,
30
+ startRowIndex: r1,
31
+ endRowIndex: r2,
32
+ startColumnIndex: c1,
33
+ endColumnIndex: c2,
25
34
  rowCount,
26
35
  });
27
- // Update sheets
36
+ // Update sheets — merges wholly inside the shifted columns ride down with their cells.
28
37
  onChangeSheets?.((prevSheets) => {
29
38
  const [nextState, patches, inversePatches] = produceWithPatches(prevSheets, (draft) => {
30
39
  const sheet = draft.find((sheet) => sheet.sheetId === sheetId);
31
- if (sheet) {
32
- for (let rowIndex = selection.range.startRowIndex; rowIndex <= selection.range.endRowIndex; rowIndex++) {
33
- sheet.merges = moveMergedCells(sheet.merges, "row-insert", rowIndex);
34
- }
40
+ if (sheet?.merges) {
41
+ sheet.merges = shiftMergesInBand(sheet.merges, "row", r1, count, true, c1, c2);
35
42
  }
36
43
  });
37
44
  // Save history
@@ -40,27 +47,37 @@ export const insertCellsShiftDown = ({ rowCount, columnCount, locale, cellXfsReg
40
47
  });
41
48
  return nextState;
42
49
  });
50
+ // Tables whose columns the band covers entirely move with it (a partial cover was refused).
51
+ onChangeTables?.((prevTables) => {
52
+ const [nextState, patches, inversePatches] = produceWithPatches(prevTables, (draft) => {
53
+ for (const table of draft) {
54
+ if (table.sheetId !== sheetId ||
55
+ !isRangeInsideBand(table.range, band)) {
56
+ continue;
57
+ }
58
+ table.range.startRowIndex += count;
59
+ table.range.endRowIndex += count;
60
+ }
61
+ });
62
+ saveHistory({ tables: { patches, inversePatches } });
63
+ return nextState;
64
+ });
43
65
  // Update citations - shift if in affected columns and after insertion row
44
66
  onChangeCitations?.((prevCitations) => {
45
67
  const [nextState, patches, inversePatches] = produceWithPatches(prevCitations, (draft) => {
46
- const affectedColStart = selection.range.startColumnIndex;
47
- const affectedColEnd = selection.range.endColumnIndex;
48
- const insertRowStart = selection.range.startRowIndex;
49
- const shiftAmount = selection.range.endRowIndex - selection.range.startRowIndex + 1;
50
68
  for (const citation of draft) {
51
69
  // Skip citations on other sheets
52
70
  if (citation.range.sheetId !== sheetId) {
53
71
  continue;
54
72
  }
55
73
  // Check if citation overlaps with affected columns
56
- const citationColStart = citation.range.startColumnIndex;
57
- const citationColEnd = citation.range.endColumnIndex;
58
- const colsOverlap = !(citationColEnd < affectedColStart || citationColStart > affectedColEnd);
74
+ const colsOverlap = !(citation.range.endColumnIndex < c1 ||
75
+ citation.range.startColumnIndex > c2);
59
76
  if (colsOverlap) {
60
77
  // If citation starts at or after the insertion row, shift it down
61
- if (citation.range.startRowIndex >= insertRowStart) {
62
- citation.range.startRowIndex += shiftAmount;
63
- citation.range.endRowIndex += shiftAmount;
78
+ if (citation.range.startRowIndex >= r1) {
79
+ citation.range.startRowIndex += count;
80
+ citation.range.endRowIndex += count;
64
81
  }
65
82
  }
66
83
  }
@@ -74,16 +91,31 @@ export const insertCellsShiftDown = ({ rowCount, columnCount, locale, cellXfsReg
74
91
  onChangeSheetData?.((prevSheetData) => {
75
92
  const [nextState, patches, inversePatches] = produceWithPatches(prevSheetData, (draft) => {
76
93
  const movedKeys = new Set();
77
- const totalRows = draft[sheetId]?.length ?? rowCount;
78
- for (let rowIndex = totalRows; rowIndex > 0; rowIndex--) {
94
+ const totalRows = Math.max(draft[sheetId]?.length ?? 0, r2 + 1);
95
+ // Everything at or below the insertion row in the band's columns moves down by `count`.
96
+ const sourceRange = {
97
+ startRowIndex: r1,
98
+ endRowIndex: totalRows + count,
99
+ startColumnIndex: c1,
100
+ endColumnIndex: c2,
101
+ };
102
+ // Formulas that read a moving cell — captured before the graph goes stale.
103
+ const dependents = collectUniqueDependentsForRange({
104
+ draft,
105
+ sheetId,
106
+ sourceRange: {
107
+ startRowIndex: r1,
108
+ endRowIndex: totalRows,
109
+ startColumnIndex: c1,
110
+ endColumnIndex: c2,
111
+ },
112
+ getDependents,
113
+ });
114
+ for (let rowIndex = totalRows; rowIndex >= r1; rowIndex--) {
79
115
  // We have a for loop
80
116
  // to cater for table expansion
81
117
  // which will push multiple columns down
82
- for (let columnIndex = selection.range.startColumnIndex; columnIndex <= selection.range.endColumnIndex; columnIndex++) {
83
- const sourceRange = {
84
- ...selection.range,
85
- endRowIndex: selection.range.endRowIndex + count,
86
- };
118
+ for (let columnIndex = c1; columnIndex <= c2; columnIndex++) {
87
119
  // Process dependents
88
120
  processSideEffectDependentCells({
89
121
  draft,
@@ -94,6 +126,7 @@ export const insertCellsShiftDown = ({ rowCount, columnCount, locale, cellXfsReg
94
126
  enqueueGraphOperation,
95
127
  enqueueCalculation,
96
128
  getDependents,
129
+ getSheetName,
97
130
  recalcCells,
98
131
  rowDelta: count,
99
132
  sourceRange,
@@ -104,26 +137,62 @@ export const insertCellsShiftDown = ({ rowCount, columnCount, locale, cellXfsReg
104
137
  cellXfsRegistry,
105
138
  sharedStringRegistry,
106
139
  });
107
- // Skip unaffected cells
108
- if (rowIndex < selection.range.startRowIndex) {
109
- continue;
110
- }
111
140
  }
112
141
  }
142
+ // SHF-08: a range living entirely inside the band's columns that straddles the insertion
143
+ // row grows by the inserted height (Excel's "insert inside a range expands it"). Runs on
144
+ // the pre-shift positions; the value shift below carries in-band dependents down.
145
+ const sheetCell = new SheetCell();
146
+ for (const pos of dependents.values()) {
147
+ if (pos.sheetId !== sheetId)
148
+ continue;
149
+ const currentSheet = createSheetDraft(draft, sheetId, pos.rowIndex);
150
+ const cellData = currentSheet[pos.rowIndex]?.values?.[pos.columnIndex];
151
+ sheetCell.assign(sheetId, { rowIndex: pos.rowIndex, columnIndex: pos.columnIndex }, cellData, { locale, cellXfsRegistry, sharedStringRegistry });
152
+ const formulaValue = sheetCell.formula;
153
+ if (!formulaValue)
154
+ continue;
155
+ const expanded = expandRangesInFormula(formulaValue, r1, count, "row", sheetName, { bandSpan: { startIndex: c1, endIndex: c2 } });
156
+ if (!expanded || expanded === formulaValue)
157
+ continue;
158
+ sheetCell.setFormulaValue(expanded, false);
159
+ currentSheet[pos.rowIndex].values[pos.columnIndex] =
160
+ sheetCell.getCellData();
161
+ const movesWithBand = pos.rowIndex >= r1 && pos.columnIndex >= c1 && pos.columnIndex <= c2;
162
+ const finalPosition = {
163
+ sheetId,
164
+ rowIndex: movesWithBand ? pos.rowIndex + count : pos.rowIndex,
165
+ columnIndex: pos.columnIndex,
166
+ };
167
+ const enqueueOperation = sheetCell.ephemeral()
168
+ ? enqueueCalculation
169
+ : enqueueGraphOperation;
170
+ enqueueOperation?.({ type: "add", position: finalPosition });
171
+ enqueueCalculation?.({
172
+ type: "dirty",
173
+ position: finalPosition,
174
+ force: true,
175
+ });
176
+ recalcCells.set(createSheetCellIdentifier(sheetId, finalPosition.rowIndex, finalPosition.columnIndex), finalPosition);
177
+ }
113
178
  // Update values
114
- for (let rowIndex = totalRows + count; rowIndex > selection.range.startRowIndex; rowIndex--) {
179
+ for (let rowIndex = totalRows + count; rowIndex > r1; rowIndex--) {
115
180
  const currentSheet = createSheetDraft(draft, sheetId, rowIndex);
116
- for (let columnIndex = selection.range.endColumnIndex; columnIndex >= selection.range.startColumnIndex; columnIndex--) {
181
+ for (let columnIndex = c2; columnIndex >= c1; columnIndex--) {
117
182
  const previousValue = currentSheet[rowIndex - count]?.values?.[columnIndex];
118
183
  currentSheet[rowIndex].values[columnIndex] = previousValue;
119
184
  }
120
185
  }
121
- // Update inserts cells
122
- for (let rowIndex = selection.range.startRowIndex; rowIndex <= selection.range.endRowIndex; rowIndex++) {
186
+ // FMT-03: the inserted cells take the formatting of the cell directly ABOVE the
187
+ // insertion; at row 1 there is none, so they keep the displaced cell's formatting.
188
+ for (let rowIndex = r1; rowIndex <= r2; rowIndex++) {
123
189
  const currentSheet = createSheetDraft(draft, sheetId, rowIndex);
124
- for (let columnIndex = selection.range.endColumnIndex; columnIndex >= selection.range.startColumnIndex; columnIndex--) {
190
+ for (let columnIndex = c2; columnIndex >= c1; columnIndex--) {
191
+ const formatSource = r1 > 1
192
+ ? currentSheet[r1 - 1]?.values?.[columnIndex]
193
+ : currentSheet[rowIndex].values[columnIndex];
125
194
  currentSheet[rowIndex].values[columnIndex] =
126
- cloneCellWithFormatting(currentSheet[rowIndex].values[columnIndex]);
195
+ cloneCellWithFormatting(formatSource);
127
196
  }
128
197
  }
129
198
  });
@@ -1,5 +1,14 @@
1
1
  import type { CellData, CellInterface, SelectionArea } from "@rowsncolumns/common-types";
2
2
  import type { SelectionAttributes, Sheet } from "@rowsncolumns/spreadsheet";
3
3
  import type { UseOnInsertCellsShiftRight } from "../hooks/use-on-insert-cells-shift-right";
4
- export declare const insertCellsShiftRight: <T extends CellData, S extends Sheet>({ rowCount, columnCount, locale, cellXfsRegistry, sharedStringRegistry, getActiveCell, getSelections, getDependents, createHistory, enqueueGraphOperation, enqueueCalculation, enqueueStructureChange, onChangeSheetData, onChangeSheets, onChangeCitations, }: UseOnInsertCellsShiftRight<T, S>) => (sheetId: number, activeCell: CellInterface, selections: SelectionArea<SelectionAttributes>[]) => void;
4
+ /**
5
+ * Insert cells ▸ Shift cells right (SHF-02, FMT-04) — the horizontal twin of
6
+ * `insertCellsShiftDown`: only the selection's rows move, every cell at or right of the selection's
7
+ * first column slides right by the selection's width. References follow shifted cells; a range
8
+ * entirely inside the shifted rows that straddles the insertion column widens (SHF-08 analog);
9
+ * the vacated cells take the formatting of the cell to the LEFT of the insertion (FMT-04). Refusals
10
+ * (torn merge/table/array, a non-blank cell pushed past `columnCount` — SHF-10) are decided by the
11
+ * hook before this runs.
12
+ */
13
+ export declare const insertCellsShiftRight: <T extends CellData, S extends Sheet>({ rowCount, columnCount, locale, cellXfsRegistry, sharedStringRegistry, getActiveCell, getSelections, getDependents, getSheetName, createHistory, enqueueGraphOperation, enqueueCalculation, enqueueStructureChange, onChangeSheetData, onChangeSheets, onChangeTables, onChangeCitations, }: UseOnInsertCellsShiftRight<T, S>) => (sheetId: number, activeCell: CellInterface, selections: SelectionArea<SelectionAttributes>[]) => void;
5
14
  //# sourceMappingURL=insert-cells-shift-right.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"insert-cells-shift-right.d.ts","sourceRoot":"","sources":["../../../interface/insert-cells-shift-right.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,QAAQ,EACR,aAAa,EAEb,aAAa,EACd,MAAM,4BAA4B,CAAC;AAEpC,OAAO,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AAM5E,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,0CAA0C,CAAC;AAG3F,eAAO,MAAM,qBAAqB,GAAI,CAAC,SAAS,QAAQ,EAAE,CAAC,SAAS,KAAK,EAAE,gQAgBxE,0BAA0B,CAAC,CAAC,EAAE,CAAC,CAAC,MAE/B,SAAS,MAAM,EACf,YAAY,aAAa,EACzB,YAAY,aAAa,CAAC,mBAAmB,CAAC,EAAE,SA2LnD,CAAC"}
1
+ {"version":3,"file":"insert-cells-shift-right.d.ts","sourceRoot":"","sources":["../../../interface/insert-cells-shift-right.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,QAAQ,EACR,aAAa,EAEb,aAAa,EACd,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAAE,mBAAmB,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AAgB5E,OAAO,KAAK,EAAE,0BAA0B,EAAE,MAAM,0CAA0C,CAAC;AAI3F;;;;;;;;GAQG;AACH,eAAO,MAAM,qBAAqB,GAAI,CAAC,SAAS,QAAQ,EAAE,CAAC,SAAS,KAAK,EAAE,8RAkBxE,0BAA0B,CAAC,CAAC,EAAE,CAAC,CAAC,MAE/B,SAAS,MAAM,EACf,YAAY,aAAa,EACzB,YAAY,aAAa,CAAC,mBAAmB,CAAC,EAAE,SAqQnD,CAAC"}