@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.
Files changed (97) hide show
  1. package/docs/INDEX.md +136 -0
  2. package/docs/sui-alert/README.md +229 -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 +229 -0
  7. package/docs/sui-banner/README.md +190 -0
  8. package/docs/sui-brand-logo/README.md +143 -0
  9. package/docs/sui-breadcrumb-item/README.md +96 -0
  10. package/docs/sui-breadcrumbs/README.md +122 -0
  11. package/docs/sui-button/README.md +209 -0
  12. package/docs/sui-button-group/README.md +178 -0
  13. package/docs/sui-button-group-item/README.md +102 -0
  14. package/docs/sui-card/README.md +168 -0
  15. package/docs/sui-chart-legend/README.md +140 -0
  16. package/docs/sui-chart-legend-dot/README.md +118 -0
  17. package/docs/sui-checkbox/README.md +147 -0
  18. package/docs/sui-checkbox-card/README.md +150 -0
  19. package/docs/sui-checkbox-label/README.md +143 -0
  20. package/docs/sui-circular-progress-bar/README.md +131 -0
  21. package/docs/sui-color-dot/README.md +112 -0
  22. package/docs/sui-color-picker/README.md +124 -0
  23. package/docs/sui-compact-avatar-group/README.md +103 -0
  24. package/docs/sui-compact-button/README.md +190 -0
  25. package/docs/sui-compact-select/README.md +127 -0
  26. package/docs/sui-compact-select-for-input/README.md +117 -0
  27. package/docs/sui-content-card/README.md +147 -0
  28. package/docs/sui-content-divider/README.md +124 -0
  29. package/docs/sui-content-label/README.md +148 -0
  30. package/docs/sui-counter-input/README.md +145 -0
  31. package/docs/sui-date-selector/README.md +98 -0
  32. package/docs/sui-datepicker/README.md +193 -0
  33. package/docs/sui-day-cell/README.md +118 -0
  34. package/docs/sui-day-label/README.md +81 -0
  35. package/docs/sui-divider/README.md +102 -0
  36. package/docs/sui-drawer-footer/README.md +127 -0
  37. package/docs/sui-drawer-header/README.md +140 -0
  38. package/docs/sui-emoji/README.md +138 -0
  39. package/docs/sui-empty-state/README.md +145 -0
  40. package/docs/sui-fancy-button/README.md +148 -0
  41. package/docs/sui-file-icon/README.md +103 -0
  42. package/docs/sui-file-upload/README.md +154 -0
  43. package/docs/sui-file-upload-card/README.md +129 -0
  44. package/docs/sui-flag/README.md +99 -0
  45. package/docs/sui-form-hint/README.md +124 -0
  46. package/docs/sui-form-label/README.md +148 -0
  47. package/docs/sui-icon/README.md +135 -0
  48. package/docs/sui-image-upload/README.md +200 -0
  49. package/docs/sui-image-upload-profile/README.md +145 -0
  50. package/docs/sui-inline-input/README.md +104 -0
  51. package/docs/sui-inline-select/README.md +101 -0
  52. package/docs/sui-input/README.md +244 -0
  53. package/docs/sui-integration-switch/README.md +142 -0
  54. package/docs/sui-kbd/README.md +97 -0
  55. package/docs/sui-key-icon/README.md +125 -0
  56. package/docs/sui-link-button/README.md +221 -0
  57. package/docs/sui-logo/README.md +152 -0
  58. package/docs/sui-notification/README.md +179 -0
  59. package/docs/sui-notification-footer/README.md +98 -0
  60. package/docs/sui-notification-tab-menu/README.md +112 -0
  61. package/docs/sui-otp-input/README.md +116 -0
  62. package/docs/sui-pagination/README.md +146 -0
  63. package/docs/sui-password-strength/README.md +99 -0
  64. package/docs/sui-payment-icon/README.md +98 -0
  65. package/docs/sui-period-range/README.md +93 -0
  66. package/docs/sui-popover/README.md +178 -0
  67. package/docs/sui-product-card/README.md +173 -0
  68. package/docs/sui-progress-bar/README.md +132 -0
  69. package/docs/sui-progress-bar-label/README.md +143 -0
  70. package/docs/sui-radio/README.md +134 -0
  71. package/docs/sui-radio-card/README.md +160 -0
  72. package/docs/sui-radio-label/README.md +137 -0
  73. package/docs/sui-rating/README.md +199 -0
  74. package/docs/sui-rich-editor/README.md +114 -0
  75. package/docs/sui-segmented-control/README.md +144 -0
  76. package/docs/sui-select/README.md +180 -0
  77. package/docs/sui-slider/README.md +143 -0
  78. package/docs/sui-social-button/README.md +158 -0
  79. package/docs/sui-status-badge/README.md +164 -0
  80. package/docs/sui-stepper/README.md +166 -0
  81. package/docs/sui-store-card/README.md +147 -0
  82. package/docs/sui-switch/README.md +146 -0
  83. package/docs/sui-switch-card/README.md +149 -0
  84. package/docs/sui-switch-label/README.md +139 -0
  85. package/docs/sui-tab-chips/README.md +185 -0
  86. package/docs/sui-tab-menu-horizontal/README.md +142 -0
  87. package/docs/sui-tab-menu-vertical/README.md +136 -0
  88. package/docs/sui-tag/README.md +145 -0
  89. package/docs/sui-tag-input/README.md +120 -0
  90. package/docs/sui-textarea/README.md +157 -0
  91. package/docs/sui-time-picker/README.md +237 -0
  92. package/docs/sui-toast/README.md +190 -0
  93. package/docs/sui-tooltip/README.md +201 -0
  94. package/fesm2022/ajuarezso-sui-design-system.mjs +466 -279
  95. package/fesm2022/ajuarezso-sui-design-system.mjs.map +1 -1
  96. package/package.json +1 -1
  97. 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
+ ```