@shbernal/ts-xlsx 1.0.0 → 1.0.3
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 +6 -0
- package/dist/core/autofilter.js +2 -2
- package/dist/core/cell.js +4 -4
- package/dist/core/color-resolution.js +1 -1
- package/dist/core/column.js +2 -2
- package/dist/core/conditional-formatting-overlay.js +1 -1
- package/dist/core/data-validation-overlay.js +2 -2
- package/dist/core/formula.js +2 -2
- package/dist/core/grid-edits.js +5 -5
- package/dist/core/merge.js +1 -1
- package/dist/core/pivot-table.js +3 -3
- package/dist/core/range.js +8 -4
- package/dist/core/row.js +2 -2
- package/dist/core/table-style.d.ts +2 -2
- package/dist/core/table-style.js +2 -2
- package/dist/core/table.js +2 -2
- package/dist/core/theme.d.ts +1 -1
- package/dist/core/theme.js +1 -1
- package/dist/core/value.d.ts +10 -10
- package/dist/core/workbook-protection.d.ts +1 -1
- package/dist/core/workbook.d.ts +11 -11
- package/dist/core/workbook.js +22 -22
- package/dist/core/worksheet-model.d.ts +1 -1
- package/dist/core/worksheet-model.js +5 -5
- package/dist/core/worksheet.d.ts +2 -2
- package/dist/core/worksheet.js +21 -21
- package/dist/customui/errors.js +1 -1
- package/dist/customui/index.js +2 -2
- package/dist/customui/ribbon.d.ts +1 -1
- package/dist/customui/ribbon.js +3 -3
- package/dist/entries/core.js +14 -14
- package/dist/entries/csv.js +2 -2
- package/dist/entries/customui.js +1 -1
- package/dist/entries/errors.js +7 -7
- package/dist/entries/vba.js +2 -2
- package/dist/entries/xlsb.js +1 -1
- package/dist/entries/xlsx.js +5 -5
- package/dist/index.js +7 -7
- package/dist/io/csv/read.js +2 -2
- package/dist/io/csv/write.js +2 -2
- package/dist/io/opc/errors.js +1 -1
- package/dist/io/opc/inflate.d.ts +1 -1
- package/dist/io/opc/inflate.js +2 -2
- package/dist/io/opc/read-opc.js +2 -2
- package/dist/io/opc/rels.js +2 -2
- package/dist/io/opc/sniff-format.js +2 -2
- package/dist/io/style/xf-style.js +2 -2
- package/dist/io/xlsb/errors.js +1 -1
- package/dist/io/xlsb/formula.js +4 -4
- package/dist/io/xlsb/primitives.js +2 -2
- package/dist/io/xlsb/read-shared-strings.js +3 -3
- package/dist/io/xlsb/read-styles.js +5 -5
- package/dist/io/xlsb/read-worksheet.js +9 -9
- package/dist/io/xlsb/read.d.ts +1 -1
- package/dist/io/xlsb/read.js +15 -15
- package/dist/io/xlsb/record-stream.js +1 -1
- package/dist/io/xlsx/cell-accumulator.js +6 -6
- package/dist/io/xlsx/cell-value.js +4 -4
- package/dist/io/xlsx/comments.js +4 -4
- package/dist/io/xlsx/conditional-formatting.js +5 -5
- package/dist/io/xlsx/data-validation.js +4 -4
- package/dist/io/xlsx/edit-vba.js +4 -4
- package/dist/io/xlsx/errors.js +1 -1
- package/dist/io/xlsx/hyperlinks.js +4 -4
- package/dist/io/xlsx/images.js +6 -6
- package/dist/io/xlsx/package-plan.js +3 -3
- package/dist/io/xlsx/pivot-read.js +2 -2
- package/dist/io/xlsx/pivot.js +4 -4
- package/dist/io/xlsx/read-rows.d.ts +3 -3
- package/dist/io/xlsx/read-rows.js +12 -12
- package/dist/io/xlsx/read-styles.js +4 -4
- package/dist/io/xlsx/read-worksheet.js +8 -8
- package/dist/io/xlsx/read.d.ts +1 -1
- package/dist/io/xlsx/read.js +28 -28
- package/dist/io/xlsx/relationships.d.ts +6 -6
- package/dist/io/xlsx/relationships.js +2 -2
- package/dist/io/xlsx/rich-runs.js +1 -1
- package/dist/io/xlsx/rich-text.js +2 -2
- package/dist/io/xlsx/shared-formulas.js +3 -3
- package/dist/io/xlsx/shared-strings-read.js +2 -2
- package/dist/io/xlsx/shared-strings.js +3 -3
- package/dist/io/xlsx/sheet-properties.js +4 -4
- package/dist/io/xlsx/styles.js +6 -6
- package/dist/io/xlsx/tables.js +5 -5
- package/dist/io/xlsx/threaded-comments.js +5 -5
- package/dist/io/xlsx/workbook-xml.js +9 -9
- package/dist/io/xlsx/worksheet-xml.js +18 -18
- package/dist/io/xlsx/write-stream.d.ts +1 -1
- package/dist/io/xlsx/write-stream.js +7 -7
- package/dist/io/xlsx/write.d.ts +3 -3
- package/dist/io/xlsx/write.js +20 -20
- package/dist/io/xlsx/x14-ext.js +1 -1
- package/dist/vba/cfb-writer.js +1 -1
- package/dist/vba/cfb.js +1 -1
- package/dist/vba/codepage.js +1 -1
- package/dist/vba/errors.js +1 -1
- package/dist/vba/index.js +5 -5
- package/dist/vba/ms-ovba.js +1 -1
- package/dist/vba/project-editor.js +7 -7
- package/dist/vba/project.js +4 -4
- package/dist/vba/vba-encoding.js +1 -1
- package/dist/xml/errors.js +1 -1
- package/dist/xml/xml-read.js +1 -1
- package/dist/xml/xml.js +1 -1
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -1,5 +1,11 @@
|
|
|
1
1
|
# ts-xlsx
|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/@shbernal/ts-xlsx)
|
|
4
|
+
[](https://www.npmjs.com/package/@shbernal/ts-xlsx)
|
|
5
|
+
[](https://www.npmjs.com/package/@shbernal/ts-xlsx)
|
|
6
|
+
[](https://github.com/shbernal/ts-xlsx/actions/workflows/build.yml)
|
|
7
|
+
[](LICENSE)
|
|
8
|
+
|
|
3
9
|
A TypeScript-first library for reading and writing spreadsheet documents
|
|
4
10
|
(`.xlsx` / OOXML, and CSV) — synchronous, `Uint8Array`-native, and dependency-lean.
|
|
5
11
|
|
package/dist/core/autofilter.js
CHANGED
package/dist/core/cell.js
CHANGED
|
@@ -4,10 +4,10 @@
|
|
|
4
4
|
// *numbers* (legacy shipped a type declaration calling them strings, which broke
|
|
5
5
|
// strict consumers). The value is the only mutable state here; assigning it routes
|
|
6
6
|
// through the value model so the cell's `type` is always consistent with what it holds.
|
|
7
|
-
import { encodeAddress } from
|
|
8
|
-
import { NAMED_STYLE_ID } from
|
|
9
|
-
import { assignStyleFacets, } from
|
|
10
|
-
import { coerceCellValue, detectValueType, } from
|
|
7
|
+
import { encodeAddress } from './address.js';
|
|
8
|
+
import { NAMED_STYLE_ID } from './internal.js';
|
|
9
|
+
import { assignStyleFacets, } from './style.js';
|
|
10
|
+
import { coerceCellValue, detectValueType, } from './value.js';
|
|
11
11
|
/**
|
|
12
12
|
* A single cell owns its value and every style facet outright. Each facet below — fill, number format,
|
|
13
13
|
* font, border, alignment, protection, quote-prefix, and note — is held in the cell's own field and
|
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
// keeps the encoding its file used, so a round-trip re-emits `theme="4" tint="0.4"` rather than
|
|
10
10
|
// rewriting every cell as a literal ARGB — which would bloat the styles table, break the link to the
|
|
11
11
|
// theme (recolouring the workbook would stop working), and change what the file means.
|
|
12
|
-
import { DEFAULT_THEME_COLOR_SCHEME, THEME_COLOR_SLOTS } from
|
|
12
|
+
import { DEFAULT_THEME_COLOR_SCHEME, THEME_COLOR_SLOTS } from './theme.js';
|
|
13
13
|
/**
|
|
14
14
|
* The built-in indexed colour palette (ECMA-376 §18.8.27), by index. Entries 0–7 duplicate 8–15 —
|
|
15
15
|
* redundancy the spec preserves for backwards compatibility with the legacy formats this palette came
|
package/dist/core/column.js
CHANGED
|
@@ -7,8 +7,8 @@
|
|
|
7
7
|
// A column carries more than a row does: besides the geometry (width, visibility, outline) it holds
|
|
8
8
|
// the six `CellStyle` facets as *defaults* for its cells, which is why the mirror below is twice the
|
|
9
9
|
// length of the row's.
|
|
10
|
-
import { encodeAddress, numberToColumn } from
|
|
11
|
-
import { INTERNAL } from
|
|
10
|
+
import { encodeAddress, numberToColumn } from './address.js';
|
|
11
|
+
import { INTERNAL } from './internal.js';
|
|
12
12
|
export class Column {
|
|
13
13
|
#sheet;
|
|
14
14
|
/** 1-based column index. Fixed for this handle's lifetime. */
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// The conditional-formatting overlay a Worksheet owns: an insertion-ordered, defensively-copied list
|
|
2
2
|
// of range-bound rule sets. Kept as its own class, the sibling to {@link DataValidationOverlay}, so
|
|
3
3
|
// Worksheet delegates the collection's storage and cloning rather than managing the array itself.
|
|
4
|
-
import { cloneConditionalFormatting } from
|
|
4
|
+
import { cloneConditionalFormatting } from './conditional-formatting.js';
|
|
5
5
|
export class ConditionalFormattingOverlay {
|
|
6
6
|
#entries = [];
|
|
7
7
|
/**
|
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
// as its own class — the sibling to {@link GridEdits} that owns splice arithmetic — rather than inline
|
|
4
4
|
// on Worksheet, since a validation's storage (a rule plus its decoded ranges) is a self-contained unit
|
|
5
5
|
// Worksheet only ever adds to, reads, or clears wholesale.
|
|
6
|
-
import { cloneDataValidation, } from
|
|
7
|
-
import { decodeSqrefRects } from
|
|
6
|
+
import { cloneDataValidation, } from './data-validation.js';
|
|
7
|
+
import { decodeSqrefRects } from './merge.js';
|
|
8
8
|
export class DataValidationOverlay {
|
|
9
9
|
#entries = [];
|
|
10
10
|
#rects = [];
|
package/dist/core/formula.js
CHANGED
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
// verbatim and hands each code run between them to a transform. `mangleParams` is the deliberate
|
|
21
21
|
// exception: LET/LAMBDA parameter scope opens and closes at paren boundaries, state `scanFormula`'s
|
|
22
22
|
// per-run transform cannot carry, so it runs its own forward walk — still deferring to `skipOpaque`.
|
|
23
|
-
import { columnToNumber, numberToColumn } from
|
|
24
|
-
import { MODERN_FUNCTIONS } from
|
|
23
|
+
import { columnToNumber, numberToColumn } from './address.js';
|
|
24
|
+
import { MODERN_FUNCTIONS } from './modern-functions.js';
|
|
25
25
|
const XLFN = '_xlfn.';
|
|
26
26
|
const XLPM = '_xlpm.';
|
|
27
27
|
/**
|
package/dist/core/grid-edits.js
CHANGED
|
@@ -4,11 +4,11 @@
|
|
|
4
4
|
// mechanics: it holds the sheet's storage containers by reference and mutates them in place, and
|
|
5
5
|
// touches none of the public cell API. Worksheet builds the cells an insert introduces, then hands
|
|
6
6
|
// the pre-built rows (or the raw column values) here for the shift.
|
|
7
|
-
import { decodeAddress, decodeRange, encodeAddress } from
|
|
8
|
-
import { Cell, copyCellContent } from
|
|
9
|
-
import { replaceContents } from
|
|
10
|
-
import { isOneCellAnchor } from
|
|
11
|
-
import { isSharedFormulaValue } from
|
|
7
|
+
import { decodeAddress, decodeRange, encodeAddress } from './address.js';
|
|
8
|
+
import { Cell, copyCellContent } from './cell.js';
|
|
9
|
+
import { replaceContents } from './containers.js';
|
|
10
|
+
import { isOneCellAnchor } from './image.js';
|
|
11
|
+
import { isSharedFormulaValue } from './value.js';
|
|
12
12
|
// The shift rule shared by every re-anchoring pass: a coordinate before the edit stays put, one at or
|
|
13
13
|
// after the edited span shifts by `delta`, and one inside a deleted span clamps to the cut line (`start`).
|
|
14
14
|
function shiftIndex(v, start, count, delta) {
|
package/dist/core/merge.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
// Inclusive grid rectangles and the geometry a worksheet uses to reason about merged regions and the
|
|
2
2
|
// `sqref` ranges that overlays (data validations, conditional formats) apply to: overlap detection and
|
|
3
3
|
// decoding an OOXML `sqref` into containment rectangles.
|
|
4
|
-
import { decodeRange } from
|
|
4
|
+
import { decodeRange } from './address.js';
|
|
5
5
|
/** Whether two inclusive grid rectangles share at least one cell. */
|
|
6
6
|
export function rectsOverlap(a, b) {
|
|
7
7
|
return a.left <= b.right && b.left <= a.right && a.top <= b.bottom && b.top <= a.bottom;
|
package/dist/core/pivot-table.js
CHANGED
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
//
|
|
10
10
|
// The source data is captured when the pivot is added — the model reads the source sheet's cells
|
|
11
11
|
// once, here, so the pivot is a stable snapshot independent of later edits to the source.
|
|
12
|
-
import { AuthoringError, InternalError } from
|
|
13
|
-
import { encodeAddress } from
|
|
14
|
-
import { isErrorValue, isFormulaValue, isHyperlinkValue, isRichTextValue, isSharedFormulaValue, richTextToPlain, } from
|
|
12
|
+
import { AuthoringError, InternalError } from '../errors.js';
|
|
13
|
+
import { encodeAddress } from './address.js';
|
|
14
|
+
import { isErrorValue, isFormulaValue, isHyperlinkValue, isRichTextValue, isSharedFormulaValue, richTextToPlain, } from './value.js';
|
|
15
15
|
const PIVOT_METRICS = new Set([
|
|
16
16
|
'sum',
|
|
17
17
|
'count',
|
package/dist/core/range.js
CHANGED
|
@@ -9,9 +9,9 @@
|
|
|
9
9
|
// million cells, whereas OOXML expresses it natively as a single `<col style>` or `<row s>` — which
|
|
10
10
|
// is what `getColumn`/`getRow` already write, in constant space. Refusing them is what lets a range
|
|
11
11
|
// materialise its cells eagerly without a cost cliff hiding behind an innocuous-looking call.
|
|
12
|
-
import { decodeRange, encodeAddress, MAX_COLUMN, MAX_ROW } from
|
|
13
|
-
import { applyCellStyle } from
|
|
14
|
-
import { CELL_STYLE_FACETS, } from
|
|
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
15
|
/**
|
|
16
16
|
* A rectangular block of a worksheet's cells: `sheet.getRange('B2:D5')`.
|
|
17
17
|
*
|
|
@@ -242,9 +242,13 @@ function setFacet(cell, facet, value) {
|
|
|
242
242
|
// order their keys were written in. Sound here and nowhere near a general deep-equal: every facet is
|
|
243
243
|
// a plain data record of strings, numbers, booleans and nested records — no functions, no cycles, no
|
|
244
244
|
// class instances — which is exactly the shape JSON round-trips faithfully.
|
|
245
|
+
//
|
|
246
|
+
// `undefined` needs a sentinel because `JSON.stringify` answers it with `undefined` rather than a
|
|
247
|
+
// string. NUL is the one that cannot collide: a defined value stringifies to `"`, `{`, `[`, a digit,
|
|
248
|
+
// or a bare `true`/`false`/`null`, and a NUL *inside* a string comes back as a six-char escape.
|
|
245
249
|
function facetKey(value) {
|
|
246
250
|
if (value === undefined)
|
|
247
|
-
return '
|
|
251
|
+
return '\u0000';
|
|
248
252
|
return JSON.stringify(value, (_key, inner) => inner !== null && typeof inner === 'object' && !Array.isArray(inner)
|
|
249
253
|
? Object.fromEntries(Object.entries(inner).sort(([a], [b]) => (a < b ? -1 : 1)))
|
|
250
254
|
: inner);
|
package/dist/core/row.js
CHANGED
|
@@ -12,8 +12,8 @@
|
|
|
12
12
|
//
|
|
13
13
|
// Formatting is created on write, never on read. Asking for `sheet.getRow(500)` costs nothing and
|
|
14
14
|
// does not extend the used range; assigning `height` is what materialises the record.
|
|
15
|
-
import { columnToNumber, encodeAddress } from
|
|
16
|
-
import { INTERNAL } from
|
|
15
|
+
import { columnToNumber, encodeAddress } from './address.js';
|
|
16
|
+
import { INTERNAL } from './internal.js';
|
|
17
17
|
export class Row {
|
|
18
18
|
#sheet;
|
|
19
19
|
/** 1-based row number. Fixed for this handle's lifetime. */
|
|
@@ -8,7 +8,7 @@ import type { DifferentialStyle } from './style.ts';
|
|
|
8
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
|
-
export declare const TABLE_STYLE_ELEMENT_TYPES: readonly [
|
|
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
14
|
/** The four element types banded across several rows or columns — the only ones {@link TableStyleElement.size} means anything on. */
|
|
@@ -61,7 +61,7 @@ export interface TableStyle {
|
|
|
61
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 {
|
|
64
|
+
* @throws {AuthoringError} if the name is empty, or a non-stripe element carries a `size`, or a `size` is not
|
|
65
65
|
* a positive integer.
|
|
66
66
|
*/
|
|
67
67
|
export declare function checkTableStyle(style: TableStyle): void;
|
package/dist/core/table-style.js
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
// gallery ("TableStyleMedium2" and its sixty siblings) is exactly this shape; a workbook that declares
|
|
6
6
|
// its own joins the gallery for that file, and a table reaches it by name through
|
|
7
7
|
// `TableStyleInfo.name`.
|
|
8
|
-
import { AuthoringError } from
|
|
8
|
+
import { AuthoringError } from '../errors.js';
|
|
9
9
|
/**
|
|
10
10
|
* The regions a table style can format (`ST_TableStyleType`).
|
|
11
11
|
*
|
|
@@ -63,7 +63,7 @@ export function isTableStyleElementType(value) {
|
|
|
63
63
|
* `size` outside a stripe is ignored — neither shows up as a repair prompt or a schema error, so the
|
|
64
64
|
* only place to catch them is the call that made them.
|
|
65
65
|
*
|
|
66
|
-
* @throws {
|
|
66
|
+
* @throws {AuthoringError} if the name is empty, or a non-stripe element carries a `size`, or a `size` is not
|
|
67
67
|
* a positive integer.
|
|
68
68
|
*/
|
|
69
69
|
export function checkTableStyle(style) {
|
package/dist/core/table.js
CHANGED
|
@@ -6,8 +6,8 @@
|
|
|
6
6
|
// only), a headerless table (data rows only), or a totals-bearing table. The writer is
|
|
7
7
|
// the OOXML gatekeeper for serialization; this model owns the invariants Excel enforces
|
|
8
8
|
// on the *shape* itself: a legal name, at least one column, and at least one row.
|
|
9
|
-
import { AuthoringError } from
|
|
10
|
-
import { decodeAddress, encodeAddress } from
|
|
9
|
+
import { AuthoringError } from '../errors.js';
|
|
10
|
+
import { decodeAddress, encodeAddress } from './address.js';
|
|
11
11
|
/** Copy a style, keeping only its defined fields off the literal so `exactOptionalPropertyTypes`
|
|
12
12
|
* never sees a fabricated `key: undefined` — an absent attribute must stay absent across a copy.
|
|
13
13
|
*
|
package/dist/core/theme.d.ts
CHANGED
|
@@ -12,7 +12,7 @@
|
|
|
12
12
|
* `test/corpus/fixtures/excel-oracle/theme-color-index-order.json`. The stylesheet's own default font
|
|
13
13
|
* is the everyday witness: it carries `<color theme="1"/>` and renders black, which is `dk1`.
|
|
14
14
|
*/
|
|
15
|
-
export declare const THEME_COLOR_SLOTS: readonly [
|
|
15
|
+
export declare const THEME_COLOR_SLOTS: readonly ['lt1', 'dk1', 'lt2', 'dk2', 'accent1', 'accent2', 'accent3', 'accent4', 'accent5', 'accent6', 'hlink', 'folHlink'];
|
|
16
16
|
/** One slot of a theme's colour scheme. */
|
|
17
17
|
export type ThemeColorSlot = (typeof THEME_COLOR_SLOTS)[number];
|
|
18
18
|
/**
|
package/dist/core/theme.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
// module reads just the `<a:clrScheme>` out of it, because that is the only piece a colour reference
|
|
5
5
|
// needs. Everything else in a theme (the format scheme's gradients, line and effect styles) is
|
|
6
6
|
// nobody's business here.
|
|
7
|
-
import { AuthoringError } from
|
|
7
|
+
import { AuthoringError } from '../errors.js';
|
|
8
8
|
/**
|
|
9
9
|
* The twelve colour-scheme slots **in the order a `theme="n"` attribute indexes them**.
|
|
10
10
|
*
|
package/dist/core/value.d.ts
CHANGED
|
@@ -1,19 +1,19 @@
|
|
|
1
1
|
import type { Font } from './style.ts';
|
|
2
2
|
/** The observable kind of a cell's value. Both formula shapes report as `Formula`. */
|
|
3
3
|
export declare const ValueType: {
|
|
4
|
-
readonly Null:
|
|
5
|
-
readonly Number:
|
|
6
|
-
readonly String:
|
|
7
|
-
readonly Boolean:
|
|
8
|
-
readonly Date:
|
|
9
|
-
readonly Error:
|
|
10
|
-
readonly Formula:
|
|
11
|
-
readonly RichText:
|
|
12
|
-
readonly Hyperlink:
|
|
4
|
+
readonly Null: 'null';
|
|
5
|
+
readonly Number: 'number';
|
|
6
|
+
readonly String: 'string';
|
|
7
|
+
readonly Boolean: 'boolean';
|
|
8
|
+
readonly Date: 'date';
|
|
9
|
+
readonly Error: 'error';
|
|
10
|
+
readonly Formula: 'formula';
|
|
11
|
+
readonly RichText: 'richText';
|
|
12
|
+
readonly Hyperlink: 'hyperlink';
|
|
13
13
|
};
|
|
14
14
|
export type ValueType = (typeof ValueType)[keyof typeof ValueType];
|
|
15
15
|
/** The canonical Excel error literals a cell (or formula result) can carry. */
|
|
16
|
-
export declare const ERROR_CODES: readonly [
|
|
16
|
+
export declare const ERROR_CODES: readonly ['#N/A', '#REF!', '#NAME?', '#DIV/0!', '#NULL!', '#VALUE!', '#NUM!', '#SPILL!', '#CALC!', '#GETTING_DATA'];
|
|
17
17
|
export type ErrorCode = (typeof ERROR_CODES)[number];
|
|
18
18
|
/** An in-cell error, e.g. `{error: '#REF!'}`. */
|
|
19
19
|
export interface ErrorValue {
|
|
@@ -6,7 +6,7 @@
|
|
|
6
6
|
* revisions guard (`revisions*`). The reader accepts only these names, so a hostile or unknown
|
|
7
7
|
* attribute is never echoed back into the output.
|
|
8
8
|
*/
|
|
9
|
-
export declare const WORKBOOK_PROTECTION_CREDENTIAL_ATTRS: readonly [
|
|
9
|
+
export declare const WORKBOOK_PROTECTION_CREDENTIAL_ATTRS: readonly ['workbookPassword', 'workbookAlgorithmName', 'workbookHashValue', 'workbookSaltValue', 'workbookSpinCount', 'revisionsPassword', 'revisionsAlgorithmName', 'revisionsHashValue', 'revisionsSaltValue', 'revisionsSpinCount'];
|
|
10
10
|
/** One of the attribute names {@link WORKBOOK_PROTECTION_CREDENTIAL_ATTRS} enumerates. */
|
|
11
11
|
export type WorkbookProtectionCredentialAttr = (typeof WORKBOOK_PROTECTION_CREDENTIAL_ATTRS)[number];
|
|
12
12
|
/**
|
package/dist/core/workbook.d.ts
CHANGED
|
@@ -173,7 +173,7 @@ export declare class Workbook {
|
|
|
173
173
|
* returned objects changes nothing on write; the original `customUI` XML is re-emitted byte-for-byte
|
|
174
174
|
* regardless. Parsed lazily on first access and memoised.
|
|
175
175
|
*
|
|
176
|
-
* @throws {
|
|
176
|
+
* @throws {CustomUiParseError} if a `customUI` part is present but its XML is malformed.
|
|
177
177
|
*/
|
|
178
178
|
get customUI(): readonly CustomUiDocument[];
|
|
179
179
|
/**
|
|
@@ -182,7 +182,7 @@ export declare class Workbook {
|
|
|
182
182
|
* verbatim — mutating the returned object changes nothing on write; the original macro blob is
|
|
183
183
|
* re-emitted byte-for-byte regardless. Parsed lazily on first access and memoised.
|
|
184
184
|
*
|
|
185
|
-
* @throws {
|
|
185
|
+
* @throws {VbaParseError} if a macro project is present but its `vbaProject.bin` is malformed.
|
|
186
186
|
*/
|
|
187
187
|
get vbaProject(): VbaProject | undefined;
|
|
188
188
|
/**
|
|
@@ -236,9 +236,9 @@ export declare class Workbook {
|
|
|
236
236
|
* To author or edit module *source* (which needs real compiled p-code), use the offline
|
|
237
237
|
* `tools/vba-compiler`, then attach its output via {@link vbaProjectBytes}.
|
|
238
238
|
*
|
|
239
|
-
* @throws {
|
|
239
|
+
* @throws {VbaAuthorError} if the workbook has no macro project, or `name` is not in the project,
|
|
240
240
|
* or names a `document`/`designer` module.
|
|
241
|
-
* @throws {
|
|
241
|
+
* @throws {VbaParseError} if the attached `vbaProject.bin` is malformed.
|
|
242
242
|
*/
|
|
243
243
|
removeVbaModule(name: string): void;
|
|
244
244
|
/**
|
|
@@ -247,9 +247,9 @@ export declare class Workbook {
|
|
|
247
247
|
* {@link addVbaReference}). Replacing the project also drops a stale signature, as
|
|
248
248
|
* {@link vbaProjectBytes} does.
|
|
249
249
|
*
|
|
250
|
-
* @throws {
|
|
250
|
+
* @throws {VbaAuthorError} if the workbook has no macro project, or any field of `ref` is invalid
|
|
251
251
|
* (see {@link VbaLibraryReference}).
|
|
252
|
-
* @throws {
|
|
252
|
+
* @throws {VbaParseError} if the attached `vbaProject.bin` is malformed.
|
|
253
253
|
*/
|
|
254
254
|
addVbaReference(ref: VbaLibraryReference): void;
|
|
255
255
|
/** The preserved differential-style (`<dxfs>`) fragments, in index order. */
|
|
@@ -283,7 +283,7 @@ export declare class Workbook {
|
|
|
283
283
|
* Registering a name a source file already defined **overrides** that definition rather than adding
|
|
284
284
|
* a second one beside it.
|
|
285
285
|
*
|
|
286
|
-
* @throws {
|
|
286
|
+
* @throws {AuthoringError} if the name is empty, or an element carries a `size` outside the four stripe
|
|
287
287
|
* types, or a `size` is not a positive integer — see {@link checkTableStyle} for why those are
|
|
288
288
|
* refused here rather than silently dropped.
|
|
289
289
|
*/
|
|
@@ -310,7 +310,7 @@ export declare class Workbook {
|
|
|
310
310
|
* unauthored keeps the source's own encoding, including the `<a:sysClr>` form Excel uses for
|
|
311
311
|
* `dk1`/`lt1` so they follow the viewer's window colours.
|
|
312
312
|
*
|
|
313
|
-
* @throws {
|
|
313
|
+
* @throws {AuthoringError} if a colour is not 6 or 8 hexadecimal digits.
|
|
314
314
|
*/
|
|
315
315
|
setTheme(overrides: ThemeOverrides): void;
|
|
316
316
|
/**
|
|
@@ -345,7 +345,7 @@ export declare class Workbook {
|
|
|
345
345
|
* follows {@link themeFonts}'s minor face, so `setTheme({fonts: {minor}})` already reaches every
|
|
346
346
|
* unstyled cell and needs no second call here. See {@link defaultFont} for the full chain.
|
|
347
347
|
*
|
|
348
|
-
* @throws {
|
|
348
|
+
* @throws {AuthoringError} if `size` is not a positive finite number, or `name` is empty — both
|
|
349
349
|
* produce a styles part Excel renders from some other font without ever reporting why.
|
|
350
350
|
*/
|
|
351
351
|
setDefaultFont(font: Font): void;
|
|
@@ -431,14 +431,14 @@ export declare class Workbook {
|
|
|
431
431
|
/**
|
|
432
432
|
* Register a defined name on the workbook.
|
|
433
433
|
*
|
|
434
|
-
* @throws {
|
|
434
|
+
* @throws {AuthoringError} if the name is empty, or if a {@link DefinedName.scope} is given that names no
|
|
435
435
|
* existing worksheet — a scoped name must target a sheet that is already part of the workbook.
|
|
436
436
|
*/
|
|
437
437
|
defineName(definedName: DefinedName): void;
|
|
438
438
|
/**
|
|
439
439
|
* Create a worksheet and append it to the workbook.
|
|
440
440
|
*
|
|
441
|
-
* @throws {
|
|
441
|
+
* @throws {AuthoringError} if the name is empty, too long, contains a forbidden character,
|
|
442
442
|
* or collides (case-insensitively) with an existing sheet.
|
|
443
443
|
*/
|
|
444
444
|
addWorksheet(name: string, options?: AddWorksheetOptions): Worksheet;
|
package/dist/core/workbook.js
CHANGED
|
@@ -4,17 +4,17 @@
|
|
|
4
4
|
// Excel's rules — names are unique case-insensitively, bounded in length, and free
|
|
5
5
|
// of the characters Excel forbids — so an invalid book cannot be constructed in the
|
|
6
6
|
// first place, rather than failing only at write time.
|
|
7
|
-
import { isCustomUiRelType, parseCustomUi } from
|
|
8
|
-
import { AuthoringError } from
|
|
9
|
-
import { addVbaReference, parseVbaProject, removeVbaModule, VBA_PROJECT_CONTENT_TYPE, VBA_PROJECT_PART_PATH, VBA_PROJECT_REL_TYPE, VbaAuthorError, vbaProjectSignatureKind, } from
|
|
10
|
-
import { resolveColor } from
|
|
11
|
-
import { commentThreadGuid } from
|
|
12
|
-
import { replaceContents } from
|
|
13
|
-
import { normalizeImageExtension } from
|
|
14
|
-
import { INTERNAL } from
|
|
15
|
-
import { checkTableStyle } from
|
|
16
|
-
import { applyThemeOverrides, DEFAULT_THEME_COLOR_SCHEME, DEFAULT_THEME_FONTS, DEFAULT_THEME_XML, OFFICE_BODY_FACE, parseThemeColorScheme, parseThemeFontScheme, THEME_COLOR_SLOTS, } from
|
|
17
|
-
import { Worksheet } from
|
|
7
|
+
import { isCustomUiRelType, parseCustomUi } from '../customui/index.js';
|
|
8
|
+
import { AuthoringError } from '../errors.js';
|
|
9
|
+
import { addVbaReference, parseVbaProject, removeVbaModule, VBA_PROJECT_CONTENT_TYPE, VBA_PROJECT_PART_PATH, VBA_PROJECT_REL_TYPE, VbaAuthorError, vbaProjectSignatureKind, } from '../vba/index.js';
|
|
10
|
+
import { resolveColor } from './color-resolution.js';
|
|
11
|
+
import { commentThreadGuid } from './comment-thread.js';
|
|
12
|
+
import { replaceContents } from './containers.js';
|
|
13
|
+
import { normalizeImageExtension } from './image.js';
|
|
14
|
+
import { INTERNAL } from './internal.js';
|
|
15
|
+
import { checkTableStyle } from './table-style.js';
|
|
16
|
+
import { applyThemeOverrides, DEFAULT_THEME_COLOR_SCHEME, DEFAULT_THEME_FONTS, DEFAULT_THEME_XML, OFFICE_BODY_FACE, parseThemeColorScheme, parseThemeFontScheme, THEME_COLOR_SLOTS, } from './theme.js';
|
|
17
|
+
import { Worksheet } from './worksheet.js';
|
|
18
18
|
/**
|
|
19
19
|
* The window geometry a workbook starts from — the values desktop Excel writes for its own default
|
|
20
20
|
* window.
|
|
@@ -144,7 +144,7 @@ export class Workbook {
|
|
|
144
144
|
* returned objects changes nothing on write; the original `customUI` XML is re-emitted byte-for-byte
|
|
145
145
|
* regardless. Parsed lazily on first access and memoised.
|
|
146
146
|
*
|
|
147
|
-
* @throws {
|
|
147
|
+
* @throws {CustomUiParseError} if a `customUI` part is present but its XML is malformed.
|
|
148
148
|
*/
|
|
149
149
|
get customUI() {
|
|
150
150
|
if (!this.#customUiParsed) {
|
|
@@ -168,7 +168,7 @@ export class Workbook {
|
|
|
168
168
|
* verbatim — mutating the returned object changes nothing on write; the original macro blob is
|
|
169
169
|
* re-emitted byte-for-byte regardless. Parsed lazily on first access and memoised.
|
|
170
170
|
*
|
|
171
|
-
* @throws {
|
|
171
|
+
* @throws {VbaParseError} if a macro project is present but its `vbaProject.bin` is malformed.
|
|
172
172
|
*/
|
|
173
173
|
get vbaProject() {
|
|
174
174
|
if (!this.#vbaParsed) {
|
|
@@ -280,9 +280,9 @@ export class Workbook {
|
|
|
280
280
|
* To author or edit module *source* (which needs real compiled p-code), use the offline
|
|
281
281
|
* `tools/vba-compiler`, then attach its output via {@link vbaProjectBytes}.
|
|
282
282
|
*
|
|
283
|
-
* @throws {
|
|
283
|
+
* @throws {VbaAuthorError} if the workbook has no macro project, or `name` is not in the project,
|
|
284
284
|
* or names a `document`/`designer` module.
|
|
285
|
-
* @throws {
|
|
285
|
+
* @throws {VbaParseError} if the attached `vbaProject.bin` is malformed.
|
|
286
286
|
*/
|
|
287
287
|
removeVbaModule(name) {
|
|
288
288
|
const bytes = this.vbaProjectBytes;
|
|
@@ -297,9 +297,9 @@ export class Workbook {
|
|
|
297
297
|
* {@link addVbaReference}). Replacing the project also drops a stale signature, as
|
|
298
298
|
* {@link vbaProjectBytes} does.
|
|
299
299
|
*
|
|
300
|
-
* @throws {
|
|
300
|
+
* @throws {VbaAuthorError} if the workbook has no macro project, or any field of `ref` is invalid
|
|
301
301
|
* (see {@link VbaLibraryReference}).
|
|
302
|
-
* @throws {
|
|
302
|
+
* @throws {VbaParseError} if the attached `vbaProject.bin` is malformed.
|
|
303
303
|
*/
|
|
304
304
|
addVbaReference(ref) {
|
|
305
305
|
const bytes = this.vbaProjectBytes;
|
|
@@ -357,7 +357,7 @@ export class Workbook {
|
|
|
357
357
|
* Registering a name a source file already defined **overrides** that definition rather than adding
|
|
358
358
|
* a second one beside it.
|
|
359
359
|
*
|
|
360
|
-
* @throws {
|
|
360
|
+
* @throws {AuthoringError} if the name is empty, or an element carries a `size` outside the four stripe
|
|
361
361
|
* types, or a `size` is not a positive integer — see {@link checkTableStyle} for why those are
|
|
362
362
|
* refused here rather than silently dropped.
|
|
363
363
|
*/
|
|
@@ -400,7 +400,7 @@ export class Workbook {
|
|
|
400
400
|
* unauthored keeps the source's own encoding, including the `<a:sysClr>` form Excel uses for
|
|
401
401
|
* `dk1`/`lt1` so they follow the viewer's window colours.
|
|
402
402
|
*
|
|
403
|
-
* @throws {
|
|
403
|
+
* @throws {AuthoringError} if a colour is not 6 or 8 hexadecimal digits.
|
|
404
404
|
*/
|
|
405
405
|
setTheme(overrides) {
|
|
406
406
|
// Validated eagerly, by running the generation the writer will later run: a colour rejected at
|
|
@@ -468,7 +468,7 @@ export class Workbook {
|
|
|
468
468
|
* follows {@link themeFonts}'s minor face, so `setTheme({fonts: {minor}})` already reaches every
|
|
469
469
|
* unstyled cell and needs no second call here. See {@link defaultFont} for the full chain.
|
|
470
470
|
*
|
|
471
|
-
* @throws {
|
|
471
|
+
* @throws {AuthoringError} if `size` is not a positive finite number, or `name` is empty — both
|
|
472
472
|
* produce a styles part Excel renders from some other font without ever reporting why.
|
|
473
473
|
*/
|
|
474
474
|
setDefaultFont(font) {
|
|
@@ -641,7 +641,7 @@ export class Workbook {
|
|
|
641
641
|
/**
|
|
642
642
|
* Register a defined name on the workbook.
|
|
643
643
|
*
|
|
644
|
-
* @throws {
|
|
644
|
+
* @throws {AuthoringError} if the name is empty, or if a {@link DefinedName.scope} is given that names no
|
|
645
645
|
* existing worksheet — a scoped name must target a sheet that is already part of the workbook.
|
|
646
646
|
*/
|
|
647
647
|
defineName(definedName) {
|
|
@@ -656,7 +656,7 @@ export class Workbook {
|
|
|
656
656
|
/**
|
|
657
657
|
* Create a worksheet and append it to the workbook.
|
|
658
658
|
*
|
|
659
|
-
* @throws {
|
|
659
|
+
* @throws {AuthoringError} if the name is empty, too long, contains a forbidden character,
|
|
660
660
|
* or collides (case-insensitively) with an existing sheet.
|
|
661
661
|
*/
|
|
662
662
|
addWorksheet(name, options = {}) {
|
|
@@ -22,7 +22,7 @@ interface ModelFacet<K extends keyof WorksheetModel = keyof WorksheetModel> {
|
|
|
22
22
|
* load-bearing: cells are placed at their exact positions before any merge exists, so a covered
|
|
23
23
|
* cell's value lands where the model says instead of being routed to a region master mid-load.
|
|
24
24
|
*/
|
|
25
|
-
export declare const WORKSHEET_MODEL_FACETS: (ModelFacet<"
|
|
25
|
+
export declare const WORKSHEET_MODEL_FACETS: (ModelFacet<"autoFilter"> | ModelFacet<"cells"> | ModelFacet<"columnBreaks"> | ModelFacet<"columns"> | ModelFacet<"conditionalFormattings"> | ModelFacet<"dataValidations"> | ModelFacet<"headerFooter"> | ModelFacet<"merges"> | ModelFacet<"outline"> | ModelFacet<"pageMargins"> | ModelFacet<"pageSetup"> | ModelFacet<"printOptions"> | ModelFacet<"properties"> | ModelFacet<"protection"> | ModelFacet<"rowBreaks"> | ModelFacet<"rows"> | ModelFacet<"state"> | ModelFacet<"tabColor"> | ModelFacet<"tables">)[];
|
|
26
26
|
type AssertNever<T extends never> = T;
|
|
27
27
|
/**
|
|
28
28
|
* Compile-time proof that {@link WORKSHEET_MODEL_FACETS} covers every {@link WorksheetModel} field.
|
|
@@ -6,11 +6,11 @@
|
|
|
6
6
|
// model contract exists to prevent. Here each field declares both directions in one place, and the
|
|
7
7
|
// registry is proved exhaustive over `keyof WorksheetModel` at compile time, so adding a field
|
|
8
8
|
// without wiring it is an error naming the field rather than a review catch.
|
|
9
|
-
import { cellToModel, copyCellContent } from
|
|
10
|
-
import { cloneConditionalFormatting } from
|
|
11
|
-
import { overwrite, replaceContents } from
|
|
12
|
-
import { cloneDataValidation } from
|
|
13
|
-
import { INTERNAL } from
|
|
9
|
+
import { cellToModel, copyCellContent } from './cell.js';
|
|
10
|
+
import { cloneConditionalFormatting } from './conditional-formatting.js';
|
|
11
|
+
import { overwrite, replaceContents } from './containers.js';
|
|
12
|
+
import { cloneDataValidation } from './data-validation.js';
|
|
13
|
+
import { INTERNAL } from './internal.js';
|
|
14
14
|
function facet(key, read, write) {
|
|
15
15
|
return { key, read, write: (sheet, model) => write(sheet, model[key]) };
|
|
16
16
|
}
|
package/dist/core/worksheet.d.ts
CHANGED
|
@@ -275,7 +275,7 @@ export declare class Worksheet {
|
|
|
275
275
|
* name, at least one column, at least one row) are enforced here; conflicts with the
|
|
276
276
|
* rest of the sheet (e.g. an overlapping merge) are the writer's concern.
|
|
277
277
|
*
|
|
278
|
-
* @throws {
|
|
278
|
+
* @throws {AuthoringError} if the name, columns, or geometry are invalid.
|
|
279
279
|
*/
|
|
280
280
|
addTable(options: TableOptions): Table;
|
|
281
281
|
/** The tables defined on this sheet, in definition order. */
|
|
@@ -288,7 +288,7 @@ export declare class Worksheet {
|
|
|
288
288
|
* read once, now, so the pivot is a snapshot: later edits to the source do not change it. The
|
|
289
289
|
* supported shape (one summed value field, at least one row and column field) is enforced here.
|
|
290
290
|
*
|
|
291
|
-
* @throws {
|
|
291
|
+
* @throws {AuthoringError} if the metric, fields, or source shape are unsupported.
|
|
292
292
|
*/
|
|
293
293
|
addPivotTable(options: PivotTableOptions): PivotTable;
|
|
294
294
|
/** The pivot tables hosted on this sheet, in definition order. */
|
package/dist/core/worksheet.js
CHANGED
|
@@ -5,25 +5,25 @@
|
|
|
5
5
|
// row metadata (widths, heights, visibility, outline grouping) are stored apart from
|
|
6
6
|
// the cell grid, because a column or row can carry formatting while holding no cells.
|
|
7
7
|
// Merges and views layer on in later slices.
|
|
8
|
-
import { AuthoringError } from
|
|
9
|
-
import { decodeAddress, decodeRange, encodeAddress } from
|
|
10
|
-
import { canonicalizeAutoFilter } from
|
|
11
|
-
import { applyCellStyle, Cell, copyCellContent } from
|
|
12
|
-
import { Column } from
|
|
13
|
-
import { commentThreadGuid, commentThreadOffset } from
|
|
14
|
-
import { ConditionalFormattingOverlay } from
|
|
15
|
-
import { replaceContents } from
|
|
16
|
-
import { DataValidationOverlay } from
|
|
17
|
-
import { GridEdits } from
|
|
18
|
-
import { PX_TO_EMU, resolveAnchorPoint, } from
|
|
19
|
-
import { INTERNAL } from
|
|
20
|
-
import { rectsOverlap } from
|
|
21
|
-
import { PivotTable } from
|
|
22
|
-
import { deriveCredential, } from
|
|
23
|
-
import { Range, rangeFrom } from
|
|
24
|
-
import { Row } from
|
|
25
|
-
import { Table, TOTALS_ROW_SUBTOTAL_CODE } from
|
|
26
|
-
import { WORKSHEET_MODEL_FACETS } from
|
|
8
|
+
import { AuthoringError } from '../errors.js';
|
|
9
|
+
import { decodeAddress, decodeRange, encodeAddress } from './address.js';
|
|
10
|
+
import { canonicalizeAutoFilter } from './autofilter.js';
|
|
11
|
+
import { applyCellStyle, Cell, copyCellContent } from './cell.js';
|
|
12
|
+
import { Column } from './column.js';
|
|
13
|
+
import { commentThreadGuid, commentThreadOffset } from './comment-thread.js';
|
|
14
|
+
import { ConditionalFormattingOverlay } from './conditional-formatting-overlay.js';
|
|
15
|
+
import { replaceContents } from './containers.js';
|
|
16
|
+
import { DataValidationOverlay } from './data-validation-overlay.js';
|
|
17
|
+
import { GridEdits } from './grid-edits.js';
|
|
18
|
+
import { PX_TO_EMU, resolveAnchorPoint, } from './image.js';
|
|
19
|
+
import { INTERNAL } from './internal.js';
|
|
20
|
+
import { rectsOverlap } from './merge.js';
|
|
21
|
+
import { PivotTable } from './pivot-table.js';
|
|
22
|
+
import { deriveCredential, } from './protection.js';
|
|
23
|
+
import { Range, rangeFrom } from './range.js';
|
|
24
|
+
import { Row } from './row.js';
|
|
25
|
+
import { Table, TOTALS_ROW_SUBTOTAL_CODE } from './table.js';
|
|
26
|
+
import { WORKSHEET_MODEL_FACETS } from './worksheet-model.js';
|
|
27
27
|
export class Worksheet {
|
|
28
28
|
name;
|
|
29
29
|
/** 1-based workbook-assigned id, stable for the sheet's lifetime. */
|
|
@@ -285,7 +285,7 @@ export class Worksheet {
|
|
|
285
285
|
* name, at least one column, at least one row) are enforced here; conflicts with the
|
|
286
286
|
* rest of the sheet (e.g. an overlapping merge) are the writer's concern.
|
|
287
287
|
*
|
|
288
|
-
* @throws {
|
|
288
|
+
* @throws {AuthoringError} if the name, columns, or geometry are invalid.
|
|
289
289
|
*/
|
|
290
290
|
addTable(options) {
|
|
291
291
|
const table = new Table(options, (row, col, value, style) => {
|
|
@@ -372,7 +372,7 @@ export class Worksheet {
|
|
|
372
372
|
* read once, now, so the pivot is a snapshot: later edits to the source do not change it. The
|
|
373
373
|
* supported shape (one summed value field, at least one row and column field) is enforced here.
|
|
374
374
|
*
|
|
375
|
-
* @throws {
|
|
375
|
+
* @throws {AuthoringError} if the metric, fields, or source shape are unsupported.
|
|
376
376
|
*/
|
|
377
377
|
addPivotTable(options) {
|
|
378
378
|
const pivot = new PivotTable(options);
|