@rowsncolumns/toolkit 24.4.2 → 24.4.4

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 (166) hide show
  1. package/dist/cjs/excel-parser/cfb-decryptor.d.ts +6 -0
  2. package/dist/cjs/excel-parser/cfb-decryptor.d.ts.map +1 -1
  3. package/dist/cjs/excel-parser/cfb-decryptor.js +6 -0
  4. package/dist/cjs/excel-parser/helpers.d.ts +9 -1
  5. package/dist/cjs/excel-parser/helpers.d.ts.map +1 -1
  6. package/dist/cjs/excel-parser/helpers.js +37 -0
  7. package/dist/cjs/excel-parser/interface.d.ts +15 -2
  8. package/dist/cjs/excel-parser/interface.d.ts.map +1 -1
  9. package/dist/cjs/excel-parser/plugins/base/index.d.ts +2 -0
  10. package/dist/cjs/excel-parser/plugins/base/index.d.ts.map +1 -1
  11. package/dist/cjs/excel-parser/plugins/base/index.js +3 -0
  12. package/dist/cjs/excel-parser/plugins/ods/index.d.ts +19 -0
  13. package/dist/cjs/excel-parser/plugins/ods/index.d.ts.map +1 -1
  14. package/dist/cjs/excel-parser/plugins/ods/index.js +676 -304
  15. package/dist/cjs/excel-parser/plugins/ods/styles.d.ts +43 -1
  16. package/dist/cjs/excel-parser/plugins/ods/styles.d.ts.map +1 -1
  17. package/dist/cjs/excel-parser/plugins/ods/styles.js +343 -45
  18. package/dist/cjs/excel-parser/plugins/xls/index.d.ts +55 -1
  19. package/dist/cjs/excel-parser/plugins/xls/index.d.ts.map +1 -1
  20. package/dist/cjs/excel-parser/plugins/xls/index.js +518 -6
  21. package/dist/cjs/excel-parser/plugins/xlsb/index.d.ts +4 -0
  22. package/dist/cjs/excel-parser/plugins/xlsb/index.d.ts.map +1 -1
  23. package/dist/cjs/excel-parser/plugins/xlsb/index.js +33 -14
  24. package/dist/cjs/excel-parser/plugins/xlsx/chartex.d.ts +37 -0
  25. package/dist/cjs/excel-parser/plugins/xlsx/chartex.d.ts.map +1 -0
  26. package/dist/cjs/excel-parser/plugins/xlsx/chartex.js +244 -0
  27. package/dist/cjs/excel-parser/plugins/xlsx/charts.d.ts +10 -0
  28. package/dist/cjs/excel-parser/plugins/xlsx/charts.d.ts.map +1 -1
  29. package/dist/cjs/excel-parser/plugins/xlsx/charts.js +68 -9
  30. package/dist/cjs/excel-parser/plugins/xlsx/drawing.d.ts +28 -1
  31. package/dist/cjs/excel-parser/plugins/xlsx/drawing.d.ts.map +1 -1
  32. package/dist/cjs/excel-parser/plugins/xlsx/drawing.js +155 -20
  33. package/dist/cjs/excel-parser/plugins/xlsx/form-controls.d.ts +70 -0
  34. package/dist/cjs/excel-parser/plugins/xlsx/form-controls.d.ts.map +1 -0
  35. package/dist/cjs/excel-parser/plugins/xlsx/form-controls.js +295 -0
  36. package/dist/cjs/excel-parser/plugins/xlsx/index.d.ts +35 -2
  37. package/dist/cjs/excel-parser/plugins/xlsx/index.d.ts.map +1 -1
  38. package/dist/cjs/excel-parser/plugins/xlsx/index.js +737 -257
  39. package/dist/cjs/excel-parser/plugins/xlsx/pivots.d.ts.map +1 -1
  40. package/dist/cjs/excel-parser/plugins/xlsx/pivots.js +125 -37
  41. package/dist/cjs/excel-parser/plugins/xlsx/sheet-views.d.ts +3 -0
  42. package/dist/cjs/excel-parser/plugins/xlsx/sheet-views.d.ts.map +1 -1
  43. package/dist/cjs/excel-parser/plugins/xlsx/sheet-views.js +4 -1
  44. package/dist/cjs/excel-parser/plugins/xlsx/styles.d.ts +15 -1
  45. package/dist/cjs/excel-parser/plugins/xlsx/styles.d.ts.map +1 -1
  46. package/dist/cjs/excel-parser/plugins/xlsx/styles.js +102 -6
  47. package/dist/cjs/excel-parser/registry.d.ts +7 -0
  48. package/dist/cjs/excel-parser/registry.d.ts.map +1 -1
  49. package/dist/cjs/excel-parser/registry.js +29 -0
  50. package/dist/cjs/excel-parser/types.d.ts +17 -2
  51. package/dist/cjs/excel-parser/types.d.ts.map +1 -1
  52. package/dist/cjs/excel-parser/workbook.d.ts +3 -0
  53. package/dist/cjs/excel-parser/workbook.d.ts.map +1 -1
  54. package/dist/cjs/excel-parser/workbook.js +24 -14
  55. package/dist/cjs/excel-parser/worker.js +131 -131
  56. package/dist/cjs/exporter/ods/ods-export.d.ts.map +1 -1
  57. package/dist/cjs/exporter/ods/ods-export.js +432 -175
  58. package/dist/cjs/exporter/xlsx/chart-builder.d.ts.map +1 -1
  59. package/dist/cjs/exporter/xlsx/chart-builder.js +215 -39
  60. package/dist/cjs/exporter/xlsx/chartex-builder.d.ts +36 -0
  61. package/dist/cjs/exporter/xlsx/chartex-builder.d.ts.map +1 -0
  62. package/dist/cjs/exporter/xlsx/chartex-builder.js +109 -0
  63. package/dist/cjs/exporter/xlsx/form-controls-builder.d.ts +61 -0
  64. package/dist/cjs/exporter/xlsx/form-controls-builder.d.ts.map +1 -0
  65. package/dist/cjs/exporter/xlsx/form-controls-builder.js +197 -0
  66. package/dist/cjs/exporter/xlsx/pivot-builder.d.ts +8 -0
  67. package/dist/cjs/exporter/xlsx/pivot-builder.d.ts.map +1 -1
  68. package/dist/cjs/exporter/xlsx/pivot-builder.js +141 -42
  69. package/dist/cjs/exporter/xlsx/style-builder.js +9 -3
  70. package/dist/cjs/exporter/xlsx/threaded-comments-builder.d.ts +7 -1
  71. package/dist/cjs/exporter/xlsx/threaded-comments-builder.d.ts.map +1 -1
  72. package/dist/cjs/exporter/xlsx/threaded-comments-builder.js +15 -3
  73. package/dist/cjs/exporter/xlsx/xlsx-export.d.ts +11 -2
  74. package/dist/cjs/exporter/xlsx/xlsx-export.d.ts.map +1 -1
  75. package/dist/cjs/exporter/xlsx/xlsx-export.js +991 -297
  76. package/dist/cjs/helpers/implicit-intersection.d.ts +16 -0
  77. package/dist/cjs/helpers/implicit-intersection.d.ts.map +1 -0
  78. package/dist/cjs/helpers/implicit-intersection.js +197 -0
  79. package/dist/cjs/print-settings.d.ts +105 -0
  80. package/dist/cjs/print-settings.d.ts.map +1 -0
  81. package/dist/cjs/print-settings.js +494 -0
  82. package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
  83. package/dist/esm/excel-parser/cfb-decryptor.d.ts +6 -0
  84. package/dist/esm/excel-parser/cfb-decryptor.d.ts.map +1 -1
  85. package/dist/esm/excel-parser/cfb-decryptor.js +6 -0
  86. package/dist/esm/excel-parser/helpers.d.ts +9 -1
  87. package/dist/esm/excel-parser/helpers.d.ts.map +1 -1
  88. package/dist/esm/excel-parser/helpers.js +34 -0
  89. package/dist/esm/excel-parser/interface.d.ts +15 -2
  90. package/dist/esm/excel-parser/interface.d.ts.map +1 -1
  91. package/dist/esm/excel-parser/plugins/base/index.d.ts +2 -0
  92. package/dist/esm/excel-parser/plugins/base/index.d.ts.map +1 -1
  93. package/dist/esm/excel-parser/plugins/base/index.js +3 -0
  94. package/dist/esm/excel-parser/plugins/ods/index.d.ts +19 -0
  95. package/dist/esm/excel-parser/plugins/ods/index.d.ts.map +1 -1
  96. package/dist/esm/excel-parser/plugins/ods/index.js +347 -33
  97. package/dist/esm/excel-parser/plugins/ods/styles.d.ts +43 -1
  98. package/dist/esm/excel-parser/plugins/ods/styles.d.ts.map +1 -1
  99. package/dist/esm/excel-parser/plugins/ods/styles.js +276 -2
  100. package/dist/esm/excel-parser/plugins/xls/index.d.ts +55 -1
  101. package/dist/esm/excel-parser/plugins/xls/index.d.ts.map +1 -1
  102. package/dist/esm/excel-parser/plugins/xls/index.js +441 -3
  103. package/dist/esm/excel-parser/plugins/xlsb/index.d.ts +4 -0
  104. package/dist/esm/excel-parser/plugins/xlsb/index.d.ts.map +1 -1
  105. package/dist/esm/excel-parser/plugins/xlsb/index.js +30 -9
  106. package/dist/esm/excel-parser/plugins/xlsx/chartex.d.ts +37 -0
  107. package/dist/esm/excel-parser/plugins/xlsx/chartex.d.ts.map +1 -0
  108. package/dist/esm/excel-parser/plugins/xlsx/chartex.js +199 -0
  109. package/dist/esm/excel-parser/plugins/xlsx/charts.d.ts +10 -0
  110. package/dist/esm/excel-parser/plugins/xlsx/charts.d.ts.map +1 -1
  111. package/dist/esm/excel-parser/plugins/xlsx/charts.js +69 -6
  112. package/dist/esm/excel-parser/plugins/xlsx/drawing.d.ts +28 -1
  113. package/dist/esm/excel-parser/plugins/xlsx/drawing.d.ts.map +1 -1
  114. package/dist/esm/excel-parser/plugins/xlsx/drawing.js +99 -0
  115. package/dist/esm/excel-parser/plugins/xlsx/form-controls.d.ts +70 -0
  116. package/dist/esm/excel-parser/plugins/xlsx/form-controls.d.ts.map +1 -0
  117. package/dist/esm/excel-parser/plugins/xlsx/form-controls.js +246 -0
  118. package/dist/esm/excel-parser/plugins/xlsx/index.d.ts +35 -2
  119. package/dist/esm/excel-parser/plugins/xlsx/index.d.ts.map +1 -1
  120. package/dist/esm/excel-parser/plugins/xlsx/index.js +454 -32
  121. package/dist/esm/excel-parser/plugins/xlsx/pivots.d.ts.map +1 -1
  122. package/dist/esm/excel-parser/plugins/xlsx/pivots.js +86 -6
  123. package/dist/esm/excel-parser/plugins/xlsx/sheet-views.d.ts +3 -0
  124. package/dist/esm/excel-parser/plugins/xlsx/sheet-views.d.ts.map +1 -1
  125. package/dist/esm/excel-parser/plugins/xlsx/sheet-views.js +2 -0
  126. package/dist/esm/excel-parser/plugins/xlsx/styles.d.ts +15 -1
  127. package/dist/esm/excel-parser/plugins/xlsx/styles.d.ts.map +1 -1
  128. package/dist/esm/excel-parser/plugins/xlsx/styles.js +92 -2
  129. package/dist/esm/excel-parser/registry.d.ts +7 -0
  130. package/dist/esm/excel-parser/registry.d.ts.map +1 -1
  131. package/dist/esm/excel-parser/registry.js +19 -0
  132. package/dist/esm/excel-parser/types.d.ts +17 -2
  133. package/dist/esm/excel-parser/types.d.ts.map +1 -1
  134. package/dist/esm/excel-parser/workbook.d.ts +3 -0
  135. package/dist/esm/excel-parser/workbook.d.ts.map +1 -1
  136. package/dist/esm/excel-parser/workbook.js +19 -10
  137. package/dist/esm/excel-parser/worker.js +12 -18
  138. package/dist/esm/exporter/ods/ods-export.d.ts.map +1 -1
  139. package/dist/esm/exporter/ods/ods-export.js +306 -35
  140. package/dist/esm/exporter/xlsx/chart-builder.d.ts.map +1 -1
  141. package/dist/esm/exporter/xlsx/chart-builder.js +161 -28
  142. package/dist/esm/exporter/xlsx/chartex-builder.d.ts +36 -0
  143. package/dist/esm/exporter/xlsx/chartex-builder.d.ts.map +1 -0
  144. package/dist/esm/exporter/xlsx/chartex-builder.js +97 -0
  145. package/dist/esm/exporter/xlsx/form-controls-builder.d.ts +61 -0
  146. package/dist/esm/exporter/xlsx/form-controls-builder.d.ts.map +1 -0
  147. package/dist/esm/exporter/xlsx/form-controls-builder.js +173 -0
  148. package/dist/esm/exporter/xlsx/pivot-builder.d.ts +8 -0
  149. package/dist/esm/exporter/xlsx/pivot-builder.d.ts.map +1 -1
  150. package/dist/esm/exporter/xlsx/pivot-builder.js +104 -19
  151. package/dist/esm/exporter/xlsx/style-builder.js +9 -3
  152. package/dist/esm/exporter/xlsx/threaded-comments-builder.d.ts +7 -1
  153. package/dist/esm/exporter/xlsx/threaded-comments-builder.d.ts.map +1 -1
  154. package/dist/esm/exporter/xlsx/threaded-comments-builder.js +15 -3
  155. package/dist/esm/exporter/xlsx/xlsx-export.d.ts +11 -2
  156. package/dist/esm/exporter/xlsx/xlsx-export.d.ts.map +1 -1
  157. package/dist/esm/exporter/xlsx/xlsx-export.js +675 -64
  158. package/dist/esm/helpers/implicit-intersection.d.ts +16 -0
  159. package/dist/esm/helpers/implicit-intersection.d.ts.map +1 -0
  160. package/dist/esm/helpers/implicit-intersection.js +184 -0
  161. package/dist/esm/print-settings.d.ts +105 -0
  162. package/dist/esm/print-settings.d.ts.map +1 -0
  163. package/dist/esm/print-settings.js +422 -0
  164. package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
  165. package/package.json +3 -3
  166. package/xlsx.d.ts +14 -0
