@jvsoft/components 1.0.0-alpha.6 → 1.0.0-alpha.7

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.
@@ -0,0 +1,268 @@
1
+ # ListaArbolComponent
2
+
3
+ Componente Angular para visualizar datos jerárquicos en estructura de árbol con soporte para selección, checkboxes y menú contextual.
4
+
5
+ ## Instalación
6
+
7
+ ```bash
8
+ npm install @jvsoft/components
9
+ ```
10
+
11
+ ## Uso Básico
12
+
13
+ ```typescript
14
+ import { ListaArbolComponent } from '@jvsoft/components/lista-arbol';
15
+
16
+ @Component({
17
+ selector: 'app-example',
18
+ standalone: true,
19
+ imports: [ListaArbolComponent],
20
+ template: `
21
+ <jvs-lista-arbol
22
+ [listaSuscription]="despachos$"
23
+ nombreColeccion="despachos"
24
+ [idTabla]="['iDespachoId']"
25
+ campoId="iDespachoId"
26
+ campoIdPadre="iDespachoPadreId"
27
+ campoStr="cDespachoNombre"
28
+ (seleccionado)="onSeleccion($event)">
29
+ </jvs-lista-arbol>
30
+ `
31
+ })
32
+ export class ExampleComponent {
33
+ despachos$ = this.service.getDespachos();
34
+
35
+ onSeleccion(item: any) {
36
+ console.log('Item seleccionado:', item);
37
+ }
38
+ }
39
+ ```
40
+
41
+ ## API
42
+
43
+ ### Inputs
44
+
45
+ #### Requeridos
46
+
47
+ | Input | Tipo | Descripción |
48
+ |-------|------|-------------|
49
+ | `listaSuscription` | `Observable<T[]>` | Observable que emite la lista de datos a mostrar |
50
+ | `nombreColeccion` | `string` | Nombre identificador de la colección |
51
+ | `idTabla` | `string \| string[]` | Campo(s) que identifican únicamente cada item |
52
+ | `campoId` | `string` | Nombre del campo que contiene el ID del item |
53
+ | `campoIdPadre` | `string` | Nombre del campo que contiene el ID del padre |
54
+
55
+ #### Opcionales
56
+
57
+ | Input | Tipo | Default | Descripción |
58
+ |-------|------|---------|-------------|
59
+ | `campoStr` | `string` | `''` | Campo a mostrar como texto del nodo |
60
+ | `checkbox` | `boolean` | `false` | Habilita checkboxes con selección cascada |
61
+ | `expandirRecursivo` | `boolean` | `false` | Expande/colapsa recursivamente |
62
+ | `checkboxSeleccionados` | `T[]` | `[]` | Items pre-seleccionados (modo checkbox) |
63
+ | `campoOrden` | `string` | - | Campo para ordenar los nodos |
64
+ | `strHijoContainer` | `string` | `'hijos'` | Nombre de la propiedad que contiene los hijos |
65
+ | `menuContextual` | `object` | - | Objeto con funciones para menú contextual |
66
+ | `classSeleccionado` | `string[]` | `['bg-primary-activo', ...]` | Clases CSS para item seleccionado |
67
+ | `classAnulado` | `string[]` | `['line-through', ...]` | Clases CSS para items anulados |
68
+ | `campoAnulado` | `string \| null` | - | Campo que indica si un item está anulado |
69
+ | `templateTxtData` | `TemplateRef<any>` | - | Template personalizado para el contenido del nodo |
70
+ | `condicionMostrar` | `(item: T) => boolean` | - | Función para filtrar items a mostrar |
71
+ | `condicionesClaseFila` | `(item: T) => string[]` | `() => []` | Función que retorna clases CSS adicionales |
72
+ | `selectionModel` | `any` | - | Modelo de selección (backward compatibility) |
73
+
74
+ ### Outputs
75
+
76
+ | Output | Tipo | Descripción |
77
+ |--------|------|-------------|
78
+ | `seleccionado` | `EventEmitter<any>` | Emite cuando se selecciona un item |
79
+ | `selectionModelChange` | `EventEmitter<any>` | Emite cambios en el modelo de selección |
80
+ | `listaCheck` | `EventEmitter<T[]>` | Emite array de items seleccionados (modo checkbox) |
81
+ | `listaCheckObj` | `EventEmitter<Record<string, any>>` | Emite objeto con items seleccionados (modo checkbox) |
82
+
83
+ ## Ejemplos
84
+
85
+ ### Árbol Simple
86
+
87
+ ```typescript
88
+ <jvs-lista-arbol
89
+ [listaSuscription]="categorias$"
90
+ nombreColeccion="categorias"
91
+ [idTabla]="['iCategoriaId']"
92
+ campoId="iCategoriaId"
93
+ campoIdPadre="iCategoriaPadreId"
94
+ campoStr="cCategoriaNombre">
95
+ </jvs-lista-arbol>
96
+ ```
97
+
98
+ ### Con Checkboxes
99
+
100
+ ```typescript
101
+ <jvs-lista-arbol
102
+ [listaSuscription]="permisos$"
103
+ nombreColeccion="permisos"
104
+ [idTabla]="['iPermisoId']"
105
+ campoId="iPermisoId"
106
+ campoIdPadre="iPermisoPadreId"
107
+ campoStr="cPermisoNombre"
108
+ [checkbox]="true"
109
+ [checkboxSeleccionados]="permisosActuales"
110
+ (listaCheck)="onPermisosChange($event)">
111
+ </jvs-lista-arbol>
112
+ ```
113
+
114
+ ### Con Template Personalizado
115
+
116
+ ```typescript
117
+ <jvs-lista-arbol
118
+ [listaSuscription]="departamentos$"
119
+ nombreColeccion="departamentos"
120
+ [idTabla]="['iDepartamentoId']"
121
+ campoId="iDepartamentoId"
122
+ campoIdPadre="iDepartamentoPadreId"
123
+ [templateTxtData]="customTemplate">
124
+ </jvs-lista-arbol>
125
+
126
+ <ng-template #customTemplate let-data="data">
127
+ <div class="flex items-center gap-2">
128
+ <mat-icon>{{ data.icono }}</mat-icon>
129
+ <span class="font-bold">{{ data.nombre }}</span>
130
+ <span class="text-gray-500">({{ data.codigo }})</span>
131
+ </div>
132
+ </ng-template>
133
+ ```
134
+
135
+ ### Con Menú Contextual
136
+
137
+ ```typescript
138
+ export class MyComponent {
139
+ menuContextual = {
140
+ abrirMenuContextual: (event: MouseEvent, item: any) => {
141
+ // Lógica para abrir menú contextual
142
+ this.menuService.open(event, item);
143
+ },
144
+ cerrarMenuContextual: () => {
145
+ this.menuService.close();
146
+ }
147
+ };
148
+ }
149
+ ```
150
+
151
+ ```html
152
+ <jvs-lista-arbol
153
+ [listaSuscription]="items$"
154
+ nombreColeccion="items"
155
+ [idTabla]="['id']"
156
+ campoId="id"
157
+ campoIdPadre="parentId"
158
+ campoStr="nombre"
159
+ [menuContextual]="menuContextual">
160
+ </jvs-lista-arbol>
161
+ ```
162
+
163
+ ### Con Ordenamiento
164
+
165
+ ```typescript
166
+ <jvs-lista-arbol
167
+ [listaSuscription]="tareas$"
168
+ nombreColeccion="tareas"
169
+ [idTabla]="['iTareaId']"
170
+ campoId="iTareaId"
171
+ campoIdPadre="iTareaPadreId"
172
+ campoStr="cTareaNombre"
173
+ campoOrden="iOrden">
174
+ </jvs-lista-arbol>
175
+ ```
176
+
177
+ ### Con Clases Condicionales
178
+
179
+ ```typescript
180
+ export class MyComponent {
181
+ getClasesFila = (item: any): string[] => {
182
+ const clases: string[] = [];
183
+ if (item.urgente) clases.push('bg-red-100');
184
+ if (item.completado) clases.push('opacity-50');
185
+ return clases;
186
+ };
187
+ }
188
+ ```
189
+
190
+ ```html
191
+ <jvs-lista-arbol
192
+ [listaSuscription]="tareas$"
193
+ nombreColeccion="tareas"
194
+ [idTabla]="['id']"
195
+ campoId="id"
196
+ campoIdPadre="parentId"
197
+ campoStr="nombre"
198
+ [condicionesClaseFila]="getClasesFila">
199
+ </jvs-lista-arbol>
200
+ ```
201
+
202
+ ## Estructura de Datos
203
+
204
+ El componente espera un array plano de objetos con referencias padre-hijo:
205
+
206
+ ```typescript
207
+ interface Item {
208
+ id: number;
209
+ parentId: number | null;
210
+ nombre: string;
211
+ // ... otros campos
212
+ }
213
+
214
+ const items: Item[] = [
215
+ { id: 1, parentId: null, nombre: 'Raíz' },
216
+ { id: 2, parentId: 1, nombre: 'Hijo 1' },
217
+ { id: 3, parentId: 1, nombre: 'Hijo 2' },
218
+ { id: 4, parentId: 2, nombre: 'Nieto 1' },
219
+ ];
220
+ ```
221
+
222
+ El componente automáticamente construye la estructura jerárquica.
223
+
224
+ ## Características
225
+
226
+ - ✅ **Signals API**: Usa la API moderna de Signals de Angular
227
+ - ✅ **Selección simple**: Click para seleccionar items
228
+ - ✅ **Checkboxes con cascada**: Selección padre-hijo automática
229
+ - ✅ **Templates personalizados**: Personaliza la visualización de nodos
230
+ - ✅ **Menú contextual**: Soporte integrado para menús contextuales
231
+ - ✅ **Ordenamiento**: Ordena nodos por campo específico
232
+ - ✅ **Clases condicionales**: Aplica estilos dinámicos
233
+ - ✅ **Cleanup automático**: Gestión automática de suscripciones
234
+ - ✅ **TypeScript**: Totalmente tipado con genéricos
235
+
236
+ ## Migración desde Versión Anterior
237
+
238
+ Si estás migrando desde una versión anterior con `@Input()` decorators:
239
+
240
+ ### Antes (v1.0.0-alpha.5)
241
+ ```typescript
242
+ @Input() listaSuscription!: Observable<T[]>;
243
+ @Input() nombreColeccion!: string;
244
+ ```
245
+
246
+ ### Después (v1.0.0-alpha.6+)
247
+ ```typescript
248
+ // El uso en templates permanece igual
249
+ <jvs-lista-arbol
250
+ [listaSuscription]="items$"
251
+ nombreColeccion="items"
252
+ ...>
253
+ </jvs-lista-arbol>
254
+ ```
255
+
256
+ **No se requieren cambios** en el código que usa el componente. La API pública permanece 100% compatible.
257
+
258
+ ## Notas Técnicas
259
+
260
+ - El componente usa `NestedTreeControl` de Angular CDK
261
+ - Las suscripciones se limpian automáticamente con `takeUntilDestroyed()`
262
+ - Los valores derivados usan `computed()` para optimización
263
+ - El estado interno usa `signal()` para reactividad
264
+ - Compatible con Angular 16+
265
+
266
+ ## Licencia
267
+
268
+ MIT
@@ -1,47 +1,46 @@
1
- import { EventEmitter, OnInit, TemplateRef } from '@angular/core';
1
+ import { TemplateRef } from '@angular/core';
2
2
  import { Observable } from 'rxjs';
