@ajuarezso/sui-design-system 0.20.0 → 0.22.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (85) hide show
  1. package/docs/INDEX.md +75 -80
  2. package/docs/sui-accordion/README.md +171 -0
  3. package/docs/sui-activity-feed/README.md +155 -0
  4. package/docs/sui-activity-filter/README.md +100 -0
  5. package/docs/sui-alert/README.md +13 -0
  6. package/docs/sui-badge/README.md +17 -0
  7. package/docs/sui-banner/README.md +190 -0
  8. package/docs/sui-breadcrumb-item/README.md +96 -0
  9. package/docs/sui-breadcrumbs/README.md +122 -0
  10. package/docs/sui-button/README.md +36 -0
  11. package/docs/sui-card/README.md +168 -0
  12. package/docs/sui-chart-legend/README.md +140 -0
  13. package/docs/sui-chart-legend-dot/README.md +118 -0
  14. package/docs/sui-checkbox-card/README.md +150 -0
  15. package/docs/sui-command-menu-footer/README.md +100 -0
  16. package/docs/sui-command-menu-item/README.md +140 -0
  17. package/docs/sui-command-menu-search/README.md +105 -0
  18. package/docs/sui-compact-select/README.md +130 -0
  19. package/docs/sui-compact-select-for-input/README.md +120 -0
  20. package/docs/sui-content-card/README.md +147 -0
  21. package/docs/sui-content-label/README.md +148 -0
  22. package/docs/sui-counter-input/README.md +148 -0
  23. package/docs/sui-date-selector/README.md +98 -0
  24. package/docs/sui-datepicker/README.md +197 -0
  25. package/docs/sui-day-cell/README.md +118 -0
  26. package/docs/sui-day-label/README.md +81 -0
  27. package/docs/sui-drawer-footer/README.md +127 -0
  28. package/docs/sui-drawer-header/README.md +140 -0
  29. package/docs/sui-dropdown-caption/README.md +92 -0
  30. package/docs/sui-dropdown-item/README.md +163 -0
  31. package/docs/sui-dropdown-menu/README.md +196 -0
  32. package/docs/sui-dropdown-search/README.md +113 -0
  33. package/docs/sui-empty-state/README.md +145 -0
  34. package/docs/sui-file-upload/README.md +154 -0
  35. package/docs/sui-file-upload-card/README.md +129 -0
  36. package/docs/sui-filter/README.md +181 -0
  37. package/docs/sui-href/README.md +85 -0
  38. package/docs/sui-image-upload/README.md +200 -0
  39. package/docs/sui-image-upload-profile/README.md +145 -0
  40. package/docs/sui-img-retry/README.md +107 -0
  41. package/docs/sui-inline-input/README.md +104 -0
  42. package/docs/sui-inline-select/README.md +101 -0
  43. package/docs/sui-input/README.md +247 -0
  44. package/docs/sui-integration-switch/README.md +142 -0
  45. package/docs/sui-modal/README.md +238 -0
  46. package/docs/sui-modal-footer/README.md +178 -0
  47. package/docs/sui-modal-header/README.md +115 -0
  48. package/docs/sui-modal-sheet/README.md +170 -0
  49. package/docs/sui-notification/README.md +179 -0
  50. package/docs/sui-notification-footer/README.md +98 -0
  51. package/docs/sui-notification-tab-menu/README.md +112 -0
  52. package/docs/sui-otp-input/README.md +116 -0
  53. package/docs/sui-page-header/README.md +123 -0
  54. package/docs/sui-pagination/README.md +146 -0
  55. package/docs/sui-password-strength/README.md +99 -0
  56. package/docs/sui-period-range/README.md +93 -0
  57. package/docs/sui-popover/README.md +178 -0
  58. package/docs/sui-product-card/README.md +173 -0
  59. package/docs/sui-radio-card/README.md +160 -0
  60. package/docs/sui-rating/README.md +7 -6
  61. package/docs/sui-rich-editor/README.md +114 -0
  62. package/docs/sui-segmented-control/README.md +3 -0
  63. package/docs/sui-select/README.md +183 -0
  64. package/docs/sui-sidebar/README.md +217 -0
  65. package/docs/sui-stepper/README.md +166 -0
  66. package/docs/sui-store-card/README.md +147 -0
  67. package/docs/sui-switch-card/README.md +149 -0
  68. package/docs/sui-switch-label/README.md +3 -0
  69. package/docs/sui-tab-chips/README.md +188 -0
  70. package/docs/sui-tab-menu-horizontal/README.md +142 -0
  71. package/docs/sui-tab-menu-vertical/README.md +136 -0
  72. package/docs/sui-table/README.md +264 -0
  73. package/docs/sui-tag-input/README.md +123 -0
  74. package/docs/sui-textarea/README.md +160 -0
  75. package/docs/sui-time-picker/README.md +240 -0
  76. package/docs/sui-toast/README.md +190 -0
  77. package/docs/sui-tooltip/README.md +201 -0
  78. package/docs/sui-topbar/README.md +153 -0
  79. package/docs/sui-topbar-button/README.md +95 -0
  80. package/docs/sui-topbar-item/README.md +97 -0
  81. package/docs/sui-topbar-user/README.md +83 -0
  82. package/fesm2022/ajuarezso-sui-design-system.mjs +291 -246
  83. package/fesm2022/ajuarezso-sui-design-system.mjs.map +1 -1
  84. package/package.json +1 -1
  85. package/types/ajuarezso-sui-design-system.d.ts +208 -50
