@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
package/README.md
CHANGED
|
@@ -149,12 +149,15 @@ const writer = new WorkbookStreamWriter();
|
|
|
149
149
|
const out = writer.addWorksheet('Big');
|
|
150
150
|
for (let i = 1; i <= 1_000_000; i++) out.addRow([i, i * i]).commit();
|
|
151
151
|
out.commit();
|
|
152
|
-
const packaged
|
|
152
|
+
const packaged = await writer.commit(); // Uint8Array here; also delivered via writer.stream
|
|
153
153
|
```
|
|
154
154
|
|
|
155
155
|
The streaming writer is asynchronous where the buffered path is synchronous: `commit()`
|
|
156
156
|
resolves to the package bytes and simultaneously pipes them through `writer.stream` (a Node
|
|
157
157
|
`Readable`), so `writer.stream.pipe(res)` streams a workbook straight to an HTTP response.
|
|
158
|
+
Hand the writer its own sink instead (`{stream}` or `{filename}`) and never touch
|
|
159
|
+
`writer.stream`, and `commit()` resolves with `undefined`: the archive goes to the sink and is
|
|
160
|
+
never assembled as one object, which is what passing a sink was for.
|
|
158
161
|
|
|
159
162
|
## Writing without blocking the event loop
|
|
160
163
|
|
package/dist/bytes.d.ts
CHANGED
|
@@ -22,3 +22,10 @@ export declare function concat(chunks: readonly Uint8Array[], size?: number): Ui
|
|
|
22
22
|
* hash, so the loop below is not on any path where its cost is measurable.
|
|
23
23
|
*/
|
|
24
24
|
export declare function toBase64(bytes: Uint8Array): string;
|
|
25
|
+
/** A string's UTF-16LE bytes, code unit by code unit, with lone surrogates carried through. */
|
|
26
|
+
export declare function utf16leBytes(text: string): Uint8Array;
|
|
27
|
+
/**
|
|
28
|
+
* Decode UTF-16LE code units. A trailing odd byte is dropped: these fields are length-prefixed by
|
|
29
|
+
* their producer, and half a code unit carries nothing to decode.
|
|
30
|
+
*/
|
|
31
|
+
export declare function decodeUtf16le(bytes: Uint8Array): string;
|
package/dist/bytes.js
CHANGED
|
@@ -32,3 +32,25 @@ export function toBase64(bytes) {
|
|
|
32
32
|
}
|
|
33
33
|
return out;
|
|
34
34
|
}
|
|
35
|
+
export function utf16leBytes(text) {
|
|
36
|
+
const bytes = new Uint8Array(text.length * 2);
|
|
37
|
+
for (let i = 0; i < text.length; i++) {
|
|
38
|
+
const unit = text.charCodeAt(i);
|
|
39
|
+
bytes[i * 2] = unit & 0xff;
|
|
40
|
+
bytes[i * 2 + 1] = unit >>> 8;
|
|
41
|
+
}
|
|
42
|
+
return bytes;
|
|
43
|
+
}
|
|
44
|
+
const CHARS_PER_BATCH = 4096;
|
|
45
|
+
export function decodeUtf16le(bytes) {
|
|
46
|
+
let text = '';
|
|
47
|
+
let batch = [];
|
|
48
|
+
for (let i = 0; i + 1 < bytes.length; i += 2) {
|
|
49
|
+
batch.push(bytes[i] | (bytes[i + 1] << 8));
|
|
50
|
+
if (batch.length === CHARS_PER_BATCH) {
|
|
51
|
+
text += String.fromCharCode(...batch);
|
|
52
|
+
batch = [];
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
return batch.length > 0 ? text + String.fromCharCode(...batch) : text;
|
|
56
|
+
}
|
package/dist/core/address.d.ts
CHANGED
|
@@ -2,6 +2,22 @@
|
|
|
2
2
|
export declare const MAX_COLUMN = 16384;
|
|
3
3
|
/** Excel's row bound: 1 through 1048576. The other axis of {@link MAX_COLUMN}. */
|
|
4
4
|
export declare const MAX_ROW = 1048576;
|
|
5
|
+
/**
|
|
6
|
+
* The same two bounds, zero-based: the last valid *index* on each axis.
|
|
7
|
+
*
|
|
8
|
+
* BIFF12 counts from zero throughout, so its reader and its formula decoder both need these, and both
|
|
9
|
+
* were deriving them privately. Two derivations of one fact is one fewer than three, but it is still
|
|
10
|
+
* the shape where a change to a limit reaches some of its consequences. The subtraction belongs beside
|
|
11
|
+
* the number it subtracts from.
|
|
12
|
+
*/
|
|
13
|
+
export declare const MAX_COLUMN_INDEX: number;
|
|
14
|
+
export declare const MAX_ROW_INDEX: number;
|
|
15
|
+
/**
|
|
16
|
+
* Refuse a coordinate that names no position on the grid.
|
|
17
|
+
*
|
|
18
|
+
* @throws {RangeError} unless `value` is an integer within the axis's bounds.
|
|
19
|
+
*/
|
|
20
|
+
export declare function assertAxisInBounds(axis: 'row' | 'column', value: number): void;
|
|
5
21
|
/** @throws {RangeError} unless `n` is an integer in `1..MAX_COLUMN`. */
|
|
6
22
|
export declare function assertColumnInBounds(n: number): void;
|
|
7
23
|
/** @throws {RangeError} unless `n` is an integer in `1..MAX_ROW`. */
|
|
@@ -56,7 +72,19 @@ export interface RangeAddress {
|
|
|
56
72
|
*
|
|
57
73
|
* @throws {RangeError} unless `n` is an integer in `1..MAX_COLUMN`. */
|
|
58
74
|
export declare function numberToColumn(n: number): string;
|
|
59
|
-
/**
|
|
75
|
+
/**
|
|
76
|
+
* Convert column letters to a 1-based number (`"A" → 1`, `"AA" → 27`), or `undefined` when they name
|
|
77
|
+
* no column that can exist: not letters at all, or past `XFD` (`"ZZZ"` is well-formed and is column
|
|
78
|
+
* 18278, which the grid does not have). The tolerant half of the pair the module header describes,
|
|
79
|
+
* for a caller reading a file rather than a caller's own argument.
|
|
80
|
+
*/
|
|
81
|
+
export declare function tryColumnToNumber(letters: string): number | undefined;
|
|
82
|
+
/**
|
|
83
|
+
* Convert column letters to a 1-based number (`"A" → 1`, `"AA" → 27`).
|
|
84
|
+
*
|
|
85
|
+
* @throws {RangeError} if the letters are malformed or name a column past `XFD`. The two are
|
|
86
|
+
* separate messages because a caller fixes them differently.
|
|
87
|
+
*/
|
|
60
88
|
export declare function columnToNumber(letters: string): number;
|
|
61
89
|
/**
|
|
62
90
|
* One corner of a range, as its reference text. An axis the corner omits contributes nothing, so a
|
|
@@ -67,6 +95,17 @@ export declare function columnToNumber(letters: string): number;
|
|
|
67
95
|
* The unbounded counterpart to {@link encodeAddress}, which names one cell and so requires both.
|
|
68
96
|
*/
|
|
69
97
|
export declare function encodeCornerRef(col: number | undefined, row: number | undefined): string;
|
|
98
|
+
/**
|
|
99
|
+
* A bounded rectangle as its canonical `tl:br` A1 range, `"B2:D5"`. A one-cell rectangle still reads
|
|
100
|
+
* as `"B2:B2"`, which is the form Excel writes and the form every consumer of these refs parses.
|
|
101
|
+
*
|
|
102
|
+
* The rendering half of {@link boundedRect}, and it belongs beside it for the same reason: ten sites
|
|
103
|
+
* spelled `` `${encodeAddress(left, top)}:${encodeAddress(right, bottom)}` `` by hand, and the risk in
|
|
104
|
+
* that expression is not its length but its argument order. `encodeAddress` takes column first and a
|
|
105
|
+
* rectangle names its rows first, so the two orders are transposed with respect to each other at every
|
|
106
|
+
* one of those sites.
|
|
107
|
+
*/
|
|
108
|
+
export declare function encodeRect(rect: GridRect): string;
|
|
70
109
|
/**
|
|
71
110
|
* Decode a single cell/row/column reference into `{address, col, row}`. Anchoring
|
|
72
111
|
* `$` signs are accepted and dropped; an absent axis is `undefined`.
|
|
@@ -98,6 +137,19 @@ export declare function decodeCellRef(reference: string): CellPosition;
|
|
|
98
137
|
* there is nothing here".
|
|
99
138
|
*/
|
|
100
139
|
export declare function tryDecodeCellRef(reference: string): CellPosition | undefined;
|
|
140
|
+
/**
|
|
141
|
+
* Narrow a decoded range to a {@link GridRect}, or `undefined` when either axis is unbounded.
|
|
142
|
+
*
|
|
143
|
+
* A whole-column reference (`A:A`) and a whole-row one (`1:1`) are legal range references whose
|
|
144
|
+
* corners are partly absent, so every caller that needs a real rectangle has to test all four. Doing
|
|
145
|
+
* that in seven places is the other half of the convention `GridRect` already exists to state once:
|
|
146
|
+
* the shape is declared here, and so is the one narrowing that produces it.
|
|
147
|
+
*
|
|
148
|
+
* A caller wanting to say something *different* about each axis (a `Range` refuses `A:A` and `1:1`
|
|
149
|
+
* with two distinct messages) still tests them separately, and one that only cares about the axis it
|
|
150
|
+
* is splicing still tests only that one. Neither is this question.
|
|
151
|
+
*/
|
|
152
|
+
export declare function boundedRect(range: RangeAddress): GridRect | undefined;
|
|
101
153
|
/**
|
|
102
154
|
* {@link decodeRange} for a reference that came out of a file: `undefined` for anything that does
|
|
103
155
|
* not name a region that can exist. The sibling of {@link tryDecodeCellRef} on the other arity: a
|
|
@@ -114,5 +166,11 @@ export declare function tryDecodeRange(reference: string): RangeAddress | undefi
|
|
|
114
166
|
* corners coincide.
|
|
115
167
|
*/
|
|
116
168
|
export declare function decodeRange(reference: string): RangeAddress;
|
|
117
|
-
/**
|
|
169
|
+
/**
|
|
170
|
+
* Encode a 1-based `col`/`row` pair into its canonical A1 address (`"B2"`).
|
|
171
|
+
*
|
|
172
|
+
* Both axes go through the shared guard. The column already did, through `numberToColumn`; the row
|
|
173
|
+
* checked only its lower bound in a message of its own, so `encodeAddress(1, 1048577)` produced an
|
|
174
|
+
* address naming a row Excel has no reference for while `encodeAddress(16385, 1)` refused.
|
|
175
|
+
*/
|
|
118
176
|
export declare function encodeAddress(col: number, row: number): string;
|
package/dist/core/address.js
CHANGED
|
@@ -1,14 +1,20 @@
|
|
|
1
|
+
import { quoted } from '../errors.js';
|
|
1
2
|
export const MAX_COLUMN = 16384;
|
|
2
3
|
export const MAX_ROW = 1048576;
|
|
3
|
-
export
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
export const MAX_COLUMN_INDEX = MAX_COLUMN - 1;
|
|
5
|
+
export const MAX_ROW_INDEX = MAX_ROW - 1;
|
|
6
|
+
const AXIS_BOUND = { row: MAX_ROW, column: MAX_COLUMN };
|
|
7
|
+
export function assertAxisInBounds(axis, value) {
|
|
8
|
+
const bound = AXIS_BOUND[axis];
|
|
9
|
+
if (!Number.isInteger(value) || value < 1 || value > bound) {
|
|
10
|
+
throw new RangeError(`${axis} ${value} is out of bounds: Excel supports 1..${bound}`);
|
|
6
11
|
}
|
|
7
12
|
}
|
|
13
|
+
export function assertColumnInBounds(n) {
|
|
14
|
+
assertAxisInBounds('column', n);
|
|
15
|
+
}
|
|
8
16
|
export function assertRowInBounds(n) {
|
|
9
|
-
|
|
10
|
-
throw new RangeError(`row ${n} is out of bounds: Excel supports 1..${MAX_ROW}`);
|
|
11
|
-
}
|
|
17
|
+
assertAxisInBounds('row', n);
|
|
12
18
|
}
|
|
13
19
|
export function rectsOverlap(a, b) {
|
|
14
20
|
return a.left <= b.right && b.left <= a.right && a.top <= b.bottom && b.top <= a.bottom;
|
|
@@ -26,38 +32,43 @@ export function numberToColumn(n) {
|
|
|
26
32
|
}
|
|
27
33
|
return letters;
|
|
28
34
|
}
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
35
|
+
const COLUMN_LETTERS = /^[A-Z]{1,3}$/;
|
|
36
|
+
export function tryColumnToNumber(letters) {
|
|
37
|
+
if (!COLUMN_LETTERS.test(letters))
|
|
38
|
+
return undefined;
|
|
33
39
|
let n = 0;
|
|
34
40
|
for (let i = 0; i < letters.length; i++) {
|
|
35
|
-
|
|
36
|
-
if (code < 65 || code > 90) {
|
|
37
|
-
throw new RangeError(`invalid column letters: "${letters}"`);
|
|
38
|
-
}
|
|
39
|
-
n = n * 26 + (code - 64);
|
|
41
|
+
n = n * 26 + (letters.charCodeAt(i) - 64);
|
|
40
42
|
}
|
|
41
|
-
|
|
42
|
-
|
|
43
|
+
return n > MAX_COLUMN ? undefined : n;
|
|
44
|
+
}
|
|
45
|
+
export function columnToNumber(letters) {
|
|
46
|
+
const n = tryColumnToNumber(letters);
|
|
47
|
+
if (n !== undefined)
|
|
48
|
+
return n;
|
|
49
|
+
if (!COLUMN_LETTERS.test(letters)) {
|
|
50
|
+
throw new RangeError(`invalid column letters: ${quoted(letters)}`);
|
|
43
51
|
}
|
|
44
|
-
|
|
52
|
+
throw new RangeError(`column ${quoted(letters)} is out of bounds: Excel supports up to ${MAX_COLUMN} (XFD)`);
|
|
45
53
|
}
|
|
46
54
|
export function encodeCornerRef(col, row) {
|
|
47
55
|
return `${col !== undefined ? numberToColumn(col) : ''}${row !== undefined ? row : ''}`;
|
|
48
56
|
}
|
|
57
|
+
export function encodeRect(rect) {
|
|
58
|
+
return `${encodeAddress(rect.left, rect.top)}:${encodeAddress(rect.right, rect.bottom)}`;
|
|
59
|
+
}
|
|
49
60
|
function makeCellAddress(col, row) {
|
|
50
61
|
return { address: encodeCornerRef(col, row), col, row };
|
|
51
62
|
}
|
|
52
63
|
export function decodeAddress(reference) {
|
|
53
64
|
const match = SINGLE_REF.exec(reference);
|
|
54
65
|
if (!match) {
|
|
55
|
-
throw new SyntaxError(`invalid cell reference:
|
|
66
|
+
throw new SyntaxError(`invalid cell reference: ${quoted(reference)}`);
|
|
56
67
|
}
|
|
57
68
|
const letters = match[1] ?? '';
|
|
58
69
|
const digits = match[2] ?? '';
|
|
59
70
|
if (letters.length === 0 && digits.length === 0) {
|
|
60
|
-
throw new SyntaxError(`invalid cell reference:
|
|
71
|
+
throw new SyntaxError(`invalid cell reference: ${quoted(reference)}`);
|
|
61
72
|
}
|
|
62
73
|
const col = letters.length > 0 ? columnToNumber(letters) : undefined;
|
|
63
74
|
const row = digits.length > 0 ? Number.parseInt(digits, 10) : undefined;
|
|
@@ -66,7 +77,7 @@ export function decodeAddress(reference) {
|
|
|
66
77
|
export function decodeCellRef(reference) {
|
|
67
78
|
const { col, row } = decodeAddress(reference);
|
|
68
79
|
if (col === undefined || row === undefined) {
|
|
69
|
-
throw new SyntaxError(
|
|
80
|
+
throw new SyntaxError(`${quoted(reference)} is not a single-cell reference: it omits a column or row`);
|
|
70
81
|
}
|
|
71
82
|
return { col, row };
|
|
72
83
|
}
|
|
@@ -83,6 +94,13 @@ export function tryDecodeCellRef(reference) {
|
|
|
83
94
|
}
|
|
84
95
|
return rowCanExist(position.row) ? position : undefined;
|
|
85
96
|
}
|
|
97
|
+
export function boundedRect(range) {
|
|
98
|
+
const { top, left, bottom, right } = range;
|
|
99
|
+
if (top === undefined || left === undefined || bottom === undefined || right === undefined) {
|
|
100
|
+
return undefined;
|
|
101
|
+
}
|
|
102
|
+
return { top, left, bottom, right };
|
|
103
|
+
}
|
|
86
104
|
export function tryDecodeRange(reference) {
|
|
87
105
|
let range;
|
|
88
106
|
try {
|
|
@@ -125,8 +143,6 @@ export function decodeRange(reference) {
|
|
|
125
143
|
};
|
|
126
144
|
}
|
|
127
145
|
export function encodeAddress(col, row) {
|
|
128
|
-
|
|
129
|
-
throw new RangeError(`row ${row} is out of bounds: rows start at 1`);
|
|
130
|
-
}
|
|
146
|
+
assertRowInBounds(row);
|
|
131
147
|
return `${numberToColumn(col)}${row}`;
|
|
132
148
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { type AxisSplice } from './grid-shift.ts';
|
|
1
2
|
/**
|
|
2
3
|
* A worksheet's autofilter: the filtered region plus any per-column criteria narrowing it. A bare
|
|
3
4
|
* range (no columns) is just the header-row dropdowns Excel draws; adding {@link FilterColumn}s
|
|
@@ -54,8 +55,8 @@ export declare const isCustomFilterOperator: (value: string) => value is CustomF
|
|
|
54
55
|
*/
|
|
55
56
|
export declare function canonicalizeAutoFilter(input: string | AutoFilter): AutoFilter;
|
|
56
57
|
/**
|
|
57
|
-
* Re-anchor a filter through a splice
|
|
58
|
-
*
|
|
58
|
+
* Re-anchor a filter through a splice, or drop it (`undefined`) when the splice deleted every line
|
|
59
|
+
* it covered.
|
|
59
60
|
*
|
|
60
61
|
* A row splice only moves the range. A column splice moves its left edge too, and a criterion is
|
|
61
62
|
* addressed by its offset from that edge rather than by an absolute column, so every offset is
|
|
@@ -63,4 +64,4 @@ export declare function canonicalizeAutoFilter(input: string | AutoFilter): Auto
|
|
|
63
64
|
* Left alone, those offsets would keep their old numbers and silently re-point each filter at a
|
|
64
65
|
* neighbouring column.
|
|
65
66
|
*/
|
|
66
|
-
export declare function shiftAutoFilter(filter: AutoFilter,
|
|
67
|
+
export declare function shiftAutoFilter(filter: AutoFilter, splice: AxisSplice): AutoFilter | undefined;
|
package/dist/core/autofilter.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { AuthoringError } from '../errors.js';
|
|
1
|
+
import { AuthoringError, quoted } from '../errors.js';
|
|
2
2
|
import { tokenSet } from '../token-set.js';
|
|
3
|
-
import { decodeRange,
|
|
4
|
-
import { isDeletedSpan, shiftIndex } from './grid-shift.js';
|
|
3
|
+
import { boundedRect, decodeRange, encodeRect } from './address.js';
|
|
4
|
+
import { isDeletedSpan, shiftIndex, shiftRect } from './grid-shift.js';
|
|
5
5
|
export const isCustomFilterOperator = tokenSet({
|
|
6
6
|
equal: true,
|
|
7
7
|
notEqual: true,
|
|
@@ -12,15 +12,16 @@ export const isCustomFilterOperator = tokenSet({
|
|
|
12
12
|
});
|
|
13
13
|
export function canonicalizeAutoFilter(input) {
|
|
14
14
|
const ref = typeof input === 'string' ? input : input.ref;
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
const decoded = decodeRange(ref);
|
|
16
|
+
const rect = boundedRect(decoded);
|
|
17
|
+
if (rect === undefined) {
|
|
18
|
+
throw new AuthoringError(`autofilter range ${quoted(ref)} must be a bounded rectangle`);
|
|
18
19
|
}
|
|
19
20
|
if (typeof input === 'string')
|
|
20
|
-
return { ref: dimensions, columns: [] };
|
|
21
|
-
const width = right - left + 1;
|
|
21
|
+
return { ref: decoded.dimensions, columns: [] };
|
|
22
|
+
const width = rect.right - rect.left + 1;
|
|
22
23
|
return {
|
|
23
|
-
ref: dimensions,
|
|
24
|
+
ref: decoded.dimensions,
|
|
24
25
|
columns: input.columns.map((column) => canonicalizeColumn(column, width)),
|
|
25
26
|
};
|
|
26
27
|
}
|
|
@@ -28,35 +29,35 @@ function canonicalizeColumn(column, width) {
|
|
|
28
29
|
if (!Number.isInteger(column.colId) || column.colId < 0 || column.colId >= width) {
|
|
29
30
|
throw new AuthoringError(`autofilter colId ${column.colId} is outside the filter range`);
|
|
30
31
|
}
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
throw new AuthoringError(`a custom filter needs one or two predicates, got ${count}`);
|
|
35
|
-
}
|
|
32
|
+
const criteria = column.criteria;
|
|
33
|
+
if (criteria.kind === 'values') {
|
|
34
|
+
return { colId: column.colId, criteria: { ...criteria, values: [...criteria.values] } };
|
|
36
35
|
}
|
|
37
|
-
|
|
36
|
+
const count = criteria.predicates.length;
|
|
37
|
+
if (count < 1 || count > 2) {
|
|
38
|
+
throw new AuthoringError(`a custom filter needs one or two predicates, got ${count}`);
|
|
39
|
+
}
|
|
40
|
+
return {
|
|
41
|
+
colId: column.colId,
|
|
42
|
+
criteria: { ...criteria, predicates: criteria.predicates.map((predicate) => ({ ...predicate })) },
|
|
43
|
+
};
|
|
38
44
|
}
|
|
39
|
-
export function shiftAutoFilter(filter,
|
|
40
|
-
const
|
|
41
|
-
if (
|
|
45
|
+
export function shiftAutoFilter(filter, splice) {
|
|
46
|
+
const rect = boundedRect(decodeRange(filter.ref));
|
|
47
|
+
if (rect === undefined)
|
|
42
48
|
return filter;
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
if (isDeletedSpan(lo, hi, start, count))
|
|
49
|
+
const moved = shiftRect(rect, splice);
|
|
50
|
+
if (moved === undefined)
|
|
46
51
|
return undefined;
|
|
47
|
-
const
|
|
48
|
-
|
|
49
|
-
if (axis === 'row') {
|
|
50
|
-
const ref = `${encodeAddress(left, movedLo)}:${encodeAddress(right, movedHi)}`;
|
|
52
|
+
const ref = encodeRect(moved);
|
|
53
|
+
if (splice.axis === 'row')
|
|
51
54
|
return { ref, columns: filter.columns };
|
|
52
|
-
}
|
|
53
|
-
const ref = `${encodeAddress(movedLo, top)}:${encodeAddress(movedHi, bottom)}`;
|
|
54
55
|
const columns = [];
|
|
55
56
|
for (const column of filter.columns) {
|
|
56
|
-
const absolute = left + column.colId;
|
|
57
|
-
if (isDeletedSpan(absolute, absolute,
|
|
57
|
+
const absolute = rect.left + column.colId;
|
|
58
|
+
if (isDeletedSpan(absolute, absolute, splice))
|
|
58
59
|
continue;
|
|
59
|
-
columns.push({ ...column, colId: shiftIndex(absolute,
|
|
60
|
+
columns.push({ ...column, colId: shiftIndex(absolute, splice) - moved.left });
|
|
60
61
|
}
|
|
61
62
|
return { ref, columns };
|
|
62
63
|
}
|
|
@@ -3,6 +3,8 @@ export declare abstract class AxisHandle<P extends object> {
|
|
|
3
3
|
protected abstract propertiesOf(): P | undefined;
|
|
4
4
|
/** This position's format record, created if it does not exist yet. */
|
|
5
5
|
protected abstract ensureProperties(): P;
|
|
6
|
+
/** Remove this position's format record entirely, so nothing is left declaring the line. */
|
|
7
|
+
protected abstract dropProperties(): void;
|
|
6
8
|
protected read<K extends keyof P>(key: K): P[K] | undefined;
|
|
7
9
|
protected write<K extends keyof P>(key: K, value: P[K]): void;
|
|
8
10
|
}
|
package/dist/core/axis-handle.js
CHANGED
|
@@ -5,8 +5,11 @@ export class AxisHandle {
|
|
|
5
5
|
write(key, value) {
|
|
6
6
|
if (value === undefined) {
|
|
7
7
|
const properties = this.propertiesOf();
|
|
8
|
-
if (properties
|
|
9
|
-
|
|
8
|
+
if (properties === undefined)
|
|
9
|
+
return;
|
|
10
|
+
delete properties[key];
|
|
11
|
+
if (Object.keys(properties).length === 0)
|
|
12
|
+
this.dropProperties();
|
|
10
13
|
return;
|
|
11
14
|
}
|
|
12
15
|
this.ensureProperties()[key] = value;
|
package/dist/core/cell.d.ts
CHANGED
|
@@ -139,9 +139,10 @@ export declare class Cell {
|
|
|
139
139
|
set note(note: string | undefined);
|
|
140
140
|
}
|
|
141
141
|
/**
|
|
142
|
-
* Whether a cell carries formatting of its own:
|
|
143
|
-
* flag
|
|
144
|
-
* tuple reaches every "is this cell blank"
|
|
142
|
+
* Whether a cell carries formatting of its own: any {@link CellContent} facet, which is the six shared
|
|
143
|
+
* style facets plus the quote-prefix flag and the link to a named style. Driven by
|
|
144
|
+
* {@link CELL_CONTENT_FACETS}, so a facet added to the tuple reaches every "is this cell blank"
|
|
145
|
+
* decision without anyone remembering to widen a literal.
|
|
145
146
|
*
|
|
146
147
|
* Row- and column-inherited formatting is not the cell's own and does not count. Neither does a
|
|
147
148
|
* note: it lives in the comments part, not the cell's `<c>` element, so a writer deciding whether an
|
package/dist/core/cell.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { assertColumnInBounds, assertRowInBounds, encodeAddress } from './address.js';
|
|
2
2
|
import { NAMED_STYLE_ID } from './internal.js';
|
|
3
|
-
import { assignStyleFacets,
|
|
3
|
+
import { assignContentFacets, assignStyleFacets, CELL_CONTENT_FACETS, } from './style.js';
|
|
4
4
|
import { cellValueToText, coerceCellValue, detectValueType, } from './value.js';
|
|
5
5
|
export class Cell {
|
|
6
6
|
row;
|
|
@@ -107,9 +107,7 @@ export class Cell {
|
|
|
107
107
|
}
|
|
108
108
|
}
|
|
109
109
|
export function cellHasOwnStyle(cell) {
|
|
110
|
-
return
|
|
111
|
-
cell.quotePrefix === true ||
|
|
112
|
-
cell[NAMED_STYLE_ID] !== undefined);
|
|
110
|
+
return CELL_CONTENT_FACETS.some((facet) => cell[facet] !== undefined);
|
|
113
111
|
}
|
|
114
112
|
export function cellCarriesContent(cell) {
|
|
115
113
|
return cell.value !== null || cell.note !== undefined || cellHasOwnStyle(cell);
|
|
@@ -119,7 +117,7 @@ export function applyCellStyle(cell, style) {
|
|
|
119
117
|
}
|
|
120
118
|
export function copyCellContent(source, target) {
|
|
121
119
|
target.value = source.value;
|
|
122
|
-
|
|
120
|
+
assignContentFacets(target, source);
|
|
123
121
|
target.note = source.note;
|
|
124
122
|
}
|
|
125
123
|
export function cellToModel(cell) {
|
|
@@ -129,6 +127,6 @@ export function cellToModel(cell) {
|
|
|
129
127
|
value: cell.value,
|
|
130
128
|
note: cell.note,
|
|
131
129
|
};
|
|
132
|
-
|
|
130
|
+
assignContentFacets(model, cell);
|
|
133
131
|
return model;
|
|
134
132
|
}
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* How far a copy of one field goes.
|
|
3
|
+
*
|
|
4
|
+
* - `'value'`: assign it. Correct for a scalar, and for anything genuinely immutable.
|
|
5
|
+
* - `'record'`: `{...v}`. A flat object whose own fields are scalars.
|
|
6
|
+
* - `'values'`: `[...v]`. An array of scalars.
|
|
7
|
+
* - `'records'`: `v.map(x => ({...x}))`. An array of flat objects.
|
|
8
|
+
* - a function: the field's own clone, for a nested type that has a plan of its own.
|
|
9
|
+
*/
|
|
10
|
+
export type CloneStrategy<V> = 'value' | 'record' | 'values' | 'records' | ((value: V) => V);
|
|
11
|
+
/**
|
|
12
|
+
* One strategy per field of `T`. The `Record` is what makes a missing field a compile error.
|
|
13
|
+
*
|
|
14
|
+
* A field's strategy sees the field's type without `undefined`, because {@link cloneWith} skips an
|
|
15
|
+
* absent field rather than handing it over. Under `exactOptionalPropertyTypes` a facet declared
|
|
16
|
+
* `font?: Font | undefined` keeps that `undefined` through `Required`, so without the exclusion a
|
|
17
|
+
* nested type's own clone function would not satisfy its own field's strategy.
|
|
18
|
+
*/
|
|
19
|
+
export type ClonePlan<T> = {
|
|
20
|
+
readonly [K in keyof Required<T>]-?: CloneStrategy<Exclude<Required<T>[K], undefined>>;
|
|
21
|
+
};
|
|
22
|
+
/**
|
|
23
|
+
* Copy `source` according to `plan`, leaving a field absent when the source leaves it absent.
|
|
24
|
+
*
|
|
25
|
+
* Absent rather than `undefined`: every one of these types declares its optional fields under
|
|
26
|
+
* `exactOptionalPropertyTypes`, where a present-but-undefined key is a different shape from a missing
|
|
27
|
+
* one, and a round-trip that fabricated the first would report formatting a cell does not have.
|
|
28
|
+
*/
|
|
29
|
+
export declare function cloneWith<T extends object>(source: T, plan: ClonePlan<T>): T;
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
export function cloneWith(source, plan) {
|
|
2
|
+
const out = {};
|
|
3
|
+
for (const key of Object.keys(plan)) {
|
|
4
|
+
const value = source[key];
|
|
5
|
+
if (value === undefined)
|
|
6
|
+
continue;
|
|
7
|
+
out[key] = applyStrategy(value, plan[key]);
|
|
8
|
+
}
|
|
9
|
+
return out;
|
|
10
|
+
}
|
|
11
|
+
function applyStrategy(value, strategy) {
|
|
12
|
+
if (typeof strategy === 'function')
|
|
13
|
+
return strategy(value);
|
|
14
|
+
switch (strategy) {
|
|
15
|
+
case 'value':
|
|
16
|
+
return value;
|
|
17
|
+
case 'record':
|
|
18
|
+
return { ...value };
|
|
19
|
+
case 'values':
|
|
20
|
+
return [...value];
|
|
21
|
+
case 'records':
|
|
22
|
+
return value.map((entry) => ({ ...entry }));
|
|
23
|
+
}
|
|
24
|
+
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { hex } from '../hex.js';
|
|
1
2
|
import { parseArgb } from './style.js';
|
|
2
3
|
import { DEFAULT_THEME_COLOR_SCHEME, THEME_COLOR_SLOTS } from './theme.js';
|
|
3
4
|
export const DEFAULT_INDEXED_COLORS = [
|
|
@@ -102,8 +103,5 @@ function hueToChannel(p, q, offset) {
|
|
|
102
103
|
return p;
|
|
103
104
|
}
|
|
104
105
|
function channelHex(value) {
|
|
105
|
-
return Math.round(clamp01(value) * 255)
|
|
106
|
-
.toString(16)
|
|
107
|
-
.toUpperCase()
|
|
108
|
-
.padStart(2, '0');
|
|
106
|
+
return hex(Math.round(clamp01(value) * 255), 2);
|
|
109
107
|
}
|
package/dist/core/column.d.ts
CHANGED
|
@@ -21,6 +21,7 @@ export declare class Column extends AxisHandle<ColumnProperties> {
|
|
|
21
21
|
get properties(): Readonly<ColumnProperties> | undefined;
|
|
22
22
|
protected propertiesOf(): ColumnProperties | undefined;
|
|
23
23
|
protected ensureProperties(): ColumnProperties;
|
|
24
|
+
protected dropProperties(): void;
|
|
24
25
|
/**
|
|
25
26
|
* Stable key naming this column so a keyed-object row (see {@link Worksheet.addRow}) can place a
|
|
26
27
|
* value under it by name rather than position. In-memory only: never serialized to OOXML.
|
package/dist/core/column.js
CHANGED
|
@@ -22,6 +22,9 @@ export class Column extends AxisHandle {
|
|
|
22
22
|
ensureProperties() {
|
|
23
23
|
return this.#sheet[INTERNAL].ensureColumnProperties(this.index);
|
|
24
24
|
}
|
|
25
|
+
dropProperties() {
|
|
26
|
+
this.#sheet[INTERNAL].dropColumnProperties(this.index);
|
|
27
|
+
}
|
|
25
28
|
get key() {
|
|
26
29
|
return this.read('key');
|
|
27
30
|
}
|
|
@@ -1,9 +1,10 @@
|
|
|
1
|
+
import { quoted } from '../errors.js';
|
|
1
2
|
const GUID = /^\{?([0-9a-f]{8})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{4})-([0-9a-f]{12})\}?$/i;
|
|
2
3
|
export function commentThreadGuid(value, what) {
|
|
3
4
|
const match = GUID.exec(value.trim());
|
|
4
5
|
if (match === null) {
|
|
5
6
|
throw new SyntaxError(`${what} must be a GUID: Excel writes threaded-comment ids as ` +
|
|
6
|
-
`"{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}", but got
|
|
7
|
+
`"{XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX}", but got ${quoted(value)}`);
|
|
7
8
|
}
|
|
8
9
|
return `{${match.slice(1).join('-').toUpperCase()}}`;
|
|
9
10
|
}
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { type ConditionalFormatting } from './conditional-formatting.ts';
|
|
2
|
+
import type { AxisSplice } from './grid-shift.ts';
|
|
2
3
|
export declare class ConditionalFormattingOverlay {
|
|
3
4
|
#private;
|
|
4
5
|
/**
|
|
@@ -14,7 +15,7 @@ export declare class ConditionalFormattingOverlay {
|
|
|
14
15
|
* Re-anchor every rule set through a row or column splice, so a highlight keeps covering the cells
|
|
15
16
|
* it was written for. A rule set whose every target area fell inside a deleted span goes with them.
|
|
16
17
|
*/
|
|
17
|
-
shift(
|
|
18
|
+
shift(splice: AxisSplice): void;
|
|
18
19
|
/** Drop every conditional formatting, leaving the overlay empty. */
|
|
19
20
|
clear(): void;
|
|
20
21
|
}
|
|
@@ -9,10 +9,10 @@ export class ConditionalFormattingOverlay {
|
|
|
9
9
|
get entries() {
|
|
10
10
|
return this.#entries;
|
|
11
11
|
}
|
|
12
|
-
shift(
|
|
12
|
+
shift(splice) {
|
|
13
13
|
const entries = [];
|
|
14
14
|
for (const entry of this.#entries) {
|
|
15
|
-
const ref = shiftSqref(entry.ref,
|
|
15
|
+
const ref = shiftSqref(entry.ref, splice);
|
|
16
16
|
if (ref !== undefined)
|
|
17
17
|
entries.push({ ...entry, ref });
|
|
18
18
|
}
|
|
@@ -1,4 +1,7 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { type ClonePlan } from './clone.ts';
|
|
2
|
+
import type { AssertNever } from './internal.ts';
|
|
3
|
+
import { type Color } from './style.ts';
|
|
4
|
+
import type { DifferentialStyle } from './workbook-styles.ts';
|
|
2
5
|
/** How a {@link CfValueObject} reads its `value`: `ST_CfvoType` verbatim. */
|
|
3
6
|
export type CfValueObjectType = 'num' | 'percent' | 'max' | 'min' | 'percentile' | 'formula';
|
|
4
7
|
/** Narrow a raw `<cfvo type>` token to a known {@link CfValueObjectType}. */
|
|
@@ -108,3 +111,8 @@ export interface ConditionalFormatting {
|
|
|
108
111
|
/** A defensive deep copy, so a stored conditional formatting never aliases the caller's object nor
|
|
109
112
|
* any of its nested arrays (rules, formulae, cfvo, colours) or the differential style. */
|
|
110
113
|
export declare function cloneConditionalFormatting(cf: ConditionalFormatting): ConditionalFormatting;
|
|
114
|
+
declare const RULE_CLONE: ClonePlan<ConditionalFormattingRule>;
|
|
115
|
+
export type EveryRuleFieldIsCloned = AssertNever<Exclude<keyof Required<ConditionalFormattingRule>, keyof typeof RULE_CLONE>>;
|
|
116
|
+
declare const STYLE_CLONE: ClonePlan<DifferentialStyle>;
|
|
117
|
+
export type EveryDifferentialStyleFieldIsCloned = AssertNever<Exclude<keyof Required<DifferentialStyle>, keyof typeof STYLE_CLONE>>;
|
|
118
|
+
export {};
|