@niicojs/excel 0.2.5 → 0.2.7
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/index.cjs +190 -16
- package/dist/index.d.cts +30 -7
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +30 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +190 -16
- package/package.json +1 -1
- package/src/pivot-table.ts +86 -23
- package/src/styles.ts +64 -4
- package/src/types.ts +4 -2
- package/src/workbook.ts +4 -0
package/dist/index.cjs
CHANGED
|
@@ -1117,6 +1117,129 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
1117
1117
|
}
|
|
1118
1118
|
}
|
|
1119
1119
|
|
|
1120
|
+
/**
|
|
1121
|
+
* Excel built-in number format IDs (0-163 are reserved).
|
|
1122
|
+
* These formats don't need to be defined in the numFmts element.
|
|
1123
|
+
*/ const BUILTIN_NUM_FMTS = new Map([
|
|
1124
|
+
[
|
|
1125
|
+
'General',
|
|
1126
|
+
0
|
|
1127
|
+
],
|
|
1128
|
+
[
|
|
1129
|
+
'0',
|
|
1130
|
+
1
|
|
1131
|
+
],
|
|
1132
|
+
[
|
|
1133
|
+
'0.00',
|
|
1134
|
+
2
|
|
1135
|
+
],
|
|
1136
|
+
[
|
|
1137
|
+
'#,##0',
|
|
1138
|
+
3
|
|
1139
|
+
],
|
|
1140
|
+
[
|
|
1141
|
+
'#,##0.00',
|
|
1142
|
+
4
|
|
1143
|
+
],
|
|
1144
|
+
[
|
|
1145
|
+
'0%',
|
|
1146
|
+
9
|
|
1147
|
+
],
|
|
1148
|
+
[
|
|
1149
|
+
'0.00%',
|
|
1150
|
+
10
|
|
1151
|
+
],
|
|
1152
|
+
[
|
|
1153
|
+
'0.00E+00',
|
|
1154
|
+
11
|
|
1155
|
+
],
|
|
1156
|
+
[
|
|
1157
|
+
'# ?/?',
|
|
1158
|
+
12
|
|
1159
|
+
],
|
|
1160
|
+
[
|
|
1161
|
+
'# ??/??',
|
|
1162
|
+
13
|
|
1163
|
+
],
|
|
1164
|
+
[
|
|
1165
|
+
'mm-dd-yy',
|
|
1166
|
+
14
|
|
1167
|
+
],
|
|
1168
|
+
[
|
|
1169
|
+
'd-mmm-yy',
|
|
1170
|
+
15
|
|
1171
|
+
],
|
|
1172
|
+
[
|
|
1173
|
+
'd-mmm',
|
|
1174
|
+
16
|
|
1175
|
+
],
|
|
1176
|
+
[
|
|
1177
|
+
'mmm-yy',
|
|
1178
|
+
17
|
|
1179
|
+
],
|
|
1180
|
+
[
|
|
1181
|
+
'h:mm AM/PM',
|
|
1182
|
+
18
|
|
1183
|
+
],
|
|
1184
|
+
[
|
|
1185
|
+
'h:mm:ss AM/PM',
|
|
1186
|
+
19
|
|
1187
|
+
],
|
|
1188
|
+
[
|
|
1189
|
+
'h:mm',
|
|
1190
|
+
20
|
|
1191
|
+
],
|
|
1192
|
+
[
|
|
1193
|
+
'h:mm:ss',
|
|
1194
|
+
21
|
|
1195
|
+
],
|
|
1196
|
+
[
|
|
1197
|
+
'm/d/yy h:mm',
|
|
1198
|
+
22
|
|
1199
|
+
],
|
|
1200
|
+
[
|
|
1201
|
+
'#,##0 ;(#,##0)',
|
|
1202
|
+
37
|
|
1203
|
+
],
|
|
1204
|
+
[
|
|
1205
|
+
'#,##0 ;[Red](#,##0)',
|
|
1206
|
+
38
|
|
1207
|
+
],
|
|
1208
|
+
[
|
|
1209
|
+
'#,##0.00;(#,##0.00)',
|
|
1210
|
+
39
|
|
1211
|
+
],
|
|
1212
|
+
[
|
|
1213
|
+
'#,##0.00;[Red](#,##0.00)',
|
|
1214
|
+
40
|
|
1215
|
+
],
|
|
1216
|
+
[
|
|
1217
|
+
'mm:ss',
|
|
1218
|
+
45
|
|
1219
|
+
],
|
|
1220
|
+
[
|
|
1221
|
+
'[h]:mm:ss',
|
|
1222
|
+
46
|
|
1223
|
+
],
|
|
1224
|
+
[
|
|
1225
|
+
'mmss.0',
|
|
1226
|
+
47
|
|
1227
|
+
],
|
|
1228
|
+
[
|
|
1229
|
+
'##0.0E+0',
|
|
1230
|
+
48
|
|
1231
|
+
],
|
|
1232
|
+
[
|
|
1233
|
+
'@',
|
|
1234
|
+
49
|
|
1235
|
+
]
|
|
1236
|
+
]);
|
|
1237
|
+
/**
|
|
1238
|
+
* Reverse lookup: built-in format ID -> format code
|
|
1239
|
+
*/ const BUILTIN_NUM_FMT_CODES = new Map(Array.from(BUILTIN_NUM_FMTS.entries()).map(([code, id])=>[
|
|
1240
|
+
id,
|
|
1241
|
+
code
|
|
1242
|
+
]));
|
|
1120
1243
|
/**
|
|
1121
1244
|
* Normalize a color to ARGB format (8 hex chars).
|
|
1122
1245
|
* Accepts: "#RGB", "#RRGGBB", "RGB", "RRGGBB", "AARRGGBB", "#AARRGGBB"
|
|
@@ -1307,7 +1430,8 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
1307
1430
|
const font = this._fonts[xf.fontId];
|
|
1308
1431
|
const fill = this._fills[xf.fillId];
|
|
1309
1432
|
const border = this._borders[xf.borderId];
|
|
1310
|
-
|
|
1433
|
+
// Check custom formats first, then fall back to built-in format codes
|
|
1434
|
+
const numFmt = this._numFmts.get(xf.numFmtId) ?? BUILTIN_NUM_FMT_CODES.get(xf.numFmtId);
|
|
1311
1435
|
const style = {};
|
|
1312
1436
|
if (font) {
|
|
1313
1437
|
if (font.bold) style.bold = true;
|
|
@@ -1439,16 +1563,30 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
1439
1563
|
return this._borders.length - 1;
|
|
1440
1564
|
}
|
|
1441
1565
|
_findOrCreateNumFmt(format) {
|
|
1442
|
-
// Check
|
|
1566
|
+
// Check built-in formats first (IDs 0-163)
|
|
1567
|
+
const builtinId = BUILTIN_NUM_FMTS.get(format);
|
|
1568
|
+
if (builtinId !== undefined) {
|
|
1569
|
+
return builtinId;
|
|
1570
|
+
}
|
|
1571
|
+
// Check if already exists in custom formats
|
|
1443
1572
|
for (const [id, code] of this._numFmts){
|
|
1444
1573
|
if (code === format) return id;
|
|
1445
1574
|
}
|
|
1446
|
-
// Create new
|
|
1447
|
-
const
|
|
1575
|
+
// Create new custom format (IDs 164+)
|
|
1576
|
+
const existingIds = Array.from(this._numFmts.keys());
|
|
1577
|
+
const id = existingIds.length > 0 ? Math.max(...existingIds) + 1 : 164;
|
|
1448
1578
|
this._numFmts.set(id, format);
|
|
1449
1579
|
return id;
|
|
1450
1580
|
}
|
|
1451
1581
|
/**
|
|
1582
|
+
* Get or create a number format ID for the given format string.
|
|
1583
|
+
* Returns built-in IDs (0-163) for standard formats, or creates custom IDs (164+).
|
|
1584
|
+
* @param format - The number format string (e.g., '0.00', '#,##0', '$#,##0.00')
|
|
1585
|
+
*/ getOrCreateNumFmtId(format) {
|
|
1586
|
+
this._dirty = true;
|
|
1587
|
+
return this._findOrCreateNumFmt(format);
|
|
1588
|
+
}
|
|
1589
|
+
/**
|
|
1452
1590
|
* Check if styles have been modified
|
|
1453
1591
|
*/ get dirty() {
|
|
1454
1592
|
return this._dirty;
|
|
@@ -1628,6 +1766,7 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
1628
1766
|
this._columnFields = [];
|
|
1629
1767
|
this._valueFields = [];
|
|
1630
1768
|
this._filterFields = [];
|
|
1769
|
+
this._styles = null;
|
|
1631
1770
|
this._name = name;
|
|
1632
1771
|
this._cache = cache;
|
|
1633
1772
|
this._targetSheet = targetSheet;
|
|
@@ -1662,6 +1801,13 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
1662
1801
|
return this._pivotTableIndex;
|
|
1663
1802
|
}
|
|
1664
1803
|
/**
|
|
1804
|
+
* Set the styles reference for number format resolution
|
|
1805
|
+
* @internal
|
|
1806
|
+
*/ setStyles(styles) {
|
|
1807
|
+
this._styles = styles;
|
|
1808
|
+
return this;
|
|
1809
|
+
}
|
|
1810
|
+
/**
|
|
1665
1811
|
* Add a field to the row area
|
|
1666
1812
|
* @param fieldName - Name of the source field (column header)
|
|
1667
1813
|
*/ addRowField(fieldName) {
|
|
@@ -1691,23 +1837,40 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
1691
1837
|
});
|
|
1692
1838
|
return this;
|
|
1693
1839
|
}
|
|
1694
|
-
|
|
1695
|
-
|
|
1696
|
-
|
|
1697
|
-
|
|
1698
|
-
|
|
1699
|
-
|
|
1840
|
+
addValueField(fieldNameOrConfig, aggregation = 'sum', displayName, numberFormat) {
|
|
1841
|
+
// Normalize arguments to a common form
|
|
1842
|
+
let fieldName;
|
|
1843
|
+
let agg;
|
|
1844
|
+
let name;
|
|
1845
|
+
let format;
|
|
1846
|
+
if (typeof fieldNameOrConfig === 'object') {
|
|
1847
|
+
fieldName = fieldNameOrConfig.field;
|
|
1848
|
+
agg = fieldNameOrConfig.aggregation ?? 'sum';
|
|
1849
|
+
name = fieldNameOrConfig.name;
|
|
1850
|
+
format = fieldNameOrConfig.numberFormat;
|
|
1851
|
+
} else {
|
|
1852
|
+
fieldName = fieldNameOrConfig;
|
|
1853
|
+
agg = aggregation;
|
|
1854
|
+
name = displayName;
|
|
1855
|
+
format = numberFormat;
|
|
1856
|
+
}
|
|
1700
1857
|
const fieldIndex = this._cache.getFieldIndex(fieldName);
|
|
1701
1858
|
if (fieldIndex < 0) {
|
|
1702
1859
|
throw new Error(`Field not found in source data: ${fieldName}`);
|
|
1703
1860
|
}
|
|
1704
|
-
const defaultName = `${
|
|
1861
|
+
const defaultName = `${agg.charAt(0).toUpperCase() + agg.slice(1)} of ${fieldName}`;
|
|
1862
|
+
// Resolve numFmtId immediately if format is provided and styles are available
|
|
1863
|
+
let numFmtId;
|
|
1864
|
+
if (format && this._styles) {
|
|
1865
|
+
numFmtId = this._styles.getOrCreateNumFmtId(format);
|
|
1866
|
+
}
|
|
1705
1867
|
this._valueFields.push({
|
|
1706
1868
|
fieldName,
|
|
1707
1869
|
fieldIndex,
|
|
1708
1870
|
axis: 'value',
|
|
1709
|
-
aggregation,
|
|
1710
|
-
displayName:
|
|
1871
|
+
aggregation: agg,
|
|
1872
|
+
displayName: name || defaultName,
|
|
1873
|
+
numFmtId
|
|
1711
1874
|
});
|
|
1712
1875
|
return this;
|
|
1713
1876
|
}
|
|
@@ -1830,17 +1993,26 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
1830
1993
|
}
|
|
1831
1994
|
// Data fields (values)
|
|
1832
1995
|
if (this._valueFields.length > 0) {
|
|
1833
|
-
const dataFieldNodes = this._valueFields.map((f)=>
|
|
1996
|
+
const dataFieldNodes = this._valueFields.map((f)=>{
|
|
1997
|
+
const attrs = {
|
|
1834
1998
|
name: f.displayName || f.fieldName,
|
|
1835
1999
|
fld: String(f.fieldIndex),
|
|
1836
2000
|
baseField: '0',
|
|
1837
2001
|
baseItem: '0',
|
|
1838
2002
|
subtotal: f.aggregation || 'sum'
|
|
1839
|
-
}
|
|
2003
|
+
};
|
|
2004
|
+
// Add numFmtId if it was resolved during addValueField
|
|
2005
|
+
if (f.numFmtId !== undefined) {
|
|
2006
|
+
attrs.numFmtId = String(f.numFmtId);
|
|
2007
|
+
}
|
|
2008
|
+
return createElement('dataField', attrs, []);
|
|
2009
|
+
});
|
|
1840
2010
|
children.push(createElement('dataFields', {
|
|
1841
2011
|
count: String(dataFieldNodes.length)
|
|
1842
2012
|
}, dataFieldNodes));
|
|
1843
2013
|
}
|
|
2014
|
+
// Check if any value field has a number format
|
|
2015
|
+
const hasNumberFormats = this._valueFields.some((f)=>f.numFmtId !== undefined);
|
|
1844
2016
|
// Pivot table style
|
|
1845
2017
|
children.push(createElement('pivotTableStyleInfo', {
|
|
1846
2018
|
name: 'PivotStyleMedium9',
|
|
@@ -1855,7 +2027,7 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
1855
2027
|
'xmlns:r': 'http://schemas.openxmlformats.org/officeDocument/2006/relationships',
|
|
1856
2028
|
name: this._name,
|
|
1857
2029
|
cacheId: String(this._cache.cacheId),
|
|
1858
|
-
applyNumberFormats: '0',
|
|
2030
|
+
applyNumberFormats: hasNumberFormats ? '1' : '0',
|
|
1859
2031
|
applyBorderFormats: '0',
|
|
1860
2032
|
applyFontFormats: '0',
|
|
1861
2033
|
applyPatternFormats: '0',
|
|
@@ -2789,6 +2961,8 @@ const builder = new fastXmlParser.XMLBuilder(builderOptions);
|
|
|
2789
2961
|
// Create pivot table
|
|
2790
2962
|
const pivotTableIndex = this._pivotTables.length + 1;
|
|
2791
2963
|
const pivotTable = new PivotTable(config.name, cache, targetSheet, targetCell, targetAddr.row + 1, targetAddr.col, pivotTableIndex);
|
|
2964
|
+
// Set styles reference for number format resolution
|
|
2965
|
+
pivotTable.setStyles(this._styles);
|
|
2792
2966
|
this._pivotTables.push(pivotTable);
|
|
2793
2967
|
return pivotTable;
|
|
2794
2968
|
}
|
package/dist/index.d.cts
CHANGED
|
@@ -89,10 +89,12 @@ type AggregationType = 'sum' | 'count' | 'average' | 'min' | 'max';
|
|
|
89
89
|
interface PivotValueConfig {
|
|
90
90
|
/** Source field name (column header) */
|
|
91
91
|
field: string;
|
|
92
|
-
/** Aggregation function */
|
|
93
|
-
aggregation
|
|
92
|
+
/** Aggregation function (default: 'sum') */
|
|
93
|
+
aggregation?: AggregationType;
|
|
94
94
|
/** Display name (e.g., "Sum of Sales") */
|
|
95
95
|
name?: string;
|
|
96
|
+
/** Number format (e.g., '$#,##0.00', '0.00%') */
|
|
97
|
+
numberFormat?: string;
|
|
96
98
|
}
|
|
97
99
|
/**
|
|
98
100
|
* Configuration for creating a pivot table
|
|
@@ -521,6 +523,12 @@ declare class Styles {
|
|
|
521
523
|
private _findOrCreateFill;
|
|
522
524
|
private _findOrCreateBorder;
|
|
523
525
|
private _findOrCreateNumFmt;
|
|
526
|
+
/**
|
|
527
|
+
* Get or create a number format ID for the given format string.
|
|
528
|
+
* Returns built-in IDs (0-163) for standard formats, or creates custom IDs (164+).
|
|
529
|
+
* @param format - The number format string (e.g., '0.00', '#,##0', '$#,##0.00')
|
|
530
|
+
*/
|
|
531
|
+
getOrCreateNumFmtId(format: string): number;
|
|
524
532
|
/**
|
|
525
533
|
* Check if styles have been modified
|
|
526
534
|
*/
|
|
@@ -620,6 +628,7 @@ declare class PivotTable {
|
|
|
620
628
|
private _valueFields;
|
|
621
629
|
private _filterFields;
|
|
622
630
|
private _pivotTableIndex;
|
|
631
|
+
private _styles;
|
|
623
632
|
constructor(name: string, cache: PivotCache, targetSheet: string, targetCell: string, targetRow: number, targetCol: number, pivotTableIndex: number);
|
|
624
633
|
/**
|
|
625
634
|
* Get the pivot table name
|
|
@@ -641,6 +650,11 @@ declare class PivotTable {
|
|
|
641
650
|
* Get the pivot table index (for file naming)
|
|
642
651
|
*/
|
|
643
652
|
get index(): number;
|
|
653
|
+
/**
|
|
654
|
+
* Set the styles reference for number format resolution
|
|
655
|
+
* @internal
|
|
656
|
+
*/
|
|
657
|
+
setStyles(styles: Styles): this;
|
|
644
658
|
/**
|
|
645
659
|
* Add a field to the row area
|
|
646
660
|
* @param fieldName - Name of the source field (column header)
|
|
@@ -652,12 +666,21 @@ declare class PivotTable {
|
|
|
652
666
|
*/
|
|
653
667
|
addColumnField(fieldName: string): this;
|
|
654
668
|
/**
|
|
655
|
-
* Add a field to the values area with aggregation
|
|
656
|
-
*
|
|
657
|
-
*
|
|
658
|
-
*
|
|
669
|
+
* Add a field to the values area with aggregation.
|
|
670
|
+
*
|
|
671
|
+
* Supports two call signatures:
|
|
672
|
+
* - Positional: `addValueField(fieldName, aggregation?, displayName?, numberFormat?)`
|
|
673
|
+
* - Object: `addValueField({ field, aggregation?, name?, numberFormat? })`
|
|
674
|
+
*
|
|
675
|
+
* @example
|
|
676
|
+
* // Positional arguments
|
|
677
|
+
* pivot.addValueField('Sales', 'sum', 'Total Sales', '$#,##0.00');
|
|
678
|
+
*
|
|
679
|
+
* // Object form
|
|
680
|
+
* pivot.addValueField({ field: 'Sales', aggregation: 'sum', name: 'Total Sales', numberFormat: '$#,##0.00' });
|
|
659
681
|
*/
|
|
660
|
-
addValueField(
|
|
682
|
+
addValueField(config: PivotValueConfig): this;
|
|
683
|
+
addValueField(fieldName: string, aggregation?: AggregationType, displayName?: string, numberFormat?: string): this;
|
|
661
684
|
/**
|
|
662
685
|
* Add a field to the filter (page) area
|
|
663
686
|
* @param fieldName - Name of the source field (column header)
|