@ajuarezso/sui-design-system 0.25.0 → 0.25.2

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 (120) hide show
  1. package/docs/sui-accordion/README.md +83 -9
  2. package/docs/sui-activity-feed/README.md +55 -1
  3. package/docs/sui-activity-filter/README.md +50 -1
  4. package/docs/sui-alert/README.md +54 -0
  5. package/docs/sui-app-store-badge/README.md +57 -3
  6. package/docs/sui-avatar/README.md +66 -6
  7. package/docs/sui-avatar-group/README.md +64 -2
  8. package/docs/sui-badge/README.md +79 -11
  9. package/docs/sui-banner/README.md +70 -5
  10. package/docs/sui-brand-logo/README.md +77 -14
  11. package/docs/sui-breadcrumb-item/README.md +51 -0
  12. package/docs/sui-breadcrumbs/README.md +48 -0
  13. package/docs/sui-button/README.md +68 -6
  14. package/docs/sui-button-group/README.md +70 -5
  15. package/docs/sui-button-group-item/README.md +68 -4
  16. package/docs/sui-card/README.md +97 -10
  17. package/docs/sui-chart-legend/README.md +53 -0
  18. package/docs/sui-chart-legend-dot/README.md +49 -0
  19. package/docs/sui-checkbox/README.md +106 -26
  20. package/docs/sui-checkbox-card/README.md +58 -2
  21. package/docs/sui-checkbox-label/README.md +89 -23
  22. package/docs/sui-circular-progress-bar/README.md +63 -5
  23. package/docs/sui-color-dot/README.md +61 -5
  24. package/docs/sui-color-picker/README.md +62 -4
  25. package/docs/sui-command-menu-footer/README.md +49 -0
  26. package/docs/sui-command-menu-item/README.md +63 -4
  27. package/docs/sui-command-menu-search/README.md +58 -4
  28. package/docs/sui-compact-avatar-group/README.md +61 -4
  29. package/docs/sui-compact-button/README.md +112 -28
  30. package/docs/sui-compact-select/README.md +58 -4
  31. package/docs/sui-compact-select-for-input/README.md +69 -5
  32. package/docs/sui-content-card/README.md +63 -2
  33. package/docs/sui-content-divider/README.md +49 -1
  34. package/docs/sui-content-label/README.md +83 -4
  35. package/docs/sui-counter-input/README.md +91 -7
  36. package/docs/sui-date-selector/README.md +61 -3
  37. package/docs/sui-datepicker/README.md +72 -5
  38. package/docs/sui-day-cell/README.md +60 -3
  39. package/docs/sui-day-label/README.md +52 -3
  40. package/docs/sui-divider/README.md +57 -5
  41. package/docs/sui-drawer-footer/README.md +63 -2
  42. package/docs/sui-drawer-header/README.md +60 -3
  43. package/docs/sui-dropdown-caption/README.md +53 -1
  44. package/docs/sui-dropdown-item/README.md +72 -6
  45. package/docs/sui-dropdown-menu/README.md +74 -5
  46. package/docs/sui-dropdown-search/README.md +57 -1
  47. package/docs/sui-emoji/README.md +82 -18
  48. package/docs/sui-empty-state/README.md +68 -4
  49. package/docs/sui-fancy-button/README.md +59 -1
  50. package/docs/sui-file-icon/README.md +56 -1
  51. package/docs/sui-file-upload/README.md +69 -9
  52. package/docs/sui-file-upload-card/README.md +53 -2
  53. package/docs/sui-filter/README.md +74 -5
  54. package/docs/sui-flag/README.md +62 -1
  55. package/docs/sui-form-hint/README.md +54 -5
  56. package/docs/sui-form-label/README.md +53 -1
  57. package/docs/sui-href/README.md +47 -0
  58. package/docs/sui-icon/README.md +69 -4
  59. package/docs/sui-image-upload/README.md +70 -3
  60. package/docs/sui-image-upload-profile/README.md +58 -0
  61. package/docs/sui-img-retry/README.md +49 -0
  62. package/docs/sui-inline-input/README.md +61 -3
  63. package/docs/sui-inline-select/README.md +60 -0
  64. package/docs/sui-input/README.md +77 -14
  65. package/docs/sui-integration-switch/README.md +69 -2
  66. package/docs/sui-kbd/README.md +71 -7
  67. package/docs/sui-key-icon/README.md +65 -6
  68. package/docs/sui-link-button/README.md +79 -13
  69. package/docs/sui-logo/README.md +74 -4
  70. package/docs/sui-modal/README.md +61 -2
  71. package/docs/sui-modal-footer/README.md +83 -9
  72. package/docs/sui-modal-header/README.md +56 -0
  73. package/docs/sui-modal-sheet/README.md +72 -10
  74. package/docs/sui-notification/README.md +71 -1
  75. package/docs/sui-notification-footer/README.md +51 -1
  76. package/docs/sui-notification-tab-menu/README.md +57 -1
  77. package/docs/sui-otp-input/README.md +67 -6
  78. package/docs/sui-page-header/README.md +83 -15
  79. package/docs/sui-pagination/README.md +60 -0
  80. package/docs/sui-password-strength/README.md +50 -1
  81. package/docs/sui-payment-icon/README.md +66 -7
  82. package/docs/sui-period-range/README.md +65 -6
  83. package/docs/sui-popover/README.md +73 -7
  84. package/docs/sui-product-card/README.md +76 -8
  85. package/docs/sui-progress-bar/README.md +72 -8
  86. package/docs/sui-progress-bar-label/README.md +69 -12
  87. package/docs/sui-radio/README.md +83 -6
  88. package/docs/sui-radio-card/README.md +58 -4
  89. package/docs/sui-radio-label/README.md +86 -19
  90. package/docs/sui-rating/README.md +81 -14
  91. package/docs/sui-rich-editor/README.md +45 -0
  92. package/docs/sui-segmented-control/README.md +83 -14
  93. package/docs/sui-select/README.md +72 -3
  94. package/docs/sui-sidebar/README.md +64 -9
  95. package/docs/sui-slider/README.md +68 -1
  96. package/docs/sui-social-button/README.md +63 -3
  97. package/docs/sui-status-badge/README.md +64 -6
  98. package/docs/sui-stepper/README.md +57 -0
  99. package/docs/sui-store-card/README.md +71 -8
  100. package/docs/sui-switch/README.md +80 -9
  101. package/docs/sui-switch-card/README.md +70 -6
  102. package/docs/sui-switch-label/README.md +90 -13
  103. package/docs/sui-tab-chips/README.md +79 -9
  104. package/docs/sui-tab-menu-horizontal/README.md +62 -1
  105. package/docs/sui-tab-menu-vertical/README.md +56 -3
  106. package/docs/sui-table/README.md +68 -7
  107. package/docs/sui-tag/README.md +75 -4
  108. package/docs/sui-tag-input/README.md +71 -7
  109. package/docs/sui-textarea/README.md +78 -11
  110. package/docs/sui-time-picker/README.md +70 -7
  111. package/docs/sui-toast/README.md +65 -5
  112. package/docs/sui-tooltip/README.md +61 -4
  113. package/docs/sui-topbar/README.md +57 -3
  114. package/docs/sui-topbar-button/README.md +57 -3
  115. package/docs/sui-topbar-item/README.md +61 -0
  116. package/docs/sui-topbar-user/README.md +56 -0
  117. package/fesm2022/ajuarezso-sui-design-system.mjs +216 -227
  118. package/fesm2022/ajuarezso-sui-design-system.mjs.map +1 -1
  119. package/package.json +1 -1
  120. package/types/ajuarezso-sui-design-system.d.ts +68 -45
