@ajuarezso/sui-design-system 0.24.1 → 0.25.1

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 +85 -11
  2. package/docs/sui-activity-feed/README.md +65 -11
  3. package/docs/sui-activity-filter/README.md +54 -5
  4. package/docs/sui-alert/README.md +54 -0
  5. package/docs/sui-app-store-badge/README.md +64 -10
  6. package/docs/sui-avatar/README.md +92 -29
  7. package/docs/sui-avatar-group/README.md +75 -9
  8. package/docs/sui-badge/README.md +85 -17
  9. package/docs/sui-banner/README.md +85 -20
  10. package/docs/sui-brand-logo/README.md +95 -31
  11. package/docs/sui-breadcrumb-item/README.md +54 -3
  12. package/docs/sui-breadcrumbs/README.md +54 -6
  13. package/docs/sui-button/README.md +76 -12
  14. package/docs/sui-button-group/README.md +91 -23
  15. package/docs/sui-button-group-item/README.md +81 -14
  16. package/docs/sui-card/README.md +105 -18
  17. package/docs/sui-chart-legend/README.md +63 -10
  18. package/docs/sui-chart-legend-dot/README.md +59 -10
  19. package/docs/sui-checkbox/README.md +109 -29
  20. package/docs/sui-checkbox-card/README.md +70 -14
  21. package/docs/sui-checkbox-label/README.md +92 -26
  22. package/docs/sui-circular-progress-bar/README.md +80 -19
  23. package/docs/sui-color-dot/README.md +72 -12
  24. package/docs/sui-color-picker/README.md +71 -13
  25. package/docs/sui-command-menu-footer/README.md +50 -1
  26. package/docs/sui-command-menu-item/README.md +74 -14
  27. package/docs/sui-command-menu-search/README.md +58 -3
  28. package/docs/sui-compact-avatar-group/README.md +73 -13
  29. package/docs/sui-compact-button/README.md +123 -34
  30. package/docs/sui-compact-select/README.md +61 -7
  31. package/docs/sui-compact-select-for-input/README.md +70 -6
  32. package/docs/sui-content-card/README.md +75 -14
  33. package/docs/sui-content-divider/README.md +67 -16
  34. package/docs/sui-content-label/README.md +99 -20
  35. package/docs/sui-counter-input/README.md +102 -18
  36. package/docs/sui-date-selector/README.md +69 -11
  37. package/docs/sui-datepicker/README.md +85 -18
  38. package/docs/sui-day-cell/README.md +75 -17
  39. package/docs/sui-day-label/README.md +56 -7
  40. package/docs/sui-divider/README.md +64 -9
  41. package/docs/sui-drawer-footer/README.md +63 -2
  42. package/docs/sui-drawer-header/README.md +69 -12
  43. package/docs/sui-dropdown-caption/README.md +58 -6
  44. package/docs/sui-dropdown-item/README.md +75 -9
  45. package/docs/sui-dropdown-menu/README.md +83 -14
  46. package/docs/sui-dropdown-search/README.md +60 -4
  47. package/docs/sui-emoji/README.md +94 -30
  48. package/docs/sui-empty-state/README.md +71 -7
  49. package/docs/sui-fancy-button/README.md +74 -14
  50. package/docs/sui-file-icon/README.md +70 -12
  51. package/docs/sui-file-upload/README.md +77 -17
  52. package/docs/sui-file-upload-card/README.md +64 -12
  53. package/docs/sui-filter/README.md +83 -14
  54. package/docs/sui-flag/README.md +73 -8
  55. package/docs/sui-form-hint/README.md +56 -7
  56. package/docs/sui-form-label/README.md +57 -5
  57. package/docs/sui-href/README.md +47 -0
  58. package/docs/sui-icon/README.md +86 -18
  59. package/docs/sui-image-upload/README.md +90 -22
  60. package/docs/sui-image-upload-profile/README.md +70 -11
  61. package/docs/sui-img-retry/README.md +50 -1
  62. package/docs/sui-inline-input/README.md +67 -9
  63. package/docs/sui-inline-select/README.md +62 -2
  64. package/docs/sui-input/README.md +90 -27
  65. package/docs/sui-integration-switch/README.md +84 -17
  66. package/docs/sui-kbd/README.md +73 -9
  67. package/docs/sui-key-icon/README.md +85 -23
  68. package/docs/sui-link-button/README.md +96 -27
  69. package/docs/sui-logo/README.md +93 -22
  70. package/docs/sui-modal/README.md +72 -13
  71. package/docs/sui-modal-footer/README.md +86 -12
  72. package/docs/sui-modal-header/README.md +57 -1
  73. package/docs/sui-modal-sheet/README.md +74 -12
  74. package/docs/sui-notification/README.md +81 -11
  75. package/docs/sui-notification-footer/README.md +55 -5
  76. package/docs/sui-notification-tab-menu/README.md +61 -5
  77. package/docs/sui-otp-input/README.md +70 -9
  78. package/docs/sui-page-header/README.md +89 -19
  79. package/docs/sui-pagination/README.md +73 -13
  80. package/docs/sui-password-strength/README.md +50 -1
  81. package/docs/sui-payment-icon/README.md +80 -18
  82. package/docs/sui-period-range/README.md +70 -11
  83. package/docs/sui-popover/README.md +81 -15
  84. package/docs/sui-product-card/README.md +89 -21
  85. package/docs/sui-progress-bar/README.md +82 -15
  86. package/docs/sui-progress-bar-label/README.md +79 -18
  87. package/docs/sui-radio/README.md +84 -7
  88. package/docs/sui-radio-card/README.md +70 -16
  89. package/docs/sui-radio-label/README.md +89 -22
  90. package/docs/sui-rating/README.md +101 -31
  91. package/docs/sui-rich-editor/README.md +47 -2
  92. package/docs/sui-segmented-control/README.md +90 -21
  93. package/docs/sui-select/README.md +80 -11
  94. package/docs/sui-sidebar/README.md +71 -16
  95. package/docs/sui-slider/README.md +75 -8
  96. package/docs/sui-social-button/README.md +71 -11
  97. package/docs/sui-status-badge/README.md +75 -14
  98. package/docs/sui-stepper/README.md +75 -18
  99. package/docs/sui-store-card/README.md +85 -22
  100. package/docs/sui-switch/README.md +82 -11
  101. package/docs/sui-switch-card/README.md +80 -16
  102. package/docs/sui-switch-label/README.md +91 -14
  103. package/docs/sui-tab-chips/README.md +93 -21
  104. package/docs/sui-tab-menu-horizontal/README.md +68 -7
  105. package/docs/sui-tab-menu-vertical/README.md +64 -11
  106. package/docs/sui-table/README.md +77 -16
  107. package/docs/sui-tag/README.md +86 -12
  108. package/docs/sui-tag-input/README.md +77 -13
  109. package/docs/sui-textarea/README.md +84 -17
  110. package/docs/sui-time-picker/README.md +96 -33
  111. package/docs/sui-toast/README.md +81 -20
  112. package/docs/sui-tooltip/README.md +80 -22
  113. package/docs/sui-topbar/README.md +64 -10
  114. package/docs/sui-topbar-button/README.md +61 -7
  115. package/docs/sui-topbar-item/README.md +68 -7
  116. package/docs/sui-topbar-user/README.md +61 -5
  117. package/fesm2022/ajuarezso-sui-design-system.mjs +15 -10
  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 +7 -3
