@lichens-innovation/ts-common 1.8.0 → 1.9.0
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 +40 -0
- package/dist/csv.cjs +37 -0
- package/dist/csv.cjs.map +1 -0
- package/dist/csv.d.cts +5 -0
- package/dist/csv.d.ts +5 -0
- package/dist/csv.js +29 -0
- package/dist/csv.js.map +1 -0
- package/dist/dimensions.utils-BwIBA5op.d.cts +6 -0
- package/dist/dimensions.utils-BwIBA5op.d.ts +6 -0
- package/dist/excel.cjs +177 -0
- package/dist/excel.cjs.map +1 -0
- package/dist/excel.d.cts +109 -0
- package/dist/excel.d.ts +109 -0
- package/dist/excel.js +167 -0
- package/dist/excel.js.map +1 -0
- package/dist/index.cjs +80 -176
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -6
- package/dist/index.d.ts +3 -6
- package/dist/index.js +5 -76
- package/dist/index.js.map +1 -1
- package/dist/mime.cjs +20 -0
- package/dist/mime.cjs.map +1 -0
- package/dist/mime.d.cts +3 -0
- package/dist/mime.d.ts +3 -0
- package/dist/mime.js +14 -0
- package/dist/mime.js.map +1 -0
- package/dist/pdf.cjs +273 -0
- package/dist/pdf.cjs.map +1 -0
- package/dist/pdf.d.cts +140 -0
- package/dist/pdf.d.ts +140 -0
- package/dist/pdf.js +264 -0
- package/dist/pdf.js.map +1 -0
- package/dist/web.cjs +235 -0
- package/dist/web.cjs.map +1 -0
- package/dist/web.d.cts +82 -0
- package/dist/web.d.ts +82 -0
- package/dist/web.js +219 -0
- package/dist/web.js.map +1 -0
- package/package.json +55 -13
package/README.md
CHANGED
|
@@ -2,6 +2,8 @@
|
|
|
2
2
|
|
|
3
3
|
Reusable generic typescript utilities, types, constants, helpers
|
|
4
4
|
|
|
5
|
+
> **Note:** The default export is **pure TypeScript** and **environment-agnostic**—it has no dependency on Node.js, browser, or React Native APIs, so the same code runs in any of these runtimes. Subpath imports such as `@lichens-innovation/ts-common/web` target a specific environment and rely on its APIs.
|
|
6
|
+
|
|
5
7
|
<!-- Package & Status -->
|
|
6
8
|
[](https://www.npmjs.com/package/@lichens-innovation/ts-common)
|
|
7
9
|
[](https://www.npmjs.com/package/@lichens-innovation/ts-common)
|
|
@@ -27,6 +29,12 @@ Table of content
|
|
|
27
29
|
- [ts-common](#ts-common)
|
|
28
30
|
- [Prerequisites](#prerequisites)
|
|
29
31
|
- [Scripts](#scripts)
|
|
32
|
+
- [Optional modules](#optional-modules)
|
|
33
|
+
- [Excel (`@lichens-innovation/ts-common/excel`)](#excel-lichens-innovationts-commonexcel)
|
|
34
|
+
- [CSV (`@lichens-innovation/ts-common/csv`)](#csv-lichens-innovationts-commoncsv)
|
|
35
|
+
- [PDF (`@lichens-innovation/ts-common/pdf`)](#pdf-lichens-innovationts-commonpdf)
|
|
36
|
+
- [Web (`@lichens-innovation/ts-common/web`)](#web-lichens-innovationts-commonweb)
|
|
37
|
+
- [MIME (`@lichens-innovation/ts-common/mime`)](#mime-lichens-innovationts-commonmime)
|
|
30
38
|
- [Contributions](#contributions)
|
|
31
39
|
- [Unit tests](#unit-tests)
|
|
32
40
|
- [Library semantic versioning](#library-semantic-versioning)
|
|
@@ -50,6 +58,38 @@ Table of content
|
|
|
50
58
|
| `yarn test:coverage` | Runs tests with coverage report using Vitest |
|
|
51
59
|
| `yarn test:watch` | Runs tests in watch mode using Vitest |
|
|
52
60
|
|
|
61
|
+
## Optional modules
|
|
62
|
+
|
|
63
|
+
This library provides optional subpath modules with external dependencies. Install only the dependencies you need.
|
|
64
|
+
|
|
65
|
+
### Excel (`@lichens-innovation/ts-common/excel`)
|
|
66
|
+
|
|
67
|
+
```bash
|
|
68
|
+
npm install exceljs mime
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
### CSV (`@lichens-innovation/ts-common/csv`)
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
npm install papaparse mime
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
### PDF (`@lichens-innovation/ts-common/pdf`)
|
|
78
|
+
|
|
79
|
+
```bash
|
|
80
|
+
npm install jspdf jspdf-autotable
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
### Web (`@lichens-innovation/ts-common/web`)
|
|
84
|
+
|
|
85
|
+
No external dependency required.
|
|
86
|
+
|
|
87
|
+
### MIME (`@lichens-innovation/ts-common/mime`)
|
|
88
|
+
|
|
89
|
+
```bash
|
|
90
|
+
npm install mime
|
|
91
|
+
```
|
|
92
|
+
|
|
53
93
|
## Contributions
|
|
54
94
|
|
|
55
95
|
Contributions to the project are made by simply improving the current codebase and then creating a Pull Request. If the version field in `package.json` is incremented, the build will be automatically triggered when the PR is merged into the `main` branch, and the new version will be published to our enterprise Git repository.
|
package/dist/csv.cjs
ADDED
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var Papa = require('papaparse');
|
|
4
|
+
var mime = require('mime');
|
|
5
|
+
|
|
6
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
7
|
+
|
|
8
|
+
var Papa__default = /*#__PURE__*/_interopDefault(Papa);
|
|
9
|
+
var mime__default = /*#__PURE__*/_interopDefault(mime);
|
|
10
|
+
|
|
11
|
+
// src/csv/index.ts
|
|
12
|
+
var getMimeType = (extension) => {
|
|
13
|
+
const mimeType = mime__default.default.getType(extension);
|
|
14
|
+
if (!mimeType) {
|
|
15
|
+
throw new Error(`[getMimeType] Mime type not found for extension: "${extension}"`);
|
|
16
|
+
}
|
|
17
|
+
return mimeType;
|
|
18
|
+
};
|
|
19
|
+
|
|
20
|
+
// src/csv/index.ts
|
|
21
|
+
var convertToCsvRow = (row) => {
|
|
22
|
+
const stringRow = {};
|
|
23
|
+
for (const [key, value] of Object.entries(row)) {
|
|
24
|
+
stringRow[key] = String(value ?? "");
|
|
25
|
+
}
|
|
26
|
+
return stringRow;
|
|
27
|
+
};
|
|
28
|
+
var generateCsvBlob = (data) => {
|
|
29
|
+
const rows = data.map(convertToCsvRow);
|
|
30
|
+
const csv = Papa__default.default.unparse(rows, { header: true, delimiter: "," });
|
|
31
|
+
return new Blob([csv], { type: getMimeType("csv") });
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
exports.convertToCsvRow = convertToCsvRow;
|
|
35
|
+
exports.generateCsvBlob = generateCsvBlob;
|
|
36
|
+
//# sourceMappingURL=csv.cjs.map
|
|
37
|
+
//# sourceMappingURL=csv.cjs.map
|
package/dist/csv.cjs.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/mime/index.ts","../src/csv/index.ts"],"names":["mime","Papa"],"mappings":";;;;;;;;;;;AAEO,IAAM,WAAA,GAAc,CAAC,SAAA,KAA8B;AACxD,EAAA,MAAM,QAAA,GAAWA,qBAAA,CAAK,OAAA,CAAQ,SAAS,CAAA;AACvC,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,kDAAA,EAAqD,SAAS,CAAA,CAAA,CAAG,CAAA;AAAA,EACnF;AAEA,EAAA,OAAO,QAAA;AACT,CAAA;;;ACJO,IAAM,eAAA,GAAkB,CAAC,GAAA,KAA2D;AACzF,EAAA,MAAM,YAAoC,EAAC;AAE3C,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,GAAG,CAAA,EAAG;AAC9C,IAAA,SAAA,CAAU,GAAG,CAAA,GAAI,MAAA,CAAO,KAAA,IAAS,EAAE,CAAA;AAAA,EACrC;AAEA,EAAA,OAAO,SAAA;AACT;AAEO,IAAM,eAAA,GAAkB,CAAC,IAAA,KAA4C;AAC1E,EAAA,MAAM,IAAA,GAAiC,IAAA,CAAK,GAAA,CAAI,eAAe,CAAA;AAC/D,EAAA,MAAM,GAAA,GAAMC,sBAAK,OAAA,CAAQ,IAAA,EAAM,EAAE,MAAA,EAAQ,IAAA,EAAM,SAAA,EAAW,GAAA,EAAK,CAAA;AAE/D,EAAA,OAAO,IAAI,IAAA,CAAK,CAAC,GAAG,CAAA,EAAG,EAAE,IAAA,EAAM,WAAA,CAAY,KAAK,CAAA,EAAG,CAAA;AACrD","file":"csv.cjs","sourcesContent":["import mime from \"mime\";\n\nexport const getMimeType = (extension: string): string => {\n const mimeType = mime.getType(extension);\n if (!mimeType) {\n throw new Error(`[getMimeType] Mime type not found for extension: \"${extension}\"`);\n }\n\n return mimeType;\n};\n","import Papa from \"papaparse\";\nimport { getMimeType } from \"../mime\";\n\nexport type CellValue = string | number | null | undefined;\n\nexport const convertToCsvRow = (row: Record<string, CellValue>): Record<string, string> => {\n const stringRow: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(row)) {\n stringRow[key] = String(value ?? \"\");\n }\n\n return stringRow;\n};\n\nexport const generateCsvBlob = (data: Record<string, CellValue>[]): Blob => {\n const rows: Record<string, string>[] = data.map(convertToCsvRow);\n const csv = Papa.unparse(rows, { header: true, delimiter: \",\" });\n\n return new Blob([csv], { type: getMimeType(\"csv\") });\n};\n"]}
|
package/dist/csv.d.cts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
type CellValue = string | number | null | undefined;
|
|
2
|
+
declare const convertToCsvRow: (row: Record<string, CellValue>) => Record<string, string>;
|
|
3
|
+
declare const generateCsvBlob: (data: Record<string, CellValue>[]) => Blob;
|
|
4
|
+
|
|
5
|
+
export { type CellValue, convertToCsvRow, generateCsvBlob };
|
package/dist/csv.d.ts
ADDED
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
type CellValue = string | number | null | undefined;
|
|
2
|
+
declare const convertToCsvRow: (row: Record<string, CellValue>) => Record<string, string>;
|
|
3
|
+
declare const generateCsvBlob: (data: Record<string, CellValue>[]) => Blob;
|
|
4
|
+
|
|
5
|
+
export { type CellValue, convertToCsvRow, generateCsvBlob };
|
package/dist/csv.js
ADDED
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import Papa from 'papaparse';
|
|
2
|
+
import mime from 'mime';
|
|
3
|
+
|
|
4
|
+
// src/csv/index.ts
|
|
5
|
+
var getMimeType = (extension) => {
|
|
6
|
+
const mimeType = mime.getType(extension);
|
|
7
|
+
if (!mimeType) {
|
|
8
|
+
throw new Error(`[getMimeType] Mime type not found for extension: "${extension}"`);
|
|
9
|
+
}
|
|
10
|
+
return mimeType;
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
// src/csv/index.ts
|
|
14
|
+
var convertToCsvRow = (row) => {
|
|
15
|
+
const stringRow = {};
|
|
16
|
+
for (const [key, value] of Object.entries(row)) {
|
|
17
|
+
stringRow[key] = String(value ?? "");
|
|
18
|
+
}
|
|
19
|
+
return stringRow;
|
|
20
|
+
};
|
|
21
|
+
var generateCsvBlob = (data) => {
|
|
22
|
+
const rows = data.map(convertToCsvRow);
|
|
23
|
+
const csv = Papa.unparse(rows, { header: true, delimiter: "," });
|
|
24
|
+
return new Blob([csv], { type: getMimeType("csv") });
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
export { convertToCsvRow, generateCsvBlob };
|
|
28
|
+
//# sourceMappingURL=csv.js.map
|
|
29
|
+
//# sourceMappingURL=csv.js.map
|
package/dist/csv.js.map
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/mime/index.ts","../src/csv/index.ts"],"names":[],"mappings":";;;;AAEO,IAAM,WAAA,GAAc,CAAC,SAAA,KAA8B;AACxD,EAAA,MAAM,QAAA,GAAW,IAAA,CAAK,OAAA,CAAQ,SAAS,CAAA;AACvC,EAAA,IAAI,CAAC,QAAA,EAAU;AACb,IAAA,MAAM,IAAI,KAAA,CAAM,CAAA,kDAAA,EAAqD,SAAS,CAAA,CAAA,CAAG,CAAA;AAAA,EACnF;AAEA,EAAA,OAAO,QAAA;AACT,CAAA;;;ACJO,IAAM,eAAA,GAAkB,CAAC,GAAA,KAA2D;AACzF,EAAA,MAAM,YAAoC,EAAC;AAE3C,EAAA,KAAA,MAAW,CAAC,GAAA,EAAK,KAAK,KAAK,MAAA,CAAO,OAAA,CAAQ,GAAG,CAAA,EAAG;AAC9C,IAAA,SAAA,CAAU,GAAG,CAAA,GAAI,MAAA,CAAO,KAAA,IAAS,EAAE,CAAA;AAAA,EACrC;AAEA,EAAA,OAAO,SAAA;AACT;AAEO,IAAM,eAAA,GAAkB,CAAC,IAAA,KAA4C;AAC1E,EAAA,MAAM,IAAA,GAAiC,IAAA,CAAK,GAAA,CAAI,eAAe,CAAA;AAC/D,EAAA,MAAM,GAAA,GAAM,KAAK,OAAA,CAAQ,IAAA,EAAM,EAAE,MAAA,EAAQ,IAAA,EAAM,SAAA,EAAW,GAAA,EAAK,CAAA;AAE/D,EAAA,OAAO,IAAI,IAAA,CAAK,CAAC,GAAG,CAAA,EAAG,EAAE,IAAA,EAAM,WAAA,CAAY,KAAK,CAAA,EAAG,CAAA;AACrD","file":"csv.js","sourcesContent":["import mime from \"mime\";\n\nexport const getMimeType = (extension: string): string => {\n const mimeType = mime.getType(extension);\n if (!mimeType) {\n throw new Error(`[getMimeType] Mime type not found for extension: \"${extension}\"`);\n }\n\n return mimeType;\n};\n","import Papa from \"papaparse\";\nimport { getMimeType } from \"../mime\";\n\nexport type CellValue = string | number | null | undefined;\n\nexport const convertToCsvRow = (row: Record<string, CellValue>): Record<string, string> => {\n const stringRow: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(row)) {\n stringRow[key] = String(value ?? \"\");\n }\n\n return stringRow;\n};\n\nexport const generateCsvBlob = (data: Record<string, CellValue>[]): Blob => {\n const rows: Record<string, string>[] = data.map(convertToCsvRow);\n const csv = Papa.unparse(rows, { header: true, delimiter: \",\" });\n\n return new Blob([csv], { type: getMimeType(\"csv\") });\n};\n"]}
|
package/dist/excel.cjs
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var ExcelJS = require('exceljs');
|
|
4
|
+
var mime = require('mime');
|
|
5
|
+
|
|
6
|
+
function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
|
|
7
|
+
|
|
8
|
+
var ExcelJS__default = /*#__PURE__*/_interopDefault(ExcelJS);
|
|
9
|
+
var mime__default = /*#__PURE__*/_interopDefault(mime);
|
|
10
|
+
|
|
11
|
+
// src/excel/excel-generator.ts
|
|
12
|
+
var WorksheetBuilder = class {
|
|
13
|
+
worksheet;
|
|
14
|
+
currentRow;
|
|
15
|
+
defaultRowStyles;
|
|
16
|
+
constructor(worksheet) {
|
|
17
|
+
this.worksheet = worksheet;
|
|
18
|
+
this.currentRow = 1;
|
|
19
|
+
this.defaultRowStyles = [];
|
|
20
|
+
}
|
|
21
|
+
/** Returns the current row index (1-based). */
|
|
22
|
+
get rowCount() {
|
|
23
|
+
return this.currentRow;
|
|
24
|
+
}
|
|
25
|
+
addCell({ row, col, value, style }) {
|
|
26
|
+
const cell = this.worksheet.getCell(row + 1, col + 1);
|
|
27
|
+
cell.value = value ?? "";
|
|
28
|
+
if (style) {
|
|
29
|
+
this.applyStyle(cell, style);
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
addRow({ values, styles }) {
|
|
33
|
+
const stylesToUse = styles ?? this.defaultRowStyles;
|
|
34
|
+
const row = this.worksheet.addRow(values);
|
|
35
|
+
for (let col = 0; col < values.length; col++) {
|
|
36
|
+
const style = stylesToUse[col];
|
|
37
|
+
if (style) {
|
|
38
|
+
const cell = row.getCell(col + 1);
|
|
39
|
+
this.applyStyle(cell, style);
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
this.currentRow++;
|
|
43
|
+
}
|
|
44
|
+
setDefaultRowStyles(styles) {
|
|
45
|
+
this.defaultRowStyles = styles;
|
|
46
|
+
}
|
|
47
|
+
addEmptyRow() {
|
|
48
|
+
this.worksheet.addRow([]);
|
|
49
|
+
this.currentRow++;
|
|
50
|
+
}
|
|
51
|
+
setColumnWidths(columnWidths) {
|
|
52
|
+
this.worksheet.columns = columnWidths.map((width) => ({ width }));
|
|
53
|
+
}
|
|
54
|
+
getWorksheet() {
|
|
55
|
+
return this.worksheet;
|
|
56
|
+
}
|
|
57
|
+
applyStyle(cell, style) {
|
|
58
|
+
if (style.font) {
|
|
59
|
+
cell.font = { ...cell.font, ...style.font };
|
|
60
|
+
}
|
|
61
|
+
if (style.fill) {
|
|
62
|
+
cell.fill = style.fill;
|
|
63
|
+
}
|
|
64
|
+
if (style.alignment) {
|
|
65
|
+
cell.alignment = { ...cell.alignment, ...style.alignment };
|
|
66
|
+
}
|
|
67
|
+
if (style.border) {
|
|
68
|
+
cell.border = { ...cell.border, ...style.border };
|
|
69
|
+
}
|
|
70
|
+
if (style.numFmt) {
|
|
71
|
+
cell.numFmt = style.numFmt;
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
// src/excel/excel-generator.types.ts
|
|
77
|
+
var rgbToArgb = (rgb) => {
|
|
78
|
+
return { argb: `FF${rgb}` };
|
|
79
|
+
};
|
|
80
|
+
var getMimeType = (extension) => {
|
|
81
|
+
const mimeType = mime__default.default.getType(extension);
|
|
82
|
+
if (!mimeType) {
|
|
83
|
+
throw new Error(`[getMimeType] Mime type not found for extension: "${extension}"`);
|
|
84
|
+
}
|
|
85
|
+
return mimeType;
|
|
86
|
+
};
|
|
87
|
+
|
|
88
|
+
// src/utils/types.utils.ts
|
|
89
|
+
var isNullish = (value) => value === null || value === void 0;
|
|
90
|
+
|
|
91
|
+
// src/excel/exceljs.utils.ts
|
|
92
|
+
var sanitizeWorksheetTitle = (title) => {
|
|
93
|
+
const MAX_LENGTH = 31;
|
|
94
|
+
const INVALID_CHARS = /[:/\\?*[\]]/g;
|
|
95
|
+
return title.trim().replace(INVALID_CHARS, "").slice(0, MAX_LENGTH);
|
|
96
|
+
};
|
|
97
|
+
var toSafeCellValue = ({ value, valueType }) => {
|
|
98
|
+
if (!isNullish(value) && valueType === "number") {
|
|
99
|
+
const numValue = typeof value === "number" ? value : Number(value);
|
|
100
|
+
return isNaN(numValue) ? value : numValue;
|
|
101
|
+
}
|
|
102
|
+
return value ?? "";
|
|
103
|
+
};
|
|
104
|
+
var toSafeCellValues = ({ headers, rowData, columnsMetadata }) => {
|
|
105
|
+
return headers.map((header) => {
|
|
106
|
+
const value = rowData[header];
|
|
107
|
+
const metadata = columnsMetadata?.[header];
|
|
108
|
+
const valueType = metadata?.valueType ?? "auto";
|
|
109
|
+
return toSafeCellValue({ value, valueType });
|
|
110
|
+
});
|
|
111
|
+
};
|
|
112
|
+
var applyTextFormatting = ({ row, headers, columnsMetadata }) => {
|
|
113
|
+
for (let colIndex = 0; colIndex < headers.length; colIndex++) {
|
|
114
|
+
const header = headers[colIndex];
|
|
115
|
+
const metadata = columnsMetadata?.[header];
|
|
116
|
+
const valueType = metadata?.valueType ?? "auto";
|
|
117
|
+
if (valueType === "text") {
|
|
118
|
+
const cell = row.getCell(colIndex + 1);
|
|
119
|
+
cell.numFmt = "@";
|
|
120
|
+
}
|
|
121
|
+
}
|
|
122
|
+
};
|
|
123
|
+
var applyHeaderStyles = (headerRow) => {
|
|
124
|
+
headerRow.font = { bold: true };
|
|
125
|
+
headerRow.fill = {
|
|
126
|
+
type: "pattern",
|
|
127
|
+
pattern: "solid",
|
|
128
|
+
fgColor: { argb: "FFE0E0E0" }
|
|
129
|
+
};
|
|
130
|
+
headerRow.alignment = { wrapText: true, vertical: "top" };
|
|
131
|
+
};
|
|
132
|
+
var setupHeaderFrozenRow = ({ worksheet, headers }) => {
|
|
133
|
+
const headerRow = worksheet.addRow(headers);
|
|
134
|
+
applyHeaderStyles(headerRow);
|
|
135
|
+
worksheet.views = [{ state: "frozen", ySplit: 1 }];
|
|
136
|
+
};
|
|
137
|
+
var applyDataRowStyles = ({ worksheet, startRowIndex, endRowIndex }) => {
|
|
138
|
+
for (let rowIndex = startRowIndex; rowIndex <= endRowIndex; rowIndex++) {
|
|
139
|
+
const row = worksheet.getRow(rowIndex);
|
|
140
|
+
row.alignment = { wrapText: false, vertical: "top" };
|
|
141
|
+
}
|
|
142
|
+
};
|
|
143
|
+
var setColumnWidths = ({ worksheet, headers, columnsMetadata }) => {
|
|
144
|
+
worksheet.columns = headers.map((header) => {
|
|
145
|
+
const metadata = columnsMetadata[header];
|
|
146
|
+
return metadata ? { width: metadata.width } : {};
|
|
147
|
+
});
|
|
148
|
+
};
|
|
149
|
+
var addDataRows = ({ worksheet, headers, columnsMetadata, data }) => {
|
|
150
|
+
for (const rowData of data) {
|
|
151
|
+
const safeCellValues = toSafeCellValues({ headers, rowData, columnsMetadata });
|
|
152
|
+
const row = worksheet.addRow(safeCellValues);
|
|
153
|
+
applyTextFormatting({ row, headers, columnsMetadata });
|
|
154
|
+
}
|
|
155
|
+
applyDataRowStyles({ worksheet, startRowIndex: 2, endRowIndex: data.length + 1 });
|
|
156
|
+
};
|
|
157
|
+
var generateExcelBlob = async (args) => {
|
|
158
|
+
const { data, columnsMetadata, worksheetTitle } = args;
|
|
159
|
+
if (data.length === 0) {
|
|
160
|
+
throw new Error("No data to export");
|
|
161
|
+
}
|
|
162
|
+
const headers = Object.keys(data[0]);
|
|
163
|
+
const workbook = new ExcelJS__default.default.Workbook();
|
|
164
|
+
const worksheet = workbook.addWorksheet(sanitizeWorksheetTitle(worksheetTitle));
|
|
165
|
+
setupHeaderFrozenRow({ worksheet, headers });
|
|
166
|
+
addDataRows({ worksheet, headers, columnsMetadata, data });
|
|
167
|
+
setColumnWidths({ worksheet, headers, columnsMetadata });
|
|
168
|
+
const buffer = await workbook.xlsx.writeBuffer();
|
|
169
|
+
return new Blob([buffer], { type: getMimeType("xlsx") });
|
|
170
|
+
};
|
|
171
|
+
|
|
172
|
+
exports.WorksheetBuilder = WorksheetBuilder;
|
|
173
|
+
exports.generateExcelBlob = generateExcelBlob;
|
|
174
|
+
exports.rgbToArgb = rgbToArgb;
|
|
175
|
+
exports.sanitizeWorksheetTitle = sanitizeWorksheetTitle;
|
|
176
|
+
//# sourceMappingURL=excel.cjs.map
|
|
177
|
+
//# sourceMappingURL=excel.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/excel/excel-generator.ts","../src/excel/excel-generator.types.ts","../src/mime/index.ts","../src/utils/types.utils.ts","../src/excel/exceljs.utils.ts"],"names":["mime","ExcelJS"],"mappings":"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","file":"excel.cjs","sourcesContent":["import ExcelJS from \"exceljs\";\nimport type { CellStyle, CellValue } from \"./excel-generator.types\";\n\ninterface AddCellArgs {\n row: number;\n col: number;\n value: CellValue;\n style?: CellStyle;\n}\n\ninterface AddRowArgs {\n values: CellValue[];\n styles?: CellStyle[];\n}\n\nexport class WorksheetBuilder {\n private worksheet: ExcelJS.Worksheet;\n private currentRow: number;\n private defaultRowStyles: CellStyle[];\n\n constructor(worksheet: ExcelJS.Worksheet) {\n this.worksheet = worksheet;\n this.currentRow = 1; // ExcelJS uses 1-based indexing\n this.defaultRowStyles = [];\n }\n\n /** Returns the current row index (1-based). */\n get rowCount(): number {\n return this.currentRow;\n }\n\n addCell({ row, col, value, style }: AddCellArgs): void {\n const cell = this.worksheet.getCell(row + 1, col + 1); // Convert to 1-based\n cell.value = value ?? \"\";\n\n if (style) {\n this.applyStyle(cell, style);\n }\n }\n\n addRow({ values, styles }: AddRowArgs): void {\n const stylesToUse = styles ?? this.defaultRowStyles;\n const row = this.worksheet.addRow(values);\n\n // Apply styles to each cell in the row\n for (let col = 0; col < values.length; col++) {\n const style = stylesToUse[col];\n if (style) {\n const cell = row.getCell(col + 1); // ExcelJS uses 1-based indexing\n this.applyStyle(cell, style);\n }\n }\n\n this.currentRow++;\n }\n\n setDefaultRowStyles(styles: CellStyle[]): void {\n this.defaultRowStyles = styles;\n }\n\n addEmptyRow(): void {\n this.worksheet.addRow([]);\n this.currentRow++;\n }\n\n setColumnWidths(columnWidths: number[]): void {\n this.worksheet.columns = columnWidths.map((width) => ({ width }));\n }\n\n getWorksheet(): ExcelJS.Worksheet {\n return this.worksheet;\n }\n\n private applyStyle(cell: ExcelJS.Cell, style: CellStyle): void {\n if (style.font) {\n cell.font = { ...cell.font, ...style.font };\n }\n\n if (style.fill) {\n cell.fill = style.fill as ExcelJS.Fill;\n }\n\n if (style.alignment) {\n cell.alignment = { ...cell.alignment, ...style.alignment };\n }\n\n if (style.border) {\n cell.border = { ...cell.border, ...style.border };\n }\n\n if (style.numFmt) {\n cell.numFmt = style.numFmt;\n }\n }\n}\n","import type { Font, Fill, Alignment } from \"exceljs\";\n\nexport type CellValue = string | number | null | undefined;\n\nexport type VerticalAlignment = \"top\" | \"middle\" | \"bottom\";\nexport type HorizontalAlignment = \"left\" | \"center\" | \"right\";\n\nexport type ColumnValueType = \"number\" | \"text\" | \"auto\";\n\nexport interface ColumnMetadata {\n name: string;\n width: number;\n valueType?: ColumnValueType;\n}\n\ninterface BorderSide {\n style?: \"thin\" | \"medium\" | \"thick\" | \"dotted\" | \"dashed\" | \"double\";\n color?: { argb: string };\n}\n\n/**\n * Type for ExcelJS cell borders.\n */\nexport interface CellBorder {\n top?: BorderSide;\n bottom?: BorderSide;\n left?: BorderSide;\n right?: BorderSide;\n}\n\n/**\n * Type for ExcelJS cell styles.\n * This is a simplified version of ExcelJS.Style that focuses on commonly used properties.\n */\nexport interface CellStyle {\n font?: Partial<Font>;\n fill?: Partial<Fill>;\n alignment?: Partial<Alignment>;\n border?: CellBorder;\n numFmt?: string;\n}\n\n/**\n * Helper type to convert RGB color string to ARGB format for ExcelJS.\n * ExcelJS uses ARGB format: \"FFFFFFFF\" where FF is alpha, then RGB.\n */\nexport type ArgbColor = { argb: string };\n\n/**\n * Helper function to convert RGB color string to ARGB format.\n * @param rgb - RGB color string without # (e.g., \"FFFFFF\")\n * @returns ARGB color object (e.g., { argb: \"FFFFFFFF\" })\n */\nexport const rgbToArgb = (rgb: string): ArgbColor => {\n return { argb: `FF${rgb}` };\n};\n","import mime from \"mime\";\n\nexport const getMimeType = (extension: string): string => {\n const mimeType = mime.getType(extension);\n if (!mimeType) {\n throw new Error(`[getMimeType] Mime type not found for extension: \"${extension}\"`);\n }\n\n return mimeType;\n};\n","export const NO_OP: () => void = () => {};\n\nexport const isNullish = (value: unknown): value is null | undefined => value === null || value === undefined;\n\nexport const isNumber = (value?: unknown | null): value is number => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'number') {\n return false;\n }\n\n if (isNaN(value)) {\n return false;\n }\n\n return true;\n};\n\nexport const isString = (value?: unknown | null): value is string => {\n if (isNullish(value)) {\n return false;\n }\n\n if (typeof value !== 'string') {\n return false;\n }\n\n return true;\n};\n","import ExcelJS from \"exceljs\";\nimport { getMimeType } from \"../mime\";\nimport { isNullish } from \"../utils\";\nimport type { CellValue, ColumnMetadata } from \"./excel-generator.types\";\n\n/**\n * Sanitizes a worksheet title to comply with Excel's limitations.\n *\n * Excel enforces strict rules for worksheet names:\n * - Maximum length of 31 characters (legacy limitation from older Excel versions)\n * - Certain characters are reserved for special purposes and cannot be used:\n * - `:` (colon) - used in cell range references (e.g., A1:B2)\n * - `/` (forward slash) - used in file paths\n * - `\\` (backslash) - used in file paths and escape sequences\n * - `?` (question mark) - used as wildcard in formulas\n * - `*` (asterisk) - used as wildcard in formulas\n * - `[` and `]` (square brackets) - used in external references (e.g., [Book1.xlsx]Sheet1!A1)\n *\n * This function removes invalid characters and truncates the title to 31 characters\n * to ensure compatibility with Excel's file format specifications.\n *\n * @param title - The original worksheet title to sanitize\n * @returns A sanitized title that complies with Excel's naming rules\n */\nexport const sanitizeWorksheetTitle = (title: string): string => {\n const MAX_LENGTH = 31;\n const INVALID_CHARS = /[:/\\\\?*[\\]]/g;\n\n return title.trim().replace(INVALID_CHARS, \"\").slice(0, MAX_LENGTH);\n};\n\ninterface ToSafeCellValueArgs {\n value: CellValue;\n valueType: ColumnMetadata[\"valueType\"];\n}\n\nconst toSafeCellValue = ({ value, valueType }: ToSafeCellValueArgs): CellValue => {\n if (!isNullish(value) && valueType === \"number\") {\n const numValue = typeof value === \"number\" ? value : Number(value);\n return isNaN(numValue) ? value : numValue;\n }\n\n return value ?? \"\";\n};\n\ninterface ToSafeCellValuesArgs {\n headers: string[];\n rowData: Record<string, CellValue>;\n columnsMetadata?: Record<string, ColumnMetadata>;\n}\n\nconst toSafeCellValues = ({ headers, rowData, columnsMetadata }: ToSafeCellValuesArgs): CellValue[] => {\n return headers.map((header) => {\n const value = rowData[header];\n const metadata = columnsMetadata?.[header];\n const valueType = metadata?.valueType ?? \"auto\";\n return toSafeCellValue({ value, valueType });\n });\n};\n\ninterface ApplyTextFormattingArgs {\n row: ExcelJS.Row;\n headers: string[];\n columnsMetadata?: Record<string, ColumnMetadata>;\n}\n\nconst applyTextFormatting = ({ row, headers, columnsMetadata }: ApplyTextFormattingArgs): void => {\n for (let colIndex = 0; colIndex < headers.length; colIndex++) {\n const header = headers[colIndex];\n const metadata = columnsMetadata?.[header];\n const valueType = metadata?.valueType ?? \"auto\";\n\n if (valueType === \"text\") {\n const cell = row.getCell(colIndex + 1);\n cell.numFmt = \"@\";\n }\n }\n};\n\nconst applyHeaderStyles = (headerRow: ExcelJS.Row): void => {\n headerRow.font = { bold: true };\n headerRow.fill = {\n type: \"pattern\",\n pattern: \"solid\",\n fgColor: { argb: \"FFE0E0E0\" },\n };\n headerRow.alignment = { wrapText: true, vertical: \"top\" };\n};\n\ninterface SetupHeaderFrozenRowArgs {\n worksheet: ExcelJS.Worksheet;\n headers: string[];\n}\n\nconst setupHeaderFrozenRow = ({ worksheet, headers }: SetupHeaderFrozenRowArgs): void => {\n const headerRow = worksheet.addRow(headers);\n applyHeaderStyles(headerRow);\n worksheet.views = [{ state: \"frozen\", ySplit: 1 }];\n};\n\ninterface ApplyDataRowStylesArgs {\n worksheet: ExcelJS.Worksheet;\n startRowIndex: number;\n endRowIndex: number;\n}\n\nconst applyDataRowStyles = ({ worksheet, startRowIndex, endRowIndex }: ApplyDataRowStylesArgs): void => {\n for (let rowIndex = startRowIndex; rowIndex <= endRowIndex; rowIndex++) {\n const row = worksheet.getRow(rowIndex);\n row.alignment = { wrapText: false, vertical: \"top\" };\n }\n};\n\ninterface SetColumnWidthsArgs {\n worksheet: ExcelJS.Worksheet;\n headers: string[];\n columnsMetadata: Record<string, ColumnMetadata>;\n}\n\nconst setColumnWidths = ({ worksheet, headers, columnsMetadata }: SetColumnWidthsArgs): void => {\n worksheet.columns = headers.map((header) => {\n const metadata = columnsMetadata[header];\n return metadata ? { width: metadata.width } : {};\n });\n};\n\ninterface AddDataRowsArgs {\n worksheet: ExcelJS.Worksheet;\n headers: string[];\n columnsMetadata: Record<string, ColumnMetadata>;\n data: Record<string, CellValue>[];\n}\n\nconst addDataRows = ({ worksheet, headers, columnsMetadata, data }: AddDataRowsArgs): void => {\n for (const rowData of data) {\n const safeCellValues = toSafeCellValues({ headers, rowData, columnsMetadata });\n const row = worksheet.addRow(safeCellValues);\n applyTextFormatting({ row, headers, columnsMetadata });\n }\n\n applyDataRowStyles({ worksheet, startRowIndex: 2, endRowIndex: data.length + 1 });\n};\n\ninterface GenerateExcelBlobArgs {\n worksheetTitle: string;\n data: Record<string, CellValue>[];\n /**\n * Metadata for columns, keyed by column name (translated header name).\n * If not provided, columns will use default width and auto-detect value types.\n */\n columnsMetadata: Record<string, ColumnMetadata>;\n}\n\nexport const generateExcelBlob = async (args: GenerateExcelBlobArgs): Promise<Blob> => {\n const { data, columnsMetadata, worksheetTitle } = args;\n\n if (data.length === 0) {\n throw new Error(\"No data to export\");\n }\n\n const headers = Object.keys(data[0]);\n const workbook = new ExcelJS.Workbook();\n const worksheet = workbook.addWorksheet(sanitizeWorksheetTitle(worksheetTitle));\n\n setupHeaderFrozenRow({ worksheet, headers });\n addDataRows({ worksheet, headers, columnsMetadata, data });\n setColumnWidths({ worksheet, headers, columnsMetadata });\n\n const buffer = await workbook.xlsx.writeBuffer();\n return new Blob([buffer], { type: getMimeType(\"xlsx\") });\n};\n"]}
|
package/dist/excel.d.cts
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import ExcelJS, { Font, Fill, Alignment } from 'exceljs';
|
|
2
|
+
|
|
3
|
+
type CellValue = string | number | null | undefined;
|
|
4
|
+
type VerticalAlignment = "top" | "middle" | "bottom";
|
|
5
|
+
type HorizontalAlignment = "left" | "center" | "right";
|
|
6
|
+
type ColumnValueType = "number" | "text" | "auto";
|
|
7
|
+
interface ColumnMetadata {
|
|
8
|
+
name: string;
|
|
9
|
+
width: number;
|
|
10
|
+
valueType?: ColumnValueType;
|
|
11
|
+
}
|
|
12
|
+
interface BorderSide {
|
|
13
|
+
style?: "thin" | "medium" | "thick" | "dotted" | "dashed" | "double";
|
|
14
|
+
color?: {
|
|
15
|
+
argb: string;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Type for ExcelJS cell borders.
|
|
20
|
+
*/
|
|
21
|
+
interface CellBorder {
|
|
22
|
+
top?: BorderSide;
|
|
23
|
+
bottom?: BorderSide;
|
|
24
|
+
left?: BorderSide;
|
|
25
|
+
right?: BorderSide;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Type for ExcelJS cell styles.
|
|
29
|
+
* This is a simplified version of ExcelJS.Style that focuses on commonly used properties.
|
|
30
|
+
*/
|
|
31
|
+
interface CellStyle {
|
|
32
|
+
font?: Partial<Font>;
|
|
33
|
+
fill?: Partial<Fill>;
|
|
34
|
+
alignment?: Partial<Alignment>;
|
|
35
|
+
border?: CellBorder;
|
|
36
|
+
numFmt?: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Helper type to convert RGB color string to ARGB format for ExcelJS.
|
|
40
|
+
* ExcelJS uses ARGB format: "FFFFFFFF" where FF is alpha, then RGB.
|
|
41
|
+
*/
|
|
42
|
+
type ArgbColor = {
|
|
43
|
+
argb: string;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Helper function to convert RGB color string to ARGB format.
|
|
47
|
+
* @param rgb - RGB color string without # (e.g., "FFFFFF")
|
|
48
|
+
* @returns ARGB color object (e.g., { argb: "FFFFFFFF" })
|
|
49
|
+
*/
|
|
50
|
+
declare const rgbToArgb: (rgb: string) => ArgbColor;
|
|
51
|
+
|
|
52
|
+
interface AddCellArgs {
|
|
53
|
+
row: number;
|
|
54
|
+
col: number;
|
|
55
|
+
value: CellValue;
|
|
56
|
+
style?: CellStyle;
|
|
57
|
+
}
|
|
58
|
+
interface AddRowArgs {
|
|
59
|
+
values: CellValue[];
|
|
60
|
+
styles?: CellStyle[];
|
|
61
|
+
}
|
|
62
|
+
declare class WorksheetBuilder {
|
|
63
|
+
private worksheet;
|
|
64
|
+
private currentRow;
|
|
65
|
+
private defaultRowStyles;
|
|
66
|
+
constructor(worksheet: ExcelJS.Worksheet);
|
|
67
|
+
/** Returns the current row index (1-based). */
|
|
68
|
+
get rowCount(): number;
|
|
69
|
+
addCell({ row, col, value, style }: AddCellArgs): void;
|
|
70
|
+
addRow({ values, styles }: AddRowArgs): void;
|
|
71
|
+
setDefaultRowStyles(styles: CellStyle[]): void;
|
|
72
|
+
addEmptyRow(): void;
|
|
73
|
+
setColumnWidths(columnWidths: number[]): void;
|
|
74
|
+
getWorksheet(): ExcelJS.Worksheet;
|
|
75
|
+
private applyStyle;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Sanitizes a worksheet title to comply with Excel's limitations.
|
|
80
|
+
*
|
|
81
|
+
* Excel enforces strict rules for worksheet names:
|
|
82
|
+
* - Maximum length of 31 characters (legacy limitation from older Excel versions)
|
|
83
|
+
* - Certain characters are reserved for special purposes and cannot be used:
|
|
84
|
+
* - `:` (colon) - used in cell range references (e.g., A1:B2)
|
|
85
|
+
* - `/` (forward slash) - used in file paths
|
|
86
|
+
* - `\` (backslash) - used in file paths and escape sequences
|
|
87
|
+
* - `?` (question mark) - used as wildcard in formulas
|
|
88
|
+
* - `*` (asterisk) - used as wildcard in formulas
|
|
89
|
+
* - `[` and `]` (square brackets) - used in external references (e.g., [Book1.xlsx]Sheet1!A1)
|
|
90
|
+
*
|
|
91
|
+
* This function removes invalid characters and truncates the title to 31 characters
|
|
92
|
+
* to ensure compatibility with Excel's file format specifications.
|
|
93
|
+
*
|
|
94
|
+
* @param title - The original worksheet title to sanitize
|
|
95
|
+
* @returns A sanitized title that complies with Excel's naming rules
|
|
96
|
+
*/
|
|
97
|
+
declare const sanitizeWorksheetTitle: (title: string) => string;
|
|
98
|
+
interface GenerateExcelBlobArgs {
|
|
99
|
+
worksheetTitle: string;
|
|
100
|
+
data: Record<string, CellValue>[];
|
|
101
|
+
/**
|
|
102
|
+
* Metadata for columns, keyed by column name (translated header name).
|
|
103
|
+
* If not provided, columns will use default width and auto-detect value types.
|
|
104
|
+
*/
|
|
105
|
+
columnsMetadata: Record<string, ColumnMetadata>;
|
|
106
|
+
}
|
|
107
|
+
declare const generateExcelBlob: (args: GenerateExcelBlobArgs) => Promise<Blob>;
|
|
108
|
+
|
|
109
|
+
export { type ArgbColor, type CellBorder, type CellStyle, type CellValue, type ColumnMetadata, type ColumnValueType, type HorizontalAlignment, type VerticalAlignment, WorksheetBuilder, generateExcelBlob, rgbToArgb, sanitizeWorksheetTitle };
|
package/dist/excel.d.ts
ADDED
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
import ExcelJS, { Font, Fill, Alignment } from 'exceljs';
|
|
2
|
+
|
|
3
|
+
type CellValue = string | number | null | undefined;
|
|
4
|
+
type VerticalAlignment = "top" | "middle" | "bottom";
|
|
5
|
+
type HorizontalAlignment = "left" | "center" | "right";
|
|
6
|
+
type ColumnValueType = "number" | "text" | "auto";
|
|
7
|
+
interface ColumnMetadata {
|
|
8
|
+
name: string;
|
|
9
|
+
width: number;
|
|
10
|
+
valueType?: ColumnValueType;
|
|
11
|
+
}
|
|
12
|
+
interface BorderSide {
|
|
13
|
+
style?: "thin" | "medium" | "thick" | "dotted" | "dashed" | "double";
|
|
14
|
+
color?: {
|
|
15
|
+
argb: string;
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* Type for ExcelJS cell borders.
|
|
20
|
+
*/
|
|
21
|
+
interface CellBorder {
|
|
22
|
+
top?: BorderSide;
|
|
23
|
+
bottom?: BorderSide;
|
|
24
|
+
left?: BorderSide;
|
|
25
|
+
right?: BorderSide;
|
|
26
|
+
}
|
|
27
|
+
/**
|
|
28
|
+
* Type for ExcelJS cell styles.
|
|
29
|
+
* This is a simplified version of ExcelJS.Style that focuses on commonly used properties.
|
|
30
|
+
*/
|
|
31
|
+
interface CellStyle {
|
|
32
|
+
font?: Partial<Font>;
|
|
33
|
+
fill?: Partial<Fill>;
|
|
34
|
+
alignment?: Partial<Alignment>;
|
|
35
|
+
border?: CellBorder;
|
|
36
|
+
numFmt?: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Helper type to convert RGB color string to ARGB format for ExcelJS.
|
|
40
|
+
* ExcelJS uses ARGB format: "FFFFFFFF" where FF is alpha, then RGB.
|
|
41
|
+
*/
|
|
42
|
+
type ArgbColor = {
|
|
43
|
+
argb: string;
|
|
44
|
+
};
|
|
45
|
+
/**
|
|
46
|
+
* Helper function to convert RGB color string to ARGB format.
|
|
47
|
+
* @param rgb - RGB color string without # (e.g., "FFFFFF")
|
|
48
|
+
* @returns ARGB color object (e.g., { argb: "FFFFFFFF" })
|
|
49
|
+
*/
|
|
50
|
+
declare const rgbToArgb: (rgb: string) => ArgbColor;
|
|
51
|
+
|
|
52
|
+
interface AddCellArgs {
|
|
53
|
+
row: number;
|
|
54
|
+
col: number;
|
|
55
|
+
value: CellValue;
|
|
56
|
+
style?: CellStyle;
|
|
57
|
+
}
|
|
58
|
+
interface AddRowArgs {
|
|
59
|
+
values: CellValue[];
|
|
60
|
+
styles?: CellStyle[];
|
|
61
|
+
}
|
|
62
|
+
declare class WorksheetBuilder {
|
|
63
|
+
private worksheet;
|
|
64
|
+
private currentRow;
|
|
65
|
+
private defaultRowStyles;
|
|
66
|
+
constructor(worksheet: ExcelJS.Worksheet);
|
|
67
|
+
/** Returns the current row index (1-based). */
|
|
68
|
+
get rowCount(): number;
|
|
69
|
+
addCell({ row, col, value, style }: AddCellArgs): void;
|
|
70
|
+
addRow({ values, styles }: AddRowArgs): void;
|
|
71
|
+
setDefaultRowStyles(styles: CellStyle[]): void;
|
|
72
|
+
addEmptyRow(): void;
|
|
73
|
+
setColumnWidths(columnWidths: number[]): void;
|
|
74
|
+
getWorksheet(): ExcelJS.Worksheet;
|
|
75
|
+
private applyStyle;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Sanitizes a worksheet title to comply with Excel's limitations.
|
|
80
|
+
*
|
|
81
|
+
* Excel enforces strict rules for worksheet names:
|
|
82
|
+
* - Maximum length of 31 characters (legacy limitation from older Excel versions)
|
|
83
|
+
* - Certain characters are reserved for special purposes and cannot be used:
|
|
84
|
+
* - `:` (colon) - used in cell range references (e.g., A1:B2)
|
|
85
|
+
* - `/` (forward slash) - used in file paths
|
|
86
|
+
* - `\` (backslash) - used in file paths and escape sequences
|
|
87
|
+
* - `?` (question mark) - used as wildcard in formulas
|
|
88
|
+
* - `*` (asterisk) - used as wildcard in formulas
|
|
89
|
+
* - `[` and `]` (square brackets) - used in external references (e.g., [Book1.xlsx]Sheet1!A1)
|
|
90
|
+
*
|
|
91
|
+
* This function removes invalid characters and truncates the title to 31 characters
|
|
92
|
+
* to ensure compatibility with Excel's file format specifications.
|
|
93
|
+
*
|
|
94
|
+
* @param title - The original worksheet title to sanitize
|
|
95
|
+
* @returns A sanitized title that complies with Excel's naming rules
|
|
96
|
+
*/
|
|
97
|
+
declare const sanitizeWorksheetTitle: (title: string) => string;
|
|
98
|
+
interface GenerateExcelBlobArgs {
|
|
99
|
+
worksheetTitle: string;
|
|
100
|
+
data: Record<string, CellValue>[];
|
|
101
|
+
/**
|
|
102
|
+
* Metadata for columns, keyed by column name (translated header name).
|
|
103
|
+
* If not provided, columns will use default width and auto-detect value types.
|
|
104
|
+
*/
|
|
105
|
+
columnsMetadata: Record<string, ColumnMetadata>;
|
|
106
|
+
}
|
|
107
|
+
declare const generateExcelBlob: (args: GenerateExcelBlobArgs) => Promise<Blob>;
|
|
108
|
+
|
|
109
|
+
export { type ArgbColor, type CellBorder, type CellStyle, type CellValue, type ColumnMetadata, type ColumnValueType, type HorizontalAlignment, type VerticalAlignment, WorksheetBuilder, generateExcelBlob, rgbToArgb, sanitizeWorksheetTitle };
|