@caribeng/ui 22.2.0 → 22.4.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 (121) hide show
  1. package/README.md +93 -11
  2. package/fesm2022/caribeng-ui-accordion.mjs +2 -2
  3. package/fesm2022/caribeng-ui-avatar.mjs +2 -2
  4. package/fesm2022/caribeng-ui-badge.mjs +8 -2
  5. package/fesm2022/caribeng-ui-breadcrumb.mjs +13 -6
  6. package/fesm2022/caribeng-ui-chart.mjs +8 -5
  7. package/fesm2022/caribeng-ui-chat.mjs +1303 -0
  8. package/fesm2022/caribeng-ui-code-block.mjs +332 -0
  9. package/fesm2022/caribeng-ui-colorpicker.mjs +99 -5
  10. package/fesm2022/caribeng-ui-command-palette.mjs +476 -0
  11. package/fesm2022/caribeng-ui-confirmdialog.mjs +78 -13
  12. package/fesm2022/caribeng-ui-confirmpopup.mjs +91 -16
  13. package/fesm2022/caribeng-ui-contextmenu.mjs +8 -4
  14. package/fesm2022/caribeng-ui-copy-button.mjs +230 -0
  15. package/fesm2022/caribeng-ui-dataview.mjs +4 -1
  16. package/fesm2022/caribeng-ui-dock.mjs +52 -12
  17. package/fesm2022/caribeng-ui-drawer.mjs +9 -4
  18. package/fesm2022/caribeng-ui-editor.mjs +21 -0
  19. package/fesm2022/caribeng-ui-fieldset.mjs +2 -2
  20. package/fesm2022/caribeng-ui-file-card.mjs +134 -17
  21. package/fesm2022/caribeng-ui-folder-card.mjs +122 -12
  22. package/fesm2022/caribeng-ui-inlinemessage.mjs +2 -2
  23. package/fesm2022/caribeng-ui-inputmask.mjs +8 -2
  24. package/fesm2022/caribeng-ui-kanban.mjs +4612 -0
  25. package/fesm2022/caribeng-ui-kbd.mjs +122 -0
  26. package/fesm2022/caribeng-ui-megamenu.mjs +13 -9
  27. package/fesm2022/caribeng-ui-mentions.mjs +495 -0
  28. package/fesm2022/caribeng-ui-menu.mjs +8 -4
  29. package/fesm2022/caribeng-ui-menubar.mjs +9 -5
  30. package/fesm2022/caribeng-ui-message.mjs +6 -3
  31. package/fesm2022/caribeng-ui-organizationchart.mjs +6 -10
  32. package/fesm2022/caribeng-ui-panel.mjs +6 -6
  33. package/fesm2022/caribeng-ui-panelmenu.mjs +9 -6
  34. package/fesm2022/caribeng-ui-phone-input.mjs +688 -0
  35. package/fesm2022/caribeng-ui-pricing.mjs +263 -69
  36. package/fesm2022/caribeng-ui-qrcode.mjs +564 -0
  37. package/fesm2022/caribeng-ui-ripple.mjs +2 -0
  38. package/fesm2022/caribeng-ui-scrolltop.mjs +3 -1
  39. package/fesm2022/caribeng-ui-sidebar.mjs +573 -0
  40. package/fesm2022/caribeng-ui-stepper.mjs +10 -3
  41. package/fesm2022/caribeng-ui-table.mjs +10 -4
  42. package/fesm2022/caribeng-ui-tabmenu.mjs +8 -4
  43. package/fesm2022/caribeng-ui-tieredmenu.mjs +109 -19
  44. package/fesm2022/caribeng-ui.mjs +388 -1
  45. package/package.json +42 -2
  46. package/schematics/collection.json +5 -0
  47. package/schematics/migrate-from-primeng/core-icons.json +1 -0
  48. package/schematics/migrate-from-primeng/icons.js +269 -0
  49. package/schematics/migrate-from-primeng/index.js +290 -0
  50. package/schematics/migrate-from-primeng/mappings/button.js +93 -0
  51. package/schematics/migrate-from-primeng/mappings/card.js +33 -0
  52. package/schematics/migrate-from-primeng/mappings/checkbox.js +44 -0
  53. package/schematics/migrate-from-primeng/mappings/confirm.js +221 -0
  54. package/schematics/migrate-from-primeng/mappings/datepicker.js +121 -0
  55. package/schematics/migrate-from-primeng/mappings/dialog.js +84 -0
  56. package/schematics/migrate-from-primeng/mappings/index.js +88 -0
  57. package/schematics/migrate-from-primeng/mappings/inputnumber.js +62 -0
  58. package/schematics/migrate-from-primeng/mappings/inputtext.js +60 -0
  59. package/schematics/migrate-from-primeng/mappings/menu.js +468 -0
  60. package/schematics/migrate-from-primeng/mappings/multiselect.js +49 -0
  61. package/schematics/migrate-from-primeng/mappings/radiobutton.js +22 -0
  62. package/schematics/migrate-from-primeng/mappings/rules.js +206 -0
  63. package/schematics/migrate-from-primeng/mappings/select.js +97 -0
  64. package/schematics/migrate-from-primeng/mappings/service-calls.js +100 -0
  65. package/schematics/migrate-from-primeng/mappings/table.js +613 -0
  66. package/schematics/migrate-from-primeng/mappings/tabs.js +225 -0
  67. package/schematics/migrate-from-primeng/mappings/tag.js +29 -0
  68. package/schematics/migrate-from-primeng/mappings/textarea.js +44 -0
  69. package/schematics/migrate-from-primeng/mappings/toast.js +173 -0
  70. package/schematics/migrate-from-primeng/mappings/toggleswitch.js +37 -0
  71. package/schematics/migrate-from-primeng/mappings/tooltip.js +44 -0
  72. package/schematics/migrate-from-primeng/mappings/types.js +10 -0
  73. package/schematics/migrate-from-primeng/report.js +79 -0
  74. package/schematics/migrate-from-primeng/schema.js +2 -0
  75. package/schematics/migrate-from-primeng/schema.json +34 -0
  76. package/schematics/migrate-from-primeng/template.js +387 -0
  77. package/schematics/migrate-from-primeng/todo.js +5 -0
  78. package/schematics/migrate-from-primeng/typescript.js +442 -0
  79. package/schematics/migrate-from-primeng/workspace.js +117 -0
  80. package/schematics/ng-add/index.js +16 -1
  81. package/types/caribeng-ui-badge.d.ts +3 -1
  82. package/types/caribeng-ui-breadcrumb.d.ts +8 -0
  83. package/types/caribeng-ui-chart.d.ts +3 -1
  84. package/types/caribeng-ui-chat.d.ts +299 -0
  85. package/types/caribeng-ui-code-block.d.ts +65 -0
  86. package/types/caribeng-ui-colorpicker.d.ts +5 -1
  87. package/types/caribeng-ui-command-palette.d.ts +133 -0
  88. package/types/caribeng-ui-confirmdialog.d.ts +34 -2
  89. package/types/caribeng-ui-confirmpopup.d.ts +39 -2
  90. package/types/caribeng-ui-contextmenu.d.ts +7 -0
  91. package/types/caribeng-ui-copy-button.d.ts +90 -0
  92. package/types/caribeng-ui-dataview.d.ts +2 -0
  93. package/types/caribeng-ui-dock.d.ts +13 -1
  94. package/types/caribeng-ui-drawer.d.ts +3 -1
  95. package/types/caribeng-ui-file-card.d.ts +27 -2
  96. package/types/caribeng-ui-folder-card.d.ts +27 -5
  97. package/types/caribeng-ui-inputmask.d.ts +3 -1
  98. package/types/caribeng-ui-kanban.d.ts +783 -0
  99. package/types/caribeng-ui-kbd.d.ts +36 -0
  100. package/types/caribeng-ui-megamenu.d.ts +11 -0
  101. package/types/caribeng-ui-mentions.d.ts +127 -0
  102. package/types/caribeng-ui-menu.d.ts +7 -0
  103. package/types/caribeng-ui-menubar.d.ts +7 -0
  104. package/types/caribeng-ui-message.d.ts +2 -0
  105. package/types/caribeng-ui-panelmenu.d.ts +7 -0
  106. package/types/caribeng-ui-phone-input.d.ts +168 -0
  107. package/types/caribeng-ui-pricing.d.ts +41 -4
  108. package/types/caribeng-ui-qrcode.d.ts +88 -0
  109. package/types/caribeng-ui-sidebar.d.ts +167 -0
  110. package/types/caribeng-ui-stepper.d.ts +2 -0
  111. package/types/caribeng-ui-table.d.ts +1 -1
  112. package/types/caribeng-ui-tabmenu.d.ts +7 -0
  113. package/types/caribeng-ui-tieredmenu.d.ts +17 -1
  114. package/types/caribeng-ui.d.ts +276 -2
  115. package/styles/caribeng.css +0 -380
  116. package/styles/scss/_mixins.scss +0 -46
  117. package/styles/scss/_overlay.scss +0 -15
  118. package/styles/scss/_tokens.scss +0 -267
  119. package/styles/scss/_utilities.scss +0 -73
  120. package/styles/scss/_variables.scss +0 -33
  121. package/styles/scss/index.scss +0 -16
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
 
