sapenlinea-components 0.11.94 → 0.12.96

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
@@ -5,6 +5,35 @@ A continuación se documenta **qué hace cada componente** y **cómo se utiliza*
5
5
 
6
6
  > Nota: todos los componentes son _standalone_ de Angular, por lo que se importan directamente en el `imports` del componente donde se usen.
7
7
 
8
+ ### Sistema de iconos
9
+
10
+ La librería unifica dos fuentes de iconos con el **mismo formato de valor** en tabla, `module-card`, `icon-picker` y formularios:
11
+
12
+ | Tipo | Formato | Ejemplo |
13
+ |---|---|---|
14
+ | CSS (`icons.css`) | clase `icon-*` | `icon-edit`, `icon-shield` |
15
+ | Lucide (`lucide-angular`) | prefijo `lucide:` + nombre kebab-case | `lucide:share-2`, `lucide:users` |
16
+
17
+ **Utilidades exportadas** desde `sapenlinea-components` (vía `IconPicker`):
18
+
19
+ ```ts
20
+ import {
21
+ isLucideIconValue,
22
+ parseIconPickerValue,
23
+ resolveIconKind,
24
+ getLucideIconName,
25
+ getCssIconClass,
26
+ CSS_ICON_CATALOG,
27
+ DEFAULT_MODULE_ICONS,
28
+ } from 'sapenlinea-components';
29
+ ```
30
+
31
+ > `iconType: 'lucide'` en `TableAction` / columnas `icon-button` sigue funcionando por retrocompatibilidad, pero está **deprecado**. Usar siempre `lucide:nombre`.
32
+
33
+ **Peer dependency:** `lucide-angular` (requerida para iconos Lucide).
34
+
35
+ Los iconos de **acción** fijos de `module-card` (editar, eliminar, flechas) están en los templates del componente y usan clases de `icons.css`. Los iconos **identificadores** de módulo/submódulo/aplicativo se configuran por datos (`icon` en `ModuleData`).
36
+
8
37
  ### 1. `lib-date-time-filter` (DateTimeFilter)
9
38
 
10
39
  **Qué hace**
