@shbernal/ts-xlsx 3.0.0 → 3.2.0

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 (277) hide show
  1. package/README.md +34 -24
  2. package/dist/bytes.d.ts +10 -2
  3. package/dist/bytes.js +22 -0
  4. package/dist/core/address.d.ts +96 -2
  5. package/dist/core/address.js +87 -33
  6. package/dist/core/array-formula-ranges.d.ts +33 -0
  7. package/dist/core/array-formula-ranges.js +62 -0
  8. package/dist/core/autofilter.d.ts +4 -3
  9. package/dist/core/autofilter.js +31 -30
  10. package/dist/core/axis-handle.d.ts +2 -0
  11. package/dist/core/axis-handle.js +5 -2
  12. package/dist/core/cell.d.ts +4 -3
  13. package/dist/core/cell.js +4 -6
  14. package/dist/core/clone.d.ts +29 -0
  15. package/dist/core/clone.js +24 -0
  16. package/dist/core/color-resolution.js +4 -6
  17. package/dist/core/column.d.ts +1 -0
  18. package/dist/core/column.js +6 -2
  19. package/dist/core/comment-thread.d.ts +7 -3
  20. package/dist/core/comment-thread.js +2 -1
  21. package/dist/core/conditional-formatting-overlay.d.ts +11 -1
  22. package/dist/core/conditional-formatting-overlay.js +30 -3
  23. package/dist/core/conditional-formatting.d.ts +107 -11
  24. package/dist/core/conditional-formatting.js +76 -19
  25. package/dist/core/containers.d.ts +12 -0
  26. package/dist/core/containers.js +5 -0
  27. package/dist/core/data-validation-overlay.d.ts +7 -1
  28. package/dist/core/data-validation-overlay.js +19 -4
  29. package/dist/core/data-validation.d.ts +22 -0
  30. package/dist/core/data-validation.js +30 -4
  31. package/dist/core/date-format.d.ts +20 -0
  32. package/dist/core/date-format.js +170 -0
  33. package/dist/core/date.d.ts +43 -8
  34. package/dist/core/date.js +19 -3
  35. package/dist/core/formula-references.d.ts +26 -0
  36. package/dist/core/formula-references.js +173 -0
  37. package/dist/core/formula-scan.d.ts +2 -0
  38. package/dist/core/formula-scan.js +58 -0
  39. package/dist/core/formula.d.ts +76 -17
  40. package/dist/core/formula.js +87 -80
  41. package/dist/core/function-values.d.ts +4 -0
  42. package/dist/core/function-values.js +58 -0
  43. package/dist/core/future-functions.d.ts +3 -0
  44. package/dist/core/{modern-functions.js → future-functions.js} +119 -88
  45. package/dist/core/grid-edits.d.ts +30 -3
  46. package/dist/core/grid-edits.js +248 -81
  47. package/dist/core/grid-shift.d.ts +58 -9
  48. package/dist/core/grid-shift.js +26 -4
  49. package/dist/core/hyperlink.d.ts +39 -0
  50. package/dist/core/hyperlink.js +88 -0
  51. package/dist/core/image.d.ts +49 -14
  52. package/dist/core/image.js +16 -5
  53. package/dist/core/internal.d.ts +10 -2
  54. package/dist/core/limits.d.ts +25 -0
  55. package/dist/core/limits.js +4 -0
  56. package/dist/core/merge.d.ts +18 -4
  57. package/dist/core/merge.js +33 -20
  58. package/dist/core/page-setup.d.ts +26 -11
  59. package/dist/core/pivot-table.d.ts +49 -10
  60. package/dist/core/pivot-table.js +112 -60
  61. package/dist/core/protection.d.ts +66 -4
  62. package/dist/core/protection.js +6 -11
  63. package/dist/core/range.js +34 -30
  64. package/dist/core/row-input.js +2 -2
  65. package/dist/core/row.d.ts +1 -0
  66. package/dist/core/row.js +6 -2
  67. package/dist/core/structured-reference.d.ts +7 -0
  68. package/dist/core/structured-reference.js +7 -0
  69. package/dist/core/style.d.ts +85 -70
  70. package/dist/core/style.js +92 -28
  71. package/dist/core/table-style.d.ts +1 -1
  72. package/dist/core/table-style.js +4 -4
  73. package/dist/core/table.d.ts +64 -16
  74. package/dist/core/table.js +87 -40
  75. package/dist/core/theme.d.ts +2 -0
  76. package/dist/core/theme.js +9 -5
  77. package/dist/core/value.d.ts +60 -28
  78. package/dist/core/value.js +48 -43
  79. package/dist/core/workbook-media.d.ts +34 -0
  80. package/dist/core/workbook-media.js +44 -0
  81. package/dist/core/workbook-styles.d.ts +83 -1
  82. package/dist/core/workbook-styles.js +4 -0
  83. package/dist/core/workbook-theme.js +5 -3
  84. package/dist/core/workbook-vba.js +3 -2
  85. package/dist/core/workbook.d.ts +41 -3
  86. package/dist/core/workbook.js +46 -41
  87. package/dist/core/worksheet-comments.d.ts +2 -1
  88. package/dist/core/worksheet-comments.js +20 -13
  89. package/dist/core/worksheet-merges.d.ts +49 -0
  90. package/dist/core/worksheet-merges.js +65 -0
  91. package/dist/core/worksheet-model.d.ts +10 -2
  92. package/dist/core/worksheet-model.js +28 -9
  93. package/dist/core/worksheet-pictures.d.ts +3 -3
  94. package/dist/core/worksheet-pictures.js +7 -6
  95. package/dist/core/worksheet.d.ts +165 -27
  96. package/dist/core/worksheet.js +206 -89
  97. package/dist/customui/ribbon.js +2 -1
  98. package/dist/entries/core.d.ts +21 -14
  99. package/dist/entries/core.js +14 -7
  100. package/dist/entries/errors.d.ts +1 -0
  101. package/dist/entries/errors.js +1 -0
  102. package/dist/entries/xlsx.d.ts +1 -1
  103. package/dist/errors.d.ts +64 -0
  104. package/dist/errors.js +22 -0
  105. package/dist/hex.d.ts +8 -0
  106. package/dist/hex.js +3 -0
  107. package/dist/io/cell-metadata/metadata.d.ts +42 -0
  108. package/dist/io/cell-metadata/metadata.js +35 -0
  109. package/dist/io/cell-metadata/rich-values.d.ts +11 -0
  110. package/dist/io/cell-metadata/rich-values.js +80 -0
  111. package/dist/io/csv/delimiter.d.ts +21 -0
  112. package/dist/io/csv/delimiter.js +18 -0
  113. package/dist/io/csv/errors.d.ts +14 -0
  114. package/dist/io/csv/errors.js +5 -0
  115. package/dist/io/csv/read.d.ts +6 -1
  116. package/dist/io/csv/read.js +51 -22
  117. package/dist/io/csv/write.d.ts +19 -5
  118. package/dist/io/csv/write.js +24 -44
  119. package/dist/io/opc/inflate.js +14 -0
  120. package/dist/io/opc/namespaces.d.ts +16 -0
  121. package/dist/io/opc/namespaces.js +4 -0
  122. package/dist/io/opc/part-paths.d.ts +9 -0
  123. package/dist/io/opc/part-paths.js +5 -5
  124. package/dist/io/opc/read-opc.d.ts +58 -14
  125. package/dist/io/opc/read-opc.js +49 -21
  126. package/dist/io/opc/read-options.d.ts +5 -1
  127. package/dist/io/opc/sniff-format.d.ts +3 -3
  128. package/dist/io/opc/sniff-format.js +2 -2
  129. package/dist/io/opc/strict-relationships.d.ts +2 -0
  130. package/dist/io/opc/strict-relationships.js +12 -0
  131. package/dist/io/read-policy/column-budget.d.ts +26 -0
  132. package/dist/io/read-policy/column-budget.js +18 -0
  133. package/dist/io/read-policy/read-repair.d.ts +74 -0
  134. package/dist/io/read-policy/read-repair.js +119 -0
  135. package/dist/io/style/cell-style-resolution.d.ts +25 -0
  136. package/dist/io/style/cell-style-resolution.js +30 -0
  137. package/dist/io/style/xf-style.d.ts +23 -1
  138. package/dist/io/style/xf-style.js +8 -0
  139. package/dist/io/xlsb/formula.d.ts +2 -2
  140. package/dist/io/xlsb/formula.js +52 -35
  141. package/dist/io/xlsb/primitives.js +8 -14
  142. package/dist/io/xlsb/ptg-functions.d.ts +19 -10
  143. package/dist/io/xlsb/ptg-functions.js +495 -596
  144. package/dist/io/xlsb/read-metadata.d.ts +7 -0
  145. package/dist/io/xlsb/read-metadata.js +69 -0
  146. package/dist/io/xlsb/read-styles.js +45 -81
  147. package/dist/io/xlsb/read-worksheet.d.ts +26 -1
  148. package/dist/io/xlsb/read-worksheet.js +148 -106
  149. package/dist/io/xlsb/read.d.ts +6 -3
  150. package/dist/io/xlsb/read.js +70 -31
  151. package/dist/io/xlsb/record-stream.d.ts +30 -0
  152. package/dist/io/xlsb/record-stream.js +20 -0
  153. package/dist/io/xlsb/record-types.d.ts +14 -5
  154. package/dist/io/xlsb/record-types.js +14 -5
  155. package/dist/io/xlsb/sheet-protection.d.ts +20 -0
  156. package/dist/io/xlsb/sheet-protection.js +60 -0
  157. package/dist/io/xlsx/cell-accumulator.d.ts +24 -9
  158. package/dist/io/xlsx/cell-accumulator.js +74 -21
  159. package/dist/io/xlsx/cell-metadata.d.ts +39 -0
  160. package/dist/io/xlsx/cell-metadata.js +162 -0
  161. package/dist/io/xlsx/cell-value.d.ts +19 -5
  162. package/dist/io/xlsx/cell-value.js +45 -30
  163. package/dist/io/xlsx/color-xml.js +3 -2
  164. package/dist/io/xlsx/column-span.d.ts +11 -0
  165. package/dist/io/xlsx/column-span.js +9 -0
  166. package/dist/io/xlsx/comments.d.ts +9 -30
  167. package/dist/io/xlsx/comments.js +25 -83
  168. package/dist/io/xlsx/conditional-formatting.d.ts +43 -23
  169. package/dist/io/xlsx/conditional-formatting.js +437 -138
  170. package/dist/io/xlsx/data-validation.d.ts +12 -7
  171. package/dist/io/xlsx/data-validation.js +67 -53
  172. package/dist/io/xlsx/edit-vba.d.ts +7 -2
  173. package/dist/io/xlsx/edit-vba.js +52 -52
  174. package/dist/io/xlsx/font-xml.d.ts +7 -0
  175. package/dist/io/xlsx/font-xml.js +104 -0
  176. package/dist/io/xlsx/hyperlinks.d.ts +19 -21
  177. package/dist/io/xlsx/hyperlinks.js +12 -32
  178. package/dist/io/xlsx/images.d.ts +49 -16
  179. package/dist/io/xlsx/images.js +149 -50
  180. package/dist/io/xlsx/namespaces.d.ts +24 -0
  181. package/dist/io/xlsx/namespaces.js +11 -0
  182. package/dist/io/xlsx/package-plan.d.ts +60 -15
  183. package/dist/io/xlsx/package-plan.js +83 -18
  184. package/dist/io/xlsx/part-names.d.ts +6 -8
  185. package/dist/io/xlsx/part-names.js +3 -7
  186. package/dist/io/xlsx/pivot.d.ts +5 -5
  187. package/dist/io/xlsx/pivot.js +26 -24
  188. package/dist/io/xlsx/preserved-splices.d.ts +6 -0
  189. package/dist/io/xlsx/preserved-splices.js +74 -0
  190. package/dist/io/xlsx/read-comments.d.ts +29 -0
  191. package/dist/io/xlsx/read-comments.js +75 -0
  192. package/dist/io/xlsx/read-parts.d.ts +59 -0
  193. package/dist/io/xlsx/read-parts.js +259 -0
  194. package/dist/io/xlsx/read-pivot.js +11 -4
  195. package/dist/io/xlsx/{rich-runs.d.ts → read-rich-runs.d.ts} +8 -5
  196. package/dist/io/xlsx/{rich-runs.js → read-rich-runs.js} +24 -4
  197. package/dist/io/xlsx/read-rows.d.ts +35 -11
  198. package/dist/io/xlsx/read-rows.js +73 -49
  199. package/dist/io/xlsx/read-shared-strings.js +3 -2
  200. package/dist/io/xlsx/read-styles.d.ts +0 -30
  201. package/dist/io/xlsx/read-styles.js +45 -105
  202. package/dist/io/xlsx/read-workbook-xml.d.ts +42 -0
  203. package/dist/io/xlsx/read-workbook-xml.js +175 -0
  204. package/dist/io/xlsx/read-worksheet.d.ts +9 -3
  205. package/dist/io/xlsx/read-worksheet.js +70 -177
  206. package/dist/io/xlsx/read.d.ts +3 -14
  207. package/dist/io/xlsx/read.js +74 -411
  208. package/dist/io/xlsx/relationships.d.ts +3 -0
  209. package/dist/io/xlsx/relationships.js +4 -0
  210. package/dist/io/xlsx/rich-text.js +1 -1
  211. package/dist/io/xlsx/row-position.d.ts +20 -0
  212. package/dist/io/xlsx/row-position.js +11 -0
  213. package/dist/io/xlsx/row-xml.d.ts +139 -0
  214. package/dist/io/xlsx/row-xml.js +222 -0
  215. package/dist/io/xlsx/shared-formulas.js +2 -2
  216. package/dist/io/xlsx/sheet-properties.d.ts +10 -1
  217. package/dist/io/xlsx/sheet-properties.js +182 -20
  218. package/dist/io/xlsx/strict-parts.d.ts +7 -0
  219. package/dist/io/xlsx/strict-parts.js +171 -0
  220. package/dist/io/xlsx/style-elements.d.ts +41 -0
  221. package/dist/io/xlsx/style-elements.js +136 -0
  222. package/dist/io/xlsx/styles.d.ts +17 -3
  223. package/dist/io/xlsx/styles.js +13 -209
  224. package/dist/io/xlsx/tables.d.ts +10 -2
  225. package/dist/io/xlsx/tables.js +55 -52
  226. package/dist/io/xlsx/theme-xml.d.ts +32 -4
  227. package/dist/io/xlsx/theme-xml.js +106 -71
  228. package/dist/io/xlsx/threaded-comments.d.ts +14 -11
  229. package/dist/io/xlsx/threaded-comments.js +16 -8
  230. package/dist/io/xlsx/workbook-xml.d.ts +45 -6
  231. package/dist/io/xlsx/workbook-xml.js +62 -54
  232. package/dist/io/xlsx/worksheet-xml.d.ts +26 -70
  233. package/dist/io/xlsx/worksheet-xml.js +69 -282
  234. package/dist/io/xlsx/write-stream.d.ts +90 -19
  235. package/dist/io/xlsx/write-stream.js +80 -25
  236. package/dist/io/xlsx/write.d.ts +12 -6
  237. package/dist/io/xlsx/write.js +226 -123
  238. package/dist/rel-type.d.ts +25 -0
  239. package/dist/rel-type.js +9 -0
  240. package/dist/token-set.d.ts +13 -0
  241. package/dist/token-set.js +4 -1
  242. package/dist/vba/bytes.d.ts +16 -5
  243. package/dist/vba/bytes.js +27 -6
  244. package/dist/vba/cfb-format.d.ts +55 -0
  245. package/dist/vba/cfb-format.js +35 -0
  246. package/dist/vba/cfb-writer.d.ts +8 -0
  247. package/dist/vba/cfb-writer.js +40 -40
  248. package/dist/vba/cfb.d.ts +21 -4
  249. package/dist/vba/cfb.js +124 -90
  250. package/dist/vba/codepage.js +2 -1
  251. package/dist/vba/dir-records.d.ts +6 -0
  252. package/dist/vba/dir-records.js +10 -2
  253. package/dist/vba/errors.d.ts +11 -4
  254. package/dist/vba/index.d.ts +0 -2
  255. package/dist/vba/index.js +0 -2
  256. package/dist/vba/ms-ovba.d.ts +6 -2
  257. package/dist/vba/ms-ovba.js +66 -22
  258. package/dist/vba/project-editor.js +129 -119
  259. package/dist/vba/project.d.ts +15 -1
  260. package/dist/vba/project.js +38 -13
  261. package/dist/vba/vba-encoding.d.ts +4 -10
  262. package/dist/vba/vba-encoding.js +3 -17
  263. package/dist/xml/xml-attrs.d.ts +37 -0
  264. package/dist/xml/xml-attrs.js +30 -0
  265. package/dist/xml/xml-chars.d.ts +40 -0
  266. package/dist/xml/xml-chars.js +12 -0
  267. package/dist/xml/xml-namespaces.d.ts +31 -0
  268. package/dist/xml/xml-namespaces.js +50 -0
  269. package/dist/xml/xml-read.d.ts +101 -11
  270. package/dist/xml/xml-read.js +150 -18
  271. package/dist/xml/xml-scan.d.ts +25 -34
  272. package/dist/xml/xml-scan.js +69 -46
  273. package/dist/xml/xml.d.ts +59 -26
  274. package/dist/xml/xml.js +45 -23
  275. package/package.json +15 -9
  276. package/skills/ts-xlsx-upstream/SKILL.md +21 -13
  277. package/dist/core/modern-functions.d.ts +0 -1
