xlkit 1.0.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/README.md ADDED
@@ -0,0 +1,182 @@
1
+ # xlkit
2
+
3
+ <p align="center">
4
+ <img src="./logo.png" alt="xlkit Logo" width="200" />
5
+ </p>
6
+
7
+ [ExcelJS](https://github.com/exceljs/exceljs) のための宣言的スキーマベースラッパーです。
8
+ シンプルなスキーマでExcelの構造を定義するだけで、スタイル、フォーマット、レイアウトをxlkitが自動で処理します。
9
+
10
+ [English README](./README_en.md)
11
+
12
+ ## 特徴
13
+
14
+ - 📝 **宣言的スキーマ**: 列、ヘッダー、スタイルを一箇所で定義。
15
+ - 🎨 **高度なスタイル設定**: ヘッダー、行、列、または特定のセルに対して、条件付きでスタイルを適用可能。
16
+ - 🔗 **自動結合**: 同じ値を持つ縦方向のセルを自動的に結合 (`merge: 'vertical'`)。
17
+ - 📏 **自動列幅**: コンテンツ(全角文字を含む)に基づいて列幅をスマートに計算。
18
+ - 🌈 **Hexカラー**: 標準的な6桁のHexコード(`#FF0000`)を直接使用可能。
19
+ - 🌐 **ユニバーサル**: Node.js(ファイル出力)とブラウザ/フロントエンド(`Uint8Array` 出力)の両方で動作。
20
+
21
+ ## インストール
22
+
23
+ ```bash
24
+ npm install xlkit
25
+ ```
26
+
27
+ ## クイックスタート
28
+
29
+ ```typescript
30
+ import { createWorkbook, defineSheet } from 'xlkit';
31
+
32
+ // 1. データ型を定義
33
+ interface User {
34
+ id: number;
35
+ name: string;
36
+ role: string;
37
+ isActive: boolean;
38
+ }
39
+
40
+ // 2. シートのスキーマを定義
41
+ const userSheet = defineSheet<User>({
42
+ name: 'Users',
43
+ columns: [
44
+ { key: 'id', header: 'ID', width: 10 },
45
+ { key: 'name', header: '氏名', width: 20 },
46
+ { key: 'role', header: '役割', width: 'auto', merge: 'vertical' },
47
+ {
48
+ key: 'isActive',
49
+ header: 'ステータス',
50
+ format: (val) => val ? '有効' : '無効',
51
+ style: (val) => ({ font: { color: val ? '#00AA00' : '#FF0000' } })
52
+ }
53
+ ],
54
+ borders: 'outer'
55
+ });
56
+
57
+ // 3. Excel生成
58
+ await createWorkbook().addSheet(userSheet, users).save('users.xlsx');
59
+ ```
60
+
61
+ ## 詳細リファレンス
62
+
63
+ ### 1. レイアウトと列定義 (`columns`)
64
+
65
+ 列の定義は `columns` 配列で行います。
66
+
67
+ | プロパティ | 型 | 説明 |
68
+ | :--- | :--- | :--- |
69
+ | `key` | `keyof T` | データのプロパティキー |
70
+ | `header` | `string` | ヘッダーに表示するテキスト |
71
+ | `width` | `number` \| `'auto'` | 列幅。`'auto'` を指定するとコンテンツに合わせて自動調整されます。 |
72
+ | `merge` | `'vertical'` | `'vertical'` を指定すると、上下のセルが同じ値の場合に自動結合されます。 |
73
+ | `format` | `string` \| `Function` | 数値/日付のフォーマット文字列(例: `'$#,##0'`, `'yyyy-mm-dd'`)または変換関数。 |
74
+ | `style` | `Style` \| `Function` | 列全体のスタイル、または値に応じた条件付きスタイル関数。 |
75
+
76
+ ### 2. 罫線 (`borders`)
77
+
78
+ シート全体の罫線プリセットを `borders` プロパティで指定できます。
79
+
80
+ - **`'all'`**: すべてのセルに格子状の罫線を引きます。
81
+ - **`'outer'`**: データ領域の外枠のみに罫線を引きます。
82
+ - **`'header-body'`**: ヘッダー行の下に太めの線を引きます。
83
+ - **`'none'`**: 罫線を引きません(デフォルト)。
84
+
85
+ ```typescript
86
+ const sheet = defineSheet<User>({
87
+ // ...
88
+ borders: 'all', // 格子状
89
+ });
90
+ ```
91
+
92
+ 個別のセルに罫線を設定したい場合は、`style` プロパティ内の `border` で詳細に指定可能です。
93
+
94
+ ### 3. スタイル (`style`)
95
+
96
+ フォント、背景色、配置などを詳細に設定できます。6桁のHexカラーコード (`#RRGGBB`) が使用可能です。
97
+
98
+ ```typescript
99
+ style: {
100
+ font: {
101
+ name: 'Arial',
102
+ size: 12,
103
+ bold: true,
104
+ color: '#FF0000' // 赤色
105
+ },
106
+ fill: {
107
+ color: '#EEEEEE' // Hexカラーで簡単に指定
108
+ },
109
+ alignment: {
110
+ vertical: 'middle',
111
+ horizontal: 'center',
112
+ wrapText: true
113
+ },
114
+ border: {
115
+ top: { style: 'thin', color: { argb: 'FF000000' } },
116
+ // ...
117
+ }
118
+ }
119
+ ```
120
+
121
+ ### 4. ヘッダー設定 (`header`)
122
+
123
+ ヘッダー行のスタイルや構成をカスタマイズできます。
124
+
125
+ ```typescript
126
+ header: {
127
+ rows: ['社員名簿 2025'], // 1行目(タイトルなど)
128
+ style: { // ヘッダー行のスタイル
129
+ font: { bold: true, color: '#FFFFFF' },
130
+ fill: { color: '#4472C4' }
131
+ },
132
+ borders: 'header-body' // ヘッダー下の罫線
133
+ }
134
+ ```
135
+
136
+ ### 5. 行スタイル (`rows`)
137
+
138
+ 行ごとのスタイル(縞模様など)を定義できます。
139
+
140
+ ```typescript
141
+ rows: {
142
+ style: (data, index) => {
143
+ // 偶数行に背景色をつける
144
+ return index % 2 === 0 ? { fill: { color: '#F0F0F0' } } : {};
145
+ }
146
+ }
147
+ ```
148
+
149
+ ### 6. ブラウザ環境でのダウンロード
150
+
151
+ ブラウザ環境では、`download()` メソッドを使って簡単にExcelファイルをダウンロードできます。
152
+
153
+ ```typescript
154
+ // Node.js環境
155
+ await createWorkbook().addSheet(sheet, data).save('output.xlsx');
156
+
157
+ // ブラウザ環境
158
+ await createWorkbook().addSheet(sheet, data).download('output.xlsx');
159
+ ```
160
+
161
+ 内部的には`saveToBuffer()`を呼び出してBlobを作成し、自動的にダウンロードを開始します。
162
+
163
+ ### 7. タイムアウト設定
164
+
165
+ 大量データ処理時のフリーズを防ぐため、`save()`、`saveToBuffer()`、`download()` にはデフォルトで10秒のタイムアウトが設定されています。
166
+
167
+ ```typescript
168
+ // デフォルト(10秒)
169
+ await createWorkbook().addSheet(sheet, data).save('output.xlsx');
170
+
171
+ // カスタムタイムアウト(30秒)
172
+ await createWorkbook().addSheet(sheet, data).save('output.xlsx', { timeout: 30000 });
173
+
174
+ // ブラウザ環境でも同様
175
+ await createWorkbook().addSheet(sheet, data).download('output.xlsx', { timeout: 15000 });
176
+ ```
177
+
178
+ > **推奨**: 10万行以下のデータであればデフォルト設定で問題ありません。それ以上の大量データを扱う場合は、ファイル分割やストリーミング処理を検討してください。
179
+
180
+ ## ライセンス
181
+
182
+ MIT
@@ -0,0 +1,18 @@
1
+ import { SheetDef } from './types';
2
+ export declare class XLKit {
3
+ private workbook;
4
+ constructor();
5
+ addSheet<T>(def: SheetDef<T>, data: T[]): XLKit;
6
+ save(path: string, options?: {
7
+ timeout?: number;
8
+ }): Promise<void>;
9
+ saveToBuffer(options?: {
10
+ timeout?: number;
11
+ }): Promise<Uint8Array>;
12
+ download(filename: string, options?: {
13
+ timeout?: number;
14
+ }): Promise<void>;
15
+ }
16
+ export declare function createWorkbook(): XLKit;
17
+ export declare function defineSheet<T>(def: SheetDef<T>): SheetDef<T>;
18
+ //# sourceMappingURL=Sheetflow.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"Sheetflow.d.ts","sourceRoot":"","sources":["../src/Sheetflow.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,QAAQ,EAAsB,MAAM,SAAS,CAAC;AAGvD,qBAAa,KAAK;IAChB,OAAO,CAAC,QAAQ,CAAmB;;IAMnC,QAAQ,CAAC,CAAC,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,EAAE,IAAI,EAAE,CAAC,EAAE,GAAG,KAAK;IAsKzC,IAAI,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;IAmBjE,YAAY,CAAC,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,UAAU,CAAC;IAajE,QAAQ,CAAC,QAAQ,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE;QAAE,OAAO,CAAC,EAAE,MAAM,CAAA;KAAE,GAAG,OAAO,CAAC,IAAI,CAAC;CAiBhF;AAED,wBAAgB,cAAc,IAAI,KAAK,CAEtC;AAED,wBAAgB,WAAW,CAAC,CAAC,EAAE,GAAG,EAAE,QAAQ,CAAC,CAAC,CAAC,GAAG,QAAQ,CAAC,CAAC,CAAC,CAE5D"}
@@ -0,0 +1,212 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ exports.XLKit = void 0;
7
+ exports.createWorkbook = createWorkbook;
8
+ exports.defineSheet = defineSheet;
9
+ const exceljs_1 = __importDefault(require("exceljs"));
10
+ const style_1 = require("./utils/style");
11
+ class XLKit {
12
+ constructor() {
13
+ this.workbook = new exceljs_1.default.Workbook();
14
+ }
15
+ addSheet(def, data) {
16
+ // Validate Sheet Name
17
+ if (!def.name) {
18
+ throw new Error('Sheet name is required.');
19
+ }
20
+ if (def.name.length > 31) {
21
+ throw new Error(`Sheet name "${def.name}" exceeds the maximum length of 31 characters.`);
22
+ }
23
+ // Invalid characters: \ / ? * [ ] :
24
+ const invalidChars = /[\\/?*[\]:]/;
25
+ if (invalidChars.test(def.name)) {
26
+ throw new Error(`Sheet name "${def.name}" contains invalid characters (\\ / ? * [ ] :).`);
27
+ }
28
+ const sheet = this.workbook.addWorksheet(def.name);
29
+ // 1. Setup Columns & Headers
30
+ const columns = def.columns.map((col, colIndex) => {
31
+ let width = col.width;
32
+ if (width === 'auto') {
33
+ let maxLen = col.header.length * (def.autoWidth?.headerIncluded !== false ? 1 : 0);
34
+ // Check data length (sample first 100 rows for performance if needed, currently all)
35
+ data.forEach(row => {
36
+ const val = row[col.key];
37
+ const str = val != null ? String(val) : '';
38
+ // Simple full-width check: count as 2 if char code > 255
39
+ let len = 0;
40
+ for (let i = 0; i < str.length; i++) {
41
+ len += str.charCodeAt(i) > 255 ? 2 : 1;
42
+ }
43
+ if (len > maxLen)
44
+ maxLen = len;
45
+ });
46
+ const padding = def.autoWidth?.padding ?? 2;
47
+ const constant = def.autoWidth?.charWidthConstant ?? 1.2;
48
+ width = (maxLen + padding) * constant;
49
+ }
50
+ return {
51
+ header: col.header,
52
+ key: String(col.key),
53
+ width: typeof width === 'number' ? width : 15,
54
+ style: col.style && typeof col.style === 'object' ? (0, style_1.mapStyle)(col.style) : undefined
55
+ };
56
+ });
57
+ sheet.columns = columns;
58
+ // 2. Add Data & Apply Row Styles
59
+ data.forEach((row, rowIndex) => {
60
+ const addedRow = sheet.addRow(row);
61
+ // Apply row-level style
62
+ if (def.rows?.style) {
63
+ const rowStyle = def.rows.style(row, rowIndex);
64
+ const mappedStyle = (0, style_1.mapStyle)(rowStyle);
65
+ addedRow.eachCell((cell) => {
66
+ cell.style = { ...cell.style, ...mappedStyle };
67
+ });
68
+ }
69
+ // Apply column-level conditional styles
70
+ def.columns.forEach((col, colIndex) => {
71
+ if (typeof col.style === 'function') {
72
+ const cell = addedRow.getCell(colIndex + 1);
73
+ const cellStyle = col.style(row[col.key], row, rowIndex);
74
+ cell.style = { ...cell.style, ...(0, style_1.mapStyle)(cellStyle) };
75
+ }
76
+ if (col.format) {
77
+ const cell = addedRow.getCell(colIndex + 1);
78
+ if (typeof col.format === 'string') {
79
+ cell.numFmt = col.format;
80
+ }
81
+ else {
82
+ cell.value = col.format(row[col.key]);
83
+ }
84
+ }
85
+ });
86
+ });
87
+ // 3. Apply Header Styles
88
+ if (def.header?.style) {
89
+ const headerRow = sheet.getRow(1);
90
+ const mappedHeaderStyle = (0, style_1.mapStyle)(def.header.style);
91
+ headerRow.eachCell((cell) => {
92
+ cell.style = { ...cell.style, ...mappedHeaderStyle };
93
+ });
94
+ }
95
+ // 4. Apply Vertical Merges
96
+ def.columns.forEach((col, colIndex) => {
97
+ if (col.merge === 'vertical') {
98
+ let startRow = 2; // 1-based, skip header (row 1)
99
+ let previousValue = null;
100
+ // Iterate from first data row to last
101
+ for (let i = 0; i < data.length; i++) {
102
+ const currentRowIndex = i + 2;
103
+ const cell = sheet.getCell(currentRowIndex, colIndex + 1);
104
+ const currentValue = cell.value;
105
+ if (i === 0) {
106
+ previousValue = currentValue;
107
+ continue;
108
+ }
109
+ // If value changed or it's the last row, process the merge
110
+ if (currentValue !== previousValue) {
111
+ if (currentRowIndex - 1 > startRow) {
112
+ sheet.mergeCells(startRow, colIndex + 1, currentRowIndex - 1, colIndex + 1);
113
+ }
114
+ startRow = currentRowIndex;
115
+ previousValue = currentValue;
116
+ }
117
+ }
118
+ // Handle the last group
119
+ const lastRowIndex = data.length + 1;
120
+ if (lastRowIndex > startRow) {
121
+ sheet.mergeCells(startRow, colIndex + 1, lastRowIndex, colIndex + 1);
122
+ }
123
+ }
124
+ });
125
+ // 5. Apply Borders
126
+ if (def.borders === 'all') {
127
+ sheet.eachRow((row) => {
128
+ row.eachCell((cell) => {
129
+ cell.border = {
130
+ top: { style: 'thin' },
131
+ left: { style: 'thin' },
132
+ bottom: { style: 'thin' },
133
+ right: { style: 'thin' }
134
+ };
135
+ });
136
+ });
137
+ }
138
+ else if (def.borders === 'outer') {
139
+ const lastRow = sheet.rowCount;
140
+ const lastCol = sheet.columnCount;
141
+ // Top & Bottom
142
+ for (let c = 1; c <= lastCol; c++) {
143
+ const topCell = sheet.getCell(1, c);
144
+ topCell.border = { ...topCell.border, top: { style: 'thin' } };
145
+ const bottomCell = sheet.getCell(lastRow, c);
146
+ bottomCell.border = { ...bottomCell.border, bottom: { style: 'thin' } };
147
+ }
148
+ // Left & Right
149
+ for (let r = 1; r <= lastRow; r++) {
150
+ const leftCell = sheet.getCell(r, 1);
151
+ leftCell.border = { ...leftCell.border, left: { style: 'thin' } };
152
+ const rightCell = sheet.getCell(r, lastCol);
153
+ rightCell.border = { ...rightCell.border, right: { style: 'thin' } };
154
+ }
155
+ }
156
+ else if (def.borders === 'header-body') {
157
+ const lastCol = sheet.columnCount;
158
+ for (let c = 1; c <= lastCol; c++) {
159
+ const headerCell = sheet.getCell(1, c);
160
+ headerCell.border = { ...headerCell.border, bottom: { style: 'medium' } };
161
+ }
162
+ }
163
+ return this;
164
+ }
165
+ async save(path, options) {
166
+ if (!path || path.trim() === '') {
167
+ throw new Error('File path cannot be empty.');
168
+ }
169
+ if (typeof process !== 'undefined' && process.versions && process.versions.node) {
170
+ const timeout = options?.timeout ?? 10000; // Default 10s
171
+ const writePromise = this.workbook.xlsx.writeFile(path);
172
+ const timeoutPromise = new Promise((_, reject) => {
173
+ setTimeout(() => reject(new Error(`Operation timed out after ${timeout}ms`)), timeout);
174
+ });
175
+ await Promise.race([writePromise, timeoutPromise]);
176
+ }
177
+ else {
178
+ throw new Error('File system access is only available in Node.js environment. Use saveToBuffer() instead.');
179
+ }
180
+ }
181
+ async saveToBuffer(options) {
182
+ const timeout = options?.timeout ?? 10000; // Default 10s
183
+ const writePromise = this.workbook.xlsx.writeBuffer();
184
+ const timeoutPromise = new Promise((_, reject) => {
185
+ setTimeout(() => reject(new Error(`Operation timed out after ${timeout}ms`)), timeout);
186
+ });
187
+ const buffer = await Promise.race([writePromise, timeoutPromise]);
188
+ return new Uint8Array(buffer);
189
+ }
190
+ async download(filename, options) {
191
+ if (typeof window === 'undefined' || typeof document === 'undefined') {
192
+ throw new Error('download() is only available in browser environment. Use save() for Node.js or saveToBuffer() for custom handling.');
193
+ }
194
+ const buffer = await this.saveToBuffer(options);
195
+ const blob = new Blob([buffer.buffer], {
196
+ type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet'
197
+ });
198
+ const url = URL.createObjectURL(blob);
199
+ const a = document.createElement('a');
200
+ a.href = url;
201
+ a.download = filename;
202
+ a.click();
203
+ URL.revokeObjectURL(url);
204
+ }
205
+ }
206
+ exports.XLKit = XLKit;
207
+ function createWorkbook() {
208
+ return new XLKit();
209
+ }
210
+ function defineSheet(def) {
211
+ return def;
212
+ }
@@ -0,0 +1,3 @@
1
+ export * from './types';
2
+ export * from './Sheetflow';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,SAAS,CAAC;AACxB,cAAc,aAAa,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __exportStar = (this && this.__exportStar) || function(m, exports) {
14
+ for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
15
+ };
16
+ Object.defineProperty(exports, "__esModule", { value: true });
17
+ __exportStar(require("./types"), exports);
18
+ __exportStar(require("./Sheetflow"), exports);
@@ -0,0 +1,43 @@
1
+ import { Alignment, Border, Fill, Font } from 'exceljs';
2
+ export type HexColor = string;
3
+ export interface XLStyle {
4
+ font?: Partial<Font> & {
5
+ color?: HexColor | {
6
+ argb: string;
7
+ };
8
+ };
9
+ fill?: Partial<Fill> & {
10
+ color?: HexColor;
11
+ };
12
+ alignment?: Partial<Alignment>;
13
+ border?: Partial<Border> | 'all' | 'outer' | 'header-body' | 'none';
14
+ }
15
+ export interface ColumnDef<T> {
16
+ key: keyof T;
17
+ header: string;
18
+ width?: number | 'auto';
19
+ merge?: 'vertical';
20
+ style?: XLStyle | ((val: any, row: T, index: number) => XLStyle);
21
+ format?: string | ((val: any) => string);
22
+ }
23
+ export interface HeaderConfig {
24
+ rows: string[];
25
+ style?: XLStyle;
26
+ borders?: 'header-body' | 'all' | 'none';
27
+ }
28
+ export interface SheetDef<T> {
29
+ name: string;
30
+ columns: ColumnDef<T>[];
31
+ header?: HeaderConfig;
32
+ rows?: {
33
+ style?: (data: T, index: number) => XLStyle;
34
+ };
35
+ defaultStyle?: XLStyle;
36
+ borders?: 'all' | 'outer' | 'header-body' | 'none';
37
+ autoWidth?: {
38
+ padding?: number;
39
+ headerIncluded?: boolean;
40
+ charWidthConstant?: number;
41
+ };
42
+ }
43
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EAAS,SAAS,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,EAAE,MAAM,SAAS,CAAC;AAE/D,MAAM,MAAM,QAAQ,GAAG,MAAM,CAAC;AAE9B,MAAM,WAAW,OAAO;IACtB,IAAI,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG;QAAE,KAAK,CAAC,EAAE,QAAQ,GAAG;YAAE,IAAI,EAAE,MAAM,CAAA;SAAE,CAAA;KAAE,CAAC;IAC/D,IAAI,CAAC,EAAE,OAAO,CAAC,IAAI,CAAC,GAAG;QAAE,KAAK,CAAC,EAAE,QAAQ,CAAA;KAAE,CAAC;IAC5C,SAAS,CAAC,EAAE,OAAO,CAAC,SAAS,CAAC,CAAC;IAC/B,MAAM,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,GAAG,KAAK,GAAG,OAAO,GAAG,aAAa,GAAG,MAAM,CAAC;CACrE;AAED,MAAM,WAAW,SAAS,CAAC,CAAC;IAC1B,GAAG,EAAE,MAAM,CAAC,CAAC;IACb,MAAM,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IACxB,KAAK,CAAC,EAAE,UAAU,CAAC;IACnB,KAAK,CAAC,EAAE,OAAO,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC,CAAC;IACjE,MAAM,CAAC,EAAE,MAAM,GAAG,CAAC,CAAC,GAAG,EAAE,GAAG,KAAK,MAAM,CAAC,CAAC;CAC1C;AAED,MAAM,WAAW,YAAY;IAC3B,IAAI,EAAE,MAAM,EAAE,CAAC;IACf,KAAK,CAAC,EAAE,OAAO,CAAC;IAChB,OAAO,CAAC,EAAE,aAAa,GAAG,KAAK,GAAG,MAAM,CAAC;CAC1C;AAED,MAAM,WAAW,QAAQ,CAAC,CAAC;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;IACxB,MAAM,CAAC,EAAE,YAAY,CAAC;IACtB,IAAI,CAAC,EAAE;QACL,KAAK,CAAC,EAAE,CAAC,IAAI,EAAE,CAAC,EAAE,KAAK,EAAE,MAAM,KAAK,OAAO,CAAC;KAC7C,CAAC;IACF,YAAY,CAAC,EAAE,OAAO,CAAC;IACvB,OAAO,CAAC,EAAE,KAAK,GAAG,OAAO,GAAG,aAAa,GAAG,MAAM,CAAC;IACnD,SAAS,CAAC,EAAE;QACV,OAAO,CAAC,EAAE,MAAM,CAAC;QACjB,cAAc,CAAC,EAAE,OAAO,CAAC;QACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;KAC5B,CAAC;CACH"}
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
@@ -0,0 +1,2 @@
1
+ export declare function toArgb(hex: string): string;
2
+ //# sourceMappingURL=color.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"color.d.ts","sourceRoot":"","sources":["../../src/utils/color.ts"],"names":[],"mappings":"AAAA,wBAAgB,MAAM,CAAC,GAAG,EAAE,MAAM,GAAG,MAAM,CAY1C"}
@@ -0,0 +1,17 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toArgb = toArgb;
4
+ function toArgb(hex) {
5
+ if (!hex)
6
+ return 'FF000000';
7
+ if (hex.startsWith('#')) {
8
+ hex = hex.slice(1);
9
+ }
10
+ if (hex.length === 3) {
11
+ hex = hex.split('').map(c => c + c).join('');
12
+ }
13
+ if (hex.length === 6) {
14
+ return 'FF' + hex.toUpperCase();
15
+ }
16
+ return hex.toUpperCase(); // Assume already ARGB or invalid
17
+ }
@@ -0,0 +1,4 @@
1
+ import { Style } from 'exceljs';
2
+ import { XLStyle } from '../types';
3
+ export declare function mapStyle(style: XLStyle): Partial<Style>;
4
+ //# sourceMappingURL=style.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"style.d.ts","sourceRoot":"","sources":["../../src/utils/style.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAW,MAAM,SAAS,CAAC;AACzC,OAAO,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAGnC,wBAAgB,QAAQ,CAAC,KAAK,EAAE,OAAO,GAAG,OAAO,CAAC,KAAK,CAAC,CAqCvD"}
@@ -0,0 +1,39 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.mapStyle = mapStyle;
4
+ const color_1 = require("./color");
5
+ function mapStyle(style) {
6
+ const excelStyle = {};
7
+ if (style.font) {
8
+ excelStyle.font = { ...style.font };
9
+ if (typeof style.font.color === 'string') {
10
+ excelStyle.font.color = { argb: (0, color_1.toArgb)(style.font.color) };
11
+ }
12
+ }
13
+ if (style.fill) {
14
+ if (style.fill.color) {
15
+ excelStyle.fill = {
16
+ type: 'pattern',
17
+ pattern: 'solid',
18
+ fgColor: { argb: (0, color_1.toArgb)(style.fill.color) },
19
+ };
20
+ }
21
+ else {
22
+ // If no color is specified but fill is provided, use it as-is
23
+ excelStyle.fill = style.fill;
24
+ }
25
+ }
26
+ if (style.alignment) {
27
+ excelStyle.alignment = style.alignment;
28
+ }
29
+ if (style.border) {
30
+ if (typeof style.border === 'string') {
31
+ // Presets handled in main logic or expanded here
32
+ // For now, we'll handle explicit border objects here
33
+ }
34
+ else {
35
+ excelStyle.border = style.border;
36
+ }
37
+ }
38
+ return excelStyle;
39
+ }
package/package.json ADDED
@@ -0,0 +1,41 @@
1
+ {
2
+ "name": "xlkit",
3
+ "version": "1.0.3",
4
+ "main": "dist/index.js",
5
+ "types": "dist/index.d.ts",
6
+ "files": [
7
+ "dist"
8
+ ],
9
+ "scripts": {
10
+ "build": "tsc",
11
+ "test": "vitest run",
12
+ "prepublishOnly": "npm run build"
13
+ },
14
+ "keywords": [
15
+ "excel",
16
+ "exceljs",
17
+ "spreadsheet",
18
+ "wrapper",
19
+ "declarative",
20
+ "schema"
21
+ ],
22
+ "author": "",
23
+ "license": "MIT",
24
+ "description": "xlkit - Declarative schema-based wrapper for ExcelJS with type safety and intuitive styling",
25
+ "repository": {
26
+ "type": "git",
27
+ "url": "git+https://github.com/yn1323/xlkit.git"
28
+ },
29
+ "bugs": {
30
+ "url": "https://github.com/yn1323/xlkit/issues"
31
+ },
32
+ "homepage": "https://github.com/yn1323/xlkit#readme",
33
+ "dependencies": {
34
+ "exceljs": "^4.4.0"
35
+ },
36
+ "devDependencies": {
37
+ "@types/node": "^24.10.1",
38
+ "typescript": "^5.9.3",
39
+ "vitest": "^4.0.12"
40
+ }
41
+ }