@@ -70,6 +99,12 @@ Componente de **selector de fecha o fecha/hora** independiente (sin lista de fil
70
99
 
71
100
  - `dateChange: EventEmitter<Date | null>` – Emite la fecha seleccionada.
72
101
 
102
+ **Comportamiento del calendario:**
103
+
104
+ - El panel se renderiza con `position: fixed` y se adjunta a `document.body` para quedar por encima de modales y contenedores con scroll.
105
+ - Se posiciona preferentemente **hacia abajo** si hay espacio; solo abre hacia arriba cuando no cabe abajo.
106
+ - Se reposiciona automáticamente en scroll y resize.
107
+
73
108
  **Ejemplo de uso:**
74
109
 
75
110
  ```html
@@ -165,7 +200,7 @@ export interface FieldConfig {
165
200
  minDate?: Date;
166
201
  maxDate?: Date;
167
202
  variant?: 'cards'; // Variante visual del checkbox
168
- uppercase?: boolean; // Texto de opciones en mayúsculas. Default: true (solo checkbox)
203
+ uppercase?: boolean; // false desactiva mayúsculas automáticas en text/number/time y en labels de checkbox
169
204
  radioGroup?: string; // Ver sección "Checkbox — comportamientos de selección"
170
205
  atLeastOneGroup?: string;
171
206
  }
@@ -188,18 +223,16 @@ export interface SectionConfig {
188
223
  **Checkbox — subtítulo y mayúsculas:**
189
224
 
190
225
  ```ts
226
+ // Checkbox: uppercase controla las etiquetas de las opciones (default: mayúsculas)
191
227
  {
192
228
  key: 'acepta',
193
229
  type: 'checkbox',
194
- uppercase: false, // opcional, default true
195
- options: [
196
- {
197
- label: 'Evidencia fotográfica',
198
- value: 'foto',
199
- subtitle: 'Adjunta imágenes del incidente' // opcional
200
- }
201
- ]
230
+ uppercase: false,
231
+ options: [{ label: 'Evidencia fotográfica', value: 'foto', subtitle: 'Adjunta imágenes' }]
202
232
  }
233
+
234
+ // Text / number / time: uppercase: false evita convertir el valor a mayúsculas al escribir
235
+ { key: 'placa', type: 'text', uppercase: false, placeholder: 'ABC123' }
203
236
  ```
204
237
 
205
238
  **Checkbox — `disabled` vs `readonly`**
@@ -455,22 +488,13 @@ Componente de **tabla dinámica** con soporte de ordenamiento, acciones por fila
455
488
 
456
489
  **Inputs:**
457
490
 
458
- - `columns: TableColumn[]` (requerido) – Definición de columnas.
459
- - `data: TableRow[]` (requerido) – Datos a mostrar.
460
- - `actions: TableAction[] | ((row: TableRow) => TableAction[])` – Acciones por fila.
461
- - `showActions: boolean = true` – Muestra/oculta columna de acciones.
462
- - `statusToneMap: StatusToneMap` – Mapa de estado normalizado → tono visual (`success`, `error`, `warning`, `info`, `neutral`).
463
- - `statusEnumMap: Record<string, string>` – Traduce el valor crudo del estado (ej. `'ACTIVE'`) a la etiqueta que se muestra (ej. `'Activo'`). La resolución del tono usa la etiqueta traducida.
464
-
465
- **Inputs:**
466
-
467
491
  - `columns: TableColumn[]` (requerido) – Definición de columnas.
468
492
  - `data: TableRow[]` (requerido) – Datos a mostrar.
469
493
  - `actions: TableAction[] | ((row: TableRow) => TableAction[])` – Acciones por fila principal.
470
494
  - `showActions: boolean = true` – Muestra/oculta columna de acciones en filas principales.
471
495
  - `subColumns: SubColumn[]` – Columnas de las sub-filas expandibles.
472
- - `subActions: TableAction[] | ((row: TableRow) => TableAction[])` – Acciones por sub-fila. Se muestran con el mismo menú contextual que las filas principales.
473
- - `showSubActions: boolean = false` – Muestra/oculta la columna de acciones en sub-filas.
496
+ - `subActions: TableAction[] | ((row: TableRow) => TableAction[])` – Acciones por sub-fila.
497
+ - `showSubActions: boolean = false` – Muestra/oculta acciones en sub-filas.
474
498
  - `statusToneMap: StatusToneMap` – Mapa de estado normalizado → tono visual.
475
499
  - `statusEnumMap: Record<string, string>` – Traduce el valor crudo del estado a la etiqueta visible.
476
500
 
@@ -489,25 +513,77 @@ Componente de **tabla dinámica** con soporte de ordenamiento, acciones por fila
489
513
  | `icon-button` | Botón con ícono clicable |
490
514
  | `email` | Email en minúsculas. Aplica `autoWidth` automáticamente. |
491
515
 
492
- **Propiedades de ancho por columna:**
516
+ **Anchos de columna (automático + overrides):**
493
517
 
494
- | Propiedad | Tipo | Default | Descripción |
495
- |---|---|---|---|
496
- | `autoWidth` | `boolean` | `true` para `text`/`email` | Ajusta el ancho al contenido hasta `maxWidth`. Pasar `false` para desactivarlo en tipos `text`/`email`. |
497
- | `maxWidth` | `number` | `200` | Ancho máximo en px cuando `autoWidth` está activo. |
498
- | `compact` | `boolean` | `false` | Comprime la columna al mínimo posible, ajustándose exactamente al contenido sin límite de ancho. Útil para columnas de ícono o estado corto. |
518
+ La tabla calcula `minWidth`, `maxWidth` y perfil (`compact` | `fixed` | `flex`) según el **tipo de columna** y la longitud del **label**. Si el ancho mínimo total supera el contenedor, aparece **scroll horizontal** con barra personalizada (sin colapsar títulos).
519
+
520
+ | Tipo | Perfil por defecto | Comportamiento |
521
+ |---|---|---|
522
+ | `icon-button`, `grade` | `compact` | Ancho mínimo fijo |
523
+ | `status`, `number`, `money`, `date`, … | `fixed` | Rango min/max acotado |
524
+ | `text`, `email` | `flex` | Crece si hay espacio; ellipsis si no |
525
+
526
+ **Overrides manuales** (columnas principales y `subColumns`):
527
+
528
+ | Propiedad | Descripción |
529
+ |---|---|
530
+ | `widthProfile` | `'compact' \| 'fixed' \| 'flex'` — fuerza el perfil |
531
+ | `minWidth` | Ancho mínimo en px |
532
+ | `maxWidth` | Ancho máximo en px |
533
+ | `compact` | Atajo a perfil compacto |
534
+ | `autoWidth: false` | Columna flexible principal (absorbe espacio libre) |
535
+ | `headerMultiline` | Fuerza salto de línea en el título del encabezado (`white-space: pre-line`) |
536
+
537
+ **Encabezados multilínea:** incluye `\n` en `label` (p. ej. `'N°\nCOMPARENDO'`) o define `headerMultiline: true`. La librería aplica el estilo sin `::ng-deep` externo. El ancho mínimo se calcula con la línea más larga del título.
499
538
 
500
539
  ```ts
501
- // Columna que no crece más de 150px
502
- { key: 'placa', label: 'PLACA', type: 'text', maxWidth: 150 }
540
+ { key: 'numero', label: 'N°\nCOMPARENDO', align: 'center' }
541
+ // o explícito:
542
+ { key: 'fecha', label: 'Fecha de\nexpedición', headerMultiline: true }
503
543
 
504
- // Columna de tipo text que NO usa autoWidth (ocupa todo el espacio disponible)
544
+ // Columna principal de descripción
505
545
  { key: 'descripcion', label: 'DESCRIPCIÓN', type: 'text', autoWidth: false }
506
546
 
507
- // Columna compacta: se ajusta exactamente al contenido
508
- { key: 'grado', label: 'GRADO', type: 'grade', compact: true }
547
+ // Subcolumna con la misma lógica automática
548
+ subColumns: SubColumn[] = [
549
+ { key: 'evento', label: 'Evento', type: 'text' },
550
+ { key: 'hora', label: 'Hora', type: 'datetime' },
551
+ ];
552
+
553
+ // Override manual
554
+ { key: 'placa', label: 'PLACA', type: 'text', widthProfile: 'fixed', maxWidth: 120 }
555
+ ```
556
+
557
+ `columnGroups` (anexos/checkbox) mantienen celdas estrechas fijas (~56px) sin configuración adicional.
558
+
559
+ Helpers exportados: `resolveTableColumnLayout`, `getTableMinWidth`, `buildChildGridTemplateColumns`.
560
+
561
+ **Iconos en acciones y columnas `icon-button`:**
562
+
563
+ Usar clases CSS o prefijo `lucide:` (ver sección **Sistema de iconos** al inicio del documento):
564
+
565
+ ```ts
566
+ // Acción del menú contextual
567
+ actions: TableAction[] = [
568
+ { key: 'edit', icon: 'icon-edit', label: 'Editar' },
569
+ { key: 'share', icon: 'lucide:share-2', label: 'Compartir', color: '#006D2F' },
570
+ ];
571
+
572
+ // Columna icon-button con estado alterno
573
+ {
574
+ key: 'download',
575
+ label: 'Documento',
576
+ type: 'icon-button',
577
+ icon: 'icon-file',
578
+ iconAlternate: 'icon-check',
579
+ iconActiveKey: 'downloadComplete',
580
+ iconAlternateColor: '#006D2F',
581
+ disabledKey: 'locked',
582
+ }
509
583
  ```
510
584
 
585
+ Propiedades de columna `icon-button`: `icon`, `iconAlternate`, `iconActiveKey`, `isIconActive`, `disabledKey`, `isIconDisabled`, `iconColor`, `iconAlternateColor`. También aplican en `columnGroups` con `type: 'icon-button'`.
586
+
511
587
  **Outputs:**
512
588
 
513
589
  - `optionSelected: EventEmitter<{ action: string; row: TableRow }>` – Emite la acción seleccionada y la fila principal.
@@ -996,10 +1072,33 @@ Modal de notificación tipo “toast grande” centrado, para mensajes important
996
1072
  ### 25. `lib-footer` (Footer)
997
1073
 
998
1074
  **Qué hace**
999
- Footer reutilizable para modales o pantallas, con información de marca / texto legal.
1075
+ Footer compacto para pantallas o modales. Se ancla al borde inferior del layout flex (no usa `position: fixed`).
1000
1076
 
1001
1077
  **Selector:** `lib-footer`
1002
1078
 
1079
+ **Inputs:**
1080
+
1081
+ | Input | Tipo | Default | Descripción |
1082
+ |---|---|---|---|
1083
+ | `backgroundColor` | `string` | `'#F0F0DB'` | Fondo del footer |
1084
+ | `textColor` | `string` | `'#787861'` | Color del texto |
1085
+ | `highlightColor` | `string` | — | Color de marca/texto destacado |
1086
+ | `year` | `number` | año actual | Año del copyright |
1087
+
1088
+ **Layout recomendado** — importar `footer-layout.css` y colocar el footer **fuera** del área con scroll:
1089
+
1090
+ ```html
1091
+ <div class="footer-layout">
1092
+ <main class="footer-layout__content">...</main>
1093
+ <lib-footer [highlightColor]="'#596300'" />
1094
+ </div>
1095
+ ```
1096
+
1097
+ ```ts
1098
+ // En styles del proyecto o global
1099
+ @import 'sapenlinea-components/lib/components/footer/footer-layout.css';
1100
+ ```
1101
+
1003
1102
  ---
1004
1103
 
1005
1104
  ### 26. `lib-button-cards` (ButtonCards)
@@ -1869,11 +1968,13 @@ Tarjeta resumen de un **rol de usuario**, con nombre, descripción, color identi
1869
1968
  | `description` | `string` | `''` | Descripción breve |
1870
1969
  | `color` | `string` | `'#596300'` | Color de acento (borde / badge) |
1871
1970
  | `userCount` | `number` | `0` | Cantidad de usuarios con ese rol |
1971
+ | `showDelete` | `boolean` | `true` | Muestra botón eliminar (esquina superior derecha) |
1872
1972
 
1873
1973
  **Outputs:**
1874
1974
 
1875
1975
  - `edit: void` – Al pulsar editar rol.
1876
1976
  - `permissions: void` – Al pulsar gestionar permisos.
1977
+ - `delete: void` – Al pulsar eliminar (solo si `showDelete` es `true`).
1877
1978
 
1878
1979
  **Ejemplo de uso:**
1879
1980
 
@@ -1883,10 +1984,14 @@ Tarjeta resumen de un **rol de usuario**, con nombre, descripción, color identi
1883
1984
  [description]="'Acceso completo al sistema'"
1884
1985
  [color]="'#596300'"
1885
1986
  [userCount]="12"
1987
+ [showDelete]="true"
1886
1988
  (edit)="onEditRol()"
1887
- (permissions)="onManagePermissions()" />
1989
+ (permissions)="onManagePermissions()"
1990
+ (delete)="onDeleteRol()" />
1888
1991
  ```
1889
1992
 
1993
+ > Combinar con `lib-color-picker` para elegir el color del rol en formularios de alta/edición.
1994
+
1890
1995
  ---
1891
1996
 
1892
1997
  ### 46. `lib-module-card` y `lib-module-card-list` (ModuleCard / ModuleCardList)
@@ -1921,7 +2026,7 @@ export interface ModuleCardAction {
1921
2026
  export interface ModuleApplicativo {
1922
2027
  id: string | number;
1923
2028
  name: string;
1924
- icon?: string; // SVG inline como string
2029
+ icon?: string; // CSS: icon-* | Lucide: lucide:nombre
1925
2030
  visible: boolean;
1926
2031
  actions: ModuleCardAction[];
1927
2032
  }
@@ -1929,7 +2034,7 @@ export interface ModuleApplicativo {
1929
2034
  export interface ModuleSubmodule {
1930
2035
  id: string | number;
1931
2036
  name: string;
1932
- icon?: string;
2037
+ icon?: string; // CSS: icon-* | Lucide: lucide:nombre
1933
2038
  visible: boolean;
1934
2039
  aplicativos: ModuleApplicativo[];
1935
2040
  }
@@ -1944,7 +2049,7 @@ export interface ModuleChildRef {
1944
2049
  export interface ModuleData {
1945
2050
  id: string | number;
1946
2051
  name: string;
1947
- icon?: string;
2052
+ icon?: string; // CSS: icon-* | Lucide: lucide:nombre
1948
2053
  visible: boolean;
1949
2054
  submodules: ModuleSubmodule[];
1950
2055
  aplicativos: ModuleApplicativo[];
@@ -2008,20 +2113,21 @@ menuModules = signal<ModuleData[]>([
2008
2113
  {
2009
2114
  id: 'mod1',
2010
2115
  name: 'Administración',
2011
- icon: '<svg>...</svg>',
2116
+ icon: 'lucide:shield-check',
2012
2117
  visible: true,
2013
2118
  submodules: [
2014
2119
  {
2015
2120
  id: 'sub1',
2016
2121
  name: 'Usuarios',
2122
+ icon: 'lucide:users',
2017
2123
  visible: true,
2018
2124
  aplicativos: [
2019
- { id: 'app1', name: 'Crear Usuario', visible: true, actions: [{ key: 'bulk', label: 'Creación masiva' }] },
2125
+ { id: 'app1', name: 'Crear Usuario', icon: 'lucide:user-plus', visible: true, actions: [{ key: 'bulk', label: 'Creación masiva' }] },
2020
2126
  ],
2021
2127
  },
2022
2128
  ],
2023
2129
  aplicativos: [
2024
- { id: 'app2', name: 'Panel General', visible: true, actions: [] },
2130
+ { id: 'app2', name: 'Panel General', icon: 'lucide:layout-grid', visible: true, actions: [] },
2025
2131
  ],
2026
2132
  childOrder: [
2027
2133
  { type: 'aplicativo', id: 'app2' }, // aplicativo directo primero
@@ -2041,7 +2147,7 @@ Usar directamente cuando solo se necesita un módulo, o dejar que `lib-module-ca
2041
2147
  |---|---|---|
2042
2148
  | `moduleId` | `string \| number` | Id del módulo |
2043
2149
  | `moduleName` | `string` | Nombre visible |
2044
- | `moduleIcon` | `string` | SVG inline (opcional) |
2150
+ | `moduleIcon` | `string` | Icono del módulo: `icon-*` o `lucide:nombre` |
2045
2151
  | `visible` | `boolean` | Visible en sidebar |
2046
2152
  | `submodules` | `ModuleSubmodule[]` | Submódulos |
2047
2153
  | `aplicativos` | `ModuleApplicativo[]` | Aplicativos directos |
@@ -2151,7 +2257,87 @@ Al reordenar módulos (`lib-module-card-list`) o hijos de un módulo (`lib-modul
2151
2257
 
2152
2258
  1. **Persistencia:** escuchar eventos y/o `childOrderChange` / `modulesChange` para guardar en API.
2153
2259
  2. **Alta de ítems:** al crear submódulo o aplicativo, agregar también su entrada en `childOrder` si ya existe uno definido.
2154
- 3. **Iconos:** pasar SVG como string en `icon`; el componente lo sanitiza con `DomSanitizer`.
2260
+ 3. **Iconos identificadores:** usar `icon-*` o `lucide:nombre` en `ModuleData`; compatible con el valor que devuelve `lib-icon-picker`.
2155
2261
  4. **Separación de responsabilidades:** `visible` controla visibilidad en menú; la autorización real por rol la define el módulo contenedor.
2156
2262
 
2157
2263
  ---
2264
+
2265
+ ### 47. `lib-icon-picker` (IconPicker)
2266
+
2267
+ **Qué hace**
2268
+ Selector visual de iconos para formularios (módulos, menús, etc.). Implementa `ControlValueAccessor`. Soporta iconos CSS (`icons.css`) y Lucide.
2269
+
2270
+ **Selector:** `lib-icon-picker`
2271
+
2272
+ **Valor almacenado:** clase CSS (`icon-layers`) o Lucide (`lucide:file-text`).
2273
+
2274
+ **Inputs:**
2275
+
2276
+ | Input | Tipo | Default | Descripción |
2277
+ |---|---|---|---|
2278
+ | `label` | `string` | `'Icono'` | Etiqueta del campo |
2279
+ | `icons` | `IconPickerOption[]` | `DEFAULT_MODULE_ICONS` | Catálogo a mostrar |
2280
+ | `columns` | `number` | `8` | Columnas de la grilla |
2281
+ | `disabled` / `readonly` | `boolean` | `false` | Bloquea selección |
2282
+
2283
+ **Outputs:** `selectionChange: string`
2284
+
2285
+ **Catálogos exportados:** `CSS_ICON_CATALOG`, `LUCIDE_ICON_CATALOG`, `DEFAULT_ICON_PICKER_ICONS`, `DEFAULT_MODULE_ICONS`
2286
+
2287
+ **Ejemplo:**
2288
+
2289
+ ```html
2290
+ <lib-icon-picker formControlName="icon" label="Icono del módulo" />
2291
+ ```
2292
+
2293
+ ```ts
2294
+ import { DEFAULT_MODULE_ICONS } from 'sapenlinea-components';
2295
+
2296
+ // Catálogo personalizado
2297
+ customIcons = [
2298
+ { value: 'icon-shield', label: 'Seguridad' },
2299
+ { value: 'lucide:puzzle', label: 'Puzzle' },
2300
+ ];
2301
+ ```
2302
+
2303
+ ```html
2304
+ <lib-icon-picker [icons]="customIcons" formControlName="icon" />
2305
+ ```
2306
+
2307
+ ---
2308
+
2309
+ ### 48. `lib-color-picker` (ColorPicker)
2310
+
2311
+ **Qué hace**
2312
+ Selector de color en grilla para roles u otros formularios. Implementa `ControlValueAccessor`.
2313
+
2314
+ **Selector:** `lib-color-picker`
2315
+
2316
+ **Inputs:**
2317
+
2318
+ | Input | Tipo | Default | Descripción |
2319
+ |---|---|---|---|
2320
+ | `label` | `string` | `'Color del rol'` | Etiqueta |
2321
+ | `colors` | `string[]` | `DEFAULT_ROLE_COLORS` | Paleta (25 colores vivos por defecto) |
2322
+ | `columns` | `number` | `10` | Columnas de la grilla |
2323
+ | `disabled` / `readonly` | `boolean` | `false` | Bloquea selección |
2324
+
2325
+ **Outputs:** `selectionChange: string` (hex)
2326
+
2327
+ **Ejemplo:**
2328
+
2329
+ ```html
2330
+ <lib-color-picker formControlName="color" label="Color" />
2331
+ ```
2332
+
2333
+ ```ts
2334
+ import { DEFAULT_ROLE_COLORS } from 'sapenlinea-components';
2335
+
2336
+ readonly brandColors = ['#596300', '#006D2F', '#CF0707', ...];
2337
+ ```
2338
+
2339
+ ```html
2340
+ <lib-color-picker [colors]="brandColors" formControlName="color" />
2341
+ ```
2342
+
2343
+ ---