@ajuarezso/sui-design-system 0.19.0 → 0.21.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.
- package/docs/INDEX.md +136 -0
- package/docs/sui-alert/README.md +229 -0
- package/docs/sui-app-store-badge/README.md +137 -0
- package/docs/sui-avatar/README.md +263 -0
- package/docs/sui-avatar-group/README.md +115 -0
- package/docs/sui-badge/README.md +229 -0
- package/docs/sui-banner/README.md +190 -0
- package/docs/sui-brand-logo/README.md +143 -0
- package/docs/sui-breadcrumb-item/README.md +96 -0
- package/docs/sui-breadcrumbs/README.md +122 -0
- package/docs/sui-button/README.md +209 -0
- package/docs/sui-button-group/README.md +178 -0
- package/docs/sui-button-group-item/README.md +102 -0
- package/docs/sui-card/README.md +168 -0
- package/docs/sui-chart-legend/README.md +140 -0
- package/docs/sui-chart-legend-dot/README.md +118 -0
- package/docs/sui-checkbox/README.md +147 -0
- package/docs/sui-checkbox-card/README.md +150 -0
- package/docs/sui-checkbox-label/README.md +143 -0
- package/docs/sui-circular-progress-bar/README.md +131 -0
- package/docs/sui-color-dot/README.md +112 -0
- package/docs/sui-color-picker/README.md +124 -0
- package/docs/sui-compact-avatar-group/README.md +103 -0
- package/docs/sui-compact-button/README.md +190 -0
- package/docs/sui-compact-select/README.md +127 -0
- package/docs/sui-compact-select-for-input/README.md +117 -0
- package/docs/sui-content-card/README.md +147 -0
- package/docs/sui-content-divider/README.md +124 -0
- package/docs/sui-content-label/README.md +148 -0
- package/docs/sui-counter-input/README.md +145 -0
- package/docs/sui-date-selector/README.md +98 -0
- package/docs/sui-datepicker/README.md +193 -0
- package/docs/sui-day-cell/README.md +118 -0
- package/docs/sui-day-label/README.md +81 -0
- package/docs/sui-divider/README.md +102 -0
- package/docs/sui-drawer-footer/README.md +127 -0
- package/docs/sui-drawer-header/README.md +140 -0
- package/docs/sui-emoji/README.md +138 -0
- package/docs/sui-empty-state/README.md +145 -0
- package/docs/sui-fancy-button/README.md +148 -0
- package/docs/sui-file-icon/README.md +103 -0
- package/docs/sui-file-upload/README.md +154 -0
- package/docs/sui-file-upload-card/README.md +129 -0
- package/docs/sui-flag/README.md +99 -0
- package/docs/sui-form-hint/README.md +124 -0
- package/docs/sui-form-label/README.md +148 -0
- package/docs/sui-icon/README.md +135 -0
- package/docs/sui-image-upload/README.md +200 -0
- package/docs/sui-image-upload-profile/README.md +145 -0
- package/docs/sui-inline-input/README.md +104 -0
- package/docs/sui-inline-select/README.md +101 -0
- package/docs/sui-input/README.md +244 -0
- package/docs/sui-integration-switch/README.md +142 -0
- package/docs/sui-kbd/README.md +97 -0
- package/docs/sui-key-icon/README.md +125 -0
- package/docs/sui-link-button/README.md +221 -0
- package/docs/sui-logo/README.md +152 -0
- package/docs/sui-notification/README.md +179 -0
- package/docs/sui-notification-footer/README.md +98 -0
- package/docs/sui-notification-tab-menu/README.md +112 -0
- package/docs/sui-otp-input/README.md +116 -0
- package/docs/sui-pagination/README.md +146 -0
- package/docs/sui-password-strength/README.md +99 -0
- package/docs/sui-payment-icon/README.md +98 -0
- package/docs/sui-period-range/README.md +93 -0
- package/docs/sui-popover/README.md +178 -0
- package/docs/sui-product-card/README.md +173 -0
- package/docs/sui-progress-bar/README.md +132 -0
- package/docs/sui-progress-bar-label/README.md +143 -0
- package/docs/sui-radio/README.md +134 -0
- package/docs/sui-radio-card/README.md +160 -0
- package/docs/sui-radio-label/README.md +137 -0
- package/docs/sui-rating/README.md +199 -0
- package/docs/sui-rich-editor/README.md +114 -0
- package/docs/sui-segmented-control/README.md +144 -0
- package/docs/sui-select/README.md +180 -0
- package/docs/sui-slider/README.md +143 -0
- package/docs/sui-social-button/README.md +158 -0
- package/docs/sui-status-badge/README.md +164 -0
- package/docs/sui-stepper/README.md +166 -0
- package/docs/sui-store-card/README.md +147 -0
- package/docs/sui-switch/README.md +146 -0
- package/docs/sui-switch-card/README.md +149 -0
- package/docs/sui-switch-label/README.md +139 -0
- package/docs/sui-tab-chips/README.md +185 -0
- package/docs/sui-tab-menu-horizontal/README.md +142 -0
- package/docs/sui-tab-menu-vertical/README.md +136 -0
- package/docs/sui-tag/README.md +145 -0
- package/docs/sui-tag-input/README.md +120 -0
- package/docs/sui-textarea/README.md +157 -0
- package/docs/sui-time-picker/README.md +237 -0
- package/docs/sui-toast/README.md +190 -0
- package/docs/sui-tooltip/README.md +201 -0
- package/fesm2022/ajuarezso-sui-design-system.mjs +466 -279
- package/fesm2022/ajuarezso-sui-design-system.mjs.map +1 -1
- package/package.json +1 -1
- package/types/ajuarezso-sui-design-system.d.ts +367 -72
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sui-tag
|
|
3
|
+
selector: sui-tag
|
|
4
|
+
resumen: Chip neutro que nombra algo elegido o aplicado y que la persona puede quitar
|
|
5
|
+
alternativas: [sui-badge (etiqueta de solo lectura, con color y significado), sui-status-badge (resultado de un ciclo), sui-tab-chips / sui-filter (chips para elegir), sui-tag-input (campo que junta tags)]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# sui-tag
|
|
9
|
+
|
|
10
|
+
Story: [Atoms/Tag](https://sui-design-system.web.app/?path=/docs/atoms-tag--docs)
|
|
11
|
+
|
|
12
|
+
## Cuándo usarlo / cuándo no
|
|
13
|
+
|
|
14
|
+
**Úsalo** para nombrar algo que la persona **eligió o aplicó y puede quitar**: un filtro activo
|
|
15
|
+
(«Abierto ahora ✕»), un local o una persona elegidos en una lista, una zona agregada. También
|
|
16
|
+
cuando la etiqueta lleva **avatar, logo o bandera** a la izquierda.
|
|
17
|
+
|
|
18
|
+
El tag **no tiene color**: es siempre gris neutro, así que nunca dice un estado.
|
|
19
|
+
|
|
20
|
+
**No lo uses** para:
|
|
21
|
+
|
|
22
|
+
- Una etiqueta de solo lectura sin nada a la izquierda: `sui-badge` gray (más liviano, va al lado
|
|
23
|
+
de un nombre).
|
|
24
|
+
- Algo con significado de color (pendiente, error, nuevo): `sui-badge` o `sui-status-badge`.
|
|
25
|
+
- **Elegir** entre opciones (chips que se prenden y apagan): `sui-tab-chips`, `sui-filter` o
|
|
26
|
+
`sui-segmented-control`. El tag no tiene estado «seleccionado».
|
|
27
|
+
- Escribir varios valores en un campo: `sui-tag-input`.
|
|
28
|
+
|
|
29
|
+
Regla corta: **se quita o lleva avatar/logo/bandera → tag · solo se lee → badge · se elige →
|
|
30
|
+
tab-chips o filter.**
|
|
31
|
+
|
|
32
|
+
## Qué variante usar
|
|
33
|
+
|
|
34
|
+
> **La guía del DS dice para qué es, no cuándo usar cada estilo.** Su página lo define como
|
|
35
|
+
> «categorizar y etiquetar contenido» y muestra `stroke` (default), `gray`, deshabilitado, con
|
|
36
|
+
> imagen, con avatar y con botón de quitar. No trae do/don't. Lo marcado «según la guía del DS»
|
|
37
|
+
> sale de ahí; el resto es **propuesta, falta aprobación**.
|
|
38
|
+
>
|
|
39
|
+
> Uso medido el 03-10-2026 en `master-pedidos/src/app`: **0 tags**, sin contar prototipos ni
|
|
40
|
+
> vitrinas (`master-preview`, `master-prototipos`, `mockup-sheets-*`, `dx/*`, `_guia-*`,
|
|
41
|
+
> `master-rutina`, `master-medico`, `master-ejercicios`, `dev-index`). Tampoco hay chips hechos a
|
|
42
|
+
> mano con botón de quitar: los chips de la app son para elegir (`rate-order`,
|
|
43
|
+
> `product-detail-view`, `driver-schedule`), y eso no es un tag.
|
|
44
|
+
|
|
45
|
+
| propiedad | valor | cuándo | ejemplo | fuente |
|
|
46
|
+
|---|---|---|---|---|
|
|
47
|
+
| `tagStyle` | `stroke` (default) | Lo normal. Sirve sobre cualquier fondo (blanco o gris claro). | — | según la guía del DS (default) |
|
|
48
|
+
| `tagStyle` | `gray` | Solo sobre blanco, cuando hay muchos tags juntos y el borde hace ruido. **Sobre gris claro desaparece.** | Varios filtros aplicados en fila | propuesta, falta aprobación |
|
|
49
|
+
| `dismissible` | `true` | Lo que se nombra se puede quitar. Emite `(dismiss)`; la pantalla lo saca. | «Abierto ahora ✕» | según la guía del DS |
|
|
50
|
+
| `dismissLabel` | texto | Siempre «Quitar» + el nombre: el lector de pantalla no ve el texto del tag desde la X. | `dismissLabel="Quitar Abierto ahora"` | propuesta, falta aprobación |
|
|
51
|
+
| `leading` + `[data-slot=start]` | avatar | Nombra a una persona o un local. | «Burger House» con su logo | según la guía del DS (con avatar, con imagen) |
|
|
52
|
+
| `leading` + `[data-slot=start]` | ícono de 16 | El ícono dice qué tipo de dato es. | «Andahuaylas» con un pin | según la guía del DS (`Tag.Icon`) |
|
|
53
|
+
| `sublabel` | texto | Cuántos resultados trae ese filtro, entre paréntesis. | «Pollerías (12)» | propuesta, falta aprobación |
|
|
54
|
+
| `disabled` | `true` | **No usar** hoy (ver abajo). | — | según la guía del DS (existe) · propuesta (cuándo) |
|
|
55
|
+
| `interactive` | `true` | **No usar** hoy (ver abajo). | — | propuesta, falta aprobación |
|
|
56
|
+
|
|
57
|
+
Usos de hoy que rompen la regla: **0** (no hay usos).
|
|
58
|
+
|
|
59
|
+
## No usar: por qué no hoy y cuándo sí
|
|
60
|
+
|
|
61
|
+
| qué | por qué no hoy | cuándo sí, en un flujo nuevo |
|
|
62
|
+
|---|---|---|
|
|
63
|
+
| El componente entero (0 usos) | No hay filtros aplicados que se quiten ni listas de elegidos con avatar. | Filtros aplicados en las tablas del super admin; los locales elegidos para una campaña; las zonas que cubre un repartidor. |
|
|
64
|
+
| `disabled` | No hay filtros fijos. | Un filtro que el sistema pone y la persona no puede quitar («Tu distrito»). |
|
|
65
|
+
| `interactive` | Un tag tocable se confunde con un chip para elegir. | Un tag que abre la ficha de lo que nombra (el local de una lista de locales vinculados). |
|
|
66
|
+
|
|
67
|
+
## Tamaño en mobile y en web
|
|
68
|
+
|
|
69
|
+
Criterio de la app: **en mobile se lee y se toca con el dedo, así que va más grande; en web
|
|
70
|
+
(desktop), más chico.** Mobile es `≤ 767 px`, el mismo corte de `BreakpointsService.isMobile()`
|
|
71
|
+
en master-pedidos. Fuente: canon del dueño (03-10-2026), el mismo patrón que ya usan 27 controles
|
|
72
|
+
de la app.
|
|
73
|
+
|
|
74
|
+
| size | alto | texto | leading | X de quitar (área táctil) |
|
|
75
|
+
|---|---|---|---|---|
|
|
76
|
+
| `medium` (default) | 24 px | `label-xs` 12/16 | 16 | 16 (16) |
|
|
77
|
+
| `large` | 32 px | `label-sm` 14/20 | 20 | 20 (32) |
|
|
78
|
+
| `xlarge` | 40 px | `label-md` 16/24 | 20 | 24 (40) |
|
|
79
|
+
|
|
80
|
+
`medium` es el de Figma; `large` y `xlarge` son extensión SUI (03-10-2026), en la escala del
|
|
81
|
+
`sui-button` (32 · 40). En los dos grandes la X tiene un área táctil invisible que llega al alto
|
|
82
|
+
del tag, sin cambiar cómo se ve.
|
|
83
|
+
|
|
84
|
+
| uso | mobile | web | fuente |
|
|
85
|
+
|---|---|---|---|
|
|
86
|
+
| Tag con X (filtro aplicado, elegido que se quita) | `xlarge` (40) | `medium` (24) | propuesta: el tamaño es extensión SUI aprobada (03-10-2026); el «cuándo» falta aprobarlo |
|
|
87
|
+
| Tag de solo lectura (sin X) | `large` (32) | `medium` (24) | propuesta: el tamaño es extensión SUI aprobada (03-10-2026); el «cuándo» falta aprobarlo |
|
|
88
|
+
| Separación entre tags | 8 px | 4–6 px | propuesta, falta aprobación |
|
|
89
|
+
|
|
90
|
+
```html
|
|
91
|
+
<sui-tag [size]="bp.isMobile() ? 'xlarge' : 'medium'" [dismissible]="true" (dismiss)="quitar(f)">{{ f.nombre }}</sui-tag>
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
El leading proyectado (ícono, avatar, bandera) se escala solo a la caja de 20 en los grandes; un
|
|
95
|
+
`<i class="ri-…">` necesita además su `font-size` en 20.
|
|
96
|
+
|
|
97
|
+
## API
|
|
98
|
+
|
|
99
|
+
<!-- api:inicio -->
|
|
100
|
+
<!-- generada: no editar a mano — `npm run docs` -->
|
|
101
|
+
|
|
102
|
+
| input | tipo | default |
|
|
103
|
+
|---|---|---|
|
|
104
|
+
| `tagStyle` | `'stroke' \| 'gray'` | `'stroke'` |
|
|
105
|
+
| `size` | `'medium' \| 'large' \| 'xlarge'` | `'medium'` |
|
|
106
|
+
| `leading` | `boolean` | `false` |
|
|
107
|
+
| `sublabel` | `string` | `''` |
|
|
108
|
+
| `dismissible` | `boolean` | `false` |
|
|
109
|
+
| `interactive` | `boolean` | `false` |
|
|
110
|
+
| `disabled` | `boolean` | `false` |
|
|
111
|
+
| `dismissLabel` | `string` | `'Quitar'` |
|
|
112
|
+
|
|
113
|
+
| output | payload |
|
|
114
|
+
|---|---|
|
|
115
|
+
| `dismiss` | `void` |
|
|
116
|
+
<!-- api:fin -->
|
|
117
|
+
|
|
118
|
+
Slots: `[data-slot=start]` (avatar, logo, bandera o ícono de 16 px) y `<ng-content>` (el texto).
|
|
119
|
+
|
|
120
|
+
## Trampas
|
|
121
|
+
|
|
122
|
+
- **`leading` no se prende solo**: si proyectas algo en `[data-slot=start]` y no pones
|
|
123
|
+
`[leading]="true"`, el padding queda mal.
|
|
124
|
+
- El estilo se llama **`tagStyle`** (en badge es `variant`, en status-badge `appearance`).
|
|
125
|
+
- **No tiene `color`**. Si hace falta color, no es un tag.
|
|
126
|
+
- El tag no saca nada de la lista: `(dismiss)` avisa y la pantalla lo quita.
|
|
127
|
+
- `disabled` bloquea también la X.
|
|
128
|
+
- Un avatar o una bandera proyectados no se ponen grises con `disabled`: los atenúa la pantalla.
|
|
129
|
+
- `sui-avatar` no tiene 16 px: el `xs` (20) se escala por CSS dentro del slot.
|
|
130
|
+
- Radio de 6 px, 24 px de alto, sin mayúsculas: no es un badge (radio completo).
|
|
131
|
+
|
|
132
|
+
## Ejemplo
|
|
133
|
+
|
|
134
|
+
```html
|
|
135
|
+
<!-- Filtro aplicado que se puede quitar -->
|
|
136
|
+
<sui-tag [dismissible]="true" dismissLabel="Quitar Abierto ahora" (dismiss)="quitarFiltro('abierto')">
|
|
137
|
+
Abierto ahora
|
|
138
|
+
</sui-tag>
|
|
139
|
+
|
|
140
|
+
<!-- Un local elegido, con su logo -->
|
|
141
|
+
<sui-tag [leading]="true" [dismissible]="true" dismissLabel="Quitar Burger House">
|
|
142
|
+
<sui-avatar data-slot="start" size="xs" [src]="local.imageUrl" [initials]="'B'" />
|
|
143
|
+
Burger House
|
|
144
|
+
</sui-tag>
|
|
145
|
+
```
|
|
@@ -0,0 +1,120 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sui-tag-input
|
|
3
|
+
selector: sui-tag-input
|
|
4
|
+
resumen: Campo donde se escriben varios valores cortos que se van sumando como chips (palabras clave)
|
|
5
|
+
alternativas: [sui-input (un solo valor), sui-select (una opción de una lista), sui-checkbox-label (varias opciones fijas)]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# sui-tag-input
|
|
9
|
+
|
|
10
|
+
Story: [Molecules/Input/Tag Input](https://sui-design-system.web.app/?path=/docs/molecules-input-tag-input--docs)
|
|
11
|
+
|
|
12
|
+
## Cuándo usarlo / cuándo no
|
|
13
|
+
|
|
14
|
+
**Úsalo** para una lista de valores **libres y cortos** que la persona escribe uno por uno: palabras
|
|
15
|
+
clave, rubros, sinónimos. Enter (y la coma, si se pide) convierte lo escrito en un chip; la ✕ de
|
|
16
|
+
cada chip lo quita; borrar con el campo vacío quita el último.
|
|
17
|
+
|
|
18
|
+
**No lo uses** para:
|
|
19
|
+
|
|
20
|
+
- Elegir varias opciones que ya existen: casillas (`sui-checkbox-label`).
|
|
21
|
+
- Un solo valor: `sui-input`.
|
|
22
|
+
- Una lista que se guarda como texto separado por comas y nadie ve como lista.
|
|
23
|
+
|
|
24
|
+
Regla corta: **varios valores libres → tag-input · varias opciones fijas → casillas.**
|
|
25
|
+
|
|
26
|
+
## Qué variante usar
|
|
27
|
+
|
|
28
|
+
> **En la guía del DS es un ejemplo de su página del input** («With Tags»: un campo con los chips
|
|
29
|
+
> Berlin, London, Paris debajo). No dice cuándo usarlo.
|
|
30
|
+
>
|
|
31
|
+
> Uso medido el 03-10-2026 en `master-pedidos/src/app`: **0 usos**. Candidato: «Rubros que entran»
|
|
32
|
+
> en `pages/super-admin/master-settings/master-settings.html:134`, un `sui-input` donde se escriben
|
|
33
|
+
> palabras separadas por coma («ferreteria, ferretería, construcción») y la pantalla las parte a mano.
|
|
34
|
+
|
|
35
|
+
| prop / evento | cuándo | fuente |
|
|
36
|
+
|---|---|---|
|
|
37
|
+
| `label` · `required` · `sublabel` · `tooltip` · `hint` | Igual que `sui-input`. El hint dice cómo se agrega: «Escribe y presiona Enter». | según la guía del DS (ejemplo con label «Tag Input») |
|
|
38
|
+
| `placeholder` | **Siempre**: el default es «Add tags...». «Agrega una palabra…». | propuesta, falta aprobación |
|
|
39
|
+
| `commaSeparator` | `true` cuando la gente ya escribe con comas (rubros, sinónimos). | propuesta, falta aprobación |
|
|
40
|
+
| `addOnBlur` | `true` en mobile: si sale del campo sin Enter, lo escrito no se pierde. | propuesta, falta aprobación |
|
|
41
|
+
| `maxTags` | Cuando hay un tope real. Hoy no hay. | propuesta, falta aprobación |
|
|
42
|
+
| `allowDuplicates` | `false` (default). | propuesta, falta aprobación |
|
|
43
|
+
| `rightIcon` | No usar hoy (ícono de info a la derecha). | — |
|
|
44
|
+
| `error` + `errorMessage` | Falta al menos un valor en un campo obligatorio. | según la guía del DS (estado de error del Figma) |
|
|
45
|
+
| `disabled` | Mientras se guarda. | API |
|
|
46
|
+
| `(tagAdded)` · `(tagRemoved)` | Guardar al momento, sin botón. | API |
|
|
47
|
+
|
|
48
|
+
## Tamaño en mobile y en web
|
|
49
|
+
|
|
50
|
+
Publicado (0.19.0): `xsmall` (32), `small` (36), `medium` (40). **En el repo del DS, sin publicar:**
|
|
51
|
+
`large` (48) y `xlarge` (52), los mismos altos de `sui-input`, y el texto a 16 px en mobile (en
|
|
52
|
+
0.19.0 queda en 14). Los chips de abajo son `sui-tag` de tamaño fijo: **la ✕ para quitar un chip
|
|
53
|
+
no crece** con el campo.
|
|
54
|
+
|
|
55
|
+
| uso | mobile | web | fuente |
|
|
56
|
+
|---|---|---|---|
|
|
57
|
+
| campo de formulario | `large` (48) **cuando se publique**; hasta entonces `medium` (40) | `medium` (40) | propuesta, falta aprobación |
|
|
58
|
+
|
|
59
|
+
```html
|
|
60
|
+
<sui-tag-input [size]="bp.isMobile() ? 'large' : 'medium'" … /> <!-- 'large' desde la próxima versión -->
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## «No usar»: por qué no hoy y cuándo sí
|
|
64
|
+
|
|
65
|
+
| qué | por qué no hoy | cuándo sí, en un flujo nuevo |
|
|
66
|
+
|---|---|---|
|
|
67
|
+
| el componente entero (0 usos) | nadie carga listas de palabras | «Rubros que entran» de una categoría del home (`master-settings.html:134`), o sinónimos de un producto para el buscador |
|
|
68
|
+
| `rightIcon` | el hint explica mejor | — |
|
|
69
|
+
| `maxTags` | no hay topes | un máximo de 5 palabras clave por producto |
|
|
70
|
+
| `allowDuplicates` | repetir no sirve | — |
|
|
71
|
+
| `size` `xsmall` / `small` | no hay tag-inputs en tablas | un filtro denso del panel |
|
|
72
|
+
|
|
73
|
+
## API
|
|
74
|
+
|
|
75
|
+
<!-- api:inicio -->
|
|
76
|
+
<!-- generada: no editar a mano — `npm run docs` -->
|
|
77
|
+
|
|
78
|
+
| input | tipo | default |
|
|
79
|
+
|---|---|---|
|
|
80
|
+
| `size` | `'xsmall' \| 'small' \| 'medium' \| 'large' \| 'xlarge'` | `'medium'` |
|
|
81
|
+
| `placeholder` | `string` | `'Add tags...'` |
|
|
82
|
+
| `disabled` | `boolean` | `false` |
|
|
83
|
+
| `error` | `boolean` | `false` |
|
|
84
|
+
| `errorMessage` | `string \| null` | `null` |
|
|
85
|
+
| `rightIcon` | `string \| boolean` | `false` |
|
|
86
|
+
| `label` | `string \| null` | `null` |
|
|
87
|
+
| `sublabel` | `string \| null` | `null` |
|
|
88
|
+
| `required` | `boolean` | `false` |
|
|
89
|
+
| `tooltip` | `string \| null` | `null` |
|
|
90
|
+
| `hint` | `string \| null` | `null` |
|
|
91
|
+
| `commaSeparator` | `boolean` | `false` |
|
|
92
|
+
| `allowDuplicates` | `boolean` | `false` |
|
|
93
|
+
| `maxTags` | `number \| null` | `null` |
|
|
94
|
+
| `addOnBlur` | `boolean` | `false` |
|
|
95
|
+
| `value` · model, `[(value)]` | `string[]` | `[]` |
|
|
96
|
+
|
|
97
|
+
| output | payload |
|
|
98
|
+
|---|---|
|
|
99
|
+
| `tagAdded` | `string` |
|
|
100
|
+
| `tagRemoved` | `string` |
|
|
101
|
+
<!-- api:fin -->
|
|
102
|
+
|
|
103
|
+
`value` es `model<string[]>`. Implementa `ControlValueAccessor`: lo que está escrito sin confirmar
|
|
104
|
+
no es parte del valor. Slot `[slot=right]` para un contenido propio a la derecha.
|
|
105
|
+
|
|
106
|
+
## Trampas
|
|
107
|
+
|
|
108
|
+
- **Placeholder en inglés por default** («Add tags...»). Pásalo siempre.
|
|
109
|
+
- Sin `commaSeparator` la coma es un carácter más: «a, b» queda como un solo chip.
|
|
110
|
+
- Sin `addOnBlur`, lo que se escribió y no se confirmó con Enter se pierde al salir.
|
|
111
|
+
- Los chips van **debajo** del campo, no adentro.
|
|
112
|
+
- El hover quita el borde (a diferencia de `sui-input`, que lo oscurece).
|
|
113
|
+
|
|
114
|
+
## Ejemplo
|
|
115
|
+
|
|
116
|
+
```html
|
|
117
|
+
<sui-tag-input label="Rubros que entran" placeholder="Agrega una palabra…"
|
|
118
|
+
hint="Un local entra si su rubro contiene alguna" [commaSeparator]="true"
|
|
119
|
+
[addOnBlur]="true" [(value)]="rubros" />
|
|
120
|
+
```
|
|
@@ -0,0 +1,157 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sui-textarea
|
|
3
|
+
selector: sui-textarea
|
|
4
|
+
resumen: Campo de varias líneas para un texto libre (motivo, nota al repartidor, descripción)
|
|
5
|
+
alternativas: [sui-input (un dato de una línea), sui-rich-editor (texto con formato, hoy sin uso), sui-select (motivos que se pueden elegir de una lista)]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# sui-textarea
|
|
9
|
+
|
|
10
|
+
Story: [Molecules/Input/Textarea](https://sui-design-system.web.app/?path=/docs/molecules-input-textarea--docs)
|
|
11
|
+
|
|
12
|
+
## Cuándo usarlo / cuándo no
|
|
13
|
+
|
|
14
|
+
**Úsalo** para un texto que la persona redacta y que puede ocupar más de una línea: el motivo de
|
|
15
|
+
un rechazo o una cancelación, la nota para el repartidor, la descripción de un producto, un
|
|
16
|
+
comentario al calificar.
|
|
17
|
+
|
|
18
|
+
**No lo uses** para:
|
|
19
|
+
|
|
20
|
+
- Un dato de una línea (nombre, referencia corta, URL, un slug): `sui-input`. Hoy hay uno así:
|
|
21
|
+
«Personaliza tu url» en `pages/mi-negocio/mi-negocio.html:706`.
|
|
22
|
+
- Un motivo que se puede elegir de una lista cerrada: radios o `sui-select`, y la opción «Otro»
|
|
23
|
+
abre el textarea (como ya hace `pages/envios/envios.html:819`, que además corta a 200 a mano con
|
|
24
|
+
`.slice(0, 200)` en vez de `[maxLength]="200"`).
|
|
25
|
+
- Texto con negritas o enlaces: no hay caso hoy (ver `sui-rich-editor`).
|
|
26
|
+
- Un `<textarea>` nativo: queda **1** (`pages/products/add-product/add-product.html:155`).
|
|
27
|
+
|
|
28
|
+
Regla corta: **se redacta y puede pasar de una línea → textarea · una línea → input · se elige → select.**
|
|
29
|
+
|
|
30
|
+
## Qué variante usar
|
|
31
|
+
|
|
32
|
+
> **La guía del DS lo presenta con contador de caracteres y su propia manija para agrandarlo**
|
|
33
|
+
> («78/200»), y aparte una versión simple sin contador. No dice cuándo va cada una.
|
|
34
|
+
>
|
|
35
|
+
> Uso medido el 03-10-2026 en `master-pedidos/src/app` (misma exclusión que `sui-badge`, más
|
|
36
|
+
> `master-ejercicios`): **50 usos en 29 archivos**. `rows`: 32 en 3, 13 en 2, 4 en 4, 1 en 8.
|
|
37
|
+
> Con `maxLength`: 8. Con `resizable=false`: 2. 38 con label, 12 sin.
|
|
38
|
+
|
|
39
|
+
| prop | cuándo | fuente |
|
|
40
|
+
|---|---|---|
|
|
41
|
+
| `label` | Siempre, salvo cuando la pregunta ya está escrita justo encima (la calificación: «¿Qué te gustó?»). | según la guía del DS (ejemplo con label) |
|
|
42
|
+
| `required` | Motivo obligatorio (rechazar, anular). | según la guía del DS (`Enter Your Message*`) |
|
|
43
|
+
| `sublabel` | **«(Opcional)»**, con paréntesis, cuando no es obligatorio, en vez de «(opcional)» dentro del label. Sin paréntesis queda pegado al label. | según la guía del DS (`(Optional)`) y canon (README de `sui-form-label`) |
|
|
44
|
+
| `tooltip` | Hoy sin caso. | según la guía del DS (ícono de info del label) |
|
|
45
|
+
| `hint` | Para quién es el texto: «Lo va a leer el cliente», «Solo lo ve tu equipo». | según la guía del DS (ejemplo con hint) |
|
|
46
|
+
| `error` + `errorMessage` | Motivo obligatorio vacío. El mensaje reemplaza al hint y el contador se pone rojo. | según la guía del DS (`Has Error`) |
|
|
47
|
+
| `placeholder` | Un ejemplo de lo que se espera: «Ej. frágil, tocar el timbre, entregar en recepción». Elipsis `…`. | canon (CANON-SUI §5.2) |
|
|
48
|
+
| `maxLength` | **Siempre que otro lo va a leer** (cliente, repartidor, local) o se guarda en la base: muestra «n/200» y corta el exceso. 200 para motivos y notas; 500 para descripciones. | según la guía del DS (su ejemplo principal lleva contador); los topes son propuesta |
|
|
49
|
+
| `size` | El mismo de los inputs del formulario: `large` en mobile, `medium` en web. Ver «Tamaño en mobile y en web». | canon (aprobado por el dueño, 03-10-2026), extensión SUI; el «cuándo» es propuesta, falta aprobación |
|
|
50
|
+
| `rows` | `3` por defecto (motivos, notas). `2` para una nota corta en un formulario largo. `4` o más solo para una descripción larga. | propuesta, falta aprobación |
|
|
51
|
+
| `resizable` | `true` en web. En mobile **`false`**: la manija no se puede arrastrar con el dedo y parece un adorno. | según la guía del DS (versión con y sin manija); el «cuándo» es propuesta |
|
|
52
|
+
| `readonly` | Mostrar un texto ya enviado con la misma forma del campo (copiar un mensaje). | propuesta, falta aprobación |
|
|
53
|
+
| `disabled` | Mientras se envía. | según la guía del DS |
|
|
54
|
+
| `autocomplete` | No hace falta: ningún texto libre se autocompleta. | propuesta, falta aprobación |
|
|
55
|
+
|
|
56
|
+
**Usos de hoy que rompen la regla:**
|
|
57
|
+
|
|
58
|
+
- **3 `sublabel="Opcional"` sin paréntesis** (se lee «Notas adicionalesOpcional») → «(Opcional)»:
|
|
59
|
+
`pages/customer/checkout/checkout.html:209`, `pages/self-delivery/self-delivery.html:101`,
|
|
60
|
+
`components/add-product-modal/add-product-modal.html:184`.
|
|
61
|
+
- **9 «(opcional)» dentro del label** → `sublabel="(Opcional)"`: `pages/super-admin/super-admin.html:333`
|
|
62
|
+
(«Notas (opcional)»), `pages/super-admin/master-orders/master-orders.html:795`,
|
|
63
|
+
`components/cuenta-bloqueada/cuenta-bloqueada.html:21`.
|
|
64
|
+
- **Textos que lee otra persona sin `maxLength`** (42 de 50 no lo tienen). Los que más importan:
|
|
65
|
+
`pages/envios/envios.html:419` («Nota para el repartidor»), `pages/customer/checkout/checkout.html:209`
|
|
66
|
+
(«Notas adicionales»), `pages/super-admin/master-users/master-users.html:266` («Motivo (lo va a leer)»).
|
|
67
|
+
- **4 placeholders con `...`**: `pages/customer/checkout/checkout.html:209`,
|
|
68
|
+
`pages/self-delivery/self-delivery.html:101`, `pages/driver/driver-order-detail/driver-order-detail.html:823`.
|
|
69
|
+
|
|
70
|
+
## Tamaño en mobile y en web
|
|
71
|
+
|
|
72
|
+
Tiene `size` con **los mismos nombres que `sui-input`** (`xsmall` · `small` · `medium` · `large` ·
|
|
73
|
+
`xlarge`): cambia el padding y el radio, y el texto arranca en la misma línea vertical que el del
|
|
74
|
+
input del mismo tamaño. El alto lo siguen poniendo las `rows`. `medium` es el de la guía del DS y el
|
|
75
|
+
default (padding 10/12, radio 12); el resto es extensión SUI.
|
|
76
|
+
|
|
77
|
+
| size | padding (arriba/der · izq) | radio | fuente |
|
|
78
|
+
|---|---|---|---|
|
|
79
|
+
| `xlarge` | 14 · 16 | 12 | canon (aprobado por el dueño, 03-10-2026), extensión SUI |
|
|
80
|
+
| `large` | 12 · 14 | 12 | canon (aprobado por el dueño, 03-10-2026), extensión SUI |
|
|
81
|
+
| `medium` (default) | 10 · 12 | 12 | según la guía del DS |
|
|
82
|
+
| `small` | 8 · 10 | 10 | canon (aprobado por el dueño, 03-10-2026), extensión SUI |
|
|
83
|
+
| `xsmall` | 6 · 8 | 8 | canon (aprobado por el dueño, 03-10-2026), extensión SUI |
|
|
84
|
+
|
|
85
|
+
**En mobile (≤ 767 px) la letra sube sola a 16/24** (Paragraph/Medium), como el input sube a 16:
|
|
86
|
+
evita el zoom de iOS al enfocar y el textarea deja de verse más chico que los campos de arriba. Por
|
|
87
|
+
eso cada fila mide 24 px en el teléfono y 20 en web (3 filas ≈ 72 + padding en mobile, 60 + padding
|
|
88
|
+
en web): el área que se toca siempre pasa los 44 px.
|
|
89
|
+
|
|
90
|
+
| uso | mobile | web | fuente |
|
|
91
|
+
|---|---|---|---|
|
|
92
|
+
| motivo, nota | el `size` del formulario (`large` si los inputs van en `large`), `rows=3`, `[resizable]="false"` | `medium`, `rows=3`, manija | propuesta, falta aprobación |
|
|
93
|
+
| descripción larga | igual, `rows=4` | `medium`, `rows=4` | propuesta, falta aprobación |
|
|
94
|
+
|
|
95
|
+
```html
|
|
96
|
+
<sui-textarea [size]="bp.isMobile() ? 'large' : 'medium'" [rows]="3"
|
|
97
|
+
[resizable]="!bp.isMobile()" … /> <!-- bp = BreakpointsService -->
|
|
98
|
+
```
|
|
99
|
+
|
|
100
|
+
## «No usar»: por qué no hoy y cuándo sí
|
|
101
|
+
|
|
102
|
+
| qué | por qué no hoy | cuándo sí, en un flujo nuevo |
|
|
103
|
+
|---|---|---|
|
|
104
|
+
| `tooltip` | el hint alcanza | un campo del panel con una regla que no entra en una línea («qué puede ver el cliente») |
|
|
105
|
+
| `rows` 6 o más | ningún texto de la app es tan largo | términos y condiciones propios de un local, o la descripción larga de una campaña |
|
|
106
|
+
| `autocomplete` | un texto libre no se autocompleta | — |
|
|
107
|
+
|
|
108
|
+
## API
|
|
109
|
+
|
|
110
|
+
<!-- api:inicio -->
|
|
111
|
+
<!-- generada: no editar a mano — `npm run docs` -->
|
|
112
|
+
|
|
113
|
+
| input | tipo | default |
|
|
114
|
+
|---|---|---|
|
|
115
|
+
| `rows` | `number` | `4` |
|
|
116
|
+
| `size` | `'xsmall' \| 'small' \| 'medium' \| 'large' \| 'xlarge'` | `'medium'` |
|
|
117
|
+
| `placeholder` | `string` | `''` |
|
|
118
|
+
| `disabled` | `boolean` | `false` |
|
|
119
|
+
| `readonly` | `boolean` | `false` |
|
|
120
|
+
| `error` | `boolean` | `false` |
|
|
121
|
+
| `errorMessage` | `string \| null` | `null` |
|
|
122
|
+
| `label` | `string \| null` | `null` |
|
|
123
|
+
| `hint` | `string \| null` | `null` |
|
|
124
|
+
| `autocomplete` | `string \| null` | `null` |
|
|
125
|
+
| `resizable` | `boolean` | `true` |
|
|
126
|
+
| `maxLength` | `number \| null` | `null` |
|
|
127
|
+
| `sublabel` | `string \| null` | `null` |
|
|
128
|
+
| `required` | `boolean` | `false` |
|
|
129
|
+
| `tooltip` | `string \| null` | `null` |
|
|
130
|
+
| `value` · model, `[(value)]` | `string` | `''` |
|
|
131
|
+
|
|
132
|
+
Outputs: ninguno.
|
|
133
|
+
<!-- api:fin -->
|
|
134
|
+
|
|
135
|
+
`value` es `model<string>`. Implementa `ControlValueAccessor` (`formControlName`, `[(ngModel)]`).
|
|
136
|
+
|
|
137
|
+
## Trampas
|
|
138
|
+
|
|
139
|
+
- **`maxLength` con L mayúscula** (en `sui-input` es `maxlength`). Con el nombre equivocado Angular
|
|
140
|
+
no avisa en un atributo plano y el tope no se aplica.
|
|
141
|
+
- Sin `maxLength` **no hay contador**; la manija sale igual mientras `resizable` sea `true`.
|
|
142
|
+
- El tope también corta lo que se pega (no solo lo que se tipea).
|
|
143
|
+
- El hint lleva siempre el ícono `information-fill`, también el neutro (así está en el Figma).
|
|
144
|
+
- El `size` no cambia el alto: eso lo deciden las `rows`. Lo que alinea con el input es el padding.
|
|
145
|
+
- `error` sin `errorMessage` pinta el borde y no dice qué falta.
|
|
146
|
+
|
|
147
|
+
## Ejemplo
|
|
148
|
+
|
|
149
|
+
```html
|
|
150
|
+
<sui-textarea label="Nota para el repartidor" sublabel="(Opcional)" [rows]="3" [maxLength]="200"
|
|
151
|
+
[size]="bp.isMobile() ? 'large' : 'medium'" placeholder="Ej. frágil, tocar el timbre, entregar en recepción"
|
|
152
|
+
[resizable]="!bp.isMobile()" [(value)]="nota" />
|
|
153
|
+
|
|
154
|
+
<sui-textarea label="Motivo del rechazo" [required]="true" [rows]="3" [maxLength]="200"
|
|
155
|
+
hint="Lo va a leer el local" [error]="intentado() && !motivo()"
|
|
156
|
+
errorMessage="Escribe el motivo" [(value)]="motivo" />
|
|
157
|
+
```
|
|
@@ -0,0 +1,237 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: sui-time-picker
|
|
3
|
+
selector: sui-time-picker
|
|
4
|
+
resumen: Campo para elegir una hora del día, con columnas de hora, minuto y AM/PM
|
|
5
|
+
alternativas: [sui-tab-chips + sui-radio-label (día y franja de entrega de un pedido programado), sui-select (pocas horas fijas), sui-input type="time" (hora libre con el selector del sistema)]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# sui-time-picker
|
|
9
|
+
|
|
10
|
+
Story: [Molecules/Date & Time/Time Picker](https://sui-design-system.web.app/?path=/docs/molecules-date-time-time-picker--docs)
|
|
11
|
+
|
|
12
|
+
## Cuándo usarlo / cuándo no
|
|
13
|
+
|
|
14
|
+
**Úsalo** para una **hora del día** que la persona escribe con libertad: el horario de apertura y
|
|
15
|
+
cierre del local, el turno del repartidor, «Cerrar hasta las 9:30 PM».
|
|
16
|
+
|
|
17
|
+
**No lo uses** para:
|
|
18
|
+
|
|
19
|
+
- **La hora de entrega de un pedido programado**: no se elige una hora libre, se elige una
|
|
20
|
+
**franja** que el local puede cumplir. Va la hoja del checkout (`sui-tab-chips` para el día y
|
|
21
|
+
`sui-radio-label` con `flip` para cada franja). Es la maqueta aprobada, y el propio código
|
|
22
|
+
descartó `sui-time-picker-item` para esas filas porque son densas y sin separación
|
|
23
|
+
(`checkout.html:475`).
|
|
24
|
+
- Pocas horas fijas («12:00», «13:00», «14:00»): `sui-select`.
|
|
25
|
+
- Una duración («30 min», «1 h»): no hay componente de duración; un `sui-select` o
|
|
26
|
+
`sui-tab-chips` con los valores (ver Trampas: los presets de este componente no son duraciones).
|
|
27
|
+
|
|
28
|
+
Regla corta: **hora libre del día → time-picker · franja de entrega → la hoja del checkout ·
|
|
29
|
+
pocas horas → select.**
|
|
30
|
+
|
|
31
|
+
## Qué variante usar
|
|
32
|
+
|
|
33
|
+
> **La guía del DS no tiene página para este componente.** Todo es **propuesta, falta
|
|
34
|
+
> aprobación**, aunque ya sea práctica de la app.
|
|
35
|
+
>
|
|
36
|
+
> Uso medido el 03-10-2026 en `master-pedidos/src/app` (sin prototipos ni vitrinas): **7 usos en
|
|
37
|
+
> 4 pantallas** — horario del local (`weekly-schedule-editor`, 2), turnos del repartidor
|
|
38
|
+
> (`driver-schedule`, 2), votaciones de cierre y recargo (`driver-dashboard`, 2) y el cierre del
|
|
39
|
+
> local en la central (`central-inicio`, 1). Los 7 van con `size="medium"`, `format="12h"`,
|
|
40
|
+
> `[durationPresets]="[]"` y footer. `step`: 5 en 5 usos, 30 en 2.
|
|
41
|
+
|
|
42
|
+
### `format`
|
|
43
|
+
|
|
44
|
+
| valor | cuándo | fuente |
|
|
45
|
+
|---|---|---|
|
|
46
|
+
| `12h` (default) | Siempre: en Perú la hora se dice «9:30 PM». Los 7 usos lo llevan. | propuesta |
|
|
47
|
+
| `24h` | No usar en la app. | no usar |
|
|
48
|
+
|
|
49
|
+
### `step`
|
|
50
|
+
|
|
51
|
+
| valor | cuándo | fuente |
|
|
52
|
+
|---|---|---|
|
|
53
|
+
| `5` (default) | Horas reales que la persona conoce al minuto: apertura y cierre del local, «cerrar hasta». Con saltos de 30 se redondeaban cierres reales (comentario en `weekly-schedule-editor.html:41`). | propuesta |
|
|
54
|
+
| `30` | Bloques de un turno, que se planifican por media hora (`driver-schedule.html:142`). | propuesta |
|
|
55
|
+
| `15` | Citas o reservas en cuartos de hora. Sin caso hoy. | propuesta |
|
|
56
|
+
| `1` | No usar: 60 opciones en la columna para una precisión que nadie pide. | no usar |
|
|
57
|
+
|
|
58
|
+
### `durationPresets`, `showFooter`, `inline`
|
|
59
|
+
|
|
60
|
+
| input | valor | cuándo | fuente |
|
|
61
|
+
|---|---|---|---|
|
|
62
|
+
| `durationPresets` | `[]` | **Siempre.** Los presets de fábrica (15 min, 30 min, 45 min, 1 h) ponen la hora como «tiempo desde la medianoche»: «30 min» deja 12:30 AM. Los 7 usos los apagan. | propuesta |
|
|
63
|
+
| `durationPresets` | lista propia | No usar mientras se lean como hora del día. | no usar |
|
|
64
|
+
| `showFooter` | `true` (default) | Cancelar/Aplicar: la hora no cambia hasta confirmar. Es lo correcto cuando la hora se guarda o publica algo (el horario del local). | propuesta |
|
|
65
|
+
| `showFooter` | `false` | Se aplica al tocar cada columna. Solo dentro de un formulario que ya tiene su propio «Guardar». | propuesta |
|
|
66
|
+
| `inline` | `true` | El panel es el contenido, dentro de un `sui-modal` (sin campo ni popover). | propuesta |
|
|
67
|
+
|
|
68
|
+
### `size`, `label`, `hint`, `error`, `required`, `disabled`
|
|
69
|
+
|
|
70
|
+
| input | cuándo | fuente |
|
|
71
|
+
|---|---|---|
|
|
72
|
+
| `size="medium"` (40 px, default) | Siempre: es el más grande que hay (ver «Tamaño»). | propuesta |
|
|
73
|
+
| `size="small"` (36 px) | Filas densas en web. | propuesta |
|
|
74
|
+
| `label` | Siempre, corto y en verbo o momento: «Abre», «Cierra», «Inicio», «Cerrar hasta». | propuesta |
|
|
75
|
+
| `sublabel` / `tooltip` | «Opcional» o una aclaración que no entra en el label. | propuesta |
|
|
76
|
+
| `hint` | Qué pasa con esa hora: «Se reabre sola a medianoche». | propuesta |
|
|
77
|
+
| `error` + `errorMessage` | La hora no cuadra: «Cierra antes de abrir». | propuesta |
|
|
78
|
+
| `required` | La hora es obligatoria para guardar. | propuesta |
|
|
79
|
+
| `disabled` | Mientras se guarda (como hacen el horario y los turnos). | propuesta |
|
|
80
|
+
| `placeholder` | Lo que se elige: «Hora de cierre». El default «Seleccionar hora» sirve. | propuesta |
|
|
81
|
+
|
|
82
|
+
Usos de hoy que rompen la regla: **0**. Los 7 cumplen `12h`, `durationPresets=[]` y `label`.
|
|
83
|
+
|
|
84
|
+
### Las piezas que trae (exportadas, sin carpeta ni story propias)
|
|
85
|
+
|
|
86
|
+
| pieza | qué es | cuándo suelta | fuente |
|
|
87
|
+
|---|---|---|---|
|
|
88
|
+
| `sui-time-picker-item` | una fila de la columna (36 px) | No usar: para filas de horario que se tocan con el pulgar va `sui-radio-label` (`checkout.html:475`). | no usar |
|
|
89
|
+
| `sui-time-picker-duration` | la pastilla de un preset | No usar: es la de los presets de duración. | no usar |
|
|
90
|
+
| `sui-time-picker-status` | pastilla Disponible/Ocupado/En reunión/Desconectado | No usar: sus textos de fábrica están en inglés y el estado del repartidor ya tiene su badge. | no usar |
|
|
91
|
+
|
|
92
|
+
## Tamaño en mobile y en web
|
|
93
|
+
|
|
94
|
+
En mobile se toca con el dedo: lo que se toca mide **44 px o más**. En web, con mouse, va más
|
|
95
|
+
chico. En pantallas de los dos se elige con el servicio de breakpoints de la app (`bp`, en
|
|
96
|
+
`services/breakpoints.service.ts`).
|
|
97
|
+
|
|
98
|
+
`size` tiene `small` (36), `medium` (40, default) y **`large` (48)**, extensión SUI: mide lo mismo
|
|
99
|
+
que `sui-input` y `sui-select` `large`, que van al lado en los formularios de horario. Con `large`
|
|
100
|
+
el panel también crece: **filas de 44** y **Cancelar / Aplicar en `large` (48)**. En mobile
|
|
101
|
+
(≤ 767 px) las filas miden 44 con cualquier `size`.
|
|
102
|
+
|
|
103
|
+
| pieza | `small` · `medium` en web | `large` | mobile (cualquier size) | fuente |
|
|
104
|
+
|---|---|---|---|---|
|
|
105
|
+
| campo (trigger) | 36 · 40 | 48 | el del `size` | 36/40: medido · 48: canon (aprobado por el dueño, 03-10-2026), extensión SUI |
|
|
106
|
+
| fila de la columna | 36 | 44 | 44 | 36: Figma · 44: canon (aprobado por el dueño, 03-10-2026), extensión SUI |
|
|
107
|
+
| Cancelar / Aplicar | 36 | 48 | el del `size` | canon (aprobado por el dueño, 03-10-2026), extensión SUI |
|
|
108
|
+
|
|
109
|
+
| uso | mobile | web | fuente |
|
|
110
|
+
|---|---|---|---|
|
|
111
|
+
| horario en un formulario | `large` | `medium`, o `small` en filas densas | propuesta, falta aprobación |
|
|
112
|
+
|
|
113
|
+
```html
|
|
114
|
+
<sui-time-picker label="Cierra" format="12h" [step]="5" [durationPresets]="[]"
|
|
115
|
+
[size]="bp.isMobile() ? 'large' : 'medium'"
|
|
116
|
+
[value]="cierre()" (valueChange)="cierre.set($event)" />
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
## «No usar»: por qué no hoy y cuándo sí
|
|
120
|
+
|
|
121
|
+
Nada se borra del DS. Lo marcado «no usar» no tiene caso en la app de hoy; si un flujo nuevo cae
|
|
122
|
+
en la columna de la derecha, se usa (aprobado por el dueño, 03-10-2026).
|
|
123
|
+
|
|
124
|
+
| qué | por qué no hoy | cuándo sí, en un flujo nuevo |
|
|
125
|
+
|---|---|---|
|
|
126
|
+
| `format="24h"` | en Perú se dice «9:30 PM» | una pantalla técnica del super admin (registro de eventos) donde se comparan horas |
|
|
127
|
+
| `step="1"` | 60 opciones sin necesidad | registrar la hora exacta de un hecho (hora real de entrega en un reclamo) |
|
|
128
|
+
| `durationPresets` (cualquiera) | se leen como hora desde la medianoche, no como duración | si el DS los cambia para sumar sobre la hora elegida («en 30 min») |
|
|
129
|
+
| `sui-time-picker-item` suelto | fila de 36 px, densa | una lista desplegable de horas en web |
|
|
130
|
+
| `sui-time-picker-duration` suelto | es parte de los presets | — |
|
|
131
|
+
| `sui-time-picker-status` | textos en inglés; el estado del repartidor ya tiene su badge | un panel de disponibilidad del equipo del local, con los textos en español |
|
|
132
|
+
|
|
133
|
+
## API
|
|
134
|
+
|
|
135
|
+
<!-- api:inicio -->
|
|
136
|
+
<!-- generada: no editar a mano — `npm run docs` -->
|
|
137
|
+
|
|
138
|
+
### `sui-time-picker`
|
|
139
|
+
|
|
140
|
+
| input | tipo | default |
|
|
141
|
+
|---|---|---|
|
|
142
|
+
| `label` | `string \| null` | `null` |
|
|
143
|
+
| `sublabel` | `string \| null` | `null` |
|
|
144
|
+
| `required` | `boolean` | `false` |
|
|
145
|
+
| `tooltip` | `string \| null` | `null` |
|
|
146
|
+
| `hint` | `string \| null` | `null` |
|
|
147
|
+
| `errorMessage` | `string \| null` | `null` |
|
|
148
|
+
| `error` | `boolean` | `false` |
|
|
149
|
+
| `placeholder` | `string` | `'Seleccionar hora'` |
|
|
150
|
+
| `disabled` | `boolean` | `false` |
|
|
151
|
+
| `size` | `'small' \| 'medium' \| 'large'` | `'medium'` |
|
|
152
|
+
| `format` | `'12h' \| '24h'` | `'12h'` |
|
|
153
|
+
| `step` | `number` | `5` |
|
|
154
|
+
| `durationPresets` | `readonly SuiTimeDurationPreset[]` | `DEFAULT_DURATION_PRESETS` |
|
|
155
|
+
| `inline` | `boolean` | `false` |
|
|
156
|
+
| `showFooter` | `boolean` | `true` |
|
|
157
|
+
| `value` · model, `[(value)]` | `SuiTimeValue \| null` | `null` |
|
|
158
|
+
|
|
159
|
+
Outputs: ninguno.
|
|
160
|
+
|
|
161
|
+
### `sui-time-picker-duration`
|
|
162
|
+
|
|
163
|
+
| input | tipo | default |
|
|
164
|
+
|---|---|---|
|
|
165
|
+
| `label` | `string` | — (requerido) |
|
|
166
|
+
| `active` | `boolean` | `false` |
|
|
167
|
+
| `disabled` | `boolean` | `false` |
|
|
168
|
+
|
|
169
|
+
| output | payload |
|
|
170
|
+
|---|---|
|
|
171
|
+
| `clicked` | `Event` |
|
|
172
|
+
|
|
173
|
+
### `sui-time-picker-item`
|
|
174
|
+
|
|
175
|
+
| input | tipo | default |
|
|
176
|
+
|---|---|---|
|
|
177
|
+
| `time` | `string` | — (requerido) |
|
|
178
|
+
| `meridiem` | `string \| null` | `null` |
|
|
179
|
+
| `rightTime` | `string \| null` | `null` |
|
|
180
|
+
| `rightMeridiem` | `string \| null` | `null` |
|
|
181
|
+
| `direction` | `'right' \| 'center'` | `'right'` |
|
|
182
|
+
| `active` | `boolean` | `false` |
|
|
183
|
+
| `highlighted` | `boolean` | `false` |
|
|
184
|
+
| `disabled` | `boolean` | `false` |
|
|
185
|
+
|
|
186
|
+
| output | payload |
|
|
187
|
+
|---|---|
|
|
188
|
+
| `clicked` | `Event` |
|
|
189
|
+
|
|
190
|
+
### `sui-time-picker-status`
|
|
191
|
+
|
|
192
|
+
| input | tipo | default |
|
|
193
|
+
|---|---|---|
|
|
194
|
+
| `type` | `'available' \| 'busy' \| 'in-meeting' \| 'offline'` | `'available'` |
|
|
195
|
+
| `label` | `string \| null` | `null` |
|
|
196
|
+
| `selected` | `boolean` | `false` |
|
|
197
|
+
| `disabled` | `boolean` | `false` |
|
|
198
|
+
|
|
199
|
+
| output | payload |
|
|
200
|
+
|---|---|
|
|
201
|
+
| `clicked` | `Event` |
|
|
202
|
+
<!-- api:fin -->
|
|
203
|
+
|
|
204
|
+
Es `ControlValueAccessor`; `writeValue` acepta también un string `"21:30"`.
|
|
205
|
+
|
|
206
|
+
```ts
|
|
207
|
+
interface SuiTimeValue { hours: number; minutes: number; } // hours en 0–23 aunque el format sea 12h
|
|
208
|
+
interface SuiTimeDurationPreset { label: string; minutes: number; }
|
|
209
|
+
```
|
|
210
|
+
|
|
211
|
+
## Trampas
|
|
212
|
+
|
|
213
|
+
- **Los presets de duración no son duraciones**: «30 min» pone la hora en 00:30 (12:30 AM). Por eso
|
|
214
|
+
se pasan siempre `[durationPresets]="[]"`. Sin eso, el panel muestra cuatro pastillas que
|
|
215
|
+
rompen la hora.
|
|
216
|
+
- **`value.hours` va de 0 a 23** aunque se muestre en 12h: 9:30 PM es `{ hours: 21, minutes: 30 }`.
|
|
217
|
+
- **Al abrir sin valor arranca en 9:00 AM**, no vacío.
|
|
218
|
+
- **Cerrar con clic afuera o Escape descarta** lo elegido, igual que Cancelar.
|
|
219
|
+
- Con `step` que no contiene el minuto guardado (21:37 con step 5), la columna de minutos no
|
|
220
|
+
marca ninguno.
|
|
221
|
+
- Cancelar / Aplicar solo crecen con `size="large"`: con `medium` en mobile siguen en 36. En el
|
|
222
|
+
teléfono va `large`.
|
|
223
|
+
- El panel flota con el overlay del DS (como `sui-select`): se da vuelta si no entra abajo.
|
|
224
|
+
- `sui-time-picker-status` dice «Disponible», «Ocupado», «En reunión» y «Desconectado» por
|
|
225
|
+
defecto (antes venían en inglés del Figma); `label` sigue sirviendo para cambiarlo.
|
|
226
|
+
- El atributo `title` del host se borra solo (no sale el tooltip gris del navegador).
|
|
227
|
+
|
|
228
|
+
## Ejemplo
|
|
229
|
+
|
|
230
|
+
```html
|
|
231
|
+
<!-- Horario del local: abre / cierra -->
|
|
232
|
+
<sui-time-picker label="Abre" format="12h" [step]="5" [durationPresets]="[]"
|
|
233
|
+
[value]="apertura()" (valueChange)="apertura.set($event)" />
|
|
234
|
+
<sui-time-picker label="Cierra" format="12h" [step]="5" [durationPresets]="[]"
|
|
235
|
+
[error]="cierraAntes()" errorMessage="Cierra antes de abrir"
|
|
236
|
+
[value]="cierre()" (valueChange)="cierre.set($event)" />
|
|
237
|
+
```
|