@rowsncolumns/spreadsheet-state 28.0.73 → 28.0.75

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 (167) 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/datatype.d.ts.map +1 -1
  5. package/dist/cjs/api/datatype.js +8 -1
  6. package/dist/cjs/api/fill-series-spec.d.ts +35 -0
  7. package/dist/cjs/api/fill-series-spec.d.ts.map +1 -0
  8. package/dist/cjs/api/fill-series-spec.js +164 -0
  9. package/dist/cjs/api/fill.d.ts +95 -3
  10. package/dist/cjs/api/fill.d.ts.map +1 -1
  11. package/dist/cjs/api/fill.js +1186 -418
  12. package/dist/cjs/api/goal-seek.d.ts +29 -0
  13. package/dist/cjs/api/goal-seek.d.ts.map +1 -0
  14. package/dist/cjs/api/goal-seek.js +131 -0
  15. package/dist/cjs/api/immer-utils.d.ts.map +1 -1
  16. package/dist/cjs/api/immer-utils.js +21 -15
  17. package/dist/cjs/api/intl.d.ts.map +1 -1
  18. package/dist/cjs/api/paint-format.d.ts +55 -0
  19. package/dist/cjs/api/paint-format.d.ts.map +1 -0
  20. package/dist/cjs/api/paint-format.js +174 -0
  21. package/dist/cjs/api/split-text.d.ts +12 -0
  22. package/dist/cjs/api/split-text.d.ts.map +1 -1
  23. package/dist/cjs/api/split-text.js +41 -0
  24. package/dist/cjs/api/subtotal.d.ts +85 -0
  25. package/dist/cjs/api/subtotal.d.ts.map +1 -0
  26. package/dist/cjs/api/subtotal.js +250 -0
  27. package/dist/cjs/commands.d.ts +19 -1
  28. package/dist/cjs/commands.d.ts.map +1 -1
  29. package/dist/cjs/components/editors/data-validation-rule-editor.d.ts.map +1 -1
  30. package/dist/cjs/components/editors/data-validation-rule-editor.js +83 -16
  31. package/dist/cjs/hooks/use-calculation-worker.d.ts.map +1 -1
  32. package/dist/cjs/hooks/use-calculation-worker.js +1 -0
  33. package/dist/cjs/hooks/use-calculation.d.ts +1 -1
  34. package/dist/cjs/hooks/use-on-advanced-filter.d.ts +21 -0
  35. package/dist/cjs/hooks/use-on-advanced-filter.d.ts.map +1 -0
  36. package/dist/cjs/hooks/use-on-advanced-filter.js +42 -0
  37. package/dist/cjs/hooks/use-on-fill.d.ts +13 -4
  38. package/dist/cjs/hooks/use-on-fill.d.ts.map +1 -1
  39. package/dist/cjs/hooks/use-on-fill.js +28 -7
  40. package/dist/cjs/hooks/use-on-goal-seek.d.ts +30 -0
  41. package/dist/cjs/hooks/use-on-goal-seek.d.ts.map +1 -0
  42. package/dist/cjs/hooks/use-on-goal-seek.js +140 -0
  43. package/dist/cjs/hooks/use-on-import-excel.d.ts +10 -1
  44. package/dist/cjs/hooks/use-on-import-excel.d.ts.map +1 -1
  45. package/dist/cjs/hooks/use-on-import-excel.js +8 -4
  46. package/dist/cjs/hooks/use-on-merge-unmerge-cells.d.ts.map +1 -1
  47. package/dist/cjs/hooks/use-on-merge-unmerge-cells.js +53 -34
  48. package/dist/cjs/hooks/use-on-subtotal.d.ts +26 -0
  49. package/dist/cjs/hooks/use-on-subtotal.d.ts.map +1 -0
  50. package/dist/cjs/hooks/use-on-subtotal.js +53 -0
  51. package/dist/cjs/hooks/use-paint-format.d.ts +19 -3
  52. package/dist/cjs/hooks/use-paint-format.d.ts.map +1 -1
  53. package/dist/cjs/hooks/use-paint-format.js +89 -32
  54. package/dist/cjs/hooks/use-sheet-properties.d.ts +2 -0
  55. package/dist/cjs/hooks/use-sheet-properties.d.ts.map +1 -1
  56. package/dist/cjs/hooks/use-sheet-properties.js +4 -0
  57. package/dist/cjs/hooks/use-split-text-to-columns.d.ts +2 -1
  58. package/dist/cjs/hooks/use-split-text-to-columns.d.ts.map +1 -1
  59. package/dist/cjs/hooks/use-split-text-to-columns.js +3 -2
  60. package/dist/cjs/hooks/use-spreadsheet-state.d.ts +50 -36
  61. package/dist/cjs/hooks/use-spreadsheet-state.d.ts.map +1 -1
  62. package/dist/cjs/hooks/use-spreadsheet-state.js +109 -19
  63. package/dist/cjs/index.d.ts +8 -0
  64. package/dist/cjs/index.d.ts.map +1 -1
  65. package/dist/cjs/index.js +8 -0
  66. package/dist/cjs/interface/apply-fill.d.ts +3 -3
  67. package/dist/cjs/interface/apply-fill.d.ts.map +1 -1
  68. package/dist/cjs/interface/apply-fill.js +100 -53
  69. package/dist/cjs/interface/change-batch-stream.d.ts.map +1 -1
  70. package/dist/cjs/interface/change-batch-stream.js +12 -6
  71. package/dist/cjs/interface/change-batch.d.ts.map +1 -1
  72. package/dist/cjs/interface/change-batch.js +17 -9
  73. package/dist/cjs/interface/change-formatting.d.ts.map +1 -1
  74. package/dist/cjs/interface/change-formatting.js +30 -11
  75. package/dist/cjs/interface/split-text.d.ts +2 -1
  76. package/dist/cjs/interface/split-text.d.ts.map +1 -1
  77. package/dist/cjs/interface/split-text.js +7 -3
  78. package/dist/cjs/interface/spreadsheet-interface.d.ts +1 -1
  79. package/dist/cjs/interface/spreadsheet-interface.d.ts.map +1 -1
  80. package/dist/cjs/interface/spreadsheet-interface.js +2 -2
  81. package/dist/cjs/sheet-cell.d.ts.map +1 -1
  82. package/dist/cjs/sheet-cell.js +37 -6
  83. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  84. package/dist/esm/api/advanced-filter.d.ts +72 -0
  85. package/dist/esm/api/advanced-filter.d.ts.map +1 -0
  86. package/dist/esm/api/advanced-filter.js +0 -0
  87. package/dist/esm/api/datatype.d.ts.map +1 -1
  88. package/dist/esm/api/datatype.js +8 -1
  89. package/dist/esm/api/fill-series-spec.d.ts +35 -0
  90. package/dist/esm/api/fill-series-spec.d.ts.map +1 -0
  91. package/dist/esm/api/fill-series-spec.js +160 -0
  92. package/dist/esm/api/fill.d.ts +95 -3
  93. package/dist/esm/api/fill.d.ts.map +1 -1
  94. package/dist/esm/api/fill.js +1158 -418
  95. package/dist/esm/api/goal-seek.d.ts +29 -0
  96. package/dist/esm/api/goal-seek.d.ts.map +1 -0
  97. package/dist/esm/api/goal-seek.js +108 -0
  98. package/dist/esm/api/immer-utils.d.ts.map +1 -1
  99. package/dist/esm/api/immer-utils.js +21 -15
  100. package/dist/esm/api/intl.d.ts.map +1 -1
  101. package/dist/esm/api/paint-format.d.ts +55 -0
  102. package/dist/esm/api/paint-format.d.ts.map +1 -0
  103. package/dist/esm/api/paint-format.js +122 -0
  104. package/dist/esm/api/split-text.d.ts +12 -0
  105. package/dist/esm/api/split-text.d.ts.map +1 -1
  106. package/dist/esm/api/split-text.js +28 -0
  107. package/dist/esm/api/subtotal.d.ts +85 -0
  108. package/dist/esm/api/subtotal.d.ts.map +1 -0
  109. package/dist/esm/api/subtotal.js +181 -0
  110. package/dist/esm/commands.d.ts +19 -1
  111. package/dist/esm/commands.d.ts.map +1 -1
  112. package/dist/esm/components/editors/data-validation-rule-editor.d.ts.map +1 -1
  113. package/dist/esm/components/editors/data-validation-rule-editor.js +52 -4
  114. package/dist/esm/hooks/use-calculation-worker.d.ts.map +1 -1
  115. package/dist/esm/hooks/use-calculation-worker.js +1 -0
  116. package/dist/esm/hooks/use-calculation.d.ts +1 -1
  117. package/dist/esm/hooks/use-on-advanced-filter.d.ts +21 -0
  118. package/dist/esm/hooks/use-on-advanced-filter.d.ts.map +1 -0
  119. package/dist/esm/hooks/use-on-advanced-filter.js +41 -0
  120. package/dist/esm/hooks/use-on-fill.d.ts +13 -4
  121. package/dist/esm/hooks/use-on-fill.d.ts.map +1 -1
  122. package/dist/esm/hooks/use-on-fill.js +24 -4
  123. package/dist/esm/hooks/use-on-goal-seek.d.ts +30 -0
  124. package/dist/esm/hooks/use-on-goal-seek.d.ts.map +1 -0
  125. package/dist/esm/hooks/use-on-goal-seek.js +82 -0
  126. package/dist/esm/hooks/use-on-import-excel.d.ts +10 -1
  127. package/dist/esm/hooks/use-on-import-excel.d.ts.map +1 -1
  128. package/dist/esm/hooks/use-on-import-excel.js +8 -4
  129. package/dist/esm/hooks/use-on-merge-unmerge-cells.d.ts.map +1 -1
  130. package/dist/esm/hooks/use-on-merge-unmerge-cells.js +13 -13
  131. package/dist/esm/hooks/use-on-subtotal.d.ts +26 -0
  132. package/dist/esm/hooks/use-on-subtotal.d.ts.map +1 -0
  133. package/dist/esm/hooks/use-on-subtotal.js +48 -0
  134. package/dist/esm/hooks/use-paint-format.d.ts +19 -3
  135. package/dist/esm/hooks/use-paint-format.d.ts.map +1 -1
  136. package/dist/esm/hooks/use-paint-format.js +75 -30
  137. package/dist/esm/hooks/use-sheet-properties.d.ts +2 -0
  138. package/dist/esm/hooks/use-sheet-properties.d.ts.map +1 -1
  139. package/dist/esm/hooks/use-sheet-properties.js +4 -0
  140. package/dist/esm/hooks/use-split-text-to-columns.d.ts +2 -1
  141. package/dist/esm/hooks/use-split-text-to-columns.d.ts.map +1 -1
  142. package/dist/esm/hooks/use-split-text-to-columns.js +3 -2
  143. package/dist/esm/hooks/use-spreadsheet-state.d.ts +50 -36
  144. package/dist/esm/hooks/use-spreadsheet-state.d.ts.map +1 -1
  145. package/dist/esm/hooks/use-spreadsheet-state.js +100 -17
  146. package/dist/esm/index.d.ts +8 -0
  147. package/dist/esm/index.d.ts.map +1 -1
  148. package/dist/esm/index.js +8 -0
  149. package/dist/esm/interface/apply-fill.d.ts +3 -3
  150. package/dist/esm/interface/apply-fill.d.ts.map +1 -1
  151. package/dist/esm/interface/apply-fill.js +93 -48
  152. package/dist/esm/interface/change-batch-stream.d.ts.map +1 -1
  153. package/dist/esm/interface/change-batch-stream.js +12 -6
  154. package/dist/esm/interface/change-batch.d.ts.map +1 -1
  155. package/dist/esm/interface/change-batch.js +15 -9
  156. package/dist/esm/interface/change-formatting.d.ts.map +1 -1
  157. package/dist/esm/interface/change-formatting.js +28 -11
  158. package/dist/esm/interface/split-text.d.ts +2 -1
  159. package/dist/esm/interface/split-text.d.ts.map +1 -1
  160. package/dist/esm/interface/split-text.js +6 -3
  161. package/dist/esm/interface/spreadsheet-interface.d.ts +1 -1
  162. package/dist/esm/interface/spreadsheet-interface.d.ts.map +1 -1
  163. package/dist/esm/interface/spreadsheet-interface.js +2 -2
  164. package/dist/esm/sheet-cell.d.ts.map +1 -1
  165. package/dist/esm/sheet-cell.js +37 -6
  166. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  167. package/package.json +20 -20
