@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
|
@@ -1,8 +1,11 @@
|
|
|
1
1
|
import { isCfValueObjectType, isConditionalFormattingOperator, isConditionalFormattingType, isIconSetType, isCfTimePeriod, } from '../../core/conditional-formatting.js';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { stripFormulaEquals } from '../../core/formula.js';
|
|
3
|
+
import { coerceNumericLiteral, enumToken, numFinite, numInteger } from '../../xml/xml-attrs.js';
|
|
4
|
+
import { elementSubtrees, TextCapture, } from '../../xml/xml-read.js';
|
|
5
|
+
import { boolPresent, boolStrict, localName } from '../../xml/xml-scan.js';
|
|
6
|
+
import { boolAttr, checkedToken, escapeAttr, escapeText, numberText, textAttr, } from '../../xml/xml.js';
|
|
4
7
|
import { colorAttrs, parseColor } from './color-xml.js';
|
|
5
|
-
import { CF_EXT_URI, DATABAR_LINK_EXT_URI, XM_NS } from './namespaces.js';
|
|
8
|
+
import { CF_EXT_URI, DATABAR_LINK_EXT_URI, isExtensionElement, XM_NS } from './namespaces.js';
|
|
6
9
|
import { x14Ext } from './x14-ext.js';
|
|
7
10
|
const DEFAULT_DATABAR_CFVO = [{ type: 'min' }, { type: 'max' }];
|
|
8
11
|
const DEFAULT_DATABAR_COLOR = { argb: 'FF638EC6' };
|
|
@@ -14,7 +17,7 @@ function needsDataBarExt(rule) {
|
|
|
14
17
|
function dataBarExtGuid(index) {
|
|
15
18
|
return `{00000000-0000-0000-0000-${String(index + 1).padStart(12, '0')}}`;
|
|
16
19
|
}
|
|
17
|
-
function dataBarExtLinks(formattings) {
|
|
20
|
+
export function dataBarExtLinks(formattings) {
|
|
18
21
|
const links = new Map();
|
|
19
22
|
let index = 0;
|
|
20
23
|
for (const cf of formattings) {
|
|
@@ -27,15 +30,13 @@ function dataBarExtLinks(formattings) {
|
|
|
27
30
|
}
|
|
28
31
|
const SCALE_KINDS = ['dataBar', 'colorScale', 'iconSet'];
|
|
29
32
|
const SCALE_TYPES = new Set(SCALE_KINDS);
|
|
30
|
-
export function conditionalFormattingsXml(formattings, styles) {
|
|
33
|
+
export function conditionalFormattingsXml(formattings, styles, extLinks) {
|
|
31
34
|
if (formattings.length === 0)
|
|
32
35
|
return '';
|
|
33
36
|
const priority = { next: 1 };
|
|
34
|
-
const extLinks = dataBarExtLinks(formattings);
|
|
35
37
|
return formattings.map((cf) => blockXml(cf, styles, priority, extLinks)).join('');
|
|
36
38
|
}
|
|
37
|
-
export function conditionalFormattingsExtXml(formattings) {
|
|
38
|
-
const extLinks = dataBarExtLinks(formattings);
|
|
39
|
+
export function conditionalFormattingsExtXml(formattings, extLinks) {
|
|
39
40
|
const items = [];
|
|
40
41
|
for (const cf of formattings) {
|
|
41
42
|
for (const rule of cf.rules) {
|
|
@@ -49,8 +50,7 @@ export function conditionalFormattingsExtXml(formattings) {
|
|
|
49
50
|
return x14Ext(CF_EXT_URI, `<x14:conditionalFormattings>${items.join('')}</x14:conditionalFormattings>`);
|
|
50
51
|
}
|
|
51
52
|
function x14DataBarXml(ref, rule, guid) {
|
|
52
|
-
const
|
|
53
|
-
const anchors = cfvo.map(x14CfvoXml).join('');
|
|
53
|
+
const anchors = dataBarAnchors(rule).map(x14Cfvo).join('');
|
|
54
54
|
const gradient = boolAttr('gradient', rule.gradient);
|
|
55
55
|
const negative = rule.negativeFillColor !== undefined
|
|
56
56
|
? `<x14:negativeFillColor ${colorAttrs(rule.negativeFillColor)}/>`
|
|
@@ -61,12 +61,23 @@ function x14DataBarXml(ref, rule, guid) {
|
|
|
61
61
|
`<x14:dataBar${gradient}>${anchors}${negative}${axis}</x14:dataBar>` +
|
|
62
62
|
`</x14:cfRule><xm:sqref>${escapeText(ref)}</xm:sqref></x14:conditionalFormatting>`);
|
|
63
63
|
}
|
|
64
|
-
function
|
|
65
|
-
|
|
66
|
-
if (cfvo.value === undefined)
|
|
67
|
-
return `<x14:cfvo type="${type}"/>`;
|
|
68
|
-
return `<x14:cfvo type="${type}"><xm:f>${escapeText(String(cfvo.value))}</xm:f></x14:cfvo>`;
|
|
64
|
+
function dataBarAnchors(rule) {
|
|
65
|
+
return rule.cfvo && rule.cfvo.length > 0 ? rule.cfvo : DEFAULT_DATABAR_CFVO;
|
|
69
66
|
}
|
|
67
|
+
function cfvoWriter(form) {
|
|
68
|
+
return (cfvo) => {
|
|
69
|
+
const type = checkedToken(cfvo.type, isCfValueObjectType, 'conditional format value type');
|
|
70
|
+
const tag = form === 'classic' ? 'cfvo' : 'x14:cfvo';
|
|
71
|
+
if (cfvo.value === undefined)
|
|
72
|
+
return `<${tag} type="${type}"/>`;
|
|
73
|
+
const value = String(cfvo.value);
|
|
74
|
+
return form === 'classic'
|
|
75
|
+
? `<${tag} type="${type}"${textAttr('val', value)}/>`
|
|
76
|
+
: `<${tag} type="${type}"><xm:f>${escapeText(value)}</xm:f></${tag}>`;
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
const cfvoXml = cfvoWriter('classic');
|
|
80
|
+
const x14Cfvo = cfvoWriter('x14');
|
|
70
81
|
function cfRuleExtLinkXml(guid) {
|
|
71
82
|
return `<extLst>${x14Ext(DATABAR_LINK_EXT_URI, `<x14:id>${guid}</x14:id>`)}</extLst>`;
|
|
72
83
|
}
|
|
@@ -139,9 +150,8 @@ function scaleXml(rule) {
|
|
|
139
150
|
return iconSetXml(rule);
|
|
140
151
|
}
|
|
141
152
|
function dataBarXml(rule) {
|
|
142
|
-
const cfvo = rule.cfvo && rule.cfvo.length > 0 ? rule.cfvo : DEFAULT_DATABAR_CFVO;
|
|
143
153
|
const color = rule.color ?? DEFAULT_DATABAR_COLOR;
|
|
144
|
-
const anchors =
|
|
154
|
+
const anchors = dataBarAnchors(rule).map(cfvoXml).join('');
|
|
145
155
|
return `<dataBar>${anchors}<color ${colorAttrs(color)}/></dataBar>`;
|
|
146
156
|
}
|
|
147
157
|
function colorScaleXml(rule) {
|
|
@@ -156,12 +166,7 @@ function iconSetXml(rule) {
|
|
|
156
166
|
const anchors = (rule.cfvo ?? []).map(cfvoXml).join('');
|
|
157
167
|
return `<iconSet${name}>${anchors}</iconSet>`;
|
|
158
168
|
}
|
|
159
|
-
function
|
|
160
|
-
const type = checkedToken(cfvo.type, isCfValueObjectType, 'conditional format value type');
|
|
161
|
-
const val = cfvo.value === undefined ? '' : textAttr('val', String(cfvo.value));
|
|
162
|
-
return `<cfvo type="${type}"${val}/>`;
|
|
163
|
-
}
|
|
164
|
-
export function parseConditionalFormattings(xml) {
|
|
169
|
+
export function conditionalFormattingPass() {
|
|
165
170
|
const blocks = [];
|
|
166
171
|
let block;
|
|
167
172
|
let draft;
|
|
@@ -172,10 +177,10 @@ export function parseConditionalFormattings(xml) {
|
|
|
172
177
|
let x14Ext;
|
|
173
178
|
let x14ExtId;
|
|
174
179
|
const x14IdCapture = new TextCapture('id');
|
|
175
|
-
|
|
176
|
-
onOpen(name, attrs, selfClosing) {
|
|
180
|
+
const handlers = {
|
|
181
|
+
onOpen(name, attrs, selfClosing, scope) {
|
|
177
182
|
const ln = localName(name);
|
|
178
|
-
if (name
|
|
183
|
+
if (isExtensionElement(scope, name)) {
|
|
179
184
|
if (ln === 'id' && draft !== undefined) {
|
|
180
185
|
x14IdCapture.open(ln, selfClosing);
|
|
181
186
|
}
|
|
@@ -232,9 +237,9 @@ export function parseConditionalFormattings(xml) {
|
|
|
232
237
|
formulaCapture.text(chunk);
|
|
233
238
|
x14IdCapture.text(chunk);
|
|
234
239
|
},
|
|
235
|
-
onClose(name) {
|
|
240
|
+
onClose(name, scope) {
|
|
236
241
|
const ln = localName(name);
|
|
237
|
-
if (name
|
|
242
|
+
if (isExtensionElement(scope, name)) {
|
|
238
243
|
const id = x14IdCapture.close(ln);
|
|
239
244
|
if (id !== undefined) {
|
|
240
245
|
if (draft !== undefined)
|
|
@@ -270,29 +275,29 @@ export function parseConditionalFormattings(xml) {
|
|
|
270
275
|
block = undefined;
|
|
271
276
|
}
|
|
272
277
|
},
|
|
273
|
-
}
|
|
274
|
-
|
|
275
|
-
const
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
278
|
+
};
|
|
279
|
+
const result = () => {
|
|
280
|
+
for (const { rule, id } of linked) {
|
|
281
|
+
const ext = extById.get(id);
|
|
282
|
+
if (ext === undefined)
|
|
283
|
+
continue;
|
|
284
|
+
if (ext.gradient !== undefined)
|
|
285
|
+
rule.gradient = ext.gradient;
|
|
286
|
+
if (ext.negativeFillColor !== undefined)
|
|
287
|
+
rule.negativeFillColor = ext.negativeFillColor;
|
|
288
|
+
if (ext.axisColor !== undefined)
|
|
289
|
+
rule.axisColor = ext.axisColor;
|
|
290
|
+
}
|
|
291
|
+
return blocks;
|
|
292
|
+
};
|
|
293
|
+
return { handlers, result };
|
|
286
294
|
}
|
|
287
295
|
function emptyExt() {
|
|
288
296
|
return { gradient: undefined, negativeFillColor: undefined, axisColor: undefined };
|
|
289
297
|
}
|
|
290
298
|
export function parseDxfs(stylesXml) {
|
|
291
|
-
const
|
|
292
|
-
|
|
293
|
-
return [];
|
|
294
|
-
const inner = block[1] ?? '';
|
|
295
|
-
return [...inner.matchAll(/<dxf\b[^>]*>[\s\S]*?<\/dxf>|<dxf\b[^>]*\/>/g)].map((m) => m[0] ?? '');
|
|
299
|
+
const { fragments } = elementSubtrees(stylesXml, new Map([['dxfs', 'dxf']]));
|
|
300
|
+
return [...(fragments.get('dxfs') ?? [])];
|
|
296
301
|
}
|
|
297
302
|
function newDraft(attrs) {
|
|
298
303
|
return {
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { type DataValidationEntry } from '../../core/data-validation.ts';
|
|
2
2
|
import type { Worksheet } from '../../core/worksheet.ts';
|
|
3
|
+
import { type CollectingPass } from '../../xml/xml-read.ts';
|
|
3
4
|
/** The standard `<dataValidations>` element for the rules stored in the legacy form, or '' when the
|
|
4
5
|
* sheet has none of them, so a sheet with only extended (or no) validations stays byte-clean here.
|
|
5
6
|
* The extended rules are emitted separately by {@link dataValidationsExtXml}. */
|
|
@@ -8,13 +9,14 @@ export declare function dataValidationsXml(entries: readonly DataValidationEntry
|
|
|
8
9
|
* none. Emitted bare (no `<extLst>` wrapper) so the worksheet serialiser can gather it into a single
|
|
9
10
|
* `<extLst>` beside the conditional-formatting extension: a worksheet may carry at most one. */
|
|
10
11
|
export declare function dataValidationsExtXml(entries: readonly DataValidationEntry[]): string;
|
|
11
|
-
/**
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
|
|
12
|
+
/** A pass gathering the standard `<dataValidation>` elements of a worksheet part, for a caller
|
|
13
|
+
* reading the part alongside its other readers in one parse. */
|
|
14
|
+
export declare function dataValidationPass(): CollectingPass<DataValidationEntry[]>;
|
|
15
|
+
/** A pass gathering the extended `<x14:dataValidation>` elements of a worksheet's `<extLst>`, so a
|
|
16
|
+
* cross-sheet or whole-column list validation Excel stored only in the 2009 extension form is read
|
|
17
|
+
* back rather than dropped. The standard pass ignores these (they are prefixed); this one,
|
|
18
|
+
* symmetrically, handles only the prefixed elements. */
|
|
19
|
+
export declare function extendedDataValidationPass(): CollectingPass<DataValidationEntry[]>;
|
|
18
20
|
/** Fold parsed validations onto a sheet, each bound to its original range and carrying its form: an
|
|
19
21
|
* `extended` entry is re-attached as extended so a round-trip writes it back to the x14 block. */
|
|
20
22
|
export declare function applyDataValidations(sheet: Worksheet, entries: readonly DataValidationEntry[]): void;
|
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
import { isDataValidationErrorStyle, isDataValidationOperator, isDataValidationType, } from '../../core/data-validation.js';
|
|
2
|
+
import { stripFormulaEquals } from '../../core/formula.js';
|
|
2
3
|
import { decodeSqrefRects } from '../../core/merge.js';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
4
|
+
import { coerceNumericLiteral } from '../../xml/xml-attrs.js';
|
|
5
|
+
import { TextCapture } from '../../xml/xml-read.js';
|
|
6
|
+
import { boolStrict, localName } from '../../xml/xml-scan.js';
|
|
7
|
+
import { checkedToken, escapeAttr, escapeText, textAttr } from '../../xml/xml.js';
|
|
8
|
+
import { DATA_VALIDATION_EXT_URI, isExtensionElement, isMainNamespaceElement, XM_NS, } from './namespaces.js';
|
|
9
|
+
import { admitting } from './read-repair.js';
|
|
6
10
|
import { x14Ext } from './x14-ext.js';
|
|
7
11
|
const TYPED = new Set(['whole', 'decimal', 'date', 'time', 'textLength']);
|
|
8
12
|
export function dataValidationsXml(entries) {
|
|
@@ -57,34 +61,30 @@ function extendedDataValidationXml(sqref, rule) {
|
|
|
57
61
|
`<xm:sqref>${escapeText(sqref)}</xm:sqref>`;
|
|
58
62
|
return `<x14:dataValidation${ruleAttrs(rule)}>${body}</x14:dataValidation>`;
|
|
59
63
|
}
|
|
60
|
-
|
|
64
|
+
const FORMULA_ELEMENTS = new Set(['formula1', 'formula2']);
|
|
65
|
+
export function dataValidationPass() {
|
|
61
66
|
const entries = [];
|
|
62
67
|
let current;
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
onOpen(name, attrs) {
|
|
68
|
+
const formula = new TextCapture(FORMULA_ELEMENTS);
|
|
69
|
+
const handlers = {
|
|
70
|
+
onOpen(name, attrs, selfClosing, scope) {
|
|
66
71
|
const ln = localName(name);
|
|
67
|
-
if (ln === 'dataValidation' &&
|
|
72
|
+
if (ln === 'dataValidation' && isMainNamespaceElement(scope, name)) {
|
|
68
73
|
current = { attrs, formulae: [] };
|
|
69
74
|
}
|
|
70
|
-
else if (current !== undefined
|
|
71
|
-
|
|
72
|
-
current.formulae[0] = '';
|
|
73
|
-
}
|
|
74
|
-
else if (current !== undefined && ln === 'formula2') {
|
|
75
|
-
slot = 1;
|
|
76
|
-
current.formulae[1] = '';
|
|
75
|
+
else if (current !== undefined) {
|
|
76
|
+
formula.open(ln, selfClosing);
|
|
77
77
|
}
|
|
78
78
|
},
|
|
79
79
|
onText(text) {
|
|
80
|
-
|
|
81
|
-
current.formulae[slot] = (current.formulae[slot] ?? '') + text;
|
|
82
|
-
}
|
|
80
|
+
formula.text(text);
|
|
83
81
|
},
|
|
84
82
|
onClose(name) {
|
|
85
83
|
const ln = localName(name);
|
|
86
|
-
|
|
87
|
-
|
|
84
|
+
const text = formula.close(ln);
|
|
85
|
+
if (text !== undefined) {
|
|
86
|
+
if (current !== undefined)
|
|
87
|
+
current.formulae[ln === 'formula1' ? 0 : 1] = text;
|
|
88
88
|
return;
|
|
89
89
|
}
|
|
90
90
|
if (ln === 'dataValidation' && current !== undefined) {
|
|
@@ -94,8 +94,8 @@ export function parseDataValidations(xml) {
|
|
|
94
94
|
current = undefined;
|
|
95
95
|
}
|
|
96
96
|
},
|
|
97
|
-
}
|
|
98
|
-
return entries;
|
|
97
|
+
};
|
|
98
|
+
return { handlers, result: () => entries };
|
|
99
99
|
}
|
|
100
100
|
function buildEntry(attrs, formulae) {
|
|
101
101
|
const { sqref } = attrs;
|
|
@@ -139,22 +139,22 @@ function buildRule(attrs, formulae) {
|
|
|
139
139
|
rule.formulae = parsed;
|
|
140
140
|
return rule;
|
|
141
141
|
}
|
|
142
|
-
export function
|
|
142
|
+
export function extendedDataValidationPass() {
|
|
143
143
|
const entries = [];
|
|
144
144
|
let current;
|
|
145
145
|
let slot;
|
|
146
146
|
const capture = new TextCapture(['f', 'sqref']);
|
|
147
|
-
|
|
148
|
-
onOpen(name, attrs, selfClosing) {
|
|
147
|
+
const handlers = {
|
|
148
|
+
onOpen(name, attrs, selfClosing, scope) {
|
|
149
149
|
const ln = localName(name);
|
|
150
|
-
const
|
|
151
|
-
if (ln === 'dataValidation' &&
|
|
150
|
+
const extension = isExtensionElement(scope, name);
|
|
151
|
+
if (ln === 'dataValidation' && extension) {
|
|
152
152
|
current = { attrs, formulae: [], sqref: '' };
|
|
153
153
|
}
|
|
154
|
-
else if (current !== undefined &&
|
|
154
|
+
else if (current !== undefined && extension && ln === 'formula1') {
|
|
155
155
|
slot = 0;
|
|
156
156
|
}
|
|
157
|
-
else if (current !== undefined &&
|
|
157
|
+
else if (current !== undefined && extension && ln === 'formula2') {
|
|
158
158
|
slot = 1;
|
|
159
159
|
}
|
|
160
160
|
else if (current !== undefined) {
|
|
@@ -164,8 +164,9 @@ export function parseExtendedDataValidations(xml) {
|
|
|
164
164
|
onText(chunk) {
|
|
165
165
|
capture.text(chunk);
|
|
166
166
|
},
|
|
167
|
-
onClose(name) {
|
|
167
|
+
onClose(name, scope) {
|
|
168
168
|
const ln = localName(name);
|
|
169
|
+
const extension = isExtensionElement(scope, name);
|
|
169
170
|
const text = capture.close(ln);
|
|
170
171
|
if (text !== undefined) {
|
|
171
172
|
if (ln === 'f') {
|
|
@@ -176,18 +177,18 @@ export function parseExtendedDataValidations(xml) {
|
|
|
176
177
|
current.sqref = text;
|
|
177
178
|
}
|
|
178
179
|
}
|
|
179
|
-
else if ((ln === 'formula1' || ln === 'formula2') &&
|
|
180
|
+
else if ((ln === 'formula1' || ln === 'formula2') && extension) {
|
|
180
181
|
slot = undefined;
|
|
181
182
|
}
|
|
182
|
-
else if (ln === 'dataValidation' &&
|
|
183
|
+
else if (ln === 'dataValidation' && extension && current !== undefined) {
|
|
183
184
|
const built = buildExtendedEntry(current.attrs, current.formulae, current.sqref);
|
|
184
185
|
if (built !== undefined)
|
|
185
186
|
entries.push(built);
|
|
186
187
|
current = undefined;
|
|
187
188
|
}
|
|
188
189
|
},
|
|
189
|
-
}
|
|
190
|
-
return entries;
|
|
190
|
+
};
|
|
191
|
+
return { handlers, result: () => entries };
|
|
191
192
|
}
|
|
192
193
|
function buildExtendedEntry(attrs, formulae, sqref) {
|
|
193
194
|
if (sqref === '')
|
|
@@ -199,6 +200,8 @@ export function applyDataValidations(sheet, entries) {
|
|
|
199
200
|
for (const { sqref, rule, extended } of entries) {
|
|
200
201
|
if (decodeSqrefRects(sqref).length === 0)
|
|
201
202
|
continue;
|
|
202
|
-
|
|
203
|
+
admitting(() => {
|
|
204
|
+
sheet.addDataValidation(sqref, rule, extended ? { extended: true } : {});
|
|
205
|
+
});
|
|
203
206
|
}
|
|
204
207
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type VbaLibraryReference } from '../../vba/project-editor.ts';
|
|
2
|
+
import { type ReadPackageOptions } from '../opc/read-options.ts';
|
|
2
3
|
/**
|
|
3
4
|
* Remove a standard module from an existing macro-enabled package's VBA project, returning new package
|
|
4
5
|
* bytes. Every part but `xl/vbaProject.bin` is preserved byte-for-byte (see {@link removeVbaModule} for
|
|
@@ -8,8 +9,10 @@ import { type VbaLibraryReference } from '../../vba/project-editor.ts';
|
|
|
8
9
|
* @throws {VbaAuthorError} if the package carries no VBA project, `name` is not in the project, or names
|
|
9
10
|
* a `document`/`designer` module.
|
|
10
11
|
* @throws {VbaParseError} if the attached `vbaProject.bin` is malformed.
|
|
12
|
+
* @throws {PackageReadError} if the input is not a readable ZIP, or exceeds the inflate bound
|
|
13
|
+
* ({@link ReadPackageOptions.maxUncompressedBytes}, defaulting as `readXlsx` does).
|
|
11
14
|
*/
|
|
12
|
-
export declare function editXlsxVbaRemoveModule(xlsx: Uint8Array, name: string): Uint8Array;
|
|
15
|
+
export declare function editXlsxVbaRemoveModule(xlsx: Uint8Array, name: string, options?: ReadPackageOptions): Uint8Array;
|
|
13
16
|
/**
|
|
14
17
|
* Add a registered (COM type-library) reference to an existing macro-enabled package's VBA project,
|
|
15
18
|
* returning new package bytes. Every part but `xl/vbaProject.bin` is preserved byte-for-byte (see
|
|
@@ -19,5 +22,7 @@ export declare function editXlsxVbaRemoveModule(xlsx: Uint8Array, name: string):
|
|
|
19
22
|
* @throws {VbaAuthorError} if the package carries no VBA project, or any field of `ref` is invalid (see
|
|
20
23
|
* {@link VbaLibraryReference}).
|
|
21
24
|
* @throws {VbaParseError} if the attached `vbaProject.bin` is malformed.
|
|
25
|
+
* @throws {PackageReadError} if the input is not a readable ZIP, or exceeds the inflate bound
|
|
26
|
+
* ({@link ReadPackageOptions.maxUncompressedBytes}, defaulting as `readXlsx` does).
|
|
22
27
|
*/
|
|
23
|
-
export declare function editXlsxVbaAddReference(xlsx: Uint8Array, ref: VbaLibraryReference): Uint8Array;
|
|
28
|
+
export declare function editXlsxVbaAddReference(xlsx: Uint8Array, ref: VbaLibraryReference, options?: ReadPackageOptions): Uint8Array;
|
package/dist/io/xlsx/edit-vba.js
CHANGED
|
@@ -1,20 +1,22 @@
|
|
|
1
|
-
import { strFromU8, strToU8,
|
|
1
|
+
import { strFromU8, strToU8, zipSync } from 'fflate';
|
|
2
2
|
import { VbaAuthorError } from '../../vba/errors.js';
|
|
3
3
|
import { addVbaReference, removeVbaModule, } from '../../vba/project-editor.js';
|
|
4
|
-
import { relsPathFor } from '../opc/part-paths.js';
|
|
5
|
-
import { parseRelationshipRecords,
|
|
4
|
+
import { relsPathFor, resolveRelativePart } from '../opc/part-paths.js';
|
|
5
|
+
import { parseRelationshipRecords, readPartRelationships } from '../opc/read-opc.js';
|
|
6
|
+
import { DEFAULT_MAX_UNCOMPRESSED } from '../opc/read-options.js';
|
|
7
|
+
import { inflateSpreadsheetPackage } from '../opc/sniff-format.js';
|
|
6
8
|
import { FIXED_ENTRY_MTIME } from '../opc/zip-mtime.js';
|
|
7
9
|
const OFFICE_DOCUMENT_REL = 'officeDocument';
|
|
8
10
|
const VBA_PROJECT_REL = 'vbaProject';
|
|
9
11
|
const VBA_SIGNATURE_REL_INFIX = 'vbaProjectSignature';
|
|
10
|
-
export function editXlsxVbaRemoveModule(xlsx, name) {
|
|
11
|
-
return applyToVbaProjectPart(xlsx, (bin) => removeVbaModule(bin, name));
|
|
12
|
+
export function editXlsxVbaRemoveModule(xlsx, name, options = {}) {
|
|
13
|
+
return applyToVbaProjectPart(xlsx, options, (bin) => removeVbaModule(bin, name));
|
|
12
14
|
}
|
|
13
|
-
export function editXlsxVbaAddReference(xlsx, ref) {
|
|
14
|
-
return applyToVbaProjectPart(xlsx, (bin) => addVbaReference(bin, ref));
|
|
15
|
+
export function editXlsxVbaAddReference(xlsx, ref, options = {}) {
|
|
16
|
+
return applyToVbaProjectPart(xlsx, options, (bin) => addVbaReference(bin, ref));
|
|
15
17
|
}
|
|
16
|
-
function applyToVbaProjectPart(xlsx, apply) {
|
|
17
|
-
const files =
|
|
18
|
+
function applyToVbaProjectPart(xlsx, options, apply) {
|
|
19
|
+
const files = inflateSpreadsheetPackage(xlsx, options.maxUncompressedBytes ?? DEFAULT_MAX_UNCOMPRESSED);
|
|
18
20
|
const binPath = locateVbaProjectPart(files);
|
|
19
21
|
const bin = binPath === undefined ? undefined : files[binPath];
|
|
20
22
|
if (binPath === undefined || bin === undefined) {
|
|
@@ -25,27 +27,11 @@ function applyToVbaProjectPart(xlsx, apply) {
|
|
|
25
27
|
return zipSync(files, { mtime: FIXED_ENTRY_MTIME });
|
|
26
28
|
}
|
|
27
29
|
function locateVbaProjectPart(files) {
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
+
const partText = (path) => textPart(files, path);
|
|
31
|
+
const workbookPath = readPartRelationships('', partText).targetPath(OFFICE_DOCUMENT_REL);
|
|
32
|
+
if (workbookPath === undefined)
|
|
30
33
|
return undefined;
|
|
31
|
-
|
|
32
|
-
if (workbookTarget === undefined)
|
|
33
|
-
return undefined;
|
|
34
|
-
const workbookPath = resolveRelativePart('', workbookTarget);
|
|
35
|
-
const workbookRels = textPart(files, relsPathFor(workbookPath));
|
|
36
|
-
if (workbookRels === undefined)
|
|
37
|
-
return undefined;
|
|
38
|
-
const vbaTarget = relationshipTargetByType(workbookRels, VBA_PROJECT_REL);
|
|
39
|
-
if (vbaTarget === undefined)
|
|
40
|
-
return undefined;
|
|
41
|
-
return resolveVbaTarget(workbookPath, vbaTarget);
|
|
42
|
-
}
|
|
43
|
-
function resolveVbaTarget(workbookPath, target) {
|
|
44
|
-
if (target.startsWith('/'))
|
|
45
|
-
return target.slice(1);
|
|
46
|
-
if (workbookPath.startsWith('xl/'))
|
|
47
|
-
return resolveWorkbookPart(target);
|
|
48
|
-
return resolveRelativePart(workbookPath, target);
|
|
34
|
+
return readPartRelationships(workbookPath, partText).targetPath(VBA_PROJECT_REL);
|
|
49
35
|
}
|
|
50
36
|
function dropStaleSignature(files, binPath) {
|
|
51
37
|
const binRelsPath = relsPathFor(binPath);
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import { type Font } from '../../core/style.ts';
|
|
2
|
+
import { type XmlAttributes } from '../../xml/xml-scan.ts';
|
|
3
|
+
export type FontDraft = {
|
|
4
|
+
-readonly [K in keyof Font]?: Font[K];
|
|
5
|
+
};
|
|
6
|
+
export declare function applyFontChild(draft: FontDraft, local: string, attrs: XmlAttributes): void;
|
|
7
|
+
export declare function fontXml(font: Font, nameTag?: 'name' | 'rFont'): string;
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { isFontScheme, isFontVerticalAlignment, isNamedUnderlineStyle, } from '../../core/style.js';
|
|
2
|
+
import { numFinite, numInteger } from '../../xml/xml-attrs.js';
|
|
3
|
+
import { boolPresent } from '../../xml/xml-scan.js';
|
|
4
|
+
import { checkedToken, escapeAttr, numberText } from '../../xml/xml.js';
|
|
5
|
+
import { colorAttrs, parseColor } from './color-xml.js';
|
|
6
|
+
export function applyFontChild(draft, local, attrs) {
|
|
7
|
+
switch (local) {
|
|
8
|
+
case 'b':
|
|
9
|
+
draft.bold = boolPresent(attrs.val);
|
|
10
|
+
break;
|
|
11
|
+
case 'i':
|
|
12
|
+
draft.italic = boolPresent(attrs.val);
|
|
13
|
+
break;
|
|
14
|
+
case 'strike':
|
|
15
|
+
draft.strike = boolPresent(attrs.val);
|
|
16
|
+
break;
|
|
17
|
+
case 'outline':
|
|
18
|
+
draft.outline = boolPresent(attrs.val);
|
|
19
|
+
break;
|
|
20
|
+
case 'u':
|
|
21
|
+
draft.underline =
|
|
22
|
+
attrs.val === undefined
|
|
23
|
+
? true
|
|
24
|
+
: attrs.val === 'none'
|
|
25
|
+
? false
|
|
26
|
+
: isNamedUnderlineStyle(attrs.val)
|
|
27
|
+
? attrs.val
|
|
28
|
+
: true;
|
|
29
|
+
break;
|
|
30
|
+
case 'vertAlign':
|
|
31
|
+
if (attrs.val !== undefined && isFontVerticalAlignment(attrs.val))
|
|
32
|
+
draft.vertAlign = attrs.val;
|
|
33
|
+
break;
|
|
34
|
+
case 'sz': {
|
|
35
|
+
const size = numFinite(attrs.val);
|
|
36
|
+
if (size !== undefined)
|
|
37
|
+
draft.size = size;
|
|
38
|
+
break;
|
|
39
|
+
}
|
|
40
|
+
case 'color':
|
|
41
|
+
draft.color = parseColor(attrs);
|
|
42
|
+
break;
|
|
43
|
+
case 'name':
|
|
44
|
+
case 'rFont':
|
|
45
|
+
if (attrs.val !== undefined)
|
|
46
|
+
draft.name = attrs.val;
|
|
47
|
+
break;
|
|
48
|
+
case 'family': {
|
|
49
|
+
const family = numInteger(attrs.val);
|
|
50
|
+
if (family !== undefined)
|
|
51
|
+
draft.family = family;
|
|
52
|
+
break;
|
|
53
|
+
}
|
|
54
|
+
case 'charset': {
|
|
55
|
+
const charset = numInteger(attrs.val);
|
|
56
|
+
if (charset !== undefined)
|
|
57
|
+
draft.charset = charset;
|
|
58
|
+
break;
|
|
59
|
+
}
|
|
60
|
+
case 'scheme':
|
|
61
|
+
if (attrs.val !== undefined && isFontScheme(attrs.val))
|
|
62
|
+
draft.scheme = attrs.val;
|
|
63
|
+
break;
|
|
64
|
+
default:
|
|
65
|
+
break;
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
export function fontXml(font, nameTag = 'name') {
|
|
69
|
+
const parts = [];
|
|
70
|
+
if (font.bold)
|
|
71
|
+
parts.push('<b/>');
|
|
72
|
+
if (font.italic)
|
|
73
|
+
parts.push('<i/>');
|
|
74
|
+
if (font.strike)
|
|
75
|
+
parts.push('<strike/>');
|
|
76
|
+
if (font.outline)
|
|
77
|
+
parts.push('<outline/>');
|
|
78
|
+
const underline = underlineXml(font.underline);
|
|
79
|
+
if (underline !== '')
|
|
80
|
+
parts.push(underline);
|
|
81
|
+
if (font.vertAlign !== undefined) {
|
|
82
|
+
parts.push(`<vertAlign val="${checkedToken(font.vertAlign, isFontVerticalAlignment, 'font vertical alignment')}"/>`);
|
|
83
|
+
}
|
|
84
|
+
if (font.size !== undefined)
|
|
85
|
+
parts.push(`<sz val="${numberText(font.size)}"/>`);
|
|
86
|
+
if (font.color !== undefined)
|
|
87
|
+
parts.push(`<color ${colorAttrs(font.color)}/>`);
|
|
88
|
+
if (font.name !== undefined)
|
|
89
|
+
parts.push(`<${nameTag} val="${escapeAttr(font.name)}"/>`);
|
|
90
|
+
if (font.family !== undefined)
|
|
91
|
+
parts.push(`<family val="${numberText(font.family)}"/>`);
|
|
92
|
+
if (font.charset !== undefined)
|
|
93
|
+
parts.push(`<charset val="${numberText(font.charset)}"/>`);
|
|
94
|
+
if (font.scheme !== undefined && font.scheme !== 'none')
|
|
95
|
+
parts.push(`<scheme val="${checkedToken(font.scheme, isFontScheme, 'font scheme')}"/>`);
|
|
96
|
+
return parts.join('');
|
|
97
|
+
}
|
|
98
|
+
function underlineXml(underline) {
|
|
99
|
+
if (underline === undefined || underline === false || underline === 'none')
|
|
100
|
+
return '';
|
|
101
|
+
if (underline === true || underline === 'single')
|
|
102
|
+
return '<u/>';
|
|
103
|
+
return `<u val="${checkedToken(underline, isNamedUnderlineStyle, 'underline style')}"/>`;
|
|
104
|
+
}
|
|
@@ -1,9 +1,18 @@
|
|
|
1
|
+
import type { Cell } from '../../core/cell.ts';
|
|
1
2
|
import type { Worksheet } from '../../core/worksheet.ts';
|
|
2
|
-
import type
|
|
3
|
+
import { type CollectingPass } from '../../xml/xml-read.ts';
|
|
4
|
+
import type { RelIdAllocator } from './package-plan.ts';
|
|
3
5
|
/** A hyperlink gathered from a sheet for serialisation: the cell it sits on, its target, and an
|
|
4
|
-
* optional tooltip. The visible label is the cell's own value and is serialised as that value.
|
|
6
|
+
* optional tooltip. The visible label is the cell's own value and is serialised as that value.
|
|
7
|
+
*
|
|
8
|
+
* `row`/`col` are the anchor's position, kept so links gathered from separate passes (the sheet's live
|
|
9
|
+
* rows, and the rows the streaming writer already flushed and evicted) can be merged back into the
|
|
10
|
+
* row-major order Excel writes them in. They are not serialised; `ref` is.
|
|
11
|
+
*/
|
|
5
12
|
export interface CollectedHyperlink {
|
|
6
13
|
readonly ref: string;
|
|
14
|
+
readonly row: number;
|
|
15
|
+
readonly col: number;
|
|
7
16
|
readonly target: string;
|
|
8
17
|
readonly tooltip?: string;
|
|
9
18
|
}
|
|
@@ -17,12 +26,36 @@ export interface HyperlinkPlan {
|
|
|
17
26
|
readonly location?: string;
|
|
18
27
|
readonly tooltip?: string;
|
|
19
28
|
}
|
|
20
|
-
/**
|
|
21
|
-
|
|
29
|
+
/**
|
|
30
|
+
* Gather every hyperlink among a run of cells.
|
|
31
|
+
*
|
|
32
|
+
* Takes the cells rather than the sheet, because the streaming writer has to ask this question of a
|
|
33
|
+
* row at the moment it commits: after that the row's cells are evicted, and a post-hoc walk of
|
|
34
|
+
* `sheet.rows()` finds nothing. Both writers therefore ask the same function, over whatever cells
|
|
35
|
+
* they still hold.
|
|
36
|
+
*/
|
|
37
|
+
export declare function collectHyperlinks(cells: Iterable<Cell>): CollectedHyperlink[];
|
|
38
|
+
/** Every cell a sheet still holds, row-major: the live half of what a writer must gather. */
|
|
39
|
+
export declare function liveCells(sheet: Worksheet): Generator<Cell, void, undefined>;
|
|
40
|
+
/** An external hyperlink: the two fields a `TargetMode="External"` relationship needs, both present. */
|
|
41
|
+
export type ExternalHyperlinkPlan = HyperlinkPlan & {
|
|
42
|
+
readonly relId: string;
|
|
43
|
+
readonly target: string;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Whether a planned hyperlink is the external kind, narrowing it so the relationship writer reads
|
|
47
|
+
* `relId` and `target` as the strings they are.
|
|
48
|
+
*
|
|
49
|
+
* A predicate rather than a filter plus two casts: the filter already proved both fields present, and
|
|
50
|
+
* a cast repeating that proof one line later is a claim the compiler cannot check against the filter
|
|
51
|
+
* it is supposed to be echoing. `planHyperlinks` sets exactly one of `relId`/`location`, so testing
|
|
52
|
+
* either field is testing the kind.
|
|
53
|
+
*/
|
|
54
|
+
export declare function isExternalHyperlink(link: HyperlinkPlan): link is ExternalHyperlinkPlan;
|
|
22
55
|
/** Split collected links into internal (location, no rel) and external (relationship) forms, drawing
|
|
23
56
|
* each external link's relationship id from the sheet's allocator so external ids follow every other
|
|
24
57
|
* sheet-local relationship in canonical order. An internal ('#'-prefixed) link consumes no id. */
|
|
25
|
-
export declare function planHyperlinks(links: readonly CollectedHyperlink[], rels:
|
|
58
|
+
export declare function planHyperlinks(links: readonly CollectedHyperlink[], rels: RelIdAllocator): HyperlinkPlan[];
|
|
26
59
|
/** The `<hyperlinks>` element, or '' when the sheet has none. Attribute order follows CT_Hyperlink:
|
|
27
60
|
* `ref`, `r:id`, `location`, `tooltip`. */
|
|
28
61
|
export declare function hyperlinksXml(links: readonly HyperlinkPlan[]): string;
|
|
@@ -34,8 +67,9 @@ interface ParsedHyperlink {
|
|
|
34
67
|
readonly location?: string;
|
|
35
68
|
readonly tooltip?: string;
|
|
36
69
|
}
|
|
37
|
-
/**
|
|
38
|
-
|
|
70
|
+
/** A pass gathering every `<hyperlink>` element of a worksheet part, for a caller reading the part
|
|
71
|
+
* alongside its other readers in one parse. */
|
|
72
|
+
export declare function sheetHyperlinkPass(): CollectingPass<ParsedHyperlink[]>;
|
|
39
73
|
/** Fold parsed hyperlinks onto a sheet's cells, wrapping each cell's existing value (its visible
|
|
40
74
|
* label) into a {@link HyperlinkValue}. `targetOf` resolves a relationship id to its raw Target: a
|
|
41
75
|
* URL for the external links hyperlinks almost always are, so it must stay unresolved against the
|