@@ -44,6 +44,15 @@ No tiene variantes. Lo que decide qué hace es la URL:
44
44
 
45
45
  No aplica: es una directiva, no dibuja nada. El tamaño es el del componente que la usa.
46
46
 
47
+ ### Por dispositivo (D16)
48
+
49
+ No aplica: es una directiva sobre un `<a>` y **no tiene `size`** ni dibuja nada. El tamaño por
50
+ dispositivo lo pone el componente que la usa (`sui-link-button`, `sui-compact-button`,
51
+ `sui-fancy-button`…), cada uno con su fila de D16 (dueño, 05-10-2026). Lo mismo en el ultrawide.
52
+
53
+ En master-pedidos: nada que pasar a `suiHref`; el `[size]` va en el componente que la lleva
54
+ (p. ej. `<sui-fancy-button [size]="bp.sizeControl()" href="/cliente/pedidos">`).
55
+
47
56
  ## API
48
57
 
49
58
  <!-- api:inicio -->
@@ -70,6 +79,44 @@ Solo se aplica a `<a>` (`selector: a[suiHref]`).
70
79
  - Un `href` relativo sin `/` inicial (`pedidos/123`) **no** se intercepta: lo resuelve el
71
80
  navegador contra la URL actual y recarga. Pasa rutas absolutas.
72
81
 
82
+ ## Bien y mal
83
+
84
+ Link interno en una plantilla de la app
85
+
86
+ ```html
87
+ <!-- ❌ mal: no marca el link activo y la app usa routerLink (98 usos, 0 de suiHref) -->
88
+ <a suiHref="/cliente/pedidos">Mis pedidos</a>
89
+ ```
90
+
91
+ ```html
92
+ <!-- ✅ bien -->
93
+ <a routerLink="/cliente/pedidos" routerLinkActive="activo">Mis pedidos</a>
94
+ ```
95
+
96
+ Ruta sin `/` inicial (el `href` que la app le pasa a un componente del DS)
97
+
98
+ ```html
99
+ <!-- ❌ mal: sui-link-button lo pinta con suiHref, pero un href relativo no se intercepta; el navegador lo resuelve y recarga el WebView -->
100
+ <sui-link-button [href]="'pedidos/' + id()">Ver pedido</sui-link-button>
101
+ ```
102
+
103
+ ```html
104
+ <!-- ✅ bien -->
105
+ <sui-link-button [href]="'/cliente/pedidos/' + id()">Ver pedido</sui-link-button>
106
+ ```
107
+
108
+ `href` configurable dentro de un componente del DS
109
+
110
+ ```html
111
+ <!-- ❌ mal: con [attr.href] un link interno recarga la app entera (en Capacitor pierde el estado) -->
112
+ <a class="sui-link-button" [attr.href]="href()" [attr.target]="target()"><ng-content /></a>
113
+ ```
114
+
115
+ ```html
116
+ <!-- ✅ bien -->
117
+ <a class="sui-link-button" [suiHref]="href()" [attr.target]="target()"><ng-content /></a>
118
+ ```
119
+
73
120
  ## Ejemplo
