@ajuarezso/sui-design-system 0.21.0 → 0.22.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 (42) hide show
  1. package/docs/INDEX.md +25 -30
  2. package/docs/sui-accordion/README.md +171 -0
  3. package/docs/sui-activity-feed/README.md +155 -0
  4. package/docs/sui-activity-filter/README.md +100 -0
  5. package/docs/sui-button/README.md +19 -0
  6. package/docs/sui-command-menu-footer/README.md +100 -0
  7. package/docs/sui-command-menu-item/README.md +140 -0
  8. package/docs/sui-command-menu-search/README.md +105 -0
  9. package/docs/sui-compact-select/README.md +4 -1
  10. package/docs/sui-compact-select-for-input/README.md +3 -0
  11. package/docs/sui-counter-input/README.md +3 -0
  12. package/docs/sui-datepicker/README.md +4 -0
  13. package/docs/sui-dropdown-caption/README.md +92 -0
  14. package/docs/sui-dropdown-item/README.md +163 -0
  15. package/docs/sui-dropdown-menu/README.md +196 -0
  16. package/docs/sui-dropdown-search/README.md +113 -0
  17. package/docs/sui-filter/README.md +181 -0
  18. package/docs/sui-href/README.md +85 -0
  19. package/docs/sui-img-retry/README.md +107 -0
  20. package/docs/sui-input/README.md +3 -0
  21. package/docs/sui-modal/README.md +238 -0
  22. package/docs/sui-modal-footer/README.md +178 -0
  23. package/docs/sui-modal-header/README.md +115 -0
  24. package/docs/sui-modal-sheet/README.md +170 -0
  25. package/docs/sui-page-header/README.md +123 -0
  26. package/docs/sui-segmented-control/README.md +3 -0
  27. package/docs/sui-select/README.md +5 -2
  28. package/docs/sui-sidebar/README.md +217 -0
  29. package/docs/sui-switch-label/README.md +3 -0
  30. package/docs/sui-tab-chips/README.md +5 -2
  31. package/docs/sui-table/README.md +264 -0
  32. package/docs/sui-tag-input/README.md +3 -0
  33. package/docs/sui-textarea/README.md +3 -0
  34. package/docs/sui-time-picker/README.md +3 -0
  35. package/docs/sui-topbar/README.md +153 -0
  36. package/docs/sui-topbar-button/README.md +95 -0
  37. package/docs/sui-topbar-item/README.md +97 -0
  38. package/docs/sui-topbar-user/README.md +83 -0
  39. package/fesm2022/ajuarezso-sui-design-system.mjs +32 -29
  40. package/fesm2022/ajuarezso-sui-design-system.mjs.map +1 -1
  41. package/package.json +1 -1
  42. package/types/ajuarezso-sui-design-system.d.ts +18 -16
