@shbernal/ts-xlsx 2.1.0 → 3.1.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 (225) hide show
  1. package/README.md +4 -1
  2. package/dist/bytes.d.ts +7 -0
  3. package/dist/bytes.js +22 -0
  4. package/dist/core/address.d.ts +60 -2
  5. package/dist/core/address.js +40 -24
  6. package/dist/core/autofilter.d.ts +5 -4
  7. package/dist/core/autofilter.js +34 -35
  8. package/dist/core/axis-handle.d.ts +10 -0
  9. package/dist/core/axis-handle.js +17 -0
  10. package/dist/core/cell.d.ts +25 -0
  11. package/dist/core/cell.js +9 -3
  12. package/dist/core/clone.d.ts +29 -0
  13. package/dist/core/clone.js +24 -0
  14. package/dist/core/color-resolution.js +2 -4
  15. package/dist/core/column.d.ts +5 -1
  16. package/dist/core/column.js +34 -35
  17. package/dist/core/comment-thread.js +2 -1
  18. package/dist/core/conditional-formatting-overlay.d.ts +2 -1
  19. package/dist/core/conditional-formatting-overlay.js +2 -2
  20. package/dist/core/conditional-formatting.d.ts +14 -6
  21. package/dist/core/conditional-formatting.js +45 -43
  22. package/dist/core/containers.d.ts +12 -0
  23. package/dist/core/containers.js +5 -0
  24. package/dist/core/data-validation-overlay.d.ts +2 -1
  25. package/dist/core/data-validation-overlay.js +4 -4
  26. package/dist/core/data-validation.d.ts +9 -3
  27. package/dist/core/data-validation.js +21 -18
  28. package/dist/core/date-format.d.ts +20 -0
  29. package/dist/core/date-format.js +160 -0
  30. package/dist/core/date.d.ts +43 -8
  31. package/dist/core/date.js +18 -2
  32. package/dist/core/formula.d.ts +26 -0
  33. package/dist/core/formula.js +20 -4
  34. package/dist/core/grid-edits.d.ts +2 -3
  35. package/dist/core/grid-edits.js +69 -70
  36. package/dist/core/grid-shift.d.ts +58 -9
  37. package/dist/core/grid-shift.js +26 -4
  38. package/dist/core/image.d.ts +14 -10
  39. package/dist/core/image.js +9 -9
  40. package/dist/core/internal.d.ts +8 -2
  41. package/dist/core/limits.d.ts +23 -0
  42. package/dist/core/limits.js +4 -0
  43. package/dist/core/merge-index.d.ts +31 -0
  44. package/dist/core/merge-index.js +60 -0
  45. package/dist/core/merge.d.ts +5 -14
  46. package/dist/core/merge.js +18 -27
  47. package/dist/core/page-setup.d.ts +72 -2
  48. package/dist/core/page-setup.js +34 -8
  49. package/dist/core/pivot-table.d.ts +1 -1
  50. package/dist/core/pivot-table.js +67 -53
  51. package/dist/core/protection.js +2 -11
  52. package/dist/core/range.js +34 -30
  53. package/dist/core/row-input.js +2 -2
  54. package/dist/core/row.d.ts +5 -1
  55. package/dist/core/row.js +22 -23
  56. package/dist/core/style.d.ts +134 -75
  57. package/dist/core/style.js +112 -53
  58. package/dist/core/table-style.d.ts +1 -1
  59. package/dist/core/table-style.js +4 -4
  60. package/dist/core/table.d.ts +28 -10
  61. package/dist/core/table.js +54 -39
  62. package/dist/core/theme.js +6 -5
  63. package/dist/core/used-extent.d.ts +44 -0
  64. package/dist/core/used-extent.js +102 -0
  65. package/dist/core/value.d.ts +9 -0
  66. package/dist/core/value.js +36 -34
  67. package/dist/core/workbook-media.d.ts +34 -0
  68. package/dist/core/workbook-media.js +44 -0
  69. package/dist/core/workbook-styles.d.ts +67 -1
  70. package/dist/core/workbook-vba.js +3 -2
  71. package/dist/core/workbook.d.ts +23 -2
  72. package/dist/core/workbook.js +24 -40
  73. package/dist/core/worksheet-comments.d.ts +2 -1
  74. package/dist/core/worksheet-comments.js +20 -13
  75. package/dist/core/worksheet-merges.d.ts +44 -0
  76. package/dist/core/worksheet-merges.js +59 -0
  77. package/dist/core/worksheet-model.d.ts +9 -1
  78. package/dist/core/worksheet-model.js +24 -9
  79. package/dist/core/worksheet.d.ts +72 -12
  80. package/dist/core/worksheet.js +68 -70
  81. package/dist/customui/ribbon.js +6 -7
  82. package/dist/entries/core.d.ts +18 -13
  83. package/dist/entries/core.js +12 -6
  84. package/dist/entries/xlsx.d.ts +1 -1
  85. package/dist/errors.d.ts +64 -0
  86. package/dist/errors.js +22 -0
  87. package/dist/hex.d.ts +8 -0
  88. package/dist/hex.js +3 -0
  89. package/dist/io/csv/delimiter.d.ts +9 -0
  90. package/dist/io/csv/delimiter.js +6 -0
  91. package/dist/io/csv/read.js +11 -4
  92. package/dist/io/csv/write.d.ts +9 -2
  93. package/dist/io/csv/write.js +9 -38
  94. package/dist/io/opc/inflate.js +15 -1
  95. package/dist/io/opc/namespaces.d.ts +16 -0
  96. package/dist/io/opc/namespaces.js +4 -0
  97. package/dist/io/opc/part-paths.d.ts +10 -0
  98. package/dist/io/opc/part-paths.js +16 -1
  99. package/dist/io/opc/read-opc.d.ts +69 -10
  100. package/dist/io/opc/read-opc.js +49 -55
  101. package/dist/io/opc/read-options.d.ts +5 -1
  102. package/dist/io/opc/rels.d.ts +1 -6
  103. package/dist/io/opc/rels.js +1 -4
  104. package/dist/io/opc/sniff-format.d.ts +3 -3
  105. package/dist/io/opc/sniff-format.js +2 -2
  106. package/dist/io/style/xf-style.d.ts +48 -2
  107. package/dist/io/style/xf-style.js +14 -0
  108. package/dist/io/xlsb/formula.d.ts +2 -2
  109. package/dist/io/xlsb/formula.js +49 -34
  110. package/dist/io/xlsb/primitives.js +8 -14
  111. package/dist/io/xlsb/ptg-functions.d.ts +19 -10
  112. package/dist/io/xlsb/ptg-functions.js +385 -596
  113. package/dist/io/xlsb/read-styles.js +50 -83
  114. package/dist/io/xlsb/read-worksheet.d.ts +20 -1
  115. package/dist/io/xlsb/read-worksheet.js +73 -89
  116. package/dist/io/xlsb/read.d.ts +6 -3
  117. package/dist/io/xlsb/read.js +47 -29
  118. package/dist/io/xlsb/record-stream.d.ts +30 -0
  119. package/dist/io/xlsb/record-stream.js +20 -0
  120. package/dist/io/xlsb/record-types.d.ts +1 -5
  121. package/dist/io/xlsb/record-types.js +1 -5
  122. package/dist/io/xlsx/cell-accumulator.d.ts +9 -2
  123. package/dist/io/xlsx/cell-accumulator.js +32 -9
  124. package/dist/io/xlsx/cell-style-resolution.d.ts +25 -0
  125. package/dist/io/xlsx/cell-style-resolution.js +29 -0
  126. package/dist/io/xlsx/cell-value.d.ts +5 -3
  127. package/dist/io/xlsx/cell-value.js +13 -15
  128. package/dist/io/xlsx/color-xml.js +3 -2
  129. package/dist/io/xlsx/column-budget.d.ts +26 -0
  130. package/dist/io/xlsx/column-budget.js +22 -0
  131. package/dist/io/xlsx/comments.d.ts +6 -2
  132. package/dist/io/xlsx/comments.js +36 -31
  133. package/dist/io/xlsx/conditional-formatting.d.ts +9 -5
  134. package/dist/io/xlsx/conditional-formatting.js +51 -46
  135. package/dist/io/xlsx/data-validation.d.ts +9 -7
  136. package/dist/io/xlsx/data-validation.js +38 -35
  137. package/dist/io/xlsx/edit-vba.d.ts +7 -2
  138. package/dist/io/xlsx/edit-vba.js +15 -29
  139. package/dist/io/xlsx/font-xml.d.ts +7 -0
  140. package/dist/io/xlsx/font-xml.js +104 -0
  141. package/dist/io/xlsx/hyperlinks.d.ts +41 -7
  142. package/dist/io/xlsx/hyperlinks.js +40 -28
  143. package/dist/io/xlsx/images.js +24 -25
  144. package/dist/io/xlsx/namespaces.d.ts +16 -0
  145. package/dist/io/xlsx/namespaces.js +7 -0
  146. package/dist/io/xlsx/package-plan.d.ts +23 -3
  147. package/dist/io/xlsx/package-plan.js +66 -25
  148. package/dist/io/xlsx/part-names.d.ts +41 -0
  149. package/dist/io/xlsx/part-names.js +46 -0
  150. package/dist/io/xlsx/pivot.js +8 -7
  151. package/dist/io/xlsx/read-parts.d.ts +59 -0
  152. package/dist/io/xlsx/read-parts.js +252 -0
  153. package/dist/io/xlsx/read-pivot.js +3 -1
  154. package/dist/io/xlsx/read-repair.d.ts +26 -0
  155. package/dist/io/xlsx/read-repair.js +39 -0
  156. package/dist/io/xlsx/{rich-runs.d.ts → read-rich-runs.d.ts} +1 -1
  157. package/dist/io/xlsx/{rich-runs.js → read-rich-runs.js} +7 -3
  158. package/dist/io/xlsx/read-rows.d.ts +18 -5
  159. package/dist/io/xlsx/read-rows.js +50 -37
  160. package/dist/io/xlsx/read-shared-strings.js +5 -3
  161. package/dist/io/xlsx/read-styles.d.ts +1 -6
  162. package/dist/io/xlsx/read-styles.js +65 -109
  163. package/dist/io/xlsx/read-workbook-xml.d.ts +36 -0
  164. package/dist/io/xlsx/read-workbook-xml.js +170 -0
  165. package/dist/io/xlsx/read-worksheet.d.ts +11 -1
  166. package/dist/io/xlsx/read-worksheet.js +51 -193
  167. package/dist/io/xlsx/read.d.ts +5 -13
  168. package/dist/io/xlsx/read.js +111 -436
  169. package/dist/io/xlsx/rich-text.js +1 -1
  170. package/dist/io/xlsx/row-position.d.ts +18 -0
  171. package/dist/io/xlsx/row-position.js +11 -0
  172. package/dist/io/xlsx/row-xml.d.ts +128 -0
  173. package/dist/io/xlsx/row-xml.js +203 -0
  174. package/dist/io/xlsx/shared-formulas.js +2 -2
  175. package/dist/io/xlsx/sheet-properties.d.ts +10 -1
  176. package/dist/io/xlsx/sheet-properties.js +166 -37
  177. package/dist/io/xlsx/style-elements.d.ts +43 -0
  178. package/dist/io/xlsx/style-elements.js +156 -0
  179. package/dist/io/xlsx/styles.d.ts +12 -3
  180. package/dist/io/xlsx/styles.js +5 -204
  181. package/dist/io/xlsx/tables.js +11 -14
  182. package/dist/io/xlsx/theme-xml.d.ts +26 -4
  183. package/dist/io/xlsx/theme-xml.js +107 -69
  184. package/dist/io/xlsx/threaded-comments.d.ts +8 -10
  185. package/dist/io/xlsx/threaded-comments.js +3 -1
  186. package/dist/io/xlsx/workbook-xml.d.ts +54 -4
  187. package/dist/io/xlsx/workbook-xml.js +84 -57
  188. package/dist/io/xlsx/worksheet-xml.d.ts +23 -70
  189. package/dist/io/xlsx/worksheet-xml.js +59 -242
  190. package/dist/io/xlsx/write-stream.d.ts +71 -16
  191. package/dist/io/xlsx/write-stream.js +79 -41
  192. package/dist/io/xlsx/write.d.ts +6 -2
  193. package/dist/io/xlsx/write.js +217 -153
  194. package/dist/rel-type.d.ts +16 -0
  195. package/dist/rel-type.js +6 -0
  196. package/dist/token-set.d.ts +28 -0
  197. package/dist/token-set.js +7 -0
  198. package/dist/vba/bytes.d.ts +14 -5
  199. package/dist/vba/bytes.js +16 -6
  200. package/dist/vba/cfb-format.d.ts +32 -0
  201. package/dist/vba/cfb-format.js +13 -0
  202. package/dist/vba/cfb-writer.d.ts +9 -0
  203. package/dist/vba/cfb-writer.js +28 -25
  204. package/dist/vba/cfb.d.ts +13 -1
  205. package/dist/vba/cfb.js +73 -57
  206. package/dist/vba/codepage.js +2 -1
  207. package/dist/vba/errors.d.ts +7 -0
  208. package/dist/vba/ms-ovba.js +79 -23
  209. package/dist/vba/project-editor.js +35 -40
  210. package/dist/vba/project.d.ts +11 -1
  211. package/dist/vba/project.js +40 -21
  212. package/dist/vba/vba-encoding.js +5 -6
  213. package/dist/xml/xml-attrs.d.ts +37 -0
  214. package/dist/xml/xml-attrs.js +30 -0
  215. package/dist/xml/xml-chars.d.ts +40 -0
  216. package/dist/xml/xml-chars.js +12 -0
  217. package/dist/xml/xml-namespaces.d.ts +31 -0
  218. package/dist/xml/xml-namespaces.js +50 -0
  219. package/dist/xml/xml-read.d.ts +128 -81
  220. package/dist/xml/xml-read.js +161 -178
  221. package/dist/xml/xml-scan.d.ts +78 -0
  222. package/dist/xml/xml-scan.js +180 -0
  223. package/dist/xml/xml.d.ts +29 -28
  224. package/dist/xml/xml.js +24 -24
  225. package/package.json +13 -9