74
121
 
75
122
  ```html
@@ -76,8 +76,8 @@ propios del DS (`info-custom-fill`, `heart-half-fill`…) también pasan por aqu
76
76
  En mobile se lee y se toca con el dedo, así que va más grande; en web, más chico. En las
77
77
  pantallas que se usan en los dos (panel del local, super admin) se elige con el servicio de
78
78
  breakpoints de la app (`bp`, en `services/breakpoints.service.ts`); en las que solo existen en
79
- el celular (cliente, repartidor) va directo el valor de mobile. No se toca, así que la tablet va
80
- con el valor de web, y el celular chico (< 380 px) también (D16).
79
+ el celular (cliente, repartidor) también se usa `bp`, porque el celular chico va con el valor de
80
+ web. No se toca, así que la tablet va con el valor de web, y el celular chico (< 380 px) también (D16).
81
81
  Es **canon (dueño, 05-10-2026, P1 b)**: un paso más grande en el celular que en computadora, y
82
82
  se migra todo ahora, no solo al tocar cada pantalla.
83
83
 
@@ -89,12 +89,36 @@ se migra todo ahora, no solo al tocar cada pantalla.
89
89
  | dentro de un botón o un input | lo decide el componente | igual |
90
90
 
91
91
  ```html
92
- <sui-icon name="store-2-line" [size]="bp.isMobile() ? 20 : 16" />
92
+ <sui-icon name="store-2-line" [size]="bp.celular() ? 20 : 16" />
93
93
  ```
94
94
 
95
95
  Un ícono suelto **nunca** es el área táctil: si se toca, va en `sui-compact-button` o
96
96
  `sui-button` `onlyIcon`, que dan los 44 px. Hoy solo 2 íconos cambian por breakpoint
97
- (`pages/discounts/discounts.html:103` y `:108`, con `bp.isTablet() ? 20 : 16`).
97
+ (`pages/discounts/discounts.html:103` y `:108`, con `bp.isTablet() ? 20 : 16`), y los dos están
98
+ mal: van dentro de un `sui-button`, que ya le da al ícono una caja de 20, así que no llevan
99
+ `size`. Hasta 1024 px dibujan 20 y llenan la caja (no se nota); en una computadora de más de
100
+ 1024 px dibujan 16 y el glifo queda chico dentro de esa caja. Además `isTablet` no es un corte
101
+ de D16 (ver «Bien y mal»).
102
+
103
+ ### Por dispositivo (D16)
104
+
105
+ El ícono no se toca: sube un paso **solo en el celular de 380 px o más**. Fuente: D16, dueño
106
+ 05-10-2026 (P1 b).
107
+
108
+ | dispositivo | size | nota |
109
+ |---|---|---|
110
+ | celular chico (< 380) | valor «web» (`16` en una fila, `24` protagonista) | no se toca: no sube aunque sea un celular |
111
+ | celular (380–767) | valor «mobile» (`20` junto al texto de una fila, `32` protagonista) | un paso más que en la computadora |
112
+ | tablet táctil (`pointer: coarse`) | valor «web» (`16` en una fila, `24` protagonista) | no se toca: el puntero no cambia nada |
113
+ | computadora (`pointer: fine`) | valor «web» (`16` en una fila, `24` protagonista) | |
114
+
115
+ En el ultrawide, igual que en la computadora: el espacio extra va a más columnas, no a íconos
116
+ más grandes. Dentro de un botón o un input no se pasa `size`: lo decide el componente.
117
+
118
+ En master-pedidos no hay helper: `[size]="bp.celular() ? 20 : 16"` (fila) o
119
+ `[size]="bp.celular() ? 32 : 24"` (protagonista). No uses `bp.isMobile()` (incluye el celular
120
+ chico) ni `bp.isTablet()` (incluye la tablet y la laptop de hasta 1024 px): los dos van con el
121
+ valor de web.
98
122
 
99
123
  ## «No usar»: por qué no hoy y cuándo sí
100
124
 
@@ -131,6 +155,47 @@ Outputs: ninguno.
131
155
  el paquete del DS no la trae.
132
156
  - Dentro de un `sui-kbd`, el kbd le fuerza el color `text-sub-600`.
133
157
 
158
+ ## Bien y mal
159
+
160
+ Ícono de una acción en una tabla
161
+
162
+ ```html
163
+ <!-- ❌ mal: el botón ya le da al ícono su caja de 20 y no hace falta size; en la computadora de más de 1024 px isTablet dibuja el glifo en 16 dentro de ella (pages/discounts/discounts.html:103) -->
164
+ <sui-button variant="ghost" color="neutral" [size]="bp.sizeControl()" [onlyIcon]="true" aria-label="Editar">
165
+ <sui-icon data-slot="start" name="edit-2-line" [size]="bp.isTablet() ? 20 : 16" />
166
+ </sui-button>
167
+ ```
168
+
169
+ ```html
170
+ <!-- ✅ bien -->
171
+ <sui-button variant="ghost" color="neutral" [size]="bp.sizeControl()" [onlyIcon]="true"
172
+ leftIcon="edit-2-line" aria-label="Editar" />
173
+ ```
174
+
175
+ Ícono protagonista
176
+
177
+ ```html
178
+ <!-- ❌ mal: 28 está fuera de la escala y no cambia por dispositivo (pages/envios/envios.html:712) -->
179
+ <sui-icon class="seg__buscando-icono" name="e-bike-2-line" [size]="28" />
180
+ ```
181
+
182
+ ```html
183
+ <!-- ✅ bien -->
184
+ <sui-icon class="seg__buscando-icono" name="e-bike-2-line" [size]="bp.celular() ? 32 : 24" />
185
+ ```
186
+
187
+ Tamaño como texto
188
+
189
+ ```html
190
+ <!-- ❌ mal: un string sin unidad no es un tamaño CSS válido; el ícono no toma tamaño (Trampas) -->
191
+ <sui-icon name="time-line" size="20" />
192
+ ```
193
+
194
+ ```html
195
+ <!-- ✅ bien -->
196
+ <sui-icon name="time-line" [size]="20" />
197
+ ```
198
+
134
199
  ## Ejemplo
135
200
 
136
201
  ```html
