@ajuarezso/sui-design-system 0.20.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 (59) hide show
  1. package/docs/INDEX.md +51 -51
  2. package/docs/sui-alert/README.md +13 -0
  3. package/docs/sui-badge/README.md +17 -0
  4. package/docs/sui-banner/README.md +190 -0
  5. package/docs/sui-breadcrumb-item/README.md +96 -0
  6. package/docs/sui-breadcrumbs/README.md +122 -0
  7. package/docs/sui-button/README.md +17 -0
  8. package/docs/sui-card/README.md +168 -0
  9. package/docs/sui-chart-legend/README.md +140 -0
  10. package/docs/sui-chart-legend-dot/README.md +118 -0
  11. package/docs/sui-checkbox-card/README.md +150 -0
  12. package/docs/sui-compact-select/README.md +127 -0
  13. package/docs/sui-compact-select-for-input/README.md +117 -0
  14. package/docs/sui-content-card/README.md +147 -0
  15. package/docs/sui-content-label/README.md +148 -0
  16. package/docs/sui-counter-input/README.md +145 -0
  17. package/docs/sui-date-selector/README.md +98 -0
  18. package/docs/sui-datepicker/README.md +193 -0
  19. package/docs/sui-day-cell/README.md +118 -0
  20. package/docs/sui-day-label/README.md +81 -0
  21. package/docs/sui-drawer-footer/README.md +127 -0
  22. package/docs/sui-drawer-header/README.md +140 -0
  23. package/docs/sui-empty-state/README.md +145 -0
  24. package/docs/sui-file-upload/README.md +154 -0
  25. package/docs/sui-file-upload-card/README.md +129 -0
  26. package/docs/sui-image-upload/README.md +200 -0
  27. package/docs/sui-image-upload-profile/README.md +145 -0
  28. package/docs/sui-inline-input/README.md +104 -0
  29. package/docs/sui-inline-select/README.md +101 -0
  30. package/docs/sui-input/README.md +244 -0
  31. package/docs/sui-integration-switch/README.md +142 -0
  32. package/docs/sui-notification/README.md +179 -0
  33. package/docs/sui-notification-footer/README.md +98 -0
  34. package/docs/sui-notification-tab-menu/README.md +112 -0
  35. package/docs/sui-otp-input/README.md +116 -0
  36. package/docs/sui-pagination/README.md +146 -0
  37. package/docs/sui-password-strength/README.md +99 -0
  38. package/docs/sui-period-range/README.md +93 -0
  39. package/docs/sui-popover/README.md +178 -0
  40. package/docs/sui-product-card/README.md +173 -0
  41. package/docs/sui-radio-card/README.md +160 -0
  42. package/docs/sui-rating/README.md +7 -6
  43. package/docs/sui-rich-editor/README.md +114 -0
  44. package/docs/sui-select/README.md +180 -0
  45. package/docs/sui-stepper/README.md +166 -0
  46. package/docs/sui-store-card/README.md +147 -0
  47. package/docs/sui-switch-card/README.md +149 -0
  48. package/docs/sui-tab-chips/README.md +185 -0
  49. package/docs/sui-tab-menu-horizontal/README.md +142 -0
  50. package/docs/sui-tab-menu-vertical/README.md +136 -0
  51. package/docs/sui-tag-input/README.md +120 -0
  52. package/docs/sui-textarea/README.md +157 -0
  53. package/docs/sui-time-picker/README.md +237 -0
  54. package/docs/sui-toast/README.md +190 -0
  55. package/docs/sui-tooltip/README.md +201 -0
  56. package/fesm2022/ajuarezso-sui-design-system.mjs +273 -231
  57. package/fesm2022/ajuarezso-sui-design-system.mjs.map +1 -1
  58. package/package.json +1 -1
  59. package/types/ajuarezso-sui-design-system.d.ts +208 -52
