@miguimono/json-schema 2.0.0

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.
Files changed (30) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +255 -0
  3. package/dist/schema/README.md +421 -0
  4. package/dist/schema/fesm2022/json-schema.mjs +1654 -0
  5. package/dist/schema/fesm2022/json-schema.mjs.map +1 -0
  6. package/dist/schema/index.d.ts +5 -0
  7. package/dist/schema/lib/components/schema-card.component.d.ts +128 -0
  8. package/dist/schema/lib/components/schema-links.component.d.ts +61 -0
  9. package/dist/schema/lib/components/schema.component.d.ts +115 -0
  10. package/dist/schema/lib/models.d.ts +1 -0
  11. package/dist/schema/lib/services/json-adapter.service.d.ts +1 -0
  12. package/dist/schema/lib/services/schema-layout.service.d.ts +1 -0
  13. package/dist/schema/lib/shared/models.d.ts +319 -0
  14. package/dist/schema/lib/shared/services/json-adapter.service.d.ts +12 -0
  15. package/dist/schema/lib/shared/services/schema-layout.service.d.ts +21 -0
  16. package/dist/schema/ng16/fesm2022/json-schema-ng16.mjs +1656 -0
  17. package/dist/schema/ng16/fesm2022/json-schema-ng16.mjs.map +1 -0
  18. package/dist/schema/ng16/index.d.ts +5 -0
  19. package/dist/schema/ng16/lib/components/schema-card.component.d.ts +64 -0
  20. package/dist/schema/ng16/lib/components/schema-links.component.d.ts +28 -0
  21. package/dist/schema/ng16/lib/components/schema.component.d.ts +132 -0
  22. package/dist/schema/ng16/lib/models.d.ts +1 -0
  23. package/dist/schema/ng16/lib/services/json-adapter.service.d.ts +1 -0
  24. package/dist/schema/ng16/lib/services/schema-layout.service.d.ts +1 -0
  25. package/dist/schema/ng16/lib/shared/models.d.ts +319 -0
  26. package/dist/schema/ng16/lib/shared/services/json-adapter.service.d.ts +12 -0
  27. package/dist/schema/ng16/lib/shared/services/schema-layout.service.d.ts +21 -0
  28. package/dist/schema/ng16/public-api.d.ts +6 -0
  29. package/dist/schema/public-api.d.ts +5 -0
  30. package/package.json +61 -0
