@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 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 data = sheet.toJson({
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 data = sheet.toJson({
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 data = sheet.toJson({
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 data = sheet.toJson({
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
- // Save to file
281
- await wb.toFile('output.xlsx');
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
- // Save to buffer (Uint8Array)
284
- const buffer = await wb.toBuffer();
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: