@gridkitjs/export-xlsx 0.0.0-dev-20261106222533

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Blagoja Blazhevski
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
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,46 @@
1
+ # @gridkitjs/export-xlsx
2
+
3
+ An Excel export for GridKit grids: a writer that turns what the grid shows
4
+ into a real `.xlsx` workbook — numbers, dates and booleans stored as Excel's
5
+ own in the column's format, a bold header row frozen at the top, each
6
+ column's width, group headers above rows Excel's outline collapses, and the
7
+ grand total. Built on
8
+ [`@gridkitjs/core`](https://www.npmjs.com/package/@gridkitjs/core) and
9
+ [`fflate`](https://www.npmjs.com/package/fflate), with no DOM: it runs in the
10
+ browser or in Node.
11
+
12
+ > **Early development.** The API is still moving and may break between minor
13
+ > versions.
14
+
15
+ ```bash
16
+ pnpm add @gridkitjs/export-xlsx
17
+ ```
18
+
19
+ Register the writer on the grid, and its "Export to Excel" command appears in
20
+ the toolbar's Export dropdown and wherever export commands are listed:
21
+
22
+ ```tsx
23
+ import { DataGrid } from "@gridkitjs/react";
24
+ import { xlsxWriter } from "@gridkitjs/export-xlsx";
25
+
26
+ const writers = [xlsxWriter({ sheetName: "Orders" })];
27
+
28
+ <DataGrid
29
+ columns={columns}
30
+ dataSource={orders}
31
+ toolbar={{ items: ["export"] }}
32
+ exportWriters={writers}
33
+ />;
34
+ ```
35
+
36
+ | Option | Type | Default | Description |
37
+ | ------------ | ------------------------------------- | ---------- | ---------------------------------------------------------------------------- |
38
+ | `sheetName` | `string` | `"Sheet1"` | The sheet's name, cut to 31 characters with `[ ] : * ? / \` replaced. |
39
+ | `headerRows` | `string[]` or `(context) => string[]` | none | Lines above the header row: a report's name, when it was exported. |
40
+ | `values` | `"typed"` or `"text"` | `"typed"` | Store numbers, dates and booleans as Excel's own, or every cell as its text. |
41
+
42
+ Full documentation: [github.com/gridkitjs/gridkitjs](https://github.com/gridkitjs/gridkitjs#readme)
43
+
44
+ ## License
45
+
46
+ MIT © Blagoja Blazhevski
@@ -0,0 +1,40 @@
1
+ import { ExportWriteContext, ExportWriter } from '@gridkitjs/core';
2
+
3
+ /** How `xlsxWriter` writes a workbook. Every field is optional. */
4
+ interface XlsxWriterOptions {
5
+ /**
6
+ * The sheet's name, `"Sheet1"` by default. Excel allows 31 characters and
7
+ * none of `[ ] : * ? / \`, so a longer name is cut and those characters
8
+ * become `_`.
9
+ */
10
+ readonly sheetName?: string | undefined;
11
+ /**
12
+ * Lines of text above the header row — a report's name, when it was
13
+ * exported, the parameters it ran with — each a row of its own in the
14
+ * first column, frozen with the header. A function is asked per export,
15
+ * so an "Exported on" line carries the export's own date.
16
+ */
17
+ readonly headerRows?: readonly string[] | ((context: ExportWriteContext) => readonly string[]) | undefined;
18
+ /**
19
+ * `"typed"` (the default): numbers, dates and booleans are stored as
20
+ * Excel's own, in a format read from the column's `type` and `format`, so
21
+ * a formula can sum them; strings are the text the grid shows. `"text"`:
22
+ * every cell is the text the grid shows, as CSV writes it.
23
+ */
24
+ readonly values?: "typed" | "text" | undefined;
25
+ }
26
+ /**
27
+ * An `ExportWriter` for Excel workbooks: one sheet holding the table, with
28
+ * a bold header row frozen at the top and printed on every page, each
29
+ * column's width, numbers and dates stored as Excel's own in a format read
30
+ * from the column, group headers bold above rows Excel's outline collapses,
31
+ * and summary rows and the grand total bold. Register it through the grid's
32
+ * `exportWriters` and the grid's `export.xlsx` command appears.
33
+ *
34
+ * `write` returns a promise, so that a table the writer cannot write
35
+ * rejects — which a renderer reports as a failed export — rather than
36
+ * throwing out of the command that ran it.
37
+ */
38
+ declare function xlsxWriter(options?: XlsxWriterOptions): ExportWriter;
39
+
40
+ export { type XlsxWriterOptions, xlsxWriter };
package/dist/index.js ADDED
@@ -0,0 +1,397 @@
1
+ // src/xlsxWriter.ts
2
+ import { strToU8, zipSync } from "fflate";
3
+
4
+ // src/xml.ts
5
+ var ILLEGAL_CHARACTERS = /[^\t\n\r\u0020-\uD7FF\uE000-\uFFFD\u{10000}-\u{10FFFF}]/gu;
6
+ var ENTITIES = {
7
+ "&": "&amp;",
8
+ "<": "&lt;",
9
+ ">": "&gt;",
10
+ '"': "&quot;"
11
+ };
12
+ function escapeXml(text) {
13
+ return text.toWellFormed().replace(ILLEGAL_CHARACTERS, "").replace(/[&<>"]/g, (character) => ENTITIES[character] ?? character);
14
+ }
15
+ var XML_DECLARATION = '<?xml version="1.0" encoding="UTF-8" standalone="yes"?>';
16
+
17
+ // src/parts/package.ts
18
+ var PART_PATHS = {
19
+ contentTypes: "[Content_Types].xml",
20
+ packageRelationships: "_rels/.rels",
21
+ workbook: "xl/workbook.xml",
22
+ workbookRelationships: "xl/_rels/workbook.xml.rels",
23
+ sheet: "xl/worksheets/sheet1.xml",
24
+ styles: "xl/styles.xml"
25
+ };
26
+ var SPREADSHEET = "application/vnd.openxmlformats-officedocument.spreadsheetml";
27
+ var RELATIONSHIPS = "http://schemas.openxmlformats.org/officeDocument/2006/relationships";
28
+ var CONTENT_TYPES_XML = `${XML_DECLARATION}<Types xmlns="http://schemas.openxmlformats.org/package/2006/content-types"><Default Extension="rels" ContentType="application/vnd.openxmlformats-package.relationships+xml"/><Default Extension="xml" ContentType="application/xml"/><Override PartName="/${PART_PATHS.workbook}" ContentType="${SPREADSHEET}.sheet.main+xml"/><Override PartName="/${PART_PATHS.sheet}" ContentType="${SPREADSHEET}.worksheet+xml"/><Override PartName="/${PART_PATHS.styles}" ContentType="${SPREADSHEET}.styles+xml"/></Types>`;
29
+ var PACKAGE_RELATIONSHIPS_XML = `${XML_DECLARATION}<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="${RELATIONSHIPS}/officeDocument" Target="${PART_PATHS.workbook}"/></Relationships>`;
30
+ var WORKBOOK_RELATIONSHIPS_XML = `${XML_DECLARATION}<Relationships xmlns="http://schemas.openxmlformats.org/package/2006/relationships"><Relationship Id="rId1" Type="${RELATIONSHIPS}/worksheet" Target="worksheets/sheet1.xml"/><Relationship Id="rId2" Type="${RELATIONSHIPS}/styles" Target="styles.xml"/></Relationships>`;
31
+
32
+ // src/dateSerial.ts
33
+ var MS_PER_DAY = 864e5;
34
+ var EPOCH = Date.UTC(1899, 11, 30);
35
+ var FIRST_SERIAL = 61;
36
+ var LAST_YEAR = 9999;
37
+ function excelDateSerial(date) {
38
+ const year = date.getFullYear();
39
+ if (Number.isNaN(year) || year < 1900 || year > LAST_YEAR) {
40
+ return null;
41
+ }
42
+ const local = Date.UTC(
43
+ year,
44
+ date.getMonth(),
45
+ date.getDate(),
46
+ date.getHours(),
47
+ date.getMinutes(),
48
+ date.getSeconds(),
49
+ date.getMilliseconds()
50
+ );
51
+ const serial = (local - EPOCH) / MS_PER_DAY;
52
+ return serial < FIRST_SERIAL ? null : serial;
53
+ }
54
+
55
+ // src/formats.ts
56
+ var DATE = "yyyy-mm-dd";
57
+ function fractionDigits(format) {
58
+ return format?.maximumFractionDigits ?? format?.minimumFractionDigits;
59
+ }
60
+ function digitsCode(digits, grouping) {
61
+ const whole = grouping ? "#,##0" : "0";
62
+ return digits > 0 ? `${whole}.${"0".repeat(digits)}` : whole;
63
+ }
64
+ function literal(text) {
65
+ return `"${text.replaceAll('"', "")}"`;
66
+ }
67
+ function currencyCode(format, locale) {
68
+ const formatter = new Intl.NumberFormat(locale, {
69
+ ...format,
70
+ style: "currency"
71
+ });
72
+ const digits = fractionDigits(format) ?? formatter.resolvedOptions().maximumFractionDigits;
73
+ const number = digitsCode(digits ?? 2, format.useGrouping !== false);
74
+ let numberWritten = false;
75
+ return formatter.formatToParts(1).map((part) => {
76
+ if (part.type === "currency" || part.type === "literal") {
77
+ return literal(part.value);
78
+ }
79
+ if (numberWritten) {
80
+ return "";
81
+ }
82
+ numberWritten = true;
83
+ return number;
84
+ }).join("");
85
+ }
86
+ function numberStyle(column, format) {
87
+ switch (column.type) {
88
+ case "percent":
89
+ case "currency":
90
+ return column.type;
91
+ case "number":
92
+ case "decimal":
93
+ return format?.style ?? "decimal";
94
+ default:
95
+ return void 0;
96
+ }
97
+ }
98
+ function numberFormatCode(column, locale) {
99
+ const format = column.format;
100
+ if (format?.notation !== void 0 && format.notation !== "standard") {
101
+ return void 0;
102
+ }
103
+ const grouping = format?.useGrouping !== false;
104
+ const digits = fractionDigits(format);
105
+ switch (numberStyle(column, format)) {
106
+ case "percent":
107
+ return `${digitsCode(digits ?? 0, false)}%`;
108
+ case "currency":
109
+ return format?.currency === void 0 ? digitsCode(digits ?? 2, grouping) : currencyCode(format, locale);
110
+ case "decimal":
111
+ return digits === void 0 ? void 0 : digitsCode(digits, grouping);
112
+ default:
113
+ return void 0;
114
+ }
115
+ }
116
+ function showsSeconds(format) {
117
+ return format === void 0 || format.second !== void 0 || format.timeStyle !== void 0 && format.timeStyle !== "short";
118
+ }
119
+ function dateFormatCode(column) {
120
+ const format = column.format;
121
+ const time = showsSeconds(format) ? "hh:mm:ss" : "hh:mm";
122
+ switch (column.type) {
123
+ case "date":
124
+ return DATE;
125
+ case "time":
126
+ return time;
127
+ default:
128
+ return `${DATE} ${time}`;
129
+ }
130
+ }
131
+
132
+ // src/parts/sheet.ts
133
+ var MAX_OUTLINE_LEVEL = 7;
134
+ var MAX_CELL_TEXT = 32767;
135
+ var PX_PER_CHARACTER = 7;
136
+ var EMPTY_CELL = { value: null, text: "" };
137
+ var DATA_ROW = { bold: false, aggregate: false };
138
+ var AGGREGATE_ROW = { bold: true, aggregate: true };
139
+ function columnName(index) {
140
+ let name = "";
141
+ for (let n = index + 1; n > 0; n = Math.floor((n - 1) / 26)) {
142
+ name = String.fromCharCode(65 + (n - 1) % 26) + name;
143
+ }
144
+ return name;
145
+ }
146
+ function styleAttribute(style) {
147
+ return style === 0 ? "" : ` s="${String(style)}"`;
148
+ }
149
+ function stringCell(reference, text, style) {
150
+ return text === "" ? "" : `<c r="${reference}"${styleAttribute(style)} t="inlineStr"><is><t xml:space="preserve">${escapeXml(
151
+ text.slice(0, MAX_CELL_TEXT)
152
+ )}</t></is></c>`;
153
+ }
154
+ function valueCell(reference, value, style, type) {
155
+ const typeAttribute = type === void 0 ? "" : ` t="${type}"`;
156
+ return `<c r="${reference}"${styleAttribute(style)}${typeAttribute}><v>${value}</v></c>`;
157
+ }
158
+ function rowXml(rowNumber, cells, outlineLevel) {
159
+ const outline = outlineLevel > 0 ? ` outlineLevel="${String(outlineLevel)}"` : "";
160
+ return `<row r="${String(rowNumber)}"${outline}>${cells.join("")}</row>`;
161
+ }
162
+ function colsXml(layouts) {
163
+ if (layouts.length === 0) {
164
+ return "";
165
+ }
166
+ const cols = layouts.map(({ column }, index) => {
167
+ const width = Math.max(
168
+ 1,
169
+ Math.round(column.width / PX_PER_CHARACTER * 100) / 100
170
+ );
171
+ const position = String(index + 1);
172
+ return `<col min="${position}" max="${position}" width="${String(width)}" customWidth="1"/>`;
173
+ });
174
+ return `<cols>${cols.join("")}</cols>`;
175
+ }
176
+ function headerRowNumber(table, titleRows) {
177
+ return table.header ? titleRows.length + 1 : void 0;
178
+ }
179
+ function sheetXml(table, options) {
180
+ const { titleRows, values, locale, styles } = options;
181
+ const layouts = table.columns.map((column, index) => ({
182
+ column,
183
+ name: columnName(index),
184
+ numberFormat: numberFormatCode(column, locale),
185
+ dateFormat: dateFormatCode(column)
186
+ }));
187
+ const plainNumber = new Intl.NumberFormat(locale);
188
+ const boldText = styles.index({ bold: true });
189
+ function isPlainNumber(cell, value) {
190
+ return cell.text === plainNumber.format(value);
191
+ }
192
+ function cellXml(reference, cell, layout, row) {
193
+ const { value } = cell;
194
+ const { bold } = row;
195
+ if (values === "typed") {
196
+ if (typeof value === "number" && Number.isFinite(value)) {
197
+ const numberFormat = row.aggregate && isPlainNumber(cell, value) ? void 0 : layout.numberFormat;
198
+ return valueCell(
199
+ reference,
200
+ String(value),
201
+ styles.index({ numberFormat, bold })
202
+ );
203
+ }
204
+ if (typeof value === "boolean") {
205
+ return valueCell(
206
+ reference,
207
+ value ? "1" : "0",
208
+ styles.index({ bold }),
209
+ "b"
210
+ );
211
+ }
212
+ const serial = value instanceof Date ? excelDateSerial(value) : null;
213
+ if (serial !== null) {
214
+ return valueCell(
215
+ reference,
216
+ String(serial),
217
+ styles.index({ numberFormat: layout.dateFormat, bold })
218
+ );
219
+ }
220
+ }
221
+ return stringCell(reference, cell.text, styles.index({ bold }));
222
+ }
223
+ function cellsXml(rowNumber, cells, row) {
224
+ return layouts.map(
225
+ (layout, index) => cellXml(
226
+ `${layout.name}${String(rowNumber)}`,
227
+ cells[index] ?? EMPTY_CELL,
228
+ layout,
229
+ row
230
+ )
231
+ );
232
+ }
233
+ const rows = [];
234
+ const nextRowNumber = () => rows.length + 1;
235
+ for (const text of titleRows) {
236
+ const rowNumber = nextRowNumber();
237
+ rows.push(
238
+ rowXml(rowNumber, [stringCell(`A${String(rowNumber)}`, text, 0)], 0)
239
+ );
240
+ }
241
+ const headerRow = headerRowNumber(table, titleRows);
242
+ if (headerRow !== void 0) {
243
+ const header = layouts.map(
244
+ (layout) => stringCell(
245
+ `${layout.name}${String(headerRow)}`,
246
+ layout.column.header,
247
+ boldText
248
+ )
249
+ );
250
+ rows.push(rowXml(headerRow, header, 0));
251
+ }
252
+ const outlined = table.rows.some((row) => row.kind === "group");
253
+ let depth = 0;
254
+ let deepest = 0;
255
+ for (const row of table.rows) {
256
+ const rowNumber = nextRowNumber();
257
+ let level;
258
+ let cells;
259
+ switch (row.kind) {
260
+ case "data":
261
+ level = depth;
262
+ cells = cellsXml(rowNumber, row.cells, DATA_ROW);
263
+ break;
264
+ case "group":
265
+ level = row.level;
266
+ depth = row.level + 1;
267
+ cells = cellsXml(rowNumber, row.cells, AGGREGATE_ROW);
268
+ cells[0] = stringCell(`A${String(rowNumber)}`, row.label, boldText);
269
+ break;
270
+ case "groupSummary":
271
+ level = row.level;
272
+ depth = row.level;
273
+ cells = cellsXml(rowNumber, row.cells, AGGREGATE_ROW);
274
+ break;
275
+ }
276
+ const outlineLevel = outlined ? Math.min(level, MAX_OUTLINE_LEVEL) : 0;
277
+ deepest = Math.max(deepest, outlineLevel);
278
+ rows.push(rowXml(rowNumber, cells, outlineLevel));
279
+ }
280
+ if (table.footer !== null) {
281
+ const rowNumber = nextRowNumber();
282
+ rows.push(
283
+ rowXml(
284
+ rowNumber,
285
+ cellsXml(rowNumber, table.footer.cells, AGGREGATE_ROW),
286
+ 0
287
+ )
288
+ );
289
+ }
290
+ const sheetProperties = outlined ? '<sheetPr><outlinePr summaryBelow="0"/></sheetPr>' : "";
291
+ const frozenPane = headerRow === void 0 ? "" : `<sheetViews><sheetView workbookViewId="0"><pane ySplit="${String(headerRow)}" topLeftCell="A${String(headerRow + 1)}" activePane="bottomLeft" state="frozen"/><selection pane="bottomLeft"/></sheetView></sheetViews>`;
292
+ const outlineLevels = deepest > 0 ? ` outlineLevelRow="${String(deepest)}"` : "";
293
+ return `${XML_DECLARATION}<worksheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships">${sheetProperties}${frozenPane}<sheetFormatPr defaultRowHeight="15"${outlineLevels}/>${colsXml(layouts)}<sheetData>${rows.join("")}</sheetData></worksheet>`;
294
+ }
295
+
296
+ // src/parts/styles.ts
297
+ var FIRST_CUSTOM_FORMAT = 164;
298
+ var FONT = '<sz val="11"/><name val="Calibri"/><family val="2"/>';
299
+ var FONTS = `<fonts count="2"><font>${FONT}</font><font><b/>${FONT}</font></fonts>`;
300
+ var FILLS = '<fills count="2"><fill><patternFill patternType="none"/></fill><fill><patternFill patternType="gray125"/></fill></fills>';
301
+ var BORDERS = '<borders count="1"><border><left/><right/><top/><bottom/><diagonal/></border></borders>';
302
+ var CELL_STYLE_FORMATS = '<cellStyleXfs count="1"><xf numFmtId="0" fontId="0" fillId="0" borderId="0"/></cellStyleXfs>';
303
+ var CELL_STYLES = '<cellStyles count="1"><cellStyle name="Normal" xfId="0" builtinId="0"/></cellStyles>';
304
+ function createStyleSheet() {
305
+ const formatIds = /* @__PURE__ */ new Map();
306
+ const styles = /* @__PURE__ */ new Map([["0|", 0]]);
307
+ const cellFormats = [
308
+ '<xf numFmtId="0" fontId="0" fillId="0" borderId="0" xfId="0"/>'
309
+ ];
310
+ function formatId(code) {
311
+ if (code === void 0) {
312
+ return 0;
313
+ }
314
+ let id = formatIds.get(code);
315
+ if (id === void 0) {
316
+ id = FIRST_CUSTOM_FORMAT + formatIds.size;
317
+ formatIds.set(code, id);
318
+ }
319
+ return id;
320
+ }
321
+ function index({ numberFormat, bold = false }) {
322
+ const key = `${bold ? "1" : "0"}|${numberFormat ?? ""}`;
323
+ const existing = styles.get(key);
324
+ if (existing !== void 0) {
325
+ return existing;
326
+ }
327
+ const added = cellFormats.length;
328
+ cellFormats.push(
329
+ `<xf numFmtId="${String(formatId(numberFormat))}" fontId="${bold ? "1" : "0"}" fillId="0" borderId="0" xfId="0" applyNumberFormat="1" applyFont="1"/>`
330
+ );
331
+ styles.set(key, added);
332
+ return added;
333
+ }
334
+ function xml() {
335
+ const numberFormats = formatIds.size === 0 ? "" : `<numFmts count="${String(formatIds.size)}">${[...formatIds].map(
336
+ ([code, id]) => `<numFmt numFmtId="${String(id)}" formatCode="${escapeXml(code)}"/>`
337
+ ).join("")}</numFmts>`;
338
+ return `${XML_DECLARATION}<styleSheet xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main">${numberFormats}${FONTS}${FILLS}${BORDERS}${CELL_STYLE_FORMATS}<cellXfs count="${String(cellFormats.length)}">${cellFormats.join("")}</cellXfs>${CELL_STYLES}</styleSheet>`;
339
+ }
340
+ return { index, xml };
341
+ }
342
+
343
+ // src/parts/workbook.ts
344
+ function quotedSheetName(sheetName) {
345
+ return `'${sheetName.replaceAll("'", "''")}'`;
346
+ }
347
+ function workbookXml(sheetName, headerRow) {
348
+ const printTitles = headerRow === void 0 ? "" : `<definedNames><definedName name="_xlnm.Print_Titles" localSheetId="0">${escapeXml(
349
+ `${quotedSheetName(sheetName)}!$${String(headerRow)}:$${String(headerRow)}`
350
+ )}</definedName></definedNames>`;
351
+ return `${XML_DECLARATION}<workbook xmlns="http://schemas.openxmlformats.org/spreadsheetml/2006/main" xmlns:r="http://schemas.openxmlformats.org/officeDocument/2006/relationships"><sheets><sheet name="${escapeXml(sheetName)}" sheetId="1" r:id="rId1"/></sheets>${printTitles}</workbook>`;
352
+ }
353
+
354
+ // src/xlsxWriter.ts
355
+ var XLSX_MIME_TYPE = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
356
+ var DEFAULT_SHEET_NAME = "Sheet1";
357
+ var MAX_SHEET_NAME = 31;
358
+ function resolveSheetName(name) {
359
+ const cleaned = Array.from(
360
+ (name ?? "").replace(/[[\]:*?/\\]/g, "_").replace(/^'+|'+$/g, "").trim()
361
+ ).slice(0, MAX_SHEET_NAME).join("").trim();
362
+ return cleaned === "" ? DEFAULT_SHEET_NAME : cleaned;
363
+ }
364
+ function writeWorkbook(table, context, options) {
365
+ const sheetName = resolveSheetName(options.sheetName);
366
+ const { headerRows = [] } = options;
367
+ const titleRows = typeof headerRows === "function" ? headerRows(context) : headerRows;
368
+ const styles = createStyleSheet();
369
+ const sheet = sheetXml(table, {
370
+ titleRows,
371
+ values: options.values ?? "typed",
372
+ locale: context.locale,
373
+ styles
374
+ });
375
+ return zipSync({
376
+ [PART_PATHS.contentTypes]: strToU8(CONTENT_TYPES_XML),
377
+ [PART_PATHS.packageRelationships]: strToU8(PACKAGE_RELATIONSHIPS_XML),
378
+ [PART_PATHS.workbook]: strToU8(
379
+ workbookXml(sheetName, headerRowNumber(table, titleRows))
380
+ ),
381
+ [PART_PATHS.workbookRelationships]: strToU8(WORKBOOK_RELATIONSHIPS_XML),
382
+ [PART_PATHS.sheet]: strToU8(sheet),
383
+ [PART_PATHS.styles]: strToU8(styles.xml())
384
+ });
385
+ }
386
+ function xlsxWriter(options = {}) {
387
+ return {
388
+ format: "xlsx",
389
+ fileExtension: "xlsx",
390
+ mimeType: XLSX_MIME_TYPE,
391
+ write: (table, context) => Promise.resolve().then(() => writeWorkbook(table, context, options))
392
+ };
393
+ }
394
+ export {
395
+ xlsxWriter
396
+ };
397
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/xlsxWriter.ts","../src/xml.ts","../src/parts/package.ts","../src/dateSerial.ts","../src/formats.ts","../src/parts/sheet.ts","../src/parts/styles.ts","../src/parts/workbook.ts"],"sourcesContent":["import type {\n ExportTable,\n ExportWriteContext,\n ExportWriter,\n} from \"@gridkitjs/core\";\nimport { strToU8, zipSync } from \"fflate\";\n\nimport {\n CONTENT_TYPES_XML,\n PACKAGE_RELATIONSHIPS_XML,\n PART_PATHS,\n WORKBOOK_RELATIONSHIPS_XML,\n} from \"./parts/package\";\nimport { headerRowNumber, sheetXml } from \"./parts/sheet\";\nimport { createStyleSheet } from \"./parts/styles\";\nimport { workbookXml } from \"./parts/workbook\";\n\n/** How `xlsxWriter` writes a workbook. Every field is optional. */\nexport interface XlsxWriterOptions {\n /**\n * The sheet's name, `\"Sheet1\"` by default. Excel allows 31 characters and\n * none of `[ ] : * ? / \\`, so a longer name is cut and those characters\n * become `_`.\n */\n readonly sheetName?: string | undefined;\n /**\n * Lines of text above the header row — a report's name, when it was\n * exported, the parameters it ran with — each a row of its own in the\n * first column, frozen with the header. A function is asked per export,\n * so an \"Exported on\" line carries the export's own date.\n */\n readonly headerRows?:\n | readonly string[]\n | ((context: ExportWriteContext) => readonly string[])\n | undefined;\n /**\n * `\"typed\"` (the default): numbers, dates and booleans are stored as\n * Excel's own, in a format read from the column's `type` and `format`, so\n * a formula can sum them; strings are the text the grid shows. `\"text\"`:\n * every cell is the text the grid shows, as CSV writes it.\n */\n readonly values?: \"typed\" | \"text\" | undefined;\n}\n\nexport const XLSX_MIME_TYPE =\n \"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet\";\n\nconst DEFAULT_SHEET_NAME = \"Sheet1\";\nconst MAX_SHEET_NAME = 31;\n\n/**\n * A name Excel accepts for a sheet: no `[ ] : * ? / \\`, no quote at either\n * end, not empty, at most 31 characters.\n */\nexport function resolveSheetName(name: string | undefined): string {\n const cleaned = Array.from(\n (name ?? \"\")\n .replace(/[[\\]:*?/\\\\]/g, \"_\")\n .replace(/^'+|'+$/g, \"\")\n .trim(),\n )\n .slice(0, MAX_SHEET_NAME)\n .join(\"\")\n .trim();\n return cleaned === \"\" ? DEFAULT_SHEET_NAME : cleaned;\n}\n\nfunction writeWorkbook(\n table: ExportTable,\n context: ExportWriteContext,\n options: XlsxWriterOptions,\n): Uint8Array {\n const sheetName = resolveSheetName(options.sheetName);\n const { headerRows = [] } = options;\n const titleRows =\n typeof headerRows === \"function\" ? headerRows(context) : headerRows;\n const styles = createStyleSheet();\n // The sheet first: the styles part lists the styles its cells asked for.\n const sheet = sheetXml(table, {\n titleRows,\n values: options.values ?? \"typed\",\n locale: context.locale,\n styles,\n });\n return zipSync({\n [PART_PATHS.contentTypes]: strToU8(CONTENT_TYPES_XML),\n [PART_PATHS.packageRelationships]: strToU8(PACKAGE_RELATIONSHIPS_XML),\n [PART_PATHS.workbook]: strToU8(\n workbookXml(sheetName, headerRowNumber(table, titleRows)),\n ),\n [PART_PATHS.workbookRelationships]: strToU8(WORKBOOK_RELATIONSHIPS_XML),\n [PART_PATHS.sheet]: strToU8(sheet),\n [PART_PATHS.styles]: strToU8(styles.xml()),\n });\n}\n\n/**\n * An `ExportWriter` for Excel workbooks: one sheet holding the table, with\n * a bold header row frozen at the top and printed on every page, each\n * column's width, numbers and dates stored as Excel's own in a format read\n * from the column, group headers bold above rows Excel's outline collapses,\n * and summary rows and the grand total bold. Register it through the grid's\n * `exportWriters` and the grid's `export.xlsx` command appears.\n *\n * `write` returns a promise, so that a table the writer cannot write\n * rejects — which a renderer reports as a failed export — rather than\n * throwing out of the command that ran it.\n */\nexport function xlsxWriter(options: XlsxWriterOptions = {}): ExportWriter {\n return {\n format: \"xlsx\",\n fileExtension: \"xlsx\",\n mimeType: XLSX_MIME_TYPE,\n write: (table, context) =>\n Promise.resolve().then(() => writeWorkbook(table, context, options)),\n };\n}\n","/**\n * Everything outside XML 1.0's `Char` production: the C0 controls other\n * than tab, line feed and carriage return, and U+FFFE/U+FFFF. Excel refuses\n * a part holding one as corrupt — even escaped as `&#1;` — so they are\n * dropped rather than escaped. A lone surrogate, which no encoding can\n * write, is first replaced by U+FFFD through `toWellFormed`.\n */\nconst ILLEGAL_CHARACTERS =\n /[^\\t\\n\\r\\u0020-\\uD7FF\\uE000-\\uFFFD\\u{10000}-\\u{10FFFF}]/gu;\n\nconst ENTITIES: Readonly<Record<string, string>> = {\n \"&\": \"&amp;\",\n \"<\": \"&lt;\",\n \">\": \"&gt;\",\n '\"': \"&quot;\",\n};\n\n/**\n * `text` made safe for an XML part, as element content or inside a\n * double-quoted attribute: the markup characters escaped, the characters\n * XML cannot hold at all removed.\n */\nexport function escapeXml(text: string): string {\n return text\n .toWellFormed()\n .replace(ILLEGAL_CHARACTERS, \"\")\n .replace(/[&<>\"]/g, (character) => ENTITIES[character] ?? character);\n}\n\n/** What every part begins with. */\nexport const XML_DECLARATION =\n '<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"yes\"?>';\n","import { XML_DECLARATION } from \"../xml\";\n\n/**\n * The parts that hold a workbook together and never change: what each part\n * is, where the workbook starts, and which sheet and styles it has. The\n * minimum ECMA-376 asks of a SpreadsheetML package with one sheet.\n */\n\n/** Where each part sits in the zip, a part name to its path. */\nexport const PART_PATHS = {\n contentTypes: \"[Content_Types].xml\",\n packageRelationships: \"_rels/.rels\",\n workbook: \"xl/workbook.xml\",\n workbookRelationships: \"xl/_rels/workbook.xml.rels\",\n sheet: \"xl/worksheets/sheet1.xml\",\n styles: \"xl/styles.xml\",\n} as const;\n\nconst SPREADSHEET =\n \"application/vnd.openxmlformats-officedocument.spreadsheetml\";\nconst RELATIONSHIPS =\n \"http://schemas.openxmlformats.org/officeDocument/2006/relationships\";\n\nexport const CONTENT_TYPES_XML = `${XML_DECLARATION}<Types xmlns=\"http://schemas.openxmlformats.org/package/2006/content-types\"><Default Extension=\"rels\" ContentType=\"application/vnd.openxmlformats-package.relationships+xml\"/><Default Extension=\"xml\" ContentType=\"application/xml\"/><Override PartName=\"/${PART_PATHS.workbook}\" ContentType=\"${SPREADSHEET}.sheet.main+xml\"/><Override PartName=\"/${PART_PATHS.sheet}\" ContentType=\"${SPREADSHEET}.worksheet+xml\"/><Override PartName=\"/${PART_PATHS.styles}\" ContentType=\"${SPREADSHEET}.styles+xml\"/></Types>`;\n\nexport const PACKAGE_RELATIONSHIPS_XML = `${XML_DECLARATION}<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\"><Relationship Id=\"rId1\" Type=\"${RELATIONSHIPS}/officeDocument\" Target=\"${PART_PATHS.workbook}\"/></Relationships>`;\n\n/** Targets are relative to the workbook's own folder, `xl/`. */\nexport const WORKBOOK_RELATIONSHIPS_XML = `${XML_DECLARATION}<Relationships xmlns=\"http://schemas.openxmlformats.org/package/2006/relationships\"><Relationship Id=\"rId1\" Type=\"${RELATIONSHIPS}/worksheet\" Target=\"worksheets/sheet1.xml\"/><Relationship Id=\"rId2\" Type=\"${RELATIONSHIPS}/styles\" Target=\"styles.xml\"/></Relationships>`;\n","const MS_PER_DAY = 86_400_000;\n\n/**\n * Day 0 of the serial numbers Excel's 1900 date system stores, as it counts\n * from March 1900 on. Excel keeps Lotus 1-2-3's 29 February 1900, a day that\n * never was, so counting from 30 December 1899 lands on the right serial for\n * every date from 1 March 1900 — and on the wrong one before it.\n */\nconst EPOCH = Date.UTC(1899, 11, 30);\n\n/** 1 March 1900, the first date the epoch above gets right. */\nconst FIRST_SERIAL = 61;\n\n/** 31 December 9999, the last date Excel shows. */\nconst LAST_YEAR = 9999;\n\n/**\n * The serial number Excel stores for `date` — whole days since its epoch,\n * the time of day as the fraction — or `null` for a date it cannot hold:\n * an invalid one, one before 1 March 1900 or after 9999.\n *\n * It is the date's local calendar date and clock time, not its UTC instant:\n * the date the grid shows. A spreadsheet cell has no time zone, so a date\n * entered as midnight 4 November reads as 4 November in Excel wherever the\n * file was written or is opened.\n */\nexport function excelDateSerial(date: Date): number | null {\n const year = date.getFullYear();\n if (Number.isNaN(year) || year < 1900 || year > LAST_YEAR) {\n return null;\n }\n const local = Date.UTC(\n year,\n date.getMonth(),\n date.getDate(),\n date.getHours(),\n date.getMinutes(),\n date.getSeconds(),\n date.getMilliseconds(),\n );\n const serial = (local - EPOCH) / MS_PER_DAY;\n return serial < FIRST_SERIAL ? null : serial;\n}\n","import type { ExportColumn, FormatLocale } from \"@gridkitjs/core\";\n\n/**\n * Excel's number format codes for a column's cells, read from its `type`\n * and `format` the way the grid reads them to print a cell — so a currency\n * column is currency in Excel too, while the cell still holds a number a\n * formula can sum. `undefined` is Excel's General, which shows a number as\n * it is stored, as the grid prints a number column given no `format`.\n *\n * A code's `,` and `.` are always the grouping and decimal marks in the\n * file; Excel shows them as the reader's own locale writes them.\n */\n\nconst DATE = \"yyyy-mm-dd\";\n\nfunction fractionDigits(\n format: Intl.NumberFormatOptions | undefined,\n): number | undefined {\n return format?.maximumFractionDigits ?? format?.minimumFractionDigits;\n}\n\n/**\n * A fixed number of decimals: Excel's optional digit (`#`) would leave a\n * whole number with a bare decimal mark, `12.`, so the grid's \"up to two\"\n * becomes \"two\" here.\n */\nfunction digitsCode(digits: number, grouping: boolean): string {\n const whole = grouping ? \"#,##0\" : \"0\";\n return digits > 0 ? `${whole}.${\"0\".repeat(digits)}` : whole;\n}\n\n/** Text Excel prints as it is, quoted; a format code cannot hold a quote. */\nfunction literal(text: string): string {\n return `\"${text.replaceAll('\"', \"\")}\"`;\n}\n\n/**\n * A currency code with the symbol and spacing `locale` writes, on the side\n * it writes them — `\"€\"#,##0.00` in English, `#,##0.00\" \"\"€\"` in German —\n * read from how `Intl` formats a sample amount.\n */\nfunction currencyCode(\n format: Intl.NumberFormatOptions,\n locale: FormatLocale,\n): string {\n const formatter = new Intl.NumberFormat(locale, {\n ...format,\n style: \"currency\",\n });\n const digits =\n fractionDigits(format) ?? formatter.resolvedOptions().maximumFractionDigits;\n const number = digitsCode(digits ?? 2, format.useGrouping !== false);\n let numberWritten = false;\n return formatter\n .formatToParts(1)\n .map((part) => {\n if (part.type === \"currency\" || part.type === \"literal\") {\n return literal(part.value);\n }\n if (numberWritten) {\n return \"\";\n }\n numberWritten = true;\n return number;\n })\n .join(\"\");\n}\n\n/**\n * The `Intl` style a numeric column prints in: its `type`'s, or for a plain\n * number column the `style` its `format` names. `undefined` for a column\n * that is not numeric.\n */\nfunction numberStyle(\n column: ExportColumn,\n format: Intl.NumberFormatOptions | undefined,\n): Intl.NumberFormatOptions[\"style\"] {\n switch (column.type) {\n case \"percent\":\n case \"currency\":\n return column.type;\n case \"number\":\n case \"decimal\":\n return format?.style ?? \"decimal\";\n default:\n return undefined;\n }\n}\n\n/**\n * The code a number in `column` is shown with. A column whose `type` is not\n * numeric shows its numbers in General, as the grid prints them as they are,\n * and so does a `format` no code reads as — a compact or scientific\n * notation, a unit.\n */\nexport function numberFormatCode(\n column: ExportColumn,\n locale: FormatLocale,\n): string | undefined {\n const format = column.format as Intl.NumberFormatOptions | undefined;\n if (format?.notation !== undefined && format.notation !== \"standard\") {\n return undefined;\n }\n const grouping = format?.useGrouping !== false;\n const digits = fractionDigits(format);\n switch (numberStyle(column, format)) {\n case \"percent\":\n return `${digitsCode(digits ?? 0, false)}%`;\n case \"currency\":\n return format?.currency === undefined\n ? digitsCode(digits ?? 2, grouping)\n : currencyCode(format, locale);\n case \"decimal\":\n return digits === undefined ? undefined : digitsCode(digits, grouping);\n default:\n return undefined;\n }\n}\n\n/**\n * Whether the grid prints seconds: its own default fields do, and so does a\n * `format` naming `second` or a `timeStyle` longer than `\"short\"`.\n */\nfunction showsSeconds(format: Intl.DateTimeFormatOptions | undefined): boolean {\n return (\n format === undefined ||\n format.second !== undefined ||\n (format.timeStyle !== undefined && format.timeStyle !== \"short\")\n );\n}\n\n/**\n * The code a date in `column` is shown with: ISO 8601's order, which reads\n * the same in every locale, the date alone under `\"date\"`, the time alone\n * under `\"time\"`, both otherwise.\n */\nexport function dateFormatCode(column: ExportColumn): string {\n const format = column.format as Intl.DateTimeFormatOptions | undefined;\n const time = showsSeconds(format) ? \"hh:mm:ss\" : \"hh:mm\";\n switch (column.type) {\n case \"date\":\n return DATE;\n case \"time\":\n return time;\n default:\n return `${DATE} ${time}`;\n }\n}\n","import type {\n ExportCell,\n ExportColumn,\n ExportTable,\n FormatLocale,\n} from \"@gridkitjs/core\";\nimport { excelDateSerial } from \"../dateSerial\";\nimport { dateFormatCode, numberFormatCode } from \"../formats\";\nimport { escapeXml, XML_DECLARATION } from \"../xml\";\nimport type { StyleSheet } from \"./styles\";\n\n/** What the sheet writes beside the table. */\nexport interface SheetOptions {\n /** Lines of text above the header row, each a row of its own in column A. */\n readonly titleRows: readonly string[];\n /**\n * `\"typed\"`: numbers, dates and booleans as Excel's own, a string as the\n * text the grid shows. `\"text\"`: every cell as the text the grid shows.\n */\n readonly values: \"typed\" | \"text\";\n /** The grid's locale, which a currency's symbol is read in. */\n readonly locale: FormatLocale;\n /** Collects the style each cell asks for; written as its own part after. */\n readonly styles: StyleSheet;\n}\n\n/** The deepest outline Excel can show. */\nconst MAX_OUTLINE_LEVEL = 7;\n\n/** The most characters an Excel cell holds; it refuses a file with more. */\nconst MAX_CELL_TEXT = 32_767;\n\n/**\n * A pixel width in Excel's unit, the width of a digit in its default font,\n * which Calibri 11 draws about 7px wide.\n */\nconst PX_PER_CHARACTER = 7;\n\nconst EMPTY_CELL: ExportCell = { value: null, text: \"\" };\n\n/** A column with what every one of its cells needs, settled once. */\ninterface ColumnLayout {\n readonly column: ExportColumn;\n /** Its letters in a cell reference: `A`, …, `Z`, `AA`. */\n readonly name: string;\n readonly numberFormat: string | undefined;\n readonly dateFormat: string;\n}\n\n/** How a row's cells are written. */\ninterface RowStyle {\n /** Header, group and total rows are bold. */\n readonly bold: boolean;\n /** Its cells hold aggregate results rather than the column's values. */\n readonly aggregate: boolean;\n}\n\nconst DATA_ROW: RowStyle = { bold: false, aggregate: false };\nconst AGGREGATE_ROW: RowStyle = { bold: true, aggregate: true };\n\nfunction columnName(index: number): string {\n let name = \"\";\n for (let n = index + 1; n > 0; n = Math.floor((n - 1) / 26)) {\n name = String.fromCharCode(65 + ((n - 1) % 26)) + name;\n }\n return name;\n}\n\nfunction styleAttribute(style: number): string {\n return style === 0 ? \"\" : ` s=\"${String(style)}\"`;\n}\n\nfunction stringCell(reference: string, text: string, style: number): string {\n return text === \"\"\n ? \"\"\n : `<c r=\"${reference}\"${styleAttribute(style)} t=\"inlineStr\"><is><t xml:space=\"preserve\">${escapeXml(\n text.slice(0, MAX_CELL_TEXT),\n )}</t></is></c>`;\n}\n\nfunction valueCell(\n reference: string,\n value: string,\n style: number,\n type?: \"b\",\n): string {\n const typeAttribute = type === undefined ? \"\" : ` t=\"${type}\"`;\n return `<c r=\"${reference}\"${styleAttribute(style)}${typeAttribute}><v>${value}</v></c>`;\n}\n\nfunction rowXml(\n rowNumber: number,\n cells: readonly string[],\n outlineLevel: number,\n): string {\n const outline =\n outlineLevel > 0 ? ` outlineLevel=\"${String(outlineLevel)}\"` : \"\";\n return `<row r=\"${String(rowNumber)}\"${outline}>${cells.join(\"\")}</row>`;\n}\n\nfunction colsXml(layouts: readonly ColumnLayout[]): string {\n if (layouts.length === 0) {\n return \"\";\n }\n const cols = layouts.map(({ column }, index) => {\n const width = Math.max(\n 1,\n Math.round((column.width / PX_PER_CHARACTER) * 100) / 100,\n );\n const position = String(index + 1);\n return `<col min=\"${position}\" max=\"${position}\" width=\"${String(width)}\" customWidth=\"1\"/>`;\n });\n return `<cols>${cols.join(\"\")}</cols>`;\n}\n\n/**\n * The 1-based row the column names stand in — below the title rows — or\n * `undefined` when the table has no header row.\n */\nexport function headerRowNumber(\n table: ExportTable,\n titleRows: readonly string[],\n): number | undefined {\n return table.header ? titleRows.length + 1 : undefined;\n}\n\n/**\n * The worksheet part: the title rows, the header row (bold and frozen), and\n * the table's rows in order — group headers bold with their label in the\n * first column and their rows outlined beneath them, so Excel's outline\n * buttons collapse a group as the grid does; summary rows and the grand\n * total bold.\n */\nexport function sheetXml(table: ExportTable, options: SheetOptions): string {\n const { titleRows, values, locale, styles } = options;\n const layouts: ColumnLayout[] = table.columns.map((column, index) => ({\n column,\n name: columnName(index),\n numberFormat: numberFormatCode(column, locale),\n dateFormat: dateFormatCode(column),\n }));\n const plainNumber = new Intl.NumberFormat(locale);\n const boldText = styles.index({ bold: true });\n\n /**\n * Whether an aggregate result was printed as a plain number rather than\n * by its column's rules — a count, which is a number of rows whatever the\n * column holds — so it is not shown as the column's currency or percent.\n */\n function isPlainNumber(cell: ExportCell, value: number): boolean {\n return cell.text === plainNumber.format(value);\n }\n\n function cellXml(\n reference: string,\n cell: ExportCell,\n layout: ColumnLayout,\n row: RowStyle,\n ): string {\n const { value } = cell;\n const { bold } = row;\n if (values === \"typed\") {\n if (typeof value === \"number\" && Number.isFinite(value)) {\n const numberFormat =\n row.aggregate && isPlainNumber(cell, value)\n ? undefined\n : layout.numberFormat;\n return valueCell(\n reference,\n String(value),\n styles.index({ numberFormat, bold }),\n );\n }\n if (typeof value === \"boolean\") {\n return valueCell(\n reference,\n value ? \"1\" : \"0\",\n styles.index({ bold }),\n \"b\",\n );\n }\n const serial = value instanceof Date ? excelDateSerial(value) : null;\n if (serial !== null) {\n return valueCell(\n reference,\n String(serial),\n styles.index({ numberFormat: layout.dateFormat, bold }),\n );\n }\n }\n return stringCell(reference, cell.text, styles.index({ bold }));\n }\n\n function cellsXml(\n rowNumber: number,\n cells: readonly ExportCell[],\n row: RowStyle,\n ): string[] {\n return layouts.map((layout, index) =>\n cellXml(\n `${layout.name}${String(rowNumber)}`,\n cells[index] ?? EMPTY_CELL,\n layout,\n row,\n ),\n );\n }\n\n const rows: string[] = [];\n const nextRowNumber = () => rows.length + 1;\n\n for (const text of titleRows) {\n const rowNumber = nextRowNumber();\n rows.push(\n rowXml(rowNumber, [stringCell(`A${String(rowNumber)}`, text, 0)], 0),\n );\n }\n\n const headerRow = headerRowNumber(table, titleRows);\n if (headerRow !== undefined) {\n const header = layouts.map((layout) =>\n stringCell(\n `${layout.name}${String(headerRow)}`,\n layout.column.header,\n boldText,\n ),\n );\n rows.push(rowXml(headerRow, header, 0));\n }\n\n // Rows are outlined only beneath group headers; without them, summary\n // rows' levels would outline groups that have no header to collapse into.\n const outlined = table.rows.some((row) => row.kind === \"group\");\n let depth = 0;\n let deepest = 0;\n for (const row of table.rows) {\n const rowNumber = nextRowNumber();\n let level: number;\n let cells: string[];\n switch (row.kind) {\n case \"data\":\n level = depth;\n cells = cellsXml(rowNumber, row.cells, DATA_ROW);\n break;\n case \"group\":\n level = row.level;\n depth = row.level + 1;\n // The label takes the first column, as the grid's group header\n // spans the row; an inline aggregate there gives way to it.\n cells = cellsXml(rowNumber, row.cells, AGGREGATE_ROW);\n cells[0] = stringCell(`A${String(rowNumber)}`, row.label, boldText);\n break;\n case \"groupSummary\":\n level = row.level;\n depth = row.level;\n cells = cellsXml(rowNumber, row.cells, AGGREGATE_ROW);\n break;\n }\n const outlineLevel = outlined ? Math.min(level, MAX_OUTLINE_LEVEL) : 0;\n deepest = Math.max(deepest, outlineLevel);\n rows.push(rowXml(rowNumber, cells, outlineLevel));\n }\n\n if (table.footer !== null) {\n const rowNumber = nextRowNumber();\n rows.push(\n rowXml(\n rowNumber,\n cellsXml(rowNumber, table.footer.cells, AGGREGATE_ROW),\n 0,\n ),\n );\n }\n\n // The outline buttons sit beside each group header, above its rows,\n // rather than below them where Excel puts them by default.\n const sheetProperties = outlined\n ? '<sheetPr><outlinePr summaryBelow=\"0\"/></sheetPr>'\n : \"\";\n const frozenPane =\n headerRow === undefined\n ? \"\"\n : `<sheetViews><sheetView workbookViewId=\"0\"><pane ySplit=\"${String(headerRow)}\" topLeftCell=\"A${String(headerRow + 1)}\" activePane=\"bottomLeft\" state=\"frozen\"/><selection pane=\"bottomLeft\"/></sheetView></sheetViews>`;\n const outlineLevels =\n deepest > 0 ? ` outlineLevelRow=\"${String(deepest)}\"` : \"\";\n return `${XML_DECLARATION}<worksheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\">${sheetProperties}${frozenPane}<sheetFormatPr defaultRowHeight=\"15\"${outlineLevels}/>${colsXml(layouts)}<sheetData>${rows.join(\"\")}</sheetData></worksheet>`;\n}\n","import { escapeXml, XML_DECLARATION } from \"../xml\";\n\n/** How a cell looks: its number format code (General when absent) and weight. */\nexport interface CellStyle {\n readonly numberFormat?: string | undefined;\n readonly bold?: boolean | undefined;\n}\n\n/**\n * The workbook's styles, collected while the sheet is written: each cell\n * asks for the index of its style, and the styles part then lists exactly\n * the styles some cell used.\n */\nexport interface StyleSheet {\n /** The index a cell's `s` attribute names for `style`; `0` is the plain default. */\n readonly index: (style: CellStyle) => number;\n /** The styles part, holding every style `index` has handed out. */\n readonly xml: () => string;\n}\n\n/** Excel numbers custom formats from here; lower ids are its built-ins. */\nconst FIRST_CUSTOM_FORMAT = 164;\n\nconst FONT = '<sz val=\"11\"/><name val=\"Calibri\"/><family val=\"2\"/>';\n\n/**\n * The fonts, fills, borders and cell style every styles part carries.\n * Excel reserves the first two fills, so both are written though no cell\n * is filled.\n */\nconst FONTS = `<fonts count=\"2\"><font>${FONT}</font><font><b/>${FONT}</font></fonts>`;\nconst FILLS =\n '<fills count=\"2\"><fill><patternFill patternType=\"none\"/></fill><fill><patternFill patternType=\"gray125\"/></fill></fills>';\nconst BORDERS =\n '<borders count=\"1\"><border><left/><right/><top/><bottom/><diagonal/></border></borders>';\nconst CELL_STYLE_FORMATS =\n '<cellStyleXfs count=\"1\"><xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\"/></cellStyleXfs>';\nconst CELL_STYLES =\n '<cellStyles count=\"1\"><cellStyle name=\"Normal\" xfId=\"0\" builtinId=\"0\"/></cellStyles>';\n\nexport function createStyleSheet(): StyleSheet {\n const formatIds = new Map<string, number>();\n const styles = new Map<string, number>([[\"0|\", 0]]);\n const cellFormats = [\n '<xf numFmtId=\"0\" fontId=\"0\" fillId=\"0\" borderId=\"0\" xfId=\"0\"/>',\n ];\n\n function formatId(code: string | undefined): number {\n if (code === undefined) {\n return 0;\n }\n let id = formatIds.get(code);\n if (id === undefined) {\n id = FIRST_CUSTOM_FORMAT + formatIds.size;\n formatIds.set(code, id);\n }\n return id;\n }\n\n function index({ numberFormat, bold = false }: CellStyle): number {\n const key = `${bold ? \"1\" : \"0\"}|${numberFormat ?? \"\"}`;\n const existing = styles.get(key);\n if (existing !== undefined) {\n return existing;\n }\n const added = cellFormats.length;\n cellFormats.push(\n `<xf numFmtId=\"${String(formatId(numberFormat))}\" fontId=\"${bold ? \"1\" : \"0\"}\" fillId=\"0\" borderId=\"0\" xfId=\"0\" applyNumberFormat=\"1\" applyFont=\"1\"/>`,\n );\n styles.set(key, added);\n return added;\n }\n\n function xml(): string {\n const numberFormats =\n formatIds.size === 0\n ? \"\"\n : `<numFmts count=\"${String(formatIds.size)}\">${[...formatIds]\n .map(\n ([code, id]) =>\n `<numFmt numFmtId=\"${String(id)}\" formatCode=\"${escapeXml(code)}\"/>`,\n )\n .join(\"\")}</numFmts>`;\n return `${XML_DECLARATION}<styleSheet xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\">${numberFormats}${FONTS}${FILLS}${BORDERS}${CELL_STYLE_FORMATS}<cellXfs count=\"${String(cellFormats.length)}\">${cellFormats.join(\"\")}</cellXfs>${CELL_STYLES}</styleSheet>`;\n }\n\n return { index, xml };\n}\n","import { escapeXml, XML_DECLARATION } from \"../xml\";\n\n/** A sheet name as a formula writes it: quoted, a quote inside doubled. */\nfunction quotedSheetName(sheetName: string): string {\n return `'${sheetName.replaceAll(\"'\", \"''\")}'`;\n}\n\n/**\n * The workbook part: its one sheet, and — when the sheet has a header row —\n * that row as the print titles, so a printout repeats the column names on\n * every page as the frozen pane keeps them on screen. `headerRow` is\n * 1-based, as Excel numbers rows.\n */\nexport function workbookXml(\n sheetName: string,\n headerRow: number | undefined,\n): string {\n const printTitles =\n headerRow === undefined\n ? \"\"\n : `<definedNames><definedName name=\"_xlnm.Print_Titles\" localSheetId=\"0\">${escapeXml(\n `${quotedSheetName(sheetName)}!$${String(headerRow)}:$${String(headerRow)}`,\n )}</definedName></definedNames>`;\n return `${XML_DECLARATION}<workbook xmlns=\"http://schemas.openxmlformats.org/spreadsheetml/2006/main\" xmlns:r=\"http://schemas.openxmlformats.org/officeDocument/2006/relationships\"><sheets><sheet name=\"${escapeXml(sheetName)}\" sheetId=\"1\" r:id=\"rId1\"/></sheets>${printTitles}</workbook>`;\n}\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,55 @@
1
+ {
2
+ "name": "@gridkitjs/export-xlsx",
3
+ "version": "0.0.0-dev-20261106222533",
4
+ "description": "An Excel (.xlsx) export writer for GridKit: typed numbers and dates, number formats, a frozen header, column widths, groups and totals.",
5
+ "license": "MIT",
6
+ "author": "Blagoja Blazhevski",
7
+ "homepage": "https://github.com/gridkitjs/gridkitjs#readme",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "git+https://github.com/gridkitjs/gridkitjs.git",
11
+ "directory": "packages/export-xlsx"
12
+ },
13
+ "keywords": [
14
+ "datagrid",
15
+ "grid",
16
+ "table",
17
+ "export",
18
+ "excel",
19
+ "xlsx",
20
+ "typescript",
21
+ "framework-agnostic"
22
+ ],
23
+ "type": "module",
24
+ "engines": {
25
+ "node": ">=22"
26
+ },
27
+ "main": "./dist/index.js",
28
+ "types": "./dist/index.d.ts",
29
+ "exports": {
30
+ ".": {
31
+ "types": "./dist/index.d.ts",
32
+ "import": "./dist/index.js"
33
+ }
34
+ },
35
+ "files": [
36
+ "dist",
37
+ "LICENSE",
38
+ "README.md"
39
+ ],
40
+ "dependencies": {
41
+ "fflate": "^0.8.3",
42
+ "@gridkitjs/core": "0.0.0-dev-20261106222533"
43
+ },
44
+ "devDependencies": {
45
+ "tsup": "^8.5.1",
46
+ "typescript": "^6.0.3",
47
+ "vitest": "^4.1.10"
48
+ },
49
+ "scripts": {
50
+ "build": "tsup",
51
+ "typecheck": "tsc --noEmit",
52
+ "lint": "eslint src",
53
+ "test": "vitest run"
54
+ }
55
+ }