@@ -1,43 +1,62 @@
1
- import { decodeRange, encodeAddress, tryDecodeCellRef } from './address.js';
1
+ import { boundedRect, decodeRange, encodeAddress, encodeRange, encodeRect, tryDecodeCellRef, tryDecodeRange, } from './address.js';
2
2
  import { shiftAutoFilter } from './autofilter.js';
3
3
  import { Cell, copyCellContent } from './cell.js';
4
4
  import { replaceContents } from './containers.js';
5
- import { isDeletedSpan, shiftIndex } from './grid-shift.js';
5
+ import { spliceFormula, translateFormula } from './formula-references.js';
6
+ import { isDeletedSpan, shiftIndex, shiftPoint, shiftRect } from './grid-shift.js';
6
7
  import { isOneCellAnchor } from './image.js';
7
- import { isSharedFormulaValue } from './value.js';
8
+ import { INTERNAL } from './internal.js';
9
+ import { splicePivotSource } from './pivot-table.js';
10
+ import { positionalPlacements } from './row-input.js';
11
+ import { isArrayFormulaValue, isDataTableFormulaValue, isFormulaValue, isSharedFormulaValue, } from './value.js';
8
12
  export class GridEdits {
9
13
  #rows;
10
14
  #rowProperties;
11
15
  #columns;
12
16
  #merges;
13
- #mergeRects;
14
17
  #tables;
18
+ #pivotTables;
19
+ #loadedPivotTables;
15
20
  #images;
16
21
  #dataValidations;
17
22
  #conditionalFormattings;
23
+ #hyperlinks;
18
24
  #comments;
25
+ #rowBreaks;
26
+ #columnBreaks;
19
27
  #autoFilter;
28
+ #sheetName;
29
+ #formulaHost;
20
30
  constructor(storage) {
21
31
  this.#rows = storage.rows;
22
32
  this.#rowProperties = storage.rowProperties;
23
33
  this.#columns = storage.columns;
24
34
  this.#merges = storage.merges;
25
- this.#mergeRects = storage.mergeRects;
26
35
  this.#tables = storage.tables;
36
+ this.#pivotTables = storage.pivotTables;
37
+ this.#loadedPivotTables = storage.loadedPivotTables;
27
38
  this.#images = storage.images;
28
39
  this.#dataValidations = storage.dataValidations;
29
40
  this.#conditionalFormattings = storage.conditionalFormattings;
41
+ this.#hyperlinks = storage.hyperlinks;
30
42
  this.#comments = storage.comments;
43
+ this.#rowBreaks = storage.rowBreaks;
44
+ this.#columnBreaks = storage.columnBreaks;
31
45
  this.#autoFilter = storage.autoFilter;
46
+ this.#sheetName = storage.sheetName;
47
+ this.#formulaHost = storage.formulaHost;
32
48
  }
33
49
  spliceRows(start, count, inserted) {
34
- const delta = inserted.length - count;
50
+ const splice = { axis: 'row', start, count, delta: inserted.length - count };
51
+ this.#moveFormulaReferences(splice);
35
52
  const shifted = new Map();
36
53
  for (const [row, cols] of this.#rows) {
37
54
  if (row < start)
38
55
  shifted.set(row, cols);
39
- else if (row >= start + count)
40
- shifted.set(row + delta, this.#relocateRow(cols, row + delta));
56
+ else if (row >= start + count) {
57
+ const dest = shiftIndex(row, splice);
58
+ shifted.set(dest, this.#relocateRow(cols, dest));
59
+ }
41
60
  }
42
61
  inserted.forEach((cols, i) => {
43
62
  shifted.set(start + i, this.#relocateRow(cols, start + i));
@@ -45,15 +64,12 @@ export class GridEdits {
45
64
  this.#rows.clear();
46
65
  for (const [row, cols] of shifted)
47
66
  this.#rows.set(row, cols);
48
- this.#shiftLineProperties(this.#rowProperties, start, count, delta);
49
- this.#shiftMerges('row', start, count, delta);
50
- this.#shiftTables('row', start, count, delta);
51
- this.#shiftImages('row', start, count, delta);
52
- this.#reanchorSharedFormulas('row', start, count, delta);
53
- this.#shiftRangeBoundOverlays('row', start, count, delta);
67
+ this.#shiftAnchored(splice, this.#rowProperties);
54
68
  }
55
69
  spliceColumns(start, count, inserts) {
56
- const delta = inserts.length - count;
70
+ const splice = { axis: 'col', start, count, delta: inserts.length - count };
71
+ this.#moveFormulaReferences(splice);
72
+ const shiftedRows = new Map();
57
73
  for (const [row, cols] of this.#rows) {
58
74
  const shifted = new Map();
59
75
  for (const [col, cell] of cols) {
@@ -61,38 +77,141 @@ export class GridEdits {
61
77
  shifted.set(col, cell);
62
78
  }
63
79
  else if (col >= start + count) {
64
- const dest = col + delta;
80
+ const dest = shiftIndex(col, splice);
65
81
  const moved = new Cell(row, dest);
66
82
  copyCellContent(cell, moved);
67
83
  shifted.set(dest, moved);
68
84
  }
69
85
  }
70
- inserts.forEach((values, i) => {
71
- const value = values[row - 1];
72
- if (value !== undefined) {
73
- const cell = new Cell(row, start + i);
74
- cell.value = value;
75
- shifted.set(start + i, cell);
86
+ shiftedRows.set(row, shifted);
87
+ }
88
+ inserts.forEach((values, i) => {
89
+ for (const [row, value] of positionalPlacements(values)) {
90
+ const cell = new Cell(row, start + i);
91
+ cell.value = value;
92
+ let cols = shiftedRows.get(row);
93
+ if (cols === undefined) {
94
+ cols = new Map();
95
+ shiftedRows.set(row, cols);
96
+ }
97
+ cols.set(start + i, cell);
98
+ }
99
+ });
100
+ for (const [row, cols] of shiftedRows)
101
+ this.#rows.set(row, cols);
102
+ this.#shiftAnchored(splice, this.#columns);
103
+ }
104
+ #moveFormulaReferences(splice) {
105
+ const edit = { sheet: this.#sheetName(), splice };
106
+ this.spliceFormulas(edit);
107
+ this.#formulaHost()?.(edit);
108
+ }
109
+ spliceFormulas(edit) {
110
+ const home = this.#sheetName();
111
+ const { splice } = edit;
112
+ const rewrite = (formula) => spliceFormula(formula, home, edit);
113
+ const cellsMove = home.toLowerCase() === edit.sheet.toLowerCase();
114
+ const after = (cell) => {
115
+ if (!cellsMove)
116
+ return cell;
117
+ return splice.axis === 'row'
118
+ ? { col: cell.col, row: shiftIndex(cell.row, splice) }
119
+ : { col: shiftIndex(cell.col, splice), row: cell.row };
120
+ };
121
+ const taken = (cell) => {
122
+ const line = splice.axis === 'row' ? cell.row : cell.col;
123
+ return cellsMove && isDeletedSpan(line, line, splice);
124
+ };
125
+ const masters = new Map();
126
+ for (const cols of this.#rows.values()) {
127
+ for (const cell of cols.values()) {
128
+ const value = cell.value;
129
+ if (isArrayFormulaValue(value)) {
130
+ const rewritten = rewrite(value.formula);
131
+ if (rewritten !== value.formula)
132
+ cell.value = { ...value, formula: rewritten };
133
+ continue;
76
134
  }
77
- });
78
- this.#rows.set(row, shifted);
135
+ if (!isFormulaValue(value))
136
+ continue;
137
+ masters.set(encodeAddress(cell.col, cell.row), {
138
+ cell,
139
+ formula: value.formula,
140
+ rewritten: rewrite(value.formula),
141
+ });
142
+ }
79
143
  }
80
- this.#shiftLineProperties(this.#columns, start, count, delta);
81
- this.#shiftMerges('col', start, count, delta);
82
- this.#shiftTables('col', start, count, delta);
83
- this.#shiftImages('col', start, count, delta);
84
- this.#reanchorSharedFormulas('col', start, count, delta);
85
- this.#shiftRangeBoundOverlays('col', start, count, delta);
86
- }
87
- #shiftRangeBoundOverlays(axis, start, count, delta) {
88
- this.#dataValidations.shift(axis, start, count, delta);
89
- this.#conditionalFormattings.shift(axis, start, count, delta);
90
- this.#comments.shift(axis, start, count, delta);
91
- const filter = this.#autoFilter.get();
92
- if (filter !== undefined) {
93
- this.#autoFilter.set(shiftAutoFilter(filter, axis, start, count, delta));
144
+ for (const cols of this.#rows.values()) {
145
+ for (const cell of cols.values()) {
146
+ const value = cell.value;
147
+ if (!isSharedFormulaValue(value))
148
+ continue;
149
+ const master = masters.get(value.sharedFormula);
150
+ if (master === undefined) {
151
+ if (value.formula === undefined)
152
+ continue;
153
+ const rewritten = rewrite(value.formula);
154
+ if (rewritten !== value.formula)
155
+ cell.value = { ...value, formula: rewritten };
156
+ continue;
157
+ }
158
+ const source = value.formula ??
159
+ translateFormula(master.formula, cell.col - master.cell.col, cell.row - master.cell.row);
160
+ const rewritten = rewrite(source);
161
+ const from = after(master.cell);
162
+ const to = after(cell);
163
+ const described = !taken(master.cell) &&
164
+ translateFormula(master.rewritten, to.col - from.col, to.row - from.row) === rewritten;
165
+ if (described) {
166
+ if (value.formula !== undefined && rewritten !== value.formula) {
167
+ cell.value = { ...value, formula: rewritten };
168
+ }
169
+ continue;
170
+ }
171
+ cell.value =
172
+ value.result === undefined
173
+ ? { formula: rewritten }
174
+ : { formula: rewritten, result: value.result };
175
+ }
176
+ }
177
+ for (const { cell, formula, rewritten } of masters.values()) {
178
+ const value = cell.value;
179
+ if (rewritten !== formula && isFormulaValue(value))
180
+ cell.value = { ...value, formula: rewritten };
181
+ }
182
+ this.#dataValidations.mapFormulas(rewrite);
183
+ this.#conditionalFormattings.mapFormulas(rewrite);
184
+ for (const table of this.#tables)
185
+ table[INTERNAL].rewriteFormulas(rewrite);
186
+ for (const pivot of this.#pivotTables)
187
+ pivot[INTERNAL].spliceSource(edit);
188
+ for (const [index, loaded] of this.#loadedPivotTables.entries()) {
189
+ const { source } = loaded;
190
+ if (source.kind !== 'worksheet' || source.inAnotherWorkbook)
191
+ continue;
192
+ const ref = splicePivotSource(source.sheet, source.ref, edit);
193
+ if (ref !== source.ref)
194
+ this.#loadedPivotTables[index] = { ...loaded, source: { ...source, ref } };
94
195
  }
95
196
  }
197
+ #shiftAnchored(splice, lineProperties) {
198
+ this.#shiftLineProperties(lineProperties, splice);
199
+ this.#shiftPageBreaks(splice);
200
+ this.#shiftMerges(splice);
201
+ this.#shiftTables(splice);
202
+ this.#shiftImages(splice);
203
+ this.#reanchorValueReferences(splice);
204
+ this.#shiftRangeBoundOverlays(splice);
205
+ }
206
+ #shiftRangeBoundOverlays(splice) {
207
+ this.#dataValidations.shift(splice);
208
+ this.#conditionalFormattings.shift(splice);
209
+ this.#hyperlinks.shift(splice);
210
+ this.#comments.shift(splice);
211
+ const filter = this.#autoFilter.get();
212
+ if (filter !== undefined)
213
+ this.#autoFilter.set(shiftAutoFilter(filter, splice));
214
+ }
96
215
  #relocateRow(cols, destRow) {
97
216
  const moved = new Map();
98
217
  for (const [col, cell] of cols) {
@@ -107,80 +226,128 @@ export class GridEdits {
107
226
  }
108
227
  return moved;
109
228
  }
110
- #reanchorSharedFormulas(axis, start, count, delta) {
229
+ #reanchorValueReferences(splice) {
111
230
  for (const cols of this.#rows.values()) {
112
231
  for (const cell of cols.values()) {
113
232
  const value = cell.value;
114
- if (!isSharedFormulaValue(value))
115
- continue;
116
- const master = tryDecodeCellRef(value.sharedFormula);
117
- if (master === undefined)
118
- continue;
119
- const anchored = axis === 'row'
120
- ? encodeAddress(master.col, shiftIndex(master.row, start, count, delta, 'row'))
121
- : encodeAddress(shiftIndex(master.col, start, count, delta, 'col'), master.row);
122
- if (anchored === value.sharedFormula)
123
- continue;
124
- const reanchored = { ...value, sharedFormula: anchored };
125
- cell.value = reanchored;
233
+ const moved = reanchoredValue(value, splice);
234
+ if (moved !== value)
235
+ cell.value = moved;
126
236
  }
127
237
  }
128
238
  }
129
- #shiftLineProperties(map, start, count, delta) {
239
+ #shiftLineProperties(map, splice) {
130
240
  const shifted = new Map();
131
241
  for (const [index, value] of map) {
132
- if (index < start)
133
- shifted.set(index, value);
134
- else if (index >= start + count)
135
- shifted.set(index + delta, value);
242
+ if (isDeletedSpan(index, index, splice))
243
+ continue;
244
+ shifted.set(shiftIndex(index, splice), value);
136
245
  }
137
246
  map.clear();
138
247
  for (const [index, value] of shifted)
139
248
  map.set(index, value);
140
249
  }
141
- #shiftMerges(axis, start, count, delta) {
142
- const shift = (v) => shiftIndex(v, start, count, delta, axis);
250
+ #shiftPageBreaks(splice) {
251
+ const breaks = splice.axis === 'row' ? this.#rowBreaks : this.#columnBreaks;
252
+ const moved = breaks.flatMap((brk) => {
253
+ const line = brk.id + 1;
254
+ if (isDeletedSpan(line, line, splice))
255
+ return [];
256
+ const id = shiftIndex(line, splice) - 1;
257
+ return [id === brk.id ? brk : { ...brk, id }];
258
+ });
259
+ replaceContents(breaks, moved);
260
+ }
261
+ #shiftMerges(splice) {
143
262
  const merges = [];
144
263
  const rects = [];
145
- for (const range of this.#merges) {
146
- const { top, left, bottom, right } = decodeRange(range);
147
- if (top === undefined || left === undefined || bottom === undefined || right === undefined) {
264
+ for (const range of this.#merges.ranges) {
265
+ const decoded = boundedRect(decodeRange(range));
266
+ if (decoded === undefined) {
148
267
  merges.push(range);
149
268
  continue;
150
269
  }
151
- const [lo, hi] = axis === 'row' ? [top, bottom] : [left, right];
152
- if (isDeletedSpan(lo, hi, start, count))
270
+ const rect = shiftRect(decoded, splice);
271
+ if (rect === undefined)
153
272
  continue;
154
- const rect = axis === 'row'
155
- ? { top: shift(top), left, bottom: shift(bottom), right }
156
- : { top, left: shift(left), bottom, right: shift(right) };
157
273
  rects.push(rect);
158
- merges.push(`${encodeAddress(rect.left, rect.top)}:${encodeAddress(rect.right, rect.bottom)}`);
274
+ merges.push(encodeRect(rect));
159
275
  }
160
- replaceContents(this.#merges, merges);
161
- replaceContents(this.#mergeRects, rects);
276
+ this.#merges.replaceAll(merges, rects);
162
277
  }
163
- #shiftTables(axis, start, count, delta) {
164
- const survivors = this.#tables.filter((table) => axis === 'row'
165
- ? table.shiftRows(start, count, delta)
166
- : table.shiftColumns(start, count, delta));
278
+ #shiftTables(splice) {
279
+ const survivors = this.#tables.filter((table) => splice.axis === 'row' ? table.shiftRows(splice) : table.shiftColumns(splice));
167
280
  replaceContents(this.#tables, survivors);
168
281
  }
169
- #shiftImages(axis, start, count, delta) {
170
- const shiftPoint = (point) => {
171
- const zeroBased = axis === 'row' ? point.row : point.col;
172
- const shifted = shiftIndex(zeroBased + 1, start, count, delta, axis) - 1;
282
+ #shiftImages(splice) {
283
+ const shiftAnchor = (point) => {
284
+ const zeroBased = splice.axis === 'row' ? point.row : point.col;
285
+ const shifted = shiftIndex(zeroBased + 1, splice) - 1;
173
286
  if (shifted === zeroBased)
174
287
  return point;
175
- return axis === 'row' ? { ...point, row: shifted } : { ...point, col: shifted };
288
+ return splice.axis === 'row' ? { ...point, row: shifted } : { ...point, col: shifted };
176
289
  };
177
290
  const moved = this.#images.map((image) => {
178
- const from = shiftPoint(image.anchor.from);
291
+ const from = shiftAnchor(image.anchor.from);
179
292
  const anchor = isOneCellAnchor(image.anchor)
180
293
  ? { ...image.anchor, from }
181
- : { ...image.anchor, from, to: shiftPoint(image.anchor.to) };
182
- return { imageId: image.imageId, anchor };
294
+ : { ...image.anchor, from, to: shiftAnchor(image.anchor.to) };
295
+ return { ...image, anchor };
183
296
  });
184
297
  replaceContents(this.#images, moved);
185
298
  }
186
299
  }
300
+ function reanchoredValue(value, splice) {
301
+ if (isSharedFormulaValue(value)) {
302
+ const master = tryDecodeCellRef(value.sharedFormula);
303
+ if (master === undefined)
304
+ return value;
305
+ const anchored = splice.axis === 'row'
306
+ ? encodeAddress(master.col, shiftIndex(master.row, splice))
307
+ : encodeAddress(shiftIndex(master.col, splice), master.row);
308
+ return anchored === value.sharedFormula ? value : { ...value, sharedFormula: anchored };
309
+ }
310
+ if (isArrayFormulaValue(value)) {
311
+ const ref = shiftedRange(value.ref, splice) ?? value.ref;
312
+ return ref === value.ref ? value : { ...value, ref };
313
+ }
314
+ if (isDataTableFormulaValue(value)) {
315
+ const ref = shiftedRange(value.ref, splice) ?? value.ref;
316
+ const r1 = inputAfter(value.r1, value.r1Deleted, splice);
317
+ const r2 = inputAfter(value.r2, value.r2Deleted, splice);
318
+ if (ref === value.ref && r1.same && r2.same)
319
+ return value;
320
+ return {
321
+ ...value,
322
+ ref,
323
+ ...(r1.ref === undefined ? {} : { r1: r1.ref }),
324
+ ...(r2.ref === undefined ? {} : { r2: r2.ref }),
325
+ ...(r1.deleted ? { r1Deleted: true } : {}),
326
+ ...(r2.deleted ? { r2Deleted: true } : {}),
327
+ };
328
+ }
329
+ return value;
330
+ }
331
+ function shiftedRange(ref, splice) {
332
+ const decoded = tryDecodeRange(ref);
333
+ const rect = decoded === undefined ? undefined : boundedRect(decoded);
334
+ if (rect === undefined)
335
+ return ref;
336
+ const moved = shiftRect(rect, splice);
337
+ if (moved === undefined)
338
+ return undefined;
339
+ return sameRect(moved, rect) ? ref : encodeRange(moved);
340
+ }
341
+ function inputAfter(ref, deleted, splice) {
342
+ const cell = ref === undefined || deleted === true ? undefined : tryDecodeCellRef(ref);
343
+ if (cell === undefined)
344
+ return { ref, deleted: deleted === true, same: true };
345
+ const moved = shiftPoint(cell, splice);
346
+ if (moved === undefined)
347
+ return { ref, deleted: true, same: false };
348
+ const same = moved.row === cell.row && moved.col === cell.col;
349
+ return { ref: same ? ref : encodeAddress(moved.col, moved.row), deleted: false, same };
350
+ }
351
+ function sameRect(a, b) {
352
+ return a.top === b.top && a.left === b.left && a.bottom === b.bottom && a.right === b.right;
353
+ }
@@ -1,19 +1,68 @@
1
+ import { type CellPosition, type GridRect } from './address.ts';
1
2
  /**
2
- * Where a 1-based coordinate on `axis` lands after `count` lines are deleted at `start` and the
3
- * replacement lines shift what follows by `delta`: before the edit it stays put, at or after the
4
- * edited span it shifts, and inside a deleted span it clamps to the cut line. The clamp is the best
5
- * effort for a geometry straddling the cut; a caller that must instead *drop* what a delete swallowed
6
- * whole tests {@link isDeletedSpan} first.
3
+ * One structural edit to an axis: `count` lines removed at the 1-based `start`, with everything
4
+ * after the removed span moved by `delta`. `delta` is the *net* movement, so a pure insert of two
5
+ * lines is `{count: 0, delta: 2}` and a pure delete of three is `{count: 3, delta: -3}`.
6
+ *
7
+ * The four travel as one value because they describe one edit, and because three of them are
8
+ * `number`: passed positionally, two adjacent ones transposed compiles, typechecks, lints, and moves
9
+ * a merge, a dropdown, a highlight or a comment anchor onto cells the author never chose -- silently,
10
+ * since nothing about the resulting file is malformed. A named field cannot be transposed.
11
+ */
12
+ export interface AxisSplice {
13
+ /** The axis the lines were spliced on. */
14
+ readonly axis: 'row' | 'col';
15
+ /** 1-based first line of the removed span. */
16
+ readonly start: number;
17
+ /** How many lines were removed. */
18
+ readonly count: number;
19
+ /** Net movement of every line after the removed span. */
20
+ readonly delta: number;
21
+ }
22
+ /**
23
+ * Where a 1-based coordinate on the spliced axis lands: before the edit it stays put, at or after
24
+ * the edited span it shifts by `delta`, and inside a deleted span it clamps to the cut line. The
25
+ * clamp is the best effort for a geometry straddling the cut; a caller that must instead *drop*
26
+ * what a delete swallowed whole tests {@link isDeletedSpan} first.
7
27
  *
8
28
  * A shift never leaves the grid: the result is clamped to the axis's last line, so a region already
9
29
  * touching the bottom (or the right edge) keeps its edge there instead of naming a line the format
10
30
  * has no room for. That shrinks such a region by what it could not move, which is the lesser of the
11
31
  * two evils and is what Excel does to the same region on the same edit.
12
32
  */
13
- export declare function shiftIndex(v: number, start: number, count: number, delta: number, axis: 'row' | 'col'): number;
33
+ export declare function shiftIndex(v: number, splice: AxisSplice): number;
14
34
  /**
15
- * Whether the inclusive span `lo..hi` lies entirely within the `count` lines deleted at `start`: the
16
- * test that separates "this moved" from "this is gone". A single coordinate is the degenerate span
35
+ * Whether the inclusive span `lo..hi` lies entirely within the deleted lines: the test that
36
+ * separates "this moved" from "this is gone". A single coordinate is the degenerate span
17
37
  * `lo === hi`.
18
38
  */
19
- export declare function isDeletedSpan(lo: number, hi: number, start: number, count: number): boolean;
39
+ export declare function isDeletedSpan(lo: number, hi: number, splice: AxisSplice): boolean;
40
+ /**
41
+ * Both edges of a region's span on the spliced axis, moved together, or `undefined` when the delete
42
+ * swallowed the span whole. The two answers are exclusive on purpose: a span straddling the cut
43
+ * clamps and survives, one wholly inside it is dropped, and a caller must not clamp its way out of
44
+ * a drop.
45
+ *
46
+ * The one-axis form, for a region whose *other* axis is not a number the caller holds -- a `sqref`
47
+ * area may be unbounded across the axis it is not being spliced on, and re-encoding it as bounded
48
+ * would rewrite the file's own spelling. Callers holding a real rectangle want {@link shiftRect}.
49
+ */
50
+ export declare function shiftSpan(lo: number, hi: number, splice: AxisSplice): {
51
+ lo: number;
52
+ hi: number;
53
+ } | undefined;
54
+ /**
55
+ * A rectangle re-anchored through the splice, or `undefined` when the delete swallowed it whole.
56
+ * The unspliced axis passes through untouched; both edges of the spliced axis move and clamp.
57
+ *
58
+ * This projection -- pick the spliced axis's two edges out of the rectangle, ask whether they
59
+ * survived, move them, put the rectangle back together -- is the shape every range-bound thing in
60
+ * the model re-anchors by, and writing it per caller is how the axis ternary gets inverted in one
61
+ * of them.
62
+ */
63
+ export declare function shiftRect(rect: GridRect, splice: AxisSplice): GridRect | undefined;
64
+ /**
65
+ * The degenerate one-coordinate form of {@link shiftRect}: a cell that moves, or `undefined` when
66
+ * the delete took the line it sat on. The coordinate off the spliced axis passes through.
67
+ */
68
+ export declare function shiftPoint(point: CellPosition, splice: AxisSplice): CellPosition | undefined;
@@ -1,8 +1,30 @@
1
1
  import { MAX_COLUMN, MAX_ROW } from './address.js';
2
- export function shiftIndex(v, start, count, delta, axis) {
2
+ export function shiftIndex(v, splice) {
3
+ const { start, count, delta } = splice;
3
4
  const moved = v < start ? v : v >= start + count ? v + delta : start;
4
- return Math.min(moved, axis === 'row' ? MAX_ROW : MAX_COLUMN);
5
+ return Math.min(moved, splice.axis === 'row' ? MAX_ROW : MAX_COLUMN);
5
6
  }
6
- export function isDeletedSpan(lo, hi, start, count) {
7
- return lo >= start && hi < start + count;
7
+ export function isDeletedSpan(lo, hi, splice) {
8
+ return lo >= splice.start && hi < splice.start + splice.count;
9
+ }
10
+ export function shiftSpan(lo, hi, splice) {
11
+ if (isDeletedSpan(lo, hi, splice))
12
+ return undefined;
13
+ return { lo: shiftIndex(lo, splice), hi: shiftIndex(hi, splice) };
14
+ }
15
+ export function shiftRect(rect, splice) {
16
+ const rowAxis = splice.axis === 'row';
17
+ const moved = shiftSpan(rowAxis ? rect.top : rect.left, rowAxis ? rect.bottom : rect.right, splice);
18
+ if (moved === undefined)
19
+ return undefined;
20
+ return rowAxis
21
+ ? { ...rect, top: moved.lo, bottom: moved.hi }
22
+ : { ...rect, left: moved.lo, right: moved.hi };
23
+ }
24
+ export function shiftPoint(point, splice) {
25
+ const line = splice.axis === 'row' ? point.row : point.col;
26
+ if (isDeletedSpan(line, line, splice))
27
+ return undefined;
28
+ const moved = shiftIndex(line, splice);
29
+ return splice.axis === 'row' ? { col: point.col, row: moved } : { col: moved, row: point.row };
8
30
  }
@@ -0,0 +1,39 @@
1
+ import { type AxisSplice } from './grid-shift.ts';
2
+ /** A hyperlink and the cells it covers. */
3
+ export interface Hyperlink {
4
+ /** The cells the link covers: one cell (`'B2'`) or a rectangle of cells (`'D1:H1'`). */
5
+ readonly ref: string;
6
+ /**
7
+ * Where the link goes: a URL or a path, or a `#`-prefixed place in this workbook such as
8
+ * `#Summary!A1` or `#TaxRate`.
9
+ */
10
+ readonly target: string;
11
+ /** The text shown when the pointer rests on the link. */
12
+ readonly tooltip?: string;
13
+ }
14
+ /** The links on one sheet, in the order they were added, each with the rectangle it covers. */
15
+ export declare class HyperlinkOverlay {
16
+ #private;
17
+ /**
18
+ * Add a link, replacing one over the same cells. The `ref` is stored in its canonical spelling.
19
+ *
20
+ * @throws {SyntaxError} if `ref` is not a reference.
21
+ * @throws {AuthoringError} if `ref` names a whole row or column rather than cells.
22
+ */
23
+ add(link: Hyperlink): void;
24
+ /** Remove the link whose `ref` names exactly these cells, and report whether there was one. */
25
+ remove(ref: string): boolean;
26
+ /** The links on the sheet, in the order they were added. */
27
+ get entries(): readonly Hyperlink[];
28
+ /** The link covering the 1-based `col`/`row`, the last added of any that do, or `undefined`. */
29
+ at(col: number, row: number): Hyperlink | undefined;
30
+ /**
31
+ * Move every link through a row or column splice. A link grows when lines are inserted inside it and
32
+ * shrinks when some of its lines are deleted, and one the delete takes whole goes with it.
33
+ */
34
+ shift(splice: AxisSplice): void;
35
+ /** Give row `to` a copy of every link that lies wholly within row `from`, as a row copy does. */
36
+ copyRow(from: number, to: number): void;
37
+ /** Drop every link, leaving the overlay empty. */
38
+ clear(): void;
39
+ }