@niicojs/excel 0.2.7 → 0.3.1
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 +585 -352
- package/dist/index.cjs +1800 -488
- package/dist/index.d.cts +364 -5
- package/dist/index.d.cts.map +1 -1
- package/dist/index.d.ts +364 -5
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1800 -489
- package/package.json +1 -1
- package/src/index.ts +45 -37
- package/src/pivot-cache.ts +300 -291
- package/src/pivot-table.ts +684 -586
- package/src/range.ts +154 -141
- package/src/shared-strings.ts +178 -129
- package/src/styles.ts +819 -648
- package/src/table.ts +386 -0
- package/src/types.ts +314 -238
- package/src/utils/address.ts +121 -118
- package/src/utils/xml.ts +140 -147
- package/src/workbook.ts +1390 -1005
- package/src/worksheet.ts +889 -422
package/README.md
CHANGED
|
@@ -1,352 +1,585 @@
|
|
|
1
|
-
# @niicojs/excel
|
|
2
|
-
|
|
3
|
-
A TypeScript library for Excel/OpenXML manipulation with maximum format preservation.
|
|
4
|
-
|
|
5
|
-
## Features
|
|
6
|
-
|
|
7
|
-
- Read and write `.xlsx` files
|
|
8
|
-
- Preserve formatting when modifying existing files
|
|
9
|
-
- Full formula support (read/write/preserve)
|
|
10
|
-
- Cell styles (fonts, fills, borders, alignment)
|
|
11
|
-
- Merged cells
|
|
12
|
-
-
|
|
13
|
-
-
|
|
14
|
-
-
|
|
15
|
-
-
|
|
16
|
-
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
sheet.
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
sheet.cell('
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
sheet.cell('
|
|
44
|
-
|
|
45
|
-
// Write
|
|
46
|
-
sheet.cell('
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
]
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
//
|
|
65
|
-
const
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
const
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
const
|
|
82
|
-
|
|
83
|
-
//
|
|
84
|
-
sheet.range('A1:
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
sheet.
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
sheet.
|
|
131
|
-
|
|
132
|
-
//
|
|
133
|
-
sheet.mergeCells('A1
|
|
134
|
-
|
|
135
|
-
//
|
|
136
|
-
sheet.
|
|
137
|
-
|
|
138
|
-
//
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
wb.
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
//
|
|
155
|
-
const
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
//
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
//
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
```
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
//
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
wb.
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
//
|
|
209
|
-
wb.
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
//
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
//
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
```
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
//
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
```
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
//
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
1
|
+
# @niicojs/excel
|
|
2
|
+
|
|
3
|
+
A TypeScript library for Excel/OpenXML manipulation with maximum format preservation.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- Read and write `.xlsx` files
|
|
8
|
+
- Preserve formatting when modifying existing files
|
|
9
|
+
- Full formula support (read/write/preserve)
|
|
10
|
+
- Cell styles (fonts, fills, borders, alignment)
|
|
11
|
+
- Merged cells
|
|
12
|
+
- Column widths and row heights
|
|
13
|
+
- Freeze panes
|
|
14
|
+
- Pivot tables with fluent API
|
|
15
|
+
- Sheet operations (add, delete, rename, copy)
|
|
16
|
+
- Create sheets from arrays of objects (`addSheetFromData`)
|
|
17
|
+
- Convert sheets to JSON arrays (`toJson`)
|
|
18
|
+
- Create new workbooks from scratch
|
|
19
|
+
- TypeScript-first with full type definitions
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
pnpm install @niicojs/excel
|
|
25
|
+
# or
|
|
26
|
+
bun add @niicojs/excel
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Quick Start
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import { Workbook } from '@niicojs/excel';
|
|
33
|
+
|
|
34
|
+
// Create a new workbook
|
|
35
|
+
const wb = Workbook.create();
|
|
36
|
+
wb.addSheet('Sheet1');
|
|
37
|
+
const sheet = wb.sheet('Sheet1');
|
|
38
|
+
|
|
39
|
+
// Write data
|
|
40
|
+
sheet.cell('A1').value = 'Hello';
|
|
41
|
+
sheet.cell('B1').value = 42;
|
|
42
|
+
sheet.cell('C1').value = true;
|
|
43
|
+
sheet.cell('D1').value = new Date();
|
|
44
|
+
|
|
45
|
+
// Write formulas
|
|
46
|
+
sheet.cell('A2').formula = 'SUM(B1:B1)';
|
|
47
|
+
|
|
48
|
+
// Write a 2D array
|
|
49
|
+
sheet.cell('A3').values = [
|
|
50
|
+
['Name', 'Age', 'City'],
|
|
51
|
+
['Alice', 30, 'NYC'],
|
|
52
|
+
['Bob', 25, 'LA'],
|
|
53
|
+
];
|
|
54
|
+
|
|
55
|
+
// Save to file
|
|
56
|
+
await wb.toFile('output.xlsx');
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Loading Existing Files
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
import { Workbook } from '@niicojs/excel';
|
|
63
|
+
|
|
64
|
+
// Load from file
|
|
65
|
+
const wb = await Workbook.fromFile('template.xlsx');
|
|
66
|
+
|
|
67
|
+
// Or load from buffer
|
|
68
|
+
const buffer = await fetch('https://example.com/file.xlsx').then((r) => r.arrayBuffer());
|
|
69
|
+
const wb = await Workbook.fromBuffer(new Uint8Array(buffer));
|
|
70
|
+
|
|
71
|
+
// Read data
|
|
72
|
+
const sheet = wb.sheet('Sheet1');
|
|
73
|
+
console.log(sheet.cell('A1').value); // The cell value
|
|
74
|
+
console.log(sheet.cell('A1').formula); // The formula (if any)
|
|
75
|
+
console.log(sheet.cell('A1').type); // 'string' | 'number' | 'boolean' | 'date' | 'error' | 'empty'
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
## Working with Ranges
|
|
79
|
+
|
|
80
|
+
```typescript
|
|
81
|
+
const sheet = wb.sheet(0);
|
|
82
|
+
|
|
83
|
+
// Read a range
|
|
84
|
+
const values = sheet.range('A1:C10').values; // 2D array
|
|
85
|
+
|
|
86
|
+
// Write to a range
|
|
87
|
+
sheet.range('A1:B2').values = [
|
|
88
|
+
[1, 2],
|
|
89
|
+
[3, 4],
|
|
90
|
+
];
|
|
91
|
+
|
|
92
|
+
// Get formulas from a range
|
|
93
|
+
const formulas = sheet.range('A1:C10').formulas;
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
## Styling Cells
|
|
97
|
+
|
|
98
|
+
```typescript
|
|
99
|
+
const sheet = wb.sheet(0);
|
|
100
|
+
|
|
101
|
+
// Apply styles to a cell
|
|
102
|
+
sheet.cell('A1').style = {
|
|
103
|
+
bold: true,
|
|
104
|
+
italic: true,
|
|
105
|
+
fontSize: 14,
|
|
106
|
+
fontName: 'Arial',
|
|
107
|
+
fontColor: '#FF0000',
|
|
108
|
+
fill: '#FFFF00',
|
|
109
|
+
border: {
|
|
110
|
+
top: 'thin',
|
|
111
|
+
bottom: 'medium',
|
|
112
|
+
left: 'thin',
|
|
113
|
+
right: 'thin',
|
|
114
|
+
},
|
|
115
|
+
alignment: {
|
|
116
|
+
horizontal: 'center',
|
|
117
|
+
vertical: 'middle',
|
|
118
|
+
wrapText: true,
|
|
119
|
+
},
|
|
120
|
+
numberFormat: '#,##0.00',
|
|
121
|
+
};
|
|
122
|
+
|
|
123
|
+
// Apply styles to a range
|
|
124
|
+
sheet.range('A1:C1').style = { bold: true, fill: '#CCCCCC' };
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
## Merged Cells
|
|
128
|
+
|
|
129
|
+
```typescript
|
|
130
|
+
const sheet = wb.sheet(0);
|
|
131
|
+
|
|
132
|
+
// Merge cells
|
|
133
|
+
sheet.mergeCells('A1:C1');
|
|
134
|
+
|
|
135
|
+
// Or using two addresses
|
|
136
|
+
sheet.mergeCells('A1', 'C1');
|
|
137
|
+
|
|
138
|
+
// Unmerge
|
|
139
|
+
sheet.unmergeCells('A1:C1');
|
|
140
|
+
|
|
141
|
+
// Get all merged regions
|
|
142
|
+
console.log(sheet.mergedCells); // ['A1:C3', 'D5:E10', ...]
|
|
143
|
+
```
|
|
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
|
+
|
|
186
|
+
## Sheet Operations
|
|
187
|
+
|
|
188
|
+
```typescript
|
|
189
|
+
const wb = Workbook.create();
|
|
190
|
+
|
|
191
|
+
// Add sheets
|
|
192
|
+
wb.addSheet('Data');
|
|
193
|
+
wb.addSheet('Summary', 0); // Insert at index 0
|
|
194
|
+
|
|
195
|
+
// Get sheet names
|
|
196
|
+
console.log(wb.sheetNames); // ['Summary', 'Data']
|
|
197
|
+
|
|
198
|
+
// Access sheets
|
|
199
|
+
const sheet = wb.sheet('Data'); // By name
|
|
200
|
+
const sheet = wb.sheet(0); // By index
|
|
201
|
+
|
|
202
|
+
// Rename sheet
|
|
203
|
+
wb.renameSheet('Data', 'RawData');
|
|
204
|
+
|
|
205
|
+
// Copy sheet
|
|
206
|
+
wb.copySheet('RawData', 'RawData_Backup');
|
|
207
|
+
|
|
208
|
+
// Delete sheet
|
|
209
|
+
wb.deleteSheet('Summary');
|
|
210
|
+
```
|
|
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
|
+
|
|
311
|
+
## Creating Sheets from Data
|
|
312
|
+
|
|
313
|
+
Create sheets directly from arrays of objects with `addSheetFromData`:
|
|
314
|
+
|
|
315
|
+
```typescript
|
|
316
|
+
const wb = Workbook.create();
|
|
317
|
+
|
|
318
|
+
// Simple usage - object keys become column headers
|
|
319
|
+
const employees = [
|
|
320
|
+
{ name: 'Alice', age: 30, city: 'Paris' },
|
|
321
|
+
{ name: 'Bob', age: 25, city: 'London' },
|
|
322
|
+
];
|
|
323
|
+
|
|
324
|
+
wb.addSheetFromData({
|
|
325
|
+
name: 'Employees',
|
|
326
|
+
data: employees,
|
|
327
|
+
});
|
|
328
|
+
|
|
329
|
+
// Custom column configuration
|
|
330
|
+
wb.addSheetFromData({
|
|
331
|
+
name: 'Custom',
|
|
332
|
+
data: employees,
|
|
333
|
+
columns: [
|
|
334
|
+
{ key: 'name', header: 'Full Name' },
|
|
335
|
+
{ key: 'age', header: 'Age (years)' },
|
|
336
|
+
{ key: 'city', header: 'Location', style: { bold: true } },
|
|
337
|
+
],
|
|
338
|
+
});
|
|
339
|
+
|
|
340
|
+
// With formulas and styles using RichCellValue
|
|
341
|
+
const orderLines = [
|
|
342
|
+
{ product: 'Widget', price: 10, qty: 5, total: { formula: 'B2*C2', style: { bold: true } } },
|
|
343
|
+
{ product: 'Gadget', price: 20, qty: 3, total: { formula: 'B3*C3', style: { bold: true } } },
|
|
344
|
+
];
|
|
345
|
+
|
|
346
|
+
wb.addSheetFromData({
|
|
347
|
+
name: 'Orders',
|
|
348
|
+
data: orderLines,
|
|
349
|
+
});
|
|
350
|
+
|
|
351
|
+
// Other options
|
|
352
|
+
wb.addSheetFromData({
|
|
353
|
+
name: 'Options',
|
|
354
|
+
data: employees,
|
|
355
|
+
headerStyle: false, // Don't bold headers
|
|
356
|
+
startCell: 'B3', // Start at B3 instead of A1
|
|
357
|
+
});
|
|
358
|
+
```
|
|
359
|
+
|
|
360
|
+
## Converting Sheets to JSON
|
|
361
|
+
|
|
362
|
+
Convert sheet data back to arrays of objects with `toJson`:
|
|
363
|
+
|
|
364
|
+
```typescript
|
|
365
|
+
const sheet = wb.sheet('Data');
|
|
366
|
+
|
|
367
|
+
// Using first row as headers
|
|
368
|
+
const data = sheet.toJson();
|
|
369
|
+
// [{ name: 'Alice', age: 30 }, { name: 'Bob', age: 25 }]
|
|
370
|
+
|
|
371
|
+
// Using custom field names (first row is data, not headers)
|
|
372
|
+
const data2 = sheet.toJson({
|
|
373
|
+
fields: ['name', 'age', 'city'],
|
|
374
|
+
});
|
|
375
|
+
|
|
376
|
+
// With TypeScript generics
|
|
377
|
+
interface Person {
|
|
378
|
+
name: string | null;
|
|
379
|
+
age: number | null;
|
|
380
|
+
}
|
|
381
|
+
const people = sheet.toJson<Person>();
|
|
382
|
+
|
|
383
|
+
// Starting from a specific position
|
|
384
|
+
const data3 = sheet.toJson({
|
|
385
|
+
startRow: 2, // Skip first 2 rows (0-based)
|
|
386
|
+
startCol: 1, // Start from column B
|
|
387
|
+
});
|
|
388
|
+
|
|
389
|
+
// Limiting the range
|
|
390
|
+
const data4 = sheet.toJson({
|
|
391
|
+
endRow: 10, // Stop at row 11 (0-based, inclusive)
|
|
392
|
+
endCol: 3, // Only read columns A-D
|
|
393
|
+
});
|
|
394
|
+
|
|
395
|
+
// Continue past empty rows
|
|
396
|
+
const data5 = sheet.toJson({
|
|
397
|
+
stopOnEmptyRow: false, // Default is true
|
|
398
|
+
});
|
|
399
|
+
|
|
400
|
+
// Control how dates are serialized
|
|
401
|
+
const data6 = sheet.toJson({
|
|
402
|
+
dateHandling: 'isoString', // 'jsDate' | 'excelSerial' | 'isoString'
|
|
403
|
+
});
|
|
404
|
+
```
|
|
405
|
+
|
|
406
|
+
### Roundtrip Example
|
|
407
|
+
|
|
408
|
+
```typescript
|
|
409
|
+
// Create from objects
|
|
410
|
+
const originalData = [
|
|
411
|
+
{ name: 'Alice', age: 30 },
|
|
412
|
+
{ name: 'Bob', age: 25 },
|
|
413
|
+
];
|
|
414
|
+
|
|
415
|
+
const sheet = wb.addSheetFromData({
|
|
416
|
+
name: 'People',
|
|
417
|
+
data: originalData,
|
|
418
|
+
});
|
|
419
|
+
|
|
420
|
+
// Read back as objects
|
|
421
|
+
const readData = sheet.toJson();
|
|
422
|
+
// readData equals originalData
|
|
423
|
+
```
|
|
424
|
+
|
|
425
|
+
## Saving
|
|
426
|
+
|
|
427
|
+
```typescript
|
|
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'
|
|
440
|
+
|
|
441
|
+
// Check if a cell exists without creating it
|
|
442
|
+
const existingCell = sheet.getCellIfExists('A1');
|
|
443
|
+
if (existingCell) {
|
|
444
|
+
console.log(existingCell.value);
|
|
445
|
+
}
|
|
446
|
+
```
|
|
447
|
+
|
|
448
|
+
## Type Definitions
|
|
449
|
+
|
|
450
|
+
```typescript
|
|
451
|
+
// Cell values
|
|
452
|
+
type CellValue = number | string | boolean | Date | null | CellError;
|
|
453
|
+
|
|
454
|
+
interface CellError {
|
|
455
|
+
error: '#NULL!' | '#DIV/0!' | '#VALUE!' | '#REF!' | '#NAME?' | '#NUM!' | '#N/A';
|
|
456
|
+
}
|
|
457
|
+
|
|
458
|
+
// Cell types
|
|
459
|
+
type CellType = 'number' | 'string' | 'boolean' | 'date' | 'error' | 'empty';
|
|
460
|
+
|
|
461
|
+
// Border types
|
|
462
|
+
type BorderType = 'thin' | 'medium' | 'thick' | 'double' | 'dotted' | 'dashed';
|
|
463
|
+
|
|
464
|
+
// Configuration for addSheetFromData
|
|
465
|
+
interface SheetFromDataConfig<T> {
|
|
466
|
+
name: string;
|
|
467
|
+
data: T[];
|
|
468
|
+
columns?: ColumnConfig<T>[];
|
|
469
|
+
headerStyle?: boolean; // Default: true
|
|
470
|
+
startCell?: string; // Default: 'A1'
|
|
471
|
+
}
|
|
472
|
+
|
|
473
|
+
interface ColumnConfig<T> {
|
|
474
|
+
key: keyof T;
|
|
475
|
+
header?: string;
|
|
476
|
+
style?: CellStyle;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
// Rich cell value for formulas/styles in data
|
|
480
|
+
interface RichCellValue {
|
|
481
|
+
value?: CellValue;
|
|
482
|
+
formula?: string;
|
|
483
|
+
style?: CellStyle;
|
|
484
|
+
}
|
|
485
|
+
|
|
486
|
+
// Configuration for toJson
|
|
487
|
+
interface SheetToJsonConfig {
|
|
488
|
+
fields?: string[];
|
|
489
|
+
startRow?: number;
|
|
490
|
+
startCol?: number;
|
|
491
|
+
endRow?: number;
|
|
492
|
+
endCol?: number;
|
|
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)
|
|
519
|
+
}
|
|
520
|
+
```
|
|
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
|
+
|
|
569
|
+
## Format Preservation
|
|
570
|
+
|
|
571
|
+
When modifying existing Excel files, this library preserves:
|
|
572
|
+
|
|
573
|
+
- Cell formatting and styles
|
|
574
|
+
- Formulas
|
|
575
|
+
- Charts and images
|
|
576
|
+
- Merged cells
|
|
577
|
+
- Conditional formatting
|
|
578
|
+
- Data validation
|
|
579
|
+
- And other Excel features
|
|
580
|
+
|
|
581
|
+
This is achieved by only modifying what's necessary and keeping the original XML structure intact.
|
|
582
|
+
|
|
583
|
+
## License
|
|
584
|
+
|
|
585
|
+
MIT
|