@niicojs/excel 0.2.3 → 0.2.4
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/README.md +232 -8
- package/dist/index.cjs +414 -28
- package/dist/index.d.cts +116 -2
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +116 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +411 -29
- package/package.json +1 -1
- package/src/index.ts +15 -1
- package/src/pivot-cache.ts +11 -1
- package/src/pivot-table.ts +122 -12
- package/src/range.ts +15 -2
- package/src/styles.ts +60 -1
- package/src/types.ts +23 -0
- package/src/utils/address.ts +4 -1
- package/src/utils/xml.ts +0 -7
- package/src/workbook.ts +18 -0
- package/src/worksheet.ts +235 -7
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,56 @@ 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
|
+
// Sort and filter fields
|
|
237
|
+
pivot
|
|
238
|
+
.sortField('Region', 'asc') // Sort ascending
|
|
239
|
+
.filterField('Product', { include: ['Widget', 'Gadget'] }); // Include only these
|
|
240
|
+
|
|
241
|
+
await wb.toFile('report.xlsx');
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
### Pivot Table API
|
|
245
|
+
|
|
246
|
+
```typescript
|
|
247
|
+
// Add fields to different areas
|
|
248
|
+
pivot.addRowField(fieldName: string): PivotTable
|
|
249
|
+
pivot.addColumnField(fieldName: string): PivotTable
|
|
250
|
+
pivot.addValueField(fieldName: string, aggregation?: AggregationType, displayName?: string): PivotTable
|
|
251
|
+
pivot.addFilterField(fieldName: string): PivotTable
|
|
252
|
+
|
|
253
|
+
// Aggregation types: 'sum' | 'count' | 'average' | 'min' | 'max'
|
|
254
|
+
|
|
255
|
+
// Sort a row or column field
|
|
256
|
+
pivot.sortField(fieldName: string, order: 'asc' | 'desc'): PivotTable
|
|
257
|
+
|
|
258
|
+
// Filter field values
|
|
259
|
+
pivot.filterField(fieldName: string, filter: { include?: string[] } | { exclude?: string[] }): PivotTable
|
|
260
|
+
```
|
|
261
|
+
|
|
168
262
|
## Creating Sheets from Data
|
|
169
263
|
|
|
170
264
|
Create sheets directly from arrays of objects with `addSheetFromData`:
|
|
@@ -226,7 +320,7 @@ const data = sheet.toJson();
|
|
|
226
320
|
// [{ name: 'Alice', age: 30 }, { name: 'Bob', age: 25 }]
|
|
227
321
|
|
|
228
322
|
// Using custom field names (first row is data, not headers)
|
|
229
|
-
const
|
|
323
|
+
const data2 = sheet.toJson({
|
|
230
324
|
fields: ['name', 'age', 'city'],
|
|
231
325
|
});
|
|
232
326
|
|
|
@@ -238,21 +332,26 @@ interface Person {
|
|
|
238
332
|
const people = sheet.toJson<Person>();
|
|
239
333
|
|
|
240
334
|
// Starting from a specific position
|
|
241
|
-
const
|
|
335
|
+
const data3 = sheet.toJson({
|
|
242
336
|
startRow: 2, // Skip first 2 rows (0-based)
|
|
243
337
|
startCol: 1, // Start from column B
|
|
244
338
|
});
|
|
245
339
|
|
|
246
340
|
// Limiting the range
|
|
247
|
-
const
|
|
341
|
+
const data4 = sheet.toJson({
|
|
248
342
|
endRow: 10, // Stop at row 11 (0-based, inclusive)
|
|
249
343
|
endCol: 3, // Only read columns A-D
|
|
250
344
|
});
|
|
251
345
|
|
|
252
346
|
// Continue past empty rows
|
|
253
|
-
const
|
|
347
|
+
const data5 = sheet.toJson({
|
|
254
348
|
stopOnEmptyRow: false, // Default is true
|
|
255
349
|
});
|
|
350
|
+
|
|
351
|
+
// Control how dates are serialized
|
|
352
|
+
const data6 = sheet.toJson({
|
|
353
|
+
dateHandling: 'isoString', // 'jsDate' | 'excelSerial' | 'isoString'
|
|
354
|
+
});
|
|
256
355
|
```
|
|
257
356
|
|
|
258
357
|
### Roundtrip Example
|
|
@@ -277,11 +376,24 @@ const readData = sheet.toJson();
|
|
|
277
376
|
## Saving
|
|
278
377
|
|
|
279
378
|
```typescript
|
|
280
|
-
//
|
|
281
|
-
await
|
|
379
|
+
// Load from file
|
|
380
|
+
const wb = await Workbook.fromFile('template.xlsx');
|
|
381
|
+
|
|
382
|
+
// Or load from buffer
|
|
383
|
+
const buffer = await fetch('https://example.com/file.xlsx').then((r) => r.arrayBuffer());
|
|
384
|
+
const wb2 = await Workbook.fromBuffer(new Uint8Array(buffer));
|
|
385
|
+
|
|
386
|
+
// Read data
|
|
387
|
+
const sheet = wb.sheet('Sheet1');
|
|
388
|
+
console.log(sheet.cell('A1').value); // The cell value
|
|
389
|
+
console.log(sheet.cell('A1').formula); // The formula (if any)
|
|
390
|
+
console.log(sheet.cell('A1').type); // 'string' | 'number' | 'boolean' | 'date' | 'error' | 'empty'
|
|
282
391
|
|
|
283
|
-
//
|
|
284
|
-
const
|
|
392
|
+
// Check if a cell exists without creating it
|
|
393
|
+
const existingCell = sheet.getCellIfExists('A1');
|
|
394
|
+
if (existingCell) {
|
|
395
|
+
console.log(existingCell.value);
|
|
396
|
+
}
|
|
285
397
|
```
|
|
286
398
|
|
|
287
399
|
## Type Definitions
|
|
@@ -330,9 +442,121 @@ interface SheetToJsonConfig {
|
|
|
330
442
|
endRow?: number;
|
|
331
443
|
endCol?: number;
|
|
332
444
|
stopOnEmptyRow?: boolean; // Default: true
|
|
445
|
+
dateHandling?: 'jsDate' | 'excelSerial' | 'isoString'; // Default: 'jsDate'
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
// Pivot table configuration
|
|
449
|
+
interface PivotTableConfig {
|
|
450
|
+
name: string;
|
|
451
|
+
source: string; // e.g., "Sheet1!A1:D100"
|
|
452
|
+
target: string; // e.g., "Sheet2!A3"
|
|
453
|
+
refreshOnLoad?: boolean; // Default: true
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
type AggregationType = 'sum' | 'count' | 'average' | 'min' | 'max';
|
|
457
|
+
type PivotSortOrder = 'asc' | 'desc';
|
|
458
|
+
interface PivotFieldFilter {
|
|
459
|
+
include?: string[];
|
|
460
|
+
exclude?: string[];
|
|
333
461
|
}
|
|
334
462
|
```
|
|
335
463
|
|
|
464
|
+
## Address Utilities
|
|
465
|
+
|
|
466
|
+
Helper functions for working with Excel cell addresses:
|
|
467
|
+
|
|
468
|
+
```typescript
|
|
469
|
+
import {
|
|
470
|
+
colToLetter,
|
|
471
|
+
letterToCol,
|
|
472
|
+
parseAddress,
|
|
473
|
+
toAddress,
|
|
474
|
+
parseRange,
|
|
475
|
+
toRange,
|
|
476
|
+
normalizeRange,
|
|
477
|
+
isInRange,
|
|
478
|
+
} from '@niicojs/excel';
|
|
479
|
+
|
|
480
|
+
// Convert column index to letter (0-based)
|
|
481
|
+
colToLetter(0); // 'A'
|
|
482
|
+
colToLetter(25); // 'Z'
|
|
483
|
+
colToLetter(26); // 'AA'
|
|
484
|
+
|
|
485
|
+
// Convert column letter to index (0-based)
|
|
486
|
+
letterToCol('A'); // 0
|
|
487
|
+
letterToCol('Z'); // 25
|
|
488
|
+
letterToCol('AA'); // 26
|
|
489
|
+
|
|
490
|
+
// Parse cell address to row/col
|
|
491
|
+
parseAddress('B3'); // { row: 2, col: 1 }
|
|
492
|
+
parseAddress('$A$1'); // { row: 0, col: 0 } (absolute refs supported)
|
|
493
|
+
|
|
494
|
+
// Convert row/col to address
|
|
495
|
+
toAddress(0, 0); // 'A1'
|
|
496
|
+
toAddress(9, 2); // 'C10'
|
|
497
|
+
|
|
498
|
+
// Parse range string
|
|
499
|
+
parseRange('A1:C10'); // { start: { row: 0, col: 0 }, end: { row: 9, col: 2 } }
|
|
500
|
+
|
|
501
|
+
// Convert range object to string
|
|
502
|
+
toRange({ start: { row: 0, col: 0 }, end: { row: 9, col: 2 } }); // 'A1:C10'
|
|
503
|
+
|
|
504
|
+
// Normalize range (ensure start is top-left, end is bottom-right)
|
|
505
|
+
normalizeRange({ start: { row: 5, col: 3 }, end: { row: 0, col: 0 } });
|
|
506
|
+
// { start: { row: 0, col: 0 }, end: { row: 5, col: 3 } }
|
|
507
|
+
|
|
508
|
+
// Check if address is within a range
|
|
509
|
+
isInRange({ row: 2, col: 1 }, { start: { row: 0, col: 0 }, end: { row: 5, col: 5 } }); // true
|
|
510
|
+
isInRange({ row: 10, col: 0 }, { start: { row: 0, col: 0 }, end: { row: 5, col: 5 } }); // false
|
|
511
|
+
```
|
|
512
|
+
|
|
513
|
+
## Style Schema
|
|
514
|
+
|
|
515
|
+
Supported style properties (CellStyle):
|
|
516
|
+
|
|
517
|
+
```typescript
|
|
518
|
+
interface CellStyle {
|
|
519
|
+
bold?: boolean;
|
|
520
|
+
italic?: boolean;
|
|
521
|
+
underline?: boolean | 'single' | 'double';
|
|
522
|
+
strike?: boolean;
|
|
523
|
+
fontSize?: number;
|
|
524
|
+
fontName?: string;
|
|
525
|
+
fontColor?: string; // Hex (RGB/RRGGBB/AARRGGBB)
|
|
526
|
+
fill?: string; // Hex (RGB/RRGGBB/AARRGGBB)
|
|
527
|
+
border?: {
|
|
528
|
+
top?: 'thin' | 'medium' | 'thick' | 'double' | 'dotted' | 'dashed';
|
|
529
|
+
bottom?: 'thin' | 'medium' | 'thick' | 'double' | 'dotted' | 'dashed';
|
|
530
|
+
left?: 'thin' | 'medium' | 'thick' | 'double' | 'dotted' | 'dashed';
|
|
531
|
+
right?: 'thin' | 'medium' | 'thick' | 'double' | 'dotted' | 'dashed';
|
|
532
|
+
};
|
|
533
|
+
alignment?: {
|
|
534
|
+
horizontal?: 'left' | 'center' | 'right' | 'justify';
|
|
535
|
+
vertical?: 'top' | 'middle' | 'bottom';
|
|
536
|
+
wrapText?: boolean;
|
|
537
|
+
textRotation?: number;
|
|
538
|
+
};
|
|
539
|
+
numberFormat?: string; // Excel format code
|
|
540
|
+
}
|
|
541
|
+
```
|
|
542
|
+
|
|
543
|
+
## Performance and Large Files
|
|
544
|
+
|
|
545
|
+
Tips for large sheets and high-volume writes:
|
|
546
|
+
|
|
547
|
+
- Prefer `addSheetFromData` for bulk writes when possible.
|
|
548
|
+
- Use `range.getValues({ createMissing: false })` to avoid creating empty cells during reads.
|
|
549
|
+
- Keep shared strings small: prefer numbers/booleans where applicable.
|
|
550
|
+
- Avoid frequent `toBuffer()` calls in loops; batch writes and serialize once.
|
|
551
|
+
|
|
552
|
+
## Limitations
|
|
553
|
+
|
|
554
|
+
The library focuses on preserving existing structure and editing common parts of workbooks.
|
|
555
|
+
|
|
556
|
+
- Chart editing is not supported (charts are preserved only).
|
|
557
|
+
- Conditional formatting and data validation are preserved but not editable yet.
|
|
558
|
+
- Some advanced Excel features (sparklines, slicers, macros) are preserved only.
|
|
559
|
+
|
|
336
560
|
## Format Preservation
|
|
337
561
|
|
|
338
562
|
When modifying existing Excel files, this library preserves:
|