@ajuarezso/sui-design-system 0.25.1 → 0.26.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 (68) hide show
  1. package/docs/INDEX.md +3 -3
  2. package/docs/TEXTOS.md +85 -0
  3. package/docs/sui-activity-feed/README.md +19 -4
  4. package/docs/sui-alert/README.md +10 -0
  5. package/docs/sui-avatar-group/README.md +10 -0
  6. package/docs/sui-banner/README.md +10 -0
  7. package/docs/sui-brand-logo/README.md +10 -0
  8. package/docs/sui-breadcrumbs/README.md +10 -0
  9. package/docs/sui-checkbox-card/README.md +2 -1
  10. package/docs/sui-circular-progress-bar/README.md +10 -0
  11. package/docs/sui-color-dot/README.md +20 -0
  12. package/docs/sui-color-picker/README.md +20 -3
  13. package/docs/sui-command-menu-footer/README.md +17 -4
  14. package/docs/sui-command-menu-item/README.md +18 -10
  15. package/docs/sui-command-menu-search/README.md +18 -8
  16. package/docs/sui-compact-select/README.md +2 -0
  17. package/docs/sui-content-card/README.md +11 -1
  18. package/docs/sui-content-divider/README.md +11 -0
  19. package/docs/sui-counter-input/README.md +13 -2
  20. package/docs/sui-date-selector/README.md +11 -0
  21. package/docs/sui-datepicker/README.md +17 -0
  22. package/docs/sui-day-cell/README.md +10 -0
  23. package/docs/sui-drawer-footer/README.md +17 -2
  24. package/docs/sui-drawer-header/README.md +11 -1
  25. package/docs/sui-dropdown-menu/README.md +9 -1
  26. package/docs/sui-dropdown-search/README.md +21 -10
  27. package/docs/sui-file-icon/README.md +11 -0
  28. package/docs/sui-file-upload/README.md +14 -1
  29. package/docs/sui-file-upload-card/README.md +20 -4
  30. package/docs/sui-filter/README.md +27 -15
  31. package/docs/sui-image-upload/README.md +19 -0
  32. package/docs/sui-image-upload-profile/README.md +16 -2
  33. package/docs/sui-inline-input/README.md +15 -2
  34. package/docs/sui-input/README.md +12 -0
  35. package/docs/sui-integration-switch/README.md +10 -0
  36. package/docs/sui-modal/README.md +51 -3
  37. package/docs/sui-modal-header/README.md +10 -0
  38. package/docs/sui-modal-sheet/README.md +24 -12
  39. package/docs/sui-notification/README.md +12 -0
  40. package/docs/sui-notification-footer/README.md +14 -2
  41. package/docs/sui-notification-tab-menu/README.md +11 -1
  42. package/docs/sui-otp-input/README.md +12 -1
  43. package/docs/sui-pagination/README.md +16 -0
  44. package/docs/sui-password-strength/README.md +21 -3
  45. package/docs/sui-popover/README.md +15 -2
  46. package/docs/sui-progress-bar/README.md +10 -0
  47. package/docs/sui-radio/README.md +4 -1
  48. package/docs/sui-radio-label/README.md +5 -2
  49. package/docs/sui-rating/README.md +16 -2
  50. package/docs/sui-rich-editor/README.md +25 -2
  51. package/docs/sui-select/README.md +11 -1
  52. package/docs/sui-sidebar/README.md +18 -1
  53. package/docs/sui-slider/README.md +11 -0
  54. package/docs/sui-social-button/README.md +10 -0
  55. package/docs/sui-stepper/README.md +12 -1
  56. package/docs/sui-tab-chips/README.md +11 -0
  57. package/docs/sui-table/README.md +15 -1
  58. package/docs/sui-tag/README.md +11 -1
  59. package/docs/sui-tag-input/README.md +12 -1
  60. package/docs/sui-time-picker/README.md +19 -1
  61. package/docs/sui-toast/README.md +11 -0
  62. package/docs/sui-tooltip/README.md +10 -0
  63. package/docs/sui-topbar/README.md +15 -2
  64. package/docs/sui-topbar-user/README.md +10 -0
  65. package/fesm2022/ajuarezso-sui-design-system.mjs +1030 -462
  66. package/fesm2022/ajuarezso-sui-design-system.mjs.map +1 -1
  67. package/package.json +1 -1
  68. package/types/ajuarezso-sui-design-system.d.ts +703 -138
package/docs/INDEX.md CHANGED
@@ -57,7 +57,7 @@ Documentados: 116 de 116.
57
57
  | `sui-drawer-header` | Cabecera de un panel lateral (título, descripción, cerrar); hoy sin contenedor en SUI y sin uso en la app | sui-modal (con title y description: la cabecera de todos los modales de la app) · sui-modal-header (la cabecera suelta del modal) · sui-page-header (título de una página) |
58
58
  | `sui-dropdown-caption` | Texto gris de pie dentro de un menú flotante (una pista, la versión, cuándo se actualizó) | sui-content-divider (cabecera de un grupo de items) · sui-form-hint (ayuda debajo de un campo) · sui-empty-state (el menú no tiene resultados) |
59
59
  | `sui-dropdown-item` | Fila de un menú flotante o de una lista de opciones, con ícono, bandera, avatar o logo a la izquierda y badge o atajo a la derecha | sui-command-menu-item (fila del buscador ⌘K) · sui-filter-item (fila de un panel de filtros) · sui-sidebar (navegación lateral, con sus items) · sui-radio-label (opciones fijas a la vista en un formulario) |
