@cj-tech-master/excelts 1.5.0 → 1.6.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (45) hide show
  1. package/dist/browser/excelts.iife.js +1057 -201
  2. package/dist/browser/excelts.iife.js.map +1 -1
  3. package/dist/browser/excelts.iife.min.js +63 -33
  4. package/dist/cjs/doc/column.js +7 -3
  5. package/dist/cjs/doc/pivot-table.js +149 -61
  6. package/dist/cjs/doc/workbook.js +3 -1
  7. package/dist/cjs/doc/worksheet.js +0 -2
  8. package/dist/cjs/stream/xlsx/worksheet-writer.js +1 -1
  9. package/dist/cjs/utils/unzip/zip-parser.js +2 -5
  10. package/dist/cjs/xlsx/xform/book/workbook-xform.js +3 -0
  11. package/dist/cjs/xlsx/xform/core/content-types-xform.js +19 -14
  12. package/dist/cjs/xlsx/xform/pivot-table/cache-field-xform.js +135 -0
  13. package/dist/cjs/xlsx/xform/pivot-table/cache-field.js +7 -4
  14. package/dist/cjs/xlsx/xform/pivot-table/pivot-cache-definition-xform.js +135 -13
  15. package/dist/cjs/xlsx/xform/pivot-table/pivot-cache-records-xform.js +193 -45
  16. package/dist/cjs/xlsx/xform/pivot-table/pivot-table-xform.js +390 -39
  17. package/dist/cjs/xlsx/xform/sheet/cell-xform.js +6 -0
  18. package/dist/cjs/xlsx/xform/sheet/worksheet-xform.js +14 -3
  19. package/dist/cjs/xlsx/xlsx.js +261 -38
  20. package/dist/esm/doc/column.js +7 -3
  21. package/dist/esm/doc/pivot-table.js +150 -62
  22. package/dist/esm/doc/workbook.js +3 -1
  23. package/dist/esm/doc/worksheet.js +0 -2
  24. package/dist/esm/stream/xlsx/worksheet-writer.js +1 -1
  25. package/dist/esm/utils/unzip/zip-parser.js +2 -5
  26. package/dist/esm/xlsx/xform/book/workbook-xform.js +3 -0
  27. package/dist/esm/xlsx/xform/core/content-types-xform.js +19 -14
  28. package/dist/esm/xlsx/xform/pivot-table/cache-field-xform.js +132 -0
  29. package/dist/esm/xlsx/xform/pivot-table/cache-field.js +7 -4
  30. package/dist/esm/xlsx/xform/pivot-table/pivot-cache-definition-xform.js +135 -13
  31. package/dist/esm/xlsx/xform/pivot-table/pivot-cache-records-xform.js +193 -45
  32. package/dist/esm/xlsx/xform/pivot-table/pivot-table-xform.js +390 -39
  33. package/dist/esm/xlsx/xform/sheet/cell-xform.js +6 -0
  34. package/dist/esm/xlsx/xform/sheet/worksheet-xform.js +14 -3
  35. package/dist/esm/xlsx/xlsx.js +261 -38
  36. package/dist/types/doc/column.d.ts +13 -6
  37. package/dist/types/doc/pivot-table.d.ts +135 -9
  38. package/dist/types/doc/workbook.d.ts +2 -0
  39. package/dist/types/index.d.ts +1 -0
  40. package/dist/types/xlsx/xform/pivot-table/cache-field-xform.d.ts +42 -0
  41. package/dist/types/xlsx/xform/pivot-table/pivot-cache-definition-xform.d.ts +45 -6
  42. package/dist/types/xlsx/xform/pivot-table/pivot-cache-records-xform.d.ts +52 -5
  43. package/dist/types/xlsx/xform/pivot-table/pivot-table-xform.d.ts +98 -5
  44. package/dist/types/xlsx/xlsx.d.ts +27 -0
  45. package/package.json +17 -17