@@ -1,5 +1,7 @@
1
- import { AuthoringError, InternalError } from '../errors.js';
2
- import { encodeAddress } from './address.js';
1
+ import { AuthoringError, InternalError, quoted } from '../errors.js';
2
+ import { tokenSet } from '../token-set.js';
3
+ import { encodeRect } from './address.js';
4
+ import { INTERNAL } from './internal.js';
3
5
  import { isErrorValue, isFormulaValue, isHyperlinkValue, isRichTextValue, isSharedFormulaValue, richTextToPlain, } from './value.js';
4
6
  const PIVOT_METRICS = {
5
7
  sum: true,
@@ -14,23 +16,18 @@ const PIVOT_METRICS = {
14
16
  var: true,
15
17
  varp: true,
16
18
  };
17
- function isPivotMetric(value) {
18
- return Object.hasOwn(PIVOT_METRICS, value);
19
- }
19
+ const isPivotMetric = tokenSet(PIVOT_METRICS);
20
20
  export function pivotMetricFromSubtotal(subtotal) {
21
21
  if (subtotal === undefined)
22
22
  return 'sum';
23
23
  return isPivotMetric(subtotal) ? subtotal : 'sum';
24
24
  }
25
- const DECLARABLE_PIVOT_SOURCE_KINDS = {
25
+ export const isDeclarablePivotSourceKind = tokenSet({
26
26
  worksheet: true,
27
27
  external: true,
28
28
  consolidation: true,
29
29
  scenario: true,
30
- };
31
- export function isDeclarablePivotSourceKind(value) {
32
- return Object.hasOwn(DECLARABLE_PIVOT_SOURCE_KINDS, value);
33
- }
30
+ });
34
31
  const BLANK = { kind: 'blank' };
35
32
  export class PivotTable {
36
33
  metric;
@@ -44,7 +41,7 @@ export class PivotTable {
44
41
  constructor(options) {
45
42
  const metric = options.metric ?? 'sum';
46
43
  if (!Object.hasOwn(PIVOT_METRICS, metric)) {
47
- throw new AuthoringError(`unsupported pivot metric "${metric}": expected one of ${Object.keys(PIVOT_METRICS).join(', ')}`);
44
+ throw new AuthoringError(`unsupported pivot metric ${quoted(metric)}: expected one of ${Object.keys(PIVOT_METRICS).join(', ')}`);
48
45
  }
49
46
  this.metric = metric;
50
47
  const source = options.source;
@@ -53,51 +50,36 @@ export class PivotTable {
53
50
  if (columnCount < 1 || lastRow < 2) {
54
51
  throw new AuthoringError('a pivot source needs a header row and at least one data row');
55
52
  }
56
- const fields = [];
57
- for (let col = 1; col <= columnCount; col++) {
58
- const name = textOf(scalarOf(source.getCell(encodeAddress(col, 1)).value));
59
- if (name !== '')
60
- fields.push({ name, col });
61
- }
62
- if (fields.length === 0)
63
- throw new AuthoringError('the pivot source header row is empty');
64
- const resolve = (role, name) => {
65
- const index = fields.findIndex((field) => field.name === name);
66
- if (index < 0) {
67
- throw new AuthoringError(`pivot ${role} field "${name}" is not a column header in the source sheet`);
68
- }
69
- return index;
70
- };
71
- if (options.rows.length === 0)
72
- throw new AuthoringError('a pivot table needs at least one row field');
73
- if (options.columns.length === 0)
74
- throw new AuthoringError('a pivot table needs at least one column field');
75
- const [valueName, ...extraValues] = options.values;
76
- if (valueName === undefined || extraValues.length > 0) {
77
- throw new AuthoringError('a pivot table needs exactly one value field');
78
- }
79
- this.rowFields = options.rows.map((name) => resolve('row', name));
80
- this.columnFields = options.columns.map((name) => resolve('column', name));
81
- this.valueField = resolve('value', valueName);
82
- const axisFields = new Set([...this.rowFields, ...this.columnFields]);
53
+ const internals = source[INTERNAL];
54
+ const valueAt = (row, col) => internals.peekCell(row, col)?.value ?? null;
55
+ const fields = discoverFields(valueAt, columnCount);
56
+ const roles = resolveRoles(options, fields);
57
+ this.rowFields = roles.rowFields;
58
+ this.columnFields = roles.columnFields;
59
+ this.valueField = roles.valueField;
60
+ const axisFields = new Set([...roles.rowFields, ...roles.columnFields]);
83
61
  const firstField = fields[0];
84
- const lastField = fields[fields.length - 1];
62
+ const lastField = fields.at(-1);
85
63
  if (firstField === undefined || lastField === undefined) {
86
- throw new AuthoringError('the pivot source header row is empty');
64
+ throw new InternalError('pivot field list is empty after discoverFields guaranteed it is not');
87
65
  }
88
66
  this.sourceSheetName = source.name;
89
- this.sourceRef = `${encodeAddress(firstField.col, 1)}:${encodeAddress(lastField.col, lastRow)}`;
67
+ this.sourceRef = encodeRect({
68
+ top: 1,
69
+ left: firstField.col,
70
+ bottom: lastRow,
71
+ right: lastField.col,
72
+ });
90
73
  const dataRowCount = lastRow - 1;
91
74
  const columnScalars = fields.map((field) => {
92
75
  const scalars = [];
93
- for (let row = 2; row <= lastRow; row++) {
94
- scalars.push(scalarOf(source.getCell(encodeAddress(field.col, row)).value));
95
- }
76
+ for (let row = 2; row <= lastRow; row++)
77
+ scalars.push(scalarOf(valueAt(row, field.col)));
96
78
  return scalars;
97
79
  });
98
80
  const catalogues = fields.map(() => null);
99
81
  this.cacheFields = fields.map((field, fieldIndex) => {
100
- const scalars = scalarsForField(columnScalars, fieldIndex);
82
+ const scalars = columnScalars[fieldIndex] ?? missingColumn(fieldIndex);
101
83
  const containsBlank = scalars.some((scalar) => scalar.kind === 'blank');
102
84
  if (axisFields.has(fieldIndex)) {
103
85
  const items = [];
@@ -114,15 +96,18 @@ export class PivotTable {
114
96
  }
115
97
  return { name: field.name, sharedItems: null, numeric: numericSummary(scalars), containsBlank };
116
98
  });
99
+ const byField = fields.map((_field, fieldIndex) => ({
100
+ scalars: columnScalars[fieldIndex] ?? missingColumn(fieldIndex),
101
+ catalogue: catalogues[fieldIndex],
102
+ }));
117
103
  const records = [];
118
104
  for (let row = 0; row < dataRowCount; row++) {
119
- records.push(fields.map((_field, fieldIndex) => {
120
- const scalar = scalarsForField(columnScalars, fieldIndex)[row];
105
+ records.push(byField.map(({ scalars, catalogue }, fieldIndex) => {
106
+ const scalar = scalars[row];
121
107
  if (scalar === undefined) {
122
108
  throw new InternalError(`pivot record row ${row} is out of range for field ${fieldIndex}: every column was ` +
123
109
  'scanned for the same dataRowCount above, so this index is always in range');
124
110
  }
125
- const catalogue = catalogues[fieldIndex];
126
111
  if (!catalogue)
127
112
  return scalar;
128
113
  const index = catalogue.get(itemKey(scalar));
@@ -142,12 +127,41 @@ export class PivotTable {
142
127
  return field.name;
143
128
  }
144
129
  }
145
- function scalarsForField(columnScalars, fieldIndex) {
146
- const scalars = columnScalars[fieldIndex];
147
- if (scalars === undefined) {
148
- throw new InternalError(`pivot field index ${fieldIndex} is out of range for columnScalars: it was built from the same fields array`);
130
+ function discoverFields(valueAt, columnCount) {
131
+ const fields = [];
132
+ for (let col = 1; col <= columnCount; col++) {
133
+ const name = textOf(scalarOf(valueAt(1, col)));
134
+ if (name !== '')
135
+ fields.push({ name, col });
136
+ }
137
+ if (fields.length === 0)
138
+ throw new AuthoringError('the pivot source header row is empty');
139
+ return fields;
140
+ }
141
+ function resolveRoles(options, fields) {
142
+ const resolve = (role, name) => {
143
+ const index = fields.findIndex((field) => field.name === name);
144
+ if (index < 0) {
145
+ throw new AuthoringError(`pivot ${role} field ${quoted(name)} is not a column header in the source sheet`);
146
+ }
147
+ return index;
148
+ };
149
+ if (options.rows.length === 0)
150
+ throw new AuthoringError('a pivot table needs at least one row field');
151
+ if (options.columns.length === 0)
152
+ throw new AuthoringError('a pivot table needs at least one column field');
153
+ const [valueName, ...extraValues] = options.values;
154
+ if (valueName === undefined || extraValues.length > 0) {
155
+ throw new AuthoringError('a pivot table needs exactly one value field');
149
156
  }
150
- return scalars;
157
+ return {
158
+ rowFields: options.rows.map((name) => resolve('row', name)),
159
+ columnFields: options.columns.map((name) => resolve('column', name)),
160
+ valueField: resolve('value', valueName),
161
+ };
162
+ }
163
+ function missingColumn(fieldIndex) {
164
+ throw new InternalError(`pivot field index ${fieldIndex} is out of range for columnScalars: it was built from the same fields array`);
151
165
  }
152
166
  function itemKey(item) {
153
167
  switch (item.kind) {
@@ -1,4 +1,4 @@
1
- import { concat, toBase64 } from '../bytes.js';
1
+ import { concat, toBase64, utf16leBytes } from '../bytes.js';
2
2
  import { sha512 } from '../sha512.js';
3
3
  export const SHEET_PROTECTION_FLAGS = [
4
4
  { key: 'formatCells', defaultForbidden: true },
@@ -22,7 +22,7 @@ const DEFAULT_SPIN_COUNT = 100000;
22
22
  const SALT_BYTES = 16;
23
23
  export function deriveCredential(password, spinCount = DEFAULT_SPIN_COUNT) {
24
24
  const salt = crypto.getRandomValues(new Uint8Array(SALT_BYTES));
25
- let hash = sha512(concat([salt, utf16le(password)]));
25
+ let hash = sha512(concat([salt, utf16leBytes(password)]));
26
26
  const spun = new Uint8Array(hash.length + 4);
27
27
  const counter = new DataView(spun.buffer, hash.length, 4);
28
28
  for (let i = 0; i < spinCount; i++) {
@@ -37,12 +37,3 @@ export function deriveCredential(password, spinCount = DEFAULT_SPIN_COUNT) {
37
37
  spinCount,
38
38
  };
39
39
  }
40
- function utf16le(text) {
41
- const bytes = new Uint8Array(text.length * 2);
42
- for (let i = 0; i < text.length; i++) {
43
- const unit = text.charCodeAt(i);
44
- bytes[i * 2] = unit & 0xff;
45
- bytes[i * 2 + 1] = unit >>> 8;
46
- }
47
- return bytes;
48
- }
@@ -1,5 +1,7 @@
1
- import { decodeRange, encodeAddress, MAX_COLUMN, MAX_ROW } from './address.js';
1
+ import { quoted } from '../errors.js';
2
+ import { assertAxisInBounds, decodeRange, encodeAddress, encodeRect, MAX_COLUMN, MAX_ROW, } from './address.js';
2
3
  import { applyCellStyle } from './cell.js';
4
+ import { INTERNAL } from './internal.js';
3
5
  import { CELL_STYLE_FACETS, } from './style.js';
4
6
  export class Range {
5
7
  #sheet;
@@ -8,10 +10,10 @@ export class Range {
8
10
  bottom;
9
11
  right;
10
12
  constructor(sheet, top, left, bottom, right) {
11
- checkBound('row', top, MAX_ROW);
12
- checkBound('row', bottom, MAX_ROW);
13
- checkBound('column', left, MAX_COLUMN);
14
- checkBound('column', right, MAX_COLUMN);
13
+ assertAxisInBounds('row', top);
14
+ assertAxisInBounds('row', bottom);
15
+ assertAxisInBounds('column', left);
16
+ assertAxisInBounds('column', right);
15
17
  this.#sheet = sheet;
16
18
  this.top = Math.min(top, bottom);
17
19
  this.bottom = Math.max(top, bottom);
@@ -22,7 +24,7 @@ export class Range {
22
24
  return this.#sheet;
23
25
  }
24
26
  get address() {
25
- return `${encodeAddress(this.left, this.top)}:${encodeAddress(this.right, this.bottom)}`;
27
+ return encodeRect(this);
26
28
  }
27
29
  get rowCount() {
28
30
  return this.bottom - this.top + 1;
@@ -45,12 +47,8 @@ export class Range {
45
47
  }
46
48
  get cells() {
47
49
  const cells = [];
48
- for (let row = this.top; row <= this.bottom; row++) {
49
- for (let col = this.left; col <= this.right; col++) {
50
- if (this.#sheet.hasCell(row, col))
51
- cells.push(this.#sheet.getCell(encodeAddress(col, row)));
52
- }
53
- }
50
+ for (const cell of this.#storedCells())
51
+ cells.push(cell);
54
52
  return cells;
55
53
  }
56
54
  get style() {
@@ -64,7 +62,7 @@ export class Range {
64
62
  applyCellStyle(cell, style);
65
63
  }
66
64
  clearStyle() {
67
- for (const cell of this.cells) {
65
+ for (const cell of this.#storedCells()) {
68
66
  for (const facet of CELL_STYLE_FACETS)
69
67
  setFacet(cell, facet, undefined);
70
68
  }
@@ -105,28 +103,42 @@ export class Range {
105
103
  set protection(protection) {
106
104
  this.#writeFacet('protection', protection);
107
105
  }
108
- #materialise() {
109
- return [...this.addresses()].map((address) => this.#sheet.getCell(address));
106
+ *#materialise() {
107
+ const internals = this.#sheet[INTERNAL];
108
+ for (let row = this.top; row <= this.bottom; row++) {
109
+ for (let col = this.left; col <= this.right; col++) {
110
+ yield internals.masterAt(row, col);
111
+ }
112
+ }
110
113
  }
111
114
  #writeFacet(facet, value) {
112
- const cells = value === undefined ? this.cells : this.#materialise();
115
+ const cells = value === undefined ? this.#storedCells() : this.#materialise();
113
116
  for (const cell of cells)
114
117
  setFacet(cell, facet, value);
115
118
  }
119
+ *#storedCells() {
120
+ const internals = this.#sheet[INTERNAL];
121
+ for (let row = this.top; row <= this.bottom; row++) {
122
+ for (let col = this.left; col <= this.right; col++) {
123
+ const cell = internals.peekCell(row, col);
124
+ if (cell !== undefined)
125
+ yield cell;
126
+ }
127
+ }
128
+ }
116
129
  #collectShared(target, facet) {
117
130
  const shared = this.#sharedFacet(facet);
118
131
  if (shared !== undefined)
119
132
  target[facet] = shared;
120
133
  }
121
134
  #sharedFacet(facet) {
135
+ const internals = this.#sheet[INTERNAL];
122
136
  let first;
123
137
  let firstKey;
124
138
  let seen = 0;
125
139
  for (let row = this.top; row <= this.bottom; row++) {
126
140
  for (let col = this.left; col <= this.right; col++) {
127
- const value = this.#sheet.hasCell(row, col)
128
- ? this.#sheet.getCell(encodeAddress(col, row))[facet]
129
- : undefined;
141
+ const value = internals.peekCell(row, col)?.[facet];
130
142
  const key = facetKey(value);
131
143
  if (seen === 0) {
132
144
  first = value;
@@ -152,24 +164,16 @@ function facetKey(value) {
152
164
  ? Object.fromEntries(Object.entries(inner).sort(([a], [b]) => (a < b ? -1 : 1)))
153
165
  : inner);
154
166
  }
155
- function checkBound(axis, value, max) {
156
- if (!Number.isInteger(value) || value < 1) {
157
- throw new RangeError(`${axis} ${value} is out of bounds: ${axis}s start at 1`);
158
- }
159
- if (value > max) {
160
- throw new RangeError(`${axis} ${value} is out of bounds: the sheet ends at ${max}`);
161
- }
162
- }
163
167
  export function rangeFrom(sheet, reference) {
164
168
  const { top, left, bottom, right, sheetName, dimensions } = decodeRange(reference);
165
169
  if (sheetName !== undefined && sheetName.toLowerCase() !== sheet.name.toLowerCase()) {
166
- throw new SyntaxError(`"${reference}" names worksheet "${sheetName}", not "${sheet.name}": a range belongs to the sheet it came from`);
170
+ throw new SyntaxError(`${quoted(reference)} names worksheet ${quoted(sheetName)}, not ${quoted(sheet.name)}: a range belongs to the sheet it came from`);
167
171
  }
168
172
  if (top === undefined || bottom === undefined) {
169
- throw new SyntaxError(`"${reference}" spans whole columns (${dimensions}): style them through getColumn(n), which says the same thing in one attribute instead of ${MAX_ROW} cells`);
173
+ throw new SyntaxError(`${quoted(reference)} spans whole columns (${dimensions}): style them through getColumn(n), which says the same thing in one attribute instead of ${MAX_ROW} cells`);
170
174
  }
171
175
  if (left === undefined || right === undefined) {
172
- throw new SyntaxError(`"${reference}" spans whole rows (${dimensions}): style them through getRow(n), which says the same thing in one attribute instead of ${MAX_COLUMN} cells`);
176
+ throw new SyntaxError(`${quoted(reference)} spans whole rows (${dimensions}): style them through getRow(n), which says the same thing in one attribute instead of ${MAX_COLUMN} cells`);
173
177
  }
174
178
  return new Range(sheet, top, left, bottom, right);
175
179
  }
@@ -1,4 +1,4 @@
1
- import { AuthoringError } from '../errors.js';
1
+ import { AuthoringError, quoted } from '../errors.js';
2
2
  import { Cell } from './cell.js';
3
3
  export function positionalPlacements(values) {
4
4
  const placements = [];
@@ -27,5 +27,5 @@ function columnIndexByKey(columns, key) {
27
27
  if (properties.key === key)
28
28
  return index;
29
29
  }
30
- throw new AuthoringError(`no column is keyed ${JSON.stringify(key)}: set getColumn(n).key first`);
30
+ throw new AuthoringError(`no column is keyed ${quoted(key)}: set getColumn(n).key first`);
31
31
  }
@@ -1,9 +1,10 @@
1
+ import { AxisHandle } from './axis-handle.ts';
1
2
  import type { Cell } from './cell.ts';
2
3
  import { type AssertNever } from './internal.ts';
3
4
  import type { Fill } from './style.ts';
4
5
  import type { CellValue } from './value.ts';
5
6
  import type { RowProperties, Worksheet } from './worksheet.ts';
6
- export declare class Row {
7
+ export declare class Row extends AxisHandle<RowProperties> {
7
8
  #private;
8
9
  /** 1-based row number. Fixed for this handle's lifetime. */
9
10
  readonly number: number;
@@ -16,6 +17,9 @@ export declare class Row {
16
17
  * formatted, and they create the record on first write.
17
18
  */
18
19
  get properties(): Readonly<RowProperties> | undefined;
20
+ protected propertiesOf(): RowProperties | undefined;
21
+ protected ensureProperties(): RowProperties;
22
+ protected dropProperties(): void;
19
23
  /**
20
24
  * Row height in points; `undefined` leaves the sheet default in force.
21
25
  *
package/dist/core/row.js CHANGED
@@ -1,45 +1,56 @@
1
1
  import { assertRowInBounds, columnToNumber, encodeAddress } from './address.js';
2
+ import { AxisHandle } from './axis-handle.js';
2
3
  import { INTERNAL } from './internal.js';
3
- export class Row {
4
+ export class Row extends AxisHandle {
4
5
  #sheet;
5
6
  number;
6
7
  constructor(sheet, number) {
8
+ super();
7
9
  assertRowInBounds(number);
8
10
  this.#sheet = sheet;
9
11
  this.number = number;
10
12
  }
11
13
  get properties() {
14
+ return this.propertiesOf();
15
+ }
16
+ propertiesOf() {
12
17
  return this.#sheet[INTERNAL].rowPropertiesOf(this.number);
13
18
  }
19
+ ensureProperties() {
20
+ return this.#sheet[INTERNAL].ensureRowProperties(this.number);
21
+ }
22
+ dropProperties() {
23
+ this.#sheet[INTERNAL].dropRowProperties(this.number);
24
+ }
14
25
  get height() {
15
- return this.#read('height');
26
+ return this.read('height');
16
27
  }
17
28
  set height(height) {
18
- this.#write('height', height);
29
+ this.write('height', height);
19
30
  }
20
31
  get hidden() {
21
- return this.#read('hidden');
32
+ return this.read('hidden');
22
33
  }
23
34
  set hidden(hidden) {
24
- this.#write('hidden', hidden);
35
+ this.write('hidden', hidden);
25
36
  }
26
37
  get outlineLevel() {
27
- return this.#read('outlineLevel');
38
+ return this.read('outlineLevel');
28
39
  }
29
40
  set outlineLevel(outlineLevel) {
30
- this.#write('outlineLevel', outlineLevel);
41
+ this.write('outlineLevel', outlineLevel);
31
42
  }
32
43
  get collapsed() {
33
- return this.#read('collapsed');
44
+ return this.read('collapsed');
34
45
  }
35
46
  set collapsed(collapsed) {
36
- this.#write('collapsed', collapsed);
47
+ this.write('collapsed', collapsed);
37
48
  }
38
49
  get fill() {
39
- return this.#read('fill');
50
+ return this.read('fill');
40
51
  }
41
52
  set fill(fill) {
42
- this.#write('fill', fill);
53
+ this.write('fill', fill);
43
54
  }
44
55
  getCell(column) {
45
56
  const index = typeof column === 'number' ? column : columnToNumber(column);
@@ -60,16 +71,4 @@ export class Row {
60
71
  this.getCell(index + 1).value = value;
61
72
  });
62
73
  }
63
- #read(key) {
64
- return this.#sheet[INTERNAL].rowPropertiesOf(this.number)?.[key];
65
- }
66
- #write(key, value) {
67
- if (value === undefined) {
68
- const properties = this.#sheet[INTERNAL].rowPropertiesOf(this.number);
69
- if (properties !== undefined)
70
- delete properties[key];
71
- return;
72
- }
73
- this.#sheet[INTERNAL].ensureRowProperties(this.number)[key] = value;
74
- }
75
74
  }