60
- | `sui-dropdown-menu` | Menú flotante anclado a un botón (el kebab ⋮ de una card, un avatar) con acciones sobre eso; también es el overlay que usan por dentro los selects | sui-select (elegir un valor de un formulario) · sui-modal-sheet (más de 5 acciones o una lista larga en mobile) · sui-popover (explicar algo con una flecha, sin anclaje propio) · sui-tooltip (ayuda corta al pasar el mouse) · sui-command-menu-item (buscar y saltar con ⌘K en desktop) |
60
+ | `sui-dropdown-menu` | Menú flotante anclado a un botón (el kebab ⋮ de una card, un avatar dentro de un botón) con acciones sobre eso; también es el overlay que usan por dentro los selects | sui-select (elegir un valor de un formulario) · sui-modal-sheet (más de 5 acciones o una lista larga en mobile) · sui-popover (explicar algo con una flecha, sin anclaje propio) · sui-tooltip (ayuda corta al pasar el mouse) · sui-command-menu-item (buscar y saltar con ⌘K en desktop) |
61
61
  | `sui-dropdown-search` | Buscador sin caja que va arriba del panel de un menú flotante para filtrar sus items | sui-input type="search" (buscador de una pantalla o una lista a la vista) · sui-command-menu-search (buscador del ⌘K) · sui-select (elegir de una lista corta, sin buscar) |
62
62
  | `sui-emoji` | Un emoji 3D animado (Fluent) para los momentos con emoción: estados vacíos, celebraciones, onboarding | sui-icon (ícono funcional, monocromo) · sui-empty-state (el estado vacío completo, que ya trae su emoji) |
63
63
  | `sui-empty-state` | Pantalla o lista sin nada que mostrar (o que no pudo cargar) con emoji animado, título, explicación y una acción opcional | sui-alert (un aviso dentro de una pantalla que sí tiene contenido) · sui-banner (aviso de toda la app) · skeleton (mientras carga, nunca un empty) |
@@ -82,10 +82,10 @@ Documentados: 116 de 116.
82
82
  | `sui-key-icon` | Un ícono dentro de un círculo, con fondo de color si el ícono dice un estado | sui-icon (el ícono suelto) · sui-avatar (cuando es una persona o un local) · sui-payment-icon (el círculo ya armado para un pago de servicio) |
83
83
  | `sui-link-button` | Acción que se lee como texto, sin caja | sui-button (acción con caja: la principal o la secundaria de un grupo) · sui-compact-button (solo ícono, sin texto) · sui-alert (sus acciones se pasan en actions[] y ya salen como link) |
84
84
  | `sui-logo` | El logo de Master Pedidos (la M y la palabra), dibujado en SVG y coloreable | sui-brand-logo (logos de OTRAS marcas: WhatsApp, Google, Apple…) · sui-avatar (el logo de un local, que es una foto) |
85
- | `sui-modal` | Diálogo que se abre encima de la pantalla y pide terminar o cerrar algo antes de seguir | sui-modal-sheet (hoja que sube desde abajo y deja ver lo de atrás, p. ej. un mapa) · sui-alert (aviso en la misma pantalla, sin interrumpir) · sui-toast (confirmación breve de una acción) · sui-popover (contenido chico anclado a un botón) · sui-drawer-header (panel lateral) |
85
+ | `sui-modal` | Diálogo que se abre encima de la pantalla y pide terminar o cerrar algo antes de seguir | sui-modal-sheet (solo en el celular: hoja que sube desde abajo y deja ver lo de atrás, p. ej. un mapa) · sui-alert (aviso en la misma pantalla, sin interrumpir) · sui-toast (confirmación breve de una acción) · sui-popover (contenido chico anclado a un botón) · sui-drawer-header (panel lateral) |
86
86
  | `sui-modal-footer` | Pie de modal con la acción principal a la derecha, la secundaria a su izquierda y un slot izquierdo para un checkbox, un link o un texto | sui-button (botones sueltos fuera de un modal o una hoja) · sui-drawer-footer (pie de un panel lateral) · sui-notification-footer (pie del panel de notificaciones: ayuda de teclado y link a ajustes) |
87
87
  | `sui-modal-header` | Cabecera de modal con título, descripción, ícono opcional y la X de cerrar; sui-modal ya la dibuja sola | sui-modal (ya trae este header por title/description/iconVariant) · sui-drawer-header (cabecera de un panel lateral) · sui-page-header (título de una pantalla) |
88
- | `sui-modal-sheet` | Hoja que sube desde abajo, se arrastra entre alturas fijas y puede dejar usable lo de atrás (un mapa) | sui-modal (pide terminar algo antes de seguir, sin fondo usable) · sui-popover (contenido chico anclado a un botón) · sui-dropdown-menu (lista de acciones) |
88
+ | `sui-modal-sheet` | Hoja del celular que sube desde abajo, se arrastra entre alturas fijas y puede dejar usable lo de atrás (un mapa); en tablet y computadora se usa sui-modal | sui-modal (pide terminar algo antes de seguir, sin fondo usable) · sui-popover (contenido chico anclado a un botón) · sui-dropdown-menu (lista de acciones) |
89
89
  | `sui-notification` | Fila de la bandeja de notificaciones, con quién, qué pasó, hace cuánto y si ya se leyó | sui-toast (aviso efímero, incluido el «notification» de esquina de la guía) · sui-alert (aviso que se queda en su contexto) · sui-banner (toda la app) |
90
90
  | `sui-notification-footer` | Pie del panel desplegable de notificaciones (web): ayuda de teclado a la izquierda y un link a los ajustes a la derecha | sui-link-button (un link suelto a los ajustes) · sui-drawer-footer (pie de un panel lateral) · sui-modal-footer (pie con botones) |
91
91
  | `sui-notification-tab-menu` | Cabecera con pestañas del panel desplegable de notificaciones (web), con conteo de no leídas y botón de filtro | sui-tab-chips (pestañas de la bandeja a pantalla completa, en mobile) · sui-tab-menu-horizontal (pestañas de una pantalla) · sui-segmented-control (cambiar de vista con 2–4 opciones) |