@@ -55,8 +55,8 @@ trigger no se dibuja.
55
55
 
56
56
  > **La guía del DS lo define como el menú de acciones que abre un botón** y lo arma por partes:
57
57
  > item con ícono, grupo con su etiqueta, separador, items con casilla o radio y submenús. No trae
58
- > variantes ni do/don't; el panel arranca en 300 px de ancho y 8 px separado del trigger. Lo
59
- > demás es **propuesta, falta aprobación**, salvo lo marcado canon.
58
+ > variantes ni do/don't; el panel arranca en 300 px de ancho y 8 px separado del trigger. Las
59
+ > reglas marcadas canon están aprobadas; lo que sigue en propuesta espera decisión.
60
60
  >
61
61
  > Uso medido el 03-10-2026 en `master-pedidos/src/app` (sin `master-preview`, `master-prototipos`,
62
62
  > `mockup-sheets-*`, `dx/*`, `master-rutina`, `master-medico`, `master-ejercicios`, `dev-index` ni las
@@ -77,15 +77,15 @@ trigger no se dibuja.
77
77
 
78
78
  | prop | cuándo | fuente |
79
79
  |---|---|---|
80
- | sin props (default) | El caso normal: un ⋮ o un botón con 2 a 5 acciones. El panel se ajusta al contenido. | propuesta |
81
- | `matchWidth` | El panel debe medir lo mismo que el trigger (un botón ancho que abre opciones). | propuesta |
80
+ | sin props (default) | El caso normal: un ⋮ o un botón con 2 a 5 acciones. El panel se ajusta al contenido. | canon (aprobado por el dueño, 05-10-2026, bloque B9) |
81
+ | `matchWidth` | El panel debe medir lo mismo que el trigger (un botón ancho que abre opciones). | canon (aprobado por el dueño, 05-10-2026, bloque B9) |
82
82
  | `minWidth` | El trigger es un ícono chico y el panel quedaría angosto: 200 a 240 px. | propuesta |
83
- | `position="above"` | El trigger vive pegado al borde de abajo (una barra inferior). Con `auto` ya cae arriba si no entra. | propuesta |
83
+ | `position="above"` | El trigger vive pegado al borde de abajo (una barra inferior). Con `auto` ya cae arriba si no entra. | canon (aprobado por el dueño, 05-10-2026, bloque B9) |
84
84
  | `popupRole="listbox"` | **Solo** si los items son `mode="option"`. El rol tiene que coincidir con el de los items. | según ARIA (documentado en el `.ts`) |
85
- | `popupRole="true"` | El panel no es un menú: un campo, un formulario chico (`option-image.ts:52`). | propuesta (ya es práctica) |
86
- | `origin` | El trigger lo maneja el padre (los selects del DS). En la app no hace falta. | propuesta |
85
+ | `popupRole="true"` | El panel no es un menú: un campo, un formulario chico (`option-image.ts:52`). | canon (aprobado por el dueño, 05-10-2026, bloque B9) |
86
+ | `origin` | El trigger lo maneja el padre (los selects del DS). En la app no hace falta. | según el código |
87
87
  | `push` | No usar: un panel empujado queda fijo y **se despega del trigger al scrollear**. | canon (comentario del `.ts`, bug real del color picker 21-07-2026) |
88
- | `flexibleDimensions=false` | Solo paneles de tamaño fijo que no pueden encogerse (el color picker). | propuesta |
88
+ | `flexibleDimensions=false` | Solo paneles de tamaño fijo que no pueden encogerse (el color picker). | canon (aprobado por el dueño, 05-10-2026, bloque B9) |
89
89
 
90
90
  **Usos de hoy que rompen la propuesta:**
91
91
 
@@ -103,14 +103,34 @@ El menú **no tiene `size`**: el alto lo ponen los items. El panel mide lo que s
103
103
 
104
104
  | uso | mobile | web | fuente |
105
105
  |---|---|---|---|
106
- | items de un menú de acciones | `sui-dropdown-item size="large"` (56) | `size="small"` (36) | propuesta; hoy los 3 items del ⋮ van en `large` en las dos |
107
- | trigger ⋮ | `sui-compact-button` de 40 o más | `sui-compact-button` `large` | propuesta (ver README de `sui-compact-button`) |
106
+ | items de un menú de acciones | `sui-dropdown-item size="large"` (56) | **pendiente** (D16, nota ⁵); hoy queda `small` (36), el default | mobile: canon (decisión del dueño 04-10-2026, piso táctil 44); `large` es el primer size ≥ 44, no hay 44 · web: pendiente (D16, dueño 05-10-2026): el item no tiene `medium` y `description` solo se ve en `large` y `xlarge`; hoy los 3 items del ⋮ van en `large` en las dos |
107
+ | trigger ⋮ | `sui-compact-button` `5xlarge` (44) | `sui-compact-button` `large` | canon (aprobado por el dueño, 05-10-2026, bloque B13) (ver README de `sui-compact-button`) |
108
108
  | más de 5 acciones, o alguna destructiva que pide confirmar | `sui-modal-sheet` | `sui-dropdown-menu` | propuesta, falta aprobación |
109
109
 
110
110
  ```html
111
- <sui-dropdown-item [size]="bp.isMobile() ? 'large' : 'small'" … /> <!-- bp = BreakpointsService -->
111
+ <sui-dropdown-item [size]="bp.tactil() ? 'large' : 'small'" … /> <!-- bp = BreakpointsService -->
112
112
  ```
113
113
 
114
+ ### Por dispositivo (D16)
115
+
116
+ El menú no tiene `size`, así que D16 se aplica a lo que va adentro y al trigger, que se tocan: la
117
+ tablet va con el celular. Para los items manda la fila `dropdown-item` de D16; para el ⋮, la de
118
+ `compact-button`.
119
+
120
+ | dispositivo | size | nota |
121
+ |---|---|---|
122
+ | celular chico (< 380) | items `large` · ⋮ `5xlarge` | D16 no fija los items aquí; `large` (56) es el primer size ≥ 44 (`small` mide 36 y el item no tiene `medium` con área de 44). El ⋮ va con su escala propia |
123
+ | celular (380–767) | items `large` · ⋮ `5xlarge` (44) | `large` (56) es el primer size ≥ 44 |
124
+ | tablet táctil (`pointer: coarse`) | items `large` · ⋮ `5xlarge` | Apple pide 44 también en iPad |
125
+ | computadora (`pointer: fine`) | items **pendiente** · ⋮ `large` | items: falta decidir (D16, nota ⁵): `small` (36) y `large` (56) no son el mismo item más chico o más grande, porque la descripción solo se ve en `large` y `xlarge`. Hoy queda el default `small` |
126
+
127
+ Ultrawide: igual que la computadora. Fuente: D16, dueño 05-10-2026.
128
+
129
+ En master-pedidos: items `[size]="bp.tactil() ? 'large' : 'small'"` y trigger
130
+ `[size]="bp.tactil() ? '5xlarge' : 'large'"` (no hay helper; el `'small'` es el default de hoy
131
+ mientras la computadora sigue pendiente). Con `bp.isMobile()` la tablet táctil se queda con el
132
+ tamaño de la computadora.
133
+
114
134
  ## API
115
135
 
116
136
  <!-- api:inicio -->
@@ -176,18 +196,67 @@ panel va en un `<ng-template suiDropdownPanel>` (hay que importar `SuiDropdownPa
176
196
  app, pero es lo primero que hay que mirar si un menú «no abre» dentro de un modal.
177
197
  - El host es `display: contents`: no ocupa caja. Para alinearlo, se alinea el trigger.
178
198
 
199
+ ## Bien y mal
200
+
201
+ El ⋮ en la tablet
202
+
203
+ ```html
204
+ <!-- ❌ mal: con isMobile la tablet táctil se queda con el ⋮ de la computadora, sin 44 (components/option-sections-manager/option-sections-manager.html:38) -->
205
+ <sui-compact-button trigger icon="more-2-line" variantStyle="ghost"
206
+ [size]="bp.isMobile() ? '5xlarge' : 'large'" ariaLabel="Opciones de la sección" />
207
+ ```
208
+
209
+ ```html
210
+ <!-- ✅ bien -->
211
+ <sui-compact-button trigger icon="more-2-line" variantStyle="ghost"
212
+ [size]="bp.tactil() ? '5xlarge' : 'large'" ariaLabel="Opciones de la sección" />
213
+ ```
214
+
215
+ Items sin `ng-template`
216
+
217
+ ```html
218
+ <!-- ❌ mal: los items no se proyectan en ningún lado y el menú no abre -->
219
+ <sui-dropdown-menu #menu="suiDropdownMenu">
220
+ <sui-compact-button trigger icon="more-2-line" variantStyle="ghost" ariaLabel="Opciones" />
221
+ <sui-dropdown-item label="Editar" (clicked)="editar()" />
222
+ </sui-dropdown-menu>
223
+ ```
224
+
225
+ ```html
226
+ <!-- ✅ bien -->
227
+ <sui-dropdown-menu #menu="suiDropdownMenu">
228
+ <sui-compact-button trigger icon="more-2-line" variantStyle="ghost" ariaLabel="Opciones" />
229
+ <ng-template suiDropdownPanel>
230
+ <sui-dropdown-item label="Editar" (clicked)="menu.close(); editar()" />
231
+ </ng-template>
232
+ </sui-dropdown-menu>
233
+ ```
234
+
235
+ Cerrar al elegir
236
+
237
+ ```html
238
+ <!-- ❌ mal: el item no cierra el menú; queda abierto encima del modal de edición -->
239
+ <sui-dropdown-item label="Editar" (clicked)="editar()" />
240
+ ```
241
+
242
+ ```html
243
+ <!-- ✅ bien -->
244
+ <sui-dropdown-item label="Editar" (clicked)="menu.close(); editar()" />
245
+ ```
246
+
179
247
  ## Ejemplo
180
248
 
181
249
  ```html
182
250
  <sui-dropdown-menu #menu="suiDropdownMenu">
183
- <sui-compact-button trigger icon="more-2-line" variantStyle="ghost" size="large"
251
+ <sui-compact-button trigger icon="more-2-line" variantStyle="ghost"
252
+ [size]="bp.tactil() ? '5xlarge' : 'large'"
184
253
  ariaLabel="Opciones de la sección" />
185
254
  <ng-template suiDropdownPanel>
186
- <sui-dropdown-item label="Editar" [size]="bp.isMobile() ? 'large' : 'small'"
255
+ <sui-dropdown-item label="Editar" [size]="bp.tactil() ? 'large' : 'small'"
187
256
  (clicked)="menu.close(); editar()">
188
257
  <sui-icon slot="leading" name="pencil-line" [size]="20" />
189
258
  </sui-dropdown-item>
190
- <sui-dropdown-item label="Archivar" [size]="bp.isMobile() ? 'large' : 'small'"
259
+ <sui-dropdown-item label="Archivar" [size]="bp.tactil() ? 'large' : 'small'"
191
260
  (clicked)="menu.close(); archivar()">
192
261
  <sui-icon slot="leading" name="archive-line" [size]="20" />
193
262
  </sui-dropdown-item>
@@ -36,8 +36,8 @@ aprobación.
36
36
  ## Qué variante usar
37
37
 
38
38
  > **La guía del DS no tiene buscador en su dropdown**; el buscador de lista vive en su command
39
- > menu. Este sale del Figma del DS («Dropdown Misc Items», estados Default / Hover / Active). Todo lo
40
- > de abajo es **propuesta, falta aprobación**, salvo lo marcado canon.
39
+ > menu. Este sale del Figma del DS («Dropdown Misc Items», estados Default / Hover / Active). Las
40
+ > reglas marcadas canon están aprobadas; lo que sigue en propuesta espera decisión.
41
41
  >
42
42
  > Uso medido el 03-10-2026 en `master-pedidos/src/app` (misma exclusión que `sui-dropdown-menu`):
43
43
  > **0 usos**.
@@ -57,11 +57,22 @@ No tiene tamaños: 36 px de alto (8 + 20 + 8), texto 14/20, en los dos.
57
57
 
58
58
  | uso | mobile | web | fuente |
59
59
  |---|---|---|---|
60
- | buscador del menú | igual (36) | igual (36) | propuesta |
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
62
  En mobile tiene dos problemas que el componente no resuelve (ver «Trampas»): el texto de 14 px hace
63
63
  que iOS haga zoom al enfocarlo, y el teclado no muestra la lupa. Para buscar en una lista larga en
64
- mobile, mejor un `sui-modal-sheet` con `sui-input type="search"` arriba. Propuesta, falta aprobación.
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
+
66
+ ### Por dispositivo (D16)
67
+
68
+ No aplica como tabla: **no tiene `size`** (36 px fijo, del celular chico al ultrawide), así que D16
69
+ no lo agranda; lo único que cambia por dispositivo es **si se usa**. Se toca, y 36 no llega a 44:
70
+ en el celular (también el chico) y en la tablet táctil va el sheet con `sui-input type="search"`
71
+ (que sí tiene `size`); en la computadora y el ultrawide, este buscador dentro del menú. La tablet
72
+ entra con el celular porque D16 pone la tablet táctil con el celular en todo lo que se toca.
73
+
74
+ En master-pedidos: `@if (bp.tactil())` → sheet con `<sui-input type="search" [size]="bp.sizeControl()">`;
75
+ `@else` → `sui-dropdown-search` en el menú, sin `[size]`.
65
76
 
66
77
  ## API
67
78
 
@@ -96,6 +107,51 @@ que además deja `value` en `''`.
96
107
  a mano.
97
108
  - El `<input>` no tiene `(keydown)`: Enter no hace nada salvo que quien lo usa lo escuche.
98
109
 
110
+ ## Bien y mal
111
+
112
+ Hoy no hay usos en la app (0, medido el 03-10-2026): los pares salen de «Trampas» y de la regla B7.
113
+
114
+ Qué se busca
115
+
116
+ ```html
117
+ <!-- ❌ mal: sin placeholder sale el genérico «Buscar…» y no dice qué se busca -->
118
+ <sui-dropdown-search [(value)]="busqueda" />
119
+ ```
120
+
121
+ ```html
122
+ <!-- ✅ bien -->
123
+ <sui-dropdown-search [(value)]="busqueda" placeholder="Buscar repartidor…" />
124
+ ```
125
+
126
+ Filtrar la lista
127
+
128
+ ```html
129
+ <!-- ❌ mal: el buscador no filtra; la lista sigue entera aunque se escriba -->
130
+ <sui-dropdown-search [(value)]="busqueda" placeholder="Buscar repartidor…" />
131
+ @for (r of repartidores(); track r.uid) { <sui-dropdown-item [label]="r.nombre" /> }
132
+ ```
133
+
134
+ ```html
135
+ <!-- ✅ bien: repartidoresFiltrados() es un computed sobre busqueda() -->
136
+ <sui-dropdown-search [(value)]="busqueda" placeholder="Buscar repartidor…" />
137
+ @for (r of repartidoresFiltrados(); track r.uid) { <sui-dropdown-item [label]="r.nombre" /> }
138
+ ```
139
+
140
+ Buscar en el celular
141
+
142
+ ```html
143
+ <!-- ❌ mal: 36 px sin lupa en el teclado, y iOS hace zoom al enfocarlo -->
144
+ <sui-dropdown-search [(value)]="busqueda" placeholder="Buscar repartidor…" />
145
+ ```
146
+
147
+ ```html
148
+ <!-- ✅ bien: en el celular y la tablet táctil, sheet con el input de búsqueda (B7) -->
149
+ <sui-modal-sheet [(open)]="buscando">
150
+ <sui-input type="search" [size]="bp.sizeControl()" placeholder="Buscar repartidor…"
151
+ [(value)]="busqueda" />
152
+ </sui-modal-sheet>
153
+ ```
154
+
99
155
  ## Ejemplo
100
156
 
101
157
  ```html
@@ -22,8 +22,8 @@ confirmado», los pasos de una landing, la barra de calificación.
22
22
 
23
23
  ## Qué variante usar
24
24
 
25
- > **La guía del DS no tiene este componente**: es propio de SUI. Lo marcado «canon» ya es regla
26
- > del proyecto; el resto es **propuesta, falta aprobación**.
25
+ > **La guía del DS no tiene este componente**: es propio de SUI. Las reglas marcadas canon están
26
+ > aprobadas; lo que sigue en propuesta espera decisión.
27
27
  >
28
28
  > Uso medido el 03-10-2026 en `master-pedidos/src/app`: 10 emojis en 3 archivos, sin contar
29
29
  > prototipos ni vitrinas. El catálogo tiene 59.
@@ -32,30 +32,30 @@ confirmado», los pasos de una landing, la barra de calificación.
32
32
 
33
33
  | caso | cómo | fuente |
34
34
  |---|---|---|
35
- | Estados vacíos y celebraciones | Emoji animado, uno solo, en `xl`. Nada de íconos genéricos en un estado vacío. | canon (CANON-SUI §5.6) para los estados vacíos · propuesta, falta aprobación para celebraciones y el `xl` |
36
- | Pasos de una landing | `lg`, uno por paso. | propuesta |
37
- | Junto a un título o dentro de un texto | `md` o `sm`, como acento. | propuesta |
38
- | Como ícono de un botón | No: el botón lleva `leftIcon`. | derivado de CANON-SUI §5.7 (emojis solo en contenido escrito por un usuario; botones con `leftIcon`), falta confirmación |
35
+ | Estados vacíos y celebraciones | Emoji animado, uno solo. Nada de íconos genéricos en un estado vacío. El del vacío lo pone `sui-empty-state` en `full` (caja de 120); el `size` de la celebración está **pendiente** (P38). | canon (CANON-SUI §5.6) para los estados vacíos · `full` en el vacío: según el código (`sui-empty-state.html`) · celebración: pendiente (P38) |
36
+ | Pasos de una landing | `lg`, uno por paso. | canon (aprobado por el dueño, 05-10-2026, bloque B1) |
37
+ | Junto a un título o dentro de un texto | `md` o `sm`, como acento. | canon (aprobado por el dueño, 05-10-2026, bloque B1) |
38
+ | Como ícono de un botón | No: el botón lleva `leftIcon`. | canon (CANON-SUI §5.7: emojis solo en contenido escrito por un usuario; en un botón, `leftIcon` con `sui-icon`) |
39
39
 
40
40
  ### `size`
41
41
 
42
42
  | valor | px | cuándo | fuente |
43
43
  |---|---|---|---|
44
- | `xl` | 48 | Estado vacío, celebración. | propuesta |
45
- | `lg` | 32 | Pasos de una landing, la barra de calificación. | propuesta |
46
- | `md` (default) | 24 | Acompaña un título. | propuesta |
47
- | `sm` | 20 | Dentro de una línea de texto. | propuesta |
48
- | `xs` | 16 | No usar hoy. | propuesta |
49
- | `full` | 100 % | Llena su caja: lo usa `sui-empty-state`. | propuesta |
44
+ | `xl` | 48 | El más grande con nombre. Para la celebración, **pendiente** (P38): todavía no se decidió su `size`. El estado vacío no lo usa: va en `full`. | pendiente (P38) |
45
+ | `lg` | 32 | Pasos de una landing, la barra de calificación. | canon (aprobado por el dueño, 05-10-2026, bloque B1) |
46
+ | `md` (default) | 24 | Acompaña un título. | canon (aprobado por el dueño, 05-10-2026, bloque B1) |
47
+ | `sm` | 20 | Dentro de una línea de texto. | canon (aprobado por el dueño, 05-10-2026, bloque B1) |
48
+ | `xs` | 16 | No usar hoy. | canon (aprobado por el dueño, 05-10-2026, bloque B6) |
49
+ | `full` | 100 % | Llena su caja: es el del estado vacío (`sui-empty-state` lo pone en una caja de 120). | según el código |
50
50
 
51
51
  ### `animated`, `src`, `name`, `ariaLabel`
52
52
 
53
53
  | input | cuándo | fuente |
54
54
  |---|---|---|
55
55
  | `animated` (default `true`) | Uno solo → animado. **Varios a la vez → `false`.** Ojo: solo las 5 caritas de la calificación (`pensive-face` … `grinning-face-smiling-eyes`) tienen copia estática; para el resto, `false` no cambia nada. | propuesta (la regla ya está escrita en `sui-emoji.ts`) |
56
- | `name` | Uno del catálogo. Si no está, se agrega al catálogo del DS. | propuesta |
57
- | `src` | Un emoji que no está en el catálogo, mientras se agrega. **Nunca** un `<img>` suelto con la URL del emoji. | propuesta |
58
- | `ariaLabel` | Solo si el emoji dice algo que el texto no dice. Si no, queda decorativo (`aria-hidden`). | propuesta |
56
+ | `name` | Uno del catálogo. Si no está, se agrega al catálogo del DS. | canon (aprobado por el dueño, 05-10-2026, bloque B9) |
57
+ | `src` | Un emoji que no está en el catálogo, mientras se agrega. **Nunca** un `<img>` suelto con la URL del emoji. | canon (aprobado por el dueño, 05-10-2026, bloque B9) |
58
+ | `ariaLabel` | Solo si el emoji dice algo que el texto no dice. Si no, queda decorativo (`aria-hidden`). | canon (aprobado por el dueño, 05-10-2026, bloque B5) |
59
59
 
60
60
  **Usos de hoy que romperían la propuesta:**
61
61
 
@@ -75,24 +75,45 @@ confirmado», los pasos de una landing, la barra de calificación.
75
75
 
76
76
  ## Tamaño en mobile y en web
77
77
 
78
- En mobile se lee y se toca con el dedo, así que va más grande; en web, más chico. En las
79
- pantallas que se usan en los dos (panel del local, super admin) se elige con el servicio de
80
- breakpoints de la app (`bp`, en `services/breakpoints.service.ts`); en las que solo existen en
81
- el celular (cliente, repartidor) va directo el valor de mobile. Todo es **propuesta**.
78
+ El emoji no se toca: en el celular va **un paso más grande** que en la computadora (D16, P1 b),
79
+ porque ahí se lee más de cerca y en una columna sola. Se elige con el servicio de breakpoints de
80
+ la app (`bp`, en `services/breakpoints.service.ts`), también en las pantallas del cliente y del
81
+ repartidor: el celular chico y la tablet llevan el valor de web.
82
82
 
83
- | uso | mobile | web |
84
- |---|---|---|
85
- | estado vacío, celebración | `xl` (48) | `xl` (48) |
86
- | pasos de una landing | `lg` (32) | `xl` (48) |
87
- | dentro de un texto | `md` (24) | `sm` (20) |
83
+ | uso | mobile | web | fuente |
84
+ |---|---|---|---|
85
+ | estado vacío | `full` (caja de 120) | `full` (caja de 120) | según el código: lo pone `sui-empty-state` y no cambia por dispositivo |
86
+ | celebración | **pendiente** (P38) | **pendiente** (P38) | P38 sigue abierta: no se decidió el `size` |
87
+ | pasos de una landing | `xl` (48) | `lg` (32) | web: canon (aprobado por el dueño, 05-10-2026, bloque B1) · mobile: D16, dueño 05-10-2026 (antes decía `lg` en mobile y `xl` en web, al revés de P1 b) |
88
+ | dentro de un texto | `md` (24) | `sm` (20) | propuesta; cuadra con D16 (un paso más en el celular) |
88
89
 
89
90
  ```html
90
- <sui-emoji name="hamburger" [size]="bp.isMobile() ? 'lg' : 'xl'" />
91
+ <sui-emoji name="hamburger" [size]="bp.celular() ? 'xl' : 'lg'" />
91
92
  ```
92
93
 
93
- El emoji no se toca, así que la regla del dedo no aplica: en los casos de ilustración va igual o
94
- más grande en web, donde sobra espacio. El tamaño con nombre más grande es `xl` (48); para uno más
95
- grande, `size="full"` dentro de una caja con el alto que se quiera.
94
+ El tamaño con nombre más grande es `xl` (48); para uno más grande, `size="full"` dentro de una
95
+ caja con el alto que se quiera.
96
+
97
+ ### Por dispositivo (D16)
98
+
99
+ No está en la tabla por componente de D16; como no se toca, sigue la columna «lo que no se toca»:
100
+ valor «web» en todos salvo el celular de 380 o más. Dos casos quedan fuera: el estado vacío no
101
+ cambia por dispositivo (lo pone `sui-empty-state` en `full`) y la celebración espera P38 («Lo que
102
+ esta hoja no cierra» de D16).
103
+
104
+ | dispositivo | size | nota |
105
+ |---|---|---|
106
+ | celular chico (< 380) | valor «web»: landing `lg` · texto `sm` · vacío `full` · celebración pendiente (P38) | lo que no se toca no sube en el celular chico |
107
+ | celular (380–767) | valor «mobile»: landing `xl` · texto `md` · vacío `full` · celebración pendiente (P38) | un paso más que en la computadora (P1 b); el vacío no cambia, lo pone `sui-empty-state` |
108
+ | tablet táctil (`pointer: coarse`) | valor «web»: landing `lg` · texto `sm` · vacío `full` · celebración pendiente (P38) | |
109
+ | computadora (`pointer: fine`) | valor «web»: landing `lg` · texto `sm` · vacío `full` · celebración pendiente (P38) | `sm` (20) aquí es el alto de una línea de texto 14/20, no el `small` de los controles que D16 saca |
110
+
111
+ Ultrawide: igual que la computadora. Fuente: D16, dueño 05-10-2026.
112
+
113
+ En master-pedidos: no hay helper (`bp.sizeAvatar()` es solo del avatar, con otra escala). Landing:
114
+ `[size]="bp.celular() ? 'xl' : 'lg'"` · dentro de un texto: `[size]="bp.celular() ? 'md' : 'sm'"` ·
115
+ estado vacío: no se usa `sui-emoji` suelto, va `sui-empty-state` (que lo pone en `full`) ·
116
+ celebración: pendiente (P38).
96
117
 
97
118
  ## «No usar»: por qué no hoy y cuándo sí
98
119
 
@@ -127,11 +148,54 @@ Outputs: ninguno.
127
148
  - Sin `ariaLabel` es decorativo: el lector de pantalla no lo dice.
128
149
  - `loading="lazy"`: fuera de la pantalla, no carga hasta que se acerca.
129
150
 
151
+ ## Bien y mal
152
+
153
+ El emoji de un botón
154
+
155
+ ```html
156
+ <!-- ❌ mal: un emoji no es un ícono (pages/orders/orders.html:323 y :354) -->
157
+ <sui-button variant="stroke" color="neutral" [size]="bp.sizeControl()" (click)="handleMyself(order)">
158
+ <sui-emoji data-slot="start" name="shop" size="sm" />
159
+ Encargarme yo
160
+ </sui-button>
161
+ ```
162
+
163
+ ```html
164
+ <!-- ✅ bien -->
165
+ <sui-button variant="stroke" color="neutral" [size]="bp.sizeControl()" leftIcon="store-2-line"
166
+ (click)="handleMyself(order)">Encargarme yo</sui-button>
167
+ ```
168
+
169
+ Pasos de una landing
170
+
171
+ ```html
172
+ <!-- ❌ mal: lg fijo; en el celular no sube el paso que pide D16 (pages/public/landing-customers/landing-customers.html:160) -->
173
+ <sui-emoji name="pushpin" size="lg" class="landing-customers__flow-icon"></sui-emoji>
174
+ ```
175
+
176
+ ```html
177
+ <!-- ✅ bien -->
178
+ <sui-emoji name="pushpin" [size]="bp.celular() ? 'xl' : 'lg'" class="landing-customers__flow-icon" />
179
+ ```
180
+
181
+ Un emoji del catálogo con `<img>`
182
+
183
+ ```html
184
+ <!-- ❌ mal: la URL del paquete escrita a mano, fuera del componente (pages/products/add-product/add-product.html:122) -->
185
+ <img src="https://raw.githubusercontent.com/Tarikul-Islam-Anik/Animated-Fluent-Emojis/master/Emojis/Objects/Package.png"
186
+ alt="Producto" width="48" height="48" />
187
+ ```
188
+
189
+ ```html
190
+ <!-- ✅ bien: decorativo, el título de al lado ya lo dice -->
191
+ <sui-emoji name="package" size="xl" />
192
+ ```
193
+
130
194
  ## Ejemplo
131
195
 
132
196
  ```html
133
- <!-- Celebración -->
134
- <sui-emoji name="party-popper" size="xl" />
197
+ <!-- Junto a un texto: un paso más en el celular (D16) -->
198
+ <p>Pedido confirmado <sui-emoji name="party-popper" [size]="bp.celular() ? 'md' : 'sm'" /></p>
135
199
 
136
200
  <!-- La barra de calificación: cinco juntos, estáticos -->
137
201
  <sui-emoji name="neutral-face" size="lg" [animated]="false" ariaLabel="Regular" />
@@ -31,8 +31,8 @@ algo que avisar → alert.**
31
31
  > propósito con Fluent Emoji animados en vez de ilustraciones: es una decisión del proyecto, escrita
32
32
  > en CANON-SUI §5.6 (ilustración = Fluent Emoji animado, default el zorro).
33
33
  >
34
- > Uso medido el 04-10-2026 en `master-pedidos/src/app` (misma exclusión que `sui-card`): **66
35
- > empty-states en 40 archivos**. 29 con `action` (10 con `route`). 8 con `emoji` propio y 58 sin
34
+ > Uso medido el 06-10-2026 en `master-pedidos/src/app` (misma exclusión que `sui-card`): **70
35
+ > empty-states en 42 archivos**. 31 con `action` (10 con `route`). 8 con `emoji` propio y 62 sin
36
36
  > `emoji` (sale el zorro). 0 con `icon`, 0 con la URL del zorro pegada, 0 con `size`.
37
37
 
38
38
  ### Las piezas
@@ -44,9 +44,9 @@ algo que avisar → alert.**
44
44
  | `title` (obligatorio) | Qué pasa, sin punto y en 2 a 4 palabras, con uno de tres patrones: «Todavía no hay <cosa>», «Sin <cosa>», «Tu <cosa> está vacío» («Tu carrito está vacío», «Sin pedidos aún»). En una búsqueda, repetir lo buscado («Nada con “pollo broaster”») queda fuera de esos patrones. | canon (CANON-SUI §5.6) · lo de la búsqueda: propuesta, falta aprobación |
45
45
  | `subtitle` | Una oración con punto, en futuro tranquilizador: «Cuando hagas tu primer pedido, aparecerá aquí.» | canon (CANON-SUI §5.6) |
46
46
  | `action` como string + `(actionClicked)` | La salida es algo de esta misma pantalla: «Reintentar», «Ver todo el catálogo», «Activar captura». | propuesta |
47
- | `action` como `{ label, route }` | La salida es otra pantalla: «Ver negocios» → `/cliente/inicio`. | propuesta |
48
- | sin `action` | No hay un paso obvio, o la pantalla ya tiene el botón para llenarla (el «Nuevo local» de arriba). | propuesta |
49
- | error de carga | Siempre con `action="Reintentar"`. Excepción: la pantalla reintenta sola y lo dice («Seguimos intentando»). | propuesta |
47
+ | `action` como `{ label, route }` | La salida es otra pantalla: «Ver negocios» → `/cliente/inicio`. | canon (aprobado por el dueño, 05-10-2026, bloque B10) |
48
+ | sin `action` | No hay un paso obvio, o la pantalla ya tiene el botón para llenarla (el «Nuevo local» de arriba). | canon (aprobado por el dueño, 05-10-2026, bloque B9) |
49
+ | error de carga | Siempre con `action="Reintentar"`. Excepción: la pantalla reintenta sola y lo dice («Seguimos intentando»). | canon (aprobado por el dueño, 05-10-2026, bloque B4) |
50
50
 
51
51
  **Usos de hoy que rompen las reglas:** 0. Los 29 `icon="…"` (que no es un input), las 22 URL del
52
52
  zorro pegadas a mano y los 4 errores de carga sin «Reintentar» se corrigieron el 03-10-2026 (commit
@@ -66,15 +66,32 @@ abajo (media query `bp.medium` del componente). El `size` solo cambia **el botó
66
66
  | uso | mobile | web | fuente |
67
67
  |---|---|---|---|
68
68
  | ilustración + texto | lo resuelve el componente | lo resuelve el componente | lo que hay en el DS |
69
- | botón de la acción | `large` (48) | `medium` (40) | propuesta: el tamaño es extensión SUI aprobada (03-10-2026); el «cuándo» falta aprobarlo |
69
+ | botón de la acción | `large` (48) | `medium` (40) | el tamaño es extensión SUI aprobada (03-10-2026) · mobile: canon (decisión del dueño 04-10-2026, piso táctil 44) · web: D16, dueño 05-10-2026 (fila `button`) |
70
70
 
71
71
  ```html
72
72
  <sui-empty-state
73
73
  title="Tu carrito está vacío"
74
74
  [action]="{ label: 'Ver negocios', route: '/cliente/inicio' }"
75
- [size]="bp.isMobile() ? 'large' : 'medium'" />
75
+ [size]="bp.sizeControl()" />
76
76
  ```
77
77
 
78
+ ### Por dispositivo (D16)
79
+
80
+ El `size` es el del botón de la acción, que se toca: sigue la fila `button` de D16. Sin `action`,
81
+ `size` no cambia nada y no hace falta pasarlo.
82
+
83
+ | dispositivo | size | nota |
84
+ |---|---|---|
85
+ | celular chico (< 380) | `large` | D16 pide `medium` con área de 44, pero el DS todavía no la estira: hasta entonces, `large` |
86
+ | celular (380–767) | `large` (48) | |
87
+ | tablet táctil (`pointer: coarse`) | `large` | Apple pide 44 también en iPad |
88
+ | computadora (`pointer: fine`) | `medium` (40) | el default |
89
+
90
+ Ultrawide: igual que la computadora. Fuente: D16, dueño 05-10-2026.
91
+
92
+ En master-pedidos: `[size]="bp.sizeControl()"` en todo empty-state con `action`. Medido el
93
+ 06-10-2026: 0 de los 31 con `action` lo pasan, así que en el celular y la tablet todos salen en 40.
94
+
78
95
  ## «No usar»: por qué no hoy y cuándo sí
79
96
 
80
97
  | qué | por qué no hoy | cuándo sí, en un flujo nuevo |
@@ -116,6 +133,53 @@ interface SuiEmptyStateAction { label: string; route?: string; }
116
133
  - El botón es `sui-button` `filled` `primary`: el color no se cambia; el tamaño sí, con `size` (`medium` 40 · `large` 48).
117
134
  - El emoji carga con `loading="lazy"`: en una captura de pantalla completa puede salir vacío.
118
135
 
136
+ ## Bien y mal
137
+
138
+ La acción en el celular
139
+
140
+ ```html
141
+ <!-- ❌ mal: sin size el botón sale en 40 también en el celular (pages/customer/cart/cart.html:18) -->
142
+ <sui-empty-state
143
+ title="Tu carrito está vacío"
144
+ [action]="{ label: 'Explorar negocios', route: '/cliente/inicio' }" />
145
+ ```
146
+
147
+ ```html
148
+ <!-- ✅ bien -->
149
+ <sui-empty-state
150
+ title="Tu carrito está vacío"
151
+ [action]="{ label: 'Explorar negocios', route: '/cliente/inicio' }"
152
+ [size]="bp.sizeControl()" />
153
+ ```
154
+
155
+ Un emoji propio
156
+
157
+ ```html
158
+ <!-- ❌ mal: emoji es una URL, no un nombre de sui-emoji; 'hourglass' no carga nada -->
159
+ <sui-empty-state emoji="hourglass" title="Aún no tienes facturas" />
160
+ ```
161
+
162
+ ```html
163
+ <!-- ✅ bien: la URL de FLUENT_EMOJI_ANIM, como pages/invoices/invoices.ts:50 -->
164
+ <sui-empty-state [emoji]="emptyEmoji" title="Aún no tienes facturas" />
165
+ <!-- emptyEmoji = FLUENT_EMOJI_ANIM.hourglass -->
166
+ ```
167
+
168
+ Mientras carga
169
+
170
+ ```html
171
+ <!-- ❌ mal: mientras llega la lista se lee «no hay nada» por un segundo -->
172
+ @if (pedidos().length === 0) {
173
+ <sui-empty-state title="Sin pedidos aún" />
174
+ }
175
+ ```
176
+
177
+ ```html
178
+ <!-- ✅ bien: skeleton hasta que cargue; el vacío solo con la lista ya cargada (el guard `hasLoaded()` es el de pages/locations/locations.html:15; esa pantalla, mientras carga, muestra un texto y no un skeleton) -->
179
+ @if (isLoading()) { <!-- skeleton de la lista --> }
180
+ @else if (hasLoaded() && pedidos().length === 0) { <sui-empty-state title="Sin pedidos aún" /> }
181
+ ```
182
+
119
183
  ## Ejemplo
120
184
 
121
185
  ```html