@cj-tech-master/excelts 4.0.4 → 4.1.0-canary.20260110033117.75bcac1

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.
Files changed (92) hide show
  1. package/dist/browser/index.browser.d.ts +2 -0
  2. package/dist/browser/index.browser.js +1 -0
  3. package/dist/browser/index.d.ts +2 -0
  4. package/dist/browser/index.js +1 -0
  5. package/dist/browser/modules/excel/cell.js +39 -1
  6. package/dist/browser/modules/excel/enums.d.ts +2 -1
  7. package/dist/browser/modules/excel/enums.js +2 -1
  8. package/dist/browser/modules/excel/form-control.d.ts +157 -0
  9. package/dist/browser/modules/excel/form-control.js +267 -0
  10. package/dist/browser/modules/excel/stream/workbook-writer.browser.d.ts +1 -0
  11. package/dist/browser/modules/excel/stream/workbook-writer.browser.js +19 -1
  12. package/dist/browser/modules/excel/table.d.ts +6 -2
  13. package/dist/browser/modules/excel/table.js +33 -5
  14. package/dist/browser/modules/excel/types.d.ts +5 -1
  15. package/dist/browser/modules/excel/utils/ooxml-paths.d.ts +4 -0
  16. package/dist/browser/modules/excel/utils/ooxml-paths.js +12 -2
  17. package/dist/browser/modules/excel/worksheet.d.ts +32 -0
  18. package/dist/browser/modules/excel/worksheet.js +44 -1
  19. package/dist/browser/modules/excel/xlsx/rel-type.d.ts +2 -0
  20. package/dist/browser/modules/excel/xlsx/rel-type.js +3 -1
  21. package/dist/browser/modules/excel/xlsx/xform/core/content-types-xform.js +24 -1
  22. package/dist/browser/modules/excel/xlsx/xform/core/feature-property-bag-xform.d.ts +8 -0
  23. package/dist/browser/modules/excel/xlsx/xform/core/feature-property-bag-xform.js +36 -0
  24. package/dist/browser/modules/excel/xlsx/xform/drawing/ctrl-prop-xform.d.ts +22 -0
  25. package/dist/browser/modules/excel/xlsx/xform/drawing/ctrl-prop-xform.js +52 -0
  26. package/dist/browser/modules/excel/xlsx/xform/drawing/vml-drawing-xform.d.ts +44 -0
  27. package/dist/browser/modules/excel/xlsx/xform/drawing/vml-drawing-xform.js +181 -0
  28. package/dist/browser/modules/excel/xlsx/xform/sheet/cell-xform.js +5 -0
  29. package/dist/browser/modules/excel/xlsx/xform/sheet/worksheet-xform.js +24 -1
  30. package/dist/browser/modules/excel/xlsx/xform/style/style-xform.d.ts +2 -0
  31. package/dist/browser/modules/excel/xlsx/xform/style/style-xform.js +11 -0
  32. package/dist/browser/modules/excel/xlsx/xform/style/styles-xform.d.ts +2 -0
  33. package/dist/browser/modules/excel/xlsx/xform/style/styles-xform.js +28 -4
  34. package/dist/browser/modules/excel/xlsx/xlsx.browser.d.ts +3 -0
  35. package/dist/browser/modules/excel/xlsx/xlsx.browser.js +43 -5
  36. package/dist/cjs/index.js +3 -1
  37. package/dist/cjs/modules/excel/cell.js +39 -1
  38. package/dist/cjs/modules/excel/enums.js +2 -1
  39. package/dist/cjs/modules/excel/form-control.js +270 -0
  40. package/dist/cjs/modules/excel/stream/workbook-writer.browser.js +19 -1
  41. package/dist/cjs/modules/excel/table.js +33 -5
  42. package/dist/cjs/modules/excel/utils/ooxml-paths.js +14 -2
  43. package/dist/cjs/modules/excel/worksheet.js +44 -1
  44. package/dist/cjs/modules/excel/xlsx/rel-type.js +3 -1
  45. package/dist/cjs/modules/excel/xlsx/xform/core/content-types-xform.js +23 -0
  46. package/dist/cjs/modules/excel/xlsx/xform/core/feature-property-bag-xform.js +39 -0
  47. package/dist/cjs/modules/excel/xlsx/xform/drawing/ctrl-prop-xform.js +55 -0
  48. package/dist/cjs/modules/excel/xlsx/xform/drawing/vml-drawing-xform.js +184 -0
  49. package/dist/cjs/modules/excel/xlsx/xform/sheet/cell-xform.js +5 -0
  50. package/dist/cjs/modules/excel/xlsx/xform/sheet/worksheet-xform.js +23 -0
  51. package/dist/cjs/modules/excel/xlsx/xform/style/style-xform.js +11 -0
  52. package/dist/cjs/modules/excel/xlsx/xform/style/styles-xform.js +28 -4
  53. package/dist/cjs/modules/excel/xlsx/xlsx.browser.js +42 -4
  54. package/dist/esm/index.browser.js +1 -0
  55. package/dist/esm/index.js +1 -0
  56. package/dist/esm/modules/excel/cell.js +39 -1
  57. package/dist/esm/modules/excel/enums.js +2 -1
  58. package/dist/esm/modules/excel/form-control.js +267 -0
  59. package/dist/esm/modules/excel/stream/workbook-writer.browser.js +19 -1
  60. package/dist/esm/modules/excel/table.js +33 -5
  61. package/dist/esm/modules/excel/utils/ooxml-paths.js +12 -2
  62. package/dist/esm/modules/excel/worksheet.js +44 -1
  63. package/dist/esm/modules/excel/xlsx/rel-type.js +3 -1
  64. package/dist/esm/modules/excel/xlsx/xform/core/content-types-xform.js +24 -1
  65. package/dist/esm/modules/excel/xlsx/xform/core/feature-property-bag-xform.js +36 -0
  66. package/dist/esm/modules/excel/xlsx/xform/drawing/ctrl-prop-xform.js +52 -0
  67. package/dist/esm/modules/excel/xlsx/xform/drawing/vml-drawing-xform.js +181 -0
  68. package/dist/esm/modules/excel/xlsx/xform/sheet/cell-xform.js +5 -0
  69. package/dist/esm/modules/excel/xlsx/xform/sheet/worksheet-xform.js +24 -1
  70. package/dist/esm/modules/excel/xlsx/xform/style/style-xform.js +11 -0
  71. package/dist/esm/modules/excel/xlsx/xform/style/styles-xform.js +28 -4
  72. package/dist/esm/modules/excel/xlsx/xlsx.browser.js +43 -5
  73. package/dist/iife/excelts.iife.js +629 -40
  74. package/dist/iife/excelts.iife.js.map +1 -1
  75. package/dist/iife/excelts.iife.min.js +30 -30
  76. package/dist/types/index.browser.d.ts +2 -0
  77. package/dist/types/index.d.ts +2 -0
  78. package/dist/types/modules/excel/enums.d.ts +2 -1
  79. package/dist/types/modules/excel/form-control.d.ts +157 -0
  80. package/dist/types/modules/excel/stream/workbook-writer.browser.d.ts +1 -0
  81. package/dist/types/modules/excel/table.d.ts +6 -2
  82. package/dist/types/modules/excel/types.d.ts +5 -1
  83. package/dist/types/modules/excel/utils/ooxml-paths.d.ts +4 -0
  84. package/dist/types/modules/excel/worksheet.d.ts +32 -0
  85. package/dist/types/modules/excel/xlsx/rel-type.d.ts +2 -0
  86. package/dist/types/modules/excel/xlsx/xform/core/feature-property-bag-xform.d.ts +8 -0
  87. package/dist/types/modules/excel/xlsx/xform/drawing/ctrl-prop-xform.d.ts +22 -0
  88. package/dist/types/modules/excel/xlsx/xform/drawing/vml-drawing-xform.d.ts +44 -0
  89. package/dist/types/modules/excel/xlsx/xform/style/style-xform.d.ts +2 -0
  90. package/dist/types/modules/excel/xlsx/xform/style/styles-xform.d.ts +2 -0
  91. package/dist/types/modules/excel/xlsx/xlsx.browser.d.ts +3 -0
  92. package/package.json +9 -9
