@shbernal/ts-xlsx 3.0.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 +4 -3
- package/dist/core/autofilter.js +31 -30
- package/dist/core/axis-handle.d.ts +2 -0
- package/dist/core/axis-handle.js +5 -2
- package/dist/core/cell.d.ts +4 -3
- package/dist/core/cell.js +4 -6
- 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 +1 -0
- package/dist/core/column.js +3 -0
- 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 +9 -1
- package/dist/core/conditional-formatting.js +34 -18
- 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 +6 -0
- package/dist/core/data-validation.js +15 -4
- 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 +13 -9
- package/dist/core/image.js +7 -5
- 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.d.ts +5 -4
- package/dist/core/merge.js +18 -19
- package/dist/core/pivot-table.js +63 -45
- 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 +1 -0
- package/dist/core/row.js +3 -0
- package/dist/core/style.d.ts +85 -70
- package/dist/core/style.js +92 -28
- package/dist/core/table-style.d.ts +1 -1
- package/dist/core/table-style.js +4 -4
- package/dist/core/table.d.ts +27 -9
- package/dist/core/table.js +51 -34
- package/dist/core/theme.js +6 -5
- 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 +55 -6
- package/dist/core/worksheet.js +36 -44
- package/dist/customui/ribbon.js +2 -1
- 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 +14 -0
- 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 +9 -0
- package/dist/io/opc/part-paths.js +1 -1
- package/dist/io/opc/read-opc.d.ts +54 -14
- package/dist/io/opc/read-opc.js +42 -20
- package/dist/io/opc/read-options.d.ts +5 -1
- 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 +23 -1
- package/dist/io/style/xf-style.js +8 -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 +45 -81
- 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 +44 -23
- 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 +8 -1
- 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 -32
- package/dist/io/xlsx/conditional-formatting.d.ts +6 -3
- package/dist/io/xlsx/conditional-formatting.js +30 -26
- package/dist/io/xlsx/data-validation.js +29 -27
- package/dist/io/xlsx/edit-vba.d.ts +7 -2
- package/dist/io/xlsx/edit-vba.js +14 -21
- 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 +37 -5
- package/dist/io/xlsx/hyperlinks.js +22 -13
- package/dist/io/xlsx/images.js +11 -13
- 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 +14 -2
- package/dist/io/xlsx/package-plan.js +39 -11
- 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 +2 -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.js → read-rich-runs.js} +6 -3
- package/dist/io/xlsx/read-rows.d.ts +18 -5
- package/dist/io/xlsx/read-rows.js +45 -31
- package/dist/io/xlsx/read-shared-strings.js +3 -2
- package/dist/io/xlsx/read-styles.d.ts +1 -6
- package/dist/io/xlsx/read-styles.js +6 -71
- 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 +2 -1
- package/dist/io/xlsx/read-worksheet.js +35 -164
- package/dist/io/xlsx/read.d.ts +5 -12
- package/dist/io/xlsx/read.js +73 -406
- 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 +145 -9
- 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 -207
- package/dist/io/xlsx/tables.js +10 -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 +2 -1
- package/dist/io/xlsx/workbook-xml.d.ts +54 -4
- package/dist/io/xlsx/workbook-xml.js +60 -34
- package/dist/io/xlsx/worksheet-xml.d.ts +23 -70
- package/dist/io/xlsx/worksheet-xml.js +48 -229
- package/dist/io/xlsx/write-stream.d.ts +65 -16
- package/dist/io/xlsx/write-stream.js +50 -20
- package/dist/io/xlsx/write.d.ts +1 -2
- package/dist/io/xlsx/write.js +114 -74
- package/dist/rel-type.d.ts +16 -0
- package/dist/rel-type.js +6 -0
- package/dist/token-set.d.ts +13 -0
- package/dist/token-set.js +4 -1
- package/dist/vba/bytes.d.ts +14 -5
- package/dist/vba/bytes.js +16 -6
- package/dist/vba/cfb-format.d.ts +8 -0
- package/dist/vba/cfb-format.js +1 -0
- package/dist/vba/cfb-writer.d.ts +9 -0
- package/dist/vba/cfb-writer.js +12 -9
- package/dist/vba/cfb.d.ts +13 -1
- package/dist/vba/cfb.js +73 -51
- package/dist/vba/codepage.js +2 -1
- package/dist/vba/errors.d.ts +7 -0
- package/dist/vba/ms-ovba.js +51 -16
- package/dist/vba/project-editor.js +35 -40
- package/dist/vba/project.d.ts +11 -1
- package/dist/vba/project.js +27 -8
- package/dist/vba/vba-encoding.js +4 -5
- 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 +72 -10
- package/dist/xml/xml-read.js +94 -18
- package/dist/xml/xml-scan.d.ts +15 -37
- package/dist/xml/xml-scan.js +12 -40
- package/dist/xml/xml.d.ts +28 -27
- package/dist/xml/xml.js +24 -24
- package/package.json +12 -7
- /package/dist/io/xlsx/{rich-runs.d.ts → read-rich-runs.d.ts} +0 -0
|
@@ -15,7 +15,6 @@ export const BRT = {
|
|
|
15
15
|
ColInfo: 60,
|
|
16
16
|
MergeCell: 176,
|
|
17
17
|
SSTItem: 19,
|
|
18
|
-
BeginSst: 159,
|
|
19
18
|
EndSst: 160,
|
|
20
19
|
Fmt: 44,
|
|
21
20
|
Font: 43,
|
|
@@ -37,6 +36,7 @@ export const BRT = {
|
|
|
37
36
|
EndCellXFs: 618,
|
|
38
37
|
BeginStyles: 619,
|
|
39
38
|
EndStyles: 620,
|
|
39
|
+
WbProp: 153,
|
|
40
40
|
BundleSh: 156,
|
|
41
41
|
BeginBundleShs: 143,
|
|
42
42
|
EndBundleShs: 144,
|
|
@@ -45,10 +45,6 @@ export const BRT = {
|
|
|
45
45
|
EndExternals: 354,
|
|
46
46
|
SupSelf: 357,
|
|
47
47
|
ExternSheet: 362,
|
|
48
|
-
WsProp: 147,
|
|
49
|
-
WsDim: 148,
|
|
50
48
|
WsFmtInfo: 485,
|
|
51
|
-
BeginSheetData: 145,
|
|
52
|
-
EndSheetData: 146,
|
|
53
49
|
ArrFmla: 426,
|
|
54
50
|
};
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { DateEpoch } from '../../core/date.ts';
|
|
1
2
|
import type { CellValue } from '../../core/value.ts';
|
|
2
3
|
import type { Worksheet } from '../../core/worksheet.ts';
|
|
3
4
|
import { type XmlAttributes } from '../../xml/xml-scan.ts';
|
|
@@ -7,13 +8,19 @@ export declare class CellAccumulator {
|
|
|
7
8
|
#private;
|
|
8
9
|
constructor(options: {
|
|
9
10
|
readonly richRuns: boolean;
|
|
11
|
+
readonly dateEpoch: DateEpoch;
|
|
10
12
|
});
|
|
11
13
|
/** This cell's `<c r>` address (`"B3"`), or '' when it carried none. */
|
|
12
14
|
get ref(): string;
|
|
13
15
|
/** This cell's own `<c s>` style index, or -1 when it carries none. */
|
|
14
16
|
get styleIndex(): number;
|
|
15
|
-
/** This cell's 1-based column, or -1 when its address was
|
|
17
|
+
/** This cell's 1-based column, or -1 when its address was unparseable. */
|
|
16
18
|
get col(): number;
|
|
19
|
+
/**
|
|
20
|
+
* Open a `<row>`: the cells that follow belong to it, and the next one with no `r` of its own is
|
|
21
|
+
* its first column. Both readers call this where they already tell the style resolver a row opened.
|
|
22
|
+
*/
|
|
23
|
+
openRow(number: number): void;
|
|
17
24
|
/**
|
|
18
25
|
* The text gathered since the current element opened. A caller reads it for the elements it
|
|
19
26
|
* captures itself (see {@link capture}); the machine reads it for its own.
|
|
@@ -1,15 +1,17 @@
|
|
|
1
|
-
import { encodeAddress, tryDecodeCellRef } from '../../core/address.js';
|
|
1
|
+
import { encodeAddress, MAX_COLUMN, tryDecodeCellRef } from '../../core/address.js';
|
|
2
2
|
import { translateFormula, unmangleFunctions } from '../../core/formula.js';
|
|
3
|
-
import {
|
|
3
|
+
import { numInteger } from '../../xml/xml-attrs.js';
|
|
4
|
+
import { boolStrict } from '../../xml/xml-scan.js';
|
|
4
5
|
import { applyXfToCell } from '../style/xf-style.js';
|
|
5
6
|
import { decodeCellContent, decodeFormulaResult, } from './cell-value.js';
|
|
6
|
-
import { RunAccumulator } from './rich-runs.js';
|
|
7
|
+
import { RunAccumulator } from './read-rich-runs.js';
|
|
7
8
|
export class CellAccumulator {
|
|
8
9
|
#ref = '';
|
|
9
10
|
#type = '';
|
|
10
11
|
#style = -1;
|
|
11
12
|
#col = -1;
|
|
12
13
|
#row = -1;
|
|
14
|
+
#nextCol = 1;
|
|
13
15
|
#formula = '';
|
|
14
16
|
#formulaShared = false;
|
|
15
17
|
#formulaSi = -1;
|
|
@@ -22,8 +24,10 @@ export class CellAccumulator {
|
|
|
22
24
|
#masters = new Map();
|
|
23
25
|
#capture = false;
|
|
24
26
|
#text = '';
|
|
27
|
+
#dateEpoch;
|
|
25
28
|
constructor(options) {
|
|
26
29
|
this.#runs = new RunAccumulator({ container: 'is', readRuns: options.richRuns });
|
|
30
|
+
this.#dateEpoch = options.dateEpoch;
|
|
27
31
|
}
|
|
28
32
|
get ref() {
|
|
29
33
|
return this.#ref;
|
|
@@ -34,13 +38,14 @@ export class CellAccumulator {
|
|
|
34
38
|
get col() {
|
|
35
39
|
return this.#col;
|
|
36
40
|
}
|
|
41
|
+
openRow(number) {
|
|
42
|
+
this.#row = number;
|
|
43
|
+
this.#nextCol = 1;
|
|
44
|
+
}
|
|
37
45
|
#beginCell(attrs) {
|
|
38
46
|
this.#type = attrs.t ?? '';
|
|
39
47
|
this.#style = numInteger(attrs.s, 0) ?? -1;
|
|
40
|
-
|
|
41
|
-
this.#ref = decoded === undefined ? '' : (attrs.r ?? '');
|
|
42
|
-
this.#col = decoded?.col ?? -1;
|
|
43
|
-
this.#row = decoded?.row ?? -1;
|
|
48
|
+
this.#placeCell(attrs.r);
|
|
44
49
|
this.#formula = '';
|
|
45
50
|
this.#valueText = '';
|
|
46
51
|
this.#runs.beginContainer();
|
|
@@ -51,6 +56,24 @@ export class CellAccumulator {
|
|
|
51
56
|
this.#sharedClone = false;
|
|
52
57
|
this.#dataTable = null;
|
|
53
58
|
}
|
|
59
|
+
#placeCell(ref) {
|
|
60
|
+
const decoded = ref === undefined ? undefined : tryDecodeCellRef(ref);
|
|
61
|
+
if (decoded !== undefined) {
|
|
62
|
+
this.#ref = ref ?? '';
|
|
63
|
+
this.#col = decoded.col;
|
|
64
|
+
this.#row = decoded.row;
|
|
65
|
+
}
|
|
66
|
+
else if (ref === undefined && this.#row > 0 && this.#nextCol <= MAX_COLUMN) {
|
|
67
|
+
this.#col = this.#nextCol;
|
|
68
|
+
this.#ref = encodeAddress(this.#col, this.#row);
|
|
69
|
+
}
|
|
70
|
+
else {
|
|
71
|
+
this.#ref = '';
|
|
72
|
+
this.#col = -1;
|
|
73
|
+
}
|
|
74
|
+
if (this.#col > 0)
|
|
75
|
+
this.#nextCol = this.#col + 1;
|
|
76
|
+
}
|
|
54
77
|
#beginFormula(attrs, selfClosing) {
|
|
55
78
|
this.#formulaShared = attrs.t === 'shared';
|
|
56
79
|
this.#formulaSi = numInteger(attrs.si, 0) ?? -1;
|
|
@@ -165,7 +188,7 @@ export class CellAccumulator {
|
|
|
165
188
|
cachedResult(style) {
|
|
166
189
|
if (!this.#hasValue)
|
|
167
190
|
return {};
|
|
168
|
-
const result = decodeFormulaResult(this.#type, this.#valueText, style?.numFmt);
|
|
191
|
+
const result = decodeFormulaResult(this.#type, this.#valueText, style?.numFmt, this.#dateEpoch);
|
|
169
192
|
return result === undefined ? {} : { result };
|
|
170
193
|
}
|
|
171
194
|
decode(sharedStrings, style) {
|
|
@@ -178,6 +201,6 @@ export class CellAccumulator {
|
|
|
178
201
|
inlineText: this.#runs.plainText,
|
|
179
202
|
richTextRuns: this.#runs.runs,
|
|
180
203
|
};
|
|
181
|
-
return decodeCellContent(raw, sharedStrings, style?.numFmt);
|
|
204
|
+
return decodeCellContent(raw, sharedStrings, style?.numFmt, this.#dateEpoch);
|
|
182
205
|
}
|
|
183
206
|
}
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
/** The attributes this resolver reads, the subset both readers' scanners already hand it. */
|
|
2
|
+
interface StyleAttributes {
|
|
3
|
+
readonly [key: string]: string | undefined;
|
|
4
|
+
}
|
|
5
|
+
/**
|
|
6
|
+
* The cell → row → column style resolution for one worksheet, fed by the same elements in the same
|
|
7
|
+
* order both readers already visit them: `<col>` before any cell references it, `<row>` before its
|
|
8
|
+
* own cells.
|
|
9
|
+
*/
|
|
10
|
+
export declare class CellStyleResolver {
|
|
11
|
+
#private;
|
|
12
|
+
/** Record a `<col min max style>` span's default for the columns it covers. */
|
|
13
|
+
noteColumnSpan(first: number, last: number, attrs: StyleAttributes): void;
|
|
14
|
+
/** Open a `<row>`, taking the format it declares for its own bare cells. */
|
|
15
|
+
openRow(attrs: StyleAttributes): void;
|
|
16
|
+
/** Close the row, so a cell outside one never inherits the last row's format. */
|
|
17
|
+
closeRow(): void;
|
|
18
|
+
/**
|
|
19
|
+
* The `xfStyles` index a cell's format resolves to, or `-1` when nothing declares one. A row's
|
|
20
|
+
* format applies only when the row is marked `customFormat`; without that flag the `s` on a `<row>`
|
|
21
|
+
* describes the row itself and is not inherited by its cells.
|
|
22
|
+
*/
|
|
23
|
+
indexFor(col: number, cellStyleIndex: number): number;
|
|
24
|
+
}
|
|
25
|
+
export {};
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { numInteger } from '../../xml/xml-attrs.js';
|
|
2
|
+
import { boolStrict } from '../../xml/xml-scan.js';
|
|
3
|
+
export class CellStyleResolver {
|
|
4
|
+
#columnStyle = new Map();
|
|
5
|
+
#rowStyle = -1;
|
|
6
|
+
#rowCustomFormat = false;
|
|
7
|
+
noteColumnSpan(first, last, attrs) {
|
|
8
|
+
const styleIndex = numInteger(attrs.style, 0) ?? -1;
|
|
9
|
+
if (styleIndex < 0)
|
|
10
|
+
return;
|
|
11
|
+
for (let index = first; index <= last; index++)
|
|
12
|
+
this.#columnStyle.set(index, styleIndex);
|
|
13
|
+
}
|
|
14
|
+
openRow(attrs) {
|
|
15
|
+
this.#rowStyle = numInteger(attrs.s, 0) ?? -1;
|
|
16
|
+
this.#rowCustomFormat = boolStrict(attrs.customFormat);
|
|
17
|
+
}
|
|
18
|
+
closeRow() {
|
|
19
|
+
this.#rowStyle = -1;
|
|
20
|
+
this.#rowCustomFormat = false;
|
|
21
|
+
}
|
|
22
|
+
indexFor(col, cellStyleIndex) {
|
|
23
|
+
if (cellStyleIndex >= 0)
|
|
24
|
+
return cellStyleIndex;
|
|
25
|
+
if (this.#rowCustomFormat && this.#rowStyle >= 0)
|
|
26
|
+
return this.#rowStyle;
|
|
27
|
+
return this.#columnStyle.get(col) ?? -1;
|
|
28
|
+
}
|
|
29
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type DateEpoch } from '../../core/date.ts';
|
|
1
2
|
import { type CellValue, type FormulaResult, type RichTextRun, type RichTextValue } from '../../core/value.ts';
|
|
2
3
|
/**
|
|
3
4
|
* One entry of the shared-strings pool. A `<si>` built from a bare `<t>` is a plain string; a `<si>`
|
|
@@ -22,11 +23,12 @@ export interface RawCell {
|
|
|
22
23
|
* Decode a gathered cell into its model value. A formula cell becomes a `{formula, result?}`
|
|
23
24
|
* object (the on-disk `_xlfn.`/`_xlpm.` mangling stripped back to the readable name); a plain
|
|
24
25
|
* numeric cell under a date number format becomes a {@link Date}; everything else decodes by its
|
|
25
|
-
* `t` type. `numFmt` is the cell's resolved number-format code, used only for date detection
|
|
26
|
+
* `t` type. `numFmt` is the cell's resolved number-format code, used only for date detection, and
|
|
27
|
+
* `epoch` the workbook's date system, which is what a serial under such a format counts from.
|
|
26
28
|
*/
|
|
27
|
-
export declare function decodeCellContent(raw: RawCell, sharedStrings: readonly SharedString[], numFmt: string | undefined): CellValue;
|
|
29
|
+
export declare function decodeCellContent(raw: RawCell, sharedStrings: readonly SharedString[], numFmt: string | undefined, epoch: DateEpoch): CellValue;
|
|
28
30
|
/** Decode a formula's cached `<v>` result by its `t` type, coercing a numeric result under a date
|
|
29
31
|
* `numFmt` to a {@link Date} exactly as a bare numeric cell is, so a date-valued formula result
|
|
30
32
|
* (e.g. `TODAY()`) reads back as a Date, not a serial. Shared by the buffered reader's shared-formula
|
|
31
33
|
* clone resolution, which caches a result the same way a plain formula cell does. */
|
|
32
|
-
export declare function decodeFormulaResult(type: string, valueText: string, numFmt
|
|
34
|
+
export declare function decodeFormulaResult(type: string, valueText: string, numFmt: string | undefined, epoch: DateEpoch): FormulaResult | undefined;
|
|
@@ -1,20 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { coerceDateSerial, parseDateText } from '../../core/date.js';
|
|
2
2
|
import { unmangleFunctions } from '../../core/formula.js';
|
|
3
3
|
import { isErrorCode, } from '../../core/value.js';
|
|
4
|
-
import {
|
|
5
|
-
|
|
4
|
+
import { decodeSpreadsheetText, numFinite, numInteger } from '../../xml/xml-attrs.js';
|
|
5
|
+
import { boolStrict } from '../../xml/xml-scan.js';
|
|
6
|
+
export function decodeCellContent(raw, sharedStrings, numFmt, epoch) {
|
|
6
7
|
if (raw.hasFormula) {
|
|
7
8
|
const stored = unmangleFunctions(raw.formula);
|
|
8
|
-
const result = raw.hasValue
|
|
9
|
+
const result = raw.hasValue
|
|
10
|
+
? decodeFormulaResult(raw.type, raw.valueText, numFmt, epoch)
|
|
11
|
+
: undefined;
|
|
9
12
|
return result === undefined ? { formula: stored } : { formula: stored, result };
|
|
10
13
|
}
|
|
11
14
|
if (raw.type === 'inlineStr' && raw.richTextRuns !== undefined && raw.richTextRuns.length > 0) {
|
|
12
15
|
return { richText: raw.richTextRuns };
|
|
13
16
|
}
|
|
14
17
|
const value = decodeValue(raw.type, raw.valueText, raw.inlineText, raw.hasValue, sharedStrings);
|
|
15
|
-
return
|
|
16
|
-
? serialToDate(value)
|
|
17
|
-
: value;
|
|
18
|
+
return coerceDateSerial(value, numFmt, epoch);
|
|
18
19
|
}
|
|
19
20
|
function decodeValue(type, valueText, inlineText, hasValue, sharedStrings) {
|
|
20
21
|
switch (type) {
|
|
@@ -23,10 +24,10 @@ function decodeValue(type, valueText, inlineText, hasValue, sharedStrings) {
|
|
|
23
24
|
case 'str':
|
|
24
25
|
return decodeSpreadsheetText(valueText);
|
|
25
26
|
case 'd':
|
|
26
|
-
return
|
|
27
|
+
return parseDateText(valueText);
|
|
27
28
|
case 's': {
|
|
28
|
-
const index =
|
|
29
|
-
return
|
|
29
|
+
const index = numInteger(valueText, 0);
|
|
30
|
+
return index === undefined ? '' : (sharedStrings[index] ?? '');
|
|
30
31
|
}
|
|
31
32
|
case 'b':
|
|
32
33
|
return boolStrict(valueText);
|
|
@@ -39,11 +40,8 @@ function decodeValue(type, valueText, inlineText, hasValue, sharedStrings) {
|
|
|
39
40
|
}
|
|
40
41
|
}
|
|
41
42
|
}
|
|
42
|
-
export function decodeFormulaResult(type, valueText, numFmt) {
|
|
43
|
-
|
|
44
|
-
return typeof result === 'number' && numFmt !== undefined && isDateFormat(numFmt)
|
|
45
|
-
? serialToDate(result)
|
|
46
|
-
: result;
|
|
43
|
+
export function decodeFormulaResult(type, valueText, numFmt, epoch) {
|
|
44
|
+
return coerceDateSerial(decodeResult(type, valueText), numFmt, epoch);
|
|
47
45
|
}
|
|
48
46
|
function decodeResult(type, valueText) {
|
|
49
47
|
switch (type) {
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import { parseArgb } from '../../core/style.js';
|
|
2
|
-
import {
|
|
2
|
+
import { quoted } from '../../errors.js';
|
|
3
|
+
import { numFinite, numInteger } from '../../xml/xml-attrs.js';
|
|
3
4
|
import { numberText } from '../../xml/xml.js';
|
|
4
5
|
function normalizeArgb(argb) {
|
|
5
6
|
const rgb = parseArgb(argb);
|
|
6
7
|
if (rgb === undefined) {
|
|
7
|
-
throw new SyntaxError(`Invalid ARGB colour ${
|
|
8
|
+
throw new SyntaxError(`Invalid ARGB colour ${quoted(argb)}: expected 6 or 8 hexadecimal digits`);
|
|
8
9
|
}
|
|
9
10
|
return rgb;
|
|
10
11
|
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
import { type XmlAttributes } from '../../xml/xml-scan.ts';
|
|
2
|
+
/**
|
|
3
|
+
* A per-sheet allowance of column touches, spent by each `<col>` span the reader applies.
|
|
4
|
+
*
|
|
5
|
+
* A span past the allowance is truncated, and one starting past it is dropped, rather than the read
|
|
6
|
+
* being refused: that is the stance both readers already take on an out-of-grid span, and it means a
|
|
7
|
+
* hostile file loses formatting it could not have meant while a real one is untouched.
|
|
8
|
+
*/
|
|
9
|
+
export declare class ColumnRecordBudget {
|
|
10
|
+
#private;
|
|
11
|
+
/** The last column of `[first, last]` this sheet can still afford, or `undefined` when none is. */
|
|
12
|
+
take(first: number, last: number): number | undefined;
|
|
13
|
+
}
|
|
14
|
+
/**
|
|
15
|
+
* The columns a `<col min max>` element actually applies to, or `undefined` for one that applies to
|
|
16
|
+
* none: unreadable bounds, a span starting past the grid, or a budget already spent.
|
|
17
|
+
*
|
|
18
|
+
* Beside the budget because the two decisions are one decision. Both readers had their own copy, and
|
|
19
|
+
* the copies had already drifted: one tested `min > MAX_COLUMN` and the other did not, which happened
|
|
20
|
+
* not to matter only because `budget.take` returns `undefined` when `first > last` - an accident of
|
|
21
|
+
* this class's contract rather than an agreement between the readers.
|
|
22
|
+
*/
|
|
23
|
+
export declare function takeColumnSpan(attrs: XmlAttributes, budget: ColumnRecordBudget): {
|
|
24
|
+
first: number;
|
|
25
|
+
last: number;
|
|
26
|
+
} | undefined;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { MAX_COLUMN } from '../../core/address.js';
|
|
2
|
+
import { numInteger } from '../../xml/xml-attrs.js';
|
|
3
|
+
import {} from '../../xml/xml-scan.js';
|
|
4
|
+
const MAX_COLUMN_RECORD_TOUCHES = MAX_COLUMN * 4;
|
|
5
|
+
export class ColumnRecordBudget {
|
|
6
|
+
#remaining = MAX_COLUMN_RECORD_TOUCHES;
|
|
7
|
+
take(first, last) {
|
|
8
|
+
if (this.#remaining <= 0 || first > last)
|
|
9
|
+
return undefined;
|
|
10
|
+
const affordable = Math.min(last, first + this.#remaining - 1);
|
|
11
|
+
this.#remaining -= affordable - first + 1;
|
|
12
|
+
return affordable;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
export function takeColumnSpan(attrs, budget) {
|
|
16
|
+
const first = numInteger(attrs.min, 1);
|
|
17
|
+
const declared = numInteger(attrs.max, 1);
|
|
18
|
+
if (first === undefined || declared === undefined || first > MAX_COLUMN)
|
|
19
|
+
return undefined;
|
|
20
|
+
const last = budget.take(first, Math.min(declared, MAX_COLUMN));
|
|
21
|
+
return last === undefined ? undefined : { first, last };
|
|
22
|
+
}
|
|
@@ -1,6 +1,8 @@
|
|
|
1
|
+
import type { Cell } from '../../core/cell.ts';
|
|
1
2
|
import type { CommentThread } from '../../core/comment-thread.ts';
|
|
2
3
|
import type { Worksheet } from '../../core/worksheet.ts';
|
|
3
|
-
/** A comment bound for `comments{n}.xml`, paired with the coordinates the VML anchor needs.
|
|
4
|
+
/** A comment bound for `comments{n}.xml`, paired with the coordinates the VML anchor needs.
|
|
5
|
+
*/
|
|
4
6
|
export interface CommentCell {
|
|
5
7
|
readonly ref: string;
|
|
6
8
|
/** 1-based row of the commented cell. */
|
|
@@ -28,7 +30,9 @@ export interface CommentCell {
|
|
|
28
30
|
* Ordered by cell, row-major, the way Excel writes the list, so a fallback lands interleaved among the
|
|
29
31
|
* notes rather than appended after them, and the VML shapes follow the same order.
|
|
30
32
|
*/
|
|
31
|
-
export declare function collectComments(
|
|
33
|
+
export declare function collectComments(cells: Iterable<Cell>, threads: readonly CommentThread[], alreadyCollected?: readonly CommentCell[]): CommentCell[];
|
|
34
|
+
/** The notes among a run of cells, for a writer that must ask before the cells are evicted. */
|
|
35
|
+
export declare function collectNotes(cells: Iterable<Cell>): CommentCell[];
|
|
32
36
|
/**
|
|
33
37
|
* The `xl/comments{n}.xml` part.
|
|
34
38
|
*
|
package/dist/io/xlsx/comments.js
CHANGED
|
@@ -1,20 +1,32 @@
|
|
|
1
1
|
import { tryDecodeCellRef } from '../../core/address.js';
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { decodeSpreadsheetText, numInteger } from '../../xml/xml-attrs.js';
|
|
3
|
+
import { parseXml, TextCapture } from '../../xml/xml-read.js';
|
|
4
|
+
import { localName } from '../../xml/xml-scan.js';
|
|
4
5
|
import { escapeText, textAttr, textElement, XML_DECLARATION } from '../../xml/xml.js';
|
|
5
6
|
import { MARKUP_COMPATIBILITY_NS, REVISION_NS, SPREADSHEETML_NS } from './namespaces.js';
|
|
6
|
-
export function collectComments(
|
|
7
|
+
export function collectComments(cells, threads, alreadyCollected = []) {
|
|
7
8
|
const fallbacks = threadFallbacks(threads);
|
|
8
9
|
const anchored = new Set(fallbacks.map((fallback) => fallback.ref));
|
|
9
|
-
const comments = [...fallbacks];
|
|
10
|
-
for (const
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
10
|
+
const comments = [...fallbacks, ...alreadyCollected];
|
|
11
|
+
for (const cell of cells) {
|
|
12
|
+
if (cell.note === undefined || anchored.has(cell.address))
|
|
13
|
+
continue;
|
|
14
|
+
comments.push(noteOn(cell, cell.note));
|
|
15
|
+
}
|
|
16
|
+
return comments
|
|
17
|
+
.filter((comment) => comment.threadId !== undefined || !anchored.has(comment.ref))
|
|
18
|
+
.sort((a, b) => a.row - b.row || a.col - b.col);
|
|
19
|
+
}
|
|
20
|
+
export function collectNotes(cells) {
|
|
21
|
+
const notes = [];
|
|
22
|
+
for (const cell of cells) {
|
|
23
|
+
if (cell.note !== undefined)
|
|
24
|
+
notes.push(noteOn(cell, cell.note));
|
|
16
25
|
}
|
|
17
|
-
return
|
|
26
|
+
return notes;
|
|
27
|
+
}
|
|
28
|
+
function noteOn(cell, text) {
|
|
29
|
+
return { ref: cell.address, row: cell.row, col: cell.col, text };
|
|
18
30
|
}
|
|
19
31
|
function threadFallbacks(threads) {
|
|
20
32
|
const fallbacks = [];
|
|
@@ -76,13 +88,14 @@ const VML_HEADER = '<xml xmlns:v="urn:schemas-microsoft-com:vml" ' +
|
|
|
76
88
|
'<v:shapetype id="_x0000_t202" coordsize="21600,21600" o:spt="202" ' +
|
|
77
89
|
'path="m,l,21600r21600,l21600,xe"><v:stroke joinstyle="miter"/>' +
|
|
78
90
|
'<v:path gradientshapeok="t" o:connecttype="rect"/></v:shapetype>';
|
|
91
|
+
const FIRST_SHAPE_ID = 1025;
|
|
79
92
|
export function vmlDrawingXml(comments) {
|
|
80
93
|
const shapes = comments
|
|
81
94
|
.map((comment, i) => {
|
|
82
95
|
const row0 = comment.row - 1;
|
|
83
96
|
const col0 = comment.col - 1;
|
|
84
97
|
const anchor = `${col0 + 1}, 15, ${row0}, 2, ${col0 + 3}, 15, ${row0 + 4}, 4`;
|
|
85
|
-
return (`<v:shape id="_x0000_s${
|
|
98
|
+
return (`<v:shape id="_x0000_s${FIRST_SHAPE_ID + i}" type="#_x0000_t202" ` +
|
|
86
99
|
'style="position:absolute;margin-left:59.25pt;margin-top:1.5pt;width:108pt;height:59.25pt;' +
|
|
87
100
|
`z-index:${i + 1};visibility:hidden" fillcolor="#ffffe1" o:insetmode="auto">` +
|
|
88
101
|
'<v:fill color2="#ffffe1"/><v:shadow on="t" color="black" obscured="t"/>' +
|
|
@@ -95,50 +108,41 @@ export function vmlDrawingXml(comments) {
|
|
|
95
108
|
.join('');
|
|
96
109
|
return `${VML_HEADER}${shapes}</xml>`;
|
|
97
110
|
}
|
|
98
|
-
const COMMENT_EMPTY_CLOSES = new Set(['author']);
|
|
111
|
+
const COMMENT_EMPTY_CLOSES = new Set(['author', 'text']);
|
|
99
112
|
const THREAD_AUTHOR_PREFIX = 'tc=';
|
|
100
113
|
export function parseComments(xml) {
|
|
101
114
|
const comments = new Map();
|
|
102
115
|
const authors = [];
|
|
103
116
|
let currentRef;
|
|
104
117
|
let currentAuthorId;
|
|
105
|
-
|
|
106
|
-
let
|
|
118
|
+
const capture = new TextCapture(['author', 'text']);
|
|
119
|
+
let body = '';
|
|
107
120
|
parseXml(xml, {
|
|
108
|
-
onOpen(name, attrs) {
|
|
121
|
+
onOpen(name, attrs, selfClosing) {
|
|
109
122
|
const local = localName(name);
|
|
110
123
|
if (local === 'comment') {
|
|
111
124
|
currentRef = attrs.ref;
|
|
112
125
|
currentAuthorId = attrs.authorId;
|
|
113
|
-
|
|
114
|
-
}
|
|
115
|
-
else if (local === 'author') {
|
|
116
|
-
capture = 'author';
|
|
117
|
-
buffer = '';
|
|
118
|
-
}
|
|
119
|
-
else if (local === 'text') {
|
|
120
|
-
capture = 'text';
|
|
126
|
+
body = '';
|
|
121
127
|
}
|
|
128
|
+
capture.open(local, selfClosing);
|
|
122
129
|
},
|
|
123
130
|
onText(text) {
|
|
124
|
-
|
|
125
|
-
buffer += text;
|
|
131
|
+
capture.text(text);
|
|
126
132
|
},
|
|
127
133
|
onClose(name) {
|
|
128
134
|
const local = localName(name);
|
|
135
|
+
const text = capture.close(local);
|
|
129
136
|
if (local === 'author') {
|
|
130
|
-
authors.push(
|
|
131
|
-
capture = undefined;
|
|
132
|
-
buffer = '';
|
|
137
|
+
authors.push(text ?? '');
|
|
133
138
|
}
|
|
134
139
|
else if (local === 'text') {
|
|
135
|
-
|
|
136
|
-
capture = undefined;
|
|
140
|
+
body = decodeSpreadsheetText(text ?? '');
|
|
137
141
|
}
|
|
138
142
|
else if (local === 'comment' && currentRef !== undefined) {
|
|
139
143
|
const threadId = threadIdOf(authors[numInteger(currentAuthorId, 0) ?? -1]);
|
|
140
144
|
comments.set(currentRef, {
|
|
141
|
-
text:
|
|
145
|
+
text: body,
|
|
142
146
|
...(threadId !== undefined ? { threadId } : {}),
|
|
143
147
|
});
|
|
144
148
|
currentRef = undefined;
|
|
@@ -1,12 +1,15 @@
|
|
|
1
|
-
import { type ConditionalFormatting } from '../../core/conditional-formatting.ts';
|
|
1
|
+
import { type ConditionalFormatting, type ConditionalFormattingRule } from '../../core/conditional-formatting.ts';
|
|
2
2
|
import { type CollectingPass } from '../../xml/xml-read.ts';
|
|
3
3
|
import type { StyleRegistry } from './styles.ts';
|
|
4
|
+
/** Each extended data bar's link id, keyed by rule identity: the one map both passes are handed. */
|
|
5
|
+
export type DataBarExtLinks = ReadonlyMap<ConditionalFormattingRule, string>;
|
|
6
|
+
export declare function dataBarExtLinks(formattings: readonly ConditionalFormatting[]): DataBarExtLinks;
|
|
4
7
|
/**
|
|
5
8
|
* Serialise every conditional formatting on a sheet into its `<conditionalFormatting>` blocks, in
|
|
6
9
|
* insertion order. Returns '' when the sheet has none. A rule missing a `priority` is assigned the
|
|
7
10
|
* next free one, so the output always satisfies Excel's requirement that every cfRule carry one.
|
|
8
11
|
*/
|
|
9
|
-
export declare function conditionalFormattingsXml(formattings: readonly ConditionalFormatting[], styles: StyleRegistry): string;
|
|
12
|
+
export declare function conditionalFormattingsXml(formattings: readonly ConditionalFormatting[], styles: StyleRegistry, extLinks: DataBarExtLinks): string;
|
|
10
13
|
/**
|
|
11
14
|
* The worksheet `<extLst>` `<ext>` carrying the x14 data-bar extensions, or '' when no data bar needs
|
|
12
15
|
* one. Each extension's `<x14:cfRule id>` is read from the same {@link dataBarExtLinks} map the classic
|
|
@@ -14,7 +17,7 @@ export declare function conditionalFormattingsXml(formattings: readonly Conditio
|
|
|
14
17
|
* (no `<extLst>` wrapper) so the worksheet serialiser can gather it into a single `<extLst>` beside the
|
|
15
18
|
* data-validation extension.
|
|
16
19
|
*/
|
|
17
|
-
export declare function conditionalFormattingsExtXml(formattings: readonly ConditionalFormatting[]): string;
|
|
20
|
+
export declare function conditionalFormattingsExtXml(formattings: readonly ConditionalFormatting[], extLinks: DataBarExtLinks): string;
|
|
18
21
|
/**
|
|
19
22
|
* A pass reading a worksheet's conditional formatting into the model. The classic `<conditionalFormatting>`
|
|
20
23
|
* blocks supply every rule; the x14 extension (`<x14:conditionalFormatting>` inside `<extLst>`) is
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { isCfValueObjectType, isConditionalFormattingOperator, isConditionalFormattingType, isIconSetType, isCfTimePeriod, } from '../../core/conditional-formatting.js';
|
|
2
|
+
import { stripFormulaEquals } from '../../core/formula.js';
|
|
3
|
+
import { coerceNumericLiteral, enumToken, numFinite, numInteger } from '../../xml/xml-attrs.js';
|
|
2
4
|
import { elementSubtrees, TextCapture, } from '../../xml/xml-read.js';
|
|
3
|
-
import { boolPresent, boolStrict,
|
|
4
|
-
import { boolAttr, checkedToken, escapeAttr, escapeText, numberText,
|
|
5
|
+
import { boolPresent, boolStrict, localName } from '../../xml/xml-scan.js';
|
|
6
|
+
import { boolAttr, checkedToken, escapeAttr, escapeText, numberText, textAttr, } from '../../xml/xml.js';
|
|
5
7
|
import { colorAttrs, parseColor } from './color-xml.js';
|
|
6
|
-
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';
|
|
7
9
|
import { x14Ext } from './x14-ext.js';
|
|
8
10
|
const DEFAULT_DATABAR_CFVO = [{ type: 'min' }, { type: 'max' }];
|
|
9
11
|
const DEFAULT_DATABAR_COLOR = { argb: 'FF638EC6' };
|
|
@@ -15,7 +17,7 @@ function needsDataBarExt(rule) {
|
|
|
15
17
|
function dataBarExtGuid(index) {
|
|
16
18
|
return `{00000000-0000-0000-0000-${String(index + 1).padStart(12, '0')}}`;
|
|
17
19
|
}
|
|
18
|
-
function dataBarExtLinks(formattings) {
|
|
20
|
+
export function dataBarExtLinks(formattings) {
|
|
19
21
|
const links = new Map();
|
|
20
22
|
let index = 0;
|
|
21
23
|
for (const cf of formattings) {
|
|
@@ -28,15 +30,13 @@ function dataBarExtLinks(formattings) {
|
|
|
28
30
|
}
|
|
29
31
|
const SCALE_KINDS = ['dataBar', 'colorScale', 'iconSet'];
|
|
30
32
|
const SCALE_TYPES = new Set(SCALE_KINDS);
|
|
31
|
-
export function conditionalFormattingsXml(formattings, styles) {
|
|
33
|
+
export function conditionalFormattingsXml(formattings, styles, extLinks) {
|
|
32
34
|
if (formattings.length === 0)
|
|
33
35
|
return '';
|
|
34
36
|
const priority = { next: 1 };
|
|
35
|
-
const extLinks = dataBarExtLinks(formattings);
|
|
36
37
|
return formattings.map((cf) => blockXml(cf, styles, priority, extLinks)).join('');
|
|
37
38
|
}
|
|
38
|
-
export function conditionalFormattingsExtXml(formattings) {
|
|
39
|
-
const extLinks = dataBarExtLinks(formattings);
|
|
39
|
+
export function conditionalFormattingsExtXml(formattings, extLinks) {
|
|
40
40
|
const items = [];
|
|
41
41
|
for (const cf of formattings) {
|
|
42
42
|
for (const rule of cf.rules) {
|
|
@@ -50,8 +50,7 @@ export function conditionalFormattingsExtXml(formattings) {
|
|
|
50
50
|
return x14Ext(CF_EXT_URI, `<x14:conditionalFormattings>${items.join('')}</x14:conditionalFormattings>`);
|
|
51
51
|
}
|
|
52
52
|
function x14DataBarXml(ref, rule, guid) {
|
|
53
|
-
const
|
|
54
|
-
const anchors = cfvo.map(x14CfvoXml).join('');
|
|
53
|
+
const anchors = dataBarAnchors(rule).map(x14Cfvo).join('');
|
|
55
54
|
const gradient = boolAttr('gradient', rule.gradient);
|
|
56
55
|
const negative = rule.negativeFillColor !== undefined
|
|
57
56
|
? `<x14:negativeFillColor ${colorAttrs(rule.negativeFillColor)}/>`
|
|
@@ -62,12 +61,23 @@ function x14DataBarXml(ref, rule, guid) {
|
|
|
62
61
|
`<x14:dataBar${gradient}>${anchors}${negative}${axis}</x14:dataBar>` +
|
|
63
62
|
`</x14:cfRule><xm:sqref>${escapeText(ref)}</xm:sqref></x14:conditionalFormatting>`);
|
|
64
63
|
}
|
|
65
|
-
function
|
|
66
|
-
|
|
67
|
-
if (cfvo.value === undefined)
|
|
68
|
-
return `<x14:cfvo type="${type}"/>`;
|
|
69
|
-
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;
|
|
70
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');
|
|
71
81
|
function cfRuleExtLinkXml(guid) {
|
|
72
82
|
return `<extLst>${x14Ext(DATABAR_LINK_EXT_URI, `<x14:id>${guid}</x14:id>`)}</extLst>`;
|
|
73
83
|
}
|
|
@@ -140,9 +150,8 @@ function scaleXml(rule) {
|
|
|
140
150
|
return iconSetXml(rule);
|
|
141
151
|
}
|
|
142
152
|
function dataBarXml(rule) {
|
|
143
|
-
const cfvo = rule.cfvo && rule.cfvo.length > 0 ? rule.cfvo : DEFAULT_DATABAR_CFVO;
|
|
144
153
|
const color = rule.color ?? DEFAULT_DATABAR_COLOR;
|
|
145
|
-
const anchors =
|
|
154
|
+
const anchors = dataBarAnchors(rule).map(cfvoXml).join('');
|
|
146
155
|
return `<dataBar>${anchors}<color ${colorAttrs(color)}/></dataBar>`;
|
|
147
156
|
}
|
|
148
157
|
function colorScaleXml(rule) {
|
|
@@ -157,11 +166,6 @@ function iconSetXml(rule) {
|
|
|
157
166
|
const anchors = (rule.cfvo ?? []).map(cfvoXml).join('');
|
|
158
167
|
return `<iconSet${name}>${anchors}</iconSet>`;
|
|
159
168
|
}
|
|
160
|
-
function cfvoXml(cfvo) {
|
|
161
|
-
const type = checkedToken(cfvo.type, isCfValueObjectType, 'conditional format value type');
|
|
162
|
-
const val = cfvo.value === undefined ? '' : textAttr('val', String(cfvo.value));
|
|
163
|
-
return `<cfvo type="${type}"${val}/>`;
|
|
164
|
-
}
|
|
165
169
|
export function conditionalFormattingPass() {
|
|
166
170
|
const blocks = [];
|
|
167
171
|
let block;
|
|
@@ -174,9 +178,9 @@ export function conditionalFormattingPass() {
|
|
|
174
178
|
let x14ExtId;
|
|
175
179
|
const x14IdCapture = new TextCapture('id');
|
|
176
180
|
const handlers = {
|
|
177
|
-
onOpen(name, attrs, selfClosing) {
|
|
181
|
+
onOpen(name, attrs, selfClosing, scope) {
|
|
178
182
|
const ln = localName(name);
|
|
179
|
-
if (name
|
|
183
|
+
if (isExtensionElement(scope, name)) {
|
|
180
184
|
if (ln === 'id' && draft !== undefined) {
|
|
181
185
|
x14IdCapture.open(ln, selfClosing);
|
|
182
186
|
}
|
|
@@ -233,9 +237,9 @@ export function conditionalFormattingPass() {
|
|
|
233
237
|
formulaCapture.text(chunk);
|
|
234
238
|
x14IdCapture.text(chunk);
|
|
235
239
|
},
|
|
236
|
-
onClose(name) {
|
|
240
|
+
onClose(name, scope) {
|
|
237
241
|
const ln = localName(name);
|
|
238
|
-
if (name
|
|
242
|
+
if (isExtensionElement(scope, name)) {
|
|
239
243
|
const id = x14IdCapture.close(ln);
|
|
240
244
|
if (id !== undefined) {
|
|
241
245
|
if (draft !== undefined)
|