@@ -100,8 +100,8 @@ El archivo que entrega **no se sube tal cual**: pasa por `ImageCompressorService
100
100
  En mobile se toca con el dedo: lo que se toca mide **44 px o más** (canon (dueño, 04-10-2026): piso táctil 44 en el teléfono). El área vacía se
101
101
  toca entera (80 a 200 px), así que pasa. Los botones con imagen se **ven** de 40 (lápiz y tacho) y
102
102
  32 («Quitar»), pero en el teléfono (≤ 767 px) **se tocan en 44 de alto** con un `::before`
103
- invisible (04-10-2026). En pantallas de los dos se elige con el servicio de breakpoints de la app
104
- (`bp`, en `services/breakpoints.service.ts`).
103
+ invisible (04-10-2026). Aquí `bp` no elige nada: `size` va por la forma de la imagen y lo que se
104
+ toca ya tiene su área sola (ver «Por dispositivo (D16)»).
105
105
 
106
106
  **Por qué no hay un size nuevo**: aquí `size` (`sm`/`md`/`lg`/`xl`) es el tamaño de la **foto**, no
107
107
  el de los botones. Un `2xl` agrandaría la imagen sin que nadie lo pida; el área invisible lleva lo
@@ -115,7 +115,7 @@ que se toca a 44 sin cambiar el dibujo ni los sizes de hoy.
115
115
  | «Quitar» (sin `editable`) | se ve de 32 (`sui-button` `xsmall`), se toca en 44 de alto: crece 4 hacia la foto (la mitad del gap de 8) y 8 hacia abajo | 32 px | canon (dueño, 04-10-2026): piso táctil 44 en el teléfono (área) · canon (aprobado por el dueño, 03-10-2026) |
116
116
 
117
117
  ```html
118
- <sui-image-upload shape="rect" [size]="bp.isMobile() ? 'xl' : 'lg'" [editable]="true"
118
+ <sui-image-upload shape="rect" size="xl" [editable]="true"
119
119
  [src]="logo()" [maxSize]="50 * 1024 * 1024" title="Sube tu logo"
120
120
  subtitle="JPG, PNG o WebP — máx 50 MB"
121
121
  (imageSelected)="alElegirLogo($event)" (editRequested)="abrirRecortador()"
@@ -127,6 +127,27 @@ de 40 y quedan en 32) y el «Quitar» es un `sui-button` `xsmall` de **32** (ant
127
127
  los dos botones van centrados abajo, dentro del círculo. Lo que haya justo debajo del «Quitar»
128
128
  pierde 8 px de toque en el teléfono: no pegarle otro control sin margen.
129
129
 
130
+ ### Por dispositivo (D16)
131
+
132
+ D16 (dueño, 05-10-2026) no mueve `size`: aquí `size` es el tamaño de la **foto**, no el de lo que
133
+ se toca, así que no sigue la escala «lo que se toca / lo que no se toca». Se elige por la forma de
134
+ la imagen (`xl` para lo cuadrado, `fluid` para capturas y apaisadas) y por el layout, igual que en
135
+ la tabla de arriba: `xl` en una columna, que es lo que hay en el celular, el celular chico y la
136
+ tablet; `lg` solo cuando la foto comparte la fila con el nombre (hoy ninguna pantalla lo hace:
137
+ los 3 `lg` medidos son el logo del onboarding y el banner y el comprobante de promociones, que
138
+ además van `fluid`; ninguno está en una fila).
139
+ Lo que decide entre `xl` y `lg` es si hay fila, no el dispositivo: cuando una pantalla arme esa
140
+ fila según el ancho, la condición es el mismo corte del layout de la pantalla, no
141
+ `bp.celular()` ni `bp.tactil()`.
142
+
143
+ Lo que se toca (lápiz, tacho, «Quitar») ya llega a 44 de alto en el celular y en el celular chico
144
+ con el área invisible (`@media (max-width: 767px)`). En la **tablet táctil** (`pointer: coarse`,
145
+ más de 767 px) esa área no se prende y quedan en 40 y 32: **pendiente**, espera el mixin
146
+ `sui-tactil` del DS que propone §0 de las decisiones (sin el sí del dueño todavía).
147
+
148
+ En master-pedidos: no hay helper de D16; `size="xl"` para lo cuadrado y `[fluid]="true"` para
149
+ capturas y apaisadas. `lg` solo en la fila del nombre, con la condición del layout de esa pantalla.
150
+
130
151
  ## «No usar»: por qué no hoy y cuándo sí
131
152
 
132
153
  Nada se borra del DS. Lo marcado «no usar» no tiene caso en la app de hoy; si un flujo nuevo cae
@@ -195,6 +216,52 @@ recortador; se llama con una referencia: `#qr` → `qr.abrirSelector()`).
195
216
  - El atributo `title` del host se borra solo (no sale el tooltip gris del navegador).
