@simplysm/excel 13.0.76 → 13.0.78

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.
Files changed (54) hide show
  1. package/README.md +539 -17
  2. package/dist/excel-cell.d.ts +4 -4
  3. package/dist/excel-cell.d.ts.map +1 -1
  4. package/dist/excel-cell.js +9 -10
  5. package/dist/excel-cell.js.map +1 -1
  6. package/dist/excel-workbook.d.ts +3 -3
  7. package/dist/excel-workbook.d.ts.map +1 -1
  8. package/dist/excel-workbook.js +3 -3
  9. package/dist/excel-workbook.js.map +1 -1
  10. package/dist/excel-worksheet.d.ts +1 -1
  11. package/dist/excel-worksheet.d.ts.map +1 -1
  12. package/dist/excel-worksheet.js +13 -17
  13. package/dist/excel-worksheet.js.map +1 -1
  14. package/dist/excel-wrapper.d.ts +1 -1
  15. package/dist/excel-wrapper.js +7 -7
  16. package/dist/excel-wrapper.js.map +1 -1
  17. package/dist/types.d.ts +1 -1
  18. package/dist/types.d.ts.map +1 -1
  19. package/dist/utils/excel-utils.d.ts +5 -5
  20. package/dist/utils/excel-utils.d.ts.map +1 -1
  21. package/dist/utils/excel-utils.js +15 -15
  22. package/dist/utils/excel-utils.js.map +1 -1
  23. package/dist/utils/zip-cache.js +3 -3
  24. package/dist/utils/zip-cache.js.map +1 -1
  25. package/dist/xml/excel-xml-relationship.js +2 -2
  26. package/dist/xml/excel-xml-relationship.js.map +1 -1
  27. package/dist/xml/excel-xml-style.js +16 -16
  28. package/dist/xml/excel-xml-style.js.map +1 -1
  29. package/dist/xml/excel-xml-workbook.js +6 -6
  30. package/dist/xml/excel-xml-workbook.js.map +1 -1
  31. package/dist/xml/excel-xml-worksheet.d.ts +5 -2
  32. package/dist/xml/excel-xml-worksheet.d.ts.map +1 -1
  33. package/dist/xml/excel-xml-worksheet.js +38 -20
  34. package/dist/xml/excel-xml-worksheet.js.map +1 -1
  35. package/package.json +2 -2
  36. package/src/excel-cell.ts +10 -11
  37. package/src/excel-workbook.ts +3 -3
  38. package/src/excel-worksheet.ts +17 -18
  39. package/src/excel-wrapper.ts +7 -7
  40. package/src/types.ts +1 -1
  41. package/src/utils/excel-utils.ts +15 -15
  42. package/src/utils/zip-cache.ts +3 -3
  43. package/src/xml/excel-xml-relationship.ts +2 -2
  44. package/src/xml/excel-xml-style.ts +16 -16
  45. package/src/xml/excel-xml-workbook.ts +6 -6
  46. package/src/xml/excel-xml-worksheet.ts +47 -22
  47. package/tests/excel-cell.spec.ts +85 -69
  48. package/tests/excel-col.spec.ts +17 -17
  49. package/tests/excel-row.spec.ts +13 -13
  50. package/tests/excel-workbook.spec.ts +26 -26
  51. package/tests/excel-worksheet.spec.ts +217 -101
  52. package/tests/excel-wrapper.spec.ts +24 -24
  53. package/tests/image-insert.spec.ts +5 -5
  54. package/tests/utils/excel-utils.spec.ts +14 -14
package/README.md CHANGED
@@ -21,12 +21,520 @@ const table = await ws.getDataTable(); // [{ Name: "Alice", Age: 30 }, ...]
21
21
 
22
22
  // Create a new file
23
23
  await using wb2 = new ExcelWorkbook();
24
- const ws2 = await wb2.createWorksheet("Sheet1");
24
+ const ws2 = await wb2.addWorksheet("Sheet1");
25
25
  await ws2.setRecords([{ Name: "Alice", Age: 30 }]);