3
3
  import { NestedTreeControl } from '@angular/cdk/tree';
4
4
  import { DataModel } from '@jvsoft/utils';
5
- import { SelectionModel } from '@angular/cdk/collections';
6
5
  import * as i0 from "@angular/core";
7
- export declare class ListaArbolComponent<T = any> implements OnInit {
8
- listaSuscription: Observable<T[]>;
9
- nombreColeccion: string;
10
- checkbox: boolean;
11
- expandirRecursivo: boolean;
12
- checkboxSeleccionados: T[];
13
- campoId: string;
14
- campoIdPadre: string;
15
- campoStr: string;
16
- campoOrden?: string;
17
- strHijoContainer: string;
18
- menuContextual?: {
6
+ export declare class ListaArbolComponent<T = any> {
7
+ listaSuscription: import("@angular/core").InputSignal<Observable<T[]>>;
8
+ nombreColeccion: import("@angular/core").InputSignal<string>;
9
+ checkbox: import("@angular/core").InputSignal<boolean>;
10
+ expandirRecursivo: import("@angular/core").InputSignal<boolean>;
11
+ checkboxSeleccionados: import("@angular/core").InputSignal<T[]>;
12
+ campoId: import("@angular/core").InputSignal<string>;
13
+ campoIdPadre: import("@angular/core").InputSignal<string>;
14
+ campoStr: import("@angular/core").InputSignal<string>;
15
+ campoOrden: import("@angular/core").InputSignal<string | undefined>;
16
+ strHijoContainer: import("@angular/core").InputSignal<string>;
17
+ menuContextual: import("@angular/core").InputSignal<{
19
18
  abrirMenuContextual: Function;
20
19
  cerrarMenuContextual: Function;
21
- };
22
- classSeleccionado: string[];
23
- classAnulado: string[];
24
- campoAnulado?: string | null;
25
- templateTxtData?: TemplateRef<any>;
26
- condicionMostrar?: (item: T) => boolean;
27
- condicionesClaseFila: (item: T) => string[];
28
- set selectionModel(val: SelectionModel<any>);
29
- selectionModelChange: EventEmitter<SelectionModel<any>>;
30
- seleccionado: EventEmitter<any>;
31
- listaCheck: EventEmitter<T[]>;
32
- listaCheckObj: EventEmitter<Record<string, any>>;
20
+ } | undefined>;
21
+ classSeleccionado: import("@angular/core").InputSignal<string[]>;
22
+ classAnulado: import("@angular/core").InputSignal<string[]>;
23
+ campoAnulado: import("@angular/core").InputSignal<string | null | undefined>;
24
+ templateTxtData: import("@angular/core").InputSignal<TemplateRef<any> | undefined>;
25
+ condicionMostrar: import("@angular/core").InputSignal<((item: T) => boolean) | undefined>;
26
+ condicionesClaseFila: import("@angular/core").InputSignal<(item: T) => string[]>;
27
+ idTabla: import("@angular/core").InputSignalWithTransform<string | string[], string | string[]>;
28
+ selectionModel: import("@angular/core").InputSignal<any>;
29
+ selectionModelChange: import("@angular/core").OutputEmitterRef<any>;
30
+ seleccionado: import("@angular/core").OutputEmitterRef<any>;
31
+ listaCheck: import("@angular/core").OutputEmitterRef<T[]>;
32
+ listaCheckObj: import("@angular/core").OutputEmitterRef<Record<string, any>>;
33
33
  private _idTabla;
34
- get idTabla(): string[];
35
- set idTabla(val: string[] | string);
36
- get buscarItemSeleccionado(): any;
37
- get objSeleccionado(): any;
38
- set objSeleccionado(val: any);
34
+ private _seleccionado;
35
+ listaMuestraArbol: import("@angular/core").WritableSignal<T[]>;
36
+ listaOriginal: import("@angular/core").WritableSignal<T[]>;
39
37
  chkLista: DataModel;
40
- listaMuestraArbol: T[];
41
- listaOriginal: T[];
38
+ idTablaValue: import("@angular/core").Signal<string[]>;
39
+ buscarItemSeleccionado: import("@angular/core").Signal<NonNullable<T> | null>;
40
+ objSeleccionado: import("@angular/core").Signal<NonNullable<T> | null>;
42
41
  treeControl: NestedTreeControl<any, any>;
43
- _seleccionado: any;
44
- ngOnInit(): void;
42
+ private listaSuscription$;
43
+ constructor();
45
44
  idTablaValor(data: any): string;
46
45
  propiedadSeleccion(item: any): string;
47
46
  esSeleccionActual(item: any): boolean;
@@ -52,7 +51,7 @@ export declare class ListaArbolComponent<T = any> implements OnInit {
52
51
  cambiarPadre(checkActual: T, estado: boolean): void;
53
52
  cambiarCheck(checkActual: T, estado: boolean): void;
54
53
  hijosActivos(tipo: 'checked' | 'indeterminate', checkActual: T): boolean;
55
- classFila(item: any): any[];
54
+ classFila(item: any): string[];
56
55
  static ɵfac: i0.ɵɵFactoryDeclaration<ListaArbolComponent<any>, never>;
57
- static ɵcmp: i0.ɵɵComponentDeclaration<ListaArbolComponent<any>, "jvs-lista-arbol", never, { "listaSuscription": { "alias": "listaSuscription"; "required": false; }; "nombreColeccion": { "alias": "nombreColeccion"; "required": false; }; "checkbox": { "alias": "checkbox"; "required": false; }; "expandirRecursivo": { "alias": "expandirRecursivo"; "required": false; }; "checkboxSeleccionados": { "alias": "checkboxSeleccionados"; "required": false; }; "campoId": { "alias": "campoId"; "required": false; }; "campoIdPadre": { "alias": "campoIdPadre"; "required": false; }; "campoStr": { "alias": "campoStr"; "required": false; }; "campoOrden": { "alias": "campoOrden"; "required": false; }; "strHijoContainer": { "alias": "strHijoContainer"; "required": false; }; "menuContextual": { "alias": "menuContextual"; "required": false; }; "classSeleccionado": { "alias": "classSeleccionado"; "required": false; }; "classAnulado": { "alias": "classAnulado"; "required": false; }; "campoAnulado": { "alias": "campoAnulado"; "required": false; }; "templateTxtData": { "alias": "templateTxtData"; "required": false; }; "condicionMostrar": { "alias": "condicionMostrar"; "required": false; }; "condicionesClaseFila": { "alias": "condicionesClaseFila"; "required": false; }; "selectionModel": { "alias": "selectionModel"; "required": false; }; "idTabla": { "alias": "idTabla"; "required": true; }; }, { "selectionModelChange": "selectionModelChange"; "seleccionado": "seleccionado"; "listaCheck": "listaCheck"; "listaCheckObj": "listaCheckObj"; }, never, never, true, never>;
56
+ static ɵcmp: i0.ɵɵComponentDeclaration<ListaArbolComponent<any>, "jvs-lista-arbol", never, { "listaSuscription": { "alias": "listaSuscription"; "required": true; "isSignal": true; }; "nombreColeccion": { "alias": "nombreColeccion"; "required": true; "isSignal": true; }; "checkbox": { "alias": "checkbox"; "required": false; "isSignal": true; }; "expandirRecursivo": { "alias": "expandirRecursivo"; "required": false; "isSignal": true; }; "checkboxSeleccionados": { "alias": "checkboxSeleccionados"; "required": false; "isSignal": true; }; "campoId": { "alias": "campoId"; "required": true; "isSignal": true; }; "campoIdPadre": { "alias": "campoIdPadre"; "required": true; "isSignal": true; }; "campoStr": { "alias": "campoStr"; "required": false; "isSignal": true; }; "campoOrden": { "alias": "campoOrden"; "required": false; "isSignal": true; }; "strHijoContainer": { "alias": "strHijoContainer"; "required": false; "isSignal": true; }; "menuContextual": { "alias": "menuContextual"; "required": false; "isSignal": true; }; "classSeleccionado": { "alias": "classSeleccionado"; "required": false; "isSignal": true; }; "classAnulado": { "alias": "classAnulado"; "required": false; "isSignal": true; }; "campoAnulado": { "alias": "campoAnulado"; "required": false; "isSignal": true; }; "templateTxtData": { "alias": "templateTxtData"; "required": false; "isSignal": true; }; "condicionMostrar": { "alias": "condicionMostrar"; "required": false; "isSignal": true; }; "condicionesClaseFila": { "alias": "condicionesClaseFila"; "required": false; "isSignal": true; }; "idTabla": { "alias": "idTabla"; "required": true; "isSignal": true; }; "selectionModel": { "alias": "selectionModel"; "required": false; "isSignal": true; }; }, { "selectionModelChange": "selectionModelChange"; "seleccionado": "seleccionado"; "listaCheck": "listaCheck"; "listaCheckObj": "listaCheckObj"; }, never, never, true, never>;
58
57
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jvsoft/components",
3
- "version": "1.0.0-alpha.6",
3
+ "version": "1.0.0-alpha.7",
4
4
  "description": "JVSOFT Angular Components",
5
5
  "publishConfig": {
6
6
  "tag": "alpha",
@@ -11,7 +11,7 @@
11
11
  "@angular/core": ">=16.0.0",
12
12
  "@angular/forms": ">=16.0.0",
13
13
  "@angular/material": ">=16.0.0",
14
- "@jvsoft/utils": "^1.0.0-alpha.6",
14
+ "@jvsoft/utils": "^1.0.0-alpha.7",
15
15
  "@ngneat/until-destroy": "^10.0.0",
16
16
  "moment": "^2.30.1",
17
17
  "shorthash2": "^1.0.5",
@@ -33,22 +33,22 @@
33
33
  "types": "./dialog-flotante/index.d.ts",
34
34
  "default": "./fesm2022/jvsoft-components-dialog-flotante.mjs"
35
35
  },
36
- "./lista-arbol": {
37
- "types": "./lista-arbol/index.d.ts",
38
- "default": "./fesm2022/jvsoft-components-lista-arbol.mjs"
39
- },
40
36
  "./luces-navidad": {
41
37
  "types": "./luces-navidad/index.d.ts",
42
38
  "default": "./fesm2022/jvsoft-components-luces-navidad.mjs"
43
39
  },
44
- "./menu": {
45
- "types": "./menu/index.d.ts",
46
- "default": "./fesm2022/jvsoft-components-menu.mjs"
40
+ "./lista-arbol": {
41
+ "types": "./lista-arbol/index.d.ts",
42
+ "default": "./fesm2022/jvsoft-components-lista-arbol.mjs"
47
43
  },
48
44
  "./mat-suffix-search-button": {
49
45
  "types": "./mat-suffix-search-button/index.d.ts",
50
46
  "default": "./fesm2022/jvsoft-components-mat-suffix-search-button.mjs"
51
47
  },
48
+ "./menu": {
49
+ "types": "./menu/index.d.ts",
50
+ "default": "./fesm2022/jvsoft-components-menu.mjs"
51
+ },
52
52
  "./tabla-mantenimiento": {
53
53
  "types": "./tabla-mantenimiento/index.d.ts",
54
54
  "default": "./fesm2022/jvsoft-components-tabla-mantenimiento.mjs"
@@ -15,16 +15,6 @@ export interface ColumnaTablaReporte {
15
15
  total?: TipoValorFuncion<any>;
16
16
  };
17
17
  }
18
- export interface IconsIcon {
19
- icon: string;
20
- iconSize?: string;
21
- toolTip?: string;
22
- toolTipField?: string;
23
- contentCss?: string;
24
- cssClass?: string;
25
- click?(row: any): void;
26
- esVisible?(row: any): boolean;
27
- }
28
18
  export type TiposColumnasNuevo = 'text' | 'checkbox' | 'money' | 'icon' | 'icons' | 'progress' | 'expandir' | 'date' | 'number';
29
19
  type PropiedadString<T> = Extract<keyof T, string>;
30
20
  interface BaseColumna<T> {