@@ -0,0 +1,181 @@
1
+ import { XmlStream } from "../../../utils/xml-stream.js";
2
+ import { BaseXform } from "../base-xform.js";
3
+ import { VmlShapeXform } from "../comment/vml-shape-xform.js";
4
+ import { FormCheckbox } from "../../../form-control.js";
5
+ class VmlDrawingXform extends BaseXform {
6
+ constructor() {
7
+ super();
8
+ this.map = {
9
+ "v:shape": new VmlShapeXform()
10
+ };
11
+ this.model = { comments: [], formControls: [] };
12
+ }
13
+ get tag() {
14
+ return "xml";
15
+ }
16
+ /**
17
+ * Render VML drawing containing both notes and form controls
18
+ */
19
+ render(xmlStream, model) {
20
+ const renderModel = model || this.model;
21
+ const hasComments = renderModel.comments && renderModel.comments.length > 0;
22
+ const hasFormControls = renderModel.formControls && renderModel.formControls.length > 0;
23
+ xmlStream.openXml(XmlStream.StdDocAttributes);
24
+ xmlStream.openNode(this.tag, VmlDrawingXform.DRAWING_ATTRIBUTES);
25
+ // Shape layout - shared by both notes and form controls
26
+ xmlStream.openNode("o:shapelayout", { "v:ext": "edit" });
27
+ xmlStream.leafNode("o:idmap", { "v:ext": "edit", data: 1 });
28
+ xmlStream.closeNode();
29
+ // Shapetype 202 for notes/comments
30
+ if (hasComments) {
31
+ xmlStream.openNode("v:shapetype", {
32
+ id: "_x0000_t202",
33
+ coordsize: "21600,21600",
34
+ "o:spt": 202,
35
+ path: "m,l,21600r21600,l21600,xe"
36
+ });
37
+ xmlStream.leafNode("v:stroke", { joinstyle: "miter" });
38
+ xmlStream.leafNode("v:path", { gradientshapeok: "t", "o:connecttype": "rect" });
39
+ xmlStream.closeNode();
40
+ }
41
+ // Shapetype 201 for form control checkboxes
42
+ if (hasFormControls) {
43
+ xmlStream.openNode("v:shapetype", {
44
+ id: "_x0000_t201",
45
+ coordsize: "21600,21600",
46
+ "o:spt": "201",
47
+ path: "m,l,21600r21600,l21600,xe"
48
+ });
49
+ xmlStream.leafNode("v:stroke", { joinstyle: "miter" });
50
+ xmlStream.leafNode("v:path", {
51
+ shadowok: "f",
52
+ "o:extrusionok": "f",
53
+ strokeok: "f",
54
+ fillok: "f",
55
+ "o:connecttype": "rect"
56
+ });
57
+ xmlStream.leafNode("o:lock", { "v:ext": "edit", shapetype: "t" });
58
+ xmlStream.closeNode();
59
+ }
60
+ // Render comment shapes
61
+ if (hasComments) {
62
+ const comments = renderModel.comments;
63
+ for (let i = 0; i < comments.length; i++) {
64
+ this.map["v:shape"].render(xmlStream, comments[i], i);
65
+ }
66
+ }
67
+ // Render form control shapes
68
+ if (hasFormControls) {
69
+ for (const control of renderModel.formControls) {
70
+ this._renderCheckboxShape(xmlStream, control);
71
+ }
72
+ }
73
+ xmlStream.closeNode();
74
+ }
75
+ /**
76
+ * Render a checkbox form control shape
77
+ */
78
+ _renderCheckboxShape(xmlStream, control) {
79
+ const shapeAttrs = {
80
+ id: `_x0000_s${control.shapeId}`,
81
+ type: "#_x0000_t201",
82
+ style: FormCheckbox.getVmlStyle(control),
83
+ "o:insetmode": "auto",
84
+ fillcolor: "buttonFace [67]",
85
+ strokecolor: "windowText [64]",
86
+ "o:preferrelative": "t",
87
+ filled: "f",
88
+ stroked: "f"
89
+ };
90
+ xmlStream.openNode("v:shape", shapeAttrs);
91
+ // Fill element
92
+ xmlStream.leafNode("v:fill", { "o:detectmouseclick": "t" });
93
+ // Lock element
94
+ xmlStream.leafNode("o:lock", { "v:ext": "edit", text: "t" });
95
+ // Textbox for label
96
+ if (control.text) {
97
+ xmlStream.openNode("v:textbox", {
98
+ style: "mso-direction-alt:auto",
99
+ "o:singleclick": "t"
100
+ });
101
+ xmlStream.openNode("div", { style: "text-align:left" });
102
+ xmlStream.openNode("font", { face: "Tahoma", size: "160", color: "auto" });
103
+ xmlStream.writeText(control.text);
104
+ xmlStream.closeNode(); // font
105
+ xmlStream.closeNode(); // div
106
+ xmlStream.closeNode(); // v:textbox
107
+ }
108
+ // ClientData - the core of the checkbox control
109
+ xmlStream.openNode("x:ClientData", { ObjectType: "Checkbox" });
110
+ // Anchor position
111
+ xmlStream.openNode("x:Anchor");
112
+ xmlStream.writeText(FormCheckbox.getVmlAnchor(control));
113
+ xmlStream.closeNode();
114
+ // Print settings
115
+ xmlStream.leafNode("x:PrintObject", undefined, control.print ? "True" : "False");
116
+ xmlStream.leafNode("x:AutoFill", undefined, "False");
117
+ xmlStream.leafNode("x:AutoLine", undefined, "False");
118
+ xmlStream.leafNode("x:TextVAlign", undefined, "Center");
119
+ // Linked cell
120
+ if (control.link) {
121
+ xmlStream.leafNode("x:FmlaLink", undefined, control.link);
122
+ }
123
+ // 3D appearance
124
+ if (control.noThreeD) {
125
+ xmlStream.leafNode("x:NoThreeD");
126
+ }
127
+ // Checked state (0 = unchecked, 1 = checked, 2 = mixed)
128
+ xmlStream.leafNode("x:Checked", undefined, String(FormCheckbox.getVmlCheckedValue(control)));
129
+ xmlStream.closeNode(); // x:ClientData
130
+ xmlStream.closeNode(); // v:shape
131
+ }
132
+ // Parsing - delegate to VmlShapeXform for notes
133
+ parseOpen(node) {
134
+ if (this.parser) {
135
+ this.parser.parseOpen(node);
136
+ return true;
137
+ }
138
+ switch (node.name) {
139
+ case this.tag:
140
+ this.reset();
141
+ this.model = {
142
+ comments: [],
143
+ formControls: []
144
+ };
145
+ break;
146
+ default:
147
+ this.parser = this.map[node.name];
148
+ if (this.parser) {
149
+ this.parser.parseOpen(node);
150
+ }
151
+ break;
152
+ }
153
+ return true;
154
+ }
155
+ parseText(text) {
156
+ if (this.parser) {
157
+ this.parser.parseText(text);
158
+ }
159
+ }
160
+ parseClose(name) {
161
+ if (this.parser) {
162
+ if (!this.parser.parseClose(name)) {
163
+ this.model.comments.push(this.parser.model);
164
+ this.parser = undefined;
165
+ }
166
+ return true;
167
+ }
168
+ switch (name) {
169
+ case this.tag:
170
+ return false;
171
+ default:
172
+ return true;
173
+ }
174
+ }
175
+ }
176
+ VmlDrawingXform.DRAWING_ATTRIBUTES = {
177
+ "xmlns:v": "urn:schemas-microsoft-com:vml",
178
+ "xmlns:o": "urn:schemas-microsoft-com:office:office",
179
+ "xmlns:x": "urn:schemas-microsoft-com:office:excel"
180
+ };
181
+ export { VmlDrawingXform };
@@ -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);
@@ -26,7 +26,7 @@ import { ColBreaksXform } from "./col-breaks-xform.js";
26
26
  import { HeaderFooterXform } from "./header-footer-xform.js";