package/docs/TEXTOS.md ADDED
@@ -0,0 +1,85 @@
1
+ # Textos de la librería en otro idioma
2
+
3
+ Desde la 0.26.0, lo que la librería escribe por su cuenta —el `aria-label` de las
4
+ X y las flechas, los textos solo para lector de pantalla, los placeholders y los
5
+ botones por defecto— sale de un solo lugar: `SUI_TEXTOS`. Sin configurar nada,
6
+ es el español de siempre. Una app en otro idioma los cambia con
7
+ `provideSuiTextos`.
8
+
9
+ ## Uso
10
+
11
+ ```ts
12
+ // app.config.ts — textos fijos
13
+ import { provideSuiTextos } from '@ajuarezso/sui-design-system';
14
+
15
+ providers: [
16
+ provideSuiTextos({
17
+ comun: { cerrar: 'Close', limpiar: 'Clear', buscar: 'Search…' },
18
+ input: { mostrarContrasena: 'Show password', ocultarContrasena: 'Hide password' },
19
+ }),
20
+ ]
21
+ ```
22
+
23
+ Con una **función que lee un signal**, los textos cambian en vivo cuando cambia
24
+ el idioma, sin recargar:
25
+
26
+ ```ts
27
+ const idioma = signal<'es' | 'en'>('es');
28
+ provideSuiTextos(() => (idioma() === 'en' ? SUI_EN : {}));
29
+ ```
30
+
31
+ Si el i18n de la app vive en un servicio, se conecta con el token directo:
32
+
33
+ ```ts
34
+ {
35
+ provide: SUI_TEXTOS,
36
+ useFactory: () => {
37
+ const i18n = inject(MiI18n);
38
+ return computed(() => mezclarSuiTextos(i18n.textosSui()));
39
+ },
40
+ }
41
+ ```
42
+
43
+ ## Reglas
44
+
45
+ - **Lo que no se pasa queda en español.** Se mezcla grupo por grupo:
46
+ `{ comun: { cerrar: 'Close' } }` cambia esa clave y deja el resto.
47
+ - **El input del componente gana.** `ariaLabel`, `placeholder`, `cancelLabel`,
48
+ `backLabel`… siguen funcionando igual; el texto global es lo que se usa cuando
49
+ no se pasa nada. La tabla «Textos» del README de cada componente dice qué
50
+ clave usa y qué input la pisa.
51
+ - **Variables entre llaves.** `'Página {n}'`, `'Paso {actual} de {total}'`,
52
+ `'Descargar {nombre}'`. La librería las rellena; un diccionario que ya usa
53
+ `{param}` las pasa tal cual.
54
+ - **Funciona por subárbol.** `provideSuiTextos` también va en los `providers` de
55
+ un componente, si solo esa parte de la pantalla necesita otros textos.
56
+
57
+ ## Las claves
58
+
59
+ La lista completa, con un comentario por clave, es la interfaz `SuiTextos`
60
+ (`core/sui-textos.ts`, en los tipos del paquete). Los grupos:
61
+
62
+ | grupo | qué cubre |
63
+ |---|---|
64
+ | `comun` | lo repetido: cerrar, limpiar, buscar, más opciones, progreso, pasos, verificado, flechas de scroll |
65
+ | `input` | mostrar / ocultar contraseña |
66
+ | `tabla` · `paginacion` | checkbox de filas, «No hay datos», botones y páginas |
67
+ | `editorTexto` · `selectorColor` | barra del editor, partes del selector y nombres de color |
68
+ | `contrasena` · `otp` · `calificacion` · `contador` · `deslizador` | campos especiales |
69
+ | `archivo` · `imagen` | subida de archivos e imágenes, sus estados y errores |
70
+ | `menuComandos` · `notificacion` · `filtro` · `navegacion` | menús, avisos, filtros, sidebar y migas |
71
+ | `fecha` | navegación del calendario, placeholders, hora y estado |
72
+ | `popover` · `varios` | botones del popover, descartar, administrar, responder, marcas |
73
+
74
+ ## Lo que todavía no cubre
75
+
76
+ - **Las fechas del calendario** (`sui-datepicker`, `sui-date-selector`): nombres
77
+ de meses y días, los rangos predefinidos («Hoy», «Últimos 7 días») y el
78
+ «Rango: …» usan `date-fns` con el locale en español. Es un cambio de locale,
79
+ no de textos.
80
+ - **`sui-store-card` y `sui-product-card`**: el texto es de negocio de
81
+ Master Pedidos (Abierto, Envío, Agotado, S/).
82
+ - **`sui-app-store-badge`**: el texto alternativo de cada tienda; se pasa con
83
+ `ariaLabel`.
84
+ - **`sui-logo`**: el nombre de la marca no se traduce.
85
+ - **La coma decimal** de la nota de `sui-rating` («4,5 de 5»).
@@ -103,6 +103,18 @@ código del DS.
103
103
  | `reply` | `{ item: SuiActivityFeedItem; event: MouseEvent }` |
104
104
  <!-- api:fin -->
105
105
 
106
+ <!-- textos:inicio -->
107
+ ### Textos
108
+
109
+ Lo que el componente escribe solo sale de `provideSuiTextos` (español por defecto, se cambia en vivo; ver `docs/TEXTOS.md`). El input de la última columna le gana al texto global.
110
+
111
+ | clave | por defecto | se pisa con |
112
+ |---|---|---|
113
+ | `comun.descargar` | Descargar {nombre} | — |
114
+ | `varios.responder` | Responder | `replyLabel` del comentario |
115
+ | `comun.masOpciones` | Más opciones | — |
116
+ <!-- textos:fin -->
117
+
106
118
  ```ts
107
119
  type SuiActivityFeedItem =
108
120
  | (Base & { type: 'default' })
@@ -130,8 +142,10 @@ interface SuiActivityTask { state: 'success' | 'warning' | 'pending' | 'error';
130
142
  - **Los tres puntos salen por defecto** (`showAction` es `true` si no lo pasas). Si no hay menú
131
143
  detrás, pon `showAction: false` en cada item o la persona toca algo que no hace nada.
132
144
  - `replyLabel` por defecto dice «Responder» (en español desde el 04-10-2026).
133
- - **`file.href` no hace nada.** El tipo dice que dispara la descarga, pero la plantilla no lo lee:
134
- la descarga la haces tú en `(fileDownload)`.
145
+ - **Con `file.href` el chip descarga solo** (desde 0.26.0): es un `<a download>` que baja el
146
+ archivo; si la URL es de otro origen (Storage), el navegador ignora `download` y lo abre en otra
147
+ pestaña. `(fileDownload)` sale igual, para registrar. Sin `href`, la descarga la haces tú en
148
+ `(fileDownload)`.
135
149
  - `timestamp` es un string: el feed no formatea ni actualiza «hace 4 min». Si la pantalla queda
136
150
  abierta, recalcúlalo tú.
137
151
  - Sin `avatar.src`, `initials` ni `icon`, el leading es la silueta gris genérica.
@@ -161,12 +175,13 @@ Historial vacío
161
175
  Descargar un archivo
162
176
 
163
177
  ```html
