@niicojs/excel 0.2.7 → 0.3.0
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 +241 -8
- package/dist/index.cjs +1455 -152
- package/dist/index.d.cts +359 -5
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +359 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1455 -153
- package/package.json +1 -1
- package/src/index.ts +9 -1
- package/src/pivot-cache.ts +10 -1
- package/src/pivot-table.ts +129 -31
- package/src/range.ts +15 -2
- package/src/shared-strings.ts +65 -16
- package/src/styles.ts +192 -21
- package/src/table.ts +386 -0
- package/src/types.ts +70 -0
- package/src/utils/address.ts +4 -1
- package/src/utils/xml.ts +0 -7
- package/src/workbook.ts +426 -41
- package/src/worksheet.ts +484 -27
package/LICENSE
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
MIT License
|
|
2
|
-
|
|
3
|
-
Copyright (c) 2026 niico
|
|
4
|
-
|
|
5
|
-
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
-
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
-
in the Software without restriction, including without limitation the rights
|
|
8
|
-
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
-
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
-
furnished to do so, subject to the following conditions:
|
|
11
|
-
|
|
12
|
-
The above copyright notice and this permission notice shall be included in all
|
|
13
|
-
copies or substantial portions of the Software.
|
|
14
|
-
|
|
15
|
-
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
-
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
-
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
-
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
-
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
-
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 niico
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
21
|
SOFTWARE.
|
package/README.md
CHANGED
|
@@ -9,6 +9,9 @@ A TypeScript library for Excel/OpenXML manipulation with maximum format preserva
|
|
|
9
9
|
- Full formula support (read/write/preserve)
|
|
10
10
|
- Cell styles (fonts, fills, borders, alignment)
|
|
11
11
|
- Merged cells
|
|
12
|
+
- Column widths and row heights
|
|
13
|
+
- Freeze panes
|
|
14
|
+
- Pivot tables with fluent API
|
|
12
15
|
- Sheet operations (add, delete, rename, copy)
|
|
13
16
|
- Create sheets from arrays of objects (`addSheetFromData`)
|
|
14
17
|
- Convert sheets to JSON arrays (`toJson`)
|
|
@@ -139,6 +142,47 @@ sheet.unmergeCells('A1:C1');
|
|
|
139
142
|
console.log(sheet.mergedCells); // ['A1:C3', 'D5:E10', ...]
|
|
140
143
|
```
|
|
141
144
|
|
|
145
|
+
## Column Widths and Row Heights
|
|
146
|
+
|
|
147
|
+
```typescript
|
|
148
|
+
const sheet = wb.sheet(0);
|
|
149
|
+
|
|
150
|
+
// Set column width (by letter or 0-based index)
|
|
151
|
+
sheet.setColumnWidth('A', 20);
|
|
152
|
+
sheet.setColumnWidth(1, 15); // Column B
|
|
153
|
+
|
|
154
|
+
// Get column width
|
|
155
|
+
const width = sheet.getColumnWidth('A'); // 20 or undefined
|
|
156
|
+
|
|
157
|
+
// Set row height (0-based index)
|
|
158
|
+
sheet.setRowHeight(0, 30); // First row
|
|
159
|
+
|
|
160
|
+
// Get row height
|
|
161
|
+
const height = sheet.getRowHeight(0); // 30 or undefined
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Freeze Panes
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
const sheet = wb.sheet(0);
|
|
168
|
+
|
|
169
|
+
// Freeze first row (header)
|
|
170
|
+
sheet.freezePane(1, 0);
|
|
171
|
+
|
|
172
|
+
// Freeze first column
|
|
173
|
+
sheet.freezePane(0, 1);
|
|
174
|
+
|
|
175
|
+
// Freeze first row and first column
|
|
176
|
+
sheet.freezePane(1, 1);
|
|
177
|
+
|
|
178
|
+
// Unfreeze
|
|
179
|
+
sheet.freezePane(0, 0);
|
|
180
|
+
|
|
181
|
+
// Get current freeze pane configuration
|
|
182
|
+
const frozen = sheet.getFrozenPane();
|
|
183
|
+
// { row: 1, col: 0 } or null
|
|
184
|
+
```
|
|
185
|
+
|
|
142
186
|
## Sheet Operations
|
|
143
187
|
|
|
144
188
|
```typescript
|
|
@@ -165,6 +209,105 @@ wb.copySheet('RawData', 'RawData_Backup');
|
|
|
165
209
|
wb.deleteSheet('Summary');
|
|
166
210
|
```
|
|
167
211
|
|
|
212
|
+
## Pivot Tables
|
|
213
|
+
|
|
214
|
+
Create pivot tables with a fluent API:
|
|
215
|
+
|
|
216
|
+
```typescript
|
|
217
|
+
const wb = await Workbook.fromFile('sales-data.xlsx');
|
|
218
|
+
|
|
219
|
+
// Create a pivot table from source data
|
|
220
|
+
const pivot = wb.createPivotTable({
|
|
221
|
+
name: 'SalesPivot',
|
|
222
|
+
source: 'Data!A1:E100', // Source range with headers
|
|
223
|
+
target: 'Summary!A3', // Where to place the pivot table
|
|
224
|
+
refreshOnLoad: true, // Refresh when file opens (default: true)
|
|
225
|
+
});
|
|
226
|
+
|
|
227
|
+
// Configure fields using fluent API
|
|
228
|
+
pivot
|
|
229
|
+
.addRowField('Region') // Group by region
|
|
230
|
+
.addRowField('Product') // Then by product
|
|
231
|
+
.addColumnField('Year') // Columns by year
|
|
232
|
+
.addValueField('Sales', 'sum', 'Total Sales') // Sum of sales
|
|
233
|
+
.addValueField('Quantity', 'count', 'Order Count') // Count of orders
|
|
234
|
+
.addFilterField('Category'); // Page filter
|
|
235
|
+
|
|
236
|
+
await wb.toFile('report.xlsx');
|
|
237
|
+
```
|
|
238
|
+
|
|
239
|
+
### Sorting Fields
|
|
240
|
+
|
|
241
|
+
Sort row or column fields in ascending or descending order:
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
pivot
|
|
245
|
+
.addRowField('Region')
|
|
246
|
+
.addRowField('Product')
|
|
247
|
+
.addColumnField('Year')
|
|
248
|
+
.sortField('Region', 'asc') // Sort regions A-Z
|
|
249
|
+
.sortField('Year', 'desc'); // Sort years newest first
|
|
250
|
+
```
|
|
251
|
+
|
|
252
|
+
### Filtering Fields
|
|
253
|
+
|
|
254
|
+
Filter field values using include or exclude lists:
|
|
255
|
+
|
|
256
|
+
```typescript
|
|
257
|
+
// Include only specific values
|
|
258
|
+
pivot.addRowField('Region').filterField('Region', { include: ['North', 'South'] });
|
|
259
|
+
|
|
260
|
+
// Exclude specific values
|
|
261
|
+
pivot.addColumnField('Product').filterField('Product', { exclude: ['Discontinued', 'Legacy'] });
|
|
262
|
+
```
|
|
263
|
+
|
|
264
|
+
### Value Fields with Number Formats
|
|
265
|
+
|
|
266
|
+
Apply number formats to value fields:
|
|
267
|
+
|
|
268
|
+
```typescript
|
|
269
|
+
pivot
|
|
270
|
+
.addValueField('Sales', 'sum', 'Total Sales', '$#,##0.00')
|
|
271
|
+
.addValueField('Quantity', 'average', 'Avg Qty', '0.00')
|
|
272
|
+
.addValueField('Margin', 'sum', 'Margin %', '0.00%');
|
|
273
|
+
|
|
274
|
+
// Or using object syntax
|
|
275
|
+
pivot.addValueField({
|
|
276
|
+
field: 'Sales',
|
|
277
|
+
aggregation: 'sum',
|
|
278
|
+
name: 'Total Sales',
|
|
279
|
+
numberFormat: '$#,##0.00',
|
|
280
|
+
});
|
|
281
|
+
```
|
|
282
|
+
|
|
283
|
+
### Pivot Table API Reference
|
|
284
|
+
|
|
285
|
+
```typescript
|
|
286
|
+
// Add fields to different areas
|
|
287
|
+
pivot.addRowField(fieldName: string): PivotTable
|
|
288
|
+
pivot.addColumnField(fieldName: string): PivotTable
|
|
289
|
+
pivot.addValueField(fieldName: string, aggregation?: AggregationType, displayName?: string, numberFormat?: string): PivotTable
|
|
290
|
+
pivot.addValueField(config: PivotValueConfig): PivotTable
|
|
291
|
+
pivot.addFilterField(fieldName: string): PivotTable
|
|
292
|
+
|
|
293
|
+
// Sort a row or column field
|
|
294
|
+
pivot.sortField(fieldName: string, order: 'asc' | 'desc'): PivotTable
|
|
295
|
+
|
|
296
|
+
// Filter field values (use include OR exclude, not both)
|
|
297
|
+
pivot.filterField(fieldName: string, filter: { include?: string[] } | { exclude?: string[] }): PivotTable
|
|
298
|
+
|
|
299
|
+
// Aggregation types
|
|
300
|
+
type AggregationType = 'sum' | 'count' | 'average' | 'min' | 'max';
|
|
301
|
+
|
|
302
|
+
// Value field config object
|
|
303
|
+
interface PivotValueConfig {
|
|
304
|
+
field: string;
|
|
305
|
+
aggregation?: AggregationType; // default: 'sum'
|
|
306
|
+
name?: string; // default: 'Sum of {field}'
|
|
307
|
+
numberFormat?: string;
|
|
308
|
+
}
|
|
309
|
+
```
|
|
310
|
+
|
|
168
311
|
## Creating Sheets from Data
|
|
169
312
|
|
|
170
313
|
Create sheets directly from arrays of objects with `addSheetFromData`:
|
|
@@ -226,7 +369,7 @@ const data = sheet.toJson();
|
|
|
226
369
|
// [{ name: 'Alice', age: 30 }, { name: 'Bob', age: 25 }]
|
|
227
370
|
|
|
228
371
|
// Using custom field names (first row is data, not headers)
|
|
229
|
-
const
|
|
372
|
+
const data2 = sheet.toJson({
|
|
230
373
|
fields: ['name', 'age', 'city'],
|
|
231
374
|
});
|
|
232
375
|
|
|
@@ -238,21 +381,26 @@ interface Person {
|
|
|
238
381
|
const people = sheet.toJson<Person>();
|
|
239
382
|
|
|
240
383
|
// Starting from a specific position
|
|
241
|
-
const
|
|
384
|
+
const data3 = sheet.toJson({
|
|
242
385
|
startRow: 2, // Skip first 2 rows (0-based)
|
|
243
386
|
startCol: 1, // Start from column B
|
|
244
387
|
});
|
|
245
388
|
|
|
246
389
|
// Limiting the range
|
|
247
|
-
const
|
|
390
|
+
const data4 = sheet.toJson({
|
|
248
391
|
endRow: 10, // Stop at row 11 (0-based, inclusive)
|
|
249
392
|
endCol: 3, // Only read columns A-D
|
|
250
393
|
});
|
|
251
394
|
|
|
252
395
|
// Continue past empty rows
|
|
253
|
-
const
|
|
396
|
+
const data5 = sheet.toJson({
|
|
254
397
|
stopOnEmptyRow: false, // Default is true
|
|
255
398
|
});
|
|
399
|
+
|
|
400
|
+
// Control how dates are serialized
|
|
401
|
+
const data6 = sheet.toJson({
|
|
402
|
+
dateHandling: 'isoString', // 'jsDate' | 'excelSerial' | 'isoString'
|
|
403
|
+
});
|
|
256
404
|
```
|
|
257
405
|
|
|
258
406
|
### Roundtrip Example
|
|
@@ -277,11 +425,24 @@ const readData = sheet.toJson();
|
|
|
277
425
|
## Saving
|
|
278
426
|
|
|
279
427
|
```typescript
|
|
280
|
-
//
|
|
281
|
-
await
|
|
428
|
+
// Load from file
|
|
429
|
+
const wb = await Workbook.fromFile('template.xlsx');
|
|
430
|
+
|
|
431
|
+
// Or load from buffer
|
|
432
|
+
const buffer = await fetch('https://example.com/file.xlsx').then((r) => r.arrayBuffer());
|
|
433
|
+
const wb2 = await Workbook.fromBuffer(new Uint8Array(buffer));
|
|
434
|
+
|
|
435
|
+
// Read data
|
|
436
|
+
const sheet = wb.sheet('Sheet1');
|
|
437
|
+
console.log(sheet.cell('A1').value); // The cell value
|
|
438
|
+
console.log(sheet.cell('A1').formula); // The formula (if any)
|
|
439
|
+
console.log(sheet.cell('A1').type); // 'string' | 'number' | 'boolean' | 'date' | 'error' | 'empty'
|
|
282
440
|
|
|
283
|
-
//
|
|
284
|
-
const
|
|
441
|
+
// Check if a cell exists without creating it
|
|
442
|
+
const existingCell = sheet.getCellIfExists('A1');
|
|
443
|
+
if (existingCell) {
|
|
444
|
+
console.log(existingCell.value);
|
|
445
|
+
}
|
|
285
446
|
```
|
|
286
447
|
|
|
287
448
|
## Type Definitions
|
|
@@ -330,9 +491,81 @@ interface SheetToJsonConfig {
|
|
|
330
491
|
endRow?: number;
|
|
331
492
|
endCol?: number;
|
|
332
493
|
stopOnEmptyRow?: boolean; // Default: true
|
|
494
|
+
dateHandling?: 'jsDate' | 'excelSerial' | 'isoString'; // Default: 'jsDate'
|
|
495
|
+
}
|
|
496
|
+
|
|
497
|
+
// Pivot table configuration
|
|
498
|
+
interface PivotTableConfig {
|
|
499
|
+
name: string;
|
|
500
|
+
source: string; // e.g., "Sheet1!A1:D100"
|
|
501
|
+
target: string; // e.g., "Sheet2!A3"
|
|
502
|
+
refreshOnLoad?: boolean; // Default: true
|
|
503
|
+
}
|
|
504
|
+
|
|
505
|
+
// Pivot table value field configuration
|
|
506
|
+
interface PivotValueConfig {
|
|
507
|
+
field: string;
|
|
508
|
+
aggregation?: AggregationType;
|
|
509
|
+
name?: string;
|
|
510
|
+
numberFormat?: string;
|
|
511
|
+
}
|
|
512
|
+
|
|
513
|
+
type AggregationType = 'sum' | 'count' | 'average' | 'min' | 'max';
|
|
514
|
+
type PivotSortOrder = 'asc' | 'desc';
|
|
515
|
+
|
|
516
|
+
interface PivotFieldFilter {
|
|
517
|
+
include?: string[]; // Show only these values
|
|
518
|
+
exclude?: string[]; // Hide these values (cannot use with include)
|
|
333
519
|
}
|
|
334
520
|
```
|
|
335
521
|
|
|
522
|
+
## Style Schema
|
|
523
|
+
|
|
524
|
+
Supported style properties (CellStyle):
|
|
525
|
+
|
|
526
|
+
```typescript
|
|
527
|
+
interface CellStyle {
|
|
528
|
+
bold?: boolean;
|
|
529
|
+
italic?: boolean;
|
|
530
|
+
underline?: boolean | 'single' | 'double';
|
|
531
|
+
strike?: boolean;
|
|
532
|
+
fontSize?: number;
|
|
533
|
+
fontName?: string;
|
|
534
|
+
fontColor?: string; // Hex (RGB/RRGGBB/AARRGGBB)
|
|
535
|
+
fill?: string; // Hex (RGB/RRGGBB/AARRGGBB)
|
|
536
|
+
border?: {
|
|
537
|
+
top?: 'thin' | 'medium' | 'thick' | 'double' | 'dotted' | 'dashed';
|
|
538
|
+
bottom?: 'thin' | 'medium' | 'thick' | 'double' | 'dotted' | 'dashed';
|
|
539
|
+
left?: 'thin' | 'medium' | 'thick' | 'double' | 'dotted' | 'dashed';
|
|
540
|
+
right?: 'thin' | 'medium' | 'thick' | 'double' | 'dotted' | 'dashed';
|
|
541
|
+
};
|
|
542
|
+
alignment?: {
|
|
543
|
+
horizontal?: 'left' | 'center' | 'right' | 'justify';
|
|
544
|
+
vertical?: 'top' | 'middle' | 'bottom';
|
|
545
|
+
wrapText?: boolean;
|
|
546
|
+
textRotation?: number;
|
|
547
|
+
};
|
|
548
|
+
numberFormat?: string; // Excel format code
|
|
549
|
+
}
|
|
550
|
+
```
|
|
551
|
+
|
|
552
|
+
## Performance and Large Files
|
|
553
|
+
|
|
554
|
+
Tips for large sheets and high-volume writes:
|
|
555
|
+
|
|
556
|
+
- Prefer `addSheetFromData` for bulk writes when possible.
|
|
557
|
+
- Use `range.getValues({ createMissing: false })` to avoid creating empty cells during reads.
|
|
558
|
+
- Keep shared strings small: prefer numbers/booleans where applicable.
|
|
559
|
+
- Avoid frequent `toBuffer()` calls in loops; batch writes and serialize once.
|
|
560
|
+
|
|
561
|
+
## Limitations
|
|
562
|
+
|
|
563
|
+
The library focuses on preserving existing structure and editing common parts of workbooks.
|
|
564
|
+
|
|
565
|
+
- Chart editing is not supported (charts are preserved only).
|
|
566
|
+
- Conditional formatting and data validation are preserved but not editable yet.
|
|
567
|
+
- Some advanced Excel features (sparklines, slicers, macros) are preserved only.
|
|
568
|
+
|
|
336
569
|
## Format Preservation
|
|
337
570
|
|
|
338
571
|
When modifying existing Excel files, this library preserves:
|