@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
@@ -125,6 +125,16 @@ dónde sale. Si la grilla va con `sui-day-label`, la cabecera también tiene que
125
125
  | `clicked` | `MouseEvent` |
126
126
  <!-- api:fin -->
127
127
 
128
+ <!-- textos:inicio -->
129
+ ### Textos
130
+
131
+ 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.
132
+
133
+ | clave | por defecto | se pisa con |
134
+ |---|---|---|
135
+ | `fecha.dia` | Día {n} | `ariaLabel` |
136
+ <!-- textos:fin -->
137
+
128
138
  ## Trampas
129
139
 
130
140
  - **`day` es el número del día, no una fecha.** Toda la lógica (qué día es, si es hoy) la hace
@@ -110,8 +110,8 @@ Igual, el par de botones va en `sui-modal-footer` (ver arriba), cuyo canon es `l
110
110
  |---|---|---|
111
111
  | `type` | `'stretch' \| 'basic' \| 'checkbox' \| 'toggle' \| 'stepper' \| 'link-button'` | `'stretch'` |
112
112
  | `size` | `'medium' \| 'large'` | `'medium'` |
113
- | `primaryButton` | `SuiFooterButtonConfig \| string` | `'Continuar'` |
114
- | `secondaryButton` | `SuiFooterButtonConfig \| string \| null` | `'Cancelar'` |
113
+ | `primaryButton` | `SuiFooterButtonConfig \| string \| null` | `null` |
114
+ | `secondaryButton` | `SuiFooterButtonConfig \| string \| null \| undefined` | `undefined` |
115
115
  | `checkboxLabel` | `string` | `''` |
116
116
  | `checkboxActive` · model, `[(checkboxActive)]` | `boolean` | `false` |
117
117
  | `toggleLabel` | `string` | `''` |
@@ -126,6 +126,19 @@ Igual, el par de botones va en `sui-modal-footer` (ver arriba), cuyo canon es `l
126
126
  | `linkClick` | `MouseEvent` |
127
127
  <!-- api:fin -->
128
128
 
129
+ <!-- textos:inicio -->
130
+ ### Textos
131
+
132
+ 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.
133
+
134
+ | clave | por defecto | se pisa con |
135
+ |---|---|---|
136
+ | `comun.progreso` | Progreso | — |
137
+ | `comun.pasoDe` | Paso {actual} de {total} | — |
138
+ | `comun.continuar` | Continuar | `primaryButton` |
139
+ | `comun.cancelar` | Cancelar | `secondaryButton` (`null` lo oculta) |
140
+ <!-- textos:fin -->
141
+
129
142
  ```ts
130
143
  type SuiDrawerFooterType = 'stretch' | 'basic' | 'checkbox' | 'toggle' | 'stepper' | 'link-button';
131
144
  interface SuiFooterButtonConfig { label: string; disabled?: boolean; color?: 'primary' | 'error'; }
@@ -146,6 +159,8 @@ interface SuiFooterButtonConfig { label: string; disabled?: boolean; color?: 'pr
146
159
  Para el lector es un `progressbar` «Progreso» que dice «Paso 2 de 4» (`aria-valuetext`, desde
147
160
  el 04-10-2026).
148
161
  - `checkboxActive` y `toggleActive` son `model`: `[(checkboxActive)]`.
162
+ - `checkboxLabel` / `toggleLabel` son el nombre del control para el lector (`aria-labelledby`,
163
+ desde el 09-10-2026): sin texto, el checkbox o el switch queda sin nombre.
149
164
  - El borde de arriba ya viene puesto: no le sumes otro.
150
165
 
151
166
  ## Bien y mal
@@ -114,7 +114,7 @@ En master-pedidos: sin helper; `@if (!bp.isMobile())` alrededor y `size="large"`
114
114
  | `link` | `SuiLinkConfig \| string \| null` | `null` |
115
115
  | `showDismiss` | `boolean` | `true` |
116
116
  | `showDivider` | `boolean` | `false` |
117
- | `dismissAriaLabel` | `string` | `'Cerrar'` |
117
+ | `dismissAriaLabel` | `string \| null` | `null` |
118
118
 
119
119
  | output | payload |
120
120
  |---|---|
@@ -122,6 +122,16 @@ En master-pedidos: sin helper; `@if (!bp.isMobile())` alrededor y `size="large"`
122
122
  | `linkClick` | `MouseEvent` |
123
123
  <!-- api:fin -->
124
124
 
125
+ <!-- textos:inicio -->
126
+ ### Textos
127
+
128
+ 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.
129
+
130
+ | clave | por defecto | se pisa con |
131
+ |---|---|---|
132
+ | `comun.cerrar` | Cerrar | `dismissAriaLabel` |
133
+ <!-- textos:fin -->
134
+
125
135
  ```ts
