@ajuarezso/sui-design-system 0.18.10 → 0.20.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 (47) hide show
  1. package/docs/INDEX.md +136 -0
  2. package/docs/sui-alert/README.md +216 -0
  3. package/docs/sui-app-store-badge/README.md +137 -0
  4. package/docs/sui-avatar/README.md +263 -0
  5. package/docs/sui-avatar-group/README.md +115 -0
  6. package/docs/sui-badge/README.md +212 -0
  7. package/docs/sui-brand-logo/README.md +143 -0
  8. package/docs/sui-button/README.md +192 -0
  9. package/docs/sui-button-group/README.md +178 -0
  10. package/docs/sui-button-group-item/README.md +102 -0
  11. package/docs/sui-checkbox/README.md +147 -0
  12. package/docs/sui-checkbox-label/README.md +143 -0
  13. package/docs/sui-circular-progress-bar/README.md +131 -0
  14. package/docs/sui-color-dot/README.md +112 -0
  15. package/docs/sui-color-picker/README.md +124 -0
  16. package/docs/sui-compact-avatar-group/README.md +103 -0
  17. package/docs/sui-compact-button/README.md +190 -0
  18. package/docs/sui-content-divider/README.md +124 -0
  19. package/docs/sui-divider/README.md +102 -0
  20. package/docs/sui-emoji/README.md +138 -0
  21. package/docs/sui-fancy-button/README.md +148 -0
  22. package/docs/sui-file-icon/README.md +103 -0
  23. package/docs/sui-flag/README.md +99 -0
  24. package/docs/sui-form-hint/README.md +124 -0
  25. package/docs/sui-form-label/README.md +148 -0
  26. package/docs/sui-icon/README.md +135 -0
  27. package/docs/sui-kbd/README.md +97 -0
  28. package/docs/sui-key-icon/README.md +125 -0
  29. package/docs/sui-link-button/README.md +221 -0
  30. package/docs/sui-logo/README.md +152 -0
  31. package/docs/sui-payment-icon/README.md +98 -0
  32. package/docs/sui-progress-bar/README.md +132 -0
  33. package/docs/sui-progress-bar-label/README.md +143 -0
  34. package/docs/sui-radio/README.md +134 -0
  35. package/docs/sui-radio-label/README.md +137 -0
  36. package/docs/sui-rating/README.md +198 -0
  37. package/docs/sui-segmented-control/README.md +144 -0
  38. package/docs/sui-slider/README.md +143 -0
  39. package/docs/sui-social-button/README.md +158 -0
  40. package/docs/sui-status-badge/README.md +164 -0
  41. package/docs/sui-switch/README.md +146 -0
  42. package/docs/sui-switch-label/README.md +139 -0
  43. package/docs/sui-tag/README.md +145 -0
  44. package/fesm2022/ajuarezso-sui-design-system.mjs +207 -52
  45. package/fesm2022/ajuarezso-sui-design-system.mjs.map +1 -1
  46. package/package.json +1 -1
  47. package/types/ajuarezso-sui-design-system.d.ts +177 -28
