@cj-tech-master/excelts 4.0.4-canary.20260109045014.00b0344 → 4.0.4-canary.20260110000241.8ac37ef
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/dist/browser/modules/excel/cell.js +39 -1
- package/dist/browser/modules/excel/enums.d.ts +2 -1
- package/dist/browser/modules/excel/enums.js +2 -1
- package/dist/browser/modules/excel/stream/workbook-writer.browser.d.ts +1 -0
- package/dist/browser/modules/excel/stream/workbook-writer.browser.js +19 -1
- package/dist/browser/modules/excel/table.d.ts +6 -2
- package/dist/browser/modules/excel/table.js +33 -5
- package/dist/browser/modules/excel/types.d.ts +5 -1
- package/dist/browser/modules/excel/utils/ooxml-paths.d.ts +2 -0
- package/dist/browser/modules/excel/utils/ooxml-paths.js +4 -2
- package/dist/browser/modules/excel/xlsx/rel-type.d.ts +1 -0
- package/dist/browser/modules/excel/xlsx/rel-type.js +2 -1
- package/dist/browser/modules/excel/xlsx/xform/core/content-types-xform.js +7 -0
- package/dist/browser/modules/excel/xlsx/xform/core/feature-property-bag-xform.d.ts +8 -0
- package/dist/browser/modules/excel/xlsx/xform/core/feature-property-bag-xform.js +36 -0
- package/dist/browser/modules/excel/xlsx/xform/sheet/cell-xform.js +5 -0
- package/dist/browser/modules/excel/xlsx/xform/style/style-xform.d.ts +2 -0
- package/dist/browser/modules/excel/xlsx/xform/style/style-xform.js +11 -0
- package/dist/browser/modules/excel/xlsx/xform/style/styles-xform.d.ts +2 -0
- package/dist/browser/modules/excel/xlsx/xform/style/styles-xform.js +28 -4
- package/dist/browser/modules/excel/xlsx/xlsx.browser.d.ts +2 -0
- package/dist/browser/modules/excel/xlsx/xlsx.browser.js +19 -0
- package/dist/cjs/modules/excel/cell.js +39 -1
- package/dist/cjs/modules/excel/enums.js +2 -1
- package/dist/cjs/modules/excel/stream/workbook-writer.browser.js +19 -1
- package/dist/cjs/modules/excel/table.js +33 -5
- package/dist/cjs/modules/excel/utils/ooxml-paths.js +4 -2
- package/dist/cjs/modules/excel/xlsx/rel-type.js +2 -1
- package/dist/cjs/modules/excel/xlsx/xform/core/content-types-xform.js +7 -0
- package/dist/cjs/modules/excel/xlsx/xform/core/feature-property-bag-xform.js +39 -0
- package/dist/cjs/modules/excel/xlsx/xform/sheet/cell-xform.js +5 -0
- package/dist/cjs/modules/excel/xlsx/xform/style/style-xform.js +11 -0
- package/dist/cjs/modules/excel/xlsx/xform/style/styles-xform.js +28 -4
- package/dist/cjs/modules/excel/xlsx/xlsx.browser.js +19 -0
- package/dist/esm/modules/excel/cell.js +39 -1
- package/dist/esm/modules/excel/enums.js +2 -1
- package/dist/esm/modules/excel/stream/workbook-writer.browser.js +19 -1
- package/dist/esm/modules/excel/table.js +33 -5
- package/dist/esm/modules/excel/utils/ooxml-paths.js +4 -2
- package/dist/esm/modules/excel/xlsx/rel-type.js +2 -1
- package/dist/esm/modules/excel/xlsx/xform/core/content-types-xform.js +7 -0
- package/dist/esm/modules/excel/xlsx/xform/core/feature-property-bag-xform.js +36 -0
- package/dist/esm/modules/excel/xlsx/xform/sheet/cell-xform.js +5 -0
- package/dist/esm/modules/excel/xlsx/xform/style/style-xform.js +11 -0
- package/dist/esm/modules/excel/xlsx/xform/style/styles-xform.js +28 -4
- package/dist/esm/modules/excel/xlsx/xlsx.browser.js +19 -0
- package/dist/iife/excelts.iife.js +165 -12
- package/dist/iife/excelts.iife.js.map +1 -1
- package/dist/iife/excelts.iife.min.js +28 -28
- package/dist/types/modules/excel/enums.d.ts +2 -1
- package/dist/types/modules/excel/stream/workbook-writer.browser.d.ts +1 -0
- package/dist/types/modules/excel/table.d.ts +6 -2
- package/dist/types/modules/excel/types.d.ts +5 -1
- package/dist/types/modules/excel/utils/ooxml-paths.d.ts +2 -0
- package/dist/types/modules/excel/xlsx/rel-type.d.ts +1 -0
- package/dist/types/modules/excel/xlsx/xform/core/feature-property-bag-xform.d.ts +8 -0
- package/dist/types/modules/excel/xlsx/xform/style/style-xform.d.ts +2 -0
- package/dist/types/modules/excel/xlsx/xform/style/styles-xform.d.ts +2 -0
- package/dist/types/modules/excel/xlsx/xlsx.browser.d.ts +2 -0
- package/package.json +1 -1
|
@@ -809,6 +809,40 @@ class BooleanValue {
|
|
|
809
809
|
return this.model.value.toString();
|
|
810
810
|
}
|
|
811
811
|
}
|
|
812
|
+
class CheckboxValue {
|
|
813
|
+
constructor(cell, value) {
|
|
814
|
+
this.model = {
|
|
815
|
+
address: cell.address,
|
|
816
|
+
type: Cell.Types.Checkbox,
|
|
817
|
+
value: value.checkbox
|
|
818
|
+
};
|
|
819
|
+
}
|
|
820
|
+
get value() {
|
|
821
|
+
return { checkbox: this.model.value };
|
|
822
|
+
}
|
|
823
|
+
set value(value) {
|
|
824
|
+
this.model.value = value.checkbox;
|
|
825
|
+
}
|
|
826
|
+
get type() {
|
|
827
|
+
return Cell.Types.Checkbox;
|
|
828
|
+
}
|
|
829
|
+
get effectiveType() {
|
|
830
|
+
return Cell.Types.Boolean;
|
|
831
|
+
}
|
|
832
|
+
get address() {
|
|
833
|
+
return this.model.address;
|
|
834
|
+
}
|
|
835
|
+
set address(value) {
|
|
836
|
+
this.model.address = value;
|
|
837
|
+
}
|
|
838
|
+
toCsvString() {
|
|
839
|
+
return this.model.value ? 1 : 0;
|
|
840
|
+
}
|
|
841
|
+
release() { }
|
|
842
|
+
toString() {
|
|
843
|
+
return this.model.value.toString();
|
|
844
|
+
}
|
|
845
|
+
}
|
|
812
846
|
class ErrorValue {
|
|
813
847
|
constructor(cell, value) {
|
|
814
848
|
this.model = {
|
|
@@ -898,6 +932,9 @@ const Value = {
|
|
|
898
932
|
return Cell.Types.Date;
|
|
899
933
|
}
|
|
900
934
|
if (typeof value === "object") {
|
|
935
|
+
if ("checkbox" in value && typeof value.checkbox === "boolean") {
|
|
936
|
+
return Cell.Types.Checkbox;
|
|
937
|
+
}
|
|
901
938
|
if ("text" in value && value.text && "hyperlink" in value && value.hyperlink) {
|
|
902
939
|
return Cell.Types.Hyperlink;
|
|
903
940
|
}
|
|
@@ -930,7 +967,8 @@ const Value = {
|
|
|
930
967
|
{ t: Cell.Types.SharedString, f: SharedStringValue },
|
|
931
968
|
{ t: Cell.Types.RichText, f: RichTextValue },
|
|
932
969
|
{ t: Cell.Types.Boolean, f: BooleanValue },
|
|
933
|
-
{ t: Cell.Types.Error, f: ErrorValue }
|
|
970
|
+
{ t: Cell.Types.Error, f: ErrorValue },
|
|
971
|
+
{ t: Cell.Types.Checkbox, f: CheckboxValue }
|
|
934
972
|
].reduce((p, t) => {
|
|
935
973
|
p[t.t] = t.f;
|
|
936
974
|
return p;
|
|
@@ -11,7 +11,8 @@ export var ValueType;
|
|
|
11
11
|
ValueType[ValueType["RichText"] = 8] = "RichText";
|
|
12
12
|
ValueType[ValueType["Boolean"] = 9] = "Boolean";
|
|
13
13
|
ValueType[ValueType["Error"] = 10] = "Error";
|
|
14
|
-
ValueType[ValueType["JSON"] = 11] = "JSON";
|
|
14
|
+
ValueType[ValueType["JSON"] = 11] = "JSON";
|
|
15
|
+
ValueType[ValueType["Checkbox"] = 12] = "Checkbox";
|
|
15
16
|
})(ValueType || (ValueType = {}));
|
|
16
17
|
export var FormulaType;
|
|
17
18
|
(function (FormulaType) {
|
|
@@ -132,6 +132,7 @@ export declare abstract class WorkbookWriterBase<TWorksheetWriter extends Worksh
|
|
|
132
132
|
addApp(): Promise<void>;
|
|
133
133
|
addCore(): Promise<void>;
|
|
134
134
|
addSharedStrings(): Promise<void>;
|
|
135
|
+
addFeaturePropertyBag(): Promise<void>;
|
|
135
136
|
addWorkbookRels(): Promise<void>;
|
|
136
137
|
addWorkbook(): Promise<void>;
|
|
137
138
|
_finalize(): Promise<this>;
|
|
@@ -20,6 +20,7 @@ import { ContentTypesXform } from "../xlsx/xform/core/content-types-xform.js";
|
|
|
20
20
|
import { AppXform } from "../xlsx/xform/core/app-xform.js";
|
|
21
21
|
import { WorkbookXform } from "../xlsx/xform/book/workbook-xform.js";
|
|
22
22
|
import { SharedStringsXform } from "../xlsx/xform/strings/shared-strings-xform.js";
|
|
23
|
+
import { FeaturePropertyBagXform } from "../xlsx/xform/core/feature-property-bag-xform.js";
|
|
23
24
|
import { theme1Xml } from "../xlsx/xml/theme1.js";
|
|
24
25
|
import { Writeable, stringToUint8Array } from "../../stream/index.browser.js";
|
|
25
26
|
import { mediaPath, OOXML_PATHS, OOXML_REL_TARGETS, worksheetRelTarget } from "../utils/ooxml-paths.js";
|
|
@@ -134,6 +135,7 @@ export class WorkbookWriterBase {
|
|
|
134
135
|
this.addCore(),
|
|
135
136
|
this.addSharedStrings(),
|
|
136
137
|
this.addStyles(),
|
|
138
|
+
this.addFeaturePropertyBag(),
|
|
137
139
|
this.addWorkbookRels()
|
|
138
140
|
]);
|
|
139
141
|
await this.addWorkbook();
|
|
@@ -226,7 +228,8 @@ export class WorkbookWriterBase {
|
|
|
226
228
|
worksheets: this._worksheets.filter(Boolean),
|
|
227
229
|
sharedStrings: this.sharedStrings,
|
|
228
230
|
commentRefs: this.commentRefs,
|
|
229
|
-
media: this.media
|
|
231
|
+
media: this.media,
|
|
232
|
+
hasCheckboxes: this.styles.hasCheckboxes
|
|
230
233
|
};
|
|
231
234
|
const xform = new ContentTypesXform();
|
|
232
235
|
this._addFile(xform.toXml(model), OOXML_PATHS.contentTypes);
|
|
@@ -280,6 +283,13 @@ export class WorkbookWriterBase {
|
|
|
280
283
|
}
|
|
281
284
|
return Promise.resolve();
|
|
282
285
|
}
|
|
286
|
+
addFeaturePropertyBag() {
|
|
287
|
+
if (this.styles.hasCheckboxes) {
|
|
288
|
+
const xform = new FeaturePropertyBagXform();
|
|
289
|
+
this._addFile(xform.toXml({}), OOXML_PATHS.xlFeaturePropertyBag);
|
|
290
|
+
}
|
|
291
|
+
return Promise.resolve();
|
|
292
|
+
}
|
|
283
293
|
addWorkbookRels() {
|
|
284
294
|
let count = 1;
|
|
285
295
|
const relationships = [
|
|
@@ -293,6 +303,14 @@ export class WorkbookWriterBase {
|
|
|
293
303
|
Target: OOXML_REL_TARGETS.workbookSharedStrings
|
|
294
304
|
});
|
|
295
305
|
}
|
|
306
|
+
// Add FeaturePropertyBag relationship if checkboxes are used
|
|
307
|
+
if (this.styles.hasCheckboxes) {
|
|
308
|
+
relationships.push({
|
|
309
|
+
Id: `rId${count++}`,
|
|
310
|
+
Type: RelType.FeaturePropertyBag,
|
|
311
|
+
Target: OOXML_REL_TARGETS.workbookFeaturePropertyBag
|
|
312
|
+
});
|
|
313
|
+
}
|
|
296
314
|
this._worksheets.forEach(ws => {
|
|
297
315
|
if (ws) {
|
|
298
316
|
ws.rId = `rId${count++}`;
|
|
@@ -52,8 +52,12 @@ declare class Table {
|
|
|
52
52
|
set model(value: TableModel);
|
|
53
53
|
cacheState(): void;
|
|
54
54
|
commit(): void;
|
|
55
|
-
addRow(values: CellValue[], rowNumber?: number
|
|
56
|
-
|
|
55
|
+
addRow(values: CellValue[], rowNumber?: number, options?: {
|
|
56
|
+
commit?: boolean;
|
|
57
|
+
}): void;
|
|
58
|
+
removeRows(rowIndex: number, count?: number, options?: {
|
|
59
|
+
commit?: boolean;
|
|
60
|
+
}): void;
|
|
57
61
|
getColumn(colIndex: number): Column;
|
|
58
62
|
addColumn(column: TableColumnProperties, values: CellValue[], colIndex?: number): void;
|
|
59
63
|
removeColumns(colIndex: number, count?: number): void;
|
|
@@ -57,6 +57,26 @@ class Table {
|
|
|
57
57
|
this.worksheet = worksheet;
|
|
58
58
|
if (table) {
|
|
59
59
|
this.table = table;
|
|
60
|
+
// When loading tables from xlsx, Excel stores table ranges and cell values in the worksheet,
|
|
61
|
+
// but may not embed row data into the table definition. Hydrate rows from the worksheet so
|
|
62
|
+
// table mutations (e.g. addRow) can correctly expand table ranges and serialize.
|
|
63
|
+
if (Array.isArray(table.rows) && table.rows.length === 0 && table.tableRef) {
|
|
64
|
+
const decoded = colCache.decode(table.tableRef);
|
|
65
|
+
if ("dimensions" in decoded) {
|
|
66
|
+
const startRow = decoded.top + (table.headerRow === false ? 0 : 1);
|
|
67
|
+
const endRow = decoded.bottom - (table.totalsRow === true ? 1 : 0);
|
|
68
|
+
if (endRow >= startRow) {
|
|
69
|
+
for (let r = startRow; r <= endRow; r++) {
|
|
70
|
+
const row = worksheet.getRow(r);
|
|
71
|
+
const values = [];
|
|
72
|
+
for (let c = decoded.left; c <= decoded.right; c++) {
|
|
73
|
+
values.push(row.getCell(c).value);
|
|
74
|
+
}
|
|
75
|
+
table.rows.push(values);
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
}
|
|
60
80
|
// check things are ok first
|
|
61
81
|
this.validate();
|
|
62
82
|
this.store();
|
|
@@ -133,9 +153,10 @@ class Table {
|
|
|
133
153
|
const { row, col } = table.tl;
|
|
134
154
|
assert(row > 0, "Table must be on valid row");
|
|
135
155
|
assert(col > 0, "Table must be on valid col");
|
|
136
|
-
const { width,
|
|
137
|
-
// autoFilterRef is a range that
|
|
138
|
-
|
|
156
|
+
const { width, tableHeight } = this;
|
|
157
|
+
// autoFilterRef is a single-row range that targets the header row only.
|
|
158
|
+
// Excel uses this for filter buttons; including data rows can break filter rendering.
|
|
159
|
+
table.autoFilterRef = colCache.encode(row, col, row, col + width - 1);
|
|
139
160
|
// tableRef is a range that includes optional headers and totals
|
|
140
161
|
table.tableRef = colCache.encode(row, col, row + tableHeight - 1, col + width - 1);
|
|
141
162
|
table.columns.forEach((column, i) => {
|
|
@@ -304,8 +325,9 @@ class Table {
|
|
|
304
325
|
}
|
|
305
326
|
}
|
|
306
327
|
this.store();
|
|
328
|
+
this._cache = undefined;
|
|
307
329
|
}
|
|
308
|
-
addRow(values, rowNumber) {
|
|
330
|
+
addRow(values, rowNumber, options) {
|
|
309
331
|
// Add a row of data, either insert at rowNumber or append
|
|
310
332
|
this.cacheState();
|
|
311
333
|
if (rowNumber === undefined) {
|
|
@@ -314,11 +336,17 @@ class Table {
|
|
|
314
336
|
else {
|
|
315
337
|
this.table.rows.splice(rowNumber, 0, values);
|
|
316
338
|
}
|
|
339
|
+
if (options?.commit !== false) {
|
|
340
|
+
this.commit();
|
|
341
|
+
}
|
|
317
342
|
}
|
|
318
|
-
removeRows(rowIndex, count = 1) {
|
|
343
|
+
removeRows(rowIndex, count = 1, options) {
|
|
319
344
|
// Remove a rows of data
|
|
320
345
|
this.cacheState();
|
|
321
346
|
this.table.rows.splice(rowIndex, count);
|
|
347
|
+
if (options?.commit !== false) {
|
|
348
|
+
this.commit();
|
|
349
|
+
}
|
|
322
350
|
}
|
|
323
351
|
getColumn(colIndex) {
|
|
324
352
|
const column = this.table.columns[colIndex];
|
|
@@ -274,7 +274,11 @@ export interface CellSharedFormulaValue {
|
|
|
274
274
|
result?: number | string | boolean | Date | CellErrorValue;
|
|
275
275
|
date1904?: boolean;
|
|
276
276
|
}
|
|
277
|
-
export
|
|
277
|
+
export interface CellCheckboxValue {
|
|
278
|
+
/** Indicates this is a checkbox value */
|
|
279
|
+
checkbox: boolean;
|
|
280
|
+
}
|
|
281
|
+
export type CellValue = null | number | string | boolean | Date | undefined | CellErrorValue | CellRichTextValue | CellHyperlinkValue | CellFormulaValue | CellArrayFormulaValue | CellSharedFormulaValue | CellCheckboxValue;
|
|
278
282
|
export interface CommentMargins {
|
|
279
283
|
insetmode: "auto" | "custom";
|
|
280
284
|
inset: number[];
|
|
@@ -8,6 +8,7 @@ export declare const OOXML_PATHS: {
|
|
|
8
8
|
readonly xlSharedStrings: "xl/sharedStrings.xml";
|
|
9
9
|
readonly xlStyles: "xl/styles.xml";
|
|
10
10
|
readonly xlTheme1: "xl/theme/theme1.xml";
|
|
11
|
+
readonly xlFeaturePropertyBag: "xl/featurePropertyBag/featurePropertyBag.xml";
|
|
11
12
|
};
|
|
12
13
|
export declare function normalizeZipPath(path: string): string;
|
|
13
14
|
export declare function getWorksheetNoFromWorksheetPath(path: string): number | undefined;
|
|
@@ -50,6 +51,7 @@ export declare const OOXML_REL_TARGETS: {
|
|
|
50
51
|
readonly workbookStyles: "styles.xml";
|
|
51
52
|
readonly workbookSharedStrings: "sharedStrings.xml";
|
|
52
53
|
readonly workbookTheme1: "theme/theme1.xml";
|
|
54
|
+
readonly workbookFeaturePropertyBag: "featurePropertyBag/featurePropertyBag.xml";
|
|
53
55
|
};
|
|
54
56
|
export declare function pivotCacheDefinitionRelTargetFromWorkbook(n: number | string): string;
|
|
55
57
|
export declare function commentsRelTargetFromWorksheet(sheetId: number | string): string;
|
|
@@ -7,7 +7,8 @@ export const OOXML_PATHS = {
|
|
|
7
7
|
xlWorkbookRels: "xl/_rels/workbook.xml.rels",
|
|
8
8
|
xlSharedStrings: "xl/sharedStrings.xml",
|
|
9
9
|
xlStyles: "xl/styles.xml",
|
|
10
|
-
xlTheme1: "xl/theme/theme1.xml"
|
|
10
|
+
xlTheme1: "xl/theme/theme1.xml",
|
|
11
|
+
xlFeaturePropertyBag: "xl/featurePropertyBag/featurePropertyBag.xml"
|
|
11
12
|
};
|
|
12
13
|
const worksheetXmlRegex = /^xl\/worksheets\/sheet(\d+)[.]xml$/;
|
|
13
14
|
const worksheetRelsXmlRegex = /^xl\/worksheets\/_rels\/sheet(\d+)[.]xml[.]rels$/;
|
|
@@ -162,7 +163,8 @@ export const OOXML_REL_TARGETS = {
|
|
|
162
163
|
// Targets inside xl/_rels/workbook.xml.rels (base: xl/)
|
|
163
164
|
workbookStyles: "styles.xml",
|
|
164
165
|
workbookSharedStrings: "sharedStrings.xml",
|
|
165
|
-
workbookTheme1: "theme/theme1.xml"
|
|
166
|
+
workbookTheme1: "theme/theme1.xml",
|
|
167
|
+
workbookFeaturePropertyBag: "featurePropertyBag/featurePropertyBag.xml"
|
|
166
168
|
};
|
|
167
169
|
export function pivotCacheDefinitionRelTargetFromWorkbook(n) {
|
|
168
170
|
// Target inside xl/_rels/workbook.xml.rels (base: xl/)
|
|
@@ -14,6 +14,7 @@ const RelType = {
|
|
|
14
14
|
Table: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/table",
|
|
15
15
|
PivotCacheDefinition: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheDefinition",
|
|
16
16
|
PivotCacheRecords: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheRecords",
|
|
17
|
-
PivotTable: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotTable"
|
|
17
|
+
PivotTable: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotTable",
|
|
18
|
+
FeaturePropertyBag: "http://schemas.microsoft.com/office/2022/11/relationships/FeaturePropertyBag"
|
|
18
19
|
};
|
|
19
20
|
export { RelType };
|
|
@@ -63,6 +63,13 @@ class ContentTypesXform extends BaseXform {
|
|
|
63
63
|
PartName: toContentTypesPartName(OOXML_PATHS.xlStyles),
|
|
64
64
|
ContentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"
|
|
65
65
|
});
|
|
66
|
+
// Add FeaturePropertyBag if checkboxes are used
|
|
67
|
+
if (model.hasCheckboxes) {
|
|
68
|
+
xmlStream.leafNode("Override", {
|
|
69
|
+
PartName: toContentTypesPartName(OOXML_PATHS.xlFeaturePropertyBag),
|
|
70
|
+
ContentType: "application/vnd.ms-excel.featurepropertybag+xml"
|
|
71
|
+
});
|
|
72
|
+
}
|
|
66
73
|
const hasSharedStrings = model.sharedStrings && model.sharedStrings.count;
|
|
67
74
|
if (hasSharedStrings) {
|
|
68
75
|
xmlStream.leafNode("Override", {
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { BaseXform } from "../base-xform.js";
|
|
2
|
+
// FeaturePropertyBag is used to enable checkbox functionality
|
|
3
|
+
// This is a static XML file that MS Excel requires for checkboxes to work
|
|
4
|
+
class FeaturePropertyBagXform extends BaseXform {
|
|
5
|
+
render(xmlStream) {
|
|
6
|
+
xmlStream.openXml({ version: "1.0", encoding: "UTF-8", standalone: "yes" });
|
|
7
|
+
xmlStream.openNode("FeaturePropertyBags", {
|
|
8
|
+
xmlns: "http://schemas.microsoft.com/office/spreadsheetml/2022/featurepropertybag"
|
|
9
|
+
});
|
|
10
|
+
// Checkbox feature
|
|
11
|
+
xmlStream.leafNode("bag", { type: "Checkbox" });
|
|
12
|
+
// XFControls bag
|
|
13
|
+
xmlStream.openNode("bag", { type: "XFControls" });
|
|
14
|
+
xmlStream.leafNode("bagId", { k: "CellControl" }, "0");
|
|
15
|
+
xmlStream.closeNode();
|
|
16
|
+
// XFComplement bag
|
|
17
|
+
xmlStream.openNode("bag", { type: "XFComplement" });
|
|
18
|
+
xmlStream.leafNode("bagId", { k: "XFControls" }, "1");
|
|
19
|
+
xmlStream.closeNode();
|
|
20
|
+
// XFComplements bag
|
|
21
|
+
xmlStream.openNode("bag", { type: "XFComplements", extRef: "XFComplementsMapperExtRef" });
|
|
22
|
+
xmlStream.openNode("a", { k: "MappedFeaturePropertyBags" });
|
|
23
|
+
xmlStream.leafNode("bagId", {}, "2");
|
|
24
|
+
xmlStream.closeNode();
|
|
25
|
+
xmlStream.closeNode();
|
|
26
|
+
xmlStream.closeNode();
|
|
27
|
+
}
|
|
28
|
+
parseOpen() {
|
|
29
|
+
return false;
|
|
30
|
+
}
|
|
31
|
+
parseText() { }
|
|
32
|
+
parseClose() {
|
|
33
|
+
return false;
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
export { FeaturePropertyBagXform };
|
|
@@ -189,6 +189,11 @@ class CellXform extends BaseXform {
|
|
|
189
189
|
xmlStream.addAttribute("t", "b");
|
|
190
190
|
xmlStream.leafNode("v", null, model.value ? "1" : "0");
|
|
191
191
|
break;
|
|
192
|
+
case Enums.ValueType.Checkbox:
|
|
193
|
+
// Checkboxes are stored as boolean values
|
|
194
|
+
xmlStream.addAttribute("t", "b");
|
|
195
|
+
xmlStream.leafNode("v", null, model.value ? "1" : "0");
|
|
196
|
+
break;
|
|
192
197
|
case Enums.ValueType.Error:
|
|
193
198
|
xmlStream.addAttribute("t", "e");
|
|
194
199
|
xmlStream.leafNode("v", null, model.value.error);
|
|
@@ -52,6 +52,17 @@ class StyleXform extends BaseXform {
|
|
|
52
52
|
if (model.protection) {
|
|
53
53
|
this.map.protection.render(xmlStream, model.protection);
|
|
54
54
|
}
|
|
55
|
+
// Add checkbox extLst if needed
|
|
56
|
+
if (model.checkbox && model.xfComplementIndex !== undefined) {
|
|
57
|
+
xmlStream.openNode("extLst");
|
|
58
|
+
xmlStream.openNode("ext", {
|
|
59
|
+
"xmlns:xfpb": "http://schemas.microsoft.com/office/spreadsheetml/2022/featurepropertybag",
|
|
60
|
+
uri: "{C7286773-470A-42A8-94C5-96B5CB345126}"
|
|
61
|
+
});
|
|
62
|
+
xmlStream.leafNode("xfpb:xfComplement", { i: model.xfComplementIndex });
|
|
63
|
+
xmlStream.closeNode();
|
|
64
|
+
xmlStream.closeNode();
|
|
65
|
+
}
|
|
55
66
|
xmlStream.closeNode();
|
|
56
67
|
}
|
|
57
68
|
parseOpen(node) {
|
|
@@ -11,6 +11,7 @@ interface StylesModel {
|
|
|
11
11
|
declare class StylesXform extends BaseXform {
|
|
12
12
|
private index?;
|
|
13
13
|
private weakMap?;
|
|
14
|
+
private _hasCheckboxes?;
|
|
14
15
|
parser: any;
|
|
15
16
|
static Mock: typeof StylesXform;
|
|
16
17
|
constructor(initialise?: boolean);
|
|
@@ -24,6 +25,7 @@ declare class StylesXform extends BaseXform {
|
|
|
24
25
|
getStyleModel(id: number): any;
|
|
25
26
|
addDxfStyle(style: any): number;
|
|
26
27
|
getDxfStyle(id: number): any;
|
|
28
|
+
get hasCheckboxes(): boolean;
|
|
27
29
|
_addStyle(style: any): number;
|
|
28
30
|
_addNumFmtStr(formatCode: string): number;
|
|
29
31
|
_addFont(font: any): number;
|
|
@@ -82,6 +82,7 @@ class StylesXform extends BaseXform {
|
|
|
82
82
|
this._addFill({ type: "pattern", pattern: "none" });
|
|
83
83
|
this._addFill({ type: "pattern", pattern: "gray125" });
|
|
84
84
|
this.weakMap = new WeakMap();
|
|
85
|
+
this._hasCheckboxes = false;
|
|
85
86
|
}
|
|
86
87
|
render(xmlStream, model) {
|
|
87
88
|
const renderModel = model || this.model;
|
|
@@ -222,12 +223,15 @@ class StylesXform extends BaseXform {
|
|
|
222
223
|
// default (zero) font
|
|
223
224
|
this._addFont({ size: 11, color: { theme: 1 }, name: "Calibri", family: 2, scheme: "minor" });
|
|
224
225
|
}
|
|
225
|
-
|
|
226
|
-
|
|
226
|
+
const type = cellType || Enums.ValueType.Number;
|
|
227
|
+
// If we have seen this style object before, assume it has the same styleId.
|
|
228
|
+
// Do not cache by object identity for checkbox cells because the styleId must
|
|
229
|
+
// include checkbox-specific extLst, and the same style object may be reused
|
|
230
|
+
// for non-checkbox cells.
|
|
231
|
+
if (type !== Enums.ValueType.Checkbox && this.weakMap && this.weakMap.has(model)) {
|
|
227
232
|
return this.weakMap.get(model);
|
|
228
233
|
}
|
|
229
234
|
const style = {};
|
|
230
|
-
const type = cellType || Enums.ValueType.Number;
|
|
231
235
|
if (model.numFmt) {
|
|
232
236
|
style.numFmtId = this._addNumFmtStr(model.numFmt);
|
|
233
237
|
}
|
|
@@ -258,8 +262,17 @@ class StylesXform extends BaseXform {
|
|
|
258
262
|
if (model.protection) {
|
|
259
263
|
style.protection = model.protection;
|
|
260
264
|
}
|
|
265
|
+
if (type === Enums.ValueType.Checkbox) {
|
|
266
|
+
// Checkbox rendering relies on style extensions (extLst) and workbook-level parts.
|
|
267
|
+
// Force applyAlignment="1" (without emitting an <alignment/> node) by providing
|
|
268
|
+
// an empty alignment object when none is specified.
|
|
269
|
+
this._hasCheckboxes = true;
|
|
270
|
+
style.alignment = style.alignment || {};
|
|
271
|
+
style.checkbox = true;
|
|
272
|
+
style.xfComplementIndex = 0;
|
|
273
|
+
}
|
|
261
274
|
const styleId = this._addStyle(style);
|
|
262
|
-
if (this.weakMap) {
|
|
275
|
+
if (type !== Enums.ValueType.Checkbox && this.weakMap) {
|
|
263
276
|
this.weakMap.set(model, styleId);
|
|
264
277
|
}
|
|
265
278
|
return styleId;
|
|
@@ -322,6 +335,10 @@ class StylesXform extends BaseXform {
|
|
|
322
335
|
getDxfStyle(id) {
|
|
323
336
|
return this.model.dxfs[id];
|
|
324
337
|
}
|
|
338
|
+
// Check if workbook uses checkbox feature
|
|
339
|
+
get hasCheckboxes() {
|
|
340
|
+
return !!this._hasCheckboxes;
|
|
341
|
+
}
|
|
325
342
|
// =========================================================================
|
|
326
343
|
// Private Interface
|
|
327
344
|
_addStyle(style) {
|
|
@@ -457,12 +474,19 @@ class StylesXformMock extends StylesXform {
|
|
|
457
474
|
// the styleId is returned. Note: cellType is used when numFmt not defined
|
|
458
475
|
addStyleModel(model, cellType) {
|
|
459
476
|
switch (cellType) {
|
|
477
|
+
case Enums.ValueType.Checkbox:
|
|
478
|
+
// Checkbox rendering relies on style extensions (extLst) and workbook-level parts.
|
|
479
|
+
// The mock style manager intentionally does not build those structures.
|
|
480
|
+
throw new Error("Checkbox cells require styles to be enabled (useStyles: true)");
|
|
460
481
|
case Enums.ValueType.Date:
|
|
461
482
|
return this.dateStyleId;
|
|
462
483
|
default:
|
|
463
484
|
return 0;
|
|
464
485
|
}
|
|
465
486
|
}
|
|
487
|
+
get hasCheckboxes() {
|
|
488
|
+
return false;
|
|
489
|
+
}
|
|
466
490
|
get dateStyleId() {
|
|
467
491
|
if (!this._dateStyleId) {
|
|
468
492
|
const dateStyle = {
|
|
@@ -92,6 +92,7 @@ declare class XLSX {
|
|
|
92
92
|
PivotCacheDefinition: string;
|
|
93
93
|
PivotCacheRecords: string;
|
|
94
94
|
PivotTable: string;
|
|
95
|
+
FeaturePropertyBag: string;
|
|
95
96
|
};
|
|
96
97
|
constructor(workbook: Workbook);
|
|
97
98
|
/**
|
|
@@ -195,6 +196,7 @@ declare class XLSX {
|
|
|
195
196
|
addThemes(zip: IZipWriter, model: any): Promise<void>;
|
|
196
197
|
addOfficeRels(zip: IZipWriter, _model: any): Promise<void>;
|
|
197
198
|
addWorkbookRels(zip: IZipWriter, model: any): Promise<void>;
|
|
199
|
+
addFeaturePropertyBag(zip: IZipWriter, model: any): Promise<void>;
|
|
198
200
|
addSharedStrings(zip: IZipWriter, model: any): Promise<void>;
|
|
199
201
|
addStyles(zip: IZipWriter, model: any): Promise<void>;
|
|
200
202
|
addWorkbook(zip: IZipWriter, model: any): Promise<void>;
|
|
@@ -17,6 +17,7 @@ import { ContentTypesXform } from "./xform/core/content-types-xform.js";
|
|
|
17
17
|
import { AppXform } from "./xform/core/app-xform.js";
|
|
18
18
|
import { WorkbookXform } from "./xform/book/workbook-xform.js";
|
|
19
19
|
import { WorkSheetXform } from "./xform/sheet/worksheet-xform.js";
|
|
20
|
+
import { FeaturePropertyBagXform } from "./xform/core/feature-property-bag-xform.js";
|
|
20
21
|
import { DrawingXform } from "./xform/drawing/drawing-xform.js";
|
|
21
22
|
import { TableXform } from "./xform/table/table-xform.js";
|
|
22
23
|
import { PivotCacheRecordsXform } from "./xform/pivot-table/pivot-cache-records-xform.js";
|
|
@@ -203,6 +204,7 @@ class XLSX {
|
|
|
203
204
|
this.addTables(zip, model);
|
|
204
205
|
this.addPivotTables(zip, model);
|
|
205
206
|
await Promise.all([this.addThemes(zip, model), this.addStyles(zip, model)]);
|
|
207
|
+
await this.addFeaturePropertyBag(zip, model);
|
|
206
208
|
await this.addMedia(zip, model);
|
|
207
209
|
await Promise.all([this.addApp(zip, model), this.addCore(zip, model)]);
|
|
208
210
|
await this.addWorkbook(zip, model);
|
|
@@ -971,6 +973,14 @@ class XLSX {
|
|
|
971
973
|
Target: OOXML_REL_TARGETS.workbookSharedStrings
|
|
972
974
|
});
|
|
973
975
|
}
|
|
976
|
+
// Add FeaturePropertyBag relationship if checkboxes are used
|
|
977
|
+
if (model.hasCheckboxes) {
|
|
978
|
+
relationships.push({
|
|
979
|
+
Id: `rId${count++}`,
|
|
980
|
+
Type: XLSX.RelType.FeaturePropertyBag,
|
|
981
|
+
Target: OOXML_REL_TARGETS.workbookFeaturePropertyBag
|
|
982
|
+
});
|
|
983
|
+
}
|
|
974
984
|
(model.pivotTables || []).forEach((pivotTable) => {
|
|
975
985
|
pivotTable.rId = `rId${count++}`;
|
|
976
986
|
relationships.push({
|
|
@@ -992,6 +1002,13 @@ class XLSX {
|
|
|
992
1002
|
const xml = xform.toXml(relationships);
|
|
993
1003
|
zip.append(xml, { name: OOXML_PATHS.xlWorkbookRels });
|
|
994
1004
|
}
|
|
1005
|
+
async addFeaturePropertyBag(zip, model) {
|
|
1006
|
+
if (!model.hasCheckboxes) {
|
|
1007
|
+
return;
|
|
1008
|
+
}
|
|
1009
|
+
const xform = new FeaturePropertyBagXform();
|
|
1010
|
+
zip.append(xform.toXml({}), { name: OOXML_PATHS.xlFeaturePropertyBag });
|
|
1011
|
+
}
|
|
995
1012
|
async addSharedStrings(zip, model) {
|
|
996
1013
|
if (model.sharedStrings && model.sharedStrings.count) {
|
|
997
1014
|
zip.append(model.sharedStrings.xml, { name: OOXML_PATHS.xlSharedStrings });
|
|
@@ -1146,6 +1163,8 @@ class XLSX {
|
|
|
1146
1163
|
});
|
|
1147
1164
|
worksheetXform.prepare(worksheet, worksheetOptions);
|
|
1148
1165
|
});
|
|
1166
|
+
// ContentTypesXform expects this flag
|
|
1167
|
+
model.hasCheckboxes = model.styles.hasCheckboxes;
|
|
1149
1168
|
}
|
|
1150
1169
|
}
|
|
1151
1170
|
XLSX.RelType = RelType;
|
|
@@ -813,6 +813,40 @@ class BooleanValue {
|
|
|
813
813
|
return this.model.value.toString();
|
|
814
814
|
}
|
|
815
815
|
}
|
|
816
|
+
class CheckboxValue {
|
|
817
|
+
constructor(cell, value) {
|
|
818
|
+
this.model = {
|
|
819
|
+
address: cell.address,
|
|
820
|
+
type: Cell.Types.Checkbox,
|
|
821
|
+
value: value.checkbox
|
|
822
|
+
};
|
|
823
|
+
}
|
|
824
|
+
get value() {
|
|
825
|
+
return { checkbox: this.model.value };
|
|
826
|
+
}
|
|
827
|
+
set value(value) {
|
|
828
|
+
this.model.value = value.checkbox;
|
|
829
|
+
}
|
|
830
|
+
get type() {
|
|
831
|
+
return Cell.Types.Checkbox;
|
|
832
|
+
}
|
|
833
|
+
get effectiveType() {
|
|
834
|
+
return Cell.Types.Boolean;
|
|
835
|
+
}
|
|
836
|
+
get address() {
|
|
837
|
+
return this.model.address;
|
|
838
|
+
}
|
|
839
|
+
set address(value) {
|
|
840
|
+
this.model.address = value;
|
|
841
|
+
}
|
|
842
|
+
toCsvString() {
|
|
843
|
+
return this.model.value ? 1 : 0;
|
|
844
|
+
}
|
|
845
|
+
release() { }
|
|
846
|
+
toString() {
|
|
847
|
+
return this.model.value.toString();
|
|
848
|
+
}
|
|
849
|
+
}
|
|
816
850
|
class ErrorValue {
|
|
817
851
|
constructor(cell, value) {
|
|
818
852
|
this.model = {
|
|
@@ -902,6 +936,9 @@ const Value = {
|
|
|
902
936
|
return Cell.Types.Date;
|
|
903
937
|
}
|
|
904
938
|
if (typeof value === "object") {
|
|
939
|
+
if ("checkbox" in value && typeof value.checkbox === "boolean") {
|
|
940
|
+
return Cell.Types.Checkbox;
|
|
941
|
+
}
|
|
905
942
|
if ("text" in value && value.text && "hyperlink" in value && value.hyperlink) {
|
|
906
943
|
return Cell.Types.Hyperlink;
|
|
907
944
|
}
|
|
@@ -934,7 +971,8 @@ const Value = {
|
|
|
934
971
|
{ t: Cell.Types.SharedString, f: SharedStringValue },
|
|
935
972
|
{ t: Cell.Types.RichText, f: RichTextValue },
|
|
936
973
|
{ t: Cell.Types.Boolean, f: BooleanValue },
|
|
937
|
-
{ t: Cell.Types.Error, f: ErrorValue }
|
|
974
|
+
{ t: Cell.Types.Error, f: ErrorValue },
|
|
975
|
+
{ t: Cell.Types.Checkbox, f: CheckboxValue }
|
|
938
976
|
].reduce((p, t) => {
|
|
939
977
|
p[t.t] = t.f;
|
|
940
978
|
return p;
|
|
@@ -14,7 +14,8 @@ var ValueType;
|
|
|
14
14
|
ValueType[ValueType["RichText"] = 8] = "RichText";
|
|
15
15
|
ValueType[ValueType["Boolean"] = 9] = "Boolean";
|
|
16
16
|
ValueType[ValueType["Error"] = 10] = "Error";
|
|
17
|
-
ValueType[ValueType["JSON"] = 11] = "JSON";
|
|
17
|
+
ValueType[ValueType["JSON"] = 11] = "JSON";
|
|
18
|
+
ValueType[ValueType["Checkbox"] = 12] = "Checkbox";
|
|
18
19
|
})(ValueType || (exports.ValueType = ValueType = {}));
|
|
19
20
|
var FormulaType;
|
|
20
21
|
(function (FormulaType) {
|