196
217
  - Ya trae el arreglo de iOS para el `:active` pegado.
197
218
 
219
+ ## Bien y mal
220
+
221
+ Logo del local
222
+
223
+ ```html
224
+ <!-- ❌ mal: el logo se ve cuadrado en el home, no en círculo; en 160 (lg) en una columna, donde va xl; y sin lápiz (pages/business-onboarding/step-info/step-info.html:42) -->
225
+ <sui-image-upload shape="circle" size="lg" [(src)]="logoPreview" title="Subir logo"
226
+ (imageSelected)="onLogoSelected($event)" />
227
+ ```
228
+
229
+ ```html
230
+ <!-- ✅ bien -->
231
+ <sui-image-upload shape="rect" size="xl" [editable]="true" [(src)]="logoPreview" title="Sube tu logo"
232
+ (imageSelected)="onLogoSelected($event)" (editRequested)="abrirRecortador()" />
233
+ ```
234
+
235
+ Comprobante de pago
236
+
237
+ ```html
238
+ <!-- ❌ mal: una captura vertical se recorta en el cuadrado de 200 y el «Quitar» queda en 32 (pages/central/central-gastos/central-gastos.html:118) -->
239
+ <sui-image-upload shape="rect" size="xl" [src]="vistaComprobante()" [maxSize]="10 * 1024 * 1024"
240
+ title="Subir comprobante" (imageSelected)="onComprobante($event)" (removed)="quitarComprobante()" />
241
+ ```
242
+
243
+ ```html
244
+ <!-- ✅ bien -->
245
+ <sui-image-upload #comprobante shape="rect" [fluid]="true" [editable]="true" [src]="vistaComprobante()"
246
+ [maxSize]="10 * 1024 * 1024" title="Sube el comprobante" (imageSelected)="onComprobante($event)"
247
+ (editRequested)="comprobante.abrirSelector()" (removed)="quitarComprobante()" />
248
+ ```
249
+
250
+ Banner apaisado
251
+
252
+ ```html
253
+ <!-- ❌ mal: con fluid, size no hace nada; y sin editable (pages/promociones/promociones.html:186) -->
254
+ <sui-image-upload shape="rect" size="lg" [fluid]="true" [src]="campaignBannerPreview()"
255
+ title="Subir banner" (imageSelected)="onBannerImage($event)" />
256
+ ```
257
+
258
+ ```html
259
+ <!-- ✅ bien -->
260
+ <sui-image-upload #banner shape="rect" [fluid]="true" [editable]="true" [src]="campaignBannerPreview()"
261
+ title="Sube el banner" (imageSelected)="onBannerImage($event)"
262
+ (editRequested)="banner.abrirSelector()" (removed)="onBannerRemoved()" />
263
+ ```
264
+
198
265
  ## Ejemplo
199
266
 
