@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.
- package/dist/browser/index.browser.d.ts +2 -0
- package/dist/browser/index.browser.js +1 -0
- package/dist/browser/index.d.ts +2 -0
- package/dist/browser/index.js +1 -0
- 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/form-control.d.ts +157 -0
- package/dist/browser/modules/excel/form-control.js +267 -0
- 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 +4 -0
- package/dist/browser/modules/excel/utils/ooxml-paths.js +12 -2
- package/dist/browser/modules/excel/worksheet.d.ts +32 -0
- package/dist/browser/modules/excel/worksheet.js +44 -1
- package/dist/browser/modules/excel/xlsx/rel-type.d.ts +2 -0
- package/dist/browser/modules/excel/xlsx/rel-type.js +3 -1
- package/dist/browser/modules/excel/xlsx/xform/core/content-types-xform.js +24 -1
- 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/drawing/ctrl-prop-xform.d.ts +22 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/ctrl-prop-xform.js +52 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/vml-drawing-xform.d.ts +44 -0
- package/dist/browser/modules/excel/xlsx/xform/drawing/vml-drawing-xform.js +181 -0
- package/dist/browser/modules/excel/xlsx/xform/sheet/cell-xform.js +5 -0
- package/dist/browser/modules/excel/xlsx/xform/sheet/worksheet-xform.js +24 -1
- 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 +3 -0
- package/dist/browser/modules/excel/xlsx/xlsx.browser.js +43 -5
- package/dist/cjs/index.js +3 -1
- package/dist/cjs/modules/excel/cell.js +39 -1
- package/dist/cjs/modules/excel/enums.js +2 -1
- package/dist/cjs/modules/excel/form-control.js +270 -0
- 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 +14 -2
- package/dist/cjs/modules/excel/worksheet.js +44 -1
- package/dist/cjs/modules/excel/xlsx/rel-type.js +3 -1
- package/dist/cjs/modules/excel/xlsx/xform/core/content-types-xform.js +23 -0
- package/dist/cjs/modules/excel/xlsx/xform/core/feature-property-bag-xform.js +39 -0
- package/dist/cjs/modules/excel/xlsx/xform/drawing/ctrl-prop-xform.js +55 -0
- package/dist/cjs/modules/excel/xlsx/xform/drawing/vml-drawing-xform.js +184 -0
- package/dist/cjs/modules/excel/xlsx/xform/sheet/cell-xform.js +5 -0
- package/dist/cjs/modules/excel/xlsx/xform/sheet/worksheet-xform.js +23 -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 +42 -4
- package/dist/esm/index.browser.js +1 -0
- package/dist/esm/index.js +1 -0
- package/dist/esm/modules/excel/cell.js +39 -1
- package/dist/esm/modules/excel/enums.js +2 -1
- package/dist/esm/modules/excel/form-control.js +267 -0
- 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 +12 -2
- package/dist/esm/modules/excel/worksheet.js +44 -1
- package/dist/esm/modules/excel/xlsx/rel-type.js +3 -1
- package/dist/esm/modules/excel/xlsx/xform/core/content-types-xform.js +24 -1
- package/dist/esm/modules/excel/xlsx/xform/core/feature-property-bag-xform.js +36 -0
- package/dist/esm/modules/excel/xlsx/xform/drawing/ctrl-prop-xform.js +52 -0
- package/dist/esm/modules/excel/xlsx/xform/drawing/vml-drawing-xform.js +181 -0
- package/dist/esm/modules/excel/xlsx/xform/sheet/cell-xform.js +5 -0
- package/dist/esm/modules/excel/xlsx/xform/sheet/worksheet-xform.js +24 -1
- 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 +43 -5
- package/dist/iife/excelts.iife.js +629 -40
- package/dist/iife/excelts.iife.js.map +1 -1
- package/dist/iife/excelts.iife.min.js +30 -30
- package/dist/types/index.browser.d.ts +2 -0
- package/dist/types/index.d.ts +2 -0
- package/dist/types/modules/excel/enums.d.ts +2 -1
- package/dist/types/modules/excel/form-control.d.ts +157 -0
- 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 +4 -0
- package/dist/types/modules/excel/worksheet.d.ts +32 -0
- package/dist/types/modules/excel/xlsx/rel-type.d.ts +2 -0
- package/dist/types/modules/excel/xlsx/xform/core/feature-property-bag-xform.d.ts +8 -0
- package/dist/types/modules/excel/xlsx/xform/drawing/ctrl-prop-xform.d.ts +22 -0
- package/dist/types/modules/excel/xlsx/xform/drawing/vml-drawing-xform.d.ts +44 -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 +3 -0
- package/package.json +9 -9
|
@@ -0,0 +1,270 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FormCheckbox = exports.EMU_PER_POINT = exports.EMU_PER_PIXEL = void 0;
|
|
4
|
+
const col_cache_1 = require("./utils/col-cache.js");
|
|
5
|
+
/**
|
|
6
|
+
* Form Control Checkbox - Legacy checkbox control compatible with Office 2007+ and WPS/LibreOffice
|
|
7
|
+
*
|
|
8
|
+
* Unlike the modern In-Cell Checkbox (which only works in Microsoft 365),
|
|
9
|
+
* Form Control Checkboxes are floating controls that work in virtually all
|
|
10
|
+
* spreadsheet applications.
|
|
11
|
+
*/
|
|
12
|
+
// ============================================================================
|
|
13
|
+
// Constants (exported for use by xforms)
|
|
14
|
+
// ============================================================================
|
|
15
|
+
/** EMU (English Metric Units) to pixels conversion factor at 96 DPI */
|
|
16
|
+
exports.EMU_PER_PIXEL = 9525;
|
|
17
|
+
/** EMU to points conversion factor */
|
|
18
|
+
exports.EMU_PER_POINT = 12700;
|
|
19
|
+
/** Default column offset in EMUs (~15 pixels) */
|
|
20
|
+
const DEFAULT_COL_OFF = 142875;
|
|
21
|
+
/** Default row offset in EMUs (~3 pixels) */
|
|
22
|
+
const DEFAULT_ROW_OFF = 28575;
|
|
23
|
+
/** Default end column offset in EMUs (~29 pixels) */
|
|
24
|
+
const DEFAULT_END_COL_OFF = 276225;
|
|
25
|
+
/** Default end row offset in EMUs (~20 pixels) */
|
|
26
|
+
const DEFAULT_END_ROW_OFF = 190500;
|
|
27
|
+
// ============================================================================
|
|
28
|
+
// FormCheckbox Class
|
|
29
|
+
// ============================================================================
|
|
30
|
+
class FormCheckbox {
|
|
31
|
+
constructor(worksheet, range, options) {
|
|
32
|
+
this.worksheet = worksheet;
|
|
33
|
+
// Parse range to get anchors
|
|
34
|
+
const { tl, br } = this._parseRange(range);
|
|
35
|
+
// Generate shape ID (starting from 1025)
|
|
36
|
+
const existingCount = worksheet.formControls?.length || 0;
|
|
37
|
+
const shapeId = 1025 + existingCount;
|
|
38
|
+
// Parse link cell reference
|
|
39
|
+
let link;
|
|
40
|
+
if (options?.link) {
|
|
41
|
+
// Ensure absolute reference format
|
|
42
|
+
link = this._toAbsoluteRef(options.link);
|
|
43
|
+
}
|
|
44
|
+
// Note: ctrlPropId is set later in worksheet-xform.ts prepare() for global uniqueness
|
|
45
|
+
this.model = {
|
|
46
|
+
shapeId,
|
|
47
|
+
ctrlPropId: 0, // Placeholder, set during prepare()
|
|
48
|
+
tl,
|
|
49
|
+
br,
|
|
50
|
+
link,
|
|
51
|
+
checked: options?.checked ? "Checked" : "Unchecked",
|
|
52
|
+
text: options?.text ?? "",
|
|
53
|
+
noThreeD: options?.noThreeD ?? true,
|
|
54
|
+
print: options?.print ?? false
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
/**
|
|
58
|
+
* Get the checked state
|
|
59
|
+
*/
|
|
60
|
+
get checked() {
|
|
61
|
+
return this.model.checked === "Checked";
|
|
62
|
+
}
|
|
63
|
+
/**
|
|
64
|
+
* Set the checked state
|
|
65
|
+
*/
|
|
66
|
+
set checked(value) {
|
|
67
|
+
this.model.checked = value ? "Checked" : "Unchecked";
|
|
68
|
+
}
|
|
69
|
+
/**
|
|
70
|
+
* Get the linked cell address
|
|
71
|
+
*/
|
|
72
|
+
get link() {
|
|
73
|
+
return this.model.link;
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Set the linked cell address
|
|
77
|
+
*/
|
|
78
|
+
set link(value) {
|
|
79
|
+
this.model.link = value ? this._toAbsoluteRef(value) : undefined;
|
|
80
|
+
}
|
|
81
|
+
/**
|
|
82
|
+
* Get the label text
|
|
83
|
+
*/
|
|
84
|
+
get text() {
|
|
85
|
+
return this.model.text;
|
|
86
|
+
}
|
|
87
|
+
/**
|
|
88
|
+
* Set the label text
|
|
89
|
+
*/
|
|
90
|
+
set text(value) {
|
|
91
|
+
this.model.text = value;
|
|
92
|
+
}
|
|
93
|
+
/**
|
|
94
|
+
* Convert cell reference to absolute format (e.g., "A1" -> "$A$1")
|
|
95
|
+
*/
|
|
96
|
+
_toAbsoluteRef(ref) {
|
|
97
|
+
// If already absolute, return as-is
|
|
98
|
+
if (ref.includes("$")) {
|
|
99
|
+
return ref;
|
|
100
|
+
}
|
|
101
|
+
// Parse and convert
|
|
102
|
+
const addr = col_cache_1.colCache.decodeAddress(ref);
|
|
103
|
+
return `$${col_cache_1.colCache.n2l(addr.col)}$${addr.row}`;
|
|
104
|
+
}
|
|
105
|
+
/**
|
|
106
|
+
* Parse range input into anchor positions
|
|
107
|
+
*/
|
|
108
|
+
_parseRange(range) {
|
|
109
|
+
let tl;
|
|
110
|
+
let br;
|
|
111
|
+
if (typeof range === "string") {
|
|
112
|
+
// Parse cell reference like "B2" or "B2:D3"
|
|
113
|
+
const decoded = col_cache_1.colCache.decode(range);
|
|
114
|
+
if ("top" in decoded) {
|
|
115
|
+
// It's a range like "B2:D3"
|
|
116
|
+
tl = {
|
|
117
|
+
col: decoded.left - 1, // Convert to 0-based
|
|
118
|
+
colOff: DEFAULT_COL_OFF,
|
|
119
|
+
row: decoded.top - 1,
|
|
120
|
+
rowOff: DEFAULT_ROW_OFF
|
|
121
|
+
};
|
|
122
|
+
br = {
|
|
123
|
+
col: decoded.right - 1,
|
|
124
|
+
colOff: DEFAULT_END_COL_OFF,
|
|
125
|
+
row: decoded.bottom - 1,
|
|
126
|
+
rowOff: DEFAULT_END_ROW_OFF
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
// Single cell reference - create default size checkbox
|
|
131
|
+
tl = {
|
|
132
|
+
col: decoded.col - 1,
|
|
133
|
+
colOff: DEFAULT_COL_OFF,
|
|
134
|
+
row: decoded.row - 1,
|
|
135
|
+
rowOff: DEFAULT_ROW_OFF
|
|
136
|
+
};
|
|
137
|
+
// Default size: about 2 columns wide, 1 row tall
|
|
138
|
+
br = {
|
|
139
|
+
col: decoded.col + 1,
|
|
140
|
+
colOff: DEFAULT_END_COL_OFF,
|
|
141
|
+
row: decoded.row,
|
|
142
|
+
rowOff: DEFAULT_END_ROW_OFF
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
else if ("startCol" in range) {
|
|
147
|
+
// startCol/startRow/endCol/endRow format (0-based)
|
|
148
|
+
tl = {
|
|
149
|
+
col: range.startCol,
|
|
150
|
+
colOff: range.startColOff ?? DEFAULT_COL_OFF,
|
|
151
|
+
row: range.startRow,
|
|
152
|
+
rowOff: range.startRowOff ?? DEFAULT_ROW_OFF
|
|
153
|
+
};
|
|
154
|
+
br = {
|
|
155
|
+
col: range.endCol,
|
|
156
|
+
colOff: range.endColOff ?? DEFAULT_END_COL_OFF,
|
|
157
|
+
row: range.endRow,
|
|
158
|
+
rowOff: range.endRowOff ?? DEFAULT_END_ROW_OFF
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
else {
|
|
162
|
+
// Object format with tl/br
|
|
163
|
+
if (typeof range.tl === "string") {
|
|
164
|
+
const decoded = col_cache_1.colCache.decodeAddress(range.tl);
|
|
165
|
+
tl = {
|
|
166
|
+
col: decoded.col - 1,
|
|
167
|
+
colOff: DEFAULT_COL_OFF,
|
|
168
|
+
row: decoded.row - 1,
|
|
169
|
+
rowOff: DEFAULT_ROW_OFF
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
else {
|
|
173
|
+
tl = {
|
|
174
|
+
col: range.tl.col,
|
|
175
|
+
colOff: range.tl.colOff ?? DEFAULT_COL_OFF,
|
|
176
|
+
row: range.tl.row,
|
|
177
|
+
rowOff: range.tl.rowOff ?? DEFAULT_ROW_OFF
|
|
178
|
+
};
|
|
179
|
+
}
|
|
180
|
+
if (range.br) {
|
|
181
|
+
if (typeof range.br === "string") {
|
|
182
|
+
const decoded = col_cache_1.colCache.decodeAddress(range.br);
|
|
183
|
+
br = {
|
|
184
|
+
col: decoded.col - 1,
|
|
185
|
+
colOff: DEFAULT_END_COL_OFF,
|
|
186
|
+
row: decoded.row - 1,
|
|
187
|
+
rowOff: DEFAULT_END_ROW_OFF
|
|
188
|
+
};
|
|
189
|
+
}
|
|
190
|
+
else {
|
|
191
|
+
br = {
|
|
192
|
+
col: range.br.col,
|
|
193
|
+
colOff: range.br.colOff ?? DEFAULT_END_COL_OFF,
|
|
194
|
+
row: range.br.row,
|
|
195
|
+
rowOff: range.br.rowOff ?? DEFAULT_END_ROW_OFF
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
else {
|
|
200
|
+
// Default size
|
|
201
|
+
br = {
|
|
202
|
+
col: tl.col + 2,
|
|
203
|
+
colOff: DEFAULT_END_COL_OFF,
|
|
204
|
+
row: tl.row + 1,
|
|
205
|
+
rowOff: DEFAULT_END_ROW_OFF
|
|
206
|
+
};
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
return { tl, br };
|
|
210
|
+
}
|
|
211
|
+
// =========================================================================
|
|
212
|
+
// Instance methods - delegate to static methods
|
|
213
|
+
// =========================================================================
|
|
214
|
+
/**
|
|
215
|
+
* Convert anchor to VML anchor string format
|
|
216
|
+
* Format: "fromCol, fromColOff, fromRow, fromRowOff, toCol, toColOff, toRow, toRowOff"
|
|
217
|
+
* VML uses pixels for offsets
|
|
218
|
+
*/
|
|
219
|
+
getVmlAnchor() {
|
|
220
|
+
return FormCheckbox.getVmlAnchor(this.model);
|
|
221
|
+
}
|
|
222
|
+
/**
|
|
223
|
+
* Get VML style string for positioning
|
|
224
|
+
*/
|
|
225
|
+
getVmlStyle() {
|
|
226
|
+
return FormCheckbox.getVmlStyle(this.model);
|
|
227
|
+
}
|
|
228
|
+
/**
|
|
229
|
+
* Get the numeric checked value for VML (0, 1, or 2)
|
|
230
|
+
*/
|
|
231
|
+
getVmlCheckedValue() {
|
|
232
|
+
return FormCheckbox.getVmlCheckedValue(this.model);
|
|
233
|
+
}
|
|
234
|
+
// =========================================================================
|
|
235
|
+
// Static utility methods - can be used with FormCheckboxModel directly
|
|
236
|
+
// =========================================================================
|
|
237
|
+
/**
|
|
238
|
+
* Convert anchor to VML anchor string format from model
|
|
239
|
+
*/
|
|
240
|
+
static getVmlAnchor(model) {
|
|
241
|
+
const { tl, br } = model;
|
|
242
|
+
const tlColOff = Math.round(tl.colOff / exports.EMU_PER_PIXEL);
|
|
243
|
+
const tlRowOff = Math.round(tl.rowOff / exports.EMU_PER_PIXEL);
|
|
244
|
+
const brColOff = Math.round(br.colOff / exports.EMU_PER_PIXEL);
|
|
245
|
+
const brRowOff = Math.round(br.rowOff / exports.EMU_PER_PIXEL);
|
|
246
|
+
return `${tl.col}, ${tlColOff}, ${tl.row}, ${tlRowOff}, ${br.col}, ${brColOff}, ${br.row}, ${brRowOff}`;
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* Get VML style string for positioning from model
|
|
250
|
+
*/
|
|
251
|
+
static getVmlStyle(model) {
|
|
252
|
+
const marginLeft = Math.round(model.tl.colOff / exports.EMU_PER_POINT);
|
|
253
|
+
const marginTop = Math.round(model.tl.rowOff / exports.EMU_PER_POINT);
|
|
254
|
+
return `position:absolute;margin-left:${marginLeft}pt;margin-top:${marginTop}pt;width:96pt;height:18pt;z-index:1;visibility:visible`;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* Get the numeric checked value for VML from model (0, 1, or 2)
|
|
258
|
+
*/
|
|
259
|
+
static getVmlCheckedValue(model) {
|
|
260
|
+
switch (model.checked) {
|
|
261
|
+
case "Checked":
|
|
262
|
+
return 1;
|
|
263
|
+
case "Mixed":
|
|
264
|
+
return 2;
|
|
265
|
+
default:
|
|
266
|
+
return 0;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
}
|
|
270
|
+
exports.FormCheckbox = FormCheckbox;
|
|
@@ -23,6 +23,7 @@ const content_types_xform_1 = require("../xlsx/xform/core/content-types-xform.js
|
|
|
23
23
|
const app_xform_1 = require("../xlsx/xform/core/app-xform.js");
|
|
24
24
|
const workbook_xform_1 = require("../xlsx/xform/book/workbook-xform.js");
|
|
25
25
|
const shared_strings_xform_1 = require("../xlsx/xform/strings/shared-strings-xform.js");
|
|
26
|
+
const feature_property_bag_xform_1 = require("../xlsx/xform/core/feature-property-bag-xform.js");
|
|
26
27
|
const theme1_1 = require("../xlsx/xml/theme1.js");
|
|
27
28
|
const _stream_1 = require("../../stream/index.js");
|
|
28
29
|
const ooxml_paths_1 = require("../utils/ooxml-paths.js");
|
|
@@ -137,6 +138,7 @@ class WorkbookWriterBase {
|
|
|
137
138
|
this.addCore(),
|
|
138
139
|
this.addSharedStrings(),
|
|
139
140
|
this.addStyles(),
|
|
141
|
+
this.addFeaturePropertyBag(),
|
|
140
142
|
this.addWorkbookRels()
|
|
141
143
|
]);
|
|
142
144
|
await this.addWorkbook();
|
|
@@ -229,7 +231,8 @@ class WorkbookWriterBase {
|
|
|
229
231
|
worksheets: this._worksheets.filter(Boolean),
|
|
230
232
|
sharedStrings: this.sharedStrings,
|
|
231
233
|
commentRefs: this.commentRefs,
|
|
232
|
-
media: this.media
|
|
234
|
+
media: this.media,
|
|
235
|
+
hasCheckboxes: this.styles.hasCheckboxes
|
|
233
236
|
};
|
|
234
237
|
const xform = new content_types_xform_1.ContentTypesXform();
|
|
235
238
|
this._addFile(xform.toXml(model), ooxml_paths_1.OOXML_PATHS.contentTypes);
|
|
@@ -283,6 +286,13 @@ class WorkbookWriterBase {
|
|
|
283
286
|
}
|
|
284
287
|
return Promise.resolve();
|
|
285
288
|
}
|
|
289
|
+
addFeaturePropertyBag() {
|
|
290
|
+
if (this.styles.hasCheckboxes) {
|
|
291
|
+
const xform = new feature_property_bag_xform_1.FeaturePropertyBagXform();
|
|
292
|
+
this._addFile(xform.toXml({}), ooxml_paths_1.OOXML_PATHS.xlFeaturePropertyBag);
|
|
293
|
+
}
|
|
294
|
+
return Promise.resolve();
|
|
295
|
+
}
|
|
286
296
|
addWorkbookRels() {
|
|
287
297
|
let count = 1;
|
|
288
298
|
const relationships = [
|
|
@@ -296,6 +306,14 @@ class WorkbookWriterBase {
|
|
|
296
306
|
Target: ooxml_paths_1.OOXML_REL_TARGETS.workbookSharedStrings
|
|
297
307
|
});
|
|
298
308
|
}
|
|
309
|
+
// Add FeaturePropertyBag relationship if checkboxes are used
|
|
310
|
+
if (this.styles.hasCheckboxes) {
|
|
311
|
+
relationships.push({
|
|
312
|
+
Id: `rId${count++}`,
|
|
313
|
+
Type: rel_type_1.RelType.FeaturePropertyBag,
|
|
314
|
+
Target: ooxml_paths_1.OOXML_REL_TARGETS.workbookFeaturePropertyBag
|
|
315
|
+
});
|
|
316
|
+
}
|
|
299
317
|
this._worksheets.forEach(ws => {
|
|
300
318
|
if (ws) {
|
|
301
319
|
ws.rId = `rId${count++}`;
|
|
@@ -60,6 +60,26 @@ class Table {
|
|
|
60
60
|
this.worksheet = worksheet;
|
|
61
61
|
if (table) {
|
|
62
62
|
this.table = table;
|
|
63
|
+
// When loading tables from xlsx, Excel stores table ranges and cell values in the worksheet,
|
|
64
|
+
// but may not embed row data into the table definition. Hydrate rows from the worksheet so
|
|
65
|
+
// table mutations (e.g. addRow) can correctly expand table ranges and serialize.
|
|
66
|
+
if (Array.isArray(table.rows) && table.rows.length === 0 && table.tableRef) {
|
|
67
|
+
const decoded = col_cache_1.colCache.decode(table.tableRef);
|
|
68
|
+
if ("dimensions" in decoded) {
|
|
69
|
+
const startRow = decoded.top + (table.headerRow === false ? 0 : 1);
|
|
70
|
+
const endRow = decoded.bottom - (table.totalsRow === true ? 1 : 0);
|
|
71
|
+
if (endRow >= startRow) {
|
|
72
|
+
for (let r = startRow; r <= endRow; r++) {
|
|
73
|
+
const row = worksheet.getRow(r);
|
|
74
|
+
const values = [];
|
|
75
|
+
for (let c = decoded.left; c <= decoded.right; c++) {
|
|
76
|
+
values.push(row.getCell(c).value);
|
|
77
|
+
}
|
|
78
|
+
table.rows.push(values);
|
|
79
|
+
}
|
|
80
|
+
}
|
|
81
|
+
}
|
|
82
|
+
}
|
|
63
83
|
// check things are ok first
|
|
64
84
|
this.validate();
|
|
65
85
|
this.store();
|
|
@@ -136,9 +156,10 @@ class Table {
|
|
|
136
156
|
const { row, col } = table.tl;
|
|
137
157
|
assert(row > 0, "Table must be on valid row");
|
|
138
158
|
assert(col > 0, "Table must be on valid col");
|
|
139
|
-
const { width,
|
|
140
|
-
// autoFilterRef is a range that
|
|
141
|
-
|
|
159
|
+
const { width, tableHeight } = this;
|
|
160
|
+
// autoFilterRef is a single-row range that targets the header row only.
|
|
161
|
+
// Excel uses this for filter buttons; including data rows can break filter rendering.
|
|
162
|
+
table.autoFilterRef = col_cache_1.colCache.encode(row, col, row, col + width - 1);
|
|
142
163
|
// tableRef is a range that includes optional headers and totals
|
|
143
164
|
table.tableRef = col_cache_1.colCache.encode(row, col, row + tableHeight - 1, col + width - 1);
|
|
144
165
|
table.columns.forEach((column, i) => {
|
|
@@ -307,8 +328,9 @@ class Table {
|
|
|
307
328
|
}
|
|
308
329
|
}
|
|
309
330
|
this.store();
|
|
331
|
+
this._cache = undefined;
|
|
310
332
|
}
|
|
311
|
-
addRow(values, rowNumber) {
|
|
333
|
+
addRow(values, rowNumber, options) {
|
|
312
334
|
// Add a row of data, either insert at rowNumber or append
|
|
313
335
|
this.cacheState();
|
|
314
336
|
if (rowNumber === undefined) {
|
|
@@ -317,11 +339,17 @@ class Table {
|
|
|
317
339
|
else {
|
|
318
340
|
this.table.rows.splice(rowNumber, 0, values);
|
|
319
341
|
}
|
|
342
|
+
if (options?.commit !== false) {
|
|
343
|
+
this.commit();
|
|
344
|
+
}
|
|
320
345
|
}
|
|
321
|
-
removeRows(rowIndex, count = 1) {
|
|
346
|
+
removeRows(rowIndex, count = 1, options) {
|
|
322
347
|
// Remove a rows of data
|
|
323
348
|
this.cacheState();
|
|
324
349
|
this.table.rows.splice(rowIndex, count);
|
|
350
|
+
if (options?.commit !== false) {
|
|
351
|
+
this.commit();
|
|
352
|
+
}
|
|
325
353
|
}
|
|
326
354
|
getColumn(colIndex) {
|
|
327
355
|
const column = this.table.columns[colIndex];
|
|
@@ -49,6 +49,8 @@ exports.pivotTableRelTargetFromWorksheetName = pivotTableRelTargetFromWorksheetN
|
|
|
49
49
|
exports.tableRelTargetFromWorksheet = tableRelTargetFromWorksheet;
|
|
50
50
|
exports.tableRelTargetFromWorksheetName = tableRelTargetFromWorksheetName;
|
|
51
51
|
exports.mediaRelTargetFromRels = mediaRelTargetFromRels;
|
|
52
|
+
exports.ctrlPropPath = ctrlPropPath;
|
|
53
|
+
exports.ctrlPropRelTargetFromWorksheet = ctrlPropRelTargetFromWorksheet;
|
|
52
54
|
exports.OOXML_PATHS = {
|
|
53
55
|
contentTypes: "[Content_Types].xml",
|
|
54
56
|
rootRels: "_rels/.rels",
|
|
@@ -58,7 +60,8 @@ exports.OOXML_PATHS = {
|
|
|
58
60
|
xlWorkbookRels: "xl/_rels/workbook.xml.rels",
|
|
59
61
|
xlSharedStrings: "xl/sharedStrings.xml",
|
|
60
62
|
xlStyles: "xl/styles.xml",
|
|
61
|
-
xlTheme1: "xl/theme/theme1.xml"
|
|
63
|
+
xlTheme1: "xl/theme/theme1.xml",
|
|
64
|
+
xlFeaturePropertyBag: "xl/featurePropertyBag/featurePropertyBag.xml"
|
|
62
65
|
};
|
|
63
66
|
const worksheetXmlRegex = /^xl\/worksheets\/sheet(\d+)[.]xml$/;
|
|
64
67
|
const worksheetRelsXmlRegex = /^xl\/worksheets\/_rels\/sheet(\d+)[.]xml[.]rels$/;
|
|
@@ -213,7 +216,8 @@ exports.OOXML_REL_TARGETS = {
|
|
|
213
216
|
// Targets inside xl/_rels/workbook.xml.rels (base: xl/)
|
|
214
217
|
workbookStyles: "styles.xml",
|
|
215
218
|
workbookSharedStrings: "sharedStrings.xml",
|
|
216
|
-
workbookTheme1: "theme/theme1.xml"
|
|
219
|
+
workbookTheme1: "theme/theme1.xml",
|
|
220
|
+
workbookFeaturePropertyBag: "featurePropertyBag/featurePropertyBag.xml"
|
|
217
221
|
};
|
|
218
222
|
function pivotCacheDefinitionRelTargetFromWorkbook(n) {
|
|
219
223
|
// Target inside xl/_rels/workbook.xml.rels (base: xl/)
|
|
@@ -258,3 +262,11 @@ function mediaRelTargetFromRels(filename) {
|
|
|
258
262
|
// Target from a rels file located under xl/*/_rels (base is one level deeper than xl/)
|
|
259
263
|
return `../media/${filename}`;
|
|
260
264
|
}
|
|
265
|
+
// Form Control (ctrlProps) path functions
|
|
266
|
+
function ctrlPropPath(id) {
|
|
267
|
+
return `xl/ctrlProps/ctrlProp${id}.xml`;
|
|
268
|
+
}
|
|
269
|
+
function ctrlPropRelTargetFromWorksheet(id) {
|
|
270
|
+
// Target inside xl/worksheets/_rels/sheetN.xml.rels (base: xl/worksheets/)
|
|
271
|
+
return `../ctrlProps/ctrlProp${id}.xml`;
|
|
272
|
+
}
|
|
@@ -9,6 +9,7 @@ const enums_1 = require("./enums.js");
|
|
|
9
9
|
const image_1 = require("./image.js");
|
|
10
10
|
const table_1 = require("./table.js");
|
|
11
11
|
const data_validations_1 = require("./data-validations.js");
|
|
12
|
+
const form_control_1 = require("./form-control.js");
|
|
12
13
|
const encryptor_1 = require("./utils/encryptor.js");
|
|
13
14
|
const utils_1 = require("../../utils/utils.js");
|
|
14
15
|
const pivot_table_1 = require("./pivot-table.js");
|
|
@@ -95,6 +96,8 @@ class Worksheet {
|
|
|
95
96
|
this.tables = {};
|
|
96
97
|
this.pivotTables = [];
|
|
97
98
|
this.conditionalFormattings = [];
|
|
99
|
+
// for form controls (legacy checkboxes, etc.)
|
|
100
|
+
this.formControls = [];
|
|
98
101
|
}
|
|
99
102
|
get name() {
|
|
100
103
|
return this._name;
|
|
@@ -748,6 +751,43 @@ class Worksheet {
|
|
|
748
751
|
return image && image.imageId;
|
|
749
752
|
}
|
|
750
753
|
// =========================================================================
|
|
754
|
+
// Form Controls (Legacy Checkboxes)
|
|
755
|
+
/**
|
|
756
|
+
* Add a form control checkbox to the worksheet.
|
|
757
|
+
*
|
|
758
|
+
* Form control checkboxes are the legacy style that work in Office 2007+,
|
|
759
|
+
* WPS Office, LibreOffice, and other spreadsheet applications.
|
|
760
|
+
*
|
|
761
|
+
* Unlike modern in-cell checkboxes (which only work in Microsoft 365),
|
|
762
|
+
* form control checkboxes are floating controls positioned over cells.
|
|
763
|
+
*
|
|
764
|
+
* @param range - Cell reference (e.g., "B2") or range (e.g., "B2:D3") for positioning
|
|
765
|
+
* @param options - Checkbox options
|
|
766
|
+
* @returns The created FormCheckbox instance
|
|
767
|
+
*
|
|
768
|
+
* @example
|
|
769
|
+
* // Simple checkbox at B2
|
|
770
|
+
* ws.addFormCheckbox("B2");
|
|
771
|
+
*
|
|
772
|
+
* // Checkbox with label and linked cell
|
|
773
|
+
* ws.addFormCheckbox("B2:D3", {
|
|
774
|
+
* text: "Accept terms",
|
|
775
|
+
* link: "A2",
|
|
776
|
+
* checked: false
|
|
777
|
+
* });
|
|
778
|
+
*/
|
|
779
|
+
addFormCheckbox(range, options) {
|
|
780
|
+
const checkbox = new form_control_1.FormCheckbox(this, range, options);
|
|
781
|
+
this.formControls.push(checkbox);
|
|
782
|
+
return checkbox;
|
|
783
|
+
}
|
|
784
|
+
/**
|
|
785
|
+
* Get all form control checkboxes in the worksheet
|
|
786
|
+
*/
|
|
787
|
+
getFormCheckboxes() {
|
|
788
|
+
return this.formControls;
|
|
789
|
+
}
|
|
790
|
+
// =========================================================================
|
|
751
791
|
// Worksheet Protection
|
|
752
792
|
/**
|
|
753
793
|
* Protect the worksheet with optional password and options
|
|
@@ -856,7 +896,8 @@ class Worksheet {
|
|
|
856
896
|
sheetProtection: this.sheetProtection,
|
|
857
897
|
tables: Object.values(this.tables).map(table => table.model),
|
|
858
898
|
pivotTables: this.pivotTables,
|
|
859
|
-
conditionalFormattings: this.conditionalFormattings
|
|
899
|
+
conditionalFormattings: this.conditionalFormattings,
|
|
900
|
+
formControls: this.formControls.map(fc => fc.model)
|
|
860
901
|
};
|
|
861
902
|
// =================================================
|
|
862
903
|
// columns
|
|
@@ -922,6 +963,8 @@ class Worksheet {
|
|
|
922
963
|
}, {});
|
|
923
964
|
this.pivotTables = value.pivotTables;
|
|
924
965
|
this.conditionalFormattings = value.conditionalFormattings;
|
|
966
|
+
// Form controls are currently write-only (not parsed from XLSX)
|
|
967
|
+
this.formControls = [];
|
|
925
968
|
}
|
|
926
969
|
}
|
|
927
970
|
exports.Worksheet = Worksheet;
|
|
@@ -17,6 +17,8 @@ const RelType = {
|
|
|
17
17
|
Table: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/table",
|
|
18
18
|
PivotCacheDefinition: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheDefinition",
|
|
19
19
|
PivotCacheRecords: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotCacheRecords",
|
|
20
|
-
PivotTable: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotTable"
|
|
20
|
+
PivotTable: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/pivotTable",
|
|
21
|
+
FeaturePropertyBag: "http://schemas.microsoft.com/office/2022/11/relationships/FeaturePropertyBag",
|
|
22
|
+
CtrlProp: "http://schemas.openxmlformats.org/officeDocument/2006/relationships/ctrlProp"
|
|
21
23
|
};
|
|
22
24
|
exports.RelType = RelType;
|
|
@@ -66,6 +66,13 @@ class ContentTypesXform extends base_xform_1.BaseXform {
|
|
|
66
66
|
PartName: (0, ooxml_paths_1.toContentTypesPartName)(ooxml_paths_1.OOXML_PATHS.xlStyles),
|
|
67
67
|
ContentType: "application/vnd.openxmlformats-officedocument.spreadsheetml.styles+xml"
|
|
68
68
|
});
|
|
69
|
+
// Add FeaturePropertyBag if checkboxes are used
|
|
70
|
+
if (model.hasCheckboxes) {
|
|
71
|
+
xmlStream.leafNode("Override", {
|
|
72
|
+
PartName: (0, ooxml_paths_1.toContentTypesPartName)(ooxml_paths_1.OOXML_PATHS.xlFeaturePropertyBag),
|
|
73
|
+
ContentType: "application/vnd.ms-excel.featurepropertybag+xml"
|
|
74
|
+
});
|
|
75
|
+
}
|
|
69
76
|
const hasSharedStrings = model.sharedStrings && model.sharedStrings.count;
|
|
70
77
|
if (hasSharedStrings) {
|
|
71
78
|
xmlStream.leafNode("Override", {
|
|
@@ -101,6 +108,22 @@ class ContentTypesXform extends base_xform_1.BaseXform {
|
|
|
101
108
|
});
|
|
102
109
|
});
|
|
103
110
|
}
|
|
111
|
+
// Add form control (ctrlProps) content types
|
|
112
|
+
if (model.formControlRefs) {
|
|
113
|
+
// Ensure vml extension is declared (may already be declared for comments)
|
|
114
|
+
if (!model.commentRefs) {
|
|
115
|
+
xmlStream.leafNode("Default", {
|
|
116
|
+
Extension: "vml",
|
|
117
|
+
ContentType: "application/vnd.openxmlformats-officedocument.vmlDrawing"
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
for (const ctrlPropId of model.formControlRefs) {
|
|
121
|
+
xmlStream.leafNode("Override", {
|
|
122
|
+
PartName: (0, ooxml_paths_1.toContentTypesPartName)((0, ooxml_paths_1.ctrlPropPath)(ctrlPropId)),
|
|
123
|
+
ContentType: "application/vnd.ms-excel.controlproperties+xml"
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
}
|
|
104
127
|
xmlStream.leafNode("Override", {
|
|
105
128
|
PartName: (0, ooxml_paths_1.toContentTypesPartName)(ooxml_paths_1.OOXML_PATHS.docPropsCore),
|
|
106
129
|
ContentType: "application/vnd.openxmlformats-package.core-properties+xml"
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.FeaturePropertyBagXform = void 0;
|
|
4
|
+
const base_xform_1 = require("../base-xform.js");
|
|
5
|
+
// FeaturePropertyBag is used to enable checkbox functionality
|
|
6
|
+
// This is a static XML file that MS Excel requires for checkboxes to work
|
|
7
|
+
class FeaturePropertyBagXform extends base_xform_1.BaseXform {
|
|
8
|
+
render(xmlStream) {
|
|
9
|
+
xmlStream.openXml({ version: "1.0", encoding: "UTF-8", standalone: "yes" });
|
|
10
|
+
xmlStream.openNode("FeaturePropertyBags", {
|
|
11
|
+
xmlns: "http://schemas.microsoft.com/office/spreadsheetml/2022/featurepropertybag"
|
|
12
|
+
});
|
|
13
|
+
// Checkbox feature
|
|
14
|
+
xmlStream.leafNode("bag", { type: "Checkbox" });
|
|
15
|
+
// XFControls bag
|
|
16
|
+
xmlStream.openNode("bag", { type: "XFControls" });
|
|
17
|
+
xmlStream.leafNode("bagId", { k: "CellControl" }, "0");
|
|
18
|
+
xmlStream.closeNode();
|
|
19
|
+
// XFComplement bag
|
|
20
|
+
xmlStream.openNode("bag", { type: "XFComplement" });
|
|
21
|
+
xmlStream.leafNode("bagId", { k: "XFControls" }, "1");
|
|
22
|
+
xmlStream.closeNode();
|
|
23
|
+
// XFComplements bag
|
|
24
|
+
xmlStream.openNode("bag", { type: "XFComplements", extRef: "XFComplementsMapperExtRef" });
|
|
25
|
+
xmlStream.openNode("a", { k: "MappedFeaturePropertyBags" });
|
|
26
|
+
xmlStream.leafNode("bagId", {}, "2");
|
|
27
|
+
xmlStream.closeNode();
|
|
28
|
+
xmlStream.closeNode();
|
|
29
|
+
xmlStream.closeNode();
|
|
30
|
+
}
|
|
31
|
+
parseOpen() {
|
|
32
|
+
return false;
|
|
33
|
+
}
|
|
34
|
+
parseText() { }
|
|
35
|
+
parseClose() {
|
|
36
|
+
return false;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
exports.FeaturePropertyBagXform = FeaturePropertyBagXform;
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.CtrlPropXform = void 0;
|
|
4
|
+
const xml_stream_1 = require("../../../utils/xml-stream.js");
|
|
5
|
+
const base_xform_1 = require("../base-xform.js");
|
|
6
|
+
/**
|
|
7
|
+
* Control Properties Xform - Generates ctrlProp*.xml for form controls
|
|
8
|
+
*
|
|
9
|
+
* Each form control (checkbox, button, etc.) has an associated ctrlProp file
|
|
10
|
+
* that stores its properties like objectType, checked state, and linked cell.
|
|
11
|
+
*/
|
|
12
|
+
class CtrlPropXform extends base_xform_1.BaseXform {
|
|
13
|
+
get tag() {
|
|
14
|
+
return "formControlPr";
|
|
15
|
+
}
|
|
16
|
+
render(xmlStream, model) {
|
|
17
|
+
const renderModel = model || this.model;
|
|
18
|
+
const attrs = {
|
|
19
|
+
xmlns: "http://schemas.microsoft.com/office/spreadsheetml/2009/9/main",
|
|
20
|
+
objectType: "CheckBox",
|
|
21
|
+
checked: renderModel.checked,
|
|
22
|
+
lockText: "1"
|
|
23
|
+
};
|
|
24
|
+
// Add linked cell reference
|
|
25
|
+
if (renderModel.link) {
|
|
26
|
+
attrs.fmlaLink = renderModel.link;
|
|
27
|
+
}
|
|
28
|
+
// Add noThreeD for flat appearance
|
|
29
|
+
if (renderModel.noThreeD) {
|
|
30
|
+
attrs.noThreeD = "1";
|
|
31
|
+
}
|
|
32
|
+
xmlStream.openXml({ version: "1.0", encoding: "UTF-8", standalone: "yes" });
|
|
33
|
+
xmlStream.leafNode(this.tag, attrs);
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* Generate XML string directly (convenience method)
|
|
37
|
+
* Uses render() internally to ensure consistency
|
|
38
|
+
*/
|
|
39
|
+
toXml(model) {
|
|
40
|
+
const xmlStream = new xml_stream_1.XmlStream();
|
|
41
|
+
this.render(xmlStream, model);
|
|
42
|
+
return xmlStream.xml;
|
|
43
|
+
}
|
|
44
|
+
// Parsing not implemented - form controls are write-only for now
|
|
45
|
+
parseOpen() {
|
|
46
|
+
return true;
|
|
47
|
+
}
|
|
48
|
+
parseText() {
|
|
49
|
+
// Not implemented
|
|
50
|
+
}
|
|
51
|
+
parseClose() {
|
|
52
|
+
return false;
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
exports.CtrlPropXform = CtrlPropXform;
|