27
27
  import { ConditionalFormattingsXform } from "./cf/conditional-formattings-xform.js";
28
28
  import { ExtLstXform } from "./ext-lst-xform.js";
29
- import { commentsRelTargetFromWorksheet, drawingRelTargetFromWorksheet, mediaRelTargetFromRels, pivotTableRelTargetFromWorksheet, tableRelTargetFromWorksheet, vmlDrawingRelTargetFromWorksheet } from "../../../utils/ooxml-paths.js";
29
+ import { commentsRelTargetFromWorksheet, ctrlPropRelTargetFromWorksheet, drawingRelTargetFromWorksheet, mediaRelTargetFromRels, pivotTableRelTargetFromWorksheet, tableRelTargetFromWorksheet, vmlDrawingRelTargetFromWorksheet } from "../../../utils/ooxml-paths.js";
30
30
  const mergeRule = (rule, extRule) => {
31
31
  Object.keys(extRule).forEach(key => {
32
32
  const value = rule[key];
@@ -275,6 +275,29 @@ class WorkSheetXform extends BaseXform {
275
275
  Target: pivotTableRelTargetFromWorksheet(pivotTable.tableNumber)
276
276
  });
277
277
  });
278
+ // prepare form controls (legacy checkboxes)
279
+ // Form controls share the VML file with comments, but need separate ctrlProp relationships
280
+ if (model.formControls && model.formControls.length > 0) {
281
+ // If no comments, we need to add the VML drawing relationship for form controls
282
+ if (model.comments.length === 0) {
283
+ rels.push({
284
+ Id: nextRid(rels),
285
+ Type: RelType.VmlDrawing,
286
+ Target: vmlDrawingRelTargetFromWorksheet(model.id)
287
+ });
288
+ }
289
+ // Add ctrlProp relationships for each form control
290
+ for (const control of model.formControls) {
291
+ const globalCtrlPropId = options.formControlRefs.length + 1;
292
+ control.ctrlPropId = globalCtrlPropId;
293
+ rels.push({
294
+ Id: nextRid(rels),
295
+ Type: RelType.CtrlProp,
296
+ Target: ctrlPropRelTargetFromWorksheet(globalCtrlPropId)
297
+ });
298
+ options.formControlRefs.push(globalCtrlPropId);
299
+ }
300
+ }
278
301
  // prepare ext items