200
267
  ```html
@@ -81,6 +81,22 @@ Usos de hoy que rompen la regla: **0**.
81
81
  **Sin tamaño suficiente para mobile.** Hace falta en el DS un `size` que lleve los botones a
82
82
  `large` (48 px). Mientras tanto, en el celular va `sui-image-upload` `circle` `xl`.
83
83
 
84
+ ### Por dispositivo (D16)
85
+
86
+ No aplica: **no tiene `size`** y nada cambia por dispositivo (avatar de 64/56 y botones `xsmall`
87
+ de 32 fijos). Con D16 (dueño, 05-10-2026) lo que se toca va `large` en el celular y en la tablet
88
+ táctil, y esos botones no llegan a 44, así que en una pantalla táctil no se usa:
89
+
90
+ | dispositivo | qué va | ¿llega a 44? |
91
+ |---|---|---|
92
+ | celular y celular chico (≤ 767) | `sui-image-upload` `circle` `xl` con `[editable]="true"` | sí: su lápiz y su tacho se tocan en 44 con el área invisible |
93
+ | tablet táctil (`pointer: coarse`, > 767) | `sui-image-upload` `circle` `xl` con `[editable]="true"` (40 contra 32, el que más se acerca) | **no, pendiente**: el área de 44 de `sui-image-upload` se prende solo hasta 767 px y en la tablet quedan en 40; espera el mixin `sui-tactil` del DS (§0 de las decisiones, sin el sí del dueño todavía) |
94
+ | computadora y ultrawide | este componente, botones de 32 | no hace falta 44; se queda en 32 porque solo tiene ese tamaño de botón. Subirlo a `medium` es código del DS (un `size`), **pendiente** |
95
+
96
+ En master-pedidos: no lleva `[size]`; si la pantalla se usa en un dispositivo táctil,
97
+ `@if (!bp.tactil())` para este componente y `sui-image-upload` en el otro caso (con la tablet
98
+ todavía en 40, ver la tabla).
99
+
84
100
  ## «No usar»: por qué no hoy y cuándo sí
85
101
 
86
102
  Nada se borra del DS. Lo marcado «no usar» no tiene caso en la app de hoy; si un flujo nuevo cae
@@ -137,6 +153,48 @@ interface SuiImageUploadProfileLabels { upload?: string; remove?: string; change
137
153
  - El atributo `title` del host se borra solo (no sale el tooltip gris del navegador); el `title`
138
154
  sí se usa como `alt` de la foto.
139
155
 
156
+ ## Bien y mal
157
+
158
+ Logo del local
159
+
160
+ ```html
161
+ <!-- ❌ mal: company lo muestra en círculo y el logo se ve cuadrado en el home (vitrina: pages/super-admin/master-preview/_guia-moleculas/fechas.ts:233) -->
162
+ <sui-image-upload-profile type="company" title="Logo del local" subtitle="JPG o PNG, cuadrado" />
163
+ ```
164
+
165
+ ```html
166
+ <!-- ✅ bien -->
167
+ <sui-image-upload shape="rect" size="xl" [editable]="true" [src]="logo()" title="Sube tu logo"
168
+ (imageSelected)="alElegirLogo($event)" (editRequested)="abrirRecortador()" />
169
+ ```
170
+
171
+ Subir la foto
172
+
173
+ ```html
174
+ <!-- ❌ mal: no tiene input de archivo; «Subir» y «Cambiar» solo emiten y no abren nada (Trampas) -->
175
+ <sui-image-upload-profile title="Foto de perfil" [imageUrl]="foto()" (upload)="subir()" />
176
+ ```
177
+
178
+ ```html
179
+ <!-- ✅ bien -->
180
+ <sui-image-upload-profile title="Foto de perfil" [imageUrl]="foto()" [loading]="subiendo()"
181
+ (upload)="input.click()" (change)="input.click()" (remove)="quitar()" />
182
+ <input #input type="file" accept="image/jpeg,image/png,image/webp" hidden (change)="alElegir($event)" />
183
+ ```
184
+
185
+ Foto de perfil en el celular
186
+
187
+ ```html
188
+ <!-- ❌ mal: los botones miden 32 y en una pantalla táctil no llegan a 44 (D16) -->
189
+ <sui-image-upload-profile title="Tu foto" [imageUrl]="foto()" (upload)="input.click()" />
190
+ ```
191
+
192
+ ```html
193
+ <!-- ✅ bien -->
194
+ <sui-image-upload shape="circle" size="xl" [editable]="true" [src]="foto()" title="Sube tu foto"
195
+ (imageSelected)="alElegir($event)" (editRequested)="abrirRecortador()" />
196
+ ```
197
+
140
198
  ## Ejemplo
141
199
 
142
200
  ```html
@@ -52,6 +52,12 @@ No tiene variantes ni inputs. Lo que hace:
52
52
 
53
53
  No aplica: es una directiva, no dibuja nada. El tamaño es el del `<img>`.
54
54
 
55
+ ### Por dispositivo (D16)
56
+
57
+ No aplica: no tiene `size` ni escala que repartir por dispositivo (D16, dueño 05-10-2026). El
58
+ `<img>` mide lo que diga el CSS de la pantalla o el componente que lo contiene (`sui-avatar` con
59
+ `bp.sizeAvatar()`, la tarjeta del local), y la directiva se pone igual en todos los dispositivos.
60
+
55
61
  ## API
56
62
 
57
63
  <!-- api:inicio -->
@@ -89,6 +95,49 @@ Solo se aplica a `<img>` (`selector: img[suiImgRetry]`). El `src` se pone como s
89
95
  - **No borra el cache completo** a propósito: solo la entrada de esa URL. Borrarlo todo mataría
90
96
  la ventaja de no volver a bajar nada.
91
97
  - Funciona igual con `loading="lazy"` y `decoding="async"`.