@@ -1,31 +1,67 @@
1
+ import { v4 as uuidv4 } from "uuid";
1
2
  import { XmlStream } from "../../../utils/xml-stream.js";
3
+ import { xmlEncode, xmlDecode } from "../../../utils/utils.js";
2
4
  import { BaseXform } from "../base-xform.js";
3
5
  class PivotTableXform extends BaseXform {
4
6
  constructor() {
5
7
  super();
6
8
  this.map = {};
9
+ this.model = null;
10
+ this.inPivotFields = false;
11
+ this.inRowFields = false;
12
+ this.inColFields = false;
13
+ this.inDataFields = false;
14
+ this.inRowItems = false;
15
+ this.inColItems = false;
16
+ this.inLocation = false;
17
+ this.currentPivotField = null;
18
+ this.inItems = false;
19
+ this.inPivotTableStyleInfo = false;
7
20
  }
8
21
  prepare(_model) {
9
- // TK
22
+ // No preparation needed
10
23
  }
11
24
  get tag() {
12
25
  // http://www.datypic.com/sc/ooxml/e-ssml_pivotTableDefinition.html
13
26
  return "pivotTableDefinition";
14
27
  }
28
+ reset() {
29
+ this.model = null;
30
+ this.inPivotFields = false;
31
+ this.inRowFields = false;
32
+ this.inColFields = false;
33
+ this.inDataFields = false;
34
+ this.inRowItems = false;
35
+ this.inColItems = false;
36
+ this.inLocation = false;
37
+ this.currentPivotField = null;
38
+ this.inItems = false;
39
+ this.inPivotTableStyleInfo = false;
40
+ }
41
+ /**
42
+ * Render pivot table XML.
43
+ * Supports both newly created models and loaded models.
44
+ */
15
45
  render(xmlStream, model) {
16
- const { rows, columns, values, cacheFields, cacheId } = model;
17
- // Examples
18
- // --------
19
- // rows: [0, 1], // only 2 items possible for now
20
- // columns: [2], // only 1 item possible for now
21
- // values: [4], // only 1 item possible for now
22
- // metric: 'sum', // only 'sum' possible for now
23
- //
24
- // the numbers are indices into `cacheFields`.
46
+ const isLoaded = model.isLoaded;
47
+ if (isLoaded) {
48
+ this.renderLoaded(xmlStream, model);
49
+ }
50
+ else {
51
+ this.renderNew(xmlStream, model);
52
+ }
53
+ }
54
+ /**
55
+ * Render newly created pivot table
56
+ */
57
+ renderNew(xmlStream, model) {
58
+ const { rows, columns, values, cacheFields, cacheId, applyWidthHeightFormats } = model;
59
+ // Generate unique UID for each pivot table to prevent Excel treating them as identical
60
+ const uniqueUid = `{${uuidv4().toUpperCase()}}`;
25
61
  xmlStream.openXml(XmlStream.StdDocAttributes);
26
62
  xmlStream.openNode(this.tag, {
27
63
  ...PivotTableXform.PIVOT_TABLE_ATTRIBUTES,
28
- "xr:uid": "{267EE50F-B116-784D-8DC2-BA77DE3F4F4A}",
64
+ "xr:uid": uniqueUid,
29
65
  name: "PivotTable2",
30
66
  cacheId,
31
67
  applyNumberFormats: "0",
@@ -33,7 +69,7 @@ class PivotTableXform extends BaseXform {
33
69
  applyFontFormats: "0",
34
70
  applyPatternFormats: "0",
35
71
  applyAlignmentFormats: "0",
36
- applyWidthHeightFormats: "1",
72
+ applyWidthHeightFormats,
37
73
  dataCaption: "Values",
38
74
  updatedVersion: "8",
39
75
  minRefreshableVersion: "3",
@@ -45,14 +81,6 @@ class PivotTableXform extends BaseXform {
45
81
  compactData: "0",
46
82
  multipleFieldFilters: "0"
47
83
  });
48
- // Note: keeping this pretty-printed and verbose for now to ease debugging.
49
- //
50
- // location: ref="A3:E15"
51
- // pivotFields
52
- // rowFields and rowItems
53
- // colFields and colItems
54
- // dataFields
55
- // pivotTableStyleInfo
56
84
  xmlStream.writeXml(`
57
85
  <location ref="A3:E15" firstHeaderRow="1" firstDataRow="2" firstDataCol="1" />
58
86
  <pivotFields count="${cacheFields.length}">
@@ -64,19 +92,16 @@ class PivotTableXform extends BaseXform {
64
92
  <rowItems count="1">
65
93
  <i t="grand"><x /></i>
66
94
  </rowItems>
67
- <colFields count="${columns.length}">
68
- ${columns.map(columnIndex => `<field x="${columnIndex}" />`).join("\n ")}
95
+ <colFields count="${columns.length === 0 ? 1 : columns.length}">
96
+ ${columns.length === 0
97
+ ? '<field x="-2" />'
98
+ : columns.map(columnIndex => `<field x="${columnIndex}" />`).join("\n ")}
69
99
  </colFields>
70
100
  <colItems count="1">
71
101
  <i t="grand"><x /></i>
72
102
  </colItems>
73
103
  <dataFields count="${values.length}">
74
- <dataField
75
- name="Sum of ${cacheFields[values[0]].name}"
76
- fld="${values[0]}"
77
- baseField="0"
78
- baseItem="0"
79
- />
104
+ ${buildDataFields(cacheFields, values, model.metric)}
80
105
  </dataFields>
81
106
  <pivotTableStyleInfo
82
107
  name="PivotStyleLight16"
@@ -109,19 +134,324 @@ class PivotTableXform extends BaseXform {
109
134
  `);
110
135
  xmlStream.closeNode();
111
136
  }
112
- parseOpen(_node) {
113
- // TK
114
- return false;
137
+ /**
138
+ * Render loaded pivot table (preserving original structure)
139
+ */
140
+ renderLoaded(xmlStream, model) {
141
+ const uniqueUid = model.uid || `{${uuidv4().toUpperCase()}}`;
142
+ xmlStream.openXml(XmlStream.StdDocAttributes);
143
+ xmlStream.openNode(this.tag, {
144
+ ...PivotTableXform.PIVOT_TABLE_ATTRIBUTES,
145
+ "xr:uid": uniqueUid,
146
+ name: model.name || "PivotTable1",
147
+ cacheId: model.cacheId,
148
+ applyNumberFormats: model.applyNumberFormats || "0",
149
+ applyBorderFormats: model.applyBorderFormats || "0",
150
+ applyFontFormats: model.applyFontFormats || "0",
151
+ applyPatternFormats: model.applyPatternFormats || "0",
152
+ applyAlignmentFormats: model.applyAlignmentFormats || "0",
153
+ applyWidthHeightFormats: model.applyWidthHeightFormats || "0",
154
+ dataCaption: model.dataCaption || "Values",
155
+ updatedVersion: model.updatedVersion || "8",
156
+ minRefreshableVersion: model.minRefreshableVersion || "3",
157
+ useAutoFormatting: model.useAutoFormatting ? "1" : "0",
158
+ itemPrintTitles: model.itemPrintTitles ? "1" : "0",
159
+ createdVersion: model.createdVersion || "8",
160
+ indent: model.indent !== undefined ? String(model.indent) : "0",
161
+ compact: model.compact ? "1" : "0",
162
+ compactData: model.compactData ? "1" : "0",
163
+ multipleFieldFilters: model.multipleFieldFilters ? "1" : "0"
164
+ });
165
+ // Location
166
+ if (model.location) {
167
+ xmlStream.leafNode("location", {
168
+ ref: model.location.ref,
169
+ firstHeaderRow: model.location.firstHeaderRow,
170
+ firstDataRow: model.location.firstDataRow,
171
+ firstDataCol: model.location.firstDataCol
172
+ });
173
+ }
174
+ // Pivot fields
175
+ if (model.pivotFields.length > 0) {
176
+ xmlStream.openNode("pivotFields", { count: model.pivotFields.length });
177
+ for (const pivotField of model.pivotFields) {
178
+ this.renderPivotFieldLoaded(xmlStream, pivotField);
179
+ }
180
+ xmlStream.closeNode();
181
+ }
182
+ // Row fields
183
+ if (model.rowFields.length > 0) {
184
+ xmlStream.openNode("rowFields", { count: model.rowFields.length });
185
+ for (const fieldIndex of model.rowFields) {
186
+ xmlStream.leafNode("field", { x: fieldIndex });
187
+ }
188
+ xmlStream.closeNode();
189
+ }
190
+ // Row items (simplified - just grand total)
191
+ xmlStream.writeXml(`
192
+ <rowItems count="1">
193
+ <i t="grand"><x /></i>
194
+ </rowItems>`);
195
+ // Col fields
196
+ const colFieldCount = model.colFields.length === 0 ? 1 : model.colFields.length;
197
+ xmlStream.openNode("colFields", { count: colFieldCount });
198
+ if (model.colFields.length === 0) {
199
+ xmlStream.leafNode("field", { x: -2 });
200
+ }
201
+ else {
202
+ for (const fieldIndex of model.colFields) {
203
+ xmlStream.leafNode("field", { x: fieldIndex });
204
+ }
205
+ }
206
+ xmlStream.closeNode();
207
+ // Col items (simplified - just grand total)
208
+ xmlStream.writeXml(`
209
+ <colItems count="1">
210
+ <i t="grand"><x /></i>
211
+ </colItems>`);
212
+ // Data fields
213
+ if (model.dataFields.length > 0) {
214
+ xmlStream.openNode("dataFields", { count: model.dataFields.length });
215
+ for (const dataField of model.dataFields) {
216
+ const attrs = {
217
+ name: dataField.name,
218
+ fld: dataField.fld,
219
+ baseField: dataField.baseField ?? 0,
220
+ baseItem: dataField.baseItem ?? 0
221
+ };
222
+ if (dataField.subtotal && dataField.subtotal !== "sum") {
223
+ attrs.subtotal = dataField.subtotal;
224
+ }
225
+ xmlStream.leafNode("dataField", attrs);
226
+ }
227
+ xmlStream.closeNode();
228
+ }
229
+ // Style info
230
+ xmlStream.leafNode("pivotTableStyleInfo", {
231
+ name: model.styleName || "PivotStyleLight16",
232
+ showRowHeaders: "1",
233
+ showColHeaders: "1",
234
+ showRowStripes: "0",
235
+ showColStripes: "0",
236
+ showLastColumn: "1"
237
+ });
238
+ // Extensions
239
+ xmlStream.writeXml(`
240
+ <extLst>
241
+ <ext
242
+ uri="{962EF5D1-5CA2-4c93-8EF4-DBF5C05439D2}"
243
+ xmlns:x14="http://schemas.microsoft.com/office/spreadsheetml/2009/9/main"
244
+ >
245
+ <x14:pivotTableDefinition
246
+ hideValuesRow="1"
247
+ xmlns:xm="http://schemas.microsoft.com/office/excel/2006/main"
248
+ />
249
+ </ext>
250
+ <ext
251
+ uri="{747A6164-185A-40DC-8AA5-F01512510D54}"
252
+ xmlns:xpdl="http://schemas.microsoft.com/office/spreadsheetml/2016/pivotdefaultlayout"
253
+ >
254
+ <xpdl:pivotTableDefinition16
255
+ EnabledSubtotalsDefault="0"
256
+ SubtotalsOnTopDefault="0"
257
+ />
258
+ </ext>
259
+ </extLst>
260
+ `);
261
+ xmlStream.closeNode();
262
+ }
263
+ /**
264
+ * Render a loaded pivot field
265
+ */
266
+ renderPivotFieldLoaded(xmlStream, field) {
267
+ const attrs = {
268
+ compact: field.compact ? "1" : "0",
269
+ outline: field.outline ? "1" : "0",
270
+ showAll: field.showAll ? "1" : "0",
271
+ defaultSubtotal: field.defaultSubtotal ? "1" : "0"
272
+ };
273
+ if (field.axis) {
274
+ attrs.axis = field.axis;
275
+ }
276
+ if (field.dataField) {
277
+ attrs.dataField = "1";
278
+ }
279
+ if (field.items && field.items.length > 0) {
280
+ xmlStream.openNode("pivotField", attrs);
281
+ xmlStream.openNode("items", { count: field.items.length + 1 });
282
+ for (const itemIndex of field.items) {
283
+ xmlStream.leafNode("item", { x: itemIndex });
284
+ }
285
+ // Grand total item
286
+ xmlStream.writeXml('<item t="default" />');
287
+ xmlStream.closeNode(); // items
288
+ xmlStream.closeNode(); // pivotField
289
+ }
290
+ else {
291
+ xmlStream.leafNode("pivotField", attrs);
292
+ }
293
+ }
294
+ parseOpen(node) {
295
+ const { name, attributes } = node;
296
+ switch (name) {
297
+ case this.tag:
298
+ // pivotTableDefinition root element
299
+ this.reset();
300
+ this.model = {
301
+ name: attributes.name,
302
+ cacheId: parseInt(attributes.cacheId || "0", 10),
303
+ uid: attributes["xr:uid"],
304
+ pivotFields: [],
305
+ rowFields: [],
306
+ colFields: [],
307
+ dataFields: [],
308
+ applyNumberFormats: attributes.applyNumberFormats,
309
+ applyBorderFormats: attributes.applyBorderFormats,
310
+ applyFontFormats: attributes.applyFontFormats,
311
+ applyPatternFormats: attributes.applyPatternFormats,
312
+ applyAlignmentFormats: attributes.applyAlignmentFormats,
313
+ applyWidthHeightFormats: attributes.applyWidthHeightFormats,
314
+ dataCaption: attributes.dataCaption,
315
+ updatedVersion: attributes.updatedVersion,
316
+ minRefreshableVersion: attributes.minRefreshableVersion,
317
+ createdVersion: attributes.createdVersion,
318
+ useAutoFormatting: attributes.useAutoFormatting === "1",
319
+ itemPrintTitles: attributes.itemPrintTitles === "1",
320
+ indent: attributes.indent ? parseInt(attributes.indent, 10) : 0,
321
+ compact: attributes.compact === "1",
322
+ compactData: attributes.compactData === "1",
323
+ multipleFieldFilters: attributes.multipleFieldFilters === "1",
324
+ isLoaded: true
325
+ };
326
+ break;
327
+ case "location":
328
+ if (this.model) {
329
+ this.model.location = {
330
+ ref: attributes.ref,
331
+ firstHeaderRow: attributes.firstHeaderRow
332
+ ? parseInt(attributes.firstHeaderRow, 10)
333
+ : undefined,
334
+ firstDataRow: attributes.firstDataRow
335
+ ? parseInt(attributes.firstDataRow, 10)
336
+ : undefined,
337
+ firstDataCol: attributes.firstDataCol
338
+ ? parseInt(attributes.firstDataCol, 10)
339
+ : undefined
340
+ };
341
+ }
342
+ break;
343
+ case "pivotFields":
344
+ this.inPivotFields = true;
345
+ break;
346
+ case "pivotField":
347
+ if (this.inPivotFields) {
348
+ this.currentPivotField = {
349
+ axis: attributes.axis,
350
+ dataField: attributes.dataField === "1",
351
+ items: [],
352
+ compact: attributes.compact === "1",
353
+ outline: attributes.outline === "1",
354
+ showAll: attributes.showAll === "1",
355
+ defaultSubtotal: attributes.defaultSubtotal === "1"
356
+ };
357
+ }
358
+ break;
359
+ case "items":
360
+ if (this.currentPivotField) {
361
+ this.inItems = true;
362
+ }
363
+ break;
364
+ case "item":
365
+ if (this.inItems && this.currentPivotField && attributes.x !== undefined) {
366
+ this.currentPivotField.items.push(parseInt(attributes.x, 10));
367
+ }
368
+ break;
369
+ case "rowFields":
370
+ this.inRowFields = true;
371
+ break;
372
+ case "colFields":
373
+ this.inColFields = true;
374
+ break;
375
+ case "dataFields":
376
+ this.inDataFields = true;
377
+ break;
378
+ case "rowItems":
379
+ this.inRowItems = true;
380
+ break;
381
+ case "colItems":
382
+ this.inColItems = true;
383
+ break;
384
+ case "field":
385
+ // Handle field element (used in rowFields, colFields)
386
+ if (this.model) {
387
+ const fieldIndex = parseInt(attributes.x || "0", 10);
388
+ if (this.inRowFields) {
389
+ this.model.rowFields.push(fieldIndex);
390
+ }
391
+ else if (this.inColFields) {
392
+ this.model.colFields.push(fieldIndex);
393
+ }
394
+ }
395
+ break;
396
+ case "dataField":
397
+ if (this.inDataFields && this.model) {
398
+ this.model.dataFields.push({
399
+ name: xmlDecode(attributes.name || ""),
400
+ fld: parseInt(attributes.fld || "0", 10),
401
+ baseField: attributes.baseField ? parseInt(attributes.baseField, 10) : 0,
402
+ baseItem: attributes.baseItem ? parseInt(attributes.baseItem, 10) : 0,
403
+ subtotal: attributes.subtotal
404
+ });
405
+ }
406
+ break;
407
+ case "pivotTableStyleInfo":
408
+ if (this.model) {
409
+ this.model.styleName = attributes.name;
410
+ }
411
+ break;
412
+ }
413
+ return true;
115
414
  }
116
415
  parseText(_text) {
117
- // TK
416
+ // No text content in pivot table elements
118
417
  }
119
- parseClose(_name) {
120
- // TK
121
- return false;
418
+ parseClose(name) {
419
+ switch (name) {
420
+ case this.tag:
421
+ // End of pivotTableDefinition
422
+ return false;
423
+ case "pivotFields":
424
+ this.inPivotFields = false;
425
+ break;
426
+ case "pivotField":
427
+ if (this.currentPivotField && this.model) {
428
+ this.model.pivotFields.push(this.currentPivotField);
429
+ this.currentPivotField = null;
430
+ }
431
+ break;
432
+ case "items":
433
+ this.inItems = false;
434
+ break;
435
+ case "rowFields":
436
+ this.inRowFields = false;
437
+ break;
438
+ case "colFields":
439
+ this.inColFields = false;
440
+ break;
441
+ case "dataFields":
442
+ this.inDataFields = false;
443
+ break;
444
+ case "rowItems":
445
+ this.inRowItems = false;
446
+ break;
447
+ case "colItems":
448
+ this.inColItems = false;
449
+ break;
450
+ }
451
+ return true;
122
452
  }
123
453
  reconcile(_model, _options) {
124
- // TK
454
+ // No reconciliation needed
125
455
  }
126
456
  }
127
457
  PivotTableXform.PIVOT_TABLE_ATTRIBUTES = {
@@ -131,14 +461,35 @@ PivotTableXform.PIVOT_TABLE_ATTRIBUTES = {
131
461
  "xmlns:xr": "http://schemas.microsoft.com/office/spreadsheetml/2014/revision"
132
462
  };
133
463
  // Helpers
464
+ /**
465
+ * Build dataField XML elements for all values in the pivot table.
466
+ * Supports multiple values when columns is empty.
467
+ */
468
+ function buildDataFields(cacheFields, values, metric) {
469
+ const metricName = metric === "count" ? "Count" : "Sum";
470
+ // For 'count' metric, Excel requires subtotal="count" attribute
471
+ const subtotalAttr = metric === "count" ? ' subtotal="count"' : "";
472
+ return values
473
+ .map(valueIndex => `<dataField
474
+ name="${metricName} of ${xmlEncode(cacheFields[valueIndex].name)}"
475
+ fld="${valueIndex}"
476
+ baseField="0"
477
+ baseItem="0"${subtotalAttr}
478
+ />`)
479
+ .join("");
480
+ }
134
481
  function renderPivotFields(pivotTable) {
482
+ // Pre-compute field type lookup for O(1) access
483
+ const rowSet = new Set(pivotTable.rows);
484
+ const colSet = new Set(pivotTable.columns);
485
+ const valueSet = new Set(pivotTable.values);
135
486
  return pivotTable.cacheFields
136
487
  .map((cacheField, fieldIndex) => {
137
- const fieldType = pivotTable.rows.indexOf(fieldIndex) >= 0
488
+ const fieldType = rowSet.has(fieldIndex)
138
489
  ? "row"
139
- : pivotTable.columns.indexOf(fieldIndex) >= 0
490
+ : colSet.has(fieldIndex)
140
491
  ? "column"
141
- : pivotTable.values.indexOf(fieldIndex) >= 0
492
+ : valueSet.has(fieldIndex)
142
493
  ? "value"
143
494
  : null;
144
495
  return renderPivotField(fieldType, cacheField.sharedItems);
@@ -340,6 +340,12 @@ class CellXform extends BaseXform {
340
340
  model.type = Enums.ValueType.Error;
341
341
  model.value = { error: model.value };
342
342
  break;
343
+ case "d":
344
+ // Strict OpenXML format stores dates as ISO strings with t="d"
345
+ // See: https://www.loc.gov/preservation/digital/formats/fdd/fdd000401.shtml
346
+ model.type = Enums.ValueType.Date;
347
+ model.value = new Date(model.value);
348
+ break;
343
349
  default:
344
350
  model.type = Enums.ValueType.Number;
345
351
  model.value = parseFloat(model.value);
@@ -265,13 +265,13 @@ class WorkSheetXform extends BaseXform {
265
265
  });
266
266
  });
267
267
  // prepare pivot tables
268
- if ((model.pivotTables || []).length) {
268
+ (model.pivotTables || []).forEach((pivotTable) => {
269
269
  rels.push({
270
270
  Id: nextRid(rels),
271
271
  Type: RelType.PivotTable,
272
- Target: "../pivotTables/pivotTable1.xml"
272
+ Target: `../pivotTables/pivotTable${pivotTable.tableNumber}.xml`
273
273
  });
274
- }
274
+ });
275
275
  // prepare ext items
276
276
  this.map.extLst.prepare(model, options);
277
277
  }
@@ -486,6 +486,17 @@ class WorkSheetXform extends BaseXform {
486
486
  const rel = rels[tablePart.rId];
487
487
  return options.tables[rel.Target];
488
488
  });
489
+ // Link pivot tables from relationships to worksheet
490
+ // This is needed so that when writing, the worksheet knows which pivot tables it contains
491
+ model.pivotTables = [];
492
+ (model.relationships || []).forEach(rel => {
493
+ if (rel.Type === RelType.PivotTable && options.pivotTables) {
494
+ const pivotTable = options.pivotTables[rel.Target];
495
+ if (pivotTable) {
496
+ model.pivotTables.push(pivotTable);
497
+ }
498
+ }
499
+ });
489
500
  delete model.relationships;
490
501
  delete model.hyperlinks;
491
502
  delete model.comments;