8
8
  <p align="center">
9
9
  <img alt="Angular 22" src="https://img.shields.io/badge/Angular-22-dd0031.svg?style=flat-square" />
10
- <img alt="Entry points" src="https://img.shields.io/badge/entry%20points-142-10b981.svg?style=flat-square" />
10
+ <img alt="Entry points" src="https://img.shields.io/badge/entry%20points-162-10b981.svg?style=flat-square" />
11
11
  <img alt="Licencia MIT" src="https://img.shields.io/badge/licencia-MIT-blue.svg?style=flat-square" />
12
12
  </p>
13
13
 
@@ -15,13 +15,14 @@
15
15
 
16
16
  ## ✨ Características
17
17
 
18
- - **152 componentes y directivas** standalone, cada uno en su propio entry point (`@caribeng/ui/button`, `@caribeng/ui/table`…): solo empaquetas lo que importas.
18
+ - **162 componentes y directivas** standalone, cada uno en su propio entry point (`@caribeng/ui/button`, `@caribeng/ui/table`…): solo empaquetas lo que importas.
19
19
  - **Angular moderno:** `input()` / `model()` / `output()` con Signals, `ChangeDetectionStrategy.OnPush` y compatible con apps zoneless.
20
20
  - **Formularios sin fricción:** `ControlValueAccessor` en todos los controles; el error y el indicador de obligatorio se sincronizan solos con Angular Forms.
21
21
  - **Accesibles:** roles y atributos ARIA, soporte de teclado y gestión de foco (WCAG 2.2 AA).
22
22
  - **Tematizables:** tokens CSS `--cb-*`, modo claro/oscuro y presets de color que se cambian en caliente.
23
23
  - **Caribe Design:** 45 componentes adicionales del sistema Caribe (`@caribeng/ui/caribe/*`).