26
- const output = await wb2.getBytes();
26
+ const output = await wb2.toBytes();
27
27
  ```
28
28
 
29
- ### Type-safe read/write with Zod (ExcelWrapper)
29
+ ---
30
+
31
+ ## Types and Utilities
32
+
33
+ ### `ExcelValueType`
34
+
35
+ Union of all supported cell value types.
36
+
37
+ ```typescript
38
+ import { ExcelValueType } from "@simplysm/excel";
39
+
40
+ type ExcelValueType = number | string | DateOnly | DateTime | Time | boolean | undefined;
41
+ ```
42
+
43
+ ### `ExcelNumberFormat`
44
+
45
+ Named number format for a cell.
46
+
47
+ ```typescript
48
+ import { ExcelNumberFormat } from "@simplysm/excel";
49
+
50
+ type ExcelNumberFormat = "number" | "string" | "DateOnly" | "DateTime" | "Time";
51
+ ```
52
+
53
+ ### `ExcelCellType`
54
+
55
+ Raw Excel cell type code stored in the XML.
56
+
57
+ ```typescript
58
+ import { ExcelCellType } from "@simplysm/excel";
59
+
60
+ // "s" - shared string
61
+ // "b" - boolean
62
+ // "str" - formula result string
63
+ // "n" - number
64
+ // "inlineStr" - inline string (rich text)
65
+ // "e" - error
66
+ type ExcelCellType = "s" | "b" | "str" | "n" | "inlineStr" | "e";
67
+ ```
68
+
69
+ ### `ExcelAddressPoint`
70
+
71
+ A 0-based row/column coordinate pair.
72
+
73
+ ```typescript
74
+ import { ExcelAddressPoint } from "@simplysm/excel";
75
+
76
+ interface ExcelAddressPoint {
77
+ r: number; // 0-based row index
78
+ c: number; // 0-based column index
79
+ }
80
+ ```
81
+
82
+ ### `ExcelAddressRangePoint`
83
+
84
+ A range defined by start and end `ExcelAddressPoint` values.
85
+
86
+ ```typescript
87
+ import { ExcelAddressRangePoint } from "@simplysm/excel";
88
+
89
+ interface ExcelAddressRangePoint {
90
+ s: ExcelAddressPoint; // start (top-left)
91
+ e: ExcelAddressPoint; // end (bottom-right)
92
+ }
93
+ ```
94
+
95
+ ### `ExcelStyleOptions`
96
+
97
+ Options for `ExcelCell.setStyle()`.
98
+
99
+ ```typescript
100
+ import { ExcelStyleOptions } from "@simplysm/excel";
101
+
102
+ interface ExcelStyleOptions {
103
+ background?: string; // ARGB 8-digit hex, e.g. "FFFF0000" (red)
104
+ border?: ExcelBorderPosition[]; // Border sides to draw
105
+ horizontalAlign?: ExcelHorizontalAlign;
106
+ verticalAlign?: ExcelVerticalAlign;
107
+ numberFormat?: ExcelNumberFormat;
108
+ }
109
+ ```
110
+
111
+ ### `ExcelBorderPosition`
112
+
113
+ ```typescript
114
+ import { ExcelBorderPosition } from "@simplysm/excel";
115
+
116
+ type ExcelBorderPosition = "left" | "right" | "top" | "bottom";
117
+ ```
118
+
119
+ ### `ExcelHorizontalAlign`
120
+
121
+ ```typescript
122
+ import { ExcelHorizontalAlign } from "@simplysm/excel";
123
+
124
+ type ExcelHorizontalAlign = "center" | "left" | "right";
125
+ ```
126
+
127
+ ### `ExcelVerticalAlign`
128
+
129
+ ```typescript
130
+ import { ExcelVerticalAlign } from "@simplysm/excel";
131
+
132
+ type ExcelVerticalAlign = "center" | "top" | "bottom";
133
+ ```
134
+
135
+ ### XML Data Types
136
+
137
+ These interfaces represent the raw XML structures parsed from the `.xlsx` ZIP package. Exported for advanced use cases (e.g., directly reading the parsed XML tree).
138
+
139
+ ```typescript
140
+ import {
141
+ ExcelXmlContentTypeData,
142
+ ExcelXmlRelationshipData,
143
+ ExcelRelationshipData,
144
+ ExcelXmlWorkbookData,
145
+ ExcelXmlWorksheetData,
146
+ ExcelRowData,
147
+ ExcelCellData,
148
+ ExcelXmlDrawingData,
149
+ ExcelXmlSharedStringData,
150
+ ExcelXmlSharedStringDataSi,
151
+ ExcelXmlSharedStringDataText,
152
+ ExcelXmlStyleData,
153
+ ExcelXmlStyleDataXf,
154
+ ExcelXmlStyleDataFill,
155
+ ExcelXmlStyleDataBorder,
156
+ ExcelXml,
157
+ } from "@simplysm/excel";
158
+ ```
159
+
160
+ | Interface / Type | Description |
161
+ |-----------------|-------------|
162
+ | `ExcelXmlContentTypeData` | `[Content_Types].xml` document structure |
163
+ | `ExcelXmlRelationshipData` | `.rels` relationship file structure |
164
+ | `ExcelRelationshipData` | A single relationship entry |
165
+ | `ExcelXmlWorkbookData` | `xl/workbook.xml` document structure |
166
+ | `ExcelXmlWorksheetData` | `xl/worksheets/sheetN.xml` document structure |
167
+ | `ExcelRowData` | A row element within worksheet XML |
168
+ | `ExcelCellData` | A cell element within worksheet XML |
169
+ | `ExcelXmlDrawingData` | Drawing XML structure (`xl/drawings/drawingN.xml`) |
170
+ | `ExcelXmlSharedStringData` | Shared strings XML structure |
171
+ | `ExcelXmlSharedStringDataSi` | A shared string entry (plain or rich text) |
172
+ | `ExcelXmlSharedStringDataText` | Text content within a shared string entry |
173
+ | `ExcelXmlStyleData` | Styles XML document structure |
174
+ | `ExcelXmlStyleDataXf` | A cell format (`xf`) entry |
175
+ | `ExcelXmlStyleDataFill` | A fill entry |
176
+ | `ExcelXmlStyleDataBorder` | A border entry |
177
+ | `ExcelXml` | Interface implemented by all XML handler classes |
178
+
179
+ ### `ExcelUtils`
180
+
181
+ A static utility class providing cell address conversion, date/number conversion, and number format processing.
182
+
183
+ ```typescript
184
+ import { ExcelUtils } from "@simplysm/excel";
185
+
186
+ // Convert 0-based coordinates to "A1" notation
187
+ ExcelUtils.stringifyAddr({ r: 0, c: 0 }); // "A1"
188
+
189
+ // Convert column index to column letter(s)
190
+ ExcelUtils.stringifyColAddr(0); // "A"
191
+ ExcelUtils.stringifyColAddr(26); // "AA"
192
+
193
+ // Convert row index to row number string
194
+ ExcelUtils.stringifyRowAddr(0); // "1"
195
+
196
+ // Parse cell address
197
+ ExcelUtils.parseCellAddr("B3"); // { r: 2, c: 1 }
198
+ ExcelUtils.parseRowAddr("B3"); // 2
199
+ ExcelUtils.parseColAddr("B3"); // 1
200
+
201
+ // Parse range address
202
+ ExcelUtils.parseRangeAddr("A1:C3"); // { s: { r: 0, c: 0 }, e: { r: 2, c: 2 } }
203
+
204
+ // Convert range coordinates to address string
205
+ ExcelUtils.stringifyRangeAddr({ s: { r: 0, c: 0 }, e: { r: 2, c: 2 } }); // "A1:C3"
206
+
207
+ // Date/number conversion (Excel serial date <-> JS timestamp)
208
+ const excelNum = ExcelUtils.convertTimeTickToNumber(Date.now());
209
+ const tick = ExcelUtils.convertNumberToTimeTick(excelNum);
210
+
211
+ // Number format conversions
212
+ ExcelUtils.convertNumFmtCodeToName("General"); // "number"
213
+ ExcelUtils.convertNumFmtIdToName(14); // "DateOnly"
214
+ ExcelUtils.convertNumFmtNameToId("DateTime"); // 22
215
+ ```
216
+
217
+ **Static Methods**
218
+
219
+ | Method | Signature | Description |
220
+ |--------|-----------|-------------|
221
+ | `stringifyAddr` | `(point: ExcelAddressPoint) => string` | Convert coordinates to "A1" format |
222
+ | `stringifyRowAddr` | `(r: number) => string` | Convert 0-based row index to row number string |
223
+ | `stringifyColAddr` | `(c: number) => string` | Convert 0-based column index to column letter(s) |
224
+ | `parseRowAddr` | `(addr: string) => number` | Extract 0-based row index from cell address |
225
+ | `parseColAddr` | `(addr: string) => number` | Extract 0-based column index from cell address |
226
+ | `parseCellAddr` | `(addr: string) => ExcelAddressPoint` | Convert cell address string to coordinates |
227
+ | `parseRangeAddr` | `(rangeAddr: string) => ExcelAddressRangePoint` | Convert range address to coordinate pair |
228
+ | `stringifyRangeAddr` | `(point: ExcelAddressRangePoint) => string` | Convert coordinate pair to range address string |
229
+ | `convertTimeTickToNumber` | `(tick: number) => number` | Convert JS timestamp (ms) to Excel serial date number |
230
+ | `convertNumberToTimeTick` | `(value: number) => number` | Convert Excel serial date number to JS timestamp (ms) |
231
+ | `convertNumFmtCodeToName` | `(numFmtCode: string) => ExcelNumberFormat` | Convert format code string to format name |
232
+ | `convertNumFmtIdToName` | `(numFmtId: number) => ExcelNumberFormat` | Convert built-in format ID to format name |
233
+ | `convertNumFmtNameToId` | `(numFmtName: ExcelNumberFormat) => number` | Convert format name to built-in format ID |
234
+
235
+ ---
236
+
237
+ ## Core Classes
238
+
239
+ ### `ExcelWorkbook`
240
+
241
+ The top-level class for reading and writing Excel workbooks. Internally manages ZIP resources; resources must be released after use.
242
+
243
+ Supports `await using` (ES2022 `Symbol.asyncDispose`). Internally uses lazy-loading: XML inside the ZIP is parsed only at the point of access, keeping memory usage low even with large files.
244
+
245
+ ```typescript
246
+ import { ExcelWorkbook } from "@simplysm/excel";
247
+
248
+ // Open an existing workbook
249
+ const bytes: Uint8Array = /* file bytes */;
250
+ await using wb = new ExcelWorkbook(bytes);
251
+
252
+ // Create a new workbook
253
+ await using wb2 = new ExcelWorkbook();
254
+ const ws = await wb2.addWorksheet("Sheet1");
255
+
256
+ // Get all worksheet names
257
+ const names = await wb.getWorksheetNames();
258
+
259
+ // Access a worksheet by index or name
260
+ const ws0 = await wb.getWorksheet(0);
261
+ const ws1 = await wb.getWorksheet("Sheet1");
262
+
263
+ // Export
264
+ const exportedBytes = await wb.toBytes();
265
+ const blob = await wb.toBlob();
266
+
267
+ // Manual resource management
268
+ const wb3 = new ExcelWorkbook(bytes);
269
+ try {
270
+ const ws = await wb3.getWorksheet(0);
271
+ // ...
272
+ } finally {
273
+ await wb3.close();
274
+ }
275
+ ```
276
+
277
+ **Constructor**
278
+
279
+ ```typescript
280
+ constructor(arg?: Blob | Bytes)
281
+ ```
282
+
283
+ - `arg` — Existing Excel file data (`Blob` or `Uint8Array`). Creates a new empty workbook if omitted.
284
+
285
+ **Properties**
286
+
287
+ | Property | Type | Description |
288
+ |----------|------|-------------|
289
+ | `zipCache` | `ZipCache` | Internal ZIP cache (advanced use) |
290
+
291
+ **Methods**
292
+
293
+ | Method | Signature | Description |
294
+ |--------|-----------|-------------|
295
+ | `getWorksheetNames` | `() => Promise<string[]>` | Return all worksheet names in the workbook |
296
+ | `addWorksheet` | `(name: string) => Promise<ExcelWorksheet>` | Create and return a new worksheet |
297
+ | `getWorksheet` | `(nameOrIndex: string \| number) => Promise<ExcelWorksheet>` | Look up a worksheet by name or 0-based index |
298
+ | `toBytes` | `() => Promise<Bytes>` | Export workbook as a `Uint8Array` byte array |
299
+ | `toBlob` | `() => Promise<Blob>` | Export workbook as a `Blob` |
300
+ | `close` | `() => Promise<void>` | Release all resources (idempotent) |
301
+ | `[Symbol.asyncDispose]` | `() => Promise<void>` | Called automatically by `await using` |
302
+
303
+ ---
304
+
305
+ ### `ExcelWorksheet`
306
+
307
+ Represents a single worksheet. Provides cell access, row/column copy operations, data import/export, view settings, and image insertion.
308
+
309
+ ```typescript
310
+ import { ExcelWorksheet } from "@simplysm/excel";
311
+
312
+ // Obtained from ExcelWorkbook
313
+ const ws = await wb.getWorksheet(0);
314
+
315
+ // Name
316
+ const name = await ws.getName();
317
+ await ws.setName("NewName");
318
+
319
+ // Cell access
320
+ const cell = ws.cell(0, 0); // row 0, col 0 -> A1
321
+ const row = ws.row(1); // row 1 (second row)
322
+ const col = ws.col(2); // col 2 -> column C
323
+
324
+ // Range and bulk cell access
325
+ const range = await ws.getRange();
326
+ const cells = await ws.getCells(); // ExcelCell[][]
327
+
328
+ // Copy operations
329
+ await ws.copyRow(0, 5); // copy row 0 to row 5
330
+ await ws.copyRowStyle(0, 5); // copy style of row 0 to row 5
331
+ await ws.copyCell({ r: 0, c: 0 }, { r: 5, c: 0 }); // copy single cell
332
+ await ws.copyCellStyle({ r: 0, c: 0 }, { r: 5, c: 0 });
333
+ await ws.insertCopyRow(0, 3); // insert-copy row 0 at position 3, shifting rows down
334
+
335
+ // Read as table (first row = headers)
336
+ const table = await ws.getDataTable();
337
+ const table2 = await ws.getDataTable({
338
+ headerRowIndex: 0,
339
+ checkEndColIndex: 0,
340
+ usableHeaderNameFn: (name) => name !== "ignore",
341
+ });
342
+
343
+ // Write data
344
+ await ws.setDataMatrix([[1, "hello"], [2, "world"]]);
345
+ await ws.setRecords([{ Name: "Alice", Age: 30 }]);
346
+
347
+ // View settings
348
+ await ws.setZoom(85); // 85%
349
+ await ws.freezeAt({ r: 1 }); // freeze first row
350
+ await ws.freezeAt({ c: 1 }); // freeze first column
351
+ await ws.freezeAt({ r: 1, c: 1 }); // freeze both
352
+
353
+ // Insert image
354
+ await ws.addImage({
355
+ bytes: imageBytes,
356
+ ext: "png",
357
+ from: { r: 0, c: 0 },
358
+ to: { r: 5, c: 3 },
359
+ });
360
+ ```
361
+
362
+ **Methods**
363
+
364
+ | Method | Signature | Description |
365
+ |--------|-----------|-------------|
366
+ | `getName` | `() => Promise<string>` | Return the worksheet name |
367
+ | `setName` | `(newName: string) => Promise<void>` | Rename the worksheet |
368
+ | `row` | `(r: number) => ExcelRow` | Return the `ExcelRow` at 0-based row index |
369
+ | `cell` | `(r: number, c: number) => ExcelCell` | Return the `ExcelCell` at 0-based row/column |
370
+ | `col` | `(c: number) => ExcelCol` | Return the `ExcelCol` at 0-based column index |
371
+ | `getRange` | `() => Promise<ExcelAddressRangePoint>` | Return the used data range |
372
+ | `getCells` | `() => Promise<ExcelCell[][]>` | Return all cells as a 2D array (row-major) |
373
+ | `copyRow` | `(srcR: number, targetR: number) => Promise<void>` | Copy source row to target row (overwrite) |
374
+ | `copyRowStyle` | `(srcR: number, targetR: number) => Promise<void>` | Copy style from source row to target row |
375
+ | `copyCell` | `(srcAddr: ExcelAddressPoint, targetAddr: ExcelAddressPoint) => Promise<void>` | Copy a single cell |
376
+ | `copyCellStyle` | `(srcAddr: ExcelAddressPoint, targetAddr: ExcelAddressPoint) => Promise<void>` | Copy style from source cell to target cell |
377
+ | `insertCopyRow` | `(srcR: number, targetR: number) => Promise<void>` | Insert-copy source row at target position (shifts rows down) |
378
+ | `getDataTable` | `(opt?) => Promise<Record<string, ExcelValueType>[]>` | Read worksheet as a header-keyed record array |
379
+ | `setDataMatrix` | `(matrix: ExcelValueType[][]) => Promise<void>` | Write a 2D array to the worksheet |
380
+ | `setRecords` | `(records: Record<string, ExcelValueType>[]) => Promise<void>` | Write records with auto-generated header row |
381
+ | `setZoom` | `(percent: number) => Promise<void>` | Set the worksheet zoom level |
382
+ | `freezeAt` | `(point: { r?: number; c?: number }) => Promise<void>` | Set freeze panes |
383
+ | `addImage` | `(opts) => Promise<void>` | Insert an image into the worksheet |
384
+
385
+ **`getDataTable` options**
386
+
387
+ ```typescript
388
+ {
389
+ headerRowIndex?: number; // Row index of the header row (default: first row in range)
390
+ checkEndColIndex?: number; // Column whose empty value signals end of data
391
+ usableHeaderNameFn?: (headerName: string) => boolean; // Filter which headers to include
392
+ }
393
+ ```
394
+
395
+ **`addImage` options**
396
+
397
+ ```typescript
398
+ {
399
+ bytes: Bytes; // Image binary data (Uint8Array)
400
+ ext: string; // File extension, e.g. "png", "jpg"
401
+ from: { r: number; c: number; rOff?: number | string; cOff?: number | string }; // Start cell (0-based), optional EMU offset
402
+ to?: { r: number; c: number; rOff?: number | string; cOff?: number | string }; // End cell (optional; defaults to from.r+1, from.c+1)
403
+ }
404
+ ```
405
+
406
+ ---
407
+
408
+ ### `ExcelRow`
409
+
410
+ Represents a row in a worksheet. Provides access to individual cells and all cells in the row.
411
+
412
+ ```typescript
413
+ import { ExcelRow } from "@simplysm/excel";
414
+
415
+ // Obtained from ExcelWorksheet
416
+ const row = ws.row(0);
417
+
418
+ // Access a cell by column index (0-based)
419
+ const cell = row.cell(2); // column C
420
+
421
+ // Get all cells in the row (within used range)
422
+ const cells = await row.getCells();
423
+ ```
424
+
425
+ **Methods**
426
+
427
+ | Method | Signature | Description |
428
+ |--------|-----------|-------------|
429
+ | `cell` | `(c: number) => ExcelCell` | Return the `ExcelCell` at the given 0-based column index |
430
+ | `getCells` | `() => Promise<ExcelCell[]>` | Return all cells in the row within the worksheet's used range |
431
+
432
+ ---
433
+
434
+ ### `ExcelCol`
435
+
436
+ Represents a column in a worksheet. Provides access to individual cells, all cells in the column, and column width configuration.
437
+
438
+ ```typescript
439
+ import { ExcelCol } from "@simplysm/excel";
440
+
441
+ // Obtained from ExcelWorksheet
442
+ const col = ws.col(0); // column A
443
+
444
+ // Access a cell by row index (0-based)
445
+ const cell = col.cell(3); // row 3
446
+
447
+ // Get all cells in the column (within used range)
448
+ const cells = await col.getCells();
449
+
450
+ // Set column width
451
+ await col.setWidth(20);
452
+ ```
453
+
454
+ **Methods**
455
+
456
+ | Method | Signature | Description |
457
+ |--------|-----------|-------------|
458
+ | `cell` | `(r: number) => ExcelCell` | Return the `ExcelCell` at the given 0-based row index |
459
+ | `getCells` | `() => Promise<ExcelCell[]>` | Return all cells in the column within the worksheet's used range |
460
+ | `setWidth` | `(size: number) => Promise<void>` | Set the column width |
461
+
462
+ ---
463
+
464
+ ### `ExcelCell`
465
+
466
+ Represents a single cell. Provides value read/write, formula access, style configuration, and cell merge.
467
+
468
+ All methods are `async` because XML sub-files (SharedStrings, Styles) are loaded lazily on demand.
469
+
470
+ ```typescript
471
+ import { ExcelCell } from "@simplysm/excel";
472
+
473
+ // Obtained from ExcelWorksheet, ExcelRow, or ExcelCol
474
+ const cell = ws.cell(0, 0); // A1
475
+
476
+ // Cell address (0-based)
477
+ console.log(cell.addr); // { r: 0, c: 0 }
478
+
479
+ // Read/write values
480
+ await cell.setValue("Hello");
481
+ await cell.setValue(42);
482
+ await cell.setValue(true);
483
+ await cell.setValue(new DateOnly(Date.now()));
484
+ await cell.setValue(new DateTime(Date.now()));
485
+ await cell.setValue(new Time(Date.now()));
486
+ await cell.setValue(undefined); // deletes the cell
487
+
488
+ const value = await cell.getValue(); // ExcelValueType
489
+
490
+ // Formulas
491
+ await cell.setFormula("=SUM(A1:A10)");
492
+ await cell.setFormula(undefined); // removes formula
493
+ const formula = await cell.getFormula();
494
+
495
+ // Style
496
+ await cell.setStyle({
497
+ background: "00FFFF00", // ARGB 8-digit hex (yellow)
498
+ border: ["left", "right", "top", "bottom"],
499
+ horizontalAlign: "center",
500
+ verticalAlign: "center",
501
+ numberFormat: "number",
502
+ });
503
+
504
+ // Raw style ID (advanced use)
505
+ const styleId = await cell.getStyleId();
506
+ await cell.setStyleId("3");
507
+
508
+ // Merge cells: merge from this cell (A1) to C3
509
+ await cell.merge(2, 2); // end row=2, end col=2
510
+ ```
511
+
512
+ **Properties**
513
+
514
+ | Property | Type | Description |
515
+ |----------|------|-------------|
516
+ | `addr` | `ExcelAddressPoint` | Cell address (0-based `{ r, c }`) |
517
+
518
+ **Methods**
519
+
520
+ | Method | Signature | Description |
521
+ |--------|-----------|-------------|
522
+ | `getValue` | `() => Promise<ExcelValueType>` | Read the cell value |
523
+ | `setValue` | `(val: ExcelValueType) => Promise<void>` | Write the cell value (`undefined` deletes the cell) |
524
+ | `getFormula` | `() => Promise<string \| undefined>` | Read the cell formula |
525
+ | `setFormula` | `(val: string \| undefined) => Promise<void>` | Write the cell formula (`undefined` removes it) |
526
+ | `getStyleId` | `() => Promise<string \| undefined>` | Read the raw style ID |
527
+ | `setStyleId` | `(styleId: string \| undefined) => Promise<void>` | Set the raw style ID |
528
+ | `setStyle` | `(opts: ExcelStyleOptions) => Promise<void>` | Apply style options to the cell |
529
+ | `merge` | `(r: number, c: number) => Promise<void>` | Merge from this cell to the given end coordinates (0-based) |
530
+
531
+ ---
532
+
533
+ ## Wrapper Classes
534
+
535
+ ### `ExcelWrapper`
536
+
537
+ A Zod schema-based high-level wrapper that provides type-safe reading and writing of Excel files. Use `.describe()` on schema fields to specify the Excel column header name.
30
538
 
31
539
  ```typescript