279
302
  this.map.extLst.prepare(model, options);
280
303
  }
@@ -9,6 +9,8 @@ interface StyleModel {
9
9
  xfId?: number;
10
10
  alignment?: any;
11
11
  protection?: any;
12
+ checkbox?: boolean;
13
+ xfComplementIndex?: number;
12
14
  }
13
15
  interface StyleOptions {
14
16
  xfId?: boolean;
@@ -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
- // if we have seen this style object before, assume it has the same styleId
226
- if (this.weakMap && this.weakMap.has(model)) {
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,8 @@ declare class XLSX {
92
92
  PivotCacheDefinition: string;
93
93
  PivotCacheRecords: string;
94
94
  PivotTable: string;
95
+ FeaturePropertyBag: string;
96
+ CtrlProp: string;
95
97
  };
96
98
  constructor(workbook: Workbook);
97
99
  /**
@@ -195,6 +197,7 @@ declare class XLSX {
195
197
  addThemes(zip: IZipWriter, model: any): Promise<void>;
196
198
  addOfficeRels(zip: IZipWriter, _model: any): Promise<void>;
197
199
  addWorkbookRels(zip: IZipWriter, model: any): Promise<void>;
200
+ addFeaturePropertyBag(zip: IZipWriter, model: any): Promise<void>;
198
201
  addSharedStrings(zip: IZipWriter, model: any): Promise<void>;
199
202
  addStyles(zip: IZipWriter, model: any): Promise<void>;
200
203
  addWorkbook(zip: IZipWriter, model: any): Promise<void>;
@@ -17,13 +17,15 @@ 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";
23
24
  import { PivotCacheDefinitionXform } from "./xform/pivot-table/pivot-cache-definition-xform.js";
24
25
  import { PivotTableXform } from "./xform/pivot-table/pivot-table-xform.js";
25
26
  import { CommentsXform } from "./xform/comment/comments-xform.js";
26
- import { VmlNotesXform } from "./xform/comment/vml-notes-xform.js";
27
+ import { VmlDrawingXform } from "./xform/drawing/vml-drawing-xform.js";
28
+ import { CtrlPropXform } from "./xform/drawing/ctrl-prop-xform.js";
27
29
  import { theme1Xml } from "./xml/theme1.js";
28
30
  import { RelType } from "./rel-type.js";
29
31
  import { StreamBuf } from "../utils/stream-buf.js";
@@ -31,7 +33,7 @@ import { bufferToString, base64ToUint8Array } from "../../../utils/utils.browser
31
33
  import { StreamingZip, ZipDeflateFile } from "../../archive/streaming-zip.js";
32
34
  import { ZipParser } from "../../archive/index.browser.js";
33
35
  import { PassThrough, concatUint8Arrays } from "../../stream/index.browser.js";
34
- import { commentsPath, commentsRelTargetFromWorksheetName, drawingPath, drawingRelsPath, OOXML_REL_TARGETS, pivotTableRelTargetFromWorksheetName, pivotCacheDefinitionRelTargetFromWorkbook, getCommentsIndexFromPath, getDrawingNameFromPath, getDrawingNameFromRelsPath, getMediaFilenameFromPath, mediaPath, getPivotCacheDefinitionNameFromPath, getPivotCacheDefinitionNameFromRelsPath, getPivotCacheRecordsNameFromPath, getPivotTableNameFromPath, getPivotTableNameFromRelsPath, pivotCacheDefinitionPath, pivotCacheDefinitionRelsPath, pivotCacheDefinitionRelTargetFromPivotTable, pivotCacheRecordsPath, pivotCacheRecordsRelTarget, pivotTablePath, pivotTableRelsPath, getTableNameFromPath, tablePath, tableRelTargetFromWorksheetName, themePath, getThemeNameFromPath, getVmlDrawingNameFromPath, getWorksheetNoFromWorksheetPath, getWorksheetNoFromWorksheetRelsPath, isBinaryEntryPath, normalizeZipPath, OOXML_PATHS, vmlDrawingRelTargetFromWorksheetName, vmlDrawingPath, worksheetPath, worksheetRelsPath } from "../utils/ooxml-paths.js";
36
+ import { commentsPath, commentsRelTargetFromWorksheetName, ctrlPropPath, drawingPath, drawingRelsPath, OOXML_REL_TARGETS, pivotTableRelTargetFromWorksheetName, pivotCacheDefinitionRelTargetFromWorkbook, getCommentsIndexFromPath, getDrawingNameFromPath, getDrawingNameFromRelsPath, getMediaFilenameFromPath, mediaPath, getPivotCacheDefinitionNameFromPath, getPivotCacheDefinitionNameFromRelsPath, getPivotCacheRecordsNameFromPath, getPivotTableNameFromPath, getPivotTableNameFromRelsPath, pivotCacheDefinitionPath, pivotCacheDefinitionRelsPath, pivotCacheDefinitionRelTargetFromPivotTable, pivotCacheRecordsPath, pivotCacheRecordsRelTarget, pivotTablePath, pivotTableRelsPath, getTableNameFromPath, tablePath, tableRelTargetFromWorksheetName, themePath, getThemeNameFromPath, getVmlDrawingNameFromPath, getWorksheetNoFromWorksheetPath, getWorksheetNoFromWorksheetRelsPath, isBinaryEntryPath, normalizeZipPath, OOXML_PATHS, vmlDrawingRelTargetFromWorksheetName, vmlDrawingPath, worksheetPath, worksheetRelsPath } from "../utils/ooxml-paths.js";
35
37
  class StreamingZipWriterAdapter {
36
38
  constructor(options) {
37
39
  this.events = new Map();
@@ -203,6 +205,7 @@ class XLSX {
203
205
  this.addTables(zip, model);
204
206
  this.addPivotTables(zip, model);
205
207
  await Promise.all([this.addThemes(zip, model), this.addStyles(zip, model)]);
208
+ await this.addFeaturePropertyBag(zip, model);
206
209
  await this.addMedia(zip, model);
207
210
  await Promise.all([this.addApp(zip, model), this.addCore(zip, model)]);
208
211
  await this.addWorkbook(zip, model);
@@ -700,7 +703,7 @@ class XLSX {
700
703
  model.drawingRels[name] = relationships;
701
704
  }
702
705
  async _processVmlDrawingEntry(entry, model, name) {
703
- const xform = new VmlNotesXform();
706
+ const xform = new VmlDrawingXform();
704
707
  const vmlDrawing = await xform.parseStream(entry);
705
708
  model.vmlDrawings[vmlDrawingRelTargetFromWorksheetName(name)] = vmlDrawing;
706
709
  }
@@ -971,6 +974,14 @@ class XLSX {
971
974
  Target: OOXML_REL_TARGETS.workbookSharedStrings
972
975
  });
973
976
  }
977
+ // Add FeaturePropertyBag relationship if checkboxes are used
978
+ if (model.hasCheckboxes) {
979
+ relationships.push({
980
+ Id: `rId${count++}`,
981
+ Type: XLSX.RelType.FeaturePropertyBag,
982
+ Target: OOXML_REL_TARGETS.workbookFeaturePropertyBag
983
+ });
984
+ }
974
985
  (model.pivotTables || []).forEach((pivotTable) => {
975
986
  pivotTable.rId = `rId${count++}`;
976
987
  relationships.push({
@@ -992,6 +1003,13 @@ class XLSX {
992
1003
  const xml = xform.toXml(relationships);
993
1004
  zip.append(xml, { name: OOXML_PATHS.xlWorkbookRels });
994
1005
  }
1006
+ async addFeaturePropertyBag(zip, model) {
1007
+ if (!model.hasCheckboxes) {
1008
+ return;
1009
+ }
1010
+ const xform = new FeaturePropertyBagXform();
1011
+ zip.append(xform.toXml({}), { name: OOXML_PATHS.xlFeaturePropertyBag });
1012
+ }
995
1013
  async addSharedStrings(zip, model) {
996
1014
  if (model.sharedStrings && model.sharedStrings.count) {
997
1015
  zip.append(model.sharedStrings.xml, { name: OOXML_PATHS.xlSharedStrings });
@@ -1011,7 +1029,8 @@ class XLSX {
1011
1029
  const worksheetXform = new WorkSheetXform();
1012
1030
  const relationshipsXform = new RelationshipsXform();
1013
1031
  const commentsXform = new CommentsXform();
1014
- const vmlNotesXform = new VmlNotesXform();
1032
+ const vmlDrawingXform = new VmlDrawingXform();
1033
+ const ctrlPropXform = new CtrlPropXform();
1015
1034
  model.worksheets.forEach((worksheet, index) => {
1016
1035
  const fileIndex = worksheet.fileIndex || index + 1;
1017
1036
  let xmlStream = new XmlStream();
@@ -1022,14 +1041,30 @@ class XLSX {
1022
1041
  relationshipsXform.render(xmlStream, worksheet.rels);
1023
1042
  zip.append(xmlStream.xml, { name: worksheetRelsPath(fileIndex) });
1024
1043
  }
1044
+ // Generate comments XML (separate from VML)
1025
1045
  if (worksheet.comments.length > 0) {
1026
1046
  xmlStream = new XmlStream();
1027
1047
  commentsXform.render(xmlStream, worksheet);
1028
1048
  zip.append(xmlStream.xml, { name: commentsPath(fileIndex) });
1049
+ }
1050
+ // Generate unified VML drawing (contains both notes and form controls)
1051
+ const hasComments = worksheet.comments.length > 0;
1052
+ const hasFormControls = worksheet.formControls && worksheet.formControls.length > 0;
1053
+ if (hasComments || hasFormControls) {
1029
1054
  xmlStream = new XmlStream();
1030
- vmlNotesXform.render(xmlStream, worksheet);
1055
+ vmlDrawingXform.render(xmlStream, {
1056
+ comments: hasComments ? worksheet.comments : [],
1057
+ formControls: hasFormControls ? worksheet.formControls : []
1058
+ });
1031
1059
  zip.append(xmlStream.xml, { name: vmlDrawingPath(fileIndex) });
1032
1060
  }
1061
+ // Generate ctrlProp files for form controls
1062
+ if (hasFormControls) {
1063
+ worksheet.formControls.forEach((control) => {
1064
+ const xml = ctrlPropXform.toXml(control);
1065
+ zip.append(xml, { name: ctrlPropPath(control.ctrlPropId) });
1066
+ });
1067
+ }
1033
1068
  });
1034
1069
  }
1035
1070
  addDrawings(zip, model) {
@@ -1135,6 +1170,7 @@ class XLSX {
1135
1170
  };
1136
1171
  worksheetOptions.drawings = model.drawings = [];
1137
1172
  worksheetOptions.commentRefs = model.commentRefs = [];
1173
+ worksheetOptions.formControlRefs = model.formControlRefs = [];
1138
1174
  let tableCount = 0;
1139
1175
  model.tables = [];
1140
1176
  model.worksheets.forEach((worksheet) => {
@@ -1146,6 +1182,8 @@ class XLSX {
1146
1182
  });
1147
1183
  worksheetXform.prepare(worksheet, worksheetOptions);
1148
1184
  });
1185
+ // ContentTypesXform expects this flag
1186
+ model.hasCheckboxes = model.styles.hasCheckboxes;
1149
1187
  }
1150
1188
  }
1151
1189
  XLSX.RelType = RelType;
package/dist/cjs/index.js CHANGED
@@ -17,7 +17,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
17
17
  for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
18
18
  };
19
19
  Object.defineProperty(exports, "__esModule", { value: true });
20
- exports.CsvFormatterStream = exports.CsvParserStream = exports.WorksheetReader = exports.WorksheetWriter = exports.WorkbookReader = exports.WorkbookWriter = exports.DataValidations = exports.Table = exports.Image = exports.Range = exports.Cell = exports.Column = exports.Row = exports.Worksheet = exports.Workbook = void 0;
20
+ exports.CsvFormatterStream = exports.CsvParserStream = exports.WorksheetReader = exports.WorksheetWriter = exports.WorkbookReader = exports.WorkbookWriter = exports.FormCheckbox = exports.DataValidations = exports.Table = exports.Image = exports.Range = exports.Cell = exports.Column = exports.Row = exports.Worksheet = exports.Workbook = void 0;
21
21
  var workbook_1 = require("./modules/excel/workbook.js");
22
22
  Object.defineProperty(exports, "Workbook", { enumerable: true, get: function () { return workbook_1.Workbook; } });
23
23
  var worksheet_1 = require("./modules/excel/worksheet.js");
@@ -37,6 +37,8 @@ var table_1 = require("./modules/excel/table.js");
37
37
  Object.defineProperty(exports, "Table", { enumerable: true, get: function () { return table_1.Table; } });
38
38
  var data_validations_1 = require("./modules/excel/data-validations.js");
39
39
  Object.defineProperty(exports, "DataValidations", { enumerable: true, get: function () { return data_validations_1.DataValidations; } });
40
+ var form_control_1 = require("./modules/excel/form-control.js");
41
+ Object.defineProperty(exports, "FormCheckbox", { enumerable: true, get: function () { return form_control_1.FormCheckbox; } });
40
42
  // =============================================================================
41
43
  // Node.js Only: Streaming Classes
42
44
  // These can also be accessed via Workbook.createStreamWriter/createStreamReader
@@ -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"; // Internal type for JSON values that serialize as String
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) {