@dimarborda/xlsx-editor 0.2.0 → 0.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +43 -232
- package/package.json +4 -2
- package/src/draw.js +12 -3
- package/src/elements.js +1 -0
- package/src/engine.d.ts +42 -2
- package/src/engine.js +132 -2
- package/src/find.js +37 -0
- package/src/grid.js +89 -0
- package/src/index.d.ts +86 -4
- package/src/style.css +154 -1
- package/src/xlsx_editor.js +1481 -51
- package/wasm/xlsx_engine.d.ts +57 -1
- package/wasm/xlsx_engine.js +180 -1
- package/wasm/xlsx_engine_bg.wasm +0 -0
- package/wasm/xlsx_engine_bg.wasm.d.ts +11 -0
package/README.md
CHANGED
|
@@ -1,265 +1,76 @@
|
|
|
1
1
|
# @dimarborda/xlsx-editor
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
View and edit Excel (`.xlsx`) workbooks in the browser, drawn on canvas the way Excel shows them, and work with them from Node.js. A Rust and WebAssembly engine reads the file, recalculates formulas and saves losslessly: what it does not touch stays byte by byte. It works with React, Vue, Svelte, Angular or plain HTML, and in desktop apps such as Tauri. It needs no server: the library sends your files nowhere.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
For AI agents, `wb.area()` gives a table's columns, types and totals without reading the whole sheet, and a change that would break something is refused with the reason, leaving the workbook as it was.
|
|
6
|
+
|
|
7
|
+
**Full documentation on GitHub:** [English](https://github.com/dimarborda/rust-web-docx/blob/master/docs/xlsx-editor/en/component.md) · [Español](https://github.com/dimarborda/rust-web-docx/blob/master/docs/xlsx-editor/es/component.md)
|
|
8
|
+
|
|
9
|
+
## Install
|
|
6
10
|
|
|
7
11
|
```bash
|
|
8
12
|
npm install @dimarborda/xlsx-editor
|
|
9
13
|
```
|
|
10
14
|
|
|
11
15
|
```html
|
|
12
|
-
<xlsx-editor src="/
|
|
16
|
+
<xlsx-editor src="/report.xlsx" locale="en" style="height: 80vh"></xlsx-editor>
|
|
13
17
|
<script type="module">
|
|
14
18
|
import '@dimarborda/xlsx-editor';
|
|
15
|
-
import '@dimarborda/xlsx-editor/fonts'; //
|
|
19
|
+
import '@dimarborda/xlsx-editor/fonts'; // optional: substitutes for Calibri, Arial…
|
|
16
20
|
import '@dimarborda/xlsx-editor/style.css';
|
|
17
21
|
</script>
|
|
18
22
|
```
|
|
19
23
|
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
- **Celdas como en Excel:** fuentes, negrita, cursiva, subrayado, tachado, colores (incluidos los del tema y sus tonos), rellenos y bordes con todos sus estilos.
|
|
23
|
-
- **Formatos de número:** separadores según el idioma, moneda, porcentajes, notación científica, fracciones, secciones y condiciones, colores como `[Red]`, y fechas y horas con nombres de meses y días en español o inglés.
|
|
24
|
-
- **Alineación:** la de Excel por defecto (números a la derecha, texto a la izquierda), horizontal y vertical, sangría, ajuste de texto, texto que se desborda sobre celdas vacías y `####` en los números que no caben.
|
|
25
|
-
- **Cuadrícula:** anchos de columna y altos de fila del archivo, filas y columnas ocultas, celdas combinadas, paneles inmovilizados y líneas de cuadrícula (o su ausencia).
|
|
26
|
-
- **Navegación:** desplazamiento fluido en hojas grandes (solo se dibuja lo visible), zoom, pestañas de hojas, selección con ratón y teclado, y una barra con la celda activa y su contenido o fórmula.
|
|
27
|
-
|
|
28
|
-
Aún no se dibujan gráficos, imágenes, formato condicional ni comentarios, y el texto girado se dibuja horizontal. Las fórmulas se recalculan tras cada cambio con el motor del paquete; las que usan algo que no admite muestran el último valor que guardó Excel.
|
|
29
|
-
|
|
30
|
-
## Edición
|
|
31
|
-
|
|
32
|
-
| Acción | Cómo |
|
|
33
|
-
| :--- | :--- |
|
|
34
|
-
| Escribir en una celda | Empieza a escribir (sustituye el contenido), pulsa **F2** o haz doble clic (edita el contenido actual), o escribe en la barra de celda. |
|
|
35
|
-
| Confirmar | **Enter** (baja), **Mayús+Enter** (sube), **Tab** / **Mayús+Tab** (derecha / izquierda). Si empezaste a escribir sin F2, las flechas también confirman y mueven; **F2** alterna entre ese modo y mover el cursor dentro del texto. |
|
|
36
|
-
| Rellenar la selección | **Ctrl/Cmd+Enter** escribe lo mismo en todas las celdas seleccionadas. |
|
|
37
|
-
| Salto de línea en la celda | **Alt+Enter**. |
|
|
38
|
-
| Cancelar | **Escape**. |
|
|
39
|
-
| Borrar | **Supr** o **Retroceso** vacían el rango seleccionado; el formato de las celdas se queda. |
|
|
40
|
-
| Copiar, cortar y pegar | **Ctrl/Cmd+C**, **X** y **V**, como texto separado por tabulaciones (TSV), el mismo que intercambian Excel y Google Sheets. Al copiar se copian los textos que muestran las celdas; si se pega en el mismo editor lo que se acaba de copiar, se pegan los valores exactos. Un bloque que cabe un número entero de veces en la selección se repite. Cortar vacía las celdas al momento (no al pegar, como hace Excel). |
|
|
41
|
-
| Deshacer y rehacer | **Ctrl/Cmd+Z**, y **Ctrl/Cmd+Y** o **Cmd+Mayús+Z**. Cada edición, borrado o pegado es un paso; deshacer vuelve a la hoja y a la selección de entonces. |
|
|
42
|
-
|
|
43
|
-
### Qué se guarda al escribir
|
|
44
|
-
|
|
45
|
-
Lo escrito se interpreta como lo hace Excel en el idioma del editor (`locale`; `es` y sus variantes usan las convenciones españolas y el resto las inglesas):
|
|
46
|
-
|
|
47
|
-
| Escribes (`es`) | Escribes (`en`) | Se guarda |
|
|
48
|
-
| :--- | :--- | :--- |
|
|
49
|
-
| `1234,5` · `1.234,5` · `-0,5` · `(300)` · `1,5E+3` | `1234.5` · `1,234.5` · `-0.5` · `(300)` · `1.5E+3` | Número |
|
|
50
|
-
| `1.234,50 €` | `$1,234.50` | Número (el símbolo se descarta) |
|
|
51
|
-
| `15%` · `12,5 %` | `15%` · `12.5 %` | Número dividido por 100 (0,15) |
|
|
52
|
-
| `15/03/2024` · `15-3-24` · `15/3` · `2024-03-15` | `3/15/2024` · `3-15-24` · `3/15` · `2024-03-15` | Fecha (sin año, el actual; años de dos cifras: 00–29 → 2000, 30–99 → 1900) |
|
|
53
|
-
| `15/03/2024 14:30` | `3/15/2024 2:30 PM` | Fecha y hora |
|
|
54
|
-
| `14:30` · `14:30:15` | `2:30 PM` | Hora, como fracción del día |
|
|
55
|
-
| `VERDADERO` · `FALSO` | `TRUE` · `FALSE` | Booleano (sin distinguir mayúsculas) |
|
|
56
|
-
| `'0012` | `'0012` | Texto `0012`: el apóstrofo inicial fuerza texto |
|
|
57
|
-
| `=SUM(A1:A3)` | `=SUM(A1:A3)` | Fórmula (ver Limitaciones) |
|
|
58
|
-
| Cualquier otra cosa | | Texto |
|
|
59
|
-
| (vacío) | | Celda vacía |
|
|
60
|
-
|
|
61
|
-
Los valores se escriben con `sheet.setValues` del motor, que conserva el formato de la celda: un número escrito en una celda con formato de moneda se ve como moneda. A las fechas se les da un formato de fecha basado en el de la celda si no lo tenía. Los porcentajes y las horas se guardan como su valor (0,15 o 0,604…) y se ven con el formato que ya tenga la celda. La misma función está disponible como `parseInput(texto, locale)`.
|
|
62
|
-
|
|
63
|
-
### Limitaciones
|
|
64
|
-
|
|
65
|
-
- **Fórmulas:** lo que empieza por `=` se guarda como fórmula y se calcula, igual que las fórmulas que dependen de ella. Se escriben en la sintaxis en inglés (`=SUM(A1:A3)`, `,` o `;` entre argumentos) en cualquier idioma del editor. Las funciones admitidas están en el [motor](#fórmulas); con otras, la celda muestra el último valor guardado y Excel la calcula al abrir el archivo (`fullCalcOnLoad`).
|
|
66
|
-
- **Formato:** escribir no cambia el formato de número (salvo en fechas), y las columnas no se ensanchan solas: una fecha en una columna estrecha se ve como `####`, como en Excel.
|
|
67
|
-
- No se puede sobrescribir la celda de origen de una fórmula compartida (la que usan otras celdas); el editor emite `error` y deja el libro como estaba.
|
|
68
|
-
|
|
69
|
-
### Áreas y contexto para agentes
|
|
70
|
-
|
|
71
|
-
- `editor.selectArea(destino)`: selecciona un área y muestra su hoja. Acepta una tabla o un rango con nombre, una dirección (`Ventas!A1:H40`) o un rango de la hoja activa. Devuelve el área (ver [Áreas para agentes](#áreas-para-agentes)).
|
|
72
|
-
- `editor.selectionContext({ maxRows })`: contexto de lo seleccionado para un agente: el rango, o con una sola celda la región que la rodea (su tabla, o el bloque que seleccionaría Ctrl+A). Devuelve título, cabecera, tipos y totales por columna, las primeras filas y una tabla Markdown.
|
|
73
|
-
|
|
74
|
-
Para dar formato desde código se usa `editor.workbook.sheet(…).setFormat(…)` y después `editor.reload()`.
|
|
75
|
-
|
|
76
|
-
## Atributos
|
|
77
|
-
|
|
78
|
-
| Atributo | Para qué |
|
|
79
|
-
| :--- | :--- |
|
|
80
|
-
| `src` | URL del `.xlsx` |
|
|
81
|
-
| `blank` | Empieza con un libro vacío |
|
|
82
|
-
| `locale` | Idioma de los formatos (`es`, `en-US`…); por defecto, el de la página |
|
|
83
|
-
| `sheet` | Hoja inicial, por nombre o posición |
|
|
84
|
-
| `zoom` | 0.25–4 |
|
|
85
|
-
| `tabs="false"` | Oculta las pestañas |
|
|
86
|
-
| `formula-bar="false"` | Oculta la barra de celda |
|
|
87
|
-
| `readonly` | Modo visor: se selecciona y se copia, pero no se edita (se puede quitar y poner en cualquier momento) |
|
|
88
|
-
| `quiet` | No escribe los errores en la consola |
|
|
89
|
-
| `wasm-url` | Dónde se sirve `xlsx_engine_bg.wasm`, si el bundler no lo hace |
|
|
90
|
-
|
|
91
|
-
Eventos en el elemento: `ready` (con `detail.editor`), `load`, `sheetchange`, `selectionchange`, `change` y `error`.
|
|
92
|
-
|
|
93
|
-
`change` llega tras cada edición, borrado, pegado, corte, deshacer o rehacer:
|
|
94
|
-
|
|
95
|
-
```js
|
|
96
|
-
element.addEventListener('change', ({ detail }) => {
|
|
97
|
-
// detail: { sheet: 'Ventas', index: 0, range: 'B3:C4', source: 'edit' | 'clear' | 'paste' | 'cut' | 'undo' | 'redo',
|
|
98
|
-
// cells: [{ ref: 'B3', value: 1200 }, …] }
|
|
99
|
-
});
|
|
100
|
-
```
|
|
101
|
-
|
|
102
|
-
## API
|
|
24
|
+
With WebAssembly, the production `script-src` needs `'wasm-unsafe-eval'`.
|
|
103
25
|
|
|
104
|
-
|
|
105
|
-
const element = document.querySelector('xlsx-editor');
|
|
106
|
-
element.addEventListener('ready', async ({ detail: { editor } }) => {
|
|
107
|
-
await editor.open(file); // URL, File/Blob, ArrayBuffer o Uint8Array
|
|
108
|
-
editor.setSheet('Resumen');
|
|
109
|
-
editor.select('B3:D7');
|
|
110
|
-
editor.setZoom(1.25);
|
|
26
|
+
## Features
|
|
111
27
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
28
|
+
- **Faithful display:** fonts, colors, borders, number formats, merged cells, frozen panes and hidden lines. Large sheets scroll smoothly.
|
|
29
|
+
- **Editing:** values and formulas, copy and paste with Excel and Google Sheets, undo, and a status bar with average, count and sum.
|
|
30
|
+
- **Rows and columns:** move, insert, delete, resize, autofit, hide and show, updating formulas, names, charts and tables across the workbook.
|
|
31
|
+
- **Productivity:** fill handle with series, autosum, find and replace, keyboard navigation and a context menu.
|
|
32
|
+
- **Formulas:** about 90 functions recalculated instantly; for anything else, Excel's saved value stays and Excel recalculates when opening the file.
|
|
33
|
+
- **Headless engine:** the same API in Node.js.
|
|
117
34
|
|
|
118
|
-
|
|
119
|
-
editor.readOnly = true; // lo mismo que el atributo readonly
|
|
35
|
+
Charts, images, conditional formats and comments are kept in the file but not drawn yet.
|
|
120
36
|
|
|
121
|
-
|
|
122
|
-
});
|
|
123
|
-
```
|
|
124
|
-
|
|
125
|
-
Los cambios hechos desde código con `editor.workbook` no forman un paso de deshacer propio: llama antes a `editor.workbook.checkpoint()` para que lo sean.
|
|
126
|
-
|
|
127
|
-
Sin el elemento: `const editor = await XlsxEditor.create(contenedor, { locale: 'es', readOnly: false })`.
|
|
128
|
-
|
|
129
|
-
## Motor sin interfaz (Node.js)
|
|
130
|
-
|
|
131
|
-
`@dimarborda/xlsx-editor/engine` es el mismo motor sin cuadrícula ni DOM: lee, escribe, recalcula y da formato a libros en Node.js (scripts, servidores, herramientas de agentes) o en el navegador.
|
|
132
|
-
|
|
133
|
-
```bash
|
|
134
|
-
npm install @dimarborda/xlsx-editor
|
|
135
|
-
```
|
|
37
|
+
## Use it from code
|
|
136
38
|
|
|
137
39
|
```js
|
|
138
40
|
import { readFile, writeFile } from 'node:fs/promises';
|
|
139
41
|
import { openWorkbook } from '@dimarborda/xlsx-editor/engine';
|
|
140
42
|
|
|
141
|
-
const wb = await openWorkbook(await readFile('
|
|
142
|
-
|
|
143
|
-
// Variables {{CAMPO}} en cualquier texto del libro
|
|
144
|
-
wb.fillTemplate({ CLIENTE: 'Acme S.A.', FECHA: '15/01/2024' });
|
|
43
|
+
const wb = await openWorkbook(await readFile('template.xlsx'));
|
|
44
|
+
const sheet = wb.sheet('Sales');
|
|
145
45
|
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
]);
|
|
46
|
+
wb.fillTemplate({ CLIENT: 'Acme Inc.' });
|
|
47
|
+
sheet.setValues('A2', [['Keyboard', 3, 29.9], ['Mouse', 5, 12.5]]);
|
|
48
|
+
sheet.setValue('D2', { formula: '=B2*C2' });
|
|
49
|
+
sheet.insertRows(2);
|
|
50
|
+
sheet.fill('D3', 'D3:D4');
|
|
152
51
|
|
|
153
|
-
|
|
154
|
-
await writeFile('salida.xlsx', wb.toBytes());
|
|
52
|
+
await writeFile('output.xlsx', wb.toBytes());
|
|
155
53
|
```
|
|
156
54
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
### API
|
|
160
|
-
|
|
161
|
-
| | |
|
|
162
|
-
|---|---|
|
|
163
|
-
| `openWorkbook(bytes)` / `createWorkbook()` | Abre un `.xlsx` o crea un libro con una hoja vacía. |
|
|
164
|
-
| `wb.sheetNames` | Nombres de las hojas en el orden de las pestañas. |
|
|
165
|
-
| `wb.sheet(nombreOÍndice)` / `wb.sheets` | Una hoja de cálculo (por nombre, sin distinguir mayúsculas, o por posición desde 0) o todas. Las hojas de gráfico no tienen celdas. |
|
|
166
|
-
| `hoja.getCell('B3')` | `{ ref, type, value, formula? }`. `type` es `empty`, `number`, `string`, `boolean`, `date` o `error` (`value` es entonces el código, p. ej. `#DIV/0!`). En las fórmulas, `value` es su resultado y `formula` su texto sin `=` (también en las celdas de una fórmula compartida). |
|
|
167
|
-
| `hoja.getValue('B3')` / `hoja.getValues('A1:C3')` | Solo los valores; las celdas vacías dan `null`. |
|
|
168
|
-
| `hoja.setValue('B3', valor)` | Escribe texto, número, booleano, `Date` o una fórmula (`{ formula: '=SUM(A1:A3)' }`); `null` borra el valor y deja el formato. Las fórmulas que dependen de la celda se recalculan. |
|
|
169
|
-
| `hoja.setValues('A2', filas)` | Escribe una matriz desde la celda indicada, creando filas y celdas en orden. `undefined` deja la celda como está. |
|
|
170
|
-
| `wb.fillTemplate(valores)` | Sustituye `{{CAMPO}}` (se admiten espacios y una pista: `{{ CAMPO }}`, `{{CAMPO: pista}}`) en las cadenas compartidas y en las cadenas en línea; devuelve cuántas sustituyó. Las variables sin valor se quedan como están. |
|
|
171
|
-
| `wb.addSheet(nombre, después?)` | Agrega una hoja vacía detrás de la pestaña `después` (nombre o posición; al final sin ella) y la devuelve. El nombre sigue las reglas de Excel: de 1 a 31 caracteres, sin `[ ] : * ? / \`, sin apóstrofo al principio ni al final, distinto de `History` y de las demás hojas (sin distinguir mayúsculas). |
|
|
172
|
-
| `wb.renameSheet(hoja, nombre)` | Renombra una pestaña (nombre o posición) y las referencias a ella en fórmulas, nombres definidos, validaciones de datos, formato condicional y gráficos; devuelve cuántas cambió. Las referencias dentro de textos (`INDIRECT("Hoja1!A1")`) y a otros libros se quedan como están, igual que en Excel. |
|
|
173
|
-
| `wb.markers({ kind })` | Marcadores del léxico estándar (el mismo de `@dimarborda/docx-editor`) en las celdas de texto de todas las hojas: `field` para `{{CLAVE}}` / `{clave}`, `pending` para `[…]` (contenido por completar). Cada uno con `sheet`, `sheetName`, `ref` (`B3`), `row`, `col`, `raw`, `key`, `hint`, `offset` y `length`. |
|
|
174
|
-
| `wb.checkpoint(selección?)` | Abre un paso de deshacer: los cambios hasta el siguiente `checkpoint` se deshacen juntos. `selección` es un texto libre que `undo()` devuelve. `wb.setSelectionAfter(selección)` guarda la de después, que devuelve `redo()`. |
|
|
175
|
-
| `wb.undo()` / `wb.redo()` | Deshace o rehace un paso: `{ selection, parts }`, o `null` si no hay nada. `wb.canUndo` / `wb.canRedo` dicen si se puede. |
|
|
176
|
-
| `wb.recalculate()` | Recalcula todas las fórmulas y devuelve cuántas celdas cambiaron de valor. |
|
|
177
|
-
| `wb.areas({ sheet })` / `hoja.areas()` | Las áreas del libro (ver [Áreas](#áreas-para-agentes)). |
|
|
178
|
-
| `wb.area(destino, { sheet, maxRows, locale })` / `hoja.area(rango)` | Contexto de un área para un agente. |
|
|
179
|
-
| `hoja.regionAt('B5')` | La región alrededor de una celda, como la selecciona Ctrl+A en Excel (o su tabla). |
|
|
180
|
-
| `hoja.setFormat('A1:C3', formato)` / `hoja.clearFormat(rango)` | Da formato (ver [Formato](#formato)) o lo quita. |
|
|
181
|
-
| `hoja.setColumnWidth('B:D', 14)` | Ancho de columnas en caracteres. |
|
|
182
|
-
| `hoja.mergeCells('A1:H1')` / `hoja.unmergeCells(rango)` | Combina o separa celdas; al combinar, solo la primera puede tener valor. |
|
|
183
|
-
| `wb.toBytes()` | El libro como `.xlsx`. |
|
|
184
|
-
| `wb.close()` | Libera la memoria del motor. |
|
|
185
|
-
|
|
186
|
-
#### Fórmulas
|
|
187
|
-
|
|
188
|
-
Las fórmulas se escriben con `{ formula: '=SUM(B2:B10)*IVA' }` y se calculan en el momento. Cada vez que se escribe una celda (o se rellena una plantilla) se recalculan, en orden de dependencias, todas las fórmulas del libro, y solo se reescribe el `<v>` de las celdas cuyo resultado cambió. `wb.recalculate()` hace lo mismo sin editar nada, por ejemplo para refrescar un libro recién abierto.
|
|
189
|
-
|
|
190
|
-
Se usa la sintaxis en inglés que guarda Excel (`SUM`, `IF`, `,` entre argumentos; también se acepta `;`). Se admiten referencias relativas y absolutas, rangos, columnas completas (`A:A`), otras hojas (`'Mis datos'!B2`), nombres definidos, fórmulas compartidas y matrices constantes (`{1,2;3,4}`), con las conversiones de Excel entre texto, número y booleano y sus códigos de error. Funciones:
|
|
191
|
-
|
|
192
|
-
- **Matemáticas:** `SUM`, `PRODUCT`, `AVERAGE`, `MIN`, `MAX`, `MEDIAN`, `COUNT`, `COUNTA`, `COUNTBLANK`, `ROUND`, `ROUNDUP`, `ROUNDDOWN`, `INT`, `TRUNC`, `ABS`, `SIGN`, `SQRT`, `POWER`, `MOD`, `PI`, `CEILING`, `FLOOR`, `CEILING.MATH`, `FLOOR.MATH`, `SUMPRODUCT`.
|
|
193
|
-
- **Condicionales:** `SUMIF`, `SUMIFS`, `COUNTIF`, `COUNTIFS`, `AVERAGEIF`, `AVERAGEIFS`, `MAXIFS`, `MINIFS` (criterios como `">5"`, `"<>x"`, `"n*"`).
|
|
194
|
-
- **Lógicas e información:** `IF`, `IFS`, `SWITCH`, `IFERROR`, `IFNA`, `AND`, `OR`, `XOR`, `NOT`, `TRUE`, `FALSE`, `ISBLANK`, `ISNUMBER`, `ISTEXT`, `ISNONTEXT`, `ISLOGICAL`, `ISERROR`, `ISERR`, `ISNA`, `NA`.
|
|
195
|
-
- **Búsqueda:** `VLOOKUP`, `HLOOKUP`, `XLOOKUP`, `MATCH`, `INDEX`, `CHOOSE`, `ROW`, `COLUMN`, `ROWS`, `COLUMNS`.
|
|
196
|
-
- **Texto:** `CONCATENATE`, `CONCAT`, `TEXTJOIN`, `LEFT`, `RIGHT`, `MID`, `LEN`, `UPPER`, `LOWER`, `PROPER`, `TRIM`, `SUBSTITUTE`, `REPT`, `EXACT`, `FIND`, `SEARCH`, `VALUE`, `TEXT`.
|
|
197
|
-
- **Fechas:** `TODAY`, `NOW`, `DATE`, `YEAR`, `MONTH`, `DAY`, `WEEKDAY`, `EDATE`, `EOMONTH`, `DAYS`.
|
|
198
|
-
|
|
199
|
-
Una fórmula que use otra cosa (una función que no está en la lista, `OFFSET`, `INDIRECT`, referencias a otros libros o a tablas, fórmulas matriciales, resultados que se desbordan a varias celdas) o que forme una referencia circular conserva el valor que guardó Excel, y las que dependen de ella lo usan. Al abrirse el archivo, Excel la calcula (ver abajo).
|
|
200
|
-
|
|
201
|
-
#### Áreas para agentes
|
|
202
|
-
|
|
203
|
-
Un agente no necesita leer toda la hoja: puede pedir las áreas y luego el contexto de la que le interesa. `wb.areas()` usa lo que el propio archivo marca, en este orden:
|
|
204
|
-
|
|
205
|
-
1. **Tablas** de Excel (Dar formato como tabla, `xl/tables/tableN.xml`): nombre, rango, nombres de columna y fila de totales.
|
|
206
|
-
2. **Nombres definidos** que apuntan a un rango (`Ventas = Datos!$A$3:$H$40`) y el **área de impresión**.
|
|
207
|
-
3. **Filtros** (`autoFilter`) de la hoja.
|
|
208
|
-
4. **Regiones**: bloques de celdas llenas contiguas, como los selecciona Ctrl+A, donde el archivo no marca nada. Las filas iniciales con una sola celda (o una combinada) son el **título**, igual que un texto suelto justo encima; la primera fila de textos seguida de datos (o en negrita) es la **cabecera**.
|
|
209
|
-
|
|
210
|
-
Cada área es `{ kind, name, sheet, sheetName, ref, address, title, headerRow, totalsRow, body, columns }`; `address` (`Ventas!A1:H64`) sirve como destino en cualquier llamada.
|
|
211
|
-
|
|
212
|
-
`wb.area(destino)` acepta el nombre de una tabla o de un rango con nombre, una dirección con hoja o, con `{ sheet }`, un rango de esa hoja. Devuelve la descripción del área y además:
|
|
213
|
-
|
|
214
|
-
- `columnsInfo`: por columna, su nombre, tipo (`number`, `text`, `date`, `mixed`…), cuántas celdas tienen valor, suma, mínimo, máximo y media de los números, y cuántas fórmulas hay con un ejemplo (`D4: =B4*C4`).
|
|
215
|
-
- `rows`: las primeras `maxRows` filas de datos (30 por defecto) con el texto que muestran las celdas, `firstRow`, `rowCount` y `truncated`.
|
|
216
|
-
- `totals`, `merges` y `markdown`: el área como tabla Markdown con números de fila y letras de columna, lista para pasarla a un modelo.
|
|
217
|
-
|
|
218
|
-
#### Formato
|
|
55
|
+
In the browser, `element.editor` is the editor and `editor.workbook` this same API:
|
|
219
56
|
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
| `fill` | Fondo `#RRGGBB`; `null` lo quita |
|
|
228
|
-
| `numberFormat` | Código de Excel: `#,##0.00`, `0%`, `dd/mm/yyyy`, `#,##0.00 "€"`… |
|
|
229
|
-
| `align`, `valign`, `wrap` | `left`, `center`, `right`…; `top`, `center`, `bottom`; ajuste de texto |
|
|
230
|
-
| `border` | Un estilo (`'thin'`, `'medium'`, `'thick'`, `'dashed'`, `'dotted'`, `'double'`) en todos los lados, `null` para quitarlos, o `{ style, color, edges }` con `edges`: `all`, `outline`, `inside`, `top`, `bottom`, `left` o `right` |
|
|
231
|
-
|
|
232
|
-
Los formatos nuevos se añaden a `xl/styles.xml` y se reutilizan los iguales que ya existan; nada de lo que ya estaba se reescribe.
|
|
233
|
-
|
|
234
|
-
#### Fechas
|
|
235
|
-
|
|
236
|
-
Excel guarda las fechas como números de serie (días desde 1900, o desde 1904 en libros con ese sistema) que se ven como fecha por el formato de la celda. Al leer, una celda numérica con formato de fecha se devuelve como `Date`; al escribir un `Date`, la celda recibe un formato de fecha basado en el que tenía (misma fuente, relleno y bordes), salvo que ya mostrara fechas.
|
|
237
|
-
|
|
238
|
-
Las fechas de Excel no tienen zona horaria: la hora que muestra Excel se representa como hora **UTC** del `Date`. Escribe con `new Date(Date.UTC(2024, 0, 15))` y lee con `getUTCFullYear()`, `getUTCHours()` o `toISOString()`.
|
|
239
|
-
|
|
240
|
-
### Sin pérdidas
|
|
241
|
-
|
|
242
|
-
Solo se reescriben las partes del archivo que cambian (la hoja editada, las cadenas compartidas y, si hace falta, los estilos), y dentro de ellas solo las celdas escritas: gráficos, imágenes, formatos condicionales, validaciones, nombres definidos y todo lo demás se guarda byte a byte tal como estaba.
|
|
243
|
-
|
|
244
|
-
Cuando un libro cambia, se elimina `xl/calcChain.xml` (Excel lo reconstruye) y se marca `fullCalcOnLoad="1"` para que Excel recalcule las fórmulas al abrirlo: así quedan bien también las que este motor no calcula.
|
|
245
|
-
|
|
246
|
-
Limitaciones actuales:
|
|
247
|
-
|
|
248
|
-
- Las fórmulas fuera de lo admitido (ver [Fórmulas](#fórmulas)) no se recalculan: `getValue` devuelve su último resultado guardado.
|
|
249
|
-
- Las fórmulas se escriben en la sintaxis en inglés (`SUM`, no `SUMA`); los nombres de función en español no se traducen.
|
|
250
|
-
- Escribir en la celda de origen de una fórmula compartida (la que usan otras celdas) da error, para no romper esas celdas.
|
|
251
|
-
- Una variable `{{CAMPO}}` partida entre fragmentos de texto con formatos distintos no se reconoce.
|
|
252
|
-
|
|
253
|
-
## Content Security Policy
|
|
57
|
+
```js
|
|
58
|
+
document.querySelector('xlsx-editor').addEventListener('ready', async ({ detail: { editor } }) => {
|
|
59
|
+
await editor.open(file);
|
|
60
|
+
editor.moveRows(4, 2, 9);
|
|
61
|
+
editor.download('report.xlsx');
|
|
62
|
+
});
|
|
63
|
+
```
|
|
254
64
|
|
|
255
|
-
|
|
65
|
+
## Documentation
|
|
256
66
|
|
|
257
|
-
|
|
67
|
+
| Guide | English | Español |
|
|
68
|
+
| :--- | :--- | :--- |
|
|
69
|
+
| Component: keyboard, attributes, events, editor API | [component](https://github.com/dimarborda/rust-web-docx/blob/master/docs/xlsx-editor/en/component.md) | [component](https://github.com/dimarborda/rust-web-docx/blob/master/docs/xlsx-editor/es/component.md) |
|
|
70
|
+
| Move, insert, delete, fill, find and replace | [operations](https://github.com/dimarborda/rust-web-docx/blob/master/docs/xlsx-editor/en/operations.md) | [operations](https://github.com/dimarborda/rust-web-docx/blob/master/docs/xlsx-editor/es/operations.md) |
|
|
71
|
+
| Engine: full API, formulas, formats, dates | [engine](https://github.com/dimarborda/rust-web-docx/blob/master/docs/xlsx-editor/en/engine.md) | [engine](https://github.com/dimarborda/rust-web-docx/blob/master/docs/xlsx-editor/es/engine.md) |
|
|
72
|
+
| Areas and context for AI agents | [agents](https://github.com/dimarborda/rust-web-docx/blob/master/docs/xlsx-editor/en/agents.md) | [agents](https://github.com/dimarborda/rust-web-docx/blob/master/docs/xlsx-editor/es/agents.md) |
|
|
258
73
|
|
|
259
|
-
|
|
74
|
+
## License
|
|
260
75
|
|
|
261
|
-
|
|
262
|
-
npm run build:wasm:xlsx
|
|
263
|
-
npm test -w packages/xlsx-editor
|
|
264
|
-
npm run dev # http://localhost:5173/xlsx.html
|
|
265
|
-
```
|
|
76
|
+
MIT
|
package/package.json
CHANGED
|
@@ -1,16 +1,18 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@dimarborda/xlsx-editor",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "Excel (.xlsx) editor for the browser and engine for Node.js: Rust + WebAssembly that edits workbooks without losing what it does not touch,
|
|
3
|
+
"version": "0.3.1",
|
|
4
|
+
"description": "Excel (.xlsx) editor for the browser and engine for Node.js: Rust + WebAssembly that edits workbooks without losing what it does not touch, from a canvas grid or from code. An AI agent can read a table's structure without loading the whole sheet",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"license": "MIT",
|
|
7
7
|
"author": "Dimar Borda",
|
|
8
|
+
"homepage": "https://github.com/dimarborda/rust-web-docx/blob/master/docs/xlsx-editor/README.md",
|
|
8
9
|
"repository": {
|
|
9
10
|
"type": "git",
|
|
10
11
|
"url": "git+https://github.com/dimarborda/rust-web-docx.git",
|
|
11
12
|
"directory": "packages/xlsx-editor"
|
|
12
13
|
},
|
|
13
14
|
"keywords": [
|
|
15
|
+
"ai-agents",
|
|
14
16
|
"xlsx",
|
|
15
17
|
"excel",
|
|
16
18
|
"spreadsheet",
|
package/src/draw.js
CHANGED
|
@@ -26,7 +26,7 @@ export function canvasFont(font, zoom = 1) {
|
|
|
26
26
|
}
|
|
27
27
|
|
|
28
28
|
const GRIDLINE = '#d9d9d9';
|
|
29
|
-
const PAD = 3; // horizontal text padding, px at zoom 1
|
|
29
|
+
export const PAD = 3; // horizontal text padding, px at zoom 1
|
|
30
30
|
|
|
31
31
|
/** Line width and dash of an Excel border style, at zoom 1 */
|
|
32
32
|
function borderLook(style) {
|
|
@@ -341,8 +341,13 @@ export function drawHeaders(ctx, { rows, cols, colSegs, rowSegs, rowHeaderW, col
|
|
|
341
341
|
ctx.clip();
|
|
342
342
|
for (let c = seg.from; c <= seg.to; c++) {
|
|
343
343
|
const size = cols.size(c) * zoom;
|
|
344
|
-
if (size === 0) continue;
|
|
345
344
|
const x = seg.start + (cols.offset(c) - cols.offset(seg.from)) * zoom;
|
|
345
|
+
if (size === 0) {
|
|
346
|
+
// Where hidden columns are: a thicker line, as Excel draws it
|
|
347
|
+
ctx.fillStyle = HEADER_TEXT;
|
|
348
|
+
ctx.fillRect(Math.round(x) - 2, 0, 2, colHeaderH);
|
|
349
|
+
continue;
|
|
350
|
+
}
|
|
346
351
|
const active = selection && inSel(c, selection.first.col, selection.last.col);
|
|
347
352
|
if (active) {
|
|
348
353
|
ctx.fillStyle = ACTIVE_BG;
|
|
@@ -364,8 +369,12 @@ export function drawHeaders(ctx, { rows, cols, colSegs, rowSegs, rowHeaderW, col
|
|
|
364
369
|
ctx.clip();
|
|
365
370
|
for (let r = seg.from; r <= seg.to; r++) {
|
|
366
371
|
const size = rows.size(r) * zoom;
|
|
367
|
-
if (size === 0) continue;
|
|
368
372
|
const y = seg.start + (rows.offset(r) - rows.offset(seg.from)) * zoom;
|
|
373
|
+
if (size === 0) {
|
|
374
|
+
ctx.fillStyle = HEADER_TEXT;
|
|
375
|
+
ctx.fillRect(0, Math.round(y) - 2, rowHeaderW, 2);
|
|
376
|
+
continue;
|
|
377
|
+
}
|
|
369
378
|
const active = selection && inSel(r, selection.first.row, selection.last.row);
|
|
370
379
|
if (active) {
|
|
371
380
|
ctx.fillStyle = ACTIVE_BG;
|
package/src/elements.js
CHANGED
|
@@ -49,6 +49,7 @@ export class XlsxEditorElement extends ElementBase {
|
|
|
49
49
|
zoom: Number(this.getAttribute('zoom')) || 1,
|
|
50
50
|
tabs: this.getAttribute('tabs') !== 'false',
|
|
51
51
|
formulaBar: this.getAttribute('formula-bar') !== 'false',
|
|
52
|
+
statusBar: this.getAttribute('status-bar') !== 'false',
|
|
52
53
|
readOnly: this.hasAttribute('readonly'),
|
|
53
54
|
wasmUrl: this.getAttribute('wasm-url') || undefined,
|
|
54
55
|
});
|
package/src/engine.d.ts
CHANGED
|
@@ -150,6 +150,46 @@ export class Sheet {
|
|
|
150
150
|
* language `locale` (`es`, `en-US`…; default `en`)
|
|
151
151
|
*/
|
|
152
152
|
cellViews(locale?: string): CellViewRow[];
|
|
153
|
+
/**
|
|
154
|
+
* Moves `count` whole rows from `first` before the row `before` (numbered as they are now), as
|
|
155
|
+
* Excel's "cut and insert": formats, heights, formulas of the whole workbook, names, charts,
|
|
156
|
+
* tables, validations, conditional formats, images and comments follow. Throws, and changes
|
|
157
|
+
* nothing, when it would tear merged cells, an array formula, a table or a filter. Returns
|
|
158
|
+
* where the first row ends up.
|
|
159
|
+
*/
|
|
160
|
+
moveRows(first: number, count: number, before: number): number;
|
|
161
|
+
/** Moves whole columns (see `moveRows`); columns as letters (`'B'`) or numbers */
|
|
162
|
+
moveColumns(first: string | number, count: number, before: string | number): number;
|
|
163
|
+
/**
|
|
164
|
+
* Inserts `count` empty rows before the row `before`, with the height and format of the one
|
|
165
|
+
* above: formulas, names, charts and the rest are updated, and ranges, merged cells and tables
|
|
166
|
+
* that the new rows fall inside grow. Returns `before`.
|
|
167
|
+
*/
|
|
168
|
+
insertRows(before: number, count?: number): number;
|
|
169
|
+
/** Inserts empty columns before `before`, like the one to their left (not inside a table) */
|
|
170
|
+
insertColumns(before: string | number, count?: number): number;
|
|
171
|
+
/**
|
|
172
|
+
* Deletes `count` rows from `first`: what pointed only at them becomes `#REF!`, and ranges,
|
|
173
|
+
* merged cells and tables shrink. Returns `first`.
|
|
174
|
+
*/
|
|
175
|
+
deleteRows(first: number, count?: number): number;
|
|
176
|
+
/** Deletes columns from `first` (not inside a table) */
|
|
177
|
+
deleteColumns(first: string | number, count?: number): number;
|
|
178
|
+
/** Hides or shows rows (`3`, `'3:5'`); they keep their cells and height */
|
|
179
|
+
setRowsHidden(rows: string | number, hidden: boolean): void;
|
|
180
|
+
/** Hides or shows columns (`'B'`, `'B:D'`, `2`); they keep their cells and width */
|
|
181
|
+
setColumnsHidden(columns: string | number, hidden: boolean): void;
|
|
182
|
+
/**
|
|
183
|
+
* Fills `target` from `source`, the cells at one of its ends, as Excel's fill handle: formats
|
|
184
|
+
* repeat, formulas move their references, and with `series` (by default) numbers continue their
|
|
185
|
+
* trend, one date goes on day by day, texts ending in a number count on and names of months and
|
|
186
|
+
* days follow their order
|
|
187
|
+
*/
|
|
188
|
+
fill(source: string, target: string, options?: { series?: boolean }): void;
|
|
189
|
+
/** The cells of a range (`A1:C3`), by row: their type, value and formula, if any */
|
|
190
|
+
getCells(range: string): Cell[][];
|
|
191
|
+
/** Height of a row or rows (`3`, `'3:5'`) in points; `null` goes back to the automatic height */
|
|
192
|
+
setRowHeight(rows: string | number, height: number | null): void;
|
|
153
193
|
/** Areas of this sheet */
|
|
154
194
|
areas(): Area[];
|
|
155
195
|
/** The region around a cell as Excel's Ctrl+A selects it (or its table, or the cell alone) */
|
|
@@ -188,10 +228,10 @@ export interface SheetLayout {
|
|
|
188
228
|
}
|
|
189
229
|
|
|
190
230
|
/**
|
|
191
|
-
* `[row, col, style, text, kind, color?]`: `kind` is `n` (number or date), `s` (text), `b`,
|
|
231
|
+
* `[row, col, style, text, kind, color?, number?]`: `kind` is `n` (number or date), `s` (text), `b`,
|
|
192
232
|
* `e` (error) or `''` (empty cell with a format); `color` comes from the number format
|
|
193
233
|
*/
|
|
194
|
-
export type CellViewRow = [number, number, number, string, 'n' | 's' | 'b' | 'e' | '', string?];
|
|
234
|
+
export type CellViewRow = [number, number, number, string, 'n' | 's' | 'b' | 'e' | '', (string | null)?, number?];
|
|
195
235
|
|
|
196
236
|
export interface FontView {
|
|
197
237
|
name: string;
|
package/src/engine.js
CHANGED
|
@@ -73,6 +73,12 @@ function columnNumber(col) {
|
|
|
73
73
|
throw new TypeError(`Columna no válida: ${col} (usa una letra como 'B' o un número desde 1).`);
|
|
74
74
|
}
|
|
75
75
|
|
|
76
|
+
/** A row or column number, or a count, for the engine */
|
|
77
|
+
function lineNumber(value, what) {
|
|
78
|
+
if (!Number.isInteger(value) || value < 0) throw new TypeError(`${what} debe ser un entero positivo: ${value}`);
|
|
79
|
+
return value;
|
|
80
|
+
}
|
|
81
|
+
|
|
76
82
|
/** One worksheet of a workbook */
|
|
77
83
|
export class Sheet {
|
|
78
84
|
/**
|
|
@@ -96,6 +102,11 @@ export class Sheet {
|
|
|
96
102
|
return this.getCell(ref).value;
|
|
97
103
|
}
|
|
98
104
|
|
|
105
|
+
/** The cells of a range (`A1:C3`), by row: their type, value and formula, if any */
|
|
106
|
+
getCells(range) {
|
|
107
|
+
return JSON.parse(call(() => this.workbook._engine.range_json(this.index, range))).map(row => row.map(toCell));
|
|
108
|
+
}
|
|
109
|
+
|
|
99
110
|
/** The values of a range (`A1:C3`), by row */
|
|
100
111
|
getValues(range) {
|
|
101
112
|
return JSON.parse(call(() => this.workbook._engine.range_json(this.index, range))).map(row => row.map(c => toCell(c).value));
|
|
@@ -132,8 +143,9 @@ export class Sheet {
|
|
|
132
143
|
|
|
133
144
|
/**
|
|
134
145
|
* Drawing API: every cell that shows something, with the text Excel shows for it in the
|
|
135
|
-
* language `locale`, as `[row, col, style, text, kind, color?]` (`kind`: `n`, `s`,
|
|
136
|
-
* or `''` for an empty cell with a format; `style` indexes
|
|
146
|
+
* language `locale`, as `[row, col, style, text, kind, color?, number?]` (`kind`: `n`, `s`,
|
|
147
|
+
* `b`, `e` or `''` for an empty cell with a format; `style` indexes
|
|
148
|
+
* `workbook.styleView().xfs`; `number` is the value of `n` cells, dates as serial numbers)
|
|
137
149
|
* @param {string} [locale]
|
|
138
150
|
*/
|
|
139
151
|
cellViews(locale = 'en') {
|
|
@@ -183,6 +195,114 @@ export class Sheet {
|
|
|
183
195
|
call(() => this.workbook._engine.set_column_width(this.index, columnNumber(a), columnNumber(b), width));
|
|
184
196
|
}
|
|
185
197
|
|
|
198
|
+
/**
|
|
199
|
+
* Moves whole rows, as Excel's "cut and insert": `count` rows from `first` go before the row
|
|
200
|
+
* `before` (numbered as they are now). Formats, heights, merged cells, formulas of the whole
|
|
201
|
+
* workbook, defined names, charts, tables, validations, conditional formats, images and
|
|
202
|
+
* comments follow. Throws, and changes nothing, when it would tear merged cells, an array
|
|
203
|
+
* formula, a table or a filter. Returns where the first row ends up.
|
|
204
|
+
* @param {number} first
|
|
205
|
+
* @param {number} count
|
|
206
|
+
* @param {number} before
|
|
207
|
+
*/
|
|
208
|
+
moveRows(first, count, before) {
|
|
209
|
+
const [a, n, b] = [lineNumber(first, 'La fila'), lineNumber(count, 'La cantidad'), lineNumber(before, 'El destino')];
|
|
210
|
+
return call(() => this.workbook._engine.move_rows(this.index, a, n, b));
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
/**
|
|
214
|
+
* Moves whole columns (see `moveRows`); `first` and `before` are letters (`'B'`) or numbers
|
|
215
|
+
* @param {string | number} first
|
|
216
|
+
* @param {number} count
|
|
217
|
+
* @param {string | number} before
|
|
218
|
+
*/
|
|
219
|
+
moveColumns(first, count, before) {
|
|
220
|
+
const [a, n, b] = [columnNumber(first), lineNumber(count, 'La cantidad'), columnNumber(before)];
|
|
221
|
+
return call(() => this.workbook._engine.move_columns(this.index, a, n, b));
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Inserts `count` empty rows before the row `before`, with the height and format of the one
|
|
226
|
+
* above. Formulas, names, charts and everything that `moveRows` follows are updated; ranges and
|
|
227
|
+
* merged cells that the new rows fall inside grow, also tables. Returns `before`.
|
|
228
|
+
* @param {number} before
|
|
229
|
+
* @param {number} [count]
|
|
230
|
+
*/
|
|
231
|
+
insertRows(before, count = 1) {
|
|
232
|
+
const [b, n] = [lineNumber(before, 'La fila'), lineNumber(count, 'La cantidad')];
|
|
233
|
+
return call(() => this.workbook._engine.insert_rows(this.index, b, n));
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/** Inserts empty columns before `before` (letters or number), like the one to their left */
|
|
237
|
+
insertColumns(before, count = 1) {
|
|
238
|
+
const [b, n] = [columnNumber(before), lineNumber(count, 'La cantidad')];
|
|
239
|
+
return call(() => this.workbook._engine.insert_columns(this.index, b, n));
|
|
240
|
+
}
|
|
241
|
+
|
|
242
|
+
/**
|
|
243
|
+
* Deletes `count` rows from `first`. What pointed only at them becomes `#REF!`; ranges and
|
|
244
|
+
* merged cells shrink, also tables. Returns `first`.
|
|
245
|
+
* @param {number} first
|
|
246
|
+
* @param {number} [count]
|
|
247
|
+
*/
|
|
248
|
+
deleteRows(first, count = 1) {
|
|
249
|
+
const [a, n] = [lineNumber(first, 'La fila'), lineNumber(count, 'La cantidad')];
|
|
250
|
+
return call(() => this.workbook._engine.delete_rows(this.index, a, n));
|
|
251
|
+
}
|
|
252
|
+
|
|
253
|
+
/** Deletes columns from `first` (letters or number), see `deleteRows` */
|
|
254
|
+
deleteColumns(first, count = 1) {
|
|
255
|
+
const [a, n] = [columnNumber(first), lineNumber(count, 'La cantidad')];
|
|
256
|
+
return call(() => this.workbook._engine.delete_columns(this.index, a, n));
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
/**
|
|
260
|
+
* Height of a row or rows (`3`, `'3:5'`) in points; `null` goes back to the automatic height
|
|
261
|
+
* @param {string | number} rows
|
|
262
|
+
* @param {number | null} height
|
|
263
|
+
*/
|
|
264
|
+
setRowHeight(rows, height) {
|
|
265
|
+
const [a, b = a] = typeof rows === 'string' ? rows.split(':').map(Number) : [rows];
|
|
266
|
+
if (height !== null && (typeof height !== 'number' || !Number.isFinite(height))) throw new TypeError('El alto debe ser un número de puntos o null.');
|
|
267
|
+
const [first, last] = [lineNumber(a, 'La fila'), lineNumber(b, 'La fila')];
|
|
268
|
+
call(() => this.workbook._engine.set_row_height(this.index, first, last, height ?? undefined));
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
/**
|
|
272
|
+
* Hides or shows rows (`3`, `'3:5'`); they keep their cells and height
|
|
273
|
+
* @param {string | number} rows
|
|
274
|
+
* @param {boolean} hidden
|
|
275
|
+
*/
|
|
276
|
+
setRowsHidden(rows, hidden) {
|
|
277
|
+
const [a, b = a] = typeof rows === 'string' ? rows.split(':').map(Number) : [rows];
|
|
278
|
+
const [first, last] = [lineNumber(a, 'La fila'), lineNumber(b, 'La fila')];
|
|
279
|
+
call(() => this.workbook._engine.set_rows_hidden(this.index, first, last, !!hidden));
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
/**
|
|
283
|
+
* Hides or shows columns (`'B'`, `'B:D'`, `2`); they keep their cells and width
|
|
284
|
+
* @param {string | number} columns
|
|
285
|
+
* @param {boolean} hidden
|
|
286
|
+
*/
|
|
287
|
+
setColumnsHidden(columns, hidden) {
|
|
288
|
+
const [a, b = a] = typeof columns === 'string' ? columns.split(':') : [columns];
|
|
289
|
+
const [first, last] = [columnNumber(a), columnNumber(b)];
|
|
290
|
+
call(() => this.workbook._engine.set_columns_hidden(this.index, first, last, !!hidden));
|
|
291
|
+
}
|
|
292
|
+
|
|
293
|
+
/**
|
|
294
|
+
* Fills `target` from `source`, the cells at one of its ends, as Excel's fill handle: formats
|
|
295
|
+
* repeat, formulas move their references, and with `series` (by default) numbers continue their
|
|
296
|
+
* trend, one date goes on day by day, texts ending in a number count on and names of months and
|
|
297
|
+
* days follow their order. Refuses to fill over merged cells.
|
|
298
|
+
* @param {string} source e.g. `A1:A2`
|
|
299
|
+
* @param {string} target e.g. `A1:A20`
|
|
300
|
+
* @param {{series?: boolean}} [options]
|
|
301
|
+
*/
|
|
302
|
+
fill(source, target, { series = true } = {}) {
|
|
303
|
+
call(() => this.workbook._engine.fill_range(this.index, source, target, !!series));
|
|
304
|
+
}
|
|
305
|
+
|
|
186
306
|
/** Merges a range into one cell; only its first cell may hold a value */
|
|
187
307
|
mergeCells(range) {
|
|
188
308
|
call(() => this.workbook._engine.merge_cells(this.index, range));
|
|
@@ -223,6 +343,16 @@ export class Workbook {
|
|
|
223
343
|
return JSON.parse(call(() => this._engine.styles_view_json()));
|
|
224
344
|
}
|
|
225
345
|
|
|
346
|
+
/**
|
|
347
|
+
* Numbers as a cell format shows them: `style` indexes `styleView().xfs`
|
|
348
|
+
* @param {number} style
|
|
349
|
+
* @param {number[]} values
|
|
350
|
+
* @param {string} [locale]
|
|
351
|
+
*/
|
|
352
|
+
formatNumbers(style, values, locale = 'en') {
|
|
353
|
+
return call(() => this._engine.format_numbers(style, Float64Array.from(values), locale));
|
|
354
|
+
}
|
|
355
|
+
|
|
226
356
|
/**
|
|
227
357
|
* A worksheet by name (ignoring case) or by tab position, from 0
|
|
228
358
|
* @param {string | number} nameOrIndex
|