@cj-tech-master/excelts 2.0.0-canary.20251228013952.4f2c3c6 → 2.0.0-canary.20251228024848.8822305

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.
@@ -46,6 +46,8 @@ class PivotCacheDefinitionXform extends base_xform_1.BaseXform {
46
46
  */
47
47
  renderNew(xmlStream, model) {
48
48
  const { source, cacheFields } = model;
49
+ // Record count = number of data rows (excluding header row)
50
+ const recordCount = source.getSheetValues().slice(2).length;
49
51
  xmlStream.openXml(xml_stream_1.XmlStream.StdDocAttributes);
50
52
  xmlStream.openNode(this.tag, {
51
53
  ...PivotCacheDefinitionXform.PIVOT_CACHE_DEFINITION_ATTRIBUTES,
@@ -56,7 +58,7 @@ class PivotCacheDefinitionXform extends base_xform_1.BaseXform {
56
58
  createdVersion: "8",
57
59
  refreshedVersion: "8",
58
60
  minRefreshableVersion: "3",
59
- recordCount: cacheFields.length + 1
61
+ recordCount
60
62
  });
61
63
  xmlStream.openNode("cacheSource", { type: "worksheet" });
62
64
  xmlStream.leafNode("worksheetSource", {
@@ -60,6 +60,22 @@ class PivotTableXform extends base_xform_1.BaseXform {
60
60
  const { rows, columns, values, cacheFields, cacheId, applyWidthHeightFormats } = model;
61
61
  // Generate unique UID for each pivot table to prevent Excel treating them as identical
62
62
  const uniqueUid = `{${crypto.randomUUID().toUpperCase()}}`;
63
+ // Build rowItems - need one <i> for each unique value in row fields, plus grand total
64
+ const rowItems = buildRowItems(rows, cacheFields);
65
+ // Build colItems - need one <i> for each unique value in col fields, plus grand total
66
+ const colItems = buildColItems(columns, cacheFields, values.length);
67
+ // Calculate pivot table dimensions
68
+ const rowFieldItemCount = rows.length > 0 ? cacheFields[rows[0]]?.sharedItems?.length || 0 : 0;
69
+ const colFieldItemCount = columns.length > 0 ? cacheFields[columns[0]]?.sharedItems?.length || 0 : 0;
70
+ // Location: A3 is where pivot table starts
71
+ // - firstHeaderRow: 1 (column headers are in first row of pivot table)
72
+ // - firstDataRow: 2 (data starts in second row)
73
+ // - firstDataCol: 1 (data starts in second column, after row labels)
74
+ // Calculate ref based on actual data size
75
+ const endRow = 3 + rowFieldItemCount + 1; // start row + data rows + grand total
76
+ const endCol = 1 + colFieldItemCount + 1; // start col + data cols + grand total
77
+ const endColLetter = String.fromCharCode(64 + endCol);
78
+ const locationRef = `A3:${endColLetter}${endRow}`;
63
79
  xmlStream.openXml(xml_stream_1.XmlStream.StdDocAttributes);
64
80
  xmlStream.openNode(this.tag, {
65
81
  ...PivotTableXform.PIVOT_TABLE_ATTRIBUTES,
@@ -84,23 +100,23 @@ class PivotTableXform extends base_xform_1.BaseXform {
84
100
  multipleFieldFilters: "0"
85
101
  });
86
102
  xmlStream.writeXml(`
87
- <location ref="A3:E15" firstHeaderRow="1" firstDataRow="2" firstDataCol="1" />
103
+ <location ref="${locationRef}" firstHeaderRow="1" firstDataRow="2" firstDataCol="1" />
88
104
  <pivotFields count="${cacheFields.length}">
89
105
  ${renderPivotFields(model)}
90
106
  </pivotFields>
91
107
  <rowFields count="${rows.length}">
92
108
  ${rows.map(rowIndex => `<field x="${rowIndex}" />`).join("\n ")}
93
109
  </rowFields>
94
- <rowItems count="1">
95
- <i t="grand"><x /></i>
110
+ <rowItems count="${rowItems.count}">
111
+ ${rowItems.xml}
96
112
  </rowItems>
97
113
  <colFields count="${columns.length === 0 ? 1 : columns.length}">
98
114
  ${columns.length === 0
99
115
  ? '<field x="-2" />'
100
116
  : columns.map(columnIndex => `<field x="${columnIndex}" />`).join("\n ")}
101
117
  </colFields>
102
- <colItems count="1">
103
- <i t="grand"><x /></i>
118
+ <colItems count="${colItems.count}">
119
+ ${colItems.xml}
104
120
  </colItems>
105
121
  <dataFields count="${values.length}">
106
122
  ${buildDataFields(cacheFields, values, model.metric)}
@@ -464,6 +480,70 @@ PivotTableXform.PIVOT_TABLE_ATTRIBUTES = {
464
480
  "xmlns:xr": "http://schemas.microsoft.com/office/spreadsheetml/2014/revision"
465
481
  };
466
482
  // Helpers
483
+ /**
484
+ * Build rowItems XML - one item for each unique value in row fields, plus grand total.
485
+ * Each <i> represents a row in the pivot table.
486
+ * - Regular items: <i><x v="index"/></i> where index is the position in sharedItems
487
+ * - Grand total: <i t="grand"><x/></i>
488
+ */
489
+ function buildRowItems(rows, cacheFields) {
490
+ if (rows.length === 0) {
491
+ // No row fields - just grand total
492
+ return { count: 1, xml: '<i t="grand"><x /></i>' };
493
+ }
494
+ // Get unique values count from the first row field
495
+ const rowFieldIndex = rows[0];
496
+ const sharedItems = cacheFields[rowFieldIndex]?.sharedItems || [];
497
+ const itemCount = sharedItems.length;
498
+ // Build items: one for each unique value + grand total
499
+ const items = [];
500
+ // Regular items - reference each unique value by index
501
+ for (let i = 0; i < itemCount; i++) {
502
+ items.push(`<i><x v="${i}" /></i>`);
503
+ }
504
+ // Grand total row
505
+ items.push('<i t="grand"><x /></i>');
506
+ return {
507
+ count: items.length,
508
+ xml: items.join("\n ")
509
+ };
510
+ }
511
+ /**
512
+ * Build colItems XML - one item for each unique value in column fields, plus grand total.
513
+ * When there are multiple data fields (values), each column value may have sub-columns.
514
+ */
515
+ function buildColItems(columns, cacheFields, valueCount) {
516
+ if (columns.length === 0) {
517
+ // No column fields - columns are based on data fields (values)
518
+ if (valueCount > 1) {
519
+ // Multiple values: one column per value + grand total
520
+ const items = [];
521
+ for (let i = 0; i < valueCount; i++) {
522
+ items.push(`<i><x v="${i}" /></i>`);
523
+ }
524
+ items.push('<i t="grand"><x /></i>');
525
+ return { count: items.length, xml: items.join("\n ") };
526
+ }
527
+ // Single value: just grand total
528
+ return { count: 1, xml: '<i t="grand"><x /></i>' };
529
+ }
530
+ // Get unique values count from the first column field
531
+ const colFieldIndex = columns[0];
532
+ const sharedItems = cacheFields[colFieldIndex]?.sharedItems || [];
533
+ const itemCount = sharedItems.length;
534
+ // Build items: one for each unique value + grand total
535
+ const items = [];
536
+ // Regular items - reference each unique value by index
537
+ for (let i = 0; i < itemCount; i++) {
538
+ items.push(`<i><x v="${i}" /></i>`);
539
+ }
540
+ // Grand total column
541
+ items.push('<i t="grand"><x /></i>');
542
+ return {
543
+ count: items.length,
544
+ xml: items.join("\n ")
545
+ };
546
+ }
467
547
  /**
468
548
  * Build dataField XML elements for all values in the pivot table.
469
549
  * Supports multiple values when columns is empty.
@@ -504,10 +584,15 @@ function renderPivotField(fieldType, sharedItems) {
504
584
  const defaultAttributes = 'compact="0" outline="0" showAll="0" defaultSubtotal="0"';
505
585
  if (fieldType === "row" || fieldType === "column") {
506
586
  const axis = fieldType === "row" ? "axisRow" : "axisCol";
587
+ // items = one for each shared item + one default item
588
+ const itemsXml = [
589
+ ...sharedItems.map((_item, index) => `<item x="${index}" />`),
590
+ '<item t="default" />' // Required default item for subtotals/grand totals
591
+ ].join("\n ");
507
592
  return `
508
593
  <pivotField axis="${axis}" ${defaultAttributes}>
509
594
  <items count="${sharedItems.length + 1}">
510
- ${sharedItems.map((_item, index) => `<item x="${index}" />`).join("\n ")}
595
+ ${itemsXml}
511
596
  </items>
512
597
  </pivotField>
513
598
  `;
@@ -43,6 +43,8 @@ class PivotCacheDefinitionXform extends BaseXform {
43
43
  */
44
44
  renderNew(xmlStream, model) {
45
45
  const { source, cacheFields } = model;
46
+ // Record count = number of data rows (excluding header row)
47
+ const recordCount = source.getSheetValues().slice(2).length;
46
48
  xmlStream.openXml(XmlStream.StdDocAttributes);
47
49
  xmlStream.openNode(this.tag, {
48
50
  ...PivotCacheDefinitionXform.PIVOT_CACHE_DEFINITION_ATTRIBUTES,
@@ -53,7 +55,7 @@ class PivotCacheDefinitionXform extends BaseXform {
53
55
  createdVersion: "8",
54
56
  refreshedVersion: "8",
55
57
  minRefreshableVersion: "3",
56
- recordCount: cacheFields.length + 1
58
+ recordCount
57
59
  });
58
60
  xmlStream.openNode("cacheSource", { type: "worksheet" });
59
61
  xmlStream.leafNode("worksheetSource", {
@@ -57,6 +57,22 @@ class PivotTableXform extends BaseXform {
57
57
  const { rows, columns, values, cacheFields, cacheId, applyWidthHeightFormats } = model;
58
58
  // Generate unique UID for each pivot table to prevent Excel treating them as identical
59
59
  const uniqueUid = `{${crypto.randomUUID().toUpperCase()}}`;
60
+ // Build rowItems - need one <i> for each unique value in row fields, plus grand total
61
+ const rowItems = buildRowItems(rows, cacheFields);
62
+ // Build colItems - need one <i> for each unique value in col fields, plus grand total
63
+ const colItems = buildColItems(columns, cacheFields, values.length);
64
+ // Calculate pivot table dimensions
65
+ const rowFieldItemCount = rows.length > 0 ? cacheFields[rows[0]]?.sharedItems?.length || 0 : 0;
66
+ const colFieldItemCount = columns.length > 0 ? cacheFields[columns[0]]?.sharedItems?.length || 0 : 0;
67
+ // Location: A3 is where pivot table starts
68
+ // - firstHeaderRow: 1 (column headers are in first row of pivot table)
69
+ // - firstDataRow: 2 (data starts in second row)
70
+ // - firstDataCol: 1 (data starts in second column, after row labels)
71
+ // Calculate ref based on actual data size
72
+ const endRow = 3 + rowFieldItemCount + 1; // start row + data rows + grand total
73
+ const endCol = 1 + colFieldItemCount + 1; // start col + data cols + grand total
74
+ const endColLetter = String.fromCharCode(64 + endCol);
75
+ const locationRef = `A3:${endColLetter}${endRow}`;
60
76
  xmlStream.openXml(XmlStream.StdDocAttributes);
61
77
  xmlStream.openNode(this.tag, {
62
78
  ...PivotTableXform.PIVOT_TABLE_ATTRIBUTES,
@@ -81,23 +97,23 @@ class PivotTableXform extends BaseXform {
81
97
  multipleFieldFilters: "0"
82
98
  });
83
99
  xmlStream.writeXml(`
84
- <location ref="A3:E15" firstHeaderRow="1" firstDataRow="2" firstDataCol="1" />
100
+ <location ref="${locationRef}" firstHeaderRow="1" firstDataRow="2" firstDataCol="1" />
85
101
  <pivotFields count="${cacheFields.length}">
86
102
  ${renderPivotFields(model)}
87
103
  </pivotFields>
88
104
  <rowFields count="${rows.length}">
89
105
  ${rows.map(rowIndex => `<field x="${rowIndex}" />`).join("\n ")}
90
106
  </rowFields>
91
- <rowItems count="1">
92
- <i t="grand"><x /></i>
107
+ <rowItems count="${rowItems.count}">
108
+ ${rowItems.xml}
93
109
  </rowItems>
94
110
  <colFields count="${columns.length === 0 ? 1 : columns.length}">
95
111
  ${columns.length === 0
96
112
  ? '<field x="-2" />'
97
113
  : columns.map(columnIndex => `<field x="${columnIndex}" />`).join("\n ")}
98
114
  </colFields>
99
- <colItems count="1">
100
- <i t="grand"><x /></i>
115
+ <colItems count="${colItems.count}">
116
+ ${colItems.xml}
101
117
  </colItems>
102
118
  <dataFields count="${values.length}">
103
119
  ${buildDataFields(cacheFields, values, model.metric)}
@@ -460,6 +476,70 @@ PivotTableXform.PIVOT_TABLE_ATTRIBUTES = {
460
476
  "xmlns:xr": "http://schemas.microsoft.com/office/spreadsheetml/2014/revision"
461
477
  };
462
478
  // Helpers
479
+ /**
480
+ * Build rowItems XML - one item for each unique value in row fields, plus grand total.
481
+ * Each <i> represents a row in the pivot table.
482
+ * - Regular items: <i><x v="index"/></i> where index is the position in sharedItems
483
+ * - Grand total: <i t="grand"><x/></i>
484
+ */
485
+ function buildRowItems(rows, cacheFields) {
486
+ if (rows.length === 0) {
487
+ // No row fields - just grand total
488
+ return { count: 1, xml: '<i t="grand"><x /></i>' };
489
+ }
490
+ // Get unique values count from the first row field
491
+ const rowFieldIndex = rows[0];
492
+ const sharedItems = cacheFields[rowFieldIndex]?.sharedItems || [];
493
+ const itemCount = sharedItems.length;
494
+ // Build items: one for each unique value + grand total
495
+ const items = [];
496
+ // Regular items - reference each unique value by index
497
+ for (let i = 0; i < itemCount; i++) {
498
+ items.push(`<i><x v="${i}" /></i>`);
499
+ }
500
+ // Grand total row
501
+ items.push('<i t="grand"><x /></i>');
502
+ return {
503
+ count: items.length,
504
+ xml: items.join("\n ")
505
+ };
506
+ }
507
+ /**
508
+ * Build colItems XML - one item for each unique value in column fields, plus grand total.
509
+ * When there are multiple data fields (values), each column value may have sub-columns.
510
+ */
511
+ function buildColItems(columns, cacheFields, valueCount) {
512
+ if (columns.length === 0) {
513
+ // No column fields - columns are based on data fields (values)
514
+ if (valueCount > 1) {
515
+ // Multiple values: one column per value + grand total
516
+ const items = [];
517
+ for (let i = 0; i < valueCount; i++) {
518
+ items.push(`<i><x v="${i}" /></i>`);
519
+ }
520
+ items.push('<i t="grand"><x /></i>');
521
+ return { count: items.length, xml: items.join("\n ") };
522
+ }
523
+ // Single value: just grand total
524
+ return { count: 1, xml: '<i t="grand"><x /></i>' };
525
+ }
526
+ // Get unique values count from the first column field
527
+ const colFieldIndex = columns[0];
528
+ const sharedItems = cacheFields[colFieldIndex]?.sharedItems || [];
529
+ const itemCount = sharedItems.length;
530
+ // Build items: one for each unique value + grand total
531
+ const items = [];
532
+ // Regular items - reference each unique value by index
533
+ for (let i = 0; i < itemCount; i++) {
534
+ items.push(`<i><x v="${i}" /></i>`);
535
+ }
536
+ // Grand total column
537
+ items.push('<i t="grand"><x /></i>');
538
+ return {
539
+ count: items.length,
540
+ xml: items.join("\n ")
541
+ };
542
+ }
463
543
  /**
464
544
  * Build dataField XML elements for all values in the pivot table.
465
545
  * Supports multiple values when columns is empty.
@@ -500,10 +580,15 @@ function renderPivotField(fieldType, sharedItems) {
500
580
  const defaultAttributes = 'compact="0" outline="0" showAll="0" defaultSubtotal="0"';
501
581
  if (fieldType === "row" || fieldType === "column") {
502
582
  const axis = fieldType === "row" ? "axisRow" : "axisCol";
583
+ // items = one for each shared item + one default item
584
+ const itemsXml = [
585
+ ...sharedItems.map((_item, index) => `<item x="${index}" />`),
586
+ '<item t="default" />' // Required default item for subtotals/grand totals
587
+ ].join("\n ");
503
588
  return `
504
589
  <pivotField axis="${axis}" ${defaultAttributes}>
505
590
  <items count="${sharedItems.length + 1}">
506
- ${sharedItems.map((_item, index) => `<item x="${index}" />`).join("\n ")}
591
+ ${itemsXml}
507
592
  </items>
508
593
  </pivotField>
509
594
  `;
@@ -24,12 +24,6 @@ export type RowTransformFunction<I = Row, O = Row> = ((row: I) => O | null) | ((
24
24
  export type RowValidateCallback = (error?: Error | null, isValid?: boolean, reason?: string) => void;
25
25
  /** Row validate function - sync or async */
26
26
  export type RowValidateFunction<T = Row> = ((row: T) => boolean) | ((row: T, callback: RowValidateCallback) => void);
27
- /** Validation result */
28
- export interface RowValidationResult<T = Row> {
29
- row: T | null;
30
- isValid: boolean;
31
- reason?: string;
32
- }
33
27
  /**
34
28
  * CSV parsing options
35
29
  */
@@ -51,7 +51,7 @@ interface EntryPayload {
51
51
  id?: string;
52
52
  }
53
53
  /** Parse event types */
54
- export type ParseEventType = "shared-strings" | "worksheet" | "hyperlinks";
54
+ export type ParseEventType = EntryPayload["type"];
55
55
  export interface SharedStringEvent {
56
56
  eventType: "shared-strings";
57
57
  value: {
@@ -10,7 +10,7 @@ export interface WorksheetHyperlink {
10
10
  rId: string;
11
11
  }
12
12
  /** Events emitted during worksheet parsing */
13
- export type WorksheetEventType = "row" | "hyperlink";
13
+ export type WorksheetEventType = RowEvent["eventType"] | HyperlinkEvent["eventType"];
14
14
  /** Row event emitted during parsing */
15
15
  export interface RowEvent {
16
16
  eventType: "row";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cj-tech-master/excelts",
3
- "version": "2.0.0-canary.20251228013952.4f2c3c6",
3
+ "version": "2.0.0-canary.20251228024848.8822305",
4
4
  "description": "TypeScript Excel Workbook Manager - Read and Write xlsx and csv Files.",
5
5
  "type": "module",
6
6
  "publishConfig": {