@@ -1,4 +1,43 @@
1
1
  import { XMLBuilder } from "fast-xml-parser";
2
+ /**
3
+ * `PivotValue.showAs` → `dataField@showDataAs` (ECMA-376) or, for the
4
+ * rank modes Excel only knows through its 2010 extension,
5
+ * `x14:dataField@pivotShowAs`.
6
+ */
7
+ const SHOW_DATA_AS = {
8
+ pct_of_total: "percentOfTotal",
9
+ pct_of_row: "percentOfRow",
10
+ pct_of_column: "percentOfCol",
11
+ running_total: "runTotal",
12
+ };
13
+ const X14_PIVOT_SHOW_AS = {
14
+ rank_asc: "rankAscending",
15
+ rank_desc: "rankDescending",
16
+ };
17
+ const X14_DATA_FIELD_EXT_URI = "{E15A36E0-9728-4e99-A89B-3F7291B0FE68}";
18
+ const X14_NS = "http://schemas.microsoft.com/office/spreadsheetml/2009/9/main";
19
+ /**
20
+ * Distinct source fields a calculated-field formula references —
21
+ * single-quoted names (`'Unit Price'`, `''` escapes a quote) or bare
22
+ * identifiers; numeric literals (incl. `1e5`) and operators are skipped.
23
+ * Mirrors `@rowsncolumns/pivot`'s tokenizer without pulling in the
24
+ * DuckDB-backed pivot package.
25
+ */
26
+ export const calculatedFieldReferences = (formula) => {
27
+ const out = [];
28
+ const push = (name) => {
29
+ if (name && !out.includes(name))
30
+ out.push(name);
31
+ };
32
+ const re = /'((?:[^']|'')*)'|(?:\d+\.?\d*|\.\d+)(?:[eE][+-]?\d+)?|([A-Za-z_\u00C0-\uFFFF][A-Za-z0-9_.\u00C0-\uFFFF]*)/g;
33
+ for (const m of formula.replace(/^\s*=/, "").matchAll(re)) {
34
+ if (m[1] !== undefined)
35
+ push(m[1].replace(/''/g, "'"));
36
+ else if (m[2] !== undefined)
37
+ push(m[2]);
38
+ }
39
+ return out;
40
+ };
2
41
  const xmlBuilder = new XMLBuilder({
3
42
  ignoreAttributes: false,
4
43
  attributeNamePrefix: "@_",
@@ -54,10 +93,15 @@ export const buildPivotCacheDefinitionXml = (pivot, sheetName, fieldNames) => {
54
93
  if (!pivot.source)
55
94
  return null;
56
95
  const sourceRef = rangeRef(pivot.source);
57
- const cacheFields = fieldNames.map((name) => ({
58
- "@_name": name,
59
- sharedItems: { "@_count": "0" },
60
- }));
96
+ // Calculated fields are cache fields too (`databaseField="0"` +
97
+ // `formula`), listed after the source columns like Excel does.
98
+ const calculatedByName = new Map((pivot.calculatedFields ?? []).map((c) => [c.name, c.formula]));
99
+ const cacheFields = fieldNames.map((name) => {
100
+ const formula = calculatedByName.get(name);
101
+ return formula !== undefined
102
+ ? { "@_name": name, "@_formula": formula, "@_databaseField": "0" }
103
+ : { "@_name": name, sharedItems: { "@_count": "0" } };
104
+ });
61
105
  const doc = {
62
106
  "?xml": { "@_version": "1.0", "@_encoding": "UTF-8", "@_standalone": "yes" },
63
107
  pivotCacheDefinition: {
@@ -95,8 +139,10 @@ export const buildPivotTableXml = (pivot, cacheId, fieldIndices) => {
95
139
  if (!pivot.pivotRange)
96
140
  return null;
97
141
  const ref = rangeRef(pivot.pivotRange);
98
- // pivotFields: one entry per cacheField, with axis attribute
99
- // (axisRow / axisCol / dataField). Order = cacheField order.
142
+ // pivotFields: one entry per cacheField, with the axis attribute
143
+ // (axisRow / axisCol) and/or `dataField="1"` for value fields
144
+ // (`ST_Axis` has no "dataField" value — a field can sit on an axis
145
+ // AND be a data field). Order = cacheField order.
100
146
  const pivotFields = [];
101
147
  // We don't know the full field universe here without the source
102
148
  // schema. Best-effort: declare entries for the union of row/col/
@@ -112,23 +158,38 @@ export const buildPivotTableXml = (pivot, cacheId, fieldIndices) => {
112
158
  if (idx != null)
113
159
  fieldAxis.set(idx, "axisCol");
114
160
  }
161
+ const dataFieldIndices = new Set();
115
162
  for (const v of pivot.values ?? []) {
116
163
  const idx = fieldIndices.get(v.field);
117
164
  if (idx != null)
118
- fieldAxis.set(idx, "dataField");
165
+ dataFieldIndices.add(idx);
119
166
  }
167
+ const calculatedNames = new Set((pivot.calculatedFields ?? []).map((c) => c.name));
120
168
  // Need to emit one <pivotField> per cacheField in order, even
121
169
  // for unused fields (Excel requires the array shapes match).
122
170
  // Caller passes us the full field set via fieldIndices.
123
171
  const sortedFieldIndices = [...fieldIndices.entries()].sort((a, b) => a[1] - b[1]);
124
- for (const [, idx] of sortedFieldIndices) {
172
+ for (const [name, idx] of sortedFieldIndices) {
125
173
  const axis = fieldAxis.get(idx);
126
- if (axis) {
127
- pivotFields.push({ "@_axis": axis, "@_compact": "0", items: {} });
174
+ const entry = {};
175
+ if (axis)
176
+ entry["@_axis"] = axis;
177
+ if (dataFieldIndices.has(idx))
178
+ entry["@_dataField"] = "1";
179
+ if (calculatedNames.has(name)) {
180
+ // Excel pins calculated fields to the values area.
181
+ entry["@_dragToRow"] = "0";
182
+ entry["@_dragToCol"] = "0";
183
+ entry["@_dragToPage"] = "0";
184
+ entry["@_showAll"] = "0";
185
+ entry["@_defaultSubtotal"] = "0";
128
186
  }
129
187
  else {
130
- pivotFields.push({ "@_compact": "0" });
188
+ entry["@_compact"] = "0";
189
+ if (axis)
190
+ entry.items = {};
131
191
  }
192
+ pivotFields.push(entry);
132
193
  }
133
194
  const rowFields = (pivot.rows ?? [])
134
195
  .map((r) => fieldIndices.get(r.field))
@@ -138,18 +199,42 @@ export const buildPivotTableXml = (pivot, cacheId, fieldIndices) => {
138
199
  .map((c) => fieldIndices.get(c.field))
139
200
  .filter((i) => i != null)
140
201
  .map((i) => ({ "@_x": String(i) }));
202
+ // Running totals need a base field: Excel defaults to the first row
203
+ // field (falls back to the first column field / field 0).
204
+ const runTotalBase = rowFields[0]?.["@_x"] ?? colFields[0]?.["@_x"] ?? "0";
141
205
  const dataFields = (pivot.values ?? [])
142
206
  .map((v) => {
143
207
  const idx = fieldIndices.get(v.field);
144
208
  if (idx == null)
145
209
  return null;
146
- return {
210
+ const entry = {
147
211
  "@_name": v.displayName ?? `${v.aggFunc} of ${v.field}`,
148
212
  "@_fld": String(idx),
149
- "@_subtotal": aggToSubtotal(v.aggFunc),
213
+ "@_subtotal": calculatedNames.has(v.field)
214
+ ? "sum"
215
+ : aggToSubtotal(v.aggFunc),
150
216
  };
217
+ const showDataAs = v.showAs ? SHOW_DATA_AS[v.showAs] : undefined;
218
+ if (showDataAs) {
219
+ entry["@_showDataAs"] = showDataAs;
220
+ if (showDataAs === "runTotal")
221
+ entry["@_baseField"] = runTotalBase;
222
+ }
223
+ const pivotShowAs = v.showAs ? X14_PIVOT_SHOW_AS[v.showAs] : undefined;
224
+ if (pivotShowAs) {
225
+ entry.extLst = {
226
+ ext: {
227
+ "@_uri": X14_DATA_FIELD_EXT_URI,
228
+ "@_xmlns:x14": X14_NS,
229
+ "x14:dataField": { "@_pivotShowAs": pivotShowAs },
230
+ },
231
+ };
232
+ }
233
+ return entry;
151
234
  })
152
235
  .filter((d) => d !== null);
236
+ const styleInfo = pivot.styleInfo ?? {};
237
+ const styleFlag = (value, fallback) => value === undefined ? fallback : value ? "1" : "0";
153
238
  const doc = {
154
239
  "?xml": { "@_version": "1.0", "@_encoding": "UTF-8", "@_standalone": "yes" },
155
240
  pivotTableDefinition: {
@@ -195,12 +280,12 @@ export const buildPivotTableXml = (pivot, cacheId, fieldIndices) => {
195
280
  ? { "@_count": String(dataFields.length), dataField: dataFields }
196
281
  : undefined,
197
282
  pivotTableStyleInfo: {
198
- "@_name": "PivotStyleLight16",
199
- "@_showRowHeaders": "1",
200
- "@_showColHeaders": "1",
201
- "@_showRowStripes": "0",
202
- "@_showColStripes": "0",
203
- "@_showLastColumn": "1",
283
+ "@_name": styleInfo.name || "PivotStyleLight16",
284
+ "@_showRowHeaders": styleFlag(styleInfo.showRowHeaders, "1"),
285
+ "@_showColHeaders": styleFlag(styleInfo.showColHeaders, "1"),
286
+ "@_showRowStripes": styleFlag(styleInfo.showRowStripes, "0"),
287
+ "@_showColStripes": styleFlag(styleInfo.showColStripes, "0"),
288
+ "@_showLastColumn": styleFlag(styleInfo.showLastColumn, "1"),
204
289
  },
205
290
  },
206
291
  };
@@ -702,10 +702,16 @@ const serializeAlignment = (record) => {
702
702
  attrs.push(`relativeIndent="${record.relativeIndent}"`);
703
703
  }
704
704
  if (record.textRotation !== undefined && record.textRotation !== null) {
705
- // Excel encodes "vertical (stacked)" text as the magic value 255.
706
- const rotation = record.textRotation === "vertical" ? 255 : record.textRotation;
705
+ // Excel encodes "vertical (stacked)" text as the magic value 255 and
706
+ // clockwise rotation as 90 + degrees (91–180); the engine keeps signed
707
+ // counter-clockwise degrees.
708
+ const rotation = record.textRotation === "vertical"
709
+ ? 255
710
+ : record.textRotation < 0
711
+ ? 90 - record.textRotation
712
+ : record.textRotation;
707
713
  if (Number.isFinite(rotation) && rotation !== 0) {
708
- attrs.push(`textRotation="${rotation}"`);
714
+ attrs.push(`textRotation="${Math.round(rotation)}"`);
709
715
  }
710
716
  }
711
717
  if (record.shrinkToFit) {
@@ -10,6 +10,7 @@ export declare const PERSON_CONTENT_TYPE = "application/vnd.ms-excel.person+xml"
10
10
  * scoping author/comment ids within a single workbook.
11
11
  */
12
12
  export declare const deterministicGuid: (seed: string) => string;
13
+ export declare const isBracedGuid: (id: string) => boolean;
13
14
  /**
14
15
  * Workbook-scoped author registry. Shared across every sheet's collector so
15
16
  * an author who comments on multiple sheets maps to a single `person` entry
@@ -35,7 +36,12 @@ export declare class ThreadedCommentsCollector {
35
36
  /** Anchor a thread to a cell. No-op if the id is unknown or empty. */
36
37
  addCell(address: string, threadId: string | number | null | undefined): void;
37
38
  isEmpty(): boolean;
38
- /** Braced root GUID for a thread — used as both the root `id` and `tc=`. */
39
+ /**
40
+ * Braced root GUID for a thread — used as both the root `id` and `tc=`.
41
+ * An id that already is a braced GUID (what Excel and both importers
42
+ * produce) passes through, so a re-exported thread keeps the identity
43
+ * Excel assigned it; anything else is hashed deterministically.
44
+ */
39
45
  private rootGuid;
40
46
  /**
41
47
  * Legacy `xl/comments{N}.xml` mirror entries. Feed these into the existing
@@ -1 +1 @@
1
- {"version":3,"file":"threaded-comments-builder.d.ts","sourceRoot":"","sources":["../../../../exporter/xlsx/threaded-comments-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAyBhE,eAAO,MAAM,yBAAyB,8EACuC,CAAC;AAC9E,eAAO,MAAM,eAAe,qEACwC,CAAC;AACrE,eAAO,MAAM,8BAA8B,kDACM,CAAC;AAClD,eAAO,MAAM,mBAAmB,wCAAwC,CAAC;AAsBzE;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,GAAI,MAAM,MAAM,KAAG,MAiBhD,CAAC;AAEF;;;;GAIG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAGlB;IAEJ,8DAA8D;IAC9D,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS;IAWtE,OAAO;IAIP,KAAK;CAYN;AAQD;;;;GAIG;AACH,qBAAa,yBAAyB;IAMlC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAL1B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA6B;IACxD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsB;gBAG3C,OAAO,EAAE,aAAa,EAAE,GAAG,SAAS,EACnB,OAAO,EAAE,cAAc;IAO1C,sEAAsE;IACtE,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS;IAQrE,OAAO;IAIP,4EAA4E;IAC5E,OAAO,CAAC,QAAQ;IAIhB;;;;;OAKG;IACH,aAAa,IAAI;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE;IAcpE;;;;OAIG;IACH,KAAK,IAAI,MAAM,GAAG,SAAS;CA+B5B"}
1
+ {"version":3,"file":"threaded-comments-builder.d.ts","sourceRoot":"","sources":["../../../../exporter/xlsx/threaded-comments-builder.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,4BAA4B,CAAC;AAyBhE,eAAO,MAAM,yBAAyB,8EACuC,CAAC;AAC9E,eAAO,MAAM,eAAe,qEACwC,CAAC;AACrE,eAAO,MAAM,8BAA8B,kDACM,CAAC;AAClD,eAAO,MAAM,mBAAmB,wCAAwC,CAAC;AAsBzE;;;;;GAKG;AACH,eAAO,MAAM,iBAAiB,GAAI,MAAM,MAAM,KAAG,MAiBhD,CAAC;AAKF,eAAO,MAAM,YAAY,GAAI,IAAI,MAAM,YAAyB,CAAC;AAEjE;;;;GAIG;AACH,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,KAAK,CAGlB;IAEJ,8DAA8D;IAC9D,QAAQ,CAAC,QAAQ,EAAE,MAAM,GAAG,SAAS,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS;IAWtE,OAAO;IAIP,KAAK;CAYN;AAQD;;;;GAIG;AACH,qBAAa,yBAAyB;IAMlC,OAAO,CAAC,QAAQ,CAAC,OAAO;IAL1B,OAAO,CAAC,QAAQ,CAAC,UAAU,CAA6B;IACxD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAsB;gBAG3C,OAAO,EAAE,aAAa,EAAE,GAAG,SAAS,EACnB,OAAO,EAAE,cAAc;IAO1C,sEAAsE;IACtE,OAAO,CAAC,OAAO,EAAE,MAAM,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,GAAG,SAAS;IAQrE,OAAO;IAIP;;;;;OAKG;IACH,OAAO,CAAC,QAAQ;IAMhB;;;;;OAKG;IACH,aAAa,IAAI;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,MAAM,EAAE,MAAM,CAAA;KAAE,EAAE;IAcpE;;;;OAIG;IACH,KAAK,IAAI,MAAM,GAAG,SAAS;CAiC5B"}
@@ -60,6 +60,9 @@ export const deterministicGuid = (seed) => {
60
60
  const p5 = all.slice(20, 32);
61
61
  return `{${p1}-${p2}-${p3}-${p4}-${p5}}`;
62
62
  };
63
+ /** `{8-4-4-4-12}` hex, the braced RFC-4122 form Excel writes for comment and person ids. */
64
+ const BRACED_GUID = /^\{[0-9A-Fa-f]{8}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{4}-[0-9A-Fa-f]{12}\}$/;
65
+ export const isBracedGuid = (id) => BRACED_GUID.test(id);
63
66
  /**
64
67
  * Workbook-scoped author registry. Shared across every sheet's collector so
65
68
  * an author who comments on multiple sheets maps to a single `person` entry
@@ -118,9 +121,16 @@ export class ThreadedCommentsCollector {
118
121
  isEmpty() {
119
122
  return this.placed.length === 0;
120
123
  }
121
- /** Braced root GUID for a thread — used as both the root `id` and `tc=`. */
124
+ /**
125
+ * Braced root GUID for a thread — used as both the root `id` and `tc=`.
126
+ * An id that already is a braced GUID (what Excel and both importers
127
+ * produce) passes through, so a re-exported thread keeps the identity
128
+ * Excel assigned it; anything else is hashed deterministically.
129
+ */
122
130
  rootGuid(thread) {
123
- return deterministicGuid(`tc:${thread.id}`);
131
+ return isBracedGuid(thread.id)
132
+ ? thread.id.toUpperCase()
133
+ : deterministicGuid(`tc:${thread.id}`);
124
134
  }
125
135
  /**
126
136
  * Legacy `xl/comments{N}.xml` mirror entries. Feed these into the existing
@@ -157,7 +167,9 @@ export class ThreadedCommentsCollector {
157
167
  const personId = this.persons.register(msg.authorId, msg.authorName);
158
168
  const id = index === 0
159
169
  ? root
160
- : deterministicGuid(`c:${thread.id}:${msg.id}`);
170
+ : isBracedGuid(msg.id)
171
+ ? msg.id.toUpperCase()
172
+ : deterministicGuid(`c:${thread.id}:${msg.id}`);
161
173
  const parent = index === 0 ? "" : ` parentId="${root}"`;
162
174
  // Resolved state rides on the root as `done="1"` (MS-XLSX);
163
175
  // replies never carry it.
@@ -1,5 +1,5 @@
1
1
  import type { CellData, Citation, CommentThread, DataValidationRuleRecord, IterativeCalculationOptions, SheetData, TextFormat } from "@rowsncolumns/common-types";
2
- import type { Color, ConditionalFormatRule, EmbeddedChart, EmbeddedObject, NamedRange, PivotTable, Sheet, Slicer, SpreadsheetTheme, TableView } from "@rowsncolumns/spreadsheet";
2
+ import type { Color, ConditionalFormatRule, EmbeddedChart, EmbeddedObject, NamedRange, PivotTable, ProtectedRange, Sheet, Slicer, SpreadsheetTheme, TableView } from "@rowsncolumns/spreadsheet";
3
3
  import type { CellXfs, SharedStrings as SharedStringsType } from "@rowsncolumns/spreadsheet-state";
4
4
  import type { PowerQueryParts } from "../../power-query";
5
5
  import type { ExternalLink, WorkbookProperties } from "../../excel-parser/types";
@@ -32,6 +32,15 @@ export type ExportExcelProps<S extends Sheet, T extends CellData, C extends Cita
32
32
  pivotTables?: PivotTable[];
33
33
  externalLinks?: ExternalLink[];
34
34
  namedRanges?: NamedRange[];
35
+ /**
36
+ * Allow-edit ranges (`useSpreadsheetState`'s `protectedRanges`), written as
37
+ * `<protectedRanges>/<protectedRange sqref name>` on the sheet each range's
38
+ * `range.sheetId` names. Sheet-level protection is read from
39
+ * `Sheet.sheetProtection` and written as `<sheetProtection …>`; both sit
40
+ * right after `<sheetData>` per `CT_Worksheet`, the position the parser and
41
+ * `rnc-import` read them back from.
42
+ */
43
+ protectedRanges?: ProtectedRange[];
35
44
  /**
36
45
  * Workbook-wide named cell styles to round-trip into the
37
46
  * `<cellStyles>` block of styles.xml. Parsed by the importer as
@@ -109,7 +118,7 @@ export type ExportExcelProps<S extends Sheet, T extends CellData, C extends Cita
109
118
  */
110
119
  properties?: WorkbookProperties;
111
120
  };
112
- export declare const createExcelFile: <S extends Sheet, T extends CellData, C extends Citation>({ conditionalFormats: _conditionalFormats, dataValidations: _dataValidations, sheets, sheetData, sharedStrings: sourceSharedStrings, tables: _tables, cellXfs, theme, defaultFont, embeds, charts, slicers, pivotTables, externalLinks, namedRanges, namedCellStyles, customTableStyles, iterativeCalculationOptions, ignoreFormulaPatterns, citations, comments, password, locale, use1904, properties, powerQuery, }: ExportExcelProps<S, T, C>) => Promise<Uint8Array<ArrayBufferLike>>;
121
+ export declare const createExcelFile: <S extends Sheet, T extends CellData, C extends Citation>({ conditionalFormats: _conditionalFormats, dataValidations: _dataValidations, sheets, sheetData, sharedStrings: sourceSharedStrings, tables: _tables, cellXfs, theme, defaultFont, embeds, charts, slicers, pivotTables, externalLinks, namedRanges, protectedRanges, namedCellStyles, customTableStyles, iterativeCalculationOptions, ignoreFormulaPatterns, citations, comments, password, locale, use1904, properties, powerQuery, }: ExportExcelProps<S, T, C>) => Promise<Uint8Array<ArrayBufferLike>>;
113
122
  /**
114
123
  * Normalize a dataValidation sqref list: collapse degenerate ranges
115
124
  * (`C7:C7` → `C7`), drop exact duplicates, and drop members fully contained
@@ -1 +1 @@
1
- {"version":3,"file":"xlsx-export.d.ts","sourceRoot":"","sources":["../../../../exporter/xlsx/xlsx-export.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,QAAQ,EACR,QAAQ,EACR,aAAa,EAEb,wBAAwB,EAGxB,2BAA2B,EAI3B,SAAS,EACT,UAAU,EAEX,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAEV,KAAK,EACL,qBAAqB,EAErB,aAAa,EACb,cAAc,EAGd,UAAU,EACV,UAAU,EAMV,KAAK,EACL,MAAM,EACN,gBAAgB,EAChB,SAAS,EACV,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EACV,OAAO,EACP,aAAa,IAAI,iBAAiB,EACnC,MAAM,iCAAiC,CAAC;AAyBzC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAUzD,OAAO,KAAK,EACV,YAAY,EACZ,kBAAkB,EACnB,MAAM,0BAA0B,CAAC;AA6BlC,MAAM,MAAM,gBAAgB,CAC1B,CAAC,SAAS,KAAK,EACf,CAAC,SAAS,QAAQ,EAClB,CAAC,SAAS,QAAQ,IAChB;IACF,MAAM,EAAE,CAAC,EAAE,CAAC;IACZ,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;IACxB,aAAa,CAAC,EAAE,iBAAiB,CAAC;IAClC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC;IACrB,kBAAkB,CAAC,EAAE,qBAAqB,EAAE,CAAC;IAC7C,eAAe,CAAC,EAAE,wBAAwB,EAAE,CAAC;IAC7C,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACzB,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB;;;;OAIG;IACH,WAAW,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAChC,MAAM,CAAC,EAAE,cAAc,EAAE,CAAC;IAC1B,MAAM,CAAC,EAAE,aAAa,EAAE,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC;IAC/B,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B;;;;;;;;OAQG;IACH,eAAe,CAAC,EAAE,KAAK,CAAC;QACtB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,aAAa,CAAC,EAAE,OAAO,CAAC;KACzB,CAAC,CAAC;IACH;;;;;;;;;;;OAWG;IACH,iBAAiB,CAAC,EAAE,KAAK,CAAC;QACxB,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,QAAQ,EAAE,KAAK,CAAC;YACd,IAAI,EAAE,MAAM,CAAC;YACb,IAAI,CAAC,EAAE,MAAM,CAAC;YACd,eAAe,CAAC,EAAE,KAAK,CAAC;YACxB,SAAS,CAAC,EAAE,KAAK,CAAC;YAClB,IAAI,CAAC,EAAE,OAAO,CAAC;YACf,MAAM,CAAC,EAAE,OAAO,CAAC;SAClB,CAAC,CAAC;KACJ,CAAC,CAAC;IACH,2BAA2B,CAAC,EAAE,2BAA2B,CAAC;IAC1D,qBAAqB,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC1C,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC;IACvB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,aAAa,EAAE,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B;;;OAGG;IACH,UAAU,CAAC,EAAE,kBAAkB,CAAC;CACjC,CAAC;AAEF,eAAO,MAAM,eAAe,GAC1B,CAAC,SAAS,KAAK,EACf,CAAC,SAAS,QAAQ,EAClB,CAAC,SAAS,QAAQ,EAClB,wZA2BC,gBAAgB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,yCAqe3B,CAAC;AA2NF;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,GAAI,MAAM,MAAM,EAAE,KAAG,MAAM,EA2C5D,CAAC"}
1
+ {"version":3,"file":"xlsx-export.d.ts","sourceRoot":"","sources":["../../../../exporter/xlsx/xlsx-export.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,QAAQ,EACR,QAAQ,EACR,aAAa,EAEb,wBAAwB,EAGxB,2BAA2B,EAI3B,SAAS,EACT,UAAU,EAEX,MAAM,4BAA4B,CAAC;AACpC,OAAO,KAAK,EAGV,KAAK,EAEL,qBAAqB,EAErB,aAAa,EACb,cAAc,EAKd,UAAU,EACV,UAAU,EAEV,cAAc,EAMd,KAAK,EAEL,MAAM,EACN,gBAAgB,EAEhB,SAAS,EAEV,MAAM,2BAA2B,CAAC;AACnC,OAAO,KAAK,EACV,OAAO,EACP,aAAa,IAAI,iBAAiB,EACnC,MAAM,iCAAiC,CAAC;AA2CzC,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAC;AAUzD,OAAO,KAAK,EACV,YAAY,EACZ,kBAAkB,EACnB,MAAM,0BAA0B,CAAC;AAsClC,MAAM,MAAM,gBAAgB,CAC1B,CAAC,SAAS,KAAK,EACf,CAAC,SAAS,QAAQ,EAClB,CAAC,SAAS,QAAQ,IAChB;IACF,MAAM,EAAE,CAAC,EAAE,CAAC;IACZ,SAAS,EAAE,SAAS,CAAC,CAAC,CAAC,CAAC;IACxB,aAAa,CAAC,EAAE,iBAAiB,CAAC;IAClC,MAAM,CAAC,EAAE,SAAS,EAAE,CAAC;IACrB,kBAAkB,CAAC,EAAE,qBAAqB,EAAE,CAAC;IAC7C,eAAe,CAAC,EAAE,wBAAwB,EAAE,CAAC;IAC7C,OAAO,CAAC,EAAE,OAAO,GAAG,IAAI,CAAC;IACzB,KAAK,CAAC,EAAE,gBAAgB,CAAC;IACzB;;;;OAIG;IACH,WAAW,CAAC,EAAE,UAAU,GAAG,IAAI,CAAC;IAChC,MAAM,CAAC,EAAE,cAAc,EAAE,CAAC;IAC1B,MAAM,CAAC,EAAE,aAAa,EAAE,CAAC;IACzB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB;;;;;;;OAOG;IACH,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B,aAAa,CAAC,EAAE,YAAY,EAAE,CAAC;IAC/B,WAAW,CAAC,EAAE,UAAU,EAAE,CAAC;IAC3B;;;;;;;OAOG;IACH,eAAe,CAAC,EAAE,cAAc,EAAE,CAAC;IACnC;;;;;;;;OAQG;IACH,eAAe,CAAC,EAAE,KAAK,CAAC;QACtB,IAAI,EAAE,MAAM,CAAC;QACb,IAAI,EAAE,MAAM,CAAC;QACb,SAAS,CAAC,EAAE,MAAM,CAAC;QACnB,MAAM,CAAC,EAAE,MAAM,CAAC;QAChB,MAAM,CAAC,EAAE,OAAO,CAAC;QACjB,aAAa,CAAC,EAAE,OAAO,CAAC;KACzB,CAAC,CAAC;IACH;;;;;;;;;;;OAWG;IACH,iBAAiB,CAAC,EAAE,KAAK,CAAC;QACxB,IAAI,EAAE,MAAM,CAAC;QACb,KAAK,CAAC,EAAE,OAAO,CAAC;QAChB,QAAQ,EAAE,KAAK,CAAC;YACd,IAAI,EAAE,MAAM,CAAC;YACb,IAAI,CAAC,EAAE,MAAM,CAAC;YACd,eAAe,CAAC,EAAE,KAAK,CAAC;YACxB,SAAS,CAAC,EAAE,KAAK,CAAC;YAClB,IAAI,CAAC,EAAE,OAAO,CAAC;YACf,MAAM,CAAC,EAAE,OAAO,CAAC;SAClB,CAAC,CAAC;KACJ,CAAC,CAAC;IACH,2BAA2B,CAAC,EAAE,2BAA2B,CAAC;IAC1D,qBAAqB,CAAC,EAAE,MAAM,GAAG,MAAM,EAAE,CAAC;IAC1C,SAAS,CAAC,EAAE,QAAQ,EAAE,CAAC;IACvB;;;;;;OAMG;IACH,QAAQ,CAAC,EAAE,aAAa,EAAE,CAAC;IAC3B,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB;;;;OAIG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;;;OAKG;IACH,UAAU,CAAC,EAAE,eAAe,CAAC;IAC7B;;;OAGG;IACH,UAAU,CAAC,EAAE,kBAAkB,CAAC;CACjC,CAAC;AAEF,eAAO,MAAM,eAAe,GAC1B,CAAC,SAAS,KAAK,EACf,CAAC,SAAS,QAAQ,EAClB,CAAC,SAAS,QAAQ,EAClB,yaA4BC,gBAAgB,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC,CAAC,yCA4gB3B,CAAC;AAwOF;;;;;;GAMG;AACH,eAAO,MAAM,qBAAqB,GAAI,MAAM,MAAM,EAAE,KAAG,MAAM,EA2C5D,CAAC"}