@@ -0,0 +1,29 @@
1
+ /**
2
+ * Excel's Data → What-If Analysis → Goal Seek solver.
3
+ *
4
+ * Excel's defaults are 100 iterations and a change-tolerance of 0.001 (its
5
+ * Calculation options → "Maximum Iterations" / "Maximum Change"). The
6
+ * search is a secant method — Excel's own is a secant/Newton hybrid — with
7
+ * bisection whenever a sign change has been bracketed and the secant step
8
+ * would leave the bracket, so a monotone target converges in a handful of
9
+ * evaluations while a badly scaled one still narrows a bracket.
10
+ *
11
+ * `evaluate(trial)` returns the set cell's value with the changing cell at
12
+ * `trial`; `undefined` means the trial produced an error (`#DIV/0!`, text…),
13
+ * which the solver treats as "step somewhere else".
14
+ */
15
+ import type { GoalSeekResult, GoalSeekStatus } from "@rowsncolumns/spreadsheet";
16
+ export declare const GOAL_SEEK_MAX_ITERATIONS = 100;
17
+ export declare const GOAL_SEEK_TOLERANCE = 0.001;
18
+ export type GoalSeekEvaluate = (trial: number) => Promise<number | undefined>;
19
+ export type { GoalSeekResult, GoalSeekStatus };
20
+ export type GoalSeekOptions = {
21
+ evaluate: GoalSeekEvaluate;
22
+ targetValue: number;
23
+ /** Starting trial — the changing cell's current value (0 when blank). */
24
+ initialValue?: number;
25
+ maxIterations?: number;
26
+ tolerance?: number;
27
+ };
28
+ export declare const goalSeek: ({ evaluate, targetValue, initialValue, maxIterations, tolerance, }: GoalSeekOptions) => Promise<GoalSeekResult>;
29
+ //# sourceMappingURL=goal-seek.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"goal-seek.d.ts","sourceRoot":"","sources":["../../../api/goal-seek.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AACH,OAAO,KAAK,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,2BAA2B,CAAC;AAEhF,eAAO,MAAM,wBAAwB,MAAM,CAAC;AAC5C,eAAO,MAAM,mBAAmB,QAAQ,CAAC;AAEzC,MAAM,MAAM,gBAAgB,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;AAE9E,YAAY,EAAE,cAAc,EAAE,cAAc,EAAE,CAAC;AAE/C,MAAM,MAAM,eAAe,GAAG;IAC5B,QAAQ,EAAE,gBAAgB,CAAC;IAC3B,WAAW,EAAE,MAAM,CAAC;IACpB,yEAAyE;IACzE,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB,CAAC;AAKF,eAAO,MAAM,QAAQ,GAAU,oEAM5B,eAAe,KAAG,OAAO,CAAC,cAAc,CAqG1C,CAAC"}
@@ -0,0 +1,108 @@
1
+ export const GOAL_SEEK_MAX_ITERATIONS = 100;
2
+ export const GOAL_SEEK_TOLERANCE = 0.001;
3
+ const isUsable = (value) => typeof value === "number" && Number.isFinite(value);
4
+ export const goalSeek = async ({ evaluate, targetValue, initialValue = 0, maxIterations = GOAL_SEEK_MAX_ITERATIONS, tolerance = GOAL_SEEK_TOLERANCE, }) => {
5
+ let iterations = 0;
6
+ let best = {
7
+ value: initialValue,
8
+ currentValue: undefined,
9
+ residual: Number.POSITIVE_INFINITY,
10
+ };
11
+ // Residual f(x) = set(x) − target; records the best trial as a side effect.
12
+ const residualAt = async (trial) => {
13
+ iterations++;
14
+ const current = await evaluate(trial);
15
+ if (!isUsable(current))
16
+ return undefined;
17
+ const residual = current - targetValue;
18
+ if (Math.abs(residual) < best.residual) {
19
+ best = { value: trial, currentValue: current, residual: Math.abs(residual) };
20
+ }
21
+ return residual;
22
+ };
23
+ const finish = (status) => ({
24
+ status,
25
+ value: best.value,
26
+ currentValue: best.currentValue,
27
+ targetValue,
28
+ iterations,
29
+ });
30
+ // Bracket of a sign change, once seen: bisection fallback + secant clamp.
31
+ let negative;
32
+ let positive;
33
+ const bracket = (x, f) => {
34
+ if (f < 0)
35
+ negative = x;
36
+ else
37
+ positive = x;
38
+ };
39
+ let x0 = initialValue;
40
+ let f0 = await residualAt(x0);
41
+ // The current value may itself be an error (=1/A1 with A1 blank): nudge off it.
42
+ let nudges = 0;
43
+ while (f0 === undefined && iterations < maxIterations && nudges < 8) {
44
+ x0 = x0 === 0 ? 1 : x0 * 2;
45
+ f0 = await residualAt(x0);
46
+ nudges++;
47
+ }
48
+ if (f0 === undefined)
49
+ return finish("not-found");
50
+ if (Math.abs(f0) <= tolerance)
51
+ return finish("found");
52
+ bracket(x0, f0);
53
+ // Excel perturbs the starting value by ~1% to get a first slope.
54
+ let x1 = x0 === 0 ? 0.01 : x0 * 1.01;
55
+ let shrinks = 0;
56
+ let backoffs = 0;
57
+ while (iterations < maxIterations) {
58
+ const f1 = await residualAt(x1);
59
+ if (f1 === undefined) {
60
+ // Errored trial: pull it halfway back toward the last good point.
61
+ if (++shrinks > 16)
62
+ return finish("not-found");
63
+ x1 = x0 + (x1 - x0) / 2;
64
+ continue;
65
+ }
66
+ shrinks = 0;
67
+ if (Math.abs(f1) <= tolerance)
68
+ return finish("found");
69
+ bracket(x1, f1);
70
+ // A secant step that made things worse with no sign change in sight is
71
+ // heading away from the root (a flat or curving function such as =1/A1):
72
+ // back off — half the step, in the opposite direction — instead of
73
+ // following it to infinity. The bracketed case below never needs this.
74
+ if ((negative === undefined || positive === undefined) &&
75
+ Math.abs(f1) > Math.abs(f0)) {
76
+ if (++backoffs > 24)
77
+ return finish("not-found");
78
+ x1 = x0 - (x1 - x0) / 2;
79
+ continue;
80
+ }
81
+ backoffs = 0;
82
+ let x2;
83
+ const slope = f1 - f0;
84
+ if (slope === 0 || !Number.isFinite(slope)) {
85
+ // Flat: widen the step in the same direction to find any change.
86
+ x2 = x1 + (x1 - x0 || 0.01) * 2;
87
+ }
88
+ else {
89
+ x2 = x1 - (f1 * (x1 - x0)) / slope;
90
+ }
91
+ if (negative !== undefined && positive !== undefined) {
92
+ const lo = Math.min(negative, positive);
93
+ const hi = Math.max(negative, positive);
94
+ if (!Number.isFinite(x2) || x2 <= lo || x2 >= hi) {
95
+ x2 = (lo + hi) / 2;
96
+ }
97
+ }
98
+ else if (!Number.isFinite(x2)) {
99
+ return finish("not-found");
100
+ }
101
+ if (x2 === x1)
102
+ return finish(Math.abs(f1) <= tolerance ? "found" : "not-found");
103
+ x0 = x1;
104
+ f0 = f1;
105
+ x1 = x2;
106
+ }
107
+ return finish("not-found");
108
+ };
@@ -1 +1 @@
1
- {"version":3,"file":"immer-utils.d.ts","sourceRoot":"","sources":["../../../api/immer-utils.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"immer-utils.d.ts","sourceRoot":"","sources":["../../../api/immer-utils.ts"],"names":[],"mappings":"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"}
@@ -47,8 +47,7 @@ const isGenericNumberFormatType = (type) => !type || type === "NUMBER" || type =
47
47
  * Date-family types — when the derived (resolved) type is one of these
48
48
  * and the precedent's type is also in this set, we preserve the
49
49
  * precedent's exact custom pattern instead of synthesizing a locale
50
- * default. Mirrors Excel's behaviour for date-arithmetic functions
51
- * (EDATE, EOMONTH, …) where `=EDATE(A1, 1)` keeps A1's custom pattern.
50
+ * default (`=DATE(YEAR(A1), MONTH(A1), 1)` keeps A1's custom pattern).
52
51
  */
53
52
  const isDateFamilyType = (type) => type === "DATE" || type === "TIME" || type === "DATE_TIME";
54
53
  const customNumberFormatMap = {
@@ -67,6 +66,7 @@ const formatDerivationRules = {
67
66
  UNIQUE: { type: "fixed_index", index: 0 },
68
67
  TAKE: { type: "fixed_index", index: 0 },
69
68
  DROP: { type: "fixed_index", index: 0 },
69
+ TRIMRANGE: { type: "fixed_index", index: 0 },
70
70
  EXPAND: { type: "fixed_index", index: 0 },
71
71
  TOCOL: { type: "fixed_index", index: 0 },
72
72
  TOROW: { type: "fixed_index", index: 0 },
@@ -160,18 +160,21 @@ const functionReturnTypes = {
160
160
  NETWORKDAYS: "NUMBER",
161
161
  "NETWORKDAYS.INTL": "NUMBER",
162
162
  DAYS360: "NUMBER",
163
+ // The date-arithmetic functions return date serials, but Excel does
164
+ // NOT auto-format them: `=EDATE(DATE(2026,1,31),1)` shows 46081 in a
165
+ // General cell until the user formats it (rowsncolumns/spreadsheet#642
166
+ // B-09). NUMBER — like DATEVALUE — rather than absent, so a nested
167
+ // DATE()/TODAY() argument cannot promote the result to a date (the
168
+ // outermost function decides, as in `=YEAR(TODAY())`).
169
+ EDATE: "NUMBER",
170
+ EOMONTH: "NUMBER",
171
+ WORKDAY: "NUMBER",
172
+ "WORKDAY.INTL": "NUMBER",
163
173
  // ─── Date/time — date producers (must render as DATE/TIME/DATE_TIME) ─
164
174
  NOW: "DATE_TIME",
165
175
  TODAY: "DATE",
166
176
  DATE: "DATE",
167
177
  TIME: "TIME",
168
- EDATE: "DATE",
169
- EOMONTH: "DATE",
170
- // WORKDAY returns a date serial number — must render as DATE, not
171
- // raw NUMBER (was a bug: cells showed e.g. "45292" instead of the
172
- // calendar date).
173
- WORKDAY: "DATE",
174
- "WORKDAY.INTL": "DATE",
175
178
  // ─── Aggregation / rounding (return NUMBER; CURRENCY precedent
176
179
  // preserved via the "expectedOutputType === NUMBER + numFormat
177
180
  // CURRENCY → keep numFormat" branch) ──────────────────────────
@@ -318,7 +321,10 @@ const currencyDecimalAdjustmentFunctions = new Set([
318
321
  "QUOTIENT",
319
322
  ]);
320
323
  // Get all function names from formula
321
- const functionRegex = /([A-Za-z0-9_]+)\s*\(/g;
324
+ // Function names may be dotted (`WORKDAY.INTL`, `PERCENTILE.INC`, `FORECAST.ETS`); without the
325
+ // `.` the match stopped at the suffix (`INTL(`) and the dotted keys of `functionReturnTypes` /
326
+ // `formatDerivationRules` never applied.
327
+ const functionRegex = /([A-Za-z0-9_.]+)\s*\(/g;
322
328
  export const getFunctionNamesFromFormula = (formula) => {
323
329
  let match;
324
330
  const matches = [];
@@ -499,7 +505,7 @@ resolvePrecedentNumberFormat) => {
499
505
  effectivePrecedents = filterPrecedentsByBounds(effectivePrecedents, directBounds);
500
506
  const upperFormula = formula?.trim().toUpperCase();
501
507
  if (upperFormula) {
502
- const functionNameMatch = upperFormula.match(/^=([A-Z0-9_]+)\(/);
508
+ const functionNameMatch = upperFormula.match(/^=([A-Z0-9_.]+)\(/);
503
509
  topLevelFunctionName = functionNameMatch ? functionNameMatch[1] : undefined;
504
510
  const rule = topLevelFunctionName
505
511
  ? formatDerivationRules[topLevelFunctionName]
@@ -672,10 +678,10 @@ resolvePrecedentNumberFormat) => {
672
678
  // Date/time types need a concrete pattern — without one the renderer
673
679
  // falls back to "General" and shows the serial number (the
674
680
  // =TODAY() bug). When the precedent itself is a date-family format
675
- // with a pattern, inherit that pattern (e.g. `=EDATE(A1, 1)` keeps
676
- // A1's custom `yyyy-mm-dd` instead of falling back to the locale
677
- // default). Otherwise mirror the format toolbar's default for the
678
- // type.
681
+ // with a pattern, inherit that pattern (e.g. `=DATE(YEAR(A1),
682
+ // MONTH(A1), 1)` keeps A1's custom `yyyy-mm-dd` instead of falling
683
+ // back to the locale default). Otherwise mirror the format toolbar's
684
+ // default for the type.
679
685
  if (isDateFamilyType(resolvedNumberFormat.type) &&
680
686
  isDateFamilyType(numFormat?.type) &&
681
687
  numFormat?.pattern) {
@@ -1 +1 @@
1
- {"version":3,"file":"intl.d.ts","sourceRoot":"","sources":["../../../api/intl.ts"],"names":[],"mappings":"AA0BA;;GAEG;AACH,eAAO,MAAM,eAAe,GAAI,OAAO,MAAM,EAAE,gBAAc,WAK5D,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,mBAAmB,GAAI,SAAS,MAAM;;;;;CA2ClD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,WAAW,GAAI,SAAS,MAAM;eA1E5B,MAAM;YAAU,OAAO;YAAU,OAAO;iBAAe,OAAO;CAqH5E,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kBAAkB,GAAI,SAAS,MAAM;UAtHxC,MAAM;YAAU,OAAO;YAAU,OAAO;CAkJjD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,eAAe,GAAI,SAAS,MAAM;aA/JlC,MAAM;eAAa,MAAM;WAAS,MAAM;aAgMpD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iBAAiB,GAAI,SAAS,MAAM,wBAkBhD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,uBAAuB,GAAI,MAAM,MAAM,YAuBnD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,mBAAmB,GAAI,aAAa,MAAM,gBA0BtD,CAAC"}
1
+ {"version":3,"file":"intl.d.ts","sourceRoot":"","sources":["../../../api/intl.ts"],"names":[],"mappings":"AA0BA;;GAEG;AACH,eAAO,MAAM,eAAe,GAAI,OAAO,MAAM,EAAE,gBAAc,WAK5D,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,mBAAmB,GAAI,SAAS,MAAM;WAlCxC,MAAM;aAAW,MAAM;eAAa,MAAM;iBAAe,MAAM;CA6EzE,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,WAAW,GAAI,SAAS,MAAM;eA1E5B,MAAM;YAAU,OAAO;YAAU,OAAO;iBAAe,OAAO;CAqH5E,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,kBAAkB,GAAI,SAAS,MAAM;UAtHxC,MAAM;YAAU,OAAO;YAAU,OAAO;CAkJjD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,eAAe,GAAI,SAAS,MAAM;aA/JlC,MAAM;eAAa,MAAM;WAAS,MAAM;aAgMpD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,iBAAiB,GAAI,SAAS,MAAM,wBAkBhD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,uBAAuB,GAAI,MAAM,MAAM,YAuBnD,CAAC;AAEF;;GAEG;AACH,eAAO,MAAM,mBAAmB,GAAI,aAAa,MAAM,gBA0BtD,CAAC"}
@@ -0,0 +1,55 @@
1
+ import type { CellFormat, CellInterface, GridRange } from "@rowsncolumns/common-types";
2
+ /**
3
+ * Format-painter pattern shared by both engines: the captured 2-D format grid plus the
4
+ * source block's merge configuration, re-based to the block's top-left so it can be tiled
5
+ * onto any target (Google Sheets copies merges with the format; Excel's painter does too).
6
+ */
7
+ export type PaintPattern = {
8
+ formats: (CellFormat | null | undefined)[][];
9
+ /** Merges fully inside the source block, as 0-based offsets from its top-left. */
10
+ merges: GridRange[];
11
+ /** Source block size — the tiling period. */
12
+ rows: number;
13
+ cols: number;
14
+ };
15
+ /**
16
+ * The merges the painter carries from a source block: every merge that lies FULLY inside
17
+ * `source`, re-based to 0-based offsets from the block's top-left. A merge reaching outside the
18
+ * block is not part of its pattern (Sheets/Excel behave the same — the painter copies the look of
19
+ * the selected cells, not of the cells around them).
20
+ */
21
+ export declare const collectPaintMerges: (merges: GridRange[] | null | undefined, source: GridRange) => GridRange[];
22
+ /**
23
+ * The block the painter captures when it is armed WITHOUT a drag selection. A plain click on a
24
+ * merged cell selects the whole merged area in Excel and Sheets, but the grid reports it as the
25
+ * anchor active cell plus an empty selection list — so the source is the merge containing the
26
+ * active cell when there is one, otherwise the single cell.
27
+ */
28
+ export declare const paintSourceForActiveCell: (merges: GridRange[] | null | undefined, activeCell: CellInterface) => GridRange;
29
+ /**
30
+ * Excel stores a merged area's style on every constituent cell, and the grid paints a merge with
31
+ * its anchor's format. Copy the anchor's captured format over the covered cells of every merge in
32
+ * the pattern (in place) so the painted target carries the fill on all of them — an unmerge at the
33
+ * target, or a partial tile that drops the merge, still leaves every cell looking as it did.
34
+ */
35
+ export declare const spreadMergeAnchorFormats: (formats: (CellFormat | null | undefined)[][], relativeMerges: GridRange[]) => void;
36
+ /**
37
+ * Tile a pattern's relative merges across `target` with the pattern's period (`rows` × `cols`),
38
+ * the same way the format grid itself is tiled by the formatting applier. A merge that a partial
39
+ * trailing tile would push past the target's edge is dropped — the painter never creates a merge
40
+ * that the user did not drag over.
41
+ */
42
+ export declare const tilePaintMerges: (relativeMerges: GridRange[], rows: number, cols: number, target: GridRange) => GridRange[];
43
+ /**
44
+ * The banded (table stripe / alternating-colour) format the grid paints under a cell, looked up
45
+ * in the map `getBandedCells` builds for a sheet.
46
+ */
47
+ export declare const getBandedCellFormat: (bandedCells: Map<string, CellFormat> | undefined | null, rowIndex: number, columnIndex: number) => CellFormat | undefined;
48
+ /**
49
+ * Layer a cell's explicit format over the banded format the grid would paint beneath it, so the
50
+ * captured pattern carries the VISIBLE look (Sheets copies the alternating colours as static cell
51
+ * fills — it does not create a table or banding at the target). Explicit keys win; an explicit
52
+ * `undefined` never clobbers a banded value.
53
+ */
54
+ export declare const layerBandedFormat: (explicit: CellFormat | null | undefined, banded: CellFormat | undefined) => CellFormat | null | undefined;
55
+ //# sourceMappingURL=paint-format.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"paint-format.d.ts","sourceRoot":"","sources":["../../../api/paint-format.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,UAAU,EACV,aAAa,EACb,SAAS,EACV,MAAM,4BAA4B,CAAC;AAGpC;;;;GAIG;AACH,MAAM,MAAM,YAAY,GAAG;IACzB,OAAO,EAAE,CAAC,UAAU,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,EAAE,CAAC;IAC7C,kFAAkF;IAClF,MAAM,EAAE,SAAS,EAAE,CAAC;IACpB,6CAA6C;IAC7C,IAAI,EAAE,MAAM,CAAC;IACb,IAAI,EAAE,MAAM,CAAC;CACd,CAAC;AAQF;;;;;GAKG;AACH,eAAO,MAAM,kBAAkB,GAC7B,QAAQ,SAAS,EAAE,GAAG,IAAI,GAAG,SAAS,EACtC,QAAQ,SAAS,KAChB,SAAS,EAaX,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,GACnC,QAAQ,SAAS,EAAE,GAAG,IAAI,GAAG,SAAS,EACtC,YAAY,aAAa,KACxB,SAkBF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,wBAAwB,GACnC,SAAS,CAAC,UAAU,GAAG,IAAI,GAAG,SAAS,CAAC,EAAE,EAAE,EAC5C,gBAAgB,SAAS,EAAE,KAC1B,IAYF,CAAC;AAEF;;;;;GAKG;AACH,eAAO,MAAM,eAAe,GAC1B,gBAAgB,SAAS,EAAE,EAC3B,MAAM,MAAM,EACZ,MAAM,MAAM,EACZ,QAAQ,SAAS,KAChB,SAAS,EAqBX,CAAC;AAEF;;;GAGG;AACH,eAAO,MAAM,mBAAmB,GAC9B,aAAa,GAAG,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,SAAS,GAAG,IAAI,EACvD,UAAU,MAAM,EAChB,aAAa,MAAM,KAClB,UAAU,GAAG,SACyC,CAAC;AAE1D;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,GAC5B,UAAU,UAAU,GAAG,IAAI,GAAG,SAAS,EACvC,QAAQ,UAAU,GAAG,SAAS,KAC7B,UAAU,GAAG,IAAI,GAAG,SAWtB,CAAC"}
@@ -0,0 +1,122 @@
1
+ import { cellIdentifier, isCellWithinBounds } from "@rowsncolumns/grid";
2
+ const rangeContains = (outer, inner) => inner.startRowIndex >= outer.startRowIndex &&
3
+ inner.endRowIndex <= outer.endRowIndex &&
4
+ inner.startColumnIndex >= outer.startColumnIndex &&
5
+ inner.endColumnIndex <= outer.endColumnIndex;
6
+ /**
7
+ * The merges the painter carries from a source block: every merge that lies FULLY inside
8
+ * `source`, re-based to 0-based offsets from the block's top-left. A merge reaching outside the
9
+ * block is not part of its pattern (Sheets/Excel behave the same — the painter copies the look of
10
+ * the selected cells, not of the cells around them).
11
+ */
12
+ export const collectPaintMerges = (merges, source) => {
13
+ if (!merges?.length)
14
+ return [];
15
+ const out = [];
16
+ for (const merge of merges) {
17
+ if (!rangeContains(source, merge))
18
+ continue;
19
+ out.push({
20
+ startRowIndex: merge.startRowIndex - source.startRowIndex,
21
+ endRowIndex: merge.endRowIndex - source.startRowIndex,
22
+ startColumnIndex: merge.startColumnIndex - source.startColumnIndex,
23
+ endColumnIndex: merge.endColumnIndex - source.startColumnIndex,
24
+ });
25
+ }
26
+ return out;
27
+ };
28
+ /**
29
+ * The block the painter captures when it is armed WITHOUT a drag selection. A plain click on a
30
+ * merged cell selects the whole merged area in Excel and Sheets, but the grid reports it as the
31
+ * anchor active cell plus an empty selection list — so the source is the merge containing the
32
+ * active cell when there is one, otherwise the single cell.
33
+ */
34
+ export const paintSourceForActiveCell = (merges, activeCell) => {
35
+ const merge = merges?.find((candidate) => isCellWithinBounds(activeCell, candidate));
36
+ if (merge) {
37
+ return {
38
+ startRowIndex: merge.startRowIndex,
39
+ endRowIndex: merge.endRowIndex,
40
+ startColumnIndex: merge.startColumnIndex,
41
+ endColumnIndex: merge.endColumnIndex,
42
+ };
43
+ }
44
+ return {
45
+ startRowIndex: activeCell.rowIndex,
46
+ endRowIndex: activeCell.rowIndex,
47
+ startColumnIndex: activeCell.columnIndex,
48
+ endColumnIndex: activeCell.columnIndex,
49
+ };
50
+ };
51
+ /**
52
+ * Excel stores a merged area's style on every constituent cell, and the grid paints a merge with
53
+ * its anchor's format. Copy the anchor's captured format over the covered cells of every merge in
54
+ * the pattern (in place) so the painted target carries the fill on all of them — an unmerge at the
55
+ * target, or a partial tile that drops the merge, still leaves every cell looking as it did.
56
+ */
57
+ export const spreadMergeAnchorFormats = (formats, relativeMerges) => {
58
+ for (const merge of relativeMerges) {
59
+ const anchor = formats[merge.startRowIndex]?.[merge.startColumnIndex];
60
+ for (let r = merge.startRowIndex; r <= merge.endRowIndex; r++) {
61
+ const row = formats[r];
62
+ if (!row)
63
+ continue;
64
+ for (let c = merge.startColumnIndex; c <= merge.endColumnIndex; c++) {
65
+ if (r === merge.startRowIndex && c === merge.startColumnIndex)
66
+ continue;
67
+ if (c < row.length)
68
+ row[c] = anchor;
69
+ }
70
+ }
71
+ }
72
+ };
73
+ /**
74
+ * Tile a pattern's relative merges across `target` with the pattern's period (`rows` × `cols`),
75
+ * the same way the format grid itself is tiled by the formatting applier. A merge that a partial
76
+ * trailing tile would push past the target's edge is dropped — the painter never creates a merge
77
+ * that the user did not drag over.
78
+ */
79
+ export const tilePaintMerges = (relativeMerges, rows, cols, target) => {
80
+ if (!relativeMerges.length || rows <= 0 || cols <= 0)
81
+ return [];
82
+ const out = [];
83
+ for (let top = target.startRowIndex; top <= target.endRowIndex; top += rows) {
84
+ for (let left = target.startColumnIndex; left <= target.endColumnIndex; left += cols) {
85
+ for (const merge of relativeMerges) {
86
+ const placed = {
87
+ startRowIndex: top + merge.startRowIndex,
88
+ endRowIndex: top + merge.endRowIndex,
89
+ startColumnIndex: left + merge.startColumnIndex,
90
+ endColumnIndex: left + merge.endColumnIndex,
91
+ };
92
+ if (rangeContains(target, placed))
93
+ out.push(placed);
94
+ }
95
+ }
96
+ }
97
+ return out;
98
+ };
99
+ /**
100
+ * The banded (table stripe / alternating-colour) format the grid paints under a cell, looked up
101
+ * in the map `getBandedCells` builds for a sheet.
102
+ */
103
+ export const getBandedCellFormat = (bandedCells, rowIndex, columnIndex) => bandedCells?.get(cellIdentifier(rowIndex, columnIndex));
104
+ /**
105
+ * Layer a cell's explicit format over the banded format the grid would paint beneath it, so the
106
+ * captured pattern carries the VISIBLE look (Sheets copies the alternating colours as static cell
107
+ * fills — it does not create a table or banding at the target). Explicit keys win; an explicit
108
+ * `undefined` never clobbers a banded value.
109
+ */
110
+ export const layerBandedFormat = (explicit, banded) => {
111
+ if (!banded)
112
+ return explicit;
113
+ const merged = { ...banded };
114
+ if (explicit) {
115
+ for (const [key, value] of Object.entries(explicit)) {
116
+ if (value !== undefined) {
117
+ merged[key] = value;
118
+ }
119
+ }
120
+ }
121
+ return merged;
122
+ };
@@ -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"}
@@ -22,3 +22,31 @@ export const splitTextToColumns = (values, delimiter = ",") => {
22
22
  return trimmedParts;
23
23
  });
24
24
  };
25
+ /** Normalized, ascending, de-duplicated positive break positions. */
26
+ export const normalizeColumnBreaks = (columnBreaks) => [...new Set(columnBreaks)]
27
+ .filter((position) => Number.isInteger(position) && position > 0)
28
+ .sort((a, b) => a - b);
29
+ /**
30
+ * Excel's Text to Columns → Fixed width: cut each cell at the given
31
+ * character positions. Fields are trimmed like the delimited path (Excel's
32
+ * General column data format drops the padding spaces fixed-width data is
33
+ * aligned with). Cells without breaks inside their text still split — the
34
+ * trailing fields are empty — so the resulting block is rectangular.
35
+ */
36
+ export const splitTextFixedWidth = (values, columnBreaks) => {
37
+ const breaks = normalizeColumnBreaks(columnBreaks);
38
+ return values.map((cell) => {
39
+ if (isNil(cell)) {
40
+ return [""];
41
+ }
42
+ const text = String(cell);
43
+ const parts = [];
44
+ let start = 0;
45
+ for (const position of breaks) {
46
+ parts.push(text.slice(start, position).trim());
47
+ start = position;
48
+ }
49
+ parts.push(text.slice(start).trim());
50
+ return parts;
51
+ });
52
+ };
@@ -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"}