@@ -0,0 +1,145 @@
1
+ ---
2
+ name: sui-empty-state
3
+ selector: sui-empty-state
4
+ resumen: Pantalla o lista sin nada que mostrar (o que no pudo cargar) con emoji animado, título, explicación y una acción opcional
5
+ alternativas: [sui-alert (un aviso dentro de una pantalla que sí tiene contenido), sui-banner (aviso de toda la app), skeleton (mientras carga, nunca un empty)]
6
+ ---
7
+
8
+ # sui-empty-state
9
+
10
+ Story: [Molecules/Empty State](https://sui-design-system.web.app/?path=/docs/molecules-empty-state--docs)
11
+
12
+ ## Cuándo usarlo / cuándo no
13
+
14
+ **Úsalo** cuando una lista o una pantalla no tiene nada que mostrar: el carrito vacío, sin pedidos
15
+ aún, una búsqueda sin resultados, un filtro que no trae nada. También cuando la lista **no pudo
16
+ cargar**, con una acción para reintentar.
17
+
18
+ **No lo uses** para:
19
+
20
+ - Mientras carga: eso es un skeleton. Un empty que aparece y después se llena se lee como «no
21
+ hay nada» por un segundo.
22
+ - Un aviso dentro de una pantalla que sí tiene contenido («Tu local está cerrado»): `sui-alert`.
23
+ - Un error de un formulario: el mensaje va en el campo (`error` / `errorMessage`).
24
+
25
+ Regla corta: **no hay nada que mostrar → empty-state · está cargando → skeleton · hay contenido y
26
+ algo que avisar → alert.**
27
+
28
+ ## Qué variante usar
29
+
30
+ > **La guía del DS no tiene página del empty state.** El componente de SUI se construyó a
31
+ > propósito con Fluent Emoji animados en vez de ilustraciones: es una decisión del proyecto
32
+ > (memoria «emojis animados en empty states», 17-04-2026).
33
+ >
34
+ > Uso medido el 03-10-2026 en `master-pedidos/src/app` (misma exclusión que `sui-card`): **66
35
+ > empty-states en 40 archivos**. 25 con `action` (10 con `route`). 4 con `emoji` propio, 22 con la
36
+ > URL del zorro pegada a mano (que ya es el default) y 40 sin `emoji`. 29 con un atributo `icon`
37
+ > que el componente no tiene.
38
+
39
+ ### Las piezas
40
+
41
+ | prop | cuándo | fuente |
42
+ |---|---|---|
43
+ | `emoji` vacío (default: el zorro) | Lo normal. El zorro es la mascota de los empty states y sale solo: **no pegues su URL**. | canon (memoria del proyecto) |
44
+ | `emoji` propio | Cuando el emoji dice algo del caso: el reloj de arena en «Aún no tienes facturas» (se está esperando algo), el chef en los avisos del local. Siempre de `FLUENT_EMOJI_ANIM` (`core/fluent-emoji.ts`) o de `assets/emoji-anim/`. | canon (memoria del proyecto) |
45
+ | `title` (obligatorio) | Qué pasa, en corto y sin punto: «Tu carrito está vacío», «Sin pedidos aún». En una búsqueda, repite lo buscado: «Nada con “pollo broaster”». | propuesta (práctica de la app) |
46
+ | `subtitle` | Qué va a pasar o qué puede hacer la persona: «Cuando hagas tu primer pedido, aparecerá aquí.» | propuesta |
47
+ | `action` como string + `(actionClicked)` | La salida es algo de esta misma pantalla: «Reintentar», «Ver todo el catálogo», «Activar captura». | propuesta |
48
+ | `action` como `{ label, route }` | La salida es otra pantalla: «Ver negocios» → `/cliente/inicio`. | propuesta |
49
+ | sin `action` | No hay un paso obvio, o la pantalla ya tiene el botón para llenarla (el «Nuevo local» de arriba). | propuesta |
50
+ | error de carga | Siempre con `action="Reintentar"`. Excepción: la pantalla reintenta sola y lo dice («Seguimos intentando»). | propuesta |
51
+
52
+ **Usos de hoy que rompen las reglas:**
53
+
54
+ - **29 con `icon="…"`**: `icon` no es un input, el atributo no hace nada. Ejemplos:
55
+ `pages/customer/cart/cart.html:18`, `pages/drivers/drivers.html:87`,
56
+ `pages/customer/order-history/order-history.html:20`. Se borra.
57
+ - **22 con la URL del zorro pegada** (`emoji="…/Animals/Fox.png"`), que ya es el default:
58
+ `pages/customers/customers.html:65`, `pages/products/products.html:113`,
59
+ `pages/locations/locations.html:16`. Se borra.
60
+ - **4 errores de carga sin «Reintentar»**:
61
+ `pages/driver/driver-dashboard/driver-dashboard.html:1071`,
62
+ `pages/customer/order-history/order-history.html:20`,
63
+ `pages/super-admin/master-drivers/master-drivers.html:524` y `:754`.
64
+ (`pages/public/pedido-en-vivo/pedido-en-vivo.html:50` no cuenta: reintenta solo.)
65
+
66
+ ## Tamaño en mobile y en web
67
+
68
+ El emoji mide 120 en las dos; el padding pasa solo de 48 / 24 en web a 32 / 20 desde tablet hacia
69
+ abajo (media query `bp.medium` del componente). El `size` solo cambia **el botón de la acción**:
70
+
71
+ | size | botón | fuente |
72
+ |---|---|---|
73
+ | `medium` (default) | 40 px (`sui-button` `medium`) | lo que había en el DS |
74
+ | `large` | 48 px (`sui-button` `large`, texto 16) | canon (aprobado por el dueño, 03-10-2026): extensión SUI |
75
+
76
+ | uso | mobile | web | fuente |
77
+ |---|---|---|---|
78
+ | ilustración + texto | lo resuelve el componente | lo resuelve el componente | lo que hay en el DS |
79
+ | botón de la acción | `large` (48) | `medium` (40) | propuesta: el tamaño es extensión SUI aprobada (03-10-2026); el «cuándo» falta aprobarlo |
80
+
81
+ ```html
82
+ <sui-empty-state
83
+ title="Tu carrito está vacío"
84
+ [action]="{ label: 'Ver negocios', route: '/cliente/inicio' }"
85
+ [size]="bp.isMobile() ? 'large' : 'medium'" />
86
+ ```
87
+
88
+ ## «No usar»: por qué no hoy y cuándo sí
89
+
90
+ | qué | por qué no hoy | cuándo sí, en un flujo nuevo |
91
+ |---|---|---|
92
+ | un emoji que no sea animado (PNG estático, SVG, ícono) | la regla del proyecto es emoji animado | nunca |
93
+ | empty con `action` que hace lo mismo que un botón ya visible en la pantalla | dos botones para lo mismo | nunca |
94
+
95
+ ## API
96
+
97
+ <!-- api:inicio -->
98
+ <!-- generada: no editar a mano — `npm run docs` -->
99
+
100
+ | input | tipo | default |
101
+ |---|---|---|
102
+ | `emoji` | `string` | `''` |
103
+ | `title` | `string` | — (requerido) |
104
+ | `subtitle` | `string` | `''` |
105
+ | `action` | `SuiEmptyStateAction \| string \| null` | `null` |
106
+ | `size` | `'medium' \| 'large'` | `'medium'` |
107
+
108
+ | output | payload |
109
+ |---|---|
110
+ | `actionClicked` | `void` |
111
+ <!-- api:fin -->
112
+
113
+ ```ts
114
+ interface SuiEmptyStateAction { label: string; route?: string; }
115
+ ```
116
+
117
+ `EMPTY_STATE_FOX_EMOJI` (exportada) es la URL del zorro.
118
+
119
+ ## Trampas
120
+
121
+ - **`icon` no existe.** Viene de un `app-empty-state` viejo; Angular no se queja porque lo toma
122
+ como atributo HTML.
123
+ - **`emoji` es una URL**, no un nombre de `SuiEmoji` (`'bell'` no funciona).
124
+ - **`title` es obligatorio** y el host borra el atributo `title` del DOM (sin tooltip nativo).
125
+ - Con `route`, el botón usa `routerLink` y **no emite** `actionClicked`.
126
+ - El botón es `sui-button` `filled` `primary`: el color no se cambia; el tamaño sí, con `size` (`medium` 40 · `large` 48).
127
+ - El emoji carga con `loading="lazy"`: en una captura de pantalla completa puede salir vacío.
128
+
129
+ ## Ejemplo
130
+
131
+ ```html
132
+ <!-- Lista vacía con salida a otra pantalla -->
133
+ <sui-empty-state
134
+ title="Tu carrito está vacío"
135
+ subtitle="Explora las tiendas y agrega productos para empezar."
136
+ [action]="{ label: 'Ver negocios', route: '/cliente/inicio' }" />
137
+
138
+ <!-- Error de carga -->
139
+ <sui-empty-state
140
+ [emoji]="emoji.thinking"
141
+ title="No se pudieron cargar tus pedidos"
142
+ subtitle="Revisa tu conexión y vuelve a intentar."
143
+ action="Reintentar"
144
+ (actionClicked)="cargar()" />
145
+ ```
@@ -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,181 @@
1
+ ---
2
+ name: sui-filter
3
+ selector: sui-filter-bar, sui-filter-item, sui-filter-panel-header, sui-filter-panel-footer
4
+ resumen: Piezas de filtros de una lista o tabla del panel; la barra horizontal (buscador, vista, orden) y el panel vertical (fila, cabecera con «Limpiar», pie con «Limpiar» / «Aplicar»)
5
+ alternativas: [sui-segmented-control (2 a 4 vistas que se aplican al instante), sui-compact-select (un filtro suelto), sui-tab-chips (filtros rápidos con chips), sui-activity-filter (filtro del feed de actividad), sui-modal-sheet (el panel de filtros en mobile)]
6
+ ---
7
+
8
+ # sui-filter
9
+
10
+ Story: [Organisms/Filter Bar](https://sui-design-system.web.app/?path=/docs/organisms-filter-bar--docs)
11
+
12
+ Son **cuatro componentes** en una carpeta, sin uno llamado `sui-filter`:
13
+
14
+ - `sui-filter-bar`: la **barra horizontal** arriba de una tabla. Es solo layout: un lado
15
+ izquierdo (`slot="start"`) y uno derecho (`slot="end"`); los controles se proyectan.
16
+ - `sui-filter-item`: una **fila** del panel vertical (una categoría de filtro: «Estado», «Local»).
17
+ - `sui-filter-panel-header`: la **cabecera** del panel, con un link «Limpiar» opcional.
18
+ - `sui-filter-panel-footer`: el **pie** del panel, con «Limpiar» y «Aplicar».
19
+
20
+ El panel vertical **no existe como contenedor**: se arma con header + items + footer dentro de un
21
+ `sui-dropdown-menu` (web) o de un `sui-modal-sheet` (mobile).
22
+
23
+ ## Cuándo usarlo / cuándo no
24
+
25
+ **Úsalo** en las listas y tablas del panel del local o del super admin que necesitan filtrar
26
+ (pedidos, productos, repartidores): la barra arriba con el buscador, la vista y el orden; el panel
27
+ cuando hay varias categorías de filtro que se combinan y se aplican juntas.
28
+
29
+ **No lo uses** para:
30
+
31
+ - 2 a 4 vistas que se aplican al tocar (Hoy · Semana · Mes): `sui-segmented-control` solo, sin barra.
32
+ - Un único filtro: un `sui-compact-select` o `sui-select` suelto.
33
+ - Filtros rápidos de un toque en mobile (Todos · Pendientes · Entregados): `sui-tab-chips`.
34
+ - El feed de actividad: `sui-activity-filter`.
35
+
36
+ Regla corta: **barra arriba de una tabla → filter-bar · varias categorías que se aplican juntas →
37
+ panel (header + items + footer) · un filtro suelto → compact-select.**
38
+
39
+ ## Qué variante usar
40
+
41
+ > **La guía del DS no tiene página de filtros**: estas piezas salen solo del Figma del DS
42
+ > («Filters»: HorizontalFilter y VerticalFilter). Todo lo de abajo es **propuesta, falta
43
+ > aprobación**, salvo lo marcado canon.
44
+ >
45
+ > Uso medido el 03-10-2026 en `master-pedidos/src/app` (misma exclusión que `sui-dropdown-menu`):
46
+ > **0 usos**. Solo aparecen en la vitrina `master-preview`.
47
+
48
+ ### `sui-filter-bar`
49
+
50
+ | variant | cuándo | fuente |
51
+ |---|---|---|
52
+ | `table` (default) | Arriba de una tabla: a la izquierda la vista (`sui-segmented-control`), a la derecha el buscador (`sui-input type="search"`) y el orden (`sui-select` o `sui-compact-select`). | propuesta (composición del Figma) |
53
+ | `calendar` | Arriba de una vista por fechas: a la izquierda «Hoy» + el rango (`sui-button-group`), a la derecha el buscador. | propuesta (composición del Figma) |
54
+
55
+ `variant` solo marca el host (`data-sui-variant`): **no cambia el layout**. Lo que se ve lo deciden
56
+ los controles que se proyectan.
57
+
58
+ ### El panel: `sui-filter-item` + header + footer
59
+
60
+ | pieza | regla | fuente |
61
+ |---|---|---|
62
+ | `sui-filter-item` `active` | La categoría abierta (la que muestra sus opciones al lado): fondo gris, texto negro, flecha a la derecha. | propuesta (estados del Figma) |
63
+ | `sui-filter-item` `leftIcon` | Un ícono por categoría si ayuda a ubicarla («Estado», «Local», «Repartidor»). | propuesta |
64
+ | `sui-filter-panel-header` `clear` | Pásale texto (`clear="Limpiar"`), no `true`: con `true` dice «Clear». | canon (CANON-SUI §5: copy en español) |
65
+ | `sui-filter-panel-footer` | «Limpiar» a la izquierda (`stroke neutral`), «Aplicar» a la derecha (`filled primary`), mitad y mitad. | canon (orden de `sui-modal-footer`, CLAUDE.md «Botones») |
66
+ | «Limpiar» en header **y** footer | Elegir uno. Con el footer, el header va sin link. | propuesta |
67
+ | `applyButton` deshabilitado | `{ label: 'Aplicar', disabled: !cambio() }` mientras no se toca nada. | propuesta |
68
+
69
+ ## Tamaño en mobile y en web
70
+
71
+ Ninguna pieza cambia sola en mobile, salvo la barra, que apila sus dos lados por debajo de 640 px.
72
+
73
+ | pieza | mobile | web | fuente |
74
+ |---|---|---|---|
75
+ | `sui-filter-bar` | sin tamaño; los controles adentro van `large` (48) | sin tamaño; los controles `small` (36) | propuesta (igual que `sui-compact-select` en una barra) |
76
+ | `sui-filter-item` | 36 de alto, no se puede cambiar: debajo de los 44 del dedo | 36 | propuesta |
77
+ | `sui-filter-panel-footer` | `medium` (40) es lo más grande que tiene; en mobile, mejor `sui-modal-footer` (48) dentro de un `sui-modal-sheet` | `small` (36, default) | canon para el 48 de mobile (CLAUDE.md: footer de modal `large`); el resto, propuesta |
78
+ | `sui-filter-panel-header` | sin tamaño | sin tamaño | — |
79
+
80
+ ```html
81
+ <sui-filter-bar>
82
+ <sui-segmented-control slot="start" … />
83
+ <div slot="end">
84
+ <sui-input type="search" [size]="bp.isMobile() ? 'large' : 'small'" placeholder="Buscar pedido…" … />
85
+ </div>
86
+ </sui-filter-bar>
87
+ ```
88
+
89
+ ## API
90
+
91
+ <!-- api:inicio -->
92
+ <!-- generada: no editar a mano — `npm run docs` -->
93
+
94
+ ### `sui-filter-bar`
95
+
96
+ | input | tipo | default |
97
+ |---|---|---|
98
+ | `variant` | `'calendar' \| 'table'` | `'table'` |
99
+
100
+ Outputs: ninguno.
101
+
102
+ ### `sui-filter-item`
103
+
104
+ | input | tipo | default |
105
+ |---|---|---|
106
+ | `active` | `boolean` | `false` |
107
+ | `disabled` | `boolean` | `false` |
108
+ | `leftIcon` | `string \| null` | `null` |
109
+ | `activeIcon` | `string` | `'arrow-right-s-line'` |
110
+
111
+ | output | payload |
112
+ |---|---|
113
+ | `selected` | `Event` |
114
+
115
+ ### `sui-filter-panel-footer`
116
+
117
+ | input | tipo | default |
118
+ |---|---|---|
119
+ | `clearButton` | `SuiFooterButtonConfig \| string` | `'Clear'` |
120
+ | `applyButton` | `SuiFooterButtonConfig \| string` | `'Apply'` |
121
+ | `size` | `'small' \| 'medium'` | `'small'` |
122
+
123
+ | output | payload |
124
+ |---|---|
125
+ | `clear` | `void` |
126
+ | `apply` | `void` |
127
+
128
+ ### `sui-filter-panel-header`
129
+
130
+ | input | tipo | default |
131
+ |---|---|---|
132
+ | `label` | `string` | `''` |
133
+ | `leftIcon` | `string \| null` | `null` |
134
+ | `clear` | `string \| boolean` | `false` |
135
+
136
+ | output | payload |
137
+ |---|---|
138
+ | `clearClick` | `void` |
139
+ <!-- api:fin -->
140
+
141
+ Slots de `sui-filter-bar`: `[slot=start]`, `[slot=end]` y el contenido por defecto (fila única si no
142
+ se usan los slots). El label de `sui-filter-item` es su contenido proyectado. `clearButton` y
143
+ `applyButton` aceptan un string o `SuiFooterButtonConfig` (`{ label, disabled? }`, la misma interfaz
144
+ de `sui-drawer-footer`).
145
+
146
+ ## Trampas
147
+
148
+ - **Los textos por defecto están en inglés**: «Clear» y «Apply» en el footer, «Clear» en el header con
149
+ `clear=true`. Pásalos siempre.
150
+ - **La barra apila a 640 px; la app cambia a mobile a 767.** Entre 641 y 767 px la app ya está en
151
+ mobile y la barra sigue en una fila.
152
+ - **Varios controles en un lado van en un contenedor.** Cada slot es una fila con 12 px de separación,
153
+ pero un solo elemento por `slot` queda más ordenado (`<div slot="end">`), como en el Figma.
154
+ - **`sui-filter-item` tiene `role="option"`**: el contenedor tiene que llevar `role="listbox"`.
155
+ - **`active` no es «elegido»**: marca la categoría abierta, no un valor aplicado. Para decir que una
156
+ categoría tiene un filtro puesto no hay prop (un `sui-badge` en el label es lo más cercano).
157
+ - **El footer separa sus botones 16 px** (del Figma); el canon de la app entre botones hermanos es 12
158
+ (CANON-SUI §0, `sui-modal-footer`).
159
+ - El footer no llega a `large`: en mobile queda más chico que el footer de cualquier modal.
160
+ - `disabled` del item usa opacidad (no está en el Figma).
161
+
162
+ ## Ejemplo
163
+
164
+ ```html
165
+ <!-- Panel de filtros en web, anclado a un botón «Filtros» -->
166
+ <sui-dropdown-menu #filtros="suiDropdownMenu" [minWidth]="320">
167
+ <sui-button trigger variant="stroke" color="neutral" size="small">Filtros</sui-button>
168
+ <ng-template suiDropdownPanel>
169
+ <sui-filter-panel-header label="Filtros" leftIcon="equalizer-line" />
170
+ <div role="listbox" aria-label="Categorías de filtro">
171
+ <sui-filter-item leftIcon="checkbox-circle-line" [active]="cat() === 'estado'"
172
+ (selected)="cat.set('estado')">Estado</sui-filter-item>
173
+ <sui-filter-item leftIcon="store-2-line" [active]="cat() === 'local'"
174
+ (selected)="cat.set('local')">Local</sui-filter-item>
175
+ </div>
176
+ <sui-filter-panel-footer clearButton="Limpiar"
177
+ [applyButton]="{ label: 'Aplicar', disabled: !cambio() }"
178
+ (clear)="limpiar()" (apply)="filtros.close(); aplicar()" />
179
+ </ng-template>
180
+ </sui-dropdown-menu>
181
+ ```