@office-kit/xlsx 0.11.1 → 0.12.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +4 -1
- package/dist/cell/cell.d.ts +44 -9
- package/dist/cell/index.d.ts +1 -1
- package/dist/{cell-BucgGwl3.mjs → cell-Df3cKRIl.mjs} +72 -30
- package/dist/cell-Df3cKRIl.mjs.map +1 -0
- package/dist/{cell-style-DuvS_Lsg.mjs → cell-style-BFmJOmcx.mjs} +130 -93
- package/dist/cell-style-BFmJOmcx.mjs.map +1 -0
- package/dist/cell.mjs +2 -2
- package/dist/{coordinate-96Ecci4d.mjs → coordinate-Bt5Gni2I.mjs} +19 -8
- package/dist/coordinate-Bt5Gni2I.mjs.map +1 -0
- package/dist/{defined-names-CviWmtQg.mjs → defined-names-Ctu3F6ls.mjs} +4 -3
- package/dist/defined-names-Ctu3F6ls.mjs.map +1 -0
- package/dist/{differential-DvKU8fJt.mjs → differential-H9SjEeIU.mjs} +2 -2
- package/dist/{differential-DvKU8fJt.mjs.map → differential-H9SjEeIU.mjs.map} +1 -1
- package/dist/{drawing-BxzLuryn.mjs → drawing-ZJ3h4VHD.mjs} +2 -2
- package/dist/{drawing-BxzLuryn.mjs.map → drawing-ZJ3h4VHD.mjs.map} +1 -1
- package/dist/drawing.mjs +1 -1
- package/dist/{inference-BrI9uIY4.mjs → inference-BMvv9cUY.mjs} +18 -3
- package/dist/inference-BMvv9cUY.mjs.map +1 -0
- package/dist/io.mjs +2 -2
- package/dist/{load-Bghb1YHY.mjs → load-CJdGh50R.mjs} +6 -6
- package/dist/{load-Bghb1YHY.mjs.map → load-CJdGh50R.mjs.map} +1 -1
- package/dist/node.mjs +1 -1
- package/dist/{save-Bqcf8w3g.mjs → save-a0InIbtJ.mjs} +7 -5
- package/dist/save-a0InIbtJ.mjs.map +1 -0
- package/dist/streaming/write-only.d.ts +7 -13
- package/dist/streaming.mjs +16 -46
- package/dist/streaming.mjs.map +1 -1
- package/dist/styles/cell-style.d.ts +78 -32
- package/dist/styles/fonts.d.ts +10 -1
- package/dist/styles/index.d.ts +3 -3
- package/dist/styles/stylesheet.d.ts +26 -0
- package/dist/styles.mjs +4 -4
- package/dist/{stylesheet-writer-HxaX7bGy.mjs → stylesheet-writer-D7Uug85X.mjs} +31 -18
- package/dist/stylesheet-writer-D7Uug85X.mjs.map +1 -0
- package/dist/{table-1P8we2gS.mjs → table-Ccro4rrz.mjs} +11 -9
- package/dist/table-Ccro4rrz.mjs.map +1 -0
- package/dist/utils/coordinate.d.ts +30 -4
- package/dist/utils/formula-text.d.ts +7 -0
- package/dist/utils/index.d.ts +1 -1
- package/dist/utils.mjs +2 -2
- package/dist/workbook/calc-properties.d.ts +8 -1
- package/dist/workbook/index.d.ts +1 -1
- package/dist/{workbook-BlZVXiyG.mjs → workbook-B15-T4cs.mjs} +6 -6
- package/dist/{workbook-BlZVXiyG.mjs.map → workbook-B15-T4cs.mjs.map} +1 -1
- package/dist/workbook.mjs +45 -3
- package/dist/workbook.mjs.map +1 -1
- package/dist/worksheet/cell-range.d.ts +10 -3
- package/dist/worksheet/data-validations.d.ts +1 -1
- package/dist/worksheet/index.d.ts +3 -3
- package/dist/worksheet/table-validate.d.ts +7 -0
- package/dist/worksheet/table.d.ts +4 -1
- package/dist/worksheet/views.d.ts +5 -0
- package/dist/worksheet/worksheet.d.ts +106 -65
- package/dist/worksheet/writer.d.ts +7 -0
- package/dist/{worksheet-C5Ensf_j.mjs → worksheet-4xW-i8j9.mjs} +235 -51
- package/dist/worksheet-4xW-i8j9.mjs.map +1 -0
- package/dist/worksheet.mjs +3 -3
- package/package.json +1 -1
- package/dist/cell-BucgGwl3.mjs.map +0 -1
- package/dist/cell-style-DuvS_Lsg.mjs.map +0 -1
- package/dist/coordinate-96Ecci4d.mjs.map +0 -1
- package/dist/defined-names-CviWmtQg.mjs.map +0 -1
- package/dist/inference-BrI9uIY4.mjs.map +0 -1
- package/dist/save-Bqcf8w3g.mjs.map +0 -1
- package/dist/stylesheet-writer-HxaX7bGy.mjs.map +0 -1
- package/dist/table-1P8we2gS.mjs.map +0 -1
- package/dist/worksheet-C5Ensf_j.mjs.map +0 -1
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { o as OpenXmlSchemaError } from "./exceptions-D-CFwxgm.mjs";
|
|
2
|
-
import { a as columnLetterFromIndex, g as tupleToCoordinate, h as rangeBoundaries, i as columnIndexFromLetter, n as MAX_ROW, r as boundariesToRangeString, s as coordinateToTuple, t as MAX_COL } from "./coordinate-
|
|
3
|
-
import { o as cellValueAsString,
|
|
2
|
+
import { a as columnLetterFromIndex, g as tupleToCoordinate, h as rangeBoundaries, i as columnIndexFromLetter, n as MAX_ROW, r as boundariesToRangeString, s as coordinateToTuple, t as MAX_COL } from "./coordinate-Bt5Gni2I.mjs";
|
|
3
|
+
import { N as richTextToString, b as makeCell, h as isFormulaValue, o as cellValueAsString, p as isErrorValue, v as isRichTextValue } from "./cell-Df3cKRIl.mjs";
|
|
4
4
|
//#region src/styles/colors.ts
|
|
5
5
|
/**
|
|
6
6
|
* Legacy 64-entry palette indexed colours fall back to. Verbatim from
|
|
@@ -311,9 +311,17 @@ function makeCellRange(minRow, minCol, maxRow, maxCol) {
|
|
|
311
311
|
maxCol: Math.max(minCol, maxCol)
|
|
312
312
|
};
|
|
313
313
|
}
|
|
314
|
-
/**
|
|
314
|
+
/**
|
|
315
|
+
* Resolve a {@link RangeRef} to numeric bounds: A1 expressions go through
|
|
316
|
+
* {@link rangeBoundaries}, pre-computed bounds through {@link makeCellRange}.
|
|
317
|
+
* Both paths validate against the grid and normalise inverted bounds, so
|
|
318
|
+
* `{ minRow: 5, maxRow: 1 }` behaves like `"A5:A1"` rather than iterating zero
|
|
319
|
+
* rows, and a fractional or off-grid bound throws here instead of half-way
|
|
320
|
+
* through the caller's loop.
|
|
321
|
+
*/
|
|
315
322
|
function parseRange(input) {
|
|
316
|
-
return rangeBoundaries(input);
|
|
323
|
+
if (typeof input === "string") return rangeBoundaries(input);
|
|
324
|
+
return makeCellRange(input.minRow, input.minCol, input.maxRow, input.maxCol);
|
|
317
325
|
}
|
|
318
326
|
/** Format a CellRange back into the canonical OOXML string. */
|
|
319
327
|
function rangeToString(r) {
|
|
@@ -566,6 +574,104 @@ function makeHyperlink(opts) {
|
|
|
566
574
|
};
|
|
567
575
|
}
|
|
568
576
|
//#endregion
|
|
577
|
+
//#region src/worksheet/table-validate.ts
|
|
578
|
+
const TWO_CORNER_REF_RE = /^[A-Za-z]{1,3}[1-9][0-9]*(:[A-Za-z]{1,3}[1-9][0-9]*)?$/;
|
|
579
|
+
/**
|
|
580
|
+
* Excel stores table header text as a string, so only a string, rich text or a
|
|
581
|
+
* formula caching a string can be paired with a column name. Reading a number
|
|
582
|
+
* or a date as its text form would mean guessing at the number format the
|
|
583
|
+
* header carries, and a wrong guess either blesses a file Excel repairs or
|
|
584
|
+
* rejects one it accepts.
|
|
585
|
+
*/
|
|
586
|
+
const readHeaderCell = (value) => {
|
|
587
|
+
if (value === null) return { kind: "empty" };
|
|
588
|
+
if (typeof value === "string") return value === "" ? { kind: "empty" } : {
|
|
589
|
+
kind: "text",
|
|
590
|
+
text: value
|
|
591
|
+
};
|
|
592
|
+
if (isRichTextValue(value)) {
|
|
593
|
+
const text = richTextToString(value.runs);
|
|
594
|
+
return text === "" ? { kind: "empty" } : {
|
|
595
|
+
kind: "text",
|
|
596
|
+
text
|
|
597
|
+
};
|
|
598
|
+
}
|
|
599
|
+
if (isFormulaValue(value)) {
|
|
600
|
+
const cached = value.cachedValue;
|
|
601
|
+
if (cached === void 0) return {
|
|
602
|
+
kind: "nonText",
|
|
603
|
+
held: "holds a formula with no cached value"
|
|
604
|
+
};
|
|
605
|
+
if (typeof cached !== "string") return {
|
|
606
|
+
kind: "nonText",
|
|
607
|
+
held: "holds a formula whose cached value is not text"
|
|
608
|
+
};
|
|
609
|
+
return cached === "" ? { kind: "empty" } : {
|
|
610
|
+
kind: "text",
|
|
611
|
+
text: cached
|
|
612
|
+
};
|
|
613
|
+
}
|
|
614
|
+
if (typeof value === "number") return {
|
|
615
|
+
kind: "nonText",
|
|
616
|
+
held: `holds the number ${value}`
|
|
617
|
+
};
|
|
618
|
+
if (typeof value === "boolean") return {
|
|
619
|
+
kind: "nonText",
|
|
620
|
+
held: `holds the boolean ${value}`
|
|
621
|
+
};
|
|
622
|
+
if (value instanceof Date) return {
|
|
623
|
+
kind: "nonText",
|
|
624
|
+
held: "holds a date"
|
|
625
|
+
};
|
|
626
|
+
if (isErrorValue(value)) return {
|
|
627
|
+
kind: "nonText",
|
|
628
|
+
held: `holds the error ${value.code}`
|
|
629
|
+
};
|
|
630
|
+
return {
|
|
631
|
+
kind: "nonText",
|
|
632
|
+
held: "holds a duration"
|
|
633
|
+
};
|
|
634
|
+
};
|
|
635
|
+
const TEXT_REMEDY = "Write the header row before adding the table, or pass headerRowCount: 0 for a header-less table.";
|
|
636
|
+
const NON_TEXT_REMEDY = "Excel stores header text as a string, so the header cell has to hold text.";
|
|
637
|
+
/**
|
|
638
|
+
* Reject a table whose declared geometry or column names disagree with the
|
|
639
|
+
* sheet underneath it.
|
|
640
|
+
*/
|
|
641
|
+
const validateTableAgainstSheet = (ws, table) => {
|
|
642
|
+
const where = `table "${table.displayName}"`;
|
|
643
|
+
const ref = table.ref.trim();
|
|
644
|
+
if (!TWO_CORNER_REF_RE.test(ref)) throw new OpenXmlSchemaError(`${where}: ref "${table.ref}" is not a plain two-corner cell range like "A1:C4"`);
|
|
645
|
+
const bounds = rangeBoundaries(ref);
|
|
646
|
+
const { columns } = table;
|
|
647
|
+
const width = bounds.maxCol - bounds.minCol + 1;
|
|
648
|
+
if (columns.length !== width) throw new OpenXmlSchemaError(`${where}: ref "${table.ref}" spans ${width} column(s) but ${columns.length} column(s) were supplied`);
|
|
649
|
+
const headerRows = table.headerRowCount ?? 1;
|
|
650
|
+
const totalsRows = table.totalsRowCount ?? 0;
|
|
651
|
+
for (const field of ["headerRowCount", "totalsRowCount"]) {
|
|
652
|
+
const count = table[field];
|
|
653
|
+
if (count !== void 0 && (!Number.isInteger(count) || count < 0 || count > 4294967295)) throw new OpenXmlSchemaError(`${where}: ${field} must be an unsigned 32-bit integer, got ${count}`);
|
|
654
|
+
}
|
|
655
|
+
const height = bounds.maxRow - bounds.minRow + 1;
|
|
656
|
+
if (height < headerRows + totalsRows) throw new OpenXmlSchemaError(`${where}: ref "${table.ref}" is ${height} row(s) tall, which cannot contain ${headerRows} header row(s) and ${totalsRows} totals row(s)`);
|
|
657
|
+
const seen = /* @__PURE__ */ new Set();
|
|
658
|
+
for (const [i, column] of columns.entries()) {
|
|
659
|
+
if (column.name === "") throw new OpenXmlSchemaError(`${where}: column ${i + 1} has no name; every table column needs one`);
|
|
660
|
+
const key = column.name.toLowerCase();
|
|
661
|
+
if (seen.has(key)) throw new OpenXmlSchemaError(`${where}: column ${i + 1} repeats the name "${column.name}"; table column names have to be unique`);
|
|
662
|
+
seen.add(key);
|
|
663
|
+
}
|
|
664
|
+
if (headerRows === 0) return;
|
|
665
|
+
const headerRow = ws.rows.get(bounds.minRow);
|
|
666
|
+
for (const [i, column] of columns.entries()) {
|
|
667
|
+
const col = bounds.minCol + i;
|
|
668
|
+
const header = readHeaderCell(headerRow?.get(col)?.value ?? null);
|
|
669
|
+
if (header.kind === "text" && header.text === column.name) continue;
|
|
670
|
+
const held = header.kind === "text" ? `holds "${header.text}"` : header.kind === "empty" ? "is empty" : header.held;
|
|
671
|
+
throw new OpenXmlSchemaError(`${where}: header cell ${tupleToCoordinate(col, bounds.minRow)} ${held} but column ${i + 1} is named "${column.name}". ${header.kind === "nonText" ? NON_TEXT_REMEDY : TEXT_REMEDY}`);
|
|
672
|
+
}
|
|
673
|
+
};
|
|
674
|
+
//#endregion
|
|
569
675
|
//#region src/worksheet/worksheet.ts
|
|
570
676
|
/** Build a Worksheet shell. */
|
|
571
677
|
function makeWorksheet(title) {
|
|
@@ -605,10 +711,17 @@ function getCell(ws, row, col) {
|
|
|
605
711
|
return ws.rows.get(row)?.get(col);
|
|
606
712
|
}
|
|
607
713
|
/**
|
|
608
|
-
*
|
|
609
|
-
*
|
|
714
|
+
* Write a Cell at (row, col), creating it when the coordinate is empty.
|
|
715
|
+
* `value` always lands on the cell, so an existing value is replaced. Use
|
|
716
|
+
* {@link ensureCell} to reach a cell without writing to it. An existing cell
|
|
717
|
+
* keeps its styleId / hyperlinkId / commentId unless `styleId` is passed.
|
|
718
|
+
*
|
|
719
|
+
* `null` is the explicit empty value: it clears the value and leaves the cell
|
|
720
|
+
* in the sheet with its fill, border and number format, the way Excel's
|
|
721
|
+
* Delete key does. {@link deleteCell} drops the cell entirely, formatting
|
|
722
|
+
* included, and {@link clearRange} does the same across a rectangle.
|
|
610
723
|
*/
|
|
611
|
-
function setCell(ws, row, col, value
|
|
724
|
+
function setCell(ws, row, col, value, styleId) {
|
|
612
725
|
let rowMap = ws.rows.get(row);
|
|
613
726
|
let cell = rowMap?.get(col);
|
|
614
727
|
if (cell === void 0) {
|
|
@@ -625,6 +738,22 @@ function setCell(ws, row, col, value = null, styleId) {
|
|
|
625
738
|
if (row > ws._appendRowCursor) ws._appendRowCursor = row;
|
|
626
739
|
return cell;
|
|
627
740
|
}
|
|
741
|
+
/**
|
|
742
|
+
* Get the Cell at (row, col), allocating an empty one when the coordinate is
|
|
743
|
+
* not populated yet. An existing cell is returned untouched, value and all,
|
|
744
|
+
* which makes this the way to reach a cell you are about to style or attach a
|
|
745
|
+
* formula to.
|
|
746
|
+
*
|
|
747
|
+
* {@link mergeCells} drops the cells underneath a merge; reaching one of those
|
|
748
|
+
* coordinates allocates it again, and the written `<sheetData>` then carries a
|
|
749
|
+
* blank `<c>` under the merge. A merged block's value lives on its top-left
|
|
750
|
+
* cell, so address that coordinate when the block is what you mean.
|
|
751
|
+
*/
|
|
752
|
+
function ensureCell(ws, row, col) {
|
|
753
|
+
const existing = ws.rows.get(row)?.get(col);
|
|
754
|
+
if (existing !== void 0) return existing;
|
|
755
|
+
return setCell(ws, row, col, null);
|
|
756
|
+
}
|
|
628
757
|
/** Delete a single cell from the sheet. Empty rows are pruned. */
|
|
629
758
|
function deleteCell(ws, row, col) {
|
|
630
759
|
const rowMap = ws.rows.get(row);
|
|
@@ -663,34 +792,44 @@ function clearAllCells(ws) {
|
|
|
663
792
|
/**
|
|
664
793
|
* Append a row of values starting at the next empty row. Returns the row index
|
|
665
794
|
* (1-based). Mirrors openpyxl's `Worksheet.append`. `null` / `undefined`
|
|
666
|
-
* entries leave the cell empty.
|
|
795
|
+
* entries leave the cell empty unless `opts.styleIds` names a style for that
|
|
796
|
+
* column.
|
|
667
797
|
*/
|
|
668
|
-
function appendRow(ws, values) {
|
|
798
|
+
function appendRow(ws, values, opts = {}) {
|
|
669
799
|
const row = ws._appendRowCursor + 1;
|
|
670
|
-
|
|
800
|
+
const styleIds = opts.styleIds;
|
|
801
|
+
const width = styleIds === void 0 ? values.length : Math.max(values.length, styleIds.length);
|
|
802
|
+
for (let i = 0; i < width; i++) {
|
|
671
803
|
const value = values[i];
|
|
672
|
-
|
|
673
|
-
|
|
804
|
+
const styleId = styleIds?.[i];
|
|
805
|
+
if ((value === void 0 || value === null) && styleId === void 0) continue;
|
|
806
|
+
setCell(ws, row, i + 1, value ?? null, styleId);
|
|
674
807
|
}
|
|
675
808
|
ws._appendRowCursor = row;
|
|
676
809
|
return row;
|
|
677
810
|
}
|
|
678
811
|
/**
|
|
679
812
|
* Bulk version of {@link appendRow}: append a 2D array of values one row at a
|
|
680
|
-
* time. Returns `{firstRow, lastRow}
|
|
681
|
-
* returns `{firstRow, lastRow: firstRow - 1}` so callers can detect the
|
|
682
|
-
* without throwing.
|
|
813
|
+
* time. Returns `{firstRow, lastRow}`, both 1-based and inclusive. An empty
|
|
814
|
+
* input returns `{firstRow, lastRow: firstRow - 1}` so callers can detect the
|
|
815
|
+
* no-op without throwing.
|
|
683
816
|
*
|
|
684
817
|
* Common usage: `appendRows(ws, csvParsedRows)` for fast import.
|
|
818
|
+
*
|
|
819
|
+
* `opts` is column-indexed, not row-indexed: the same
|
|
820
|
+
* {@link AppendRowOptions.styleIds} apply to every row, which is the point when
|
|
821
|
+
* a column has one format down the whole table. Rows shorter than `styleIds`
|
|
822
|
+
* still get the trailing styled blanks described there, so trim per row when
|
|
823
|
+
* the input is ragged.
|
|
685
824
|
*/
|
|
686
|
-
function appendRows(ws, rows) {
|
|
825
|
+
function appendRows(ws, rows, opts = {}) {
|
|
687
826
|
const firstRow = ws._appendRowCursor + 1;
|
|
688
827
|
if (rows.length === 0) return {
|
|
689
828
|
firstRow,
|
|
690
829
|
lastRow: firstRow - 1
|
|
691
830
|
};
|
|
692
831
|
let lastRow = firstRow - 1;
|
|
693
|
-
for (const row of rows) lastRow = appendRow(ws, row);
|
|
832
|
+
for (const row of rows) lastRow = appendRow(ws, row, opts);
|
|
694
833
|
return {
|
|
695
834
|
firstRow,
|
|
696
835
|
lastRow
|
|
@@ -712,7 +851,9 @@ function appendRows(ws, rows) {
|
|
|
712
851
|
*/
|
|
713
852
|
function writeRange(ws, startRef, values) {
|
|
714
853
|
if (values.length === 0) return void 0;
|
|
715
|
-
const
|
|
854
|
+
const anchor = typeof startRef === "string" ? coordinateToTuple(startRef) : startRef;
|
|
855
|
+
if (typeof startRef !== "string") validateRowCol(anchor.row, anchor.col);
|
|
856
|
+
const { col: startCol, row: startRow } = anchor;
|
|
716
857
|
let maxRow = startRow;
|
|
717
858
|
let maxCol = startCol;
|
|
718
859
|
for (let i = 0; i < values.length; i++) {
|
|
@@ -950,30 +1091,53 @@ function* getCellsInRange(ws, range) {
|
|
|
950
1091
|
}
|
|
951
1092
|
}
|
|
952
1093
|
}
|
|
953
|
-
|
|
1094
|
+
const PLAIN_COORD_RE = /^([A-Za-z]{1,3})([1-9][0-9]*)$/;
|
|
1095
|
+
const plainCoordToRowCol = (coord) => {
|
|
1096
|
+
const m = PLAIN_COORD_RE.exec(coord);
|
|
1097
|
+
if (m === null || m[1] === void 0 || m[2] === void 0) return void 0;
|
|
1098
|
+
return {
|
|
1099
|
+
row: Number.parseInt(m[2], 10),
|
|
1100
|
+
col: columnIndexFromLetter(m[1])
|
|
1101
|
+
};
|
|
1102
|
+
};
|
|
1103
|
+
/**
|
|
1104
|
+
* A1-addressed {@link setCell}: resolves `coord` to a numeric (row, col) and
|
|
1105
|
+
* writes `value` there. Throws `OpenXmlSchemaError` when `coord` is not a
|
|
1106
|
+
* plain A1 reference.
|
|
1107
|
+
*/
|
|
954
1108
|
function setCellByCoord(ws, coord, value, styleId) {
|
|
955
|
-
const
|
|
956
|
-
if (
|
|
957
|
-
|
|
958
|
-
return setCell(ws, Number.parseInt(m[2], 10), col, value, styleId);
|
|
1109
|
+
const rc = plainCoordToRowCol(coord);
|
|
1110
|
+
if (rc === void 0) throw new OpenXmlSchemaError(`setCellByCoord: invalid coordinate "${coord}"`);
|
|
1111
|
+
return setCell(ws, rc.row, rc.col, value, styleId);
|
|
959
1112
|
}
|
|
960
1113
|
/** Convenience getter accepting an "A1" coordinate. */
|
|
961
1114
|
function getCellByCoord(ws, coord) {
|
|
962
|
-
const
|
|
963
|
-
if (
|
|
964
|
-
|
|
965
|
-
|
|
1115
|
+
const rc = plainCoordToRowCol(coord);
|
|
1116
|
+
if (rc === void 0) return void 0;
|
|
1117
|
+
return getCell(ws, rc.row, rc.col);
|
|
1118
|
+
}
|
|
1119
|
+
/**
|
|
1120
|
+
* A1-addressed {@link ensureCell}. Throws `OpenXmlSchemaError` when `coord` is
|
|
1121
|
+
* not a plain A1 reference.
|
|
1122
|
+
*/
|
|
1123
|
+
function ensureCellByCoord(ws, coord) {
|
|
1124
|
+
const rc = plainCoordToRowCol(coord);
|
|
1125
|
+
if (rc === void 0) throw new OpenXmlSchemaError(`ensureCellByCoord: invalid coordinate "${coord}"`);
|
|
1126
|
+
return ensureCell(ws, rc.row, rc.col);
|
|
966
1127
|
}
|
|
967
|
-
const toCellRange = (refOrRange) => typeof refOrRange === "string" ? parseRange(refOrRange) : refOrRange;
|
|
968
1128
|
/**
|
|
969
1129
|
* Merge a range. The top-left cell keeps its value; every other cell in the
|
|
970
1130
|
* range is dropped from `ws.rows` so the on-wire `<sheetData>` won't carry
|
|
971
1131
|
* phantom cells underneath the merge. Mirrors openpyxl's
|
|
972
1132
|
* `MergedCellRange.format()`. Idempotent for an identical range, throws when
|
|
973
1133
|
* the range overlaps an existing merge.
|
|
1134
|
+
*
|
|
1135
|
+
* The registered range is a validated, normalised copy of `refOrRange`, so
|
|
1136
|
+
* mutating a bounds object afterwards can't rewrite a merge that is already on
|
|
1137
|
+
* the sheet.
|
|
974
1138
|
*/
|
|
975
1139
|
function mergeCells(ws, refOrRange) {
|
|
976
|
-
const range =
|
|
1140
|
+
const range = parseRange(refOrRange);
|
|
977
1141
|
for (const existing of ws.mergedCells) {
|
|
978
1142
|
if (rangeToString(existing) === rangeToString(range)) return existing;
|
|
979
1143
|
if (rangesOverlap(existing, range)) throw new OpenXmlSchemaError(`mergeCells: range ${rangeToString(range)} overlaps existing merged range ${rangeToString(existing)}`);
|
|
@@ -989,7 +1153,7 @@ function mergeCells(ws, refOrRange) {
|
|
|
989
1153
|
}
|
|
990
1154
|
/** Drop a previously-merged range. No-op if the range isn't registered. */
|
|
991
1155
|
function unmergeCells(ws, refOrRange) {
|
|
992
|
-
const target = rangeToString(
|
|
1156
|
+
const target = rangeToString(parseRange(refOrRange));
|
|
993
1157
|
const idx = ws.mergedCells.findIndex((r) => rangeToString(r) === target);
|
|
994
1158
|
if (idx < 0) return false;
|
|
995
1159
|
ws.mergedCells.splice(idx, 1);
|
|
@@ -1047,30 +1211,38 @@ const ensurePrimaryView = (ws) => {
|
|
|
1047
1211
|
return view;
|
|
1048
1212
|
};
|
|
1049
1213
|
/**
|
|
1050
|
-
* Freeze rows / columns above + left of
|
|
1214
|
+
* Freeze rows / columns above + left of the given top-left cell. Takes either
|
|
1215
|
+
* the A1 ref of the first unfrozen cell (`"B2"` freezes 1 row + 1 column) or
|
|
1216
|
+
* the counts directly (`{ rows: 1, cols: 0 }` freezes the header row alone).
|
|
1051
1217
|
* Pass `undefined` to clear any existing freeze. Targets the workbook's primary
|
|
1052
1218
|
* SheetView (`ws.views[0]`); creates one if absent.
|
|
1053
1219
|
*/
|
|
1054
|
-
function setFreezePanes(ws,
|
|
1055
|
-
if (
|
|
1220
|
+
function setFreezePanes(ws, topLeft) {
|
|
1221
|
+
if (topLeft === void 0) {
|
|
1056
1222
|
if (ws.views[0]) delete ws.views[0].pane;
|
|
1057
1223
|
return;
|
|
1058
1224
|
}
|
|
1225
|
+
const ref = typeof topLeft === "string" ? topLeft : freezeCountsToRef(topLeft);
|
|
1059
1226
|
const view = ensurePrimaryView(ws);
|
|
1060
|
-
view.pane = makeFreezePane(
|
|
1227
|
+
view.pane = makeFreezePane(ref);
|
|
1061
1228
|
}
|
|
1229
|
+
/**
|
|
1230
|
+
* Translate freeze counts to the A1 ref of the first unfrozen cell. Zero on
|
|
1231
|
+
* both axes composes to `"A1"`, rejected by {@link makeFreezePane} as the
|
|
1232
|
+
* no-op freeze, so that condition has one message in one place however it was
|
|
1233
|
+
* spelled.
|
|
1234
|
+
*/
|
|
1235
|
+
const freezeCountsToRef = ({ rows, cols }) => {
|
|
1236
|
+
if (!Number.isInteger(rows) || rows < 0) throw new OpenXmlSchemaError(`setFreezePanes: rows must be a non-negative integer; got ${rows}`);
|
|
1237
|
+
if (!Number.isInteger(cols) || cols < 0) throw new OpenXmlSchemaError(`setFreezePanes: cols must be a non-negative integer; got ${cols}`);
|
|
1238
|
+
return `${columnLetterFromIndex(cols + 1)}${rows + 1}`;
|
|
1239
|
+
};
|
|
1062
1240
|
/** Inverse of {@link setFreezePanes}; returns the top-left ref or undefined when no freeze is active. */
|
|
1063
1241
|
function getFreezePanes(ws) {
|
|
1064
1242
|
const view = ws.views[0];
|
|
1065
1243
|
if (!view) return void 0;
|
|
1066
1244
|
return freezePaneRef(view);
|
|
1067
1245
|
}
|
|
1068
|
-
/** Freeze both top `rows` rows AND left `cols` columns. */
|
|
1069
|
-
function freezePanes(ws, rows, cols) {
|
|
1070
|
-
if (!Number.isInteger(rows) || rows < 1) throw new OpenXmlSchemaError(`freezePanes: rows must be a positive integer; got ${rows}`);
|
|
1071
|
-
if (!Number.isInteger(cols) || cols < 1) throw new OpenXmlSchemaError(`freezePanes: cols must be a positive integer; got ${cols}`);
|
|
1072
|
-
setFreezePanes(ws, `${columnLetterFromIndex(cols + 1)}${rows + 1}`);
|
|
1073
|
-
}
|
|
1074
1246
|
/** Lazily get-or-create `ws.sheetProperties` so tab-color helpers don't have to branch. */
|
|
1075
1247
|
const ensureSheetProperties = (ws) => {
|
|
1076
1248
|
if (!ws.sheetProperties) ws.sheetProperties = makeSheetProperties();
|
|
@@ -1103,13 +1275,21 @@ function setSheetViewMode(ws, mode) {
|
|
|
1103
1275
|
* down starting at the top-left of `range`; subsequent rows follow. `null` /
|
|
1104
1276
|
* `undefined` entries skip the cell. Useful for dropping a header + data block
|
|
1105
1277
|
* in one call.
|
|
1278
|
+
*
|
|
1279
|
+
* Values past the range's bottom or right edge are dropped rather than written
|
|
1280
|
+
* outside it, the way {@link copyRange} clips to its target extent. Use
|
|
1281
|
+
* {@link writeRange} for the unbounded form, which takes an anchor and grows to
|
|
1282
|
+
* fit the array.
|
|
1106
1283
|
*/
|
|
1107
1284
|
function setRangeValues(ws, range, rows) {
|
|
1108
|
-
const { minRow, minCol } = parseRange(range);
|
|
1109
|
-
|
|
1285
|
+
const { minRow, maxRow, minCol, maxCol } = parseRange(range);
|
|
1286
|
+
const height = Math.min(rows.length, maxRow - minRow + 1);
|
|
1287
|
+
const width = maxCol - minCol + 1;
|
|
1288
|
+
for (let i = 0; i < height; i++) {
|
|
1110
1289
|
const row = rows[i];
|
|
1111
1290
|
if (!row) continue;
|
|
1112
|
-
|
|
1291
|
+
const rowWidth = Math.min(row.length, width);
|
|
1292
|
+
for (let j = 0; j < rowWidth; j++) {
|
|
1113
1293
|
const v = row[j];
|
|
1114
1294
|
if (v === null || v === void 0) continue;
|
|
1115
1295
|
setCell(ws, minRow + i, minCol + j, v);
|
|
@@ -1122,11 +1302,7 @@ function setRangeValues(ws, range, rows) {
|
|
|
1122
1302
|
*/
|
|
1123
1303
|
function applyToRange(ws, range, visit) {
|
|
1124
1304
|
const { minRow, maxRow, minCol, maxCol } = parseRange(range);
|
|
1125
|
-
for (let r = minRow; r <= maxRow; r++) for (let c = minCol; c <= maxCol; c++)
|
|
1126
|
-
let cell = ws.rows.get(r)?.get(c);
|
|
1127
|
-
if (!cell) cell = setCell(ws, r, c);
|
|
1128
|
-
visit(cell, r, c);
|
|
1129
|
-
}
|
|
1305
|
+
for (let r = minRow; r <= maxRow; r++) for (let c = minCol; c <= maxCol; c++) visit(ensureCell(ws, r, c), r, c);
|
|
1130
1306
|
}
|
|
1131
1307
|
/**
|
|
1132
1308
|
* Read a rectangular range as a dense 2-D array of values. Empty cells yield
|
|
@@ -1675,8 +1851,16 @@ function setAutoFilter(ws, filter) {
|
|
|
1675
1851
|
function getAutoFilter(ws) {
|
|
1676
1852
|
return ws.autoFilter;
|
|
1677
1853
|
}
|
|
1678
|
-
/**
|
|
1854
|
+
/**
|
|
1855
|
+
* Append a table, rejecting one whose geometry or column names disagree with
|
|
1856
|
+
* the cells under it: the column count has to match the width of `ref`, `ref`
|
|
1857
|
+
* has to contain the header and totals rows, column names have
|
|
1858
|
+
* to be unique and non-empty, and every header cell has to hold its column's
|
|
1859
|
+
* name as text. The id and displayName must be workbook-unique, and stay the
|
|
1860
|
+
* caller's responsibility: neither is visible from a single sheet.
|
|
1861
|
+
*/
|
|
1679
1862
|
function addTable(ws, table) {
|
|
1863
|
+
validateTableAgainstSheet(ws, table);
|
|
1680
1864
|
ws.tables.push(table);
|
|
1681
1865
|
return table;
|
|
1682
1866
|
}
|
|
@@ -1753,6 +1937,6 @@ function removeIgnoredErrors(ws, predicate) {
|
|
|
1753
1937
|
return before - ws.ignoredErrors.length;
|
|
1754
1938
|
}
|
|
1755
1939
|
//#endregion
|
|
1756
|
-
export {
|
|
1940
|
+
export { iterCells as $, makeColumnDimension as $t, getCellsInRange as A, rgbColor as An, setHyperlink as At, getPopulatedColumnIndices as B, unhideColumn as Bt, findCells as C, lighten as Cn, setColumnDimension as Ct, getCellComment as D, normaliseRgb as Dn, setDefaultColumnWidth as Dt, getCellByCoord as E, mixColors as En, setComment as Et, getMaxCol as F, setSheetTabColor as Ft, groupColumns as G, unmergeCellsAt as Gt, getRangeValues as H, unhideRow as Ht, getMaxRow as I, setSheetViewMode as It, hideColumns as J, makeSheetProperties as Jt, groupRows as K, writeRange as Kt, getMergedCells as L, setSheetZoom as Lt, getColumnDimension as M, setRowDimension as Mt, getDataExtent as N, setRowHeight as Nt, getCellHyperlink as O, pickReadableTextColor as On, setDefaultRowHeight as Ot, getFreezePanes as P, setRowHeights as Pt, isWorksheetEmpty as Q, makeSheetView as Qt, getMergedRangeAt as R, ungroupColumns as Rt, expandRowGroup as S, hslToHex as Sn, setCellByCoord as St, getCell as T, makeColor as Tn, setColumnWidths as Tt, getRowDimension as U, unhideRows as Ut, getPopulatedRowIndices as V, unhideColumns as Vt, getTable as W, unmergeCells as Wt, hideRows as X, freezePaneRef as Xt, hideRow as Y, makeLegacyComment as Yt, isMergedCell as Z, makeFreezePane as Zt, countCellsByKind as _, adjustSaturation as _n, removeHyperlink as _t, addTable as a, multiCellRangeContainsCell as an, listTables as at, ensureCellByCoord as b, darken as bn, setAutoFilter as bt, applyToRange as c, parseRange as cn, moveRange as ct, clearAllCells as d, rangeContainsRange as dn, removeAllDataValidations as dt, makeRowDimension as en, iterRows as et, clearRange as f, rangeToString as fn, removeAllHyperlinks as ft, countCells as g, adjustLightness as gn, removeDataValidations as gt, copyRange as h, unionRange as hn, removeCellWatches as ht, addIgnoredError as i, isRangeInRange as in, listHyperlinks as it, getCellsInRow as j, rotateHue as jn, setRangeValues as jt, getCellsInColumn as k, resolveIndexedColor as kn, setFreezePanes as kt, autofitColumns as l, rangeArea as ln, removeAllComments as lt, collapseRowGroup as m, shiftRange as mn, removeAllTables as mt, addConditionalFormatting as n, intersectionRange as nn, listComments as nt, appendRow as o, multiCellRangeToString as on, makeWorksheet as ot, collapseColumnGroup as p, rangesOverlap as pn, removeAllMergedRanges as pt, hideColumn as q, makeHyperlink as qt, addDataValidation as r, isCellInRange as rn, listDataValidations as rt, appendRows as s, parseMultiCellRange as sn, mergeCells as st, addCellWatch as t, expandRangeStr as tn, iterValues as tt, classifyCellValue as u, rangeContainsCell as un, removeAllConditionalFormatting as ut, deleteCell as v, colorToHex as vn, removeIgnoredErrors as vt, getAutoFilter as w, luminance as wn, setColumnWidth as wt, expandColumnGroup as x, hexToHsl as xn, setCell as xt, ensureCell as y, contrastRatio as yn, removeTable as yt, getNonEmptyCellCount as z, ungroupRows as zt };
|
|
1757
1941
|
|
|
1758
|
-
//# sourceMappingURL=worksheet-
|
|
1942
|
+
//# sourceMappingURL=worksheet-4xW-i8j9.mjs.map
|