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 +2107 -0
- package/README.md +2104 -909
- package/dist/han-excel.cjs.js +1 -1
- package/dist/han-excel.cjs.js.map +1 -1
- package/dist/han-excel.es.js +1789 -9
- package/dist/han-excel.es.js.map +1 -1
- package/dist/index-0188b143.cjs +11 -0
- package/dist/index-0188b143.cjs.map +1 -0
- package/dist/index-8081eac4.js +1679 -0
- package/dist/index-8081eac4.js.map +1 -0
- package/dist/index.d.ts +1183 -5
- package/package.json +1 -1
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
|