@lotics/xlsx 0.1.2 → 0.1.3
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/package.json +1 -1
- package/src/csv_parser.ts +6 -2
- package/src/data_workbook.test.ts +30 -1
- package/src/data_workbook.ts +33 -13
- package/src/index.ts +1 -1
package/package.json
CHANGED
package/src/csv_parser.ts
CHANGED
|
@@ -8,8 +8,12 @@ import type {
|
|
|
8
8
|
|
|
9
9
|
const MAX_ROWS = 10_000;
|
|
10
10
|
|
|
11
|
-
export async function parseCsvFile(
|
|
12
|
-
|
|
11
|
+
export async function parseCsvFile(
|
|
12
|
+
url: string,
|
|
13
|
+
signal?: AbortSignal,
|
|
14
|
+
credentials?: RequestCredentials,
|
|
15
|
+
): Promise<ParsedSpreadsheet> {
|
|
16
|
+
const response = await fetch(url, { signal, credentials });
|
|
13
17
|
if (!response.ok) {
|
|
14
18
|
throw new Error(`Failed to fetch file: ${response.status} ${response.statusText}`);
|
|
15
19
|
}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { describe, it, expect } from "vitest";
|
|
2
|
-
import { buildDataWorkbook } from "./data_workbook";
|
|
2
|
+
import { buildDataWorkbook, buildDataSheet } from "./data_workbook";
|
|
3
3
|
import { exportWorkbook } from "./xlsx_writer";
|
|
4
4
|
import { parseExcelBuffer } from "./excel_parser";
|
|
5
5
|
import { parseToExcelSerial } from "./numfmt_type";
|
|
6
|
+
import { WorkbookModel, StyleRegistry } from "./workbook_model";
|
|
6
7
|
import type { ParsedSheet, ParsedCell } from "./types";
|
|
7
8
|
|
|
8
9
|
interface FeeRow {
|
|
@@ -109,3 +110,31 @@ describe("buildDataWorkbook", () => {
|
|
|
109
110
|
expect(widths.get(2)).toBe(40); // explicit override wins
|
|
110
111
|
});
|
|
111
112
|
});
|
|
113
|
+
|
|
114
|
+
describe("buildDataSheet", () => {
|
|
115
|
+
it("builds multiple sheets into one shared registry that round-trips with styles intact", () => {
|
|
116
|
+
// Two sheets sharing ONE registry — cell styles are stored by index into a
|
|
117
|
+
// single registry, so this is the only correct way to merge data sheets into
|
|
118
|
+
// one workbook (two buildDataWorkbook calls each mint a private registry).
|
|
119
|
+
const styles = new StyleRegistry();
|
|
120
|
+
const columns = [
|
|
121
|
+
{ header: "Container", value: (r: FeeRow) => r.container },
|
|
122
|
+
{ header: "Số tiền", type: "currency" as const, value: (r: FeeRow) => r.amount },
|
|
123
|
+
];
|
|
124
|
+
const a = buildDataSheet({ sheetName: "Gate IN", currencyFormat: '#,##0" ₫"', columns, rows: [ROWS[0]] }, styles);
|
|
125
|
+
const b = buildDataSheet({ sheetName: "Gate OUT", currencyFormat: '#,##0" ₫"', columns, rows: [ROWS[1]] }, styles);
|
|
126
|
+
const wb = new WorkbookModel({ sheets: [a, b], activeSheetIndex: 0, styles });
|
|
127
|
+
const parsed = parseExcelBuffer(new Uint8Array(exportWorkbook(wb)).buffer);
|
|
128
|
+
|
|
129
|
+
expect(parsed.sheets.map((s) => s.name)).toEqual(["Gate IN", "Gate OUT"]);
|
|
130
|
+
// Each sheet's body landed on its own tab.
|
|
131
|
+
expect(cellAt(parsed.sheets[0], 2, 1)?.value).toBe("ABCD1234567");
|
|
132
|
+
expect(cellAt(parsed.sheets[0], 2, 2)?.rawValue).toBe(1_500_000);
|
|
133
|
+
expect(cellAt(parsed.sheets[1], 2, 1)?.value).toBe("WXYZ7654321");
|
|
134
|
+
// Styles resolve correctly across both sheets: bold header on sheet 2, and the
|
|
135
|
+
// currency body cell on sheet 1 keeps its right alignment (proves the shared
|
|
136
|
+
// registry's style indices are valid for every sheet, not just the first).
|
|
137
|
+
expect(cellAt(parsed.sheets[1], 1, 1)?.style?.fontBold).toBe(true);
|
|
138
|
+
expect(cellAt(parsed.sheets[0], 2, 2)?.style?.horizontalAlign).toBe("right");
|
|
139
|
+
});
|
|
140
|
+
});
|
package/src/data_workbook.ts
CHANGED
|
@@ -123,23 +123,21 @@ function clampWidth(chars: number): number {
|
|
|
123
123
|
}
|
|
124
124
|
|
|
125
125
|
/**
|
|
126
|
-
* Build
|
|
126
|
+
* Build ONE styled data sheet from typed columns + rows into a caller-supplied
|
|
127
|
+
* style registry. Use this when assembling a multi-sheet workbook so every sheet
|
|
128
|
+
* shares one registry (cell styles are stored by index into a single registry —
|
|
129
|
+
* sheets built against different registries cannot be merged). For the common
|
|
130
|
+
* single-sheet case use {@link buildDataWorkbook}.
|
|
127
131
|
*
|
|
128
132
|
* ```ts
|
|
129
|
-
* const
|
|
130
|
-
*
|
|
131
|
-
*
|
|
132
|
-
*
|
|
133
|
-
* { header: "Container", value: (r) => r.container },
|
|
134
|
-
* { header: "Ngày", type: "date", value: (r) => r.date },
|
|
135
|
-
* { header: "Số tiền", type: "currency", value: (r) => r.amount },
|
|
136
|
-
* ],
|
|
137
|
-
* rows,
|
|
138
|
-
* });
|
|
133
|
+
* const styles = new StyleRegistry();
|
|
134
|
+
* const a = buildDataSheet({ sheetName: "A", columns, rows: rowsA }, styles);
|
|
135
|
+
* const b = buildDataSheet({ sheetName: "B", columns, rows: rowsB }, styles);
|
|
136
|
+
* const wb = new WorkbookModel({ sheets: [a, b], activeSheetIndex: 0, styles });
|
|
139
137
|
* const bytes = exportWorkbook(wb);
|
|
140
138
|
* ```
|
|
141
139
|
*/
|
|
142
|
-
export function
|
|
140
|
+
export function buildDataSheet<R>(opts: BuildDataWorkbookOptions<R>, styles: StyleRegistry): SheetModel {
|
|
143
141
|
const {
|
|
144
142
|
columns,
|
|
145
143
|
rows,
|
|
@@ -149,7 +147,6 @@ export function buildDataWorkbook<R>(opts: BuildDataWorkbookOptions<R>): Workboo
|
|
|
149
147
|
headerStyle = DEFAULT_HEADER_STYLE,
|
|
150
148
|
} = opts;
|
|
151
149
|
|
|
152
|
-
const styles = new StyleRegistry();
|
|
153
150
|
const sheet = new SheetModel(sanitizeSheetName(sheetName), styles);
|
|
154
151
|
|
|
155
152
|
// Per-type body cell style (interned once each via the StyleRegistry).
|
|
@@ -182,5 +179,28 @@ export function buildDataWorkbook<R>(opts: BuildDataWorkbookOptions<R>): Workboo
|
|
|
182
179
|
// Freeze the header row.
|
|
183
180
|
if (rows.length > 0 || columns.length > 0) sheet.setFreeze("A2");
|
|
184
181
|
|
|
182
|
+
return sheet;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
/**
|
|
186
|
+
* Build a single-sheet workbook from typed columns + rows.
|
|
187
|
+
*
|
|
188
|
+
* ```ts
|
|
189
|
+
* const wb = buildDataWorkbook({
|
|
190
|
+
* sheetName: "Phí",
|
|
191
|
+
* currencyFormat: '#,##0" ₫"',
|
|
192
|
+
* columns: [
|
|
193
|
+
* { header: "Container", value: (r) => r.container },
|
|
194
|
+
* { header: "Ngày", type: "date", value: (r) => r.date },
|
|
195
|
+
* { header: "Số tiền", type: "currency", value: (r) => r.amount },
|
|
196
|
+
* ],
|
|
197
|
+
* rows,
|
|
198
|
+
* });
|
|
199
|
+
* const bytes = exportWorkbook(wb);
|
|
200
|
+
* ```
|
|
201
|
+
*/
|
|
202
|
+
export function buildDataWorkbook<R>(opts: BuildDataWorkbookOptions<R>): WorkbookModel {
|
|
203
|
+
const styles = new StyleRegistry();
|
|
204
|
+
const sheet = buildDataSheet(opts, styles);
|
|
185
205
|
return new WorkbookModel({ sheets: [sheet], activeSheetIndex: 0, styles });
|
|
186
206
|
}
|
package/src/index.ts
CHANGED
|
@@ -106,7 +106,7 @@ export { exportWorkbook } from "./xlsx_writer";
|
|
|
106
106
|
// Data → workbook (the common "rows in, .xlsx out" helper)
|
|
107
107
|
// -----------------------------------------------------------------------------
|
|
108
108
|
|
|
109
|
-
export { buildDataWorkbook } from "./data_workbook";
|
|
109
|
+
export { buildDataWorkbook, buildDataSheet } from "./data_workbook";
|
|
110
110
|
export type { DataColumn, DataColumnType, BuildDataWorkbookOptions } from "./data_workbook";
|
|
111
111
|
|
|
112
112
|
export { unzipXlsx } from "./ooxml_zip";
|