@shbernal/ts-xlsx 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +23 -0
- package/README.md +230 -0
- package/dist/core/address.d.ts +49 -0
- package/dist/core/address.js +125 -0
- package/dist/core/autofilter.d.ts +55 -0
- package/dist/core/autofilter.js +48 -0
- package/dist/core/cell.d.ts +132 -0
- package/dist/core/cell.js +249 -0
- package/dist/core/color-resolution.d.ts +58 -0
- package/dist/core/color-resolution.js +170 -0
- package/dist/core/column.d.ts +87 -0
- package/dist/core/column.js +167 -0
- package/dist/core/comment-thread.d.ts +116 -0
- package/dist/core/comment-thread.js +62 -0
- package/dist/core/conditional-formatting-overlay.d.ts +15 -0
- package/dist/core/conditional-formatting-overlay.js +24 -0
- package/dist/core/conditional-formatting.d.ts +70 -0
- package/dist/core/conditional-formatting.js +35 -0
- package/dist/core/containers.d.ts +2 -0
- package/dist/core/containers.js +25 -0
- package/dist/core/data-validation-overlay.d.ts +28 -0
- package/dist/core/data-validation-overlay.js +56 -0
- package/dist/core/data-validation.d.ts +38 -0
- package/dist/core/data-validation.js +13 -0
- package/dist/core/date.d.ts +25 -0
- package/dist/core/date.js +58 -0
- package/dist/core/formula.d.ts +49 -0
- package/dist/core/formula.js +319 -0
- package/dist/core/grid-edits.d.ts +22 -0
- package/dist/core/grid-edits.js +206 -0
- package/dist/core/image.d.ts +76 -0
- package/dist/core/image.js +65 -0
- package/dist/core/internal.d.ts +11 -0
- package/dist/core/internal.js +29 -0
- package/dist/core/merge.d.ts +13 -0
- package/dist/core/merge.js +26 -0
- package/dist/core/modern-functions.d.ts +1 -0
- package/dist/core/modern-functions.js +165 -0
- package/dist/core/page-setup.d.ts +94 -0
- package/dist/core/page-setup.js +6 -0
- package/dist/core/pivot-table.d.ts +113 -0
- package/dist/core/pivot-table.js +249 -0
- package/dist/core/preserved.d.ts +58 -0
- package/dist/core/preserved.js +5 -0
- package/dist/core/protection.d.ts +67 -0
- package/dist/core/protection.js +68 -0
- package/dist/core/range.d.ts +112 -0
- package/dist/core/range.js +283 -0
- package/dist/core/row.d.ts +73 -0
- package/dist/core/row.js +135 -0
- package/dist/core/style.d.ts +246 -0
- package/dist/core/style.js +125 -0
- package/dist/core/table-style.d.ts +67 -0
- package/dist/core/table-style.js +90 -0
- package/dist/core/table.d.ts +194 -0
- package/dist/core/table.js +291 -0
- package/dist/core/theme.d.ts +90 -0
- package/dist/core/theme.js +272 -0
- package/dist/core/value.d.ts +115 -0
- package/dist/core/value.js +144 -0
- package/dist/core/workbook-protection.d.ts +27 -0
- package/dist/core/workbook-protection.js +25 -0
- package/dist/core/workbook.d.ts +528 -0
- package/dist/core/workbook.js +732 -0
- package/dist/core/worksheet-model.d.ts +34 -0
- package/dist/core/worksheet-model.js +100 -0
- package/dist/core/worksheet.d.ts +647 -0
- package/dist/core/worksheet.js +1025 -0
- package/dist/customui/errors.d.ts +15 -0
- package/dist/customui/errors.js +15 -0
- package/dist/customui/index.d.ts +2 -0
- package/dist/customui/index.js +7 -0
- package/dist/customui/ribbon.d.ts +86 -0
- package/dist/customui/ribbon.js +180 -0
- package/dist/entries/core.d.ts +23 -0
- package/dist/entries/core.js +20 -0
- package/dist/entries/csv.d.ts +2 -0
- package/dist/entries/csv.js +6 -0
- package/dist/entries/customui.d.ts +1 -0
- package/dist/entries/customui.js +3 -0
- package/dist/entries/errors.d.ts +7 -0
- package/dist/entries/errors.js +20 -0
- package/dist/entries/vba.d.ts +2 -0
- package/dist/entries/vba.js +7 -0
- package/dist/entries/xlsb.d.ts +1 -0
- package/dist/entries/xlsb.js +7 -0
- package/dist/entries/xlsx.d.ts +5 -0
- package/dist/entries/xlsx.js +18 -0
- package/dist/errors.d.ts +65 -0
- package/dist/errors.js +66 -0
- package/dist/index.d.ts +7 -0
- package/dist/index.js +19 -0
- package/dist/io/csv/read.d.ts +15 -0
- package/dist/io/csv/read.js +113 -0
- package/dist/io/csv/write.d.ts +28 -0
- package/dist/io/csv/write.js +124 -0
- package/dist/io/opc/errors.d.ts +46 -0
- package/dist/io/opc/errors.js +49 -0
- package/dist/io/opc/inflate.d.ts +15 -0
- package/dist/io/opc/inflate.js +91 -0
- package/dist/io/opc/namespaces.d.ts +18 -0
- package/dist/io/opc/namespaces.js +18 -0
- package/dist/io/opc/part-paths.d.ts +4 -0
- package/dist/io/opc/part-paths.js +35 -0
- package/dist/io/opc/read-opc.d.ts +24 -0
- package/dist/io/opc/read-opc.js +153 -0
- package/dist/io/opc/read-options.d.ts +10 -0
- package/dist/io/opc/read-options.js +7 -0
- package/dist/io/opc/rels.d.ts +15 -0
- package/dist/io/opc/rels.js +28 -0
- package/dist/io/opc/sniff-format.d.ts +27 -0
- package/dist/io/opc/sniff-format.js +92 -0
- package/dist/io/style/xf-style.d.ts +47 -0
- package/dist/io/style/xf-style.js +95 -0
- package/dist/io/xlsb/errors.d.ts +15 -0
- package/dist/io/xlsb/errors.js +15 -0
- package/dist/io/xlsb/formula.d.ts +48 -0
- package/dist/io/xlsb/formula.js +398 -0
- package/dist/io/xlsb/primitives.d.ts +86 -0
- package/dist/io/xlsb/primitives.js +230 -0
- package/dist/io/xlsb/ptg-functions.d.ts +16 -0
- package/dist/io/xlsb/ptg-functions.js +659 -0
- package/dist/io/xlsb/read-shared-strings.d.ts +2 -0
- package/dist/io/xlsb/read-shared-strings.js +22 -0
- package/dist/io/xlsb/read-styles.d.ts +4 -0
- package/dist/io/xlsb/read-styles.js +386 -0
- package/dist/io/xlsb/read-worksheet.d.ts +9 -0
- package/dist/io/xlsb/read-worksheet.js +301 -0
- package/dist/io/xlsb/read.d.ts +20 -0
- package/dist/io/xlsb/read.js +199 -0
- package/dist/io/xlsb/record-stream.d.ts +14 -0
- package/dist/io/xlsb/record-stream.js +56 -0
- package/dist/io/xlsb/record-types.d.ts +55 -0
- package/dist/io/xlsb/record-types.js +73 -0
- package/dist/io/xlsx/cell-accumulator.d.ts +25 -0
- package/dist/io/xlsx/cell-accumulator.js +172 -0
- package/dist/io/xlsx/cell-value.d.ts +32 -0
- package/dist/io/xlsx/cell-value.js +85 -0
- package/dist/io/xlsx/comments.d.ts +74 -0
- package/dist/io/xlsx/comments.js +243 -0
- package/dist/io/xlsx/conditional-formatting.d.ts +32 -0
- package/dist/io/xlsx/conditional-formatting.js +464 -0
- package/dist/io/xlsx/data-validation.d.ts +20 -0
- package/dist/io/xlsx/data-validation.js +258 -0
- package/dist/io/xlsx/edit-vba.d.ts +23 -0
- package/dist/io/xlsx/edit-vba.js +139 -0
- package/dist/io/xlsx/errors.d.ts +17 -0
- package/dist/io/xlsx/errors.js +17 -0
- package/dist/io/xlsx/hyperlinks.d.ts +42 -0
- package/dist/io/xlsx/hyperlinks.js +139 -0
- package/dist/io/xlsx/images.d.ts +34 -0
- package/dist/io/xlsx/images.js +218 -0
- package/dist/io/xlsx/namespaces.d.ts +55 -0
- package/dist/io/xlsx/namespaces.js +55 -0
- package/dist/io/xlsx/package-plan.d.ts +103 -0
- package/dist/io/xlsx/package-plan.js +168 -0
- package/dist/io/xlsx/pivot-read.d.ts +5 -0
- package/dist/io/xlsx/pivot-read.js +134 -0
- package/dist/io/xlsx/pivot.d.ts +12 -0
- package/dist/io/xlsx/pivot.js +141 -0
- package/dist/io/xlsx/read-rows.d.ts +85 -0
- package/dist/io/xlsx/read-rows.js +286 -0
- package/dist/io/xlsx/read-styles.d.ts +8 -0
- package/dist/io/xlsx/read-styles.js +551 -0
- package/dist/io/xlsx/read-worksheet.d.ts +4 -0
- package/dist/io/xlsx/read-worksheet.js +528 -0
- package/dist/io/xlsx/read.d.ts +32 -0
- package/dist/io/xlsx/read.js +680 -0
- package/dist/io/xlsx/relationships.d.ts +33 -0
- package/dist/io/xlsx/relationships.js +47 -0
- package/dist/io/xlsx/rich-runs.d.ts +13 -0
- package/dist/io/xlsx/rich-runs.js +59 -0
- package/dist/io/xlsx/rich-text.d.ts +7 -0
- package/dist/io/xlsx/rich-text.js +23 -0
- package/dist/io/xlsx/shared-formulas.d.ts +6 -0
- package/dist/io/xlsx/shared-formulas.js +52 -0
- package/dist/io/xlsx/shared-strings-read.d.ts +2 -0
- package/dist/io/xlsx/shared-strings-read.js +70 -0
- package/dist/io/xlsx/shared-strings.d.ts +17 -0
- package/dist/io/xlsx/shared-strings.js +50 -0
- package/dist/io/xlsx/sheet-properties.d.ts +13 -0
- package/dist/io/xlsx/sheet-properties.js +211 -0
- package/dist/io/xlsx/styles.d.ts +138 -0
- package/dist/io/xlsx/styles.js +821 -0
- package/dist/io/xlsx/tables.d.ts +9 -0
- package/dist/io/xlsx/tables.js +208 -0
- package/dist/io/xlsx/threaded-comments.d.ts +102 -0
- package/dist/io/xlsx/threaded-comments.js +308 -0
- package/dist/io/xlsx/workbook-xml.d.ts +12 -0
- package/dist/io/xlsx/workbook-xml.js +353 -0
- package/dist/io/xlsx/worksheet-xml.d.ts +86 -0
- package/dist/io/xlsx/worksheet-xml.js +643 -0
- package/dist/io/xlsx/write-stream.d.ts +171 -0
- package/dist/io/xlsx/write-stream.js +399 -0
- package/dist/io/xlsx/write.d.ts +81 -0
- package/dist/io/xlsx/write.js +396 -0
- package/dist/io/xlsx/x14-ext.d.ts +9 -0
- package/dist/io/xlsx/x14-ext.js +12 -0
- package/dist/vba/cfb-writer.d.ts +19 -0
- package/dist/vba/cfb-writer.js +277 -0
- package/dist/vba/cfb.d.ts +16 -0
- package/dist/vba/cfb.js +324 -0
- package/dist/vba/codepage.d.ts +18 -0
- package/dist/vba/codepage.js +87 -0
- package/dist/vba/errors.d.ts +24 -0
- package/dist/vba/errors.js +24 -0
- package/dist/vba/index.d.ts +5 -0
- package/dist/vba/index.js +15 -0
- package/dist/vba/ms-ovba.d.ts +12 -0
- package/dist/vba/ms-ovba.js +176 -0
- package/dist/vba/project-editor.d.ts +62 -0
- package/dist/vba/project-editor.js +377 -0
- package/dist/vba/project.d.ts +45 -0
- package/dist/vba/project.js +160 -0
- package/dist/vba/vba-encoding.d.ts +14 -0
- package/dist/vba/vba-encoding.js +39 -0
- package/dist/xml/errors.d.ts +15 -0
- package/dist/xml/errors.js +15 -0
- package/dist/xml/xml-read.d.ts +107 -0
- package/dist/xml/xml-read.js +283 -0
- package/dist/xml/xml.d.ts +41 -0
- package/dist/xml/xml.js +86 -0
- package/package.json +117 -0
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { type Cell } from './cell.ts';
|
|
2
|
+
import { type Alignment, type Border, type CellStyle, type Fill, type Font, type Protection } from './style.ts';
|
|
3
|
+
import type { Worksheet } from './worksheet.ts';
|
|
4
|
+
/**
|
|
5
|
+
* A rectangular block of a worksheet's cells: `sheet.getRange('B2:D5')`.
|
|
6
|
+
*
|
|
7
|
+
* Cheap and stateless — constructing one creates no cells and does not extend the used range.
|
|
8
|
+
* {@link addresses} walks the block without materialising anything; {@link cells} reports only what
|
|
9
|
+
* already exists.
|
|
10
|
+
*
|
|
11
|
+
* Bounds are **inclusive first/last**, never start-and-count. That is the convention every
|
|
12
|
+
* range-shaped accessor in this library follows, so the three axes cannot disagree about what a
|
|
13
|
+
* pair of numbers means.
|
|
14
|
+
*/
|
|
15
|
+
export declare class Range {
|
|
16
|
+
#private;
|
|
17
|
+
/** 1-based row of the block's top edge. Fixed for this handle's lifetime. */
|
|
18
|
+
readonly top: number;
|
|
19
|
+
/** 1-based column of the block's left edge. Fixed for this handle's lifetime. */
|
|
20
|
+
readonly left: number;
|
|
21
|
+
/** 1-based row of the block's bottom edge, inclusive. */
|
|
22
|
+
readonly bottom: number;
|
|
23
|
+
/** 1-based column of the block's right edge, inclusive. */
|
|
24
|
+
readonly right: number;
|
|
25
|
+
/**
|
|
26
|
+
* Build a handle from inclusive corners, in any order — `(5, 4, 2, 2)` and `(2, 2, 5, 4)` name the
|
|
27
|
+
* same block, exactly as `D5:B2` and `B2:D5` do. Prefer {@link Worksheet.getRange}.
|
|
28
|
+
*
|
|
29
|
+
* @throws {RangeError} if a corner is not a positive integer or falls outside the sheet's bounds.
|
|
30
|
+
*/
|
|
31
|
+
constructor(sheet: Worksheet, top: number, left: number, bottom: number, right: number);
|
|
32
|
+
/** The worksheet this block belongs to. */
|
|
33
|
+
get sheet(): Worksheet;
|
|
34
|
+
/** Canonical `tl:br` A1 form — `"B2:D5"`. A one-cell block still reads as `"B2:B2"`. */
|
|
35
|
+
get address(): string;
|
|
36
|
+
/** Rows spanned, inclusive of both edges. */
|
|
37
|
+
get rowCount(): number;
|
|
38
|
+
/** Columns spanned, inclusive of both edges. */
|
|
39
|
+
get columnCount(): number;
|
|
40
|
+
/** Cells the block covers — `rowCount * columnCount`, whether or not they exist yet. */
|
|
41
|
+
get cellCount(): number;
|
|
42
|
+
/** Whether a 1-based position falls inside the block. */
|
|
43
|
+
contains(row: number, col: number): boolean;
|
|
44
|
+
/**
|
|
45
|
+
* Every address the block covers, row-major (`B2`, `C2`, `D2`, `B3`, …). Materialises nothing, so
|
|
46
|
+
* this is the cheap way to walk a large block — and, being a generator, it can be abandoned
|
|
47
|
+
* part-way without having built the whole list.
|
|
48
|
+
*/
|
|
49
|
+
addresses(): IterableIterator<string>;
|
|
50
|
+
/**
|
|
51
|
+
* The block's **materialised** cells, row-major. Sparse: a position nothing has ever written to is
|
|
52
|
+
* simply absent, which is what distinguishes "never written" from a cell holding `null`. Reading
|
|
53
|
+
* this creates nothing — mirroring {@link Column.cells}.
|
|
54
|
+
*/
|
|
55
|
+
get cells(): readonly Cell[];
|
|
56
|
+
/**
|
|
57
|
+
* The block's style, facet by facet — the counterpart of {@link Cell.style} over a rectangle, with
|
|
58
|
+
* the same semantics in both directions.
|
|
59
|
+
*
|
|
60
|
+
* **Reading** reports a facet only when *every* position in the block carries a structurally
|
|
61
|
+
* identical one, and `undefined` when they differ or any position is still empty. A block styled
|
|
62
|
+
* through this handle therefore reads back what was written; a block whose cells disagree says so
|
|
63
|
+
* rather than picking a corner's answer and passing it off as the whole.
|
|
64
|
+
*
|
|
65
|
+
* **Writing** lays each facet the payload names onto every cell, leaving facets it omits untouched
|
|
66
|
+
* — exactly what `cell.style = {...}` does, so this composes with prior styling instead of clearing
|
|
67
|
+
* it. Use {@link clearStyle} first for a wholesale replace.
|
|
68
|
+
*
|
|
69
|
+
* Writing **materialises** every position in the block, because a styled-but-valueless cell is the
|
|
70
|
+
* only way an empty cell renders with a fill: skipping the holes would leave gaps in a header band.
|
|
71
|
+
* The cost is bounded by construction — a range is always a bounded rectangle, and whole-axis
|
|
72
|
+
* styling belongs to {@link Worksheet.getColumn}/{@link Worksheet.getRow} instead. {@link cellCount}
|
|
73
|
+
* is the exact number of cells a write will create.
|
|
74
|
+
*/
|
|
75
|
+
get style(): CellStyle;
|
|
76
|
+
set style(style: Readonly<CellStyle>);
|
|
77
|
+
/**
|
|
78
|
+
* Strip every style facet from every cell in the block, leaving values untouched. Assigning
|
|
79
|
+
* {@link style} composes, so this is how a wholesale replace is said: `clearStyle()` then assign.
|
|
80
|
+
* Materialises nothing — a cell that does not exist carries no style to clear.
|
|
81
|
+
*/
|
|
82
|
+
clearStyle(): void;
|
|
83
|
+
/** Fill applied to every cell in the block; `undefined` when they do not all agree. */
|
|
84
|
+
get fill(): Fill | undefined;
|
|
85
|
+
set fill(fill: Fill | undefined);
|
|
86
|
+
/** Number format applied to every cell in the block; `undefined` when they do not all agree. */
|
|
87
|
+
get numFmt(): string | undefined;
|
|
88
|
+
set numFmt(numFmt: string | undefined);
|
|
89
|
+
/** Font applied to every cell in the block; `undefined` when they do not all agree. */
|
|
90
|
+
get font(): Font | undefined;
|
|
91
|
+
set font(font: Font | undefined);
|
|
92
|
+
/** Border applied to every cell in the block; `undefined` when they do not all agree. */
|
|
93
|
+
get border(): Border | undefined;
|
|
94
|
+
set border(border: Border | undefined);
|
|
95
|
+
/** Alignment applied to every cell in the block; `undefined` when they do not all agree. */
|
|
96
|
+
get alignment(): Alignment | undefined;
|
|
97
|
+
set alignment(alignment: Alignment | undefined);
|
|
98
|
+
/** Protection flags applied to every cell in the block; `undefined` when they do not all agree. */
|
|
99
|
+
get protection(): Protection | undefined;
|
|
100
|
+
set protection(protection: Protection | undefined);
|
|
101
|
+
}
|
|
102
|
+
/**
|
|
103
|
+
* Resolve an A1 range reference against a worksheet into a {@link Range}.
|
|
104
|
+
*
|
|
105
|
+
* A reference carrying a sheet prefix is accepted only when it names *this* sheet (case-insensitively,
|
|
106
|
+
* as sheet lookup is everywhere else): a range handed out by `sheetA` cannot be a block of `sheetB`,
|
|
107
|
+
* and quietly ignoring the prefix would let a pasted `Sheet2!B2:D5` style the wrong sheet.
|
|
108
|
+
*
|
|
109
|
+
* @throws {SyntaxError} if the reference is unparseable, names another sheet, or leaves an axis
|
|
110
|
+
* unbounded (`A:A`, `1:1`).
|
|
111
|
+
*/
|
|
112
|
+
export declare function rangeFrom(sheet: Worksheet, reference: string): Range;
|
|
@@ -0,0 +1,283 @@
|
|
|
1
|
+
// A handle on a rectangular block of cells, reached by an A1 range reference or by its corners.
|
|
2
|
+
//
|
|
3
|
+
// The third axis handle, after `Row` and `Column`, and the same contract as both: a live view over
|
|
4
|
+
// the worksheet's stores, bounds fixed at construction, nothing materialised on read. See
|
|
5
|
+
// `core/row.ts` for why these are handles rather than records.
|
|
6
|
+
//
|
|
7
|
+
// **The rectangle is bounded, always.** `A:A` and `1:1` are legal range references and are rejected
|
|
8
|
+
// here, because a whole-axis "range" is not this shape of thing: styling one means stamping a
|
|
9
|
+
// million cells, whereas OOXML expresses it natively as a single `<col style>` or `<row s>` — which
|
|
10
|
+
// is what `getColumn`/`getRow` already write, in constant space. Refusing them is what lets a range
|
|
11
|
+
// materialise its cells eagerly without a cost cliff hiding behind an innocuous-looking call.
|
|
12
|
+
import { decodeRange, encodeAddress, MAX_COLUMN, MAX_ROW } from "./address.js";
|
|
13
|
+
import { applyCellStyle } from "./cell.js";
|
|
14
|
+
import { CELL_STYLE_FACETS, } from "./style.js";
|
|
15
|
+
/**
|
|
16
|
+
* A rectangular block of a worksheet's cells: `sheet.getRange('B2:D5')`.
|
|
17
|
+
*
|
|
18
|
+
* Cheap and stateless — constructing one creates no cells and does not extend the used range.
|
|
19
|
+
* {@link addresses} walks the block without materialising anything; {@link cells} reports only what
|
|
20
|
+
* already exists.
|
|
21
|
+
*
|
|
22
|
+
* Bounds are **inclusive first/last**, never start-and-count. That is the convention every
|
|
23
|
+
* range-shaped accessor in this library follows, so the three axes cannot disagree about what a
|
|
24
|
+
* pair of numbers means.
|
|
25
|
+
*/
|
|
26
|
+
export class Range {
|
|
27
|
+
#sheet;
|
|
28
|
+
/** 1-based row of the block's top edge. Fixed for this handle's lifetime. */
|
|
29
|
+
top;
|
|
30
|
+
/** 1-based column of the block's left edge. Fixed for this handle's lifetime. */
|
|
31
|
+
left;
|
|
32
|
+
/** 1-based row of the block's bottom edge, inclusive. */
|
|
33
|
+
bottom;
|
|
34
|
+
/** 1-based column of the block's right edge, inclusive. */
|
|
35
|
+
right;
|
|
36
|
+
/**
|
|
37
|
+
* Build a handle from inclusive corners, in any order — `(5, 4, 2, 2)` and `(2, 2, 5, 4)` name the
|
|
38
|
+
* same block, exactly as `D5:B2` and `B2:D5` do. Prefer {@link Worksheet.getRange}.
|
|
39
|
+
*
|
|
40
|
+
* @throws {RangeError} if a corner is not a positive integer or falls outside the sheet's bounds.
|
|
41
|
+
*/
|
|
42
|
+
constructor(sheet, top, left, bottom, right) {
|
|
43
|
+
checkBound('row', top, MAX_ROW);
|
|
44
|
+
checkBound('row', bottom, MAX_ROW);
|
|
45
|
+
checkBound('column', left, MAX_COLUMN);
|
|
46
|
+
checkBound('column', right, MAX_COLUMN);
|
|
47
|
+
this.#sheet = sheet;
|
|
48
|
+
this.top = Math.min(top, bottom);
|
|
49
|
+
this.bottom = Math.max(top, bottom);
|
|
50
|
+
this.left = Math.min(left, right);
|
|
51
|
+
this.right = Math.max(left, right);
|
|
52
|
+
}
|
|
53
|
+
/** The worksheet this block belongs to. */
|
|
54
|
+
get sheet() {
|
|
55
|
+
return this.#sheet;
|
|
56
|
+
}
|
|
57
|
+
/** Canonical `tl:br` A1 form — `"B2:D5"`. A one-cell block still reads as `"B2:B2"`. */
|
|
58
|
+
get address() {
|
|
59
|
+
return `${encodeAddress(this.left, this.top)}:${encodeAddress(this.right, this.bottom)}`;
|
|
60
|
+
}
|
|
61
|
+
/** Rows spanned, inclusive of both edges. */
|
|
62
|
+
get rowCount() {
|
|
63
|
+
return this.bottom - this.top + 1;
|
|
64
|
+
}
|
|
65
|
+
/** Columns spanned, inclusive of both edges. */
|
|
66
|
+
get columnCount() {
|
|
67
|
+
return this.right - this.left + 1;
|
|
68
|
+
}
|
|
69
|
+
/** Cells the block covers — `rowCount * columnCount`, whether or not they exist yet. */
|
|
70
|
+
get cellCount() {
|
|
71
|
+
return this.rowCount * this.columnCount;
|
|
72
|
+
}
|
|
73
|
+
/** Whether a 1-based position falls inside the block. */
|
|
74
|
+
contains(row, col) {
|
|
75
|
+
return row >= this.top && row <= this.bottom && col >= this.left && col <= this.right;
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Every address the block covers, row-major (`B2`, `C2`, `D2`, `B3`, …). Materialises nothing, so
|
|
79
|
+
* this is the cheap way to walk a large block — and, being a generator, it can be abandoned
|
|
80
|
+
* part-way without having built the whole list.
|
|
81
|
+
*/
|
|
82
|
+
*addresses() {
|
|
83
|
+
for (let row = this.top; row <= this.bottom; row++) {
|
|
84
|
+
for (let col = this.left; col <= this.right; col++) {
|
|
85
|
+
yield encodeAddress(col, row);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* The block's **materialised** cells, row-major. Sparse: a position nothing has ever written to is
|
|
91
|
+
* simply absent, which is what distinguishes "never written" from a cell holding `null`. Reading
|
|
92
|
+
* this creates nothing — mirroring {@link Column.cells}.
|
|
93
|
+
*/
|
|
94
|
+
get cells() {
|
|
95
|
+
const cells = [];
|
|
96
|
+
for (let row = this.top; row <= this.bottom; row++) {
|
|
97
|
+
for (let col = this.left; col <= this.right; col++) {
|
|
98
|
+
if (this.#sheet.hasCell(row, col))
|
|
99
|
+
cells.push(this.#sheet.getCell(encodeAddress(col, row)));
|
|
100
|
+
}
|
|
101
|
+
}
|
|
102
|
+
return cells;
|
|
103
|
+
}
|
|
104
|
+
/**
|
|
105
|
+
* The block's style, facet by facet — the counterpart of {@link Cell.style} over a rectangle, with
|
|
106
|
+
* the same semantics in both directions.
|
|
107
|
+
*
|
|
108
|
+
* **Reading** reports a facet only when *every* position in the block carries a structurally
|
|
109
|
+
* identical one, and `undefined` when they differ or any position is still empty. A block styled
|
|
110
|
+
* through this handle therefore reads back what was written; a block whose cells disagree says so
|
|
111
|
+
* rather than picking a corner's answer and passing it off as the whole.
|
|
112
|
+
*
|
|
113
|
+
* **Writing** lays each facet the payload names onto every cell, leaving facets it omits untouched
|
|
114
|
+
* — exactly what `cell.style = {...}` does, so this composes with prior styling instead of clearing
|
|
115
|
+
* it. Use {@link clearStyle} first for a wholesale replace.
|
|
116
|
+
*
|
|
117
|
+
* Writing **materialises** every position in the block, because a styled-but-valueless cell is the
|
|
118
|
+
* only way an empty cell renders with a fill: skipping the holes would leave gaps in a header band.
|
|
119
|
+
* The cost is bounded by construction — a range is always a bounded rectangle, and whole-axis
|
|
120
|
+
* styling belongs to {@link Worksheet.getColumn}/{@link Worksheet.getRow} instead. {@link cellCount}
|
|
121
|
+
* is the exact number of cells a write will create.
|
|
122
|
+
*/
|
|
123
|
+
get style() {
|
|
124
|
+
const style = {};
|
|
125
|
+
for (const facet of CELL_STYLE_FACETS)
|
|
126
|
+
this.#collectShared(style, facet);
|
|
127
|
+
return style;
|
|
128
|
+
}
|
|
129
|
+
set style(style) {
|
|
130
|
+
for (const cell of this.#materialise())
|
|
131
|
+
applyCellStyle(cell, style);
|
|
132
|
+
}
|
|
133
|
+
/**
|
|
134
|
+
* Strip every style facet from every cell in the block, leaving values untouched. Assigning
|
|
135
|
+
* {@link style} composes, so this is how a wholesale replace is said: `clearStyle()` then assign.
|
|
136
|
+
* Materialises nothing — a cell that does not exist carries no style to clear.
|
|
137
|
+
*/
|
|
138
|
+
clearStyle() {
|
|
139
|
+
for (const cell of this.cells) {
|
|
140
|
+
for (const facet of CELL_STYLE_FACETS)
|
|
141
|
+
setFacet(cell, facet, undefined);
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
/** Fill applied to every cell in the block; `undefined` when they do not all agree. */
|
|
145
|
+
get fill() {
|
|
146
|
+
return this.#sharedFacet('fill');
|
|
147
|
+
}
|
|
148
|
+
set fill(fill) {
|
|
149
|
+
this.#writeFacet('fill', fill);
|
|
150
|
+
}
|
|
151
|
+
/** Number format applied to every cell in the block; `undefined` when they do not all agree. */
|
|
152
|
+
get numFmt() {
|
|
153
|
+
return this.#sharedFacet('numFmt');
|
|
154
|
+
}
|
|
155
|
+
set numFmt(numFmt) {
|
|
156
|
+
this.#writeFacet('numFmt', numFmt);
|
|
157
|
+
}
|
|
158
|
+
/** Font applied to every cell in the block; `undefined` when they do not all agree. */
|
|
159
|
+
get font() {
|
|
160
|
+
return this.#sharedFacet('font');
|
|
161
|
+
}
|
|
162
|
+
set font(font) {
|
|
163
|
+
this.#writeFacet('font', font);
|
|
164
|
+
}
|
|
165
|
+
/** Border applied to every cell in the block; `undefined` when they do not all agree. */
|
|
166
|
+
get border() {
|
|
167
|
+
return this.#sharedFacet('border');
|
|
168
|
+
}
|
|
169
|
+
set border(border) {
|
|
170
|
+
this.#writeFacet('border', border);
|
|
171
|
+
}
|
|
172
|
+
/** Alignment applied to every cell in the block; `undefined` when they do not all agree. */
|
|
173
|
+
get alignment() {
|
|
174
|
+
return this.#sharedFacet('alignment');
|
|
175
|
+
}
|
|
176
|
+
set alignment(alignment) {
|
|
177
|
+
this.#writeFacet('alignment', alignment);
|
|
178
|
+
}
|
|
179
|
+
/** Protection flags applied to every cell in the block; `undefined` when they do not all agree. */
|
|
180
|
+
get protection() {
|
|
181
|
+
return this.#sharedFacet('protection');
|
|
182
|
+
}
|
|
183
|
+
set protection(protection) {
|
|
184
|
+
this.#writeFacet('protection', protection);
|
|
185
|
+
}
|
|
186
|
+
// Every cell in the block, created where it does not exist yet. An address covered by a merge
|
|
187
|
+
// resolves to that region's master, so a block overlapping a merge restyles the master rather than
|
|
188
|
+
// stranding a style on a covered cell the serializer would then have to drop.
|
|
189
|
+
#materialise() {
|
|
190
|
+
return [...this.addresses()].map((address) => this.#sheet.getCell(address));
|
|
191
|
+
}
|
|
192
|
+
// Assigning a facet replaces that facet on every cell — the block-wide reading of `cell.fill = x`.
|
|
193
|
+
// Clearing one (`undefined`) touches only the cells that exist: there is nothing to clear on a hole,
|
|
194
|
+
// and materialising the block to write nothing onto it would be pure cost.
|
|
195
|
+
#writeFacet(facet, value) {
|
|
196
|
+
const cells = value === undefined ? this.cells : this.#materialise();
|
|
197
|
+
for (const cell of cells)
|
|
198
|
+
setFacet(cell, facet, value);
|
|
199
|
+
}
|
|
200
|
+
// One facet of the block-wide style onto the record being assembled. Narrowed to a single key for
|
|
201
|
+
// the same reason {@link setFacet} is — see there.
|
|
202
|
+
#collectShared(target, facet) {
|
|
203
|
+
const shared = this.#sharedFacet(facet);
|
|
204
|
+
if (shared !== undefined)
|
|
205
|
+
target[facet] = shared;
|
|
206
|
+
}
|
|
207
|
+
// A facet's value when every position in the block carries a structurally identical one, else
|
|
208
|
+
// undefined. A hole counts as "no facet", so a partly-styled block is reported as disagreeing —
|
|
209
|
+
// which it does, since the empty positions render unstyled.
|
|
210
|
+
#sharedFacet(facet) {
|
|
211
|
+
let first;
|
|
212
|
+
let firstKey;
|
|
213
|
+
let seen = 0;
|
|
214
|
+
for (let row = this.top; row <= this.bottom; row++) {
|
|
215
|
+
for (let col = this.left; col <= this.right; col++) {
|
|
216
|
+
const value = this.#sheet.hasCell(row, col)
|
|
217
|
+
? this.#sheet.getCell(encodeAddress(col, row))[facet]
|
|
218
|
+
: undefined;
|
|
219
|
+
const key = facetKey(value);
|
|
220
|
+
if (seen === 0) {
|
|
221
|
+
first = value;
|
|
222
|
+
firstKey = key;
|
|
223
|
+
}
|
|
224
|
+
else if (key !== firstKey) {
|
|
225
|
+
return undefined;
|
|
226
|
+
}
|
|
227
|
+
seen++;
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return first;
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
// Write one facet through a `Cell`'s own setter. Narrowed to a single key — rather than assigning
|
|
234
|
+
// `cell[facet]` with `facet` still a union — for the same reason `style.ts`'s `copyFacet` is: the
|
|
235
|
+
// compiler cannot correlate a union key with its value type across two object types, and a cast here
|
|
236
|
+
// would be the one place a facet could be written to the wrong slot without anything noticing.
|
|
237
|
+
function setFacet(cell, facet, value) {
|
|
238
|
+
const target = cell;
|
|
239
|
+
target[facet] = value;
|
|
240
|
+
}
|
|
241
|
+
// A canonical string for a facet value, so two structurally identical records compare equal whatever
|
|
242
|
+
// order their keys were written in. Sound here and nowhere near a general deep-equal: every facet is
|
|
243
|
+
// a plain data record of strings, numbers, booleans and nested records — no functions, no cycles, no
|
|
244
|
+
// class instances — which is exactly the shape JSON round-trips faithfully.
|
|
245
|
+
function facetKey(value) {
|
|
246
|
+
if (value === undefined)
|
|
247
|
+
return '';
|
|
248
|
+
return JSON.stringify(value, (_key, inner) => inner !== null && typeof inner === 'object' && !Array.isArray(inner)
|
|
249
|
+
? Object.fromEntries(Object.entries(inner).sort(([a], [b]) => (a < b ? -1 : 1)))
|
|
250
|
+
: inner);
|
|
251
|
+
}
|
|
252
|
+
function checkBound(axis, value, max) {
|
|
253
|
+
if (!Number.isInteger(value) || value < 1) {
|
|
254
|
+
throw new RangeError(`${axis} ${value} is out of bounds — ${axis}s start at 1`);
|
|
255
|
+
}
|
|
256
|
+
if (value > max) {
|
|
257
|
+
throw new RangeError(`${axis} ${value} is out of bounds — the sheet ends at ${max}`);
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
/**
|
|
261
|
+
* Resolve an A1 range reference against a worksheet into a {@link Range}.
|
|
262
|
+
*
|
|
263
|
+
* A reference carrying a sheet prefix is accepted only when it names *this* sheet (case-insensitively,
|
|
264
|
+
* as sheet lookup is everywhere else): a range handed out by `sheetA` cannot be a block of `sheetB`,
|
|
265
|
+
* and quietly ignoring the prefix would let a pasted `Sheet2!B2:D5` style the wrong sheet.
|
|
266
|
+
*
|
|
267
|
+
* @throws {SyntaxError} if the reference is unparseable, names another sheet, or leaves an axis
|
|
268
|
+
* unbounded (`A:A`, `1:1`).
|
|
269
|
+
*/
|
|
270
|
+
export function rangeFrom(sheet, reference) {
|
|
271
|
+
const { top, left, bottom, right, sheetName, dimensions } = decodeRange(reference);
|
|
272
|
+
if (sheetName !== undefined && sheetName.toLowerCase() !== sheet.name.toLowerCase()) {
|
|
273
|
+
throw new SyntaxError(`"${reference}" names worksheet "${sheetName}", not "${sheet.name}" — a range belongs to the sheet it came from`);
|
|
274
|
+
}
|
|
275
|
+
// An unbounded *row* axis is what `A:A` has: it names whole columns, every row of them.
|
|
276
|
+
if (top === undefined || bottom === undefined) {
|
|
277
|
+
throw new SyntaxError(`"${reference}" spans whole columns (${dimensions}) — style them through getColumn(n), which says the same thing in one attribute instead of ${MAX_ROW} cells`);
|
|
278
|
+
}
|
|
279
|
+
if (left === undefined || right === undefined) {
|
|
280
|
+
throw new SyntaxError(`"${reference}" spans whole rows (${dimensions}) — style them through getRow(n), which says the same thing in one attribute instead of ${MAX_COLUMN} cells`);
|
|
281
|
+
}
|
|
282
|
+
return new Range(sheet, top, left, bottom, right);
|
|
283
|
+
}
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
import type { Cell } from './cell.ts';
|
|
2
|
+
import type { Fill } from './style.ts';
|
|
3
|
+
import type { CellValue } from './value.ts';
|
|
4
|
+
import type { RowProperties, Worksheet } from './worksheet.ts';
|
|
5
|
+
export declare class Row {
|
|
6
|
+
#private;
|
|
7
|
+
/** 1-based row number. Fixed for this handle's lifetime. */
|
|
8
|
+
readonly number: number;
|
|
9
|
+
/** @throws {RangeError} if the number is not a positive integer. */
|
|
10
|
+
constructor(sheet: Worksheet, number: number);
|
|
11
|
+
/**
|
|
12
|
+
* The row's format record if it has one, else `undefined` — a read that never fabricates, so a
|
|
13
|
+
* serializer can ask every row it visits whether there are attributes to emit without giving each
|
|
14
|
+
* one an empty record. Read-only on purpose: {@link height} and its siblings are how a row is
|
|
15
|
+
* formatted, and they create the record on first write.
|
|
16
|
+
*/
|
|
17
|
+
get properties(): Readonly<RowProperties> | undefined;
|
|
18
|
+
/** Row height in points; `undefined` leaves the sheet default in force. */
|
|
19
|
+
get height(): number | undefined;
|
|
20
|
+
set height(height: number | undefined);
|
|
21
|
+
/** Whether the row is hidden. */
|
|
22
|
+
get hidden(): boolean | undefined;
|
|
23
|
+
set hidden(hidden: boolean | undefined);
|
|
24
|
+
/** Outline (grouping) depth; 0 or `undefined` means ungrouped. */
|
|
25
|
+
get outlineLevel(): number | undefined;
|
|
26
|
+
set outlineLevel(outlineLevel: number | undefined);
|
|
27
|
+
/** Whether this row is the collapsed summary of an outline group. */
|
|
28
|
+
get collapsed(): boolean | undefined;
|
|
29
|
+
set collapsed(collapsed: boolean | undefined);
|
|
30
|
+
/** Background fill for the row's cells that carry no fill of their own. */
|
|
31
|
+
get fill(): Fill | undefined;
|
|
32
|
+
set fill(fill: Fill | undefined);
|
|
33
|
+
/**
|
|
34
|
+
* The cell at a column in this row, creating it on first access. The column is a 1-based index
|
|
35
|
+
* (`row.getCell(2)`) or its letters (`row.getCell('B')`).
|
|
36
|
+
*
|
|
37
|
+
* Resolves through merges exactly as {@link Worksheet.getCell} does: addressing a cell covered by
|
|
38
|
+
* a merged region yields that region's master.
|
|
39
|
+
*
|
|
40
|
+
* @throws {RangeError} if the column is not a positive integer.
|
|
41
|
+
* @throws {SyntaxError} if the column letters do not name a column.
|
|
42
|
+
*/
|
|
43
|
+
getCell(column: number | string): Cell;
|
|
44
|
+
/**
|
|
45
|
+
* The row's materialised cells in ascending column order. Sparse: a column never written to has
|
|
46
|
+
* no cell here, and the array is a fresh snapshot of *which* cells exist — the cells themselves
|
|
47
|
+
* are the live ones.
|
|
48
|
+
*/
|
|
49
|
+
get cells(): readonly Cell[];
|
|
50
|
+
/**
|
|
51
|
+
* The row's values by position, index 0 being column A. Sparse in the same way {@link cells} is:
|
|
52
|
+
* a column with no cell is a hole, which is what distinguishes "never written" from a cell
|
|
53
|
+
* holding `null`.
|
|
54
|
+
*
|
|
55
|
+
* Assigning places each value it names and leaves every other column untouched — a hole or an
|
|
56
|
+
* explicit `undefined` skips that column, and a shorter array does not clear the tail. These are
|
|
57
|
+
* {@link Worksheet.addRow}'s rules, deliberately: `values` is that same row shape addressed by
|
|
58
|
+
* number rather than appended. To *replace* a row, including clearing what it held, splice it —
|
|
59
|
+
* `sheet.spliceRows(n, 1, values)`.
|
|
60
|
+
*/
|
|
61
|
+
get values(): (CellValue | undefined)[];
|
|
62
|
+
set values(values: (CellValue | undefined)[]);
|
|
63
|
+
}
|
|
64
|
+
type AssertNever<T extends never> = T;
|
|
65
|
+
/**
|
|
66
|
+
* Compile-time proof that {@link Row} mirrors every {@link RowProperties} field. A field added to
|
|
67
|
+
* the record without an accessor here resolves this to that field's name, which does not satisfy
|
|
68
|
+
* `never`, so the build fails naming what is unreachable. Without it the mirror would rot silently:
|
|
69
|
+
* the record would carry the new field, the codecs would read and write it, and the public handle
|
|
70
|
+
* would simply never mention it.
|
|
71
|
+
*/
|
|
72
|
+
export type EveryRowPropertyIsMirrored = AssertNever<Exclude<keyof RowProperties, keyof Row & keyof RowProperties>>;
|
|
73
|
+
export {};
|
package/dist/core/row.js
ADDED
|
@@ -0,0 +1,135 @@
|
|
|
1
|
+
// A handle on one row of a worksheet: its formatting and its cells, reached by row number.
|
|
2
|
+
//
|
|
3
|
+
// A handle, not a record. `Worksheet` keeps the authoritative stores — the cell grid and the sparse
|
|
4
|
+
// map of per-row formatting — and a `Row` reads and writes straight through to them, so two handles
|
|
5
|
+
// on the same number always agree and neither can hold a stale copy. That is the whole reason this
|
|
6
|
+
// is not a snapshot: a row object that copied its cells out would be the shape of the merge-loss
|
|
7
|
+
// class of bug the model contract exists to prevent.
|
|
8
|
+
//
|
|
9
|
+
// Position is fixed at construction, exactly as `Cell` fixes `(row, col)`. `sheet.getRow(3)` means
|
|
10
|
+
// "row 3" and keeps meaning row 3 — a splice that moves content past it does not carry the handle
|
|
11
|
+
// along, any more than it re-points a `Cell`.
|
|
12
|
+
//
|
|
13
|
+
// Formatting is created on write, never on read. Asking for `sheet.getRow(500)` costs nothing and
|
|
14
|
+
// does not extend the used range; assigning `height` is what materialises the record.
|
|
15
|
+
import { columnToNumber, encodeAddress } from "./address.js";
|
|
16
|
+
import { INTERNAL } from "./internal.js";
|
|
17
|
+
export class Row {
|
|
18
|
+
#sheet;
|
|
19
|
+
/** 1-based row number. Fixed for this handle's lifetime. */
|
|
20
|
+
number;
|
|
21
|
+
/** @throws {RangeError} if the number is not a positive integer. */
|
|
22
|
+
constructor(sheet, number) {
|
|
23
|
+
if (!Number.isInteger(number) || number < 1) {
|
|
24
|
+
throw new RangeError(`row ${number} is out of bounds — rows start at 1`);
|
|
25
|
+
}
|
|
26
|
+
this.#sheet = sheet;
|
|
27
|
+
this.number = number;
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* The row's format record if it has one, else `undefined` — a read that never fabricates, so a
|
|
31
|
+
* serializer can ask every row it visits whether there are attributes to emit without giving each
|
|
32
|
+
* one an empty record. Read-only on purpose: {@link height} and its siblings are how a row is
|
|
33
|
+
* formatted, and they create the record on first write.
|
|
34
|
+
*/
|
|
35
|
+
get properties() {
|
|
36
|
+
return this.#sheet[INTERNAL].rowPropertiesOf(this.number);
|
|
37
|
+
}
|
|
38
|
+
/** Row height in points; `undefined` leaves the sheet default in force. */
|
|
39
|
+
get height() {
|
|
40
|
+
return this.#read('height');
|
|
41
|
+
}
|
|
42
|
+
set height(height) {
|
|
43
|
+
this.#write('height', height);
|
|
44
|
+
}
|
|
45
|
+
/** Whether the row is hidden. */
|
|
46
|
+
get hidden() {
|
|
47
|
+
return this.#read('hidden');
|
|
48
|
+
}
|
|
49
|
+
set hidden(hidden) {
|
|
50
|
+
this.#write('hidden', hidden);
|
|
51
|
+
}
|
|
52
|
+
/** Outline (grouping) depth; 0 or `undefined` means ungrouped. */
|
|
53
|
+
get outlineLevel() {
|
|
54
|
+
return this.#read('outlineLevel');
|
|
55
|
+
}
|
|
56
|
+
set outlineLevel(outlineLevel) {
|
|
57
|
+
this.#write('outlineLevel', outlineLevel);
|
|
58
|
+
}
|
|
59
|
+
/** Whether this row is the collapsed summary of an outline group. */
|
|
60
|
+
get collapsed() {
|
|
61
|
+
return this.#read('collapsed');
|
|
62
|
+
}
|
|
63
|
+
set collapsed(collapsed) {
|
|
64
|
+
this.#write('collapsed', collapsed);
|
|
65
|
+
}
|
|
66
|
+
/** Background fill for the row's cells that carry no fill of their own. */
|
|
67
|
+
get fill() {
|
|
68
|
+
return this.#read('fill');
|
|
69
|
+
}
|
|
70
|
+
set fill(fill) {
|
|
71
|
+
this.#write('fill', fill);
|
|
72
|
+
}
|
|
73
|
+
/**
|
|
74
|
+
* The cell at a column in this row, creating it on first access. The column is a 1-based index
|
|
75
|
+
* (`row.getCell(2)`) or its letters (`row.getCell('B')`).
|
|
76
|
+
*
|
|
77
|
+
* Resolves through merges exactly as {@link Worksheet.getCell} does: addressing a cell covered by
|
|
78
|
+
* a merged region yields that region's master.
|
|
79
|
+
*
|
|
80
|
+
* @throws {RangeError} if the column is not a positive integer.
|
|
81
|
+
* @throws {SyntaxError} if the column letters do not name a column.
|
|
82
|
+
*/
|
|
83
|
+
getCell(column) {
|
|
84
|
+
const index = typeof column === 'number' ? column : columnToNumber(column);
|
|
85
|
+
return this.#sheet.getCell(encodeAddress(index, this.number));
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* The row's materialised cells in ascending column order. Sparse: a column never written to has
|
|
89
|
+
* no cell here, and the array is a fresh snapshot of *which* cells exist — the cells themselves
|
|
90
|
+
* are the live ones.
|
|
91
|
+
*/
|
|
92
|
+
get cells() {
|
|
93
|
+
return this.#sheet[INTERNAL].rowCells(this.number);
|
|
94
|
+
}
|
|
95
|
+
/**
|
|
96
|
+
* The row's values by position, index 0 being column A. Sparse in the same way {@link cells} is:
|
|
97
|
+
* a column with no cell is a hole, which is what distinguishes "never written" from a cell
|
|
98
|
+
* holding `null`.
|
|
99
|
+
*
|
|
100
|
+
* Assigning places each value it names and leaves every other column untouched — a hole or an
|
|
101
|
+
* explicit `undefined` skips that column, and a shorter array does not clear the tail. These are
|
|
102
|
+
* {@link Worksheet.addRow}'s rules, deliberately: `values` is that same row shape addressed by
|
|
103
|
+
* number rather than appended. To *replace* a row, including clearing what it held, splice it —
|
|
104
|
+
* `sheet.spliceRows(n, 1, values)`.
|
|
105
|
+
*/
|
|
106
|
+
get values() {
|
|
107
|
+
const values = [];
|
|
108
|
+
for (const cell of this.cells)
|
|
109
|
+
values[cell.col - 1] = cell.value;
|
|
110
|
+
return values;
|
|
111
|
+
}
|
|
112
|
+
set values(values) {
|
|
113
|
+
values.forEach((value, index) => {
|
|
114
|
+
if (value !== undefined)
|
|
115
|
+
this.getCell(index + 1).value = value;
|
|
116
|
+
});
|
|
117
|
+
}
|
|
118
|
+
#read(key) {
|
|
119
|
+
return this.#sheet[INTERNAL].rowPropertiesOf(this.number)?.[key];
|
|
120
|
+
}
|
|
121
|
+
// `undefined` clears rather than stores: `RowProperties` is declared with optional fields under
|
|
122
|
+
// `exactOptionalPropertyTypes`, so a present-but-undefined key is not the same shape as an absent
|
|
123
|
+
// one — and it would make a formatting-free row look formatted to anything reading `properties`.
|
|
124
|
+
// Clearing a row that has no record at all is a no-op, so a write of `undefined` never
|
|
125
|
+
// materialises one.
|
|
126
|
+
#write(key, value) {
|
|
127
|
+
if (value === undefined) {
|
|
128
|
+
const properties = this.#sheet[INTERNAL].rowPropertiesOf(this.number);
|
|
129
|
+
if (properties !== undefined)
|
|
130
|
+
delete properties[key];
|
|
131
|
+
return;
|
|
132
|
+
}
|
|
133
|
+
this.#sheet[INTERNAL].ensureRowProperties(this.number)[key] = value;
|
|
134
|
+
}
|
|
135
|
+
}
|