32
540
  import { ExcelWrapper } from "@simplysm/excel";
@@ -35,27 +543,41 @@ import { z } from "zod";
35
543
  const schema = z.object({
36
544
  name: z.string().describe("Name"),
37
545
  age: z.number().describe("Age"),
546
+ joinDate: z.instanceof(DateOnly).optional().describe("Join Date"),
38
547
  });
39
548
 
40
549
  const wrapper = new ExcelWrapper(schema);
41
550
 
42
- // Read
551
+ // Read Excel file into typed records
43
552
  const records = await wrapper.read(fileBytes);
553
+ const records2 = await wrapper.read(fileBytes, "Sheet1");
554
+ const records3 = await wrapper.read(fileBytes, 0, { excludes: ["age"] });
44
555
 
45
- // Write
556
+ // Write records to Excel workbook
46
557
  await using wb = await wrapper.write("Members", records);
47
- const output = await wb.getBytes();
558
+ const bytes = await wb.toBytes();
559
+
560
+ // Write with excludes
561
+ await using wb2 = await wrapper.write("Members", records, { excludes: ["joinDate"] });
48
562
  ```
49
563
 
50
- ## Modules
564
+ **Constructor**
565
+
566
+ ```typescript
567
+ constructor(schema: TSchema extends z.ZodObject<z.ZodRawShape>)
568
+ ```
569
+
570
+ **Methods**
571
+
572
+ | Method | Signature | Description |
573
+ |--------|-----------|-------------|
574
+ | `read` | `(file: Bytes \| Blob, wsNameOrIndex?: string \| number, options?: { excludes?: (keyof TSchema)[] }) => Promise<z.infer<TSchema>[]>` | Read an Excel file into a typed record array |
575
+ | `write` | `(wsName: string, records: Partial<z.infer<TSchema>>[], options?: { excludes?: (keyof TSchema)[] }) => Promise<ExcelWorkbook>` | Write records to a new `ExcelWorkbook` (caller manages lifecycle) |
576
+
577
+ **Behavior Notes**
51
578
 
52
- | Module | Description | Details |
53
- |--------|-------------|---------|
54
- | `ExcelWorkbook` | Top-level workbook handle; open/create/export `.xlsx` files | [docs/workbook.md](docs/workbook.md) |
55
- | `ExcelWorksheet` | Single worksheet; cell access, copy, data import/export, images | [docs/workbook.md](docs/workbook.md) |
56
- | `ExcelRow` | Row handle; access cells in a row | [docs/workbook.md](docs/workbook.md) |
57
- | `ExcelCol` | Column handle; access cells, set width | [docs/workbook.md](docs/workbook.md) |
58
- | `ExcelCell` | Single cell; read/write values, formulas, styles, merge | [docs/workbook.md](docs/workbook.md) |
59
- | `ExcelUtils` | Static helpers for address conversion and date/format conversion | [docs/utils.md](docs/utils.md) |
60
- | `ExcelWrapper` | Zod schema-based type-safe read/write | [docs/wrapper.md](docs/wrapper.md) |
61
- | Types | Value, address, style, and XML data type definitions | [docs/types.md](docs/types.md) |
579
+ - Header names are taken from Zod field `.describe()` values; if not set, the field key is used.
580
+ - `read` throws if no data rows are found after the header row.
581
+ - `read` validates each row with the Zod schema and throws on validation failure.
582
+ - `write` automatically applies borders to all data cells, highlights required (non-optional, non-boolean) header cells with a yellow background (`"00FFFF00"`), sets zoom to 85%, and freezes the first row.
583
+ - The `ExcelWorkbook` returned by `write` must be closed by the caller (`await using` or `.close()`).
@@ -7,7 +7,7 @@ import type { ExcelAddressPoint, ExcelStyleOptions, ExcelValueType } from "./typ
7
7
  * @remarks
8
8
  * ## Async Method Design
9
9
  *
10
- * Why all cell methods like `getVal()`, `setVal()` are `async`:
10
+ * Why all cell methods like `getValue()`, `setValue()` are `async`:
11
11
  * - Only the XML needed for the cell type is selectively loaded
12
12
  * - String cell: loads SharedStrings.xml
13
13
  * - Number cell: does not load SharedStrings
@@ -29,9 +29,9 @@ export declare class ExcelCell {
29
29
  /** Return cell formula */
30
30
  getFormula(): Promise<string | undefined>;
31
31
  /** Set cell value (undefined: delete cell) */
32
- setVal(val: ExcelValueType): Promise<void>;
32
+ setValue(val: ExcelValueType): Promise<void>;
33
33
  /** Return cell value */
34
- getVal(): Promise<ExcelValueType>;
34
+ getValue(): Promise<ExcelValueType>;
35
35
  /**
36
36
  * Merge cells from current cell to the specified end coordinates
37
37
  * @param r End row index of merge (0-based)
@@ -48,7 +48,7 @@ export declare class ExcelCell {
48
48
  /**
49
49
  * Set cell style
50
50
  * @param opts Style options
51
- * @param opts.background Background color (ARGB format, 8-digit hex. e.g. "FFFF0000")
51
+ * @param opts.background Background color (ARGB format, 8-digit hex. e.g. "00FF0000")
52
52
  * @param opts.border Border position array (e.g. ["left", "right", "top", "bottom"])
53
53
  * @param opts.horizontalAlign Horizontal alignment ("left", "center", "right")
54
54
  * @param opts.verticalAlign Vertical alignment ("top", "center", "bottom")
@@ -1 +1 @@
1
- {"version":3,"file":"excel-cell.d.ts","sourceRoot":"","sources":["..\\src\\excel-cell.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAapF;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,SAAS;IAKlB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,EAAE;IACnB,OAAO,CAAC,QAAQ,CAAC,EAAE;IAPrB,8CAA8C;IAC9C,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;gBAGd,SAAS,EAAE,QAAQ,EACnB,eAAe,EAAE,MAAM,EACvB,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM;IAO7B,sDAAsD;IAChD,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAWxD,0BAA0B;IACpB,UAAU,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAK/C,8CAA8C;IACxC,MAAM,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAwChD,wBAAwB;IAClB,MAAM,IAAI,OAAO,CAAC,cAAc,CAAC;IAwFvC;;;;;;;OAOG;IACG,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAShD,2BAA2B;IACrB,UAAU,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAK/C,wBAAwB;IAClB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAK5D;;;;;;;;OAQG;IACG,QAAQ,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;YAiCxC,WAAW;YAKX,UAAU;YAIV,iBAAiB;YAYjB,YAAY;YAIZ,UAAU;YAIV,aAAa;YAIb,aAAa;YAIb,kBAAkB;YAqBlB,qBAAqB;CAsBpC"}
1
+ {"version":3,"file":"excel-cell.d.ts","sourceRoot":"","sources":["..\\src\\excel-cell.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,mBAAmB,CAAC;AAClD,OAAO,KAAK,EAAE,iBAAiB,EAAE,iBAAiB,EAAE,cAAc,EAAE,MAAM,SAAS,CAAC;AAYpF;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,SAAS;IAKlB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,eAAe;IAChC,OAAO,CAAC,QAAQ,CAAC,EAAE;IACnB,OAAO,CAAC,QAAQ,CAAC,EAAE;IAPrB,8CAA8C;IAC9C,QAAQ,CAAC,IAAI,EAAE,iBAAiB,CAAC;gBAGd,SAAS,EAAE,QAAQ,EACnB,eAAe,EAAE,MAAM,EACvB,EAAE,EAAE,MAAM,EACV,EAAE,EAAE,MAAM;IAO7B,sDAAsD;IAChD,UAAU,CAAC,GAAG,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAWxD,0BAA0B;IACpB,UAAU,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAK/C,8CAA8C;IACxC,QAAQ,CAAC,GAAG,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC;IAwClD,wBAAwB;IAClB,QAAQ,IAAI,OAAO,CAAC,cAAc,CAAC;IAwFzC;;;;;;;OAOG;IACG,KAAK,CAAC,CAAC,EAAE,MAAM,EAAE,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAShD,2BAA2B;IACrB,UAAU,IAAI,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC;IAK/C,wBAAwB;IAClB,UAAU,CAAC,OAAO,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC;IAK5D;;;;;;;;OAQG;IACG,QAAQ,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,IAAI,CAAC;YAiCxC,WAAW;YAKX,UAAU;YAIV,iBAAiB;YAYjB,YAAY;YAIZ,UAAU;YAIV,aAAa;YAIb,aAAa;YAIb,kBAAkB;YAqBlB,qBAAqB;CAsBpC"}
@@ -1,9 +1,8 @@
1
1
  import {
2
2
  DateOnly,
3
3
  DateTime,
4
- numParseFloat,
5
- numParseInt,
6
- strIsNullOrEmpty,
4
+ num,
5
+ str,
7
6
  Time
8
7
  } from "@simplysm/core-common";
9
8
  import { ExcelXmlSharedString as ExcelXmlSharedStringClass } from "./xml/excel-xml-shared-string.js";
@@ -37,7 +36,7 @@ class ExcelCell {
37
36
  return wsData.getCellFormula(this.addr);
38
37
  }
39
38
  /** Set cell value (undefined: delete cell) */
40
- async setVal(val) {
39
+ async setValue(val) {
41
40
  if (val === void 0) {
42
41
  await this._deleteCell(this.addr);
43
42
  } else if (typeof val === "string") {
@@ -75,16 +74,16 @@ class ExcelCell {
75
74
  }
76
75
  }
77
76
  /** Return cell value */
78
- async getVal() {
77
+ async getValue() {
79
78
  const wsData = await this._getWsData();
80
79
  const cellVal = wsData.getCellVal(this.addr);
81
- if (cellVal === void 0 || strIsNullOrEmpty(cellVal)) {
80
+ if (cellVal === void 0 || str.isNullOrEmpty(cellVal)) {
82
81
  return void 0;
83
82
  }
84
83
  const cellType = wsData.getCellType(this.addr);
85
84
  if (cellType === "s") {
86
85
  const ssData = await this._getOrCreateSsData();
87
- const ssId = numParseInt(cellVal);
86
+ const ssId = num.parseInt(cellVal);
88
87
  if (ssId == null) {
89
88
  throw new Error(
90
89
  `[${ExcelUtils.stringifyAddr(this.addr)}] Failed to parse SharedString ID: ${cellVal}`
@@ -121,7 +120,7 @@ class ExcelCell {
121
120
  if (numFmtCode !== void 0) {
122
121
  numFmt = ExcelUtils.convertNumFmtCodeToName(numFmtCode);
123
122
  } else {
124
- const numFmtIdNum = numParseInt(numFmtId);
123
+ const numFmtIdNum = num.parseInt(numFmtId);
125
124
  if (numFmtIdNum == null) {
126
125
  throw new Error(
127
126
  `[${ExcelUtils.stringifyAddr(this.addr)}] Failed to parse numFmtId: ${numFmtId}`
@@ -134,7 +133,7 @@ class ExcelCell {
134
133
  } else if (numFmt === "string") {
135
134
  return cellVal;
136
135
  } else {
137
- const dateNum = numParseFloat(cellVal);
136
+ const dateNum = num.parseFloat(cellVal);
138
137
  if (dateNum == null) {
139
138
  throw new Error(
140
139
  `[${ExcelUtils.stringifyAddr(this.addr)}] Failed to parse date number: ${cellVal}`
@@ -180,7 +179,7 @@ class ExcelCell {
180
179
  /**
181
180
  * Set cell style
182
181
  * @param opts Style options
183
- * @param opts.background Background color (ARGB format, 8-digit hex. e.g. "FFFF0000")
182
+ * @param opts.background Background color (ARGB format, 8-digit hex. e.g. "00FF0000")
184
183
  * @param opts.border Border position array (e.g. ["left", "right", "top", "bottom"])
185
184
  * @param opts.horizontalAlign Horizontal alignment ("left", "center", "right")
186
185
  * @param opts.verticalAlign Vertical alignment ("top", "center", "bottom")