@shbernal/ts-xlsx 1.3.1 → 2.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 +20 -10
- package/dist/bytes.d.ts +24 -0
- package/dist/bytes.js +34 -0
- package/dist/core/address.d.ts +72 -3
- package/dist/core/address.js +50 -10
- package/dist/core/autofilter.d.ts +13 -2
- package/dist/core/autofilter.js +35 -10
- package/dist/core/cell.d.ts +17 -16
- package/dist/core/cell.js +3 -7
- package/dist/core/color-resolution.d.ts +8 -8
- package/dist/core/color-resolution.js +7 -8
- package/dist/core/column.d.ts +8 -9
- package/dist/core/column.js +2 -4
- package/dist/core/comment-thread.d.ts +32 -23
- package/dist/core/comment-thread.js +4 -4
- package/dist/core/conditional-formatting-overlay.d.ts +6 -1
- package/dist/core/conditional-formatting-overlay.js +11 -0
- package/dist/core/conditional-formatting.d.ts +48 -8
- package/dist/core/conditional-formatting.js +88 -0
- package/dist/core/data-validation-overlay.d.ts +12 -1
- package/dist/core/data-validation-overlay.js +21 -2
- package/dist/core/data-validation.d.ts +12 -5
- package/dist/core/data-validation.js +34 -0
- package/dist/core/date.d.ts +3 -3
- package/dist/core/formula.d.ts +6 -6
- package/dist/core/grid-edits.d.ts +16 -0
- package/dist/core/grid-edits.js +29 -11
- package/dist/core/grid-shift.d.ts +19 -0
- package/dist/core/grid-shift.js +8 -0
- package/dist/core/image.d.ts +39 -2
- package/dist/core/image.js +10 -0
- package/dist/core/internal.d.ts +9 -1
- package/dist/core/limits.d.ts +4 -4
- package/dist/core/merge.d.ts +17 -12
- package/dist/core/merge.js +36 -5
- package/dist/core/page-setup.d.ts +20 -6
- package/dist/core/page-setup.js +8 -1
- package/dist/core/pivot-table.d.ts +4 -4
- package/dist/core/pivot-table.js +29 -20
- package/dist/core/preserved.d.ts +3 -3
- package/dist/core/protection.d.ts +7 -4
- package/dist/core/protection.js +20 -14
- package/dist/core/range.d.ts +16 -14
- package/dist/core/range.js +5 -5
- package/dist/core/row-input.d.ts +9 -8
- package/dist/core/row-input.js +12 -10
- package/dist/core/row.d.ts +7 -8
- package/dist/core/row.js +2 -4
- package/dist/core/style.d.ts +41 -14
- package/dist/core/style.js +82 -67
- package/dist/core/table-style.d.ts +9 -9
- package/dist/core/table-style.js +1 -1
- package/dist/core/table.d.ts +53 -51
- package/dist/core/table.js +83 -41
- package/dist/core/theme.d.ts +16 -44
- package/dist/core/theme.js +6 -148
- package/dist/core/value.d.ts +7 -7
- package/dist/core/workbook-protection.d.ts +2 -2
- package/dist/core/workbook-styles.d.ts +26 -0
- package/dist/core/workbook-styles.js +48 -0
- package/dist/core/workbook-theme.d.ts +25 -0
- package/dist/core/workbook-theme.js +49 -0
- package/dist/core/workbook-vba.d.ts +17 -0
- package/dist/core/workbook-vba.js +79 -0
- package/dist/core/workbook.d.ts +133 -87
- package/dist/core/workbook.js +74 -133
- package/dist/core/worksheet-comments.d.ts +17 -0
- package/dist/core/worksheet-comments.js +70 -0
- package/dist/core/worksheet-model.d.ts +4 -4
- package/dist/core/worksheet-model.js +1 -0
- package/dist/core/worksheet-pictures.d.ts +29 -0
- package/dist/core/worksheet-pictures.js +42 -0
- package/dist/core/worksheet.d.ts +100 -79
- package/dist/core/worksheet.js +63 -142
- package/dist/customui/errors.d.ts +1 -1
- package/dist/customui/ribbon.d.ts +3 -3
- package/dist/customui/ribbon.js +24 -23
- package/dist/entries/core.d.ts +7 -7
- package/dist/entries/core.js +1 -1
- package/dist/entries/csv.d.ts +1 -1
- package/dist/entries/node-unavailable.d.ts +15 -0
- package/dist/entries/node-unavailable.js +20 -0
- package/dist/entries/node.d.ts +1 -0
- package/dist/entries/node.js +1 -0
- package/dist/entries/vba.d.ts +1 -1
- package/dist/entries/vba.js +1 -1
- package/dist/entries/xlsx.d.ts +1 -1
- package/dist/entries/xlsx.js +1 -1
- package/dist/errors.d.ts +12 -12
- package/dist/io/csv/read.js +2 -3
- package/dist/io/csv/write.d.ts +19 -4
- package/dist/io/csv/write.js +34 -4
- package/dist/io/opc/errors.d.ts +6 -6
- package/dist/io/opc/inflate.d.ts +1 -1
- package/dist/io/opc/inflate.js +2 -12
- package/dist/io/opc/namespaces.d.ts +1 -1
- package/dist/io/opc/read-opc.d.ts +19 -3
- package/dist/io/opc/read-opc.js +14 -8
- package/dist/io/opc/read-options.d.ts +2 -2
- package/dist/io/opc/rels.d.ts +11 -0
- package/dist/io/opc/rels.js +3 -2
- package/dist/io/opc/sniff-format.d.ts +2 -2
- package/dist/io/style/xf-style.d.ts +44 -7
- package/dist/io/style/xf-style.js +27 -0
- package/dist/io/xlsb/errors.d.ts +1 -1
- package/dist/io/xlsb/formula.d.ts +4 -4
- package/dist/io/xlsb/primitives.d.ts +8 -8
- package/dist/io/xlsb/ptg-functions.d.ts +1 -1
- package/dist/io/xlsb/read-styles.js +2 -20
- package/dist/io/xlsb/read.d.ts +2 -2
- package/dist/io/xlsx/cell-accumulator.d.ts +26 -9
- package/dist/io/xlsx/cell-accumulator.js +89 -46
- package/dist/io/xlsx/cell-value.d.ts +3 -3
- package/dist/io/xlsx/cell-value.js +9 -6
- package/dist/io/xlsx/color-xml.d.ts +1 -1
- package/dist/io/xlsx/color-xml.js +18 -23
- package/dist/io/xlsx/comments.d.ts +5 -5
- package/dist/io/xlsx/comments.js +17 -8
- package/dist/io/xlsx/conditional-formatting.d.ts +4 -4
- package/dist/io/xlsx/conditional-formatting.js +64 -58
- package/dist/io/xlsx/data-validation.d.ts +3 -3
- package/dist/io/xlsx/data-validation.js +35 -33
- package/dist/io/xlsx/errors.d.ts +3 -3
- package/dist/io/xlsx/hyperlinks.d.ts +4 -2
- package/dist/io/xlsx/hyperlinks.js +10 -18
- package/dist/io/xlsx/images.d.ts +1 -1
- package/dist/io/xlsx/images.js +25 -28
- package/dist/io/xlsx/namespaces.d.ts +2 -2
- package/dist/io/xlsx/package-plan.js +5 -2
- package/dist/io/xlsx/read-pivot.js +4 -13
- package/dist/io/xlsx/read-rows.d.ts +13 -13
- package/dist/io/xlsx/read-rows.js +19 -55
- package/dist/io/xlsx/read-shared-strings.js +7 -44
- package/dist/io/xlsx/read-styles.d.ts +3 -3
- package/dist/io/xlsx/read-styles.js +42 -66
- package/dist/io/xlsx/read-worksheet.js +73 -129
- package/dist/io/xlsx/read.d.ts +4 -6
- package/dist/io/xlsx/read.js +104 -142
- package/dist/io/xlsx/rich-runs.d.ts +41 -7
- package/dist/io/xlsx/rich-runs.js +94 -30
- package/dist/io/xlsx/rich-text.d.ts +1 -1
- package/dist/io/xlsx/shared-strings.d.ts +2 -2
- package/dist/io/xlsx/sheet-properties.d.ts +1 -1
- package/dist/io/xlsx/sheet-properties.js +14 -10
- package/dist/io/xlsx/styles.d.ts +9 -22
- package/dist/io/xlsx/styles.js +65 -89
- package/dist/io/xlsx/tables.d.ts +2 -2
- package/dist/io/xlsx/tables.js +20 -25
- package/dist/io/xlsx/theme-xml.d.ts +37 -0
- package/dist/io/xlsx/theme-xml.js +145 -0
- package/dist/io/xlsx/threaded-comments.d.ts +18 -27
- package/dist/io/xlsx/threaded-comments.js +26 -38
- package/dist/io/xlsx/workbook-xml.js +14 -8
- package/dist/io/xlsx/worksheet-xml.d.ts +2 -2
- package/dist/io/xlsx/worksheet-xml.js +40 -48
- package/dist/io/xlsx/write-stream.d.ts +23 -16
- package/dist/io/xlsx/write-stream.js +13 -14
- package/dist/io/xlsx/write.d.ts +8 -7
- package/dist/io/xlsx/write.js +6 -3
- package/dist/io/xlsx/x14-ext.d.ts +1 -1
- package/dist/sha512.d.ts +2 -0
- package/dist/sha512.js +146 -0
- package/dist/vba/bytes.d.ts +11 -0
- package/dist/vba/bytes.js +29 -0
- package/dist/vba/cfb-writer.d.ts +1 -1
- package/dist/vba/cfb.d.ts +1 -1
- package/dist/vba/cfb.js +15 -51
- package/dist/vba/codepage.d.ts +2 -2
- package/dist/vba/dir-records.d.ts +53 -0
- package/dist/vba/dir-records.js +28 -0
- package/dist/vba/errors.d.ts +2 -2
- package/dist/vba/index.d.ts +2 -2
- package/dist/vba/index.js +2 -2
- package/dist/vba/ms-ovba.d.ts +1 -1
- package/dist/vba/ms-ovba.js +1 -3
- package/dist/vba/project-editor.d.ts +9 -9
- package/dist/vba/project-editor.js +6 -44
- package/dist/vba/project.d.ts +5 -5
- package/dist/vba/project.js +3 -28
- package/dist/vba/vba-encoding.d.ts +1 -1
- package/dist/xml/errors.d.ts +1 -1
- package/dist/xml/xml-read.d.ts +98 -9
- package/dist/xml/xml-read.js +70 -0
- package/dist/xml/xml.d.ts +74 -10
- package/dist/xml/xml.js +39 -7
- package/package.json +59 -36
package/dist/core/style.js
CHANGED
|
@@ -1,83 +1,93 @@
|
|
|
1
|
-
const NAMED_UNDERLINE_STYLES =
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
1
|
+
const NAMED_UNDERLINE_STYLES = {
|
|
2
|
+
none: true,
|
|
3
|
+
single: true,
|
|
4
|
+
double: true,
|
|
5
|
+
singleAccounting: true,
|
|
6
|
+
doubleAccounting: true,
|
|
7
|
+
};
|
|
8
8
|
export function isNamedUnderlineStyle(value) {
|
|
9
|
-
return
|
|
9
|
+
return Object.hasOwn(NAMED_UNDERLINE_STYLES, value);
|
|
10
|
+
}
|
|
11
|
+
export function parseArgb(value) {
|
|
12
|
+
const hex = value.startsWith('#') ? value.slice(1) : value;
|
|
13
|
+
const argb = hex.length === 6 ? `FF${hex}` : hex;
|
|
14
|
+
return /^[0-9a-fA-F]{8}$/.test(argb) ? argb : undefined;
|
|
10
15
|
}
|
|
11
|
-
const FILL_PATTERN_TYPES =
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
16
|
+
const FILL_PATTERN_TYPES = {
|
|
17
|
+
none: true,
|
|
18
|
+
solid: true,
|
|
19
|
+
gray125: true,
|
|
20
|
+
darkGray: true,
|
|
21
|
+
mediumGray: true,
|
|
22
|
+
lightGray: true,
|
|
23
|
+
gray0625: true,
|
|
24
|
+
darkHorizontal: true,
|
|
25
|
+
darkVertical: true,
|
|
26
|
+
darkDown: true,
|
|
27
|
+
darkUp: true,
|
|
28
|
+
darkGrid: true,
|
|
29
|
+
darkTrellis: true,
|
|
30
|
+
lightHorizontal: true,
|
|
31
|
+
lightVertical: true,
|
|
32
|
+
lightDown: true,
|
|
33
|
+
lightUp: true,
|
|
34
|
+
lightGrid: true,
|
|
35
|
+
lightTrellis: true,
|
|
36
|
+
};
|
|
32
37
|
export function isFillPatternType(value) {
|
|
33
|
-
return
|
|
38
|
+
return Object.hasOwn(FILL_PATTERN_TYPES, value);
|
|
34
39
|
}
|
|
35
|
-
const BORDER_STYLES =
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
40
|
+
const BORDER_STYLES = {
|
|
41
|
+
thin: true,
|
|
42
|
+
medium: true,
|
|
43
|
+
thick: true,
|
|
44
|
+
dashed: true,
|
|
45
|
+
dotted: true,
|
|
46
|
+
double: true,
|
|
47
|
+
hair: true,
|
|
48
|
+
mediumDashed: true,
|
|
49
|
+
dashDot: true,
|
|
50
|
+
mediumDashDot: true,
|
|
51
|
+
dashDotDot: true,
|
|
52
|
+
mediumDashDotDot: true,
|
|
53
|
+
slantDashDot: true,
|
|
54
|
+
};
|
|
50
55
|
export function isBorderStyle(value) {
|
|
51
|
-
return
|
|
56
|
+
return Object.hasOwn(BORDER_STYLES, value);
|
|
52
57
|
}
|
|
58
|
+
const FONT_VERTICAL_ALIGNMENTS = {
|
|
59
|
+
superscript: true,
|
|
60
|
+
subscript: true,
|
|
61
|
+
};
|
|
53
62
|
export function isFontVerticalAlignment(value) {
|
|
54
|
-
return
|
|
63
|
+
return Object.hasOwn(FONT_VERTICAL_ALIGNMENTS, value);
|
|
55
64
|
}
|
|
65
|
+
const FONT_SCHEMES = { minor: true, major: true, none: true };
|
|
56
66
|
export function isFontScheme(value) {
|
|
57
|
-
return
|
|
67
|
+
return Object.hasOwn(FONT_SCHEMES, value);
|
|
58
68
|
}
|
|
59
|
-
const HORIZONTAL_ALIGNMENTS =
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
+
const HORIZONTAL_ALIGNMENTS = {
|
|
70
|
+
general: true,
|
|
71
|
+
left: true,
|
|
72
|
+
center: true,
|
|
73
|
+
right: true,
|
|
74
|
+
fill: true,
|
|
75
|
+
justify: true,
|
|
76
|
+
centerContinuous: true,
|
|
77
|
+
distributed: true,
|
|
78
|
+
};
|
|
69
79
|
export function isHorizontalAlignment(value) {
|
|
70
|
-
return
|
|
80
|
+
return Object.hasOwn(HORIZONTAL_ALIGNMENTS, value);
|
|
71
81
|
}
|
|
72
|
-
const VERTICAL_ALIGNMENTS =
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
82
|
+
const VERTICAL_ALIGNMENTS = {
|
|
83
|
+
top: true,
|
|
84
|
+
center: true,
|
|
85
|
+
bottom: true,
|
|
86
|
+
justify: true,
|
|
87
|
+
distributed: true,
|
|
88
|
+
};
|
|
79
89
|
export function isVerticalAlignment(value) {
|
|
80
|
-
return
|
|
90
|
+
return Object.hasOwn(VERTICAL_ALIGNMENTS, value);
|
|
81
91
|
}
|
|
82
92
|
const CELL_STYLE_FACET_KEYS = {
|
|
83
93
|
fill: true,
|
|
@@ -92,6 +102,11 @@ export function assignStyleFacets(target, source) {
|
|
|
92
102
|
for (const facet of CELL_STYLE_FACETS)
|
|
93
103
|
copyFacet(target, source, facet);
|
|
94
104
|
}
|
|
105
|
+
export function pickStyleFacets(source) {
|
|
106
|
+
const facets = {};
|
|
107
|
+
assignStyleFacets(facets, source);
|
|
108
|
+
return facets;
|
|
109
|
+
}
|
|
95
110
|
function copyFacet(target, source, key) {
|
|
96
111
|
const value = source[key];
|
|
97
112
|
if (value !== undefined)
|
|
@@ -4,19 +4,19 @@ import type { DifferentialStyle } from './style.ts';
|
|
|
4
4
|
*
|
|
5
5
|
* The first thirteen apply to a **table**; the rest style a **pivot table**, which has regions a
|
|
6
6
|
* table does not have (subtotal rows, page-field labels, subheadings). Both live in the same
|
|
7
|
-
* enumeration and the same `<tableStyle>` element
|
|
8
|
-
* the style's own `table`/`pivot` flags, not the element names
|
|
7
|
+
* enumeration and the same `<tableStyle>` element. What decides which regions a consumer honours is
|
|
8
|
+
* the style's own `table`/`pivot` flags, not the element names, so the type carries all of them
|
|
9
9
|
* rather than splitting into two enumerations that a caller would have to choose between up front.
|
|
10
10
|
*/
|
|
11
11
|
export declare const TABLE_STYLE_ELEMENT_TYPES: readonly ['wholeTable', 'headerRow', 'totalRow', 'firstColumn', 'lastColumn', 'firstRowStripe', 'secondRowStripe', 'firstColumnStripe', 'secondColumnStripe', 'firstHeaderCell', 'lastHeaderCell', 'firstTotalCell', 'lastTotalCell', 'firstSubtotalColumn', 'secondSubtotalColumn', 'thirdSubtotalColumn', 'firstSubtotalRow', 'secondSubtotalRow', 'thirdSubtotalRow', 'blankRow', 'firstColumnSubheading', 'secondColumnSubheading', 'thirdColumnSubheading', 'firstRowSubheading', 'secondRowSubheading', 'thirdRowSubheading', 'pageFieldLabels', 'pageFieldValues'];
|
|
12
12
|
/** One region of a table or pivot that a table style can format. */
|
|
13
13
|
export type TableStyleElementType = (typeof TABLE_STYLE_ELEMENT_TYPES)[number];
|
|
14
|
-
/** The four element types banded across several rows or columns
|
|
14
|
+
/** The four element types banded across several rows or columns: the only ones {@link TableStyleElement.size} means anything on. */
|
|
15
15
|
export declare const STRIPE_ELEMENT_TYPES: ReadonlySet<TableStyleElementType>;
|
|
16
16
|
export declare function isTableStyleElementType(value: string): value is TableStyleElementType;
|
|
17
17
|
/**
|
|
18
18
|
* How one region of a table is formatted: a {@link DifferentialStyle} laid over whatever the cells
|
|
19
|
-
* already carry, plus
|
|
19
|
+
* already carry, plus, for a stripe, how many rows or columns wide one band is.
|
|
20
20
|
*
|
|
21
21
|
* A `numFmt` here is carried faithfully but has no visible effect: Excel's own table-style element
|
|
22
22
|
* exposes a font, an interior and borders, and nothing for a number format. See
|
|
@@ -24,7 +24,7 @@ export declare function isTableStyleElementType(value: string): value is TableSt
|
|
|
24
24
|
*/
|
|
25
25
|
export interface TableStyleElement extends DifferentialStyle {
|
|
26
26
|
/**
|
|
27
|
-
* The band width, in rows or columns, for a striped element
|
|
27
|
+
* The band width, in rows or columns, for a striped element: `2` makes each band two rows deep.
|
|
28
28
|
* Defaults to 1.
|
|
29
29
|
*
|
|
30
30
|
* Meaningful **only** on the four stripe types ({@link STRIPE_ELEMENT_TYPES}); ECMA-376 says so and
|
|
@@ -41,7 +41,7 @@ export interface TableStyleElement extends DifferentialStyle {
|
|
|
41
41
|
* Elements are applied in the order ECMA-376 fixes, not the order they are written here: whole table,
|
|
42
42
|
* then the column stripes, then the row stripes, then last/first column, header row, total row, and
|
|
43
43
|
* the four corner cells. So a row stripe wins over a column stripe, and both win over the whole-table
|
|
44
|
-
* formatting
|
|
44
|
+
* formatting, which is worth knowing when a stripe colour appears not to take.
|
|
45
45
|
*/
|
|
46
46
|
export interface TableStyle {
|
|
47
47
|
/** The name a table references, and the name Excel shows in its style gallery. */
|
|
@@ -58,10 +58,10 @@ export interface TableStyle {
|
|
|
58
58
|
*
|
|
59
59
|
* Both failures here are of the same kind: Excel accepts the file and quietly does nothing with the
|
|
60
60
|
* part the caller cared about. An empty name means no table can ever reference the style, and a
|
|
61
|
-
* `size` outside a stripe is ignored
|
|
61
|
+
* `size` outside a stripe is ignored. Neither shows up as a repair prompt or a schema error, so the
|
|
62
62
|
* only place to catch them is the call that made them.
|
|
63
63
|
*
|
|
64
|
-
* @throws {AuthoringError} if the name is empty, or a non-stripe element carries a `size
|
|
65
|
-
*
|
|
64
|
+
* @throws {AuthoringError} if the name is empty, or a non-stripe element carries a `size`.
|
|
65
|
+
* @throws {RangeError} if a `size` is not a positive integer.
|
|
66
66
|
*/
|
|
67
67
|
export declare function checkTableStyle(style: TableStyle): void;
|
package/dist/core/table-style.js
CHANGED
|
@@ -56,7 +56,7 @@ export function checkTableStyle(style) {
|
|
|
56
56
|
`${[...STRIPE_ELEMENT_TYPES].join(', ')}`);
|
|
57
57
|
}
|
|
58
58
|
if (!Number.isInteger(size) || size < 1) {
|
|
59
|
-
throw new
|
|
59
|
+
throw new RangeError(`Invalid table style band size ${size}: expected a positive integer`);
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
}
|
package/dist/core/table.d.ts
CHANGED
|
@@ -1,36 +1,51 @@
|
|
|
1
|
+
import { type GridRect } from './address.ts';
|
|
1
2
|
import type { CellStyle } from './style.ts';
|
|
2
3
|
import type { CellValue } from './value.ts';
|
|
3
|
-
/** A per-column cell format applied to a table's body cells
|
|
4
|
+
/** A per-column cell format applied to a table's body cells: the facets Excel's table-column style
|
|
4
5
|
* bakes into the cells rather than storing as table metadata. Every facet ({@link CellStyle}) is
|
|
5
6
|
* optional; only the ones set are applied, leaving the rest of each cell's style untouched. */
|
|
6
7
|
export type TableColumnStyle = Readonly<CellStyle>;
|
|
7
|
-
/**
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
*
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
8
|
+
/**
|
|
9
|
+
* The channel a registered table holds into its owning worksheet's grid. A worksheet supplies it
|
|
10
|
+
* when it registers the table; a table built standalone (a unit test, a bare model) has none, so it
|
|
11
|
+
* can be inspected but cannot materialise or append cells, and appending throws rather than
|
|
12
|
+
* silently dropping the values.
|
|
13
|
+
*
|
|
14
|
+
* All three coordinates are 1-based.
|
|
15
|
+
*/
|
|
16
|
+
export interface TableGrid {
|
|
17
|
+
/**
|
|
18
|
+
* Whether the cell at this position already holds a value. The materialiser's round-trip guard
|
|
19
|
+
* asks this and nothing else: it must not create the cell, because asking whether a table's frame
|
|
20
|
+
* is already filled would otherwise fill the grid with the empty cells it was asking about.
|
|
21
|
+
*/
|
|
22
|
+
holdsValue(row: number, col: number): boolean;
|
|
23
|
+
/** Write a value, applying the column's style (if any) to the cell. */
|
|
24
|
+
writeCell(row: number, col: number, value: CellValue, style?: TableColumnStyle): void;
|
|
25
|
+
/**
|
|
26
|
+
* Insert one empty row at `row`, shifting that row and everything below it down by one: how a
|
|
27
|
+
* table with a totals row opens a slot for an appended data row above the totals. Relocating the
|
|
28
|
+
* totals row lives in the grid, which is why this is the grid's job and not the table's.
|
|
29
|
+
*/
|
|
30
|
+
insertRow(row: number): void;
|
|
31
|
+
}
|
|
17
32
|
/**
|
|
18
33
|
* A table's visual style (`<tableStyleInfo>`): the named style to apply plus the banding/highlight
|
|
19
|
-
* toggles. Every field is a tri-state so a round-trip stays faithful
|
|
34
|
+
* toggles. Every field is a tri-state so a round-trip stays faithful: a value present in the source
|
|
20
35
|
* re-emits, one the source omitted stays omitted rather than being defaulted to `"0"`. A workbook
|
|
21
36
|
* whose part carries no `<tableStyleInfo>` at all leaves {@link TableOptions.style} undefined.
|
|
22
37
|
*/
|
|
23
38
|
export interface TableStyleInfo {
|
|
24
39
|
/**
|
|
25
|
-
* Named table style to apply
|
|
40
|
+
* Named table style to apply: one of the built-in gallery (`"TableStyleMedium2"`, …) or a custom
|
|
26
41
|
* one the workbook defines with {@link Workbook.addTableStyle}.
|
|
27
42
|
*
|
|
28
|
-
* **Not validated.** A name that matches nothing renders the table unstyled, silently
|
|
29
|
-
* library must not be the thing that rejects it. A reader has to accept a name from a newer
|
|
30
|
-
* than the gallery list it was built with, and a writer that threw would make round-tripping
|
|
31
|
-
* file impossible; there is also no diagnostics channel to warn through, so the only
|
|
32
|
-
* "throw" and "accept". Accepting is the one that never makes a readable file
|
|
33
|
-
* warning channel is ever added, this is the first thing that should use it.
|
|
43
|
+
* **Not validated.** A name that matches nothing renders the table unstyled, silently. Even so,
|
|
44
|
+
* this library must not be the thing that rejects it. A reader has to accept a name from a newer
|
|
45
|
+
* Excel than the gallery list it was built with, and a writer that threw would make round-tripping
|
|
46
|
+
* such a file impossible; there is also no diagnostics channel to warn through, so the only
|
|
47
|
+
* options were "throw" and "accept". Accepting is the one that never makes a readable file
|
|
48
|
+
* unreadable. If a warning channel is ever added, this is the first thing that should use it.
|
|
34
49
|
*/
|
|
35
50
|
readonly name?: string;
|
|
36
51
|
/** Emphasise the first column. */
|
|
@@ -44,17 +59,17 @@ export interface TableStyleInfo {
|
|
|
44
59
|
}
|
|
45
60
|
/**
|
|
46
61
|
* OOXML's totals-row function names (`ST_TotalsRowFunction`) to the `SUBTOTAL` first-argument code
|
|
47
|
-
* Excel writes into a materialised totals cell. The `10x` band ignores manually hidden rows
|
|
62
|
+
* Excel writes into a materialised totals cell. The `10x` band ignores manually hidden rows, the
|
|
48
63
|
* behaviour Excel's totals row uses. The one inversion trap: `count` is COUNTA (103, non-empty) while
|
|
49
64
|
* `countNums` is COUNT (102, numbers only). `none` (no aggregate) has no built-in code, so a column
|
|
50
|
-
* carrying it is absent here and its totals cell is left unmaterialised
|
|
65
|
+
* carrying it is absent here and its totals cell is left unmaterialised: Excel accepts the blank
|
|
51
66
|
* cell. `custom` is likewise absent: its aggregate is not a `SUBTOTAL` but the arbitrary formula
|
|
52
67
|
* stored in {@link TableColumn.totalsRowFormula}, which the reader/writer round-trip and the
|
|
53
68
|
* materialiser writes into the cell verbatim.
|
|
54
69
|
*/
|
|
55
70
|
export declare const TOTALS_ROW_SUBTOTAL_CODE: Readonly<Partial<Record<TotalsRowFunction, number>>>;
|
|
56
71
|
/**
|
|
57
|
-
* The values `ST_TotalsRowFunction` (ECMA-376 §18.18.86) can take
|
|
72
|
+
* The values `ST_TotalsRowFunction` (ECMA-376 §18.18.86) can take: a closed OOXML enumeration Excel
|
|
58
73
|
* does not extend over time (unlike, say, a conditional-formatting rule type), so an author-side typo
|
|
59
74
|
* such as `"avg"` is a compile error here rather than a silently no-op attribute at write time.
|
|
60
75
|
*/
|
|
@@ -63,7 +78,7 @@ export type TotalsRowFunction = 'average' | 'countNums' | 'count' | 'max' | 'min
|
|
|
63
78
|
export declare function isTotalsRowFunction(value: string): value is TotalsRowFunction;
|
|
64
79
|
/** One column of a table: a header name and its optional totals-row behaviour. */
|
|
65
80
|
export interface TableColumn {
|
|
66
|
-
/** The column's header/display name. Must be unique within the table (case-insensitively)
|
|
81
|
+
/** The column's header/display name. Must be unique within the table (case-insensitively):
|
|
67
82
|
* Excel writes a table with colliding column names as corrupt. A collision supplied at construction
|
|
68
83
|
* is disambiguated deterministically (the first keeps its name, later clashes gain a numeric
|
|
69
84
|
* suffix), the same repair the reader applies to a loaded file, rather than being rejected. */
|
|
@@ -73,7 +88,7 @@ export interface TableColumn {
|
|
|
73
88
|
/** Built-in totals-row aggregate (`"sum"`, `"average"`, `"count"`, …), or `"custom"` when the
|
|
74
89
|
* column's total is the arbitrary formula in {@link totalsRowFormula} rather than a `SUBTOTAL`. */
|
|
75
90
|
readonly totalsRowFunction?: TotalsRowFunction;
|
|
76
|
-
/** The formula (no leading `=`) backing a `totalsRowFunction: "custom"` column
|
|
91
|
+
/** The formula (no leading `=`) backing a `totalsRowFunction: "custom"` column. This is OOXML's
|
|
77
92
|
* `<totalsRowFormula>` child. Round-tripped verbatim and written into the totals cell as the
|
|
78
93
|
* cell's formula. Meaningful only alongside `totalsRowFunction: "custom"`; ignored otherwise. */
|
|
79
94
|
readonly totalsRowFormula?: string;
|
|
@@ -83,13 +98,13 @@ export interface TableColumn {
|
|
|
83
98
|
readonly style?: TableColumnStyle;
|
|
84
99
|
}
|
|
85
100
|
export interface TableOptions {
|
|
86
|
-
/** Table name
|
|
101
|
+
/** Table name: a valid Excel identifier, unique across the workbook. This is the name used in
|
|
87
102
|
* structured formula references (`Table1[Column]`). */
|
|
88
103
|
name: string;
|
|
89
104
|
/** Human-facing display name shown in the UI. A free-form label (spaces allowed) that need not
|
|
90
105
|
* be a valid identifier. Defaults to {@link name} when omitted. */
|
|
91
106
|
displayName?: string;
|
|
92
|
-
/** A1 reference of the table's top-left cell (an anchor, e.g. `"A1"
|
|
107
|
+
/** A1 reference of the table's top-left cell (an anchor, e.g. `"A1"`, not the full range). */
|
|
93
108
|
ref: string;
|
|
94
109
|
/** The table's columns, left to right. At least one is required. */
|
|
95
110
|
columns: readonly TableColumn[];
|
|
@@ -99,36 +114,23 @@ export interface TableOptions {
|
|
|
99
114
|
headerRow?: boolean;
|
|
100
115
|
/** Whether the table has a totals row. Defaults to `false`. */
|
|
101
116
|
totalsRow?: boolean;
|
|
102
|
-
/** The `totalsRowShown` flag on a table *without* a totals row
|
|
117
|
+
/** The `totalsRowShown` flag on a table *without* a totals row: Excel's record of whether a
|
|
103
118
|
* totals row has ever been toggled on. Tri-state so a round-trip is faithful: `false` re-emits
|
|
104
119
|
* `totalsRowShown="0"`, `true` re-emits `totalsRowShown="1"`, and `undefined` (the authoring
|
|
105
|
-
* default) emits nothing
|
|
120
|
+
* default) emits nothing: a file read without the attribute must not have one injected. Ignored
|
|
106
121
|
* when {@link totalsRow} is set, since a present totals row already implies it is shown. */
|
|
107
122
|
totalsRowShown?: boolean;
|
|
108
123
|
/** Whether the header row carries an autoFilter. Defaults to {@link headerRow}: a header table
|
|
109
124
|
* gains an autoFilter, a headerless one never can. Set `false` to keep a header table's rows
|
|
110
|
-
* unfiltered
|
|
125
|
+
* unfiltered: a file read without an autoFilter must round-trip without one being injected. */
|
|
111
126
|
autoFilter?: boolean;
|
|
112
127
|
/** The table's visual style. Preserved verbatim across a round-trip; when omitted, a freshly
|
|
113
128
|
* authored table is written with Excel's default (`TableStyleMedium2`, banded rows). A part read
|
|
114
129
|
* with no `<tableStyleInfo>` sets this to `undefined`. See {@link TableStyleInfo}. */
|
|
115
130
|
style?: TableStyleInfo;
|
|
116
131
|
}
|
|
117
|
-
/**
|
|
118
|
-
|
|
119
|
-
* keeps its name; a later clash gains the smallest numeric suffix that resolves it (`foo`, `foo2`,
|
|
120
|
-
* `foo3`, …). OOXML requires unique column names within a table — Excel treats a collision as
|
|
121
|
-
* corruption — so this is applied both when a table is authored and when one is read from a file,
|
|
122
|
-
* keeping the two paths identical rather than rejecting a name list the reader would accept.
|
|
123
|
-
*/
|
|
124
|
-
export declare function disambiguateColumnNames(columns: readonly TableColumn[]): TableColumn[];
|
|
125
|
-
/** The rectangle a table occupies, in 1-based coordinates. */
|
|
126
|
-
export interface TableRegion {
|
|
127
|
-
readonly top: number;
|
|
128
|
-
readonly left: number;
|
|
129
|
-
readonly bottom: number;
|
|
130
|
-
readonly right: number;
|
|
131
|
-
}
|
|
132
|
+
/** The rectangle a table occupies, as the {@link GridRect} every range-shaped thing in the library is. */
|
|
133
|
+
export type TableRegion = GridRect;
|
|
132
134
|
export declare class Table {
|
|
133
135
|
#private;
|
|
134
136
|
readonly name: string;
|
|
@@ -139,9 +141,9 @@ export declare class Table {
|
|
|
139
141
|
readonly totalsRowShown: boolean | undefined;
|
|
140
142
|
readonly autoFilter: boolean;
|
|
141
143
|
readonly style: TableStyleInfo | undefined;
|
|
142
|
-
constructor(options: TableOptions,
|
|
144
|
+
constructor(options: TableOptions, grid?: TableGrid);
|
|
143
145
|
get columnCount(): number;
|
|
144
|
-
/** The number of data rows (excludes the header and totals rows). Always defined
|
|
146
|
+
/** The number of data rows (excludes the header and totals rows). Always defined: a table loaded
|
|
145
147
|
* from a file derives it from the stored range, so reading the height never throws. */
|
|
146
148
|
get rowCount(): number;
|
|
147
149
|
/**
|
|
@@ -150,9 +152,9 @@ export declare class Table {
|
|
|
150
152
|
* one, so this works identically whether the table was built in memory or read from a file.
|
|
151
153
|
*
|
|
152
154
|
* A table carrying a totals row appends above it: the new data row lands where the totals row sat,
|
|
153
|
-
* and the totals row (with any sheet content below) shifts down by one
|
|
155
|
+
* and the totals row (with any sheet content below) shifts down by one, exactly what inserting a
|
|
154
156
|
* worksheet row does. That relocation lives in the grid, so a totals-row table not attached to a
|
|
155
|
-
* worksheet throws, as does passing `values` on any detached table
|
|
157
|
+
* worksheet throws, as does passing `values` on any detached table: there is nowhere to put them.
|
|
156
158
|
*/
|
|
157
159
|
addRow(values?: readonly CellValue[]): void;
|
|
158
160
|
/**
|
|
@@ -171,7 +173,7 @@ export declare class Table {
|
|
|
171
173
|
*/
|
|
172
174
|
shiftColumns(start: number, count: number, delta: number): boolean;
|
|
173
175
|
/**
|
|
174
|
-
* The options that reconstruct this table
|
|
176
|
+
* The options that reconstruct this table: the anchor as a single-cell ref (not the derived
|
|
175
177
|
* full range), the columns, and the data-row count with the header/totals flags. Feeding this
|
|
176
178
|
* back to the constructor yields an equivalent table, so a worksheet model can carry a table
|
|
177
179
|
* losslessly across an export/import round-trip.
|
|
@@ -179,11 +181,11 @@ export declare class Table {
|
|
|
179
181
|
get options(): TableOptions;
|
|
180
182
|
/** The full A1 range the table occupies: header (if any) + data rows + totals (if any). Distinct
|
|
181
183
|
* from {@link TableOptions.ref} (and {@link options}'s own `ref`), which is only the single-cell
|
|
182
|
-
* anchor a table is constructed from
|
|
184
|
+
* anchor a table is constructed from; this is the anchor plus the columns/rows it has grown to
|
|
183
185
|
* cover. */
|
|
184
186
|
get range(): string;
|
|
185
187
|
/**
|
|
186
|
-
* The autoFilter range
|
|
188
|
+
* The autoFilter range (the header row plus the data rows, never the totals row), or
|
|
187
189
|
* `undefined` when the table has no autoFilter: either it is headerless (an autoFilter has
|
|
188
190
|
* nothing to anchor to and Excel treats its presence as corruption) or its {@link autoFilter}
|
|
189
191
|
* flag is off (a table read without one must not gain one on round-trip).
|
package/dist/core/table.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { AuthoringError } from '../errors.js';
|
|
2
|
-
import {
|
|
2
|
+
import { decodeCellRef, encodeAddress } from './address.js';
|
|
3
|
+
import { isDeletedSpan, shiftIndex } from './grid-shift.js';
|
|
3
4
|
function cloneStyleInfo(style) {
|
|
4
5
|
const clone = {};
|
|
5
6
|
if (style.name !== undefined && style.name !== 'None')
|
|
@@ -24,23 +25,23 @@ export const TOTALS_ROW_SUBTOTAL_CODE = {
|
|
|
24
25
|
sum: 109,
|
|
25
26
|
var: 110,
|
|
26
27
|
};
|
|
27
|
-
const TOTALS_ROW_FUNCTIONS =
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
28
|
+
const TOTALS_ROW_FUNCTIONS = {
|
|
29
|
+
average: true,
|
|
30
|
+
countNums: true,
|
|
31
|
+
count: true,
|
|
32
|
+
max: true,
|
|
33
|
+
min: true,
|
|
34
|
+
stdDev: true,
|
|
35
|
+
sum: true,
|
|
36
|
+
var: true,
|
|
37
|
+
custom: true,
|
|
38
|
+
none: true,
|
|
39
|
+
};
|
|
39
40
|
export function isTotalsRowFunction(value) {
|
|
40
|
-
return
|
|
41
|
+
return Object.hasOwn(TOTALS_ROW_FUNCTIONS, value);
|
|
41
42
|
}
|
|
42
43
|
const IDENTIFIER = /^[\p{L}\\_][\p{L}\p{N}._]*$/u;
|
|
43
|
-
|
|
44
|
+
function disambiguateColumnNames(columns) {
|
|
44
45
|
const seen = new Set();
|
|
45
46
|
return columns.map((column) => {
|
|
46
47
|
let candidate = column.name;
|
|
@@ -52,10 +53,10 @@ export function disambiguateColumnNames(columns) {
|
|
|
52
53
|
}
|
|
53
54
|
function validateTableName(name) {
|
|
54
55
|
if (name.length === 0 || name.length > 255) {
|
|
55
|
-
throw new
|
|
56
|
+
throw new RangeError(`table name ${JSON.stringify(name)} must be between 1 and 255 characters`);
|
|
56
57
|
}
|
|
57
58
|
if (!IDENTIFIER.test(name)) {
|
|
58
|
-
throw new
|
|
59
|
+
throw new SyntaxError(`table name ${JSON.stringify(name)} is not a valid Excel identifier: it must start with a letter, ` +
|
|
59
60
|
'underscore, or backslash and contain only letters, digits, periods, and underscores');
|
|
60
61
|
}
|
|
61
62
|
}
|
|
@@ -71,9 +72,8 @@ export class Table {
|
|
|
71
72
|
#anchorCol;
|
|
72
73
|
#anchorRow;
|
|
73
74
|
#dataRowCount;
|
|
74
|
-
#
|
|
75
|
-
|
|
76
|
-
constructor(options, writeCell, insertRow) {
|
|
75
|
+
#grid;
|
|
76
|
+
constructor(options, grid) {
|
|
77
77
|
validateTableName(options.name);
|
|
78
78
|
if (options.columns.length === 0) {
|
|
79
79
|
throw new AuthoringError(`table "${options.name}" must declare at least one column`);
|
|
@@ -81,10 +81,16 @@ export class Table {
|
|
|
81
81
|
if (!Number.isInteger(options.rowCount) || options.rowCount < 0) {
|
|
82
82
|
throw new RangeError(`table "${options.name}" has an invalid data-row count (${options.rowCount})`);
|
|
83
83
|
}
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
84
|
+
let anchor;
|
|
85
|
+
try {
|
|
86
|
+
anchor = decodeCellRef(options.ref);
|
|
87
|
+
}
|
|
88
|
+
catch (cause) {
|
|
89
|
+
throw new SyntaxError(`table ref "${options.ref}" must anchor at a single cell (e.g. "A1")`, {
|
|
90
|
+
cause,
|
|
91
|
+
});
|
|
87
92
|
}
|
|
93
|
+
const { col, row } = anchor;
|
|
88
94
|
this.name = options.name;
|
|
89
95
|
this.displayName = options.displayName ?? options.name;
|
|
90
96
|
this.columns = disambiguateColumnNames(options.columns);
|
|
@@ -96,11 +102,12 @@ export class Table {
|
|
|
96
102
|
this.#anchorCol = col;
|
|
97
103
|
this.#anchorRow = row;
|
|
98
104
|
this.#dataRowCount = options.rowCount;
|
|
99
|
-
this.#
|
|
100
|
-
this.#insertRow = insertRow;
|
|
105
|
+
this.#grid = grid;
|
|
101
106
|
if (this.#rowSpan < 1) {
|
|
102
|
-
throw new AuthoringError(`table "${this.name}" has no rows
|
|
107
|
+
throw new AuthoringError(`table "${this.name}" has no rows: it needs a header row or at least one data row`);
|
|
103
108
|
}
|
|
109
|
+
if (grid !== undefined)
|
|
110
|
+
this.#materializeFrame(grid);
|
|
104
111
|
}
|
|
105
112
|
get columnCount() {
|
|
106
113
|
return this.columns.length;
|
|
@@ -113,32 +120,67 @@ export class Table {
|
|
|
113
120
|
throw new RangeError(`row has ${values.length} values but table "${this.name}" has ${this.columnCount} columns`);
|
|
114
121
|
}
|
|
115
122
|
const target = this.#anchorRow + (this.headerRow ? 1 : 0) + this.#dataRowCount;
|
|
123
|
+
const grid = this.#grid;
|
|
116
124
|
if (this.totalsRow) {
|
|
117
|
-
if (
|
|
118
|
-
throw new AuthoringError(`table "${this.name}" is not attached to a worksheet
|
|
125
|
+
if (grid === undefined) {
|
|
126
|
+
throw new AuthoringError(`table "${this.name}" is not attached to a worksheet: cannot relocate its totals row to append a data row`);
|
|
119
127
|
}
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
this.#writeCell?.(target, this.#anchorCol + index, value, this.columns[index]?.style);
|
|
123
|
-
});
|
|
128
|
+
grid.insertRow(target);
|
|
129
|
+
this.#writeRow(grid, target, values);
|
|
124
130
|
return;
|
|
125
131
|
}
|
|
126
132
|
if (values.length > 0) {
|
|
127
|
-
if (
|
|
128
|
-
throw new AuthoringError(`table "${this.name}" is not attached to a worksheet
|
|
133
|
+
if (grid === undefined) {
|
|
134
|
+
throw new AuthoringError(`table "${this.name}" is not attached to a worksheet: cannot write appended row values`);
|
|
129
135
|
}
|
|
130
|
-
|
|
131
|
-
this.#writeCell?.(target, this.#anchorCol + index, value, this.columns[index]?.style);
|
|
132
|
-
});
|
|
136
|
+
this.#writeRow(grid, target, values);
|
|
133
137
|
}
|
|
134
138
|
this.#dataRowCount += 1;
|
|
135
139
|
}
|
|
140
|
+
#writeRow(grid, row, values) {
|
|
141
|
+
values.forEach((value, index) => {
|
|
142
|
+
grid.writeCell(row, this.#anchorCol + index, value, this.columns[index]?.style);
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
#materializeFrame(grid) {
|
|
146
|
+
if (this.headerRow) {
|
|
147
|
+
const { top, left } = this.region;
|
|
148
|
+
this.columns.forEach((column, index) => {
|
|
149
|
+
const col = left + index;
|
|
150
|
+
if (grid.holdsValue(top, col))
|
|
151
|
+
return;
|
|
152
|
+
grid.writeCell(top, col, column.name);
|
|
153
|
+
});
|
|
154
|
+
}
|
|
155
|
+
if (this.totalsRow) {
|
|
156
|
+
const { left, bottom } = this.region;
|
|
157
|
+
this.columns.forEach((column, index) => {
|
|
158
|
+
const col = left + index;
|
|
159
|
+
if (grid.holdsValue(bottom, col))
|
|
160
|
+
return;
|
|
161
|
+
if (column.totalsRowLabel !== undefined) {
|
|
162
|
+
grid.writeCell(bottom, col, column.totalsRowLabel);
|
|
163
|
+
return;
|
|
164
|
+
}
|
|
165
|
+
if (column.totalsRowFunction === undefined)
|
|
166
|
+
return;
|
|
167
|
+
const code = TOTALS_ROW_SUBTOTAL_CODE[column.totalsRowFunction];
|
|
168
|
+
if (code !== undefined) {
|
|
169
|
+
grid.writeCell(bottom, col, {
|
|
170
|
+
formula: `SUBTOTAL(${code},${this.name}[${column.name}])`,
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
else if (column.totalsRowFunction === 'custom' && column.totalsRowFormula !== undefined) {
|
|
174
|
+
grid.writeCell(bottom, col, { formula: column.totalsRowFormula });
|
|
175
|
+
}
|
|
176
|
+
});
|
|
177
|
+
}
|
|
178
|
+
}
|
|
136
179
|
shiftRows(start, count, delta) {
|
|
137
|
-
if (this.#anchorRow
|
|
180
|
+
if (isDeletedSpan(this.#anchorRow, this.#bottom, start, count))
|
|
138
181
|
return false;
|
|
139
|
-
const
|
|
140
|
-
const
|
|
141
|
-
const bottom = shift(this.#bottom);
|
|
182
|
+
const top = shiftIndex(this.#anchorRow, start, count, delta, 'row');
|
|
183
|
+
const bottom = shiftIndex(this.#bottom, start, count, delta, 'row');
|
|
142
184
|
const span = bottom - top + 1;
|
|
143
185
|
const fixedRows = (this.headerRow ? 1 : 0) + (this.totalsRow ? 1 : 0);
|
|
144
186
|
const dataRows = span - fixedRows;
|