164
- <!-- ❌ mal: file.href no lo lee la plantilla; sin (fileDownload) el chip no baja nada -->
178
+ <!-- ❌ mal: los archivos sin href y nadie escucha (fileDownload): el chip no baja nada -->
165
179
  <sui-activity-feed [items]="comprobantes()" />
166
180
  ```
167
181
 
168
182
  ```html
169
- <!-- ✅ bien -->
183
+ <!-- ✅ bien: cada archivo con su href (el chip descarga solo), o (fileDownload) si hay que pedir la URL -->
184
+ <sui-activity-feed [items]="comprobantesConUrl()" />
170
185
  <sui-activity-feed [items]="comprobantes()" (fileDownload)="descargar($event.file)" />
171
186
  ```
172
187
 
@@ -186,6 +186,16 @@ X en la **tablet táctil**: hoy se prende solo con `max-width: 767px`, y el arre
186
186
  | `closed` | `void` |
187
187
  <!-- api:fin -->
188
188
 
189
+ <!-- textos:inicio -->
190
+ ### Textos
191
+
192
+ Lo que el componente escribe solo sale de `provideSuiTextos` (español por defecto, se cambia en vivo; ver `docs/TEXTOS.md`). El input de la última columna le gana al texto global.
193
+
194
+ | clave | por defecto | se pisa con |
195
+ |---|---|---|
196
+ | `comun.cerrar` | Cerrar | — |
197
+ <!-- textos:fin -->
198
+
189
199
  `description` se pinta con `innerHTML`: acepta HTML y no se escapa.
190
200
 
191
201
  Signal pública: `dismissed` (`signal(false)`).
@@ -109,6 +109,16 @@ está en «no usar» — pendiente de decidir si la cabecera se queda en `lg` ta
109
109
  Outputs: ninguno.
110
110
  <!-- api:fin -->
111
111
 
112
+ <!-- textos:inicio -->
113
+ ### Textos
114
+
115
+ Lo que el componente escribe solo sale de `provideSuiTextos` (español por defecto, se cambia en vivo; ver `docs/TEXTOS.md`). El input de la última columna le gana al texto global.
116
+
117
+ | clave | por defecto | se pisa con |
118
+ |---|---|---|
119
+ | `comun.masN` | {n} más | — |
120
+ <!-- textos:fin -->
121
+
112
122
  ```ts