24
- - **Iconos incluidos:** 361 iconos núcleo listos para usar y un catálogo opcional de 1891 iconos.
24
+ - **Iconos incluidos:** 363 iconos núcleo listos para usar y un catálogo opcional de 1891 iconos.
25
+ - **Migración desde PrimeNG:** un schematic traduce imports, plantillas e iconos y deja un informe con lo pendiente ([ver más](#migrar-desde-primeng)).
25
26
 
26
27
  ## 🖥 Compatibilidad
27
28
 
@@ -71,6 +72,87 @@ yarn add @caribeng/ui
71
72
  pnpm add @caribeng/ui
72
73
  ```
73
74
 
75
+ ### Migrar desde PrimeNG
76
+
77
+ En un proyecto con PrimeNG 18 o superior, instala CaribeNG y ejecuta el migrador:
78
+
79
+ ```bash
80
+ npm install @caribeng/ui@22
81
+ ng generate @caribeng/ui:migrate-from-primeng
82
+ ```
83
+
84
+ > Haz commit (o una copia) antes de ejecutarlo para poder revisar los cambios con `git diff`. Con `--dry-run` ves qué archivos cambiaría sin escribir nada. Si ejecutas `ng add @caribeng/ui` en un proyecto con PrimeNG, te recordará este comando.
85
+
86
+ ```html
87
+ <!-- Antes -->
88
+ <p-button label="Guardar" icon="pi pi-check" [outlined]="true" (onClick)="guardar()" />
89
+ <input pInputText id="email" [(ngModel)]="email" />
90
+ <p-dialog header="Editar" [(visible)]="abierto" [modal]="true" [style]="{ width: '25rem' }">…</p-dialog>
91
+
92
+ <!-- Después -->
93
+ <cb-button variant="outlined" (click)="guardar()"><cb-icon name="check" />Guardar</cb-button>
94
+ <cb-input inputId="email" [(ngModel)]="email" />
95
+ <cb-dialog header="Editar" [(visible)]="abierto" width="25rem">…</cb-dialog>
96
+ ```
97
+
98
+ Qué hace:
99
+
100
+ - cambia los imports de PrimeNG por los de CaribeNG (`ButtonModule` → `CbButton`, `providePrimeNG()` → `provideCaribeNG()`) y añade a cada componente los que usa su plantilla,
101
+ - traduce las plantillas con sus inputs y eventos (`[outlined]` → `variant`, `size="small"` → `size="sm"`, `(onClick)` → `(click)`, `subheader` → `subtitle`…), incluido el pie de Dialog y Card (`#footer` → `<div cb-dialog-footer>` / `<div cb-card-footer>`),
102
+ - cambia `MessageService` por `CbToastService`: traduce los mensajes (`severity: 'error'` → `'danger'`, `'warn'` → `'warning'`, `sticky: true` → `life: 0`) y lo quita de los `providers`, porque `CbToastService` es único para toda la app,
103
+ - convierte los iconos `pi pi-*` en `<cb-icon>` y registra con `provideCbIcons` los que no están en el set núcleo,
104
+ - quita PrimeIcons de `angular.json` y de las hojas de estilo, y añade `@caribeng/ui/styles.css`,
105
+ - quita `primeng`, `primeicons` y `@primeuix/*` de `package.json` **solo cuando ya no queda ninguna referencia**; mientras tanto las dos librerías conviven.
106
+
107
+ Lo que no puede traducir con seguridad (un input sin equivalente, una plantilla `#header` personalizada, un `pButton` sobre un enlace…) lo marca con un comentario junto al código, conservando el atributo original:
108
+
109
+ ```html
110
+ <!-- TODO(caribeng): [rounded]="true" — cb-button no tiene variante redondeada; usa una clase propia o <cb-icon-button>. -->
111
+ <cb-button severity="danger">Borrar</cb-button>
112
+ ```
113
+
114
+ Todos los avisos, las referencias a PrimeNG que aún quedan (por ejemplo componentes que el migrador no traduce todavía) y los imports que haya que añadir a mano se resumen en `caribeng-migration-report.md`, con archivo y línea.
115
+
116
+ **Componentes que traduce hoy:**
117
+
118
+ | PrimeNG | CaribeNG |
119
+ |---|---|
120
+ | `<p-button>`, `<button pButton>` | `<cb-button>` |
121
+ | `<input pInputText>` | `<cb-input>` |
122
+ | `<textarea pTextarea>`, `<textarea pInputTextarea>` | `<cb-textarea>` |
123
+ | `<p-inputnumber>` | `<cb-input-number>` |
124
+ | `<p-select>`, `<p-dropdown>` | `<cb-select>` |
125
+ | `<p-multiselect>` | `<cb-multiselect>` |
126
+ | `<p-toggleswitch>`, `<p-inputSwitch>` | `<cb-toggle>` |
127
+ | `<p-datepicker>`, `<p-calendar>` | `<cb-datepicker>` |
128
+ | `<p-dialog>` | `<cb-dialog>` |
129
+ | `<p-confirmdialog>` + `ConfirmationService` | `<cb-confirm-dialog service>` + `CbConfirmationService` |
130
+ | `<p-confirmpopup>` | `<cb-confirm-popup service>` |
131
+ | `<p-menu>`, `<p-menubar>`, `<p-tieredmenu>`, `<p-panelmenu>`, `<p-breadcrumb>`, `<p-tabmenu>` | `<cb-menu>`, `<cb-menubar>`, `<cb-tieredmenu>`, `<cb-panelmenu>`, `<cb-breadcrumb>`, `<cb-tabmenu>` |
132
+ | `MenuItem[]` (`icon`, `command`, `routerLink`…) | el tipo de item del menú que lo recibe (`CbMenubarItem[]`…) |
133
+ | `<p-splitbutton>` | `<cb-split-button>` |
134
+ | `<p-table>` (columnas escritas en `#header`/`#body` o dinámicas con `@for`) | `<cb-table [columns]>` + un `cbTableCell` por celda personalizada |
135
+ | `<p-tabs>` (y `<p-tabView>`) | `<cb-tabs>` + `<cb-tab label="…">` |
136
+ | `<p-accordion>` (y `<p-accordionTab>`) | `<cb-accordion>` + `<cb-accordion-panel header="…">` |
137
+ | `<p-tag>` | `<cb-tag>` |
138
+ | `<p-toast>` + `MessageService` | `<cb-toast>` + `CbToastService` |
139
+ | `<p-card>` | `<cb-card>` |
140
+ | `pTooltip` (en cualquier elemento) | `cbTooltip` |
141
+ | `<p-checkbox>` | `<cb-checkbox>` |
142
+ | `<i class="pi pi-*">` | `<cb-icon>` |
143
+
144
+ En los menús, los arrays de `MenuItem` cambian de tipo según el menú que los recibe en la plantilla; `icon: 'pi pi-plus'` pasa a `'plus'`, `command` y `visible` se conservan, y `routerLink: '/x'` pasa a `url` (los `routerLink` con parámetros quedan con aviso). En las tablas, cada `<th>`/`<td>` pasa a una columna de `[columns]` (`pSortableColumn` → `sortable`, `<p-columnFilter>` → `filter`, `pFrozenColumn` → `frozen`); las celdas que no son solo `{{ fila.campo }}` pasan a un `<ng-template cbTableCell>` con su contenido migrado, y las columnas de checkbox y de expandir desaparecen porque `cb-table` las pone sola. Con columnas dinámicas (`[columns]="cols"` + `@for`), `cols` pasa directamente a `[columns]`: el migrador le añade `sortable: true` / `filter: true` si la cabecera los aplicaba a todas, y las ramas `@if (col.field === '…')` o `@switch (col.field)` del cuerpo pasan a un `cbTableCell` por campo. Las tablas con celdas de lógica genérica, varias filas por registro o plantillas sin equivalente se dejan con aviso y conservan `TableModule`, para que sigan compilando. Las pestañas y los acordeones de PrimeNG 18+ se reestructuran (la cabecera pasa a `label`/`header`); si se generan con `@for` o la cabecera lleva iconos, quedan con aviso.
145
+
146
+ Los radios (`<p-radiobutton>`) y los menús contextuales (`<p-contextmenu>`) se marcan con un aviso pero no se convierten, porque cambia la estructura de la página: en CaribeNG el modelo de los radios va en un `<cb-radio-group>` que envuelve las opciones, y `<cb-contextmenu>` envuelve el área sobre la que se abre en vez de apuntar a ella con `[target]`. El resto de componentes aparece en el informe para migrarlo a mano; se irán añadiendo en próximas versiones.
147
+
148
+ | Opción | Uso |
149
+ |---|---|
150
+ | `--path=src/app/ventas` | Migrar solo una carpeta (migración por partes). |
151
+ | `--remove-primeng=false` | No tocar las dependencias de `package.json`. |
152
+ | `--report=false` | No escribir el informe. |
153
+ | `--skip-install` | No instalar dependencias al terminar. |
154
+ | `--force` | Migrar aunque falte `@caribeng/ui` o la versión de PrimeNG sea anterior a la 18. |
155
+
74
156
  ## 🔨 Uso
75
157
 
76
158
  > Con `ng add @caribeng/ui` los pasos 1 y 2 ya están hechos.
@@ -192,7 +274,7 @@ export class Header {
192
274
 
193
275
  ## 🔣 Iconos
194
276
 
195
- Los 361 iconos núcleo se registran solos:
277
+ Los 363 iconos núcleo se registran solos:
196
278
 
197
279
  ```html
198
280
  <cb-icon name="house" [size]="20" />
@@ -209,15 +291,15 @@ providers: [provideCbIcons({ 'heart-pulse': heartPulse, rocket })];
209
291
 
210
292
  ## 📚 Catálogo
211
293
 
212
- - **Form:** `autocomplete`, `button`, `cascadeselect`, `checkbox`, `chips`, `colorpicker`, `datepicker`, `editor`, `fileupload`, `floatlabel`, `iconfield`, `input`, `input-number`, `inputgroup`, `inputmask`, `inputotp`, `keyfilter`, `knob`, `listbox`, `multiselect`, `password`, `radio`, `rating`, `repeater`, `select`, `selectbutton`, `slider`, `textarea`, `toggle`, `togglebutton`, `treeselect`.
213
- - **Data:** `dataview`, `descriptions`, `orderlist`, `organizationchart`, `paginator`, `picklist`, `statistic`, `table`, `timeline`, `tree`, `treetable`, `virtualscroller`.
214
- - **Overlay:** `confirmdialog`, `confirmpopup`, `dialog`, `drawer`, `dynamicdialog`, `popover`, `tooltip`.
215
- - **Messages:** `badge`, `chip`, `empty`, `inlinemessage`, `message`, `result`, `tag`, `toast`.
216
- - **Menu:** `anchor`, `breadcrumb`, `contextmenu`, `dock`, `megamenu`, `menu`, `menubar`, `panelmenu`, `speeddial`, `splitbutton`, `steps`, `tabmenu`, `tieredmenu`.
294
+ - **Form:** `autocomplete`, `button`, `cascadeselect`, `checkbox`, `chips`, `colorpicker`, `datepicker`, `editor`, `fileupload`, `floatlabel`, `iconfield`, `input`, `input-number`, `inputgroup`, `inputmask`, `inputotp`, `keyfilter`, `knob`, `listbox`, `mentions`, `multiselect`, `password`, `phone-input`, `radio`, `rating`, `repeater`, `select`, `selectbutton`, `slider`, `textarea`, `toggle`, `togglebutton`, `treeselect`.
295
+ - **Data:** `dataview`, `descriptions`, `kanban`, `orderlist`, `organizationchart`, `paginator`, `picklist`, `statistic`, `table`, `timeline`, `tree`, `treetable`, `virtualscroller`.
296
+ - **Overlay:** `command-palette`, `confirmdialog`, `confirmpopup`, `dialog`, `drawer`, `dynamicdialog`, `popover`, `tooltip`.
297
+ - **Messages:** `badge`, `chat`, `chip`, `empty`, `inlinemessage`, `message`, `result`, `tag`, `toast`.
298
+ - **Menu:** `anchor`, `breadcrumb`, `contextmenu`, `dock`, `megamenu`, `menu`, `menubar`, `panelmenu`, `sidebar`, `speeddial`, `splitbutton`, `steps`, `tabmenu`, `tieredmenu`.
217
299
  - **Panel:** `accordion`, `card`, `divider`, `fieldset`, `panel`, `scrollpanel`, `scrolltop`, `splitter`, `stepper`, `tabs`, `toolbar`.
218
- - **Media:** `avatar`, `avatargroup`, `carousel`, `galleria`, `image`, `pdfviewer`.
300
+ - **Media:** `avatar`, `avatargroup`, `carousel`, `galleria`, `image`, `pdfviewer`, `qrcode`.
219
301
  - **Archivos y precios:** `file-card`, `folder-card`, `pricing`.
220
- - **Misc y directivas:** `animateonscroll`, `autofocus`, `blockui`, `chart`, `focustrap`, `icon`, `icons`, `inplace`, `metergroup`, `progressbar`, `ripple`, `skeleton`, `spinner`, `styleclass`, `terminal`, `themetoggle`.
302
+ - **Misc y directivas:** `animateonscroll`, `autofocus`, `blockui`, `chart`, `code-block`, `copy-button`, `focustrap`, `icon`, `icons`, `inplace`, `kbd`, `metergroup`, `progressbar`, `ripple`, `skeleton`, `spinner`, `styleclass`, `terminal`, `themetoggle`.
221
303
  - **Caribe Design (`@caribeng/ui/caribe/*`):** `alert`, `animation-lottie`, `button-group`, `calendar`, `card-content`, `card-move`, `card-primary`, `card-select`, `circle-loading`, `coachmark`, `data-table`, `dots-indicator`, `expansion-panel`, `fab-menu`, `file-download`, `file-select`, `float-menu`, `footer`, `header`, `icon-button`, `illustration`, `inline-alert`, `input-date`, `input-search`, `input-select`, `input-token`, `link`, `loader`, `logo`, `modal`, `off-canvas`, `onboarding`, `pictogram`, `pop-over`, `preloader`, `progress-bar`, `shortcut`, `status`, `step-indicator`, `stroke`, `switch`, `tag-button`, `tracker`, `transaction-status`, `voucher`.
222
304
 
223
305
  ## 🧭 Próximos pasos
@@ -66,7 +66,7 @@ class CbAccordionPanel {
66
66
  class="cb-accordion__header"
67
67
  [id]="headerId"
68
68
  [attr.aria-expanded]="open()"
69
- [attr.aria-controls]="regionId"
69
+ [attr.aria-controls]="open() ? regionId : null"
70
70
  [disabled]="disabled()"
71
71
  (click)="toggle()"
72
72
  >
@@ -99,7 +99,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
99
99
  class="cb-accordion__header"
100
100
  [id]="headerId"
101
101
  [attr.aria-expanded]="open()"
102
- [attr.aria-controls]="regionId"
102
+ [attr.aria-controls]="open() ? regionId : null"
103
103
  [disabled]="disabled()"
104
104
  (click)="toggle()"
105
105
  >
@@ -44,7 +44,7 @@ class CbAvatar {
44
44
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbAvatar, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
45
45
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: CbAvatar, isStandalone: true, selector: "cb-avatar", inputs: { image: { classPropertyName: "image", publicName: "image", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, shape: { classPropertyName: "shape", publicName: "shape", isSignal: true, isRequired: false, transformFunction: null }, severity: { classPropertyName: "severity", publicName: "severity", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "'cb-avatar cb-avatar--' + shape() + ' cb-avatar--' + severity()", "style.width.px": "px()", "style.height.px": "px()", "style.font-size.px": "px() * 0.4", "attr.aria-label": "ariaLabel() || null", "attr.role": "ariaLabel() ? \"img\" : null" }, classAttribute: "cb-avatar" }, ngImport: i0, template: `
46
46
  @if (image()) {
47
- <img class="cb-avatar__img" [src]="image()" [alt]="ariaLabel()" />
47
+ <img class="cb-avatar__img" [src]="image()" alt="" />
48
48
  } @else if (icon()) {
49
49
  <cb-icon [name]="icon()" [size]="px() * 0.5" aria-hidden="true" />
50
50
  } @else {
@@ -64,7 +64,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
64
64
  '[attr.role]': 'ariaLabel() ? "img" : null',
65
65
  }, template: `
66
66
  @if (image()) {
67
- <img class="cb-avatar__img" [src]="image()" [alt]="ariaLabel()" />
67
+ <img class="cb-avatar__img" [src]="image()" alt="" />
68
68
  } @else if (icon()) {
69
69
  <cb-icon [name]="icon()" [size]="px() * 0.5" aria-hidden="true" />
70
70
  } @else {
@@ -23,9 +23,12 @@ class CbBadge {
23
23
  ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
24
24
  /** Muestra solo un punto (sin texto). */
25
25
  this.dot = input(false, { ...(ngDevMode ? { debugName: "dot" } : /* istanbul ignore next */ {}), transform: toBoolean });
26
+ /** Nombre accesible que sustituye al valor (p. ej. "3 mensajes sin leer"). Sin él, un `dot` es decorativo. */
27
+ this.ariaLabel = input('', /* @ts-ignore */
28
+ ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
26
29
  }
27
30
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbBadge, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
28
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: CbBadge, isStandalone: true, selector: "cb-badge", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, severity: { classPropertyName: "severity", publicName: "severity", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, dot: { classPropertyName: "dot", publicName: "dot", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "'cb-badge cb-badge--' + severity() + ' cb-badge--' + size()", "class.cb-badge--dot": "dot()" }, classAttribute: "cb-badge" }, ngImport: i0, template: `@if (!dot()) {{{ value() }}}`, isInline: true, styles: [":host{display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;min-width:1.25rem;height:1.25rem;padding:0 .375rem;border-radius:var(--cb-radius-full);font-family:var(--cb-font-sans);font-size:var(--cb-text-xs);font-weight:var(--cb-font-weight-bold);line-height:1;color:var(--_fg);background-color:var(--_bg)}:host(.cb-badge--sm){min-width:1rem;height:1rem;font-size:.625rem;padding:0 .25rem}:host(.cb-badge--lg){min-width:1.5rem;height:1.5rem;font-size:var(--cb-text-sm);padding:0 .5rem}:host(.cb-badge--dot){min-width:.625rem;width:.625rem;height:.625rem;padding:0}:host(.cb-badge--dot.cb-badge--sm){min-width:.5rem;width:.5rem;height:.5rem}:host(.cb-badge--dot.cb-badge--lg){min-width:.75rem;width:.75rem;height:.75rem}:host(.cb-badge--primary){--_bg: var(--cb-color-primary-500);--_fg: var(--cb-color-primary-contrast)}:host(.cb-badge--secondary){--_bg: var(--cb-color-surface-500);--_fg: var(--cb-color-surface-50)}:host(.cb-badge--success){--_bg: var(--cb-color-success-500);--_fg: var(--cb-color-success-contrast)}:host(.cb-badge--info){--_bg: var(--cb-color-info-500);--_fg: var(--cb-color-info-contrast)}:host(.cb-badge--warning){--_bg: var(--cb-color-warning-500);--_fg: var(--cb-color-warning-contrast)}:host(.cb-badge--danger){--_bg: var(--cb-color-danger-500);--_fg: var(--cb-color-danger-contrast)}:host(.cb-badge--help){--_bg: var(--cb-color-help-500);--_fg: var(--cb-color-help-contrast)}:host(.cb-badge--contrast){--_bg: var(--cb-color-text);--_fg: var(--cb-color-bg)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
31
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: CbBadge, isStandalone: true, selector: "cb-badge", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, severity: { classPropertyName: "severity", publicName: "severity", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, dot: { classPropertyName: "dot", publicName: "dot", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "'cb-badge cb-badge--' + severity() + ' cb-badge--' + size()", "class.cb-badge--dot": "dot()", "attr.role": "ariaLabel() ? \"img\" : null", "attr.aria-label": "ariaLabel() || null", "attr.aria-hidden": "dot() && !ariaLabel() ? \"true\" : null" }, classAttribute: "cb-badge" }, ngImport: i0, template: `@if (!dot()) {{{ value() }}}`, isInline: true, styles: [":host{display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;min-width:1.25rem;height:1.25rem;padding:0 .375rem;border-radius:var(--cb-radius-full);font-family:var(--cb-font-sans);font-size:var(--cb-text-xs);font-weight:var(--cb-font-weight-bold);line-height:1;color:var(--_fg);background-color:var(--_bg)}:host(.cb-badge--sm){min-width:1rem;height:1rem;font-size:.625rem;padding:0 .25rem}:host(.cb-badge--lg){min-width:1.5rem;height:1.5rem;font-size:var(--cb-text-sm);padding:0 .5rem}:host(.cb-badge--dot){min-width:.625rem;width:.625rem;height:.625rem;padding:0}:host(.cb-badge--dot.cb-badge--sm){min-width:.5rem;width:.5rem;height:.5rem}:host(.cb-badge--dot.cb-badge--lg){min-width:.75rem;width:.75rem;height:.75rem}:host(.cb-badge--primary){--_bg: var(--cb-color-primary-500);--_fg: var(--cb-color-primary-contrast)}:host(.cb-badge--secondary){--_bg: var(--cb-color-surface-500);--_fg: var(--cb-color-surface-50)}:host(.cb-badge--success){--_bg: var(--cb-color-success-500);--_fg: var(--cb-color-success-contrast)}:host(.cb-badge--info){--_bg: var(--cb-color-info-500);--_fg: var(--cb-color-info-contrast)}:host(.cb-badge--warning){--_bg: var(--cb-color-warning-500);--_fg: var(--cb-color-warning-contrast)}:host(.cb-badge--danger){--_bg: var(--cb-color-danger-500);--_fg: var(--cb-color-danger-contrast)}:host(.cb-badge--help){--_bg: var(--cb-color-help-500);--_fg: var(--cb-color-help-contrast)}:host(.cb-badge--contrast){--_bg: var(--cb-color-text);--_fg: var(--cb-color-bg)}\n"], changeDetection: i0.ChangeDetectionStrategy.OnPush }); }
29
32
  }
