@ajuarezso/sui-design-system 0.21.0 → 0.23.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 (114) hide show
  1. package/docs/INDEX.md +30 -35
  2. package/docs/sui-accordion/README.md +171 -0
  3. package/docs/sui-activity-feed/README.md +154 -0
  4. package/docs/sui-activity-filter/README.md +100 -0
  5. package/docs/sui-alert/README.md +28 -19
  6. package/docs/sui-app-store-badge/README.md +5 -4
  7. package/docs/sui-avatar/README.md +16 -16
  8. package/docs/sui-avatar-group/README.md +4 -4
  9. package/docs/sui-badge/README.md +27 -9
  10. package/docs/sui-banner/README.md +10 -9
  11. package/docs/sui-brand-logo/README.md +3 -3
  12. package/docs/sui-breadcrumbs/README.md +1 -1
  13. package/docs/sui-button/README.md +64 -21
  14. package/docs/sui-button-group/README.md +6 -4
  15. package/docs/sui-button-group-item/README.md +3 -3
  16. package/docs/sui-card/README.md +17 -19
  17. package/docs/sui-chart-legend/README.md +8 -7
  18. package/docs/sui-checkbox/README.md +11 -6
  19. package/docs/sui-checkbox-card/README.md +24 -19
  20. package/docs/sui-checkbox-label/README.md +32 -10
  21. package/docs/sui-circular-progress-bar/README.md +2 -0
  22. package/docs/sui-color-dot/README.md +1 -1
  23. package/docs/sui-color-picker/README.md +22 -7
  24. package/docs/sui-command-menu-footer/README.md +100 -0
  25. package/docs/sui-command-menu-item/README.md +140 -0
  26. package/docs/sui-command-menu-search/README.md +105 -0
  27. package/docs/sui-compact-avatar-group/README.md +4 -3
  28. package/docs/sui-compact-button/README.md +41 -44
  29. package/docs/sui-compact-select/README.md +12 -5
  30. package/docs/sui-compact-select-for-input/README.md +5 -2
  31. package/docs/sui-content-card/README.md +6 -5
  32. package/docs/sui-content-divider/README.md +4 -4
  33. package/docs/sui-content-label/README.md +11 -6
  34. package/docs/sui-counter-input/README.md +21 -17
  35. package/docs/sui-date-selector/README.md +16 -12
  36. package/docs/sui-datepicker/README.md +46 -28
  37. package/docs/sui-day-cell/README.md +17 -8
  38. package/docs/sui-day-label/README.md +2 -2
  39. package/docs/sui-divider/README.md +5 -5
  40. package/docs/sui-drawer-footer/README.md +19 -5
  41. package/docs/sui-dropdown-caption/README.md +92 -0
  42. package/docs/sui-dropdown-item/README.md +163 -0
  43. package/docs/sui-dropdown-menu/README.md +196 -0
  44. package/docs/sui-dropdown-search/README.md +113 -0
  45. package/docs/sui-emoji/README.md +6 -6
  46. package/docs/sui-empty-state/README.md +13 -23
  47. package/docs/sui-fancy-button/README.md +24 -18
  48. package/docs/sui-file-icon/README.md +1 -1
  49. package/docs/sui-file-upload/README.md +3 -0
  50. package/docs/sui-file-upload-card/README.md +8 -7
  51. package/docs/sui-filter/README.md +181 -0
  52. package/docs/sui-flag/README.md +3 -2
  53. package/docs/sui-form-hint/README.md +12 -8
  54. package/docs/sui-form-label/README.md +11 -16
  55. package/docs/sui-href/README.md +85 -0
  56. package/docs/sui-icon/README.md +4 -3
  57. package/docs/sui-image-upload/README.md +28 -16
  58. package/docs/sui-img-retry/README.md +107 -0
  59. package/docs/sui-inline-input/README.md +7 -6
  60. package/docs/sui-inline-select/README.md +8 -5
  61. package/docs/sui-input/README.md +27 -33
  62. package/docs/sui-integration-switch/README.md +14 -7
  63. package/docs/sui-kbd/README.md +4 -4
  64. package/docs/sui-key-icon/README.md +6 -2
  65. package/docs/sui-link-button/README.md +21 -17
  66. package/docs/sui-modal/README.md +238 -0
  67. package/docs/sui-modal-footer/README.md +178 -0
  68. package/docs/sui-modal-header/README.md +115 -0
  69. package/docs/sui-modal-sheet/README.md +170 -0
  70. package/docs/sui-notification/README.md +8 -6
  71. package/docs/sui-notification-footer/README.md +2 -2
  72. package/docs/sui-notification-tab-menu/README.md +5 -3
  73. package/docs/sui-page-header/README.md +123 -0
  74. package/docs/sui-pagination/README.md +8 -5
  75. package/docs/sui-password-strength/README.md +1 -1
  76. package/docs/sui-period-range/README.md +18 -14
  77. package/docs/sui-popover/README.md +13 -7
  78. package/docs/sui-product-card/README.md +14 -10
  79. package/docs/sui-progress-bar/README.md +4 -2
  80. package/docs/sui-progress-bar-label/README.md +2 -0
  81. package/docs/sui-radio/README.md +14 -7
  82. package/docs/sui-radio-card/README.md +12 -12
  83. package/docs/sui-radio-label/README.md +36 -11
  84. package/docs/sui-rating/README.md +28 -9
  85. package/docs/sui-rich-editor/README.md +3 -3
  86. package/docs/sui-segmented-control/README.md +33 -19
  87. package/docs/sui-select/README.md +40 -33
  88. package/docs/sui-sidebar/README.md +216 -0
  89. package/docs/sui-slider/README.md +7 -0
  90. package/docs/sui-social-button/README.md +24 -11
  91. package/docs/sui-status-badge/README.md +7 -3
  92. package/docs/sui-stepper/README.md +30 -17
  93. package/docs/sui-store-card/README.md +9 -4
  94. package/docs/sui-switch/README.md +16 -11
  95. package/docs/sui-switch-card/README.md +18 -16
  96. package/docs/sui-switch-label/README.md +25 -11
  97. package/docs/sui-tab-chips/README.md +56 -39
  98. package/docs/sui-tab-menu-horizontal/README.md +19 -11
  99. package/docs/sui-tab-menu-vertical/README.md +10 -5
  100. package/docs/sui-table/README.md +264 -0
  101. package/docs/sui-tag/README.md +25 -11
  102. package/docs/sui-tag-input/README.md +25 -12
  103. package/docs/sui-textarea/README.md +23 -20
  104. package/docs/sui-time-picker/README.md +39 -30
  105. package/docs/sui-toast/README.md +27 -22
  106. package/docs/sui-tooltip/README.md +14 -17
  107. package/docs/sui-topbar/README.md +153 -0
  108. package/docs/sui-topbar-button/README.md +95 -0
  109. package/docs/sui-topbar-item/README.md +97 -0
  110. package/docs/sui-topbar-user/README.md +83 -0
  111. package/fesm2022/ajuarezso-sui-design-system.mjs +1076 -242
  112. package/fesm2022/ajuarezso-sui-design-system.mjs.map +1 -1
  113. package/package.json +1 -1
  114. package/types/ajuarezso-sui-design-system.d.ts +616 -280
