han-excel-builder 1.1.0 → 1.1.2

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.es.md ADDED
@@ -0,0 +1,2107 @@
1
+ # Han Excel Builder
2
+
3
+ 🚀 **Generador avanzado de archivos Excel con soporte TypeScript, estilos completos y rendimiento optimizado**
4
+
5
+ Una biblioteca moderna y completamente tipada para crear informes Excel complejos con múltiples hojas de cálculo, estilos avanzados y alto rendimiento.
6
+
7
+ **📖 [Read in English / Leer en Inglés](README.md)**
8
+
9
+ ---
10
+
11
+ ## 📑 Tabla de Contenidos
12
+
13
+ - [✨ Características](#-características)
14
+ - [📊 Estructura de Datos](#-estructura-de-datos)
15
+ - [📈 Tipos de Datos](#-tipos-de-datos)
16
+ - [🎨 Estilos Avanzados](#-estilos-avanzados)
17
+ - [🔧 Características Avanzadas](#-características-avanzadas)
18
+ - [🌐 Compatibilidad con Navegador y Node.js](#-compatibilidad-con-navegador-y-nodejs)
19
+ - [Tabla de Compatibilidad](#tabla-de-compatibilidad)
20
+ - [Detalles Específicos del Entorno](#detalles-específicos-del-entorno)
21
+ - [💾 Exportar Archivos: Navegador vs Node.js](#-exportar-archivos-navegador-vs-nodejs)
22
+ - [🌐 Entorno de Navegador](#-entorno-de-navegador)
23
+ - [🖥️ Entorno Node.js](#️-entorno-nodejs)
24
+ - [📊 Tabla Comparativa](#-tabla-comparativa)
25
+ - [💡 Mejores Prácticas](#-mejores-prácticas)
26
+ - [📦 Instalación](#-instalación)
27
+ - [🚀 Inicio Rápido](#-inicio-rápido)
28
+ - [📚 Documentación de la API](#-documentación-de-la-api)
29
+ - [Clases Principales](#clases-principales)
30
+ - [Tipos de Datos](#tipos-de-datos)
31
+ - [Estilos](#estilos)
32
+ - [🎯 Ejemplos Avanzados](#-ejemplos-avanzados)
33
+ - [🧪 Pruebas](#-pruebas)
34
+ - [🛠️ Desarrollo](#️-desarrollo)
35
+ - [📋 Migración desde legacy-excel](#-migración-desde-legacy-excel)
36
+ - [📚 Recursos Adicionales](#-recursos-adicionales)
37
+ - [🤝 Contribuir](#-contribuir)
38
+ - [📄 Licencia](#-licencia)
39
+ - [🆘 Soporte](#-soporte)
40
+
41
+ ---
42
+
43
+ ## ✨ Características
44
+
45
+ ### 📊 Estructura de Datos
46
+ - ✅ **Multiple Worksheets** - Crear libros de trabajo complejos con múltiples hojas
47
+ - ✅ **Multiple Tables per Sheet** - Crear múltiples tablas independientes en una sola hoja
48
+ - ✅ **Nested Headers** - Soporte completo para encabezados con múltiples niveles de anidamiento
49
+ - ✅ **Hierarchical Data** - Soporte para datos con estructura de hijos (datos anidados)
50
+
51
+ ### 📈 Tipos de Datos
52
+ - ✅ **STRING** - Valores de texto
53
+ - ✅ **NUMBER** - Valores numéricos
54
+ - ✅ **BOOLEAN** - Valores verdadero/falso
55
+ - ✅ **DATE** - Valores de fecha
56
+ - ✅ **PERCENTAGE** - Valores de porcentaje
57
+ - ✅ **CURRENCY** - Valores de moneda
58
+ - ✅ **LINK** - Hipervínculos con texto personalizable
59
+ - ✅ **FORMULA** - Fórmulas de Excel
60
+
61
+ ### 🎨 Estilos Avanzados
62
+ - ✅ **Fluent API** - StyleBuilder con métodos encadenables
63
+ - ✅ **Fonts** - Control completo sobre nombre, tamaño, color, negrita, cursiva, subrayado
64
+ - ✅ **Colors** - Fondos, colores de texto con soporte para hex, RGB y temas
65
+ - ✅ **Borders** - Bordes personalizables en todos los lados con múltiples estilos
66
+ - ✅ **Alignment** - Horizontal (izquierda, centro, derecha, justificado) y vertical (arriba, medio, abajo)
67
+ - ✅ **Text** - Ajuste de texto, reducir para ajustar, rotación de texto
68
+ - ✅ **Number Formats** - Múltiples formatos predefinidos y personalizados
69
+ - ✅ **Alternating Rows** - Soporte para rayas alternadas en tablas
70
+
71
+ ### 🔧 Características Avanzadas
72
+ - ✅ **TypeScript First** - Seguridad de tipos completa con interfaces exhaustivas
73
+ - ✅ **Event System** - EventEmitter para monitorear el proceso de construcción
74
+ - ✅ **Validation** - Sistema robusto de validación de datos
75
+ - ✅ **Metadata** - Soporte completo para metadatos del libro de trabajo (autor, título, descripción, etc.)
76
+ - ✅ **Multiple Export Formats** - Descarga directa, Buffer, Blob
77
+ - ✅ **Excel Reading** - Leer archivos Excel y convertirlos a JSON
78
+ - ✅ **Hyperlinks** - Crear enlaces con texto personalizable
79
+ - ✅ **Cell Merging** - Fusión de celdas horizontal y vertical
80
+ - ✅ **Custom Dimensions** - Ancho de columna y alto de fila personalizables
81
+ - ✅ **Cell Comments** - Agregar comentarios a celdas (soporte de lectura y escritura)
82
+ - ✅ **Data Validation** - Aplicar reglas de validación de datos a celdas (lista, entero, decimal, longitud de texto, fecha, personalizado)
83
+ - ✅ **Auto Filters** - Habilitar filtros automáticos para tablas y hojas de cálculo
84
+ - ✅ **Conditional Formatting** - Aplicar reglas de formato condicional a celdas basadas en valores o fórmulas
85
+ - ✅ **Freeze Panes** - Congelar filas y columnas para facilitar la navegación
86
+ - ✅ **Worksheet Protection** - Proteger hojas de cálculo con contraseña y configuración de permisos
87
+ - ✅ **Images/Pictures** - Agregar imágenes a hojas de cálculo (PNG, JPEG, GIF, BMP, WebP)
88
+ - ✅ **Row/Column Grouping** - Agrupar filas y columnas para esquemas colapsables
89
+ - ✅ **Named Ranges** - Definir rangos con nombre para referencias fáciles
90
+ - ✅ **Excel Structured Tables** - Crear tablas estructuradas de Excel con estilos automáticos
91
+ - ✅ **Advanced Print Settings** - Encabezados/pies de página y repetir filas/columnas en cada página
92
+ - ✅ **Hide/Show Rows & Columns** - Ocultar o mostrar filas y columnas específicas
93
+ - ✅ **Rich Text in Cells** - Formatear texto con múltiples estilos dentro de una sola celda
94
+ - ✅ **Cell-level Protection** - Proteger celdas individuales con opciones de bloqueo/ocultación
95
+ - ✅ **Pivot Tables** - Crear tablas dinámicas para análisis de datos
96
+ - ✅ **Slicers** - Filtros visuales para tablas y tablas dinámicas (documentado, requiere ExcelJS avanzado)
97
+ - ✅ **Watermarks** - Agregar marcas de agua a hojas de cálculo (texto o imagen)
98
+ - ✅ **Cell-level Page Breaks** - Saltos de página manuales a nivel de fila
99
+ - ✅ **Data Connections** - Conexiones de datos externas (documentado, requiere ExcelJS avanzado)
100
+ - ✅ **Cell Styles (Predefined)** - Estilos de celda reutilizables para consistencia
101
+ - ✅ **Themes** - Temas de color para todo el libro de trabajo
102
+ - ✅ **Split Panes** - Dividir ventana en paneles (diferente de congelar paneles)
103
+ - ✅ **Sheet Views** - Múltiples vistas de la misma hoja (normal, vista previa de salto de página, diseño de página)
104
+
105
+ ## 🌐 Compatibilidad con Navegador y Node.js
106
+
107
+ Han Excel Builder funciona tanto en entornos de **navegador** como en **Node.js**. La mayoría de las características son totalmente compatibles con ambos, pero algunas tienen limitaciones según el entorno.
108
+
109
+ ### Tabla de Compatibilidad
110
+
111
+ | Feature | Browser | Node.js | Notes |
112
+ |---------|---------|---------|-------|
113
+ | **Basic Features** |
114
+ | Multiple Worksheets | ✅ | ✅ | Fully compatible |
115
+ | Nested Headers | ✅ | ✅ | Fully compatible |
116
+ | Hierarchical Data | ✅ | ✅ | Fully compatible |
117
+ | All Data Types (STRING, NUMBER, etc.) | ✅ | ✅ | Fully compatible |
118
+ | **Styling** |
119
+ | StyleBuilder & Fluent API | ✅ | ✅ | Fully compatible |
120
+ | Fonts, Colors, Borders | ✅ | ✅ | Fully compatible |
121
+ | Conditional Formatting | ✅ | ✅ | Fully compatible |
122
+ | Themes | ✅ | ✅ | Fully compatible |
123
+ | Predefined Cell Styles | ✅ | ✅ | Fully compatible |
124
+ | **Advanced Features** |
125
+ | Images/Pictures | ✅ | ✅ | Fully compatible |
126
+ | Pivot Tables | ✅ | ✅ | Fully compatible |
127
+ | Freeze Panes | ✅ | ✅ | Fully compatible |
128
+ | Worksheet Protection | ✅ | ✅ | Fully compatible |
129
+ | Data Validation | ✅ | ✅ | Fully compatible |
130
+ | Rich Text in Cells | ✅ | ✅ | Fully compatible |
131
+ | Cell-level Protection | ✅ | ✅ | Fully compatible |
132
+ | Row/Column Grouping | ✅ | ✅ | Fully compatible |
133
+ | Named Ranges | ✅ | ✅ | Fully compatible |
134
+ | Excel Structured Tables | ✅ | ✅ | Fully compatible |
135
+ | Hide/Show Rows & Columns | ✅ | ✅ | Fully compatible |
136
+ | Split Panes | ✅ | ✅ | Fully compatible |
137
+ | Sheet Views | ✅ | ✅ | Fully compatible |
138
+ | **File Operations** |
139
+ | Generate & Download | ✅ | ✅ | Browser: Uses Blob/Download. Node: Can write to file |
140
+ | Read Excel Files | ✅ | ✅ | Browser: From File/Blob. Node: Also from file path |
141
+ | **Features with Limitations** |
142
+ | Templates | ⚠️ | ✅ | Browser: Only ArrayBuffer/Blob. Node: Also file paths |
143
+ | Streaming | ⚠️ | ✅ | Browser: Limited. Node: Full support |
144
+ | Charts (as image) | ✅ | ✅ | Requires chart library compatible with environment |
145
+ | Sparklines (as image) | ✅ | ✅ | Requires chart library compatible with environment |
146
+
147
+ ### Leyenda
148
+ - ✅ **Compatible**: Works fully in this environment
149
+ - ⚠️ **Limited**: Works but with restrictions or requires special configuration
150
+ - ❌ **Not Available**: Does not work in this environment
151
+
152
+ ### Detalles Específicos del Entorno
153
+
154
+ #### ✅ Características Totalmente Compatibles
155
+ La mayoría de las características funcionan de manera idéntica tanto en navegador como en Node.js:
156
+ - All styling features (StyleBuilder, themes, conditional formatting)
157
+ - All data structure features (worksheets, tables, headers)
158
+ - All cell features (merging, protection, validation)
159
+ - Image insertion
160
+ - Pivot tables
161
+ - All export formats (Buffer, Blob, download)
162
+
163
+ #### ⚠️ Características con Limitaciones
164
+
165
+ **Templates:**
166
+ - **Browser**: Can only load templates from `ArrayBuffer` or `Blob` (e.g., from `fetch()` or File input)
167
+ ```typescript
168
+ // Browser: Load from fetch
169
+ const response = await fetch('/template.xlsx');
170
+ const buffer = await response.arrayBuffer();
171
+ await builder.loadTemplate(buffer);
172
+ ```
173
+ - **Node.js**: Can load from file path or `ArrayBuffer`
174
+ ```typescript
175
+ // Node: Load from file
176
+ await builder.loadTemplate('./template.xlsx');
177
+ // Or from buffer
178
+ await builder.loadTemplate(buffer);
179
+ ```
180
+
181
+ **Streaming (Large Files):**
182
+ - **Browser**: Limited by browser stream capabilities. May require polyfills or alternatives
183
+ - **Node.js**: Full support with `ExcelJS.stream.xlsx.WorkbookWriter`
184
+ ```typescript
185
+ // Node: Full streaming support
186
+ const stream = new ExcelJS.stream.xlsx.WorkbookWriter(options);
187
+ ```
188
+
189
+ **Charts/Sparklines (as images):**
190
+ - **Browser**: Requires browser-compatible chart library (Chart.js, D3.js, Plotly.js)
191
+ - **Node.js**: Requires Node-compatible chart library (can use canvas/server-side rendering)
192
+ - **Note**: The chart library must be compatible with the execution environment
193
+
194
+ **File Reading:**
195
+ - **Browser**: Use `ExcelReader.fromFile()` or `ExcelReader.fromBlob()`
196
+ - **Node.js**: Can also use `ExcelReader.fromPath()` for file system access
197
+
198
+ ## 💾 Exportar Archivos: Navegador vs Node.js
199
+
200
+ La forma de exportar archivos Excel difiere entre entornos de navegador y Node.js. Aquí te mostramos cómo manejar cada caso:
201
+
202
+ ### 🌐 Entorno de Navegador
203
+
204
+ En el navegador, tienes tres opciones principales:
205
+
206
+ #### 1. **Direct Download** (Recommended for Browser)
207
+ Activa automáticamente una descarga en el navegador del usuario:
208
+
209
+ ```typescript
210
+ import { ExcelBuilder } from 'han-excel-builder';
211
+
212
+ const builder = new ExcelBuilder();
213
+ // ... build your workbook ...
214
+
215
+ // Automatically downloads the file
216
+ const result = await builder.generateAndDownload('report.xlsx');
217
+
218
+ if (result.success) {
219
+ console.log('File downloaded successfully!');
220
+ } else {
221
+ console.error('Download failed:', result.error);
222
+ }
223
+ ```
224
+
225
+ **Result**: The file is automatically downloaded to the user's default download folder.
226
+
227
+ #### 2. **Get as Blob** (For Custom Handling)
228
+ Obtener el archivo como Blob para manejo personalizado (por ejemplo, subir al servidor, vista previa, etc.):
229
+
230
+ ```typescript
231
+ // Get as Blob
232
+ const result = await builder.toBlob();
233
+
234
+ if (result.success) {
235
+ const blob = result.data; // Blob object
236
+
237
+ // Option A: Upload to server
238
+ const formData = new FormData();
239
+ formData.append('file', blob, 'report.xlsx');
240
+ await fetch('/api/upload', { method: 'POST', body: formData });
241
+
242
+ // Option B: Create object URL for preview
243
+ const url = URL.createObjectURL(blob);
244
+ window.open(url);
245
+
246
+ // Option C: Manual download
247
+ const link = document.createElement('a');
248
+ link.href = url;
249
+ link.download = 'report.xlsx';
250
+ link.click();
251
+ }
252
+ ```
253
+
254
+ **Result**: Returns a `Blob` object that you can use for custom operations.
255
+
256
+ #### 3. **Get as ArrayBuffer** (For Low-Level Operations)
257
+ Obtener los datos binarios sin procesar:
258
+
259
+ ```typescript
260
+ // Get as ArrayBuffer
261
+ const result = await builder.toBuffer();
262
+
263
+ if (result.success) {
264
+ const buffer = result.data; // ArrayBuffer
265
+
266
+ // Use for low-level operations
267
+ // e.g., send via WebSocket, process with other libraries, etc.
268
+ }
269
+ ```
270
+
271
+ **Result**: Returns an `ArrayBuffer` with the raw Excel file data.
272
+
273
+ ---
274
+
275
+ ### 🖥️ Entorno Node.js
276
+
277
+ En Node.js, normalmente querrás guardar el archivo en disco. Aquí están las opciones:
278
+
279
+ #### 1. **Guardar en Archivo** (Recomendado para Node.js - Simple y Directo)
280
+ **¡NUEVO!** Ahora tan simple como en el navegador - solo una llamada a método:
281
+
282
+ ```typescript
283
+ import { ExcelBuilder } from 'han-excel-builder';
284
+
285
+ const builder = new ExcelBuilder();
286
+ // ... construir tu libro de trabajo ...
287
+
288
+ // Guardar directamente en archivo - crea directorios automáticamente si es necesario
289
+ const result = await builder.saveToFile('./output/report.xlsx');
290
+
291
+ if (result.success) {
292
+ console.log('¡Archivo guardado exitosamente!');
293
+ } else {
294
+ console.error('Error al guardar:', result.error);
295
+ }
296
+ ```
297
+
298
+ **Resultado**: El archivo se guarda en la ruta especificada en el sistema de archivos. Los directorios padre se crean automáticamente.
299
+
300
+ **Opciones:**
301
+ ```typescript
302
+ await builder.saveToFile('./output/report.xlsx', {
303
+ createDir: true, // Crear directorios padre (por defecto: true)
304
+ encoding: 'binary' // Codificación del archivo (por defecto: 'binary')
305
+ });
306
+ ```
307
+
308
+ #### 2. **Guardar en Stream** (Para Archivos Grandes)
309
+ Para archivos muy grandes, puedes escribir directamente a un stream:
310
+
311
+ ```typescript
312
+ import { ExcelBuilder } from 'han-excel-builder';
313
+ import fs from 'fs';
314
+
315
+ const builder = new ExcelBuilder();
316
+ // ... construir tu libro de trabajo ...
317
+
318
+ const writeStream = fs.createWriteStream('./output/report.xlsx');
319
+ const result = await builder.saveToStream(writeStream);
320
+
321
+ if (result.success) {
322
+ console.log('¡Archivo guardado en stream exitosamente!');
323
+ writeStream.end();
324
+ }
325
+ ```
326
+
327
+ **Resultado**: El archivo se escribe en disco usando streams (mejor para archivos grandes).
328
+
329
+ #### 3. **Guardado Manual** (Usando toBuffer + fs)
330
+ Si necesitas más control, aún puedes usar el enfoque manual:
331
+
332
+ ```typescript
333
+ import { ExcelBuilder } from 'han-excel-builder';
334
+ import fs from 'fs/promises';
335
+
336
+ const builder = new ExcelBuilder();
337
+ // ... construir tu libro de trabajo ...
338
+
339
+ // Obtener como buffer
340
+ const result = await builder.toBuffer();
341
+
342
+ if (result.success) {
343
+ const buffer = result.data; // ArrayBuffer
344
+
345
+ // Escribir en archivo
346
+ await fs.writeFile('./output/report.xlsx', Buffer.from(buffer));
347
+ console.log('¡Archivo guardado exitosamente!');
348
+ } else {
349
+ console.error('Error al construir:', result.error);
350
+ }
351
+ ```
352
+
353
+ **Resultado**: El archivo se guarda en la ruta especificada en el sistema de archivos.
354
+
355
+ #### 4. **Enviar como Respuesta HTTP** (Para Servidores Web)
356
+ Si estás construyendo un servidor web, puedes enviar el archivo directamente:
357
+
358
+ ```typescript
359
+ import { ExcelBuilder } from 'han-excel-builder';
360
+ import express from 'express';
361
+
362
+ const app = express();
363
+
364
+ app.get('/download-report', async (req, res) => {
365
+ const builder = new ExcelBuilder();
366
+ // ... build your workbook ...
367
+
368
+ const result = await builder.toBuffer();
369
+
370
+ if (result.success) {
371
+ res.setHeader('Content-Type',
372
+ 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet');
373
+ res.setHeader('Content-Disposition',
374
+ 'attachment; filename="report.xlsx"');
375
+ res.send(Buffer.from(result.data));
376
+ } else {
377
+ res.status(500).json({ error: result.error });
378
+ }
379
+ });
380
+ ```
381
+
382
+ **Result**: File is sent as HTTP response for download.
383
+
384
+ #### 5. **Usando generateAndDownload() en Node.js**
385
+ Aunque `generateAndDownload()` funciona en Node.js, no se recomienda ya que usa APIs específicas del navegador. Usa `saveToFile()` en su lugar:
386
+
387
+ ```typescript
388
+ // ❌ No recomendado en Node.js
389
+ await builder.generateAndDownload('report.xlsx');
390
+
391
+ // ✅ Recomendado en Node.js
392
+ await builder.saveToFile('report.xlsx');
393
+ ```
394
+
395
+ **Resultado**: `saveToFile()` es el equivalente de Node.js de `generateAndDownload()` - ¡simple y directo!
396
+
397
+ ---
398
+
399
+ ### 📊 Tabla Comparativa
400
+
401
+ | Método | Navegador | Node.js | Tipo de Resultado | Caso de Uso |
402
+ |--------|-----------|---------|-------------------|-------------|
403
+ | `generateAndDownload()` | ✅ Recomendado | ⚠️ Funciona pero no ideal | `void` | Descarga directa (navegador) |
404
+ | `saveToFile()` | ❌ No disponible | ✅ **Recomendado** | `void` | **Guardado directo (Node.js)** - ¡Simple! |
405
+ | `saveToStream()` | ❌ No disponible | ✅ Bueno | `void` | Stream a archivo (archivos grandes) |
406
+ | `toBlob()` | ✅ Bueno | ✅ Funciona | `Blob` | Manejo personalizado, subidas |
407
+ | `toBuffer()` | ✅ Funciona | ✅ Bueno | `ArrayBuffer` | Guardado manual, respuesta HTTP |
408
+
409
+ ### 💡 Mejores Prácticas
410
+
411
+ **Navegador:**
412
+ - Usa `generateAndDownload()` para descargas simples
413
+ - Usa `toBlob()` cuando necesites subir a un servidor o manejar el archivo programáticamente
414
+ - Usa `toBuffer()` para operaciones de bajo nivel
415
+
416
+ **Node.js:**
417
+ - **Usa `saveToFile()` para guardar archivos de forma simple** - ¡Igual que `generateAndDownload()` en el navegador!
418
+ - Usa `saveToStream()` para archivos muy grandes
419
+ - Usa `toBuffer()` + respuesta HTTP para servidores web
420
+ - Evita `generateAndDownload()` en Node.js (usa `saveToFile()` en su lugar)
421
+
422
+ ### 🔄 Example: Universal Export Function
423
+
424
+ Aquí hay una función auxiliar que funciona en ambos entornos:
425
+
426
+ ```typescript
427
+ async function exportExcel(
428
+ builder: ExcelBuilder,
429
+ filename: string
430
+ ): Promise<void> {
431
+ const isBrowser = typeof window !== 'undefined';
432
+
433
+ if (isBrowser) {
434
+ // Navegador: Descarga directa
435
+ await builder.generateAndDownload(filename);
436
+ } else {
437
+ // Node.js: Guardar en archivo - ¡Ahora tan simple como en el navegador!
438
+ const result = await builder.saveToFile(filename);
439
+
440
+ if (result.success) {
441
+ console.log(`Archivo guardado: ${filename}`);
442
+ } else {
443
+ throw new Error(result.error?.message || 'Error al exportar');
444
+ }
445
+ }
446
+ }
447
+
448
+ // Uso
449
+ await exportExcel(builder, 'report.xlsx');
450
+ ```
451
+
452
+ **Nota**: Ambos métodos (`generateAndDownload()` y `saveToFile()`) son ahora igual de simples - ¡una sola llamada a método!
453
+ ```
454
+
455
+ ## 📦 Instalación
456
+
457
+ ```bash
458
+ npm install han-excel-builder
459
+ # or
460
+ yarn add han-excel-builder
461
+ # or
462
+ pnpm add han-excel-builder
463
+ ```
464
+
465
+ ## 🚀 Inicio Rápido
466
+
467
+ ### Ejemplo Básico
468
+
469
+ ```typescript
470
+ import { ExcelBuilder, CellType, NumberFormat, StyleBuilder, BorderStyle } from 'han-excel-builder';
471
+
472
+ // Create a simple report
473
+ const builder = new ExcelBuilder({
474
+ metadata: {
475
+ title: 'Sales Report',
476
+ author: 'My Company',
477
+ description: 'Monthly sales report'
478
+ }
479
+ });
480
+
481
+ const worksheet = builder.addWorksheet('Sales');
482
+
483
+ // Add main header
484
+ worksheet.addHeader({
485
+ key: 'title',
486
+ value: 'Monthly Sales Report',
487
+ type: CellType.STRING,
488
+ mergeCell: true,
489
+ styles: new StyleBuilder()
490
+ .fontName('Arial')
491
+ .fontSize(16)
492
+ .fontBold()
493
+ .backgroundColor('#4472C4')
494
+ .fontColor('#FFFFFF')
495
+ .centerAlign()
496
+ .border(BorderStyle.THIN, '#8EAADB')
497
+ .build()
498
+ });
499
+
500
+ // Add sub-headers
501
+ worksheet.addSubHeaders([
502
+ {
503
+ key: 'product',
504
+ value: 'Product',
505
+ type: CellType.STRING,
506
+ colWidth: 20,
507
+ styles: new StyleBuilder()
508
+ .fontBold()
509
+ .backgroundColor('#8EAADB')
510
+ .fontColor('#FFFFFF')
511
+ .centerAlign()
512
+ .border(BorderStyle.THIN, '#8EAADB')
513
+ .build()
514
+ },
515
+ {
516
+ key: 'sales',
517
+ value: 'Sales',
518
+ type: CellType.CURRENCY,
519
+ colWidth: 15,
520
+ numberFormat: '$#,##0',
521
+ styles: new StyleBuilder()
522
+ .fontBold()
523
+ .backgroundColor('#8EAADB')
524
+ .fontColor('#FFFFFF')
525
+ .centerAlign()
526
+ .border(BorderStyle.THIN, '#8EAADB')
527
+ .build()
528
+ }
529
+ ]);
530
+
531
+ // Add data
532
+ worksheet.addRow([
533
+ {
534
+ key: 'product-1',
535
+ value: 'Product A',
536
+ type: CellType.STRING,
537
+ header: 'Product'
538
+ },
539
+ {
540
+ key: 'sales-1',
541
+ value: 1500.50,
542
+ type: CellType.CURRENCY,
543
+ header: 'Sales',
544
+ numberFormat: '$#,##0.00'
545
+ }
546
+ ]);
547
+
548
+ // Generate and download
549
+ await builder.generateAndDownload('sales-report.xlsx');
550
+ ```
551
+
552
+ ## 📚 Documentación de la API
553
+
554
+ ### Clases Principales
555
+
556
+ #### `ExcelBuilder`
557
+
558
+ Clase principal para crear libros de trabajo Excel.
559
+
560
+ ```typescript
561
+ const builder = new ExcelBuilder({
562
+ metadata: {
563
+ title: 'My Report',
564
+ author: 'My Name',
565
+ company: 'My Company',
566
+ description: 'Report description',
567
+ keywords: 'excel, report, data',
568
+ created: new Date(),
569
+ modified: new Date()
570
+ },
571
+ enableValidation: true,
572
+ enableEvents: true,
573
+ maxWorksheets: 255,
574
+ maxRowsPerWorksheet: 1048576,
575
+ maxColumnsPerWorksheet: 16384
576
+ });
577
+
578
+ // Métodos principales
579
+ builder.addWorksheet(name, config); // Agregar una hoja de cálculo
580
+ builder.getWorksheet(name); // Obtener una hoja de cálculo
581
+ builder.removeWorksheet(name); // Eliminar una hoja de cálculo
582
+ builder.setCurrentWorksheet(name); // Establecer la hoja de cálculo actual
583
+ builder.build(options); // Construir y obtener ArrayBuffer
584
+ builder.generateAndDownload(fileName); // Generar y descargar (navegador)
585
+ builder.saveToFile(filePath, options); // Guardar en archivo (Node.js) - ¡Simple!
586
+ builder.saveToStream(stream, options); // Guardar en stream (Node.js) - Archivos grandes
587
+ builder.toBuffer(options); // Obtener como Buffer
588
+ builder.toBlob(options); // Obtener como Blob
589
+ builder.validate(); // Validar el libro de trabajo
590
+ builder.clear(); // Limpiar todas las hojas de cálculo
591
+ builder.getStats(); // Obtener estadísticas
592
+
593
+ // Event system
594
+ builder.on(eventType, listener);
595
+ builder.off(eventType, listenerId);
596
+ builder.removeAllListeners(eventType);
597
+ ```
598
+
599
+ #### `ExcelReader`
600
+
601
+ Clase para leer archivos Excel y convertirlos a JSON con 3 formatos de salida diferentes.
602
+
603
+ **Available formats:**
604
+ - `worksheet` (default) - Complete structure with sheets, rows and cells
605
+ - `detailed` - Each cell with position information (text, column, row)
606
+ - `flat` - Just the data, without structure
607
+
608
+ ```typescript
609
+ import { ExcelReader, OutputFormat } from 'han-excel-builder';
610
+
611
+ // ===== FORMAT 1: WORKSHEET (default) =====
612
+ // Complete structure organized by sheets
613
+ const result = await ExcelReader.fromFile(file, {
614
+ outputFormat: OutputFormat.WORKSHEET, // or 'worksheet'
615
+ useFirstRowAsHeaders: true
616
+ });
617
+
618
+ if (result.success) {
619
+ const workbook = result.data;
620
+ // workbook.sheets[] - Array of sheets
621
+ // workbook.sheets[0].rows[] - Array of rows
622
+ // workbook.sheets[0].rows[0].cells[] - Array of cells
623
+ // workbook.sheets[0].rows[0].cells[0].comment - Cell comment (if exists)
624
+ // workbook.sheets[0].rows[0].data - Object with data (if useFirstRowAsHeaders)
625
+ }
626
+
627
+ // ===== FORMAT 2: DETAILED =====
628
+ // Each cell with position information
629
+ const result = await ExcelReader.fromFile(file, {
630
+ outputFormat: OutputFormat.DETAILED, // or 'detailed'
631
+ includeFormatting: true
632
+ });
633
+
634
+ if (result.success) {
635
+ const detailed = result.data;
636
+ // detailed.cells[] - Array of all cells with:
637
+ // - value: cell value
638
+ // - text: cell text
639
+ // - column: column number (1-based)
640
+ // - columnLetter: column letter (A, B, C...)
641
+ // - row: row number (1-based)
642
+ // - reference: cell reference (A1, B2...)
643
+ // - sheet: sheet name
644
+ // - comment: cell comment (if exists)
645
+ detailed.cells.forEach(cell => {
646
+ console.log(`${cell.sheet}!${cell.reference}: ${cell.text}`);
647
+ if (cell.comment) {
648
+ console.log(` Comment: ${cell.comment}`);
649
+ }
650
+ });
651
+ }
652
+
653
+ // ===== FORMAT 3: FLAT =====
654
+ // Just the data, without structure
655
+ const result = await ExcelReader.fromFile(file, {
656
+ outputFormat: OutputFormat.FLAT, // or 'flat'
657
+ useFirstRowAsHeaders: true
658
+ });
659
+
660
+ if (result.success) {
661
+ const flat = result.data;
662
+
663
+ // If single sheet:
664
+ if ('data' in flat) {
665
+ // flat.data[] - Array of objects or arrays
666
+ // flat.headers[] - Headers (if useFirstRowAsHeaders)
667
+ flat.data.forEach(row => {
668
+ console.log(row); // { Product: 'A', Price: 100 } or ['A', 100]
669
+ });
670
+ }
671
+
672
+ // If multiple sheets:
673
+ if ('sheets' in flat) {
674
+ // flat.sheets['SheetName'].data[] - Data by sheet
675
+ Object.keys(flat.sheets).forEach(sheetName => {
676
+ console.log(`Sheet: ${sheetName}`);
677
+ flat.sheets[sheetName].data.forEach(row => {
678
+ console.log(row);
679
+ });
680
+ });
681
+ }
682
+ }
683
+
684
+ // ===== USING MAPPER TO TRANSFORM DATA =====
685
+ // The mapper allows transforming the response before returning it
686
+ const result = await ExcelReader.fromFile(file, {
687
+ outputFormat: OutputFormat.WORKSHEET,
688
+ useFirstRowAsHeaders: true,
689
+ // Mapper receives the payload and returns the transformation
690
+ mapper: (data) => {
691
+ // Transform data according to needs
692
+ const transformed = {
693
+ totalSheets: data.totalSheets,
694
+ sheets: data.sheets.map(sheet => ({
695
+ name: sheet.name,
696
+ // Convert rows to objects with transformed data
697
+ rows: sheet.rows.map(row => {
698
+ if (row.data) {
699
+ // Transform each field
700
+ return {
701
+ ...row.data,
702
+ // Add calculated fields
703
+ total: Object.values(row.data).reduce((sum, val) => {
704
+ return sum + (typeof val === 'number' ? val : 0);
705
+ }, 0)
706
+ };
707
+ }
708
+ return row;
709
+ })
710
+ }))
711
+ };
712
+ return transformed;
713
+ }
714
+ });
715
+
716
+ // Example with FLAT format and mapper
717
+ const result = await ExcelReader.fromFile(file, {
718
+ outputFormat: OutputFormat.FLAT,
719
+ useFirstRowAsHeaders: true,
720
+ mapper: (data) => {
721
+ // If flat format from single sheet
722
+ if ('data' in data && Array.isArray(data.data)) {
723
+ return data.data.map((row: any) => ({
724
+ ...row,
725
+ // Add validations or transformations
726
+ isValid: Object.values(row).every(val => val !== null && val !== undefined)
727
+ }));
728
+ }
729
+ return data;
730
+ }
731
+ });
732
+
733
+ // Example with DETAILED format and mapper
734
+ const result = await ExcelReader.fromFile(file, {
735
+ outputFormat: OutputFormat.DETAILED,
736
+ mapper: (data) => {
737
+ // Group cells by sheet
738
+ const groupedBySheet: Record<string, typeof data.cells> = {};
739
+ data.cells.forEach(cell => {
740
+ if (!groupedBySheet[cell.sheet]) {
741
+ groupedBySheet[cell.sheet] = [];
742
+ }
743
+ groupedBySheet[cell.sheet].push(cell);
744
+ });
745
+ return {
746
+ sheets: Object.keys(groupedBySheet).map(sheetName => ({
747
+ name: sheetName,
748
+ cells: groupedBySheet[sheetName]
749
+ }))
750
+ };
751
+ }
752
+ });
753
+ ```
754
+
755
+ **Reading options:**
756
+
757
+ ```typescript
758
+ interface IExcelReaderOptions {
759
+ outputFormat?: 'worksheet' | 'detailed' | 'flat' | OutputFormat; // Output format
760
+ mapper?: (data: IJsonWorkbook | IDetailedFormat | IFlatFormat | IFlatFormatMultiSheet) => unknown; // Function to transform the response
761
+ useFirstRowAsHeaders?: boolean; // Use first row as headers
762
+ includeEmptyRows?: boolean; // Include empty rows
763
+ headers?: string[] | Record<number, string>; // Custom headers
764
+ sheetName?: string | number; // Sheet name or index
765
+ startRow?: number; // Starting row (1-based)
766
+ endRow?: number; // Ending row (1-based)
767
+ startColumn?: number; // Starting column (1-based)
768
+ endColumn?: number; // Ending column (1-based)
769
+ includeFormatting?: boolean; // Include formatting information
770
+ includeFormulas?: boolean; // Include formulas
771
+ datesAsISO?: boolean; // Convert dates to ISO string
772
+ }
773
+ ```
774
+
775
+ **Output formats:**
776
+
777
+ - **`worksheet`** (default): Complete structure with sheets, rows and cells
778
+ - **`detailed`**: Array of cells with position information (text, column, row, reference)
779
+ - **`flat`**: Just the data, without structure (flat arrays or objects)
780
+
781
+ #### `Worksheet`
782
+
783
+ Representa una hoja de cálculo individual.
784
+
785
+ ```typescript
786
+ const worksheet = builder.addWorksheet('My Sheet', {
787
+ tabColor: '#FF0000',
788
+ defaultRowHeight: 20,
789
+ defaultColWidth: 15,
790
+ pageSetup: {
791
+ orientation: 'portrait',
792
+ paperSize: 9
793
+ }
794
+ });
795
+
796
+ // Main methods
797
+ worksheet.addHeader(header); // Add main header
798
+ worksheet.addSubHeaders(headers); // Add sub-headers
799
+ worksheet.addRow(row); // Add data row
800
+ worksheet.addFooter(footer); // Add footer
801
+ worksheet.addTable(config); // Create new table
802
+ worksheet.finalizeTable(); // Finalize current table
803
+ worksheet.getTable(name); // Get table by name
804
+ worksheet.validate(); // Validate sheet
805
+ ```
806
+
807
+ ### Tipos de Datos
808
+
809
+ #### `CellType`
810
+
811
+ ```typescript
812
+ enum CellType {
813
+ STRING = 'string', // Text
814
+ NUMBER = 'number', // Number
815
+ BOOLEAN = 'boolean', // True/False
816
+ DATE = 'date', // Date
817
+ PERCENTAGE = 'percentage', // Percentage
818
+ CURRENCY = 'currency', // Currency
819
+ LINK = 'link', // Hyperlink
820
+ FORMULA = 'formula' // Formula
821
+ }
822
+ ```
823
+
824
+ #### `NumberFormat`
825
+
826
+ ```typescript
827
+ enum NumberFormat {
828
+ GENERAL = 'General',
829
+ NUMBER = '#,##0',
830
+ NUMBER_DECIMALS = '#,##0.00',
831
+ CURRENCY = '$#,##0.00',
832
+ CURRENCY_INTEGER = '$#,##0',
833
+ PERCENTAGE = '0%',
834
+ PERCENTAGE_DECIMALS = '0.00%',
835
+ DATE = 'dd/mm/yyyy',
836
+ DATE_TIME = 'dd/mm/yyyy hh:mm',
837
+ TIME = 'hh:mm:ss',
838
+ CUSTOM = 'custom'
839
+ }
840
+ ```
841
+
842
+ ### Estilos
843
+
844
+ #### `StyleBuilder`
845
+
846
+ API fluida para crear estilos de celda.
847
+
848
+ ```typescript
849
+ const style = new StyleBuilder()
850
+ // Fonts
851
+ .fontName('Arial')
852
+ .fontSize(12)
853
+ .fontBold()
854
+ .fontItalic()
855
+ .fontUnderline()
856
+ .fontColor('#FF0000')
857
+
858
+ // Backgrounds and borders
859
+ .backgroundColor('#FFFF00')
860
+ .border(BorderStyle.THIN, '#000000')
861
+ .borderTop(BorderStyle.MEDIUM, '#000000')
862
+ .borderLeft(BorderStyle.THIN, '#000000')
863
+ .borderBottom(BorderStyle.THIN, '#000000')
864
+ .borderRight(BorderStyle.THIN, '#000000')
865
+
866
+ // Alignment
867
+ .centerAlign()
868
+ .leftAlign()
869
+ .rightAlign()
870
+ .horizontalAlign(HorizontalAlignment.CENTER)
871
+ .verticalAlign(VerticalAlignment.MIDDLE)
872
+ .wrapText()
873
+
874
+ // Formats
875
+ .numberFormat('$#,##0.00')
876
+ .striped()
877
+
878
+ // Conditional formatting
879
+ .conditionalFormat({
880
+ type: 'cellIs',
881
+ operator: 'greaterThan',
882
+ values: [1000],
883
+ style: StyleBuilder.create()
884
+ .backgroundColor('#90EE90')
885
+ .fontColor('#006400')
886
+ .build()
887
+ })
888
+
889
+ .build();
890
+
891
+ // Alternative static method
892
+ const style2 = StyleBuilder.create()
893
+ .fontBold()
894
+ .fontSize(14)
895
+ .build();
896
+ ```
897
+
898
+ #### `BorderStyle`
899
+
900
+ ```typescript
901
+ enum BorderStyle {
902
+ THIN = 'thin',
903
+ MEDIUM = 'medium',
904
+ THICK = 'thick',
905
+ DOTTED = 'dotted',
906
+ DASHED = 'dashed',
907
+ DOUBLE = 'double',
908
+ HAIR = 'hair',
909
+ MEDIUM_DASHED = 'mediumDashed',
910
+ DASH_DOT = 'dashDot',
911
+ MEDIUM_DASH_DOT = 'mediumDashDot',
912
+ DASH_DOT_DOT = 'dashDotDot',
913
+ MEDIUM_DASH_DOT_DOT = 'mediumDashDotDot',
914
+ SLANT_DASH_DOT = 'slantDashDot'
915
+ }
916
+ ```
917
+
918
+ ## 🎯 Ejemplos Avanzados
919
+
920
+ ### Multiple Tables in a Sheet
921
+
922
+ ```typescript
923
+ import { ExcelBuilder, CellType, StyleBuilder, BorderStyle } from 'han-excel-builder';
924
+
925
+ const builder = new ExcelBuilder();
926
+ const worksheet = builder.addWorksheet('Complete Report');
927
+
928
+ // ===== FIRST TABLE =====
929
+ worksheet.addTable({
930
+ name: 'Sales',
931
+ showBorders: true,
932
+ showStripes: true,
933
+ style: 'TableStyleLight1'
934
+ });
935
+
936
+ worksheet.addHeader({
937
+ key: 'header-sales',
938
+ type: CellType.STRING,
939
+ value: 'SALES SUMMARY',
940
+ mergeCell: true,
941
+ styles: new StyleBuilder()
942
+ .fontBold()
943
+ .fontSize(16)
944
+ .backgroundColor('#4472C4')
945
+ .fontColor('#FFFFFF')
946
+ .centerAlign()
947
+ .build()
948
+ });
949
+
950
+ worksheet.addSubHeaders([
951
+ { key: 'product', type: CellType.STRING, value: 'Product' },
952
+ { key: 'sales', type: CellType.CURRENCY, value: 'Sales' }
953
+ ]);
954
+
955
+ worksheet.addRow([
956
+ { key: 'p1', type: CellType.STRING, value: 'Product A', header: 'Product' },
957
+ { key: 'v1', type: CellType.CURRENCY, value: 1500, header: 'Sales' }
958
+ ]);
959
+
960
+ worksheet.finalizeTable();
961
+
962
+ // ===== SECOND TABLE =====
963
+ worksheet.addTable({
964
+ name: 'Employees',
965
+ showBorders: true,
966
+ showStripes: true,
967
+ style: 'TableStyleMedium1'
968
+ });
969
+
970
+ worksheet.addHeader({
971
+ key: 'header-employees',
972
+ type: CellType.STRING,
973
+ value: 'TOP EMPLOYEES',
974
+ mergeCell: true,
975
+ styles: new StyleBuilder()
976
+ .fontBold()
977
+ .fontSize(16)
978
+ .backgroundColor('#70AD47')
979
+ .fontColor('#FFFFFF')
980
+ .centerAlign()
981
+ .build()
982
+ });
983
+
984
+ worksheet.addSubHeaders([
985
+ { key: 'name', type: CellType.STRING, value: 'Name' },
986
+ { key: 'sales', type: CellType.CURRENCY, value: 'Sales' }
987
+ ]);
988
+
989
+ worksheet.addRow([
990
+ { key: 'e1', type: CellType.STRING, value: 'John Doe', header: 'Name' },
991
+ { key: 've1', type: CellType.CURRENCY, value: 150000, header: 'Sales' }
992
+ ]);
993
+
994
+ worksheet.finalizeTable();
995
+
996
+ await builder.generateAndDownload('multiple-tables.xlsx');
997
+ ```
998
+
999
+ ### Nested Headers
1000
+
1001
+ ```typescript
1002
+ worksheet.addSubHeaders([
1003
+ {
1004
+ key: 'sales',
1005
+ value: 'Sales',
1006
+ type: CellType.STRING,
1007
+ children: [
1008
+ {
1009
+ key: 'sales-q1',
1010
+ value: 'Q1',
1011
+ type: CellType.STRING
1012
+ },
1013
+ {
1014
+ key: 'sales-q2',
1015
+ value: 'Q2',
1016
+ type: CellType.STRING
1017
+ }
1018
+ ]
1019
+ },
1020
+ {
1021
+ key: 'expenses',
1022
+ value: 'Expenses',
1023
+ type: CellType.STRING,
1024
+ children: [
1025
+ {
1026
+ key: 'expenses-q1',
1027
+ value: 'Q1',
1028
+ type: CellType.STRING
1029
+ },
1030
+ {
1031
+ key: 'expenses-q2',
1032
+ value: 'Q2',
1033
+ type: CellType.STRING
1034
+ }
1035
+ ]
1036
+ }
1037
+ ]);
1038
+ ```
1039
+
1040
+ ### Cell Comments
1041
+
1042
+ Add comments to cells for additional context or notes:
1043
+
1044
+ ```typescript
1045
+ // Add comment to a header
1046
+ worksheet.addHeader({
1047
+ key: 'header-1',
1048
+ type: CellType.STRING,
1049
+ value: 'Sales Report',
1050
+ comment: 'This is the main title of the report'
1051
+ });
1052
+
1053
+ // Add comment to a data cell
1054
+ worksheet.addRow([
1055
+ {
1056
+ key: 'product-1',
1057
+ value: 'Product A',
1058
+ type: CellType.STRING,
1059
+ header: 'Product',
1060
+ comment: 'Best selling product this month'
1061
+ },
1062
+ {
1063
+ key: 'sales-1',
1064
+ value: 1500.50,
1065
+ type: CellType.CURRENCY,
1066
+ header: 'Sales',
1067
+ comment: 'Sales increased 15% from last month'
1068
+ }
1069
+ ]);
1070
+
1071
+ // Comments are also supported in subheaders and footers
1072
+ worksheet.addSubHeaders([
1073
+ {
1074
+ key: 'product',
1075
+ type: CellType.STRING,
1076
+ value: 'Product',
1077
+ comment: 'Product name column'
1078
+ }
1079
+ ]);
1080
+ ```
1081
+
1082
+ When reading Excel files, comments are included in the output:
1083
+
1084
+ ```typescript
1085
+ const result = await ExcelReader.fromFile(file, {
1086
+ outputFormat: OutputFormat.WORKSHEET
1087
+ });
1088
+
1089
+ if (result.success) {
1090
+ result.data.sheets.forEach(sheet => {
1091
+ sheet.rows.forEach(row => {
1092
+ row.cells.forEach(cell => {
1093
+ if (cell.comment) {
1094
+ console.log(`Cell ${cell.reference}: ${cell.comment}`);
1095
+ }
1096
+ });
1097
+ });
1098
+ });
1099
+ }
1100
+ ```
1101
+
1102
+ ### Hyperlinks
1103
+
1104
+ ```typescript
1105
+ worksheet.addRow([
1106
+ {
1107
+ key: 'link-1',
1108
+ type: CellType.LINK,
1109
+ value: 'Visit site',
1110
+ link: 'https://example.com',
1111
+ mask: 'Click here', // Visible text
1112
+ header: 'Link'
1113
+ }
1114
+ ]);
1115
+ ```
1116
+
1117
+ ### Data Validation
1118
+
1119
+ Apply data validation rules to cells to restrict input values:
1120
+
1121
+ ```typescript
1122
+ // List validation (dropdown)
1123
+ worksheet.addRow([
1124
+ {
1125
+ key: 'status-1',
1126
+ value: 'Active',
1127
+ type: CellType.STRING,
1128
+ header: 'Status',
1129
+ validation: {
1130
+ type: 'list',
1131
+ formula1: 'Active,Inactive,Pending',
1132
+ showErrorMessage: true,
1133
+ errorMessage: 'Please select a valid status',
1134
+ showInputMessage: true,
1135
+ inputMessage: 'Select status from the list',
1136
+ allowBlank: false
1137
+ }
1138
+ }
1139
+ ]);
1140
+
1141
+ // Number range validation
1142
+ worksheet.addRow([
1143
+ {
1144
+ key: 'age-1',
1145
+ value: 25,
1146
+ type: CellType.NUMBER,
1147
+ header: 'Age',
1148
+ validation: {
1149
+ type: 'whole',
1150
+ operator: 'between',
1151
+ formula1: 18,
1152
+ formula2: 100,
1153
+ showErrorMessage: true,
1154
+ errorMessage: 'Age must be between 18 and 100',
1155
+ allowBlank: false
1156
+ }
1157
+ }
1158
+ ]);
1159
+
1160
+ // Date validation
1161
+ worksheet.addRow([
1162
+ {
1163
+ key: 'date-1',
1164
+ value: new Date(),
1165
+ type: CellType.DATE,
1166
+ header: 'Date',
1167
+ validation: {
1168
+ type: 'date',
1169
+ operator: 'greaterThan',
1170
+ formula1: new Date('2020-01-01'),
1171
+ showErrorMessage: true,
1172
+ errorMessage: 'Date must be after 2020-01-01',
1173
+ allowBlank: true
1174
+ }
1175
+ }
1176
+ ]);
1177
+
1178
+ // Text length validation
1179
+ worksheet.addRow([
1180
+ {
1181
+ key: 'name-1',
1182
+ value: 'John Doe',
1183
+ type: CellType.STRING,
1184
+ header: 'Name',
1185
+ validation: {
1186
+ type: 'textLength',
1187
+ operator: 'lessThanOrEqual',
1188
+ formula1: 50,
1189
+ showErrorMessage: true,
1190
+ errorMessage: 'Name must be 50 characters or less',
1191
+ allowBlank: false
1192
+ }
1193
+ }
1194
+ ]);
1195
+
1196
+ // Custom formula validation
1197
+ worksheet.addRow([
1198
+ {
1199
+ key: 'value-1',
1200
+ value: 100,
1201
+ type: CellType.NUMBER,
1202
+ header: 'Value',
1203
+ validation: {
1204
+ type: 'custom',
1205
+ formula1: '=A1>0',
1206
+ showErrorMessage: true,
1207
+ errorMessage: 'Value must be greater than 0',
1208
+ allowBlank: false
1209
+ }
1210
+ }
1211
+ ]);
1212
+ ```
1213
+
1214
+ ### Auto Filters
1215
+
1216
+ Habilitar filtros automáticos para tablas y hojas de cálculo to allow users to filter data:
1217
+
1218
+ ```typescript
1219
+ // Enable auto filter for a table
1220
+ worksheet.addTable({
1221
+ name: 'Sales',
1222
+ autoFilter: true, // Enable auto filter for this table
1223
+ showBorders: true
1224
+ });
1225
+
1226
+ // Enable auto filter at worksheet level
1227
+ const worksheet = builder.addWorksheet('Sales Report', {
1228
+ autoFilter: {
1229
+ enabled: true,
1230
+ startRow: 2, // Start from row 2 (skip header)
1231
+ endRow: 100, // End at row 100
1232
+ startColumn: 1,
1233
+ endColumn: 5
1234
+ }
1235
+ });
1236
+
1237
+ // Or use a range
1238
+ const worksheet = builder.addWorksheet('Sales Report', {
1239
+ autoFilter: {
1240
+ enabled: true,
1241
+ range: {
1242
+ start: { row: 2, col: 1, reference: 'A2' },
1243
+ end: { row: 100, col: 5, reference: 'E100' }
1244
+ }
1245
+ }
1246
+ });
1247
+ ```
1248
+
1249
+ ### Data with Children (Hierarchical Structure)
1250
+
1251
+ ```typescript
1252
+ worksheet.addRow([
1253
+ {
1254
+ key: 'row-1',
1255
+ type: CellType.STRING,
1256
+ value: 'Main Category',
1257
+ header: 'Category',
1258
+ children: [
1259
+ {
1260
+ key: 'child-1',
1261
+ type: CellType.STRING,
1262
+ value: 'Subcategory 1',
1263
+ header: 'Subcategory'
1264
+ },
1265
+ {
1266
+ key: 'child-2',
1267
+ type: CellType.NUMBER,
1268
+ value: 100,
1269
+ header: 'Value'
1270
+ }
1271
+ ]
1272
+ }
1273
+ ]);
1274
+ ```
1275
+
1276
+ ### Conditional Formatting
1277
+
1278
+ Apply conditional formatting rules to cells based on values, formulas, or conditions:
1279
+
1280
+ ```typescript
1281
+ worksheet.addRow([
1282
+ {
1283
+ key: 'sales-1',
1284
+ type: CellType.NUMBER,
1285
+ value: 1500,
1286
+ header: 'Sales',
1287
+ styles: new StyleBuilder()
1288
+ .conditionalFormat({
1289
+ type: 'cellIs',
1290
+ operator: 'greaterThan',
1291
+ values: [1000],
1292
+ style: StyleBuilder.create()
1293
+ .backgroundColor('#90EE90')
1294
+ .fontColor('#006400')
1295
+ .build()
1296
+ })
1297
+ .conditionalFormat({
1298
+ type: 'cellIs',
1299
+ operator: 'lessThan',
1300
+ values: [500],
1301
+ style: StyleBuilder.create()
1302
+ .backgroundColor('#FFB6C1')
1303
+ .fontColor('#8B0000')
1304
+ .build()
1305
+ })
1306
+ .build()
1307
+ }
1308
+ ]);
1309
+
1310
+ // Multiple conditional formats with different types
1311
+ worksheet.addRow([
1312
+ {
1313
+ key: 'status-1',
1314
+ type: CellType.STRING,
1315
+ value: 'Active',
1316
+ header: 'Status',
1317
+ styles: new StyleBuilder()
1318
+ .conditionalFormat({
1319
+ type: 'containsText',
1320
+ operator: 'equal',
1321
+ values: ['Active'],
1322
+ style: StyleBuilder.create()
1323
+ .backgroundColor('#90EE90')
1324
+ .build()
1325
+ })
1326
+ .build()
1327
+ }
1328
+ ]);
1329
+ ```
1330
+
1331
+ ### Freeze Panes
1332
+
1333
+ Freeze rows and columns to keep headers visible while scrolling:
1334
+
1335
+ ```typescript
1336
+ const worksheet = builder.addWorksheet('Sales Report', {
1337
+ freezePanes: {
1338
+ row: 2, // Freeze from row 2 (header row)
1339
+ col: 1, // Freeze from column 1
1340
+ reference: 'A2' // Optional: cell reference
1341
+ }
1342
+ });
1343
+ ```
1344
+
1345
+ ### Worksheet Protection
1346
+
1347
+ Protect worksheets with password and configure permissions:
1348
+
1349
+ ```typescript
1350
+ const worksheet = builder.addWorksheet('Protected Sheet', {
1351
+ protected: true,
1352
+ protectionPassword: 'mypassword123',
1353
+ // Other protection options are handled by ExcelJS defaults
1354
+ });
1355
+ ```
1356
+
1357
+ ### Images/Pictures
1358
+
1359
+ Add images to worksheets (logos, charts, signatures, etc.):
1360
+
1361
+ ```typescript
1362
+ // From ArrayBuffer (e.g., from fetch)
1363
+ const response = await fetch('https://example.com/logo.png');
1364
+ const arrayBuffer = await response.arrayBuffer();
1365
+
1366
+ worksheet.addImage({
1367
+ buffer: arrayBuffer,
1368
+ extension: 'png',
1369
+ position: {
1370
+ row: 1,
1371
+ col: 1
1372
+ },
1373
+ size: {
1374
+ width: 200,
1375
+ height: 100
1376
+ },
1377
+ description: 'Company Logo'
1378
+ });
1379
+
1380
+ // From base64 string
1381
+ worksheet.addImage({
1382
+ buffer: 'data:image/png;base64,iVBORw0KGgoAAAANS...',
1383
+ extension: 'png',
1384
+ position: {
1385
+ row: 'A2', // Can use cell reference
1386
+ col: 1
1387
+ },
1388
+ size: {
1389
+ scaleX: 0.5, // Scale to 50%
1390
+ scaleY: 0.5
1391
+ },
1392
+ hyperlink: 'https://example.com'
1393
+ });
1394
+ ```
1395
+
1396
+ ### Row/Column Grouping
1397
+
1398
+ Group rows and columns to create collapsible outlines:
1399
+
1400
+ ```typescript
1401
+ // Group rows 2-10 (collapsible)
1402
+ worksheet.groupRows(2, 10, true);
1403
+
1404
+ // Group columns A-C
1405
+ worksheet.groupColumns(1, 3, false);
1406
+
1407
+ // Nested grouping
1408
+ worksheet.groupRows(2, 5, false); // Level 1
1409
+ worksheet.groupRows(2, 3, false); // Level 2 (nested)
1410
+ ```
1411
+
1412
+ ### Named Ranges
1413
+
1414
+ Definir rangos con nombre para referencias fáciles in formulas:
1415
+
1416
+ ```typescript
1417
+ // Using string range
1418
+ worksheet.addNamedRange('SalesData', 'A1:D100');
1419
+
1420
+ // Using ICellRange
1421
+ worksheet.addNamedRange('HeaderRow', {
1422
+ start: { row: 1, col: 1, reference: 'A1' },
1423
+ end: { row: 1, col: 5, reference: 'E1' }
1424
+ });
1425
+
1426
+ // Named range with scope (worksheet-specific)
1427
+ worksheet.addNamedRange('LocalRange', 'A1:A10', 'Sheet1');
1428
+ ```
1429
+
1430
+ ### Excel Structured Tables
1431
+
1432
+ Crear tablas estructuradas de Excel con estilos automáticos and features:
1433
+
1434
+ ```typescript
1435
+ // First, add data to the worksheet
1436
+ worksheet.addSubHeaders([
1437
+ { key: 'product', value: 'Product', type: CellType.STRING },
1438
+ { key: 'sales', value: 'Sales', type: CellType.NUMBER },
1439
+ { key: 'revenue', value: 'Revenue', type: CellType.CURRENCY }
1440
+ ]);
1441
+
1442
+ // Add data rows...
1443
+ worksheet.addRow([...]);
1444
+
1445
+ // Then add Excel structured table
1446
+ worksheet.addExcelTable({
1447
+ name: 'SalesTable',
1448
+ range: {
1449
+ start: 'A1',
1450
+ end: 'C10'
1451
+ },
1452
+ style: 'TableStyleMedium2',
1453
+ headerRow: true,
1454
+ totalRow: true,
1455
+ columns: [
1456
+ { name: 'Product', filterButton: true },
1457
+ { name: 'Sales', filterButton: true, totalsRowFunction: 'sum' },
1458
+ { name: 'Revenue', filterButton: true, totalsRowFunction: 'sum' }
1459
+ ]
1460
+ });
1461
+ ```
1462
+
1463
+ ### Advanced Print Settings
1464
+
1465
+ Configure headers, footers, and repeat rows/columns:
1466
+
1467
+ ```typescript
1468
+ const worksheet = builder.addWorksheet('Report', {
1469
+ printHeadersFooters: {
1470
+ header: {
1471
+ left: 'Company Name',
1472
+ center: 'Sales Report',
1473
+ right: 'Page &P of &N'
1474
+ },
1475
+ footer: {
1476
+ left: 'Confidential',
1477
+ center: 'Generated on &D',
1478
+ right: '&F'
1479
+ }
1480
+ },
1481
+ printRepeat: {
1482
+ rows: [1, 2], // Repeat header rows on each page
1483
+ columns: 'A:B' // Repeat first two columns
1484
+ }
1485
+ });
1486
+ ```
1487
+
1488
+ ### Hide/Show Rows & Columns
1489
+
1490
+ Ocultar o mostrar filas y columnas específicas:
1491
+
1492
+ ```typescript
1493
+ // Hide single row
1494
+ worksheet.hideRows(5);
1495
+
1496
+ // Hide multiple rows
1497
+ worksheet.hideRows([3, 4, 5, 10]);
1498
+
1499
+ // Show rows (if previously hidden)
1500
+ worksheet.showRows([3, 4]);
1501
+
1502
+ // Hide columns by number or letter
1503
+ worksheet.hideColumns(1); // Column A
1504
+ worksheet.hideColumns('B'); // Column B
1505
+ worksheet.hideColumns([1, 2, 3]); // Columns A, B, C
1506
+ worksheet.hideColumns(['A', 'D']); // Columns A and D
1507
+
1508
+ // Show columns
1509
+ worksheet.showColumns([1, 2]);
1510
+ ```
1511
+
1512
+ ### Rich Text in Cells
1513
+
1514
+ Formatear texto con múltiples estilos dentro de una sola celda:
1515
+
1516
+ ```typescript
1517
+ worksheet.addRow([
1518
+ {
1519
+ key: 'rich-text-1',
1520
+ type: CellType.STRING,
1521
+ value: '', // Empty value when using richText
1522
+ header: 'Description',
1523
+ richText: [
1524
+ {
1525
+ text: 'This is ',
1526
+ font: 'Arial',
1527
+ size: 11
1528
+ },
1529
+ {
1530
+ text: 'bold',
1531
+ font: 'Arial',
1532
+ size: 11,
1533
+ bold: true,
1534
+ color: '#FF0000'
1535
+ },
1536
+ {
1537
+ text: ' and ',
1538
+ font: 'Arial',
1539
+ size: 11
1540
+ },
1541
+ {
1542
+ text: 'italic',
1543
+ font: 'Arial',
1544
+ size: 11,
1545
+ italic: true,
1546
+ color: '#0000FF'
1547
+ },
1548
+ {
1549
+ text: ' text!',
1550
+ font: 'Arial',
1551
+ size: 11
1552
+ }
1553
+ ]
1554
+ }
1555
+ ]);
1556
+ ```
1557
+
1558
+ ### Cell-level Protection
1559
+
1560
+ Proteger celdas individuales con opciones de bloqueo/ocultación:
1561
+
1562
+ ```typescript
1563
+ worksheet.addRow([
1564
+ {
1565
+ key: 'protected-1',
1566
+ type: CellType.STRING,
1567
+ value: 'Locked Cell',
1568
+ header: 'Status',
1569
+ cellProtection: {
1570
+ locked: true, // Cell cannot be edited
1571
+ hidden: false // Formula is visible
1572
+ }
1573
+ },
1574
+ {
1575
+ key: 'unlocked-1',
1576
+ type: CellType.STRING,
1577
+ value: 'Editable Cell',
1578
+ header: 'Notes',
1579
+ cellProtection: {
1580
+ locked: false, // Cell can be edited
1581
+ hidden: false
1582
+ }
1583
+ },
1584
+ {
1585
+ key: 'hidden-1',
1586
+ type: CellType.FORMULA,
1587
+ value: '=SUM(A1:A10)',
1588
+ header: 'Total',
1589
+ cellProtection: {
1590
+ locked: true,
1591
+ hidden: true // Formula is hidden (shows only result)
1592
+ }
1593
+ }
1594
+ ]);
1595
+
1596
+ // Note: Worksheet must be protected for cell protection to take effect
1597
+ const worksheet = builder.addWorksheet('Protected Sheet', {
1598
+ protected: true,
1599
+ protectionPassword: 'password123'
1600
+ });
1601
+ ```
1602
+
1603
+ ### Pivot Tables
1604
+
1605
+ Crear tablas dinámicas para análisis de datos:
1606
+
1607
+ ```typescript
1608
+ // First, create a data sheet
1609
+ const dataSheet = builder.addWorksheet('Sales Data');
1610
+ dataSheet.addSubHeaders([
1611
+ { key: 'category', value: 'Category', type: CellType.STRING },
1612
+ { key: 'product', value: 'Product', type: CellType.STRING },
1613
+ { key: 'sales', value: 'Sales', type: CellType.NUMBER },
1614
+ { key: 'revenue', value: 'Revenue', type: CellType.CURRENCY }
1615
+ ]);
1616
+
1617
+ // Add data rows...
1618
+ dataSheet.addRow([...]);
1619
+
1620
+ // Create a pivot table sheet
1621
+ const pivotSheet = builder.addWorksheet('Pivot Analysis');
1622
+ pivotSheet.addPivotTable({
1623
+ name: 'SalesPivot',
1624
+ ref: 'A1',
1625
+ sourceRange: 'A1:D100',
1626
+ sourceSheet: 'Sales Data',
1627
+ fields: {
1628
+ rows: ['Category', 'Product'],
1629
+ columns: [],
1630
+ values: [
1631
+ { name: 'Sales', stat: 'sum' },
1632
+ { name: 'Revenue', stat: 'sum' }
1633
+ ],
1634
+ filters: []
1635
+ },
1636
+ options: {
1637
+ showRowGrandTotals: true,
1638
+ showColGrandTotals: true,
1639
+ showHeaders: true
1640
+ }
1641
+ });
1642
+ ```
1643
+
1644
+ ### Slicers
1645
+
1646
+ Add visual filters (slicers) to tables and pivot tables:
1647
+
1648
+ ```typescript
1649
+ // Note: Slicers require advanced ExcelJS XML manipulation
1650
+ // This feature is documented but requires manual XML editing for full implementation
1651
+
1652
+ worksheet.addSlicer({
1653
+ name: 'CategorySlicer',
1654
+ targetTable: 'SalesTable',
1655
+ column: 'Category',
1656
+ position: {
1657
+ row: 1,
1658
+ col: 'F'
1659
+ },
1660
+ size: {
1661
+ width: 200,
1662
+ height: 300
1663
+ }
1664
+ });
1665
+ ```
1666
+
1667
+ ### Watermarks
1668
+
1669
+ Agregar marcas de agua a hojas de cálculo (texto o imagen):
1670
+
1671
+ ```typescript
1672
+ // Text watermark
1673
+ worksheet.addWatermark({
1674
+ text: 'CONFIDENTIAL',
1675
+ position: {
1676
+ horizontal: 'center',
1677
+ vertical: 'middle'
1678
+ },
1679
+ opacity: 0.3,
1680
+ fontSize: 72,
1681
+ fontColor: '#CCCCCC',
1682
+ rotation: -45
1683
+ });
1684
+
1685
+ // Image watermark
1686
+ const watermarkImage = await fetch('https://example.com/watermark.png')
1687
+ .then(r => r.arrayBuffer());
1688
+
1689
+ worksheet.addWatermark({
1690
+ image: {
1691
+ buffer: watermarkImage,
1692
+ extension: 'png',
1693
+ position: {
1694
+ row: 500,
1695
+ col: 10
1696
+ },
1697
+ size: {
1698
+ width: 400,
1699
+ height: 300,
1700
+ scaleX: 0.3,
1701
+ scaleY: 0.3
1702
+ }
1703
+ },
1704
+ position: {
1705
+ horizontal: 'center',
1706
+ vertical: 'middle'
1707
+ },
1708
+ opacity: 0.3
1709
+ });
1710
+ ```
1711
+
1712
+ ### Cell-level Page Breaks
1713
+
1714
+ Add manual page breaks at row level:
1715
+
1716
+ ```typescript
1717
+ worksheet.addRow([
1718
+ {
1719
+ key: 'row-1',
1720
+ type: CellType.STRING,
1721
+ value: 'Data Row 1',
1722
+ header: 'Name'
1723
+ }
1724
+ ]);
1725
+
1726
+ // Add page break before this row
1727
+ worksheet.addRow([
1728
+ {
1729
+ key: 'row-2',
1730
+ type: CellType.STRING,
1731
+ value: 'Data Row 2',
1732
+ header: 'Name',
1733
+ pageBreak: true // Page break before this row
1734
+ }
1735
+ ]);
1736
+
1737
+ // Page breaks also work in headers and footers
1738
+ worksheet.addHeader({
1739
+ key: 'section-header',
1740
+ value: 'New Section',
1741
+ type: CellType.STRING,
1742
+ pageBreak: true // Page break before this header
1743
+ });
1744
+ ```
1745
+
1746
+ ### Cell Styles (Predefined)
1747
+
1748
+ Create reusable cell styles for consistency across your workbook:
1749
+
1750
+ ```typescript
1751
+ import { ExcelBuilder, StyleBuilder, CellType } from 'han-excel-builder';
1752
+
1753
+ const builder = new ExcelBuilder();
1754
+
1755
+ // Define reusable styles
1756
+ builder.addCellStyle('headerStyle', StyleBuilder.create()
1757
+ .font({ name: 'Arial', size: 14, bold: true })
1758
+ .fill({ backgroundColor: '#4472C4' })
1759
+ .fontColor('#FFFFFF')
1760
+ .build()
1761
+ );
1762
+
1763
+ builder.addCellStyle('highlightStyle', StyleBuilder.create()
1764
+ .fill({ backgroundColor: '#FFE699' })
1765
+ .font({ bold: true })
1766
+ .build()
1767
+ );
1768
+
1769
+ // Use predefined styles in cells
1770
+ const sheet = builder.addWorksheet('Data');
1771
+ sheet.addHeader({
1772
+ key: 'name',
1773
+ value: 'Name',
1774
+ type: CellType.STRING,
1775
+ styleName: 'headerStyle' // Use predefined style
1776
+ });
1777
+
1778
+ sheet.addRow({
1779
+ key: 'row1',
1780
+ header: 'name',
1781
+ value: 'John Doe',
1782
+ type: CellType.STRING,
1783
+ styleName: 'highlightStyle' // Use predefined style
1784
+ });
1785
+ ```
1786
+
1787
+ ### Themes
1788
+
1789
+ Apply color themes to the entire workbook:
1790
+
1791
+ ```typescript
1792
+ const builder = new ExcelBuilder();
1793
+
1794
+ // Set workbook theme
1795
+ builder.setTheme({
1796
+ name: 'Corporate Theme',
1797
+ colors: {
1798
+ dark1: '#000000',
1799
+ light1: '#FFFFFF',
1800
+ dark2: '#1F4E78',
1801
+ light2: '#EEECE1',
1802
+ accent1: '#4472C4',
1803
+ accent2: '#ED7D31',
1804
+ accent3: '#A5A5A5',
1805
+ accent4: '#FFC000',
1806
+ accent5: '#5B9BD5',
1807
+ accent6: '#70AD47',
1808
+ hyperlink: '#0563C1',
1809
+ followedHyperlink: '#954F72'
1810
+ },
1811
+ fonts: {
1812
+ major: {
1813
+ latin: 'Calibri',
1814
+ eastAsian: 'Calibri',
1815
+ complexScript: 'Calibri'
1816
+ },
1817
+ minor: {
1818
+ latin: 'Calibri',
1819
+ eastAsian: 'Calibri',
1820
+ complexScript: 'Calibri'
1821
+ }
1822
+ }
1823
+ });
1824
+
1825
+ // Theme colors will be applied throughout the workbook
1826
+ const sheet = builder.addWorksheet('Report');
1827
+ // ... add data
1828
+ ```
1829
+
1830
+ ### Split Panes
1831
+
1832
+ Divide the window into panes for comparing distant sections:
1833
+
1834
+ ```typescript
1835
+ const sheet = builder.addWorksheet('Data', {
1836
+ splitPanes: {
1837
+ xSplit: 3, // Split after column C
1838
+ ySplit: 5, // Split after row 5
1839
+ topLeftCell: 'D6', // Top-left cell in bottom-right pane
1840
+ activePane: 'bottomRight' // Active pane: 'topLeft' | 'topRight' | 'bottomLeft' | 'bottomRight'
1841
+ }
1842
+ });
1843
+ ```
1844
+
1845
+ ### Sheet Views
1846
+
1847
+ Configure different views of the same sheet:
1848
+
1849
+ ```typescript
1850
+ const sheet = builder.addWorksheet('Report', {
1851
+ views: {
1852
+ state: 'normal', // 'normal' | 'pageBreakPreview' | 'pageLayout'
1853
+ zoomScale: 100, // Zoom level (10-400)
1854
+ zoomScaleNormal: 100, // Normal zoom level
1855
+ showGridLines: true,
1856
+ showRowColHeaders: true,
1857
+ showRuler: true, // For page layout view
1858
+ rightToLeft: false
1859
+ }
1860
+ });
1861
+ ```
1862
+
1863
+ ### Data Connections
1864
+
1865
+ Add external data connections:
1866
+
1867
+ ```typescript
1868
+ // Note: Data connections require advanced ExcelJS XML manipulation
1869
+ // This feature is documented but requires manual XML editing for full implementation
1870
+
1871
+ worksheet.addDataConnection({
1872
+ name: 'SalesDB',
1873
+ type: 'odbc',
1874
+ connectionString: 'Driver={SQL Server};Server=server;Database=SalesDB;',
1875
+ commandText: 'SELECT * FROM Sales WHERE Year = 2024',
1876
+ refresh: {
1877
+ refreshOnOpen: true,
1878
+ refreshInterval: 60 // minutes
1879
+ },
1880
+ credentials: {
1881
+ username: 'user',
1882
+ integratedSecurity: false
1883
+ // Password should be set by user in Excel after opening
1884
+ }
1885
+ });
1886
+ ```
1887
+
1888
+ ### Multiple Worksheets
1889
+
1890
+ ```typescript
1891
+ const builder = new ExcelBuilder();
1892
+
1893
+ // Sheet 1: Summary
1894
+ const summarySheet = builder.addWorksheet('Summary');
1895
+ summarySheet.addHeader({
1896
+ key: 'title',
1897
+ value: 'Executive Summary',
1898
+ type: CellType.STRING,
1899
+ mergeCell: true
1900
+ });
1901
+
1902
+ // Sheet 2: Details
1903
+ const detailsSheet = builder.addWorksheet('Details');
1904
+ detailsSheet.addSubHeaders([
1905
+ { key: 'date', value: 'Date', type: CellType.DATE },
1906
+ { key: 'amount', value: 'Amount', type: CellType.CURRENCY }
1907
+ ]);
1908
+
1909
+ await builder.generateAndDownload('multi-sheet-report.xlsx');
1910
+ ```
1911
+
1912
+ ### Export in Different Formats
1913
+
1914
+ ```typescript
1915
+ // Direct download (browser)
1916
+ await builder.generateAndDownload('report.xlsx');
1917
+
1918
+ // Get as Buffer
1919
+ const bufferResult = await builder.toBuffer();
1920
+ if (bufferResult.success) {
1921
+ const buffer = bufferResult.data;
1922
+ // Use buffer...
1923
+ }
1924
+
1925
+ // Get as Blob
1926
+ const blobResult = await builder.toBlob();
1927
+ if (blobResult.success) {
1928
+ const blob = blobResult.data;
1929
+ // Use blob...
1930
+ }
1931
+ ```
1932
+
1933
+ ### Event System
1934
+
1935
+ ```typescript
1936
+ builder.on('build:started', (event) => {
1937
+ console.log('Build started');
1938
+ });
1939
+
1940
+ builder.on('build:completed', (event) => {
1941
+ console.log('Build completed', event.data);
1942
+ });
1943
+
1944
+ builder.on('build:error', (event) => {
1945
+ console.error('Build error', event.data.error);
1946
+ });
1947
+
1948
+ // Remove listener
1949
+ const listenerId = builder.on('build:started', handler);
1950
+ builder.off('build:started', listenerId);
1951
+ ```
1952
+
1953
+ ### Read Excel and Convert to JSON
1954
+
1955
+ ```typescript
1956
+ import { ExcelReader } from 'han-excel-builder';
1957
+
1958
+ // Read from a file (browser)
1959
+ const fileInput = document.querySelector('input[type="file"]');
1960
+ fileInput.addEventListener('change', async (e) => {
1961
+ const file = (e.target as HTMLInputElement).files?.[0];
1962
+ if (!file) return;
1963
+
1964
+ const result = await ExcelReader.fromFile(file, {
1965
+ useFirstRowAsHeaders: true,
1966
+ datesAsISO: true,
1967
+ includeFormatting: false
1968
+ });
1969
+
1970
+ if (result.success) {
1971
+ const workbook = result.data;
1972
+
1973
+ // Process each sheet
1974
+ workbook.sheets.forEach(sheet => {
1975
+ console.log(`Processing sheet: ${sheet.name}`);
1976
+
1977
+ // Convert to array of objects (if using headers)
1978
+ const data = sheet.rows.map(row => row.data || {});
1979
+ console.log('Data:', data);
1980
+ });
1981
+ }
1982
+ });
1983
+
1984
+ // Read from ArrayBuffer (from API)
1985
+ async function readExcelFromAPI() {
1986
+ const response = await fetch('/api/excel-file');
1987
+ const buffer = await response.arrayBuffer();
1988
+
1989
+ const result = await ExcelReader.fromBuffer(buffer, {
1990
+ useFirstRowAsHeaders: true,
1991
+ sheetName: 'Sales' // Read only 'Sales' sheet
1992
+ });
1993
+
1994
+ if (result.success) {
1995
+ const sheet = result.data.sheets[0];
1996
+ const sales = sheet.rows.map(row => row.data);
1997
+ return sales;
1998
+ }
1999
+ }
2000
+
2001
+ // Read from path (Node.js)
2002
+ async function readExcelFromPath() {
2003
+ const result = await ExcelReader.fromPath('./report.xlsx', {
2004
+ useFirstRowAsHeaders: true,
2005
+ startRow: 2, // Skip header
2006
+ includeFormulas: true
2007
+ });
2008
+
2009
+ if (result.success) {
2010
+ console.log(`Processing time: ${result.processingTime}ms`);
2011
+ return result.data;
2012
+ }
2013
+ }
2014
+ ```
2015
+
2016
+ ## 🧪 Pruebas
2017
+
2018
+ ```bash
2019
+ # Run tests
2020
+ npm test
2021
+
2022
+ # Run tests with coverage
2023
+ npm run test:coverage
2024
+
2025
+ # Run tests in watch mode
2026
+ npm run test:watch
2027
+ ```
2028
+
2029
+ ## 🛠️ Desarrollo
2030
+
2031
+ ```bash
2032
+ # Install dependencies
2033
+ npm install
2034
+
2035
+ # Start development server
2036
+ npm run dev
2037
+
2038
+ # Build for production
2039
+ npm run build
2040
+
2041
+ # Run linting
2042
+ npm run lint
2043
+
2044
+ # Format code
2045
+ npm run format
2046
+
2047
+ # Type checking
2048
+ npm run type-check
2049
+
2050
+ # Generate documentation
2051
+ npm run docs
2052
+ ```
2053
+
2054
+ ## 📋 Migración desde legacy-excel
2055
+
2056
+ Si estás migrando desde la versión legacy, aquí hay una comparación rápida:
2057
+
2058
+ ```typescript
2059
+ // Legacy way
2060
+ const worksheets: IWorksheets[] = [{
2061
+ name: "Report",
2062
+ tables: [{
2063
+ headers: [...],
2064
+ subHeaders: [...],
2065
+ body: [...],
2066
+ footers: [...]
2067
+ }]
2068
+ }];
2069
+ await fileBuilder(worksheets, "report");
2070
+
2071
+ // New way
2072
+ const builder = new ExcelBuilder();
2073
+ const worksheet = builder.addWorksheet('Report');
2074
+ worksheet.addHeader({...});
2075
+ worksheet.addSubHeaders([...]);
2076
+ worksheet.addRow([...]);
2077
+ worksheet.addFooter([...]);
2078
+ await builder.generateAndDownload('report');
2079
+ ```
2080
+
2081
+ ## 📚 Recursos Adicionales
2082
+
2083
+ - 📖 [Multiple Tables Guide](./MULTIPLE-TABLES-GUIDE.md)
2084
+ - 📖 [Implemented Improvements](./IMPROVEMENTS.md)
2085
+ - 📖 [Test Results](./TEST-RESULTS.md)
2086
+
2087
+ ## 🤝 Contribuir
2088
+
2089
+ 1. Hacer fork del repositorio
2090
+ 2. Crear una rama de característica (`git checkout -b feature/my-feature`)
2091
+ 3. Confirmar tus cambios (`git commit -m 'Add my feature'`)
2092
+ 4. Hacer push a la rama (`git push origin feature/my-feature`)
2093
+ 5. Abrir un Pull Request
2094
+
2095
+ ## 📄 Licencia
2096
+
2097
+ Este proyecto está licenciado bajo la Licencia MIT - consulta el archivo [LICENSE](LICENSE) para más detalles.
2098
+
2099
+ ## 🆘 Soporte
2100
+
2101
+ - 📖 [Documentation](https://github.com/hannndler/-han-excel)
2102
+ - 🐛 [Issues](https://github.com/hannndler/-han-excel/issues)
2103
+ - 💬 [Discussions](https://github.com/hannndler/-han-excel/discussions)
2104
+
2105
+ ---
2106
+
2107
+ Hecho con ❤️ por el equipo de Han Excel