98
+ - **Con `srcset` no**: el reintento y el `fetch` van sobre `img.src`, no sobre la candidata que el
99
+ navegador eligió (`currentSrc`), así que purgaría la URL equivocada. Va un `src` único (en el home,
100
+ la portada chica de 800 px; ver `master-pedidos/src/app/core/services/state.service.ts:45`).
101
+
102
+ ## Bien y mal
103
+
104
+ Logo en las sugerencias de búsqueda
105
+
106
+ ```html
107
+ <!-- ❌ mal: listado del cliente sin la directiva; tras un corte el logo queda roto (pages/customer/favorites/favorites.html:32; la misma sugerencia del home sí la lleva) -->
108
+ <img class="favorites__suggestion-img" [src]="s.logoUrl" [alt]="s.name" loading="lazy" />
109
+ ```
110
+
111
+ ```html
112
+ <!-- ✅ bien (con las reglas de sui-img-loaded y data-sui-img-dead en el scss) -->
113
+ <img class="favorites__suggestion-img" suiImgRetry [src]="s.logoUrl" [alt]="s.name"
114
+ loading="lazy" decoding="async" />
115
+ ```
116
+
117
+ Logo del local con inicial de respaldo
118
+
119
+ ```html
120
+ <!-- ❌ mal: el (error) a mano esconde la imagen al primer fallo, sin reintento ni inicial (pages/customer/order-history/order-history.html:168) -->
121
+ <img [src]="logo" [alt]="order.tenantName ?? 'Local'" (error)="$any($event).target.style.display='none'" />
122
+ ```
123
+
124
+ ```html
125
+ <!-- ✅ bien: logo con iniciales de respaldo → sui-avatar, no la directiva -->
126
+ <sui-avatar [size]="bp.sizeAvatar('md')" [src]="logo" [initials]="storeInitial(order)"
127
+ [alt]="order.tenantName ?? 'Local'" />
128
+ ```
129
+
130
+ Portada con varias resoluciones
131
+
132
+ ```html
133
+ <!-- ❌ mal: la directiva reintenta sobre img.src; con srcset purga la candidata equivocada (lo documenta pages/customer/store-browser/store-browser.html:332) -->
134
+ <img class="fila__img" suiImgRetry [src]="store.coverThumbUrl" [srcset]="portadaSrcset(store)" [alt]="store.name" />
135
+ ```
136
+
137
+ ```html
138
+ <!-- ✅ bien: src único, del tamaño que pide la tarjeta -->
139
+ <img class="fila__img" suiImgRetry [src]="store.coverThumbUrl" [alt]="store.name" decoding="async" />
140
+ ```
92
141
 
93
142
  ## Ejemplo
94
143
 
@@ -51,13 +51,25 @@ Regla corta: **un dato, en su lugar, que se corrige rápido → inline · varios
51
51
  **No tiene `size`**: mide 38 px de alto (8 + 20 + 8 de padding y línea, más 1 + 1 de borde) y sus
52
52
  botones (lápiz, ✓, ✕) son de 24 × 24 (ícono de 20 + 2 de padding). En edición el texto sube a 16 px
53
53
  en mobile (sin zoom de iOS). **No tiene tamaño suficiente para mobile**: los botones
54
- de 24 px quedan lejos de los 44 que pide un dedo. Usarlo en el panel web; en el teléfono, editar
55
- con un `sui-input` `large`.
54
+ de 24 px quedan lejos de los 44 que pide un dedo. Usarlo en el panel web; en el teléfono y en la
55
+ tablet táctil, editar con un `sui-input` `large`.
56
56
 
57
57
  | uso | mobile | web |
58
58
  |---|---|---|
59
59
  | renombrar en una lista | `sui-input` `large` en un modal | este, 38 px |
60
60
 
61
+ ### Por dispositivo (D16)
62
+
63
+ **No tiene `size`**: mide 38 px fijos y sus botones son de 24, así que no hay escala que repartir
64
+ por dispositivo. Lo que sí aplica es la regla de lo que se toca (D16, dueño 05-10-2026): en toda
65
+ pantalla táctil (celular chico, celular y tablet con `pointer: coarse`) los botones de 24 no llegan
66
+ a 44, y ahí se edita con un `sui-input` en un modal. Este queda para la computadora (y el
67
+ ultrawide, igual que ella). La condición es `bp.tactil()` y no `bp.isMobile()`: con `isMobile` el
68
+ iPad se queda con los botones de 24.
69
+
70
+ En master-pedidos: `@if (bp.tactil())` → `sui-input` con `[size]="bp.sizeControl()"` en un modal;
71
+ si no, `sui-inline-input`.
72
+
61
73
  ## «No usar»: por qué no hoy y cuándo sí
62
74
 
63
75
  | qué | por qué no hoy | cuándo sí, en un flujo nuevo |
@@ -90,12 +102,58 @@ Implementa `ControlValueAccessor`.
90
102
 
91
103
  ## Trampas
92
104
 
93
- - **Los defaults están pensados para la story**: `placeholder` «Placeholder text...» y
105
+ - **Los defaults están pensados para la story**: `placeholder` «Escribe aquí…» y
94
106
  `leftIcon` `user-3-line`. Pasa los dos siempre.
95
107
  - `error` no trae mensaje: decir qué falta en otro lado (toast o hint cercano).
96
108
  - Cancelar restaura el texto de antes; `(confirm)` es el único momento de guardar.
97
109
  - Ocupa todo el ancho de su contenedor.
98
110
 
