@shbernal/ts-xlsx 1.2.0 → 2.0.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 +5 -0
- package/dist/core/address.d.ts +49 -2
- package/dist/core/address.js +31 -3
- package/dist/core/autofilter.d.ts +2 -2
- package/dist/core/cell.d.ts +17 -16
- package/dist/core/cell.js +3 -7
- package/dist/core/color-resolution.d.ts +7 -7
- package/dist/core/column.d.ts +8 -9
- package/dist/core/column.js +2 -4
- package/dist/core/comment-thread.d.ts +32 -23
- package/dist/core/comment-thread.js +4 -4
- package/dist/core/conditional-formatting-overlay.d.ts +1 -1
- package/dist/core/conditional-formatting.d.ts +3 -3
- package/dist/core/data-validation-overlay.d.ts +1 -1
- package/dist/core/data-validation.d.ts +3 -3
- package/dist/core/date.d.ts +3 -3
- package/dist/core/formula.d.ts +6 -6
- package/dist/core/grid-edits.js +3 -3
- package/dist/core/image.d.ts +37 -2
- package/dist/core/image.js +6 -0
- package/dist/core/internal.d.ts +9 -1
- package/dist/core/limits.d.ts +4 -4
- package/dist/core/merge.d.ts +6 -12
- package/dist/core/merge.js +0 -3
- package/dist/core/page-setup.d.ts +4 -4
- package/dist/core/pivot-table.d.ts +2 -4
- package/dist/core/pivot-table.js +5 -5
- package/dist/core/preserved.d.ts +3 -3
- package/dist/core/protection.d.ts +4 -4
- package/dist/core/range.d.ts +16 -14
- package/dist/core/range.js +5 -5
- package/dist/core/row-input.d.ts +2 -8
- package/dist/core/row-input.js +2 -2
- package/dist/core/row.d.ts +7 -8
- package/dist/core/row.js +2 -4
- package/dist/core/style.d.ts +14 -14
- package/dist/core/table-style.d.ts +9 -9
- package/dist/core/table-style.js +1 -1
- package/dist/core/table.d.ts +30 -42
- package/dist/core/table.js +16 -10
- package/dist/core/theme.d.ts +13 -43
- package/dist/core/theme.js +2 -148
- package/dist/core/value.d.ts +7 -7
- package/dist/core/workbook-protection.d.ts +2 -2
- package/dist/core/workbook-styles.d.ts +26 -0
- package/dist/core/workbook-styles.js +48 -0
- package/dist/core/workbook-theme.d.ts +25 -0
- package/dist/core/workbook-theme.js +49 -0
- package/dist/core/workbook-vba.d.ts +17 -0
- package/dist/core/workbook-vba.js +79 -0
- package/dist/core/workbook.d.ts +141 -83
- package/dist/core/workbook.js +74 -133
- package/dist/core/worksheet-comments.d.ts +9 -0
- package/dist/core/worksheet-comments.js +52 -0
- package/dist/core/worksheet-model.d.ts +4 -4
- package/dist/core/worksheet-model.js +1 -0
- package/dist/core/worksheet-pictures.d.ts +29 -0
- package/dist/core/worksheet-pictures.js +42 -0
- package/dist/core/worksheet.d.ts +94 -77
- package/dist/core/worksheet.js +36 -95
- package/dist/customui/errors.d.ts +1 -1
- package/dist/customui/ribbon.d.ts +3 -3
- package/dist/entries/core.d.ts +4 -4
- package/dist/entries/core.js +1 -1
- package/dist/entries/vba.d.ts +1 -1
- package/dist/entries/vba.js +1 -1
- package/dist/entries/xlsx.d.ts +1 -0
- package/dist/entries/xlsx.js +1 -0
- package/dist/errors.d.ts +12 -12
- package/dist/io/csv/read.js +1 -2
- package/dist/io/csv/write.d.ts +8 -3
- package/dist/io/csv/write.js +17 -2
- package/dist/io/opc/errors.d.ts +6 -6
- package/dist/io/opc/inflate.d.ts +1 -1
- package/dist/io/opc/namespaces.d.ts +1 -1
- package/dist/io/opc/read-opc.d.ts +19 -3
- package/dist/io/opc/read-opc.js +14 -8
- package/dist/io/opc/read-options.d.ts +2 -2
- package/dist/io/opc/sniff-format.d.ts +2 -2
- package/dist/io/opc/zip-mtime.d.ts +5 -0
- package/dist/io/opc/zip-mtime.js +1 -0
- package/dist/io/style/xf-style.d.ts +38 -6
- package/dist/io/style/xf-style.js +22 -0
- package/dist/io/xlsb/errors.d.ts +1 -1
- package/dist/io/xlsb/formula.d.ts +4 -4
- package/dist/io/xlsb/primitives.d.ts +8 -8
- package/dist/io/xlsb/ptg-functions.d.ts +1 -1
- package/dist/io/xlsb/read-styles.js +2 -20
- package/dist/io/xlsb/read.d.ts +2 -2
- package/dist/io/xlsx/cell-accumulator.d.ts +25 -6
- package/dist/io/xlsx/cell-accumulator.js +111 -30
- package/dist/io/xlsx/cell-value.d.ts +2 -2
- package/dist/io/xlsx/cell-value.js +3 -3
- package/dist/io/xlsx/color-xml.js +11 -17
- package/dist/io/xlsx/comments.d.ts +5 -5
- package/dist/io/xlsx/comments.js +13 -6
- package/dist/io/xlsx/conditional-formatting.d.ts +3 -3
- package/dist/io/xlsx/conditional-formatting.js +7 -16
- package/dist/io/xlsx/data-validation.d.ts +2 -2
- package/dist/io/xlsx/data-validation.js +1 -1
- package/dist/io/xlsx/edit-vba.js +2 -1
- package/dist/io/xlsx/errors.d.ts +3 -3
- package/dist/io/xlsx/hyperlinks.d.ts +4 -2
- package/dist/io/xlsx/hyperlinks.js +5 -5
- package/dist/io/xlsx/images.d.ts +1 -1
- package/dist/io/xlsx/images.js +8 -8
- package/dist/io/xlsx/namespaces.d.ts +2 -2
- package/dist/io/xlsx/package-plan.js +5 -2
- package/dist/io/xlsx/read-pivot.js +2 -5
- package/dist/io/xlsx/read-rows.d.ts +13 -13
- package/dist/io/xlsx/read-rows.js +19 -55
- package/dist/io/xlsx/read-shared-strings.js +7 -5
- package/dist/io/xlsx/read-styles.d.ts +3 -3
- package/dist/io/xlsx/read-styles.js +40 -65
- package/dist/io/xlsx/read-worksheet.js +67 -115
- package/dist/io/xlsx/read.d.ts +3 -5
- package/dist/io/xlsx/read.js +94 -71
- package/dist/io/xlsx/rich-runs.d.ts +1 -1
- package/dist/io/xlsx/rich-runs.js +1 -1
- package/dist/io/xlsx/rich-text.d.ts +1 -1
- package/dist/io/xlsx/shared-strings.d.ts +2 -2
- package/dist/io/xlsx/sheet-properties.js +5 -4
- package/dist/io/xlsx/styles.d.ts +8 -9
- package/dist/io/xlsx/styles.js +4 -3
- package/dist/io/xlsx/tables.d.ts +2 -2
- package/dist/io/xlsx/tables.js +8 -13
- package/dist/io/xlsx/theme-xml.d.ts +37 -0
- package/dist/io/xlsx/theme-xml.js +142 -0
- package/dist/io/xlsx/threaded-comments.d.ts +18 -27
- package/dist/io/xlsx/threaded-comments.js +9 -21
- package/dist/io/xlsx/workbook-xml.d.ts +1 -1
- package/dist/io/xlsx/workbook-xml.js +6 -2
- package/dist/io/xlsx/worksheet-xml.d.ts +2 -2
- package/dist/io/xlsx/worksheet-xml.js +7 -12
- package/dist/io/xlsx/write-stream.d.ts +15 -15
- package/dist/io/xlsx/write-stream.js +5 -3
- package/dist/io/xlsx/write.d.ts +12 -7
- package/dist/io/xlsx/write.js +10 -6
- package/dist/io/xlsx/x14-ext.d.ts +1 -1
- package/dist/vba/bytes.d.ts +12 -0
- package/dist/vba/bytes.js +40 -0
- package/dist/vba/cfb-writer.d.ts +1 -1
- package/dist/vba/cfb.d.ts +1 -1
- package/dist/vba/cfb.js +15 -51
- package/dist/vba/codepage.d.ts +2 -2
- package/dist/vba/errors.d.ts +2 -2
- package/dist/vba/index.d.ts +2 -2
- package/dist/vba/index.js +2 -2
- package/dist/vba/ms-ovba.d.ts +1 -1
- package/dist/vba/ms-ovba.js +1 -3
- package/dist/vba/project-editor.d.ts +9 -9
- package/dist/vba/project-editor.js +2 -11
- package/dist/vba/project.d.ts +5 -5
- package/dist/vba/project.js +1 -10
- package/dist/vba/vba-encoding.d.ts +1 -1
- package/dist/xml/errors.d.ts +1 -1
- package/dist/xml/xml-read.d.ts +38 -9
- package/dist/xml/xml-read.js +23 -0
- package/dist/xml/xml.d.ts +31 -9
- package/dist/xml/xml.js +23 -3
- package/package.json +41 -35
package/dist/core/workbook.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
import { isCustomUiRelType, parseCustomUi } from '../customui/index.js';
|
|
2
2
|
import { AuthoringError } from '../errors.js';
|
|
3
|
-
import { addVbaReference,
|
|
4
|
-
import { resolveColor } from './color-resolution.js';
|
|
3
|
+
import { addVbaReference, removeVbaModule, } from '../vba/index.js';
|
|
5
4
|
import { commentThreadGuid } from './comment-thread.js';
|
|
6
|
-
import {
|
|
7
|
-
import { normalizeImageExtension } from './image.js';
|
|
5
|
+
import { findRegisteredImage, normalizeImageExtension, } from './image.js';
|
|
8
6
|
import { INTERNAL } from './internal.js';
|
|
9
7
|
import { checkTableStyle } from './table-style.js';
|
|
10
|
-
import {
|
|
8
|
+
import { OFFICE_BODY_FACE, THEME_COLOR_SLOTS, } from './theme.js';
|
|
9
|
+
import { WorkbookStyleTables } from './workbook-styles.js';
|
|
10
|
+
import { WorkbookTheme } from './workbook-theme.js';
|
|
11
|
+
import { WorkbookVbaProject } from './workbook-vba.js';
|
|
11
12
|
import { Worksheet } from './worksheet.js';
|
|
12
13
|
export const DEFAULT_WORKBOOK_VIEW = {
|
|
13
14
|
x: -110,
|
|
@@ -27,12 +28,8 @@ export class Workbook {
|
|
|
27
28
|
#nextSheetId = 1;
|
|
28
29
|
#media = [];
|
|
29
30
|
#definedNames = [];
|
|
30
|
-
#
|
|
31
|
-
#
|
|
32
|
-
#indexedColors = [];
|
|
33
|
-
#mruColors = [];
|
|
34
|
-
#tableStyles = { styles: [] };
|
|
35
|
-
#theme;
|
|
31
|
+
#styles = new WorkbookStyleTables();
|
|
32
|
+
#theme = new WorkbookTheme(() => this.#styles.indexedPalette());
|
|
36
33
|
#preservedReferences = [];
|
|
37
34
|
#persons = new Map();
|
|
38
35
|
get worksheets() {
|
|
@@ -65,131 +62,58 @@ export class Workbook {
|
|
|
65
62
|
}
|
|
66
63
|
return this.#customUI;
|
|
67
64
|
}
|
|
68
|
-
#
|
|
69
|
-
#vbaProject = undefined;
|
|
65
|
+
#vba = new WorkbookVbaProject(this.#preservedReferences);
|
|
70
66
|
get vbaProject() {
|
|
71
|
-
|
|
72
|
-
const bytes = this.#vbaProjectEntry()?.bytes;
|
|
73
|
-
this.#vbaProject = bytes ? parseVbaProject(bytes) : undefined;
|
|
74
|
-
this.#vbaParsed = true;
|
|
75
|
-
}
|
|
76
|
-
return this.#vbaProject;
|
|
67
|
+
return this.#vba.project;
|
|
77
68
|
}
|
|
78
69
|
get vbaProjectBytes() {
|
|
79
|
-
return this.#
|
|
70
|
+
return this.#vba.bytes;
|
|
80
71
|
}
|
|
81
72
|
set vbaProjectBytes(bytes) {
|
|
82
|
-
|
|
83
|
-
parseVbaProject(bytes);
|
|
84
|
-
replaceContents(this.#preservedReferences, this.#preservedReferences.filter((r) => !r.relType.endsWith('/vbaProject')));
|
|
85
|
-
if (bytes !== undefined) {
|
|
86
|
-
this.#preservedReferences.push({
|
|
87
|
-
relType: VBA_PROJECT_REL_TYPE,
|
|
88
|
-
entryPath: VBA_PROJECT_PART_PATH,
|
|
89
|
-
parts: [
|
|
90
|
-
{
|
|
91
|
-
path: VBA_PROJECT_PART_PATH,
|
|
92
|
-
contentType: VBA_PROJECT_CONTENT_TYPE,
|
|
93
|
-
bytes: bytes.slice(),
|
|
94
|
-
rels: [],
|
|
95
|
-
},
|
|
96
|
-
],
|
|
97
|
-
});
|
|
98
|
-
}
|
|
99
|
-
this.#vbaParsed = false;
|
|
100
|
-
this.#vbaProject = undefined;
|
|
73
|
+
this.#vba.bytes = bytes;
|
|
101
74
|
}
|
|
102
75
|
get vbaProjectSigned() {
|
|
103
|
-
return this.#
|
|
76
|
+
return this.#vba.signatures.length > 0;
|
|
104
77
|
}
|
|
105
78
|
get vbaProjectSignatures() {
|
|
106
|
-
return this.#
|
|
107
|
-
}
|
|
108
|
-
#vbaSignatures() {
|
|
109
|
-
const ref = this.#vbaProjectRef();
|
|
110
|
-
const entry = ref?.parts.find((p) => p.path === ref.entryPath);
|
|
111
|
-
if (ref === undefined || entry === undefined)
|
|
112
|
-
return [];
|
|
113
|
-
const partByPath = new Map(ref.parts.map((p) => [p.path, p]));
|
|
114
|
-
const signatures = [];
|
|
115
|
-
for (const rel of entry.rels) {
|
|
116
|
-
const kind = vbaProjectSignatureKind(rel.type);
|
|
117
|
-
const part = kind === undefined ? undefined : partByPath.get(rel.targetPath);
|
|
118
|
-
if (kind !== undefined && part !== undefined) {
|
|
119
|
-
signatures.push({ kind, bytes: part.bytes.slice() });
|
|
120
|
-
}
|
|
121
|
-
}
|
|
122
|
-
return signatures;
|
|
79
|
+
return this.#vba.signatures;
|
|
123
80
|
}
|
|
124
81
|
removeVbaModule(name) {
|
|
125
|
-
|
|
126
|
-
if (bytes === undefined) {
|
|
127
|
-
throw new VbaAuthorError('workbook has no VBA project to remove a module from');
|
|
128
|
-
}
|
|
129
|
-
this.vbaProjectBytes = removeVbaModule(bytes, name);
|
|
82
|
+
this.#vba.removeModule(name);
|
|
130
83
|
}
|
|
131
84
|
addVbaReference(ref) {
|
|
132
|
-
|
|
133
|
-
if (bytes === undefined) {
|
|
134
|
-
throw new VbaAuthorError('workbook has no VBA project to add a reference to');
|
|
135
|
-
}
|
|
136
|
-
this.vbaProjectBytes = addVbaReference(bytes, ref);
|
|
137
|
-
}
|
|
138
|
-
#vbaProjectRef() {
|
|
139
|
-
return this.#preservedReferences.find((r) => r.relType.endsWith('/vbaProject'));
|
|
140
|
-
}
|
|
141
|
-
#vbaProjectEntry() {
|
|
142
|
-
const ref = this.#vbaProjectRef();
|
|
143
|
-
return ref?.parts.find((p) => p.path === ref.entryPath);
|
|
85
|
+
this.#vba.addReference(ref);
|
|
144
86
|
}
|
|
145
87
|
get differentialStyles() {
|
|
146
|
-
return this.#
|
|
88
|
+
return this.#styles.differentialStyles;
|
|
147
89
|
}
|
|
148
90
|
get indexedColors() {
|
|
149
|
-
return this.#indexedColors;
|
|
91
|
+
return this.#styles.indexedColors;
|
|
150
92
|
}
|
|
151
93
|
get mruColors() {
|
|
152
|
-
return this.#mruColors;
|
|
94
|
+
return this.#styles.mruColors;
|
|
153
95
|
}
|
|
154
96
|
get tableStyles() {
|
|
155
|
-
return this.#tableStyles;
|
|
97
|
+
return this.#styles.tableStyles;
|
|
156
98
|
}
|
|
157
|
-
#customTableStyles = new Map();
|
|
158
99
|
addTableStyle(style) {
|
|
159
100
|
checkTableStyle(style);
|
|
160
|
-
this.#
|
|
101
|
+
this.#styles.addCustomTableStyle(style);
|
|
161
102
|
}
|
|
162
103
|
get customTableStyles() {
|
|
163
|
-
return
|
|
104
|
+
return this.#styles.customTableStyles;
|
|
164
105
|
}
|
|
165
106
|
get themePart() {
|
|
166
|
-
return this.#theme;
|
|
107
|
+
return this.#theme.part;
|
|
167
108
|
}
|
|
168
|
-
#themeColors;
|
|
169
|
-
#authoredTheme = {
|
|
170
|
-
colors: {},
|
|
171
|
-
fonts: {},
|
|
172
|
-
};
|
|
173
109
|
setTheme(overrides) {
|
|
174
|
-
|
|
175
|
-
Object.assign(this.#authoredTheme.colors, overrides.colors ?? {});
|
|
176
|
-
this.#authoredTheme.fonts = { ...this.#authoredTheme.fonts, ...(overrides.fonts ?? {}) };
|
|
177
|
-
this.#themeColors = undefined;
|
|
110
|
+
this.#theme.author(overrides);
|
|
178
111
|
}
|
|
179
112
|
get themeColors() {
|
|
180
|
-
|
|
181
|
-
const xml = this.#themeXml();
|
|
182
|
-
const parsed = xml === undefined ? {} : parseThemeColorScheme(xml);
|
|
183
|
-
const base = Object.keys(parsed).length === 0 ? DEFAULT_THEME_COLOR_SCHEME : parsed;
|
|
184
|
-
this.#themeColors = { ...base, ...this.#authoredTheme.colors };
|
|
185
|
-
}
|
|
186
|
-
return this.#themeColors;
|
|
113
|
+
return this.#theme.colors;
|
|
187
114
|
}
|
|
188
115
|
get themeFonts() {
|
|
189
|
-
|
|
190
|
-
const parsed = xml === undefined ? {} : parseThemeFontScheme(xml);
|
|
191
|
-
const base = Object.keys(parsed).length === 0 ? DEFAULT_THEME_FONTS : parsed;
|
|
192
|
-
return { ...base, ...this.#authoredTheme.fonts };
|
|
116
|
+
return this.#theme.fonts;
|
|
193
117
|
}
|
|
194
118
|
#declaredDefaultFont;
|
|
195
119
|
#authoredDefaultFont;
|
|
@@ -198,17 +122,17 @@ export class Workbook {
|
|
|
198
122
|
}
|
|
199
123
|
setDefaultFont(font) {
|
|
200
124
|
if (font.size !== undefined && !(Number.isFinite(font.size) && font.size > 0)) {
|
|
201
|
-
throw new
|
|
125
|
+
throw new RangeError(`default font size must be a positive number, not ${font.size}`);
|
|
202
126
|
}
|
|
203
127
|
if (font.name !== undefined && font.name === '') {
|
|
204
|
-
throw new
|
|
128
|
+
throw new RangeError('default font name cannot be empty');
|
|
205
129
|
}
|
|
206
130
|
this.#authoredDefaultFont = { ...this.#authoredDefaultFont, ...font };
|
|
207
131
|
}
|
|
208
132
|
get defaultFont() {
|
|
209
133
|
const declared = this.#declaredDefaultFont;
|
|
210
134
|
const authored = this.#authoredDefaultFont;
|
|
211
|
-
const authoredFace = this.#
|
|
135
|
+
const authoredFace = this.#theme.authoredFonts.minor;
|
|
212
136
|
if (declared !== undefined && authored === undefined && authoredFace === undefined) {
|
|
213
137
|
return declared;
|
|
214
138
|
}
|
|
@@ -236,30 +160,14 @@ export class Workbook {
|
|
|
236
160
|
}
|
|
237
161
|
return font;
|
|
238
162
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
if (Object.keys(colors).length === 0 && Object.keys(fonts).length === 0)
|
|
242
|
-
return undefined;
|
|
243
|
-
return applyThemeOverrides(this.#baseThemeXml(), { colors, fonts });
|
|
244
|
-
}
|
|
245
|
-
#baseThemeXml() {
|
|
246
|
-
return this.#themeXml() ?? DEFAULT_THEME_XML;
|
|
247
|
-
}
|
|
248
|
-
#themeXml() {
|
|
249
|
-
const theme = this.#theme;
|
|
250
|
-
if (theme === undefined)
|
|
251
|
-
return undefined;
|
|
252
|
-
const entry = theme.parts.find((part) => part.path === theme.entryPath);
|
|
253
|
-
return entry === undefined ? undefined : new TextDecoder().decode(entry.bytes);
|
|
163
|
+
get themeOverrides() {
|
|
164
|
+
return this.#theme.overrides;
|
|
254
165
|
}
|
|
255
166
|
resolveColor(color) {
|
|
256
|
-
return resolveColor(color
|
|
257
|
-
}
|
|
258
|
-
#indexedPalette() {
|
|
259
|
-
return this.#indexedColors.map((fragment) => /\brgb="([^"]*)"/.exec(fragment)?.[1] ?? '');
|
|
167
|
+
return this.#theme.resolveColor(color);
|
|
260
168
|
}
|
|
261
169
|
get namedStyles() {
|
|
262
|
-
return this.#namedStyles;
|
|
170
|
+
return this.#styles.namedStyles;
|
|
263
171
|
}
|
|
264
172
|
addPerson(person) {
|
|
265
173
|
const id = commentThreadGuid(person.id, 'a person id');
|
|
@@ -284,6 +192,40 @@ export class Workbook {
|
|
|
284
192
|
getImage(id) {
|
|
285
193
|
return this.#media[id];
|
|
286
194
|
}
|
|
195
|
+
exportImages(sheet) {
|
|
196
|
+
const resolve = (id) => {
|
|
197
|
+
const image = this.#media[id];
|
|
198
|
+
if (image === undefined) {
|
|
199
|
+
throw new AuthoringError(`worksheet "${sheet.name}" shows image id ${id}, which is not registered on this ` +
|
|
200
|
+
"workbook: a sheet's images can only be exported by the workbook that holds them");
|
|
201
|
+
}
|
|
202
|
+
return image;
|
|
203
|
+
};
|
|
204
|
+
const background = sheet.backgroundImageId;
|
|
205
|
+
return {
|
|
206
|
+
anchored: sheet.images.map(({ imageId, anchor }) => ({ image: resolve(imageId), anchor })),
|
|
207
|
+
background: background === undefined ? undefined : resolve(background),
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
importImages(sheet, images) {
|
|
211
|
+
for (const id of new Set(sheet.images.map((image) => image.imageId)))
|
|
212
|
+
sheet.removeImage(id);
|
|
213
|
+
sheet.removeBackgroundImage();
|
|
214
|
+
for (const { image, anchor } of images.anchored) {
|
|
215
|
+
sheet.addImageAnchor(this.#registerImage(image), anchor);
|
|
216
|
+
}
|
|
217
|
+
if (images.background !== undefined) {
|
|
218
|
+
sheet.addBackgroundImage(this.#registerImage(images.background));
|
|
219
|
+
}
|
|
220
|
+
}
|
|
221
|
+
#registerImage(image) {
|
|
222
|
+
const candidate = {
|
|
223
|
+
extension: normalizeImageExtension(image.extension, image.data),
|
|
224
|
+
data: image.data,
|
|
225
|
+
};
|
|
226
|
+
return (findRegisteredImage(this.#media, candidate) ??
|
|
227
|
+
this.addImage({ buffer: candidate.data, extension: candidate.extension }));
|
|
228
|
+
}
|
|
287
229
|
get definedNames() {
|
|
288
230
|
return this.#definedNames;
|
|
289
231
|
}
|
|
@@ -345,23 +287,22 @@ export class Workbook {
|
|
|
345
287
|
this.#preservedRootReferences.push(reference);
|
|
346
288
|
},
|
|
347
289
|
restoreDifferentialStyles: (fragments) => {
|
|
348
|
-
|
|
290
|
+
this.#styles.restoreDifferentialStyles(fragments);
|
|
349
291
|
},
|
|
350
292
|
restoreIndexedColors: (fragments) => {
|
|
351
|
-
|
|
293
|
+
this.#styles.restoreIndexedColors(fragments);
|
|
352
294
|
},
|
|
353
295
|
restoreMruColors: (fragments) => {
|
|
354
|
-
|
|
296
|
+
this.#styles.restoreMruColors(fragments);
|
|
355
297
|
},
|
|
356
298
|
restoreTableStyles: (table) => {
|
|
357
|
-
this.#
|
|
299
|
+
this.#styles.restoreTableStyles(table);
|
|
358
300
|
},
|
|
359
|
-
restoreThemePart: (theme) => {
|
|
360
|
-
this.#theme
|
|
361
|
-
this.#themeColors = undefined;
|
|
301
|
+
restoreThemePart: (theme, declared) => {
|
|
302
|
+
this.#theme.restorePart(theme, declared);
|
|
362
303
|
},
|
|
363
304
|
restoreNamedStyles: (styles) => {
|
|
364
|
-
|
|
305
|
+
this.#styles.restoreNamedStyles(styles);
|
|
365
306
|
},
|
|
366
307
|
restoreDefaultFont: (font) => {
|
|
367
308
|
this.#declaredDefaultFont = font;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type CommentThread } from './comment-thread.ts';
|
|
2
|
+
export declare class WorksheetComments {
|
|
3
|
+
#private;
|
|
4
|
+
constructor(sheetName: () => string);
|
|
5
|
+
get threads(): readonly CommentThread[];
|
|
6
|
+
add(thread: CommentThread): void;
|
|
7
|
+
at(reference: string): CommentThread | undefined;
|
|
8
|
+
restore(threads: readonly CommentThread[]): void;
|
|
9
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { decodeCellRef, encodeAddress } from './address.js';
|
|
2
|
+
import { commentThreadGuid, commentThreadOffset } from './comment-thread.js';
|
|
3
|
+
import { replaceContents } from './containers.js';
|
|
4
|
+
export class WorksheetComments {
|
|
5
|
+
#sheetName;
|
|
6
|
+
#threads = [];
|
|
7
|
+
constructor(sheetName) {
|
|
8
|
+
this.#sheetName = sheetName;
|
|
9
|
+
}
|
|
10
|
+
get threads() {
|
|
11
|
+
return this.#threads;
|
|
12
|
+
}
|
|
13
|
+
add(thread) {
|
|
14
|
+
const taken = new Set(this.#threads.flatMap((held) => held.comments.map((comment) => comment.id)));
|
|
15
|
+
const comments = thread.comments.map((comment) => {
|
|
16
|
+
const id = commentThreadGuid(comment.id, 'a comment id');
|
|
17
|
+
if (taken.has(id)) {
|
|
18
|
+
throw new SyntaxError(`a comment id must be unique within a sheet, but "${id}" is already used on "${this.#sheetName()}": ` +
|
|
19
|
+
'a reply and the legacy fallback comment both find their thread by it');
|
|
20
|
+
}
|
|
21
|
+
taken.add(id);
|
|
22
|
+
return {
|
|
23
|
+
...comment,
|
|
24
|
+
id,
|
|
25
|
+
...(comment.personId !== undefined
|
|
26
|
+
? { personId: commentThreadGuid(comment.personId, "a comment's author id") }
|
|
27
|
+
: {}),
|
|
28
|
+
mentions: comment.mentions.map((mention) => ({
|
|
29
|
+
...mention,
|
|
30
|
+
personId: commentThreadGuid(mention.personId, "a mention's person id"),
|
|
31
|
+
startIndex: commentThreadOffset(mention.startIndex, "a mention's startIndex"),
|
|
32
|
+
length: commentThreadOffset(mention.length, "a mention's length"),
|
|
33
|
+
...(mention.mentionId !== undefined
|
|
34
|
+
? { mentionId: commentThreadGuid(mention.mentionId, 'a mention id') }
|
|
35
|
+
: {}),
|
|
36
|
+
})),
|
|
37
|
+
};
|
|
38
|
+
});
|
|
39
|
+
this.#threads.push({ ...thread, ref: anchorRef(thread.ref), comments });
|
|
40
|
+
}
|
|
41
|
+
at(reference) {
|
|
42
|
+
const anchor = anchorRef(reference);
|
|
43
|
+
return this.#threads.find((thread) => thread.ref === anchor);
|
|
44
|
+
}
|
|
45
|
+
restore(threads) {
|
|
46
|
+
replaceContents(this.#threads, threads);
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function anchorRef(reference) {
|
|
50
|
+
const { col, row } = decodeCellRef(reference);
|
|
51
|
+
return encodeAddress(col, row);
|
|
52
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type AssertNever } from './internal.ts';
|
|
1
2
|
import type { Worksheet, WorksheetModel } from './worksheet.ts';
|
|
2
3
|
/** One field of a {@link WorksheetModel}, with both directions of its round-trip declared together. */
|
|
3
4
|
interface ModelFacet<K extends keyof WorksheetModel = keyof WorksheetModel> {
|
|
@@ -7,12 +8,12 @@ interface ModelFacet<K extends keyof WorksheetModel = keyof WorksheetModel> {
|
|
|
7
8
|
/**
|
|
8
9
|
* Apply the field to a sheet whose content has already been reset. Takes the whole model rather
|
|
9
10
|
* than the field so that a loop over the registry needs no correlation between `key` and the
|
|
10
|
-
* field's type
|
|
11
|
+
* field's type. {@link facet} does that projection once, where the key is still a single type.
|
|
11
12
|
*
|
|
12
13
|
* The obvious shape, `write(sheet, value: WorksheetModel[K])` stored as-is, cannot be called from
|
|
13
14
|
* a loop: over a union of `ModelFacet<K>` the parameter is contravariant under
|
|
14
15
|
* `strictFunctionTypes`, which breaks the correlation. Declaring `write` with method syntax makes
|
|
15
|
-
* it compile
|
|
16
|
+
* it compile, by making the position bivariant, which buys the call back by switching the check
|
|
16
17
|
* off. Projecting inside the helper is correlated *and* sound; do not "simplify" it back.
|
|
17
18
|
*/
|
|
18
19
|
readonly write: (sheet: Worksheet, model: WorksheetModel) => void;
|
|
@@ -22,8 +23,7 @@ interface ModelFacet<K extends keyof WorksheetModel = keyof WorksheetModel> {
|
|
|
22
23
|
* load-bearing: cells are placed at their exact positions before any merge exists, so a covered
|
|
23
24
|
* cell's value lands where the model says instead of being routed to a region master mid-load.
|
|
24
25
|
*/
|
|
25
|
-
export declare const WORKSHEET_MODEL_FACETS: (ModelFacet<"autoFilter"> | ModelFacet<"cells"> | ModelFacet<"columnBreaks"> | ModelFacet<"columns"> | ModelFacet<"conditionalFormattings"> | ModelFacet<"dataValidations"> | ModelFacet<"headerFooter"> | ModelFacet<"merges"> | ModelFacet<"outline"> | ModelFacet<"pageMargins"> | ModelFacet<"pageSetup"> | ModelFacet<"printOptions"> | ModelFacet<"properties"> | ModelFacet<"protection"> | ModelFacet<"rowBreaks"> | ModelFacet<"rows"> | ModelFacet<"state"> | ModelFacet<"tabColor"> | ModelFacet<"tables">)[];
|
|
26
|
-
type AssertNever<T extends never> = T;
|
|
26
|
+
export declare const WORKSHEET_MODEL_FACETS: (ModelFacet<"autoFilter"> | ModelFacet<"cells"> | ModelFacet<"columnBreaks"> | ModelFacet<"columns"> | ModelFacet<"conditionalFormattings"> | ModelFacet<"dataValidations"> | ModelFacet<"headerFooter"> | ModelFacet<"merges"> | ModelFacet<"outline"> | ModelFacet<"pageMargins"> | ModelFacet<"pageSetup"> | ModelFacet<"printOptions"> | ModelFacet<"properties"> | ModelFacet<"protection"> | ModelFacet<"rowBreaks"> | ModelFacet<"rows"> | ModelFacet<"state"> | ModelFacet<"tabColor"> | ModelFacet<"tables"> | ModelFacet<"view">)[];
|
|
27
27
|
/**
|
|
28
28
|
* Compile-time proof that {@link WORKSHEET_MODEL_FACETS} covers every {@link WorksheetModel} field.
|
|
29
29
|
* A field added without a facet resolves this to that field's name, which does not satisfy `never`,
|
|
@@ -15,6 +15,7 @@ export const WORKSHEET_MODEL_FACETS = [
|
|
|
15
15
|
}),
|
|
16
16
|
facet('properties', (sheet) => ({ ...sheet.properties }), (sheet, value) => overwrite(sheet.properties, value)),
|
|
17
17
|
facet('outline', (sheet) => ({ ...sheet.outline }), (sheet, value) => overwrite(sheet.outline, value)),
|
|
18
|
+
facet('view', (sheet) => ({ ...sheet.view }), (sheet, value) => overwrite(sheet.view, value)),
|
|
18
19
|
facet('pageSetup', (sheet) => ({ ...sheet.pageSetup }), (sheet, value) => overwrite(sheet.pageSetup, value)),
|
|
19
20
|
facet('printOptions', (sheet) => ({ ...sheet.printOptions }), (sheet, value) => overwrite(sheet.printOptions, value)),
|
|
20
21
|
facet('pageMargins', (sheet) => ({ ...sheet.pageMargins }), (sheet, value) => overwrite(sheet.pageMargins, value)),
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { type AnchoredImage, type AnchorPoint, type ImageAnchor, type ImageEditAs } from './image.ts';
|
|
2
|
+
/** The two shapes {@link WorksheetPictures.add} accepts, in caller-facing pixel units. */
|
|
3
|
+
export type PixelAnchor = {
|
|
4
|
+
readonly tl: AnchorPoint;
|
|
5
|
+
readonly br: AnchorPoint;
|
|
6
|
+
readonly editAs?: ImageEditAs;
|
|
7
|
+
} | {
|
|
8
|
+
readonly tl: AnchorPoint;
|
|
9
|
+
readonly ext: {
|
|
10
|
+
readonly width: number;
|
|
11
|
+
readonly height: number;
|
|
12
|
+
};
|
|
13
|
+
};
|
|
14
|
+
/** How a sheet reports the sizes an anchor point resolves against; `undefined` means "the default". */
|
|
15
|
+
export interface AnchorMetrics {
|
|
16
|
+
columnWidth(col: number): number | undefined;
|
|
17
|
+
rowHeight(row: number): number | undefined;
|
|
18
|
+
}
|
|
19
|
+
export declare class WorksheetPictures {
|
|
20
|
+
#private;
|
|
21
|
+
constructor(metrics: AnchorMetrics);
|
|
22
|
+
/** The anchored images, in the order they were added. */
|
|
23
|
+
get anchors(): AnchoredImage[];
|
|
24
|
+
get backgroundImageId(): number | undefined;
|
|
25
|
+
add(imageId: number, anchor: PixelAnchor): void;
|
|
26
|
+
addAnchor(imageId: number, anchor: ImageAnchor): void;
|
|
27
|
+
remove(imageId: number): void;
|
|
28
|
+
setBackground(imageId: number | undefined): void;
|
|
29
|
+
}
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { replaceContents } from './containers.js';
|
|
2
|
+
import { PX_TO_EMU, resolveAnchorPoint, } from './image.js';
|
|
3
|
+
export class WorksheetPictures {
|
|
4
|
+
#metrics;
|
|
5
|
+
#anchors = [];
|
|
6
|
+
#backgroundImageId;
|
|
7
|
+
constructor(metrics) {
|
|
8
|
+
this.#metrics = metrics;
|
|
9
|
+
}
|
|
10
|
+
get anchors() {
|
|
11
|
+
return this.#anchors;
|
|
12
|
+
}
|
|
13
|
+
get backgroundImageId() {
|
|
14
|
+
return this.#backgroundImageId;
|
|
15
|
+
}
|
|
16
|
+
add(imageId, anchor) {
|
|
17
|
+
const columnWidth = (col) => this.#metrics.columnWidth(col);
|
|
18
|
+
const rowHeight = (row) => this.#metrics.rowHeight(row);
|
|
19
|
+
if ('ext' in anchor) {
|
|
20
|
+
const ext = {
|
|
21
|
+
cx: Math.round(anchor.ext.width * PX_TO_EMU),
|
|
22
|
+
cy: Math.round(anchor.ext.height * PX_TO_EMU),
|
|
23
|
+
};
|
|
24
|
+
const from = resolveAnchorPoint(anchor.tl, columnWidth, rowHeight);
|
|
25
|
+
this.#anchors.push({ imageId, anchor: { from, ext } });
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const from = resolveAnchorPoint(anchor.tl, columnWidth, rowHeight);
|
|
29
|
+
const to = resolveAnchorPoint(anchor.br, columnWidth, rowHeight);
|
|
30
|
+
const twoCell = anchor.editAs !== undefined ? { from, to, editAs: anchor.editAs } : { from, to };
|
|
31
|
+
this.#anchors.push({ imageId, anchor: twoCell });
|
|
32
|
+
}
|
|
33
|
+
addAnchor(imageId, anchor) {
|
|
34
|
+
this.#anchors.push({ imageId, anchor });
|
|
35
|
+
}
|
|
36
|
+
remove(imageId) {
|
|
37
|
+
replaceContents(this.#anchors, this.#anchors.filter((image) => image.imageId !== imageId));
|
|
38
|
+
}
|
|
39
|
+
setBackground(imageId) {
|
|
40
|
+
this.#backgroundImageId = imageId;
|
|
41
|
+
}
|
|
42
|
+
}
|