30
33
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbBadge, decorators: [{
31
34
  type: Component,
@@ -33,8 +36,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
33
36
  class: 'cb-badge',
34
37
  '[class]': "'cb-badge cb-badge--' + severity() + ' cb-badge--' + size()",
35
38
  '[class.cb-badge--dot]': 'dot()',
39
+ '[attr.role]': 'ariaLabel() ? "img" : null',
40
+ '[attr.aria-label]': 'ariaLabel() || null',
41
+ '[attr.aria-hidden]': 'dot() && !ariaLabel() ? "true" : null',
36
42
  }, template: `@if (!dot()) {{{ value() }}}`, styles: [":host{display:inline-flex;align-items:center;justify-content:center;box-sizing:border-box;min-width:1.25rem;height:1.25rem;padding:0 .375rem;border-radius:var(--cb-radius-full);font-family:var(--cb-font-sans);font-size:var(--cb-text-xs);font-weight:var(--cb-font-weight-bold);line-height:1;color:var(--_fg);background-color:var(--_bg)}:host(.cb-badge--sm){min-width:1rem;height:1rem;font-size:.625rem;padding:0 .25rem}:host(.cb-badge--lg){min-width:1.5rem;height:1.5rem;font-size:var(--cb-text-sm);padding:0 .5rem}:host(.cb-badge--dot){min-width:.625rem;width:.625rem;height:.625rem;padding:0}:host(.cb-badge--dot.cb-badge--sm){min-width:.5rem;width:.5rem;height:.5rem}:host(.cb-badge--dot.cb-badge--lg){min-width:.75rem;width:.75rem;height:.75rem}:host(.cb-badge--primary){--_bg: var(--cb-color-primary-500);--_fg: var(--cb-color-primary-contrast)}:host(.cb-badge--secondary){--_bg: var(--cb-color-surface-500);--_fg: var(--cb-color-surface-50)}:host(.cb-badge--success){--_bg: var(--cb-color-success-500);--_fg: var(--cb-color-success-contrast)}:host(.cb-badge--info){--_bg: var(--cb-color-info-500);--_fg: var(--cb-color-info-contrast)}:host(.cb-badge--warning){--_bg: var(--cb-color-warning-500);--_fg: var(--cb-color-warning-contrast)}:host(.cb-badge--danger){--_bg: var(--cb-color-danger-500);--_fg: var(--cb-color-danger-contrast)}:host(.cb-badge--help){--_bg: var(--cb-color-help-500);--_fg: var(--cb-color-help-contrast)}:host(.cb-badge--contrast){--_bg: var(--cb-color-text);--_fg: var(--cb-color-bg)}\n"] }]
