@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.
- package/dist/browser/excelts.esm.js +71 -10
- package/dist/browser/excelts.esm.js.map +1 -1
- package/dist/browser/excelts.esm.min.js +34 -31
- package/dist/browser/excelts.iife.js +71 -10
- package/dist/browser/excelts.iife.js.map +1 -1
- package/dist/browser/excelts.iife.min.js +32 -29
- package/dist/cjs/xlsx/xform/pivot-table/pivot-cache-definition-xform.js +3 -1
- package/dist/cjs/xlsx/xform/pivot-table/pivot-table-xform.js +91 -6
- package/dist/esm/xlsx/xform/pivot-table/pivot-cache-definition-xform.js +3 -1
- package/dist/esm/xlsx/xform/pivot-table/pivot-table-xform.js +91 -6
- package/dist/types/csv/csv-core.d.ts +0 -6
- package/dist/types/stream/xlsx/workbook-reader.d.ts +1 -1
- package/dist/types/stream/xlsx/worksheet-reader.d.ts +1 -1
- package/package.json +1 -1
|
@@ -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
|
|
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="
|
|
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="
|
|
95
|
-
|
|
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="
|
|
103
|
-
|
|
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
|
-
${
|
|
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
|
|
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="
|
|
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="
|
|
92
|
-
|
|
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="
|
|
100
|
-
|
|
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
|
-
${
|
|
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 = "
|
|
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 = "
|
|
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.
|
|
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": {
|