@@ -47,7 +47,7 @@ Regla corta: **ajuste con texto → este · ajuste con peso → switch-card · s
47
47
  | `sublabel` | Precisión corta entre paréntesis: «(beta)». | según la guía del DS |
48
48
  | `badge` | Solo novedad. Con string sale naranja `light`: pasa el objeto. | según la guía del DS (pieza «NEW»); el color es propuesta |
49
49
  | `link` | «Saber más» que no toca el interruptor. Solo se ve con `description`. | según la guía del DS |
50
- | `flip` | Interruptor a la **derecha** en listas de ajustes: el texto se lee primero y el control queda bajo el pulgar, como en iOS y Android (es lo que hacen a mano `drivers.html:58`, `discounts.html:198`, `schedule.html:46`). Sin `flip` (izquierda) dentro de un formulario, alineado con las casillas o los switches del resto del form. | canon (aprobado por el dueño, 03-10-2026) |
50
+ | `flip` | Interruptor a la **derecha** en listas de ajustes: el texto se lee primero y el control queda bajo el pulgar, como en iOS y Android (es lo que hacen a mano `drivers.html:58`, `discounts.html:207`, `schedule.html:46`). Sin `flip` (izquierda) dentro de un formulario, alineado con las casillas o los switches del resto del form. | canon (aprobado por el dueño, 03-10-2026) |
51
51
  | `disabled` | Mientras se guarda el cambio o cuando no aplica. | según la guía del DS |
52
52
 
53
53
  ### `size`
@@ -60,25 +60,37 @@ Regla corta: **ajuste con texto → este · ajuste con peso → switch-card · s
60
60
 
61
61
  **Usos de hoy que rompen las reglas:**
62
62
 