37
- }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], severity: [{ type: i0.Input, args: [{ isSignal: true, alias: "severity", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], dot: [{ type: i0.Input, args: [{ isSignal: true, alias: "dot", required: false }] }] } });
43
+ }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], severity: [{ type: i0.Input, args: [{ isSignal: true, alias: "severity", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], dot: [{ type: i0.Input, args: [{ isSignal: true, alias: "dot", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }] } });
38
44
  function toBoolean(value) {
39
45
  return value !== false && value != null && `${value}` !== 'false';
40
46
  }
@@ -1,7 +1,7 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, output, ViewEncapsulation, ChangeDetectionStrategy, Component } from '@angular/core';
2
+ import { input, computed, output, ViewEncapsulation, ChangeDetectionStrategy, Component } from '@angular/core';
3
3
  import { CbIcon } from '@caribeng/ui/icon';
4
- import { injectCbTranslation } from '@caribeng/ui';
4
+ import { injectCbTranslation, cbVisibleItems, cbRunCommand } from '@caribeng/ui';
5
5
 
6
6
  /**
7
7
  * Breadcrumb de CaribeNG: rastro de navegación jerárquica.
@@ -21,20 +21,27 @@ class CbBreadcrumb {
21
21
  this.t = injectCbTranslation();
22
22
  this.items = input([], /* @ts-ignore */
23
23
  ...(ngDevMode ? [{ debugName: "items" }] : /* istanbul ignore next */ []));