package/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Miguimono
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,255 @@
1
+ # JSON Schema
2
+
3
+ Esta librería tiene **dos variantes** de build:
4
+
5
+ - `projects/schema-ng19`: build principal para Angular moderno (en este repo se usa Angular 19).
6
+ - `projects/schema-ng16`: build compatible con Angular 16 (export en subpath `./ng16`).
7
+
8
+ Ambas se construyen desde **la raíz** del repo. No necesitas instalar dependencias dentro de `projects/schema-ng19` ni `projects/schema-ng16`.
9
+
10
+ ## Requisitos y versiones
11
+
12
+ - Node.js: **no hay `engines` definidos** en los `package.json`, por lo que debes usar una versión compatible con tu Angular objetivo.
13
+ - NPM: la versión incluida con tu Node.
14
+ - Angular CLI: viene como dependencia del workspace cuando instalas en la raíz.
15
+
16
+ Recomendación práctica usando `nvm`:
17
+
18
+ - Para construir la variante principal (Angular moderno): `nvm use 22`
19
+ - Para probar compatibilidad Angular 16 en entornos antiguos: `nvm use 16` (o una versión LTS más nueva si tu entorno lo permite)
20
+
21
+ Si cambias de versión de Node, **reinstala dependencias en la raíz** para evitar binarios incompatibles.
22
+
23
+ ## Instalación (única, en la raíz)
24
+
25
+ Desde la raíz del repo:
26
+
27
+ ```bash
28
+ nvm use 22
29
+ npm install
30
+ ```
31
+
32
+ Esto crea un **solo** `node_modules` en la raíz.
33
+ Las carpetas `projects/schema-ng19/node_modules` y `projects/schema-ng16/node_modules` **no deberían existir**.
34
+
35
+ ## Compilación
36
+
37
+ Desde la raíz del repo:
38
+
39
+ ```bash
40
+ # build Angular moderno
41
+ npm run build:schema-ng19
42
+
43
+ # build Angular 16 (subpath ./ng16)
44
+ npm run build:schema-ng16
45
+
46
+ # build de ambas
47
+ npm run build
48
+ ```
49
+
50
+ Los artefactos quedan en:
51
+
52
+ - `dist/schema/` (principal)
53
+ - `dist/schema/ng16/` (compatibilidad)
54
+
55
+ ## Empaquetado (npm pack)
56
+
57
+ ```bash
58
+ npm run pack
59
+ ```
60
+
61
+ Esto:
62
+
63
+ 1. Construye ambas variantes.
64
+ 2. Genera un `.tgz` en la raíz, por ejemplo `json-schema-x.y.z.tgz`.
65
+
66
+ Si quieres empaquetar manualmente desde la carpeta de salida:
67
+
68
+ ```bash
69
+ cd dist/schema
70
+ npm pack
71
+ ```
72
+
73
+ ## Consumo en proyectos
74
+
75
+ Import normal (Angular moderno):
76
+
77
+ ```ts
78
+ import { SchemaComponent } from "@miguimono/json-schema";
79
+ ```
80
+
81
+ Import explícito Angular 19:
82
+
83
+ ```ts
84
+ import { SchemaComponent } from "@miguimono/json-schema/ng19";
85
+ ```
86
+
87
+ Import compatible Angular 16:
88
+
89
+ ```ts
90
+ import { SchemaComponent } from "@miguimono/json-schema/ng16";
91
+ ```
92
+
93
+ ## ¿Qué hace la librería?
94
+
95
+ Convierte un JSON arbitrario en un **grafo visual interactivo** de cards y enlaces.
96
+ Incluye normalización del JSON, layout jerárquico determinista y render con pan/zoom.
97
+
98
+ ## Módulos y responsabilidades
99
+
100
+ - `SchemaComponent`: orquesta el pipeline completo (normalización → layout → render), maneja pan/zoom, overlays y toolbar.
101
+ - `SchemaCardComponent`: renderiza un nodo como card, admite `cardTemplate`, badges e imagen opcional.
102
+ - `SchemaLinksComponent`: dibuja las aristas en SVG según el estilo configurado.
103
+ - `JsonAdapterService`: normaliza el JSON en nodos/aristas con metadatos.
104
+ - `SchemaLayoutService`: calcula posiciones y puntos de aristas.
105
+ - `models.ts`: contratos y `DEFAULT_SETTINGS`.
106
+
107
+ ## Flujo interno (alto nivel)
108
+
109
+ 1. `JsonAdapterService` normaliza el JSON en `NormalizedGraph`.
110
+ 2. `SchemaLayoutService` calcula posiciones y puntos de aristas.
111
+ 3. `SchemaComponent` renderiza cards/enlaces y gestiona pan/zoom.
112
+
113
+ ## Recomendaciones de performance
114
+
115
+ - Para grafos muy grandes, considera:
116
+ - `dataView.previewMaxKeys` bajo (ej. 3–8).
117
+ - `dataView.maxDepth` (ej. 3–6).
118
+ - `dataView.autoResizeCards: false` si necesitas mayor velocidad.
119
+ - `viewport.height` más alto si el grafo es muy vertical.
120
+ - Si usas imágenes, define `dataView.imageSizePx` y `imageFit` para evitar relayouts.
121
+
122
+ ## Troubleshooting (errores comunes)
123
+
124
+ - `Cannot destructure property 'pos' of 'file.referencedFiles[index]' ...`
125
+ Suele indicar incompatibilidad de TypeScript. Para Angular 19 usa TS `~5.5.x`.
126
+ - `Could not find the '@angular-devkit/build-angular:ng-packagr' builder`
127
+ Verifica que `@angular-devkit/build-angular` y `ng-packagr` estén en `devDependencies`.
128
+ - `Unknown argument: verbose`
129
+ Usa `npx ng ...` para forzar el CLI local del proyecto.
130
+ - Si el esquema no se re-renderiza tras mutar el JSON, reasigna `data` con un
131
+ nuevo objeto (cache referencial).
132
+
133
+ ## Estructura recomendada (mantenimiento)
134
+
135
+ - `projects/schema-ng19`: build principal (Angular 19).
136
+ - `projects/schema-ng16`: build compatibilidad (Angular 16).
137
+ - `projects/schema-shared`: fuente única de modelos y servicios.
138
+ - `scripts/sync-shared.js`: copia `schema-shared` dentro de cada build antes de compilar.
139
+ - `projects/schema-shared/src/lib/styles`: tokens, mixins y base de estilos.
140
+
141
+ Notas de mantenimiento:
142
+
143
+ - **No edites** manualmente las copias dentro de `schema-ng16/src/lib/shared` ni `schema-ng19/src/lib/shared`.
144
+ - Haz cambios en `projects/schema-shared/src/lib` y compila (el script las sincroniza).
145
+ - Mantén la versión **única** en el `package.json` raíz (se sincroniza a los subproyectos).
146
+ - Puedes verificar sincronización con `npm run check:shared`.
147
+ - Para builds limpios: `npm run clean:dist` (funciona en Windows, macOS y Linux).
148
+
149
+ ## Uso rápido (Angular 19 / 16)
150
+
151
+ ### Angular 19 (default)
152
+
153
+ ```ts
154
+ import { SchemaComponent } from "@miguimono/json-schema";
155
+ ```
156
+
157
+ ### Angular 16
158
+
159
+ ```ts
160
+ import { SchemaComponent } from "@miguimono/json-schema/ng16";
161
+ ```
162
+
163
+ ### Ejemplo mínimo
164
+
165
+ ```ts
166
+ import { Component } from "@angular/core";
167
+ import { CommonModule } from "@angular/common";
168
+ import { SchemaComponent } from "@miguimono/json-schema";
169
+
170
+ @Component({
171
+ selector: "app-demo",
172
+ standalone: true,
173
+ imports: [CommonModule, SchemaComponent],
174
+ template: `<schema [data]="data"></schema>`,
175
+ })
176
+ export class DemoComponent {
177
+ data = {
178
+ root: {
179
+ name: "Root",
180
+ children: [{ name: "Child A" }, { name: "Child B" }],
181
+ },
182
+ };
183
+ }
184
+ ```
185
+
186
+ ### Ejemplo con settings básicos
187
+
188
+ ```ts
189
+ import { SchemaSettings, DEFAULT_SETTINGS } from "@miguimono/json-schema";
190
+
191
+ const settings: SchemaSettings = {
192
+ ...DEFAULT_SETTINGS,
193
+ layout: {
194
+ ...DEFAULT_SETTINGS.layout,
195
+ layoutDirection: "RIGHT",
196
+ linkStyle: "curve",
197
+ },
198
+ dataView: {
199
+ ...DEFAULT_SETTINGS.dataView,
200
+ titleKeyPriority: ["name", "title", "id"],
201
+ previewMaxKeys: 5,
202
+ },
203
+ };
204
+ ```
205
+
206
+ ```html
207
+ <schema [data]="data" [settings]="settings"></schema>
208
+ ```
209
+
210
+ ## Instalación directa desde `.tgz` local (sin publicar)
211
+
212
+ Si copiaste el paquete a `lib/json-schema-x.y.z.tgz` dentro del proyecto consumidor:
213
+
214
+ ```bash
215
+ npm install ./lib/json-schema-x.y.z.tgz
216
+ ```
217
+
218
+ Luego:
219
+
220
+ - Angular 19+:
221
+ ```ts
222
+ import { SchemaComponent } from "@miguimono/json-schema";
223
+ ```
224
+ - Angular 19 explícito:
225
+ ```ts
226
+ import { SchemaComponent } from "@miguimono/json-schema/ng19";
227
+ ```
228
+ - Angular 16:
229
+ ```ts
230
+ import { SchemaComponent } from "@miguimono/json-schema/ng16";
231
+ ```
232
+
233
+ Nota: el `.tgz` puede estar en cualquier ruta local; usa la ruta relativa o absoluta.
234
+
235
+ ## Notas importantes
236
+
237
+ - Hay **3 `package.json`**:
238
+ - `package.json` raíz: scripts y exports.
239
+ - `projects/schema-ng19/package.json`: metadata del build principal.
240
+ - `projects/schema-ng16/package.json`: metadata del build `ng16`.
241
+ - **Solo se instala en la raíz**.
242
+ Si instalas dentro de `projects/schema-ng19` o `projects/schema-ng16`, tendrás tres `node_modules` y eso genera inconsistencias.
243
+
244
+ ## Limpieza si ya instalaste en subproyectos
245
+
246
+ Si ya creaste `node_modules` dentro de los subproyectos, bórralos y vuelve a instalar en la raíz:
247
+
248
+ ```bash
249
+ rm -rf projects/schema-ng19/node_modules projects/schema-ng16/node_modules
250
+ npm install
251
+ ```
252
+
253
+ ## Licencia
254
+
255
+ MIT — Copyright (c) 2026 Miguimono
@@ -0,0 +1,421 @@
1
+ # @miguimono/json-schema
2
+
3
+ ![License](https://img.shields.io/badge/license-MIT-blue.svg)
4
+ ![Angular](https://img.shields.io/badge/Angular-19-red.svg)
5
+ ![TypeScript](https://img.shields.io/badge/TypeScript-5.x-blue.svg)
6
+ ![Build Status](https://img.shields.io/badge/build-passing-brightgreen.svg)
7
+
8
+ **Schema** es una librería para **Angular 19** que transforma cualquier objeto **JSON arbitrario** en un **grafo visual interactivo** (cards + conexiones).
9
+ Construida con **standalone components**, **Angular Signals** e `input()`. Compatible con Angular Material 19 (opcional en tu app).
10
+
11
+ Pensada para explorar, depurar y presentar jerarquías de datos complejos en aplicaciones empresariales.
12
+
13
+ ---
14
+
15
+ ## Tabla de contenidos
16
+
17
+ - [@miguimono/json-schema](#miguimonoschema)
18
+ - [Tabla de contenidos](#tabla-de-contenidos)
19
+ - [✨ Características](#-características)
20
+ - [📦 Instalación](#-instalación)
21
+ - [🧠 Conceptos y arquitectura](#-conceptos-y-arquitectura)
22
+ - [🧩 Componentes públicos](#-componentes-públicos)
23
+ - [🧪 Ejemplos](#-ejemplos)
24
+ - [Ejemplo básico (solo JSON)](#ejemplo-básico-solo-json)
25
+ - [Ejemplo completo (todas las entradas)](#ejemplo-completo-todas-las-entradas)
26
+ - [🔎 API](#-api)
27
+ - [Inputs de `<schema>`](#inputs-de-schema)
28
+ - [`SchemaSettings`](#schemasettings)
29
+ - [Estilos de enlaces](#estilos-de-enlaces)
30
+ - [✅ Buenas prácticas](#-buenas-prácticas)
31
+ - [❓ FAQ](#-faq)
32
+ - [🧭 Roadmap / Notas de versión](#-roadmap--notas-de-versión)
33
+ - [📄 Licencia](#-licencia)
34
+
35
+ ---
36
+
37
+ ## ✨ Características
38
+
39
+ - Convierte **cualquier JSON** en un grafo navegable (sin premodelar).
40
+ - **Layout determinista** tipo _tidy tree_: sin solapes, orden estable tal cual el JSON.
41
+ - Dirección de flujo **RIGHT** (izq→der) o **DOWN** (arriba→abajo).
42
+ - **Alineación** del padre: con el **primer hijo** o **centrado** respecto a sus hijos.
43
+ - **Enlaces**: curva, ortogonal o línea recta (con animación suave en relayout).
44
+ - **Zoom, pan, reset** y **fit-to-view** automáticos.
45
+ - **Cards personalizables** vía `ng-template` (título + atributos + badges, o tu propio markup).
46
+ - **Auto-resize**: mide el DOM real y reacomoda el layout hasta estabilizar.
47
+ - **Collapse/Expand** por card (opcional) con preservación de “slots”.
48
+ - **Acento visual por clave booleana**: bordes/rellenos según `true/false/null`.
49
+ - Tooling moderno: **TypeScript 5**, **Angular Signals**, standalone components.
50
+
51
+ > **Nota**: La librería expone una API de alto nivel. Internamente utiliza:
52
+ >
53
+ > - `JsonAdapterService`: JSON → grafo (nodos/aristas) con metadatos.
54
+ > - `SchemaLayoutService`: posiciones deterministas + puntos de enlace.
55
+
56
+ ---
57
+
58
+ ## 📦 Instalación
59
+
60
+ ```bash
61
+ npm i @miguimono/json-schema
62
+ ```
63
+
64
+ **Peer deps:**
65
+
66
+ - `@angular/core` `^19.0.0`
67
+ - `@angular/common` `^19.0.0`
68
+ - `@angular/cdk` `^19.0.0` (si usas Material en tu app)
69
+ - `rxjs` `~7.8.0`
70
+ - TypeScript `5.x`
71
+
72
+ ---
73
+
74
+ ## 🧠 Conceptos y arquitectura
75
+
76
+ - **Nodo (SchemaNode):** card con `label`/título, preview de atributos y badges de arrays.
77
+ El adapter determina orden entre hermanos (prop `jsonMeta.childOrder`) para preservar **el orden del JSON**.
78
+ - **Arista (SchemaEdge):** une dos nodos (`source` → `target`) y contiene `points` (start/bends/end).
79
+ - **Grafo normalizado (NormalizedGraph):** `nodes + edges + meta`.
80
+ - **Layout:** el servicio organiza los nodos por profundidad y subárboles.
81
+ - `layoutDirection`: `RIGHT` o `DOWN`.
82
+ - `layoutAlign`: `'firstChild' | 'center'`.
83
+ - `linkStyle`: `'curve' | 'orthogonal' | 'line'`.
84
+
85
+ ---
86
+
87
+ ## 🧩 Componentes públicos
88
+
89
+ | Componente | Descripción rápida |
90
+ | ---------------------- | ------------------------------------------------------------------------------------------ |
91
+ | `SchemaComponent` | Orquestador (pan/zoom, render de cards y enlaces, medición, relayout y colapso/expansión). |
92
+ | `SchemaCardComponent` | Card genérica por nodo. Soporta `ng-template` para personalizar contenido. |
93
+ | `SchemaLinksComponent` | Dibuja todas las aristas en un único `<svg>`. |
94
+
95
+ > Usualmente **solo importas** `SchemaComponent`. Los demás son internos.
96
+
97
+ ---
98
+
99
+ ## 🧪 Ejemplos
100
+
101
+ ### Ejemplo básico (solo JSON)
102
+
103
+ ```ts
104
+ // app/demo-basic.component.ts
105
+ // ---------------------------------------------------------
106
+ // Ejemplo mínimo: envío únicamente un JSON.
107
+ // ---------------------------------------------------------
108
+ import { Component } from '@angular/core';
109
+ import { CommonModule } from '@angular/common';
110
+ import { SchemaComponent } from '@miguimono/json-schema';
111
+
112
+ @Component({
113
+ selector: 'app-demo-basic',
114
+ standalone: true,
115
+ imports: [CommonModule, SchemaComponent],
116
+ template: \`
117
+ <schema [data]="exampleJson"></schema>
118
+ \`,
119
+ })
120
+ export class DemoBasicComponent {
121
+ exampleJson = {
122
+ central: {
123
+ central_name: 'Centro | Bogotá',
124
+ cables: [
125
+ { cable_name: 'GA-05', in_damage: true },
126
+ { cable_name: 'GA-06', in_damage: false },
127
+ ],
128
+ },
129
+ };
130
+ }
131
+ ```
132
+
133
+ ### Ejemplo completo (todas las entradas)
134
+
135
+ ```ts
136
+ // app/demo-full.component.ts
137
+ // ---------------------------------------------------------
138
+ // Demostración completa: settings, template, mensajes y estados.
139
+ // ---------------------------------------------------------
140
+ import { Component, signal } from '@angular/core';
141
+ import { CommonModule } from '@angular/common';
142
+ import { SchemaComponent, DEFAULT_SETTINGS, SchemaSettings } from '@miguimono/json-schema';
143
+
144
+ @Component({
145
+ selector: 'app-demo-full',
146
+ standalone: true,
147
+ imports: [CommonModule, SchemaComponent],
148
+ template: \`
149
+ <schema
150
+ [data]="data()"
151
+ [settings]="settings"
152
+ [cardTemplate]="cardTpl"
153
+ [isLoading]="isLoading()"
154
+ [isError]="isError()"
155
+ [emptyMessage]="'No hay datos para mostrar'"
156
+ [loadingMessage]="'Cargando…'"
157
+ [errorMessage]="'No se pudo renderizar el esquema'"
158
+ (nodeClick)="onNodeClick($event)"
159
+ (linkClick)="onLinkClick($event)"
160
+ ></schema>
161
+
162
+ <!-- Template personalizado para cada card -->
163
+ <ng-template #cardTpl let-node>
164
+ <div style="padding:12px 14px; max-width:360px">
165
+ <div style="font-weight:700; margin-bottom:6px">
166
+ {{ node.jsonMeta?.title || node.label }}
167
+ </div>
168
+ <div *ngIf="node.jsonMeta?.attributes as attrs">
169
+ <div *ngFor="let e of (attrs | keyvalue)" style="font-size:12px; margin:2px 0">
170
+ <strong style="opacity:.7">{{ e.key }}:</strong> {{ e.value }}
171
+ </div>
172
+ </div>
173
+ </div>
174
+ </ng-template>
175
+ \`,
176
+ })
177
+ export class DemoFullComponent {
178
+ // Datos reactivos (puedes reemplazar por tu fachada)
179
+ data = signal<any>({
180
+ nivel: 'nivel 0',
181
+ children: [
182
+ { nivel: 'Nivel 1', info: '...', children: [] },
183
+ { nivel: 'Nivel 2', info: '...', children: [] },
184
+ ],
185
+ });
186
+
187
+ // Settings (parte de ellos, usando DEFAULT_SETTINGS como base)
188
+ settings: SchemaSettings = {
189
+ ...DEFAULT_SETTINGS,
190
+ layout: {
191
+ ...DEFAULT_SETTINGS.layout,
192
+ layoutDirection: 'RIGHT', // RIGHT | DOWN
193
+ layoutAlign: 'firstChild', // firstChild | center
194
+ linkStyle: 'curve', // curve | orthogonal | line
195
+ straightThresholdDx: 160,
196
+ curveTension: 80,
197
+ snapChainSegmentsY: true, // alinear cadenas lineales
198
+ },
199
+ colors: {
200
+ ...DEFAULT_SETTINGS.colors,
201
+ linkStroke: '#019df4',
202
+ linkStrokeWidth: 2,
203
+ accentByKey: 'certified', // toma true/false/null de node.data[certified]
204
+ accentFill: true,
205
+ showColorTrue: true,
206
+ showColorFalse: true,
207
+ showColorNull: true,
208
+ },
209
+ dataView: {
210
+ ...DEFAULT_SETTINGS.dataView,
211
+ titleKeyPriority: ['name', 'title', 'id', 'label'],
212
+ previewMaxKeys: 4,
213
+ treatScalarArraysAsAttribute: true,
214
+ collapseArrayContainers: true,
215
+ collapseSingleChildWrappers: true,
216
+ enableCollapse: true, // muestra botón por card si tiene hijos
217
+ defaultNodeSize: { width: 220, height: 96 },
218
+ noWrapKeys: ['service_number'], // muestra esas claves en una sola línea
219
+ maxCardWidth: 380, // restricciones a la medición
220
+ maxCardHeight: null,
221
+ autoResizeCards: true,
222
+ measureExtraWidthPx: 24,
223
+ measureExtraHeightPx: 0,
224
+ },
225
+ viewport: {
226
+ ...DEFAULT_SETTINGS.viewport,
227
+ height: 800,
228
+ minHeight: 480,
229
+ showToolbar: true,
230
+ },
231
+ messages: {
232
+ ...DEFAULT_SETTINGS.messages,
233
+ isLoading: false,
234
+ isError: false,
235
+ emptyMessage: 'No hay datos para mostrar',
236
+ loadingMessage: 'Cargando…',
237
+ errorMessage: 'Error al cargar el esquema',
238
+ },
239
+ debug: {
240
+ ...DEFAULT_SETTINGS.debug,
241
+ measure: false,
242
+ layout: false,
243
+ paintBounds: false,
244
+ exposeOnWindow: false,
245
+ },
246
+ };
247
+
248
+ // Estados (por ejemplo, mientras llega el JSON)
249
+ isLoading = signal(false);
250
+ isError = signal(false);
251
+
252
+ onNodeClick(n: any) {
253
+ console.log('nodeClick', n);
254
+ }
255
+ onLinkClick(e: any) {
256
+ console.log('linkClick', e);
257
+ }
258
+ }
259
+ ```
260
+
261
+ ---
262
+
263
+ ## 🔎 API
264
+
265
+ ### Inputs de `<schema>`
266
+
267
+ | Input | Tipo | Default | Descripción |
268
+ | ---------------- | -------------------------- | ------------------------------ | -------------------------------------------------------------------------- |
269
+ | `data` | `any` | — | **Requerido.** JSON arbitrario a visualizar. |
270
+ | `settings` | `SchemaSettings \| null` | `DEFAULT_SETTINGS` | Configuración por secciones. Los valores no provistos heredan del default. |
271
+ | `cardTemplate` | `TemplateRef<any> \| null` | `null` | Plantilla personalizada para cada card (el nodo llega como `$implicit`). |
272
+ | `isLoading` | `boolean` | `false` | Fuerza overlay de carga (también configurable en `settings.messages`). |
273
+ | `isError` | `boolean` | `false` | Fuerza overlay de error (también configurable en `settings.messages`). |
274
+ | `emptyMessage` | `string` | `'No hay datos para mostrar'` | Texto del estado vacío. |
275
+ | `loadingMessage` | `string` | `'Cargando…'` | Texto del estado de carga. |
276
+ | `errorMessage` | `string` | `'Error al cargar el esquema'` | Texto del estado de error. |
277
+
278
+ **Outputs**
279
+
280
+ | Output | Payload | Descripción |
281
+ | ----------- | ------------ | ----------------------------------------- |
282
+ | `nodeClick` | `SchemaNode` | Se emite al hacer click sobre una card. |
283
+ | `linkClick` | `SchemaEdge` | Se emite al hacer click sobre una arista. |
284
+
285
+ > El layout y la medición son internos. El contenedor solo provee `data` y, opcionalmente, `settings` y `cardTemplate`.
286
+
287
+ ---
288
+
289
+ ### `SchemaSettings`
290
+
291
+ Las opciones están **agrupadas por secciones**. Cualquier sección/propiedad omitida toma su valor desde `DEFAULT_SETTINGS` (puedes importarlo).
292
+
293
+ **layout**
294
+
295
+ | Propiedad | Tipo | Default | Descripción |
296
+ | --------------------- | ----------------------------------- | -------------- | ------------------------------------------------------------------------------- |
297
+ | `layoutDirection` | `'RIGHT' \| 'DOWN'` | `'RIGHT'` | Orientación del grafo. |
298
+ | `layoutAlign` | `'firstChild' \| 'center'` | `'firstChild'` | Alineación vertical (RIGHT) u horizontal (DOWN) del padre respecto a sus hijos. |
299
+ | `linkStyle` | `'curve' \| 'orthogonal' \| 'line'` | `'curve'` | Estilo visual de aristas. |
300
+ | `curveTension` | `number` | `80` | Tensión para curvas; clamp 20–200. |
301
+ | `straightThresholdDx` | `number` | `160` | Si `dx < threshold`, una curva se dibuja recta para evitar curvas muy cerradas. |
302
+ | `snapChainSegmentsY` | `boolean` | `false` | Alinea cadenas lineales (out=1) al eje del hijo para “líneas rectas”. |
303
+
304
+ **dataView**
305
+
306
+ | Propiedad | Tipo | Default |
307
+ | ------------------------------ | --------------------------------- | ------------------------------- |
308
+ | `titleKeyPriority` | `string[]` | `['name','title','id','label']` |
309
+ | `hiddenKeysGlobal` | `string[]` | `[]` |
310
+ | `titleMode` | `'auto' \| 'none'` | `'auto'` |
311
+ | `previewMaxKeys` | `number` | `4` |
312
+ | `treatScalarArraysAsAttribute` | `boolean` | `true` |
313
+ | `collapseArrayContainers` | `boolean` | `true` |
314
+ | `collapseSingleChildWrappers` | `boolean` | `true` |
315
+ | `maxDepth` | `number \| null` | `null` |
316
+ | `defaultNodeSize` | `{ width:number; height:number }` | `{220,96}` |
317
+ | `noWrapKeys` | `string[]` | `[]` |
318
+ | `maxCardWidth` | `number \| null` | `null` |
319
+ | `maxCardHeight` | `number \| null` | `null` |
320
+ | `autoResizeCards` | `boolean` | `true` |
321
+ | `measureExtraWidthPx` | `number` | `24` |
322
+ | `measureExtraHeightPx` | `number` | `0` |
323
+ | `enableCollapse` | `boolean` | `false` |
324
+
325
+ **colors**
326
+
327
+ | Propiedad | Tipo | Default | Comentario |
328
+ | -------------------------- | ---------------- | --------- | --------------------------------------------------- |
329
+ | `linkStroke` | `string` | `#019df4` | Color de aristas. |
330
+ | `linkStrokeWidth` | `number` | `2` | Grosor de aristas. |
331
+ | `accentByKey` | `string \| null` | `null` | Clave booleana en `node.data` para acento por card. |
332
+ | `accentFill` | `boolean` | `false` | Relleno además del borde. |
333
+ | `accentInverse` | `boolean` | `false` | Invierte mapping de colores. |
334
+ | `showColorTrue/False/Null` | `boolean` | `false` | Habilita color por cada caso. |
335
+
336
+ **viewport**
337
+
338
+ | Propiedad | Tipo | Default |
339
+ | ------------- | --------- | ------- |
340
+ | `height` | `number` | `800` |
341
+ | `minHeight` | `number` | `480` |
342
+ | `showToolbar` | `boolean` | `true` |
343
+
344
+ **messages**
345
+
346
+ | Propiedad | Tipo | Default |
347
+ | ---------------- | --------- | ------------------------------ |
348
+ | `isLoading` | `boolean` | `false` |
349
+ | `isError` | `boolean` | `false` |
350
+ | `emptyMessage` | `string` | `'No hay datos para mostrar'` |
351
+ | `loadingMessage` | `string` | `'Cargando…'` |
352
+ | `errorMessage` | `string` | `'Error al cargar el esquema'` |
353
+
354
+ **debug**
355
+
356
+ | Propiedad | Tipo | Default |
357
+ | ---------------- | --------- | ------- |
358
+ | `measure` | `boolean` | `false` |
359
+ | `layout` | `boolean` | `false` |
360
+ | `paintBounds` | `boolean` | `false` |
361
+ | `exposeOnWindow` | `boolean` | `false` |
362
+
363
+ > Para ver todos los defaults en código, revisa `DEFAULT_SETTINGS` (exportado desde `projects/schema-ng19/src/lib/models.ts`).
364
+
365
+ ---
366
+
367
+ ### Estilos de enlaces
368
+
369
+ - **`curve`** (default): curva cúbica con tensión configurable (`curveTension`).
370
+ Si la separación horizontal `dx` es pequeña (< `straightThresholdDx`), se renderiza recta.
371
+ - **`orthogonal`**: segmentos en “L” (con codo intermedio).
372
+ - **`line`**: línea recta simple.
373
+
374
+ Cámbialo desde la toolbar integrada o vía `settings.layout.linkStyle`.
375
+
376
+ ---
377
+
378
+ ## ✅ Buenas prácticas
379
+
380
+ - Pasa `data` ya “listo para leer”; el adapter colapsa _wrappers_ triviales y arrays escalares (opcional).
381
+ - Si usas `cardTemplate`, **no dependas** de orden de claves en objetos; accede por `node.data.tuClave`.
382
+ - Si activas `enableCollapse`, deja que el contenedor preserve el estado de colapso si recreas el componente.
383
+ - Para árboles muy altos, puedes aumentar `viewport.height` o usar el ajuste automático del stage (incluido en el componente).
384
+
385
+ ---
386
+
387
+ ## ❓ FAQ
388
+
389
+ **¿Cómo personalizo el botón de colapso (dirección del caret)?**
390
+ El icono se determina en `SchemaCardComponent` (método `arrowGlyph()`), que muestra **◀/▶** para `RIGHT` y **▲/▼** para `DOWN`, según `isCollapsed`.
391
+
392
+ **¿Se respeta el orden del JSON?**
393
+ Sí. El adapter genera `jsonMeta.childOrder` y el layout lo respeta en todas las capas.
394
+
395
+ **¿Por qué una arista curva a veces se ve recta?**
396
+ Si `dx < straightThresholdDx`, se dibuja recta para evitar curvas muy cerradas. Ajusta ese umbral en `settings.layout`.
397
+
398
+ ---
399
+
400
+ ## 🧭 Roadmap / Notas de versión
401
+
402
+ - **0.4.11**
403
+
404
+ - Layout **tidy** determinista (sin solapes, orden estable).
405
+ - Alineación `firstChild` real (padre al centro de la **card** del primer hijo).
406
+ - **Snap** opcional de cadenas (alineación perfecta en columnas).
407
+ - Restauración de `curve/line` con puntos `[start,end]` (el renderer aplica la curva).
408
+ - Mejora en stage dinámico (evita “recortes” en árboles profundos).
409
+
410
+ - **0.4.10**
411
+ - Migración a `SchemaSettings` + `DEFAULT_SETTINGS`.
412
+ - Cards con botón de colapso/expansión opcional.
413
+ - Acento visual por clave booleana.
414
+
415
+ > Cambios _breaking_: si venías de una versión con `options`, migra a `settings` (misma estructura, agrupada por secciones).
416
+
417
+ ---
418
+
419
+ ## 📄 Licencia
420
+
421
+ [MIT](./LICENSE) © 2025 [Miguel Niño (@miguimono)](https://github.com/miguimono)