@rowsncolumns/spreadsheet-state 28.0.72 → 28.0.74

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 (185) hide show
  1. package/dist/cjs/api/advanced-filter.d.ts +72 -0
  2. package/dist/cjs/api/advanced-filter.d.ts.map +1 -0
  3. package/dist/cjs/api/advanced-filter.js +0 -0
  4. package/dist/cjs/api/goal-seek.d.ts +29 -0
  5. package/dist/cjs/api/goal-seek.d.ts.map +1 -0
  6. package/dist/cjs/api/goal-seek.js +131 -0
  7. package/dist/cjs/api/immer-utils.d.ts.map +1 -1
  8. package/dist/cjs/api/immer-utils.js +2 -0
  9. package/dist/cjs/api/paint-format.d.ts +41 -0
  10. package/dist/cjs/api/paint-format.d.ts.map +1 -0
  11. package/dist/cjs/api/paint-format.js +115 -0
  12. package/dist/cjs/api/paste-special.d.ts +48 -0
  13. package/dist/cjs/api/paste-special.d.ts.map +1 -0
  14. package/dist/cjs/api/paste-special.js +229 -0
  15. package/dist/cjs/api/split-text.d.ts +12 -0
  16. package/dist/cjs/api/split-text.d.ts.map +1 -1
  17. package/dist/cjs/api/split-text.js +41 -0
  18. package/dist/cjs/api/subtotal.d.ts +85 -0
  19. package/dist/cjs/api/subtotal.d.ts.map +1 -0
  20. package/dist/cjs/api/subtotal.js +250 -0
  21. package/dist/cjs/commands.d.ts +13 -0
  22. package/dist/cjs/commands.d.ts.map +1 -1
  23. package/dist/cjs/components/editors/data-validation-rule-editor.d.ts.map +1 -1
  24. package/dist/cjs/components/editors/data-validation-rule-editor.js +83 -16
  25. package/dist/cjs/components/editors/table-editor.js +7 -2
  26. package/dist/cjs/hooks/use-calculation-worker.d.ts.map +1 -1
  27. package/dist/cjs/hooks/use-calculation-worker.js +39 -8
  28. package/dist/cjs/hooks/use-calculation.d.ts +12 -2
  29. package/dist/cjs/hooks/use-calculation.d.ts.map +1 -1
  30. package/dist/cjs/hooks/use-calculation.js +71 -35
  31. package/dist/cjs/hooks/use-on-advanced-filter.d.ts +21 -0
  32. package/dist/cjs/hooks/use-on-advanced-filter.d.ts.map +1 -0
  33. package/dist/cjs/hooks/use-on-advanced-filter.js +42 -0
  34. package/dist/cjs/hooks/use-on-goal-seek.d.ts +30 -0
  35. package/dist/cjs/hooks/use-on-goal-seek.d.ts.map +1 -0
  36. package/dist/cjs/hooks/use-on-goal-seek.js +140 -0
  37. package/dist/cjs/hooks/use-on-import-excel.d.ts +10 -1
  38. package/dist/cjs/hooks/use-on-import-excel.d.ts.map +1 -1
  39. package/dist/cjs/hooks/use-on-import-excel.js +8 -4
  40. package/dist/cjs/hooks/use-on-merge-unmerge-cells.d.ts.map +1 -1
  41. package/dist/cjs/hooks/use-on-merge-unmerge-cells.js +53 -34
  42. package/dist/cjs/hooks/use-on-paste.d.ts.map +1 -1
  43. package/dist/cjs/hooks/use-on-paste.js +82 -52
  44. package/dist/cjs/hooks/use-on-remove-duplicates.d.ts +2 -1
  45. package/dist/cjs/hooks/use-on-remove-duplicates.d.ts.map +1 -1
  46. package/dist/cjs/hooks/use-on-remove-duplicates.js +5 -3
  47. package/dist/cjs/hooks/use-on-show-column.d.ts.map +1 -1
  48. package/dist/cjs/hooks/use-on-show-column.js +2 -0
  49. package/dist/cjs/hooks/use-on-show-row.d.ts.map +1 -1
  50. package/dist/cjs/hooks/use-on-show-row.js +2 -0
  51. package/dist/cjs/hooks/use-on-subtotal.d.ts +26 -0
  52. package/dist/cjs/hooks/use-on-subtotal.d.ts.map +1 -0
  53. package/dist/cjs/hooks/use-on-subtotal.js +53 -0
  54. package/dist/cjs/hooks/use-paint-format.d.ts +19 -3
  55. package/dist/cjs/hooks/use-paint-format.d.ts.map +1 -1
  56. package/dist/cjs/hooks/use-paint-format.js +85 -31
  57. package/dist/cjs/hooks/use-sheet-properties.d.ts +3 -0
  58. package/dist/cjs/hooks/use-sheet-properties.d.ts.map +1 -1
  59. package/dist/cjs/hooks/use-sheet-properties.js +8 -0
  60. package/dist/cjs/hooks/use-split-text-to-columns.d.ts +2 -1
  61. package/dist/cjs/hooks/use-split-text-to-columns.d.ts.map +1 -1
  62. package/dist/cjs/hooks/use-split-text-to-columns.js +3 -2
  63. package/dist/cjs/hooks/use-spreadsheet-state.d.ts +50 -36
  64. package/dist/cjs/hooks/use-spreadsheet-state.d.ts.map +1 -1
  65. package/dist/cjs/hooks/use-spreadsheet-state.js +98 -18
  66. package/dist/cjs/index.d.ts +9 -0
  67. package/dist/cjs/index.d.ts.map +1 -1
  68. package/dist/cjs/index.js +9 -0
  69. package/dist/cjs/interface/calculation-interface.d.ts.map +1 -1
  70. package/dist/cjs/interface/calculation-interface.js +5 -0
  71. package/dist/cjs/interface/change-batch-stream.d.ts.map +1 -1
  72. package/dist/cjs/interface/change-batch-stream.js +12 -6
  73. package/dist/cjs/interface/change-batch.d.ts.map +1 -1
  74. package/dist/cjs/interface/change-batch.js +17 -9
  75. package/dist/cjs/interface/change-formatting.d.ts.map +1 -1
  76. package/dist/cjs/interface/change-formatting.js +30 -11
  77. package/dist/cjs/interface/hide-column.d.ts.map +1 -1
  78. package/dist/cjs/interface/hide-column.js +4 -0
  79. package/dist/cjs/interface/hide-row.d.ts.map +1 -1
  80. package/dist/cjs/interface/hide-row.js +4 -0
  81. package/dist/cjs/interface/remove-duplicates.d.ts +3 -2
  82. package/dist/cjs/interface/remove-duplicates.d.ts.map +1 -1
  83. package/dist/cjs/interface/remove-duplicates.js +41 -7
  84. package/dist/cjs/interface/split-text.d.ts +2 -1
  85. package/dist/cjs/interface/split-text.d.ts.map +1 -1
  86. package/dist/cjs/interface/split-text.js +7 -3
  87. package/dist/cjs/interface/spreadsheet-interface.d.ts +2 -1
  88. package/dist/cjs/interface/spreadsheet-interface.d.ts.map +1 -1
  89. package/dist/cjs/interface/spreadsheet-interface.js +5 -5
  90. package/dist/cjs/sheet-cell.d.ts.map +1 -1
  91. package/dist/cjs/sheet-cell.js +7 -0
  92. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  93. package/dist/esm/api/advanced-filter.d.ts +72 -0
  94. package/dist/esm/api/advanced-filter.d.ts.map +1 -0
  95. package/dist/esm/api/advanced-filter.js +0 -0
  96. package/dist/esm/api/goal-seek.d.ts +29 -0
  97. package/dist/esm/api/goal-seek.d.ts.map +1 -0
  98. package/dist/esm/api/goal-seek.js +108 -0
  99. package/dist/esm/api/immer-utils.d.ts.map +1 -1
  100. package/dist/esm/api/immer-utils.js +2 -0
  101. package/dist/esm/api/paint-format.d.ts +41 -0
  102. package/dist/esm/api/paint-format.d.ts.map +1 -0
  103. package/dist/esm/api/paint-format.js +77 -0
  104. package/dist/esm/api/paste-special.d.ts +48 -0
  105. package/dist/esm/api/paste-special.d.ts.map +1 -0
  106. package/dist/esm/api/paste-special.js +220 -0
  107. package/dist/esm/api/split-text.d.ts +12 -0
  108. package/dist/esm/api/split-text.d.ts.map +1 -1
  109. package/dist/esm/api/split-text.js +28 -0
  110. package/dist/esm/api/subtotal.d.ts +85 -0
  111. package/dist/esm/api/subtotal.d.ts.map +1 -0
  112. package/dist/esm/api/subtotal.js +181 -0
  113. package/dist/esm/commands.d.ts +13 -0
  114. package/dist/esm/commands.d.ts.map +1 -1
  115. package/dist/esm/components/editors/data-validation-rule-editor.d.ts.map +1 -1
  116. package/dist/esm/components/editors/data-validation-rule-editor.js +52 -4
  117. package/dist/esm/components/editors/table-editor.js +7 -4
  118. package/dist/esm/hooks/use-calculation-worker.d.ts.map +1 -1
  119. package/dist/esm/hooks/use-calculation-worker.js +20 -2
  120. package/dist/esm/hooks/use-calculation.d.ts +12 -2
  121. package/dist/esm/hooks/use-calculation.d.ts.map +1 -1
  122. package/dist/esm/hooks/use-calculation.js +38 -7
  123. package/dist/esm/hooks/use-on-advanced-filter.d.ts +21 -0
  124. package/dist/esm/hooks/use-on-advanced-filter.d.ts.map +1 -0
  125. package/dist/esm/hooks/use-on-advanced-filter.js +41 -0
  126. package/dist/esm/hooks/use-on-goal-seek.d.ts +30 -0
  127. package/dist/esm/hooks/use-on-goal-seek.d.ts.map +1 -0
  128. package/dist/esm/hooks/use-on-goal-seek.js +82 -0
  129. package/dist/esm/hooks/use-on-import-excel.d.ts +10 -1
  130. package/dist/esm/hooks/use-on-import-excel.d.ts.map +1 -1
  131. package/dist/esm/hooks/use-on-import-excel.js +8 -4
  132. package/dist/esm/hooks/use-on-merge-unmerge-cells.d.ts.map +1 -1
  133. package/dist/esm/hooks/use-on-merge-unmerge-cells.js +13 -13
  134. package/dist/esm/hooks/use-on-paste.d.ts.map +1 -1
  135. package/dist/esm/hooks/use-on-paste.js +49 -14
  136. package/dist/esm/hooks/use-on-remove-duplicates.d.ts +2 -1
  137. package/dist/esm/hooks/use-on-remove-duplicates.d.ts.map +1 -1
  138. package/dist/esm/hooks/use-on-remove-duplicates.js +5 -3
  139. package/dist/esm/hooks/use-on-show-column.d.ts.map +1 -1
  140. package/dist/esm/hooks/use-on-show-column.js +2 -0
  141. package/dist/esm/hooks/use-on-show-row.d.ts.map +1 -1
  142. package/dist/esm/hooks/use-on-show-row.js +2 -0
  143. package/dist/esm/hooks/use-on-subtotal.d.ts +26 -0
  144. package/dist/esm/hooks/use-on-subtotal.d.ts.map +1 -0
  145. package/dist/esm/hooks/use-on-subtotal.js +48 -0
  146. package/dist/esm/hooks/use-paint-format.d.ts +19 -3
  147. package/dist/esm/hooks/use-paint-format.d.ts.map +1 -1
  148. package/dist/esm/hooks/use-paint-format.js +71 -29
  149. package/dist/esm/hooks/use-sheet-properties.d.ts +3 -0
  150. package/dist/esm/hooks/use-sheet-properties.d.ts.map +1 -1
  151. package/dist/esm/hooks/use-sheet-properties.js +8 -0
  152. package/dist/esm/hooks/use-split-text-to-columns.d.ts +2 -1
  153. package/dist/esm/hooks/use-split-text-to-columns.d.ts.map +1 -1
  154. package/dist/esm/hooks/use-split-text-to-columns.js +3 -2
  155. package/dist/esm/hooks/use-spreadsheet-state.d.ts +50 -36
  156. package/dist/esm/hooks/use-spreadsheet-state.d.ts.map +1 -1
  157. package/dist/esm/hooks/use-spreadsheet-state.js +89 -16
  158. package/dist/esm/index.d.ts +9 -0
  159. package/dist/esm/index.d.ts.map +1 -1
  160. package/dist/esm/index.js +9 -0
  161. package/dist/esm/interface/calculation-interface.d.ts.map +1 -1
  162. package/dist/esm/interface/calculation-interface.js +3 -0
  163. package/dist/esm/interface/change-batch-stream.d.ts.map +1 -1
  164. package/dist/esm/interface/change-batch-stream.js +12 -6
  165. package/dist/esm/interface/change-batch.d.ts.map +1 -1
  166. package/dist/esm/interface/change-batch.js +15 -9
  167. package/dist/esm/interface/change-formatting.d.ts.map +1 -1
  168. package/dist/esm/interface/change-formatting.js +28 -11
  169. package/dist/esm/interface/hide-column.d.ts.map +1 -1
  170. package/dist/esm/interface/hide-column.js +4 -0
  171. package/dist/esm/interface/hide-row.d.ts.map +1 -1
  172. package/dist/esm/interface/hide-row.js +4 -0
  173. package/dist/esm/interface/remove-duplicates.d.ts +3 -2
  174. package/dist/esm/interface/remove-duplicates.d.ts.map +1 -1
  175. package/dist/esm/interface/remove-duplicates.js +37 -6
  176. package/dist/esm/interface/split-text.d.ts +2 -1
  177. package/dist/esm/interface/split-text.d.ts.map +1 -1
  178. package/dist/esm/interface/split-text.js +6 -3
  179. package/dist/esm/interface/spreadsheet-interface.d.ts +2 -1
  180. package/dist/esm/interface/spreadsheet-interface.d.ts.map +1 -1
  181. package/dist/esm/interface/spreadsheet-interface.js +5 -5
  182. package/dist/esm/sheet-cell.d.ts.map +1 -1
  183. package/dist/esm/sheet-cell.js +7 -0
  184. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  185. package/package.json +18 -18