63
- - **2 en `medium`:** `pages/products/products.html:300` («Horario propio
63
+ - **2 en `medium`:** `pages/products/products.html:301` («Horario propio
64
64
  para esta categoría») y `pages/public/privacidad/privacidad.html:132` («Permitir analítica de uso»).
65
65
  - **19 `sui-switch` con texto a mano + 1 nativo** que deberían ser este componente: el detalle
66
- está en el README de `sui-switch`. Ejemplos: `pages/discounts/discounts.html:198`,
66
+ está en el README de `sui-switch`. Ejemplos: `pages/discounts/discounts.html:207`,
67
67
  `pages/drivers/drivers.html:58`, `components/settings-panel/settings-panel.html:97`.
68
68
 
69
69
  ## Tamaño en mobile y en web
70
70
 
71
- | uso | mobile | web |
72
- |---|---|---|
73
- | fila de ajustes | `xlarge` (48 × 28) | `large` (40 × 24) |
74
- | ajuste que es la acción principal de la pantalla («Cerrar por hoy») | `xlarge` | `xlarge` |
71
+ | uso | mobile | web | fuente |
72
+ |---|---|---|---|
73
+ | fila de ajustes | `2xlarge` (fila de 44 como mínimo, switch 48 × 28) | `large` (40 × 24) | canon (dueño, 04-10-2026): piso táctil 44 en el teléfono |
74
+ | ajuste que es la acción principal de la pantalla («Cerrar por hoy») | `2xlarge` | `xlarge` | canon (dueño, 04-10-2026): piso táctil 44 en el teléfono |
75
75
 
76
76
  ```html
77
- <sui-switch-label [size]="bp.controlSize()" … /> <!-- isMobile() ? 'xlarge' : 'large' -->
77
+ <sui-switch-label [size]="bp.isMobile() ? '2xlarge' : 'large'" … />
78
78
  ```
79
79
 
80
- El bloque entero se toca, así que con descripción la fila pasa los 44 px sola. Sin descripción,
81
- dale a la fila un alto mínimo de 44 px.
80
+ | size | switch | label | descripción | alto de la fila sin descripción |
81
+ |---|---|---|---|---|
82
+ | `medium` (default, Figma) | 32 × 20 | 14/20 | 12/16 | 20 |
83
+ | `large` | 40 × 24 | 16/24 | 12/16 | 24 |
84
+ | `xlarge` | 48 × 28 | 16/24 | 12/16 | 28 |
85
+ | `2xlarge` | 48 × 28 | 16/24 | 12/16 | **44** (`min-height`) |
86
+
87
+ El bloque entero se toca, así que con descripción la fila pasa los 44 px sola. Sin descripción
88
+ `xlarge` mide 28: en el teléfono va `2xlarge` (canon (dueño, 04-10-2026): piso táctil 44 en el teléfono), que es la misma fila con
89
+ `min-height: 44px` y todo centrado en vertical. `2xlarge` no es un size del switch: el switch
90
+ interno se queda en `xlarge`.
91
+
92
+ En large y xlarge el texto sube a 16 px (regla del 03-10-2026): el label va en 16/24; sublabel y
93
+ descripción se quedan en 12.
82
94
 
83
95
  ## «No usar»: por qué no hoy y cuándo sí
84
96
 
@@ -98,7 +110,7 @@ dale a la fila un alto mínimo de 44 px.
98
110
  | `label` | `string` | `''` |
99
111
  | `sublabel` | `string \| null` | `null` |
100
112
  | `description` | `string \| null` | `null` |
101
- | `size` | `'medium' \| 'large' \| 'xlarge'` | `'medium'` |
113
+ | `size` | `'medium' \| 'large' \| 'xlarge' \| '2xlarge'` | `'medium'` |
102
114
  | `badge` | `SuiBadgeConfig \| string \| null` | `null` |
103
115
  | `link` | `SuiLinkConfig \| string \| null` | `null` |
104
116
  | `flip` | `boolean` | `false` |
@@ -124,6 +136,8 @@ Implementa `ControlValueAccessor` (`[(ngModel)]`, `formControlName`).
124
136
  - Deshabilitado baja todo el bloque a 0.5 de opacidad **además** del gris propio del switch.
125
137
  - El badge con string sale naranja; en `sui-switch-card` el default es azul.
126
138
  - `flip` usa CSS `order`: el orden de lectura no cambia.
139
+ - El switch de adentro es el tab stop y toma su nombre del `label` y su descripción de la
140
+ `description` (`aria-labelledby` / `aria-describedby`) (revisión 04-10-2026).
127
141
 
128
142
  ## Ejemplo
129
143
 
@@ -13,8 +13,9 @@ Story: [Molecules/Tabs/Tab Chips](https://sui-design-system.web.app/?path=/docs/
13
13
 
14
14
  **Úsalo** para:
15
15
 
16
- - **Filtrar una lista** por estado, período, rol o zona: «Todos · Activos · Entregados · Cancelados»,
17
- «Hoy · 7 días · 30 días», los meses de la central.
16
+ - **Filtrar una lista** por estado, período, rol o zona con **5 o más opciones**, o con opciones que
17
+ no caben y se deslizan: «Todos · Pendientes · En camino · Entregados · Cancelados», los meses de la
18
+ central.
18
19
  - **Saltar entre categorías** de un catálogo que viene de la base («Hamburguesas · Pizzas ·
19
20
  Bebidas»), con `sticky` en la carta del local.
20
21
 
@@ -22,35 +23,38 @@ Sirve cuando las opciones son muchas, cambian según los datos o no caben y tien
22
23
 
23
24
  **No lo uses** para:
24
25
 
25
- - **2 a 4 modos de la misma vista** (por semana / día vs día): `sui-segmented-control`.
26
+ - **2 a 4 opciones excluyentes**, sean modos de la vista (por semana / día vs día) o un período o
27
+ filtro fijo («Hoy · 7 días · 30 días»): `sui-segmented-control`. **Período o filtro con 2 a 4 opciones excluyentes → `sui-segmented-control`; con 5 o más, o si no caben y tienen que deslizarse → `sui-tab-chips`** (canon, dueño 04-10-2026).
26
28
  - **Secciones de contenido distinto** (Ventas · Productos · Repartidores): `sui-tab-menu-horizontal`.
27
29
  - **Elegir un valor que se guarda** con un botón (radio de visibilidad, motivo de rechazo,
28
- sumar o descontar): `sui-radio-card` o `sui-segmented-control`. El tab-chips **siempre tiene uno
29
- elegido** y no puede quedar vacío (ver Trampas).
30
+ sumar o descontar): `sui-radio-card`. (`sui-segmented-control` no sirve para esto: su README lo
31
+ deja para opciones que se aplican en el momento.) El tab-chips **siempre tiene uno elegido** y no
32
+ puede quedar vacío (ver Trampas).
30
33
  - Un chip que la persona quita: `sui-tag`.
31
34
 
32
- Regla corta (la misma del README de `sui-segmented-control`, 03-10-2026): **2-4 modos de la misma
33
- vista → segmented · secciones → tab menu · filtros o categorías → tab-chips.**
35
+ Regla corta (canon, dueño 04-10-2026; la misma del README de `sui-segmented-control`): **2-4
36
+ opciones excluyentes (modo, período o filtro) → segmented · 5+ o que deslizan → tab-chips ·
37
+ secciones → tab menu.**
34
38
 
35
39
  ## Qué variante usar
36
40
 
37
41
  > **La guía del DS no tiene este componente**: es una extensión de SUI (la «cat nav» de las apps de
38
42
  > delivery). Todo lo que sigue es propuesta salvo lo marcado como canon.
39
43
  >
40
- > Uso medido el 03-10-2026 en `master-pedidos/src/app` (sin `master-preview`, `master-prototipos`,
41
- > `mockup-*`, `dx/*`, `_guia-*`, `master-rutina`, `master-medico`, `master-ejercicios`):
42
- > **41 usos en 28 archivos `.html`**. `[sticky]="true"`: 1 (carta del local). `count`: 5 listas.
43
- > `[(value)]`: 2; el resto `[value]` + `(valueChange)`. `autoScroll`: 0. `size`: 0 (no existe en
44
- > la versión publicada).
44
+ > Uso medido el 04-10-2026 en `master-pedidos/src/app` (sin `master-preview`, `master-prototipos`,
45
+ > `mockup-sheets-*`, `dx/*`, `master-rutina`, `master-medico`, `dev-index`):
46
+ > **42 usos en 29 archivos `.html`** (incluye `master-ejercicios`). `[sticky]="true"`: 2 (la carta
47
+ > del local y `master-ejercicios`). `count`: 5 listas (cifra del 03-10). `[(value)]`: 2; el resto
48
+ > `[value]` + `(valueChange)`. `autoScroll`: 0. `size`: 20, todos `bp.isMobile() ? 'large' : 'medium'`.
45
49
 
46
50
  ### Para qué se usa (el contenido de los chips)
47
51
 
48
52
  | caso | cuándo | ejemplo real de la app | fuente |
49
53
  |---|---|---|---|
50
54
  | filtro de estado | La lista es la misma; los chips la recortan. «Todos» va primero y es el elegido por defecto. | `pages/envios/envios.html:98` («Todos · Activos · Entregados · Cancelados») · `pages/orders/orders.html:64` | propuesta, falta aprobación |
51
- | filtro de período | Rango de fechas de un reporte o gráfico. | `pages/super-admin/master-earnings/master-earnings.html:11` · `pages/central/central-gastos/central-gastos.html:10` (meses) | propuesta, falta aprobación |
55
+ | filtro de período | Rango de fechas de un reporte o gráfico **con 5 o más opciones** (los meses). Con 2 a 4 («Hoy · 7 días · 30 días») va `sui-segmented-control`. | `pages/central/central-gastos/central-gastos.html:10` (meses) | canon (dueño, 04-10-2026) |
52
56
  | categorías del catálogo | Saltar a una categoría de la carta; con `sticky` y `autoScroll`. | `pages/customer/restaurant-menu/restaurant-menu.html:226` | propuesta, falta aprobación |
53
- | sugerencias rápidas | **No.** Un motivo frecuente que completa un texto no es una selección: el chip elige el primero solo. Si se usa, el primer chip es el neutro («Otro motivo») y el texto lleva su propio signal. | `pages/super-admin/master-orders/master-orders.html:898` (bien resuelto) | propuesta, falta aprobación |
57
+ | sugerencias rápidas | **No.** Un motivo frecuente que completa un texto no es una selección: el chip elige el primero solo. Si se usa, el primer chip es el neutro («Otro motivo») y el texto lleva su propio signal. | `pages/super-admin/master-orders/master-orders.html:900` (bien resuelto) | propuesta, falta aprobación |
54
58
 
55
59
  ### `sticky`
56
60
 
@@ -77,47 +81,54 @@ vista → segmented · secciones → tab menu · filtros o categorías → tab-c
77
81
 
78
82
  **Usos de hoy que rompen las reglas:**
79
83
 
80
- - **3 con un bug real: el motivo escrito a mano rebota al primer chip.** En los modales de rechazo
81
- del super admin, los chips y el textarea comparten el mismo signal. Cada tecla deja un texto que
82
- no es ningún chip; el componente vuelve al primero y lo emite, así que el textarea se reescribe con
83
- «El comprobante no se lee». No se puede escribir un motivo propio, y al abrir el modal ya hay un
84
- motivo elegido sin que nadie lo toque. `pages/super-admin/master-drivers/master-drivers.html:652`,
85
- `:924` y `:1002`. Es el mismo bug que se arregló en `master-orders` el 01-09-2026 (signal propio
86
- para los chips y «Otro motivo» primero).
84
+ - **Resuelto el 03-10-2026: el motivo escrito a mano rebotaba al primer chip.** En los modales de
85
+ rechazo del super admin (`pages/super-admin/master-drivers/master-drivers.html:657`, `:933` y
86
+ `:1012`) los chips y el textarea compartían el signal. Ahora los chips llevan el suyo
87
+ (`master-drivers/motivo-con-chips.ts`, «Otro motivo» primero), igual que `master-orders` desde el
88
+ 01-09-2026.
87
89
  - **4 son secciones de contenido distinto** (→ `sui-tab-menu-horizontal` en web; en mobile, a
88
90
  discutir porque el tab menu no se desliza, ver su README): `pages/products/products.html:6`
89
91
  («Categorías · Productos · Secciones reutilizables»), `pages/promociones/promociones.html:7`
90
92
  («Carrusel pagado · Push notifications»), `pages/super-admin/master-drivers/master-drivers.html:16`,
91
- `pages/reports/reports.html:98` («Ventas · Productos · Repartidores · Clientes»).
93
+ `pages/reports/reports.html:99` («Ventas · Productos · Repartidores · Clientes»).
92
94
  - **2 son modos de la misma vista** (→ `sui-segmented-control`):
93
- `pages/super-admin/master-semanas/master-semanas.html:67` (3 métricas del mismo gráfico) y `:72`
95
+ `pages/super-admin/master-semanas/master-semanas.html:75` (3 métricas del mismo gráfico) y `:80`
94
96
  («Por semana · Día vs día»).
95
- - **4 eligen un valor de un formulario** (→ `sui-radio-card` o `sui-segmented-control`):
96
- `components/option-sections-manager/option-sections-manager.html:175` (a qué se aplica),
97
- `pages/super-admin/master-drivers/master-drivers.html:98` («Sumar · Descontar»),
98
- `pages/mi-negocio/mi-negocio.html:938` (radio de visibilidad) y `:973` (tiempo para confirmar).
99
- A discutir: `pages/customer/checkout/checkout.html:468` (día del pedido programado), que filtra
97
+ - **4 eligen un valor de un formulario** (→ `sui-radio-card`; `sui-segmented-control` solo si se
98
+ aplica en el momento, sin botón «Guardar»):
99
+ `components/option-sections-manager/option-sections-manager.html:177` (a qué se aplica),
100
+ `pages/super-admin/master-drivers/master-drivers.html:99` («Sumar · Descontar»),
101
+ `pages/mi-negocio/mi-negocio.html:952` (radio de visibilidad) y `:988` (tiempo para confirmar).
102
+ A discutir: `pages/customer/checkout/checkout.html:460` (día del pedido programado), que filtra
100
103
  las franjas de abajo y se parece más a un filtro.
101
- - **1 con el número dentro del texto:** `pages/notifications/notifications.ts:191`
104
+ - **1 con el número dentro del texto:** `pages/notifications/notifications.ts:194`
102
105
  (`No leídas (3)` → `count: 3`).
103
- - **41 sin tamaño por plataforma** (el input todavía no está publicado): cuando se publique, los de
104
- pantallas del cliente y del repartidor pasan a `large`.
106
+ - **22 sin tamaño por plataforma.** Los 20 que ya lo tienen (`bp.isMobile() ? 'large' : 'medium'`)
107
+ entraron el 03-10-2026; los 22 que faltan son casi todos del super admin y de la central.
105
108
 
106
109
  ## Tamaño en mobile y en web
107
110
 
108
- > `size` está **en el repo del DS (03-10-2026) y no en la versión publicada 0.19.0**. Hasta que se
109
- > publique, todos miden lo de `medium`.
111
+ `size` está publicado desde la 0.20.0; la app usa la 0.22.0.
110
112
 
111
- Alto del chip: `small` 32 px · `medium` 36 px (el de siempre, default) · `large` 40 px. Los tres
112
- con texto de 14 px; cambia el alto y el aire lateral. El scroller suma 12 px arriba y abajo.
113
+ Alto del chip: `small` 32 px (`height: 32px`) · `medium` 36 px (el de siempre, default; sin alto
114
+ fijo: 8 + 8 de padding + `line-height: normal` + 2 de borde, 36 medido en Storybook con Satoshi) ·
115
+ `large` 40 px (`height: 40px`, con `border-box`) · `xlarge` 44 px (`height: 44px`, padding lateral 18).
116
+ `small` y `medium` con texto de 14 px; cambia el alto y el aire lateral. El scroller suma 12 px arriba
117
+ y abajo, pero ese aire es del `<nav>`, no del botón: tocar ahí no elige el chip.
118
+
119
+ En large y xlarge el texto sube a 16 px (regla del 03-10-2026): el chip va en 16/24. El número del
120
+ contador mide 11 px en `small` y `medium`, y 12 px (pastilla de 20) en `large` y `xlarge`.
121
+
122
+ **Área táctil: 44 px en `xlarge`**, el piso del teléfono (canon del dueño, 04-10-2026: piso táctil
123
+ 44 en el teléfono). `large` (40) queda para pantallas que ya lo usan y para tablet.
113
124
 
114
125
  | uso | mobile | web | fuente |
115
126
  |---|---|---|---|
116
- | filtros y categorías | `large` (40; con el aire del scroller llega a 44+ de área táctil) | `medium` | canon (dueño: tamaño por plataforma, 03-10-2026) |
127
+ | filtros y categorías | `xlarge` (44) | `medium` | canon (dueño, 04-10-2026): piso táctil 44 en el teléfono; tamaño por plataforma, 03-10-2026 |
117
128
  | filtros densos en la cabecera de una tabla | — | `small` | propuesta, falta aprobación |
118
129
 
119
130
  ```html
120
- <sui-tab-chips [chips]="filtros" [(value)]="filtro" [size]="bp.isMobile() ? 'large' : 'medium'" />
131
+ <sui-tab-chips [chips]="filtros" [(value)]="filtro" [size]="bp.isMobile() ? 'xlarge' : 'medium'" />
121
132
  ```
122
133
 
123
134
  ## «No usar»: por qué no hoy y cuándo sí
@@ -138,7 +149,7 @@ con texto de 14 px; cambia el alto y el aire lateral. El scroller suma 12 px arr
138
149
  | `chips` | `readonly (string \| SuiTabChipItem)[]` | — (requerido) |
139
150
  | `value` · model, `[(value)]` | `string` | `''` |
140
151
  | `sticky` | `boolean` | `false` |
141
- | `size` | `'small' \| 'medium' \| 'large'` | `'medium'` |
152
+ | `size` | `'small' \| 'medium' \| 'large' \| 'xlarge'` | `'medium'` |
142
153
  | `autoScroll` | `boolean` | `true` |
143
154
 
144
155
  Outputs: ninguno.
@@ -150,7 +161,7 @@ interface SuiTabChipItem {
150
161
  label: string;
151
162
  count?: number; // 0 o undefined no se dibuja
152
163
  }
153
- type SuiTabChipsSize = 'small' | 'medium' | 'large'; // repo del DS, no en 0.19.0
164
+ type SuiTabChipsSize = 'small' | 'medium' | 'large';
154
165
  ```
155
166
 
156
167
  ## Trampas
@@ -162,6 +173,12 @@ type SuiTabChipsSize = 'small' | 'medium' | 'large'; // repo del DS, no en 0.1
162
173
  - **`value` es `string`.** Para números (km, minutos) hay que convertir en los dos sentidos
163
174
  (`radioEfectivo() + ''` y `+$event`).
164
175
  - **Lleva `role="tablist"`** aunque se use como filtro: el lector de pantalla lo anuncia como pestañas.
176
+ - **Teclado (04-10-2026):** un solo tab stop (el chip activo). ← → mueven el foco entre chips con
177
+ vuelta al inicio, Home/End van al primero/último, y Enter/Espacio elige. Las flechas **no**
178
+ seleccionan (activación manual): cada filtro recarga datos y no se dispara una carga por chip
179
+ recorrido. Al salir del grupo, el tab stop vuelve al chip activo.
180
+ - **La cuenta va en el nombre accesible** («Pendientes, 3»): el número visible sigue
181
+ `aria-hidden` para que no se lea dos veces.
165
182
  - **No tiene padding lateral propio**: lo pone la pantalla. Sí trae 12 px arriba y abajo.
166
183
  - **`sticky` cambia el host a `display: contents`**: las clases que le pongas al host dejan de
167
184
  pintar caja (márgenes, anchos). Pon el estilo en un contenedor.
@@ -49,18 +49,19 @@ segmented · secciones → tab menu · filtros → tab-chips.**
49
49
 
50
50
  ### `size`
51
51
 
52
- | size | cuándo | fuente |
53
- |---|---|---|
54
- | `medium` (default) | El único que cambia algo: **ninguno**. El input existe pero no tiene estilos; los cuatro valores se ven igual. No lo pases. | según el código (`sui-tab-menu-horizontal.scss`: no hay reglas por tamaño) |
55
- | `small` · `large` · `xlarge` | No usar: no hacen nada. | según el código |
52
+ Los cuatro valores (`small`, `medium`, `large`, `xlarge`) cambian el alto de la franja y el texto
53
+ desde el 03-10-2026 (`sui-tab-menu-horizontal.scss`, bloque «Tamaños»). Cuándo usar cada uno: ver
54
+ «Tamaño en mobile y en web».
56
55
 
57
56
  **Usos de hoy que rompen las reglas:**
58
57
 
59
58
  - **0** en este componente. Los casos que deberían ser tab menu y hoy son `sui-tab-chips` están en el
60
59
  README de `sui-tab-chips` (4: `pages/products/products.html:6`, `pages/promociones/promociones.html:7`,
61
- `pages/super-admin/master-drivers/master-drivers.html:16`, `pages/reports/reports.html:98`).
62
- - **A verificar en mobile:** el uso actual es del modal del local, que el cliente abre en el
63
- teléfono. La pestaña mide 20 px de alto tocable (ver Tamaño).
60
+ `pages/super-admin/master-drivers/master-drivers.html:16`, `pages/reports/reports.html:99`).
61
+ - **A discutir, 1 sin tamaño por plataforma:** el único uso
62
+ (`components/store-ratings-modal/store-ratings-modal.html:43`) es del modal del local, que el
63
+ cliente abre en el teléfono, y no pasa `size`: mide 48 px tocables (`medium`). Llega a 44, pero la
64
+ propuesta de abajo pide `large` en mobile.
64
65
 
65
66
  ## Tamaño en mobile y en web
66
67
 
@@ -69,10 +70,13 @@ Cada pestaña ocupa **todo el alto de la franja**, así que lo tocable es la fra
69
70
 
70
71
  | size | franja tocable (+ 2 de bordes) | texto | ícono | gap | fuente |
71
72
  |---|---|---|---|---|---|
72
- | `small` | 40 | `label-sm` 14/20 | 20 | 20 | canon (aprobado por el dueño, 03-10-2026): extensión SUI |
73
- | `medium` (default) | 48 | `label-sm` 14/20 | 20 | 24 | Figma |
74
- | `large` | 52 | `label-md` 16/24 | 20 | 24 | canon (aprobado por el dueño, 03-10-2026): extensión SUI |
75
- | `xlarge` | 56 | `label-md` 16/24 | 24 | 28 | canon (aprobado por el dueño, 03-10-2026): extensión SUI |
73
+ | `small` | 40 (10 + 20 + 10) | `label-sm` 14/20 | 20 | 20 | canon (aprobado por el dueño, 03-10-2026): extensión SUI |
74
+ | `medium` (default) | 48 (14 + 20 + 14) | `label-sm` 14/20 | 20 | 24 | Figma |
75
+ | `large` | 52 (14 + 24 + 14) | `label-md` 16/24 | 20 | 24 | canon (aprobado por el dueño, 03-10-2026): extensión SUI |
76
+ | `xlarge` | 56 (16 + 24 + 16) | `label-md` 16/24 | 24 | 28 | canon (aprobado por el dueño, 03-10-2026): extensión SUI |
77
+
78
+ El alto sale del `padding` vertical del botón más el `line-height` del texto. A lo ancho, lo tocable
79
+ es solo el texto: el `gap` entre pestañas no responde.
76
80
 
77
81
  | uso | mobile | web | fuente |
78
82
  |---|---|---|---|
@@ -128,6 +132,10 @@ interface SuiTabMenuHorizontalItem<T = string> {
128
132
  que Figma pone en el contenedor ahora va dentro de cada pestaña. Si algo de afuera le pone
129
133
  padding al `.sui-tab-menu-horizontal__track`, la franja crece al doble.
130
134
  - **Emite dos veces**: `tabChange` y `activeChange`. Con `[(active)]` no hace falta `(tabChange)`.
135
+ - **Teclado (04-10-2026):** un solo tab stop (la pestaña activa). ← → mueven el foco **y activan**
136
+ (patrón de tablist con activación automática), con vuelta al inicio y saltando las deshabilitadas;
137
+ Home/End a los extremos. Cada flecha emite `tabChange`. No hay `aria-controls`: el `tabpanel` lo
138
+ arma quien lo usa.
131
139
  - **Trae bordes arriba y abajo** (`stroke-soft-200`) y fondo blanco: no lo metas en una card que ya
132
140
  tiene borde, o quedan dos líneas.
133
141
  - El ícono de la pestaña activa es `strong-950`, no naranja; en el tab menu vertical es naranja.
@@ -23,8 +23,8 @@ de pago · Repartidores»), el perfil del super admin.
23
23
  - 2 a 4 secciones arriba del contenido: `sui-tab-menu-horizontal`.
24
24
  - Filtros de una lista: `sui-tab-chips`.
25
25
 
26
- Regla corta: **secciones de una página de ajustes en web → tab menu vertical · en mobile → lista de
27
- filas que abren pantalla.**
26
+ Regla corta (propuesta, falta aprobación): **secciones de una página de ajustes en web → tab menu
27
+ vertical · en mobile → lista de filas que abren pantalla.**
28
28
 
29
29
  ## Qué variante usar
30
30
 
@@ -63,7 +63,8 @@ Usos de hoy que rompen las reglas: **0** (no se usa).
63
63
 
64
64
  ## Tamaño en mobile y en web
65
65
 
66
- Ítem de **36 px** (8 + 20 + 8), ícono de 20. **No tiene `size`** y no tiene tamaño para mobile.
66
+ Ítem de **36 px** (padding 8 + `line-height` 20 + padding 8; el ícono de 20 entra en la línea).
67
+ **No tiene `size`** y no tiene tamaño para mobile: no llega a los 44 px del dedo.
67
68
 
68
69
  | uso | mobile | web | fuente |
69
70
  |---|---|---|---|
@@ -114,12 +115,16 @@ type SuiTabMenuVerticalStyle = 'card' | 'list';
114
115
 
115
116
  ## Trampas
116
117
 
117
- - **El estilo se llama `menuStyle`**, no `style` ni `variant` (ni `verticalStyle`, que era el nombre
118
- viejo de `sui-tabs`).
118
+ - **El estilo se llama `menuStyle`**, no `style` ni `variant` (ni `verticalStyle`, el input de
119
+ `sui-tabs variant="vertical"`, el componente que existió hasta la v0.2.x y ya no está en el DS).
119
120
  - **Sin `active` no se marca ningún ítem.** Pásale un valor inicial.
120
121
  - **Ocupa el ancho del padre**: dale un ancho fijo a la columna (240-280 px).
121
122
  - **El ícono activo es naranja** (`primary-base`); en el tab menu horizontal es negro. Es a propósito.
122
123
  - **Emite dos veces**: `tabChange` y `activeChange`.
124
+ - **Teclado (04-10-2026):** `role="tablist"` con `aria-orientation="vertical"` y un solo tab
125
+ stop. ↑ ↓ mueven el foco **y activan**, con vuelta al inicio y saltando los deshabilitados;
126
+ Home/End a los extremos. Con `dividerLabel`, ese texto es el nombre del grupo
127
+ (`aria-labelledby`).
123
128
  - `role="tablist"`: si cada ítem navega a una ruta, el lector de pantalla lo anuncia mal; para
124
129
  navegación entre rutas, `sui-sidebar`.
125
130
 
@@ -0,0 +1,264 @@
1
+ ---
2
+ name: sui-table
3
+ selector: sui-table
4
+ resumen: Tabla de datos — una fila por registro, una columna por dato — para comparar y recorrer listas en web
5
+ alternativas: [tarjetas en grilla con sui-card (la misma lista cuando no entran las columnas), sui-empty-state (no hay nada que listar todavía), sui-pagination (paginar por número de página, debajo de la tabla), sui-activity-feed (historial de quién hizo qué)]
6
+ ---
7
+
8
+ # sui-table
9
+
10
+ Story: [Organisms/Table](https://sui-design-system.web.app/?path=/docs/organisms-table--docs)
11
+
12
+ ## Cuándo usarlo / cuándo no
13
+
14
+ **Úsalo** cuando la persona tiene que **comparar varios registros por las mismas columnas**:
15
+ pedidos, repartidores, gastos, recargas. La tabla sirve si leer en vertical una columna
16
+ («¿cuál es el monto más alto?», «¿quién no tiene repartidor?») es parte de la decisión.
17
+
18
+ **No lo uses** para:
19
+
20
+ - Una lista donde cada registro se lee solo (sin comparar con el de al lado): tarjetas.
21
+ - Pantallas del cliente o del repartidor en el teléfono: ahí la lista va en tarjetas o
22
+ filas de una columna. La tabla es de las pantallas de gestión (empresa, super admin, central).
23
+ - Un historial de acciones con autor y hora: `sui-activity-feed`.
24
+ - «Todavía no hay nada» a página completa: `sui-empty-state`. La tabla tiene su propio
25
+ vacío (`emptyMessage`), que es otro registro (ver abajo).
26
+
27
+ Regla corta: **comparar por columnas → tabla · leer de a uno → tarjetas · historial → activity feed.**
28
+
29
+ ## Qué variante usar
30
+
31
+ > **La guía del DS casi no dice cuándo ni cómo usar la tabla.** Su página la define como «una
32
+ > estructura de dos dimensiones donde cada fila es un ítem y cada columna un dato del ítem»,
33
+ > muestra un solo ejemplo (un directorio de personas **con selección por checkbox**), le pone
34
+ > `overflow-x-auto` al contenedor y deriva el orden, la selección y la paginación a una
35
+ > librería externa. No trae do/don't, tamaños ni comportamiento en mobile. Casi todo lo de
36
+ > abajo es **práctica de la app** o **propuesta, falta aprobación**; lo marcado «canon» sale de
37
+ > `master-pedidos/CLAUDE.md` o de `docs/CANON-SUI.md`.
38
+ >
39
+ > Uso medido el 03-10-2026 en `master-pedidos/src/app`: **9 tablas** en 7 pantallas, sin contar
40
+ > prototipos ni vitrinas (`master-preview`, `master-prototipos`, `mockup-sheets-*`, `dx/*`,
41
+ > `master-rutina`, `master-medico`, `dev-index`). `sortable`, `selectable` y `striped`: 0 usos.
42
+ > `sticky`: 1, en una vitrina.
43
+
44
+ ### Lo que sí dice la guía del DS
45
+
46
+ - **Qué es**: filas = ítems, columnas = datos del ítem.
47
+ - **Partes**: cabecera, filas con hover, celdas con esquinas redondeadas y un **divisor entre
48
+ filas**. SUI lo replica (header gris `bg-weak-50`, filas con radio 12 y divisor de 1 px).
49
+ - **Ancho**: el contenedor scrollea en horizontal (`overflow-x-auto`). En SUI eso pasa solo
50
+ **hasta 767 px** o con una columna `sticky` (ver Trampas).
51
+ - **Selección**: su único ejemplo trae checkbox por fila. En SUI es `selectable`.
52
+
53
+ ### Las columnas (`SuiTableColumn`)
54
+
55
+ | prop de columna | cuándo | ejemplo real de la app | fuente |
56
+ |---|---|---|---|
57
+ | `priority: 'leading'` + `descriptionKey` | La **primera** columna: el dato que identifica la fila arriba (14/500) y un dato de apoyo debajo (12/400). Se usa en vez de una plantilla propia para la columna principal. | `pages/orders/orders.ts:278` (pedido + «hace 12 min») · `pages/super-admin/master-orders/master-orders.ts:390` | práctica de la app (7 de 9 tablas) |
58
+ | `priority: 'passive'` | Datos de contexto que no deciden nada: fecha, número de comprobante. Se leen en gris (`text-sub-600`). | `pages/super-admin/master-overview/master-overview.ts:192` | propuesta, falta aprobación |
59
+ | `priority: 'none'` | Columna de acciones (íconos) sin texto propio. Con `label: ''` y `width` fijo. | `pages/super-admin/master-gastos/master-gastos.ts:143` | práctica de la app |
60
+ | `align: 'right'` | Montos. Así se comparan en vertical. | `pages/central/central-gastos/central-gastos.ts:82` | propuesta, falta aprobación |
61
+ | `width` | Columnas de largo fijo: monto, estado, fecha, acciones. | `central-gastos.ts:82-83` | práctica de la app |
62
+ | `minWidth` | Columnas de texto que crecen (nombre, concepto): el piso para que no se aplasten. | `master-orders.ts:392` | práctica de la app |
63
+ | `maxWidth` + `truncate` | Nombres que pueden ser muy largos: corta con «…» y el valor completo sale en tooltip. | `master-overview.ts:193` | práctica de la app |
64
+
65
+ **Una ID nunca es el título de la fila** (`CLAUDE.md`, «Pasada de UX/UI»): la columna `leading`
66
+ lleva el nombre o el número legible del pedido, no el uid.
67
+
68
+ ### Plantillas por columna (`suiTableCell`)
69
+
70
+ Si una celda necesita algo más que texto, se proyecta un `<ng-template suiTableCell="<key>" let-row>`.
71
+ Dentro va un componente SUI, no HTML a mano:
72
+
73
+ | contenido | componente | fuente |
74
+ |---|---|---|
75
+ | estado o categoría | `<sui-badge variant="lighter" size="small">` | canon (CANON-SUI §0: «Chip de estado») |
76
+ | acciones de la fila | `<sui-button variant="ghost" color="neutral" size="2xsmall" [onlyIcon]="true">` | canon (README de `sui-button`: tabla densa = `2xsmall`) |
77
+ | prender/apagar | `<sui-switch>` | propuesta, falta aprobación |
78
+ | calificación | `<sui-rating>` de solo lectura | propuesta, falta aprobación |
79
+
80
+ Si la columna tiene plantilla, `descriptionKey` y `truncate` **no se aplican**: solo funcionan en
81
+ el render de texto plano. Por eso `orders.html:94` deja la columna del pedido sin plantilla.
82
+
83
+ ### Cargar las filas: cuenta + página por cursor
84
+
85
+ **Canon** (`master-pedidos/CLAUDE.md`, «Performance — Firestore reads»): una pantalla con tabla
86
+ **no baja la colección entera** para mostrar 30 filas. Los totales salen de `getCount`, las filas
87
+ de `getCollectionPage` con cursor, y debajo de la tabla va un «Cargar más»
88
+ (`sui-button variant="stroke" color="neutral"`). Referencia: `pages/orders/orders.ts:716`
89
+ (`loadMore`) y `orders.html:132`.
90
+
91
+ - El orden de la tabla lo fija la consulta (`orderBy`). **No uses `sortable`** sobre una lista
92
+ paginada: ordenaría solo lo cargado y mentiría sobre el resto.
93
+ - `sui-pagination` (números de página) no calza con cursor: Firestore no salta a «la página 7».
94
+
95
+ ### Vacío y carga
96
+
97
+ - `loading` pinta 5 filas esqueleto con las columnas reales. Úsalo mientras llega la primera
98
+ página. Canon (memoria del proyecto: skeleton obligatorio en pantalla nueva).
99
+ - `emptyMessage` es una línea con un ícono de bandeja, sin ilustración ni botón. **Canon**
100
+ (CANON-SUI §5.6): no se mezcla con `sui-empty-state`. Escríbelo como en la app, una frase con
101
+ punto: «Nadie recargó en este período.», «Sin gastos registrados todavía.». El default
102
+ `'No hay datos'` no dice nada: siempre se pasa uno propio (los 9 usos lo hacen).
103
+
104
+ ## Tamaño en mobile y en web
105
+
106
+ `size` cambia **el alto mínimo de la fila**, no la letra (14 px en todos):
107
+
108
+ | size | alto | nombre en el Figma | fuente |
109
+ |---|---|---|---|
110
+ | `small` | 48 | Large | Figma |
111
+ | `medium` (default) | 64 | X-Large | Figma |
112
+ | `large` | 80 | — | extensión SUI |
113
+ | `xlarge` | 96 | — | extensión SUI |
114
+
115
+ Uso de hoy: 2 `medium` explícitos (`orders`, `drivers`), 1 `small` (`master-overview`, una tabla
116
+ secundaria dentro de un tablero) y 6 sin `size` (o sea `medium`).
117
+
118
+ | uso | mobile | web | fuente |
119
+ |---|---|---|---|
120
+ | tabla principal de una pantalla | `medium` | `medium` | práctica de la app |
121
+ | tabla secundaria dentro de un tablero | `small` | `small` | práctica de la app (1 uso) |
122
+ | filas con dos líneas y un badge | `medium` | `medium` | propuesta, falta aprobación |
123
+
124
+ El patrón `[size]="bp.isMobile() ? 'large' : 'medium'"` de los botones **no aplica aquí**: la fila
125
+ ya mide 64 y el toque es sobre toda la fila. `large`/`xlarge` no tienen uso hoy.
126
+
127
+ **Qué pasa en mobile (≤ 767 px):** la tabla scrollea en horizontal dentro de sí misma; la
128
+ cabecera y el cuerpo miden lo mismo y la barra de scroll se oculta. En la app conviven dos
129
+ salidas, y las dos son práctica, no regla:
130
+
131
+ | salida | dónde | cuándo elegirla (propuesta) |
132
+ |---|---|---|
133
+ | la misma tabla, con scroll lateral | `orders`, `master-orders`, `central-gastos`, `central-ingresos` | 3-4 columnas, y la primera (`leading`) ya dice casi todo |
134
+ | tarjetas en grilla por debajo de 1350 px | `drivers` (`drivers.ts:282`), `master-gastos` | 6+ columnas: con el sidebar abierto no entran ni en laptop ni en iPad |
135
+
136
+ **Android (canon del componente, no se repite en la página):** un contenedor con
137
+ `overflow-x: auto` se queda con el gesto vertical y la página deja de scrollear sobre la tabla.
138
+ Ningún `touch-action` solo lo arregla (probado en device). `sui-table` lo resuelve **por dentro y
139
+ solo en Android**: pone `touch-action: pan-x` por JS para que el scroll horizontal sea el nativo,
140
+ y mueve él mismo la página con el gesto vertical (con inercia). En iOS no hace nada: WebKit delega
141
+ el vertical solo. Por eso la página **no** agrega `touch-action` ni un wrapper con `overflow-x`.
142
+
143
+ ## «No usar»: por qué no hoy y cuándo sí
144
+
145
+ | qué | por qué no hoy | cuándo sí, en un flujo nuevo |
146
+ |---|---|---|
147
+ | `sortable` | toda tabla de la app es una lista paginada por cursor: el orden lo da la consulta | una tabla chica que se carga entera (≤ 50 filas, un reporte del mes) |
148
+ | `selectable` | no hay acciones en lote | aprobar o rechazar varias recargas de una vez, exportar filas elegidas |
149
+ | `striped` | con divisor entre filas ya se lee la fila; las rayas suman ruido | tablas de muchas columnas numéricas (un reporte contable) |
150
+ | `sticky` | la columna fija tapó otra en 375 px (`central-gastos.ts:80`) | una tabla ancha en desktop donde la primera columna es la única referencia |
151
+ | `large` / `xlarge` | 64 ya alcanza al dedo | filas con avatar grande y tres líneas |
152
+
153
+ ## API
154
+
155
+ <!-- api:inicio -->
156
+ <!-- generada: no editar a mano — `npm run docs` -->
157
+
158
+ ### `sui-table`
159
+
160
+ | input | tipo | default |
161
+ |---|---|---|
162
+ | `columns` | `SuiTableColumn<T>[]` | — (requerido) |
163
+ | `rows` | `T[]` | — (requerido) |
164
+ | `size` | `'small' \| 'medium' \| 'large' \| 'xlarge'` | `'medium'` |
165
+ | `loading` | `boolean` | `false` |
166
+ | `emptyMessage` | `string` | `'No hay datos'` |
167
+ | `striped` | `boolean` | `false` |
168
+ | `selectable` | `boolean` | `false` |
169
+ | `rowKey` | `keyof T \| ((row: T) => any)` | `undefined` |
170
+ | `selectedRows` · model, `[(selectedRows)]` | `Set<any>` | `new Set()` |
171
+ | `sortKey` · model, `[(sortKey)]` | `string \| null` | `null` |
172
+ | `sortDir` · model, `[(sortDir)]` | `'asc' \| 'desc' \| null` | `null` |
173
+
174
+ | output | payload |
175
+ |---|---|
176
+ | `rowClicked` | `T` |
177
+ | `sortChanged` | `{ key: string; dir: 'asc' \| 'desc' \| null }` |
178
+
179
+ ### `[suiTableCell]`
180
+
181
+ | input | tipo | default |
182
+ |---|---|---|
183
+ | `suiTableCell` | `string` | — (requerido) |
184
+
185
+ Outputs: ninguno.
186
+ <!-- api:fin -->
187
+
188
+ ```ts
189
+ interface SuiTableColumn<T = any> {
190
+ key: string; // campo de la fila; único
191
+ label: string; // '' para la columna de acciones
192
+ width?: string; // ancho fijo ('100px', '20%')
193
+ minWidth?: string; // piso (manda en el scroll de mobile)
194
+ maxWidth?: string;
195
+ align?: 'left' | 'center' | 'right';
196
+ sortable?: boolean;
197
+ disabled?: boolean; // cabecera apagada, sin orden
198
+ priority?: 'regular' | 'leading' | 'passive' | 'none';
199
+ descriptionKey?: string; // segunda línea (solo texto plano)
200
+ truncate?: boolean; // «…» + tooltip (solo texto plano)
201
+ misc?: SuiTableCellMisc; // documental: qué componente va en la plantilla
202
+ sticky?: boolean; // fija la columna y activa el scroll lateral en cualquier ancho
203
+ }
204
+ ```
205
+
206
+ `SuiTableCellDirective` se importa aparte (`<ng-template suiTableCell="key" let-row>`).
207
+ El orden cicla `asc → desc → sin orden` y emite `sortChanged` en cada paso; ordenar las filas es
208
+ trabajo de la página.
209
+
210
+ ## Trampas
211
+
212
+ - **Por encima de 767 px la tabla recorta, no scrollea.** Sin una columna `sticky`, el contenedor
213
+ es `overflow: hidden` en desktop: lo que no entra **desaparece** a la derecha (monto, estado,
214
+ acciones). Las columnas tienen que entrar en el ancho real, que con el sidebar abierto es
215
+ ~483 px en iPad y ~1015 px en una pantalla de 1351. Lo comentan tres pantallas:
216
+ `pages/drivers/drivers.html:210`, `pages/super-admin/master-gastos/master-gastos.html:38`,
217
+ `pages/central/central-gastos/central-gastos.ts:76`. El wrapper `drivers__table-wrap` es un
218
+ respaldo de eso; no se copia.
219
+ - `sticky` en **cualquier** columna activa el scroll lateral en todos los anchos (no solo en mobile)
220
+ y quita el recorte redondeado de las filas.
221
+ - `descriptionKey` y `truncate` **se ignoran** si la columna tiene plantilla `suiTableCell`.
222
+ - Sin `rowKey`, la fila se identifica por su índice: la selección se mezcla al reordenar o al cargar
223
+ más. Si usas `selectable` o la lista cambia en vivo, pasa `rowKey="id"`.
224
+ - `rowClicked` dispara en **toda** la fila. Un botón dentro de una plantilla necesita
225
+ `$event.stopPropagation()` o abre el detalle además de hacer lo suyo.
226
+ - `toggleAll` selecciona **lo cargado**, no la colección: con cursor, «todas» son las de la página.
227
+ - El hover de fila solo existe con mouse (`@media (hover: hover)`); en táctil no hay feedback de
228
+ toque: si la fila abre algo, que se note por el contenido (una pista «Toca un gasto para…», como
229
+ `central-gastos.html:46`).
230
+ - La fila esqueleto son siempre 5, sin importar el tamaño de página.
231
+ - No le pongas `touch-action` ni `overflow-x` desde la página: el componente ya maneja el gesto en
232
+ Android y lo romperías.
233
+
234
+ ## Ejemplo
235
+
236
+ ```html
237
+ <sui-card>
238
+ <sui-table
239
+ [columns]="columnas"
240
+ [rows]="filas()"
241
+ rowKey="id"
242
+ [loading]="cargando()"
243
+ emptyMessage="Sin pedidos en este estado."
244
+ (rowClicked)="abrirDetalle($event)">
245
+ <ng-template suiTableCell="estado" let-row>
246
+ <sui-badge [color]="row.color" variant="lighter" size="small">{{ row.estadoLabel }}</sui-badge>
247
+ </ng-template>
248
+ </sui-table>
249
+
250
+ @if (hayMas()) {
251
+ <sui-button variant="stroke" color="neutral" [size]="bp.isMobile() ? 'large' : 'small'"
252
+ [disabled]="cargandoMas()" (click)="cargarMas()">Cargar más</sui-button>
253
+ }
254
+ </sui-card>
255
+ ```
256
+
257
+ ```ts
258
+ readonly columnas: SuiTableColumn[] = [
259
+ { key: 'numero', label: 'Pedido', priority: 'leading', descriptionKey: 'desdeHace', minWidth: '130px' },
260
+ { key: 'cliente', label: 'Cliente', minWidth: '160px', maxWidth: '220px', truncate: true },
261
+ { key: 'total', label: 'Total', align: 'right', width: '100px' },
262
+ { key: 'estado', label: 'Estado', width: '130px' },
263
+ ];
264
+ ```