@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.
- package/README.md +4 -1
- package/dist/bytes.d.ts +7 -0
- package/dist/bytes.js +22 -0
- package/dist/core/address.d.ts +60 -2
- package/dist/core/address.js +40 -24
- package/dist/core/autofilter.d.ts +5 -4
- package/dist/core/autofilter.js +34 -35
- package/dist/core/axis-handle.d.ts +10 -0
- package/dist/core/axis-handle.js +17 -0
- package/dist/core/cell.d.ts +25 -0
- package/dist/core/cell.js +9 -3
- package/dist/core/clone.d.ts +29 -0
- package/dist/core/clone.js +24 -0
- package/dist/core/color-resolution.js +2 -4
- package/dist/core/column.d.ts +5 -1
- package/dist/core/column.js +34 -35
- package/dist/core/comment-thread.js +2 -1
- package/dist/core/conditional-formatting-overlay.d.ts +2 -1
- package/dist/core/conditional-formatting-overlay.js +2 -2
- package/dist/core/conditional-formatting.d.ts +14 -6
- package/dist/core/conditional-formatting.js +45 -43
- package/dist/core/containers.d.ts +12 -0
- package/dist/core/containers.js +5 -0
- package/dist/core/data-validation-overlay.d.ts +2 -1
- package/dist/core/data-validation-overlay.js +4 -4
- package/dist/core/data-validation.d.ts +9 -3
- package/dist/core/data-validation.js +21 -18
- package/dist/core/date-format.d.ts +20 -0
- package/dist/core/date-format.js +160 -0
- package/dist/core/date.d.ts +43 -8
- package/dist/core/date.js +18 -2
- package/dist/core/formula.d.ts +26 -0
- package/dist/core/formula.js +20 -4
- package/dist/core/grid-edits.d.ts +2 -3
- package/dist/core/grid-edits.js +69 -70
- package/dist/core/grid-shift.d.ts +58 -9
- package/dist/core/grid-shift.js +26 -4
- package/dist/core/image.d.ts +14 -10
- package/dist/core/image.js +9 -9
- package/dist/core/internal.d.ts +8 -2
- package/dist/core/limits.d.ts +23 -0
- package/dist/core/limits.js +4 -0
- package/dist/core/merge-index.d.ts +31 -0
- package/dist/core/merge-index.js +60 -0
- package/dist/core/merge.d.ts +5 -14
- package/dist/core/merge.js +18 -27
- package/dist/core/page-setup.d.ts +72 -2
- package/dist/core/page-setup.js +34 -8
- package/dist/core/pivot-table.d.ts +1 -1
- package/dist/core/pivot-table.js +67 -53
- package/dist/core/protection.js +2 -11
- package/dist/core/range.js +34 -30
- package/dist/core/row-input.js +2 -2
- package/dist/core/row.d.ts +5 -1
- package/dist/core/row.js +22 -23
- package/dist/core/style.d.ts +134 -75
- package/dist/core/style.js +112 -53
- package/dist/core/table-style.d.ts +1 -1
- package/dist/core/table-style.js +4 -4
- package/dist/core/table.d.ts +28 -10
- package/dist/core/table.js +54 -39
- package/dist/core/theme.js +6 -5
- package/dist/core/used-extent.d.ts +44 -0
- package/dist/core/used-extent.js +102 -0
- package/dist/core/value.d.ts +9 -0
- package/dist/core/value.js +36 -34
- package/dist/core/workbook-media.d.ts +34 -0
- package/dist/core/workbook-media.js +44 -0
- package/dist/core/workbook-styles.d.ts +67 -1
- package/dist/core/workbook-vba.js +3 -2
- package/dist/core/workbook.d.ts +23 -2
- package/dist/core/workbook.js +24 -40
- package/dist/core/worksheet-comments.d.ts +2 -1
- package/dist/core/worksheet-comments.js +20 -13
- package/dist/core/worksheet-merges.d.ts +44 -0
- package/dist/core/worksheet-merges.js +59 -0
- package/dist/core/worksheet-model.d.ts +9 -1
- package/dist/core/worksheet-model.js +24 -9
- package/dist/core/worksheet.d.ts +72 -12
- package/dist/core/worksheet.js +68 -70
- package/dist/customui/ribbon.js +6 -7
- package/dist/entries/core.d.ts +18 -13
- package/dist/entries/core.js +12 -6
- package/dist/entries/xlsx.d.ts +1 -1
- package/dist/errors.d.ts +64 -0
- package/dist/errors.js +22 -0
- package/dist/hex.d.ts +8 -0
- package/dist/hex.js +3 -0
- package/dist/io/csv/delimiter.d.ts +9 -0
- package/dist/io/csv/delimiter.js +6 -0
- package/dist/io/csv/read.js +11 -4
- package/dist/io/csv/write.d.ts +9 -2
- package/dist/io/csv/write.js +9 -38
- package/dist/io/opc/inflate.js +15 -1
- package/dist/io/opc/namespaces.d.ts +16 -0
- package/dist/io/opc/namespaces.js +4 -0
- package/dist/io/opc/part-paths.d.ts +10 -0
- package/dist/io/opc/part-paths.js +16 -1
- package/dist/io/opc/read-opc.d.ts +69 -10
- package/dist/io/opc/read-opc.js +49 -55
- package/dist/io/opc/read-options.d.ts +5 -1
- package/dist/io/opc/rels.d.ts +1 -6
- package/dist/io/opc/rels.js +1 -4
- package/dist/io/opc/sniff-format.d.ts +3 -3
- package/dist/io/opc/sniff-format.js +2 -2
- package/dist/io/style/xf-style.d.ts +48 -2
- package/dist/io/style/xf-style.js +14 -0
- package/dist/io/xlsb/formula.d.ts +2 -2
- package/dist/io/xlsb/formula.js +49 -34
- package/dist/io/xlsb/primitives.js +8 -14
- package/dist/io/xlsb/ptg-functions.d.ts +19 -10
- package/dist/io/xlsb/ptg-functions.js +385 -596
- package/dist/io/xlsb/read-styles.js +50 -83
- package/dist/io/xlsb/read-worksheet.d.ts +20 -1
- package/dist/io/xlsb/read-worksheet.js +73 -89
- package/dist/io/xlsb/read.d.ts +6 -3
- package/dist/io/xlsb/read.js +47 -29
- package/dist/io/xlsb/record-stream.d.ts +30 -0
- package/dist/io/xlsb/record-stream.js +20 -0
- package/dist/io/xlsb/record-types.d.ts +1 -5
- package/dist/io/xlsb/record-types.js +1 -5
- package/dist/io/xlsx/cell-accumulator.d.ts +9 -2
- package/dist/io/xlsx/cell-accumulator.js +32 -9
- package/dist/io/xlsx/cell-style-resolution.d.ts +25 -0
- package/dist/io/xlsx/cell-style-resolution.js +29 -0
- package/dist/io/xlsx/cell-value.d.ts +5 -3
- package/dist/io/xlsx/cell-value.js +13 -15
- package/dist/io/xlsx/color-xml.js +3 -2
- package/dist/io/xlsx/column-budget.d.ts +26 -0
- package/dist/io/xlsx/column-budget.js +22 -0
- package/dist/io/xlsx/comments.d.ts +6 -2
- package/dist/io/xlsx/comments.js +36 -31
- package/dist/io/xlsx/conditional-formatting.d.ts +9 -5
- package/dist/io/xlsx/conditional-formatting.js +51 -46
- package/dist/io/xlsx/data-validation.d.ts +9 -7
- package/dist/io/xlsx/data-validation.js +38 -35
- package/dist/io/xlsx/edit-vba.d.ts +7 -2
- package/dist/io/xlsx/edit-vba.js +15 -29
- package/dist/io/xlsx/font-xml.d.ts +7 -0
- package/dist/io/xlsx/font-xml.js +104 -0
- package/dist/io/xlsx/hyperlinks.d.ts +41 -7
- package/dist/io/xlsx/hyperlinks.js +40 -28
- package/dist/io/xlsx/images.js +24 -25
- package/dist/io/xlsx/namespaces.d.ts +16 -0
- package/dist/io/xlsx/namespaces.js +7 -0
- package/dist/io/xlsx/package-plan.d.ts +23 -3
- package/dist/io/xlsx/package-plan.js +66 -25
- package/dist/io/xlsx/part-names.d.ts +41 -0
- package/dist/io/xlsx/part-names.js +46 -0
- package/dist/io/xlsx/pivot.js +8 -7
- package/dist/io/xlsx/read-parts.d.ts +59 -0
- package/dist/io/xlsx/read-parts.js +252 -0
- package/dist/io/xlsx/read-pivot.js +3 -1
- package/dist/io/xlsx/read-repair.d.ts +26 -0
- package/dist/io/xlsx/read-repair.js +39 -0
- package/dist/io/xlsx/{rich-runs.d.ts → read-rich-runs.d.ts} +1 -1
- package/dist/io/xlsx/{rich-runs.js → read-rich-runs.js} +7 -3
- package/dist/io/xlsx/read-rows.d.ts +18 -5
- package/dist/io/xlsx/read-rows.js +50 -37
- package/dist/io/xlsx/read-shared-strings.js +5 -3
- package/dist/io/xlsx/read-styles.d.ts +1 -6
- package/dist/io/xlsx/read-styles.js +65 -109
- package/dist/io/xlsx/read-workbook-xml.d.ts +36 -0
- package/dist/io/xlsx/read-workbook-xml.js +170 -0
- package/dist/io/xlsx/read-worksheet.d.ts +11 -1
- package/dist/io/xlsx/read-worksheet.js +51 -193
- package/dist/io/xlsx/read.d.ts +5 -13
- package/dist/io/xlsx/read.js +111 -436
- package/dist/io/xlsx/rich-text.js +1 -1
- package/dist/io/xlsx/row-position.d.ts +18 -0
- package/dist/io/xlsx/row-position.js +11 -0
- package/dist/io/xlsx/row-xml.d.ts +128 -0
- package/dist/io/xlsx/row-xml.js +203 -0
- package/dist/io/xlsx/shared-formulas.js +2 -2
- package/dist/io/xlsx/sheet-properties.d.ts +10 -1
- package/dist/io/xlsx/sheet-properties.js +166 -37
- package/dist/io/xlsx/style-elements.d.ts +43 -0
- package/dist/io/xlsx/style-elements.js +156 -0
- package/dist/io/xlsx/styles.d.ts +12 -3
- package/dist/io/xlsx/styles.js +5 -204
- package/dist/io/xlsx/tables.js +11 -14
- package/dist/io/xlsx/theme-xml.d.ts +26 -4
- package/dist/io/xlsx/theme-xml.js +107 -69
- package/dist/io/xlsx/threaded-comments.d.ts +8 -10
- package/dist/io/xlsx/threaded-comments.js +3 -1
- package/dist/io/xlsx/workbook-xml.d.ts +54 -4
- package/dist/io/xlsx/workbook-xml.js +84 -57
- package/dist/io/xlsx/worksheet-xml.d.ts +23 -70
- package/dist/io/xlsx/worksheet-xml.js +59 -242
- package/dist/io/xlsx/write-stream.d.ts +71 -16
- package/dist/io/xlsx/write-stream.js +79 -41
- package/dist/io/xlsx/write.d.ts +6 -2
- package/dist/io/xlsx/write.js +217 -153
- package/dist/rel-type.d.ts +16 -0
- package/dist/rel-type.js +6 -0
- package/dist/token-set.d.ts +28 -0
- package/dist/token-set.js +7 -0
- package/dist/vba/bytes.d.ts +14 -5
- package/dist/vba/bytes.js +16 -6
- package/dist/vba/cfb-format.d.ts +32 -0
- package/dist/vba/cfb-format.js +13 -0
- package/dist/vba/cfb-writer.d.ts +9 -0
- package/dist/vba/cfb-writer.js +28 -25
- package/dist/vba/cfb.d.ts +13 -1
- package/dist/vba/cfb.js +73 -57
- package/dist/vba/codepage.js +2 -1
- package/dist/vba/errors.d.ts +7 -0
- package/dist/vba/ms-ovba.js +79 -23
- package/dist/vba/project-editor.js +35 -40
- package/dist/vba/project.d.ts +11 -1
- package/dist/vba/project.js +40 -21
- package/dist/vba/vba-encoding.js +5 -6
- package/dist/xml/xml-attrs.d.ts +37 -0
- package/dist/xml/xml-attrs.js +30 -0
- package/dist/xml/xml-chars.d.ts +40 -0
- package/dist/xml/xml-chars.js +12 -0
- package/dist/xml/xml-namespaces.d.ts +31 -0
- package/dist/xml/xml-namespaces.js +50 -0
- package/dist/xml/xml-read.d.ts +128 -81
- package/dist/xml/xml-read.js +161 -178
- package/dist/xml/xml-scan.d.ts +78 -0
- package/dist/xml/xml-scan.js +180 -0
- package/dist/xml/xml.d.ts +29 -28
- package/dist/xml/xml.js +24 -24
- package/package.json +13 -9
package/dist/core/table.js
CHANGED
|
@@ -1,18 +1,20 @@
|
|
|
1
|
-
import { AuthoringError } from '../errors.js';
|
|
2
|
-
import {
|
|
1
|
+
import { AuthoringError, quoted } from '../errors.js';
|
|
2
|
+
import { tokenSet } from '../token-set.js';
|
|
3
|
+
import { decodeCellRef, encodeAddress, encodeRect, MAX_COLUMN, MAX_ROW, numberToColumn, } from './address.js';
|
|
4
|
+
import { cloneWith } from './clone.js';
|
|
3
5
|
import { isDeletedSpan, shiftIndex } from './grid-shift.js';
|
|
6
|
+
import { MAX_TABLE_NAME_LENGTH, TABLE_NAME_PATTERN } from './limits.js';
|
|
7
|
+
const STYLE_INFO_CLONE = {
|
|
8
|
+
name: (name) => (name === 'None' ? undefined : name),
|
|
9
|
+
showFirstColumn: 'value',
|
|
10
|
+
showLastColumn: 'value',
|
|
11
|
+
showRowStripes: 'value',
|
|
12
|
+
showColumnStripes: 'value',
|
|
13
|
+
};
|
|
4
14
|
function cloneStyleInfo(style) {
|
|
5
|
-
const clone =
|
|
6
|
-
if (
|
|
7
|
-
clone.name
|
|
8
|
-
if (style.showFirstColumn !== undefined)
|
|
9
|
-
clone.showFirstColumn = style.showFirstColumn;
|
|
10
|
-
if (style.showLastColumn !== undefined)
|
|
11
|
-
clone.showLastColumn = style.showLastColumn;
|
|
12
|
-
if (style.showRowStripes !== undefined)
|
|
13
|
-
clone.showRowStripes = style.showRowStripes;
|
|
14
|
-
if (style.showColumnStripes !== undefined)
|
|
15
|
-
clone.showColumnStripes = style.showColumnStripes;
|
|
15
|
+
const clone = cloneWith(style, STYLE_INFO_CLONE);
|
|
16
|
+
if (clone.name === undefined)
|
|
17
|
+
delete clone.name;
|
|
16
18
|
return clone;
|
|
17
19
|
}
|
|
18
20
|
export const TOTALS_ROW_SUBTOTAL_CODE = {
|
|
@@ -25,7 +27,7 @@ export const TOTALS_ROW_SUBTOTAL_CODE = {
|
|
|
25
27
|
sum: 109,
|
|
26
28
|
var: 110,
|
|
27
29
|
};
|
|
28
|
-
const
|
|
30
|
+
export const isTotalsRowFunction = tokenSet({
|
|
29
31
|
average: true,
|
|
30
32
|
countNums: true,
|
|
31
33
|
count: true,
|
|
@@ -36,11 +38,7 @@ const TOTALS_ROW_FUNCTIONS = {
|
|
|
36
38
|
var: true,
|
|
37
39
|
custom: true,
|
|
38
40
|
none: true,
|
|
39
|
-
};
|
|
40
|
-
export function isTotalsRowFunction(value) {
|
|
41
|
-
return Object.hasOwn(TOTALS_ROW_FUNCTIONS, value);
|
|
42
|
-
}
|
|
43
|
-
const IDENTIFIER = /^[\p{L}\\_][\p{L}\p{N}._]*$/u;
|
|
41
|
+
});
|
|
44
42
|
function disambiguateColumnNames(columns) {
|
|
45
43
|
const seen = new Set();
|
|
46
44
|
return columns.map((column) => {
|
|
@@ -52,11 +50,11 @@ function disambiguateColumnNames(columns) {
|
|
|
52
50
|
});
|
|
53
51
|
}
|
|
54
52
|
function validateTableName(name) {
|
|
55
|
-
if (name.length === 0 || name.length >
|
|
56
|
-
throw new RangeError(`table name ${
|
|
53
|
+
if (name.length === 0 || name.length > MAX_TABLE_NAME_LENGTH) {
|
|
54
|
+
throw new RangeError(`table name ${quoted(name)} must be between 1 and ${MAX_TABLE_NAME_LENGTH} characters`);
|
|
57
55
|
}
|
|
58
|
-
if (!
|
|
59
|
-
throw new SyntaxError(`table name ${
|
|
56
|
+
if (!TABLE_NAME_PATTERN.test(name)) {
|
|
57
|
+
throw new SyntaxError(`table name ${quoted(name)} is not a valid Excel identifier: it must start with a letter, ` +
|
|
60
58
|
'underscore, or backslash and contain only letters, digits, periods, and underscores');
|
|
61
59
|
}
|
|
62
60
|
}
|
|
@@ -76,17 +74,17 @@ export class Table {
|
|
|
76
74
|
constructor(options, grid) {
|
|
77
75
|
validateTableName(options.name);
|
|
78
76
|
if (options.columns.length === 0) {
|
|
79
|
-
throw new AuthoringError(`table
|
|
77
|
+
throw new AuthoringError(`table ${quoted(options.name)} must declare at least one column`);
|
|
80
78
|
}
|
|
81
79
|
if (!Number.isInteger(options.rowCount) || options.rowCount < 0) {
|
|
82
|
-
throw new RangeError(`table
|
|
80
|
+
throw new RangeError(`table ${quoted(options.name)} has an invalid data-row count (${options.rowCount})`);
|
|
83
81
|
}
|
|
84
82
|
let anchor;
|
|
85
83
|
try {
|
|
86
84
|
anchor = decodeCellRef(options.ref);
|
|
87
85
|
}
|
|
88
86
|
catch (cause) {
|
|
89
|
-
throw new SyntaxError(`table ref
|
|
87
|
+
throw new SyntaxError(`table ref ${quoted(options.ref)} must anchor at a single cell (e.g. "A1")`, {
|
|
90
88
|
cause,
|
|
91
89
|
});
|
|
92
90
|
}
|
|
@@ -104,7 +102,15 @@ export class Table {
|
|
|
104
102
|
this.#dataRowCount = options.rowCount;
|
|
105
103
|
this.#grid = grid;
|
|
106
104
|
if (this.#rowSpan < 1) {
|
|
107
|
-
throw new AuthoringError(`table
|
|
105
|
+
throw new AuthoringError(`table ${quoted(this.name)} has no rows: it needs a header row or at least one data row`);
|
|
106
|
+
}
|
|
107
|
+
if (this.#right > MAX_COLUMN) {
|
|
108
|
+
throw new RangeError(`table ${quoted(this.name)} spans ${this.columns.length} columns from ` +
|
|
109
|
+
`${numberToColumn(this.#anchorCol)}, past the last column (${numberToColumn(MAX_COLUMN)})`);
|
|
110
|
+
}
|
|
111
|
+
if (this.#bottom > MAX_ROW) {
|
|
112
|
+
throw new RangeError(`table ${quoted(this.name)} spans ${this.#rowSpan} rows from ${this.#anchorRow}, ` +
|
|
113
|
+
`past the last row (${MAX_ROW})`);
|
|
108
114
|
}
|
|
109
115
|
if (grid !== undefined)
|
|
110
116
|
this.#materializeFrame(grid);
|
|
@@ -117,13 +123,13 @@ export class Table {
|
|
|
117
123
|
}
|
|
118
124
|
addRow(values = []) {
|
|
119
125
|
if (values.length > this.columnCount) {
|
|
120
|
-
throw new RangeError(`row has ${values.length} values but table
|
|
126
|
+
throw new RangeError(`row has ${values.length} values but table ${quoted(this.name)} has ${this.columnCount} columns`);
|
|
121
127
|
}
|
|
122
128
|
const target = this.#anchorRow + (this.headerRow ? 1 : 0) + this.#dataRowCount;
|
|
123
129
|
const grid = this.#grid;
|
|
124
130
|
if (this.totalsRow) {
|
|
125
131
|
if (grid === undefined) {
|
|
126
|
-
throw new AuthoringError(`table
|
|
132
|
+
throw new AuthoringError(`table ${quoted(this.name)} is not attached to a worksheet: cannot relocate its totals row to append a data row`);
|
|
127
133
|
}
|
|
128
134
|
grid.insertRow(target);
|
|
129
135
|
this.#writeRow(grid, target, values);
|
|
@@ -131,7 +137,7 @@ export class Table {
|
|
|
131
137
|
}
|
|
132
138
|
if (values.length > 0) {
|
|
133
139
|
if (grid === undefined) {
|
|
134
|
-
throw new AuthoringError(`table
|
|
140
|
+
throw new AuthoringError(`table ${quoted(this.name)} is not attached to a worksheet: cannot write appended row values`);
|
|
135
141
|
}
|
|
136
142
|
this.#writeRow(grid, target, values);
|
|
137
143
|
}
|
|
@@ -176,11 +182,11 @@ export class Table {
|
|
|
176
182
|
});
|
|
177
183
|
}
|
|
178
184
|
}
|
|
179
|
-
shiftRows(
|
|
180
|
-
if (isDeletedSpan(this.#anchorRow, this.#bottom,
|
|
185
|
+
shiftRows(splice) {
|
|
186
|
+
if (isDeletedSpan(this.#anchorRow, this.#bottom, splice))
|
|
181
187
|
return false;
|
|
182
|
-
const top = shiftIndex(this.#anchorRow,
|
|
183
|
-
const bottom = shiftIndex(this.#bottom,
|
|
188
|
+
const top = shiftIndex(this.#anchorRow, splice);
|
|
189
|
+
const bottom = shiftIndex(this.#bottom, splice);
|
|
184
190
|
const span = bottom - top + 1;
|
|
185
191
|
const fixedRows = (this.headerRow ? 1 : 0) + (this.totalsRow ? 1 : 0);
|
|
186
192
|
const dataRows = span - fixedRows;
|
|
@@ -190,9 +196,13 @@ export class Table {
|
|
|
190
196
|
this.#dataRowCount = dataRows;
|
|
191
197
|
return true;
|
|
192
198
|
}
|
|
193
|
-
shiftColumns(
|
|
194
|
-
if (this.#anchorCol
|
|
195
|
-
|
|
199
|
+
shiftColumns(splice) {
|
|
200
|
+
if (isDeletedSpan(this.#anchorCol, this.#right, splice))
|
|
201
|
+
return false;
|
|
202
|
+
const anchor = shiftIndex(this.#anchorCol, splice);
|
|
203
|
+
if (anchor + this.columns.length - 1 > MAX_COLUMN)
|
|
204
|
+
return false;
|
|
205
|
+
this.#anchorCol = anchor;
|
|
196
206
|
return true;
|
|
197
207
|
}
|
|
198
208
|
get options() {
|
|
@@ -213,13 +223,18 @@ export class Table {
|
|
|
213
223
|
return options;
|
|
214
224
|
}
|
|
215
225
|
get range() {
|
|
216
|
-
return
|
|
226
|
+
return encodeRect({
|
|
227
|
+
top: this.#anchorRow,
|
|
228
|
+
left: this.#anchorCol,
|
|
229
|
+
bottom: this.#bottom,
|
|
230
|
+
right: this.#right,
|
|
231
|
+
});
|
|
217
232
|
}
|
|
218
233
|
get autoFilterRef() {
|
|
219
234
|
if (!this.autoFilter)
|
|
220
235
|
return undefined;
|
|
221
236
|
const bottom = this.#anchorRow + this.#dataRowCount;
|
|
222
|
-
return
|
|
237
|
+
return encodeRect({ top: this.#anchorRow, left: this.#anchorCol, bottom, right: this.#right });
|
|
223
238
|
}
|
|
224
239
|
get region() {
|
|
225
240
|
return { top: this.#anchorRow, left: this.#anchorCol, bottom: this.#bottom, right: this.#right };
|
package/dist/core/theme.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
import { quoted } from '../errors.js';
|
|
2
|
+
import { parseArgb } from './style.js';
|
|
1
3
|
export const THEME_COLOR_SLOTS = [
|
|
2
4
|
'lt1',
|
|
3
5
|
'dk1',
|
|
@@ -36,10 +38,9 @@ export const DEFAULT_THEME_FONTS = {
|
|
|
36
38
|
minor: OFFICE_BODY_FACE,
|
|
37
39
|
};
|
|
38
40
|
export function normalizeThemeColor(value) {
|
|
39
|
-
const
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
throw new SyntaxError(`Invalid theme colour ${JSON.stringify(value)}: expected 6 hexadecimal digits (RRGGBB)`);
|
|
41
|
+
const argb = parseArgb(value);
|
|
42
|
+
if (argb === undefined) {
|
|
43
|
+
throw new SyntaxError(`invalid theme colour ${quoted(value)}: expected 6 hexadecimal digits (RRGGBB)`);
|
|
43
44
|
}
|
|
44
|
-
return
|
|
45
|
+
return argb.slice(2).toUpperCase();
|
|
45
46
|
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { type Cell } from './cell.ts';
|
|
2
|
+
import type { MergeRect } from './merge.ts';
|
|
3
|
+
/**
|
|
4
|
+
* The sheet storage an extent reads. Held by reference and never reassigned, so the extent always
|
|
5
|
+
* sees the live grid; the value types are irrelevant to it, only which keys are present.
|
|
6
|
+
*/
|
|
7
|
+
export interface ExtentStorage {
|
|
8
|
+
readonly rows: ReadonlyMap<number, ReadonlyMap<number, Cell>>;
|
|
9
|
+
readonly rowProperties: ReadonlyMap<number, unknown>;
|
|
10
|
+
readonly columns: ReadonlyMap<number, unknown>;
|
|
11
|
+
readonly mergeRects: readonly MergeRect[];
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* The last used row and column of a sheet, tracked incrementally. The sheet reports every edit that
|
|
15
|
+
* can move the grid's outer keys; this turns those reports into the two accessors behind
|
|
16
|
+
* `Worksheet.rowCount` and `Worksheet.columnCount`.
|
|
17
|
+
*/
|
|
18
|
+
export declare class UsedExtent {
|
|
19
|
+
#private;
|
|
20
|
+
constructor(storage: ExtentStorage);
|
|
21
|
+
/** Note a cell materialised at `(row, col)`. Whether it goes on to carry anything is not this
|
|
22
|
+
* class's business: the bound is on where a used cell *could* be, and the read confirms it. */
|
|
23
|
+
noteCell(row: number, col: number): void;
|
|
24
|
+
/** Note a row that bounds the used range by declaration: one given properties of its own. */
|
|
25
|
+
noteDeclaredRow(row: number): void;
|
|
26
|
+
/** Note a column that bounds the used range by declaration: one given properties of its own. */
|
|
27
|
+
noteDeclaredColumn(col: number): void;
|
|
28
|
+
/** Note a merged region, which occupies its whole rectangle and so bounds both axes. */
|
|
29
|
+
noteMerge(rect: MergeRect): void;
|
|
30
|
+
/**
|
|
31
|
+
* Report an edit that can pull the grid's outer keys inward: an eviction, a splice, an unmerge, a
|
|
32
|
+
* row replaced wholesale. The bounds are recomputed once, on the next read, rather than adjusted
|
|
33
|
+
* here, because finding the *new* maximum after a deletion is the same scan as finding it from
|
|
34
|
+
* scratch. Appending never invalidates, which is the whole point.
|
|
35
|
+
*/
|
|
36
|
+
invalidate(): void;
|
|
37
|
+
/** Report that the grid has been emptied, which puts the bounds at a known zero rather than
|
|
38
|
+
* stale: nothing is left to scan. */
|
|
39
|
+
reset(): void;
|
|
40
|
+
/** The 1-based index of the last row carrying anything, or 0 when the sheet holds nothing. */
|
|
41
|
+
get lastRow(): number;
|
|
42
|
+
/** The 1-based index of the last column carrying anything, or 0 when the sheet holds nothing. */
|
|
43
|
+
get lastColumn(): number;
|
|
44
|
+
}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { cellCarriesContent } from './cell.js';
|
|
2
|
+
function rowIsUsed(cols) {
|
|
3
|
+
for (const cell of cols.values()) {
|
|
4
|
+
if (cellCarriesContent(cell))
|
|
5
|
+
return true;
|
|
6
|
+
}
|
|
7
|
+
return false;
|
|
8
|
+
}
|
|
9
|
+
export class UsedExtent {
|
|
10
|
+
#storage;
|
|
11
|
+
#topRowKey = 0;
|
|
12
|
+
#topColumnKey = 0;
|
|
13
|
+
#topDeclaredRow = 0;
|
|
14
|
+
#topDeclaredColumn = 0;
|
|
15
|
+
#stale = false;
|
|
16
|
+
constructor(storage) {
|
|
17
|
+
this.#storage = storage;
|
|
18
|
+
}
|
|
19
|
+
noteCell(row, col) {
|
|
20
|
+
if (row > this.#topRowKey)
|
|
21
|
+
this.#topRowKey = row;
|
|
22
|
+
if (col > this.#topColumnKey)
|
|
23
|
+
this.#topColumnKey = col;
|
|
24
|
+
}
|
|
25
|
+
noteDeclaredRow(row) {
|
|
26
|
+
if (row > this.#topDeclaredRow)
|
|
27
|
+
this.#topDeclaredRow = row;
|
|
28
|
+
}
|
|
29
|
+
noteDeclaredColumn(col) {
|
|
30
|
+
if (col > this.#topDeclaredColumn)
|
|
31
|
+
this.#topDeclaredColumn = col;
|
|
32
|
+
}
|
|
33
|
+
noteMerge(rect) {
|
|
34
|
+
this.noteDeclaredRow(rect.bottom);
|
|
35
|
+
this.noteDeclaredColumn(rect.right);
|
|
36
|
+
}
|
|
37
|
+
invalidate() {
|
|
38
|
+
this.#stale = true;
|
|
39
|
+
}
|
|
40
|
+
reset() {
|
|
41
|
+
this.#topRowKey = 0;
|
|
42
|
+
this.#topColumnKey = 0;
|
|
43
|
+
this.#topDeclaredRow = 0;
|
|
44
|
+
this.#topDeclaredColumn = 0;
|
|
45
|
+
this.#stale = false;
|
|
46
|
+
}
|
|
47
|
+
get lastRow() {
|
|
48
|
+
this.#refresh();
|
|
49
|
+
if (this.#topRowKey <= this.#topDeclaredRow)
|
|
50
|
+
return this.#topDeclaredRow;
|
|
51
|
+
const top = this.#storage.rows.get(this.#topRowKey);
|
|
52
|
+
if (top !== undefined && rowIsUsed(top))
|
|
53
|
+
return this.#topRowKey;
|
|
54
|
+
let last = this.#topDeclaredRow;
|
|
55
|
+
for (const [number, cols] of this.#storage.rows) {
|
|
56
|
+
if (number > last && rowIsUsed(cols))
|
|
57
|
+
last = number;
|
|
58
|
+
}
|
|
59
|
+
return last;
|
|
60
|
+
}
|
|
61
|
+
get lastColumn() {
|
|
62
|
+
this.#refresh();
|
|
63
|
+
if (this.#topColumnKey <= this.#topDeclaredColumn)
|
|
64
|
+
return this.#topDeclaredColumn;
|
|
65
|
+
for (const cols of this.#storage.rows.values()) {
|
|
66
|
+
const cell = cols.get(this.#topColumnKey);
|
|
67
|
+
if (cell !== undefined && cellCarriesContent(cell))
|
|
68
|
+
return this.#topColumnKey;
|
|
69
|
+
}
|
|
70
|
+
let last = this.#topDeclaredColumn;
|
|
71
|
+
for (const cols of this.#storage.rows.values()) {
|
|
72
|
+
for (const [col, cell] of cols) {
|
|
73
|
+
if (col > last && cellCarriesContent(cell))
|
|
74
|
+
last = col;
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
return last;
|
|
78
|
+
}
|
|
79
|
+
#refresh() {
|
|
80
|
+
if (!this.#stale)
|
|
81
|
+
return;
|
|
82
|
+
this.#stale = false;
|
|
83
|
+
this.#topRowKey = 0;
|
|
84
|
+
this.#topColumnKey = 0;
|
|
85
|
+
this.#topDeclaredRow = 0;
|
|
86
|
+
this.#topDeclaredColumn = 0;
|
|
87
|
+
for (const [number, cols] of this.#storage.rows) {
|
|
88
|
+
if (number > this.#topRowKey)
|
|
89
|
+
this.#topRowKey = number;
|
|
90
|
+
for (const col of cols.keys()) {
|
|
91
|
+
if (col > this.#topColumnKey)
|
|
92
|
+
this.#topColumnKey = col;
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
for (const number of this.#storage.rowProperties.keys())
|
|
96
|
+
this.noteDeclaredRow(number);
|
|
97
|
+
for (const index of this.#storage.columns.keys())
|
|
98
|
+
this.noteDeclaredColumn(index);
|
|
99
|
+
for (const rect of this.#storage.mergeRects)
|
|
100
|
+
this.noteMerge(rect);
|
|
101
|
+
}
|
|
102
|
+
}
|
package/dist/core/value.d.ts
CHANGED
|
@@ -15,6 +15,15 @@ export type ValueType = (typeof ValueType)[keyof typeof ValueType];
|
|
|
15
15
|
/** The canonical Excel error literals a cell (or formula result) can carry. */
|
|
16
16
|
export declare const ERROR_CODES: readonly ['#N/A', '#REF!', '#NAME?', '#DIV/0!', '#NULL!', '#VALUE!', '#NUM!', '#SPILL!', '#CALC!', '#GETTING_DATA'];
|
|
17
17
|
export type ErrorCode = (typeof ERROR_CODES)[number];
|
|
18
|
+
/**
|
|
19
|
+
* The error a spreadsheet puts in place of a reference that has nowhere left to point.
|
|
20
|
+
*
|
|
21
|
+
* Named here rather than in the two formula codecs that produce it. Both of them -- the A1 renderer
|
|
22
|
+
* in `core/formula.ts` and the BIFF12 one in `io/xlsb/formula.ts` -- had a private literal of their
|
|
23
|
+
* own, spelled the same and named differently, for a value {@link ERROR_CODES} already publishes and
|
|
24
|
+
* `isErrorCode` already recognises.
|
|
25
|
+
*/
|
|
26
|
+
export declare const REF_ERROR: ErrorCode;
|
|
18
27
|
/** An in-cell error, e.g. `{error: '#REF!'}`. */
|
|
19
28
|
export interface ErrorValue {
|
|
20
29
|
readonly error: ErrorCode;
|
package/dist/core/value.js
CHANGED
|
@@ -21,6 +21,7 @@ export const ERROR_CODES = [
|
|
|
21
21
|
'#CALC!',
|
|
22
22
|
'#GETTING_DATA',
|
|
23
23
|
];
|
|
24
|
+
export const REF_ERROR = '#REF!';
|
|
24
25
|
const ERROR_SET = new Set(ERROR_CODES);
|
|
25
26
|
function hasKey(value, key) {
|
|
26
27
|
return typeof value === 'object' && value !== null && key in value;
|
|
@@ -47,58 +48,59 @@ export function richTextToPlain(value) {
|
|
|
47
48
|
return value.richText.map((run) => run.text).join('');
|
|
48
49
|
}
|
|
49
50
|
export function cellValueToText(value) {
|
|
50
|
-
|
|
51
|
-
return '';
|
|
52
|
-
switch (typeof value) {
|
|
53
|
-
case 'number':
|
|
54
|
-
return String(value);
|
|
55
|
-
case 'string':
|
|
56
|
-
return value;
|
|
57
|
-
case 'boolean':
|
|
58
|
-
return value ? 'TRUE' : 'FALSE';
|
|
59
|
-
default:
|
|
60
|
-
break;
|
|
61
|
-
}
|
|
62
|
-
if (value instanceof Date)
|
|
63
|
-
return Number.isNaN(value.getTime()) ? '' : value.toISOString();
|
|
64
|
-
if (isHyperlinkValue(value)) {
|
|
65
|
-
return typeof value.text === 'string' ? value.text : richTextToPlain(value.text);
|
|
66
|
-
}
|
|
67
|
-
if (isFormulaValue(value) || isSharedFormulaValue(value) || isDataTableFormulaValue(value)) {
|
|
68
|
-
return value.result === undefined ? '' : cellValueToText(value.result);
|
|
69
|
-
}
|
|
70
|
-
if (isRichTextValue(value))
|
|
71
|
-
return richTextToPlain(value);
|
|
72
|
-
if (isErrorValue(value))
|
|
73
|
-
return value.error;
|
|
74
|
-
return unsupportedValue(value);
|
|
51
|
+
return classify(value, TO_TEXT);
|
|
75
52
|
}
|
|
76
53
|
export function detectValueType(value) {
|
|
54
|
+
return classify(value, TO_TYPE);
|
|
55
|
+
}
|
|
56
|
+
function classify(value, visit) {
|
|
77
57
|
if (value === null)
|
|
78
|
-
return
|
|
58
|
+
return visit.null(value);
|
|
79
59
|
switch (typeof value) {
|
|
80
60
|
case 'number':
|
|
81
|
-
return
|
|
61
|
+
return visit.number(value);
|
|
82
62
|
case 'string':
|
|
83
|
-
return
|
|
63
|
+
return visit.string(value);
|
|
84
64
|
case 'boolean':
|
|
85
|
-
return
|
|
65
|
+
return visit.boolean(value);
|
|
86
66
|
default:
|
|
87
67
|
break;
|
|
88
68
|
}
|
|
89
69
|
if (value instanceof Date)
|
|
90
|
-
return
|
|
70
|
+
return visit.date(value);
|
|
91
71
|
if (isHyperlinkValue(value))
|
|
92
|
-
return
|
|
72
|
+
return visit.hyperlink(value);
|
|
93
73
|
if (isFormulaValue(value) || isSharedFormulaValue(value) || isDataTableFormulaValue(value)) {
|
|
94
|
-
return
|
|
74
|
+
return visit.formula(value);
|
|
95
75
|
}
|
|
96
76
|
if (isRichTextValue(value))
|
|
97
|
-
return
|
|
77
|
+
return visit.richText(value);
|
|
98
78
|
if (isErrorValue(value))
|
|
99
|
-
return
|
|
79
|
+
return visit.error(value);
|
|
100
80
|
return unsupportedValue(value);
|
|
101
81
|
}
|
|
82
|
+
const TO_TYPE = {
|
|
83
|
+
null: () => ValueType.Null,
|
|
84
|
+
number: () => ValueType.Number,
|
|
85
|
+
string: () => ValueType.String,
|
|
86
|
+
boolean: () => ValueType.Boolean,
|
|
87
|
+
date: () => ValueType.Date,
|
|
88
|
+
error: () => ValueType.Error,
|
|
89
|
+
formula: () => ValueType.Formula,
|
|
90
|
+
richText: () => ValueType.RichText,
|
|
91
|
+
hyperlink: () => ValueType.Hyperlink,
|
|
92
|
+
};
|
|
93
|
+
const TO_TEXT = {
|
|
94
|
+
null: () => '',
|
|
95
|
+
number: (value) => String(value),
|
|
96
|
+
string: (value) => value,
|
|
97
|
+
boolean: (value) => (value ? 'TRUE' : 'FALSE'),
|
|
98
|
+
date: (value) => (Number.isNaN(value.getTime()) ? '' : value.toISOString()),
|
|
99
|
+
error: (value) => value.error,
|
|
100
|
+
formula: (value) => (value.result === undefined ? '' : cellValueToText(value.result)),
|
|
101
|
+
richText: richTextToPlain,
|
|
102
|
+
hyperlink: (value) => (typeof value.text === 'string' ? value.text : richTextToPlain(value.text)),
|
|
103
|
+
};
|
|
102
104
|
function unsupportedValue(value) {
|
|
103
105
|
throw new TypeError(`unsupported cell value: ${describe(value)}`);
|
|
104
106
|
}
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { type WorkbookImage } from './image.ts';
|
|
2
|
+
export declare class WorkbookMedia {
|
|
3
|
+
#private;
|
|
4
|
+
/** The registered images, indexed by the id {@link register} returned. Live, not a copy. */
|
|
5
|
+
get all(): readonly WorkbookImage[];
|
|
6
|
+
/** Look up a registered image by its id, or `undefined` if no image carries that id. */
|
|
7
|
+
get(id: number): WorkbookImage | undefined;
|
|
8
|
+
/**
|
|
9
|
+
* {@link get} for a caller that has no answer for an absent id.
|
|
10
|
+
*
|
|
11
|
+
* An unregistered id is what a sheet belonging to *another* workbook looks like from here, which
|
|
12
|
+
* is why the message names the sheet rather than the id alone. Emitting a package with a drawing
|
|
13
|
+
* pointing at media nobody registered is the silently-broken-image failure this refuses to start.
|
|
14
|
+
*
|
|
15
|
+
* @throws {AuthoringError} if no image carries that id.
|
|
16
|
+
*/
|
|
17
|
+
require(id: number, forSheetName: string): WorkbookImage;
|
|
18
|
+
/** Store a picture's bytes and return the id that names them. */
|
|
19
|
+
register(extension: string | undefined, buffer: Uint8Array): number;
|
|
20
|
+
/**
|
|
21
|
+
* Register a picture arriving from elsewhere, re-using an identical one already held.
|
|
22
|
+
*
|
|
23
|
+
* The extension is re-normalised rather than trusted: a hand-built {@link WorkbookImage} may carry
|
|
24
|
+
* `".PNG"` where the registry holds `"png"`, and two spellings of one kind must not read as two
|
|
25
|
+
* pictures.
|
|
26
|
+
*
|
|
27
|
+
* Through the content index rather than a scan. Comparing byte-by-byte against every held picture
|
|
28
|
+
* made importing n distinct images cost n squared byte comparisons: fifty 1 MB pictures carried
|
|
29
|
+
* between workbooks compared about 2.5 GB. The *rule* is unchanged, and `imageContentKey` states
|
|
30
|
+
* why identity here is content and never object identity. The index is built in reverse so the
|
|
31
|
+
* FIRST id registered under a key wins, which is the answer the scan gave.
|
|
32
|
+
*/
|
|
33
|
+
registerExisting(image: WorkbookImage): number;
|
|
34
|
+
}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { AuthoringError, quoted } from '../errors.js';
|
|
2
|
+
import { imageContentKey, normalizeImageExtension } from './image.js';
|
|
3
|
+
export class WorkbookMedia {
|
|
4
|
+
#images = [];
|
|
5
|
+
#byContent;
|
|
6
|
+
get all() {
|
|
7
|
+
return this.#images;
|
|
8
|
+
}
|
|
9
|
+
get(id) {
|
|
10
|
+
return this.#images[id];
|
|
11
|
+
}
|
|
12
|
+
require(id, forSheetName) {
|
|
13
|
+
const image = this.#images[id];
|
|
14
|
+
if (image === undefined) {
|
|
15
|
+
throw new AuthoringError(`worksheet ${quoted(forSheetName)} shows image id ${id}, which is not registered on this ` +
|
|
16
|
+
"workbook: a sheet's images can only be exported by the workbook that holds them");
|
|
17
|
+
}
|
|
18
|
+
return image;
|
|
19
|
+
}
|
|
20
|
+
register(extension, buffer) {
|
|
21
|
+
const image = {
|
|
22
|
+
extension: normalizeImageExtension(extension, buffer),
|
|
23
|
+
data: buffer,
|
|
24
|
+
};
|
|
25
|
+
this.#images.push(image);
|
|
26
|
+
const id = this.#images.length - 1;
|
|
27
|
+
const index = this.#byContent;
|
|
28
|
+
if (index !== undefined) {
|
|
29
|
+
const key = imageContentKey(image);
|
|
30
|
+
if (!index.has(key))
|
|
31
|
+
index.set(key, id);
|
|
32
|
+
}
|
|
33
|
+
return id;
|
|
34
|
+
}
|
|
35
|
+
registerExisting(image) {
|
|
36
|
+
const candidate = {
|
|
37
|
+
extension: normalizeImageExtension(image.extension, image.data),
|
|
38
|
+
data: image.data,
|
|
39
|
+
};
|
|
40
|
+
this.#byContent ??= new Map(this.#images.map((held, id) => [imageContentKey(held), id]).reverse());
|
|
41
|
+
return (this.#byContent.get(imageContentKey(candidate)) ??
|
|
42
|
+
this.register(candidate.extension, candidate.data));
|
|
43
|
+
}
|
|
44
|
+
}
|
|
@@ -1,5 +1,71 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { CellStyle } from './style.ts';
|
|
2
2
|
import type { TableStyle } from './table-style.ts';
|
|
3
|
+
/**
|
|
4
|
+
* A named cell style: the OOXML `cellStyleXfs`/`cellStyles` layer. A spreadsheet applies a built-in
|
|
5
|
+
* or custom style (e.g. "Normal", "Accent1") whose visual facets live in this shared, named layer
|
|
6
|
+
* rather than on each cell's direct format; a cell links to it and inherits any facet the direct
|
|
7
|
+
* format leaves unset. The facets are a cell's own (see {@link CellStyle}); `name` is the style's
|
|
8
|
+
* display name and `builtinId` its Excel gallery index when it is a built-in style.
|
|
9
|
+
*/
|
|
10
|
+
export type NamedCellStyle = Readonly<CellStyle> & {
|
|
11
|
+
readonly name?: string;
|
|
12
|
+
readonly builtinId?: number;
|
|
13
|
+
};
|
|
14
|
+
/**
|
|
15
|
+
* A differential style (OOXML CT_Dxf): formatting laid *over* whatever a cell already carries. Only
|
|
16
|
+
* the facets present override; the rest of the cell's own style shows through. It carries the subset
|
|
17
|
+
* of the cell-style facets (see {@link CellStyle}) a `<dxf>` can express: font, number format, fill,
|
|
18
|
+
* and border.
|
|
19
|
+
*
|
|
20
|
+
* Differential styles live in one workbook-level table (`<dxfs>`) that several features index into:
|
|
21
|
+
* a conditional-formatting rule's highlight format, and a table style's per-element formatting
|
|
22
|
+
* (`<tableStyleElement dxfId="…">`). They are interned and shared, so two features asking for the
|
|
23
|
+
* same formatting land on one entry.
|
|
24
|
+
*
|
|
25
|
+
* Not every facet reaches every consumer. As a **table style element**, Excel applies only the font,
|
|
26
|
+
* fill, and border: its own object model exposes `Font`, `Interior`, and `Borders` on a table style
|
|
27
|
+
* element and nothing for a number format, so a `numFmt` set here is carried faithfully through a
|
|
28
|
+
* round-trip but has no visible effect. The type is left whole rather than split, because the same
|
|
29
|
+
* value is legitimately reused across both consumers and narrowing it would only move the surprise.
|
|
30
|
+
*/
|
|
31
|
+
export type DifferentialStyle = Pick<CellStyle, 'font' | 'numFmt' | 'fill' | 'border'>;
|
|
32
|
+
/**
|
|
33
|
+
* The `<tableStyles>` block of a styles part: the custom table/pivot style definitions a file
|
|
34
|
+
* declares, and the two gallery names it nominates as the default for a new table and a new pivot.
|
|
35
|
+
*
|
|
36
|
+
* Each entry of {@link styles} is one `<tableStyle>…</tableStyle>` fragment kept verbatim, for the
|
|
37
|
+
* same reason a `<dxf>` is: a `tableStyleElement`'s `dxfId` indexes the differential-style table,
|
|
38
|
+
* which the writer re-emits **at its original indices**, so the references stay valid without
|
|
39
|
+
* reparsing anything. That index-stability is load-bearing: renumbering the dxf table would
|
|
40
|
+
* silently re-point every preserved table style at a different format.
|
|
41
|
+
*
|
|
42
|
+
* The two default names are ordinary strings, not fragments: they are re-escaped on write, so they
|
|
43
|
+
* are held decoded.
|
|
44
|
+
*/
|
|
45
|
+
export interface TableStyleTable {
|
|
46
|
+
readonly styles: readonly string[];
|
|
47
|
+
readonly defaultTableStyle?: string | undefined;
|
|
48
|
+
readonly defaultPivotStyle?: string | undefined;
|
|
49
|
+
/**
|
|
50
|
+
* The namespace prefixes the verbatim {@link styles} fragments use, mapped to their URI and to
|
|
51
|
+
* whether the source marked the prefix ignorable (`mc:Ignorable`).
|
|
52
|
+
*
|
|
53
|
+
* Carrying a fragment verbatim carries its *prefixes* too. Excel stamps a revision id
|
|
54
|
+
* (`xr9:uid="{…}"`) on every `<tableStyle>` it writes, so a fragment re-emitted under a
|
|
55
|
+
* `<styleSheet>` that declares only the default namespace is not namespace-well-formed, and no
|
|
56
|
+
* consumer can parse the part at all, which is a far louder failure than the dropped table style
|
|
57
|
+
* this preservation exists to prevent. The writer re-declares each prefix on `<styleSheet>` and
|
|
58
|
+
* re-states the ignorable ones, exactly as the source did.
|
|
59
|
+
*/
|
|
60
|
+
readonly namespaces?: readonly TableStyleNamespace[];
|
|
61
|
+
}
|
|
62
|
+
/** One namespace declaration a preserved `<tableStyle>` fragment depends on. */
|
|
63
|
+
export interface TableStyleNamespace {
|
|
64
|
+
readonly prefix: string;
|
|
65
|
+
readonly uri: string;
|
|
66
|
+
/** Whether the source listed this prefix in the stylesheet's `mc:Ignorable`. */
|
|
67
|
+
readonly ignorable: boolean;
|
|
68
|
+
}
|
|
3
69
|
/**
|
|
4
70
|
* The preserved style tables of a workbook: the `<dxfs>` fragments, the named cell styles, the two
|
|
5
71
|
* colour lists, and the table-style definitions a file declared plus the ones a caller authored.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { isRelType } from '../rel-type.js';
|
|
1
2
|
import { addVbaReference, parseVbaProject, removeVbaModule, VBA_PROJECT_CONTENT_TYPE, VBA_PROJECT_PART_PATH, VBA_PROJECT_REL_TYPE, VbaAuthorError, vbaProjectSignatureKind, } from '../vba/index.js';
|
|
2
3
|
import { replaceContents } from './containers.js';
|
|
3
4
|
export class WorkbookVbaProject {
|
|
@@ -21,7 +22,7 @@ export class WorkbookVbaProject {
|
|
|
21
22
|
set bytes(bytes) {
|
|
22
23
|
if (bytes !== undefined)
|
|
23
24
|
parseVbaProject(bytes);
|
|
24
|
-
replaceContents(this.#references, this.#references.filter((r) => !r.relType
|
|
25
|
+
replaceContents(this.#references, this.#references.filter((r) => !isRelType(r.relType, 'vbaProject')));
|
|
25
26
|
if (bytes !== undefined) {
|
|
26
27
|
this.#references.push({
|
|
27
28
|
relType: VBA_PROJECT_REL_TYPE,
|
|
@@ -70,7 +71,7 @@ export class WorkbookVbaProject {
|
|
|
70
71
|
this.bytes = addVbaReference(bytes, ref);
|
|
71
72
|
}
|
|
72
73
|
#ref() {
|
|
73
|
-
return this.#references.find((r) => r.relType
|
|
74
|
+
return this.#references.find((r) => isRelType(r.relType, 'vbaProject'));
|
|
74
75
|
}
|
|
75
76
|
#entry() {
|
|
76
77
|
const ref = this.#ref();
|