@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
@@ -0,0 +1,78 @@
1
+ import type { CellData, CellInterface, GridRange, SelectionArea } from "@rowsncolumns/common-types";
2
+ import type { SelectionAttributes, TableView } from "@rowsncolumns/spreadsheet";
3
+ import { type StructuralRefusal } from "../constants";
4
+ import type { SheetData } from "../hooks/use-spreadsheet-state";
5
+ /**
6
+ * Pure predicates behind Excel's structural-edit refusals. The hooks run them BEFORE the interface
7
+ * function touches any state and `showError` the Excel message on refusal, so a refused edit leaves
8
+ * sheetData / merges / tables / selection exactly as they were.
9
+ *
10
+ * A "band" is the rectangle of cells an edit relocates. Shift cells down at `B2:B3` moves
11
+ * `B2:B∞` — an open end is expressed with `Number.POSITIVE_INFINITY`.
12
+ */
13
+ export type StructuralBand = GridRange;
14
+ export type ShiftDirection = "down" | "right" | "up" | "left";
15
+ /** The selection a cell shift operates on — the LAST selection, or the active cell as a 1×1. The
16
+ * hooks' guards and the interface functions must agree on it, so both resolve it here. */
17
+ export declare const resolveShiftSelection: (activeCell: CellInterface, selections: SelectionArea<SelectionAttributes>[]) => SelectionArea<SelectionAttributes>;
18
+ /** The cells a shift relocates: the selection's rows/columns from its leading edge to the sheet edge. */
19
+ export declare const shiftBand: (direction: ShiftDirection, range: GridRange) => StructuralBand;
20
+ export declare const isNonBlankCell: (cell: CellData | null | undefined) => boolean;
21
+ /**
22
+ * INS-06 / INS-09 / SHF-10 — the first non-blank cell that a shift of `count` rows (axis `row`) or
23
+ * columns (axis `column`) would push past `limit` (the last valid row/column index). Only cells
24
+ * inside `band` move. `undefined` when the edit is safe.
25
+ */
26
+ export declare const findOffSheetPush: <T extends CellData>({ rows, axis, count, limit, band, }: {
27
+ rows: SheetData<T>[number] | undefined;
28
+ axis: "row" | "column";
29
+ count: number;
30
+ limit: number | undefined;
31
+ band: StructuralBand;
32
+ }) => CellInterface | undefined;
33
+ /**
34
+ * A range the band reaches but does not contain whole is TORN by the shift. For a delete, a range
35
+ * that touches the deleted block must lie wholly inside it (it is removed with the block) — one
36
+ * straddling the block's edge is torn too.
37
+ */
38
+ export declare const findTornRange: (ranges: readonly GridRange[] | undefined | null, band: StructuralBand, deletedBlock?: GridRange) => GridRange | undefined;
39
+ /** SHF-05 / MRG-07 — a merge the band covers only partially. */
40
+ export declare const findTornMerge: (merges: readonly GridRange[] | undefined | null, band: StructuralBand, deletedBlock?: GridRange) => GridRange | undefined;
41
+ /** TBL-05 — a table the band covers only partially (a table wholly inside the band moves intact). */
42
+ export declare const findTornTable: (tables: readonly TableView[] | undefined | null, sheetId: number, band: StructuralBand, deletedBlock?: GridRange) => TableView | undefined;
43
+ /** ANC-14 — a CSE / dynamic-array range the band covers only partially. */
44
+ export declare const findTornArray: (arrayRanges: readonly GridRange[] | undefined | null, band: StructuralBand, deletedBlock?: GridRange) => GridRange | undefined;
45
+ /** Ranges (merges / tables) that travel with the band: wholly inside it and, for a delete, not
46
+ * inside the deleted block. */
47
+ export declare const isRangeInsideBand: (range: GridRange, band: StructuralBand) => boolean;
48
+ export declare const isRangeInsideBlock: (outer: GridRange, inner: GridRange) => boolean;
49
+ /**
50
+ * The Excel refusal (if any) for an insert/delete-cells shift of `range`. Checks run in Excel's
51
+ * order: array tear, merge tear, table tear, off-sheet push. `undefined` means the edit may proceed.
52
+ */
53
+ export declare const getShiftCellsRefusal: <T extends CellData>({ direction, range, sheetId, rows, rowCount, columnCount, merges, tables, arrayRanges, }: {
54
+ direction: ShiftDirection;
55
+ range: GridRange;
56
+ sheetId: number;
57
+ rows: SheetData<T>[number] | undefined;
58
+ /** Last valid row index for a vertical shift (the grid does not grow on a cell shift). */
59
+ rowCount?: number;
60
+ /** Last valid column index for a horizontal shift. */
61
+ columnCount?: number;
62
+ merges?: readonly GridRange[] | null;
63
+ tables?: readonly TableView[] | null;
64
+ arrayRanges?: readonly GridRange[] | null;
65
+ }) => StructuralRefusal | undefined;
66
+ /**
67
+ * INS-06 / INS-09 — the refusal for a whole-row (`axis: "row"`) or whole-column insert of `count`
68
+ * lines before `referenceIndex`, given the hard grid cap (`maxRowCount` / `maxColumnCount`). Blank
69
+ * trailing lines may fall off the end; a non-blank one refuses the insert.
70
+ */
71
+ export declare const getInsertLinesRefusal: <T extends CellData>({ axis, referenceIndex, count, rows, limit, }: {
72
+ axis: "row" | "column";
73
+ referenceIndex: number;
74
+ count: number;
75
+ rows: SheetData<T>[number] | undefined;
76
+ limit: number | undefined;
77
+ }) => StructuralRefusal | undefined;
78
+ //# sourceMappingURL=structural-guards.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"structural-guards.d.ts","sourceRoot":"","sources":["../../../api/structural-guards.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,186 @@
1
+ import { selectionFromActiveCell } from "@rowsncolumns/utils";
2
+ import { structuralRefusal, } from "../constants";
3
+ /** The selection a cell shift operates on — the LAST selection, or the active cell as a 1×1. The
4
+ * hooks' guards and the interface functions must agree on it, so both resolve it here. */
5
+ export const resolveShiftSelection = (activeCell, selections) => {
6
+ const finalSelections = selections.length
7
+ ? selections
8
+ : selectionFromActiveCell(activeCell);
9
+ return finalSelections[finalSelections.length - 1];
10
+ };
11
+ const rangesIntersect = (a, b) => a.startRowIndex <= b.endRowIndex &&
12
+ a.endRowIndex >= b.startRowIndex &&
13
+ a.startColumnIndex <= b.endColumnIndex &&
14
+ a.endColumnIndex >= b.startColumnIndex;
15
+ const rangeContains = (outer, inner) => inner.startRowIndex >= outer.startRowIndex &&
16
+ inner.endRowIndex <= outer.endRowIndex &&
17
+ inner.startColumnIndex >= outer.startColumnIndex &&
18
+ inner.endColumnIndex <= outer.endColumnIndex;
19
+ /** The cells a shift relocates: the selection's rows/columns from its leading edge to the sheet edge. */
20
+ export const shiftBand = (direction, range) => {
21
+ switch (direction) {
22
+ case "down":
23
+ case "up":
24
+ return {
25
+ startRowIndex: range.startRowIndex,
26
+ endRowIndex: Number.POSITIVE_INFINITY,
27
+ startColumnIndex: range.startColumnIndex,
28
+ endColumnIndex: range.endColumnIndex,
29
+ };
30
+ case "right":
31
+ case "left":
32
+ return {
33
+ startRowIndex: range.startRowIndex,
34
+ endRowIndex: range.endRowIndex,
35
+ startColumnIndex: range.startColumnIndex,
36
+ endColumnIndex: Number.POSITIVE_INFINITY,
37
+ };
38
+ }
39
+ };
40
+ /**
41
+ * A cell counts as non-blank when it carries a value, a formula, a format, a note, a link or a
42
+ * citation — Excel's "might appear empty but have blank values, some formatting, or a formula".
43
+ * Derived render results (CF / DV results) don't count.
44
+ */
45
+ const DERIVED_CELL_KEYS = new Set([
46
+ "conditionalFormattingResultById",
47
+ "dataValidationResult",
48
+ ]);
49
+ export const isNonBlankCell = (cell) => {
50
+ if (!cell)
51
+ return false;
52
+ return Object.entries(cell).some(([key, value]) => !DERIVED_CELL_KEYS.has(key) && value !== undefined && value !== null);
53
+ };
54
+ /**
55
+ * INS-06 / INS-09 / SHF-10 — the first non-blank cell that a shift of `count` rows (axis `row`) or
56
+ * columns (axis `column`) would push past `limit` (the last valid row/column index). Only cells
57
+ * inside `band` move. `undefined` when the edit is safe.
58
+ */
59
+ export const findOffSheetPush = ({ rows, axis, count, limit, band, }) => {
60
+ if (!rows || limit === undefined || count <= 0)
61
+ return undefined;
62
+ const firstPushed = limit - count + 1;
63
+ if (axis === "row") {
64
+ const startRow = Math.max(band.startRowIndex, firstPushed, 1);
65
+ for (let rowIndex = startRow; rowIndex < rows.length; rowIndex++) {
66
+ const values = rows[rowIndex]?.values;
67
+ if (!values)
68
+ continue;
69
+ const startCol = Math.max(band.startColumnIndex, 1);
70
+ const endCol = Math.min(band.endColumnIndex, values.length - 1);
71
+ for (let columnIndex = startCol; columnIndex <= endCol; columnIndex++) {
72
+ if (isNonBlankCell(values[columnIndex])) {
73
+ return { rowIndex, columnIndex };
74
+ }
75
+ }
76
+ }
77
+ return undefined;
78
+ }
79
+ const startRow = Math.max(band.startRowIndex, 1);
80
+ const endRow = Math.min(band.endRowIndex, rows.length - 1);
81
+ for (let rowIndex = startRow; rowIndex <= endRow; rowIndex++) {
82
+ const values = rows[rowIndex]?.values;
83
+ if (!values)
84
+ continue;
85
+ const startCol = Math.max(band.startColumnIndex, firstPushed, 1);
86
+ for (let columnIndex = startCol; columnIndex < values.length; columnIndex++) {
87
+ if (isNonBlankCell(values[columnIndex])) {
88
+ return { rowIndex, columnIndex };
89
+ }
90
+ }
91
+ }
92
+ return undefined;
93
+ };
94
+ /**
95
+ * A range the band reaches but does not contain whole is TORN by the shift. For a delete, a range
96
+ * that touches the deleted block must lie wholly inside it (it is removed with the block) — one
97
+ * straddling the block's edge is torn too.
98
+ */
99
+ export const findTornRange = (ranges, band, deletedBlock) => {
100
+ if (!ranges)
101
+ return undefined;
102
+ return ranges.find((range) => {
103
+ if (rangesIntersect(range, band) && !rangeContains(band, range)) {
104
+ return true;
105
+ }
106
+ if (deletedBlock &&
107
+ rangesIntersect(range, deletedBlock) &&
108
+ !rangeContains(deletedBlock, range)) {
109
+ return true;
110
+ }
111
+ return false;
112
+ });
113
+ };
114
+ /** SHF-05 / MRG-07 — a merge the band covers only partially. */
115
+ export const findTornMerge = (merges, band, deletedBlock) => findTornRange(merges, band, deletedBlock);
116
+ /** TBL-05 — a table the band covers only partially (a table wholly inside the band moves intact). */
117
+ export const findTornTable = (tables, sheetId, band, deletedBlock) => {
118
+ if (!tables)
119
+ return undefined;
120
+ return tables.find((table) => table.sheetId === sheetId &&
121
+ !!findTornRange([table.range], band, deletedBlock));
122
+ };
123
+ /** ANC-14 — a CSE / dynamic-array range the band covers only partially. */
124
+ export const findTornArray = (arrayRanges, band, deletedBlock) => findTornRange(arrayRanges, band, deletedBlock);
125
+ /** Ranges (merges / tables) that travel with the band: wholly inside it and, for a delete, not
126
+ * inside the deleted block. */
127
+ export const isRangeInsideBand = (range, band) => rangeContains(band, range);
128
+ export const isRangeInsideBlock = rangeContains;
129
+ /**
130
+ * The Excel refusal (if any) for an insert/delete-cells shift of `range`. Checks run in Excel's
131
+ * order: array tear, merge tear, table tear, off-sheet push. `undefined` means the edit may proceed.
132
+ */
133
+ export const getShiftCellsRefusal = ({ direction, range, sheetId, rows, rowCount, columnCount, merges, tables, arrayRanges, }) => {
134
+ const band = shiftBand(direction, range);
135
+ const isDelete = direction === "up" || direction === "left";
136
+ const deletedBlock = isDelete ? range : undefined;
137
+ if (findTornArray(arrayRanges, band, deletedBlock)) {
138
+ return structuralRefusal("torn-array");
139
+ }
140
+ if (findTornMerge(merges, band, deletedBlock)) {
141
+ return structuralRefusal("torn-merge");
142
+ }
143
+ if (findTornTable(tables, sheetId, band, deletedBlock)) {
144
+ return structuralRefusal("torn-table");
145
+ }
146
+ if (!isDelete) {
147
+ const vertical = direction === "down";
148
+ const count = vertical
149
+ ? range.endRowIndex - range.startRowIndex + 1
150
+ : range.endColumnIndex - range.startColumnIndex + 1;
151
+ const pushed = findOffSheetPush({
152
+ rows,
153
+ axis: vertical ? "row" : "column",
154
+ count,
155
+ limit: vertical ? rowCount : columnCount,
156
+ band,
157
+ });
158
+ if (pushed) {
159
+ return structuralRefusal("off-sheet-push");
160
+ }
161
+ }
162
+ return undefined;
163
+ };
164
+ /**
165
+ * INS-06 / INS-09 — the refusal for a whole-row (`axis: "row"`) or whole-column insert of `count`
166
+ * lines before `referenceIndex`, given the hard grid cap (`maxRowCount` / `maxColumnCount`). Blank
167
+ * trailing lines may fall off the end; a non-blank one refuses the insert.
168
+ */
169
+ export const getInsertLinesRefusal = ({ axis, referenceIndex, count, rows, limit, }) => {
170
+ const band = axis === "row"
171
+ ? {
172
+ startRowIndex: referenceIndex,
173
+ endRowIndex: Number.POSITIVE_INFINITY,
174
+ startColumnIndex: 1,
175
+ endColumnIndex: Number.POSITIVE_INFINITY,
176
+ }
177
+ : {
178
+ startRowIndex: 1,
179
+ endRowIndex: Number.POSITIVE_INFINITY,
180
+ startColumnIndex: referenceIndex,
181
+ endColumnIndex: Number.POSITIVE_INFINITY,
182
+ };
183
+ return findOffSheetPush({ rows, axis, count, limit, band })
184
+ ? structuralRefusal("off-sheet-push")
185
+ : undefined;
186
+ };
@@ -0,0 +1,94 @@
1
+ import type { GridRange } from "@rowsncolumns/common-types";
2
+ import type { CellData, TableView } from "@rowsncolumns/spreadsheet";
3
+ import type { Draft } from "immer";
4
+ import type { useCalculation } from "../hooks/use-calculation";
5
+ import type { CellXfsRegistry } from "../hooks/use-cellxfs-registry";
6
+ import type { SheetData } from "../hooks/use-spreadsheet-state";
7
+ import type { SharedStringRegistry } from "../hooks/use-ss-registry";
8
+ import type { RecalcCellCoordinate } from "../types";
9
+ type StructuralAxis = "row" | "column";
10
+ /**
11
+ * The table parts a row/column delete takes away, computed from the PRE-edit
12
+ * table definitions: tables the delete removes entirely, and — for a column
13
+ * delete — the named columns it drops out of the tables that survive. Excel
14
+ * breaks the structured references into them: `=SUM(Table1[Price])` →
15
+ * `=SUM(#REF!)` once Table1 is gone, `=Table1[@Qty]*2` → `=Table1[@#REF!]*2`
16
+ * once the `Qty` column is deleted, so the cell shows `#REF!` rather than a
17
+ * stale value, `#NAME?` or `#VALUE!`.
18
+ */
19
+ export type RemovedTableParts = {
20
+ tables: string[];
21
+ columns: Array<{
22
+ table: string;
23
+ sheetId: number;
24
+ /** Pre-edit range, to attribute bare `[@Col]` references to this table. */
25
+ range: GridRange;
26
+ columns: string[];
27
+ }>;
28
+ };
29
+ /**
30
+ * Which tables / table columns `deleteRow` (a table goes with its header row)
31
+ * or `deleteColumn` (a table goes when every column is deleted; otherwise the
32
+ * covered columns are spliced out) removes for the band
33
+ * `[minIndex, maxIndex]` on `sheetId`. Mirrors the table updates those
34
+ * interfaces apply to `tables`.
35
+ */
36
+ export declare const collectRemovedTableParts: (tables: TableView[] | undefined, sheetId: number, axis: StructuralAxis, minIndex: number, maxIndex: number) => RemovedTableParts;
37
+ export type BreakStructuredRefsParams<T extends CellData> = {
38
+ draft: Draft<SheetData<T>>;
39
+ /** The edited sheet. */
40
+ sheetId: number;
41
+ axis: StructuralAxis;
42
+ /** Smallest deleted index and the number of deleted rows/columns. */
43
+ referenceIndex: number;
44
+ count: number;
45
+ removed: RemovedTableParts;
46
+ getGraphNodes?: ReturnType<typeof useCalculation>["getGraphNodes"];
47
+ enqueueCalculation?: ReturnType<typeof useCalculation>["enqueueCalculation"];
48
+ enqueueGraphOperation?: ReturnType<typeof useCalculation>["enqueueGraphOperation"];
49
+ recalcCells: Map<string, RecalcCellCoordinate>;
50
+ locale?: string;
51
+ cellXfsRegistry?: CellXfsRegistry;
52
+ sharedStringRegistry?: SharedStringRegistry;
53
+ };
54
+ /**
55
+ * Rewrite, across every sheet, the structured references that point at the
56
+ * table parts a delete removes (see `RemovedTableParts`). Runs BEFORE the
57
+ * splice, so bare `[@Col]` references are attributed to the table by the
58
+ * formula's pre-edit position. Formula cells come from the dependency graph
59
+ * when it is available (cost O(formula cells)); without one the draft is swept.
60
+ */
61
+ export declare const breakStructuredRefsForRemovedTableParts: <T extends CellData>({ draft, sheetId, axis, referenceIndex, count, removed, getGraphNodes, enqueueCalculation, enqueueGraphOperation, recalcCells, locale, cellXfsRegistry, sharedStringRegistry, }: BreakStructuredRefsParams<T>) => void;
62
+ /**
63
+ * Drop the materialised spill cells of every dynamic-array anchor that sits
64
+ * inside the deleted band `[minIndex, maxIndex]` (rows or columns) of
65
+ * `sheetId`. Spilled values live in the sheet data as computed-only cells (no
66
+ * `ue`); once the anchor goes with its row/column they would otherwise slide
67
+ * into place as orphan plain values (Excel: the whole spill disappears).
68
+ * Runs BEFORE the splice. The anchor records its spill as an A1 range in `af`.
69
+ */
70
+ export type SpillRange = {
71
+ startRowIndex: number;
72
+ startColumnIndex: number;
73
+ endRowIndex: number;
74
+ endColumnIndex: number;
75
+ };
76
+ /** The calculator's live spill rectangle for an anchor cell, if it spills. */
77
+ export type GetArrayRange = (position: {
78
+ sheetId: number;
79
+ rowIndex: number;
80
+ columnIndex: number;
81
+ }) => SpillRange | undefined;
82
+ export declare const removeSpillsOfDeletedAnchors: <T extends CellData>({ draft, sheetId, axis, minIndex, maxIndex, getArrayRange, locale, cellXfsRegistry, sharedStringRegistry, }: {
83
+ draft: Draft<SheetData<T>>;
84
+ sheetId: number;
85
+ axis: StructuralAxis;
86
+ minIndex: number;
87
+ maxIndex: number;
88
+ getArrayRange?: GetArrayRange;
89
+ locale?: string;
90
+ cellXfsRegistry?: CellXfsRegistry;
91
+ sharedStringRegistry?: SharedStringRegistry;
92
+ }) => void;
93
+ export {};
94
+ //# sourceMappingURL=structural-table-refs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"structural-table-refs.d.ts","sourceRoot":"","sources":["../../../api/structural-table-refs.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,219 @@
1
+ import { isCellNode } from "@rowsncolumns/dag";
2
+ import { addressToCell, getCellUserEnteredValue, getExtendedValueFormula, } from "@rowsncolumns/utils";
3
+ import { SheetCell } from "../sheet-cell";
4
+ import { breakTableColumnsInFormula, breakTableReferencesInFormula, createSheetCellIdentifier, } from "./formula";
5
+ import { createSheetDraft } from "./immer-utils";
6
+ /**
7
+ * Which tables / table columns `deleteRow` (a table goes with its header row)
8
+ * or `deleteColumn` (a table goes when every column is deleted; otherwise the
9
+ * covered columns are spliced out) removes for the band
10
+ * `[minIndex, maxIndex]` on `sheetId`. Mirrors the table updates those
11
+ * interfaces apply to `tables`.
12
+ */
13
+ export const collectRemovedTableParts = (tables, sheetId, axis, minIndex, maxIndex) => {
14
+ const removed = { tables: [], columns: [] };
15
+ for (const table of tables ?? []) {
16
+ if (table.sheetId !== sheetId || !table.title)
17
+ continue;
18
+ if (axis === "row") {
19
+ const { startRowIndex } = table.range;
20
+ if (startRowIndex >= minIndex && startRowIndex <= maxIndex) {
21
+ removed.tables.push(table.title);
22
+ }
23
+ continue;
24
+ }
25
+ const { startColumnIndex, endColumnIndex } = table.range;
26
+ if (maxIndex < startColumnIndex || minIndex > endColumnIndex)
27
+ continue;
28
+ if (minIndex <= startColumnIndex && maxIndex >= endColumnIndex) {
29
+ removed.tables.push(table.title);
30
+ continue;
31
+ }
32
+ const overlapStart = Math.max(minIndex, startColumnIndex);
33
+ const overlapEnd = Math.min(maxIndex, endColumnIndex);
34
+ const names = [];
35
+ for (let column = overlapStart; column <= overlapEnd; column++) {
36
+ const name = table.columns?.[column - startColumnIndex]?.name;
37
+ if (name)
38
+ names.push(name);
39
+ }
40
+ if (names.length) {
41
+ removed.columns.push({
42
+ table: table.title,
43
+ sheetId,
44
+ range: table.range,
45
+ columns: names,
46
+ });
47
+ }
48
+ }
49
+ return removed;
50
+ };
51
+ const isEmptyRemoval = (removed) => removed.tables.length === 0 && removed.columns.length === 0;
52
+ const isWithinRange = (range, rowIndex, columnIndex) => rowIndex >= range.startRowIndex &&
53
+ rowIndex <= range.endRowIndex &&
54
+ columnIndex >= range.startColumnIndex &&
55
+ columnIndex <= range.endColumnIndex;
56
+ /**
57
+ * Rewrite, across every sheet, the structured references that point at the
58
+ * table parts a delete removes (see `RemovedTableParts`). Runs BEFORE the
59
+ * splice, so bare `[@Col]` references are attributed to the table by the
60
+ * formula's pre-edit position. Formula cells come from the dependency graph
61
+ * when it is available (cost O(formula cells)); without one the draft is swept.
62
+ */
63
+ export const breakStructuredRefsForRemovedTableParts = ({ draft, sheetId, axis, referenceIndex, count, removed, getGraphNodes, enqueueCalculation, enqueueGraphOperation, recalcCells, locale, cellXfsRegistry, sharedStringRegistry, }) => {
64
+ if (isEmptyRemoval(removed))
65
+ return;
66
+ const sheetCell = new SheetCell();
67
+ const isRow = axis === "row";
68
+ const afterBand = referenceIndex + count;
69
+ for (const pos of collectFormulaPositions(draft, getGraphNodes)) {
70
+ const currentSheet = createSheetDraft(draft, pos.sheetId, pos.rowIndex);
71
+ const cellData = currentSheet[pos.rowIndex]?.values?.[pos.columnIndex];
72
+ const formula = getExtendedValueFormula(getCellUserEnteredValue(cellData));
73
+ if (!formula || !formula.includes("["))
74
+ continue;
75
+ let updated = formula;
76
+ for (const table of removed.tables) {
77
+ updated = breakTableReferencesInFormula(updated, table);
78
+ }
79
+ for (const entry of removed.columns) {
80
+ updated = breakTableColumnsInFormula(updated, entry.table, entry.columns);
81
+ // A bare `[@Col]` belongs to the table the formula cell sits in.
82
+ if (pos.sheetId === entry.sheetId &&
83
+ isWithinRange(entry.range, pos.rowIndex, pos.columnIndex)) {
84
+ updated = breakTableColumnsInFormula(updated, undefined, entry.columns);
85
+ }
86
+ }
87
+ if (updated === formula)
88
+ continue;
89
+ sheetCell.assign(pos.sheetId, { rowIndex: pos.rowIndex, columnIndex: pos.columnIndex }, cellData, { locale, cellXfsRegistry, sharedStringRegistry });
90
+ sheetCell.setFormulaValue(updated, false);
91
+ currentSheet[pos.rowIndex].values[pos.columnIndex] =
92
+ sheetCell.getCellData();
93
+ // Where the cell ends up after the splice that follows (cells strictly
94
+ // after the deleted band on the edited sheet close up by `count`).
95
+ const shifts = pos.sheetId === sheetId &&
96
+ (isRow ? pos.rowIndex : pos.columnIndex) >= afterBand;
97
+ const nextPosition = {
98
+ sheetId: pos.sheetId,
99
+ rowIndex: pos.rowIndex - (shifts && isRow ? count : 0),
100
+ columnIndex: pos.columnIndex - (shifts && !isRow ? count : 0),
101
+ };
102
+ const enqueueOperation = sheetCell.ephemeral()
103
+ ? enqueueCalculation
104
+ : enqueueGraphOperation;
105
+ enqueueOperation?.({ type: "remove", position: pos });
106
+ enqueueOperation?.({ type: "add", position: nextPosition, force: true });
107
+ recalcCells.set(createSheetCellIdentifier(nextPosition.sheetId, nextPosition.rowIndex, nextPosition.columnIndex), { ...nextPosition, force: true });
108
+ }
109
+ };
110
+ const collectFormulaPositions = (draft, getGraphNodes) => {
111
+ if (getGraphNodes) {
112
+ let graphNodes;
113
+ try {
114
+ graphNodes = getGraphNodes();
115
+ }
116
+ catch {
117
+ graphNodes = undefined;
118
+ }
119
+ if (graphNodes && typeof graphNodes.values === "function") {
120
+ const positions = [];
121
+ let sawNode = false;
122
+ for (const node of graphNodes.values()) {
123
+ if (!isCellNode(node))
124
+ continue;
125
+ sawNode = true;
126
+ positions.push({ ...node.position });
127
+ }
128
+ if (sawNode)
129
+ return positions;
130
+ }
131
+ }
132
+ // Fallback sweep (no graph, or an empty one): every formula cell in the draft.
133
+ const positions = [];
134
+ for (const key of Object.keys(draft)) {
135
+ const sheetId = Number(key);
136
+ const rows = draft[sheetId];
137
+ if (!rows)
138
+ continue;
139
+ rows.forEach((row, rowIndex) => {
140
+ row?.values?.forEach((cellData, columnIndex) => {
141
+ if (getExtendedValueFormula(getCellUserEnteredValue(cellData))) {
142
+ positions.push({ sheetId, rowIndex, columnIndex });
143
+ }
144
+ });
145
+ });
146
+ }
147
+ return positions;
148
+ };
149
+ export const removeSpillsOfDeletedAnchors = ({ draft, sheetId, axis, minIndex, maxIndex, getArrayRange, locale, cellXfsRegistry, sharedStringRegistry, }) => {
150
+ const sheetRows = draft[sheetId];
151
+ if (!sheetRows)
152
+ return;
153
+ const sheetCell = new SheetCell();
154
+ const anchors = [];
155
+ const collect = (rowIndex, columnIndex) => {
156
+ const cellData = sheetRows[rowIndex]?.values?.[columnIndex];
157
+ if (!cellData)
158
+ return;
159
+ // The calculator knows the current spill; the persisted `af` (written on
160
+ // calc success by hosts that track it) is the fallback.
161
+ const live = getExtendedValueFormula(getCellUserEnteredValue(cellData)) !== undefined
162
+ ? getArrayRange?.({ sheetId, rowIndex, columnIndex })
163
+ : undefined;
164
+ const range = live ?? spillRangeFromRef(cellData.af);
165
+ if (range)
166
+ anchors.push({ rowIndex, columnIndex, range });
167
+ };
168
+ if (axis === "row") {
169
+ for (let rowIndex = minIndex; rowIndex <= maxIndex; rowIndex++) {
170
+ sheetRows[rowIndex]?.values?.forEach((_cell, columnIndex) => collect(rowIndex, columnIndex));
171
+ }
172
+ }
173
+ else {
174
+ sheetRows.forEach((row, rowIndex) => {
175
+ if (!row?.values)
176
+ return;
177
+ for (let columnIndex = minIndex; columnIndex <= maxIndex; columnIndex++) {
178
+ collect(rowIndex, columnIndex);
179
+ }
180
+ });
181
+ }
182
+ for (const anchor of anchors) {
183
+ const { range } = anchor;
184
+ for (let rowIndex = range.startRowIndex; rowIndex <= range.endRowIndex; rowIndex++) {
185
+ const rowValues = sheetRows[rowIndex]?.values;
186
+ if (!rowValues)
187
+ continue;
188
+ for (let columnIndex = range.startColumnIndex; columnIndex <= range.endColumnIndex; columnIndex++) {
189
+ if (rowIndex === anchor.rowIndex &&
190
+ columnIndex === anchor.columnIndex) {
191
+ continue;
192
+ }
193
+ const cellData = rowValues[columnIndex];
194
+ if (!cellData || getCellUserEnteredValue(cellData) !== undefined) {
195
+ continue;
196
+ }
197
+ sheetCell.assign(sheetId, { rowIndex, columnIndex }, cellData, { locale, cellXfsRegistry, sharedStringRegistry });
198
+ sheetCell.deleteContents();
199
+ rowValues[columnIndex] = sheetCell.getCellData();
200
+ }
201
+ }
202
+ }
203
+ };
204
+ /** `C1:C3` (the anchor's persisted `af`) → its rectangle; `undefined` when absent/unparseable. */
205
+ const spillRangeFromRef = (ref) => {
206
+ if (!ref)
207
+ return undefined;
208
+ const [startAddress, endAddress] = ref.split(":");
209
+ const start = addressToCell(startAddress);
210
+ const end = addressToCell(endAddress ?? startAddress);
211
+ if (!start || !end)
212
+ return undefined;
213
+ return {
214
+ startRowIndex: start.rowIndex,
215
+ startColumnIndex: start.columnIndex,
216
+ endRowIndex: end.rowIndex,
217
+ endColumnIndex: end.columnIndex,
218
+ };
219
+ };
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Excel's refusal messages for structural edits (insert/delete cells with shift, whole row/column
3
+ * insert, move/cut). The engines refuse the edit and leave the document untouched; the UI shows the
4
+ * message. Shared by the legacy JS hooks (`api/structural-guards.ts`) and the rnc engine's
5
+ * `CommitResult.rejected` channel (`libs/rnc-engine`), so both surfaces speak the same words.
6
+ */
7
+ export declare const STRUCTURAL_REFUSAL_TITLE = "Microsoft Excel";
8
+ /** INS-06 / INS-09 / SHF-10 — a non-blank (or formatted) cell would be pushed past the last row/column. */
9
+ export declare const OFF_SHEET_PUSH_MESSAGE = "Microsoft Excel can't insert new cells because it would push non-empty cells off the end of the worksheet. These non-empty cells might appear empty but have blank values, some formatting, or a formula. Delete enough rows or columns to make room for what you want to insert and then try again.";
10
+ /** SHF-05 / MRG-07 — the shifted band covers only part of a merged cell. */
11
+ export declare const MERGED_CELL_REFUSAL_MESSAGE = "We can't do that to a merged cell.";
12
+ /** TBL-05 — the shifted band would tear a table apart. */
13
+ export declare const TABLE_SHIFT_REFUSAL_MESSAGE = "This won't work because it would move cells in a table on your worksheet.";
14
+ /** ANC-14 — the shifted band covers only part of a CSE / dynamic array. */
15
+ export declare const ARRAY_REFUSAL_MESSAGE = "You can't change part of an array.";
16
+ /** Machine-readable refusal codes — the rnc engine's `CommitResult.rejected.code` uses the same set. */
17
+ export type StructuralRefusalCode = "off-sheet-push" | "torn-merge" | "torn-table" | "torn-array";
18
+ export type StructuralRefusal = {
19
+ code: StructuralRefusalCode;
20
+ title: string;
21
+ message: string;
22
+ };
23
+ export declare const STRUCTURAL_REFUSAL_MESSAGES: Record<StructuralRefusalCode, string>;
24
+ export declare const structuralRefusal: (code: StructuralRefusalCode) => StructuralRefusal;
25
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../constants.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,oBAAoB,CAAC;AAE1D,2GAA2G;AAC3G,eAAO,MAAM,sBAAsB,ySACqQ,CAAC;AAEzS,4EAA4E;AAC5E,eAAO,MAAM,2BAA2B,uCAAuC,CAAC;AAEhF,0DAA0D;AAC1D,eAAO,MAAM,2BAA2B,8EACqC,CAAC;AAE9E,2EAA2E;AAC3E,eAAO,MAAM,qBAAqB,uCAAuC,CAAC;AAE1E,wGAAwG;AACxG,MAAM,MAAM,qBAAqB,GAC7B,gBAAgB,GAChB,YAAY,GACZ,YAAY,GACZ,YAAY,CAAC;AAEjB,MAAM,MAAM,iBAAiB,GAAG;IAC9B,IAAI,EAAE,qBAAqB,CAAC;IAC5B,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;CACjB,CAAC;AAEF,eAAO,MAAM,2BAA2B,EAAE,MAAM,CAAC,qBAAqB,EAAE,MAAM,CAK7E,CAAC;AAEF,eAAO,MAAM,iBAAiB,GAC5B,MAAM,qBAAqB,KAC1B,iBAID,CAAC"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * Excel's refusal messages for structural edits (insert/delete cells with shift, whole row/column
3
+ * insert, move/cut). The engines refuse the edit and leave the document untouched; the UI shows the
4
+ * message. Shared by the legacy JS hooks (`api/structural-guards.ts`) and the rnc engine's
5
+ * `CommitResult.rejected` channel (`libs/rnc-engine`), so both surfaces speak the same words.
6
+ */
7
+ export const STRUCTURAL_REFUSAL_TITLE = "Microsoft Excel";
8
+ /** INS-06 / INS-09 / SHF-10 — a non-blank (or formatted) cell would be pushed past the last row/column. */
9
+ export const OFF_SHEET_PUSH_MESSAGE = "Microsoft Excel can't insert new cells because it would push non-empty cells off the end of the worksheet. These non-empty cells might appear empty but have blank values, some formatting, or a formula. Delete enough rows or columns to make room for what you want to insert and then try again.";
10
+ /** SHF-05 / MRG-07 — the shifted band covers only part of a merged cell. */
11
+ export const MERGED_CELL_REFUSAL_MESSAGE = "We can't do that to a merged cell.";
12
+ /** TBL-05 — the shifted band would tear a table apart. */
13
+ export const TABLE_SHIFT_REFUSAL_MESSAGE = "This won't work because it would move cells in a table on your worksheet.";
14
+ /** ANC-14 — the shifted band covers only part of a CSE / dynamic array. */
15
+ export const ARRAY_REFUSAL_MESSAGE = "You can't change part of an array.";
16
+ export const STRUCTURAL_REFUSAL_MESSAGES = {
17
+ "off-sheet-push": OFF_SHEET_PUSH_MESSAGE,
18
+ "torn-merge": MERGED_CELL_REFUSAL_MESSAGE,
19
+ "torn-table": TABLE_SHIFT_REFUSAL_MESSAGE,
20
+ "torn-array": ARRAY_REFUSAL_MESSAGE,
21
+ };
22
+ export const structuralRefusal = (code) => ({
23
+ code,
24
+ title: STRUCTURAL_REFUSAL_TITLE,
25
+ message: STRUCTURAL_REFUSAL_MESSAGES[code],
26
+ });
@@ -1 +1 @@
1
- {"version":3,"file":"use-calculation-worker.d.ts","sourceRoot":"","sources":["../../../hooks/use-calculation-worker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,8BAA8B,EAQ9B,mBAAmB,EACpB,MAAM,kCAAkC,CAAC;AAU1C,OAAO,KAAK,EACV,2BAA2B,EAE5B,MAAM,4BAA4B,CAAC;AAmBpC,OAAO,KAAK,EAEV,UAAU,EACV,SAAS,EACV,MAAM,2BAA2B,CAAC;AAgBnC,OAAO,KAAK,EAKV,mBAAmB,EACpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGxD,KAAK,wBAAwB,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AAElE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,cAAc,CAAC;IACrB,KAAK,EACD,OAAO,GACP,YAAY,GACZ,iBAAiB,GACjB,iBAAiB,GACjB,yBAAyB,GACzB,cAAc,GACd,UAAU,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAyEF,MAAM,MAAM,yBAAyB,GAAG,mBAAmB,GAAG;IAC5D,MAAM,EAAE,mBAAmB,EAAE,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,MAAM,CAAC;IAC5B,YAAY,CAAC,EAAE,8BAA8B,CAAC,QAAQ,CAAC,CAAC;IACxD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC;IACrB,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oBAAoB,CAAC,EAAE,2BAA2B,CAAC;IACnD,kBAAkB,CAAC,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAAC;IAClD,wBAAwB,CAAC,EAAE,OAAO,CAAC;CACpC,CAAC;AA8KF,eAAO,MAAM,oBAAoB,GAC/B,OAAO,yBAAyB,KAC/B,wBA+/DF,CAAC"}
1
+ {"version":3,"file":"use-calculation-worker.d.ts","sourceRoot":"","sources":["../../../hooks/use-calculation-worker.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAEV,8BAA8B,EAQ9B,mBAAmB,EACpB,MAAM,kCAAkC,CAAC;AAU1C,OAAO,KAAK,EACV,2BAA2B,EAE5B,MAAM,4BAA4B,CAAC;AAmBpC,OAAO,KAAK,EAEV,UAAU,EACV,SAAS,EACV,MAAM,2BAA2B,CAAC;AAgBnC,OAAO,KAAK,EAKV,mBAAmB,EACpB,MAAM,mBAAmB,CAAC;AAC3B,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAGxD,KAAK,wBAAwB,GAAG,UAAU,CAAC,OAAO,cAAc,CAAC,CAAC;AAElE,MAAM,MAAM,gBAAgB,GAAG;IAC7B,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,EAAE,MAAM,CAAC;IACtB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;CACvB,CAAC;AAEF,MAAM,MAAM,eAAe,GAAG;IAC5B,IAAI,EAAE,cAAc,CAAC;IACrB,KAAK,EACD,OAAO,GACP,YAAY,GACZ,iBAAiB,GACjB,iBAAiB,GACjB,yBAAyB,GACzB,cAAc,GACd,UAAU,CAAC;IACf,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB,CAAC;AAyEF,MAAM,MAAM,yBAAyB,GAAG,mBAAmB,GAAG;IAC5D,MAAM,EAAE,mBAAmB,EAAE,CAAC;IAC9B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,YAAY,CAAC,EAAE,MAAM,MAAM,CAAC;IAC5B,YAAY,CAAC,EAAE,8BAA8B,CAAC,QAAQ,CAAC,CAAC;IACxD,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC;IACrB,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,sBAAsB,CAAC,EAAE,MAAM,CAAC;IAChC,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,oBAAoB,CAAC,EAAE,2BAA2B,CAAC;IACnD,kBAAkB,CAAC,CAAC,KAAK,EAAE,eAAe,GAAG,IAAI,CAAC;IAClD,wBAAwB,CAAC,EAAE,OAAO,CAAC;CACpC,CAAC;AAsLF,eAAO,MAAM,oBAAoB,GAC/B,OAAO,yBAAyB,KAC/B,wBA4iEF,CAAC"}