24
+ /** Items sin los de `visible: false`. */
25
+ this.visibleItems = computed(() => cbVisibleItems(this.items()), /* @ts-ignore */
26
+ ...(ngDevMode ? [{ debugName: "visibleItems" }] : /* istanbul ignore next */ []));
24
27
  /** Icono separador entre elementos. */
25
28
  this.separator = input('chevron-right', /* @ts-ignore */
26
29
  ...(ngDevMode ? [{ debugName: "separator" }] : /* istanbul ignore next */ []));
27
30
  /** Se emite al hacer clic en un elemento con enlace. */
28
31
  this.itemClick = output();
29
32
  }
33
+ select(item, event) {
34
+ cbRunCommand(item, event);
35
+ this.itemClick.emit(item);
36
+ }
30
37
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.5", ngImport: i0, type: CbBreadcrumb, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
31
38
  static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.5", type: CbBreadcrumb, isStandalone: true, selector: "cb-breadcrumb", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: false, transformFunction: null }, separator: { classPropertyName: "separator", publicName: "separator", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { itemClick: "itemClick" }, host: { classAttribute: "cb-breadcrumb" }, ngImport: i0, template: `
32
39
  <nav [attr.aria-label]="t().breadcrumb">
33
40
  <ol class="cb-breadcrumb__list">
