@niicojs/excel 0.3.1 → 0.3.2
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/LICENSE +20 -20
- package/README.md +585 -585
- package/dist/index.cjs +740 -392
- package/dist/index.d.cts +18 -0
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +18 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +740 -392
- package/package.json +3 -3
- package/src/cell.ts +14 -0
- package/src/index.ts +45 -45
- package/src/pivot-cache.ts +300 -300
- package/src/pivot-table.ts +684 -684
- package/src/range.ts +154 -154
- package/src/shared-strings.ts +178 -178
- package/src/styles.ts +819 -819
- package/src/table.ts +386 -386
- package/src/types.ts +16 -10
- package/src/utils/address.ts +121 -121
- package/src/utils/format.ts +356 -0
- package/src/utils/xml.ts +140 -140
- package/src/workbook.ts +1406 -1390
- package/src/worksheet.ts +85 -84
package/dist/index.cjs
CHANGED
|
@@ -4,10 +4,10 @@ var promises = require('fs/promises');
|
|
|
4
4
|
var fastXmlParser = require('fast-xml-parser');
|
|
5
5
|
var fflate = require('fflate');
|
|
6
6
|
|
|
7
|
-
/**
|
|
8
|
-
* Converts a column index (0-based) to Excel column letters (A, B, ..., Z, AA, AB, ...)
|
|
9
|
-
* @param col - 0-based column index
|
|
10
|
-
* @returns Column letter(s)
|
|
7
|
+
/**
|
|
8
|
+
* Converts a column index (0-based) to Excel column letters (A, B, ..., Z, AA, AB, ...)
|
|
9
|
+
* @param col - 0-based column index
|
|
10
|
+
* @returns Column letter(s)
|
|
11
11
|
*/ const colToLetter = (col)=>{
|
|
12
12
|
let result = '';
|
|
13
13
|
let n = col;
|
|
@@ -17,10 +17,10 @@ var fflate = require('fflate');
|
|
|
17
17
|
}
|
|
18
18
|
return result;
|
|
19
19
|
};
|
|
20
|
-
/**
|
|
21
|
-
* Converts Excel column letters to a 0-based column index
|
|
22
|
-
* @param letters - Column letter(s) like 'A', 'B', 'AA'
|
|
23
|
-
* @returns 0-based column index
|
|
20
|
+
/**
|
|
21
|
+
* Converts Excel column letters to a 0-based column index
|
|
22
|
+
* @param letters - Column letter(s) like 'A', 'B', 'AA'
|
|
23
|
+
* @returns 0-based column index
|
|
24
24
|
*/ const letterToCol = (letters)=>{
|
|
25
25
|
const upper = letters.toUpperCase();
|
|
26
26
|
let col = 0;
|
|
@@ -29,10 +29,10 @@ var fflate = require('fflate');
|
|
|
29
29
|
}
|
|
30
30
|
return col - 1;
|
|
31
31
|
};
|
|
32
|
-
/**
|
|
33
|
-
* Parses an Excel cell address (e.g., 'A1', '$B$2') to row/col indices
|
|
34
|
-
* @param address - Cell address string
|
|
35
|
-
* @returns CellAddress with 0-based row and col
|
|
32
|
+
/**
|
|
33
|
+
* Parses an Excel cell address (e.g., 'A1', '$B$2') to row/col indices
|
|
34
|
+
* @param address - Cell address string
|
|
35
|
+
* @returns CellAddress with 0-based row and col
|
|
36
36
|
*/ const parseAddress = (address)=>{
|
|
37
37
|
// Remove $ signs for absolute references
|
|
38
38
|
const clean = address.replace(/\$/g, '');
|
|
@@ -49,18 +49,18 @@ var fflate = require('fflate');
|
|
|
49
49
|
col
|
|
50
50
|
};
|
|
51
51
|
};
|
|
52
|
-
/**
|
|
53
|
-
* Converts row/col indices to an Excel cell address
|
|
54
|
-
* @param row - 0-based row index
|
|
55
|
-
* @param col - 0-based column index
|
|
56
|
-
* @returns Cell address string like 'A1'
|
|
52
|
+
/**
|
|
53
|
+
* Converts row/col indices to an Excel cell address
|
|
54
|
+
* @param row - 0-based row index
|
|
55
|
+
* @param col - 0-based column index
|
|
56
|
+
* @returns Cell address string like 'A1'
|
|
57
57
|
*/ const toAddress = (row, col)=>{
|
|
58
58
|
return `${colToLetter(col)}${row + 1}`;
|
|
59
59
|
};
|
|
60
|
-
/**
|
|
61
|
-
* Parses an Excel range (e.g., 'A1:B10') to start/end addresses
|
|
62
|
-
* @param range - Range string
|
|
63
|
-
* @returns RangeAddress with start and end
|
|
60
|
+
/**
|
|
61
|
+
* Parses an Excel range (e.g., 'A1:B10') to start/end addresses
|
|
62
|
+
* @param range - Range string
|
|
63
|
+
* @returns RangeAddress with start and end
|
|
64
64
|
*/ const parseRange = (range)=>{
|
|
65
65
|
const parts = range.split(':');
|
|
66
66
|
if (parts.length === 1) {
|
|
@@ -79,10 +79,10 @@ var fflate = require('fflate');
|
|
|
79
79
|
end: parseAddress(parts[1])
|
|
80
80
|
};
|
|
81
81
|
};
|
|
82
|
-
/**
|
|
83
|
-
* Converts a RangeAddress to a range string
|
|
84
|
-
* @param range - RangeAddress object
|
|
85
|
-
* @returns Range string like 'A1:B10'
|
|
82
|
+
/**
|
|
83
|
+
* Converts a RangeAddress to a range string
|
|
84
|
+
* @param range - RangeAddress object
|
|
85
|
+
* @returns Range string like 'A1:B10'
|
|
86
86
|
*/ const toRange = (range)=>{
|
|
87
87
|
const start = toAddress(range.start.row, range.start.col);
|
|
88
88
|
const end = toAddress(range.end.row, range.end.col);
|
|
@@ -91,8 +91,8 @@ var fflate = require('fflate');
|
|
|
91
91
|
}
|
|
92
92
|
return `${start}:${end}`;
|
|
93
93
|
};
|
|
94
|
-
/**
|
|
95
|
-
* Normalizes a range so start is always top-left and end is bottom-right
|
|
94
|
+
/**
|
|
95
|
+
* Normalizes a range so start is always top-left and end is bottom-right
|
|
96
96
|
*/ const normalizeRange = (range)=>{
|
|
97
97
|
return {
|
|
98
98
|
start: {
|
|
@@ -106,6 +106,334 @@ var fflate = require('fflate');
|
|
|
106
106
|
};
|
|
107
107
|
};
|
|
108
108
|
|
|
109
|
+
const DEFAULT_LOCALE = 'fr-FR';
|
|
110
|
+
const formatPartsCache = new Map();
|
|
111
|
+
const getLocaleNumberInfo = (locale)=>{
|
|
112
|
+
const cacheKey = `num-info:${locale}`;
|
|
113
|
+
const cached = formatPartsCache.get(cacheKey);
|
|
114
|
+
if (cached) {
|
|
115
|
+
const decimal = cached.find((p)=>p.type === 'decimal')?.value ?? ',';
|
|
116
|
+
const group = cached.find((p)=>p.type === 'group')?.value ?? ' ';
|
|
117
|
+
return {
|
|
118
|
+
decimal,
|
|
119
|
+
group
|
|
120
|
+
};
|
|
121
|
+
}
|
|
122
|
+
const parts = new Intl.NumberFormat(locale).formatToParts(12345.6);
|
|
123
|
+
formatPartsCache.set(cacheKey, parts);
|
|
124
|
+
const decimal = parts.find((p)=>p.type === 'decimal')?.value ?? ',';
|
|
125
|
+
const group = parts.find((p)=>p.type === 'group')?.value ?? ' ';
|
|
126
|
+
return {
|
|
127
|
+
decimal,
|
|
128
|
+
group
|
|
129
|
+
};
|
|
130
|
+
};
|
|
131
|
+
const normalizeSpaces = (value)=>{
|
|
132
|
+
return value.replace(/[\u00a0\u202f]/g, ' ');
|
|
133
|
+
};
|
|
134
|
+
const splitFormatSections = (format)=>{
|
|
135
|
+
const sections = [];
|
|
136
|
+
let current = '';
|
|
137
|
+
let inQuote = false;
|
|
138
|
+
for(let i = 0; i < format.length; i++){
|
|
139
|
+
const ch = format[i];
|
|
140
|
+
if (ch === '"') {
|
|
141
|
+
inQuote = !inQuote;
|
|
142
|
+
current += ch;
|
|
143
|
+
continue;
|
|
144
|
+
}
|
|
145
|
+
if (ch === ';' && !inQuote) {
|
|
146
|
+
sections.push(current);
|
|
147
|
+
current = '';
|
|
148
|
+
continue;
|
|
149
|
+
}
|
|
150
|
+
current += ch;
|
|
151
|
+
}
|
|
152
|
+
sections.push(current);
|
|
153
|
+
return sections;
|
|
154
|
+
};
|
|
155
|
+
const extractFormatLiterals = (format)=>{
|
|
156
|
+
let prefix = '';
|
|
157
|
+
let suffix = '';
|
|
158
|
+
let cleaned = '';
|
|
159
|
+
let inQuote = false;
|
|
160
|
+
let sawPlaceholder = false;
|
|
161
|
+
for(let i = 0; i < format.length; i++){
|
|
162
|
+
const ch = format[i];
|
|
163
|
+
if (ch === '"') {
|
|
164
|
+
inQuote = !inQuote;
|
|
165
|
+
continue;
|
|
166
|
+
}
|
|
167
|
+
if (inQuote) {
|
|
168
|
+
if (!sawPlaceholder) {
|
|
169
|
+
prefix += ch;
|
|
170
|
+
} else {
|
|
171
|
+
suffix += ch;
|
|
172
|
+
}
|
|
173
|
+
continue;
|
|
174
|
+
}
|
|
175
|
+
if (ch === '\\' && i + 1 < format.length) {
|
|
176
|
+
const escaped = format[i + 1];
|
|
177
|
+
if (!sawPlaceholder) {
|
|
178
|
+
prefix += escaped;
|
|
179
|
+
} else {
|
|
180
|
+
suffix += escaped;
|
|
181
|
+
}
|
|
182
|
+
i++;
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
if (ch === '_' || ch === '*') {
|
|
186
|
+
if (i + 1 < format.length) {
|
|
187
|
+
i++;
|
|
188
|
+
}
|
|
189
|
+
continue;
|
|
190
|
+
}
|
|
191
|
+
if (ch === '[') {
|
|
192
|
+
const end = format.indexOf(']', i + 1);
|
|
193
|
+
if (end !== -1) {
|
|
194
|
+
const content = format.slice(i + 1, end);
|
|
195
|
+
const currencyMatch = content.match(/[$€]/);
|
|
196
|
+
if (currencyMatch) {
|
|
197
|
+
if (!sawPlaceholder) {
|
|
198
|
+
prefix += currencyMatch[0];
|
|
199
|
+
} else {
|
|
200
|
+
suffix += currencyMatch[0];
|
|
201
|
+
}
|
|
202
|
+
}
|
|
203
|
+
i = end;
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
if (ch === '%') {
|
|
208
|
+
if (!sawPlaceholder) {
|
|
209
|
+
prefix += ch;
|
|
210
|
+
} else {
|
|
211
|
+
suffix += ch;
|
|
212
|
+
}
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
215
|
+
if (ch === '0' || ch === '#' || ch === '?' || ch === '.' || ch === ',') {
|
|
216
|
+
sawPlaceholder = true;
|
|
217
|
+
cleaned += ch;
|
|
218
|
+
continue;
|
|
219
|
+
}
|
|
220
|
+
if (!sawPlaceholder) {
|
|
221
|
+
prefix += ch;
|
|
222
|
+
} else {
|
|
223
|
+
suffix += ch;
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
return {
|
|
227
|
+
cleaned,
|
|
228
|
+
prefix,
|
|
229
|
+
suffix
|
|
230
|
+
};
|
|
231
|
+
};
|
|
232
|
+
const parseNumberFormat = (format)=>{
|
|
233
|
+
const trimmed = format.trim();
|
|
234
|
+
if (!trimmed) return null;
|
|
235
|
+
const { cleaned, prefix, suffix } = extractFormatLiterals(trimmed);
|
|
236
|
+
const lower = cleaned.toLowerCase();
|
|
237
|
+
if (!/[0#?]/.test(lower)) return null;
|
|
238
|
+
const percent = /%/.test(trimmed);
|
|
239
|
+
const section = lower;
|
|
240
|
+
const lastDot = section.lastIndexOf('.');
|
|
241
|
+
const lastComma = section.lastIndexOf(',');
|
|
242
|
+
let decimalSeparator = null;
|
|
243
|
+
if (lastDot >= 0 && lastComma >= 0) {
|
|
244
|
+
decimalSeparator = lastDot > lastComma ? '.' : ',';
|
|
245
|
+
} else if (lastDot >= 0 || lastComma >= 0) {
|
|
246
|
+
const candidate = lastDot >= 0 ? '.' : ',';
|
|
247
|
+
const index = lastDot >= 0 ? lastDot : lastComma;
|
|
248
|
+
const fractionSection = section.slice(index + 1);
|
|
249
|
+
const fractionDigitsCandidate = fractionSection.replace(/[^0#?]/g, '').length;
|
|
250
|
+
if (fractionDigitsCandidate > 0) {
|
|
251
|
+
if (fractionDigitsCandidate === 3) {
|
|
252
|
+
decimalSeparator = null;
|
|
253
|
+
} else {
|
|
254
|
+
decimalSeparator = candidate;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
let decimalIndex = decimalSeparator === '.' ? lastDot : decimalSeparator === ',' ? lastComma : -1;
|
|
259
|
+
if (decimalIndex >= 0) {
|
|
260
|
+
const fractionSection = section.slice(decimalIndex + 1);
|
|
261
|
+
if (!/[0#?]/.test(fractionSection)) {
|
|
262
|
+
decimalIndex = -1;
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
const decimalSection = decimalIndex >= 0 ? section.slice(decimalIndex + 1) : '';
|
|
266
|
+
const fractionDigits = decimalSection.replace(/[^0#?]/g, '').length;
|
|
267
|
+
const integerSection = decimalIndex >= 0 ? section.slice(0, decimalIndex) : section;
|
|
268
|
+
const useGrouping = /[,.\s\u00a0\u202f]/.test(integerSection) && integerSection.length > 0;
|
|
269
|
+
return {
|
|
270
|
+
fractionDigits,
|
|
271
|
+
percent,
|
|
272
|
+
useGrouping,
|
|
273
|
+
literalPrefix: prefix || undefined,
|
|
274
|
+
literalSuffix: suffix || undefined
|
|
275
|
+
};
|
|
276
|
+
};
|
|
277
|
+
const formatNumber = (value, info, locale)=>{
|
|
278
|
+
const adjusted = info.percent ? value * 100 : value;
|
|
279
|
+
const { decimal, group } = getLocaleNumberInfo(locale);
|
|
280
|
+
const absValue = Math.abs(adjusted);
|
|
281
|
+
const fixed = absValue.toFixed(info.fractionDigits);
|
|
282
|
+
const [integerPart, fractionPart] = fixed.split('.');
|
|
283
|
+
const grouped = info.useGrouping ? integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, group) : integerPart;
|
|
284
|
+
const fraction = info.fractionDigits > 0 ? `${decimal}${fractionPart}` : '';
|
|
285
|
+
const signed = adjusted < 0 ? '-' : '';
|
|
286
|
+
let result = `${signed}${grouped}${fraction}`;
|
|
287
|
+
if (info.literalPrefix) {
|
|
288
|
+
result = `${info.literalPrefix}${result}`;
|
|
289
|
+
}
|
|
290
|
+
if (info.literalSuffix) {
|
|
291
|
+
result = `${result}${info.literalSuffix}`;
|
|
292
|
+
}
|
|
293
|
+
return normalizeSpaces(result);
|
|
294
|
+
};
|
|
295
|
+
const padNumber = (value, length)=>{
|
|
296
|
+
const str = String(value);
|
|
297
|
+
return str.length >= length ? str : `${'0'.repeat(length - str.length)}${str}`;
|
|
298
|
+
};
|
|
299
|
+
const formatDatePart = (value, token, locale)=>{
|
|
300
|
+
switch(token){
|
|
301
|
+
case 'yyyy':
|
|
302
|
+
return String(value.getFullYear());
|
|
303
|
+
case 'yy':
|
|
304
|
+
return padNumber(value.getFullYear() % 100, 2);
|
|
305
|
+
case 'mmmm':
|
|
306
|
+
return value.toLocaleString(locale, {
|
|
307
|
+
month: 'long'
|
|
308
|
+
});
|
|
309
|
+
case 'mmm':
|
|
310
|
+
return value.toLocaleString(locale, {
|
|
311
|
+
month: 'short'
|
|
312
|
+
});
|
|
313
|
+
case 'mm':
|
|
314
|
+
return padNumber(value.getMonth() + 1, 2);
|
|
315
|
+
case 'm':
|
|
316
|
+
return String(value.getMonth() + 1);
|
|
317
|
+
case 'dd':
|
|
318
|
+
return padNumber(value.getDate(), 2);
|
|
319
|
+
case 'd':
|
|
320
|
+
return String(value.getDate());
|
|
321
|
+
case 'hh':
|
|
322
|
+
{
|
|
323
|
+
const hours = value.getHours();
|
|
324
|
+
return padNumber(hours, 2);
|
|
325
|
+
}
|
|
326
|
+
case 'h':
|
|
327
|
+
return String(value.getHours());
|
|
328
|
+
case 'min2':
|
|
329
|
+
return padNumber(value.getMinutes(), 2);
|
|
330
|
+
case 'min1':
|
|
331
|
+
return String(value.getMinutes());
|
|
332
|
+
case 'ss':
|
|
333
|
+
return padNumber(value.getSeconds(), 2);
|
|
334
|
+
case 's':
|
|
335
|
+
return String(value.getSeconds());
|
|
336
|
+
default:
|
|
337
|
+
return token;
|
|
338
|
+
}
|
|
339
|
+
};
|
|
340
|
+
const tokenizeDateFormat = (format)=>{
|
|
341
|
+
const tokens = [];
|
|
342
|
+
let i = 0;
|
|
343
|
+
while(i < format.length){
|
|
344
|
+
const ch = format[i];
|
|
345
|
+
if (ch === '"') {
|
|
346
|
+
let literal = '';
|
|
347
|
+
i++;
|
|
348
|
+
while(i < format.length && format[i] !== '"'){
|
|
349
|
+
literal += format[i];
|
|
350
|
+
i++;
|
|
351
|
+
}
|
|
352
|
+
i++;
|
|
353
|
+
if (literal) tokens.push(literal);
|
|
354
|
+
continue;
|
|
355
|
+
}
|
|
356
|
+
if (ch === '\\' && i + 1 < format.length) {
|
|
357
|
+
tokens.push(format[i + 1]);
|
|
358
|
+
i += 2;
|
|
359
|
+
continue;
|
|
360
|
+
}
|
|
361
|
+
if (ch === '[') {
|
|
362
|
+
const end = format.indexOf(']', i + 1);
|
|
363
|
+
if (end !== -1) {
|
|
364
|
+
i = end + 1;
|
|
365
|
+
continue;
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
const lower = format.slice(i).toLowerCase();
|
|
369
|
+
const match = [
|
|
370
|
+
'yyyy',
|
|
371
|
+
'yy',
|
|
372
|
+
'mmmm',
|
|
373
|
+
'mmm',
|
|
374
|
+
'mm',
|
|
375
|
+
'm',
|
|
376
|
+
'dd',
|
|
377
|
+
'd',
|
|
378
|
+
'hh',
|
|
379
|
+
'h',
|
|
380
|
+
'ss',
|
|
381
|
+
's'
|
|
382
|
+
].find((t)=>lower.startsWith(t));
|
|
383
|
+
if (match) {
|
|
384
|
+
if (match === 'm' || match === 'mm') {
|
|
385
|
+
let j = i - 1;
|
|
386
|
+
let previousChar = '';
|
|
387
|
+
while(j >= 0 && previousChar === ''){
|
|
388
|
+
const candidate = format[j];
|
|
389
|
+
if (candidate && candidate !== ' ') {
|
|
390
|
+
previousChar = candidate;
|
|
391
|
+
}
|
|
392
|
+
j--;
|
|
393
|
+
}
|
|
394
|
+
const isMinute = previousChar === 'h' || previousChar === 'H' || previousChar === ':';
|
|
395
|
+
if (isMinute) {
|
|
396
|
+
tokens.push(match === 'mm' ? 'min2' : 'min1');
|
|
397
|
+
i += match.length;
|
|
398
|
+
continue;
|
|
399
|
+
}
|
|
400
|
+
}
|
|
401
|
+
tokens.push(match);
|
|
402
|
+
i += match.length;
|
|
403
|
+
continue;
|
|
404
|
+
}
|
|
405
|
+
tokens.push(ch);
|
|
406
|
+
i++;
|
|
407
|
+
}
|
|
408
|
+
return tokens;
|
|
409
|
+
};
|
|
410
|
+
const isDateFormat = (format)=>{
|
|
411
|
+
const lowered = format.toLowerCase();
|
|
412
|
+
return /[ymdhss]/.test(lowered);
|
|
413
|
+
};
|
|
414
|
+
const formatDate = (value, format, locale)=>{
|
|
415
|
+
const tokens = tokenizeDateFormat(format);
|
|
416
|
+
return tokens.map((token)=>formatDatePart(value, token, locale)).join('');
|
|
417
|
+
};
|
|
418
|
+
const formatCellValue = (value, style, locale)=>{
|
|
419
|
+
const numberFormat = style?.numberFormat;
|
|
420
|
+
if (!numberFormat) return null;
|
|
421
|
+
const normalizedLocale = locale || DEFAULT_LOCALE;
|
|
422
|
+
const sections = splitFormatSections(numberFormat);
|
|
423
|
+
const hasNegativeSection = sections.length > 1;
|
|
424
|
+
const section = value instanceof Date ? sections[0] : value < 0 ? sections[1] ?? sections[0] : sections[0];
|
|
425
|
+
if (value instanceof Date && isDateFormat(section)) {
|
|
426
|
+
return formatDate(value, section, normalizedLocale);
|
|
427
|
+
}
|
|
428
|
+
if (typeof value === 'number') {
|
|
429
|
+
const info = parseNumberFormat(section);
|
|
430
|
+
if (!info) return null;
|
|
431
|
+
const numericValue = value < 0 && hasNegativeSection ? Math.abs(value) : value;
|
|
432
|
+
return formatNumber(numericValue, info, normalizedLocale);
|
|
433
|
+
}
|
|
434
|
+
return null;
|
|
435
|
+
};
|
|
436
|
+
|
|
109
437
|
// Excel epoch: December 30, 1899 (accounting for the 1900 leap year bug)
|
|
110
438
|
const EXCEL_EPOCH = new Date(Date.UTC(1899, 11, 30));
|
|
111
439
|
const MS_PER_DAY = 24 * 60 * 60 * 1000;
|
|
@@ -306,12 +634,21 @@ const ERROR_TYPES = new Set([
|
|
|
306
634
|
/**
|
|
307
635
|
* Get the formatted text (as displayed in Excel)
|
|
308
636
|
*/ get text() {
|
|
637
|
+
return this.textWithLocale();
|
|
638
|
+
}
|
|
639
|
+
/**
|
|
640
|
+
* Get the formatted text using a specific locale
|
|
641
|
+
*/ textWithLocale(locale) {
|
|
309
642
|
if (this._data.w) {
|
|
310
643
|
return this._data.w;
|
|
311
644
|
}
|
|
312
645
|
const val = this.value;
|
|
313
646
|
if (val === null) return '';
|
|
314
647
|
if (typeof val === 'object' && 'error' in val) return val.error;
|
|
648
|
+
if (val instanceof Date || typeof val === 'number') {
|
|
649
|
+
const formatted = formatCellValue(val, this.style, locale ?? this._worksheet.workbook.locale);
|
|
650
|
+
if (formatted !== null) return formatted;
|
|
651
|
+
}
|
|
315
652
|
if (val instanceof Date) return val.toISOString().split('T')[0];
|
|
316
653
|
return String(val);
|
|
317
654
|
}
|
|
@@ -409,38 +746,38 @@ const ERROR_TYPES = new Set([
|
|
|
409
746
|
|
|
410
747
|
var _computedKey;
|
|
411
748
|
_computedKey = Symbol.iterator;
|
|
412
|
-
/**
|
|
413
|
-
* Represents a range of cells in a worksheet
|
|
749
|
+
/**
|
|
750
|
+
* Represents a range of cells in a worksheet
|
|
414
751
|
*/ class Range {
|
|
415
752
|
constructor(worksheet, range){
|
|
416
753
|
this._worksheet = worksheet;
|
|
417
754
|
this._range = normalizeRange(range);
|
|
418
755
|
}
|
|
419
|
-
/**
|
|
420
|
-
* Get the range address as a string
|
|
756
|
+
/**
|
|
757
|
+
* Get the range address as a string
|
|
421
758
|
*/ get address() {
|
|
422
759
|
const start = toAddress(this._range.start.row, this._range.start.col);
|
|
423
760
|
const end = toAddress(this._range.end.row, this._range.end.col);
|
|
424
761
|
if (start === end) return start;
|
|
425
762
|
return `${start}:${end}`;
|
|
426
763
|
}
|
|
427
|
-
/**
|
|
428
|
-
* Get the number of rows in the range
|
|
764
|
+
/**
|
|
765
|
+
* Get the number of rows in the range
|
|
429
766
|
*/ get rowCount() {
|
|
430
767
|
return this._range.end.row - this._range.start.row + 1;
|
|
431
768
|
}
|
|
432
|
-
/**
|
|
433
|
-
* Get the number of columns in the range
|
|
769
|
+
/**
|
|
770
|
+
* Get the number of columns in the range
|
|
434
771
|
*/ get colCount() {
|
|
435
772
|
return this._range.end.col - this._range.start.col + 1;
|
|
436
773
|
}
|
|
437
|
-
/**
|
|
438
|
-
* Get all values in the range as a 2D array
|
|
774
|
+
/**
|
|
775
|
+
* Get all values in the range as a 2D array
|
|
439
776
|
*/ get values() {
|
|
440
777
|
return this.getValues();
|
|
441
778
|
}
|
|
442
|
-
/**
|
|
443
|
-
* Get all values in the range as a 2D array with options
|
|
779
|
+
/**
|
|
780
|
+
* Get all values in the range as a 2D array with options
|
|
444
781
|
*/ getValues(options = {}) {
|
|
445
782
|
const { createMissing = true } = options;
|
|
446
783
|
const result = [];
|
|
@@ -459,8 +796,8 @@ _computedKey = Symbol.iterator;
|
|
|
459
796
|
}
|
|
460
797
|
return result;
|
|
461
798
|
}
|
|
462
|
-
/**
|
|
463
|
-
* Set values in the range from a 2D array
|
|
799
|
+
/**
|
|
800
|
+
* Set values in the range from a 2D array
|
|
464
801
|
*/ set values(data) {
|
|
465
802
|
for(let r = 0; r < data.length && r < this.rowCount; r++){
|
|
466
803
|
const row = data[r];
|
|
@@ -470,8 +807,8 @@ _computedKey = Symbol.iterator;
|
|
|
470
807
|
}
|
|
471
808
|
}
|
|
472
809
|
}
|
|
473
|
-
/**
|
|
474
|
-
* Get all formulas in the range as a 2D array
|
|
810
|
+
/**
|
|
811
|
+
* Get all formulas in the range as a 2D array
|
|
475
812
|
*/ get formulas() {
|
|
476
813
|
const result = [];
|
|
477
814
|
for(let r = this._range.start.row; r <= this._range.end.row; r++){
|
|
@@ -484,8 +821,8 @@ _computedKey = Symbol.iterator;
|
|
|
484
821
|
}
|
|
485
822
|
return result;
|
|
486
823
|
}
|
|
487
|
-
/**
|
|
488
|
-
* Set formulas in the range from a 2D array
|
|
824
|
+
/**
|
|
825
|
+
* Set formulas in the range from a 2D array
|
|
489
826
|
*/ set formulas(data) {
|
|
490
827
|
for(let r = 0; r < data.length && r < this.rowCount; r++){
|
|
491
828
|
const row = data[r];
|
|
@@ -495,13 +832,13 @@ _computedKey = Symbol.iterator;
|
|
|
495
832
|
}
|
|
496
833
|
}
|
|
497
834
|
}
|
|
498
|
-
/**
|
|
499
|
-
* Get the style of the top-left cell
|
|
835
|
+
/**
|
|
836
|
+
* Get the style of the top-left cell
|
|
500
837
|
*/ get style() {
|
|
501
838
|
return this._worksheet.cell(this._range.start.row, this._range.start.col).style;
|
|
502
839
|
}
|
|
503
|
-
/**
|
|
504
|
-
* Set style for all cells in the range
|
|
840
|
+
/**
|
|
841
|
+
* Set style for all cells in the range
|
|
505
842
|
*/ set style(style) {
|
|
506
843
|
for(let r = this._range.start.row; r <= this._range.end.row; r++){
|
|
507
844
|
for(let c = this._range.start.col; c <= this._range.end.col; c++){
|
|
@@ -510,8 +847,8 @@ _computedKey = Symbol.iterator;
|
|
|
510
847
|
}
|
|
511
848
|
}
|
|
512
849
|
}
|
|
513
|
-
/**
|
|
514
|
-
* Iterate over all cells in the range
|
|
850
|
+
/**
|
|
851
|
+
* Iterate over all cells in the range
|
|
515
852
|
*/ *[_computedKey]() {
|
|
516
853
|
for(let r = this._range.start.row; r <= this._range.end.row; r++){
|
|
517
854
|
for(let c = this._range.start.col; c <= this._range.end.col; c++){
|
|
@@ -519,8 +856,8 @@ _computedKey = Symbol.iterator;
|
|
|
519
856
|
}
|
|
520
857
|
}
|
|
521
858
|
}
|
|
522
|
-
/**
|
|
523
|
-
* Iterate over cells row by row
|
|
859
|
+
/**
|
|
860
|
+
* Iterate over cells row by row
|
|
524
861
|
*/ *rows() {
|
|
525
862
|
for(let r = this._range.start.row; r <= this._range.end.row; r++){
|
|
526
863
|
const row = [];
|
|
@@ -558,19 +895,19 @@ const builderOptions = {
|
|
|
558
895
|
};
|
|
559
896
|
const parser = new fastXmlParser.XMLParser(parserOptions);
|
|
560
897
|
const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
561
|
-
/**
|
|
562
|
-
* Parses an XML string into a JavaScript object
|
|
563
|
-
* Preserves element order and attributes for round-trip compatibility
|
|
898
|
+
/**
|
|
899
|
+
* Parses an XML string into a JavaScript object
|
|
900
|
+
* Preserves element order and attributes for round-trip compatibility
|
|
564
901
|
*/ const parseXml = (xml)=>{
|
|
565
902
|
return parser.parse(xml);
|
|
566
903
|
};
|
|
567
|
-
/**
|
|
568
|
-
* Converts a JavaScript object back to an XML string
|
|
904
|
+
/**
|
|
905
|
+
* Converts a JavaScript object back to an XML string
|
|
569
906
|
*/ const stringifyXml = (obj)=>{
|
|
570
907
|
return builder.build(obj);
|
|
571
908
|
};
|
|
572
|
-
/**
|
|
573
|
-
* Finds the first element with the given tag name in the XML tree
|
|
909
|
+
/**
|
|
910
|
+
* Finds the first element with the given tag name in the XML tree
|
|
574
911
|
*/ const findElement = (nodes, tagName)=>{
|
|
575
912
|
for (const node of nodes){
|
|
576
913
|
if (tagName in node) {
|
|
@@ -579,8 +916,8 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
579
916
|
}
|
|
580
917
|
return undefined;
|
|
581
918
|
};
|
|
582
|
-
/**
|
|
583
|
-
* Gets the children of an element
|
|
919
|
+
/**
|
|
920
|
+
* Gets the children of an element
|
|
584
921
|
*/ const getChildren = (node, tagName)=>{
|
|
585
922
|
const children = node[tagName];
|
|
586
923
|
if (Array.isArray(children)) {
|
|
@@ -588,14 +925,14 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
588
925
|
}
|
|
589
926
|
return [];
|
|
590
927
|
};
|
|
591
|
-
/**
|
|
592
|
-
* Gets an attribute value from a node
|
|
928
|
+
/**
|
|
929
|
+
* Gets an attribute value from a node
|
|
593
930
|
*/ const getAttr = (node, name)=>{
|
|
594
931
|
const attrs = node[':@'];
|
|
595
932
|
return attrs?.[`@_${name}`];
|
|
596
933
|
};
|
|
597
|
-
/**
|
|
598
|
-
* Creates a new XML element
|
|
934
|
+
/**
|
|
935
|
+
* Creates a new XML element
|
|
599
936
|
*/ const createElement = (tagName, attrs, children)=>{
|
|
600
937
|
const node = {
|
|
601
938
|
[tagName]: children || []
|
|
@@ -609,17 +946,17 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
609
946
|
}
|
|
610
947
|
return node;
|
|
611
948
|
};
|
|
612
|
-
/**
|
|
613
|
-
* Creates a text node
|
|
949
|
+
/**
|
|
950
|
+
* Creates a text node
|
|
614
951
|
*/ const createText = (text)=>{
|
|
615
952
|
return {
|
|
616
953
|
'#text': text
|
|
617
954
|
};
|
|
618
955
|
};
|
|
619
956
|
|
|
620
|
-
/**
|
|
621
|
-
* Maps table total function names to SUBTOTAL function numbers
|
|
622
|
-
* SUBTOTAL uses 101-111 for functions that ignore hidden values
|
|
957
|
+
/**
|
|
958
|
+
* Maps table total function names to SUBTOTAL function numbers
|
|
959
|
+
* SUBTOTAL uses 101-111 for functions that ignore hidden values
|
|
623
960
|
*/ const TOTAL_FUNCTION_NUMBERS = {
|
|
624
961
|
average: 101,
|
|
625
962
|
count: 102,
|
|
@@ -631,8 +968,8 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
631
968
|
var: 110,
|
|
632
969
|
none: 0
|
|
633
970
|
};
|
|
634
|
-
/**
|
|
635
|
-
* Maps table total function names to XML attribute values
|
|
971
|
+
/**
|
|
972
|
+
* Maps table total function names to XML attribute values
|
|
636
973
|
*/ const TOTAL_FUNCTION_NAMES = {
|
|
637
974
|
average: 'average',
|
|
638
975
|
count: 'count',
|
|
@@ -644,8 +981,8 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
644
981
|
var: 'var',
|
|
645
982
|
none: 'none'
|
|
646
983
|
};
|
|
647
|
-
/**
|
|
648
|
-
* Represents an Excel Table (ListObject) with auto-filter, banded styling, and total row.
|
|
984
|
+
/**
|
|
985
|
+
* Represents an Excel Table (ListObject) with auto-filter, banded styling, and total row.
|
|
649
986
|
*/ class Table {
|
|
650
987
|
constructor(worksheet, config, tableId){
|
|
651
988
|
this._columns = [];
|
|
@@ -681,80 +1018,80 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
681
1018
|
// Extract column names from worksheet headers
|
|
682
1019
|
this._extractColumns();
|
|
683
1020
|
}
|
|
684
|
-
/**
|
|
685
|
-
* Get the table name
|
|
1021
|
+
/**
|
|
1022
|
+
* Get the table name
|
|
686
1023
|
*/ get name() {
|
|
687
1024
|
return this._name;
|
|
688
1025
|
}
|
|
689
|
-
/**
|
|
690
|
-
* Get the table display name
|
|
1026
|
+
/**
|
|
1027
|
+
* Get the table display name
|
|
691
1028
|
*/ get displayName() {
|
|
692
1029
|
return this._displayName;
|
|
693
1030
|
}
|
|
694
|
-
/**
|
|
695
|
-
* Get the table ID
|
|
1031
|
+
/**
|
|
1032
|
+
* Get the table ID
|
|
696
1033
|
*/ get id() {
|
|
697
1034
|
return this._id;
|
|
698
1035
|
}
|
|
699
|
-
/**
|
|
700
|
-
* Get the worksheet this table belongs to
|
|
1036
|
+
/**
|
|
1037
|
+
* Get the worksheet this table belongs to
|
|
701
1038
|
*/ get worksheet() {
|
|
702
1039
|
return this._worksheet;
|
|
703
1040
|
}
|
|
704
|
-
/**
|
|
705
|
-
* Get the table range address string
|
|
1041
|
+
/**
|
|
1042
|
+
* Get the table range address string
|
|
706
1043
|
*/ get range() {
|
|
707
1044
|
return toRange(this._range);
|
|
708
1045
|
}
|
|
709
|
-
/**
|
|
710
|
-
* Get the base range excluding total row
|
|
1046
|
+
/**
|
|
1047
|
+
* Get the base range excluding total row
|
|
711
1048
|
*/ get baseRange() {
|
|
712
1049
|
return toRange(this._baseRange);
|
|
713
1050
|
}
|
|
714
|
-
/**
|
|
715
|
-
* Get the table range as RangeAddress
|
|
1051
|
+
/**
|
|
1052
|
+
* Get the table range as RangeAddress
|
|
716
1053
|
*/ get rangeAddress() {
|
|
717
1054
|
return {
|
|
718
1055
|
...this._range
|
|
719
1056
|
};
|
|
720
1057
|
}
|
|
721
|
-
/**
|
|
722
|
-
* Get column names
|
|
1058
|
+
/**
|
|
1059
|
+
* Get column names
|
|
723
1060
|
*/ get columns() {
|
|
724
1061
|
return this._columns.map((c)=>c.name);
|
|
725
1062
|
}
|
|
726
|
-
/**
|
|
727
|
-
* Check if table has a total row
|
|
1063
|
+
/**
|
|
1064
|
+
* Check if table has a total row
|
|
728
1065
|
*/ get hasTotalRow() {
|
|
729
1066
|
return this._totalRow;
|
|
730
1067
|
}
|
|
731
|
-
/**
|
|
732
|
-
* Check if table has a header row
|
|
1068
|
+
/**
|
|
1069
|
+
* Check if table has a header row
|
|
733
1070
|
*/ get hasHeaderRow() {
|
|
734
1071
|
return this._headerRow;
|
|
735
1072
|
}
|
|
736
|
-
/**
|
|
737
|
-
* Check if table has auto-filter enabled
|
|
1073
|
+
/**
|
|
1074
|
+
* Check if table has auto-filter enabled
|
|
738
1075
|
*/ get hasAutoFilter() {
|
|
739
1076
|
return this._autoFilter;
|
|
740
1077
|
}
|
|
741
|
-
/**
|
|
742
|
-
* Get the current style configuration
|
|
1078
|
+
/**
|
|
1079
|
+
* Get the current style configuration
|
|
743
1080
|
*/ get style() {
|
|
744
1081
|
return {
|
|
745
1082
|
...this._style
|
|
746
1083
|
};
|
|
747
1084
|
}
|
|
748
|
-
/**
|
|
749
|
-
* Check if the table has been modified
|
|
1085
|
+
/**
|
|
1086
|
+
* Check if the table has been modified
|
|
750
1087
|
*/ get dirty() {
|
|
751
1088
|
return this._dirty;
|
|
752
1089
|
}
|
|
753
|
-
/**
|
|
754
|
-
* Set a total function for a column
|
|
755
|
-
* @param columnName - Name of the column (header text)
|
|
756
|
-
* @param fn - Aggregation function to use
|
|
757
|
-
* @returns this for method chaining
|
|
1090
|
+
/**
|
|
1091
|
+
* Set a total function for a column
|
|
1092
|
+
* @param columnName - Name of the column (header text)
|
|
1093
|
+
* @param fn - Aggregation function to use
|
|
1094
|
+
* @returns this for method chaining
|
|
758
1095
|
*/ setTotalFunction(columnName, fn) {
|
|
759
1096
|
if (!this._totalRow) {
|
|
760
1097
|
throw new Error('Cannot set total function: table does not have a total row enabled');
|
|
@@ -769,25 +1106,25 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
769
1106
|
this._writeTotalRowFormula(column);
|
|
770
1107
|
return this;
|
|
771
1108
|
}
|
|
772
|
-
/**
|
|
773
|
-
* Get total function for a column if set
|
|
1109
|
+
/**
|
|
1110
|
+
* Get total function for a column if set
|
|
774
1111
|
*/ getTotalFunction(columnName) {
|
|
775
1112
|
const column = this._columns.find((c)=>c.name === columnName);
|
|
776
1113
|
return column?.totalFunction;
|
|
777
1114
|
}
|
|
778
|
-
/**
|
|
779
|
-
* Enable or disable auto-filter
|
|
780
|
-
* @param enabled - Whether auto-filter should be enabled
|
|
781
|
-
* @returns this for method chaining
|
|
1115
|
+
/**
|
|
1116
|
+
* Enable or disable auto-filter
|
|
1117
|
+
* @param enabled - Whether auto-filter should be enabled
|
|
1118
|
+
* @returns this for method chaining
|
|
782
1119
|
*/ setAutoFilter(enabled) {
|
|
783
1120
|
this._autoFilter = enabled;
|
|
784
1121
|
this._dirty = true;
|
|
785
1122
|
return this;
|
|
786
1123
|
}
|
|
787
|
-
/**
|
|
788
|
-
* Update table style configuration
|
|
789
|
-
* @param style - Style options to apply
|
|
790
|
-
* @returns this for method chaining
|
|
1124
|
+
/**
|
|
1125
|
+
* Update table style configuration
|
|
1126
|
+
* @param style - Style options to apply
|
|
1127
|
+
* @returns this for method chaining
|
|
791
1128
|
*/ setStyle(style) {
|
|
792
1129
|
if (style.name !== undefined) this._style.name = style.name;
|
|
793
1130
|
if (style.showRowStripes !== undefined) this._style.showRowStripes = style.showRowStripes;
|
|
@@ -797,10 +1134,10 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
797
1134
|
this._dirty = true;
|
|
798
1135
|
return this;
|
|
799
1136
|
}
|
|
800
|
-
/**
|
|
801
|
-
* Enable or disable the total row
|
|
802
|
-
* @param enabled - Whether total row should be shown
|
|
803
|
-
* @returns this for method chaining
|
|
1137
|
+
/**
|
|
1138
|
+
* Enable or disable the total row
|
|
1139
|
+
* @param enabled - Whether total row should be shown
|
|
1140
|
+
* @returns this for method chaining
|
|
804
1141
|
*/ setTotalRow(enabled) {
|
|
805
1142
|
if (this._totalRow === enabled) return this;
|
|
806
1143
|
this._totalRow = enabled;
|
|
@@ -830,8 +1167,8 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
830
1167
|
}
|
|
831
1168
|
return this;
|
|
832
1169
|
}
|
|
833
|
-
/**
|
|
834
|
-
* Extract column names from the header row of the worksheet
|
|
1170
|
+
/**
|
|
1171
|
+
* Extract column names from the header row of the worksheet
|
|
835
1172
|
*/ _extractColumns() {
|
|
836
1173
|
const headerRow = this._range.start.row;
|
|
837
1174
|
const startCol = this._range.start.col;
|
|
@@ -847,8 +1184,8 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
847
1184
|
});
|
|
848
1185
|
}
|
|
849
1186
|
}
|
|
850
|
-
/**
|
|
851
|
-
* Write the SUBTOTAL formula to a total row cell
|
|
1187
|
+
/**
|
|
1188
|
+
* Write the SUBTOTAL formula to a total row cell
|
|
852
1189
|
*/ _writeTotalRowFormula(column) {
|
|
853
1190
|
if (!this._totalRow || !column.totalFunction || column.totalFunction === 'none') {
|
|
854
1191
|
return;
|
|
@@ -861,8 +1198,8 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
861
1198
|
const formula = `SUBTOTAL(${funcNum},[${column.name}])`;
|
|
862
1199
|
cell.formula = formula;
|
|
863
1200
|
}
|
|
864
|
-
/**
|
|
865
|
-
* Get the auto-filter range (excludes total row if present)
|
|
1201
|
+
/**
|
|
1202
|
+
* Get the auto-filter range (excludes total row if present)
|
|
866
1203
|
*/ _getAutoFilterRange() {
|
|
867
1204
|
const start = toAddress(this._range.start.row, this._range.start.col);
|
|
868
1205
|
// Auto-filter excludes the total row
|
|
@@ -873,8 +1210,8 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
873
1210
|
const end = toAddress(endRow, this._range.end.col);
|
|
874
1211
|
return `${start}:${end}`;
|
|
875
1212
|
}
|
|
876
|
-
/**
|
|
877
|
-
* Generate the table definition XML
|
|
1213
|
+
/**
|
|
1214
|
+
* Generate the table definition XML
|
|
878
1215
|
*/ toXml() {
|
|
879
1216
|
const children = [];
|
|
880
1217
|
// Auto-filter element
|
|
@@ -1354,12 +1691,12 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
1354
1691
|
return table;
|
|
1355
1692
|
}
|
|
1356
1693
|
/**
|
|
1357
|
-
* Convert sheet data to an array of JSON objects.
|
|
1358
|
-
*
|
|
1359
|
-
* @param config - Configuration options
|
|
1360
|
-
* @returns Array of objects where keys are field names and values are cell values
|
|
1694
|
+
* Convert sheet data to an array of JSON objects.
|
|
1361
1695
|
*
|
|
1362
|
-
* @
|
|
1696
|
+
* @param config - Configuration options
|
|
1697
|
+
* @returns Array of objects where keys are field names and values are cell values
|
|
1698
|
+
*
|
|
1699
|
+
* @example
|
|
1363
1700
|
* ```typescript
|
|
1364
1701
|
* // Using first row as headers
|
|
1365
1702
|
* const data = sheet.toJson();
|
|
@@ -1368,10 +1705,10 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
1368
1705
|
* const data = sheet.toJson({ fields: ['name', 'age', 'city'] });
|
|
1369
1706
|
*
|
|
1370
1707
|
* // Starting from a specific row/column
|
|
1371
|
-
* const data = sheet.toJson({ startRow: 2, startCol: 1 });
|
|
1372
|
-
* ```
|
|
1708
|
+
* const data = sheet.toJson({ startRow: 2, startCol: 1 });
|
|
1709
|
+
* ```
|
|
1373
1710
|
*/ toJson(config = {}) {
|
|
1374
|
-
const { fields, startRow = 0, startCol = 0, endRow, endCol, stopOnEmptyRow = true, dateHandling = this._workbook.dateHandling, asText = false } = config;
|
|
1711
|
+
const { fields, startRow = 0, startCol = 0, endRow, endCol, stopOnEmptyRow = true, dateHandling = this._workbook.dateHandling, asText = false, locale } = config;
|
|
1375
1712
|
// Get the bounds of data in the sheet
|
|
1376
1713
|
const bounds = this._getDataBounds();
|
|
1377
1714
|
if (!bounds) {
|
|
@@ -1407,7 +1744,7 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
1407
1744
|
let value;
|
|
1408
1745
|
if (asText) {
|
|
1409
1746
|
// Return formatted text instead of raw value
|
|
1410
|
-
value = cell?.
|
|
1747
|
+
value = cell?.textWithLocale(locale) ?? '';
|
|
1411
1748
|
if (value !== '') {
|
|
1412
1749
|
hasData = true;
|
|
1413
1750
|
}
|
|
@@ -1748,12 +2085,12 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
1748
2085
|
}
|
|
1749
2086
|
}
|
|
1750
2087
|
|
|
1751
|
-
/**
|
|
1752
|
-
* Manages the shared strings table from xl/sharedStrings.xml
|
|
1753
|
-
* Excel stores strings in a shared table to reduce file size
|
|
2088
|
+
/**
|
|
2089
|
+
* Manages the shared strings table from xl/sharedStrings.xml
|
|
2090
|
+
* Excel stores strings in a shared table to reduce file size
|
|
1754
2091
|
*/ class SharedStrings {
|
|
1755
|
-
/**
|
|
1756
|
-
* Parse shared strings from XML content
|
|
2092
|
+
/**
|
|
2093
|
+
* Parse shared strings from XML content
|
|
1757
2094
|
*/ static parse(xml) {
|
|
1758
2095
|
const ss = new SharedStrings();
|
|
1759
2096
|
const parsed = parseXml(xml);
|
|
@@ -1783,9 +2120,9 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
1783
2120
|
}
|
|
1784
2121
|
return ss;
|
|
1785
2122
|
}
|
|
1786
|
-
/**
|
|
1787
|
-
* Extract text from a string item (si element)
|
|
1788
|
-
* Handles both simple <t> elements and rich text <r> elements
|
|
2123
|
+
/**
|
|
2124
|
+
* Extract text from a string item (si element)
|
|
2125
|
+
* Handles both simple <t> elements and rich text <r> elements
|
|
1789
2126
|
*/ extractText(nodes) {
|
|
1790
2127
|
let text = '';
|
|
1791
2128
|
for (const node of nodes){
|
|
@@ -1814,14 +2151,14 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
1814
2151
|
}
|
|
1815
2152
|
return text;
|
|
1816
2153
|
}
|
|
1817
|
-
/**
|
|
1818
|
-
* Get a string by index
|
|
2154
|
+
/**
|
|
2155
|
+
* Get a string by index
|
|
1819
2156
|
*/ getString(index) {
|
|
1820
2157
|
return this.entries[index]?.text;
|
|
1821
2158
|
}
|
|
1822
|
-
/**
|
|
1823
|
-
* Add a string and return its index
|
|
1824
|
-
* If the string already exists, returns the existing index
|
|
2159
|
+
/**
|
|
2160
|
+
* Add a string and return its index
|
|
2161
|
+
* If the string already exists, returns the existing index
|
|
1825
2162
|
*/ addString(str) {
|
|
1826
2163
|
const existing = this.stringToIndex.get(str);
|
|
1827
2164
|
if (existing !== undefined) {
|
|
@@ -1847,23 +2184,23 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
1847
2184
|
this._dirty = true;
|
|
1848
2185
|
return index;
|
|
1849
2186
|
}
|
|
1850
|
-
/**
|
|
1851
|
-
* Check if the shared strings table has been modified
|
|
2187
|
+
/**
|
|
2188
|
+
* Check if the shared strings table has been modified
|
|
1852
2189
|
*/ get dirty() {
|
|
1853
2190
|
return this._dirty;
|
|
1854
2191
|
}
|
|
1855
|
-
/**
|
|
1856
|
-
* Get the count of strings
|
|
2192
|
+
/**
|
|
2193
|
+
* Get the count of strings
|
|
1857
2194
|
*/ get count() {
|
|
1858
2195
|
return this.entries.length;
|
|
1859
2196
|
}
|
|
1860
|
-
/**
|
|
1861
|
-
* Get total usage count of shared strings
|
|
2197
|
+
/**
|
|
2198
|
+
* Get total usage count of shared strings
|
|
1862
2199
|
*/ get totalCount() {
|
|
1863
2200
|
return Math.max(this._totalCount, this.entries.length);
|
|
1864
2201
|
}
|
|
1865
|
-
/**
|
|
1866
|
-
* Generate XML for the shared strings table
|
|
2202
|
+
/**
|
|
2203
|
+
* Generate XML for the shared strings table
|
|
1867
2204
|
*/ toXml() {
|
|
1868
2205
|
const siElements = [];
|
|
1869
2206
|
for (const entry of this.entries){
|
|
@@ -1900,9 +2237,9 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
1900
2237
|
}
|
|
1901
2238
|
}
|
|
1902
2239
|
|
|
1903
|
-
/**
|
|
1904
|
-
* Excel built-in number format IDs (0-163 are reserved).
|
|
1905
|
-
* These formats don't need to be defined in the numFmts element.
|
|
2240
|
+
/**
|
|
2241
|
+
* Excel built-in number format IDs (0-163 are reserved).
|
|
2242
|
+
* These formats don't need to be defined in the numFmts element.
|
|
1906
2243
|
*/ const BUILTIN_NUM_FMTS = new Map([
|
|
1907
2244
|
[
|
|
1908
2245
|
'General',
|
|
@@ -2017,15 +2354,15 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
2017
2354
|
49
|
|
2018
2355
|
]
|
|
2019
2356
|
]);
|
|
2020
|
-
/**
|
|
2021
|
-
* Reverse lookup: built-in format ID -> format code
|
|
2357
|
+
/**
|
|
2358
|
+
* Reverse lookup: built-in format ID -> format code
|
|
2022
2359
|
*/ const BUILTIN_NUM_FMT_CODES = new Map(Array.from(BUILTIN_NUM_FMTS.entries()).map(([code, id])=>[
|
|
2023
2360
|
id,
|
|
2024
2361
|
code
|
|
2025
2362
|
]));
|
|
2026
|
-
/**
|
|
2027
|
-
* Normalize a color to ARGB format (8 hex chars).
|
|
2028
|
-
* Accepts: "#RGB", "#RRGGBB", "RGB", "RRGGBB", "AARRGGBB", "#AARRGGBB"
|
|
2363
|
+
/**
|
|
2364
|
+
* Normalize a color to ARGB format (8 hex chars).
|
|
2365
|
+
* Accepts: "#RGB", "#RRGGBB", "RGB", "RRGGBB", "AARRGGBB", "#AARRGGBB"
|
|
2029
2366
|
*/ const normalizeColor = (color)=>{
|
|
2030
2367
|
let c = color.replace(/^#/, '').toUpperCase();
|
|
2031
2368
|
// Handle shorthand 3-char format (e.g., "FFF" -> "FFFFFF")
|
|
@@ -2038,14 +2375,14 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
2038
2375
|
}
|
|
2039
2376
|
return c;
|
|
2040
2377
|
};
|
|
2041
|
-
/**
|
|
2042
|
-
* Manages the styles (xl/styles.xml)
|
|
2378
|
+
/**
|
|
2379
|
+
* Manages the styles (xl/styles.xml)
|
|
2043
2380
|
*/ class Styles {
|
|
2044
|
-
/**
|
|
2045
|
-
* Generate a deterministic cache key for a style object.
|
|
2046
|
-
* More efficient than JSON.stringify as it avoids the overhead of
|
|
2047
|
-
* full JSON serialization and produces a consistent key regardless
|
|
2048
|
-
* of property order.
|
|
2381
|
+
/**
|
|
2382
|
+
* Generate a deterministic cache key for a style object.
|
|
2383
|
+
* More efficient than JSON.stringify as it avoids the overhead of
|
|
2384
|
+
* full JSON serialization and produces a consistent key regardless
|
|
2385
|
+
* of property order.
|
|
2049
2386
|
*/ _getStyleKey(style) {
|
|
2050
2387
|
// Use a delimiter that won't appear in values
|
|
2051
2388
|
const SEP = '\x00';
|
|
@@ -2085,8 +2422,8 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
2085
2422
|
}
|
|
2086
2423
|
return parts.join(SEP);
|
|
2087
2424
|
}
|
|
2088
|
-
/**
|
|
2089
|
-
* Parse styles from XML content
|
|
2425
|
+
/**
|
|
2426
|
+
* Parse styles from XML content
|
|
2090
2427
|
*/ static parse(xml) {
|
|
2091
2428
|
const styles = new Styles();
|
|
2092
2429
|
styles._xmlNodes = parseXml(xml);
|
|
@@ -2142,8 +2479,8 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
2142
2479
|
}
|
|
2143
2480
|
return styles;
|
|
2144
2481
|
}
|
|
2145
|
-
/**
|
|
2146
|
-
* Create an empty styles object with defaults
|
|
2482
|
+
/**
|
|
2483
|
+
* Create an empty styles object with defaults
|
|
2147
2484
|
*/ static createDefault() {
|
|
2148
2485
|
const styles = new Styles();
|
|
2149
2486
|
// Default font (Calibri 11)
|
|
@@ -2282,8 +2619,8 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
2282
2619
|
textRotation: parseInt(getAttr(alignNode, 'textRotation') || '0', 10)
|
|
2283
2620
|
};
|
|
2284
2621
|
}
|
|
2285
|
-
/**
|
|
2286
|
-
* Get a style by index
|
|
2622
|
+
/**
|
|
2623
|
+
* Get a style by index
|
|
2287
2624
|
*/ getStyle(index) {
|
|
2288
2625
|
const cached = this._styleObjectCache.get(index);
|
|
2289
2626
|
if (cached) return {
|
|
@@ -2347,9 +2684,9 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
2347
2684
|
});
|
|
2348
2685
|
return style;
|
|
2349
2686
|
}
|
|
2350
|
-
/**
|
|
2351
|
-
* Create a style and return its index
|
|
2352
|
-
* Uses caching to deduplicate identical styles
|
|
2687
|
+
/**
|
|
2688
|
+
* Create a style and return its index
|
|
2689
|
+
* Uses caching to deduplicate identical styles
|
|
2353
2690
|
*/ createStyle(style) {
|
|
2354
2691
|
const key = this._getStyleKey(style);
|
|
2355
2692
|
const cached = this._styleCache.get(key);
|
|
@@ -2388,8 +2725,8 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
2388
2725
|
});
|
|
2389
2726
|
return index;
|
|
2390
2727
|
}
|
|
2391
|
-
/**
|
|
2392
|
-
* Clone an existing style by index, optionally overriding fields.
|
|
2728
|
+
/**
|
|
2729
|
+
* Clone an existing style by index, optionally overriding fields.
|
|
2393
2730
|
*/ cloneStyle(index, overrides = {}) {
|
|
2394
2731
|
const baseStyle = this.getStyle(index);
|
|
2395
2732
|
return this.createStyle({
|
|
@@ -2473,21 +2810,21 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
2473
2810
|
this._numFmts.set(id, format);
|
|
2474
2811
|
return id;
|
|
2475
2812
|
}
|
|
2476
|
-
/**
|
|
2477
|
-
* Get or create a number format ID for the given format string.
|
|
2478
|
-
* Returns built-in IDs (0-163) for standard formats, or creates custom IDs (164+).
|
|
2479
|
-
* @param format - The number format string (e.g., '0.00', '#,##0', '$#,##0.00')
|
|
2813
|
+
/**
|
|
2814
|
+
* Get or create a number format ID for the given format string.
|
|
2815
|
+
* Returns built-in IDs (0-163) for standard formats, or creates custom IDs (164+).
|
|
2816
|
+
* @param format - The number format string (e.g., '0.00', '#,##0', '$#,##0.00')
|
|
2480
2817
|
*/ getOrCreateNumFmtId(format) {
|
|
2481
2818
|
this._dirty = true;
|
|
2482
2819
|
return this._findOrCreateNumFmt(format);
|
|
2483
2820
|
}
|
|
2484
|
-
/**
|
|
2485
|
-
* Check if styles have been modified
|
|
2821
|
+
/**
|
|
2822
|
+
* Check if styles have been modified
|
|
2486
2823
|
*/ get dirty() {
|
|
2487
2824
|
return this._dirty;
|
|
2488
2825
|
}
|
|
2489
|
-
/**
|
|
2490
|
-
* Generate XML for styles
|
|
2826
|
+
/**
|
|
2827
|
+
* Generate XML for styles
|
|
2491
2828
|
*/ toXml() {
|
|
2492
2829
|
const children = [];
|
|
2493
2830
|
// Number formats
|
|
@@ -2687,8 +3024,8 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
2687
3024
|
}
|
|
2688
3025
|
}
|
|
2689
3026
|
|
|
2690
|
-
/**
|
|
2691
|
-
* Represents an Excel pivot table with a fluent API for configuration.
|
|
3027
|
+
/**
|
|
3028
|
+
* Represents an Excel pivot table with a fluent API for configuration.
|
|
2692
3029
|
*/ class PivotTable {
|
|
2693
3030
|
constructor(name, cache, targetSheet, targetCell, targetRow, targetCol, pivotTableIndex, cacheFileIndex){
|
|
2694
3031
|
this._rowFields = [];
|
|
@@ -2706,47 +3043,47 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
2706
3043
|
this._pivotTableIndex = pivotTableIndex;
|
|
2707
3044
|
this._cacheFileIndex = cacheFileIndex;
|
|
2708
3045
|
}
|
|
2709
|
-
/**
|
|
2710
|
-
* Get the pivot table name
|
|
3046
|
+
/**
|
|
3047
|
+
* Get the pivot table name
|
|
2711
3048
|
*/ get name() {
|
|
2712
3049
|
return this._name;
|
|
2713
3050
|
}
|
|
2714
|
-
/**
|
|
2715
|
-
* Get the target sheet name
|
|
3051
|
+
/**
|
|
3052
|
+
* Get the target sheet name
|
|
2716
3053
|
*/ get targetSheet() {
|
|
2717
3054
|
return this._targetSheet;
|
|
2718
3055
|
}
|
|
2719
|
-
/**
|
|
2720
|
-
* Get the target cell address
|
|
3056
|
+
/**
|
|
3057
|
+
* Get the target cell address
|
|
2721
3058
|
*/ get targetCell() {
|
|
2722
3059
|
return this._targetCell;
|
|
2723
3060
|
}
|
|
2724
|
-
/**
|
|
2725
|
-
* Get the pivot cache
|
|
3061
|
+
/**
|
|
3062
|
+
* Get the pivot cache
|
|
2726
3063
|
*/ get cache() {
|
|
2727
3064
|
return this._cache;
|
|
2728
3065
|
}
|
|
2729
|
-
/**
|
|
2730
|
-
* Get the pivot table index (for file naming)
|
|
3066
|
+
/**
|
|
3067
|
+
* Get the pivot table index (for file naming)
|
|
2731
3068
|
*/ get index() {
|
|
2732
3069
|
return this._pivotTableIndex;
|
|
2733
3070
|
}
|
|
2734
|
-
/**
|
|
2735
|
-
* Get the pivot cache file index used for rels.
|
|
2736
|
-
* @internal
|
|
3071
|
+
/**
|
|
3072
|
+
* Get the pivot cache file index used for rels.
|
|
3073
|
+
* @internal
|
|
2737
3074
|
*/ get cacheFileIndex() {
|
|
2738
3075
|
return this._cacheFileIndex;
|
|
2739
3076
|
}
|
|
2740
|
-
/**
|
|
2741
|
-
* Set the styles reference for number format resolution
|
|
2742
|
-
* @internal
|
|
3077
|
+
/**
|
|
3078
|
+
* Set the styles reference for number format resolution
|
|
3079
|
+
* @internal
|
|
2743
3080
|
*/ setStyles(styles) {
|
|
2744
3081
|
this._styles = styles;
|
|
2745
3082
|
return this;
|
|
2746
3083
|
}
|
|
2747
|
-
/**
|
|
2748
|
-
* Add a field to the row area
|
|
2749
|
-
* @param fieldName - Name of the source field (column header)
|
|
3084
|
+
/**
|
|
3085
|
+
* Add a field to the row area
|
|
3086
|
+
* @param fieldName - Name of the source field (column header)
|
|
2750
3087
|
*/ addRowField(fieldName) {
|
|
2751
3088
|
const fieldIndex = this._cache.getFieldIndex(fieldName);
|
|
2752
3089
|
if (fieldIndex < 0) {
|
|
@@ -2761,9 +3098,9 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
2761
3098
|
this._fieldAssignments.set(fieldIndex, assignment);
|
|
2762
3099
|
return this;
|
|
2763
3100
|
}
|
|
2764
|
-
/**
|
|
2765
|
-
* Add a field to the column area
|
|
2766
|
-
* @param fieldName - Name of the source field (column header)
|
|
3101
|
+
/**
|
|
3102
|
+
* Add a field to the column area
|
|
3103
|
+
* @param fieldName - Name of the source field (column header)
|
|
2767
3104
|
*/ addColumnField(fieldName) {
|
|
2768
3105
|
const fieldIndex = this._cache.getFieldIndex(fieldName);
|
|
2769
3106
|
if (fieldIndex < 0) {
|
|
@@ -2817,9 +3154,9 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
2817
3154
|
this._fieldAssignments.set(fieldIndex, assignment);
|
|
2818
3155
|
return this;
|
|
2819
3156
|
}
|
|
2820
|
-
/**
|
|
2821
|
-
* Add a field to the filter (page) area
|
|
2822
|
-
* @param fieldName - Name of the source field (column header)
|
|
3157
|
+
/**
|
|
3158
|
+
* Add a field to the filter (page) area
|
|
3159
|
+
* @param fieldName - Name of the source field (column header)
|
|
2823
3160
|
*/ addFilterField(fieldName) {
|
|
2824
3161
|
const fieldIndex = this._cache.getFieldIndex(fieldName);
|
|
2825
3162
|
if (fieldIndex < 0) {
|
|
@@ -2834,10 +3171,10 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
2834
3171
|
this._fieldAssignments.set(fieldIndex, assignment);
|
|
2835
3172
|
return this;
|
|
2836
3173
|
}
|
|
2837
|
-
/**
|
|
2838
|
-
* Set a sort order for a row or column field
|
|
2839
|
-
* @param fieldName - Name of the field to sort
|
|
2840
|
-
* @param order - Sort order ('asc' or 'desc')
|
|
3174
|
+
/**
|
|
3175
|
+
* Set a sort order for a row or column field
|
|
3176
|
+
* @param fieldName - Name of the field to sort
|
|
3177
|
+
* @param order - Sort order ('asc' or 'desc')
|
|
2841
3178
|
*/ sortField(fieldName, order) {
|
|
2842
3179
|
const fieldIndex = this._cache.getFieldIndex(fieldName);
|
|
2843
3180
|
if (fieldIndex < 0) {
|
|
@@ -2853,10 +3190,10 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
2853
3190
|
assignment.sortOrder = order;
|
|
2854
3191
|
return this;
|
|
2855
3192
|
}
|
|
2856
|
-
/**
|
|
2857
|
-
* Filter items for a row, column, or filter field
|
|
2858
|
-
* @param fieldName - Name of the field to filter
|
|
2859
|
-
* @param filter - Filter configuration with include or exclude list
|
|
3193
|
+
/**
|
|
3194
|
+
* Filter items for a row, column, or filter field
|
|
3195
|
+
* @param fieldName - Name of the field to filter
|
|
3196
|
+
* @param filter - Filter configuration with include or exclude list
|
|
2860
3197
|
*/ filterField(fieldName, filter) {
|
|
2861
3198
|
const fieldIndex = this._cache.getFieldIndex(fieldName);
|
|
2862
3199
|
if (fieldIndex < 0) {
|
|
@@ -2872,8 +3209,8 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
2872
3209
|
assignment.filter = filter;
|
|
2873
3210
|
return this;
|
|
2874
3211
|
}
|
|
2875
|
-
/**
|
|
2876
|
-
* Generate the pivotTableDefinition XML
|
|
3212
|
+
/**
|
|
3213
|
+
* Generate the pivotTableDefinition XML
|
|
2877
3214
|
*/ toXml() {
|
|
2878
3215
|
const children = [];
|
|
2879
3216
|
// Calculate location (estimate based on fields)
|
|
@@ -3033,8 +3370,8 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
3033
3370
|
pivotTableNode
|
|
3034
3371
|
])}`;
|
|
3035
3372
|
}
|
|
3036
|
-
/**
|
|
3037
|
-
* Build a pivotField node for a given field index
|
|
3373
|
+
/**
|
|
3374
|
+
* Build a pivotField node for a given field index
|
|
3038
3375
|
*/ _buildPivotFieldNode(fieldIndex) {
|
|
3039
3376
|
const attrs = {};
|
|
3040
3377
|
const children = [];
|
|
@@ -3092,8 +3429,8 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
3092
3429
|
}
|
|
3093
3430
|
return createElement('pivotField', attrs, children);
|
|
3094
3431
|
}
|
|
3095
|
-
/**
|
|
3096
|
-
* Build item nodes for a pivot field, with optional filtering
|
|
3432
|
+
/**
|
|
3433
|
+
* Build item nodes for a pivot field, with optional filtering
|
|
3097
3434
|
*/ _buildItemNodes(sharedItems, filter) {
|
|
3098
3435
|
const itemNodes = [];
|
|
3099
3436
|
for(let i = 0; i < sharedItems.length; i++){
|
|
@@ -3121,8 +3458,8 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
3121
3458
|
}, []));
|
|
3122
3459
|
return itemNodes;
|
|
3123
3460
|
}
|
|
3124
|
-
/**
|
|
3125
|
-
* Build row items based on unique values in row fields
|
|
3461
|
+
/**
|
|
3462
|
+
* Build row items based on unique values in row fields
|
|
3126
3463
|
*/ _buildRowItems() {
|
|
3127
3464
|
const items = [];
|
|
3128
3465
|
if (this._rowFields.length === 0) return items;
|
|
@@ -3146,8 +3483,8 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
3146
3483
|
]));
|
|
3147
3484
|
return items;
|
|
3148
3485
|
}
|
|
3149
|
-
/**
|
|
3150
|
-
* Build column items based on unique values in column fields
|
|
3486
|
+
/**
|
|
3487
|
+
* Build column items based on unique values in column fields
|
|
3151
3488
|
*/ _buildColItems() {
|
|
3152
3489
|
const items = [];
|
|
3153
3490
|
if (this._columnFields.length === 0) return items;
|
|
@@ -3204,8 +3541,8 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
3204
3541
|
}
|
|
3205
3542
|
return items;
|
|
3206
3543
|
}
|
|
3207
|
-
/**
|
|
3208
|
-
* Calculate the location reference for the pivot table output
|
|
3544
|
+
/**
|
|
3545
|
+
* Calculate the location reference for the pivot table output
|
|
3209
3546
|
*/ _calculateLocationRef() {
|
|
3210
3547
|
// Estimate output size based on fields
|
|
3211
3548
|
const numRows = this._estimateRowCount();
|
|
@@ -3216,8 +3553,8 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
3216
3553
|
const endCol = startCol + numCols - 1;
|
|
3217
3554
|
return `${this._colToLetter(startCol)}${startRow}:${this._colToLetter(endCol)}${endRow}`;
|
|
3218
3555
|
}
|
|
3219
|
-
/**
|
|
3220
|
-
* Estimate number of rows in pivot table output
|
|
3556
|
+
/**
|
|
3557
|
+
* Estimate number of rows in pivot table output
|
|
3221
3558
|
*/ _estimateRowCount() {
|
|
3222
3559
|
let count = 1; // Header row
|
|
3223
3560
|
// Add filter area rows
|
|
@@ -3232,8 +3569,8 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
3232
3569
|
}
|
|
3233
3570
|
return Math.max(count, 3);
|
|
3234
3571
|
}
|
|
3235
|
-
/**
|
|
3236
|
-
* Estimate number of columns in pivot table output
|
|
3572
|
+
/**
|
|
3573
|
+
* Estimate number of columns in pivot table output
|
|
3237
3574
|
*/ _estimateColCount() {
|
|
3238
3575
|
let count = 0;
|
|
3239
3576
|
// Row label columns
|
|
@@ -3249,8 +3586,8 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
3249
3586
|
}
|
|
3250
3587
|
return Math.max(count, 2);
|
|
3251
3588
|
}
|
|
3252
|
-
/**
|
|
3253
|
-
* Convert 0-based column index to letter (A, B, ..., Z, AA, etc.)
|
|
3589
|
+
/**
|
|
3590
|
+
* Convert 0-based column index to letter (A, B, ..., Z, AA, etc.)
|
|
3254
3591
|
*/ _colToLetter(col) {
|
|
3255
3592
|
let result = '';
|
|
3256
3593
|
let n = col;
|
|
@@ -3262,9 +3599,9 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
3262
3599
|
}
|
|
3263
3600
|
}
|
|
3264
3601
|
|
|
3265
|
-
/**
|
|
3266
|
-
* Manages the pivot cache (definition and records) for a pivot table.
|
|
3267
|
-
* The cache stores source data metadata and cached values.
|
|
3602
|
+
/**
|
|
3603
|
+
* Manages the pivot cache (definition and records) for a pivot table.
|
|
3604
|
+
* The cache stores source data metadata and cached values.
|
|
3268
3605
|
*/ class PivotCache {
|
|
3269
3606
|
constructor(cacheId, sourceSheet, sourceRange, fileIndex){
|
|
3270
3607
|
this._fields = [];
|
|
@@ -3278,55 +3615,55 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
3278
3615
|
this._sourceSheet = sourceSheet;
|
|
3279
3616
|
this._sourceRange = sourceRange;
|
|
3280
3617
|
}
|
|
3281
|
-
/**
|
|
3282
|
-
* Get the cache ID
|
|
3618
|
+
/**
|
|
3619
|
+
* Get the cache ID
|
|
3283
3620
|
*/ get cacheId() {
|
|
3284
3621
|
return this._cacheId;
|
|
3285
3622
|
}
|
|
3286
|
-
/**
|
|
3287
|
-
* Get the file index for this cache (used for file naming).
|
|
3623
|
+
/**
|
|
3624
|
+
* Get the file index for this cache (used for file naming).
|
|
3288
3625
|
*/ get fileIndex() {
|
|
3289
3626
|
return this._fileIndex;
|
|
3290
3627
|
}
|
|
3291
|
-
/**
|
|
3292
|
-
* Set refreshOnLoad option
|
|
3628
|
+
/**
|
|
3629
|
+
* Set refreshOnLoad option
|
|
3293
3630
|
*/ set refreshOnLoad(value) {
|
|
3294
3631
|
this._refreshOnLoad = value;
|
|
3295
3632
|
}
|
|
3296
|
-
/**
|
|
3297
|
-
* Get refreshOnLoad option
|
|
3633
|
+
/**
|
|
3634
|
+
* Get refreshOnLoad option
|
|
3298
3635
|
*/ get refreshOnLoad() {
|
|
3299
3636
|
return this._refreshOnLoad;
|
|
3300
3637
|
}
|
|
3301
|
-
/**
|
|
3302
|
-
* Get the source sheet name
|
|
3638
|
+
/**
|
|
3639
|
+
* Get the source sheet name
|
|
3303
3640
|
*/ get sourceSheet() {
|
|
3304
3641
|
return this._sourceSheet;
|
|
3305
3642
|
}
|
|
3306
|
-
/**
|
|
3307
|
-
* Get the source range
|
|
3643
|
+
/**
|
|
3644
|
+
* Get the source range
|
|
3308
3645
|
*/ get sourceRange() {
|
|
3309
3646
|
return this._sourceRange;
|
|
3310
3647
|
}
|
|
3311
|
-
/**
|
|
3312
|
-
* Get the full source reference (Sheet!Range)
|
|
3648
|
+
/**
|
|
3649
|
+
* Get the full source reference (Sheet!Range)
|
|
3313
3650
|
*/ get sourceRef() {
|
|
3314
3651
|
return `${this._sourceSheet}!${this._sourceRange}`;
|
|
3315
3652
|
}
|
|
3316
|
-
/**
|
|
3317
|
-
* Get the fields in this cache
|
|
3653
|
+
/**
|
|
3654
|
+
* Get the fields in this cache
|
|
3318
3655
|
*/ get fields() {
|
|
3319
3656
|
return this._fields;
|
|
3320
3657
|
}
|
|
3321
|
-
/**
|
|
3322
|
-
* Get the number of data records
|
|
3658
|
+
/**
|
|
3659
|
+
* Get the number of data records
|
|
3323
3660
|
*/ get recordCount() {
|
|
3324
3661
|
return this._recordCount;
|
|
3325
3662
|
}
|
|
3326
|
-
/**
|
|
3327
|
-
* Build the cache from source data.
|
|
3328
|
-
* @param headers - Array of column header names
|
|
3329
|
-
* @param data - 2D array of data rows (excluding headers)
|
|
3663
|
+
/**
|
|
3664
|
+
* Build the cache from source data.
|
|
3665
|
+
* @param headers - Array of column header names
|
|
3666
|
+
* @param data - 2D array of data rows (excluding headers)
|
|
3330
3667
|
*/ buildFromData(headers, data) {
|
|
3331
3668
|
this._recordCount = data.length;
|
|
3332
3669
|
// Initialize fields from headers
|
|
@@ -3387,19 +3724,19 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
3387
3724
|
// Store records
|
|
3388
3725
|
this._records = data;
|
|
3389
3726
|
}
|
|
3390
|
-
/**
|
|
3391
|
-
* Get field by name
|
|
3727
|
+
/**
|
|
3728
|
+
* Get field by name
|
|
3392
3729
|
*/ getField(name) {
|
|
3393
3730
|
return this._fields.find((f)=>f.name === name);
|
|
3394
3731
|
}
|
|
3395
|
-
/**
|
|
3396
|
-
* Get field index by name
|
|
3732
|
+
/**
|
|
3733
|
+
* Get field index by name
|
|
3397
3734
|
*/ getFieldIndex(name) {
|
|
3398
3735
|
const field = this._fields.find((f)=>f.name === name);
|
|
3399
3736
|
return field ? field.index : -1;
|
|
3400
3737
|
}
|
|
3401
|
-
/**
|
|
3402
|
-
* Generate the pivotCacheDefinition XML
|
|
3738
|
+
/**
|
|
3739
|
+
* Generate the pivotCacheDefinition XML
|
|
3403
3740
|
*/ toDefinitionXml(recordsRelId) {
|
|
3404
3741
|
const cacheFieldNodes = this._fields.map((field)=>{
|
|
3405
3742
|
const sharedItemsAttrs = {};
|
|
@@ -3471,8 +3808,8 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
3471
3808
|
definitionNode
|
|
3472
3809
|
])}`;
|
|
3473
3810
|
}
|
|
3474
|
-
/**
|
|
3475
|
-
* Generate the pivotCacheRecords XML
|
|
3811
|
+
/**
|
|
3812
|
+
* Generate the pivotCacheRecords XML
|
|
3476
3813
|
*/ toRecordsXml() {
|
|
3477
3814
|
const recordNodes = [];
|
|
3478
3815
|
for (const row of this._records){
|
|
@@ -3577,8 +3914,8 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
3577
3914
|
files.set(path, fflate.strToU8(content));
|
|
3578
3915
|
};
|
|
3579
3916
|
|
|
3580
|
-
/**
|
|
3581
|
-
* Represents an Excel workbook (.xlsx file)
|
|
3917
|
+
/**
|
|
3918
|
+
* Represents an Excel workbook (.xlsx file)
|
|
3582
3919
|
*/ class Workbook {
|
|
3583
3920
|
constructor(){
|
|
3584
3921
|
this._files = new Map();
|
|
@@ -3595,17 +3932,18 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
3595
3932
|
this._nextTableId = 1;
|
|
3596
3933
|
// Date serialization handling
|
|
3597
3934
|
this._dateHandling = 'jsDate';
|
|
3935
|
+
this._locale = 'fr-FR';
|
|
3598
3936
|
this._sharedStrings = new SharedStrings();
|
|
3599
3937
|
this._styles = Styles.createDefault();
|
|
3600
3938
|
}
|
|
3601
|
-
/**
|
|
3602
|
-
* Load a workbook from a file path
|
|
3939
|
+
/**
|
|
3940
|
+
* Load a workbook from a file path
|
|
3603
3941
|
*/ static async fromFile(path) {
|
|
3604
3942
|
const data = await promises.readFile(path);
|
|
3605
3943
|
return Workbook.fromBuffer(new Uint8Array(data));
|
|
3606
3944
|
}
|
|
3607
|
-
/**
|
|
3608
|
-
* Load a workbook from a buffer
|
|
3945
|
+
/**
|
|
3946
|
+
* Load a workbook from a buffer
|
|
3609
3947
|
*/ static async fromBuffer(data) {
|
|
3610
3948
|
const workbook = new Workbook();
|
|
3611
3949
|
workbook._files = await readZip(data);
|
|
@@ -3631,52 +3969,62 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
3631
3969
|
}
|
|
3632
3970
|
return workbook;
|
|
3633
3971
|
}
|
|
3634
|
-
/**
|
|
3635
|
-
* Create a new empty workbook
|
|
3972
|
+
/**
|
|
3973
|
+
* Create a new empty workbook
|
|
3636
3974
|
*/ static create() {
|
|
3637
3975
|
const workbook = new Workbook();
|
|
3638
3976
|
workbook._dirty = true;
|
|
3639
3977
|
return workbook;
|
|
3640
3978
|
}
|
|
3641
|
-
/**
|
|
3642
|
-
* Get sheet names
|
|
3979
|
+
/**
|
|
3980
|
+
* Get sheet names
|
|
3643
3981
|
*/ get sheetNames() {
|
|
3644
3982
|
return this._sheetDefs.map((s)=>s.name);
|
|
3645
3983
|
}
|
|
3646
|
-
/**
|
|
3647
|
-
* Get number of sheets
|
|
3984
|
+
/**
|
|
3985
|
+
* Get number of sheets
|
|
3648
3986
|
*/ get sheetCount() {
|
|
3649
3987
|
return this._sheetDefs.length;
|
|
3650
3988
|
}
|
|
3651
|
-
/**
|
|
3652
|
-
* Get shared strings table
|
|
3989
|
+
/**
|
|
3990
|
+
* Get shared strings table
|
|
3653
3991
|
*/ get sharedStrings() {
|
|
3654
3992
|
return this._sharedStrings;
|
|
3655
3993
|
}
|
|
3656
|
-
/**
|
|
3657
|
-
* Get styles
|
|
3994
|
+
/**
|
|
3995
|
+
* Get styles
|
|
3658
3996
|
*/ get styles() {
|
|
3659
3997
|
return this._styles;
|
|
3660
3998
|
}
|
|
3661
|
-
/**
|
|
3662
|
-
* Get the workbook date handling strategy.
|
|
3999
|
+
/**
|
|
4000
|
+
* Get the workbook date handling strategy.
|
|
3663
4001
|
*/ get dateHandling() {
|
|
3664
4002
|
return this._dateHandling;
|
|
3665
4003
|
}
|
|
3666
|
-
/**
|
|
3667
|
-
* Set the workbook date handling strategy.
|
|
4004
|
+
/**
|
|
4005
|
+
* Set the workbook date handling strategy.
|
|
3668
4006
|
*/ set dateHandling(value) {
|
|
3669
4007
|
this._dateHandling = value;
|
|
3670
4008
|
}
|
|
3671
|
-
/**
|
|
3672
|
-
* Get the
|
|
3673
|
-
|
|
3674
|
-
|
|
4009
|
+
/**
|
|
4010
|
+
* Get the workbook locale for formatting.
|
|
4011
|
+
*/ get locale() {
|
|
4012
|
+
return this._locale;
|
|
4013
|
+
}
|
|
4014
|
+
/**
|
|
4015
|
+
* Set the workbook locale for formatting.
|
|
4016
|
+
*/ set locale(value) {
|
|
4017
|
+
this._locale = value;
|
|
4018
|
+
}
|
|
4019
|
+
/**
|
|
4020
|
+
* Get the next unique table ID for this workbook.
|
|
4021
|
+
* Table IDs must be unique across all worksheets.
|
|
4022
|
+
* @internal
|
|
3675
4023
|
*/ getNextTableId() {
|
|
3676
4024
|
return this._nextTableId++;
|
|
3677
4025
|
}
|
|
3678
|
-
/**
|
|
3679
|
-
* Get a worksheet by name or index
|
|
4026
|
+
/**
|
|
4027
|
+
* Get a worksheet by name or index
|
|
3680
4028
|
*/ sheet(nameOrIndex) {
|
|
3681
4029
|
let def;
|
|
3682
4030
|
if (typeof nameOrIndex === 'number') {
|
|
@@ -3705,8 +4053,8 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
3705
4053
|
this._sheets.set(def.name, worksheet);
|
|
3706
4054
|
return worksheet;
|
|
3707
4055
|
}
|
|
3708
|
-
/**
|
|
3709
|
-
* Add a new worksheet
|
|
4056
|
+
/**
|
|
4057
|
+
* Add a new worksheet
|
|
3710
4058
|
*/ addSheet(name, index) {
|
|
3711
4059
|
// Check for duplicate name
|
|
3712
4060
|
if (this._sheetDefs.some((s)=>s.name === name)) {
|
|
@@ -3738,8 +4086,8 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
3738
4086
|
this._sheets.set(name, worksheet);
|
|
3739
4087
|
return worksheet;
|
|
3740
4088
|
}
|
|
3741
|
-
/**
|
|
3742
|
-
* Delete a worksheet by name or index
|
|
4089
|
+
/**
|
|
4090
|
+
* Delete a worksheet by name or index
|
|
3743
4091
|
*/ deleteSheet(nameOrIndex) {
|
|
3744
4092
|
let index;
|
|
3745
4093
|
if (typeof nameOrIndex === 'number') {
|
|
@@ -3768,8 +4116,8 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
3768
4116
|
this._relationships.splice(relIndex, 1);
|
|
3769
4117
|
}
|
|
3770
4118
|
}
|
|
3771
|
-
/**
|
|
3772
|
-
* Rename a worksheet
|
|
4119
|
+
/**
|
|
4120
|
+
* Rename a worksheet
|
|
3773
4121
|
*/ renameSheet(oldName, newName) {
|
|
3774
4122
|
const def = this._sheetDefs.find((s)=>s.name === oldName);
|
|
3775
4123
|
if (!def) {
|
|
@@ -3788,8 +4136,8 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
3788
4136
|
}
|
|
3789
4137
|
def.name = newName;
|
|
3790
4138
|
}
|
|
3791
|
-
/**
|
|
3792
|
-
* Copy a worksheet
|
|
4139
|
+
/**
|
|
4140
|
+
* Copy a worksheet
|
|
3793
4141
|
*/ copySheet(sourceName, newName) {
|
|
3794
4142
|
const source = this.sheet(sourceName);
|
|
3795
4143
|
const copy = this.addSheet(newName);
|
|
@@ -3874,49 +4222,49 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
3874
4222
|
}
|
|
3875
4223
|
return false;
|
|
3876
4224
|
}
|
|
3877
|
-
/**
|
|
3878
|
-
* Create a new worksheet from an array of objects.
|
|
3879
|
-
*
|
|
3880
|
-
* The first row contains headers (object keys or custom column headers),
|
|
3881
|
-
* and subsequent rows contain the object values.
|
|
3882
|
-
*
|
|
3883
|
-
* @param config - Configuration for the sheet creation
|
|
3884
|
-
* @returns The created Worksheet
|
|
3885
|
-
*
|
|
3886
|
-
* @example
|
|
3887
|
-
* ```typescript
|
|
3888
|
-
* const data = [
|
|
3889
|
-
* { name: 'Alice', age: 30, city: 'Paris' },
|
|
3890
|
-
* { name: 'Bob', age: 25, city: 'London' },
|
|
3891
|
-
* { name: 'Charlie', age: 35, city: 'Berlin' },
|
|
3892
|
-
* ];
|
|
3893
|
-
*
|
|
3894
|
-
* // Simple usage - all object keys become columns
|
|
3895
|
-
* const sheet = wb.addSheetFromData({
|
|
3896
|
-
* name: 'People',
|
|
3897
|
-
* data: data,
|
|
3898
|
-
* });
|
|
3899
|
-
*
|
|
3900
|
-
* // With custom column configuration
|
|
3901
|
-
* const sheet2 = wb.addSheetFromData({
|
|
3902
|
-
* name: 'People Custom',
|
|
3903
|
-
* data: data,
|
|
3904
|
-
* columns: [
|
|
3905
|
-
* { key: 'name', header: 'Full Name' },
|
|
3906
|
-
* { key: 'age', header: 'Age (years)' },
|
|
3907
|
-
* ],
|
|
3908
|
-
* });
|
|
3909
|
-
*
|
|
3910
|
-
* // With rich cell values (value, formula, style)
|
|
3911
|
-
* const dataWithFormulas = [
|
|
3912
|
-
* { product: 'Widget', price: 10, qty: 5, total: { formula: 'B2*C2', style: { bold: true } } },
|
|
3913
|
-
* { product: 'Gadget', price: 20, qty: 3, total: { formula: 'B3*C3', style: { bold: true } } },
|
|
3914
|
-
* ];
|
|
3915
|
-
* const sheet3 = wb.addSheetFromData({
|
|
3916
|
-
* name: 'With Formulas',
|
|
3917
|
-
* data: dataWithFormulas,
|
|
3918
|
-
* });
|
|
3919
|
-
* ```
|
|
4225
|
+
/**
|
|
4226
|
+
* Create a new worksheet from an array of objects.
|
|
4227
|
+
*
|
|
4228
|
+
* The first row contains headers (object keys or custom column headers),
|
|
4229
|
+
* and subsequent rows contain the object values.
|
|
4230
|
+
*
|
|
4231
|
+
* @param config - Configuration for the sheet creation
|
|
4232
|
+
* @returns The created Worksheet
|
|
4233
|
+
*
|
|
4234
|
+
* @example
|
|
4235
|
+
* ```typescript
|
|
4236
|
+
* const data = [
|
|
4237
|
+
* { name: 'Alice', age: 30, city: 'Paris' },
|
|
4238
|
+
* { name: 'Bob', age: 25, city: 'London' },
|
|
4239
|
+
* { name: 'Charlie', age: 35, city: 'Berlin' },
|
|
4240
|
+
* ];
|
|
4241
|
+
*
|
|
4242
|
+
* // Simple usage - all object keys become columns
|
|
4243
|
+
* const sheet = wb.addSheetFromData({
|
|
4244
|
+
* name: 'People',
|
|
4245
|
+
* data: data,
|
|
4246
|
+
* });
|
|
4247
|
+
*
|
|
4248
|
+
* // With custom column configuration
|
|
4249
|
+
* const sheet2 = wb.addSheetFromData({
|
|
4250
|
+
* name: 'People Custom',
|
|
4251
|
+
* data: data,
|
|
4252
|
+
* columns: [
|
|
4253
|
+
* { key: 'name', header: 'Full Name' },
|
|
4254
|
+
* { key: 'age', header: 'Age (years)' },
|
|
4255
|
+
* ],
|
|
4256
|
+
* });
|
|
4257
|
+
*
|
|
4258
|
+
* // With rich cell values (value, formula, style)
|
|
4259
|
+
* const dataWithFormulas = [
|
|
4260
|
+
* { product: 'Widget', price: 10, qty: 5, total: { formula: 'B2*C2', style: { bold: true } } },
|
|
4261
|
+
* { product: 'Gadget', price: 20, qty: 3, total: { formula: 'B3*C3', style: { bold: true } } },
|
|
4262
|
+
* ];
|
|
4263
|
+
* const sheet3 = wb.addSheetFromData({
|
|
4264
|
+
* name: 'With Formulas',
|
|
4265
|
+
* data: dataWithFormulas,
|
|
4266
|
+
* });
|
|
4267
|
+
* ```
|
|
3920
4268
|
*/ addSheetFromData(config) {
|
|
3921
4269
|
const { name, data, columns, headerStyle = true, startCell = 'A1' } = config;
|
|
3922
4270
|
if (!data?.length) return this.addSheet(name);
|
|
@@ -3971,8 +4319,8 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
3971
4319
|
}
|
|
3972
4320
|
return sheet;
|
|
3973
4321
|
}
|
|
3974
|
-
/**
|
|
3975
|
-
* Check if a value is a rich cell value object with value, formula, or style fields
|
|
4322
|
+
/**
|
|
4323
|
+
* Check if a value is a rich cell value object with value, formula, or style fields
|
|
3976
4324
|
*/ _isRichCellValue(value) {
|
|
3977
4325
|
if (value === null || value === undefined) {
|
|
3978
4326
|
return false;
|
|
@@ -3984,15 +4332,15 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
3984
4332
|
const obj = value;
|
|
3985
4333
|
return 'value' in obj || 'formula' in obj || 'style' in obj;
|
|
3986
4334
|
}
|
|
3987
|
-
/**
|
|
3988
|
-
* Infer column configuration from the first data object
|
|
4335
|
+
/**
|
|
4336
|
+
* Infer column configuration from the first data object
|
|
3989
4337
|
*/ _inferColumns(sample) {
|
|
3990
4338
|
return Object.keys(sample).map((key)=>({
|
|
3991
4339
|
key
|
|
3992
4340
|
}));
|
|
3993
4341
|
}
|
|
3994
|
-
/**
|
|
3995
|
-
* Convert an unknown value to a CellValue
|
|
4342
|
+
/**
|
|
4343
|
+
* Convert an unknown value to a CellValue
|
|
3996
4344
|
*/ _toCellValue(value) {
|
|
3997
4345
|
if (value === null || value === undefined) {
|
|
3998
4346
|
return null;
|
|
@@ -4009,25 +4357,25 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
4009
4357
|
// Convert other types to string
|
|
4010
4358
|
return String(value);
|
|
4011
4359
|
}
|
|
4012
|
-
/**
|
|
4013
|
-
* Create a pivot table from source data.
|
|
4014
|
-
*
|
|
4015
|
-
* @param config - Pivot table configuration
|
|
4016
|
-
* @returns PivotTable instance for fluent configuration
|
|
4017
|
-
*
|
|
4018
|
-
* @example
|
|
4019
|
-
* ```typescript
|
|
4020
|
-
* const pivot = wb.createPivotTable({
|
|
4021
|
-
* name: 'SalesPivot',
|
|
4022
|
-
* source: 'DataSheet!A1:D100',
|
|
4023
|
-
* target: 'PivotSheet!A3',
|
|
4024
|
-
* });
|
|
4025
|
-
*
|
|
4026
|
-
* pivot
|
|
4027
|
-
* .addRowField('Region')
|
|
4028
|
-
* .addColumnField('Product')
|
|
4029
|
-
* .addValueField('Sales', 'sum', 'Total Sales');
|
|
4030
|
-
* ```
|
|
4360
|
+
/**
|
|
4361
|
+
* Create a pivot table from source data.
|
|
4362
|
+
*
|
|
4363
|
+
* @param config - Pivot table configuration
|
|
4364
|
+
* @returns PivotTable instance for fluent configuration
|
|
4365
|
+
*
|
|
4366
|
+
* @example
|
|
4367
|
+
* ```typescript
|
|
4368
|
+
* const pivot = wb.createPivotTable({
|
|
4369
|
+
* name: 'SalesPivot',
|
|
4370
|
+
* source: 'DataSheet!A1:D100',
|
|
4371
|
+
* target: 'PivotSheet!A3',
|
|
4372
|
+
* });
|
|
4373
|
+
*
|
|
4374
|
+
* pivot
|
|
4375
|
+
* .addRowField('Region')
|
|
4376
|
+
* .addColumnField('Product')
|
|
4377
|
+
* .addValueField('Sales', 'sum', 'Total Sales');
|
|
4378
|
+
* ```
|
|
4031
4379
|
*/ createPivotTable(config) {
|
|
4032
4380
|
this._dirty = true;
|
|
4033
4381
|
// Parse source reference (Sheet!Range)
|
|
@@ -4061,8 +4409,8 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
4061
4409
|
this._pivotTables.push(pivotTable);
|
|
4062
4410
|
return pivotTable;
|
|
4063
4411
|
}
|
|
4064
|
-
/**
|
|
4065
|
-
* Parse a sheet reference like "Sheet1!A1:D100" into sheet name and range
|
|
4412
|
+
/**
|
|
4413
|
+
* Parse a sheet reference like "Sheet1!A1:D100" into sheet name and range
|
|
4066
4414
|
*/ _parseSheetRef(ref) {
|
|
4067
4415
|
const match = ref.match(/^(.+?)!(.+)$/);
|
|
4068
4416
|
if (!match) {
|
|
@@ -4073,8 +4421,8 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
4073
4421
|
range: match[2]
|
|
4074
4422
|
};
|
|
4075
4423
|
}
|
|
4076
|
-
/**
|
|
4077
|
-
* Extract headers and data from a source range
|
|
4424
|
+
/**
|
|
4425
|
+
* Extract headers and data from a source range
|
|
4078
4426
|
*/ _extractSourceData(sheet, rangeStr) {
|
|
4079
4427
|
const range = parseRange(rangeStr);
|
|
4080
4428
|
const headers = [];
|
|
@@ -4098,14 +4446,14 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
4098
4446
|
data
|
|
4099
4447
|
};
|
|
4100
4448
|
}
|
|
4101
|
-
/**
|
|
4102
|
-
* Save the workbook to a file
|
|
4449
|
+
/**
|
|
4450
|
+
* Save the workbook to a file
|
|
4103
4451
|
*/ async toFile(path) {
|
|
4104
4452
|
const buffer = await this.toBuffer();
|
|
4105
4453
|
await promises.writeFile(path, buffer);
|
|
4106
4454
|
}
|
|
4107
|
-
/**
|
|
4108
|
-
* Save the workbook to a buffer
|
|
4455
|
+
/**
|
|
4456
|
+
* Save the workbook to a buffer
|
|
4109
4457
|
*/ async toBuffer() {
|
|
4110
4458
|
// Update files map with modified content
|
|
4111
4459
|
this._updateFiles();
|
|
@@ -4419,8 +4767,8 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
4419
4767
|
])}`);
|
|
4420
4768
|
}
|
|
4421
4769
|
}
|
|
4422
|
-
/**
|
|
4423
|
-
* Generate all pivot table related files
|
|
4770
|
+
/**
|
|
4771
|
+
* Generate all pivot table related files
|
|
4424
4772
|
*/ _updatePivotTableFiles() {
|
|
4425
4773
|
// Track which sheets have pivot tables for their .rels files
|
|
4426
4774
|
const sheetPivotTables = new Map();
|
|
@@ -4538,8 +4886,8 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
4538
4886
|
])}`);
|
|
4539
4887
|
}
|
|
4540
4888
|
}
|
|
4541
|
-
/**
|
|
4542
|
-
* Generate all table related files
|
|
4889
|
+
/**
|
|
4890
|
+
* Generate all table related files
|
|
4543
4891
|
*/ _updateTableFiles() {
|
|
4544
4892
|
// Collect all tables with their global indices
|
|
4545
4893
|
let globalTableIndex = 1;
|