@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,116 @@
1
+ ---
2
+ name: sui-otp-input
3
+ selector: sui-otp-input
4
+ resumen: Código de verificación dígito por dígito, una celda por número (SMS, código de entrega)
5
+ alternativas: [sui-input (un código largo o que se pega entero), sui-input type="password-toggle" (una clave que no se ve)]
6
+ ---
7
+
8
+ # sui-otp-input
9
+
10
+ Story: [Molecules/Input/Otp Input](https://sui-design-system.web.app/?path=/docs/molecules-input-otp-input--docs)
11
+
12
+ ## Cuándo usarlo / cuándo no
13
+
14
+ **Úsalo** para un código corto que llega por otro canal y se copia dígito por dígito: un código
15
+ por SMS o WhatsApp, o un código de entrega que el cliente le dicta al repartidor. Salta solo a la
16
+ celda siguiente, retrocede con borrar, reparte lo que se pega y avisa con `(completed)` cuando
17
+ está lleno. La primera celda pide `autocomplete="one-time-code"` (el teléfono sugiere el código
18
+ del SMS).
19
+
20
+ **No lo uses** para:
21
+
22
+ - Una contraseña o PIN que se elige: `sui-input type="password-toggle"`.
23
+ - Un código de más de 6 caracteres o con letras y guiones (un cupón): `sui-input`.
24
+
25
+ Regla corta: **código corto que llega por otro canal → otp · cupón o clave → input.**
26
+
27
+ ## Qué variante usar
28
+
29
+ > **La guía del DS lo define como el campo para escribir códigos de un solo uso dígito por dígito,
30
+ > pensado para flujos de verificación** con códigos numéricos cortos. Muestra error, deshabilitado
31
+ > y celdas cuadradas. No dice cuántas celdas.
32
+ >
33
+ > Uso medido el 03-10-2026 en `master-pedidos/src/app`: **0 usos**. Hoy no hay login por SMS ni
34
+ > código de entrega.
35
+
36
+ | prop / evento | cuándo | fuente |
37
+ |---|---|---|
38
+ | `type="number"` (default) | Códigos numéricos: teclado de números. | según la guía del DS (códigos numéricos) |
39
+ | `type="text"` | No usar hoy: un código con letras se tipea mejor en un `sui-input`. | propuesta, falta aprobación |
40
+ | `length` | `6` para un SMS (lo que manda Firebase); `4` para un código de entrega dictado en voz alta. | propuesta, falta aprobación |
41
+ | `error` | El código no coincide. Pinta todas las celdas; el mensaje va aparte, debajo. | según la guía del DS (ejemplo con error) |
42
+ | `disabled` | Mientras se verifica el código. | según la guía del DS (ejemplo deshabilitado) |
43
+ | `ariaLabel` | Qué código es: «Código que te llegó por SMS». El default es «Código de verificación». | propuesta, falta aprobación |
44
+ | `(completed)` | Verificar apenas se llena, sin botón «Continuar». | propuesta, falta aprobación |
45
+
46
+ ## Tamaño en mobile y en web
47
+
48
+ Tiene `size` (extensión SUI) para la celda. `large` es el de la guía y el default: **80 × 64** con 8
49
+ px entre celdas (6 celdas = 520 px). Para el teléfono, **`medium` (52 × 56)**: 6 celdas miden 352
50
+ px y entran en los 358 útiles de una pantalla de 390. `small` (44 × 48, dígito de 20) queda para
51
+ 360 px o un código dentro de una tarjeta.
52
+
53
+ Además, **el componente nunca es más ancho que su contenedor**: si las celdas no entran, se achican
54
+ parejo en vez de salirse de la pantalla (un `large` de 6 en 390 queda en celdas de 53 × 64). Es la red
55
+ de seguridad, no el tamaño a elegir: se ve angosto y alto.
56
+
57
+ | size | celda | 6 celdas | fuente |
58
+ |---|---|---|---|
59
+ | `large` (default) | 80 × 64 · dígito 24 | 520 px | según la guía del DS |
60
+ | `medium` | 52 × 56 · dígito 24 | 352 px | canon (aprobado por el dueño, 03-10-2026), extensión SUI |
61
+ | `small` | 44 × 48 · dígito 20 | 304 px | canon (aprobado por el dueño, 03-10-2026), extensión SUI |
62
+
63
+ | uso | mobile (390) | web | fuente |
64
+ |---|---|---|---|
65
+ | código de 6 dígitos | `medium` | `large` | propuesta, falta aprobación |
66
+ | código de 4 dígitos | `large` (344 px, entra) | `large` | propuesta, falta aprobación |
67
+
68
+ ```html
69
+ <sui-otp-input [length]="6" [size]="bp.isMobile() ? 'medium' : 'large'" [(value)]="codigo" />
70
+ <!-- bp = BreakpointsService -->
71
+ ```
72
+
73
+ ## «No usar»: por qué no hoy y cuándo sí
74
+
75
+ | qué | por qué no hoy | cuándo sí, en un flujo nuevo |
76
+ |---|---|---|
77
+ | el componente entero (0 usos) | no hay verificación por código | verificar el teléfono del cliente por SMS, o un código de entrega de 4 dígitos que el cliente le dicta al repartidor |
78
+ | `type="text"` | los códigos que llegan son numéricos | un código alfanumérico corto de una campaña |
79
+ | `size="small"` | `medium` ya entra en 390 | pantallas de 360 px o un código dentro de una tarjeta |
80
+
81
+ ## API
82
+
83
+ <!-- api:inicio -->
84
+ <!-- generada: no editar a mano — `npm run docs` -->
85
+
86
+ | input | tipo | default |
87
+ |---|---|---|
88
+ | `length` | `number` | `6` |
89
+ | `size` | `'large' \| 'medium' \| 'small'` | `'large'` |
90
+ | `type` | `'number' \| 'text'` | `'number'` |
91
+ | `disabled` · model, `[(disabled)]` | `boolean` | `false` |
92
+ | `error` | `boolean` | `false` |
93
+ | `ariaLabel` | `string` | `'Código de verificación'` |
94
+ | `value` · model, `[(value)]` | `string` | `''` |
95
+
96
+ | output | payload |
97
+ |---|---|
98
+ | `completed` | `string` |
99
+ <!-- api:fin -->
100
+
101
+ `value` y `disabled` son `model`. Implementa `ControlValueAccessor`.
102
+
103
+ ## Trampas
104
+
105
+ - **6 celdas `large` no entran en un teléfono** (520 px): se achican solas, pero en mobile va
106
+ `size="medium"`. Ver «Tamaño en mobile y en web».
107
+ - `error` no trae mensaje: el texto va debajo, con `sui-form-hint` en variante error.
108
+ - `disabled` es un `model`: si el formulario lo deshabilita, el componente lo escribe de vuelta.
109
+ - No hay grupos (3-3): si se necesitan, son dos componentes.
110
+
111
+ ## Ejemplo
112
+
113
+ ```html
114
+ <sui-otp-input [length]="4" ariaLabel="Código de entrega" [error]="codigoMalo()"
115
+ [(value)]="codigo" (completed)="verificar($event)" />
116
+ ```
@@ -0,0 +1,123 @@
1
+ ---
2
+ name: sui-page-header
3
+ selector: sui-page-header
4
+ resumen: Título y subtítulo de una pantalla, con las acciones de la pantalla a la derecha en desktop y debajo en mobile
5
+ alternativas: [sui-modal-header (cabecera de un modal), sui-drawer-header (cabecera de un panel lateral), sui-topbar (barra superior de la app, no de la pantalla)]
6
+ ---
7
+
8
+ # sui-page-header
9
+
10
+ Story: [Organisms/Page Header](https://sui-design-system.web.app/?path=/docs/organisms-page-header--docs)
11
+
12
+ ## Cuándo usarlo / cuándo no
13
+
14
+ **Úsalo** una vez por pantalla, **arriba del todo**, antes de cualquier contenido: dice dónde
15
+ está la persona («Pedidos», «Descuentos») y, si hace falta, junta las acciones que valen para
16
+ toda la pantalla («Agregar», «Exportar»).
17
+
18
+ **No lo uses** para:
19
+
20
+ - Títulos de sección dentro de la pantalla: un `<h2>` con el token de tipografía, o el título de
21
+ la `sui-card`.
22
+ - Modales y paneles: `sui-modal-header` / `sui-drawer-header`.
23
+ - Meter contenido debajo del título: **solo** acepta `slot="actions"`; todo lo demás se descarta
24
+ sin avisar.
25
+ - Re-implementarlo con un `<h1>` y CSS propio (canon, CANON-SUI §0: «no re-implementar»).
26
+
27
+ ## Qué variante usar
28
+
29
+ > **No está en la guía del DS: es extensión de SUI.** Nació como el título de las pantallas del
30
+ > panel de empresa y no tiene Figma. Lo marcado **canon** sale de `master-pedidos/docs/CANON-SUI.md`;
31
+ > lo demás es **propuesta**.
32
+ >
33
+ > Uso medido el 03-10-2026 en `master-pedidos/src/app`: 64 page headers, sin contar prototipos ni
34
+ > vitrinas (`master-preview`, `master-prototipos`, `mockup-sheets-*`, `dx/*`, `master-rutina`,
35
+ > `master-medico`, `dev-index`) ni las copias de `components/sui/`. 50 llevan `subtitle` fijo, 14
36
+ > calculado; 17 tienen acciones; 0 usan `sticky`.
37
+
38
+ ### Lo que sí dice la guía del DS
39
+
40
+ Nada: el page header no está en la guía. Su tipografía (24/600, 28 en desktop) **no existe en la
41
+ escala del DS** (CANON-SUI §7): si se homologa, sería `title-h5`.
42
+
43
+ ### Contenido
44
+
45
+ | pieza | regla | ejemplo real de la app | fuente |
46
+ |---|---|---|---|
47
+ | `title` | El nombre de la pantalla, el mismo que en el menú. Sentence case, sin punto. Nunca un ID. | `pages/discounts/discounts.html:2` | canon (CANON-SUI, copy) |
48
+ | `subtitle` | Una línea que dice **qué se hace aquí**, no que repite el título. | `pages/super-admin/master-orders/master-orders.html:1` | propuesta |
49
+ | acciones | Las que valen para toda la pantalla: una `filled` como máximo (la principal) y el resto `stroke`. Hoy: 9 `filled`, 7 `stroke`. | `pages/drivers/drivers.html:2` | canon para «una principal» (CANON-SUI §6.2), propuesta el resto |
50
+
51
+ **Usos de hoy que rompen el copy canon** (sentence case en botones): `pages/drivers/drivers.html:8`
52
+ («Nuevo Repartidor»).
53
+
54
+ ### `sticky`
55
+
56
+ | valor | cuándo | fuente |
57
+ |---|---|---|
58
+ | `false` (default) | el caso normal: el título se va con el scroll | propuesta |
59
+ | `true` | listas largas donde las acciones del header se usan mientras se recorre (filtros, «Agregar»). Le pone fondo blanco. 0 usos hoy | propuesta |
60
+
61
+ ## Tamaño en mobile y en web
62
+
63
+ El page header **no tiene `size`**. Cambia solo en 767 px:
64
+
65
+ | | mobile (< 767 px) | web (≥ 767 px) | fuente |
66
+ |---|---|---|---|
67
+ | título | 24 px | 28 px | componente |
68
+ | disposición | título y subtítulo arriba, acciones **debajo** | título a la izquierda, acciones a la derecha, alineadas abajo | componente |
69
+ | padding vertical | 24 arriba y abajo | 32 arriba, 16 abajo | componente |
70
+ | padding lateral | ninguno: lo pone el contenedor del layout | igual | componente |
71
+ | botones de las acciones | `large` (48) | `small` (36) | propuesta |
72
+
73
+ Los botones de las acciones hoy: 10 `small` fijos, 2 `bp.isMobile() ? 'large' : 'small'`, 2
74
+ `bp.controlSize()` (`large`/`xlarge`), 2 `large`, 1 `medium`. La propuesta es la pareja que ya
75
+ usan dos pantallas: en mobile las acciones bajan a su fila y tienen sitio para 48 px; en desktop
76
+ van al lado del título de 28 y `small` no le compite.
77
+
78
+ ```html
79
+ <sui-button [size]="bp.isMobile() ? 'large' : 'small'" slot="actions">Agregar</sui-button>
80
+ ```
81
+
82
+ ## API
83
+
84
+ <!-- api:inicio -->
85
+ <!-- generada: no editar a mano — `npm run docs` -->
86
+
87
+ | input | tipo | default |
88
+ |---|---|---|
89
+ | `title` | `string` | — (requerido) |
90
+ | `subtitle` | `string \| null` | `null` |
91
+ | `sticky` | `boolean` | `false` |
92
+
93
+ Outputs: ninguno.
94
+ <!-- api:fin -->
95
+
96
+ Slot: `slot="actions"`, a la derecha (desktop) o debajo (mobile), gap 8. No hay slot default.
97
+
98
+ ## Trampas
99
+
100
+ - **Lo que proyectas sin `slot="actions"` desaparece.** El JSDoc viejo hablaba de un slot
101
+ default; no existe.
102
+ - **No respeta el safe-area de arriba**: el padding es fijo. El layout de la pantalla se encarga
103
+ del notch; si lo pones en una pantalla sin layout, el título queda bajo la barra de estado.
104
+ - **Sin padding lateral**: depende de que el contenedor de la página lo tenga
105
+ (`.layout__body`, `.ml__content`). Fuera de esos layouts queda pegado al borde.
106
+ - `sticky` se pega a `top: 0` del **contenedor que scrollea**, no de la ventana. En los layouts de
107
+ la app el scroll vive en `.layout__body` / `.ml__content`, así que funciona; en un contenedor
108
+ sin overflow no hace nada.
109
+ - El breakpoint es `min-width: 767px`: a 767 px exactos ya está en modo desktop, un píxel antes
110
+ que el `max-width: 767px` de los mixins de la app.
111
+ - `title` es requerido: sin él, Angular falla al compilar la plantilla.
112
+ - El host borra el atributo `title` nativo (sin tooltip gris del navegador).
113
+
114
+ ## Ejemplo
115
+
116
+ ```html
117
+ <sui-page-header title="Descuentos" subtitle="Promociones y campañas activas de tu negocio.">
118
+ <sui-button slot="actions" variant="filled"
119
+ [size]="bp.isMobile() ? 'large' : 'small'" (click)="crear()">
120
+ Crear descuento
121
+ </sui-button>
122
+ </sui-page-header>
123
+ ```
@@ -0,0 +1,146 @@
1
+ ---
2
+ name: sui-pagination
3
+ selector: sui-pagination
4
+ resumen: Números de página para recorrer una tabla larga en web cuando todos los datos ya están cargados
5
+ alternativas: [scroll infinito (directiva appInfiniteScroll de la app, para listas de Firestore), sui-stepper (pasos de un flujo), sui-tab-chips (filtrar en vez de paginar)]
6
+ ---
7
+
8
+ # sui-pagination
9
+
10
+ Story: [Molecules/Pagination](https://sui-design-system.web.app/?path=/docs/molecules-pagination--docs)
11
+
12
+ ## Cuándo usarlo / cuándo no
13
+
14
+ **Úsalo** en **web**, debajo de una **tabla larga cuyos datos ya están en memoria** (un reporte, un
15
+ export, una tabla filtrada en el navegador), cuando ayuda saber «voy por la 3 de 12» o saltar a una
16
+ página.
17
+
18
+ **No lo uses** para:
19
+
20
+ - **Mobile**: las listas se recorren con el dedo. Va scroll infinito (la directiva
21
+ `appInfiniteScroll` que ya usan productos, clientes y equipo).
22
+ - **Listas que se leen de Firestore por cursor** (`getCollectionPage`): con cursores solo se puede
23
+ pedir la página siguiente. Saltar a la página 7 obliga a leer las 6 anteriores, y Firestore las
24
+ cobra igual. El canon de la app para esas listas es scroll infinito (CLAUDE.md, «Performance —
25
+ Firestore reads»).
26
+ - Pasos de un flujo: `sui-stepper`.
27
+ - Pocas filas (una sola página): no se muestra.
28
+
29
+ Regla corta: **tabla con todo cargado en web → pagination · lista de Firestore o mobile → scroll infinito.**
30
+
31
+ ## Qué variante usar
32
+
33
+ > **Lo que dice la guía del DS**: es un conjunto horizontal de enlaces para **navegar contenido
34
+ > paginado**. Trae tres formas: `basic`, `rounded` (en SUI, `full-radius`) y `group`, más botones
35
+ > de primera/última y anterior/siguiente, y la página actual marcada. No trae do/don't ni dice
36
+ > cuándo cada forma.
37
+ >
38
+ > Uso medido el 03-10-2026 en `master-pedidos/src/app` (misma exclusión que `sui-badge`): **0 usos**.
39
+ > Las 3 listas largas de la app (`pages/products/products.html:242`, `pages/customers/customers.html:161`,
40
+ > `pages/settings/team/team.ts:347`) usan scroll infinito.
41
+
42
+ ### `type`
43
+
44
+ | type | cuándo | fuente |
45
+ |---|---|---|
46
+ | `basic` (default) | El normal, debajo de una tabla con bordes. | según la guía del DS (es su default); el cuándo es propuesta |
47
+ | `group` | Cuando la tabla va dentro de una card y la paginación se apoya en su borde: un solo bloque se lee como parte de la tabla. | según la guía del DS (la forma existe); el cuándo es propuesta |
48
+ | `full-radius` | No: el resto de los controles de la app (botones, inputs) tienen esquinas de 8-10, no círculos. | según la guía del DS (la forma existe); el no es propuesta |
49
+
50
+ ### `size`
51
+
52
+ | size | qué mide | cuándo | fuente |
53
+ |---|---|---|---|
54
+ | `medium` (default) | celdas de 32 px | Web, debajo de una tabla. | extensión de SUI (la guía tiene un tamaño); el cuándo es propuesta |
55
+ | `small` | celdas de 28 px | Web, en una tabla dentro de un modal o panel angosto. | propuesta, falta aprobación |
56
+
57
+ ### Botones y texto
58
+
59
+ | input | valor | cuándo | fuente |
60
+ |---|---|---|---|
61
+ | `nextPrevious` | `true` (default) | Siempre. | según la guía del DS |
62
+ | `firstLast` | `false` (default) | Lo normal: con las elipsis ya se ve la primera y la última. | propuesta, falta aprobación |
63
+ | `firstLast` | `true` | Más de 20 páginas, donde ir al final es un caso de uso (la última semana de un export). | según la guía del DS (los botones existen); el cuándo es propuesta |
64
+ | `siblingCount` | `1` (default) | Siempre. Más números a cada lado ensanchan sin ayudar. | propuesta, falta aprobación |
65
+ | `showInfo` | `true` (default) | Con `infoFormat="items"`: «Mostrando 11-20 de 134». | propuesta, falta aprobación |
66
+ | `infoFormat` | `'items'` (default) | El único en español. | según el código |
67
+ | `infoFormat` | `'pages'` | Imprime «Página 2 de 14» (en español desde 03-10-2026). Cuando no se sabe el total de ítems o no importa. | propuesta, falta aprobación |
68
+ | `pageSizeOptions` | `[]` (default) | Lo normal: sin selector de filas por página. | propuesta, falta aprobación |
69
+ | `pageSizeOptions` | `[10, 25, 50]` | Las opciones dicen «10 / página» (en español desde 03-10-2026). | propuesta, falta aprobación |
70
+
71
+ Usos de hoy que rompen las reglas: **0** (no se usa).
72
+
73
+ ## Tamaño en mobile y en web
74
+
75
+ **No tiene tamaño para mobile**: la celda más grande mide 32 px, por debajo de los 44 tocables. En
76
+ mobile no se usa.
77
+
78
+ | uso | mobile | web | fuente |
79
+ |---|---|---|---|
80
+ | debajo de una tabla | no se usa: scroll infinito | `medium` (32) | propuesta, falta aprobación |
81
+ | tabla en un panel angosto | no se usa | `small` (28) | propuesta, falta aprobación |
82
+
83
+ ```html
84
+ @if (!bp.isMobile()) {
85
+ <sui-pagination [total]="filas().length" [(currentPage)]="pagina" [pageSize]="25" />
86
+ }
87
+ ```
88
+
89
+ ## «No usar»: por qué no hoy y cuándo sí
90
+
91
+ | qué | por qué no hoy | cuándo sí, en un flujo nuevo |
92
+ |---|---|---|
93
+ | el componente entero | las listas de la app leen de Firestore por cursor y usan scroll infinito | una tabla de web con todos los datos ya cargados (un reporte de 300 filas filtrado en el navegador) |
94
+ | `full-radius` | no combina con las esquinas del resto de la app | nunca, salvo que la app pase a botones redondos |
95
+ | `infoFormat="pages"` | sale en inglés | cuando el DS lo traduzca («Página 2 de 14») |
96
+ | `firstLast` | ninguna tabla tiene 20+ páginas | un export largo donde ir al final es habitual |
97
+
98
+ ## API
99
+
100
+ <!-- api:inicio -->
101
+ <!-- generada: no editar a mano — `npm run docs` -->
102
+
103
+ | input | tipo | default |
104
+ |---|---|---|
105
+ | `total` | `number` | — (requerido) |
106
+ | `pageSize` · model, `[(pageSize)]` | `number` | `10` |
107
+ | `siblingCount` | `number` | `1` |
108
+ | `showInfo` | `boolean` | `true` |
109
+ | `size` | `'small' \| 'medium'` | `'medium'` |
110
+ | `type` | `'basic' \| 'full-radius' \| 'group'` | `'basic'` |
111
+ | `firstLast` | `boolean` | `false` |
112
+ | `nextPrevious` | `boolean` | `true` |
113
+ | `infoFormat` | `'items' \| 'pages'` | `'items'` |
114
+ | `pageSizeOptions` | `readonly number[]` | `[]` |
115
+ | `currentPage` · model, `[(currentPage)]` | `number` | `1` |
116
+
117
+ | output | payload |
118
+ |---|---|
119
+ | `pageChange` | `number` |
120
+ | `pageSizeChange` | `number` |
121
+ <!-- api:fin -->
122
+
123
+ ```ts
124
+ type SuiPaginationType = 'basic' | 'full-radius' | 'group';
125
+ type SuiPaginationSize = 'small' | 'medium';
126
+ type SuiPaginationInfoFormat = 'items' | 'pages';
127
+ ```
128
+
129
+ ## Trampas
130
+
131
+ - **`total` es la cantidad de ítems, no de páginas.** Las páginas salen de `total / pageSize`.
132
+ - **`currentPage` arranca en 1** (el stepper arranca en 0).
133
+ - **`currentPage` y `pageSize` son `model`**: con `[(currentPage)]` no hace falta `(pageChange)`.
134
+ - **No carga datos**: solo dice qué página se pidió. Cortar las filas lo hace la pantalla.
135
+ - **Textos en español** desde 03-10-2026: «Página X de Y» (`infoFormat="pages"`), «N / página»
136
+ (`pageSizeOptions`) y el default «Mostrando X-Y de Z».
137
+ - **La página actual se pinta con `bg-weak-50`**, el mismo gris de las superficies de fondo: sobre
138
+ una card gris o un fondo `weak-50` no se distingue cuál es. Ponla sobre blanco.
139
+ - **Con `total` en 0 no dibuja el texto**, pero sí los botones (una sola página «1»). Ocúltalo tú.
140
+ - Las flechas son `<i class="ri-…">`: necesitan la fuente de Remix Icon cargada.
141
+
142
+ ## Ejemplo
143
+
144
+ ```html
145
+ <sui-pagination [total]="134" [pageSize]="10" [(currentPage)]="pagina" type="group" />
146
+ ```
@@ -0,0 +1,99 @@
1
+ ---
2
+ name: sui-password-strength
3
+ selector: sui-password-strength
4
+ resumen: Barra de 3 niveles y lista de condiciones que acompaña al campo de una contraseña nueva
5
+ alternativas: [sui-input con hint (una sola regla, como «Mínimo 6 caracteres»)]
6
+ ---
7
+
8
+ # sui-password-strength
9
+
10
+ Story: [Molecules/Input/Password Strength](https://sui-design-system.web.app/?path=/docs/molecules-input-password-strength--docs)
11
+
12
+ ## Cuándo usarlo / cuándo no
13
+
14
+ **Úsalo** debajo del campo de una **contraseña nueva** cuando hay **dos o más reglas** que cumplir:
15
+ la barra se llena (rojo, naranja, verde) y cada condición se marca a medida que se escribe.
16
+
17
+ **No lo uses** para:
18
+
19
+ - El login: la contraseña ya existe, no se evalúa.
20
+ - Una sola regla («Mínimo 6 caracteres»): alcanza el `hint` del `sui-input`, como hoy en
21
+ `pages/auth/register/register.html:121`.
22
+
23
+ Regla corta: **contraseña nueva con 2+ reglas → este · una regla → hint.**
24
+
25
+ ## Qué variante usar
26
+
27
+ > **En la guía del DS es un ejemplo de su página del input** («Password With Levels»: campo, barra
28
+ > de niveles y «Debe contener al menos» con tres condiciones). No dice cuándo usarlo.
29
+ >
30
+ > Uso medido el 03-10-2026 en `master-pedidos/src/app`: **0 usos**. El registro pide solo 6
31
+ > caracteres (lo que exige Firebase).
32
+
33
+ | prop / evento | cuándo | fuente |
34
+ |---|---|---|
35
+ | `value` | La contraseña que se está escribiendo. | API |
36
+ | `conditions` | **Las reglas reales de la app.** Los defaults (mayúscula, número, 8 caracteres) no son las de Master Pedidos. `[]` deja solo la barra. | según la guía del DS (condiciones bajo la barra); qué reglas, propuesta |
37
+ | `caption` | «Debe contener al menos:» (default). `''` lo oculta. | según la guía del DS |
38
+ | `strength` | Forzar el nivel cuando lo calcula el servidor. Hoy sin caso. | API |
39
+ | `levelLabels` · `ariaLabel` | Lo que lee el lector de pantalla. Los defaults en español sirven. | API |
40
+ | `(strengthChange)` | Apagar «Crear cuenta» hasta llegar a `strong`. | propuesta, falta aprobación |
41
+
42
+ ## Tamaño en mobile y en web
43
+
44
+ No se toca: es texto (12 px) y una barra de 4 px a todo el ancho. **No necesita `bp`.**
45
+
46
+ ## «No usar»: por qué no hoy y cuándo sí
47
+
48
+ | qué | por qué no hoy | cuándo sí, en un flujo nuevo |
49
+ |---|---|---|
50
+ | el componente entero (0 usos) | el registro pide una sola regla (6 caracteres) | si las cuentas con dinero (local, super admin) pasan a exigir contraseña más fuerte |
51
+ | `strength` forzado | nadie calcula el nivel afuera | si el servidor rechaza contraseñas filtradas y devuelve el nivel |
52
+ | condiciones default | no son las reglas de la app | — (siempre pasar las propias) |
53
+
54
+ ## API
55
+
56
+ <!-- api:inicio -->
57
+ <!-- generada: no editar a mano — `npm run docs` -->
58
+
59
+ | input | tipo | default |
60
+ |---|---|---|
61
+ | `value` | `string` | `''` |
62
+ | `strength` | `'empty' \| 'weak' \| 'moderate' \| 'strong' \| null` | `null` |
63
+ | `caption` | `string` | `'Debe contener al menos:'` |
64
+ | `conditions` | `SuiPasswordStrengthCondition[]` | `SUI_PASSWORD_STRENGTH_DEFAULT_CONDITIONS` |
65
+ | `levelLabels` | `Partial<Record<SuiPasswordStrengthLevel, string>>` | `{}` |
66
+ | `ariaLabel` | `string` | `'Seguridad de la contraseña'` |
67
+
68
+ | output | payload |
69
+ |---|---|
70
+ | `strengthChange` | `'empty' \| 'weak' \| 'moderate' \| 'strong'` |
71
+ <!-- api:fin -->
72
+
73
+ Exporta `SUI_PASSWORD_STRENGTH_DEFAULT_CONDITIONS` y `SUI_PASSWORD_STRENGTH_DEFAULT_LEVEL_LABELS`.
74
+
75
+ ```ts
76
+ interface SuiPasswordStrengthCondition {
77
+ label: string;
78
+ test?: RegExp | ((value: string) => boolean);
79
+ met?: boolean; // si está, gana sobre test
80
+ }
81
+ ```
82
+
83
+ ## Trampas
84
+
85
+ - **Las condiciones default piden 8 caracteres, una mayúscula y un número**: más que lo que pide la
86
+ app (6). Sin `[conditions]` propias, la barra marca «débil» contraseñas que el registro acepta.
87
+ - Los segmentos tienen color fijo (1 rojo, 2 naranja, 3 verde): en «fuerte» la barra es de tres
88
+ colores, no toda verde.
89
+ - `strengthChange` no emite el nivel inicial.
90
+ - No incluye el campo: va debajo de un `sui-input type="password-toggle"`.
91
+
92
+ ## Ejemplo
93
+
94
+ ```html
95
+ <sui-input label="Contraseña" type="password-toggle" autocomplete="new-password" [(value)]="clave" />
96
+ <sui-password-strength [value]="clave()" [conditions]="reglas" />
97
+ <!-- reglas = [{ label: 'Al menos 8 caracteres', test: v => v.length >= 8 },
98
+ { label: 'Al menos 1 número', test: /\d/ }] -->
99
+ ```
@@ -0,0 +1,93 @@
1
+ ---
2
+ name: sui-period-range
3
+ selector: sui-period-range
4
+ resumen: Un período predefinido (Hoy, Últimos 7 días) en la columna de presets del calendario de rango
5
+ alternativas: [sui-datepicker mode="range" (ya los trae), sui-tab-chips (períodos fijos a la vista: super admin, ganancias del repartidor), sui-select (períodos fijos en poco espacio: resumen del local)]
6
+ ---
7
+
8
+ # sui-period-range
9
+
10
+ Story: [Molecules/Date & Time/Period Range](https://sui-design-system.web.app/?path=/docs/molecules-date-time-period-range--docs)
11
+
12
+ ## Cuándo usarlo / cuándo no
13
+
14
+ **Úsalo** solo dentro de un **selector de rango** armado a mano, como la columna de atajos al
15
+ costado del calendario.
16
+
17
+ **No lo uses** para:
18
+
19
+ - El rango normal: `sui-datepicker mode="range"` ya pinta los presets con este componente y los
20
+ cambia por `[presets]`.
21
+ - **Cambiar de período en un resumen** («Hoy · 7 días · 30 días»): `sui-tab-chips`, como hoy en
22
+ el super admin (`master-overview.html:13`) y en las ganancias del repartidor; o `sui-select`,
23
+ como el resumen del local (`overview.html:46`).
24
+
25
+ ## Qué variante usar
26
+
27
+ > **La guía del DS no tiene página para este componente** (vive dentro del datepicker). Todo es
28
+ > **propuesta, falta aprobación**.
29
+ >
30
+ > Uso medido el 03-10-2026 en `master-pedidos/src/app`: **0 usos directos**. Lo usa por dentro
31
+ > `sui-datepicker`.
32
+
33
+ | input | valor | cuándo | fuente |
34
+ |---|---|---|---|
35
+ | `label` | texto | El período en palabras de la persona: «Hoy», «Últimos 7 días», «Este mes». Sin fechas: para eso está el calendario al lado. | propuesta |
36
+ | `active` | `true` | El preset que coincide con el rango elegido. Uno solo; si el rango se tocó a mano, ninguno. | propuesta |
37
+ | `disabled` | `true` | El período no tiene datos (un local que abrió hace 10 días con «Últimos 3 meses»). Mejor sacarlo de la lista si nunca va a tener datos. | propuesta |
38
+
39
+ Usos de hoy que rompen la regla: **0**.
40
+
41
+ ## Tamaño en mobile y en web
42
+
43
+ **No tiene `size`.** Mide 168 × 36: en mobile **no llega** a los 44 px. En el datepicker, en
44
+ mobile, los presets pasan a una fila que se desplaza de costado.
45
+
46
+ | uso | mobile | web | fuente |
47
+ |---|---|---|---|
48
+ | preset de un rango | 36 px de alto — **no llega** | 168 × 36 | medido |
49
+
50
+ ```html
51
+ <!-- Web: columna de presets -->
52
+ <sui-period-range label="Últimos 7 días" [active]="preset() === '7d'" (clicked)="aplicar('7d')" />
53
+ ```
54
+
55
+ **Sin tamaño suficiente para mobile.** En el celular, los períodos fijos van en `sui-tab-chips`,
56
+ como ya hacen el super admin y las ganancias del repartidor.
57
+
58
+ ## «No usar»: por qué no hoy y cuándo sí
59
+
60
+ | qué | por qué no hoy | cuándo sí, en un flujo nuevo |
61
+ |---|---|---|
62
+ | todo el componente suelto (0 usos) | los períodos de la app son fijos y van en tab-chips o select | una columna de atajos al lado de un calendario propio, en web |
63
+ | `disabled` | ningún período queda sin datos hoy | presets de un local nuevo («Últimos 12 meses» con 2 semanas de vida) |
64
+
65
+ ## API
66
+
67
+ <!-- api:inicio -->
68
+ <!-- generada: no editar a mano — `npm run docs` -->
69
+
70
+ | input | tipo | default |
71
+ |---|---|---|
72
+ | `label` | `string` | — (requerido) |
73
+ | `active` | `boolean` | `false` |
74
+ | `disabled` | `boolean` | `false` |
75
+
76
+ | output | payload |
77
+ |---|---|
78
+ | `clicked` | `void` |
79
+ <!-- api:fin -->
80
+
81
+ ## Trampas
82
+
83
+ - **No calcula fechas**: solo emite `clicked`. El rango lo resuelve quien lo usa (en el datepicker,
84
+ `DatepickerPreset.resolve`).
85
+ - Ancho fijo de 168 px.
86
+ - `active` no se apaga solo: lo maneja quien lo usa.
87
+
88
+ ## Ejemplo
89
+
90
+ ```html
91
+ <sui-period-range label="Hoy" [active]="true" />
92
+ <sui-period-range label="Últimos 30 días" (clicked)="aplicar('30d')" />
93
+ ```