34
- @for (item of items(); track $index; let last = $last) {
41
+ @for (item of visibleItems(); track $index; let last = $last) {
35
42
  <li class="cb-breadcrumb__item">
36
43
  @if (item.url && !last) {
37
- <a class="cb-breadcrumb__link" [href]="item.url" (click)="itemClick.emit(item)">
44
+ <a class="cb-breadcrumb__link" [href]="item.url" (click)="select(item, $event)">
38
45
  @if (item.icon) { <cb-icon [name]="item.icon" [size]="15" aria-hidden="true" /> }
39
46
  <span>{{ item.label }}</span>
40
47
  </a>
@@ -58,10 +65,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.5", ngImpor
58
65
  args: [{ selector: 'cb-breadcrumb', changeDetection: ChangeDetectionStrategy.OnPush, encapsulation: ViewEncapsulation.Emulated, imports: [CbIcon], host: { class: 'cb-breadcrumb' }, template: `
59
66
  <nav [attr.aria-label]="t().breadcrumb">
60
67
  <ol class="cb-breadcrumb__list">
61
- @for (item of items(); track $index; let last = $last) {
68
+ @for (item of visibleItems(); track $index; let last = $last) {
62
69
  <li class="cb-breadcrumb__item">
63
70
  @if (item.url && !last) {
64
- <a class="cb-breadcrumb__link" [href]="item.url" (click)="itemClick.emit(item)">
71
+ <a class="cb-breadcrumb__link" [href]="item.url" (click)="select(item, $event)">
65
72
  @if (item.icon) { <cb-icon [name]="item.icon" [size]="15" aria-hidden="true" /> }
66
73
  <span>{{ item.label }}</span>
67
74
  </a>
@@ -1,6 +1,6 @@
1
1
  import { DOCUMENT, NgTemplateOutlet } from '@angular/common';
2
2
  import * as i0 from '@angular/core';
3
- import { signal, Injectable, input, ChangeDetectionStrategy, Component, Directive, forwardRef, booleanAttribute, numberAttribute, output, contentChildren, viewChild, inject, ElementRef, computed, DestroyRef, afterNextRender, effect, untracked, ViewEncapsulation } from '@angular/core';
3
+ import { signal, Injectable, input, ChangeDetectionStrategy, Component, Directive, forwardRef, booleanAttribute, numberAttribute, computed, output, contentChildren, viewChild, inject, ElementRef, DestroyRef, afterNextRender, effect, untracked, ViewEncapsulation } from '@angular/core';
4
4
  import { injectCbTranslation } from '@caribeng/ui';
5
5
 
6
6
  // Tipos públicos del gráfico y modelo interno de la escena que dibuja cada renderer.
@@ -1751,9 +1751,12 @@ class CbChart {
1751
1751
  /** Formato de los valores (ejes, tooltip y tabla). */
1752
1752
  this.valueFormat = input(undefined, /* @ts-ignore */
1753
1753
  ...(ngDevMode ? [{ debugName: "valueFormat" }] : /* istanbul ignore next */ []));
1754
- /** Locale BCP 47 para el formato (por defecto, el del navegador). */
1754
+ /** Locale BCP 47 para el formato (por defecto, el del idioma activo de CaribeNG: `intlLocale`). */
1755
1755
  this.locale = input(undefined, /* @ts-ignore */
1756
1756
  ...(ngDevMode ? [{ debugName: "locale" }] : /* istanbul ignore next */ []));
1757
+ /** Locale efectivo: el input o, si no se indica, el del idioma activo. */
1758
+ this.intlLocale = computed(() => this.locale() ?? this.t().intlLocale, /* @ts-ignore */
1759
+ ...(ngDevMode ? [{ debugName: "intlLocale" }] : /* istanbul ignore next */ []));
1757
1760
  /** Valor mínimo del medidor (`gauge`). */
1758
1761
  this.min = input(0, /* @ts-ignore */
1759
1762
  ...(ngDevMode ? [{ debugName: "min" }] : /* istanbul ignore next */ []));
@@ -1824,7 +1827,7 @@ class CbChart {
1824
1827
  ...(ngDevMode ? [{ debugName: "isEmpty" }] : /* istanbul ignore next */ []));
1825
1828
  this.numberFormat = computed(() => this.formatter(this.valueFormat()), /* @ts-ignore */
1826
1829
  ...(ngDevMode ? [{ debugName: "numberFormat" }] : /* istanbul ignore next */ []));
1827
- this.percentFormat = computed(() => new Intl.NumberFormat(this.locale(), { style: 'percent', maximumFractionDigits: 1 }).format, /* @ts-ignore */
1830
+ this.percentFormat = computed(() => new Intl.NumberFormat(this.intlLocale(), { style: 'percent', maximumFractionDigits: 1 }).format, /* @ts-ignore */
1828
1831
  ...(ngDevMode ? [{ debugName: "percentFormat" }] : /* istanbul ignore next */ []));
1829
1832
  /** Lo declarado en los hijos, como datos planos para el renderer. */
1830
1833
  this.composedSpec = computed(() => {
@@ -1894,7 +1897,7 @@ class CbChart {
1894
1897
  bands: this.bands(),
1895
1898
  format: this.numberFormat(),
1896
1899
  percent: this.percentFormat(),
1897
- locale: this.locale(),
1900
+ locale: this.intlLocale(),
1898
1901
  text: { total: t.chartTotal, change: t.chartChange, open: t.chartOpen, high: t.chartHigh, low: t.chartLow, close: t.chartClose },
1899
1902
  composed: this.composedSpec(),
1900
1903
  };
@@ -1977,7 +1980,7 @@ class CbChart {
1977
1980
  });
1978
1981
  }
1979
1982
  formatter(vf) {
1980
- return typeof vf === 'function' ? vf : new Intl.NumberFormat(this.locale(), vf ?? { maximumFractionDigits: 2 }).format;
1983
+ return typeof vf === 'function' ? vf : new Intl.NumberFormat(this.intlLocale(), vf ?? { maximumFractionDigits: 2 }).format;
1981
1984
  }
1982
1985
  setWindow(window) {
1983
1986
  this.active.set(null);