113
123
  interface SuiAvatarGroupItem {
114
124
  src?: string;
@@ -183,6 +183,16 @@ en la columna de la derecha, se usa.
183
183
  | `closed` | `void` |
184
184
  <!-- api:fin -->
185
185
 
186
+ <!-- textos:inicio -->
187
+ ### Textos
188
+
189
+ Lo que el componente escribe solo sale de `provideSuiTextos` (español por defecto, se cambia en vivo; ver `docs/TEXTOS.md`). El input de la última columna le gana al texto global.
190
+
191
+ | clave | por defecto | se pisa con |
192
+ |---|---|---|
193
+ | `comun.cerrar` | Cerrar | — |
194
+ <!-- textos:fin -->
195
+
186
196
  Signal pública: `dismissed` (`signal(false)`). Slots: `[slot=icon]` (reemplaza el ícono del tipo)
187
197
  y `[slot=action]` (el link).
188
198
 
@@ -141,6 +141,16 @@ solo en `bp.celular()`.
141
141
  Outputs: ninguno.
142
142
  <!-- api:fin -->
143
143
 
144
+ <!-- textos:inicio -->
145
+ ### Textos
146
+
147
+ Lo que el componente escribe solo sale de `provideSuiTextos` (español por defecto, se cambia en vivo; ver `docs/TEXTOS.md`). El input de la última columna le gana al texto global.
148
+
149
+ | clave | por defecto | se pisa con |
150
+ |---|---|---|
151
+ | `varios.logoDe` | Logo de {marca} | `ariaLabel` |
152
+ <!-- textos:fin -->
153
+
144
154
  ## Trampas
145
155
 
146
156
  - **Los SVG no vienen en el paquete npm.** Se piden a `/assets/brand/brand-logos/<marca>-<variant>.svg`:
@@ -101,6 +101,16 @@ En master-pedidos: sin `[size]`; se muestra con `@if (!bp.isMobile())`.
101
101
  | `navigate` | `SuiBreadcrumbEntry` |
102
102
  <!-- api:fin -->
103
103
 
104
+ <!-- textos:inicio -->
105
+ ### Textos
106
+
107
+ Lo que el componente escribe solo sale de `provideSuiTextos` (español por defecto, se cambia en vivo; ver `docs/TEXTOS.md`). El input de la última columna le gana al texto global.
108
+
109
+ | clave | por defecto | se pisa con |
110
+ |---|---|---|
111
+ | `navegacion.migas` | Ruta de navegación | — |
112
+ <!-- textos:fin -->
113
+
104
114
  ```ts
105
115
  interface SuiBreadcrumbEntry {
106
116
  label?: string;
@@ -152,7 +152,8 @@ Slot: `[slot=leading]` (solo para `card-provider`, `brand` y `company`). Impleme
152
152
  proyectas una imagen con padding o borde propios, ponle `box-sizing` tú.
153
153
  - `badge` con string sale **azul `light` `small`**.
154
154
  - **El host es el único control** (role checkbox, tab stop, Espacio y Enter): la casilla de la
155
- derecha solo dibuja el estado, va oculta al lector y fuera del Tab (`[focusable]="false"`).
155
+ derecha solo dibuja el estado, va oculta al lector y fuera del Tab (`[focusable]="false"`,
156
+ `aria-hidden` e `inert`; sin `inert`, axe marca nested-interactive por su `tabindex="-1"`).
156
157
  Hasta el 04-10-2026 quedaba enfocable con `aria-hidden` y ahí Espacio no marcaba nada.
157
158
 
158
159
  ## Bien y mal
@@ -134,6 +134,16 @@ En master-pedidos: no hay helper; `[size]="bp.celular() ? 64 : 56"` en una fila
134
134
  Outputs: ninguno.
135
135
  <!-- api:fin -->
136
136
 
137
+ <!-- textos:inicio -->
138
+ ### Textos
139
+
140
+ Lo que el componente escribe solo sale de `provideSuiTextos` (español por defecto, se cambia en vivo; ver `docs/TEXTOS.md`). El input de la última columna le gana al texto global.
141
+
142
+ | clave | por defecto | se pisa con |
143
+ |---|---|---|
144
+ | `comun.progreso` | Progreso | `ariaLabel` |
145
+ <!-- textos:fin -->
146
+
137
147
  ## Trampas
138
148
 
139
149
  - **`size` es un número, no un texto**: `[size]="64"`, no `size="64"` (un string no tipa).
@@ -113,6 +113,26 @@ el helper de botón y campo).
113
113
  | `clicked` | `void` |
114
114
  <!-- api:fin -->
115
115
 
116
+ <!-- textos:inicio -->
117
+ ### Textos
118
+
119
+ Lo que el componente escribe solo sale de `provideSuiTextos` (español por defecto, se cambia en vivo; ver `docs/TEXTOS.md`). El input de la última columna le gana al texto global.
120
+
121
+ | clave | por defecto | se pisa con |
122
+ |---|---|---|
123
+ | `selectorColor.color` | Color {hex} | `ariaLabel` |
124
+ | `selectorColor.gris` | Gris | `ariaLabel` |
125
+ | `selectorColor.azul` | Azul | `ariaLabel` |
126
+ | `selectorColor.naranja` | Naranja | `ariaLabel` |
127
+ | `selectorColor.rojo` | Rojo | `ariaLabel` |
128
+ | `selectorColor.verde` | Verde | `ariaLabel` |
129
+ | `selectorColor.amarillo` | Amarillo | `ariaLabel` |
130
+ | `selectorColor.morado` | Morado | `ariaLabel` |
131
+ | `selectorColor.celeste` | Celeste | `ariaLabel` |
132
+ | `selectorColor.rosado` | Rosado | `ariaLabel` |
133
+ | `selectorColor.turquesa` | Turquesa | `ariaLabel` |
134
+ <!-- textos:fin -->
135
+
116
136
  Output: `(clicked)`, solo si no está `disabled`. El dot no cambia su propio `state`: la pantalla
117
137
  lo pasa a `selected`.
118
138
 
@@ -108,21 +108,38 @@ que `bp.sizeControl()`, el helper de botón y campo).
108
108
  |---|---|---|
109
109
  | `colors` | `readonly SuiColorDotColor[]` | `SUI_COLOR_DOT_COLORS` |
110
110
  | `value` · model, `[(value)]` | `string \| null` | `null` |
111
- | `placeholder` | `string` | `'Elige un color'` |
112
- | `recommendedLabel` | `string` | `'Colores recomendados'` |
111
+ | `placeholder` | `string \| null` | `null` |
112
+ | `recommendedLabel` | `string \| null` | `null` |
113
113
  | `presetsOnly` | `boolean` | `false` |
114
114
  | `alpha` | `boolean` | `true` |
115
115
  | `eyedropper` | `boolean` | `true` |
116
116
  | `showFormatLabel` | `boolean` | `true` |
117
117
  | `disabled` | `boolean` | `false` |
118
118
  | `size` | `'medium' \| 'large'` | `'medium'` |
119
- | `ariaLabel` | `string` | `'Seleccionar color'` |
119
+ | `ariaLabel` | `string \| null` | `null` |
120
120
 
121
121
  | output | payload |
122
122
  |---|---|
123
123
  | `change` | `string` |
124
124
  <!-- api:fin -->
125
125
 
126
+ <!-- textos:inicio -->
127
+ ### Textos
128
+
129
+ Lo que el componente escribe solo sale de `provideSuiTextos` (español por defecto, se cambia en vivo; ver `docs/TEXTOS.md`). El input de la última columna le gana al texto global.
130
+
131
+ | clave | por defecto | se pisa con |
132
+ |---|---|---|
133
+ | `selectorColor.saturacion` | Saturación y brillo | — |
134
+ | `selectorColor.matiz` | Matiz | — |
135
+ | `selectorColor.opacidad` | Opacidad | — |
136
+ | `selectorColor.cuentagotas` | Cuentagotas | — |
137
+ | `selectorColor.hexadecimal` | Valor hexadecimal | — |
138
+ | `selectorColor.placeholder` | Elige un color | `placeholder` |
139
+ | `selectorColor.recomendados` | Colores recomendados | `recommendedLabel` |
140
+ | `selectorColor.nombre` | Seleccionar color | `ariaLabel` |
141
+ <!-- textos:fin -->
142
+
126
143
  `SuiColorDotColor`: `'gray' | 'blue' | 'orange' | 'red' | 'green' | 'yellow' | 'purple' | 'sky' | 'pink' | 'teal'`.
127
144
 
128
145
  ## Trampas
@@ -69,16 +69,29 @@ En master-pedidos: `@if (!bp.isMobile())` alrededor del pie, sin `[size]`.
69
69
 
70
70
  | input | tipo | default |
71
71
  |---|---|---|
72
- | `navigateLabel` | `string` | `'Navegar'` |
73
- | `selectLabel` | `string` | `'Seleccionar'` |
74
- | `helpText` | `string \| null` | `'¿No es lo que buscas? Prueba el'` |
75
- | `helpLink` | `SuiLinkConfig \| string \| null` | `'Centro de ayuda'` |
72
+ | `navigateLabel` | `string \| null` | `null` |
73
+ | `selectLabel` | `string \| null` | `null` |
74
+ | `helpText` | `string \| null \| undefined` | `undefined` |
75
+ | `helpLink` | `SuiLinkConfig \| string \| null \| undefined` | `undefined` |
76
76
 
77
77
  | output | payload |
78
78
  |---|---|
79
79
  | `helpClicked` | `MouseEvent` |
80
80
  <!-- api:fin -->
81
81
 
82
+ <!-- textos:inicio -->
83
+ ### Textos
84
+
85
+ Lo que el componente escribe solo sale de `provideSuiTextos` (español por defecto, se cambia en vivo; ver `docs/TEXTOS.md`). El input de la última columna le gana al texto global.
86
+
87
+ | clave | por defecto | se pisa con |
88
+ |---|---|---|
89
+ | `menuComandos.navegar` | Navegar | `navigateLabel` |
90
+ | `menuComandos.seleccionar` | Seleccionar | `selectLabel` |
91
+ | `menuComandos.ayuda` | ¿No es lo que buscas? Prueba el | `helpText` (`null` la oculta) |
92
+ | `menuComandos.centroAyuda` | Centro de ayuda | `helpLink` (`null` lo oculta) |
93
+ <!-- textos:fin -->
94
+
82
95
  `helpLink` como string dibuja un botón que solo emite `helpClicked`; como `SuiLinkConfig`
83
96
  (`{ label, href?, target?, rel? }`) dibuja un `<a>`, y un `href` interno navega dentro de la app.
84
97
 
@@ -121,16 +121,24 @@ default de hoy mientras la computadora sigue pendiente). Con `description`: `siz
121
121
  | `buttonClicked` | `MouseEvent` |
122
122
  <!-- api:fin -->
123
123
 
124
+ <!-- textos:inicio -->
125
+ ### Textos
126
+
127
+ Lo que el componente escribe solo sale de `provideSuiTextos` (español por defecto, se cambia en vivo; ver `docs/TEXTOS.md`). El input de la última columna le gana al texto global.
128
+
129
+ | clave | por defecto | se pisa con |
130
+ |---|---|---|
131
+ | `menuComandos.abrir` | Abrir | — |
132
+ <!-- textos:fin -->
133
+
124
134
  Slot: `[slot=brand]` (solo con `type="brand"`). `avatar` acepta un string (la URL) o
125
135
  `SuiAvatarConfig` (`{ src?, initials?, color? }`).
126
136
 
127
137
  ## Trampas
128
138
 
129
- - **`(clicked)` nunca sale.** El componente declara el output pero no lo conecta a ningún clic (el
130
- host no escucha `click` y el template no llama a `onClick`; el spec no lo prueba). Escucha el
131
- `(click)` nativo del host: `<sui-command-menu-item (click)="ir(r)" … />`.
132
- - **Enter y Espacio no hacen nada.** Tiene `role="button"` y `tabindex="0"`, así que se enfoca con
133
- Tab, pero activarlo con teclado lo tiene que cablear quien lo usa (`(keydown.enter)`).
139
+ - **Escucha `(clicked)` y nada más.** Sale con el clic, con Enter y con Espacio (desde 0.26.0;
140
+ antes no estaba conectado y nunca salía). No le sumes `(keydown.enter)` ni `(click)`: Enter ya
141
+ despacha un clic, y con los dos el handler corre dos veces.
134
142
  - **No hay estado «elegido»** para la navegación con flechas: el fondo `weak-50` sale con hover y
135
143
  con `:focus-visible`. Para mover la selección con ↑ ↓ hay que mover el foco entre filas.
136
144
  - `description` solo se ve en `medium`.
@@ -145,15 +153,15 @@ Slot: `[slot=brand]` (solo con `type="brand"`). `avatar` acepta un string (la UR
145
153
  Abrir el resultado
146
154
 
147
155
  ```html
148
- <!-- ❌ mal: (clicked) nunca sale; el componente no lo conecta a ningún clic -->
156
+ <!-- ❌ mal: Enter ya despacha un clic; con (keydown.enter) además, ir() corre dos veces -->
149
157
  <sui-command-menu-item type="left-icon" icon="file-list-3-line" title="Pedidos"
150
- (clicked)="ir('/empresa/pedidos')" />
158
+ (clicked)="ir('/empresa/pedidos')" (keydown.enter)="ir('/empresa/pedidos')" />
151
159
  ```
152
160
 
153
161
  ```html
154
- <!-- ✅ bien: el (click) nativo del host, y Enter cableado a mano -->
162
+ <!-- ✅ bien: (clicked) cubre clic, Enter y Espacio -->
155
163
  <sui-command-menu-item type="left-icon" icon="file-list-3-line" title="Pedidos"
156
- (click)="ir('/empresa/pedidos')" (keydown.enter)="ir('/empresa/pedidos')" />
164
+ (clicked)="ir('/empresa/pedidos')" />
157
165
  ```
158
166
 
159
167
  Resultado con segunda línea
@@ -191,7 +199,7 @@ Ir a una pantalla
191
199
  @for (p of pantallas(); track p.ruta) {
192
200
  <sui-command-menu-item type="left-icon" [icon]="p.icono" [title]="p.nombre"
193
201
  [size]="bp.tactil() ? 'medium' : 'small'"
194
- (click)="ir(p.ruta)" (keydown.enter)="ir(p.ruta)" />
202
+ (clicked)="ir(p.ruta)" />
195
203
  }
196
204
  @if (!bp.isMobile()) {
197
205
  <sui-command-menu-footer navigateLabel="Moverte" selectLabel="Abrir" [helpText]="null" />
@@ -47,7 +47,7 @@ más oscuro) y con texto (lupa naranja, X para limpiar).
47
47
 
48
48
  ## Tamaño en mobile y en web
49
49
 
50
- No tiene tamaños: 48 px de alto (14 + 20 + 13 + 1 de borde), texto 14/20, hasta 600 px de ancho.
50
+ No tiene tamaños: 48 px de alto (14 + 20 + 13 + 1 de borde), texto 14/20 (16 en ≤ 767 px, anti-zoom de iOS, desde 0.26.0), hasta 600 px de ancho.
51
51
 
52
52
  | uso | mobile | web | fuente |
53
53
  |---|---|---|---|
@@ -60,7 +60,7 @@ No tiene tamaños: 48 px de alto (14 + 20 + 13 + 1 de borde), texto 14/20, hasta
60
60
 
61
61
  ### Por dispositivo (D16)
62
62
 
63
- No aplica: no tiene input `size` (48 px de alto y texto 14 en todos los dispositivos), así que D16 no
63
+ No aplica: no tiene input `size` (48 px de alto en todos los dispositivos; el texto sube a 16 solo por ancho, ≤ 767 px), así que D16 no
64
64
  lo mueve. Por dispositivo solo cambian dos cosas, y no con la misma señal:
65
65
 
66
66
  - **La X de limpiar se toca** (`sui-compact-button` `medium`, 20 px): va apagada en el celular **y en
@@ -79,7 +79,8 @@ En master-pedidos: `[shortcut]="!bp.isMobile()" [button]="!bp.tactil()"`, sin `[
79
79
  | input | tipo | default |
80
80
  |---|---|---|
81
81
  | `value` | `string` | `''` |
82
- | `placeholder` | `string` | `'Buscar o ir a…'` |
82
+ | `placeholder` | `string \| null` | `null` |
83
+ | `ariaLabel` | `string \| null` | `null` |
83
84
  | `shortcut` | `boolean` | `true` |
84
85
  | `button` | `boolean` | `true` |
85
86
 
@@ -89,6 +90,17 @@ En master-pedidos: `[shortcut]="!bp.isMobile()" [button]="!bp.tactil()"`, sin `[
89
90
  | `cleared` | `void` |
90
91
  <!-- api:fin -->
91
92
 
93
+ <!-- textos:inicio -->
94
+ ### Textos
95
+
96
+ Lo que el componente escribe solo sale de `provideSuiTextos` (español por defecto, se cambia en vivo; ver `docs/TEXTOS.md`). El input de la última columna le gana al texto global.
97
+
98
+ | clave | por defecto | se pisa con |
99
+ |---|---|---|
100
+ | `comun.limpiarBusqueda` | Limpiar búsqueda | — |
101
+ | `menuComandos.placeholder` | Buscar o ir a… | `placeholder` |
102
+ <!-- textos:fin -->
103
+
92
104
  `value` no es `model`, pero el par `value` + `valueChange` permite `[(value)]`. La X emite
93
105
  `valueChange('')` y `cleared`, y devuelve el foco al campo.
94
106
 
@@ -99,11 +111,9 @@ En master-pedidos: `[shortcut]="!bp.isMobile()" [button]="!bp.tactil()"`, sin `[
99
111
  del input `value`, no de lo escrito.
100
112
  - **El chip dice ⌘K, fijo.** En Windows el atajo es Ctrl+K y el chip no cambia; tampoco hay atajo
101
113
  real: el `keydown` global lo cablea quien arma el command menu.
102
- - **iOS hace zoom al enfocarlo**: el campo está en 14 px y la regla de la app pide 16 px en los
103
- inputs de mobile (CLAUDE.md, «Responsive mobile»).
104
- - **El teclado no trae la lupa**: el `<input>` es `type="text"` sin `enterkeyhint="search"` (CLAUDE.md,
105
- «La lupa del teclado»).
106
- - **El campo no tiene nombre accesible**: solo placeholder, sin `aria-label`.
114
+ - **El nombre accesible es el placeholder** si no pasas `ariaLabel`.
115
+ - Lleva `enterkeyhint="search"` y **no** `inputmode` (D14: `inputmode="search"` le quita la lupa al
116
+ teclado de Android). Texto en 16 en ≤ 767 px. Las dos cosas desde 0.26.0.
107
117
  - No tiene `disabled`.
108
118
  - El ícono de info del estado vacío es decorativo: no abre ayuda ni tiene `aria-label`.
109
119
 
@@ -139,6 +139,8 @@ Implementa `ControlValueAccessor` (el valor es el `value` de la opción).
139
139
  - `type="icon"` **no muestra texto**: solo el ícono de la opción elegida.
140
140
  Pásale `ariaLabel` («Ordenar por»): es el nombre que lee el lector de pantalla (desde el
141
141
  04-10-2026; sin él usa el label de la opción elegida o el placeholder).
142
+ - `type="country"` con **solo la bandera** (sin `value` ni `placeholder`) tampoco tiene texto:
143
+ pásale `ariaLabel` («País»). Si muestra el prefijo («+51») ya tiene nombre.
142
144
  - El menú mide al menos 200 px aunque el campo sea más angosto.
143
145
  - Las opciones miden 36 px en web hasta `medium`; 44 en `large` y en mobile (ver «Tamaño»).
144
146
 
@@ -132,7 +132,7 @@ En master-pedidos: `[size]="bp.tactil() ? 'large' : 'medium'"` (no hay helper pr
132
132
  | `companyLogoSrc` | `string \| null` | `null` |
133
133
  | `brand` | `SuiBrandLogoCompany \| null` | `null` |
134
134
  | `dismissible` | `boolean` | `true` |
135
- | `dismissAriaLabel` | `string` | `'Descartar'` |
135
+ | `dismissAriaLabel` | `string \| null` | `null` |
136
136
  | `size` | `'medium' \| 'large'` | `'medium'` |
137
137
  | `disabled` | `boolean` | `false` |
138
138
 
@@ -141,6 +141,16 @@ En master-pedidos: `[size]="bp.tactil() ? 'large' : 'medium'"` (no hay helper pr
141
141
  | `dismiss` | `MouseEvent` |
142
142
  <!-- api:fin -->
143
143
 
144
+ <!-- textos:inicio -->
145
+ ### Textos
146
+
147
+ Lo que el componente escribe solo sale de `provideSuiTextos` (español por defecto, se cambia en vivo; ver `docs/TEXTOS.md`). El input de la última columna le gana al texto global.
148
+
149
+ | clave | por defecto | se pisa con |
150
+ |---|---|---|
151
+ | `varios.descartar` | Descartar | `dismissAriaLabel` |
152
+ <!-- textos:fin -->
153
+
144
154
  ## Trampas
145
155
 
146
156
  - **`dismissible` arranca en `true`**: sin `[dismissible]="false"` aparece una X que no hace nada.
@@ -111,6 +111,17 @@ En master-pedidos: no hay helper ni `[size]`; solo `type` y `text`.
111
111
  | `itemClick` | `{ item: SuiContentDividerItem; index: number; event: MouseEvent }` |
112
112
  <!-- api:fin -->
113
113
 
114
+ <!-- textos:inicio -->
115
+ ### Textos
116
+
117
+ Lo que el componente escribe solo sale de `provideSuiTextos` (español por defecto, se cambia en vivo; ver `docs/TEXTOS.md`). El input de la última columna le gana al texto global.
118
+
119
+ | clave | por defecto | se pisa con |
120
+ |---|---|---|
121
+ | `comun.accion` | Acción | `ariaLabel` |
122
+ | `comun.accionN` | Acción {n} | `ariaLabel` de cada ítem |
123
+ <!-- textos:fin -->
124
+
114
125
  Outputs: `(buttonClick)` en `icon-button` y `text-button`; `(itemClick)` con
115
126
  `{ item, index, event }` en los `-group`.
116
127
 
@@ -139,14 +139,25 @@ En una fila o en un formulario `[size]="bp.tactil() ? 'xlarge' : 'medium'"`; jun
139
139
  | `step` | `number` | `1` |
140
140
  | `disabled` | `boolean` | `false` |
141
141
  | `ariaLabel` | `string \| null` | `null` |
142
- | `decrementLabel` | `string` | `'Disminuir'` |
143
- | `incrementLabel` | `string` | `'Aumentar'` |
142
+ | `decrementLabel` | `string \| null` | `null` |
143
+ | `incrementLabel` | `string \| null` | `null` |
144
144
 
145
145
  | output | payload |
146
146
  |---|---|
147
147
  | `valueChange` | `number` |
148
148
  <!-- api:fin -->
149
149
 
150
+ <!-- textos:inicio -->
151
+ ### Textos
152
+
153
+ Lo que el componente escribe solo sale de `provideSuiTextos` (español por defecto, se cambia en vivo; ver `docs/TEXTOS.md`). El input de la última columna le gana al texto global.
154
+
155
+ | clave | por defecto | se pisa con |
156
+ |---|---|---|
157
+ | `contador.disminuir` | Disminuir | `decrementLabel` |
158
+ | `contador.aumentar` | Aumentar | `incrementLabel` |
159
+ <!-- textos:fin -->
160
+
150
161
  `value` es un `input` y `valueChange` un `output`: `[(value)]` funciona. Implementa
151
162
  `ControlValueAccessor`.
152
163
 
@@ -103,6 +103,17 @@ devuelve `bp.sizeControl()`, que también sirve).
103
103
  | `next` | `void` |
104
104
  <!-- api:fin -->
105
105
 
106
+ <!-- textos:inicio -->
107
+ ### Textos
108
+
109
+ Lo que el componente escribe solo sale de `provideSuiTextos` (español por defecto, se cambia en vivo; ver `docs/TEXTOS.md`). El input de la última columna le gana al texto global.
110
+
111
+ | clave | por defecto | se pisa con |
112
+ |---|---|---|
113
+ | `fecha.mesAnterior` | Mes anterior | — |
114
+ | `fecha.mesSiguiente` | Mes siguiente | — |
115
+ <!-- textos:fin -->
116
+
106
117
  ## Trampas
107
118
 
108
119
  - **`leftIcon` y `rightIcon` son booleanos**, no nombres de ícono: prenden o apagan las flechas.
@@ -185,6 +185,20 @@ en la columna de la derecha, se usa (aprobado por el dueño, 03-10-2026).
185
185
  Outputs: ninguno.
186
186
  <!-- api:fin -->
187
187
 
188
+ <!-- textos:inicio -->
189
+ ### Textos
190
+
191
+ Lo que el componente escribe solo sale de `provideSuiTextos` (español por defecto, se cambia en vivo; ver `docs/TEXTOS.md`). El input de la última columna le gana al texto global.
192
+
193
+ | clave | por defecto | se pisa con |
194
+ |---|---|---|
195
+ | `fecha.rangos` | Rangos predefinidos | — |
196
+ | `fecha.eligePeriodo` | Elige el período | `placeholder` |
197
+ | `fecha.eligeFecha` | Elige una fecha | `placeholder` |
198
+ | `fecha.elegirPeriodo` | Elegir período | — |
199
+ | `fecha.elegirFecha` | Elegir fecha | — |
200
+ <!-- textos:fin -->
201
+
188
202
  Es `ControlValueAccessor`: con `mode="date"` el valor es `Date | null`; con `mode="range"`,
189
203
  `{ start, end }`.
190
204
 
@@ -218,6 +232,9 @@ interface DatepickerPreset { id: DatepickerPresetId; label: string; resolve: (ho
218
232
  anuncia con la fecha completa («martes 7 de octubre de 2026»), hoy lleva `aria-current="date"`,
219
233
  inicio y fin del rango salen seleccionados, y las flechas mueven el foco entre días (← → un
220
234
  día, ↑ ↓ una semana, Inicio/Fin a la punta de la semana). Las flechas no cambian de mes.
235
+ El pie (Cancelar/Aplicar) y la lista de rangos predefinidos son `div` (la lista con
236
+ `role="group"`), no `<footer>`/`<aside>`: esos crean landmarks de página y dos pickers en la
237
+ misma pantalla los duplicaban (09-10-2026).
221
238
  - Cierra con clic afuera y con Escape, y eso **descarta** la elección (como Cancelar).
222
239
  - Las piezas internas son `sui-date-selector`, `sui-day-label`, `sui-day-cell` y
223
240
  `sui-period-range`: no se arman a mano.