126
136
  type SuiDrawerHeaderType = 'basic' | 'left-icon';
127
137
  type SuiDrawerHeaderSize = 'small' | 'large' | 'xlarge';
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: sui-dropdown-menu
3
3
  selector: sui-dropdown-menu, ng-template[suiDropdownPanel]
4
- resumen: 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
4
+ resumen: 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
5
5
  alternativas: [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)]
6
6
  ---
7
7
 
@@ -195,6 +195,14 @@ panel va en un `<ng-template suiDropdownPanel>` (hay que importar `SuiDropdownPa
195
195
  dentro de un modal queda detrás. La copia de la app no trae ese bloque: no se verificó en la
196
196
  app, pero es lo primero que hay que mirar si un menú «no abre» dentro de un modal.
197
197
  - El host es `display: contents`: no ocupa caja. Para alinearlo, se alinea el trigger.
198
+ - **Accesibilidad (09-10-2026).** `aria-haspopup` y `aria-expanded` se ponen en el control que se
199
+ proyecta como trigger (el `<button>`, el `sui-button`, el `<button>` interno del
200
+ `sui-compact-button`), no en el span que lo envuelve. Si el trigger no es un botón ni un enlace
201
+ (un `sui-avatar` suelto, un `div` con `tabindex`), no recibe esos atributos y además no se
202
+ alcanza bien con teclado: envuélvelo en un botón. Sin `[origin]` y con
203
+ `popupRole="menu"`, el panel abierto lleva `role="menu"`; por eso un panel que mezcla items con
204
+ un buscador u otro campo no es un menú válido: ahí va `popupRole="true"` y los items van dentro
205
+ de un contenedor propio con `role="menu"`.
198
206
 
199
207
  ## Bien y mal
200
208
 
@@ -53,14 +53,14 @@ para limpiar.
53
53
 
54
54
  ## Tamaño en mobile y en web
55
55
 
56
- No tiene tamaños: 36 px de alto (8 + 20 + 8), texto 14/20, en los dos.
56
+ No tiene tamaños: 36 px de alto (8 + 20 + 8), texto 14/20 en web y 16 en ≤ 767 px (anti-zoom de iOS, desde 0.26.0).
57
57
 
58
58
  | uso | mobile | web | fuente |
59
59
  |---|---|---|---|
60
60
  | buscador del menú | no se usa: `sui-modal-sheet` con `sui-input type="search"` arriba | 36, fijo | mobile: canon (aprobado por el dueño, 05-10-2026, bloque B7) y D16 (dueño 05-10-2026: lo que se toca llega a 44, y este campo mide 36) · web: lo que hay en el DS |
61
61
 
62
- En mobile tiene dos problemas que el componente no resuelve (ver «Trampas»): el texto de 14 px hace
63
- que iOS haga zoom al enfocarlo, y el teclado no muestra la lupa. Para buscar en una lista larga en
62
+ Desde 0.26.0 el texto sube a 16 en ≤ 767 px (sin zoom de iOS) y el teclado trae la lupa
63
+ (`enterkeyhint="search"`), pero sigue midiendo 36, bajo el piso de 44. Para buscar en una lista larga en
64
64
  mobile, mejor un `sui-modal-sheet` con `sui-input type="search"` arriba. Canon (aprobado por el dueño, 05-10-2026, bloque B7).
65
65
 
66
66
  ### Por dispositivo (D16)
@@ -82,8 +82,9 @@ En master-pedidos: `@if (bp.tactil())` → sheet con `<sui-input type="search" [
82
82
  | input | tipo | default |
83
83
  |---|---|---|
84
84
  | `value` · model, `[(value)]` | `string` | `''` |
85
- | `placeholder` | `string` | `'Buscar…'` |
85
+ | `placeholder` | `string \| null` | `null` |
86
86
  | `disabled` | `boolean` | `false` |
87
+ | `ariaLabel` | `string \| null` | `null` |
87
88
 
88
89
  | output | payload |
89
90
  |---|---|
@@ -91,6 +92,17 @@ En master-pedidos: `@if (bp.tactil())` → sheet con `<sui-input type="search" [
91
92
  | `valueChanged` | `string` |
92
93
  <!-- api:fin -->
93
94
 
95
+ <!-- textos:inicio -->
96
+ ### Textos
97
+
98
+ 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.
99
+
100
+ | clave | por defecto | se pisa con |
101
+ |---|---|---|
102
+ | `comun.limpiarBusqueda` | Limpiar búsqueda | — |
103
+ | `comun.buscar` | Buscar… | `placeholder` |
104
+ <!-- textos:fin -->
105
+
94
106
  `valueChanged` sale con cada tecla (lo mismo que `value`, como evento). `cleared` sale al tocar la X,
95
107
  que además deja `value` en `''`.
96
108
 
@@ -98,11 +110,10 @@ que además deja `value` en `''`.
98
110
 
99
111
  - **No filtra.** Hay que filtrar la lista con el `value` (un `computed` sobre las opciones).
100
112
  - **El default del placeholder es genérico** (`'Buscar…'`, en español desde el 04-10-2026): pásalo siempre diciendo qué se busca.
101
- - **iOS hace zoom al enfocarlo**: el campo está en 14 px y la regla de la app pide 16 px en los
102
- inputs de mobile (CLAUDE.md, «Responsive mobile»). Hoy no hay forma de cambiarlo desde afuera.
103
- - **El teclado no trae la lupa**: el `<input>` es `type="text"` sin `enterkeyhint="search"` (la regla
104
- de la app para buscadores, CLAUDE.md «La lupa del teclado»).
105
- - **El campo no tiene nombre accesible**: solo placeholder, sin `aria-label`.
113
+ - **El nombre accesible es el placeholder** si no pasas `ariaLabel`. Con un placeholder de ejemplo
114
+ («Ej. Juan…») pasa `ariaLabel` con lo que se busca.
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.
106
117
  - **Al abrir el menú el foco no entra al buscador**: quien quiera escribir de una tiene que enfocarlo
107
118
  a mano.
108
119
  - El `<input>` no tiene `(keydown)`: Enter no hace nada salvo que quien lo usa lo escuche.
@@ -140,7 +151,7 @@ Filtrar la lista
140
151
  Buscar en el celular
141
152
 
142
153
  ```html
143
- <!-- ❌ mal: 36 px sin lupa en el teclado, y iOS hace zoom al enfocarlo -->
154
+ <!-- ❌ mal: mide 36, bajo el piso de 44 en lo que se toca (D1, D16) -->
144
155
  <sui-dropdown-search [(value)]="busqueda" placeholder="Buscar repartidor…" />
145
156
  ```
146
157
 
@@ -106,6 +106,17 @@ En master-pedidos: `size="medium"` fijo, o se omite (es el default). No hay help
106
106
  Outputs: ninguno.
107
107
  <!-- api:fin -->
108
108
 
109
+ <!-- textos:inicio -->
110
+ ### Textos
111
+
112
+ 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.
113
+
114
+ | clave | por defecto | se pisa con |
115
+ |---|---|---|
116
+ | `archivo.tipo` | Archivo {ext} | — |
117
+ | `archivo.icono` | Archivo | — |
118
+ <!-- textos:fin -->
119
+
109
120
  ## Trampas
110
121
 
111
122
  - **La extensión se corta a 4 letras** y va en mayúsculas: `.jpeg` → `JPEG`, `.numbers` → `NUMB`.
@@ -133,7 +133,7 @@ en la columna de la derecha, se usa (aprobado por el dueño, 03-10-2026).
133
133
  | `maxSize` | `number` | `0` |
134
134
  | `title` | `string \| null` | `null` |
135
135
  | `subtitle` | `string \| null` | `null` |
136
- | `browseLabel` | `string` | `'Buscar archivo'` |
136
+ | `browseLabel` | `string \| null` | `null` |
137
137
  | `disabled` | `boolean` | `false` |
138
138
  | `error` | `boolean` | `false` |
139
139
  | `errorMessage` | `string \| null` | `null` |
@@ -145,6 +145,19 @@ en la columna de la derecha, se usa (aprobado por el dueño, 03-10-2026).
145
145
  | `errorEvent` | `string` |
146
146
  <!-- api:fin -->
147
147
 
148
+ <!-- textos:inicio -->
149
+ ### Textos
150
+
151
+ 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.
152
+
153
+ | clave | por defecto | se pisa con |
154
+ |---|---|---|
155
+ | `archivo.buscar` | Buscar archivo | `browseLabel` |
156
+ | `archivo.soltarVarios` | Arrastra y suelta los archivos aquí | `title` |
157
+ | `archivo.soltarUno` | Arrastra y suelta el archivo aquí | `title` |
158
+ | `archivo.oClic` | o haz clic para seleccionar | `subtitle` |
159
+ <!-- textos:fin -->
160
+
148
161
  ## Trampas
149
162
 
150
163
  - `browseLabel` dice «Buscar archivo» por defecto (antes «Browse File»).
@@ -100,10 +100,10 @@ en la columna de la derecha, se usa (aprobado por el dueño, 03-10-2026).
100
100
  | `progress` | `number` | `10` |
101
101
  | `statusText` | `string \| null` | `null` |
102
102
  | `showStatus` | `boolean` | `true` |
103
- | `cancelLabel` | `string` | `'Cancelar subida'` |
104
- | `removeLabel` | `string` | `'Quitar archivo'` |
105
- | `progressLabel` | `string` | `'Progreso de la subida'` |
106
- | `retryLabel` | `string` | `'Reintentar'` |
103
+ | `cancelLabel` | `string \| null` | `null` |
104
+ | `removeLabel` | `string \| null` | `null` |
105
+ | `progressLabel` | `string \| null` | `null` |
106
+ | `retryLabel` | `string \| null` | `null` |
107
107
 
108
108
  | output | payload |
109
109
  |---|---|
@@ -111,6 +111,22 @@ en la columna de la derecha, se usa (aprobado por el dueño, 03-10-2026).
111
111
  | `retryClicked` | `void` |
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
+ | `archivo.cancelarSubida` | Cancelar subida | `cancelLabel` |
122
+ | `archivo.quitar` | Quitar archivo | `removeLabel` |
123
+ | `archivo.progreso` | Progreso de la subida | `progressLabel` |
124
+ | `archivo.reintentar` | Reintentar | `retryLabel` |
125
+ | `archivo.subiendo` | Subiendo… | `statusText` |
126
+ | `archivo.completado` | Completado | `statusText` |
127
+ | `archivo.fallo` | Falló | `statusText` |
128
+ <!-- textos:fin -->
129
+
114
130
  ```ts
115
131
  interface SuiFileUploadCardFile { filename: string; extension?: string; }
116
132
  ```
@@ -74,7 +74,7 @@ Ninguna pieza cambia sola en mobile, salvo la barra, que apila sus dos lados por
74
74
  |---|---|---|---|
75
75
  | `sui-filter-bar` | sin tamaño; los controles adentro van `large` (48) | sin tamaño; los controles `medium` (40) | canon (D16, dueño 05-10-2026) |
76
76
  | `sui-filter-item` | 36 de alto, no se puede cambiar: debajo de los 44 del dedo | 36 | pendiente (P19: área invisible de 44 o no usarlo en mobile) |
77
- | `sui-filter-panel-footer` | `medium` (40) es lo más grande que tiene; en mobile, mejor `sui-modal-footer` (48) dentro de un `sui-modal-sheet` | `medium` (40) | canon para el 48 de mobile (CLAUDE.md: footer de modal `large`); web `medium` (40): canon (dueño, 05-10-2026, D16), reemplaza el `small` del bloque B1 (`small` es el default del componente: hay que pasarlo) |
77
+ | `sui-filter-panel-footer` | `large` (48, desde 0.26.0); o el panel en un `sui-modal-sheet` con `sui-modal-footer` (48) | `medium` (40, el default desde 0.26.0) | canon para el 48 de mobile (CLAUDE.md: footer de modal `large`, piso de 44); web `medium` (40): canon (dueño, 05-10-2026, D16), reemplaza el `small` del bloque B1 |
78
78
  | `sui-filter-panel-header` | sin tamaño | sin tamaño | — |
79
79
 
80
80
  ```html
@@ -95,18 +95,19 @@ controles que se proyectan en la barra. Fuente: D16, dueño 05-10-2026 (fila `fi
95
95
  |---|---|---|
96
96
  | celular chico (< 380) | sin regla en la tabla (`—`) | la fila `filter` de D16 no lo define; `bp.sizeControl()` da `large`, igual que en el celular, mientras el DS no estire a 44 el área de los `medium` |
97
97
  | celular (380–767) | controles `large` · segmented `2xlarge` | el panel va en un `sui-modal-sheet` con `sui-modal-footer` (48), no con este pie; el área de 44 de `sui-filter-item` está pendiente (P19) |
98
- | tablet táctil (`pointer: coarse`) | controles `large` · segmented `2xlarge` | se toca como un celular: el panel lleva `sui-modal-footer` (48) en vez de este pie, que llega solo a `medium` (40). D16 no fija el pie en la tablet; esto sigue la columna «mobile» de arriba. El área de 44 de `sui-filter-item` está pendiente (P19), igual que en el celular |
99
- | computadora (`pointer: fine`) | controles `medium` · `sui-filter-panel-footer` `medium` | el default del pie es `small`: hay que pasarlo |
98
+ | tablet táctil (`pointer: coarse`) | controles `large` · segmented `2xlarge` · pie `large` | se toca como un celular: el pie va en `large` (48) o el panel lleva `sui-modal-footer`. D16 no fija el pie en la tablet; esto sigue la columna «mobile» de arriba. El área de 44 de `sui-filter-item` está pendiente (P19), igual que en el celular |
99
+ | computadora (`pointer: fine`) | controles `medium` · `sui-filter-panel-footer` `medium` | `medium` es el default del pie desde 0.26.0 (antes era `small` y había que pasarlo) |
100
100
 
101
101
  En el ultrawide, igual que en la computadora: el espacio extra va al ancho de la tabla, no a
102
102
  controles más grandes.
103
103
 
104
104
  En master-pedidos: `[size]="bp.sizeControl()"` en el `sui-input`, `sui-select` o
105
105
  `sui-compact-select` de la barra, `[size]="bp.tactil() ? '2xlarge' : 'medium'"` en el
106
- `sui-segmented-control` (no tiene helper) y `size="medium"` fijo en `sui-filter-panel-footer`,
107
- que solo se usa en la computadora (solo tiene `small` y `medium`: `bp.sizeControl()` le pasaría un
108
- `large` que no existe); con `bp.tactil()` el panel lleva `sui-modal-footer` en su lugar. No uses
109
- `bp.isMobile() ? 'large' : 'medium'`: deja la tablet táctil en `medium`.
106
+ `sui-segmented-control` (no tiene helper) y `[size]="bp.tactil() ? 'large' : 'medium'"` en
107
+ `sui-filter-panel-footer` (`large` existe desde 0.26.0; con la versión anterior, `size="medium"` fijo y
108
+ `sui-modal-footer` en táctil). No uses `bp.isMobile() ? 'large' : 'medium'`: deja la tablet táctil en
109
+ `medium`. Los botones del pie van separados 12 px, como todo grupo de botones (canon de
110
+ `sui-modal-footer`; el Figma dibuja 16).
110
111
 
111
112
  ## API
112
113
 
@@ -138,9 +139,9 @@ Outputs: ninguno.
138
139
 
139
140
  | input | tipo | default |
140
141
  |---|---|---|
141
- | `clearButton` | `SuiFooterButtonConfig \| string` | `'Limpiar'` |
142
- | `applyButton` | `SuiFooterButtonConfig \| string` | `'Aplicar'` |
143
- | `size` | `'small' \| 'medium'` | `'small'` |
142
+ | `clearButton` | `SuiFooterButtonConfig \| string \| null` | `null` |
143
+ | `applyButton` | `SuiFooterButtonConfig \| string \| null` | `null` |
144
+ | `size` | `'small' \| 'medium' \| 'large'` | `'medium'` |
144
145
 
145
146
  | output | payload |
146
147
  |---|---|
@@ -160,6 +161,17 @@ Outputs: ninguno.
160
161
  | `clearClick` | `void` |
161
162
  <!-- api:fin -->
162
163
 
164
+ <!-- textos:inicio -->
165
+ ### Textos
166
+
167
+ 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.
168
+
169
+ | clave | por defecto | se pisa con |
170
+ |---|---|---|
171
+ | `filtro.limpiar` | Limpiar | `clearButton` · `clear` del header |
172
+ | `filtro.aplicar` | Aplicar | `applyButton` |
173
+ <!-- textos:fin -->
174
+
163
175
  Slots de `sui-filter-bar`: `[slot=start]`, `[slot=end]` y el contenido por defecto (fila única si no
164
176
  se usan los slots). El label de `sui-filter-item` es su contenido proyectado. `clearButton` y
165
177
  `applyButton` aceptan un string o `SuiFooterButtonConfig` (`{ label, disabled? }`, la misma interfaz
@@ -186,16 +198,16 @@ de `sui-drawer-footer`).
186
198
  > Hoy no hay usos de estas piezas fuera de la vitrina `master-preview`: los pares salen de las
187
199
  > trampas de este README.
188
200
 
189
- Pie del panel en la computadora
201
+ Pie del panel
190
202
 
191
203
  ```html
192
- <!-- ❌ mal: sin size queda en small (36), y D16 no usa small -->
193
- <sui-filter-panel-footer (clear)="limpiar()" (apply)="aplicar()" />
204
+ <!-- ❌ mal: small (36) no se usa en ningún dispositivo (D16) -->
205
+ <sui-filter-panel-footer size="small" (clear)="limpiar()" (apply)="aplicar()" />
194
206
  ```
195
207
 
196
208
  ```html
197
- <!-- ✅ bien -->
198
- <sui-filter-panel-footer size="medium" (clear)="limpiar()" (apply)="aplicar()" />
209
+ <!-- ✅ bien: medium en la computadora, large (48) donde se toca -->
210
+ <sui-filter-panel-footer [size]="bp.tactil() ? 'large' : 'medium'" (clear)="limpiar()" (apply)="aplicar()" />
199
211
  ```
200
212
 
201
213
  «Limpiar» en dos lugares
@@ -186,6 +186,25 @@ en la columna de la derecha, se usa (aprobado por el dueño, 03-10-2026).
186
186
  | `errorEvent` | `string` |
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
+ | `imagen.vistaPrevia` | Vista previa | — |
197
+ | `imagen.editar` | Editar imagen | — |
198
+ | `imagen.quitar` | Quitar imagen | — |
199
+ | `imagen.cambiar` | Cambiar imagen | — |
200
+ | `imagen.subeFoto` | Sube tu foto | `title` |
201
+ | `imagen.subeLogo` | Sube tu logo | `title` |
202
+ | `imagen.tipoNoSoportado` | Tipo no soportado. Usa JPG, PNG, WebP, GIF o HEIC. | — |
203
+ | `imagen.superaTamano` | Imagen supera el tamaño máximo ({tamano}) | — |
204
+ | `imagen.formatosMax` | {formatos} — máx {tamano} | `subtitle` |
205
+ | `imagen.formatos` | JPG, PNG, WebP o HEIC | `subtitle` |
206
+ <!-- textos:fin -->
207
+
189
208
  Método público: `abrirSelector()` — abre el selector de archivo (para el lápiz cuando no hay
190
209
  recortador; se llama con una referencia: `#qr` → `qr.abrirSelector()`).
191
210
 
@@ -117,8 +117,8 @@ en la columna de la derecha, se usa (aprobado por el dueño, 03-10-2026).
117
117
  |---|---|---|
118
118
  | `imageUrl` | `string \| null` | `null` |
119
119
  | `companyLogoSrc` | `string \| null` | `null` |
120
- | `title` | `string` | `'Subir imagen'` |
121
- | `subtitle` | `string` | `'Mín. 400x400px, PNG o JPEG'` |
120
+ | `title` | `string \| null` | `null` |
121
+ | `subtitle` | `string \| null` | `null` |
122
122
  | `alignment` | `'vertical' \| 'horizontal'` | `'vertical'` |
123
123
  | `type` | `'avatar' \| 'company'` | `'avatar'` |
124
124
  | `labels` | `SuiImageUploadProfileLabels` | `{}` |
@@ -132,6 +132,20 @@ en la columna de la derecha, se usa (aprobado por el dueño, 03-10-2026).
132
132
  | `change` | `void` |
133
133
  <!-- api:fin -->
134
134
 
135
+ <!-- textos:inicio -->
136
+ ### Textos
137
+
138
+ 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.
139
+
140
+ | clave | por defecto | se pisa con |
141
+ |---|---|---|
142
+ | `imagen.perfilTitulo` | Subir imagen | `title` |
143
+ | `imagen.perfilSubtitulo` | Mín. 400x400px, PNG o JPEG | `subtitle` |
144
+ | `imagen.subir` | Subir | `labels.upload` |
145
+ | `imagen.eliminar` | Eliminar | `labels.remove` |
146
+ | `imagen.cambiarCorto` | Cambiar | `labels.change` |
147
+ <!-- textos:fin -->
148
+
135
149
  Es `ControlValueAccessor` de la URL (`string | null`); `remove` la deja en `null`.
136
150
 
137
151
  ```ts
@@ -85,11 +85,11 @@ si no, `sui-inline-input`.
85
85
  | input | tipo | default |
86
86
  |---|---|---|
87
87
  | `value` · model, `[(value)]` | `string` | `''` |
88
- | `placeholder` | `string` | `'Escribe aquí…'` |
88
+ | `placeholder` | `string \| null` | `null` |
89
89
  | `leftIcon` | `string \| false` | `'user-3-line'` |
90
90
  | `error` | `boolean` | `false` |
91
91
  | `disabled` | `boolean` | `false` |
92
- | `editLabel` | `string` | `'Editar'` |
92
+ | `editLabel` | `string \| null` | `null` |
93
93
 
94
94
  | output | payload |
95
95
  |---|---|
@@ -98,6 +98,19 @@ si no, `sui-inline-input`.
98
98
  | `edit` | `void` |
99
99
  <!-- api:fin -->
100
100
 
101
+ <!-- textos:inicio -->
102
+ ### Textos
103
+
104
+ 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.
105
+
106
+ | clave | por defecto | se pisa con |
107
+ |---|---|---|
108
+ | `comun.cancelar` | Cancelar | — |
109
+ | `comun.confirmar` | Confirmar | — |
110
+ | `comun.escribeAqui` | Escribe aquí… | `placeholder` |
111
+ | `comun.editar` | Editar | `editLabel` |
112
+ <!-- textos:fin -->
113
+
101
114
  Implementa `ControlValueAccessor`.
102
115
 
103
116
  ## Trampas
@@ -213,6 +213,18 @@ en la columna de la derecha, se usa.
213
213
  Outputs: ninguno.
214
214
  <!-- api:fin -->
215
215
 
216
+ <!-- textos:inicio -->
217
+ ### Textos
218
+
219
+ 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.
220
+
221
+ | clave | por defecto | se pisa con |
222
+ |---|---|---|
223
+ | `comun.limpiar` | Limpiar | — |
224
+ | `input.ocultarContrasena` | Ocultar contraseña | — |
225
+ | `input.mostrarContrasena` | Mostrar contraseña | — |
226
+ <!-- textos:fin -->
227
+
216
228
  `value` es `model<string>`: `[(value)]` o `[value]` + `(valueChange)`. También funciona con
217
229
  `formControlName` y `[(ngModel)]` (implementa `ControlValueAccessor`).
218
230
 
@@ -137,6 +137,16 @@ En master-pedidos: `[size]="bp.sizeControl()"`. El `type` sigue por ancho
137
137
  | `manageClick` | `MouseEvent` |
138
138
  <!-- api:fin -->
139
139
 
140
+ <!-- textos:inicio -->
141
+ ### Textos
142
+
143
+ 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.
144
+
145
+ | clave | por defecto | se pisa con |
146
+ |---|---|---|
147
+ | `varios.administrar` | Administrar | `manage` |
148
+ <!-- textos:fin -->
149
+
140
150
  ```ts
141
151
  interface SuiIntegrationManageConfig { label?: string; icon?: string; } // icon default 'settings-2-line'
142
152
  ```
@@ -2,7 +2,7 @@
2
2
  name: sui-modal
3
3
  selector: sui-modal
4
4
  resumen: Diálogo que se abre encima de la pantalla y pide terminar o cerrar algo antes de seguir
5
- alternativas: [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)]
5
+ alternativas: [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)]
6
6
  ---
7
7
 
8
8
  # sui-modal
@@ -28,6 +28,43 @@ comprobante, el detalle de un pedido).
28
28
 
29
29
  Regla corta: **hay que responder → modal · se mira junto al fondo → sheet · solo se avisa → alert/toast.**
30
30
 
31
+ ## Celular, tablet y computadora: modal u hoja
32
+
33
+ Canon (decisión del dueño, 08-10-2026). La superficie depende del **dispositivo**, y el ancho del
34
+ modal depende de **cuánto contenido** hay:
35
+
36
+ | dispositivo | contenido para mirar o elegir (listas, grillas, detalles) | formularios y confirmaciones |
37
+ |---|---|---|
38
+ | celular (≤ 768 px) | `sui-modal-sheet` | `sui-modal` (a pantalla completa en la app) |
39
+ | tablet y computadora (> 768 px) | **`sui-modal` centrado, nunca una hoja** | `sui-modal` centrado |
40
+
41
+ En tablet y computadora el ancho sale de la cantidad de contenido:
42
+
43
+ - **mucho contenido** (una grilla, una lista larga, más de una pantalla de opciones): el más ancho,
44
+ `xlarge`;
45
+ - **poco contenido** (unas frases, un formulario corto): `small` o `medium`;
46
+ - entre medio, `large`. La tabla de `size` de abajo tiene los casos.
47
+
48
+ Cuando el mismo contenido se abre en los dos, va en un `ng-template` y se elige la superficie con
49
+ el breakpoint de la app:
50
+
51
+ ```html
52
+ <ng-template #contenido> … </ng-template>
53
+
54
+ @if (bp.isMobile()) {
55
+ <sui-modal-sheet [open]="abierta()" (closed)="abierta.set(false)">
56
+ <ng-container *ngTemplateOutlet="contenido" />
57
+ </sui-modal-sheet>
58
+ } @else {
59
+ <sui-modal [open]="abierta()" size="xlarge" title="Categorías" (closed)="abierta.set(false)">
60
+ <ng-container *ngTemplateOutlet="contenido" />
61
+ </sui-modal>
62
+ }
63
+ ```
64
+
65
+ Ejemplo real: «Ver todo» de las categorías del inicio del cliente
66
+ (`pages/customer/store-browser/store-browser.html`), con más de 30 rubros en `xlarge`.
67
+
31
68
  ## Qué variante usar
32
69
 
33
70
  El modal tiene tres decisiones: **cómo se presenta en mobile** (`fullscreen`), **qué tan ancho
@@ -89,14 +126,15 @@ de 0.21.0 se abren **a pantalla completa** dentro de la app):
89
126
  ### `size` — el ancho en desktop
90
127
 
91
128
  En mobile **no se usa**: a 768 px o menos el contenedor toma `max-width: 100%` (centrado) o la
92
- pantalla entera (fullscreen). Elegirlo es una decisión de desktop.
129
+ pantalla entera (fullscreen). Elegirlo es una decisión de desktop, y la regla es **cuánto
130
+ contenido hay**: mucho → el más ancho; poco → el más angosto que lo contenga sin apretarlo.
93
131
 
94
132
  | size | cuándo | ejemplo real de la app | fuente |
95
133
  |---|---|---|---|
96
134
  | `small` (400) | confirmación o aviso: título, una o dos frases y los botones | `pages/customer/order-tracking/order-tracking.html:756` | canon (aprobado por el dueño, 05-10-2026, bloque B1) |
97
135
  | `medium` (440, default) | formulario corto (hasta ~5 campos) o un detalle simple | `pages/discounts/discounts.html:122` («Crear descuento») | canon (aprobado por el dueño, 05-10-2026, bloque B1) |
98
136
  | `large` (560) | detalle con tabla o imagen: comprobantes, el detalle de un pedido | `pages/orders/orders.html:146` · `pages/super-admin/master-drivers/master-drivers.html:685` | canon (aprobado por el dueño, 05-10-2026, bloque B1) |
99
- | `xlarge` (720) | mapa, formularios de dos columnas, listas largas de opciones | `components/address-picker-modal/address-picker-modal.html:1` · `pages/customer/checkout/checkout.html:1` | canon (aprobado por el dueño, 05-10-2026, bloque B1) |
137
+ | `xlarge` (720) | **mucho contenido**: mapa, formularios de dos columnas, listas largas o grillas de opciones | `components/address-picker-modal/address-picker-modal.html:1` · `pages/customer/checkout/checkout.html:1` · Categorías del inicio (`pages/customer/store-browser/store-browser.html`) | canon (aprobado por el dueño, 05-10-2026, bloque B1; «mucho contenido → el más ancho», 08-10-2026) |
100
138
 
101
139
  Hoy: 45 `small`, 63 `medium` (50 explícitos + 13 por default), 5 `large`, 14 `xlarge` y 1 que cambia según el caso.
102
140
 
@@ -177,6 +215,16 @@ En master-pedidos: sin helper para el modal; `<sui-modal-footer>` sin `buttonSiz
177
215
  | `closed` | `void` |
178
216
  <!-- api:fin -->
179
217
 
218
+ <!-- textos:inicio -->
219
+ ### Textos
220
+
221
+ 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.
222
+
223
+ | clave | por defecto | se pisa con |
224
+ |---|---|---|
225
+ | `comun.cerrar` | Cerrar | — |
226
+ <!-- textos:fin -->
227
+
180
228
  Slots: el contenido sin atributo va al **body**; `slot="footer"` va al pie. El pie canon es
181
229
  `<sui-modal-footer slot="footer">` (trae su propio padding y borde); un `<div slot="footer">`
182
230
  también funciona y recibe padding 16 × 20, gap 12 y alineación a la derecha.
@@ -95,6 +95,16 @@ En master-pedidos: `[size]="description ? 'medium' : 'small'"`, nunca una señal
95
95
  | `closed` | `void` |
96
96
  <!-- api:fin -->
97
97
 
98
+ <!-- textos:inicio -->
99
+ ### Textos
100
+
101
+ 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.
102
+
103
+ | clave | por defecto | se pisa con |
104
+ |---|---|---|
105
+ | `comun.cerrar` | Cerrar | — |
106
+ <!-- textos:fin -->
107
+
98
108
  `titleId` y `descriptionId` ponen el `id` al `<h2>` y al `<p>`: sirven para el
99
109
  `aria-labelledby` / `aria-describedby` del contenedor que hace de diálogo.
100
110