@simplysm/excel 13.0.69 → 13.0.71
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 +19 -516
- package/dist/excel-cell.d.ts +28 -28
- package/dist/excel-cell.d.ts.map +1 -1
- package/dist/excel-cell.js +24 -24
- package/dist/excel-cell.js.map +1 -1
- package/dist/excel-col.d.ts +4 -4
- package/dist/excel-col.d.ts.map +1 -1
- package/dist/excel-col.js +3 -3
- package/dist/excel-row.d.ts +3 -3
- package/dist/excel-row.d.ts.map +1 -1
- package/dist/excel-row.js +2 -2
- package/dist/excel-workbook.d.ts +23 -23
- package/dist/excel-workbook.d.ts.map +1 -1
- package/dist/excel-workbook.js +15 -15
- package/dist/excel-workbook.js.map +1 -1
- package/dist/excel-worksheet.d.ts +32 -32
- package/dist/excel-worksheet.d.ts.map +1 -1
- package/dist/excel-worksheet.js +32 -32
- package/dist/excel-worksheet.js.map +1 -1
- package/dist/excel-wrapper.d.ts +7 -7
- package/dist/excel-wrapper.js +7 -7
- package/dist/excel-wrapper.js.map +1 -1
- package/dist/types.d.ts +16 -16
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/excel-utils.d.ts +23 -23
- package/dist/utils/excel-utils.d.ts.map +1 -1
- package/dist/utils/excel-utils.js +25 -25
- package/dist/utils/excel-utils.js.map +1 -1
- package/dist/utils/zip-cache.d.ts +6 -6
- package/dist/xml/excel-xml-content-type.d.ts +2 -2
- package/dist/xml/excel-xml-drawing.d.ts +2 -2
- package/dist/xml/excel-xml-relationship.d.ts +2 -2
- package/dist/xml/excel-xml-relationship.js +1 -1
- package/dist/xml/excel-xml-relationship.js.map +1 -1
- package/dist/xml/excel-xml-shared-string.d.ts +2 -2
- package/dist/xml/excel-xml-style.d.ts +2 -2
- package/dist/xml/excel-xml-style.js +6 -6
- package/dist/xml/excel-xml-style.js.map +1 -1
- package/dist/xml/excel-xml-unknown.d.ts +2 -2
- package/dist/xml/excel-xml-workbook.d.ts +2 -2
- package/dist/xml/excel-xml-workbook.js +1 -1
- package/dist/xml/excel-xml-workbook.js.map +1 -1
- package/dist/xml/excel-xml-worksheet.d.ts +6 -6
- package/dist/xml/excel-xml-worksheet.d.ts.map +1 -1
- package/dist/xml/excel-xml-worksheet.js +9 -8
- package/dist/xml/excel-xml-worksheet.js.map +1 -1
- package/package.json +6 -5
- package/src/excel-cell.ts +35 -35
- package/src/excel-col.ts +4 -4
- package/src/excel-row.ts +3 -3
- package/src/excel-workbook.ts +30 -30
- package/src/excel-worksheet.ts +47 -47
- package/src/excel-wrapper.ts +18 -18
- package/src/index.ts +3 -3
- package/src/types.ts +15 -17
- package/src/utils/excel-utils.ts +38 -38
- package/src/utils/zip-cache.ts +6 -6
- package/src/xml/excel-xml-content-type.ts +3 -3
- package/src/xml/excel-xml-drawing.ts +2 -2
- package/src/xml/excel-xml-relationship.ts +3 -3
- package/src/xml/excel-xml-shared-string.ts +2 -2
- package/src/xml/excel-xml-style.ts +11 -11
- package/src/xml/excel-xml-unknown.ts +2 -2
- package/src/xml/excel-xml-workbook.ts +5 -5
- package/src/xml/excel-xml-worksheet.ts +44 -43
- package/tests/excel-cell.spec.ts +448 -0
- package/tests/excel-col.spec.ts +112 -0
- package/tests/excel-row.spec.ts +71 -0
- package/tests/excel-workbook.spec.ts +219 -0
- package/tests/excel-worksheet.spec.ts +389 -0
- package/tests/excel-wrapper.spec.ts +296 -0
- package/tests/fixtures/logo.png +0 -0
- package/tests/fixtures//354/264/210/352/270/260/355/231/224.xlsx +0 -0
- package/tests/image-insert.spec.ts +190 -0
- package/tests/utils/excel-utils.spec.ts +242 -0
package/src/excel-worksheet.ts
CHANGED
|
@@ -14,8 +14,8 @@ import type { ExcelXmlWorkbook } from "./xml/excel-xml-workbook";
|
|
|
14
14
|
import type { ExcelXmlWorksheet } from "./xml/excel-xml-worksheet";
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
17
|
+
* Class representing an Excel worksheet.
|
|
18
|
+
* Provides cell access, row/column copying, data table processing, and image insertion.
|
|
19
19
|
*/
|
|
20
20
|
export class ExcelWorksheet {
|
|
21
21
|
private readonly _rowMap = new Map<number, ExcelRow>();
|
|
@@ -29,17 +29,17 @@ export class ExcelWorksheet {
|
|
|
29
29
|
|
|
30
30
|
//#region Name Methods
|
|
31
31
|
|
|
32
|
-
/**
|
|
32
|
+
/** Return worksheet name */
|
|
33
33
|
async getName(): Promise<string> {
|
|
34
34
|
const wbXmlData = await this._getWbData();
|
|
35
35
|
const name = wbXmlData.getWorksheetNameById(this._relId);
|
|
36
36
|
if (name == null) {
|
|
37
|
-
throw new Error(
|
|
37
|
+
throw new Error(`Cannot find name for worksheet ID ${this._relId}`);
|
|
38
38
|
}
|
|
39
39
|
return name;
|
|
40
40
|
}
|
|
41
41
|
|
|
42
|
-
/**
|
|
42
|
+
/** Rename worksheet */
|
|
43
43
|
async setName(newName: string): Promise<void> {
|
|
44
44
|
const wbXmlData = await this._getWbData();
|
|
45
45
|
wbXmlData.setWorksheetNameById(this._relId, newName);
|
|
@@ -49,17 +49,17 @@ export class ExcelWorksheet {
|
|
|
49
49
|
|
|
50
50
|
//#region Cell Access Methods
|
|
51
51
|
|
|
52
|
-
/**
|
|
52
|
+
/** Return row object (0-based) */
|
|
53
53
|
row(r: number): ExcelRow {
|
|
54
54
|
return this._rowMap.getOrCreate(r, new ExcelRow(this._zipCache, this._targetFileName, r));
|
|
55
55
|
}
|
|
56
56
|
|
|
57
|
-
/**
|
|
57
|
+
/** Return cell object (0-based row/column) */
|
|
58
58
|
cell(r: number, c: number): ExcelCell {
|
|
59
59
|
return this.row(r).cell(c);
|
|
60
60
|
}
|
|
61
61
|
|
|
62
|
-
/**
|
|
62
|
+
/** Return column object (0-based) */
|
|
63
63
|
col(c: number): ExcelCol {
|
|
64
64
|
return this._colMap.getOrCreate(c, new ExcelCol(this._zipCache, this._targetFileName, c));
|
|
65
65
|
}
|
|
@@ -68,7 +68,7 @@ export class ExcelWorksheet {
|
|
|
68
68
|
|
|
69
69
|
//#region Copy Methods
|
|
70
70
|
|
|
71
|
-
/**
|
|
71
|
+
/** Copy style from source row to target row */
|
|
72
72
|
async copyRowStyle(srcR: number, targetR: number): Promise<void> {
|
|
73
73
|
const range = await this.getRange();
|
|
74
74
|
|
|
@@ -77,7 +77,7 @@ export class ExcelWorksheet {
|
|
|
77
77
|
}
|
|
78
78
|
}
|
|
79
79
|
|
|
80
|
-
/**
|
|
80
|
+
/** Copy style from source cell to target cell */
|
|
81
81
|
async copyCellStyle(srcAddr: ExcelAddressPoint, targetAddr: ExcelAddressPoint): Promise<void> {
|
|
82
82
|
const wsData = await this._getWsData();
|
|
83
83
|
|
|
@@ -87,36 +87,36 @@ export class ExcelWorksheet {
|
|
|
87
87
|
}
|
|
88
88
|
}
|
|
89
89
|
|
|
90
|
-
/**
|
|
90
|
+
/** Copy source row to target row (overwrite) */
|
|
91
91
|
async copyRow(srcR: number, targetR: number): Promise<void> {
|
|
92
92
|
const wsData = await this._getWsData();
|
|
93
93
|
wsData.copyRow(srcR, targetR);
|
|
94
94
|
}
|
|
95
95
|
|
|
96
|
-
/**
|
|
96
|
+
/** Copy source cell to target cell */
|
|
97
97
|
async copyCell(srcAddr: ExcelAddressPoint, targetAddr: ExcelAddressPoint): Promise<void> {
|
|
98
98
|
const wsData = await this._getWsData();
|
|
99
99
|
wsData.copyCell(srcAddr, targetAddr);
|
|
100
100
|
}
|
|
101
101
|
|
|
102
102
|
/**
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
* @param srcR
|
|
106
|
-
* @param targetR
|
|
103
|
+
* Insert-copy the source row at the target position.
|
|
104
|
+
* Existing rows at and below the target are shifted down by one.
|
|
105
|
+
* @param srcR Source row index to copy (0-based)
|
|
106
|
+
* @param targetR Target row index to insert at (0-based)
|
|
107
107
|
*/
|
|
108
108
|
async insertCopyRow(srcR: number, targetR: number): Promise<void> {
|
|
109
109
|
const wsData = await this._getWsData();
|
|
110
110
|
const range = wsData.range;
|
|
111
111
|
|
|
112
|
-
//
|
|
112
|
+
// Increment row index by 1 for all merge cells at or below targetR
|
|
113
113
|
const mergeCells = wsData.getMergeCells();
|
|
114
114
|
for (const mc of mergeCells) {
|
|
115
115
|
if (mc.s.r >= targetR) mc.s.r++;
|
|
116
116
|
if (mc.e.r >= targetR) mc.e.r++;
|
|
117
117
|
}
|
|
118
118
|
|
|
119
|
-
// srcR >= targetR
|
|
119
|
+
// When srcR >= targetR, adjust for the shifted position of srcR
|
|
120
120
|
const adjustedSrcR = srcR >= targetR ? srcR + 1 : srcR;
|
|
121
121
|
|
|
122
122
|
for (let r = range.e.r; r >= targetR; r--) {
|
|
@@ -130,13 +130,13 @@ export class ExcelWorksheet {
|
|
|
130
130
|
|
|
131
131
|
//#region Range Methods
|
|
132
132
|
|
|
133
|
-
/**
|
|
133
|
+
/** Return data range of the worksheet */
|
|
134
134
|
async getRange(): Promise<ExcelAddressRangePoint> {
|
|
135
135
|
const xml = await this._getWsData();
|
|
136
136
|
return xml.range;
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
/**
|
|
139
|
+
/** Return all cells as a 2D array */
|
|
140
140
|
async getCells(): Promise<ExcelCell[][]> {
|
|
141
141
|
const xml = await this._getWsData();
|
|
142
142
|
const range = xml.range;
|
|
@@ -154,10 +154,10 @@ export class ExcelWorksheet {
|
|
|
154
154
|
//#region Data Methods
|
|
155
155
|
|
|
156
156
|
/**
|
|
157
|
-
*
|
|
158
|
-
* @param opt.headerRowIndex
|
|
159
|
-
* @param opt.checkEndColIndex
|
|
160
|
-
* @param opt.usableHeaderNameFn
|
|
157
|
+
* Return worksheet data as a table (record array).
|
|
158
|
+
* @param opt.headerRowIndex Header row index (default: first row)
|
|
159
|
+
* @param opt.checkEndColIndex Column index to determine data end. Data ends when this column is empty.
|
|
160
|
+
* @param opt.usableHeaderNameFn Function to filter usable headers
|
|
161
161
|
*/
|
|
162
162
|
async getDataTable(opt?: {
|
|
163
163
|
headerRowIndex?: number;
|
|
@@ -201,8 +201,8 @@ export class ExcelWorksheet {
|
|
|
201
201
|
}
|
|
202
202
|
|
|
203
203
|
/**
|
|
204
|
-
*
|
|
205
|
-
* @param matrix
|
|
204
|
+
* Write 2D array data to the worksheet
|
|
205
|
+
* @param matrix 2D array data (row-major, index 0 is the first row)
|
|
206
206
|
*/
|
|
207
207
|
async setDataMatrix(matrix: ExcelValueType[][]): Promise<void> {
|
|
208
208
|
for (let r = 0; r < matrix.length; r++) {
|
|
@@ -213,8 +213,8 @@ export class ExcelWorksheet {
|
|
|
213
213
|
}
|
|
214
214
|
|
|
215
215
|
/**
|
|
216
|
-
*
|
|
217
|
-
* @param records
|
|
216
|
+
* Write record array to the worksheet
|
|
217
|
+
* @param records Record array. Headers are auto-generated in the first row, data follows in subsequent rows.
|
|
218
218
|
*/
|
|
219
219
|
async setRecords(records: Record<string, ExcelValueType>[]): Promise<void> {
|
|
220
220
|
const headers = records
|
|
@@ -237,7 +237,7 @@ export class ExcelWorksheet {
|
|
|
237
237
|
|
|
238
238
|
//#region View Methods
|
|
239
239
|
|
|
240
|
-
/**
|
|
240
|
+
/** Set worksheet zoom scale (percent) */
|
|
241
241
|
async setZoom(percent: number): Promise<void> {
|
|
242
242
|
const wbXml = await this._getWbData();
|
|
243
243
|
wbXml.initializeView();
|
|
@@ -246,7 +246,7 @@ export class ExcelWorksheet {
|
|
|
246
246
|
wsXml.setZoom(percent);
|
|
247
247
|
}
|
|
248
248
|
|
|
249
|
-
/**
|
|
249
|
+
/** Set freeze panes for rows/columns */
|
|
250
250
|
async setFix(point: { r?: number; c?: number }): Promise<void> {
|
|
251
251
|
const wbXml = await this._getWbData();
|
|
252
252
|
wbXml.initializeView();
|
|
@@ -260,11 +260,11 @@ export class ExcelWorksheet {
|
|
|
260
260
|
//#region Image Methods
|
|
261
261
|
|
|
262
262
|
/**
|
|
263
|
-
*
|
|
264
|
-
* @param opts.bytes
|
|
265
|
-
* @param opts.ext
|
|
266
|
-
* @param opts.from
|
|
267
|
-
* @param opts.to
|
|
263
|
+
* Insert an image into the worksheet.
|
|
264
|
+
* @param opts.bytes Image binary data
|
|
265
|
+
* @param opts.ext Image extension (png, jpg, etc.)
|
|
266
|
+
* @param opts.from Image start position (0-based row/column index, rOff/cOff in EMU offset)
|
|
267
|
+
* @param opts.to Image end position (if omitted, inserted at from position with original size)
|
|
268
268
|
*/
|
|
269
269
|
async addImage(opts: {
|
|
270
270
|
bytes: Bytes;
|
|
@@ -274,10 +274,10 @@ export class ExcelWorksheet {
|
|
|
274
274
|
}): Promise<void> {
|
|
275
275
|
const mimeType = mime.getType(opts.ext);
|
|
276
276
|
if (mimeType == null) {
|
|
277
|
-
throw new Error(
|
|
277
|
+
throw new Error(`Cannot determine MIME type for extension '${opts.ext}'`);
|
|
278
278
|
}
|
|
279
279
|
|
|
280
|
-
// 1. media
|
|
280
|
+
// 1. Determine media filename and save
|
|
281
281
|
let mediaIndex = 1;
|
|
282
282
|
while ((await this._zipCache.get(`xl/media/image${mediaIndex}.${opts.ext}`)) !== undefined) {
|
|
283
283
|
mediaIndex++;
|
|
@@ -285,16 +285,16 @@ export class ExcelWorksheet {
|
|
|
285
285
|
const mediaPath = `xl/media/image${mediaIndex}.${opts.ext}`;
|
|
286
286
|
this._zipCache.set(mediaPath, opts.bytes);
|
|
287
287
|
|
|
288
|
-
// 2. [Content_Types].xml
|
|
288
|
+
// 2. Update [Content_Types].xml
|
|
289
289
|
const typeXml = (await this._zipCache.get("[Content_Types].xml")) as ExcelXmlContentType;
|
|
290
290
|
typeXml.add(`/xl/media/image${mediaIndex}.${opts.ext}`, mimeType);
|
|
291
291
|
|
|
292
|
-
// 3.
|
|
292
|
+
// 3. Check for existing drawing in worksheet
|
|
293
293
|
const wsXml = await this._getWsData();
|
|
294
294
|
const sheetRelsPath = `xl/worksheets/_rels/${this._targetFileName}.rels`;
|
|
295
295
|
let sheetRels = (await this._zipCache.get(sheetRelsPath)) as ExcelXmlRelationship | undefined;
|
|
296
296
|
|
|
297
|
-
//
|
|
297
|
+
// Find existing drawing
|
|
298
298
|
let drawingIndex: number | undefined;
|
|
299
299
|
let drawingPath: string | undefined;
|
|
300
300
|
let drawing: ExcelXmlDrawing | undefined;
|
|
@@ -307,7 +307,7 @@ export class ExcelWorksheet {
|
|
|
307
307
|
"http://schemas.openxmlformats.org/officeDocument/2006/relationships/drawing",
|
|
308
308
|
);
|
|
309
309
|
if (existingDrawingRel != null) {
|
|
310
|
-
//
|
|
310
|
+
// Extract index from existing drawing path
|
|
311
311
|
const match = existingDrawingRel.$.Target.match(/drawing(\d+)\.xml$/);
|
|
312
312
|
if (match != null) {
|
|
313
313
|
drawingIndex = parseInt(match[1], 10);
|
|
@@ -320,7 +320,7 @@ export class ExcelWorksheet {
|
|
|
320
320
|
}
|
|
321
321
|
}
|
|
322
322
|
|
|
323
|
-
// 4. drawing
|
|
323
|
+
// 4. Create new drawing if none exists
|
|
324
324
|
if (drawingIndex == null || drawingPath == null || drawing == null) {
|
|
325
325
|
drawingIndex = 1;
|
|
326
326
|
while ((await this._zipCache.get(`xl/drawings/drawing${drawingIndex}.xml`)) !== undefined) {
|
|
@@ -329,10 +329,10 @@ export class ExcelWorksheet {
|
|
|
329
329
|
drawingPath = `xl/drawings/drawing${drawingIndex}.xml`;
|
|
330
330
|
drawing = new ExcelXmlDrawing();
|
|
331
331
|
|
|
332
|
-
// [Content_Types].xml
|
|
332
|
+
// Add drawing type to [Content_Types].xml
|
|
333
333
|
typeXml.add("/" + drawingPath, "application/vnd.openxmlformats-officedocument.drawing+xml");
|
|
334
334
|
|
|
335
|
-
// worksheet
|
|
335
|
+
// Add drawing to worksheet rels
|
|
336
336
|
sheetRels = sheetRels ?? new ExcelXmlRelationship();
|
|
337
337
|
const sheetRelNum = sheetRels.addAndGetId(
|
|
338
338
|
`../drawings/drawing${drawingIndex}.xml`,
|
|
@@ -341,7 +341,7 @@ export class ExcelWorksheet {
|
|
|
341
341
|
const drawingRelIdOnWorksheet = `rId${sheetRelNum}`;
|
|
342
342
|
this._zipCache.set(sheetRelsPath, sheetRels);
|
|
343
343
|
|
|
344
|
-
// worksheet XML
|
|
344
|
+
// Add drawing to worksheet XML
|
|
345
345
|
wsXml.data.worksheet.$["xmlns:r"] =
|
|
346
346
|
wsXml.data.worksheet.$["xmlns:r"] ??
|
|
347
347
|
"http://schemas.openxmlformats.org/officeDocument/2006/relationships";
|
|
@@ -350,7 +350,7 @@ export class ExcelWorksheet {
|
|
|
350
350
|
this._zipCache.set(`xl/worksheets/${this._targetFileName}`, wsXml);
|
|
351
351
|
}
|
|
352
352
|
|
|
353
|
-
// 5. drawing rels
|
|
353
|
+
// 5. Prepare drawing rels (create if not exists)
|
|
354
354
|
drawingRels = drawingRels ?? new ExcelXmlRelationship();
|
|
355
355
|
const mediaFileName = mediaPath.slice(3);
|
|
356
356
|
const drawingTarget = `../${mediaFileName}`;
|
|
@@ -360,7 +360,7 @@ export class ExcelWorksheet {
|
|
|
360
360
|
);
|
|
361
361
|
this._zipCache.set(`xl/drawings/_rels/drawing${drawingIndex}.xml.rels`, drawingRels);
|
|
362
362
|
|
|
363
|
-
// 6.
|
|
363
|
+
// 6. Add image to drawing
|
|
364
364
|
const blipRelId = `rId${relNum}`;
|
|
365
365
|
drawing.addPicture({
|
|
366
366
|
from: opts.from,
|
package/src/excel-wrapper.ts
CHANGED
|
@@ -13,18 +13,18 @@ import { ExcelWorkbook } from "./excel-workbook";
|
|
|
13
13
|
import type { ExcelValueType } from "./types";
|
|
14
14
|
|
|
15
15
|
/**
|
|
16
|
-
* Zod
|
|
16
|
+
* Zod schema-based Excel wrapper
|
|
17
17
|
*
|
|
18
|
-
*
|
|
18
|
+
* Infers type information from schema to provide type-safe read/write
|
|
19
19
|
*/
|
|
20
20
|
export class ExcelWrapper<TSchema extends z.ZodObject<z.ZodRawShape>> {
|
|
21
21
|
/**
|
|
22
|
-
* @param _schema Zod
|
|
22
|
+
* @param _schema Zod schema (defines record structure, use `.describe()` to specify Excel header names)
|
|
23
23
|
*/
|
|
24
24
|
constructor(private readonly _schema: TSchema) {}
|
|
25
25
|
|
|
26
26
|
/**
|
|
27
|
-
* Excel
|
|
27
|
+
* Read Excel file into record array
|
|
28
28
|
*/
|
|
29
29
|
async read(
|
|
30
30
|
file: Bytes | Blob,
|
|
@@ -46,7 +46,7 @@ export class ExcelWrapper<TSchema extends z.ZodObject<z.ZodRawShape>> {
|
|
|
46
46
|
|
|
47
47
|
if (rawData.length === 0) {
|
|
48
48
|
throw new Error(
|
|
49
|
-
`[${wsName}]
|
|
49
|
+
`[${wsName}] No data found in Excel file. (Expected headers: ${displayNames.join(", ")})`,
|
|
50
50
|
);
|
|
51
51
|
}
|
|
52
52
|
|
|
@@ -73,13 +73,13 @@ export class ExcelWrapper<TSchema extends z.ZodObject<z.ZodRawShape>> {
|
|
|
73
73
|
continue;
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
-
// Zod
|
|
76
|
+
// Validate with Zod schema
|
|
77
77
|
const parseResult = this._schema.safeParse(record);
|
|
78
78
|
if (!parseResult.success) {
|
|
79
79
|
const errors = parseResult.error.issues
|
|
80
80
|
.map((issue) => `${issue.path.join(".")}: ${issue.message}`)
|
|
81
81
|
.join(", ");
|
|
82
|
-
throw new Error(`[${wsName}]
|
|
82
|
+
throw new Error(`[${wsName}] Data validation failed: ${errors}`);
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
result.push(parseResult.data);
|
|
@@ -89,11 +89,11 @@ export class ExcelWrapper<TSchema extends z.ZodObject<z.ZodRawShape>> {
|
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
/**
|
|
92
|
-
*
|
|
92
|
+
* Record array to Excel workbook
|
|
93
93
|
*
|
|
94
94
|
* @remarks
|
|
95
|
-
*
|
|
96
|
-
* `await using
|
|
95
|
+
* The caller is responsible for managing the returned workbook's resources.
|
|
96
|
+
* Use `await using` or call `close()` after use.
|
|
97
97
|
*
|
|
98
98
|
* @example
|
|
99
99
|
* ```typescript
|
|
@@ -113,12 +113,12 @@ export class ExcelWrapper<TSchema extends z.ZodObject<z.ZodRawShape>> {
|
|
|
113
113
|
const keys = Object.keys(displayNameMap) as (keyof z.infer<TSchema>)[];
|
|
114
114
|
const headers = keys.map((key) => displayNameMap[key as string]);
|
|
115
115
|
|
|
116
|
-
//
|
|
116
|
+
// Write header row
|
|
117
117
|
for (let c = 0; c < headers.length; c++) {
|
|
118
118
|
await ws.cell(0, c).setVal(headers[c]);
|
|
119
119
|
}
|
|
120
120
|
|
|
121
|
-
//
|
|
121
|
+
// Write data rows
|
|
122
122
|
for (let r = 0; r < records.length; r++) {
|
|
123
123
|
for (let c = 0; c < keys.length; c++) {
|
|
124
124
|
const key = keys[c];
|
|
@@ -127,7 +127,7 @@ export class ExcelWrapper<TSchema extends z.ZodObject<z.ZodRawShape>> {
|
|
|
127
127
|
}
|
|
128
128
|
}
|
|
129
129
|
|
|
130
|
-
//
|
|
130
|
+
// Apply border style
|
|
131
131
|
for (let r = 0; r < records.length + 1; r++) {
|
|
132
132
|
for (let c = 0; c < keys.length; c++) {
|
|
133
133
|
await ws.cell(r, c).setStyle({
|
|
@@ -136,7 +136,7 @@ export class ExcelWrapper<TSchema extends z.ZodObject<z.ZodRawShape>> {
|
|
|
136
136
|
}
|
|
137
137
|
}
|
|
138
138
|
|
|
139
|
-
//
|
|
139
|
+
// Highlight required field headers (yellow)
|
|
140
140
|
const shape = this._schema.shape;
|
|
141
141
|
for (let c = 0; c < keys.length; c++) {
|
|
142
142
|
const fieldKey = keys[c] as string;
|
|
@@ -149,7 +149,7 @@ export class ExcelWrapper<TSchema extends z.ZodObject<z.ZodRawShape>> {
|
|
|
149
149
|
}
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
//
|
|
152
|
+
// View settings
|
|
153
153
|
await ws.setZoom(85);
|
|
154
154
|
await ws.setFix({ r: 0 });
|
|
155
155
|
|
|
@@ -198,7 +198,7 @@ export class ExcelWrapper<TSchema extends z.ZodObject<z.ZodRawShape>> {
|
|
|
198
198
|
return Boolean(rawValue);
|
|
199
199
|
}
|
|
200
200
|
|
|
201
|
-
// DateOnly, DateTime, Time
|
|
201
|
+
// DateOnly, DateTime, Time are handled via instanceof
|
|
202
202
|
if (rawValue instanceof DateOnly || rawValue instanceof DateTime || rawValue instanceof Time) {
|
|
203
203
|
return rawValue;
|
|
204
204
|
}
|
|
@@ -218,7 +218,7 @@ export class ExcelWrapper<TSchema extends z.ZodObject<z.ZodRawShape>> {
|
|
|
218
218
|
|
|
219
219
|
private _getDefaultForSchema(schema: z.ZodType): unknown {
|
|
220
220
|
if (schema instanceof ZodDefault) {
|
|
221
|
-
// ZodDefault.parse(undefined)
|
|
221
|
+
// ZodDefault.parse(undefined) returns the default value
|
|
222
222
|
return schema.parse(undefined);
|
|
223
223
|
}
|
|
224
224
|
|
|
@@ -226,7 +226,7 @@ export class ExcelWrapper<TSchema extends z.ZodObject<z.ZodRawShape>> {
|
|
|
226
226
|
return undefined;
|
|
227
227
|
}
|
|
228
228
|
|
|
229
|
-
//
|
|
229
|
+
// Default value for required boolean fields is false
|
|
230
230
|
const innerSchema = this._unwrapSchema(schema);
|
|
231
231
|
if (innerSchema instanceof ZodBoolean) {
|
|
232
232
|
return false;
|
package/src/index.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
//
|
|
1
|
+
// Types and utilities
|
|
2
2
|
export * from "./types";
|
|
3
3
|
export * from "./utils/excel-utils";
|
|
4
4
|
|
|
5
|
-
//
|
|
5
|
+
// Core classes
|
|
6
6
|
export * from "./excel-cell";
|
|
7
7
|
export * from "./excel-row";
|
|
8
8
|
export * from "./excel-col";
|
|
9
9
|
export * from "./excel-worksheet";
|
|
10
10
|
export * from "./excel-workbook";
|
|
11
11
|
|
|
12
|
-
//
|
|
12
|
+
// Wrapper classes
|
|
13
13
|
export * from "./excel-wrapper";
|
package/src/types.ts
CHANGED
|
@@ -148,9 +148,7 @@ export interface ExcelCellData {
|
|
|
148
148
|
v?: [string];
|
|
149
149
|
f?: [string];
|
|
150
150
|
is?: {
|
|
151
|
-
t?: {
|
|
152
|
-
_?: string;
|
|
153
|
-
}[];
|
|
151
|
+
t?: (string | { _?: string })[];
|
|
154
152
|
}[];
|
|
155
153
|
}
|
|
156
154
|
|
|
@@ -326,13 +324,13 @@ export type ExcelValueType = number | string | DateOnly | DateTime | Time | bool
|
|
|
326
324
|
export type ExcelNumberFormat = "number" | "string" | "DateOnly" | "DateTime" | "Time";
|
|
327
325
|
|
|
328
326
|
/**
|
|
329
|
-
* Excel
|
|
330
|
-
* - s:
|
|
331
|
-
* - b:
|
|
332
|
-
* - str:
|
|
333
|
-
* - n:
|
|
334
|
-
* - inlineStr:
|
|
335
|
-
* - e:
|
|
327
|
+
* Excel cell type
|
|
328
|
+
* - s: shared string (SharedString)
|
|
329
|
+
* - b: boolean
|
|
330
|
+
* - str: formula result string
|
|
331
|
+
* - n: number
|
|
332
|
+
* - inlineStr: inline string (rich text)
|
|
333
|
+
* - e: error
|
|
336
334
|
*/
|
|
337
335
|
export type ExcelCellType = "s" | "b" | "str" | "n" | "inlineStr" | "e";
|
|
338
336
|
|
|
@@ -368,11 +366,11 @@ export type ExcelHorizontalAlign = "center" | "left" | "right";
|
|
|
368
366
|
export type ExcelVerticalAlign = "center" | "top" | "bottom";
|
|
369
367
|
|
|
370
368
|
/**
|
|
371
|
-
*
|
|
369
|
+
* Cell style options
|
|
372
370
|
* @example
|
|
373
371
|
* ```typescript
|
|
374
372
|
* await cell.setStyle({
|
|
375
|
-
* background: "00FF0000", //
|
|
373
|
+
* background: "00FF0000", // red
|
|
376
374
|
* border: ["left", "right", "top", "bottom"],
|
|
377
375
|
* horizontalAlign: "center",
|
|
378
376
|
* verticalAlign: "center",
|
|
@@ -381,15 +379,15 @@ export type ExcelVerticalAlign = "center" | "top" | "bottom";
|
|
|
381
379
|
* ```
|
|
382
380
|
*/
|
|
383
381
|
export interface ExcelStyleOptions {
|
|
384
|
-
/**
|
|
382
|
+
/** Background color (ARGB format, e.g. "00FF0000") */
|
|
385
383
|
background?: string;
|
|
386
|
-
/**
|
|
384
|
+
/** Border positions */
|
|
387
385
|
border?: ExcelBorderPosition[];
|
|
388
|
-
/**
|
|
386
|
+
/** Horizontal alignment */
|
|
389
387
|
horizontalAlign?: ExcelHorizontalAlign;
|
|
390
|
-
/**
|
|
388
|
+
/** Vertical alignment */
|
|
391
389
|
verticalAlign?: ExcelVerticalAlign;
|
|
392
|
-
/**
|
|
390
|
+
/** Number format */
|
|
393
391
|
numberFormat?: ExcelNumberFormat;
|
|
394
392
|
}
|
|
395
393
|
|