@@ -0,0 +1,229 @@
1
+ import { __assign, __rest } from "tslib";
2
+ import { getCellUserEnteredValue, getExtendedValueBool, getExtendedValueFormula, getExtendedValueNumber, getExtendedValueString, isNil, } from "@rowsncolumns/utils";
3
+ import { SheetCell } from "../sheet-cell";
4
+ import { convertToNumber, isNumber } from "./datatype";
5
+ /** Synthetic clipboard key carrying the legacy single-mode `PasteSpecialType`. */
6
+ export var PASTE_SPECIAL_TYPE_KEY = "pasteSpecialType";
7
+ /** Synthetic clipboard key carrying a JSON-encoded `PasteSpecialOptions`. */
8
+ export var PASTE_SPECIAL_OPTIONS_KEY = "pasteSpecialOptions";
9
+ var PLAIN_PASTE = {
10
+ paste: "all",
11
+ skipBlanks: false,
12
+ transpose: false,
13
+ isSpecial: false,
14
+ };
15
+ var LEGACY_TYPE_TO_OPTIONS = {
16
+ Transposed: { transpose: true },
17
+ Formatting: { paste: "formats" },
18
+ Formula: { paste: "formulas" },
19
+ Value: { paste: "values" },
20
+ Link: {},
21
+ };
22
+ var isOperation = function (value) {
23
+ return value === "add" ||
24
+ value === "subtract" ||
25
+ value === "multiply" ||
26
+ value === "divide";
27
+ };
28
+ var isPasteMode = function (value) {
29
+ return value === "all" ||
30
+ value === "values" ||
31
+ value === "formulas" ||
32
+ value === "formats";
33
+ };
34
+ /**
35
+ * Normalize a `PasteSpecialOptions` (or the legacy single-mode string) into
36
+ * the flags the paste pipelines branch on.
37
+ */
38
+ export var normalizePasteSpecialOptions = function (input) {
39
+ var _a;
40
+ if (!input)
41
+ return PLAIN_PASTE;
42
+ var options = typeof input === "string" ? (_a = LEGACY_TYPE_TO_OPTIONS[input]) !== null && _a !== void 0 ? _a : {} : input;
43
+ var paste = isPasteMode(options.paste) ? options.paste : "all";
44
+ // Excel greys Operation out under Paste → Formats: a formats paste never
45
+ // touches destination values, so an operation is dropped here for both
46
+ // pipelines rather than each guarding separately.
47
+ var operation = paste !== "formats" && isOperation(options.operation)
48
+ ? options.operation
49
+ : undefined;
50
+ var skipBlanks = Boolean(options.skipBlanks);
51
+ var transpose = Boolean(options.transpose);
52
+ return {
53
+ paste: paste,
54
+ operation: operation,
55
+ skipBlanks: skipBlanks,
56
+ transpose: transpose,
57
+ isSpecial: paste !== "all" || operation !== undefined || skipBlanks || transpose,
58
+ };
59
+ };
60
+ /**
61
+ * Read the Paste Special request the grid stashed on the clipboard event
62
+ * (`pasteSpecialOptions` JSON wins over the legacy `pasteSpecialType`).
63
+ */
64
+ export var resolvePasteSpecialOptions = function (clipboardData) {
65
+ if (!clipboardData)
66
+ return PLAIN_PASTE;
67
+ var encoded = clipboardData.getData(PASTE_SPECIAL_OPTIONS_KEY);
68
+ if (encoded) {
69
+ try {
70
+ var parsed = JSON.parse(encoded);
71
+ if (parsed && typeof parsed === "object") {
72
+ return normalizePasteSpecialOptions(parsed);
73
+ }
74
+ }
75
+ catch (_a) {
76
+ // Fall through to the legacy key
77
+ }
78
+ }
79
+ var legacy = clipboardData.getData(PASTE_SPECIAL_TYPE_KEY);
80
+ return normalizePasteSpecialOptions(legacy || undefined);
81
+ };
82
+ /**
83
+ * A clipboard cell that carries no value: Paste Special "Skip blanks" leaves
84
+ * the destination untouched for these. A shared-string reference counts as a
85
+ * value.
86
+ */
87
+ export var isBlankClipboardCell = function (cell) {
88
+ if (isNil(cell))
89
+ return true;
90
+ if (!isNil(cell.ss))
91
+ return false;
92
+ var ue = getCellUserEnteredValue(cell);
93
+ if (isNil(ue))
94
+ return true;
95
+ return (isNil(getExtendedValueFormula(ue)) &&
96
+ isNil(getExtendedValueNumber(ue)) &&
97
+ isNil(getExtendedValueString(ue)) &&
98
+ isNil(getExtendedValueBool(ue)));
99
+ };
100
+ var classifyOperand = function (cell, locale) {
101
+ if (isNil(cell))
102
+ return { kind: "blank" };
103
+ var ue = getCellUserEnteredValue(cell);
104
+ var formula = getExtendedValueFormula(ue);
105
+ if (!isNil(formula)) {
106
+ var body = formula.trim().replace(/^=/, "");
107
+ return body ? { kind: "formula", body: body } : { kind: "blank" };
108
+ }
109
+ var nv = getExtendedValueNumber(ue);
110
+ if (!isNil(nv))
111
+ return { kind: "number", value: nv };
112
+ var bv = getExtendedValueBool(ue);
113
+ if (!isNil(bv))
114
+ return { kind: "number", value: bv ? 1 : 0 };
115
+ var sv = getExtendedValueString(ue);
116
+ if (isNil(sv) || sv === "")
117
+ return { kind: "blank" };
118
+ if (isNumber(sv, locale)) {
119
+ return { kind: "number", value: convertToNumber(sv, locale) };
120
+ }
121
+ return { kind: "text", value: sv };
122
+ };
123
+ var OPERATOR = {
124
+ add: "+",
125
+ subtract: "-",
126
+ multiply: "*",
127
+ divide: "/",
128
+ };
129
+ var compute = function (operation, left, right) {
130
+ switch (operation) {
131
+ case "add":
132
+ return left + right;
133
+ case "subtract":
134
+ return left - right;
135
+ case "multiply":
136
+ return left * right;
137
+ case "divide":
138
+ return left / right;
139
+ }
140
+ };
141
+ var numberLiteral = function (value) {
142
+ return value < 0 ? "(".concat(String(value), ")") : String(value);
143
+ };
144
+ /** Render an operand as a formula fragment (a formula body is parenthesized). */
145
+ var toFormulaFragment = function (operand) {
146
+ switch (operand.kind) {
147
+ case "blank":
148
+ return "0";
149
+ case "number":
150
+ return numberLiteral(operand.value);
151
+ case "formula":
152
+ return "(".concat(operand.body, ")");
153
+ case "text":
154
+ return "\"".concat(operand.value.replace(/"/g, '""'), "\"");
155
+ }
156
+ };
157
+ /**
158
+ * Strip every value facet from a clipboard cell, keeping its formatting so a
159
+ * "Paste all + operation" still carries the source formats.
160
+ */
161
+ var withoutValue = function (cell) {
162
+ if (isNil(cell))
163
+ return {};
164
+ var _a = cell, _ue = _a.ue, _ev = _a.ev, _fv = _a.fv, _ss = _a.ss, _userEnteredValue = _a.userEnteredValue, _effectiveValue = _a.effectiveValue, _formattedValue = _a.formattedValue, rest = __rest(_a, ["ue", "ev", "fv", "ss", "userEnteredValue", "effectiveValue", "formattedValue"]);
165
+ return rest;
166
+ };
167
+ /**
168
+ * Excel Paste Special → Operation. Combines one clipboard cell with the
169
+ * destination cell it lands on and returns the cell data to write there:
170
+ *
171
+ * - number ⊕ number → the arithmetic result (blank destination counts as 0,
172
+ * numeric text is coerced; a non-finite result such as `x/0` is written as
173
+ * the formula `=x/0` so the cell shows `#DIV/0!`).
174
+ * - a formula on either side → a combined formula (`=(dest)+(source)`), the
175
+ * source formula already relocated to the destination by the paste.
176
+ * - blank source → the destination is left as it is (Excel: `d + 0`; the
177
+ * classic "paste-add a blank cell" trick still coerces numeric text).
178
+ * - non-numeric text destination → left as it is.
179
+ * - non-numeric text source → `=dest+"text"` (Excel's `#VALUE!`).
180
+ *
181
+ * `paste: "values"` callers pre-resolve the source to values with
182
+ * `pickValue`, so the source side is never a formula there.
183
+ */
184
+ export var applyPasteOperation = function (source, destination, operation, sheetId, locale) {
185
+ var src = classifyOperand(source, locale);
186
+ var dst = classifyOperand(destination, locale);
187
+ var base = withoutValue(source);
188
+ var keepDestination = function () {
189
+ var destinationValue = getCellUserEnteredValue(destination);
190
+ return isNil(destinationValue) ? base : __assign(__assign({}, base), { ue: destinationValue });
191
+ };
192
+ if (dst.kind === "text") {
193
+ return keepDestination();
194
+ }
195
+ if (src.kind === "blank") {
196
+ if (dst.kind === "number" &&
197
+ (operation === "add" || operation === "subtract")) {
198
+ // `d + 0` — the destination's numeric text becomes a number.
199
+ return writeValue(base, dst.value, sheetId, locale);
200
+ }
201
+ return keepDestination();
202
+ }
203
+ var leftIsNumeric = dst.kind === "blank" || dst.kind === "number";
204
+ if (leftIsNumeric && src.kind === "number") {
205
+ var left = dst.kind === "number" ? dst.value : 0;
206
+ var result = compute(operation, left, src.value);
207
+ if (Number.isFinite(result)) {
208
+ return writeValue(base, result, sheetId, locale);
209
+ }
210
+ }
211
+ var formula = "=".concat(toFormulaFragment(dst)).concat(OPERATOR[operation]).concat(toFormulaFragment(src));
212
+ return writeValue(base, formula, sheetId, locale);
213
+ };
214
+ var writeValue = function (base, value, sheetId, locale) {
215
+ var _a;
216
+ var sheetCell = new SheetCell();
217
+ sheetCell.setLocale(locale);
218
+ sheetCell.assign(sheetId, undefined, base);
219
+ sheetCell.setUserEnteredValue(value);
220
+ var result = (_a = sheetCell.getCellData()) !== null && _a !== void 0 ? _a : __assign({}, base);
221
+ // Typing a number stamps an inferred `uf.numberFormat`; an unformatted
222
+ // source (values-only paste) must stay unformatted so the destination's
223
+ // own format survives the merge.
224
+ if (!base.uf && result.uf) {
225
+ var _uf = result.uf, rest = __rest(result, ["uf"]);
226
+ return rest;
227
+ }
228
+ return result;
229
+ };
@@ -1,2 +1,14 @@
1
+ import type { SplitTextOptions } from "@rowsncolumns/spreadsheet";
1
2
  export declare const splitTextToColumns: (values: (string | null | undefined)[], delimiter?: string) => string[][];
3
+ export type { SplitTextOptions };
4
+ /** Normalized, ascending, de-duplicated positive break positions. */
5
+ export declare const normalizeColumnBreaks: (columnBreaks: number[]) => number[];
6
+ /**
7
+ * Excel's Text to Columns → Fixed width: cut each cell at the given
8
+ * character positions. Fields are trimmed like the delimited path (Excel's
9
+ * General column data format drops the padding spaces fixed-width data is
10
+ * aligned with). Cells without breaks inside their text still split — the
11
+ * trailing fields are empty — so the resulting block is rectangular.
12
+ */
13
+ export declare const splitTextFixedWidth: (values: (string | null | undefined)[], columnBreaks: number[]) => string[][];
2
14
  //# sourceMappingURL=split-text.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"split-text.d.ts","sourceRoot":"","sources":["../../../api/split-text.ts"],"names":[],"mappings":"AAGA,eAAO,MAAM,kBAAkB,GAC7B,QAAQ,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,EACrC,YAAW,MAAY,KACtB,MAAM,EAAE,EAwBV,CAAC"}
1
+ {"version":3,"file":"split-text.d.ts","sourceRoot":"","sources":["../../../api/split-text.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,2BAA2B,CAAC;AAIlE,eAAO,MAAM,kBAAkB,GAC7B,QAAQ,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,EACrC,YAAW,MAAY,KACtB,MAAM,EAAE,EAwBV,CAAC;AAEF,YAAY,EAAE,gBAAgB,EAAE,CAAC;AAEjC,qEAAqE;AACrE,eAAO,MAAM,qBAAqB,GAAI,cAAc,MAAM,EAAE,KAAG,MAAM,EAG3C,CAAC;AAE3B;;;;;;GAMG;AACH,eAAO,MAAM,mBAAmB,GAC9B,QAAQ,CAAC,MAAM,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,EACrC,cAAc,MAAM,EAAE,KACrB,MAAM,EAAE,EAgBV,CAAC"}
@@ -1,3 +1,4 @@
1
+ import { __read, __spreadArray, __values } from "tslib";
1
2
  import { isNil } from "@rowsncolumns/utils";
2
3
  // Split text to columns - takes a single column and returns 2D array
3
4
  export var splitTextToColumns = function (values, delimiter) {
@@ -23,3 +24,43 @@ export var splitTextToColumns = function (values, delimiter) {
23
24
  return trimmedParts;
24
25
  });
25
26
  };
27
+ /** Normalized, ascending, de-duplicated positive break positions. */
28
+ export var normalizeColumnBreaks = function (columnBreaks) {
29
+ return __spreadArray([], __read(new Set(columnBreaks)), false).filter(function (position) { return Number.isInteger(position) && position > 0; })
30
+ .sort(function (a, b) { return a - b; });
31
+ };
32
+ /**
33
+ * Excel's Text to Columns → Fixed width: cut each cell at the given
34
+ * character positions. Fields are trimmed like the delimited path (Excel's
35
+ * General column data format drops the padding spaces fixed-width data is
36
+ * aligned with). Cells without breaks inside their text still split — the
37
+ * trailing fields are empty — so the resulting block is rectangular.
38
+ */
39
+ export var splitTextFixedWidth = function (values, columnBreaks) {
40
+ var breaks = normalizeColumnBreaks(columnBreaks);
41
+ return values.map(function (cell) {
42
+ var e_1, _a;
43
+ if (isNil(cell)) {
44
+ return [""];
45
+ }
46
+ var text = String(cell);
47
+ var parts = [];
48
+ var start = 0;
49
+ try {
50
+ for (var breaks_1 = __values(breaks), breaks_1_1 = breaks_1.next(); !breaks_1_1.done; breaks_1_1 = breaks_1.next()) {
51
+ var position = breaks_1_1.value;
52
+ parts.push(text.slice(start, position).trim());
53
+ start = position;
54
+ }
55
+ }
56
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
57
+ finally {
58
+ try {
59
+ if (breaks_1_1 && !breaks_1_1.done && (_a = breaks_1.return)) _a.call(breaks_1);
60
+ }
61
+ finally { if (e_1) throw e_1.error; }
62
+ }
63
+ parts.push(text.slice(start).trim());
64
+ return parts;
65
+ });
66
+ };
@@ -0,0 +1,85 @@
1
+ import type { GridRange } from "@rowsncolumns/common-types";
2
+ import { SUBTOTAL_FUNCTIONS, type SubtotalFunction, type SubtotalOptions } from "@rowsncolumns/spreadsheet";
3
+ export { SUBTOTAL_FUNCTIONS };
4
+ export type { SubtotalFunction, SubtotalOptions };
5
+ /**
6
+ * The `SUBTOTAL(function_num, …)` code each "Use function" entry writes.
7
+ * Codes 1–11 (not 101–111): the subtotal rows Excel inserts count manually
8
+ * hidden rows.
9
+ */
10
+ export declare const SUBTOTAL_FUNCTION_NUMBERS: Record<SubtotalFunction, number>;
11
+ /** Excel's total-row labels: "<group> Total" per group, "Grand Total" at the bottom. */
12
+ export declare const SUBTOTAL_GRAND_TOTAL_LABEL = "Grand Total";
13
+ export declare const subtotalGroupLabel: (groupValue: string) => string;
14
+ /**
15
+ * Reads a cell of the data range. `getValue` should return the DISPLAYED text
16
+ * when the host has one (Excel groups and labels on what the cells show —
17
+ * `1/1/2024 Total`, not `45292 Total`). `getFormula` returns the `=…` text for
18
+ * a formula cell. `getOutlineLevel` returns a row's current outline level so
19
+ * "Replace current subtotals" can rebuild the outline from level 0.
20
+ */
21
+ export type SubtotalReader = {
22
+ getValue: (rowIndex: number, columnIndex: number) => string | number | boolean | null | undefined;
23
+ getFormula: (rowIndex: number, columnIndex: number) => string | undefined;
24
+ getOutlineLevel?: (rowIndex: number) => number | undefined;
25
+ };
26
+ /** One `change-batch` write: a single row segment at final coordinates. */
27
+ export type SubtotalWrite = {
28
+ rowIndex: number;
29
+ startColumnIndex: number;
30
+ values: (string | null)[];
31
+ };
32
+ /**
33
+ * The plan applies in this order, every index already in FINAL coordinates
34
+ * (the sheet after all removals and insertions):
35
+ * 1. `deleteRows` (descending original indexes — existing subtotal rows),
36
+ * 2. `insertRows` ascending: inserting at final row `r` shifts the not-yet
37
+ * final rows below it, so ascending order lands each new row exactly
38
+ * where the plan expects it,
39
+ * 3. `writes` (labels + SUBTOTAL formulas),
40
+ * 4. `ungroupRows`: one `ungroup-rows` call per entry — the outline reset of
41
+ * "Replace current subtotals". Group / ungroup are ±1 steps in both
42
+ * engines, so pass `k` lists the surviving data rows that were deeper
43
+ * than level `k`; after the passes every row of the range is back at
44
+ * level 0 (empty when nothing was outlined or when not replacing), then
45
+ * 5. `outlineGroups`: one `group-rows` call per entry (Excel's levels — the
46
+ * first spans every row above the grand total, the second the detail
47
+ * rows of each group, so detail rows end at level 2 and subtotal rows
48
+ * at level 1 — the same three-level outline on every re-apply).
49
+ */
50
+ export type SubtotalPlan = {
51
+ deleteRows: number[];
52
+ insertRows: number[];
53
+ writes: SubtotalWrite[];
54
+ ungroupRows: number[][];
55
+ outlineGroups: number[][];
56
+ /** Number of groups found. */
57
+ groupCount: number;
58
+ /** The range after the plan is applied (header row included when present). */
59
+ resultRange: GridRange;
60
+ };
61
+ /**
62
+ * Plans Excel's Subtotal command over `options.range`. Consecutive rows with
63
+ * the same group-column value form a group (Excel does not sort — it expects
64
+ * the data sorted and totals each run of equal values). Each group gets a
65
+ * total row right below it holding "<value> Total" in the group column and
66
+ * `=SUBTOTAL(n, first:last)` over the group's data rows in every aggregate
67
+ * column; a grand-total row closes the range with `SUBTOTAL` over the whole
68
+ * data block (SUBTOTAL ignores the nested subtotal rows, like Excel). With
69
+ * `replaceCurrent` the range's existing outline is cleared too, so a re-apply
70
+ * yields Excel's fresh detail 2 / subtotal 1 / grand total 0 levels instead
71
+ * of nesting deeper; without it the new levels stack on the old ones (Excel's
72
+ * nested subtotals). Returns `undefined` when the range has no data rows.
73
+ */
74
+ export declare const planSubtotals: (options: SubtotalOptions, reader: SubtotalReader) => SubtotalPlan | undefined;
75
+ /** The state callbacks a plan is applied through — the same in both engines. */
76
+ export type SubtotalPlanExecutor = {
77
+ onDeleteRow: (sheetId: number, rowIndexes: number[]) => void;
78
+ onInsertRow: (sheetId: number, referenceRowIndex: number, numRows?: number) => void;
79
+ onChangeBatch: (sheetId: number, range: GridRange, values: (string | null)[][]) => void;
80
+ onGroupRows: (sheetId: number, rowIndexes: number[]) => void;
81
+ onUngroupRows: (sheetId: number, rowIndexes: number[]) => void;
82
+ };
83
+ /** Applies a {@link SubtotalPlan} in its documented order. */
84
+ export declare const applySubtotalPlan: (sheetId: number, plan: SubtotalPlan, executor: SubtotalPlanExecutor) => void;
85
+ //# sourceMappingURL=subtotal.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"subtotal.d.ts","sourceRoot":"","sources":["../../../api/subtotal.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,4BAA4B,CAAC;AAC5D,OAAO,EACL,kBAAkB,EAClB,KAAK,gBAAgB,EACrB,KAAK,eAAe,EACrB,MAAM,2BAA2B,CAAC;AAGnC,OAAO,EAAE,kBAAkB,EAAE,CAAC;AAC9B,YAAY,EAAE,gBAAgB,EAAE,eAAe,EAAE,CAAC;AAElD;;;;GAIG;AACH,eAAO,MAAM,yBAAyB,EAAE,MAAM,CAAC,gBAAgB,EAAE,MAAM,CAYtE,CAAC;AAEF,wFAAwF;AACxF,eAAO,MAAM,0BAA0B,gBAAgB,CAAC;AACxD,eAAO,MAAM,kBAAkB,GAAI,YAAY,MAAM,WACP,CAAC;AAE/C;;;;;;GAMG;AACH,MAAM,MAAM,cAAc,GAAG;IAC3B,QAAQ,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,MAAM,GAAG,MAAM,GAAG,OAAO,GAAG,IAAI,GAAG,SAAS,CAAC;IAClG,UAAU,EAAE,CAAC,QAAQ,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;IAC1E,eAAe,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAC;CAC5D,CAAC;AAEF,2EAA2E;AAC3E,MAAM,MAAM,aAAa,GAAG;IAC1B,QAAQ,EAAE,MAAM,CAAC;IACjB,gBAAgB,EAAE,MAAM,CAAC;IACzB,MAAM,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,CAAC;CAC3B,CAAC;AAEF;;;;;;;;;;;;;;;;;GAiBG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,UAAU,EAAE,MAAM,EAAE,CAAC;IACrB,MAAM,EAAE,aAAa,EAAE,CAAC;IACxB,WAAW,EAAE,MAAM,EAAE,EAAE,CAAC;IACxB,aAAa,EAAE,MAAM,EAAE,EAAE,CAAC;IAC1B,8BAA8B;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,8EAA8E;IAC9E,WAAW,EAAE,SAAS,CAAC;CACxB,CAAC;AAcF;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,aAAa,GACxB,SAAS,eAAe,EACxB,QAAQ,cAAc,KACrB,YAAY,GAAG,SAyHjB,CAAC;AAEF,gFAAgF;AAChF,MAAM,MAAM,oBAAoB,GAAG;IACjC,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC7D,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,iBAAiB,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,KAAK,IAAI,CAAC;IACpF,aAAa,EAAE,CACb,OAAO,EAAE,MAAM,EACf,KAAK,EAAE,SAAS,EAChB,MAAM,EAAE,CAAC,MAAM,GAAG,IAAI,CAAC,EAAE,EAAE,KACxB,IAAI,CAAC;IACV,WAAW,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;IAC7D,aAAa,EAAE,CAAC,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,EAAE,KAAK,IAAI,CAAC;CAChE,CAAC;AAEF,8DAA8D;AAC9D,eAAO,MAAM,iBAAiB,GAC5B,SAAS,MAAM,EACf,MAAM,YAAY,EAClB,UAAU,oBAAoB,SAwB/B,CAAC"}
@@ -0,0 +1,250 @@
1
+ import { __assign, __read, __spreadArray, __values } from "tslib";
2
+ import { SUBTOTAL_FUNCTIONS, } from "@rowsncolumns/spreadsheet";
3
+ import { number2Alpha } from "@rowsncolumns/utils";
4
+ export { SUBTOTAL_FUNCTIONS };
5
+ /**
6
+ * The `SUBTOTAL(function_num, …)` code each "Use function" entry writes.
7
+ * Codes 1–11 (not 101–111): the subtotal rows Excel inserts count manually
8
+ * hidden rows.
9
+ */
10
+ export var SUBTOTAL_FUNCTION_NUMBERS = {
11
+ AVERAGE: 1,
12
+ COUNT: 2,
13
+ COUNTA: 3,
14
+ MAX: 4,
15
+ MIN: 5,
16
+ PRODUCT: 6,
17
+ STDEV: 7,
18
+ STDEVP: 8,
19
+ SUM: 9,
20
+ VAR: 10,
21
+ VARP: 11,
22
+ };
23
+ /** Excel's total-row labels: "<group> Total" per group, "Grand Total" at the bottom. */
24
+ export var SUBTOTAL_GRAND_TOTAL_LABEL = "Grand Total";
25
+ export var subtotalGroupLabel = function (groupValue) {
26
+ return groupValue ? "".concat(groupValue, " Total") : "Total";
27
+ };
28
+ var isSubtotalFormula = function (formula) {
29
+ return !!formula && /^=\s*(_xlfn\.)?SUBTOTAL\s*\(/i.test(formula);
30
+ };
31
+ /** What a cell shows: booleans display as `TRUE`/`FALSE` (Excel's label is "TRUE Total"). */
32
+ var displayValue = function (value) {
33
+ if (value === null || value === undefined)
34
+ return "";
35
+ if (typeof value === "boolean")
36
+ return value ? "TRUE" : "FALSE";
37
+ return String(value);
38
+ };
39
+ var columnLetters = function (columnIndex) { return number2Alpha(columnIndex - 1); };
40
+ /**
41
+ * Plans Excel's Subtotal command over `options.range`. Consecutive rows with
42
+ * the same group-column value form a group (Excel does not sort — it expects
43
+ * the data sorted and totals each run of equal values). Each group gets a
44
+ * total row right below it holding "<value> Total" in the group column and
45
+ * `=SUBTOTAL(n, first:last)` over the group's data rows in every aggregate
46
+ * column; a grand-total row closes the range with `SUBTOTAL` over the whole
47
+ * data block (SUBTOTAL ignores the nested subtotal rows, like Excel). With
48
+ * `replaceCurrent` the range's existing outline is cleared too, so a re-apply
49
+ * yields Excel's fresh detail 2 / subtotal 1 / grand total 0 levels instead
50
+ * of nesting deeper; without it the new levels stack on the old ones (Excel's
51
+ * nested subtotals). Returns `undefined` when the range has no data rows.
52
+ */
53
+ export var planSubtotals = function (options, reader) {
54
+ var e_1, _a, e_2, _b;
55
+ var _c, _d, _e;
56
+ var range = options.range, groupByColumnIndex = options.groupByColumnIndex, aggregateColumnIndices = options.aggregateColumnIndices, _f = options.fn, fn = _f === void 0 ? "SUM" : _f, _g = options.hasHeader, hasHeader = _g === void 0 ? true : _g, _h = options.replaceCurrent, replaceCurrent = _h === void 0 ? true : _h;
57
+ var functionNumber = SUBTOTAL_FUNCTION_NUMBERS[fn];
58
+ var aggregates = __spreadArray([], __read(new Set(aggregateColumnIndices)), false).filter(function (columnIndex) {
59
+ return columnIndex >= range.startColumnIndex &&
60
+ columnIndex <= range.endColumnIndex;
61
+ })
62
+ .sort(function (a, b) { return a - b; });
63
+ var firstDataRow = hasHeader ? range.startRowIndex + 1 : range.startRowIndex;
64
+ if (firstDataRow > range.endRowIndex)
65
+ return undefined;
66
+ // Existing subtotal rows (any aggregate column, or the group column's
67
+ // "… Total" label next to a SUBTOTAL anywhere in the range) are dropped
68
+ // first when replacing.
69
+ var deleteRows = [];
70
+ var dataRows = [];
71
+ for (var rowIndex = firstDataRow; rowIndex <= range.endRowIndex; rowIndex++) {
72
+ var isTotalRow = false;
73
+ if (replaceCurrent) {
74
+ for (var columnIndex = range.startColumnIndex; columnIndex <= range.endColumnIndex && !isTotalRow; columnIndex++) {
75
+ isTotalRow = isSubtotalFormula(reader.getFormula(rowIndex, columnIndex));
76
+ }
77
+ }
78
+ if (isTotalRow)
79
+ deleteRows.push(rowIndex);
80
+ else
81
+ dataRows.push(rowIndex);
82
+ }
83
+ if (dataRows.length === 0)
84
+ return undefined;
85
+ deleteRows.sort(function (a, b) { return b - a; });
86
+ // Runs of equal group values over the surviving data rows.
87
+ var groups = [];
88
+ try {
89
+ for (var dataRows_1 = __values(dataRows), dataRows_1_1 = dataRows_1.next(); !dataRows_1_1.done; dataRows_1_1 = dataRows_1.next()) {
90
+ var rowIndex = dataRows_1_1.value;
91
+ var label = displayValue(reader.getValue(rowIndex, groupByColumnIndex));
92
+ var last = groups[groups.length - 1];
93
+ if (last && last.label === label)
94
+ last.size++;
95
+ else
96
+ groups.push({ label: label, size: 1 });
97
+ }
98
+ }
99
+ catch (e_1_1) { e_1 = { error: e_1_1 }; }
100
+ finally {
101
+ try {
102
+ if (dataRows_1_1 && !dataRows_1_1.done && (_a = dataRows_1.return)) _a.call(dataRows_1);
103
+ }
104
+ finally { if (e_1) throw e_1.error; }
105
+ }
106
+ // Lay out the final rows: data rows are compacted (deleted rows gone),
107
+ // then each group is followed by its total row.
108
+ var insertRows = [];
109
+ var writes = [];
110
+ var detailRows = [];
111
+ var startColumn = Math.min.apply(Math, __spreadArray([groupByColumnIndex], __read(aggregates), false));
112
+ var endColumn = Math.max.apply(Math, __spreadArray([groupByColumnIndex], __read(aggregates), false));
113
+ var totalRowValues = function (label, firstRow, lastRow) {
114
+ var values = [];
115
+ for (var columnIndex = startColumn; columnIndex <= endColumn; columnIndex++) {
116
+ if (columnIndex === groupByColumnIndex) {
117
+ values.push(label);
118
+ }
119
+ else if (aggregates.includes(columnIndex)) {
120
+ var letters = columnLetters(columnIndex);
121
+ values.push("=SUBTOTAL(".concat(functionNumber, ",").concat(letters).concat(firstRow, ":").concat(letters).concat(lastRow, ")"));
122
+ }
123
+ else {
124
+ values.push(null);
125
+ }
126
+ }
127
+ return values;
128
+ };
129
+ // "Replace current subtotals": the surviving data rows carry the previous
130
+ // outline (detail rows at 2, or deeper after repeated applies) into their
131
+ // final positions — the inserted total rows arrive at level 0 — so record
132
+ // how many ungroup steps each needs before the fresh levels go on.
133
+ var ungroupRows = [];
134
+ var dataRowCursor = 0;
135
+ var cursor = firstDataRow;
136
+ try {
137
+ for (var groups_1 = __values(groups), groups_1_1 = groups_1.next(); !groups_1_1.done; groups_1_1 = groups_1.next()) {
138
+ var group = groups_1_1.value;
139
+ var groupStart = cursor;
140
+ for (var i = 0; i < group.size; i++) {
141
+ var originalRow = dataRows[dataRowCursor++];
142
+ var level = replaceCurrent ? ((_d = (_c = reader.getOutlineLevel) === null || _c === void 0 ? void 0 : _c.call(reader, originalRow)) !== null && _d !== void 0 ? _d : 0) : 0;
143
+ for (var pass = 0; pass < level; pass++) {
144
+ ((_e = ungroupRows[pass]) !== null && _e !== void 0 ? _e : (ungroupRows[pass] = [])).push(cursor);
145
+ }
146
+ detailRows.push(cursor++);
147
+ }
148
+ var groupEnd = cursor - 1;
149
+ insertRows.push(cursor);
150
+ writes.push({
151
+ rowIndex: cursor,
152
+ startColumnIndex: startColumn,
153
+ values: totalRowValues(subtotalGroupLabel(group.label), groupStart, groupEnd),
154
+ });
155
+ cursor++;
156
+ }
157
+ }
158
+ catch (e_2_1) { e_2 = { error: e_2_1 }; }
159
+ finally {
160
+ try {
161
+ if (groups_1_1 && !groups_1_1.done && (_b = groups_1.return)) _b.call(groups_1);
162
+ }
163
+ finally { if (e_2) throw e_2.error; }
164
+ }
165
+ var lastRowBeforeGrand = cursor - 1;
166
+ insertRows.push(cursor);
167
+ writes.push({
168
+ rowIndex: cursor,
169
+ startColumnIndex: startColumn,
170
+ values: totalRowValues(SUBTOTAL_GRAND_TOTAL_LABEL, firstDataRow, lastRowBeforeGrand),
171
+ });
172
+ var level1 = [];
173
+ for (var rowIndex = firstDataRow; rowIndex <= lastRowBeforeGrand; rowIndex++) {
174
+ level1.push(rowIndex);
175
+ }
176
+ return {
177
+ deleteRows: deleteRows,
178
+ insertRows: insertRows,
179
+ writes: writes,
180
+ ungroupRows: ungroupRows,
181
+ outlineGroups: [level1, detailRows],
182
+ groupCount: groups.length,
183
+ resultRange: __assign(__assign({}, range), { endRowIndex: cursor }),
184
+ };
185
+ };
186
+ /** Applies a {@link SubtotalPlan} in its documented order. */
187
+ export var applySubtotalPlan = function (sheetId, plan, executor) {
188
+ var e_3, _a, e_4, _b, e_5, _c, e_6, _d;
189
+ if (plan.deleteRows.length)
190
+ executor.onDeleteRow(sheetId, plan.deleteRows);
191
+ try {
192
+ for (var _e = __values(plan.insertRows), _f = _e.next(); !_f.done; _f = _e.next()) {
193
+ var rowIndex = _f.value;
194
+ executor.onInsertRow(sheetId, rowIndex, 1);
195
+ }
196
+ }
197
+ catch (e_3_1) { e_3 = { error: e_3_1 }; }
198
+ finally {
199
+ try {
200
+ if (_f && !_f.done && (_a = _e.return)) _a.call(_e);
201
+ }
202
+ finally { if (e_3) throw e_3.error; }
203
+ }
204
+ try {
205
+ for (var _g = __values(plan.writes), _h = _g.next(); !_h.done; _h = _g.next()) {
206
+ var write = _h.value;
207
+ executor.onChangeBatch(sheetId, {
208
+ startRowIndex: write.rowIndex,
209
+ endRowIndex: write.rowIndex,
210
+ startColumnIndex: write.startColumnIndex,
211
+ endColumnIndex: write.startColumnIndex + write.values.length - 1,
212
+ }, [write.values]);
213
+ }
214
+ }
215
+ catch (e_4_1) { e_4 = { error: e_4_1 }; }
216
+ finally {
217
+ try {
218
+ if (_h && !_h.done && (_b = _g.return)) _b.call(_g);
219
+ }
220
+ finally { if (e_4) throw e_4.error; }
221
+ }
222
+ try {
223
+ for (var _j = __values(plan.ungroupRows), _k = _j.next(); !_k.done; _k = _j.next()) {
224
+ var rows = _k.value;
225
+ if (rows.length)
226
+ executor.onUngroupRows(sheetId, rows);
227
+ }
228
+ }
229
+ catch (e_5_1) { e_5 = { error: e_5_1 }; }
230
+ finally {
231
+ try {
232
+ if (_k && !_k.done && (_c = _j.return)) _c.call(_j);
233
+ }
234
+ finally { if (e_5) throw e_5.error; }
235
+ }
236
+ try {
237
+ for (var _l = __values(plan.outlineGroups), _m = _l.next(); !_m.done; _m = _l.next()) {
238
+ var rows = _m.value;
239
+ if (rows.length)
240
+ executor.onGroupRows(sheetId, rows);
241
+ }
242
+ }
243
+ catch (e_6_1) { e_6 = { error: e_6_1 }; }
244
+ finally {
245
+ try {
246
+ if (_m && !_m.done && (_d = _l.return)) _d.call(_l);
247
+ }
248
+ finally { if (e_6) throw e_6.error; }
249
+ }
250
+ };
@@ -53,6 +53,10 @@ export type Command = {
53
53
  value: string | null;
54
54
  format?: CellFormat | null;
55
55
  hyperlink?: string | null;
56
+ /** Paste Special "Skip blanks": leave the destination untouched (value, format,
57
+ * hyperlink) — unlike `null`, which clears it. The other fields are then only a
58
+ * fallback for an engine build predating `skip`. */
59
+ skip?: boolean;
56
60
  } | null)[][];
57
61
  /** When the clipboard came from a CUT, the source range to clear after the paste lands. */
58
62
  cut?: {
@@ -75,12 +79,21 @@ export type Command = {
75
79
  activeCell: CellInterface;
76
80
  selections: SelectionArea<SelectionAttributes>[];
77
81
  columnIndices?: number[];
82
+ hasHeader?: boolean;
78
83
  } | {
79
84
  command: "split-text-to-columns";
80
85
  sheetId: number;
81
86
  activeCell: CellInterface;
82
87
  selections: SelectionArea<SelectionAttributes>[];
83
88
  delimiter: string;
89
+ /** Fixed-width mode: character positions of the column breaks (wins over `delimiter`). */
90
+ columnBreaks?: number[];
91
+ } | {
92
+ command: "goal-seek";
93
+ sheetId: number;
94
+ setCell: CellInterface;
95
+ targetValue: number;
96
+ changingCell: CellInterface;
84
97
  } | {
85
98
  command: "find-replace";
86
99
  ranges: SheetRange[];