@@ -0,0 +1,137 @@
1
+ ---
2
+ name: sui-radio-label
3
+ selector: sui-radio-label
4
+ resumen: Círculo con su texto (y opcionalmente descripción, badge y link) para elegir una opción de una lista corta
5
+ alternativas: [sui-radio (el círculo solo), sui-radio-card (cada opción es una tarjeta), sui-select (más de 6 opciones), sui-checkbox-label (varias a la vez)]
6
+ ---
7
+
8
+ # sui-radio-label
9
+
10
+ Story: [Atoms/Checkbox, Radio & Switch/Radio Label](https://sui-design-system.web.app/?path=/docs/atoms-checkbox-radio-switch-radio-label--docs)
11
+
12
+ ## Cuándo usarlo / cuándo no
13
+
14
+ **Úsalo** para una lista corta de opciones de texto donde se elige una y la lista es densa: las
15
+ franjas horarias del checkout, tipo de documento. Toda la fila se toca. Va dentro de un
16
+ `sui-radio-group`.
17
+
18
+ **No lo uses** para:
19
+
20
+ - Opciones con identidad propia (ícono o logo, descripción, borde): `sui-radio-card`. Es lo que
21
+ usa la app para método de pago, tipo de entrega y modo de asignación.
22
+ - **Una lista de motivos que se toca en el teléfono**: la app ya resolvió ese caso con
23
+ `sui-radio-card type="basic"` y `bp.controlSize()` (motivos de cancelación del local,
24
+ `pages/orders/orders.html:566`). El radio-label no tiene `size` y en mobile queda chico.
25
+ - Más de ~6 opciones: `sui-select`.
26
+ - Varias a la vez: `sui-checkbox-label`.
27
+ - Prender algo al instante: `sui-switch-label`.
28
+
29
+ Regla corta: **lista densa de texto → radio-label · motivos u opciones que se tocan en el teléfono
30
+ o con peso → radio-card · muchas → select.**
31
+
32
+ ## Qué variante usar
33
+
34
+ > **La guía del DS muestra las piezas, no cuándo usarlas.** Su ejemplo con label es una lista de
35
+ > motivos («Spam», «Acoso», «Infringe las reglas»); el «avanzado» suma (Sublabel), badge «NEW»,
36
+ > descripción, Link Button y la versión con el círculo a la derecha.
37
+ >
38
+ > Uso medido el 03-10-2026 en `master-pedidos/src/app` (misma exclusión que `sui-badge`):
39
+ > **1 uso**, las franjas horarias del checkout, con `flip`. Más **1 radio nativo** de motivos
40
+ > (ver abajo a qué componente va).
41
+
42
+ ### Las piezas
43
+
44
+ | prop | cuándo | fuente |
45
+ |---|---|---|
46
+ | `value` | Siempre (es obligatorio). Lo que lee el `sui-radio-group`. | API |
47
+ | `label` | Siempre. La opción en pocas palabras («Dirección no encontrada», «12:00 — 12:30 p. m.»). | según la guía del DS (pieza) |
48
+ | `sublabel` | Precisión corta entre paréntesis: «(recomendado)». | según la guía del DS |
49
+ | `description` | Una frase de qué pasa si se elige. Sube el label a peso medio. Si casi todas las opciones la necesitan, probablemente es `sui-radio-card`. | según la guía del DS (pieza); el «cuándo» es propuesta |
50
+ | `badge` | Solo novedad. Con string sale naranja `light`: pasa el objeto. | según la guía del DS (pieza «NEW»); el color es propuesta |
51
+ | `link` | «Ver detalle» que no elige la opción. Solo se ve con `description`. | según la guía del DS |
52
+ | `flip` | Círculo a la derecha en listas de opciones tipo hoja (sheet) y de ajustes, donde el texto es lo que se lee y el círculo es el indicador, como las franjas del checkout. En formularios, a la izquierda. | canon (aprobado por el dueño, 03-10-2026) |
53
+ | `disabled` | La opción existe pero no aplica (franja ya llena). | según la guía del DS |
54
+ | `size` | `medium` (default, Figma): círculo 20, label 14/20. `large`: círculo 24, label 16/24. `xlarge`: círculo 28, label 16/24. En large y xlarge el sublabel y la descripción pasan a 14/20, el badge sin `size` propio sube a `medium` / `large` y el link a `medium`. Se usa con `[size]="bp.controlSize()"`. | `medium`: según la guía del DS · `large` / `xlarge`: canon (aprobado por el dueño, 03-10-2026), extensión SUI |
55
+
56
+ **Usos de hoy:**
57
+
58
+ - **1 radio nativo de motivos:** `pages/driver/driver-order-detail/driver-order-detail.html:812`
59
+ (motivos de cancelación del repartidor). Por la pantalla hermana va a `sui-radio-card
60
+ type="basic"` como `pages/orders/orders.html:566`, no a este componente.
61
+ - El único uso actual, `pages/customer/checkout/checkout.html:480`, controla la selección con
62
+ `[checked]` + `(selected)` por fila, sin `sui-radio-group`. Funciona porque el padre fija el
63
+ estado, pero no se enchufa a un formulario.
64
+
65
+ ## Tamaño en mobile y en web
66
+
67
+ En mobile se toca con el dedo: el área táctil tiene que medir **44 px o más**. En web, con mouse,
68
+ va un tamaño más chico. La app ya lo resuelve con `BreakpointsService`
69
+ (`src/app/services/breakpoints.service.ts`, mobile = hasta 767 px); para controles,
70
+ `bp.controlSize()` devuelve `xlarge` en mobile y `large` en web (regla de controles grandes del
71
+ dueño, 28-09-2026).
72
+
73
+ | uso | mobile | web | fuente |
74
+ |---|---|---|---|
75
+ | opciones de un formulario o una hoja (franjas horarias, método de entrega) | `xlarge` (círculo 28, label 16/24) | `large` (círculo 24, label 16/24) | canon (aprobado por el dueño, 03-10-2026) |
76
+ | lista densa de desktop | — | `medium` (círculo 20, label 14/20) | propuesta, falta aprobación |
77
+ | opciones con mucho contexto cada una | `sui-radio-card` con `bp.controlSize()` | `sui-radio-card` con `bp.controlSize()` | propuesta |
78
+
79
+ ```html
80
+ <sui-radio-label value="recojo" [size]="bp.controlSize()" label="Recojo en tienda" />
81
+ ```
82
+
83
+ Toda la fila se toca (el host recibe el click). Con `xlarge` el contenido baja 2 px para que la
84
+ línea del label (24) quede centrada con el círculo (28).
85
+
86
+ ## «No usar»: por qué no hoy y cuándo sí
87
+
88
+ | qué | por qué no hoy | cuándo sí, en un flujo nuevo |
89
+ |---|---|---|
90
+ | `badge` | ninguna opción es nueva | una opción recién agregada a una lista que el usuario ya conoce |
91
+ | `link` | ninguna opción necesita detalle | «Plan Pro» con «Ver precios» (con `description`) |
92
+
93
+ ## API
94
+
95
+ <!-- api:inicio -->
96
+ <!-- generada: no editar a mano — `npm run docs` -->
97
+
98
+ | input | tipo | default |
99
+ |---|---|---|
100
+ | `value` | `string` | — (requerido) |
101
+ | `label` | `string` | `''` |
102
+ | `sublabel` | `string \| null` | `null` |
103
+ | `description` | `string \| null` | `null` |
104
+ | `badge` | `SuiBadgeConfig \| string \| null` | `null` |
105
+ | `link` | `SuiLinkConfig \| string \| null` | `null` |
106
+ | `size` | `'medium' \| 'large' \| 'xlarge'` | `'medium'` |
107
+ | `flip` | `boolean` | `false` |
108
+ | `disabled` | `boolean` | `false` |
109
+ | `checked` · model, `[(checked)]` | `boolean` | `false` |
110
+
111
+ | output | payload |
112
+ |---|---|
113
+ | `selected` | `string` |
114
+ | `linkClick` | `MouseEvent` |
115
+ <!-- api:fin -->
116
+
117
+ ## Trampas
118
+
119
+ - **No implementa `ControlValueAccessor`.** El formulario se engancha al `sui-radio-group`, no a
120
+ cada opción. Sin grupo, se maneja con `[(checked)]`.
121
+ - **No tiene `size`**: el círculo mide 20 px siempre. Choca con la regla de controles grandes
122
+ (dueño, 28-09-2026) en mobile; si hace falta grande, `sui-radio-card`.
123
+ - **Tocar una opción ya elegida no la desmarca** (bien, es un radio). Tocar el link tampoco la elige.
124
+ - **`link` solo se dibuja con `description`.**
125
+ - El `sui-radio` de adentro no recibe clics (`pointer-events: none`): no le pongas handlers.
126
+ - El badge con string sale naranja; en `sui-radio-card` el default es azul.
127
+ - `flip` usa CSS `order`: el orden para lector de pantalla no cambia.
128
+
129
+ ## Ejemplo
130
+
131
+ ```html
132
+ <sui-radio-group [(ngModel)]="motivo" class="lista-vertical">
133
+ @for (m of motivos; track m) {
134
+ <sui-radio-label [value]="m" [label]="m" />
135
+ }
136
+ </sui-radio-group>
137
+ ```
@@ -0,0 +1,198 @@
1
+ ---
2
+ name: sui-rating
3
+ selector: sui-rating
4
+ resumen: Muestra una nota con estrellas o la pide (estrellas, emojis, corazones o números)
5
+ alternativas: [sui-icon (una estrella o un corazón sueltos, sin nota), sui-badge (la nota escrita como dato corto)]
6
+ ---
7
+
8
+ # sui-rating
9
+
10
+ Story: [Atoms/Rating](https://sui-design-system.web.app/?path=/docs/atoms-rating--docs)
11
+
12
+ ## Cuándo usarlo / cuándo no
13
+
14
+ **Úsalo** para dos cosas distintas:
15
+
16
+ 1. **Mostrar una nota** (de un local, un repartidor, un cliente, una reseña): `readonly`, y con
17
+ `halfStep` si es un promedio.
18
+ 2. **Pedir una nota** (calificar el pedido, al local, al repartidor): interactivo, con
19
+ `(valueChange)` o un `FormControl`.
20
+
21
+ **No lo uses** para:
22
+
23
+ - Un ícono suelto de estrella o corazón (favoritos, «lo guardaron 12»): `sui-icon`.
24
+ - Escribir el número de la nota: eso lo escribe la pantalla con coma («4,7»), no el rating.
25
+ - Recalcular la nota a partir de una lista de reseñas (ver la regla de abajo).
26
+
27
+ **La nota que se muestra sale del agregado de la entidad** (`rating` y `ratingCount` del
28
+ documento, que calcula la Cloud Function sobre todas las calificaciones), nunca de promediar la
29
+ lista cargada, que viene con tope. La fórmula depende de `n`: otro denominador da otro número.
30
+ **Canon** (CLAUDE.md, «Una nota, una sola cuenta», 26-08-2026).
31
+
32
+ ## Qué variante usar
33
+
34
+ > **La guía del DS no trae un componente de rating: trae ejemplos** para que cada producto arme
35
+ > el suyo («cada sistema de calificación se comporta distinto»). Los ejemplos son: nota con
36
+ > reseñas («4.5 ∙ 5.2K Ratings · 18 reviews»), celdas, barra de estrellas, barra de corazones,
37
+ > barra de emojis, barra de números del 1 al 5 y barra con área de texto. No trae tamaños, ni
38
+ > do/don't. Lo marcado «según la guía del DS» es que el ejemplo existe; el cuándo es
39
+ > **propuesta, falta aprobación**, salvo lo que diga «canon».
40
+ >
41
+ > Uso medido el 03-10-2026 en `master-pedidos/src/app`: **23 ratings en 13 pantallas**, sin
42
+ > contar prototipos ni vitrinas (`master-preview`, `master-prototipos`, `mockup-sheets-*`,
43
+ > `dx/*`, `_guia-*`, `master-rutina`, `master-medico`, `master-ejercicios`, `dev-index`).
44
+ > `appearance`: 21 `inline` (13 por default, 8 explícitos), 2 `bar`. `type`: 20 `star`,
45
+ > 1 `emoji`, 1 `heart`, 1 dinámico. `size`: 10 `small`, 6 `medium`, 1 `large`, 4 `xlarge`, 2 en
46
+ > `bar` (sin size). 20 son `readonly`.
47
+
48
+ ### `appearance` — para qué
49
+
50
+ | appearance | cuándo | ejemplo real | fuente |
51
+ |---|---|---|---|
52
+ | `inline` (default), `readonly` | **Mostrar** una nota: la fila de estrellas pelada. | `components/ratings-detail/ratings-detail.html:31` (la nota del local) | según la guía del DS (ejemplo «Rating Review») |
53
+ | `inline`, interactivo | **A discutir.** Pedir la nota en grande (`xlarge`). Cada estrella mide 28 px, por debajo de los 44 que pide un dedo. | `pages/customer/rate-order/rate-order.html:93` y `:166` | propuesta, falta aprobación |
54
+ | `bar` | **Pedir** una nota en el celular: celdas de 64 × 36 fáciles de tocar. | `components/rate-entity-card/rate-entity-card.html:40` | según la guía del DS (ejemplo «Rating Bar») |
55
+ | `cell` | **No usar** hoy (ver abajo). | — | según la guía del DS (ejemplo «Rating Cell») |
56
+ | `bar-area` | **No usar** hoy (ver abajo). | — | según la guía del DS (ejemplo «Rating Bar With Textarea») |
57
+
58
+ ### `type`
59
+
60
+ | type | cuándo | fuente |
61
+ |---|---|---|
62
+ | `star` (default) | La nota de un pedido, un local, un repartidor o un cliente: la que entra al promedio. | según la guía del DS (ejemplo) · propuesta (cuándo) |
63
+ | `emoji` | Cómo le fue a la persona con la app o con un proceso (no entra a la nota de nadie). Solo en `bar`. Hoy: `components/rate-entity-card/rate-entity-card.html:38`. | según la guía del DS (ejemplo «Single Selectable») · propuesta (cuándo) |
64
+ | `heart` | **No usar** como nota (ver abajo). | según la guía del DS (ejemplo «Rating Bar Heart») |
65
+ | `number` | **No usar** hoy (ver abajo). Solo en `bar`. | según la guía del DS (ejemplo de 1 a 5) |
66
+
67
+ ### `size` (solo cuenta en `inline`)
68
+
69
+ | size | px | cuándo | fuente |
70
+ |---|---|---|---|
71
+ | `small` | 16 | Web: filas y listas (cada reseña, tablas, historial). | propuesta, falta aprobación |
72
+ | `medium` (default) | 20 | Web: perfil, ficha, detalle. Mobile: filas y listas. | propuesta, falta aprobación |
73
+ | `large` | 24 | Web: la nota grande de un resumen, la card de una calificación enviada. Mobile: perfil y ficha. | propuesta, falta aprobación |
74
+ | `xlarge` | 28 | Extensión SUI. Mobile: la nota grande de un resumen. | propuesta, falta aprobación |
75
+
76
+ ### El resto de las propiedades
77
+
78
+ | propiedad | valor | cuándo | fuente |
79
+ |---|---|---|---|
80
+ | `readonly` | `true` | **Todo rating que muestra una nota.** Sin él se puede tocar y cambiar. | propuesta, falta aprobación |
81
+ | `halfStep` | `true` | Obligatorio si el valor es un **promedio** (sin él, 4,5 se pinta como 4). Una nota individual es entera y no lo necesita. Solo funciona en `inline` + `readonly`. | según la guía del DS (muestra 4.5) |
82
+ | `value` | `0` | Solo dentro del botón que invita a calificar, junto al texto «Calificar» (`pages/customer/order-history/order-history.html:352`). Suelto, cinco estrellas vacías se leen como «0 estrellas». | propuesta, falta aprobación |
83
+ | `max` | `5` (default) | Siempre 5 en notas: la fórmula de la nota asume 5. | propuesta, falta aprobación |
84
+ | `max` | otro | Solo con `type="number"` (encuesta de 1 a 10). | propuesta, falta aprobación |
85
+ | `alignment` | `inline` (default) | Solo los íconos; el número y el conteo los escribe la pantalla. | propuesta, falta aprobación |
86
+ | `alignment` | `horizontal` | Junto con `reviewLink`. | propuesta, falta aprobación |
87
+ | `alignment` | `vertical` | **No usar** (ver abajo). | propuesta, falta aprobación |
88
+ | `reviewLink` | `{ label, href? }` | Link «Ver 128 reseñas» que abre la hoja de reseñas. Hoy sin usos. | según la guía del DS («18 reviews») |
89
+ | `showValue` | `true` | **No usar** (ver abajo). | propuesta, falta aprobación |
90
+ | `reviewCount` | número | **No usar** (ver abajo). | propuesta, falta aprobación |
91
+ | `emojis` | lista | Dejar el default (cinco caras, de triste a feliz). Cambiarlo rompe la lectura de la escala. | propuesta, falta aprobación |
92
+ | `ariaLabel` | texto | Decir qué se califica: «Califica al repartidor», «Nota del local». El default es «Calificación». | propuesta, falta aprobación |
93
+ | `reason` / `reasonPlaceholder` / `reasonRows` | — | Solo en `bar-area`. **El placeholder va siempre en español**: el default está en inglés. | propuesta, falta aprobación |
94
+
95
+ **Usos de hoy que rompen la regla:**
96
+
97
+ - **1 `reviewCount`** (pinta «Ratings» en inglés):
98
+ `pages/super-admin/master-users/master-user-sheet.html:142`.
99
+ - **1 rating usado como ícono** (`type="heart"` con `max=1` para los favoritos):
100
+ `components/ratings-detail/ratings-detail.html:45`. Va `sui-icon name="heart-3-fill"`.
101
+ - **A discutir, 2 `inline` interactivos de 28 px** como input:
102
+ `pages/customer/rate-order/rate-order.html:93` y `:166`. Alternativa: `appearance="bar"`.
103
+
104
+ ## No usar: por qué no hoy y cuándo sí
105
+
106
+ | qué | por qué no hoy | cuándo sí, en un flujo nuevo |
107
+ |---|---|---|
108
+ | `appearance="cell"` | En el celular `bar` es más clara y más fácil de tocar. | Calificar una sola cosa a pantalla completa en tablet o web. |
109
+ | `appearance="bar-area"` | La calificación de hoy separa la nota, los chips y el comentario. | Una encuesta corta de una sola card: nota + «Cuéntanos qué pasó». |
110
+ | `type="heart"` | La app califica con estrellas; el corazón es favoritos. | «¿Cuánto te gustó?» de una campaña, donde una estrella se leería como la nota del local. |
111
+ | `type="number"` | No hay encuestas de 1 a 10. | «¿Qué tan probable es que nos recomiendes?» con `max=10` (ocupa 640 px de ancho). |
112
+ | `showValue` | Escribe «4.7» con punto; la app escribe «4,7». | Si el DS formatea en `es-PE`. |
113
+ | `reviewCount` | Agrega la palabra «Ratings» en inglés y abrevia «1.2K». | Si el DS traduce la etiqueta. |
114
+ | `alignment="vertical"` | Solo ordena `showValue` y `reviewCount`, que no se usan. | Junto con esos dos, si se arreglan. |
115
+
116
+ ## Tamaño en mobile y en web
117
+
118
+ Criterio de la app: **en mobile se lee y se toca con el dedo, así que va más grande; en web
119
+ (desktop), más chico.** Mobile es `≤ 767 px`, el mismo corte de `BreakpointsService.isMobile()`
120
+ en master-pedidos. Fuente: canon del dueño (03-10-2026), el mismo patrón que ya usan 27 controles
121
+ de la app.
122
+
123
+ | uso | mobile | web | fuente |
124
+ |---|---|---|---|
125
+ | Mostrar en una fila o lista | `medium` (20) | `small` (16) | propuesta, falta aprobación |
126
+ | Mostrar en perfil o ficha | `large` (24) | `medium` (20) | propuesta, falta aprobación |
127
+ | La nota grande de un resumen | `xlarge` (28) | `large` (24) | propuesta, falta aprobación |
128
+ | Pedir una nota | `appearance="bar"` (celdas de 64 × 36) | `bar`, o `inline` `large` con mouse | propuesta, falta aprobación |
129
+
130
+ ```ts
131
+ readonly bp = inject(BreakpointsService); // master-pedidos: src/app/services/breakpoints.service.ts
132
+ ```
133
+
134
+ ```html
135
+ <sui-rating [value]="local.rating" [readonly]="true" [halfStep]="true"
136
+ [size]="bp.isMobile() ? 'medium' : 'small'" ariaLabel="Nota del local" />
137
+ ```
138
+
139
+ En `bar` el `size` no cuenta (íconos fijos de 20) y en `cell` tampoco (32): el tamaño por
140
+ plataforma solo se elige en `inline`.
141
+
142
+ ## API
143
+
144
+ <!-- api:inicio -->
145
+ <!-- generada: no editar a mano — `npm run docs` -->
146
+
147
+ | input | tipo | default |
148
+ |---|---|---|
149
+ | `appearance` | `'inline' \| 'cell' \| 'bar' \| 'bar-area'` | `'inline'` |
150
+ | `type` | `'star' \| 'heart' \| 'emoji' \| 'number'` | `'star'` |
151
+ | `value` · model, `[(value)]` | `number` | `0` |
152
+ | `max` | `number` | `5` |
153
+ | `size` | `'small' \| 'medium' \| 'large' \| 'xlarge'` | `'medium'` |
154
+ | `readonly` | `boolean` | `false` |
155
+ | `halfStep` | `boolean` | `false` |
156
+ | `showValue` | `boolean` | `false` |
157
+ | `reviewCount` | `number \| null` | `null` |
158
+ | `reviewLink` | `SuiRatingReviewLink \| null` | `null` |
159
+ | `alignment` | `'inline' \| 'vertical' \| 'horizontal'` | `'inline'` |
160
+ | `emojis` | `string[]` | `[ 'pensive-face', 'confused-face', 'neutral-face', 'slightly-smiling-face', 'grinning-face-smiling-eyes', ]` |
161
+ | `ariaLabel` | `string` | `'Calificación'` |
162
+ | `reason` · model, `[(reason)]` | `string` | `''` |
163
+ | `reasonPlaceholder` | `string` | `'Tell us why!'` |
164
+ | `reasonRows` | `number` | `3` |
165
+
166
+ | output | payload |
167
+ |---|---|
168
+ | `valueChange` | `number` |
169
+ <!-- api:fin -->
170
+
171
+ Implementa `ControlValueAccessor` sobre `value` (sirve con `formControlName`). `reason` es un
172
+ `model` aparte: no entra al `FormControl`.
173
+
174
+ ## Trampas
175
+
176
+ - **`showValue`, `reviewCount` y `reviewLink` solo se ven con `alignment="horizontal"` o
177
+ `"vertical"`.** Con el default `inline` no aparecen.
178
+ - **`reviewCount` agrega «Ratings» en inglés** y `showValue` formatea con punto.
179
+ - **`reasonPlaceholder` viene en inglés** («Tell us why!»).
180
+ - **`halfStep` solo funciona en `inline` + `readonly`.** En un input no hay medias.
181
+ - **En `bar` el `size` no hace nada** (íconos de 20, celdas de 64); en `cell` los íconos miden 32.
182
+ - **En `bar` y `bar-area` se pinta solo la celda elegida**, no las anteriores: no es una fila de
183
+ estrellas que se llena.
184
+ - `emoji` y `number` **solo se dibujan en `bar`**.
185
+ - `(valueChange)` emite de 1 a `max`, nunca 0; volver a tocar la misma no la quita.
186
+ - El host lleva `role="radiogroup"` y `aria-label`: si la pantalla ya escribe la nota al lado,
187
+ ponle `aria-hidden="true"` para que no se lea dos veces.
188
+
189
+ ## Ejemplo
190
+
191
+ ```html
192
+ <!-- Mostrar la nota del local (promedio, desde el agregado) -->
193
+ <span class="nota">{{ notaTexto }}</span>
194
+ <sui-rating [value]="local.rating" [readonly]="true" [halfStep]="true" size="small" aria-hidden="true" />
195
+
196
+ <!-- Pedir la nota al repartidor -->
197
+ <sui-rating appearance="bar" type="star" [(value)]="nota" ariaLabel="Califica al repartidor" />
198
+ ```
@@ -0,0 +1,144 @@
1
+ ---
2
+ name: sui-segmented-control
3
+ selector: sui-segmented-control
4
+ resumen: Píldora de 2 a 4 opciones hermanas donde se elige una y se aplica en el momento
5
+ alternativas: [sui-tab-menu-horizontal (navegar entre secciones de contenido), sui-tab-chips (fila de filtros con muchas opciones), sui-radio-card (elegir una opción de un formulario que se guarda después), sui-button-group (varias opciones a la vez)]
6
+ ---
7
+
8
+ # sui-segmented-control
9
+
10
+ Story: [Atoms/Segmented Control](https://sui-design-system.web.app/?path=/docs/atoms-segmented-control--docs)
11
+
12
+ ## Cuándo usarlo / cuándo no
13
+
14
+ **Úsalo** para cambiar entre 2 a 4 modos de **la misma vista**, que se aplican apenas se tocan:
15
+ cuadrícula o lista en el catálogo, el local cuyo horario estás viendo, el rol que vas a invitar.
16
+
17
+ **No lo uses** para:
18
+
19
+ - Navegar entre secciones de contenido distinto: `sui-tab-menu-horizontal`.
20
+ - Una fila de filtros con muchas opciones o que scrollea: `sui-tab-chips`.
21
+ - Elegir un valor que se guarda después con un botón: `sui-radio-card` (no se enchufa a un formulario).
22
+ - Varias opciones a la vez: `sui-button-group`.
23
+ - 5 o más opciones: los segmentos se reparten el ancho y los textos se cortan.
24
+
25
+ Regla corta: **2-4 modos de la misma vista, efecto inmediato → segmented · secciones → tab menu · filtros → tab-chips.**
26
+
27
+ ## Qué variante usar
28
+
29
+ > **La guía del DS lo define como control para elegir una opción de un conjunto lineal de
30
+ > opciones muy relacionadas «y aplicarla de inmediato»**. Muestra la versión base y la redondeada
31
+ > (`Rounded`). No trae tamaños ni do/don't.
32
+ >
33
+ > Uso medido el 03-10-2026 en `master-pedidos/src/app` (misma exclusión que `sui-badge`):
34
+ > **5 usos en 4 archivos**. Tamaños: 3 `small`, 1 `medium`, 1 sin `size` (`medium`). 1 con solo
35
+ > íconos (vista cuadrícula / lista del catálogo).
36
+
37
+ ### `size`
38
+
39
+ | size | cuándo | fuente |
40
+ |---|---|---|
41
+ | `xlarge` | Mobile: es el único que se acerca a los 44 px de área táctil (segmento de 40 px). | canon (dueño: «componentes grandes», 28-09-2026, y tamaño por plataforma, 03-10-2026) |
42
+ | `large` | No aporta: 32 px, chico para el dedo y grande para web. | propuesta, falta aprobación |
43
+ | `medium` (default) | Web, junto a títulos o filtros. | canon (dueño: tamaño por plataforma, 03-10-2026); el valor exacto es propuesta |
44
+ | `small` | Web, solo íconos en una barra de herramientas (cuadrícula / lista). | propuesta, falta aprobación |
45
+
46
+ ### Ítems (`tabs`)
47
+
48
+ | qué | cuándo | fuente |
49
+ |---|---|---|
50
+ | texto | Lo normal. Una o dos palabras por segmento («Por distancia», «Precio único»). | según la guía del DS (ejemplos con texto) |
51
+ | `icon` + texto | Cuando el ícono ayuda a reconocer el modo rápido. | propuesta, falta aprobación |
52
+ | `iconOnly` | Solo para modos universales (cuadrícula / lista). El `label` pasa a ser el nombre accesible: escríbelo igual. | propuesta, falta aprobación |
53
+ | `disabled` (por ítem) | Un modo que existe pero no aplica todavía. | propuesta, falta aprobación |
54
+
55
+ **Usos de hoy que rompen las reglas:**
56
+
57
+ - **2 dentro de un borrador que se guarda después (a discutir):**
58
+ `pages/super-admin/master-settings/master-settings.html:296` (modo de envío: «Por distancia» /
59
+ «Precio único») y `:329` («Por calle» / «En línea recta»). La guía dice que se aplica de
60
+ inmediato; aquí la elección espera al «Guardar». Es trabajo de `sui-radio-card`.
61
+ - **1 con textos largos, a verificar en mobile:** `pages/super-admin/master-roles/master-roles.html:47`
62
+ («Administrador de ciudad» / «Jefe de operaciones»). Por ancho de texto quedan apretados cerca
63
+ del borde a 390 px; no se midió en la pantalla real.
64
+ - **2 en `small` con texto** (la propuesta deja `small` solo para íconos): `master-settings.html:296`
65
+ y `:329`. El tercero en `small`, `pages/products/products.html:80`, está bien: solo íconos.
66
+ - **5 sin tamaño por plataforma** (todos fijos): ninguno cambia a `xlarge` en mobile. Ejemplo:
67
+ `pages/driver/driver-schedule/driver-schedule.html:32` (`size="medium"` en una pantalla del
68
+ repartidor, que se usa en el teléfono).
69
+
70
+ ## Tamaño en mobile y en web
71
+
72
+ Alto de cada segmento: `small` 28 px, `medium` 28 px, `large` 32 px, `xlarge` 40 px (el control
73
+ suma 8 px de borde interno).
74
+
75
+ | uso | mobile | web |
76
+ |---|---|---|
77
+ | modos con texto (local del horario, rol) | `xlarge` (segmento de 40 px) | `medium` |
78
+ | solo íconos en una barra (cuadrícula / lista) | `xlarge` | `small` |
79
+
80
+ ```html
81
+ <sui-segmented-control [size]="bp.isMobile() ? 'xlarge' : 'medium'" … />
82
+ ```
83
+
84
+ El más grande llega a 40 px por segmento: queda 4 px corto de los 44 px de área táctil. Es el
85
+ mejor disponible para mobile.
86
+
87
+ ## «No usar»: por qué no hoy y cuándo sí
88
+
89
+ | qué | por qué no hoy | cuándo sí, en un flujo nuevo |
90
+ |---|---|---|
91
+ | `large` | 32 px: no llega al dedo en mobile y sobra en web | un control dentro de un sheet de mobile muy apretado donde `xlarge` no entra |
92
+ | `disabled` por ítem | ningún modo está bloqueado | un modo que depende del plan («Reportes completos» en plan básico) |
93
+ | 5+ ítems | se cortan los textos | nunca: pasa a `sui-tab-chips` |
94
+
95
+ ## API
96
+
97
+ <!-- api:inicio -->
98
+ <!-- generada: no editar a mano — `npm run docs` -->
99
+
100
+ | input | tipo | default |
101
+ |---|---|---|
102
+ | `tabs` | `SuiSegmentedControlItem<T>[]` | — (requerido) |
103
+ | `active` · model, `[(active)]` | `T` | `undefined` |
104
+ | `size` | `'small' \| 'medium' \| 'large' \| 'xlarge'` | `'medium'` |
105
+
106
+ | output | payload |
107
+ |---|---|
108
+ | `tabChange` | `T` |
109
+ <!-- api:fin -->
110
+
111
+ ```ts
112
+ interface SuiSegmentedControlItem<T = string> {
113
+ value: T;
114
+ label: string;
115
+ icon?: string; // RemixIcon, 20 px fijo
116
+ iconOnly?: boolean; // requiere icon; label pasa a aria-label
117
+ disabled?: boolean;
118
+ }
119
+ ```
120
+
121
+ ## Trampas
122
+
123
+ - **Sin `active` igual dibuja el fondo blanco bajo el primer ítem**, aunque ninguno esté elegido.
124
+ Pásale siempre un valor inicial.
125
+ - **Emite dos veces**: `tabChange` y `activeChange`. Con `[(active)]` no hace falta `(tabChange)`.
126
+ - **No implementa `ControlValueAccessor`**: no va con `formControlName`.
127
+ - **Ocupa todo el ancho del padre.** Para que no se estire, envuélvelo con un `max-width`.
128
+ - El indicador se mide con el DOM: si arranca oculto queda en ancho 0 hasta que algo cambie de tamaño.
129
+ - `iconOnly` sin `icon` no dibuja nada. El ícono mide 20 px en todos los tamaños.
130
+ - `disabled` es por ítem; no hay `disabled` del control entero.
131
+ - Los tamaños son extensión de SUI: la guía tiene uno solo. Su versión redondeada (`Rounded`)
132
+ no existe en SUI.
133
+
134
+ ## Ejemplo
135
+
136
+ ```html
137
+ <div style="max-width: 320px">
138
+ <sui-segmented-control
139
+ [size]="bp.isMobile() ? 'xlarge' : 'small'"
140
+ [tabs]="[{ value: 'grid', label: 'Vista cuadrícula', icon: 'layout-grid-line', iconOnly: true },
141
+ { value: 'list', label: 'Vista lista', icon: 'list-unordered', iconOnly: true }]"
142
+ [(active)]="vista" />
143
+ </div>
144
+ ```
@@ -0,0 +1,143 @@
1
+ ---
2
+ name: sui-slider
3
+ selector: sui-slider
4
+ resumen: Barra para elegir un valor, o un rango, dentro de una escala donde importa ver la posición
5
+ alternativas: [sui-counter-input (cantidad chica y exacta con − y +), sui-input type="number" (valor exacto o rango abierto), sui-progress-bar (avance de solo lectura)]
6
+ ---
7
+
8
+ # sui-slider
9
+
10
+ Story: [Atoms/Slider](https://sui-design-system.web.app/?path=/docs/atoms-slider--docs)
11
+
12
+ ## Cuándo usarlo / cuándo no
13
+
14
+ **Úsalo** cuando el valor vive en una escala con mínimo y máximo y **ver dónde cae** ayuda a
15
+ decidir: el radio de cobertura del repartidor en km, un rango de precios en un filtro, un
16
+ porcentaje.
17
+
18
+ **No lo uses** para:
19
+
20
+ - Una cantidad chica y exacta (unidades, comensales): `sui-counter-input`.
21
+ - Un valor exacto o sin tope (un monto, un DNI): `sui-input`.
22
+ - Mostrar avance sin que se pueda mover: `sui-progress-bar`.
23
+ - Dinero que se cobra: el arrastre no da precisión; va un `sui-input type="amount"`.
24
+
25
+ Regla corta: **escala con tope y la posición importa → slider · número exacto → input · cantidad chica → counter.**
26
+
27
+ ## Qué variante usar
28
+
29
+ > **La guía del DS lo define como selección «precisa de un valor dentro de un rango»** y muestra
30
+ > tres ejemplos: valor único, rango (dos manijas) y con tooltip. No dice cuándo va cada uno.
31
+ >
32
+ > Uso medido el 03-10-2026 en `master-pedidos/src/app` (misma exclusión que `sui-badge`):
33
+ > **1 uso**, `components/driver-coverage-modal/driver-coverage-modal.html:83` (radio de
34
+ > cobertura, 1–15 km, con `label`, `amount` y `tooltip`). Más **1 `<input type="range">` nativo**.
35
+
36
+ ### `type`
37
+
38
+ | type | cuándo | fuente |
39
+ |---|---|---|
40
+ | `single` (default) | Un valor: radio de cobertura, porcentaje. | según la guía del DS |
41
+ | `range` | Un mínimo y un máximo en la misma escala: rango de precios de un filtro. Una sola barra con dos manijas, nunca dos sliders. | según la guía del DS (existe); el «cuándo» es propuesta |
42
+
43
+ ### Las piezas
44
+
45
+ | prop | cuándo | fuente |
46
+ |---|---|---|
47
+ | `label` | Siempre que el slider no esté dentro de un campo que ya lo titula. Sin `label` desaparece toda la fila de arriba, `amount` incluido. | propuesta, falta aprobación |
48
+ | `amount` | El valor con su unidad, a la derecha del label: «12 km», «S/ 10 – S/ 40». Sin `amount` escribe el número pelado. | propuesta, falta aprobación |
49
+ | `sublabel` | Una precisión corta junto al label: «(máximo 15)». | propuesta, falta aprobación |
50
+ | `tooltip` | Solo **sin** `amount`: el número flota sobre la manija mientras se arrastra. Con `amount` repite el valor y sin unidad. | según la guía del DS (existe); el «cuándo» es propuesta |
51
+ | `tooltipAlways` | No usar (ver abajo). | propuesta, falta aprobación |
52
+ | `min` / `max` / `step` | La escala real del negocio (1–15 km, paso 1). | API |
53
+ | `disabled` | La escala existe pero hoy no se puede mover (falta un dato antes). | propuesta, falta aprobación |
54
+ | `size` | `medium` (default, Figma): manija 16, riel 6, label 12/16. `large`: manija 24, riel 8, punto interno 8, label 14/20 — para mobile. | `medium`: según la guía del DS · `large`: canon (aprobado por el dueño, 03-10-2026), extensión SUI |
55
+
56
+ **Usos de hoy que rompen la propuesta:**
57
+
58
+ - **1 con `amount` y `tooltip` a la vez:** `components/driver-coverage-modal/driver-coverage-modal.html:83`
59
+ muestra «12 km» arriba y «12» flotando al arrastrar.
60
+ - **1 nativo → `sui-slider`:** `components/image-cropper/image-cropper.html:57` (zoom del recorte).
61
+ A discutir: es un control de herramienta sin label, con botones − y + al lado.
62
+
63
+ ## Tamaño en mobile y en web
64
+
65
+ En mobile se toca con el dedo: el área táctil tiene que medir **44 px o más**. En web, con mouse,
66
+ va un tamaño más chico. La app ya lo resuelve con `BreakpointsService`
67
+ (`src/app/services/breakpoints.service.ts`, mobile = hasta 767 px). Regla del dueño, 03-10-2026.
68
+
69
+ | uso | mobile | web | fuente |
70
+ |---|---|---|---|
71
+ | slider con label (radio de cobertura, filtro de precio) | `large` (manija 24, riel 8) | `medium` (manija 16, riel 6) | canon (aprobado por el dueño, 03-10-2026) |
72
+
73
+ ```html
74
+ <sui-slider [size]="bp.isMobile() ? 'large' : 'medium'" label="Radio de cobertura" amount="12 km"
75
+ [min]="1" [max]="15" [(value)]="radioKm" />
76
+ ```
77
+
78
+ Toda la barra responde al toque (24 de alto en `large`), así que no hace falta acertarle a la
79
+ manija. Igual la barra mide menos de 44: el slider necesita aire arriba y abajo en la fila. La
80
+ manija en 0 o en 100 sobresale media manija (12 px en `large`) del borde de la barra: el
81
+ contenedor tiene que tener margen lateral.
82
+
83
+ ## «No usar»: por qué no hoy y cuándo sí
84
+
85
+ | qué | por qué no hoy | cuándo sí, en un flujo nuevo |
86
+ |---|---|---|
87
+ | `type="range"` (0 usos) | ningún filtro de la app pide mínimo y máximo | un filtro de rango de precios o de distancia en la búsqueda del cliente |
88
+ | `tooltipAlways` | un número fijo encima de la manija tapa el contenido de arriba y repite el `amount` | un slider sin `label` dentro de una herramienta (zoom del recorte), donde el número es lo único que orienta |
89
+
90
+ ## API
91
+
92
+ <!-- api:inicio -->
93
+ <!-- generada: no editar a mano — `npm run docs` -->
94
+
95
+ | input | tipo | default |
96
+ |---|---|---|
97
+ | `type` | `'single' \| 'range'` | `'single'` |
98
+ | `size` | `'medium' \| 'large'` | `'medium'` |
99
+ | `min` | `number` | `0` |
100
+ | `max` | `number` | `100` |
101
+ | `step` | `number` | `1` |
102
+ | `label` | `string` | `''` |
103
+ | `sublabel` | `string` | `''` |
104
+ | `amount` | `string \| null` | `null` |
105
+ | `tooltip` | `boolean` | `false` |
106
+ | `tooltipAlways` | `boolean` | `false` |
107
+ | `disabled` | `boolean` | `false` |
108
+ | `value` | `number` | `0` |
109
+ | `minValue` | `number` | `0` |
110
+ | `maxValue` | `number` | `0` |
111
+
112
+ | output | payload |
113
+ |---|---|
114
+ | `valueChange` | `number` |
115
+ | `minValueChange` | `number` |
116
+ | `maxValueChange` | `number` |
117
+ <!-- api:fin -->
118
+
119
+ Implementa `ControlValueAccessor` **solo para `single`**.
120
+
121
+ ## Trampas
122
+
123
+ - **`label=""` borra toda la fila**, `amount` incluido. No hay forma de tener solo el valor.
124
+ - **El tooltip va 28 px arriba de la manija**: deja aire arriba o queda cortado.
125
+ - En `range`, `amount` vacío escribe «min–max» con guion largo; el formulario no lo cubre: usa
126
+ `[(minValue)]` y `[(maxValue)]`.
127
+ - Con `formControlName` ignora `[value]`. No mezcles las dos formas.
128
+ - `value` es input + output (`[(value)]` funciona), no un model.
129
+ - El punto de la manija queda **gris** cuando el valor está en el mínimo: es a propósito.
130
+ - Mide el ancho con el DOM: si arranca oculto (`display: none`, una pestaña cerrada) el arrastre
131
+ calcula mal hasta que se muestra.
132
+ - Las flechas, Inicio y Fin lo mueven con teclado.
133
+
134
+ ## Ejemplo
135
+
136
+ ```html
137
+ <sui-slider
138
+ label="Radio de cobertura"
139
+ [amount]="radioKm() + ' km'"
140
+ [min]="1" [max]="15" [step]="1"
141
+ [value]="radioKm()"
142
+ (valueChange)="radioKm.set($event)" />
143
+ ```