nexabase-report 0.1.1 → 0.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.md CHANGED
@@ -1,6 +1,48 @@
1
- # NexaBase Report (Designer + Viewer)
2
-
3
- Librería para diseñar y visualizar reportes tipo banded (inspirada en Stimulsoft) con exportación a PDF/Excel/Word.
1
+ # nexabase-report
2
+
3
+ [![npm version](https://img.shields.io/npm/v/nexabase-report)](https://www.npmjs.com/package/nexabase-report)
4
+ [![license](https://img.shields.io/npm/l/nexabase-report)](LICENSE)
5
+
6
+ > Librería Vue 3 + TypeScript para diseñar y visualizar reportes tipo banded (inspirada en Stimulsoft / DevExpress). Incluye **diseñador WYSIWYG**, **visor framework-agnostic** como Custom Element, y **exportación 100% cliente** a PDF, Excel, Word y CSV.
7
+
8
+ ## Tabla de contenidos
9
+
10
+ - [Características](#características)
11
+ - [Instalación](#instalación)
12
+ - [Uso rápido](#uso-rápido)
13
+ - [React](#react)
14
+ - [Vue 3](#vue-3)
15
+ - [Angular](#angular)
16
+ - [HTML puro (CDN)](#html-puro-cdn)
17
+ - [API del Viewer](#api-del-viewer)
18
+ - [Props](#props)
19
+ - [Métodos](#métodos)
20
+ - [Eventos](#eventos)
21
+ - [Formato de datos](#formato-de-datos)
22
+ - [Variables de sistema](#variables-de-sistema)
23
+ - [Funciones de expresiones](#funciones-de-expresiones)
24
+ - [Exportación programática](#exportación-programática)
25
+ - [Solución de problemas](#solución-de-problemas)
26
+ - [Documentación adicional](#documentación-adicional)
27
+
28
+ ---
29
+
30
+ ## Características
31
+
32
+ | Característica | Descripción |
33
+ |----------------|-------------|
34
+ | **Custom Element** | `<nexa-viewer>` funciona en Vue, React, Angular, Svelte o HTML puro |
35
+ | **Diseñador WYSIWYG** | Arrastra y suelta bandas, textos, tablas, imágenes, gráficos |
36
+ | **Exportación cliente** | PDF (vectorial con jsPDF), Excel (SheetJS), Word (docx), CSV |
37
+ | **Gráficos** | ECharts integrado (barras, líneas, pastel, etc.) |
38
+ | **Tablas dinámicas** | Crosstabs (pivot tables) nativos |
39
+ | **Códigos QR / Barras** | jsbarcode + qrcode |
40
+ | **Formato condicional** | Reglas visuales por campo y valor |
41
+ | **Master-Detail** | Subreportes anidados |
42
+ | **Motor de expresiones** | Seguro (sin `eval`): `FormatNumber`, `IIF`, `ISNULL`, etc. |
43
+ | **Sin backend requerido** | Los datos se pasan directamente por props |
44
+
45
+ ---
4
46
 
5
47
  ## Instalación
6
48
 
@@ -8,55 +50,17 @@ Librería para diseñar y visualizar reportes tipo banded (inspirada en Stimulso
8
50
  npm install nexabase-report
9
51
  ```
10
52
 
11
- ## Uso rápido: Viewer (apps externas)
12
-
13
- El Viewer se expone como Custom Element: `<nexa-viewer />`.
14
-
15
- ## Flujo real (NexaBase): cargar JSON exportado + pasar data
16
-
17
- 1. Exporta el reporte desde NexaBase y guarda el JSON en tu app (por ejemplo en `public/report.json`).
18
- 2. Carga ese JSON por `fetch()` y asígnalo a `definition`.
19
- 3. Pasa tus datos del sistema en `data`.
20
-
21
- ### 1) Vue 3
53
+ También necesitarás importar los estilos globales **una vez** en tu app:
22
54
 
23
55
  ```ts
24
- import { registerNexaReport } from 'nexabase-report';
25
56
  import 'nexabase-report/style.css';
26
-
27
- registerNexaReport();
28
57
  ```
29
58
 
30
- ```vue
31
- <script setup lang="ts">
32
- import { onMounted, ref } from 'vue';
59
+ ---
33
60
 
34
- const definition = ref<any>(null);
35
- const data = ref<any[]>([]);
61
+ ## Uso rápido
36
62
 
37
- onMounted(async () => {
38
- const res = await fetch('/report.json');
39
- definition.value = await res.json();
40
-
41
- data.value = [
42
- { nombre: 'Producto A', precio: 100, cantidad: 2 },
43
- { nombre: 'Producto B', precio: 50, cantidad: 5 },
44
- ];
45
- });
46
- </script>
47
-
48
- <template>
49
- <nexa-viewer
50
- :definition="definition"
51
- :data="data"
52
- :parameters="{ fechaDesde: '2026-01-01' }"
53
- minimal
54
- skip-params-dialog
55
- />
56
- </template>
57
- ```
58
-
59
- ### 2) React
63
+ ### React
60
64
 
61
65
  ```tsx
62
66
  import { useEffect, useRef, useState } from 'react';
@@ -66,18 +70,17 @@ import 'nexabase-report/style.css';
66
70
  registerNexaReport();
67
71
 
68
72
  export function ReportViewer() {
69
- const ref = useRef<any>(null);
73
+ const ref = useRef<HTMLElement>(null);
70
74
  const [definition, setDefinition] = useState<any>(null);
71
75
  const [data, setData] = useState<any[]>([]);
72
- const [parameters] = useState<any>({ fechaDesde: '2026-01-01' });
73
76
 
74
77
  useEffect(() => {
75
78
  (async () => {
76
79
  const res = await fetch('/report.json');
77
80
  setDefinition(await res.json());
78
81
  setData([
79
- { nombre: 'Producto A', precio: 100, cantidad: 2 },
80
- { nombre: 'Producto B', precio: 50, cantidad: 5 },
82
+ { nombre: 'Producto A', precio: 100 },
83
+ { nombre: 'Producto B', precio: 50 },
81
84
  ]);
82
85
  })();
83
86
  }, []);
@@ -86,87 +89,242 @@ export function ReportViewer() {
86
89
  if (!ref.current) return;
87
90
  ref.current.definition = definition;
88
91
  ref.current.data = data;
89
- ref.current.parameters = parameters;
90
- }, [definition, data, parameters]);
92
+ }, [definition, data]);
91
93
 
92
94
  return <nexa-viewer ref={ref} minimal />;
93
95
  }
94
96
  ```
95
97
 
96
- ### 3) Angular
98
+ ### Vue 3
99
+
100
+ ```vue
101
+ <script setup lang="ts">
102
+ import { ref, watch } from 'vue';
103
+ import { registerNexaReport } from 'nexabase-report';
104
+ import 'nexabase-report/style.css';
105
+
106
+ registerNexaReport();
97
107
 
98
- En Angular, pon el JSON exportado en `src/assets/report.json` y cárgalo con `HttpClient` o `fetch()`.
108
+ const props = defineProps<{ definition: any; data: any[] }>();
109
+ const viewerRef = ref<HTMLElement | null>(null);
110
+
111
+ watch(
112
+ () => [props.definition, props.data],
113
+ () => {
114
+ const el = viewerRef.value as any;
115
+ if (!el) return;
116
+ el.definition = props.definition;
117
+ el.data = props.data;
118
+ },
119
+ { immediate: true }
120
+ );
121
+ </script>
122
+
123
+ <template>
124
+ <nexa-viewer ref="viewerRef" minimal style="display:block;height:100%" />
125
+ </template>
126
+ ```
127
+
128
+ ### Angular
99
129
 
100
130
  ```ts
101
- import { AfterViewInit, Component, ElementRef, ViewChild } from '@angular/core';
131
+ import { Component, ElementRef, ViewChild, AfterViewInit } from '@angular/core';
132
+ import { registerNexaReport } from 'nexabase-report';
133
+ import 'nexabase-report/style.css';
134
+
135
+ registerNexaReport();
102
136
 
103
137
  @Component({
104
138
  selector: 'app-root',
105
- template: '<nexa-viewer #viewer></nexa-viewer>',
139
+ template: '<nexa-viewer #viewer minimal></nexa-viewer>',
106
140
  })
107
141
  export class AppComponent implements AfterViewInit {
108
- @ViewChild('viewer', { static: true }) viewerRef!: ElementRef<any>;
142
+ @ViewChild('viewer', { static: true }) viewerRef!: ElementRef<HTMLElement>;
109
143
 
110
144
  async ngAfterViewInit() {
111
- const v = this.viewerRef.nativeElement;
145
+ const el = this.viewerRef.nativeElement as any;
112
146
  const res = await fetch('/assets/report.json');
113
- v.definition = await res.json();
114
- v.data = [
115
- { nombre: 'Producto A', precio: 100, cantidad: 2 },
116
- { nombre: 'Producto B', precio: 50, cantidad: 5 },
117
- ];
118
- v.minimal = true;
119
- v.skipParamsDialog = true;
147
+ el.definition = await res.json();
148
+ el.data = [{ nombre: 'Producto A', precio: 100 }];
120
149
  }
121
150
  }
122
151
  ```
123
152
 
124
- ### 3) HTML puro (CDN / UMD)
153
+ ### HTML puro (CDN)
125
154
 
126
155
  ```html
127
- <link rel="stylesheet" href="https://unpkg.com/nexabase-report@0.1.0/dist/style.css">
128
- <script src="https://unpkg.com/nexabase-report@0.1.0/dist/nexabase-report.umd.js"></script>
129
- <script>
130
- window.NexaReport.registerNexaReport();
131
- </script>
156
+ <!DOCTYPE html>
157
+ <html>
158
+ <head>
159
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/nexabase-report@latest/dist/style.css">
160
+ </head>
161
+ <body>
162
+ <nexa-viewer id="viewer" minimal></nexa-viewer>
163
+
164
+ <script src="https://cdn.jsdelivr.net/npm/nexabase-report@latest/dist/nexabase-report.umd.js"></script>
165
+ <script>
166
+ window.NexaReport.registerNexaReport();
167
+ const v = document.getElementById('viewer');
168
+ v.definition = { /* ... */ };
169
+ v.data = [ /* ... */ ];
170
+ </script>
171
+ </body>
172
+ </html>
173
+ ```
132
174
 
133
- <nexa-viewer id="viewer"></nexa-viewer>
134
- <script>
135
- const v = document.getElementById('viewer');
136
- v.definition = {/* ... */};
137
- v.data = [{/* ... */}];
138
- </script>
175
+ ---
176
+
177
+ ## API del Viewer
178
+
179
+ ### Props
180
+
181
+ | Prop | Tipo | Default | Descripción |
182
+ |------|------|---------|-------------|
183
+ | `definition` | `string \| object` | — | Definición del reporte (JSON o objeto) |
184
+ | `data` | `string \| any[] \| Record<string, any[]>` | — | Datos del reporte |
185
+ | `parameters` | `Record<string, any>` | `{}` | Valores de parámetros |
186
+ | `minimal` | `boolean \| string` | `false` | Oculta la toolbar de exportación |
187
+ | `skipParamsDialog` | `boolean` | `false` | Salta el diálogo de parámetros |
188
+ | `showToolbar` | `boolean \| string` | `true` | Muestra/oculta toolbar |
189
+ | `showThumbs` | `boolean \| string` | `true` | Muestra/oculta panel de miniaturas |
190
+ | `currentPage` | `number` | `1` | Página actual (control externo) |
191
+
192
+ ### Métodos (vía ref DOM)
193
+
194
+ ```ts
195
+ const viewer = document.querySelector('nexa-viewer');
196
+
197
+ await viewer.exportPdf();
198
+ await viewer.exportExcel();
199
+ await viewer.exportWord();
200
+ await viewer.exportCsv();
201
+
202
+ viewer.goToPage(3);
203
+ viewer.updateData({ main: [...] });
139
204
  ```
140
205
 
141
- ## Probar sin publicar (recomendado)
206
+ ### Eventos
142
207
 
143
- Desde este repo:
208
+ ```ts
209
+ viewer.addEventListener('page-change', (e) => console.log(e.detail));
210
+ viewer.addEventListener('drill-click', (e) => console.log(e.detail));
211
+ ```
144
212
 
145
- ```bash
146
- npm run build
147
- npm pack
213
+ ---
214
+
215
+ ## Formato de datos
216
+
217
+ ### Single DataSource (array simple)
218
+
219
+ ```json
220
+ [
221
+ { "id": 1, "nombre": "Juan", "ventas": 1500 },
222
+ { "id": 2, "nombre": "María", "ventas": 2300 }
223
+ ]
148
224
  ```
149
225
 
150
- En el proyecto externo:
226
+ ### Multiple DataSources (objeto con alias)
151
227
 
152
- ```bash
153
- npm install /ruta/al/nexabase-report-0.1.0.tgz
228
+ ```json
229
+ {
230
+ "clientes": [
231
+ { "id": 1, "nombre": "Juan" }
232
+ ],
233
+ "pedidos": [
234
+ { "id": 101, "cliente_id": 1, "total": 500 }
235
+ ]
236
+ }
154
237
  ```
155
238
 
156
- ## Publicar en npm
239
+ ---
157
240
 
158
- Guía paso a paso: [docs/PUBLISHING.md](docs/PUBLISHING.md)
241
+ ## Variables de sistema
159
242
 
160
- Resumen:
243
+ | Variable | Descripción | Ejemplo |
244
+ |----------|-------------|---------|
245
+ | `{{Page}}` | Página actual | `1` |
246
+ | `{{TotalPages}}` | Total de páginas | `5` |
247
+ | `{{Today}}` | Fecha actual | `2024-01-15` |
248
+ | `{{Now}}` | Fecha y hora actual | `2024-01-15 14:30` |
249
+ | `{{ReportName}}` | Nombre del reporte | `Ventas Mensuales` |
250
+ | `{{RowNumber}}` | Número de fila | `1` |
251
+ | `{{TotalRows}}` | Total de filas | `50` |
252
+ | `{{EvenRow}}` / `{{OddRow}}` | Fila par/impar | `true` / `false` |
161
253
 
162
- ```bash
163
- npm run build
164
- npm pack --dry-run
165
- npm version patch
166
- npm publish --access public
254
+ ---
255
+
256
+ ## Funciones de expresiones
257
+
258
+ Usar sintaxis `{[...]}`:
259
+
260
+ ```
261
+ {[FormatNumber(precio, 'es-ES')]} → 1.234,56
262
+ {[FormatDate(fecha, 'dd/MM/yyyy')]} → 15/01/2024
263
+ {[FormatCurrency(total, 'USD')]} → $1,234.56
264
+ {[IIF(total > 100, 'OK', 'Bajo')]} → OK
265
+ {[ISNULL(campo, 'Sin dato')]} → Sin dato
266
+ {[UPPER(nombre)]} → JUAN
267
+ {[SUBSTRING(texto, 1, 3)]} → pri
268
+ {[ABS(-5)]} → 5
269
+ {[CEIL(5.3)]} → 6
270
+ {[FLOOR(5.9)]} → 5
167
271
  ```
168
272
 
169
- ## Documentación
273
+ ---
274
+
275
+ ## Exportación programática
276
+
277
+ ```ts
278
+ // React / Vue / Angular
279
+ const viewer = viewerRef.current;
280
+ await viewer.exportPdf();
281
+
282
+ // HTML puro
283
+ const viewer = document.getElementById('viewer');
284
+ await viewer.exportPdf();
285
+ ```
286
+
287
+ ---
288
+
289
+ ## Solución de problemas
290
+
291
+ ### `<nexa-viewer>` no se renderiza
292
+
293
+ Asegúrate de llamar `registerNexaReport()` **antes** de montar el componente, e importar `nexabase-report/style.css`.
294
+
295
+ ### TypeScript: `'nexa-viewer' is not a known element`
296
+
297
+ En React/Vue/Angular, el custom element no está en el JSX/TSX estándar. Agrega declaraciones de tipo locales:
298
+
299
+ ```ts
300
+ declare module 'nexabase-report' {
301
+ export function registerNexaReport(): void;
302
+ }
303
+
304
+ declare global {
305
+ namespace JSX {
306
+ interface IntrinsicElements {
307
+ 'nexa-viewer': any;
308
+ }
309
+ }
310
+ }
311
+ ```
312
+
313
+ En Angular usa `CUSTOM_ELEMENTS_SCHEMA` en tu `@Component`.
314
+
315
+ ### Los datos no aparecen
316
+
317
+ Verifica que `dataSource` en la definición del reporte coincida con el alias de tus datos. El primer datasource suele tener alias `"main"`.
318
+
319
+ ---
320
+
321
+ ## Documentación adicional
322
+
323
+ - [API del Viewer](docs/VIEWER_API.md)
324
+ - [Integración externa](docs/EXTERNAL_INTEGRATION.md)
325
+ - [Plan de QA](docs/QA_PLAN.md)
326
+ - [Guía de publicación](docs/PUBLISHING.md)
327
+
328
+ ## Licencia
170
329
 
171
- - API del Viewer: [docs/VIEWER_API.md](docs/VIEWER_API.md)
172
- - Integración externa: [docs/EXTERNAL_INTEGRATION.md](docs/EXTERNAL_INTEGRATION.md)
330
+ MIT © NexaBase Team
Binary file
@@ -1,4 +1,4 @@
1
- import { c as MQ } from "./index-j9lAJl17.js";
1
+ import { c as MQ } from "./index-D3l3xYdG.js";
2
2
  var nt = { exports: {} };
3
3
  /*!
4
4
  * html2canvas 1.4.1 <https://html2canvas.hertzen.com>
@@ -1,5 +1,5 @@
1
- import { a as c } from "./index-j9lAJl17.js";
2
- import { r as f } from "./html2canvas-MMn8dUQK.js";
1
+ import { a as c } from "./index-D3l3xYdG.js";
2
+ import { r as f } from "./html2canvas-Bt7A8WrM.js";
3
3
  function l(r, n) {
4
4
  for (var o = 0; o < n.length; o++) {
5
5
  const e = n[o];
@@ -1,6 +1,6 @@
1
- import { g as De, a as Ke, c as Te } from "./index-j9lAJl17.js";
2
- import { j as Ge } from "./jspdf.es.min-DIevzj7_.js";
3
- import { r as Be } from "./html2canvas-MMn8dUQK.js";
1
+ import { g as De, a as Ke, c as Te } from "./index-D3l3xYdG.js";
2
+ import { j as Ge } from "./jspdf.es.min-M0h3R1kR.js";
3
+ import { r as Be } from "./html2canvas-Bt7A8WrM.js";
4
4
  function Ue(ge, we) {
5
5
  for (var me = 0; me < we.length; me++) {
6
6
  const ce = we[me];