@@ -0,0 +1,154 @@
1
+ ---
2
+ name: sui-file-upload
3
+ selector: sui-file-upload
4
+ resumen: Área para arrastrar o elegir archivos que no son una foto (PDF, Excel, CSV)
5
+ alternativas: [sui-image-upload (una sola imagen con vista previa: logo, portada, comprobante), sui-image-upload-profile (foto de perfil con botones Subir/Cambiar/Eliminar), sui-file-upload-card (el archivo ya elegido, con su progreso)]
6
+ ---
7
+
8
+ # sui-file-upload
9
+
10
+ Story: [Molecules/Upload/File Upload](https://sui-design-system.web.app/?path=/docs/molecules-upload-file-upload--docs)
11
+
12
+ ## Cuándo usarlo / cuándo no
13
+
14
+ **Úsalo** para subir **documentos**: un PDF, un Excel, un CSV, o varios archivos a la vez. La
15
+ persona arrastra el archivo o toca para buscarlo.
16
+
17
+ **No lo uses** para:
18
+
19
+ - **Una imagen** (logo, portada, foto del producto, captura del Yape): `sui-image-upload`, que
20
+ muestra la vista previa y se integra con el recortador y el compresor de la app.
21
+ - Una foto de perfil con botones: `sui-image-upload-profile`.
22
+ - Mostrar el archivo que ya se eligió: va `sui-file-upload-card` debajo.
23
+
24
+ Regla corta: **foto → image-upload · documento o varios archivos → file-upload · el archivo ya
25
+ elegido → file-upload-card.**
26
+
27
+ ## Qué variante usar
28
+
29
+ > **Lo que dice la guía del DS** (página del file upload): es la forma de **arrastrar y soltar, o
30
+ > buscar en el equipo**, para subir un archivo. Su ejemplo arma el texto así: una línea que invita
31
+ > («Elige un archivo o arrástralo aquí»), una línea con **formatos y tamaño máximo** («JPEG, PNG,
32
+ > PDF y MP4, hasta 50 MB») y el botón **«Buscar archivo»**. Trae aparte el ícono por tipo de
33
+ > archivo (en SUI, `sui-file-icon`). No dice cuándo usar cada tamaño. El resto es **propuesta,
34
+ > falta aprobación**.
35
+ >
36
+ > Uso medido el 03-10-2026 en `master-pedidos/src/app`: **0 usos**. Hoy todo lo que se sube es
37
+ > imagen y va con `sui-image-upload` (13) o con un `<input type="file">` a mano (3: `renewal-modal`,
38
+ > `add-product` y la miniatura de `option-image`; ver el README de `sui-image-upload`).
39
+
40
+ ### Textos: `title`, `subtitle`, `browseLabel`
41
+
42
+ | input | cuándo | fuente |
43
+ |---|---|---|
44
+ | `title` | Qué se sube, en la voz de la persona: «Sube tu carta en PDF», «Arrastra tu Excel aquí». Si se omite, sale «Arrastra y suelta el archivo aquí». | guía (línea que invita) |
45
+ | `subtitle` | **Formatos y tamaño máximo**, siempre: «PDF o Excel, hasta 10 MB». El default «o haz clic para seleccionar» no dice nada que el área no diga. | guía (formatos y tamaño) |
46
+ | `browseLabel` | El default ya es «Buscar archivo»; se pasa solo para otro texto («Elegir foto»). | guía (el botón) · el texto es propuesta |
47
+
48
+ ### `accept`, `multiple`, `maxSize`
49
+
50
+ | input | cuándo | fuente |
51
+ |---|---|---|
52
+ | `accept` | Siempre, con lo mismo que dice el `subtitle`: `".pdf,.xlsx,.csv"`. Si el texto y el filtro no coinciden, la persona elige un archivo que se rechaza. | propuesta |
53
+ | `multiple` | Varios archivos del mismo tipo (las boletas del mes). Con `multiple` cada archivo va en su `sui-file-upload-card`. | propuesta |
54
+ | `maxSize` | Siempre, en **bytes** (`10 * 1024 * 1024`), y el mismo número en el `subtitle`. | propuesta |
55
+
56
+ ### `size`
57
+
58
+ | valor | padding | cuándo | fuente |
59
+ |---|---|---|---|
60
+ | `small` | 24 | En mobile y dentro de un modal o una card. | propuesta |
61
+ | `medium` (default) | 32 | En web, en una página o un modal ancho. | guía (es el único tamaño que muestra) |
62
+ | `large` | 40 | Cuando subir el archivo es lo único de la pantalla (importar la carta). | propuesta |
63
+ | `xlarge` | 48 | No usar: más aire no ayuda a soltar el archivo. | no usar |
64
+
65
+ ### Estados: `error`, `errorMessage`, `disabled`
66
+
67
+ | input | cuándo | fuente |
68
+ |---|---|---|
69
+ | `error` + `errorMessage` | El archivo se subió y el servidor lo rechazó («El PDF está protegido con clave»). Lo de tamaño y tipo ya lo avisa solo. | propuesta |
70
+ | `disabled` | Mientras se sube, o si ya se llegó al máximo de archivos. | propuesta |
71
+
72
+ Usos de hoy que rompen la regla: **0** (no hay file-upload).
73
+
74
+ ## Tamaño en mobile y en web
75
+
76
+ En mobile se toca con el dedo: lo que se toca mide **44 px o más**. Aquí **toda el área se toca**
77
+ (abre el selector), así que pasa en cualquier tamaño; en el celular no se arrastra, por eso va
78
+ más compacta. En pantallas de los dos, con el servicio de breakpoints de la app (`bp`, en
79
+ `services/breakpoints.service.ts`).
80
+
81
+ | uso | mobile | web | fuente |
82
+ |---|---|---|---|
83
+ | subir un documento en un formulario o modal | `small` | `medium` | propuesta |
84
+ | subir es lo único de la pantalla | `medium` | `large` | propuesta |
85
+
86
+ ```html
87
+ <sui-file-upload [size]="bp.isMobile() ? 'small' : 'medium'"
88
+ title="Sube tu carta en PDF" subtitle="PDF, hasta 10 MB"
89
+ accept=".pdf" [maxSize]="10 * 1024 * 1024" (filesSelected)="subir($event)" />
90
+ ```
91
+
92
+ Llena el ancho del contenedor (antes 400 fijo; extensión SUI, 03-10-2026). El botón «Buscar
93
+ archivo» mide 32 px (`xsmall`) en todos los tamaños: solo, en mobile **no
94
+ llegaría**; pasa porque el área entera también abre el selector.
95
+
96
+ ## «No usar»: por qué no hoy y cuándo sí
97
+
98
+ Nada se borra del DS. Lo marcado «no usar» no tiene caso en la app de hoy; si un flujo nuevo cae
99
+ en la columna de la derecha, se usa (aprobado por el dueño, 03-10-2026).
100
+
101
+ | qué | por qué no hoy | cuándo sí, en un flujo nuevo |
102
+ |---|---|---|
103
+ | todo el componente (0 usos) | todo lo que se sube hoy es una imagen | la carta del local en PDF o Excel para cargarla, la ficha RUC, los documentos del repartidor escaneados en PDF |
104
+ | `multiple` | no hay subidas de varios archivos | las boletas del mes de un gasto, varias fotos de un reclamo |
105
+ | `size="xlarge"` | más aire no ayuda | ninguno |
106
+ | para imágenes | no muestra la vista previa ni pasa por el recortador | — |
107
+
108
+ ## API
109
+
110
+ <!-- api:inicio -->
111
+ <!-- generada: no editar a mano — `npm run docs` -->
112
+
113
+ | input | tipo | default |
114
+ |---|---|---|
115
+ | `accept` | `string` | `''` |
116
+ | `multiple` | `boolean` | `false` |
117
+ | `maxSize` | `number` | `0` |
118
+ | `title` | `string \| null` | `null` |
119
+ | `subtitle` | `string \| null` | `null` |
120
+ | `browseLabel` | `string` | `'Buscar archivo'` |
121
+ | `disabled` | `boolean` | `false` |
122
+ | `error` | `boolean` | `false` |
123
+ | `errorMessage` | `string \| null` | `null` |
124
+ | `size` | `'small' \| 'medium' \| 'large' \| 'xlarge'` | `'medium'` |
125
+
126
+ | output | payload |
127
+ |---|---|
128
+ | `filesSelected` | `File[]` |
129
+ | `errorEvent` | `string` |
130
+ <!-- api:fin -->
131
+
132
+ ## Trampas
133
+
134
+ - `browseLabel` dice «Buscar archivo» por defecto (antes «Browse File»).
135
+ - **Llena el ancho del contenedor** desde el 03-10-2026 (antes medía 400 fijo y había que forzar
136
+ `style="width: 100%"`). Las pantallas que lo dejaban en 400 en un contenedor ancho ahora se
137
+ estiran; para un ancho fijo, se lo pone el consumer al host.
138
+ - **`maxSize` va en bytes**, no en MB. `0` = sin límite.
139
+ - **Emite solo los archivos válidos**: si de tres uno pesa de más, `filesSelected` trae dos y
140
+ `errorEvent` avisa del tercero (con el texto ya armado: «"x.pdf" supera el tamaño máximo (10.0 MB)»).
141
+ - Sin `multiple`, aunque se suelten varios, **emite solo el primero**.
142
+ - `accept` filtra al elegir **y** al soltar, por extensión (`.pdf`) o por tipo (`image/*`).
143
+ - El error interno queda pegado hasta el próximo intento válido; `errorMessage` gana sobre él.
144
+ - **No sube nada**: entrega los `File` y la subida (y el progreso en `sui-file-upload-card`) la
145
+ hace quien lo usa.
146
+ - El atributo `title` del host se borra solo (no sale el tooltip gris del navegador).
147
+
148
+ ## Ejemplo
149
+
150
+ ```html
151
+ <sui-file-upload title="Sube tu carta en PDF o Excel" subtitle="PDF o Excel, hasta 10 MB"
152
+ browseLabel="Buscar archivo" accept=".pdf,.xlsx" [maxSize]="10 * 1024 * 1024"
153
+ (filesSelected)="subirCarta($event)" (errorEvent)="avisar($event)" />
154
+ ```
@@ -0,0 +1,129 @@
1
+ ---
2
+ name: sui-file-upload-card
3
+ selector: sui-file-upload-card
4
+ resumen: La fila de un archivo que se está subiendo, con su progreso, listo o error para reintentar
5
+ alternativas: [sui-file-upload (el área para elegir el archivo), sui-image-upload (una imagen: muestra la vista previa en vez de la fila), sui-file-icon (un archivo en una lista, sin subida)]
6
+ ---
7
+
8
+ # sui-file-upload-card
9
+
10
+ Story: [Molecules/Upload/File Upload Card](https://sui-design-system.web.app/?path=/docs/molecules-upload-file-upload-card--docs)
11
+
12
+ ## Cuándo usarlo / cuándo no
13
+
14
+ **Úsalo** debajo de un `sui-file-upload`, una fila por archivo, para mostrar **cómo va la subida**:
15
+ subiendo con su barra, listo, o falló con «Reintentar».
16
+
17
+ **No lo uses** para:
18
+
19
+ - Una imagen: `sui-image-upload` muestra la vista previa, que dice más que un nombre de archivo.
20
+ - Una lista de archivos ya guardados (sin subida): `sui-file-icon` en una fila normal.
21
+ - Un progreso que no es de un archivo: `sui-progress-bar`.
22
+
23
+ ## Qué variante usar
24
+
25
+ > **La guía del DS no tiene página para este componente** (su página del file upload solo muestra
26
+ > el área y el ícono por tipo). Todo es **propuesta, falta aprobación**.
27
+ >
28
+ > Uso medido el 03-10-2026 en `master-pedidos/src/app`: **0 usos**. Las subidas de hoy son de
29
+ > imágenes y avisan con el `subtitle` del image-upload («Procesando…») o lo deshabilitan.
30
+
31
+ ### `state`
32
+
33
+ | valor | cuándo | qué muestra | fuente |
34
+ |---|---|---|---|
35
+ | `in-progress` (default) | Mientras sube. Va con `progress`. | barra azul, ícono de carga, X para cancelar | propuesta |
36
+ | `success` | Subió. | check verde, tacho para quitar | propuesta |
37
+ | `error` | Falló. Va con `statusText` que diga por qué. | ícono rojo, «Reintentar», tacho | propuesta |
38
+
39
+ ### Textos y datos
40
+
41
+ | input | cuándo | fuente |
42
+ |---|---|---|
43
+ | `file` | El nombre real del archivo (`'carta-octubre.pdf'`). La extensión da el ícono y el color. | propuesta |
44
+ | `description` | **Siempre** se pasa: el default es un texto de ejemplo («0 KB de 120 KB»). Subiendo: «1,2 MB de 3,4 MB»; listo: «3,4 MB». | propuesta |
45
+ | `progress` | 0–100, solo en `in-progress`. | propuesta |
46
+ | `statusText` | Los defaults ya están en español («Subiendo…», «Completado», «Falló»); en error, mejor el motivo: «Se cortó la conexión». | propuesta |
47
+ | `showStatus=false` | Solo en `success`, cuando el check sobra porque la lista ya dice «Archivos subidos». | propuesta |
48
+ | `retryLabel` | El default ya es «Reintentar». | propuesta |
49
+
50
+ Usos de hoy que rompen la regla: **0**.
51
+
52
+ ## Tamaño en mobile y en web
53
+
54
+ **No tiene `size`.** Llena el ancho del contenedor (antes 400 fijo) y la X o el tacho de la derecha
55
+ es un `sui-compact-button` `2xlarge` de **32 px** (Figma: 24), que con el aire de la tarjeta (16 de
56
+ padding, 72 de alto mínimo) se toca bien con el dedo. «Reintentar» sigue siendo un link de 20 px de
57
+ alto: en mobile conviene que la fila entera también reintente.
58
+
59
+ | pieza | mobile | web | fuente |
60
+ |---|---|---|---|
61
+ | ancho | el del contenedor | el del contenedor | canon (aprobado por el dueño, 03-10-2026), extensión SUI |
62
+ | X / tacho | 32 px | 32 px | canon (aprobado por el dueño, 03-10-2026), extensión SUI |
63
+ | «Reintentar» | link de 20 px — **no llega** | link | medido |
64
+
65
+ ```html
66
+ <sui-file-upload-card [file]="a.nombre" [state]="a.estado" [progress]="a.pct"
67
+ [description]="a.peso" (actionClicked)="quitar(a)" (retryClicked)="reintentar(a)" />
68
+ ```
69
+
70
+ ## «No usar»: por qué no hoy y cuándo sí
71
+
72
+ Nada se borra del DS. Lo marcado «no usar» no tiene caso en la app de hoy; si un flujo nuevo cae
73
+ en la columna de la derecha, se usa (aprobado por el dueño, 03-10-2026).
74
+
75
+ | qué | por qué no hoy | cuándo sí, en un flujo nuevo |
76
+ |---|---|---|
77
+ | todo el componente (0 usos) | todo lo que se sube es una imagen y se ve con su vista previa | junto a `sui-file-upload`: la carta en PDF, varias boletas de un gasto, documentos del repartidor |
78
+ | para imágenes | la vista previa dice más que el nombre del archivo | — |
79
+
80
+ ## API
81
+
82
+ <!-- api:inicio -->
83
+ <!-- generada: no editar a mano — `npm run docs` -->
84
+
85
+ | input | tipo | default |
86
+ |---|---|---|
87
+ | `file` | `SuiFileUploadCardFile \| string` | `'file.pdf'` |
88
+ | `description` | `string` | `'0 KB de 120 KB'` |
89
+ | `state` | `'in-progress' \| 'success' \| 'error'` | `'in-progress'` |
90
+ | `progress` | `number` | `10` |
91
+ | `statusText` | `string \| null` | `null` |
92
+ | `showStatus` | `boolean` | `true` |
93
+ | `cancelLabel` | `string` | `'Cancelar subida'` |
94
+ | `removeLabel` | `string` | `'Quitar archivo'` |
95
+ | `progressLabel` | `string` | `'Progreso de la subida'` |
96
+ | `retryLabel` | `string` | `'Reintentar'` |
97
+
98
+ | output | payload |
99
+ |---|---|
100
+ | `actionClicked` | `void` |
101
+ | `retryClicked` | `void` |
102
+ <!-- api:fin -->
103
+
104
+ ```ts
105
+ interface SuiFileUploadCardFile { filename: string; extension?: string; }
106
+ ```
107
+
108
+ ## Trampas
109
+
110
+ - Los textos de fábrica están en español desde el 03-10-2026: «0 KB de 120 KB», «Subiendo…»,
111
+ «Completado», «Falló», «Reintentar», y para el lector de pantalla «Cancelar subida», «Quitar
112
+ archivo» y «Progreso de la subida» (estos tres, con `cancelLabel`, `removeLabel` y
113
+ `progressLabel`).
114
+ - Llena el ancho del contenedor, igual que `sui-file-upload` (antes 400 fijo).
115
+ - **`progress` arranca en 10**, no en 0.
116
+ - **`actionClicked` significa dos cosas**: cancelar mientras sube y quitar cuando terminó. Quien lo
117
+ usa mira `state` para saber cuál.
118
+ - La barra de progreso es azul, no naranja.
119
+ - No sube nada: refleja el estado que le pasan.
120
+
121
+ ## Ejemplo
122
+
123
+ ```html
124
+ <sui-file-upload-card file="carta-octubre.pdf" state="in-progress" [progress]="45"
125
+ description="1,2 MB de 2,6 MB" statusText="Subiendo…" />
126
+ <sui-file-upload-card file="carta-octubre.pdf" state="error" description="2,6 MB"
127
+ statusText="Se cortó la conexión" retryLabel="Reintentar"
128
+ (retryClicked)="reintentar()" (actionClicked)="quitar()" />
129
+ ```
@@ -0,0 +1,200 @@
1
+ ---
2
+ name: sui-image-upload
3
+ selector: sui-image-upload
4
+ resumen: Subir una imagen y ver su vista previa en el mismo lugar (logo, portada, foto de producto, comprobante)
5
+ alternativas: [sui-image-upload-profile (foto de perfil con botones Subir/Cambiar/Eliminar al lado), sui-file-upload (documentos que no son foto: PDF, Excel), app-image-cropper (el recortador de la app, que se abre desde el lápiz)]
6
+ ---
7
+
8
+ # sui-image-upload
9
+
10
+ Story: [Molecules/Upload/Image Upload](https://sui-design-system.web.app/?path=/docs/molecules-upload-image-upload--docs)
11
+
12
+ ## Cuándo usarlo / cuándo no
13
+
14
+ **Úsalo** para subir **una imagen** y que la persona la vea apenas la elige: el logo y la portada
15
+ del local, la foto de un producto, la foto de perfil, la captura del Yape o del comprobante, el QR
16
+ de cobro del repartidor, el banner de una campaña.
17
+
18
+ **No lo uses** para:
19
+
20
+ - Documentos que no son foto (PDF, Excel): `sui-file-upload`.
21
+ - Varias imágenes a la vez: no lo soporta; una por campo.
22
+ - La miniatura de una opción de producto (36 px dentro de una fila): `option-image` de la app.
23
+
24
+ El archivo que entrega **no se sube tal cual**: pasa por `ImageCompressorService` (HEIC → JPEG,
25
+ 1200 px + miniatura de 400) y, si hay que encuadrarlo, por `app-image-cropper` antes. Ver Trampas.
26
+
27
+ ## Qué variante usar
28
+
29
+ > **La guía del DS no tiene página para este componente** (el «Image Upload» del Figma de origen
30
+ > es otra cosa: la foto con botones, que en SUI es `sui-image-upload-profile`). Todo es
31
+ > **propuesta, falta aprobación**.
32
+ >
33
+ > Uso medido el 03-10-2026 en `master-pedidos/src/app` (sin prototipos ni vitrinas): **13 usos en
34
+ > 10 pantallas**. `shape`: 11 `rect`, 2 `circle`. `size`: 7 `xl`, 3 `lg`, 3 sin size (fluidos).
35
+ > `fluid`: 6. `editable`: 3 `true`, 2 según haya imagen, 1 `false`, 7 sin poner.
36
+
37
+ ### `shape` — según cómo se va a ver la imagen después
38
+
39
+ | valor | cuándo | fuente |
40
+ |---|---|---|
41
+ | `circle` (default) | **Solo la foto de una persona** (perfil del cliente, del repartidor, del usuario del local). Así se ve después en `sui-avatar`. | propuesta |
42
+ | `rect` | **Todo lo demás**: el logo del local (en el home se ve como cuadrado redondeado), la portada, la foto del producto, comprobantes, QR, banners. | propuesta |
43
+
44
+ ### `size` y `fluid` — según la forma de la imagen
45
+
46
+ | valor | px | cuándo | fuente |
47
+ |---|---|---|---|
48
+ | `fluid` | ancho del contenedor, alto hasta 320 | **Capturas y documentos** (comprobante, Yape, QR) y **imágenes apaisadas** (portada 16:9, banner). Una captura de celular es vertical: en un cuadrado se recorta. | propuesta |
49
+ | `xl` | 200 | **Imágenes cuadradas**: logo, foto del producto, foto de perfil. | propuesta |
50
+ | `lg` (default) | 160 | Imagen cuadrada al lado de otra cosa en la misma fila (el logo junto al nombre del local en web). | propuesta |
51
+ | `md` | 120 | No usar: chico para ver si la foto quedó bien. | no usar |
52
+ | `sm` | 80 | No usar: no entra el texto (el subtítulo se esconde). | no usar |
53
+
54
+ ### `editable` — qué hace el lápiz
55
+
56
+ | valor | cuándo | fuente |
57
+ |---|---|---|
58
+ | `true` | **Siempre.** Con imagen muestra dos botones a la vista: lápiz y tacho. El lápiz emite `editRequested`: si la pantalla tiene recortador, lo abre (`app-image-cropper`); si no, llama a `abrirSelector()` para elegir otra (como el Yape del repartidor). | propuesta |
59
+ | `false` (default) | No usar: con imagen, el lápiz aparece **solo al pasar el mouse** (en el celular no se ve) y el «Quitar» de abajo es un botón aparte (32 px). | no usar |
60
+
61
+ ### Textos: `title`, `subtitle`
62
+
63
+ | input | cuándo | fuente |
64
+ |---|---|---|
65
+ | `title` | Verbo + qué: «Subir logo», «Subir comprobante», «Subir captura del pago». | propuesta |
66
+ | `subtitle` | Formatos y tamaño, **solo los que el componente acepta** (JPG, PNG o WebP), o el encuadre que se va a aplicar («se recorta a 16:9»). Mientras procesa: «Procesando…». | propuesta |
67
+
68
+ ### `maxSize`, `disabled`, `src`
69
+
70
+ | input | cuándo | fuente |
71
+ |---|---|---|
72
+ | `maxSize` | Siempre, en **bytes**. 10 MB para capturas y comprobantes, 20–50 MB para fotos que se van a comprimir. | propuesta |
73
+ | `disabled` | Mientras se comprime o se sube (`subiendo()`), para que no se elija otra encima. | propuesta |
74
+ | `src` (model) | La URL guardada, o la vista previa local mientras se sube. Con `[(src)]` el componente la pone solo al elegir. | propuesta |
75
+
76
+ ### Usos de hoy que rompen la propuesta
77
+
78
+ - **5 subtítulos prometen HEIC**: hasta el 03-10-2026 el componente lo rechazaba; con la versión
79
+ nueva del DS ya pasa (ver Trampas por la vista previa en Chrome):
80
+ `components/plan-change-modal/plan-change-modal.html:150`, `pages/driver/driver-wallet/driver-wallet.html:243`,
81
+ `pages/promociones/promociones.html:185` y `:269`, `pages/customer/order-tracking/order-tracking.html:985`.
82
+ - **1 logo en círculo** → `rect`: `pages/business-onboarding/step-info/step-info.html:42` (en
83
+ `mi-negocio.html:518` el mismo logo es `rect`, y en el home del cliente se ve cuadrado).
84
+ - **3 imágenes en cuadrado que deberían ir `fluid`**: comprobantes en
85
+ `components/plan-change-modal/plan-change-modal.html:150` y `pages/central/central-gastos/central-gastos.html:118`;
86
+ la portada 16:9 en `pages/mi-negocio/mi-negocio.html:572` (en el onboarding ya va `fluid`).
87
+ - **8 sin `editable`**: `components/avatar-editor/avatar-editor.ts:33` (pone `false` aunque tiene
88
+ recortador), `pages/customer/order-tracking/order-tracking.html:985` (cliente, solo celular),
89
+ `components/plan-change-modal/plan-change-modal.html:150`, `pages/central/central-gastos/central-gastos.html:118`,
90
+ `pages/promociones/promociones.html:185` y `:269`, y `pages/business-onboarding/step-info/step-info.html:42` y `:56`.
91
+ - **2 áreas de subida armadas a mano** que van con este componente (`rect`, `fluid`, `editable`):
92
+ `components/renewal-modal/renewal-modal.html:64` (comprobante, con SVG propio) y
93
+ `pages/products/add-product/add-product.html:223` (foto del producto, con dropzone propia).
94
+ No cuenta `components/option-image/option-image.ts:68`: es una miniatura de 36 px en una fila.
95
+
96
+ ## Tamaño en mobile y en web
97
+
98
+ En mobile se toca con el dedo: lo que se toca mide **44 px o más**. El área vacía se toca entera
99
+ (80 a 200 px), así que pasa; los botones con imagen, no. En pantallas de los dos se elige con el
100
+ servicio de breakpoints de la app (`bp`, en `services/breakpoints.service.ts`).
101
+
102
+ | uso | mobile | web | fuente |
103
+ |---|---|---|---|
104
+ | comprobante, captura, QR, portada, banner | `fluid` | `fluid` (en una columna de ≤ 480 px) | propuesta |
105
+ | logo, foto de producto, foto de perfil | `xl` (200) | `xl` (200), o `lg` (160) al lado del nombre | propuesta |
106
+ | lápiz y tacho (`editable`) | 40 px (32 en `sm`) | 40 px | canon (aprobado por el dueño, 03-10-2026), extensión SUI |
107
+ | «Quitar» (sin `editable`) | 32 px (`sui-button` `xsmall`) | 32 px | canon (aprobado por el dueño, 03-10-2026) |
108
+
109
+ ```html
110
+ <sui-image-upload shape="rect" [size]="bp.isMobile() ? 'xl' : 'lg'" [editable]="true"
111
+ [src]="logo()" [maxSize]="50 * 1024 * 1024" title="Subir logo"
112
+ subtitle="JPG, PNG o WebP — máx 50 MB"
113
+ (imageSelected)="alElegirLogo($event)" (editRequested)="abrirRecortador()"
114
+ (removed)="quitarLogo()" />
115
+ ```
116
+
117
+ Desde el 03-10-2026 el lápiz y el tacho miden **40** (antes 34; en `sm`, de 80 de lado, no entran dos
118
+ de 40 y quedan en 32) y el «Quitar» es un `sui-button` `xsmall` de **32** (antes 28). En `circle`
119
+ los dos botones van centrados abajo, dentro del círculo.
120
+
121
+ ## «No usar»: por qué no hoy y cuándo sí
122
+
123
+ Nada se borra del DS. Lo marcado «no usar» no tiene caso en la app de hoy; si un flujo nuevo cae
124
+ en la columna de la derecha, se usa (aprobado por el dueño, 03-10-2026).
125
+
126
+ | qué | por qué no hoy | cuándo sí, en un flujo nuevo |
127
+ |---|---|---|
128
+ | `size="md"` (120) | chico para ver si la foto quedó bien | una grilla de fotos extra del producto (galería) |
129
+ | `size="sm"` (80) | no entra el texto | una fila de varias miniaturas con su «+» |
130
+ | `editable=false` | el lápiz solo aparece con el mouse; en el celular no se ve | una pantalla solo de web donde reemplazar al pasar el mouse sea lo esperado |
131
+ | `shape="circle"` para algo que no es una persona | el logo y los productos se ven cuadrados | — |
132
+ | para PDF | no muestra vista previa de documentos | — (va `sui-file-upload`) |
133
+
134
+ ## API
135
+
136
+ <!-- api:inicio -->
137
+ <!-- generada: no editar a mano — `npm run docs` -->
138
+
139
+ | input | tipo | default |
140
+ |---|---|---|
141
+ | `fluid` | `boolean` | `false` |
142
+ | `shape` | `'circle' \| 'rect'` | `'circle'` |
143
+ | `size` | `'sm' \| 'md' \| 'lg' \| 'xl'` | `'lg'` |
144
+ | `src` · model, `[(src)]` | `string \| null` | `null` |
145
+ | `maxSize` | `number` | `0` |
146
+ | `title` | `string \| null` | `null` |
147
+ | `subtitle` | `string \| null` | `null` |
148
+ | `disabled` | `boolean` | `false` |
149
+ | `editable` | `boolean` | `false` |
150
+
151
+ | output | payload |
152
+ |---|---|
153
+ | `imageSelected` | `File` |
154
+ | `removed` | `void` |
155
+ | `editRequested` | `void` |
156
+ | `errorEvent` | `string` |
157
+ <!-- api:fin -->
158
+
159
+ Método público: `abrirSelector()` — abre el selector de archivo (para el lápiz cuando no hay
160
+ recortador; se llama con una referencia: `#qr` → `qr.abrirSelector()`).
161
+
162
+ ## Trampas
163
+
164
+ - **Acepta HEIC/HEIF** desde el 03-10-2026 (por tipo y, como Chrome suele dejar el tipo vacío, por
165
+ extensión). **La vista previa de un HEIC no se ve en Chrome** (solo Safari lo dibuja): el
166
+ componente pone el archivo crudo como `src`, así que en Android o en la compu queda en blanco
167
+ hasta que la pantalla lo pasa por `ImageCompressorService` y pone el JPG en `src`.
168
+ - El mensaje de tipo no soportado dice «Tipo no soportado. Usa JPG, PNG, WebP, GIF o HEIC.»
169
+ (antes en voseo).
170
+ - **El default de `shape` es `circle`** y el de `size` es `lg`: para un logo o un comprobante hay
171
+ que pasar los dos.
172
+ - **Con `fluid`, `size` no hace nada** (`promociones.html:185` pasa `size="lg"` y `fluid` a la vez).
173
+ - **`removed` ya vacía `src`** por dentro; quien lo usa solo limpia su `File` y su estado.
174
+ - **Al elegir, pone la vista previa sola** (`src` = data URL) y emite el `File` original, sin
175
+ comprimir ni recortar. Comprimir y recortar es trabajo de la pantalla.
176
+ - Con imagen y sin `editable`, **el componente mide el cuadrado + 8 + 32** (el «Quitar» queda
177
+ dentro; antes se salía y tapaba lo de abajo). Al reservar espacio en un layout, contar ese alto.
178
+ - Con `circle` + `editable` los botones van centrados abajo, sobre la foto (antes, en la esquina, el
179
+ círculo recortaba el tacho).
180
+ - Con `src` puesto, tocar el área vacía no hace nada: para elegir otra va el lápiz o `abrirSelector()`.
181
+ - El `alt` de la vista previa dice «Vista previa».
182
+ - El atributo `title` del host se borra solo (no sale el tooltip gris del navegador).
183
+ - Ya trae el arreglo de iOS para el `:active` pegado.
184
+
185
+ ## Ejemplo
186
+
187
+ ```html
188
+ <!-- Comprobante: ancho completo, el lápiz elige otra captura -->
189
+ <sui-image-upload #comprobante shape="rect" [fluid]="true" [editable]="true"
190
+ [src]="vistaPrevia()" [maxSize]="10 * 1024 * 1024"
191
+ title="Subir comprobante" subtitle="Captura del Yape o del banco — máx 10 MB"
192
+ (imageSelected)="alElegir($event)" (editRequested)="comprobante.abrirSelector()"
193
+ (removed)="quitar()" />
194
+
195
+ <!-- Foto del producto: cuadrada, el lápiz abre el recortador -->
196
+ <sui-image-upload shape="rect" size="xl" [editable]="true" [src]="foto()"
197
+ title="Subir foto" subtitle="JPG, PNG o WebP — se comprime sola"
198
+ (imageSelected)="alElegir($event)" (editRequested)="abrirRecortador()"
199
+ (removed)="quitarFoto()" />
200
+ ```
@@ -0,0 +1,145 @@
1
+ ---
2
+ name: sui-image-upload-profile
3
+ selector: sui-image-upload-profile
4
+ resumen: Foto de perfil o logo en círculo con los botones Subir, o Eliminar y Cambiar, al lado
5
+ alternativas: [sui-image-upload (la imagen grande con su vista previa y el lápiz que abre el recortador; es lo que usa hoy la app), sui-avatar (solo mostrar la foto)]
6
+ ---
7
+
8
+ # sui-image-upload-profile
9
+
10
+ Story: [Molecules/Upload/Image Upload Profile](https://sui-design-system.web.app/?path=/docs/molecules-upload-image-upload-profile--docs)
11
+
12
+ ## Cuándo usarlo / cuándo no
13
+
14
+ **Úsalo** en una **pantalla de ajustes** donde la foto es un dato más de la cuenta: un círculo
15
+ chico (56–64 px) con su texto y los botones al lado, en una fila de un formulario.
16
+
17
+ **No lo uses** para:
18
+
19
+ - **La foto de perfil de la app hoy**: `app-avatar-editor` usa `sui-image-upload` `circle` `xl`
20
+ con el recortador, en las tres pantallas de perfil (cliente, repartidor, local). Una foto de
21
+ 200 px que se ve antes de guardar es mejor para quien se sube su cara por primera vez.
22
+ - El logo del local: en la app el logo se ve **cuadrado** (home del cliente) y este componente lo
23
+ muestra en círculo.
24
+ - Comprobantes, portadas o fotos de productos: `sui-image-upload`.
25
+ - Solo mostrar la foto: `sui-avatar`.
26
+
27
+ ## Qué variante usar
28
+
29
+ > **La guía del DS no tiene página para este componente** (existe en el Figma de origen, sin
30
+ > texto de uso). Todo es **propuesta, falta aprobación**.
31
+ >
32
+ > Uso medido el 03-10-2026 en `master-pedidos/src/app`: **0 usos**. La foto de perfil va con
33
+ > `app-avatar-editor` (3 pantallas).
34
+
35
+ ### `type`
36
+
37
+ | valor | cuándo | fuente |
38
+ |---|---|---|
39
+ | `avatar` (default) | La foto de una persona. Vacío muestra una silueta. | propuesta |
40
+ | `company` | Un negocio. Vacío muestra edificios. Ojo: lo muestra en círculo. | propuesta |
41
+
42
+ ### `alignment`
43
+
44
+ | valor | cuándo | fuente |
45
+ |---|---|---|
46
+ | `vertical` (default) | Con texto: título y requisitos («Mín. 400 × 400 px, PNG o JPEG»), avatar de 64. | propuesta |
47
+ | `horizontal` | Sin texto, solo los botones al lado del avatar de 56. Cuando el label ya está arriba (`sui-form-label`). | propuesta |
48
+
49
+ ### Textos y estados
50
+
51
+ | input | cuándo | fuente |
52
+ |---|---|---|
53
+ | `title` | Qué es: «Tu foto», «Foto de perfil». Solo se ve en `vertical`. | propuesta |
54
+ | `subtitle` | Requisitos reales: formatos que se aceptan y para qué se usa («La ve el cliente cuando llevas su pedido»). | propuesta |
55
+ | `labels` | Ya vienen en español con tuteo (Subir, Eliminar, Cambiar). Cambiarlos solo si el contexto pide otro verbo («Quitar»). | propuesta |
56
+ | `imageUrl` | La URL guardada. Con imagen muestra Eliminar + Cambiar. | propuesta |
57
+ | `companyLogoSrc` | Solo con `type="company"`: el logo, que gana sobre `imageUrl`. | propuesta |
58
+ | `loading` | **Siempre** mientras se comprime y se sube: deshabilita los botones y atenúa la foto. | propuesta |
59
+ | `disabled` | La persona no puede cambiarla (cuenta verificada con foto bloqueada). | propuesta |
60
+
61
+ Usos de hoy que rompen la regla: **0**.
62
+
63
+ ## Tamaño en mobile y en web
64
+
65
+ **No tiene `size`.** El avatar mide 64 (vertical) o 56 (horizontal) y los botones son `sui-button`
66
+ `xsmall` de **32 px**: en mobile **no llegan** a los 44 px.
67
+
68
+ | uso | mobile | web | fuente |
69
+ |---|---|---|---|
70
+ | foto en una pantalla de ajustes | — **no hay tamaño**: botones de 32 | `vertical`, botones de 32 | medido |
71
+
72
+ ```html
73
+ <!-- Solo web, hasta que tenga size -->
74
+ <sui-image-upload-profile title="Foto de perfil" subtitle="JPG o PNG, mín. 400 × 400 px"
75
+ [imageUrl]="foto()" [loading]="subiendo()"
76
+ (upload)="input.click()" (change)="input.click()" (remove)="quitar()" />
77
+ <input #input type="file" accept="image/jpeg,image/png,image/webp" hidden (change)="alElegir($event)" />
78
+ ```
79
+
80
+ **Sin tamaño suficiente para mobile.** Hace falta en el DS un `size` que lleve los botones a
81
+ `large` (48 px). Mientras tanto, en el celular va `sui-image-upload` `circle` `xl`.
82
+
83
+ ## «No usar»: por qué no hoy y cuándo sí
84
+
85
+ Nada se borra del DS. Lo marcado «no usar» no tiene caso en la app de hoy; si un flujo nuevo cae
86
+ en la columna de la derecha, se usa (aprobado por el dueño, 03-10-2026).
87
+
88
+ | qué | por qué no hoy | cuándo sí, en un flujo nuevo |
89
+ |---|---|---|
90
+ | todo el componente (0 usos) | la foto de perfil va con `app-avatar-editor` (grande, con recortador) | ajustes del equipo del local en web (foto de cada usuario en una fila) |
91
+ | `type="company"` | el logo del local se ve cuadrado, no en círculo | si el DS le suma la forma cuadrada |
92
+ | `alignment="horizontal"` | sin caso | una fila de una tabla de usuarios del local, con el nombre en la columna de al lado |
93
+
94
+ ## API
95
+
96
+ <!-- api:inicio -->
97
+ <!-- generada: no editar a mano — `npm run docs` -->
98
+
99
+ | input | tipo | default |
100
+ |---|---|---|
101
+ | `imageUrl` | `string \| null` | `null` |
102
+ | `companyLogoSrc` | `string \| null` | `null` |
103
+ | `title` | `string` | `'Subir imagen'` |
104
+ | `subtitle` | `string` | `'Mín. 400x400px, PNG o JPEG'` |
105
+ | `alignment` | `'vertical' \| 'horizontal'` | `'vertical'` |
106
+ | `type` | `'avatar' \| 'company'` | `'avatar'` |
107
+ | `labels` | `SuiImageUploadProfileLabels` | `{}` |
108
+ | `disabled` | `boolean` | `false` |
109
+ | `loading` | `boolean` | `false` |
110
+
111
+ | output | payload |
112
+ |---|---|
113
+ | `upload` | `void` |
114
+ | `remove` | `void` |
115
+ | `change` | `void` |
116
+ <!-- api:fin -->
117
+
118
+ Es `ControlValueAccessor` de la URL (`string | null`); `remove` la deja en `null`.
119
+
120
+ ```ts
121
+ interface SuiImageUploadProfileLabels { upload?: string; remove?: string; change?: string; }
122
+ ```
123
+
124
+ ## Trampas
125
+
126
+ - **No abre el selector de archivos**: no tiene `<input type="file">`. «Subir» y «Cambiar» solo
127
+ emiten `upload` y `change`; quien lo usa pone su propio input (y luego el compresor y el
128
+ recortador).
129
+ - **El avatar siempre es gris** (`color="gray"` fijo): no sigue el color por rol aprobado (local
130
+ naranja, repartidor verde, cliente azul) para el estado vacío.
131
+ - **El orden de los botones lo fija el componente**: Eliminar (rojo) a la izquierda, Cambiar a la
132
+ derecha. No sigue el orden del footer (secundario a la izquierda, principal a la derecha).
133
+ - `type="company"` es un círculo igual que el avatar.
134
+ - En `horizontal` no se ve el texto aunque se pase `title`.
135
+ - `loading` también deshabilita; no muestra spinner.
136
+ - El atributo `title` del host se borra solo (no sale el tooltip gris del navegador); el `title`
137
+ sí se usa como `alt` de la foto.
138
+
139
+ ## Ejemplo
140
+
141
+ ```html
142
+ <sui-image-upload-profile title="Foto de perfil" subtitle="La ve el cliente cuando llevas su pedido"
143
+ [imageUrl]="foto()" [loading]="subiendo()"
144
+ (upload)="elegir()" (change)="elegir()" (remove)="quitar()" />
145
+ ```