@@ -0,0 +1,170 @@
1
+ ---
2
+ name: sui-modal-sheet
3
+ selector: sui-modal-sheet
4
+ resumen: Hoja que sube desde abajo, se arrastra entre alturas fijas y puede dejar usable lo de atrás (un mapa)
5
+ alternativas: [sui-modal (pide terminar algo antes de seguir, sin fondo usable), sui-popover (contenido chico anclado a un botón), sui-dropdown-menu (lista de acciones)]
6
+ ---
7
+
8
+ # sui-modal-sheet
9
+
10
+ Story: [Organisms/Modal/Modal Sheet](https://sui-design-system.web.app/?path=/docs/organisms-modal-modal-sheet--docs)
11
+
12
+ ## Cuándo usarlo / cuándo no
13
+
14
+ **Úsalo** cuando el contenido **convive con lo de atrás** o se mira a distintas alturas:
15
+
16
+ - El panel del seguimiento sobre un mapa (pedido, envío, viaje): a 25 % se ve el mapa, a 100 %
17
+ el detalle. El mapa sigue respondiendo.
18
+ - Una lista o un detalle que se abre desde abajo y se cierra deslizando (reseñas, movimientos
19
+ de la billetera, elegir la hora de entrega).
20
+
21
+ **No lo uses** para:
22
+
23
+ - Un formulario o una confirmación que hay que terminar: `sui-modal`. El sheet se cierra con un
24
+ deslizón y se pierde lo escrito.
25
+ - Desktop como superficie principal: en una pantalla de 1440 px un sheet a todo el ancho se lee
26
+ como un error (ver tamaño). Si en desktop hay sitio, un panel lateral sirve mejor.
27
+
28
+ ## Qué variante usar
29
+
30
+ > **No está en la guía del DS: es extensión de SUI**, inspirada en el `ion-modal` en modo sheet
31
+ > de Ionic y en las hojas de Apple Maps. No hay Figma que la legisle. Lo marcado **canon** sale
32
+ > de `master-pedidos/CLAUDE.md`; lo demás es **propuesta** sacada del uso real.
33
+ >
34
+ > Uso medido el 03-10-2026 en `master-pedidos/src/app`: 13 sheets, sin contar prototipos ni
35
+ > vitrinas (`master-preview`, `master-prototipos`, `mockup-sheets-*`, `dx/*`, `master-rutina`,
36
+ > `master-medico`, `dev-index`) ni las copias de `components/sui/`.
37
+
38
+ ### Lo que sí dice la guía del DS
39
+
40
+ Nada: el sheet no está en la guía. Lo único que hereda del DS es el criterio del body (20 px) y
41
+ el radio Android (`--sui-radius-20`).
42
+
43
+ ### Los dos patrones de la app
44
+
45
+ El sheet no tiene variantes: se configura con tres números. En la app salen **dos recetas**, y
46
+ conviene copiar una de las dos en vez de inventar otra.
47
+
48
+ | patrón | `breakpoints` | `initialBreakpoint` | `backdropBreakpoint` | cuándo | ejemplo real de la app | fuente |
49
+ |---|---|---|---|---|---|---|
50
+ | **Sobre un mapa** | `[0.25, 0.5, 0.7, 1]` (sin `0`) | `0.5` | `1` | el seguimiento: el mapa se usa a cualquier altura y la hoja no se puede cerrar | `pages/customer/order-tracking/order-tracking.html:130` · `pages/driver/driver-order-detail/driver-order-detail.html:60` | propuesta |
51
+ | **Hoja de lectura** | `[0, 0.92]` | `0.92` | `0` | una lista o detalle que se abre y se cierra deslizando, con el fondo apagado | `pages/customer/order-tracking/order-tracking.html:809` (reseñas) · `pages/customer/checkout/checkout.html:448` (hora) | propuesta |
52
+
53
+ Hoy: 4 «sobre un mapa» (más 2 variantes con alturas propias: `pages/envios/envios.html:578` y
54
+ `pages/customer/viaje/viaje.html:24`), 5 hojas de lectura en `0.92`–`0.96`, 2 en `[0, 1]` y 1 en
55
+ `[0, 0.6, 0.95]`.
56
+
57
+ | input | regla | fuente |
58
+ |---|---|---|
59
+ | `breakpoints` | Con `0` en la lista, la hoja **se cierra deslizando**. Sin `0`, no se cierra con el dedo (solo por código o backdrop). | componente |
60
+ | `initialBreakpoint` | Tiene que estar en `breakpoints`. | componente |
61
+ | `backdropBreakpoint` | `0` = fondo apagado desde que abre. `1` = **nunca hay backdrop**: el mapa de atrás se toca siempre. | componente |
62
+ | tope en las hojas de lectura | `0.92`, no `1`: deja ver arriba un borde de la pantalla de atrás, que es lo que dice «esto es una hoja». | propuesta |
63
+ | `showHandle` | Déjalo en `true` siempre que la hoja se arrastre. Hoy nadie lo apaga. | propuesta |
64
+ | `fullBody` | Solo para contenido a sangre (una foto, un mapa dentro de la hoja). | canon (CLAUDE.md, 2026-07-21) |
65
+
66
+ **Usos de hoy a revisar:**
67
+
68
+ - `pages/customer/viaje/viaje.html:24` reabre la hoja en `(closed)` (`hojaAbierta.set(true)`)
69
+ para que no se pueda cerrar. Sin `0` en `breakpoints` y con `[closeOnBackdrop]="false"` ya no se
70
+ cierra; el reabrir sobra o tapa otro caso.
71
+ - 2 footers de modal metidos en hojas: van sin `slot` (no hay slot de footer). Está bien, pero el
72
+ botón scrollea con el contenido: `pages/customer/checkout/checkout.html:490`,
73
+ `pages/super-admin/master-sonidos-push/master-sonidos-push.html:146`.
74
+
75
+ ## Tamaño en mobile y en web
76
+
77
+ El sheet **no tiene `size`**. Mide lo que mide su contenido, con tope en el breakpoint más alto
78
+ (y nunca más que la pantalla menos el safe-area de arriba y 16 px).
79
+
80
+ | | mobile | web desktop | fuente |
81
+ |---|---|---|---|
82
+ | ancho | toda la pantalla menos 8 px a cada lado | **igual: todo el ancho menos 8 px** | componente |
83
+ | esquinas | iOS 36 arriba / 54 abajo · Android 20 | igual | canon (CLAUDE.md, 2026-07-22) |
84
+ | padding del body | `0 20px 20px` | igual | canon (CLAUDE.md, 2026-07-21) |
85
+ | ancho en desktop | — | acotarlo a una columna centrada de 560 px | propuesta (hoy lo hace cada pantalla a mano) |
86
+
87
+ **El sheet no se acota en desktop.** La app lo arregla con CSS propio, cinco reglas en cuatro archivos:
88
+ `components/store-ratings-modal/store-ratings-modal.scss:81`,
89
+ `pages/driver/movimientos/movimientos-sheet.scss:24`,
90
+ `pages/driver/driver-order-detail/driver-order-detail.scss:1028` y
91
+ `pages/customer/order-tracking/order-tracking.scss:1175` y `:1238`, todos a 560 px. Propuesta:
92
+ que el DS traiga ese tope (`max-width` desde 768 px, centrado) y se borren esos parches.
93
+
94
+ ## API
95
+
96
+ <!-- api:inicio -->
97
+ <!-- generada: no editar a mano — `npm run docs` -->
98
+
99
+ | input | tipo | default |
100
+ |---|---|---|
101
+ | `open` · model, `[(open)]` | `boolean` | `false` |
102
+ | `breakpoints` | `number[]` | `[0, 1]` |
103
+ | `initialBreakpoint` | `number` | `1` |
104
+ | `backdropBreakpoint` | `number` | `0` |
105
+ | `showHandle` | `boolean` | `true` |
106
+ | `closeOnBackdrop` | `boolean` | `true` |
107
+ | `closeOnEscape` | `boolean` | `true` |
108
+ | `fullBody` | `boolean` | `false` |
109
+
110
+ | output | payload |
111
+ |---|---|
112
+ | `closed` | `void` |
113
+ | `breakpointChange` | `number` |
114
+ <!-- api:fin -->
115
+
116
+ Métodos públicos: `setCurrentBreakpoint(bp, animated = true)` (avisa por consola y no hace nada
117
+ si `bp` no está en `breakpoints`) y `close()`.
118
+
119
+ Signals públicas: `currentBreakpoint` y `progress` (cambian al soltar, no durante el arrastre) y
120
+ `dragging`.
121
+
122
+ Slot: un solo `<ng-content>`, el body. No hay header ni footer.
123
+
124
+ ## Trampas
125
+
126
+ - **El padding del body es fuente única** (`0 20px 20px`). No le pongas padding lateral ni abajo
127
+ a tus secciones ni lo pises con `::ng-deep .sui-sheet__body`: el margen queda doble (pasó en el
128
+ tracking del repartidor: ~36 px a los lados y ~68 px bajo el último botón).
129
+ - **Anidar radios**: el body ya cumple `exterior = interior + padding` (36 − 20 = 16, el radio de
130
+ `sui-card`). Una card adentro no necesita otro radio.
131
+ - **`breakpointChange` y `progress` no cambian durante el arrastre**, solo al soltar. Si algo
132
+ tiene que seguir al dedo (el padding del mapa), usa `breakpointChange` y acepta el salto, o lee
133
+ la posición del panel.
134
+ - **Arrastre y scroll**: en la altura máxima el body scrollea; desde arriba del todo, jalar hacia
135
+ abajo mueve la hoja. En alturas intermedias el body entero arrastra (`touch-action: none`): un
136
+ carrusel o un slider dentro puede pelear con el gesto. Los campos (`input`, `textarea`,
137
+ `select`) no arrancan arrastre; para otro control que necesita el dedo, ponle el atributo
138
+ `data-sui-no-sheet-drag` (o a un ancestro).
139
+ - **Sin `0` en `breakpoints` no se cierra deslizando.** Si quieres que se cierre y no lo hace, es
140
+ eso.
141
+ - **`closeOnBackdrop` solo actúa cuando hay backdrop** (altura actual por encima de
142
+ `backdropBreakpoint`). Con `backdropBreakpoint` en `1` nunca hay.
143
+ - **El transform es tuyo, el movimiento no**: el sheet se mueve con la propiedad `translate` del
144
+ panel interno, así que puedes escribir `left` / `right` / `transform` sobre `.sui-sheet` (por
145
+ ejemplo para centrarlo en desktop) sin romper el arrastre.
146
+ - Pide ocultar la barra inferior de la app por `SUI_NAVBAR_VISIBILITY`, igual que `sui-modal`.
147
+
148
+ ## Ejemplo
149
+
150
+ ```html
151
+ <!-- Sobre un mapa: no se cierra, el mapa se usa siempre -->
152
+ <sui-modal-sheet
153
+ [(open)]="hojaAbierta"
154
+ [breakpoints]="[0.25, 0.5, 0.7, 1]"
155
+ [initialBreakpoint]="0.5"
156
+ [backdropBreakpoint]="1"
157
+ (breakpointChange)="mapa.setSheetRatio($event)">
158
+ <h2>Tu pedido va en camino</h2>
159
+ …
160
+ </sui-modal-sheet>
161
+
162
+ <!-- Hoja de lectura: se cierra deslizando -->
163
+ <sui-modal-sheet
164
+ [(open)]="resenasAbiertas"
165
+ [breakpoints]="[0, 0.92]"
166
+ [initialBreakpoint]="0.92"
167
+ [backdropBreakpoint]="0">
168
+ <app-ratings-detail … />
169
+ </sui-modal-sheet>
170
+ ```
@@ -0,0 +1,123 @@
1
+ ---
2
+ name: sui-page-header
3
+ selector: sui-page-header
4
+ resumen: Título y subtítulo de una pantalla, con las acciones de la pantalla a la derecha en desktop y debajo en mobile
5
+ alternativas: [sui-modal-header (cabecera de un modal), sui-drawer-header (cabecera de un panel lateral), sui-topbar (barra superior de la app, no de la pantalla)]
6
+ ---
7
+
8
+ # sui-page-header
9
+
10
+ Story: [Organisms/Page Header](https://sui-design-system.web.app/?path=/docs/organisms-page-header--docs)
11
+
12
+ ## Cuándo usarlo / cuándo no
13
+
14
+ **Úsalo** una vez por pantalla, **arriba del todo**, antes de cualquier contenido: dice dónde
15
+ está la persona («Pedidos», «Descuentos») y, si hace falta, junta las acciones que valen para
16
+ toda la pantalla («Agregar», «Exportar»).
17
+
18
+ **No lo uses** para:
19
+
20
+ - Títulos de sección dentro de la pantalla: un `<h2>` con el token de tipografía, o el título de
21
+ la `sui-card`.
22
+ - Modales y paneles: `sui-modal-header` / `sui-drawer-header`.
23
+ - Meter contenido debajo del título: **solo** acepta `slot="actions"`; todo lo demás se descarta
24
+ sin avisar.
25
+ - Re-implementarlo con un `<h1>` y CSS propio (canon, CANON-SUI §0: «no re-implementar»).
26
+
27
+ ## Qué variante usar
28
+
29
+ > **No está en la guía del DS: es extensión de SUI.** Nació como el título de las pantallas del
30
+ > panel de empresa y no tiene Figma. Lo marcado **canon** sale de `master-pedidos/docs/CANON-SUI.md`;
31
+ > lo demás es **propuesta**.
32
+ >
33
+ > Uso medido el 03-10-2026 en `master-pedidos/src/app`: 64 page headers, sin contar prototipos ni
34
+ > vitrinas (`master-preview`, `master-prototipos`, `mockup-sheets-*`, `dx/*`, `master-rutina`,
35
+ > `master-medico`, `dev-index`) ni las copias de `components/sui/`. 50 llevan `subtitle` fijo, 14
36
+ > calculado; 17 tienen acciones; 0 usan `sticky`.
37
+
38
+ ### Lo que sí dice la guía del DS
39
+
40
+ Nada: el page header no está en la guía. Su tipografía (24/600, 28 en desktop) **no existe en la
41
+ escala del DS** (CANON-SUI §7): si se homologa, sería `title-h5`.
42
+
43
+ ### Contenido
44
+
45
+ | pieza | regla | ejemplo real de la app | fuente |
46
+ |---|---|---|---|
47
+ | `title` | El nombre de la pantalla, el mismo que en el menú. Sentence case, sin punto. Nunca un ID. | `pages/discounts/discounts.html:2` | canon (CANON-SUI, copy) |
48
+ | `subtitle` | Una línea que dice **qué se hace aquí**, no que repite el título. | `pages/super-admin/master-orders/master-orders.html:1` | propuesta |
49
+ | acciones | Las que valen para toda la pantalla: una `filled` como máximo (la principal) y el resto `stroke`. Hoy: 9 `filled`, 7 `stroke`. | `pages/drivers/drivers.html:2` | canon para «una principal» (CANON-SUI §6.2), propuesta el resto |
50
+
51
+ **Usos de hoy que rompen el copy canon** (sentence case en botones): `pages/drivers/drivers.html:8`
52
+ («Nuevo Repartidor»).
53
+
54
+ ### `sticky`
55
+
56
+ | valor | cuándo | fuente |
57
+ |---|---|---|
58
+ | `false` (default) | el caso normal: el título se va con el scroll | propuesta |
59
+ | `true` | listas largas donde las acciones del header se usan mientras se recorre (filtros, «Agregar»). Le pone fondo blanco. 0 usos hoy | propuesta |
60
+
61
+ ## Tamaño en mobile y en web
62
+
63
+ El page header **no tiene `size`**. Cambia solo en 767 px:
64
+
65
+ | | mobile (< 767 px) | web (≥ 767 px) | fuente |
66
+ |---|---|---|---|
67
+ | título | 24 px | 28 px | componente |
68
+ | disposición | título y subtítulo arriba, acciones **debajo** | título a la izquierda, acciones a la derecha, alineadas abajo | componente |
69
+ | padding vertical | 24 arriba y abajo | 32 arriba, 16 abajo | componente |
70
+ | padding lateral | ninguno: lo pone el contenedor del layout | igual | componente |
71
+ | botones de las acciones | `large` (48) | `small` (36) | propuesta |
72
+
73
+ Los botones de las acciones hoy: 10 `small` fijos, 2 `bp.isMobile() ? 'large' : 'small'`, 2
74
+ `bp.controlSize()` (`large`/`xlarge`), 2 `large`, 1 `medium`. La propuesta es la pareja que ya
75
+ usan dos pantallas: en mobile las acciones bajan a su fila y tienen sitio para 48 px; en desktop
76
+ van al lado del título de 28 y `small` no le compite.
77
+
78
+ ```html
79
+ <sui-button [size]="bp.isMobile() ? 'large' : 'small'" slot="actions">Agregar</sui-button>
80
+ ```
81
+
82
+ ## API
83
+
84
+ <!-- api:inicio -->
85
+ <!-- generada: no editar a mano — `npm run docs` -->
86
+
87
+ | input | tipo | default |
88
+ |---|---|---|
89
+ | `title` | `string` | — (requerido) |
90
+ | `subtitle` | `string \| null` | `null` |
91
+ | `sticky` | `boolean` | `false` |
92
+
93
+ Outputs: ninguno.
94
+ <!-- api:fin -->
95
+
96
+ Slot: `slot="actions"`, a la derecha (desktop) o debajo (mobile), gap 8. No hay slot default.
97
+
98
+ ## Trampas
99
+
100
+ - **Lo que proyectas sin `slot="actions"` desaparece.** El JSDoc viejo hablaba de un slot
101
+ default; no existe.
102
+ - **No respeta el safe-area de arriba**: el padding es fijo. El layout de la pantalla se encarga
103
+ del notch; si lo pones en una pantalla sin layout, el título queda bajo la barra de estado.
104
+ - **Sin padding lateral**: depende de que el contenedor de la página lo tenga
105
+ (`.layout__body`, `.ml__content`). Fuera de esos layouts queda pegado al borde.
106
+ - `sticky` se pega a `top: 0` del **contenedor que scrollea**, no de la ventana. En los layouts de
107
+ la app el scroll vive en `.layout__body` / `.ml__content`, así que funciona; en un contenedor
108
+ sin overflow no hace nada.
109
+ - El breakpoint es `min-width: 767px`: a 767 px exactos ya está en modo desktop, un píxel antes
110
+ que el `max-width: 767px` de los mixins de la app.
111
+ - `title` es requerido: sin él, Angular falla al compilar la plantilla.
112
+ - El host borra el atributo `title` nativo (sin tooltip gris del navegador).
113
+
114
+ ## Ejemplo
115
+
116
+ ```html
117
+ <sui-page-header title="Descuentos" subtitle="Promociones y campañas activas de tu negocio.">
118
+ <sui-button slot="actions" variant="filled"
119
+ [size]="bp.isMobile() ? 'large' : 'small'" (click)="crear()">
120
+ Crear descuento
121
+ </sui-button>
122
+ </sui-page-header>
123
+ ```
@@ -72,6 +72,9 @@ Regla corta: **2-4 modos de la misma vista, efecto inmediato → segmented · se
72
72
  Alto de cada segmento: `small` 28 px, `medium` 28 px, `large` 32 px, `xlarge` 40 px (el control
73
73
  suma 8 px de borde interno).
74
74
 
75
+ En large y xlarge el texto sube a 16 px (regla del 03-10-2026): Label/Medium 16/24. En `large` el
76
+ padding del segmento con texto bajó de 6 a 4 para que siga midiendo 32.
77
+
75
78
  | uso | mobile | web |
76
79
  |---|---|---|
77
80
  | modos con texto (local del horario, rol) | `xlarge` (segmento de 40 px) | `medium` |
@@ -94,8 +94,11 @@ Regla corta: **sale de datos o son más de 6 → select · 2–6 fijas → radio
94
94
  El campo sigue la escala del input y del botón (`large` 48 y `xlarge` 52 son extensión de SUI;
95
95
  la guía tiene `medium` 40, `small` 36 y `xsmall` 32). **La lista acompaña al campo**: las opciones
96
96
  miden 36 hasta `medium` (las de la guía), 44 en `large` y 48 en `xlarge`, y **en mobile (≤ 767 px)
97
- nunca bajan de 44**, sea cual sea el `size`. El texto de la opción queda en 14/20, el mismo del
98
- valor del campo.
97
+ nunca bajan de 44**, sea cual sea el `size`. El texto de la opción es el mismo del valor del
98
+ campo: 14/20 hasta `medium` y 16/24 en `large` y `xlarge`.
99
+
100
+ En large y xlarge el texto sube a 16 px (regla del 03-10-2026). Vale para el valor, el placeholder
101
+ y las opciones de la lista; el alto no cambia (las opciones bajan 2 de padding para seguir en 44/48).
99
102
 
100
103
  | size del campo | opción en web | opción en mobile | fuente |
101
104
  |---|---|---|---|
@@ -0,0 +1,217 @@
1
+ ---
2
+ name: sui-sidebar
3
+ selector: sui-sidebar
4
+ resumen: Navegación principal de un rol — columna fija a la izquierda en web y cajón que entra desde la izquierda en mobile
5
+ alternativas: [la barra inferior nativa del layout (las 4-5 secciones más usadas en el teléfono), sui-topbar (navegación arriba, sin columna lateral), sui-tab-menu-vertical (secciones dentro de UNA pantalla, como ajustes), sui-dropdown-menu (acciones de una cuenta o un botón)]
6
+ ---
7
+
8
+ # sui-sidebar
9
+
10
+ Story: [Organisms/Sidebar](https://sui-design-system.web.app/?path=/docs/organisms-sidebar--docs)
11
+
12
+ ## Cuándo usarlo / cuándo no
13
+
14
+ **Úsalo** como **la** navegación de un layout de rol: una por layout, montada en el layout y no
15
+ en las páginas. En la app hay 4 y cubren los roles: empresa (`components/sidebar/sidebar.html:43`,
16
+ dentro del shell), repartidor (`layouts/driver/driver-layout.html:4`), super admin y central
17
+ (`layouts/master/master-layout.html:5`) y cliente (`layouts/customer/customer-layout.html:5`).
18
+
19
+ **No lo uses** para:
20
+
21
+ - Navegar entre secciones **de una sola pantalla** (las pestañas de Ajustes, los pasos de un
22
+ formulario): `sui-tab-menu-vertical` o `sui-tab-chips`.
23
+ - Un menú de acciones (editar, eliminar, cerrar sesión de un item): `sui-dropdown-menu`.
24
+ - Reemplazar la barra inferior en el teléfono. En mobile, las secciones de todos los días van en la
25
+ barra inferior (nativa en iOS) y el sidebar es el cajón de «Más».
26
+
27
+ Regla corta: **navegación del rol → sidebar · secciones de una pantalla → tab menu · acciones → dropdown.**
28
+
29
+ ## Qué variante usar
30
+
31
+ > **La guía del DS no tiene página del sidebar.** El componente sale del Figma de SUI (Sidebar
32
+ > [Navigation], node 3802:11759) y el cajón de mobile no existe en ese Figma: se portó del layout
33
+ > del super admin. Lo de abajo es **práctica de la app** (los 4 usos están homologados desde el
34
+ > 25-07-2026) o **propuesta, falta aprobación**; lo marcado «canon» sale de `docs/CANON-SUI.md`
35
+ > o de `master-pedidos/CLAUDE.md`.
36
+ >
37
+ > Uso medido el 03-10-2026 en `master-pedidos/src/app`: **4 sidebars**, sin contar vitrinas
38
+ > (`master-preview` tiene el quinto).
39
+
40
+ ### La composición (igual en los 4 layouts)
41
+
42
+ | pieza | cómo | fuente |
43
+ |---|---|---|
44
+ | `brand` | `{ name: 'Master Pedidos', subtitle: '<Rol>', logo: 'assets/app-icon.png', fallback: 'M' }`. En empresa, el nombre del local. | práctica de la app (4 de 4) |
45
+ | `showHeaderButton` + `headerButtonActive` + panel `[header-panel]` | El botón del header abre el **cambio de rol** (o de negocio/sucursal en empresa). El panel se proyecta con `<div header-panel>` y adentro van `sui-dropdown-item mode="option" size="large"`. El cliente lo muestra solo en mobile. | práctica de la app (4 de 4) |
46
+ | tres secciones con rótulo | `items` + `itemsLabel` (lo de todos los días), `secondaryItems` + `secondaryItemsLabel` (dinero o crecimiento), `supportingItems` + `supportingItemsLabel` (cuenta, administración: va anclado abajo). Rótulos en sentence case: «Operación», «Dinero», «Cuenta». | práctica de la app (4 de 4) |
47
+ | `activeRoute` | Siempre `[activeRoute]="currentUrl()"`, aunque los items tengan `route`: así el activo se resuelve por prefijo (`/cliente/pedidos/123` deja marcado «Pedidos»). | práctica de la app (4 de 4) |
48
+ | `profile` | Nombre + `avatarUrl` (nunca `photoURL`) + correo. | canon (CLAUDE.md, «Fotos de otra persona»: el campo correcto es `avatarUrl`) |
49
+ | cerrar sesión | Hoy **ningún** item usa `action: 'logout'`: el cierre real vive en el selector de rol y el «salir» del panel solo cambia de rol (`components/sidebar/sidebar.ts:367`). Los 4 layouts dejan `(logoutClicked)` conectado por si se agrega. | práctica de la app |
50
+ | `[(mobileOpen)]` + `closeOnItemClick` | Siempre los dos. Más `cerrarAlNavegar(...)` en el constructor del layout (ver Trampas). | canon (CANON-SUI §1.1) |
51
+ | `search` | `{ placeholder: 'Buscar...', shortcut: '⌘K' }` y el layout **filtra** los items con `searchValue` (el componente no filtra). | práctica de la app (4 de 4) |
52
+ | `indicatorColor` | Default `brand` (naranja). No se cambia. | canon del DS (accent = marca) |
53
+ | `collapsed` | Solo el cliente lo usa, con un item «colapsar», y **solo en web**: `[collapsed]="colapsado() && !bp.isMobile()"`. | práctica de la app (1 uso) |
54
+
55
+ ### El item (`SuiSidebarItem`)
56
+
57
+ | prop | cuándo | fuente |
58
+ |---|---|---|
59
+ | `icon` | Siempre, RemixIcon `-line`. Sin ícono, el modo colapsado queda vacío. | propuesta, falta aprobación |
60
+ | `route` | Navegación normal (usa `routerLink`). | práctica de la app |
61
+ | sin `route` | Acción, no navegación (abrir Configuración, colapsar): `action` o solo `value`, y se escucha `(itemClicked)`. | práctica de la app |
62
+ | `badge` | Un contador que pide acción: notificaciones sin leer, con tope «99+» (`components/sidebar/sidebar.ts:129`). Nunca un «NEW» decorativo. | práctica de la app (el «nunca NEW» es propuesta) |
63
+ | `variant: 'danger'` | Solo «Cerrar sesión» o salidas destructivas. | propuesta, falta aprobación |
64
+ | `shortcut`, `colorDot` | No usar (ver «No usar»). | propuesta, falta aprobación |
65
+
66
+ ## Tamaño en mobile y en web
67
+
68
+ El sidebar **no tiene `size`**. El ancho lo decide el viewport:
69
+
70
+ | estado | ancho | fuente |
71
+ |---|---|---|
72
+ | web (> 767 px) | 272 fijo, alto `100dvh` | Figma |
73
+ | web colapsado (`collapsed`) | 80, solo íconos; los rótulos siguen visibles y cada item tiene tooltip nativo con su label | Figma |
74
+ | mobile `drawer` (default) | `min(86vw, 360px)`, entra desde la izquierda con fondo oscuro | extensión SUI (no está en el Figma) |
75
+ | mobile `sheet` | `100vw - 40px`, se siente casi a pantalla completa | extensión SUI |
76
+
77
+ | uso | mobile | web | fuente |
78
+ |---|---|---|---|
79
+ | navegación del rol | cajón `drawer`, cerrado por defecto | columna fija de 272 | práctica de la app (4 de 4) |
80
+ | colapsar | nunca (`!bp.isMobile()`) | opcional, a pedido de la persona | práctica de la app |
81
+ | `sheet` | no usar (0 usos) | — | propuesta, falta aprobación |
82
+
83
+ **Qué pasa en mobile (≤ 767 px):** el sidebar sale del flujo y queda `position: fixed` fuera de la
84
+ pantalla. Lo abre la acción `toggle-sidebar` de la **barra inferior** («Más»), que el layout
85
+ traduce a `mobileOpen = true` (`layouts/shell/shell.ts:326`, `customer-layout.ts:322`). Se cierra
86
+ con el fondo, con Escape, al tocar un item (`closeOnItemClick`) y —por el helper del proyecto—
87
+ al terminar cualquier navegación. Cerrado queda `inert` y `aria-hidden`: el foco no se mete.
88
+ Respeta las safe areas de arriba, izquierda y abajo.
89
+
90
+ ## «No usar»: por qué no hoy y cuándo sí
91
+
92
+ | qué | por qué no hoy | cuándo sí, en un flujo nuevo |
93
+ |---|---|---|
94
+ | `indicatorColor="information"` | el activo es naranja de marca en toda la app | nunca en esta app (es el azul del Figma original) |
95
+ | `mobileMode="sheet"` | el `drawer` deja ver la página detrás y se entiende que es un cajón | un rol con muchísimos items donde 360 px quedan cortos |
96
+ | `shortcut` en items | no hay atajos de teclado por sección | si se agregan atajos tipo `⌘1` en el panel web |
97
+ | `colorDot` | no hay «favoritos» ni etiquetas de color | proyectos o locales favoritos fijados por la persona |
98
+ | `profileDropdown` | el perfil se abre desde «Cuenta» | si el perfil pasa a tener menú propio (cambiar cuenta, idioma) |
99
+ | feature card (`[feature-card]`) | no hay promo dentro de la navegación | invitar a mejorar el plan en el panel de empresa |
100
+ | `showBackdrop` en `false` | sin fondo, el cajón parece pegado a la página | nunca en mobile |
101
+
102
+ ## API
103
+
104
+ <!-- api:inicio -->
105
+ <!-- generada: no editar a mano — `npm run docs` -->
106
+
107
+ | input | tipo | default |
108
+ |---|---|---|
109
+ | `collapsed` | `boolean` | `false` |
110
+ | `indicatorColor` | `'information' \| 'brand'` | `'brand'` |
111
+ | `brand` | `SuiSidebarBrandConfig \| null` | `null` |
112
+ | `showHeaderButton` | `boolean` | `false` |
113
+ | `headerButtonActive` | `boolean` | `false` |
114
+ | `search` | `SuiSidebarSearchConfig \| boolean \| null` | `false` |
115
+ | `searchValue` · model, `[(searchValue)]` | `string` | `''` |
116
+ | `items` | `SuiSidebarItem[]` | `[]` |
117
+ | `secondaryItems` | `SuiSidebarItem[]` | `[]` |
118
+ | `supportingItems` | `SuiSidebarItem[]` | `[]` |
119
+ | `supportingAnchorBottom` | `boolean` | `true` |
120
+ | `itemsLabel` | `string \| null` | `null` |
121
+ | `secondaryItemsLabel` | `string \| null` | `null` |
122
+ | `supportingItemsLabel` | `string \| null` | `null` |
123
+ | `activeRoute` | `string \| null` | `null` |
124
+ | `profile` | `SuiSidebarProfile \| null` | `null` |
125
+ | `profileDropdown` | `boolean` | `false` |
126
+ | `mobileOpen` · model, `[(mobileOpen)]` | `boolean` | `false` |
127
+ | `closeOnItemClick` | `boolean` | `true` |
128
+ | `showBackdrop` | `boolean` | `true` |
129
+ | `mobileMode` | `'drawer' \| 'sheet'` | `'drawer'` |
130
+
131
+ | output | payload |
132
+ |---|---|
133
+ | `itemClicked` | `SuiSidebarItem` |
134
+ | `logoutClicked` | `void` |
135
+ | `backdropClicked` | `void` |
136
+ | `headerButtonClicked` | `void` |
137
+ | `profileClicked` | `void` |
138
+ | `searchSubmitted` | `string` |
139
+ <!-- api:fin -->
140
+
141
+ ```ts
142
+ interface SuiSidebarItem {
143
+ value: string; // lo emite (itemClicked)
144
+ label: string;
145
+ icon?: string; // RemixIcon sin 'ri-'
146
+ route?: string; // routerLink; sin route solo emite
147
+ external?: boolean;
148
+ variant?: 'default' | 'danger';
149
+ action?: 'logout' | string; // cualquier action anula el route
150
+ badge?: string | number;
151
+ shortcut?: string;
152
+ colorDot?: string; // reemplaza el ícono por un punto de color
153
+ tooltip?: string; // tooltip en colapsado; default = label
154
+ }
155
+ interface SuiSidebarBrandConfig { name: string; subtitle?: string; subtitleAsBadge?: boolean; logo?: string; fallback?: string; }
156
+ interface SuiSidebarSearchConfig { placeholder?: string; shortcut?: string; }
157
+ interface SuiSidebarProfile { name: string; avatarUrl?: string; initials?: string; status?: SuiAvatarStatus; email?: string; verified?: boolean; }
158
+ ```
159
+
160
+ Slots: `[header-panel]` (debajo del header: el switcher), `[feature-card]` (al fondo del contenido).
161
+
162
+ ## Trampas
163
+
164
+ - **El cajón no se cierra solo al navegar desde afuera.** Se cierra con el fondo, Escape y el toque
165
+ en un item; cualquier otra navegación (atrás, un push, `router.navigate`, una ruta cacheada que
166
+ vuelve) lo deja abierto tapando la página siguiente. Mientras el DS no traiga `closeOnNavigate`,
167
+ cada layout llama `cerrarAlNavegar(menuAbierto, switcherAbierto)`
168
+ (`core/services/cerrar-al-navegar.ts`). Canon: CANON-SUI §1.1.
169
+ - **El buscador no filtra nada.** Solo expone `[(searchValue)]` y `(searchSubmitted)`; el layout
170
+ filtra los items (`master-layout.ts:125`). Y su placeholder por defecto es **`'Search...'`, en
171
+ inglés**: pásale siempre `{ placeholder: 'Buscar...' }`. (En `sui-topbar` el default sí es «Buscar...».)
172
+ - **`collapsed` no mira el viewport.** Con `collapsed` en `true`, el cajón de mobile también sale
173
+ de 80 px. Condiciónalo: `[collapsed]="colapsado() && !bp.isMobile()"`.
174
+ - Un item con `route` queda activo por `routerLinkActive` **y** por `activeRoute`; un item sin
175
+ `route` solo por `activeRoute` (por `value` exacto). Si no pasas `activeRoute`, los items-acción
176
+ nunca se marcan.
177
+ - Cualquier `action` (no solo `'logout'`) anula el `route`: el item no navega.
178
+ - El `title` nativo del host se borra (sin tooltip gris); en colapsado el tooltip va en cada item.
179
+ - El cajón tiene `z-index: 300` sobre la barra inferior (200). Mientras está abierto el layout
180
+ esconde la barra (`customer-layout.ts:66`); si armas un layout nuevo, haz lo mismo.
181
+ - El comentario de cabecera del `.ts` todavía dice 240 px; el ancho real es 272 (`sui-sidebar.scss:50`).
182
+ - Los items ya traen el arreglo de iOS del `:active` pegado: no lo agregues.
183
+
184
+ ## Ejemplo
185
+
186
+ ```html
187
+ <sui-sidebar
188
+ [brand]="{ name: 'Master Pedidos', subtitle: 'Repartidor', logo: 'assets/app-icon.png', fallback: 'M' }"
189
+ [showHeaderButton]="true"
190
+ [headerButtonActive]="switcherOpen()"
191
+ (headerButtonClicked)="switcherOpen.set(!switcherOpen())"
192
+ [search]="{ placeholder: 'Buscar...', shortcut: '⌘K' }"
193
+ [(searchValue)]="busqueda"
194
+ itemsLabel="Operación" [items]="items()"
195
+ secondaryItemsLabel="Dinero" [secondaryItems]="itemsDinero()"
196
+ supportingItemsLabel="Cuenta" [supportingItems]="itemsCuenta()"
197
+ [activeRoute]="currentUrl()"
198
+ [profile]="perfil()"
199
+ [(mobileOpen)]="menuAbierto"
200
+ [closeOnItemClick]="true"
201
+ (logoutClicked)="logout()">
202
+ @if (switcherOpen()) {
203
+ <div header-panel role="listbox" aria-label="Cambiar de rol">
204
+ @for (rol of roles; track rol.value) {
205
+ <sui-dropdown-item [label]="rol.label" [sublabel]="rol.sublabel" mode="option" size="large"
206
+ (clicked)="irARol(rol)" />
207
+ }
208
+ </div>
209
+ }
210
+ </sui-sidebar>
211
+ ```
212
+
213
+ ```ts
214
+ constructor() {
215
+ cerrarAlNavegar(this.menuAbierto, this.switcherOpen); // mientras el DS no cierre solo
216
+ }
217
+ ```
@@ -80,6 +80,9 @@ Regla corta: **ajuste con texto → este · ajuste con peso → switch-card · s
80
80
  El bloque entero se toca, así que con descripción la fila pasa los 44 px sola. Sin descripción,
81
81
  dale a la fila un alto mínimo de 44 px.
82
82
 
83
+ En large y xlarge el texto sube a 16 px (regla del 03-10-2026): el label va en 16/24; sublabel y
84
+ descripción se quedan en 12.
85
+
83
86
  ## «No usar»: por qué no hoy y cuándo sí
84
87
 
85
88
  | qué | por qué no hoy | cuándo sí, en un flujo nuevo |
@@ -108,8 +108,11 @@ vista → segmented · secciones → tab menu · filtros o categorías → tab-c
108
108
  > `size` está **en el repo del DS (03-10-2026) y no en la versión publicada 0.19.0**. Hasta que se
109
109
  > publique, todos miden lo de `medium`.
110
110
 
111
- Alto del chip: `small` 32 px · `medium` 36 px (el de siempre, default) · `large` 40 px. Los tres
112
- con texto de 14 px; cambia el alto y el aire lateral. El scroller suma 12 px arriba y abajo.
111
+ Alto del chip: `small` 32 px · `medium` 36 px (el de siempre, default) · `large` 40 px. `small` y
112
+ `medium` con texto de 14 px; cambia el alto y el aire lateral. El scroller suma 12 px arriba y abajo.
113
+
114
+ En large y xlarge el texto sube a 16 px (regla del 03-10-2026): el chip `large` va en 16/24 y sigue
115
+ midiendo 40. El número del contador se queda en 12.
113
116
 
114
117
  | uso | mobile | web | fuente |
115
118
  |---|---|---|---|