111
+ ## Bien y mal
112
+
113
+ Alias de una dirección
114
+
115
+ ```html
116
+ <!-- ❌ mal: con los defaults, vacío dice «Escribe aquí…» y el ícono es una persona (user-3-line) -->
117
+ <sui-inline-input [(value)]="alias" (confirm)="guardarAlias($event)" />
118
+ ```
119
+
120
+ ```html
121
+ <!-- ✅ bien -->
122
+ <sui-inline-input [(value)]="alias" placeholder="Sin alias" leftIcon="map-pin-line"
123
+ editLabel="Editar alias" (confirm)="guardarAlias($event)" />
124
+ ```
125
+
126
+ Formulario reactivo
127
+
128
+ ```html
129
+ <!-- ❌ mal: dos dueños del mismo valor; [(value)] y formControlName se pisan -->
130
+ <sui-inline-input formControlName="nombre" [(value)]="nombre" placeholder="Sin nombre" />
131
+ ```
132
+
133
+ ```html
134
+ <!-- ✅ bien -->
135
+ <sui-inline-input formControlName="nombre" placeholder="Sin nombre" leftIcon="price-tag-3-line"
136
+ editLabel="Editar nombre" (confirm)="renombrar($event)" />
137
+ ```
138
+
139
+ Renombrar en una pantalla táctil
140
+
141
+ ```html
142
+ <!-- ❌ mal: con bp.isMobile() la tablet táctil se queda con el lápiz, el ✓ y el ✕ de 24 px -->
143
+ @if (!bp.isMobile()) {
144
+ <sui-inline-input [(value)]="nombreCategoria" placeholder="Sin nombre" leftIcon="price-tag-3-line" />
145
+ }
146
+ ```
147
+
148
+ ```html
149
+ <!-- ✅ bien (el sui-input va dentro de un sui-modal) -->
150
+ @if (bp.tactil()) {
151
+ <sui-input label="Nombre de la categoría" [size]="bp.sizeControl()" [(value)]="nombreCategoria" />
152
+ } @else {
153
+ <sui-inline-input [(value)]="nombreCategoria" placeholder="Sin nombre" leftIcon="price-tag-3-line" />
154
+ }
155
+ ```
156
+
99
157
  ## Ejemplo
100
158
 
101
159
  ```html
@@ -56,6 +56,20 @@ entera.
56
56
  Las opciones del menú miden **36 px también en mobile**: a diferencia de `sui-select` y de los
57
57
  compactos, este no tiene la regla de 44 en ≤ 767 px (`sui-inline-select.scss` no la trae).
58
58
 
59
+ ### Por dispositivo (D16)
60
+
61
+ **No tiene `size`**: mide 20 px (la línea del texto) en cualquier pantalla, así que no hay escala
62
+ que repartir por dispositivo. D16 (dueño, 05-10-2026) se aplica según dónde va:
63
+
64
+ - **Al final de un `sui-input`**: el alto lo pone el input, que sigue su fila D16 (`large` en el
65
+ celular chico, el celular y la tablet táctil; `medium` en la computadora y el ultrawide).
66
+ - **Dentro de una frase**: solo en la computadora (y el ultrawide). En toda pantalla táctil
67
+ (celular chico, celular y tablet con `pointer: coarse`) la palabra de 20 px no llega a 44: va un
68
+ `sui-select`. La condición es `bp.tactil()`, no `bp.isMobile()`, para que el iPad también cambie.
69
+
70
+ En master-pedidos: el `sui-input` que lo contiene con `[size]="bp.sizeControl()"`; en una frase,
71
+ `@if (bp.tactil())` → `sui-select` con `[size]="bp.sizeControl()"`.
72
+
59
73
  ## «No usar»: por qué no hoy y cuándo sí
60
74
 
61
75
  | qué | por qué no hoy | cuándo sí, en un flujo nuevo |
@@ -95,6 +109,52 @@ Implementa `ControlValueAccessor`.
95
109
  - No mezclar `[(selected)]` con `formControlName`.
96
110
  - El menú mide al menos 200 px aunque el texto sea corto.
97
111
 
112
+ ## Bien y mal
113
+
114
+ Unidad al final de un campo
115
+
116
+ ```html
117
+ <!-- ❌ mal: sin [trailingControl] el slot es un ícono de 20 × 20 con pointer-events: none y el selector no responde -->
118
+ <sui-input label="Peso" type="number" [size]="bp.sizeControl()" [(value)]="peso">
119
+ <sui-inline-select data-slot="end" icon="ruler-line" [items]="unidades" [(selected)]="unidad" />
120
+ </sui-input>
121
+ ```
122
+
123
+ ```html
124
+ <!-- ✅ bien -->
125
+ <sui-input label="Peso" type="number" [size]="bp.sizeControl()" [trailingControl]="true" [(value)]="peso">
126
+ <sui-inline-select data-slot="end" icon="ruler-line" [items]="unidades" [(selected)]="unidad" />
127
+ </sui-input>
128
+ ```
129
+
130
+ Formulario reactivo
131
+
132
+ ```html
133
+ <!-- ❌ mal: dos dueños del mismo valor; [(selected)] y formControlName se pisan -->
134
+ <sui-inline-select formControlName="periodo" [items]="periodos" [(selected)]="periodo" />
135
+ ```
136
+
137
+ ```html
138
+ <!-- ✅ bien -->
139
+ <sui-inline-select formControlName="periodo" icon="calendar-line" [items]="periodos" />
140
+ ```
141
+
142
+ Frase en una pantalla táctil
143
+
144
+ ```html
145
+ <!-- ❌ mal: en el celular y la tablet táctil se toca una palabra de 20 px de alto -->
146
+ <p>Mostrar ventas de <sui-inline-select icon="calendar-line" [items]="periodos" [(selected)]="periodo" /></p>
147
+ ```
148
+
149
+ ```html
150
+ <!-- ✅ bien -->
151
+ @if (bp.tactil()) {
152
+ <sui-select label="Periodo" [options]="opcionesPeriodo" [size]="bp.sizeControl()" [(value)]="periodoId" />
153
+ } @else {
154
+ <p>Mostrar ventas de <sui-inline-select icon="calendar-line" [items]="periodos" [(selected)]="periodo" /></p>
155
+ }
156
+ ```
157
+
98
158
  ## Ejemplo
99
159
 
100
160
  ```html