@kaufmann-lab/ui 0.1.19 → 0.1.21

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.
@@ -31,8 +31,16 @@ import { SafeResourceUrl } from '@angular/platform-browser';
31
31
  * `map-pin`, que en este mismo dominio representa ubicación geográfica real
32
32
  * de vehículos; reusarlo para "fijar" generaría ambigüedad con los markers
33
33
  * de mapa.
34
+ *
35
+ * `refresh` (`refresh-cw` de Lucide) e `info` son extensión propia: la
36
+ * acción "Reintentar" y los avisos informativos (ej. estados de
37
+ * `kf-empty-state` en tono `info`) no tenían glyph equivalente en el set.
38
+ *
39
+ * `layout-grid` (de Lucide, cuatro cuadros) es extensión propia: representa
40
+ * la vista de tarjetas; `grid` dibuja una cuadrícula de tabla y no se
41
+ * distingue de la vista de tabla.
34
42
  */
35
- type IconName = 'activity' | 'alert-circle' | 'alert-octagon' | 'alert-triangle' | 'award' | 'bar-chart' | 'bell' | 'bus' | 'calculator' | 'calendar' | 'car' | 'cart' | 'check' | 'check-circle' | 'check-square' | 'chevron-down' | 'chevron-left' | 'chevron-left-bar' | 'chevron-right' | 'clipboard' | 'clock' | 'credit-card' | 'crosshair' | 'download' | 'edit' | 'external-link' | 'eye' | 'eye-off' | 'file' | 'filter' | 'gauge' | 'gift' | 'grid' | 'history' | 'home' | 'inbox' | 'layers' | 'list' | 'log-out' | 'mail' | 'map' | 'map-pin' | 'maximize' | 'menu' | 'message-circle' | 'mic' | 'mic-off' | 'minimize' | 'minus' | 'monitor' | 'more-vertical' | 'navigation' | 'pause' | 'phone' | 'pin' | 'play' | 'plus' | 'search' | 'settings' | 'shield-check' | 'sort' | 'star' | 'ticket' | 'tool' | 'trash' | 'truck' | 'upload-cloud' | 'user' | 'user-search' | 'video' | 'x' | 'zap';
43
+ type IconName = 'activity' | 'alert-circle' | 'alert-octagon' | 'alert-triangle' | 'award' | 'bar-chart' | 'bell' | 'bus' | 'calculator' | 'calendar' | 'car' | 'cart' | 'check' | 'check-circle' | 'check-square' | 'chevron-down' | 'chevron-left' | 'chevron-left-bar' | 'chevron-right' | 'clipboard' | 'clock' | 'credit-card' | 'crosshair' | 'download' | 'edit' | 'external-link' | 'eye' | 'eye-off' | 'file' | 'filter' | 'gauge' | 'gift' | 'grid' | 'history' | 'home' | 'inbox' | 'info' | 'layers' | 'layout-grid' | 'list' | 'log-out' | 'mail' | 'map' | 'map-pin' | 'maximize' | 'menu' | 'message-circle' | 'mic' | 'mic-off' | 'minimize' | 'minus' | 'monitor' | 'more-vertical' | 'navigation' | 'pause' | 'phone' | 'pin' | 'play' | 'plus' | 'refresh' | 'search' | 'settings' | 'shield-check' | 'sort' | 'star' | 'ticket' | 'tool' | 'trash' | 'truck' | 'upload-cloud' | 'user' | 'user-search' | 'video' | 'x' | 'zap';
36
44
 
37
45
  type IconSize = 'sm' | 'md' | 'lg';
38
46
  /** Ángulos de rotación soportados por `rotate` — cubre los casos reales del
@@ -128,7 +136,7 @@ declare class AdminBadge {
128
136
  * Envuelve un `<button>` nativo: mantiene teclado, foco y submit de formulario.
129
137
  * Como es un elemento personalizado, ningún atributo ARIA puesto en el host
130
138
  * llega al `<button>` interno — por eso `disabled`, `type`, `ariaLabel`,
131
- * `ariaExpanded` y `ariaPressed` se reenvían a mano.
139
+ * `ariaExpanded`, `ariaPressed` y `ariaHaspopup` se reenvían a mano.
132
140
  *
133
141
  * Seis variantes: `primary`, `secondary`, `ghost`, `outline`, `danger`,
134
142
  * `surface`. Tres tamaños (`sm`/`md`/`lg`). `icon` agrega un glifo antes del
@@ -147,6 +155,15 @@ declare class AdminBadge {
147
155
  *
148
156
  * `(click)` no se reenvía: burbujea del botón interno al host solo.
149
157
  *
158
+ * Objetivo táctil: el alto y el ancho mínimos del `<button>` interno salen
159
+ * de `--kf-button-min-height` y `--kf-button-min-width` (default `auto`,
160
+ * sin cambio). Un contenedor los sube (ej. a 44px en pantallas táctiles,
161
+ * sobre todo para un botón solo-ícono) declarando las variables en su
162
+ * propio CSS — heredan a través del límite de encapsulación.
163
+ *
164
+ * `focus()` enfoca el `<button>` interno: el consumidor no necesita
165
+ * buscarlo con `querySelector` en el DOM del componente.
166
+ *
150
167
  * `loading` reemplaza el contenido proyectado por `kf-spinner` y fuerza
151
168
  * `disabled`.
152
169
  *
@@ -162,10 +179,10 @@ declare class AdminBadge {
162
179
  declare class Button {
163
180
  readonly variant: _angular_core.InputSignal<"primary" | "secondary" | "ghost" | "outline" | "danger" | "surface">;
164
181
  readonly size: _angular_core.InputSignal<"sm" | "md" | "lg">;
165
- readonly shape: _angular_core.InputSignal<"rect" | "circle">;
182
+ readonly shape: _angular_core.InputSignal<"circle" | "rect">;
166
183
  readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
167
184
  /** Igual al default nativo: hace submit dentro de un `<form>`. */
168
- readonly type: _angular_core.InputSignal<"button" | "submit" | "reset">;
185
+ readonly type: _angular_core.InputSignal<"button" | "reset" | "submit">;
169
186
  /** Ícono líder, renderizado con `kf-icon` antes del label proyectado. */
170
187
  readonly icon: _angular_core.InputSignal<IconName | undefined>;
171
188
  /** Estira el botón a todo el ancho de su contenedor. */
@@ -186,12 +203,18 @@ declare class Button {
186
203
  * más fuerte para leerse activo, y así ningún consumidor tiene que
187
204
  * acordarse de pedirlo aparte. */
188
205
  readonly ariaPressed: _angular_core.InputSignal<boolean | undefined>;
206
+ /** Tipo de elemento emergente que abre el botón (ej. `'dialog'` para un
207
+ * panel de filtros, `'listbox'`) — mismo motivo de reenvío manual. */
208
+ readonly ariaHaspopup: _angular_core.InputSignal<"dialog" | "menu" | "listbox" | "true" | undefined>;
209
+ private readonly nativeButton;
210
+ /** Enfoca el `<button>` interno (mismas opciones que `HTMLElement.focus`). */
211
+ focus(options?: FocusOptions): void;
189
212
  /** `ghost`/`outline`/`surface` no tienen fondo oscuro sólido — el anillo
190
213
  * del spinner por defecto ya se lee bien ahí sin variante propia
191
214
  * (`surface` es fondo claro con texto oscuro, mismo motivo que `outline`). */
192
215
  protected readonly hasSolidFill: _angular_core.Signal<boolean>;
193
216
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<Button, never>;
194
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<Button, "kf-button", never, { "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "shape": { "alias": "shape"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "type": { "alias": "type"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "block": { "alias": "block"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "ariaExpanded": { "alias": "ariaExpanded"; "required": false; "isSignal": true; }; "ariaPressed": { "alias": "ariaPressed"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
217
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<Button, "kf-button", never, { "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "shape": { "alias": "shape"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "type": { "alias": "type"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "block": { "alias": "block"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "ariaExpanded": { "alias": "ariaExpanded"; "required": false; "isSignal": true; }; "ariaPressed": { "alias": "ariaPressed"; "required": false; "isSignal": true; }; "ariaHaspopup": { "alias": "ariaHaspopup"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
195
218
  }
196
219
 
197
220
  type AlertTone = 'info' | 'ok' | 'warn' | 'error';
@@ -218,7 +241,7 @@ declare class Alert {
218
241
  * `alert` interrumpe al lector de pantalla; se reserva para error/warn.
219
242
  * Los avisos informativos se anuncian sin cortar lo que se esté leyendo.
220
243
  */
221
- protected readonly ariaRole: _angular_core.Signal<"alert" | "status">;
244
+ protected readonly ariaRole: _angular_core.Signal<"status" | "alert">;
222
245
  protected readonly toneIcon: _angular_core.Signal<IconName>;
223
246
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<Alert, never>;
224
247
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<Alert, "kf-alert", never, { "tone": { "alias": "tone"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; "dismissible": { "alias": "dismissible"; "required": false; "isSignal": true; }; "open": { "alias": "open"; "required": false; "isSignal": true; }; }, { "open": "openChange"; }, never, ["*"], true, never>;
@@ -279,13 +302,24 @@ declare class Avatar {
279
302
  * punto (`dot`) usa el color BASE de cada tono, no `-strong` — ese último
280
303
  * está pensado para texto legible sobre el fondo pastel de `soft` y se ve
281
304
  * apagado sobre una superficie translúcida/oscura.
305
+ *
306
+ * `truncate` corta un texto largo con elipsis (ej. un estado de SAP como
307
+ * "WF Aprobación Política descuento") en vez de ensanchar la fila que lo
308
+ * contiene: el badge se limita al ancho de su contenedor (`max-width: 100%`,
309
+ * `min-width: 0`) y el punto no se recorta. El texto completo lo pone el
310
+ * consumidor en `title` (el badge no puede leer su propio contenido
311
+ * proyectado para generarlo):
312
+ *
313
+ * <kf-badge tone="warn" dot truncate [title]="q.statusLabel">{{ q.statusLabel }}</kf-badge>
282
314
  */
283
315
  declare class Badge {
284
- readonly tone: _angular_core.InputSignal<"neutral" | "ok" | "warn" | "error" | "info" | "service">;
316
+ readonly tone: _angular_core.InputSignal<"error" | "info" | "neutral" | "ok" | "warn" | "service">;
285
317
  readonly variant: _angular_core.InputSignal<"soft" | "glass">;
286
318
  readonly dot: _angular_core.InputSignalWithTransform<boolean, unknown>;
319
+ /** Corta el texto con elipsis al ancho del contenedor. Ver JSDoc de la clase. */
320
+ readonly truncate: _angular_core.InputSignalWithTransform<boolean, unknown>;
287
321
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<Badge, never>;
288
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<Badge, "kf-badge", never, { "tone": { "alias": "tone"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "dot": { "alias": "dot"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
322
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<Badge, "kf-badge", never, { "tone": { "alias": "tone"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "dot": { "alias": "dot"; "required": false; "isSignal": true; }; "truncate": { "alias": "truncate"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
289
323
  }
290
324
 
291
325
  interface BreadcrumbItem {
@@ -641,7 +675,7 @@ declare class Checkbox implements ControlValueAccessor {
641
675
  * de superficie/borde/texto-muted más cercanos).
642
676
  */
643
677
  declare class Chip {
644
- readonly tone: _angular_core.InputSignal<"neutral" | "ok" | "warn" | "error">;
678
+ readonly tone: _angular_core.InputSignal<"error" | "neutral" | "ok" | "warn">;
645
679
  readonly dot: _angular_core.InputSignalWithTransform<boolean, unknown>;
646
680
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<Chip, never>;
647
681
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<Chip, "kf-chip", never, { "tone": { "alias": "tone"; "required": false; "isSignal": true; }; "dot": { "alias": "dot"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
@@ -836,10 +870,14 @@ declare class ContentViewer {
836
870
  * usa como `src` salvo que la columna traiga su `cellTemplates`.
837
871
  * - `title`: título del encabezado (ej. la patente/unidad).
838
872
  * - `status`: chip/estado al lado del título.
839
- * - `field` (default): par label/valor en el cuerpo.
873
+ * - `field` (default): par label/valor en el cuerpo. Con `cardClamp`, el
874
+ * valor ocupa el ancho completo (sin label visible) recortado a N líneas.
875
+ * - `meta`: dato secundario corto (fecha, monto) en una fila inferior de la
876
+ * tarjeta, rótulo chico arriba y valor abajo; con `emphasis` se destaca
877
+ * (ej. el precio) y se alinea al final.
840
878
  * - `action`: va al pie, separado del cuerpo por una línea.
841
879
  */
842
- type DataTableCardRole = 'media' | 'title' | 'status' | 'field' | 'action';
880
+ type DataTableCardRole = 'media' | 'title' | 'status' | 'field' | 'meta' | 'action';
843
881
  interface DataTableColumn {
844
882
  key: string;
845
883
  label: string;
@@ -862,6 +900,46 @@ interface DataTableColumn {
862
900
  * corto) se comprima más de la cuenta cuando hay poco espacio y otras
863
901
  * columnas compiten por él. */
864
902
  minWidth?: string;
903
+ /** Ancho fijo de la columna (ej. `'56px'` para una columna de acciones
904
+ * solo ícono, `'9rem'` para una fecha). En la tabla va como `width` de la
905
+ * celda; en `kf-quote-picker` (`modalLayout="list"`), como el ancho de su
906
+ * pista en la grilla. */
907
+ width?: string;
908
+ /** Techo de ancho. En la tabla, junto con `truncate`, reemplaza al
909
+ * techo automático (ver `truncate`). En una tabla con `grow` admite
910
+ * unidades de contenedor relativas al ancho de la tabla, ej.
911
+ * `'min(14rem, 22cqw)'`: el techo baja solo cuando la tabla es angosta. */
912
+ maxWidth?: string;
913
+ /** Peso con el que la columna se reparte TODO el espacio sobrante de la
914
+ * tabla (ej. `1`; con dos columnas `grow: 2` y `grow: 1`, la primera
915
+ * recibe el doble). Con al menos una columna `grow` la tabla pasa a
916
+ * reparto de anchos: las columnas con `width` mantienen ese ancho, las que
917
+ * no traen `width` ni `grow` se ajustan a su contenido (con `maxWidth` como
918
+ * techo) y las `grow` absorben el resto. Se combina con `truncate` para
919
+ * que el texto largo ocupe el espacio sin empujar a las demás. Sin ninguna
920
+ * columna `grow`, el reparto es el de siempre. */
921
+ grow?: number;
922
+ /** Texto largo en UNA línea con elipsis y el valor completo en `title`
923
+ * (ej. el modelo de un vehículo). La columna deja de imponer su ancho
924
+ * mínimo: cede espacio para que las demás (y la de acciones) entren en
925
+ * pantalla sin scroll horizontal. Con `maxWidth`, se corta en ese ancho. */
926
+ truncate?: boolean;
927
+ /** Alineación horizontal. `'end'` equivale a `right: true` (que se
928
+ * mantiene por compatibilidad). */
929
+ align?: 'start' | 'center' | 'end';
930
+ /** Oculta el rótulo del encabezado a la vista (queda para lectores de
931
+ * pantalla) — ej. una columna de acciones solo ícono. */
932
+ hideLabel?: boolean;
933
+ /** Valor en tipografía monoespaciada (ej. un identificador "COT-2026-0042"),
934
+ * en la celda de la tabla y como título de la tarjeta. */
935
+ mono?: boolean;
936
+ /** Vista tarjetas, columna `field`: el valor va a ancho completo (rótulo
937
+ * solo para lectores de pantalla), recortado a este número de líneas con
938
+ * el texto completo en `title` — ej. el modelo del vehículo con `2`. */
939
+ cardClamp?: number;
940
+ /** Destaca el valor (peso y color de tinta) — ej. el monto. En la vista
941
+ * tarjetas, una columna `meta` con `emphasis` va al final de su fila. */
942
+ emphasis?: boolean;
865
943
  }
866
944
  /** Vistas disponibles cuando `cardable` está activo. */
867
945
  type DataTableView = 'table' | 'cards';
@@ -940,6 +1018,12 @@ type DataTableSort = `${string}:${'asc' | 'desc'}`;
940
1018
  * <kf-data-table [columns]="columns" [rows]="rows" [pageSize]="25" itemLabel="beneficios" />
941
1019
  * <kf-data-table [columns]="columns" [rows]="rows" [pageSize]="25" [pageSizeOptions]="[]" />
942
1020
  *
1021
+ * La página es `[(page)]` (el consumidor puede llevarla a la URL) y se
1022
+ * corrige sola si queda fuera de rango. Por defecto un cambio de `rows` la
1023
+ * devuelve a 1; con `keepPageOnRowsChange` la conserva (el consumidor decide
1024
+ * cuándo volver a 1). Un `pageSize` nuevo del consumidor pisa el elegido en
1025
+ * el selector, y `pageSizeChange` avisa cuando el usuario lo cambia.
1026
+ *
943
1027
  * Ordenamiento por columna: cada `DataTableColumn` con `sortable: true`
944
1028
  * vuelve su header clickeable, con el mismo formato `key:direction` que
945
1029
  * usa `sort` de `kf-quote-picker` (two-way manual, no `model()` — el
@@ -953,6 +1037,55 @@ type DataTableSort = `${string}:${'asc' | 'desc'}`;
953
1037
  *
954
1038
  * Densidad: `density="compact"` reduce el padding vertical de cada fila,
955
1039
  * para priorizar cantidad de filas visibles sobre aire entre ellas.
1040
+ *
1041
+ * Listados largos con textos largos (ej. cotizaciones con el modelo del
1042
+ * vehículo): la columna de texto largo lleva `truncate` (elipsis + `title`),
1043
+ * las de dato corto `width`/`align: 'end'`, y la de acciones solo ícono
1044
+ * `width` angosto + `hideLabel` + `sticky: 'right'`. Así la acción queda
1045
+ * siempre a la vista sin scroll horizontal:
1046
+ *
1047
+ * columns = [
1048
+ * { key: 'id', label: 'Cotización', width: '9rem' },
1049
+ * { key: 'model', label: 'Vehículo', truncate: true, minWidth: '10rem' },
1050
+ * { key: 'amount', label: 'Monto', align: 'end', width: '8rem' },
1051
+ * { key: 'open', label: 'Abrir', hideLabel: true, width: '56px', sticky: 'right' },
1052
+ * ];
1053
+ * <kf-data-table [columns]="columns" [rows]="quotes" [rowClickable]="true"
1054
+ * (rowClick)="open($event.row)" [cellTemplates]="{ open: openCell }" />
1055
+ *
1056
+ * Reparto de anchos: con `grow` en alguna columna, esa(s) se reparten TODO el
1057
+ * espacio sobrante según su peso, las de `width` lo mantienen y las que no
1058
+ * traen `width` ni `grow` se ajustan a su contenido (con `maxWidth` como
1059
+ * techo). Así una columna de dato corto (estado, precio, fecha) no acapara
1060
+ * espacio y el texto largo (el vehículo) lo aprovecha:
1061
+ *
1062
+ * columns = [
1063
+ * { key: 'id', label: 'Cotización', width: '8.5rem', mono: true },
1064
+ * { key: 'model', label: 'Vehículo', grow: 1, truncate: true },
1065
+ * { key: 'issuedAt', label: 'Emisión' },
1066
+ * { key: 'price', label: 'Precio', align: 'end', emphasis: true },
1067
+ * { key: 'status', label: 'Estado', truncate: true, maxWidth: '14rem' },
1068
+ * ];
1069
+ *
1070
+ * Listado completo con vista tarjetas (ej. cotizaciones): la vista es
1071
+ * two-way (`[(view)]`) y el toggle interno se oculta con
1072
+ * `[showViewToggle]="false"` cuando el consumidor lo pone en su barra. En
1073
+ * tarjetas: título (`mono`) y estado en una fila, el campo principal a
1074
+ * ancho completo en 2 líneas (`cardClamp: 2`), y fecha/monto en una fila
1075
+ * inferior (`cardRole: 'meta'`, el monto con `emphasis`). Las tarjetas de
1076
+ * una fila miden lo mismo; `cardMinWidth` fija el ancho mínimo. Con
1077
+ * `rowClickable` la tarjeta entera es un botón (Enter/Espacio):
1078
+ *
1079
+ * columns = [
1080
+ * { key: 'id', label: 'Cotización', cardRole: 'title', mono: true },
1081
+ * { key: 'status', label: 'Estado', cardRole: 'status' },
1082
+ * { key: 'model', label: 'Vehículo', truncate: true, cardClamp: 2 },
1083
+ * { key: 'date', label: 'Fecha', cardRole: 'meta', width: '7rem' },
1084
+ * { key: 'amount', label: 'Monto', cardRole: 'meta', align: 'end', emphasis: true },
1085
+ * ];
1086
+ * <kf-data-table [columns]="columns" [rows]="quotes" cardable [(view)]="view"
1087
+ * [showViewToggle]="false" cardMinWidth="300px" [rowClickable]="true"
1088
+ * (rowClick)="open($event.row)" />
956
1089
  */
957
1090
  declare class DataTable<T = unknown> {
958
1091
  readonly columns: _angular_core.InputSignal<DataTableColumn[]>;
@@ -972,6 +1105,13 @@ declare class DataTable<T = unknown> {
972
1105
  */
973
1106
  protected readonly canScrollLeft: _angular_core.WritableSignal<boolean>;
974
1107
  protected readonly canScrollRight: _angular_core.WritableSignal<boolean>;
1108
+ /**
1109
+ * Hay contenido oculto por scroll horizontal. Solo entonces el contenedor
1110
+ * es una región nombrada y enfocable (`tabindex="0"`, `role="region"`):
1111
+ * sin foco, quien navega con teclado no puede desplazarlo (WCAG 2.1.1).
1112
+ * Una tabla que entra completa no suma una parada de Tab vacía.
1113
+ */
1114
+ protected readonly hasOverflow: _angular_core.WritableSignal<boolean>;
975
1115
  constructor();
976
1116
  private updateScrollState;
977
1117
  /** Default: índice de la fila dentro de `rows`. */
@@ -979,8 +1119,14 @@ declare class DataTable<T = unknown> {
979
1119
  /** Sin borde/sombra de card — para tablas anidadas dentro de otra card. */
980
1120
  readonly flush: _angular_core.InputSignal<boolean>;
981
1121
  /** Habilita el toggle de vista tabla/cards. */
982
- readonly cardable: _angular_core.InputSignal<boolean>;
1122
+ readonly cardable: _angular_core.InputSignalWithTransform<boolean, unknown>;
983
1123
  readonly defaultView: _angular_core.InputSignal<DataTableView>;
1124
+ /** Muestra el toggle tabla/tarjetas interno (con `cardable`). En `false`,
1125
+ * el consumidor controla la vista con `[(view)]` desde su propio control. */
1126
+ readonly showViewToggle: _angular_core.InputSignalWithTransform<boolean, unknown>;
1127
+ /** Ancho mínimo de cada tarjeta en la grilla (default `220px`, histórico).
1128
+ * Nunca supera el ancho disponible: en pantallas angostas, una columna. */
1129
+ readonly cardMinWidth: _angular_core.InputSignal<string>;
984
1130
  /**
985
1131
  * Formateo simple de celda como texto: `(row) => string`. Para marcado
986
1132
  * real (badges, links, botones) usar `cellTemplates`.
@@ -1000,6 +1146,15 @@ declare class DataTable<T = unknown> {
1000
1146
  row: T;
1001
1147
  index: number;
1002
1148
  }>;
1149
+ /** Nombre accesible del contenedor con scroll horizontal (solo se aplica cuando hay overflow). */
1150
+ readonly ariaLabel: _angular_core.InputSignal<string>;
1151
+ /**
1152
+ * Clave (la misma que devuelve `rowKey`, o el índice sin él) de la fila
1153
+ * "actual" — ej. el registro que se está viendo en un selector. Esa fila
1154
+ * o card lleva `aria-current="true"` y la clase `--current` (fondo
1155
+ * `primary-soft`). `null` (default): ninguna.
1156
+ */
1157
+ readonly currentRowKey: _angular_core.InputSignal<string | number | null>;
1003
1158
  /**
1004
1159
  * Paginación interna opcional — sin `pageSize`, cero cambio de
1005
1160
  * comportamiento (el consumidor sigue paginando afuera si quiere, vía el
@@ -1014,7 +1169,16 @@ declare class DataTable<T = unknown> {
1014
1169
  * portal) — pasar `[]` explícitamente desactiva el selector de tamaño
1015
1170
  * aunque `pageSize` esté activo (paginación sin selector). */
1016
1171
  readonly pageSizeOptions: _angular_core.InputSignal<number[]>;
1017
- readonly pageChange: _angular_core.OutputEmitterRef<number>;
1172
+ /** Página actual (desde 1), bidireccional: `[(page)]="page"`. El consumidor
1173
+ * puede llevarla fuera de la tabla (ej. en la URL) y la tabla la corrige
1174
+ * si queda fuera de rango. `pageChange` emite en cada cambio, también los
1175
+ * automáticos (volver a 1 al cambiar de tamaño de página). */
1176
+ readonly page: _angular_core.ModelSignal<number>;
1177
+ /** Con `true`, un cambio de `rows` NO devuelve la página a 1: el
1178
+ * consumidor decide cuándo (ej. al filtrar) y la tabla solo la corrige si
1179
+ * queda fuera de rango. Default `false`: vuelve a 1, como siempre. */
1180
+ readonly keepPageOnRowsChange: _angular_core.InputSignalWithTransform<boolean, unknown>;
1181
+ /** Emite el tamaño elegido en el selector de la paginación interna. */
1018
1182
  readonly pageSizeChange: _angular_core.OutputEmitterRef<number>;
1019
1183
  /** Nombre de la entidad listada (ej. "beneficios", "facturas"), en
1020
1184
  * plural — se agrega al final del texto informativo de la paginación
@@ -1022,7 +1186,6 @@ declare class DataTable<T = unknown> {
1022
1186
  * números ("1–5 de 7"): la librería no impone un idioma ni un nombre
1023
1187
  * fijo, cada consumidor decide el suyo. */
1024
1188
  readonly itemLabel: _angular_core.InputSignal<string>;
1025
- protected readonly page: _angular_core.WritableSignal<number>;
1026
1189
  private readonly internalPageSize;
1027
1190
  /** Tamaño de página efectivo: el que el usuario elija en el selector
1028
1191
  * (si `pageSizeOptions` está activo), o el `pageSize` del consumidor. */
@@ -1044,8 +1207,8 @@ declare class DataTable<T = unknown> {
1044
1207
  * asc → desc → sin orden; clickear otra columna reinicia el ciclo en esa
1045
1208
  * columna nueva.
1046
1209
  */
1047
- readonly sort: _angular_core.InputSignal<`${string}:desc` | `${string}:asc` | null>;
1048
- readonly sortChange: _angular_core.OutputEmitterRef<`${string}:desc` | `${string}:asc` | null>;
1210
+ readonly sort: _angular_core.InputSignal<`${string}:asc` | `${string}:desc` | null>;
1211
+ readonly sortChange: _angular_core.OutputEmitterRef<`${string}:asc` | `${string}:desc` | null>;
1049
1212
  protected readonly sortedRows: _angular_core.Signal<T[]>;
1050
1213
  /** Filas realmente renderizadas: ordenadas y, si `pageSize` está activo,
1051
1214
  * recortadas a la página actual. */
@@ -1055,21 +1218,60 @@ declare class DataTable<T = unknown> {
1055
1218
  protected toggleSort(column: DataTableColumn): void;
1056
1219
  protected goToPage(target: number): void;
1057
1220
  protected setPageSize(size: number | null): void;
1221
+ /**
1222
+ * Reparto de anchos activo: hay al menos una columna `grow`. Sin ella, los
1223
+ * anchos son los de siempre (`width`/`minWidth`/`maxWidth` tal cual).
1224
+ */
1225
+ protected readonly weighted: _angular_core.Signal<boolean>;
1226
+ /** Porción (%) del espacio sobrante de cada columna `grow`, según su peso. */
1227
+ private readonly growShares;
1228
+ private growWeight;
1229
+ /**
1230
+ * `width` de la celda. En reparto de anchos: la columna `grow` toma su
1231
+ * porción en porcentaje (las porciones suman 100% y el navegador las escala
1232
+ * al espacio que dejan las demás), la de `width` conserva su ancho y la que
1233
+ * no trae ni `width` ni `grow` pide 1% — con `nowrap` queda en el ancho de
1234
+ * su contenido.
1235
+ */
1236
+ protected widthFor(column: DataTableColumn): string | null;
1237
+ /**
1238
+ * `min-width` de la celda. En reparto de anchos, el `width` de una columna
1239
+ * (también de una `grow`) funciona como piso: ni las porciones en
1240
+ * porcentaje ni el contenido de las demás pueden comprimirla.
1241
+ */
1242
+ protected minWidthFor(column: DataTableColumn): string | null;
1243
+ /**
1244
+ * `max-width` de la celda. En reparto de anchos, una columna que se ajusta
1245
+ * a su contenido y es `truncate` SIN `maxWidth` no debe heredar el techo de
1246
+ * 0 de `truncate` (quedaría sin ancho): se libera con `none`.
1247
+ */
1248
+ protected maxWidthFor(column: DataTableColumn): string | null;
1058
1249
  /** Columnas fijas del lado izquierdo/derecho, con el offset acumulado
1059
1250
  * (medido en runtime, ver `measureStickyOffsets`) para `position: sticky`. */
1060
1251
  protected readonly stickyLeftColumns: _angular_core.Signal<DataTableColumn[]>;
1061
1252
  protected readonly stickyRightColumns: _angular_core.Signal<DataTableColumn[]>;
1062
1253
  protected readonly stickyOffsets: _angular_core.WritableSignal<Map<string, number>>;
1063
1254
  protected stickyStyle(column: DataTableColumn): Record<string, string> | null;
1064
- private readonly internalView;
1065
- /** Vista activa: la elegida por el usuario, o `defaultView` si no tocó el toggle. */
1066
- readonly view: _angular_core.Signal<DataTableView>;
1255
+ /**
1256
+ * Vista elegida, two-way: `[(view)]="view"`. Sin valor (default) manda
1257
+ * `defaultView`; el toggle interno la escribe. Para controlarla desde
1258
+ * afuera (ej. con el toggle en `kf-list-toolbar` y el estado en la URL),
1259
+ * enlazar `[(view)]` y ocultar el interno con `[showViewToggle]="false"`.
1260
+ * La vista efectivamente renderizada es `activeView()`.
1261
+ */
1262
+ readonly view: _angular_core.ModelSignal<DataTableView | undefined>;
1263
+ /** Vista activa: `view` si está definida, si no `defaultView`. */
1264
+ readonly activeView: _angular_core.Signal<DataTableView>;
1067
1265
  /** Primera columna de cada rol único; el resto se ignora en el encabezado. */
1068
1266
  readonly cardMediaColumn: _angular_core.Signal<DataTableColumn | undefined>;
1069
1267
  readonly cardTitleColumn: _angular_core.Signal<DataTableColumn | undefined>;
1070
1268
  readonly cardStatusColumns: _angular_core.Signal<DataTableColumn[]>;
1071
1269
  readonly cardActionColumns: _angular_core.Signal<DataTableColumn[]>;
1072
1270
  readonly cardFieldColumns: _angular_core.Signal<DataTableColumn[]>;
1271
+ /** Campos a ancho completo y recortados (`cardClamp`), antes del resto. */
1272
+ readonly cardLeadColumns: _angular_core.Signal<DataTableColumn[]>;
1273
+ /** Fila inferior de datos secundarios (`cardRole: 'meta'`). */
1274
+ readonly cardMetaColumns: _angular_core.Signal<DataTableColumn[]>;
1073
1275
  /**
1074
1276
  * La card entera es clickeable solo si no tiene acciones propias: un
1075
1277
  * `role="button"` con botones adentro es inválido y deja el foco ambiguo.
@@ -1087,8 +1289,28 @@ declare class DataTable<T = unknown> {
1087
1289
  readonly rowInteractive: _angular_core.Signal<boolean>;
1088
1290
  setView(next: DataTableView): void;
1089
1291
  trackRow(row: T, index: number): string | number;
1292
+ /** Alineada al final: `align: 'end'` o el histórico `right: true`. */
1293
+ protected isEndAligned(column: DataTableColumn): boolean;
1294
+ /** `title` de una celda `truncate`: el valor completo como texto. */
1295
+ protected truncateTitle(row: T, column: DataTableColumn): string | null;
1090
1296
  cellValue(row: T, column: DataTableColumn): string;
1091
- onRowClick(row: T, index: number): void;
1297
+ protected isCurrentRow(row: T, index: number): boolean;
1298
+ /**
1299
+ * Espacio activa la fila/card igual que Enter, como un botón. Solo si el
1300
+ * foco está en la fila misma (no en un control dentro de ella): ahí
1301
+ * `preventDefault` evita el scroll de página; en cualquier otro caso
1302
+ * Espacio conserva su comportamiento nativo.
1303
+ */
1304
+ protected onRowSpace(event: Event, row: T, index: number): void;
1305
+ /**
1306
+ * Emite `rowClick` salvo que el evento nazca dentro de un control
1307
+ * interactivo de la celda (enlace, botón, campo…): ese control ya resuelve
1308
+ * su propia acción. Así una celda con un `<a href>` real (clic derecho →
1309
+ * "abrir en nueva pestaña", Ctrl/Cmd+clic) no navega dos veces. El parámetro
1310
+ * `event` es opcional para no romper llamadas programáticas existentes.
1311
+ */
1312
+ onRowClick(row: T, index: number, event?: Event): void;
1313
+ private originatesInInteractiveControl;
1092
1314
  /**
1093
1315
  * Ancho real de cada columna `sticky` — se mide después de cada render
1094
1316
  * porque el ancho de una columna depende de su contenido, no hay forma
@@ -1099,7 +1321,7 @@ declare class DataTable<T = unknown> {
1099
1321
  */
1100
1322
  protected measureStickyOffsets(): void;
1101
1323
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<DataTable<any>, never>;
1102
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<DataTable<any>, "kf-data-table", never, { "columns": { "alias": "columns"; "required": true; "isSignal": true; }; "rows": { "alias": "rows"; "required": true; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "rowKey": { "alias": "rowKey"; "required": false; "isSignal": true; }; "flush": { "alias": "flush"; "required": false; "isSignal": true; }; "cardable": { "alias": "cardable"; "required": false; "isSignal": true; }; "defaultView": { "alias": "defaultView"; "required": false; "isSignal": true; }; "render": { "alias": "render"; "required": false; "isSignal": true; }; "cellTemplates": { "alias": "cellTemplates"; "required": false; "isSignal": true; }; "density": { "alias": "density"; "required": false; "isSignal": true; }; "rowClickable": { "alias": "rowClickable"; "required": false; "isSignal": true; }; "pageSize": { "alias": "pageSize"; "required": false; "isSignal": true; }; "pageSizeOptions": { "alias": "pageSizeOptions"; "required": false; "isSignal": true; }; "itemLabel": { "alias": "itemLabel"; "required": false; "isSignal": true; }; "sort": { "alias": "sort"; "required": false; "isSignal": true; }; }, { "rowClick": "rowClick"; "pageChange": "pageChange"; "pageSizeChange": "pageSizeChange"; "sortChange": "sortChange"; }, never, ["[empty]", "[footer]"], true, never>;
1324
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<DataTable<any>, "kf-data-table", never, { "columns": { "alias": "columns"; "required": true; "isSignal": true; }; "rows": { "alias": "rows"; "required": true; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "rowKey": { "alias": "rowKey"; "required": false; "isSignal": true; }; "flush": { "alias": "flush"; "required": false; "isSignal": true; }; "cardable": { "alias": "cardable"; "required": false; "isSignal": true; }; "defaultView": { "alias": "defaultView"; "required": false; "isSignal": true; }; "showViewToggle": { "alias": "showViewToggle"; "required": false; "isSignal": true; }; "cardMinWidth": { "alias": "cardMinWidth"; "required": false; "isSignal": true; }; "render": { "alias": "render"; "required": false; "isSignal": true; }; "cellTemplates": { "alias": "cellTemplates"; "required": false; "isSignal": true; }; "density": { "alias": "density"; "required": false; "isSignal": true; }; "rowClickable": { "alias": "rowClickable"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "currentRowKey": { "alias": "currentRowKey"; "required": false; "isSignal": true; }; "pageSize": { "alias": "pageSize"; "required": false; "isSignal": true; }; "pageSizeOptions": { "alias": "pageSizeOptions"; "required": false; "isSignal": true; }; "page": { "alias": "page"; "required": false; "isSignal": true; }; "keepPageOnRowsChange": { "alias": "keepPageOnRowsChange"; "required": false; "isSignal": true; }; "itemLabel": { "alias": "itemLabel"; "required": false; "isSignal": true; }; "sort": { "alias": "sort"; "required": false; "isSignal": true; }; "view": { "alias": "view"; "required": false; "isSignal": true; }; }, { "rowClick": "rowClick"; "page": "pageChange"; "pageSizeChange": "pageSizeChange"; "sortChange": "sortChange"; "view": "viewChange"; }, never, ["[empty]", "[footer]"], true, never>;
1103
1325
  }
1104
1326
 
1105
1327
  /**
@@ -1164,7 +1386,14 @@ declare class DocumentRow {
1164
1386
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<DocumentRow, "kf-document-row", never, { "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "tone": { "alias": "tone"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": true; "isSignal": true; }; "meta": { "alias": "meta"; "required": false; "isSignal": true; }; "href": { "alias": "href"; "required": false; "isSignal": true; }; "downloadLabel": { "alias": "downloadLabel"; "required": false; "isSignal": true; }; "pendingLabel": { "alias": "pendingLabel"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
1165
1387
  }
1166
1388
 
1167
- type DrawerSide = 'left' | 'right';
1389
+ /** Borde de anclaje del panel. `bottom` es una hoja inferior (bottom sheet). */
1390
+ type DrawerSide = 'left' | 'right' | 'bottom';
1391
+ /**
1392
+ * Rol del panel. `navigation` (default) es el menú lateral histórico;
1393
+ * `dialog` es un panel modal de tarea (filtros, resumen) con
1394
+ * `aria-modal="true"`.
1395
+ */
1396
+ type DrawerRole = 'navigation' | 'dialog';
1168
1397
  /**
1169
1398
  * Panel lateral genérico, agnóstico de contenido — no sabe qué es navegación
1170
1399
  * ni qué es sesión, solo da tres lugares para proyectar contenido
@@ -1197,17 +1426,71 @@ type DrawerSide = 'left' | 'right';
1197
1426
  * <a href="/">Home</a>
1198
1427
  * <a href="/vehiculos">Vehículos</a>
1199
1428
  * </kf-drawer>
1429
+ *
1430
+ * ## Panel de tarea (`role="dialog"`)
1431
+ *
1432
+ * Para un panel modal que no es navegación (filtros de un listado, resumen
1433
+ * de un registro): `role="dialog"` expone `role="dialog"` + `aria-modal`,
1434
+ * `title` dibuja una barra clara con el título (que nombra el diálogo vía
1435
+ * `aria-labelledby`) y un botón de cierre con `closeLabel`. En pantallas de
1436
+ * hasta 640px el panel ocupa el ancho completo; arriba de eso mide
1437
+ * `--kf-drawer-width` (default 480px). El pie (`[kf-drawer-footer]`) queda
1438
+ * fijo abajo mientras el cuerpo hace scroll.
1439
+ *
1440
+ * <kf-drawer [(open)]="filtersOpen" role="dialog" side="right"
1441
+ * title="Filtros" closeLabel="Cerrar filtros">
1442
+ * <kf-option-list label="Estado" ... />
1443
+ * <div kf-drawer-footer> ...Limpiar / Ver 12 resultados... </div>
1444
+ * </kf-drawer>
1445
+ *
1446
+ * `side="bottom"` lo convierte en hoja inferior (grip, esquinas superiores
1447
+ * redondeadas, alto máximo 80vh) — pensado para un resumen en mobile.
1448
+ *
1449
+ * Escape cierra el panel salvo que un control interno ya haya consumido la
1450
+ * tecla (`event.defaultPrevented`, ej. un `kf-select` abierto dentro del
1451
+ * drawer cierra solo su propio panel).
1452
+ *
1453
+ * Nombres por defecto: con `role="navigation"` el panel se llama "Menú" y
1454
+ * el cierre "Cerrar menú" (comportamiento histórico). Con `role="dialog"`
1455
+ * los defaults son neutros ("Diálogo" / "Cerrar") y en modo desarrollo se
1456
+ * avisa por consola si no hay `title` ni `ariaLabel`: un diálogo sin nombre
1457
+ * propio no le dice nada al lector de pantalla.
1458
+ *
1459
+ * Limitación conocida: NO colocar un `kf-select` (ni otro control con panel
1460
+ * flotante del CDK) dentro de un drawer `role="dialog"`. Su listbox se
1461
+ * monta en `.cdk-overlay-container`, fuera del diálogo con `aria-modal`: un
1462
+ * lector de pantalla puede tratarlo como contenido inerte y la trampa de
1463
+ * foco no lo incluye. Para elegir una opción dentro del panel usar
1464
+ * `kf-option-list`.
1465
+ *
1466
+ * Al cerrar, el foco vuelve al elemento que lo tenía al abrir solo si sigue
1467
+ * en el documento (`isConnected`); si fue desmontado mientras el drawer
1468
+ * estaba abierto, no se fuerza ningún destino.
1200
1469
  */
1201
1470
  declare class Drawer {
1202
1471
  /** Visibilidad del panel, two-way: `[(open)]="menuOpen"`. */
1203
1472
  readonly open: _angular_core.ModelSignal<boolean>;
1204
1473
  /** Borde de anclaje del panel. `left` (default) es la convención dominante
1205
1474
  * (Material/iOS/Android): el botón que abre el drawer y el propio panel
1206
- * quedan del mismo lado. */
1475
+ * quedan del mismo lado. `bottom` lo convierte en hoja inferior. */
1207
1476
  readonly side: _angular_core.InputSignal<DrawerSide>;
1208
- /** `aria-label` del `<nav>` interno — describe el propósito del panel para
1209
- * lectores de pantalla (ej. "Menú de navegación"). */
1210
- readonly ariaLabel: _angular_core.InputSignal<string>;
1477
+ /** `navigation` (default, menú) o `dialog` (panel modal de tarea). Ver JSDoc de la clase. */
1478
+ readonly role: _angular_core.InputSignal<DrawerRole>;
1479
+ /** `aria-label` del panel — describe su propósito para lectores de
1480
+ * pantalla (ej. "Menú de navegación"). Ignorado cuando hay `title`, que
1481
+ * nombra el panel con su texto visible. Default: "Menú" (`navigation`) o
1482
+ * "Diálogo" (`dialog`). */
1483
+ readonly ariaLabel: _angular_core.InputSignal<string | undefined>;
1484
+ /** Título visible en una barra clara arriba del panel, con botón de cierre. Sin él no hay barra (comportamiento histórico). */
1485
+ readonly title: _angular_core.InputSignal<string | undefined>;
1486
+ /** Nombre accesible del botón de cierre de la barra de título y del
1487
+ * overlay. Default: "Cerrar menú" (`navigation`) o "Cerrar" (`dialog`). */
1488
+ readonly closeLabel: _angular_core.InputSignal<string | undefined>;
1489
+ /** `ariaLabel` o, sin él, el default según el rol. */
1490
+ protected readonly resolvedAriaLabel: _angular_core.Signal<string>;
1491
+ /** `closeLabel` o, sin él, el default según el rol. */
1492
+ protected readonly resolvedCloseLabel: _angular_core.Signal<string>;
1493
+ protected readonly titleId: string;
1211
1494
  private readonly panelRef;
1212
1495
  private readonly focusTrapFactory;
1213
1496
  private readonly document;
@@ -1215,10 +1498,10 @@ declare class Drawer {
1215
1498
  private lastFocusedElement;
1216
1499
  constructor();
1217
1500
  /** Cierra el drawer con Escape — mismo patrón que `Dropdown`/`Modal`. */
1218
- protected onEscape(): void;
1501
+ protected onEscape(event: Event): void;
1219
1502
  private getOrCreateFocusTrap;
1220
1503
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<Drawer, never>;
1221
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<Drawer, "kf-drawer", never, { "open": { "alias": "open"; "required": false; "isSignal": true; }; "side": { "alias": "side"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; }, { "open": "openChange"; }, never, ["[kf-drawer-header]", "*", "[kf-drawer-footer]"], true, never>;
1504
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<Drawer, "kf-drawer", never, { "open": { "alias": "open"; "required": false; "isSignal": true; }; "side": { "alias": "side"; "required": false; "isSignal": true; }; "role": { "alias": "role"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; "closeLabel": { "alias": "closeLabel"; "required": false; "isSignal": true; }; }, { "open": "openChange"; }, never, ["[kf-drawer-header]", "*", "[kf-drawer-footer]"], true, never>;
1222
1505
  }
1223
1506
 
1224
1507
  /**
@@ -1334,6 +1617,8 @@ declare class DropdownPanel {
1334
1617
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<DropdownPanel, "kf-dropdown-panel", never, { "variant": { "alias": "variant"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
1335
1618
  }
1336
1619
 
1620
+ /** Tono del estado. `warn`/`info` solo cambian el chip del ícono. */
1621
+ type EmptyStateTone = 'neutral' | 'error' | 'warn' | 'info';
1337
1622
  /**
1338
1623
  * Placeholder para una sección sin contenido. Dos tonos, con anatomía
1339
1624
  * distinta (páginas 27-28 del design system, que los trata como piezas
@@ -1353,13 +1638,45 @@ declare class DropdownPanel {
1353
1638
  * No pudimos cargar tus vehículos
1354
1639
  * <kf-button (click)="retry()">Reintentar</kf-button>
1355
1640
  * </kf-empty-state>
1641
+ *
1642
+ * ## Variante `card`
1643
+ * Tarjeta centrada (fondo blanco, borde, sombra, ancho máximo 480px) para
1644
+ * un estado que REEMPLAZA a un contenido grande — ej. un iframe que no
1645
+ * cargó o un documento que aún no existe. Ícono en un círculo tonal de
1646
+ * 56px según `tone` (`warn`, `info`, `error` o `neutral`), `title` como
1647
+ * título, el contenido proyectado como texto (puede incluir una lista de
1648
+ * pasos) y `[kf-empty-state-actions]` para los botones. Los roles
1649
+ * (`role="alert"` para un error) los pone el consumidor en el host: no
1650
+ * todo estado de esta forma debe interrumpir al lector de pantalla.
1651
+ *
1652
+ * <kf-empty-state variant="card" tone="warn" icon="alert-triangle"
1653
+ * title="No pudimos cargar la propuesta" role="alert">
1654
+ * <p>Este contenido solo está disponible dentro de la red de la empresa.</p>
1655
+ * <ol>…pasos…</ol>
1656
+ * <div kf-empty-state-actions>
1657
+ * <kf-button (click)="retry()">Reintentar</kf-button>
1658
+ * </div>
1659
+ * </kf-empty-state>
1660
+ *
1661
+ * `[kf-empty-state-footer]` va DESPUÉS de las acciones, para pistas y
1662
+ * metadatos debajo de los botones ("¿Ya estás conectado? …", "Intento 2 ·
1663
+ * 10:42"). Vacío, no ocupa lugar.
1664
+ *
1665
+ * `title` se dibuja como texto destacado, no como encabezado (`<h*>`): el
1666
+ * nivel de encabezado depende de la página que lo contiene, y un estado
1667
+ * vacío no debe alterar su estructura.
1356
1668
  */
1357
1669
  declare class EmptyState {
1358
1670
  readonly icon: _angular_core.InputSignal<IconName | undefined>;
1359
- /** `neutral` (default): cero resultados. `error`: fallo real de carga. */
1360
- readonly tone: _angular_core.InputSignal<"neutral" | "error">;
1671
+ /** `neutral` (default): cero resultados. `error`: fallo real de carga. `warn`/`info`: tiñen el chip del ícono. */
1672
+ readonly tone: _angular_core.InputSignal<EmptyStateTone>;
1673
+ /** `inline` (default, histórico) o `card` (tarjeta centrada). Ver JSDoc de la clase. */
1674
+ readonly variant: _angular_core.InputSignal<"inline" | "card">;
1675
+ /** Título destacado sobre el contenido. Opcional en ambas variantes. */
1676
+ readonly title: _angular_core.InputSignal<string | undefined>;
1677
+ protected readonly iconSize: _angular_core.Signal<IconSize>;
1361
1678
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EmptyState, never>;
1362
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<EmptyState, "kf-empty-state", never, { "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "tone": { "alias": "tone"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
1679
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EmptyState, "kf-empty-state", never, { "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "tone": { "alias": "tone"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; }, {}, never, ["*", "[kf-empty-state-actions]", "[kf-empty-state-footer]"], true, never>;
1363
1680
  }
1364
1681
 
1365
1682
  /** Color del marcador. Distingue la CLASE de evento de un vistazo en un log
@@ -1627,43 +1944,63 @@ declare class Filter {
1627
1944
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<Filter, "kf-filter", never, { "fields": { "alias": "fields"; "required": true; "isSignal": true; }; "values": { "alias": "values"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": false; "isSignal": true; }; "mobileTitle": { "alias": "mobileTitle"; "required": false; "isSignal": true; }; }, { "fieldChange": "fieldChange"; }, never, never, true, never>;
1628
1945
  }
1629
1946
 
1630
- /** Forma normalizada interna de cada opción, ya resuelta a value/label. */
1947
+ /**
1948
+ * Opción en forma de objeto. `count` es opcional: cuando está presente se
1949
+ * muestra a la derecha del label (ej. "Pendientes 3"), con cifras tabulares
1950
+ * para que el ancho del chip no salte al cambiar el número.
1951
+ */
1631
1952
  interface FilterChipOption {
1632
1953
  value: string;
1633
1954
  label: string;
1955
+ count?: number;
1634
1956
  }
1635
1957
  /**
1636
1958
  * Fila de chips tipo "pill" para filtros de una sola selección.
1637
1959
  *
1638
1960
  * Cada opción puede pasarse como string simple (se usa como value y label
1639
- * a la vez) o como objeto { value, label } cuando el texto mostrado difiere
1640
- * del valor interno.
1961
+ * a la vez) o como objeto `{ value, label, count? }` cuando el texto
1962
+ * mostrado difiere del valor interno o cuando cada chip lleva un contador.
1641
1963
  *
1642
1964
  * El chip activo se resalta con `--kf-color-primary` — mismo color de marca
1643
1965
  * que `kf-tabs`/`kf-select` para su propio estado activo, así el filtro
1644
1966
  * seleccionado se lee con el color del tema activo, no un negro fijo que
1645
1967
  * no cambia entre marcas.
1646
1968
  *
1969
+ * Densidad: el padding horizontal de cada chip sale de
1970
+ * `--kf-filter-chips-padding-inline` (default `--kf-space-md`). Un
1971
+ * contenedor puede reducirlo sin tocar este componente — `kf-list-toolbar`
1972
+ * lo baja a 12px en su modo compacto.
1973
+ *
1647
1974
  * <kf-filter-chips [options]="['Todos', 'Activos', 'Archivados']"
1648
1975
  * [value]="filtro"
1649
1976
  * (valueChange)="filtro = $event" />
1650
1977
  *
1651
- * <kf-filter-chips [options]="[{ value: 'all', label: 'Todos' }]"
1978
+ * `layout="scroll"` deja todos los chips en UNA línea con desplazamiento
1979
+ * horizontal (para mobile: los filtros no ocupan más alto que una fila); el
1980
+ * default `wrap` los reparte en varias líneas. `deselectable` permite
1981
+ * quitar el filtro volviendo a tocar el chip activo (emite `''`), para el
1982
+ * caso "ningún chip = sin filtro".
1983
+ *
1984
+ * <kf-filter-chips ariaLabel="Filtrar por estado"
1985
+ * [options]="[{ value: '', label: 'Todas', count: 12 }]"
1652
1986
  * [value]="filtro"
1653
1987
  * (valueChange)="filtro = $event" />
1654
1988
  */
1655
1989
  declare class FilterChips {
1656
- readonly options: _angular_core.InputSignal<(string | {
1657
- value: string;
1658
- label: string;
1659
- })[]>;
1990
+ readonly options: _angular_core.InputSignal<(string | FilterChipOption)[]>;
1660
1991
  readonly value: _angular_core.InputSignal<string>;
1992
+ /** Nombre accesible del grupo (ej. "Filtrar por estado"). Sin él, el grupo se anuncia sin nombre. */
1993
+ readonly ariaLabel: _angular_core.InputSignal<string | undefined>;
1994
+ /** `wrap` (default): varias líneas. `scroll`: una sola línea con desplazamiento horizontal. */
1995
+ readonly layout: _angular_core.InputSignal<"scroll" | "wrap">;
1996
+ /** Tocar el chip activo lo desactiva y emite `''`. Default `false` (comportamiento histórico). */
1997
+ readonly deselectable: _angular_core.InputSignalWithTransform<boolean, unknown>;
1661
1998
  readonly valueChange: _angular_core.OutputEmitterRef<string>;
1662
- /** Normaliza cada opción string/objeto a la forma { value, label }. */
1999
+ /** Normaliza cada opción string/objeto a la forma `{ value, label, count? }`. */
1663
2000
  readonly normalizedOptions: _angular_core.Signal<FilterChipOption[]>;
1664
2001
  select(value: string): void;
1665
2002
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<FilterChips, never>;
1666
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<FilterChips, "kf-filter-chips", never, { "options": { "alias": "options"; "required": true; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; }, { "valueChange": "valueChange"; }, never, never, true, never>;
2003
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<FilterChips, "kf-filter-chips", never, { "options": { "alias": "options"; "required": true; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "layout": { "alias": "layout"; "required": false; "isSignal": true; }; "deselectable": { "alias": "deselectable"; "required": false; "isSignal": true; }; }, { "valueChange": "valueChange"; }, never, never, true, never>;
1667
2004
  }
1668
2005
 
1669
2006
  /** Un link del pie — legales, contacto, soporte. */
@@ -1740,6 +2077,27 @@ declare class Footer {
1740
2077
  * está en vuelo).
1741
2078
  *
1742
2079
  * <kf-input label="DNI" readonly formControlName="documentId" />
2080
+ *
2081
+ * Buscador sin label visible: `ariaLabel` da el nombre accesible del
2082
+ * `<input>` (el placeholder no lo reemplaza: desaparece al escribir) y
2083
+ * `clearable` agrega un botón "×" mientras haya texto. Borrar limpia el
2084
+ * valor, notifica al formulario (`ngModel`/`formControl`), dispara un
2085
+ * evento `input` nativo (para quien escucha `(input)`), emite `cleared` y
2086
+ * devuelve el foco al campo.
2087
+ *
2088
+ * <kf-input type="search" icon="search" ariaLabel="Buscar cotizaciones"
2089
+ * placeholder="Buscar cotización" clearable [(ngModel)]="query" />
2090
+ *
2091
+ * `focus()` enfoca el `<input>` nativo, sin que el consumidor busque el
2092
+ * control interno con `querySelector`.
2093
+ *
2094
+ * `autocomplete`, `name` e `inputmode` se reenvían al `<input>` nativo (un
2095
+ * atributo en el host no llega adentro) para que el navegador y los gestores
2096
+ * de contraseñas identifiquen el campo; sin valor no se renderiza ningún
2097
+ * atributo. Funcionan igual con `type="password"` y `toggleable`.
2098
+ *
2099
+ * <kf-input label="RUT" autocomplete="username" name="rut" inputmode="text" formControlName="rut" />
2100
+ * <kf-input type="password" label="Contraseña" toggleable autocomplete="current-password" name="password" formControlName="password" />
1743
2101
  */
1744
2102
  declare class Input implements ControlValueAccessor, AfterViewInit {
1745
2103
  private static nextId;
@@ -1766,11 +2124,29 @@ declare class Input implements ControlValueAccessor, AfterViewInit {
1766
2124
  readonly readonly: _angular_core.InputSignalWithTransform<boolean, unknown>;
1767
2125
  readonly type: _angular_core.InputSignal<string>;
1768
2126
  readonly placeholder: _angular_core.InputSignal<string>;
1769
- /** Teclado virtual sugerido en mobile (ej. `"numeric"` para montos o
1770
- * kilometraje) — sin valor, el navegador decide según `type`. */
1771
- readonly inputmode: _angular_core.InputSignal<string | undefined>;
2127
+ /** Token de autocompletado HTML (`username`, `email`, `current-password`,
2128
+ * `new-password`, `off`, etc.) que se reenvía al `<input>` nativo para que
2129
+ * el navegador y los gestores de contraseñas identifiquen el propósito del
2130
+ * campo (WCAG 1.3.5 y 3.3.8). Sin valor, el atributo no se renderiza. */
2131
+ readonly autocomplete: _angular_core.InputSignal<string | null | undefined>;
2132
+ /** `name` del `<input>` nativo; los gestores de contraseñas y el envío
2133
+ * nativo de formularios lo usan para identificar el campo. Sin valor, el
2134
+ * atributo no se renderiza. */
2135
+ readonly name: _angular_core.InputSignal<string | null | undefined>;
2136
+ /** Sugiere el teclado virtual (`numeric`, `email`, `tel`, etc.) sin cambiar
2137
+ * el `type`. Sin valor, el atributo no se renderiza. */
2138
+ readonly inputmode: _angular_core.InputSignal<string | null | undefined>;
1772
2139
  /** Agrega el botón de mostrar/ocultar — sin efecto si `type` no es `"password"`. */
1773
2140
  readonly toggleable: _angular_core.InputSignalWithTransform<boolean, unknown>;
2141
+ /** Nombre accesible del `<input>` cuando no hay `label` visible. Ignorado
2142
+ * si hay `label`: un `aria-label` pisaría al `<label for>` visible. */
2143
+ readonly ariaLabel: _angular_core.InputSignal<string | undefined>;
2144
+ /** Agrega un botón "×" para borrar el valor mientras haya texto (sin efecto con `disabled`/`readonly`). */
2145
+ readonly clearable: _angular_core.InputSignalWithTransform<boolean, unknown>;
2146
+ /** Nombre accesible del botón de borrar. */
2147
+ readonly clearLabel: _angular_core.InputSignal<string>;
2148
+ /** Emitido después de borrar el valor con el botón "×". */
2149
+ readonly cleared: _angular_core.OutputEmitterRef<void>;
1774
2150
  protected readonly inputId: string;
1775
2151
  protected readonly hintId: string;
1776
2152
  protected readonly errorId: string;
@@ -1784,6 +2160,8 @@ declare class Input implements ControlValueAccessor, AfterViewInit {
1784
2160
  protected readonly passwordVisible: _angular_core.WritableSignal<boolean>;
1785
2161
  /** El botón solo tiene sentido si el campo realmente es de contraseña. */
1786
2162
  protected readonly showPasswordToggle: _angular_core.Signal<boolean>;
2163
+ /** El botón de borrar solo aparece con texto y con el campo editable. */
2164
+ protected readonly showClear: _angular_core.Signal<boolean>;
1787
2165
  /**
1788
2166
  * `type` real que recibe el `<input>` nativo: igual a `type()` salvo
1789
2167
  * cuando el toggle está activo y visible, donde pasa a `text` para
@@ -1794,12 +2172,22 @@ declare class Input implements ControlValueAccessor, AfterViewInit {
1794
2172
  private onChange;
1795
2173
  private onTouched;
1796
2174
  ngAfterViewInit(): void;
2175
+ /** Enfoca el `<input>` nativo (mismas opciones que `HTMLElement.focus`). */
2176
+ focus(options?: FocusOptions): void;
1797
2177
  writeValue(value: string): void;
1798
2178
  registerOnChange(fn: (value: string) => void): void;
1799
2179
  registerOnTouched(fn: () => void): void;
1800
2180
  setDisabledState(isDisabled: boolean): void;
1801
2181
  protected onInput(rawValue: string): void;
1802
2182
  protected onBlur(): void;
2183
+ /**
2184
+ * Borra el valor reutilizando el mismo camino que el tipeo: un evento
2185
+ * `input` nativo pasa por `onInput` (notifica al formulario) y además
2186
+ * llega a cualquier `(input)` del consumidor. Con el mouse no hay blur:
2187
+ * el `mousedown` del botón hace `preventDefault` (ver Input.html), así el
2188
+ * campo no se marca `touched` por usar su propio "×".
2189
+ */
2190
+ protected onClear(): void;
1803
2191
  /**
1804
2192
  * Alterna la visibilidad del valor. No dispara `onBlur`/pierde el foco:
1805
2193
  * el botón vive dentro del mismo `.kf-input__field`, así que un click acá
@@ -1807,7 +2195,7 @@ declare class Input implements ControlValueAccessor, AfterViewInit {
1807
2195
  */
1808
2196
  protected onTogglePasswordVisibility(): void;
1809
2197
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<Input, never>;
1810
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<Input, "kf-input", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "suffix": { "alias": "suffix"; "required": false; "isSignal": true; }; "hint": { "alias": "hint"; "required": false; "isSignal": true; }; "invalid": { "alias": "invalid"; "required": false; "isSignal": true; }; "errorMessage": { "alias": "errorMessage"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "readonly": { "alias": "readonly"; "required": false; "isSignal": true; }; "type": { "alias": "type"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "inputmode": { "alias": "inputmode"; "required": false; "isSignal": true; }; "toggleable": { "alias": "toggleable"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
2198
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<Input, "kf-input", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "suffix": { "alias": "suffix"; "required": false; "isSignal": true; }; "hint": { "alias": "hint"; "required": false; "isSignal": true; }; "invalid": { "alias": "invalid"; "required": false; "isSignal": true; }; "errorMessage": { "alias": "errorMessage"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "readonly": { "alias": "readonly"; "required": false; "isSignal": true; }; "type": { "alias": "type"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "autocomplete": { "alias": "autocomplete"; "required": false; "isSignal": true; }; "name": { "alias": "name"; "required": false; "isSignal": true; }; "inputmode": { "alias": "inputmode"; "required": false; "isSignal": true; }; "toggleable": { "alias": "toggleable"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "clearable": { "alias": "clearable"; "required": false; "isSignal": true; }; "clearLabel": { "alias": "clearLabel"; "required": false; "isSignal": true; }; }, { "cleared": "cleared"; }, never, never, true, never>;
1811
2199
  }
1812
2200
 
1813
2201
  type JourneyStepStatus = 'done' | 'current' | 'delayed' | 'pending';
@@ -1900,6 +2288,205 @@ declare class Link {
1900
2288
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<Link, "kf-link", never, { "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "href": { "alias": "href"; "required": false; "isSignal": true; }; "showArrow": { "alias": "showArrow"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
1901
2289
  }
1902
2290
 
2291
+ /**
2292
+ * Modo de `kf-list-toolbar` según el ancho REAL disponible:
2293
+ * - `full`: todo en una fila con la densidad normal.
2294
+ * - `compact`: todo en una fila, con los chips más angostos (`data-density="compact"`).
2295
+ * - `panel`: solo el buscador y un botón "Filtros"; filtros y acciones finales se ocultan.
2296
+ */
2297
+ type ListToolbarMode = 'full' | 'compact' | 'panel';
2298
+ /** Anchos medidos (px): disponible en la barra y requerido por cada densidad. */
2299
+ interface ListToolbarWidths {
2300
+ available: number;
2301
+ full: number;
2302
+ compact: number;
2303
+ }
2304
+ /**
2305
+ * Margen de histéresis (px) para SUBIR de modo. Bajar es inmediato (si no
2306
+ * cabe, no cabe); subir exige este margen extra, así un ancho que oscila
2307
+ * justo en el umbral (scrollbar que aparece y desaparece, un contador que
2308
+ * pasa de 9 a 10) no hace parpadear la barra entre dos modos.
2309
+ */
2310
+ declare const LIST_TOOLBAR_HYSTERESIS = 24;
2311
+ /**
2312
+ * Decide el modo a partir del modo actual y los anchos medidos. Función pura
2313
+ * para poder probar la histéresis sin layout real.
2314
+ */
2315
+ declare function resolveListToolbarMode(widths: ListToolbarWidths, current: ListToolbarMode, hysteresis?: number): ListToolbarMode;
2316
+
2317
+ /**
2318
+ * Barra de herramientas de un listado: buscador, filtros y acciones finales
2319
+ * (orden, cambio de vista) en UNA fila, que se adapta al ancho REAL que
2320
+ * tiene disponible — no al del viewport. El mismo listado puede renderizarse
2321
+ * con el menú lateral abierto o cerrado, dentro de un panel o a pantalla
2322
+ * completa, y una `@media` no distingue esos casos; la barra mide su propio
2323
+ * ancho con `ResizeObserver`.
2324
+ *
2325
+ * ## Cuándo usarla y cuándo `kf-filter`
2326
+ * - `kf-list-toolbar`: chips con contador (`kf-filter-chips`) + orden + cambio
2327
+ * de vista + panel de filtros en modo estrecho, fija al hacer scroll y
2328
+ * guiada por el ancho del contenedor.
2329
+ * - `kf-filter`: selects, multi-select y buscador de texto en una fila de
2330
+ * formulario, que se pliega en un `kf-collapsible` a ≤768px de viewport.
2331
+ * Si el listado se filtra con campos, usar `kf-filter`; no se combinan ni
2332
+ * se reimplementan entre sí.
2333
+ *
2334
+ * ## Slots
2335
+ * - `[kf-toolbar-search]`: el buscador (ej. `kf-input icon="search"`). Siempre visible; ocupa el espacio libre.
2336
+ * - `[kf-toolbar-filters]`: filtros rápidos (ej. `kf-filter-chips`).
2337
+ * - `[kf-toolbar-end]`: acciones al final de la fila (ej. `kf-select` de orden, `kf-view-toggle`).
2338
+ *
2339
+ * Cada slot acepta VARIOS elementos con el mismo atributo y los ubica en
2340
+ * fila con `gap`; marcar cada control (no un `<div>` envoltorio sin
2341
+ * `display: flex`, que apilaría sus hijos en bloque).
2342
+ *
2343
+ * La barra ocupa el ancho de su contenedor (`display: block`). Dentro de un
2344
+ * contenedor flex que ajusta su ancho al contenido, darle un ancho
2345
+ * explícito: si el contenedor mide lo que la barra pide, la barra mide lo
2346
+ * que el contenedor le da y el modo nunca puede subir de `panel`.
2347
+ * - default: contenido no visual que debe quedar SIEMPRE presente, como una
2348
+ * región `aria-live` con el conteo de resultados (nunca dentro de un slot
2349
+ * que se oculta).
2350
+ *
2351
+ * ## Modos (`mode`, `modeChange`, atributo `data-mode`)
2352
+ * - `full`: todo cabe en una fila.
2353
+ * - `compact`: cabe en una fila solo con chips más angostos. El host expone
2354
+ * `data-density="compact"` y redefine `--kf-filter-chips-padding-inline`
2355
+ * (12px), que `kf-filter-chips` adopta sin configuración extra.
2356
+ * - `panel`: no cabe. Se ocultan `filters` y `end` y aparece, junto al
2357
+ * buscador, un botón "Filtros" (`filtersLabel`) con un badge de
2358
+ * `activeCount` (filtros/orden distintos del valor por defecto) que emite
2359
+ * `filtersClick` — el consumidor abre su propio panel (típicamente
2360
+ * `kf-drawer role="dialog"` con `kf-option-list`).
2361
+ *
2362
+ * Contrato con el panel de filtros: el consumidor DEBE pasar
2363
+ * `[filtersExpanded]` con el estado de su panel y DEBE cerrarlo al recibir
2364
+ * un `modeChange` distinto de `'panel'`. Al salir de `panel` con el panel
2365
+ * abierto, la barra mueve el foco al primer filtro visible (el botón
2366
+ * "Filtros" ya no existe para recibirlo); si el panel modal siguiera
2367
+ * abierto, ese foco quedaría fuera del diálogo.
2368
+ *
2369
+ * El modo sale de comparar el ancho disponible con el ancho natural de los
2370
+ * slots en cada densidad (más `searchMinWidth` para el buscador), con
2371
+ * histéresis al subir de modo (`LIST_TOOLBAR_HYSTERESIS`) para que no
2372
+ * parpadee en el umbral. Los slots ocultos en modo `panel` se siguen
2373
+ * midiendo (quedan fuera de vista con `visibility: hidden`, sin foco ni
2374
+ * árbol de accesibilidad), así un contador que crece se refleja igual.
2375
+ *
2376
+ * ## Fija (`sticky`)
2377
+ * Con `sticky`, la barra queda pegada arriba al hacer scroll (`stickyTop`
2378
+ * compensa una cabecera fija del shell). Cuando efectivamente queda pegada,
2379
+ * el host recibe la clase `kf-list-toolbar--stuck`: sombra más marcada y
2380
+ * menos alto. "Pegada" = el contenedor con scroll (el ancestro más cercano
2381
+ * con `overflow-y: auto|scroll`, o la ventana) ya se desplazó Y la barra
2382
+ * está en su posición fija. Sin centinela: un centinela por encima de la
2383
+ * barra quedaría recortado cuando la barra es el primer elemento del
2384
+ * contenedor, y la marcaría pegada en reposo. El fondo alrededor de
2385
+ * la barra fija sale de `--kf-list-toolbar-backdrop` (default
2386
+ * `--kf-color-surface`, el fondo habitual del área de contenido).
2387
+ *
2388
+ * Foco tapado (WCAG 2.4.11): una barra fija puede quedar encima del control
2389
+ * que recibe el foco al tabular hacia arriba. El consumidor debe aplicar
2390
+ * `scroll-padding-top` = `stickyTop` + alto de la barra al contenedor que
2391
+ * hace scroll (o a `html` si es la ventana), así el navegador deja ese
2392
+ * margen al llevar un elemento enfocado a la vista. El alto vigente se
2393
+ * publica como señal `height` (px), output `heightChange` y variable CSS
2394
+ * `--kf-list-toolbar-height` en el host (esta última solo la heredan los
2395
+ * descendientes; un ancestro debe usar la señal):
2396
+ *
2397
+ * <main class="content" [style.scroll-padding-top.px]="56 + toolbar.height()">
2398
+ * <kf-list-toolbar #toolbar sticky [stickyTop]="56" ... />
2399
+ * </main>
2400
+ *
2401
+ * <kf-list-toolbar sticky ariaLabel="Filtrar cotizaciones" [activeCount]="activeFilters()"
2402
+ * (filtersClick)="filtersOpen.set(true)" (modeChange)="mode.set($event)">
2403
+ * <span class="sr-only" aria-live="polite">{{ resultsLabel() }}</span>
2404
+ * <kf-input kf-toolbar-search icon="search" ... />
2405
+ * <kf-filter-chips kf-toolbar-filters ... />
2406
+ * <kf-select kf-toolbar-end variant="compact" icon="sort" ariaLabel="Ordenar por" ... />
2407
+ * <kf-view-toggle kf-toolbar-end ... />
2408
+ * </kf-list-toolbar>
2409
+ */
2410
+ declare class ListToolbar {
2411
+ /** Nombre accesible del landmark de búsqueda (`role="search"`). */
2412
+ readonly ariaLabel: _angular_core.InputSignal<string>;
2413
+ /** Fija la barra arriba al hacer scroll. */
2414
+ readonly sticky: _angular_core.InputSignalWithTransform<boolean, unknown>;
2415
+ /** Offset superior de la barra fija: número (px) o longitud CSS (ej. `'var(--shell-header-height)'`). */
2416
+ readonly stickyTop: _angular_core.InputSignal<string | number>;
2417
+ /** Ancho mínimo útil del buscador (px) — entra en el cálculo de si todo cabe en una fila. */
2418
+ readonly searchMinWidth: _angular_core.InputSignal<number>;
2419
+ /** Texto del botón del modo `panel`. */
2420
+ readonly filtersLabel: _angular_core.InputSignal<string>;
2421
+ /** Cantidad de filtros/orden distintos del valor por defecto; se muestra como badge en el botón del modo `panel`. */
2422
+ readonly activeCount: _angular_core.InputSignal<number>;
2423
+ /**
2424
+ * `aria-expanded` del botón del modo `panel`: el estado del panel que abre
2425
+ * el consumidor. Además, si es `true` al salir de `panel`, el foco pasa al
2426
+ * primer filtro visible — por eso el consumidor DEBE cerrar su panel en
2427
+ * ese mismo `modeChange` (ver "Contrato con el panel de filtros").
2428
+ */
2429
+ readonly filtersExpanded: _angular_core.InputSignal<boolean | undefined>;
2430
+ readonly filtersClick: _angular_core.OutputEmitterRef<void>;
2431
+ /**
2432
+ * Nuevo modo. Al recibir un valor distinto de `'panel'`, el consumidor
2433
+ * DEBE cerrar su panel de filtros (ver "Contrato con el panel de filtros").
2434
+ */
2435
+ readonly modeChange: _angular_core.OutputEmitterRef<ListToolbarMode>;
2436
+ /** Alto del host (px) cada vez que cambia — ver "Foco tapado" en el JSDoc de la clase. */
2437
+ readonly heightChange: _angular_core.OutputEmitterRef<number>;
2438
+ private readonly modeState;
2439
+ /** Modo vigente (también disponible como `data-mode` en el host). */
2440
+ readonly mode: _angular_core.Signal<ListToolbarMode>;
2441
+ private readonly stuckState;
2442
+ /** `true` mientras la barra fija está efectivamente pegada arriba. */
2443
+ readonly stuck: _angular_core.Signal<boolean>;
2444
+ private readonly heightState;
2445
+ /** Alto vigente del host en px (barra + padding de la variante fija). */
2446
+ readonly height: _angular_core.Signal<number>;
2447
+ protected readonly stickyTopCss: _angular_core.Signal<string>;
2448
+ protected readonly filtersAriaLabel: _angular_core.Signal<string | undefined>;
2449
+ private readonly hostRef;
2450
+ private readonly document;
2451
+ private readonly destroyRef;
2452
+ private readonly barRef;
2453
+ private readonly filtersRef;
2454
+ private readonly endRef;
2455
+ private readonly filtersButtonRef;
2456
+ /** A dónde mover el foco tras un cambio de modo que desmonta/oculta el elemento enfocado. */
2457
+ private pendingFocus;
2458
+ /** `requestAnimationFrame` pendiente de `measure()` (coalesce de notificaciones). */
2459
+ private measureFrame;
2460
+ private stuckFrame;
2461
+ private removeScrollListener;
2462
+ constructor();
2463
+ protected onFiltersClick(): void;
2464
+ /**
2465
+ * Mide el ancho disponible y el natural de cada slot en ambas
2466
+ * densidades, y actualiza el modo. La densidad de medición se fuerza con
2467
+ * `data-measure` (que gana sobre `data-density` en el CSS) y se retira en
2468
+ * el mismo frame: el navegador nunca pinta ese estado intermedio.
2469
+ */
2470
+ private measure;
2471
+ private updateHeight;
2472
+ private applyMode;
2473
+ private slotsContain;
2474
+ private restorePendingFocus;
2475
+ private observeSize;
2476
+ private scheduleMeasure;
2477
+ /** Engancha (o suelta) el cálculo de `stuck` al scroll del contenedor. */
2478
+ private watchScroll;
2479
+ /**
2480
+ * Pegada = el contenedor ya se desplazó Y la barra está en su posición
2481
+ * fija (`top` resuelto, con medio píxel de tolerancia por subpíxeles).
2482
+ * Sin la primera condición, una barra que es el primer elemento del
2483
+ * contenedor se leería pegada en reposo.
2484
+ */
2485
+ private updateStuck;
2486
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<ListToolbar, never>;
2487
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<ListToolbar, "kf-list-toolbar", never, { "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "sticky": { "alias": "sticky"; "required": false; "isSignal": true; }; "stickyTop": { "alias": "stickyTop"; "required": false; "isSignal": true; }; "searchMinWidth": { "alias": "searchMinWidth"; "required": false; "isSignal": true; }; "filtersLabel": { "alias": "filtersLabel"; "required": false; "isSignal": true; }; "activeCount": { "alias": "activeCount"; "required": false; "isSignal": true; }; "filtersExpanded": { "alias": "filtersExpanded"; "required": false; "isSignal": true; }; }, { "filtersClick": "filtersClick"; "modeChange": "modeChange"; "heightChange": "heightChange"; }, never, ["*", "[kf-toolbar-search]", "[kf-toolbar-filters]", "[kf-toolbar-end]"], true, never>;
2488
+ }
2489
+
1903
2490
  /**
1904
2491
  * Card de estadística/KPI: label, valor grande, ícono opcional en la esquina
1905
2492
  * superior derecha, badge de esquina opcional (pegado al valor) y una nota
@@ -1974,6 +2561,27 @@ declare class MetricCard {
1974
2561
  *
1975
2562
  * <kf-link footer class="kf-modal__footer-start" disabled>Ver documento</kf-link>
1976
2563
  * <kf-button footer (click)="open.set(false)">Cerrar</kf-button>
2564
+ *
2565
+ * ## Modal de listado (alto acotado, filtros fijos)
2566
+ * Para un diálogo con mucho contenido (ej. elegir un ítem entre cientos):
2567
+ * - `maxHeight` (ej. `"85vh"`) acota el panel; solo `.kf-modal__body` se
2568
+ * desplaza, el encabezado, la zona `[kf-modal-toolbar]` y el footer
2569
+ * quedan fijos.
2570
+ * - `[kf-modal-toolbar]`: zona FIJA entre el encabezado y el cuerpo, para
2571
+ * buscador y filtros.
2572
+ * - `[kf-modal-meta]`: dato corto junto al título (ej. "12 de 86").
2573
+ * - `fullscreenOnMobile`: hasta 640px de viewport ocupa toda la pantalla
2574
+ * (`100dvh`, sin radio).
2575
+ * - `initialFocus`: selector CSS del elemento que recibe el foco al abrir
2576
+ * (si apunta a un contenedor, se enfoca su primer control). Sin él, el
2577
+ * `<dialog>` nativo decide (`autofocus` o el primer control).
2578
+ *
2579
+ * <kf-modal [(open)]="open" title="Cambiar cotización" width="1040px"
2580
+ * maxHeight="85vh" fullscreenOnMobile initialFocus=".search">
2581
+ * <span kf-modal-meta aria-live="polite">12 de 86</span>
2582
+ * <div kf-modal-toolbar><kf-input class="search" … /></div>
2583
+ * <ul>…</ul>
2584
+ * </kf-modal>
1977
2585
  */
1978
2586
  declare class Modal {
1979
2587
  readonly title: _angular_core.InputSignal<string>;
@@ -1986,11 +2594,26 @@ declare class Modal {
1986
2594
  * más, ej. `width="720px"` — sigue topeado por `100vw` en pantallas
1987
2595
  * angostas, mismo `min()` de siempre. */
1988
2596
  readonly width: _angular_core.InputSignal<string | null>;
2597
+ /** Alto máximo del panel (ej. `"85vh"`); con valor, solo el cuerpo se
2598
+ * desplaza. Sin valor (default) el panel crece con su contenido. */
2599
+ readonly maxHeight: _angular_core.InputSignal<string | null>;
2600
+ /** Hasta 640px de viewport, ocupa toda la pantalla (100dvh, sin radio). */
2601
+ readonly fullscreenOnMobile: _angular_core.InputSignalWithTransform<boolean, unknown>;
2602
+ /** Selector CSS (dentro del panel) del elemento a enfocar al abrir. */
2603
+ readonly initialFocus: _angular_core.InputSignal<string | null>;
1989
2604
  protected readonly titleId: string;
1990
2605
  private readonly dialog;
2606
+ private readonly panel;
1991
2607
  private readonly document;
1992
2608
  private readonly overlayContainer;
1993
2609
  constructor();
2610
+ /**
2611
+ * Enfoca `initialFocus` DESPUÉS de `showModal()` (que ya movió el foco al
2612
+ * primer control): un elemento dentro de un `<dialog>` cerrado no se puede
2613
+ * enfocar. Si el selector apunta a un host (ej. `kf-input`), se enfoca su
2614
+ * primer control interno.
2615
+ */
2616
+ private focusInitial;
1994
2617
  /** `Escape` dispara `cancel`; cancelarlo lo deja abierto. */
1995
2618
  protected onCancel(event: Event): void;
1996
2619
  /**
@@ -2004,7 +2627,7 @@ declare class Modal {
2004
2627
  */
2005
2628
  protected onHostClick(event: MouseEvent): void;
2006
2629
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<Modal, never>;
2007
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<Modal, "kf-modal", never, { "title": { "alias": "title"; "required": false; "isSignal": true; }; "open": { "alias": "open"; "required": false; "isSignal": true; }; "dismissible": { "alias": "dismissible"; "required": false; "isSignal": true; }; "width": { "alias": "width"; "required": false; "isSignal": true; }; }, { "open": "openChange"; }, never, ["*", "[footer]"], true, never>;
2630
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<Modal, "kf-modal", never, { "title": { "alias": "title"; "required": false; "isSignal": true; }; "open": { "alias": "open"; "required": false; "isSignal": true; }; "dismissible": { "alias": "dismissible"; "required": false; "isSignal": true; }; "width": { "alias": "width"; "required": false; "isSignal": true; }; "maxHeight": { "alias": "maxHeight"; "required": false; "isSignal": true; }; "fullscreenOnMobile": { "alias": "fullscreenOnMobile"; "required": false; "isSignal": true; }; "initialFocus": { "alias": "initialFocus"; "required": false; "isSignal": true; }; }, { "open": "openChange"; }, never, ["[kf-modal-meta]", "[kf-modal-toolbar]", "*", "[footer]"], true, never>;
2008
2631
  }
2009
2632
 
2010
2633
  /** Color de la franja izquierda y del fondo. */
@@ -2026,6 +2649,84 @@ declare class NoteBlock {
2026
2649
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<NoteBlock, "kf-note-block", never, { "title": { "alias": "title"; "required": false; "isSignal": true; }; "subtitle": { "alias": "subtitle"; "required": false; "isSignal": true; }; "signature": { "alias": "signature"; "required": false; "isSignal": true; }; "tone": { "alias": "tone"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
2027
2650
  }
2028
2651
 
2652
+ /** Opción de `kf-option-list`. `count` se muestra alineado a la derecha. */
2653
+ interface OptionListOption {
2654
+ value: string;
2655
+ label: string;
2656
+ count?: number;
2657
+ }
2658
+ /**
2659
+ * Lista vertical de opciones de UNA sola selección para paneles (ej. las
2660
+ * secciones "Estado", "Ordenar por" y "Vista" de un panel de filtros): una
2661
+ * fila de al menos 44px por opción, contador opcional a la derecha y la
2662
+ * opción elegida resaltada con `--kf-color-primary-soft`.
2663
+ *
2664
+ * Es la contraparte en panel de `kf-filter-chips`: misma función (filtrar u
2665
+ * ordenar lo que se muestra) cuando no hay ancho para una fila de chips. No
2666
+ * es `kf-selectable-card-group`: aquel responde una pregunta de un flujo
2667
+ * ("¿con qué método pagas?") con affordance de radio y cards grandes; esta
2668
+ * lista ajusta una vista, con filas densas y contador.
2669
+ *
2670
+ * Semántica: `role="radiogroup"` + `role="radio"` con `aria-checked`, no
2671
+ * botones con `aria-pressed`. Las opciones son mutuamente excluyentes (eso
2672
+ * es un radio; `aria-pressed` describe toggles independientes), el lector
2673
+ * de pantalla anuncia la posición ("2 de 4") y cada grupo es UNA sola
2674
+ * parada de Tab: un panel con tres secciones se recorre con 3 Tabs en vez
2675
+ * de uno por opción. Dentro del grupo, flechas arriba/abajo y Home/End
2676
+ * mueven el foco y seleccionan (`handleArrowKeyNavigation`, mismo helper que
2677
+ * `kf-tabs`/`kf-selectable-card-group`).
2678
+ *
2679
+ * La opción elegida lleva además un check (no solo color), para que el
2680
+ * estado no dependa de distinguir el tinte `primary-soft` (WCAG 1.4.1).
2681
+ *
2682
+ * El `@for` hace `track` por índice: cuando el padre re-renderiza (ej. los
2683
+ * contadores cambian al elegir), los botones existentes se reutilizan y el
2684
+ * foco no se pierde. Por índice y no por `value` para no fallar (NG0955)
2685
+ * si dos opciones repiten `value`; aun así los values deben ser únicos:
2686
+ * con repetidos, ambas filas se marcan elegidas.
2687
+ *
2688
+ * `label` dibuja el título de la sección (chico, en mayúsculas) y nombra el
2689
+ * grupo vía `aria-labelledby`; sin título visible, pasar `ariaLabel`.
2690
+ *
2691
+ * <kf-option-list label="Estado"
2692
+ * [options]="[{ value: '', label: 'Todas', count: 12 },
2693
+ * { value: 'pending', label: 'Pendientes', count: 3 }]"
2694
+ * [value]="status()"
2695
+ * (valueChange)="status.set($event)" />
2696
+ */
2697
+ declare class OptionList {
2698
+ readonly options: _angular_core.InputSignal<(string | OptionListOption)[]>;
2699
+ readonly value: _angular_core.InputSignal<string>;
2700
+ /** Título visible de la sección; nombra el grupo. */
2701
+ readonly label: _angular_core.InputSignal<string | undefined>;
2702
+ /** Nombre accesible del grupo cuando no hay `label` visible. */
2703
+ readonly ariaLabel: _angular_core.InputSignal<string | undefined>;
2704
+ readonly valueChange: _angular_core.OutputEmitterRef<string>;
2705
+ private readonly instanceId;
2706
+ protected readonly labelId: string;
2707
+ /**
2708
+ * Prefijo de id de cada opción — único por instancia (dos listas con los
2709
+ * mismos values no colisionan). El id termina en el ÍNDICE, no en el
2710
+ * value: un value con espacios u otros caracteres daría un id inválido.
2711
+ */
2712
+ protected readonly optionIdPrefix: string;
2713
+ readonly normalizedOptions: _angular_core.Signal<OptionListOption[]>;
2714
+ /**
2715
+ * Opción que recibe `tabindex="0"`: la seleccionada o, si ninguna lo
2716
+ * está, la primera — el grupo siempre debe ser alcanzable con Tab.
2717
+ */
2718
+ protected readonly tabStopValue: _angular_core.Signal<string>;
2719
+ select(value: string): void;
2720
+ /**
2721
+ * `handleArrowKeyNavigation` ubica el botón destino por `idPrefix + value`;
2722
+ * como los ids usan el índice, se le pasa la lista con el índice como
2723
+ * value y se traduce de vuelta al value real al seleccionar.
2724
+ */
2725
+ protected onKeydown(event: KeyboardEvent, index: number): void;
2726
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<OptionList, never>;
2727
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<OptionList, "kf-option-list", never, { "options": { "alias": "options"; "required": true; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; }, { "valueChange": "valueChange"; }, never, never, true, never>;
2728
+ }
2729
+
2029
2730
  type TextAs = 'h1' | 'h2' | 'h3' | 'p' | 'span';
2030
2731
  type TextVariant = 'title-xxl' | 'title-xl' | 'title-lg' | 'title-md' | 'title-sm' | 'body' | 'body-muted' | 'caption' | 'overline' | 'overline-strong';
2031
2732
  type TextTone = 'neutral' | 'ok' | 'warn' | 'error' | 'info';
@@ -2628,10 +3329,20 @@ declare class Segmented {
2628
3329
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<Segmented, "kf-segmented", never, { "options": { "alias": "options"; "required": true; "isSignal": true; }; "value": { "alias": "value"; "required": true; "isSignal": true; }; }, { "valueChange": "valueChange"; }, never, never, true, never>;
2629
3330
  }
2630
3331
 
2631
- /** Normalized shape used internally once `options` (string[] or object[]) is resolved. */
3332
+ /**
3333
+ * Opción en forma de objeto. `tag` se muestra como `kf-badge` a la derecha
3334
+ * del label (en el panel y en el trigger) y `detail` como línea secundaria
3335
+ * atenuada debajo, solo en el panel. Ambos entran en la búsqueda.
3336
+ * `shortLabel` reemplaza al label SOLO en el trigger (ej. panel "Más
3337
+ * recientes primero", trigger "Más recientes") — para triggers compactos
3338
+ * donde el label completo se truncaría.
3339
+ */
2632
3340
  interface SelectOption {
2633
3341
  value: string;
2634
3342
  label: string;
3343
+ tag?: string;
3344
+ detail?: string;
3345
+ shortLabel?: string;
2635
3346
  }
2636
3347
  /**
2637
3348
  * Dropdown de selección simple con panel propio — NUNCA usa el `<select>`
@@ -2644,6 +3355,7 @@ interface SelectOption {
2644
3355
  *
2645
3356
  * <kf-select label="Sede" [options]="['Lima', 'Arequipa', 'Trujillo']" placeholder="Selecciona…" />
2646
3357
  * <kf-select [options]="[{ value: 'pe', label: 'Perú' }]" (valueChange)="onCountry($event)" />
3358
+ * <kf-select [options]="[{ value: '1002099', label: 'BBVA', tag: 'Empresa', detail: 'RUC 20100130204' }]" />
2647
3359
  *
2648
3360
  * `variant="compact"` cambia el ancho de `240px` fijo a intrínseco con un
2649
3361
  * techo de `160px` (se ajusta al contenido de la opción seleccionada hasta
@@ -2660,10 +3372,29 @@ interface SelectOption {
2660
3372
  * layouts flex/grid donde el ancho fijo de `md` no encaja.
2661
3373
  *
2662
3374
  * <kf-select width="full" [options]="branches" [value]="branch()" (valueChange)="..." />
3375
+ *
3376
+ * Control de orden de una barra de herramientas (ícono + valor + caret, sin
3377
+ * label visible): `icon` agrega un glifo `sm` a la izquierda del valor,
3378
+ * `ariaLabel` da el nombre accesible (el trigger se anuncia como
3379
+ * `"<ariaLabel>: <valor actual>"`), `panelTitle` agrega un título chico en
3380
+ * mayúsculas dentro del panel (decorativo, `aria-hidden`) y
3381
+ * `panelWidth="content"` deja que el panel crezca hasta el ancho de sus
3382
+ * opciones en vez de truncarlas al ancho del trigger compacto.
3383
+ *
3384
+ * <kf-select variant="compact" icon="sort" ariaLabel="Ordenar por"
3385
+ * panelTitle="Ordenar por" panelWidth="content"
3386
+ * [options]="sortOptions" [value]="sort()" (valueChange)="sort.set($event)" />
3387
+ *
3388
+ * Teclado (patrón listbox de WAI-ARIA): ArrowDown/ArrowUp sobre el trigger
3389
+ * cerrado lo abren; al abrir, el foco pasa a la opción seleccionada (o al
3390
+ * buscador, si está visible). En el panel, flechas y Home/End mueven el
3391
+ * foco, Enter/Espacio eligen, Escape cierra y devuelve el foco al trigger,
3392
+ * y Tab cierra el panel y continúa la tabulación desde el trigger. Las
3393
+ * opciones nunca son paradas de Tab (`tabindex="-1"`).
2663
3394
  */
2664
3395
  declare class Select {
2665
3396
  readonly label: _angular_core.InputSignal<string | undefined>;
2666
- readonly options: _angular_core.InputSignal<string[] | SelectOption[]>;
3397
+ readonly options: _angular_core.InputSignal<readonly (string | SelectOption)[]>;
2667
3398
  readonly value: _angular_core.InputSignal<string | undefined>;
2668
3399
  readonly placeholder: _angular_core.InputSignal<string>;
2669
3400
  readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
@@ -2671,7 +3402,7 @@ declare class Select {
2671
3402
  readonly invalid: _angular_core.InputSignalWithTransform<boolean, unknown>;
2672
3403
  /** Mensaje de error con `role="alert"`, debajo del trigger. */
2673
3404
  readonly errorMessage: _angular_core.InputSignal<string | undefined>;
2674
- readonly variant: _angular_core.InputSignal<"field" | "compact">;
3405
+ readonly variant: _angular_core.InputSignal<"compact" | "field">;
2675
3406
  /**
2676
3407
  * Ancho del control. 'md' (240px) es el default histórico y preserva el
2677
3408
  * comportamiento actual sin cambios para consumidores existentes.
@@ -2687,6 +3418,26 @@ declare class Select {
2687
3418
  */
2688
3419
  readonly searchable: _angular_core.InputSignal<boolean | undefined>;
2689
3420
  readonly searchThreshold: _angular_core.InputSignal<number>;
3421
+ /**
3422
+ * Nombre accesible del control cuando no hay `label` visible (ej. "Ordenar
3423
+ * por"). El trigger se anuncia como `"<ariaLabel>: <valor visible>"` (el
3424
+ * mismo texto que muestra el trigger, `shortLabel` si la opción lo trae),
3425
+ * así el nombre contiene el texto visible (WCAG 2.5.3) y además dice qué se
3426
+ * está eligiendo. Ignorado si hay `label`: un `aria-label` pisaría al
3427
+ * `<label for>` visible.
3428
+ */
3429
+ readonly ariaLabel: _angular_core.InputSignal<string | undefined>;
3430
+ /** Ícono `sm` a la izquierda del valor en el trigger (ej. `'sort'`). */
3431
+ readonly icon: _angular_core.InputSignal<IconName | undefined>;
3432
+ /** Título chico en mayúsculas dentro del panel, sobre las opciones. Decorativo (`aria-hidden`): el nombre del listbox ya lo da `ariaLabel`/`label`. */
3433
+ readonly panelTitle: _angular_core.InputSignal<string | undefined>;
3434
+ /**
3435
+ * `'trigger'` (default): el panel mide lo mismo que el trigger.
3436
+ * `'content'`: el trigger es el ancho mínimo y el panel crece hasta el
3437
+ * ancho de la opción más larga — para triggers compactos cuyas opciones
3438
+ * son más largas que el valor resumido.
3439
+ */
3440
+ readonly panelWidth: _angular_core.InputSignal<"content" | "trigger">;
2690
3441
  readonly valueChange: _angular_core.OutputEmitterRef<string>;
2691
3442
  private readonly instanceId;
2692
3443
  readonly triggerId: string;
@@ -2695,15 +3446,25 @@ declare class Select {
2695
3446
  readonly isOpen: _angular_core.WritableSignal<boolean>;
2696
3447
  readonly searchQuery: _angular_core.WritableSignal<string>;
2697
3448
  readonly activeIndex: _angular_core.WritableSignal<number>;
2698
- /** Resolves the union (`string[] | { value; label }[]`) into a single shape for the template. */
3449
+ /** Resolves `string | SelectOption` entries into a single shape for the template. */
2699
3450
  readonly normalizedOptions: _angular_core.Signal<SelectOption[]>;
2700
3451
  readonly selectedOption: _angular_core.Signal<SelectOption | undefined>;
3452
+ /** Texto que muestra el trigger: `shortLabel` o `label` de la opción elegida, o el placeholder. */
3453
+ readonly triggerText: _angular_core.Signal<string>;
3454
+ /**
3455
+ * `aria-label` del trigger: solo con `ariaLabel` y SIN `label` visible. Un
3456
+ * `aria-label` tiene prioridad sobre el `<label for>` en el cálculo del
3457
+ * nombre accesible, así que con label visible no se emite.
3458
+ */
3459
+ readonly triggerAriaLabel: _angular_core.Signal<string | null>;
2701
3460
  readonly showSearch: _angular_core.Signal<boolean>;
2702
3461
  /** Opciones visibles tras aplicar el filtro de búsqueda (case/acento-insensitive). */
2703
3462
  readonly filteredOptions: _angular_core.Signal<SelectOption[]>;
2704
3463
  private readonly triggerRef;
2705
3464
  private readonly panelTemplate;
2706
3465
  private readonly searchInputRef;
3466
+ private readonly panelRef;
3467
+ private readonly document;
2707
3468
  private readonly overlay;
2708
3469
  constructor();
2709
3470
  /**
@@ -2714,6 +3475,40 @@ declare class Select {
2714
3475
  */
2715
3476
  onDocumentEscape(): void;
2716
3477
  toggle(): void;
3478
+ /** Abre el panel con la opción seleccionada como activa (o la primera). */
3479
+ open(): void;
3480
+ /**
3481
+ * Cierra el panel. `restoreFocus` (default: solo si el foco está dentro
3482
+ * del panel) devuelve el foco al trigger ANTES de desmontar el panel —
3483
+ * si no, el elemento enfocado desaparece y el foco cae al `<body>`. Un
3484
+ * click afuera no lo restaura: el foco ya se fue a donde se hizo click.
3485
+ */
3486
+ close(restoreFocus?: boolean): void;
3487
+ /** ArrowDown/ArrowUp con el panel cerrado lo abren (patrón listbox). */
3488
+ onTriggerKeydown(event: KeyboardEvent): void;
3489
+ /**
3490
+ * Teclado con el foco en una opción del panel. Las flechas parten SIEMPRE
3491
+ * de la opción que recibió el evento (`index`), no de `activeIndex`: el
3492
+ * `mouseenter` mueve `activeIndex` sin mover el foco, y sin esto, con el
3493
+ * foco en la opción 1 y el mouse sobre la 4, ArrowDown saltaría a la 5.
3494
+ */
3495
+ onOptionKeydown(event: KeyboardEvent, option: SelectOption, index: number): void;
3496
+ /** Teclas de salida del buscador: Escape y Tab, igual que desde una opción. */
3497
+ onSearchKeydown(event: KeyboardEvent): void;
3498
+ /**
3499
+ * Escape cierra y devuelve el foco al trigger; `preventDefault` le avisa a
3500
+ * un contenedor que también escucha Escape (ej. `kf-drawer`) que la tecla
3501
+ * ya se consumió. Tab cierra y mueve el foco al trigger SIN
3502
+ * `preventDefault`: la tabulación nativa continúa desde el trigger, así
3503
+ * Tab/Shift+Tab salen del panel hacia el control siguiente/anterior.
3504
+ */
3505
+ private handleExitKeys;
3506
+ private focusOption;
3507
+ private isFocusInPanel;
3508
+ /** Id de cada opción, para `aria-activedescendant` del buscador. */
3509
+ optionId(index: number): string;
3510
+ /** Nombre accesible explícito solo para opciones con `tag`/`detail`, para separar las partes al leerlas. */
3511
+ optionAriaLabel(option: SelectOption): string | null;
2717
3512
  select(option: SelectOption): void;
2718
3513
  onSearchInput(event: Event): void;
2719
3514
  /**
@@ -2723,9 +3518,16 @@ declare class Select {
2723
3518
  * cuando se combina con un buscador que puede reducir la lista a 1 opción.
2724
3519
  */
2725
3520
  moveActive(delta: number): void;
3521
+ /**
3522
+ * Con el foco en el buscador, la opción activa se marca con
3523
+ * `aria-activedescendant` pero no recibe foco, así que el navegador no la
3524
+ * desplaza a la vista: se hace a mano. (`scrollIntoView` puede no existir
3525
+ * en entornos sin layout.)
3526
+ */
3527
+ private scrollActiveIntoView;
2726
3528
  selectActive(): void;
2727
3529
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<Select, never>;
2728
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<Select, "kf-select", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": true; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "invalid": { "alias": "invalid"; "required": false; "isSignal": true; }; "errorMessage": { "alias": "errorMessage"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "width": { "alias": "width"; "required": false; "isSignal": true; }; "searchable": { "alias": "searchable"; "required": false; "isSignal": true; }; "searchThreshold": { "alias": "searchThreshold"; "required": false; "isSignal": true; }; }, { "valueChange": "valueChange"; }, never, never, true, never>;
3530
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<Select, "kf-select", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": true; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "invalid": { "alias": "invalid"; "required": false; "isSignal": true; }; "errorMessage": { "alias": "errorMessage"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "width": { "alias": "width"; "required": false; "isSignal": true; }; "searchable": { "alias": "searchable"; "required": false; "isSignal": true; }; "searchThreshold": { "alias": "searchThreshold"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "panelTitle": { "alias": "panelTitle"; "required": false; "isSignal": true; }; "panelWidth": { "alias": "panelWidth"; "required": false; "isSignal": true; }; }, { "valueChange": "valueChange"; }, never, never, true, never>;
2729
3531
  }
2730
3532
 
2731
3533
  interface SelectableCardOption {
@@ -2999,7 +3801,7 @@ declare class StatTile {
2999
3801
  * <kf-status-pill tone="error" dot>Fuera de servicio</kf-status-pill>
3000
3802
  */
3001
3803
  declare class StatusPill {
3002
- readonly tone: _angular_core.InputSignal<"ok" | "warn" | "error" | "blue">;
3804
+ readonly tone: _angular_core.InputSignal<"error" | "ok" | "warn" | "blue">;
3003
3805
  readonly dot: _angular_core.InputSignalWithTransform<boolean, unknown>;
3004
3806
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<StatusPill, never>;
3005
3807
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<StatusPill, "kf-status-pill", never, { "tone": { "alias": "tone"; "required": false; "isSignal": true; }; "dot": { "alias": "dot"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
@@ -3443,17 +4245,31 @@ type ViewToggleOption = 'table' | 'cards';
3443
4245
  * navegación con flechas izquierda/derecha, igual que un <input type="radio">
3444
4246
  * nativo agrupado.
3445
4247
  *
4248
+ * Con `iconOnly` cada opción muestra solo su ícono (`list` / `grid`); el
4249
+ * nombre accesible y el tooltip salen de `tableLabel` / `cardsLabel`. El
4250
+ * objetivo táctil mide 32px en escritorio y 44px en pantallas táctiles o de
4251
+ * hasta 640px.
4252
+ *
3446
4253
  * <kf-view-toggle [view]="view" (viewChange)="view = $event" />
4254
+ * <kf-view-toggle iconOnly [view]="view" (viewChange)="view = $event" />
3447
4255
  */
3448
4256
  declare class ViewToggle {
3449
4257
  readonly view: _angular_core.InputSignal<ViewToggleOption>;
4258
+ /** Solo íconos (el rótulo queda como nombre accesible y `title`). */
4259
+ readonly iconOnly: _angular_core.InputSignalWithTransform<boolean, unknown>;
4260
+ /** Rótulo de la opción de tabla. */
4261
+ readonly tableLabel: _angular_core.InputSignal<string>;
4262
+ /** Rótulo de la opción de tarjetas. */
4263
+ readonly cardsLabel: _angular_core.InputSignal<string>;
4264
+ /** Nombre accesible del grupo. */
4265
+ readonly ariaLabel: _angular_core.InputSignal<string>;
3450
4266
  readonly viewChange: _angular_core.OutputEmitterRef<ViewToggleOption>;
3451
4267
  select(next: ViewToggleOption): void;
3452
4268
  /** Navegación estándar de ARIA radiogroup: flechas mueven foco y seleccionan. */
3453
4269
  onKeydown(event: KeyboardEvent): void;
3454
4270
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<ViewToggle, never>;
3455
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<ViewToggle, "kf-view-toggle", never, { "view": { "alias": "view"; "required": true; "isSignal": true; }; }, { "viewChange": "viewChange"; }, never, never, true, never>;
4271
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<ViewToggle, "kf-view-toggle", never, { "view": { "alias": "view"; "required": true; "isSignal": true; }; "iconOnly": { "alias": "iconOnly"; "required": false; "isSignal": true; }; "tableLabel": { "alias": "tableLabel"; "required": false; "isSignal": true; }; "cardsLabel": { "alias": "cardsLabel"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; }, { "viewChange": "viewChange"; }, never, never, true, never>;
3456
4272
  }
3457
4273
 
3458
- export { ActionTile, AdminBadge, Alert, AlertRow, Avatar, Badge, Breadcrumb, BucketProgress, Button, Calendar, Card, Carousel, CarouselSlide, Checkbox, Chip, Collapsible, ContactPopover, ContentSlot, ContentState, ContentViewer, DEFAULT_PAGE_SIZE_OPTIONS, DataTable, Divider, DocumentRow, Drawer, Dropdown, DropdownItem, DropdownPanel, EmptyState, EventTimeline, FeatureCard, Field, FieldLabel, Filter, FilterChips, FolderTabs, FolderTabsPanel, Footer, FormatInputDirective, Icon, Input, JourneyStepper, Link, MetricCard, Modal, MultiSelect, NoteBlock, PageHeader, Pagination, Panel, PortalHeader, Progress, PulseDot, RowActions, SectionHeader, SectionNav, SectionNavItem, SectionNavSubsection, Segmented, Select, SelectableCardGroup, SidebarShell, Skeleton, Spinner, StatCard, StatTile, StatusPill, Tabs, Text, Textarea, TileGrid, TimeSlotPicker, ToastHost, ToastService, TooltipDirective, ViewToggle, dataTableViewSignal };
3459
- export type { AlertRowTone, AlertTone, BreadcrumbItem, BucketProgressItem, CalendarDay, ContentStateKind, ContentStateLoad, ContentViewerType, DataTableCardRole, DataTableColumn, DataTableDensity, DataTableSort, DataTableView, DividerOrientation, DocumentRowTone, DrawerSide, DropdownItemTone, DropdownPanelVariant, EventTimelineItem, EventTimelineTone, FilterFieldChange, FilterFieldConfig, FilterFieldType, FilterValue, FolderTabOption, FooterLink, IconName, IconRotation, IconSize, JourneyStep, JourneyStepStatus, JourneyStepperColorScheme, JourneyStepperSurface, LinkVariant, MultiSelectOption, NoteBlockTone, PageHeaderTitleSize, PaginationItem, PortalHeaderAdminAction, PortalHeaderNavItem, ProgressTone, RowAction, RowActionKind, RowActionsMode, SectionNavSubsectionData, SegmentedOption, SelectableCardOption, SidebarNavItemData, SkeletonVariant, SpinnerSize, StatCardTone, StatTileTone, StatTileVariant, TabOption, TextAs, TextTone, TextVariant, TimeSlot, ToastAction, ToastOptions, ToastPromiseMessages, ToastState, ToastTone, TooltipPosition, ViewToggleOption };
4274
+ export { ActionTile, AdminBadge, Alert, AlertRow, Avatar, Badge, Breadcrumb, BucketProgress, Button, Calendar, Card, Carousel, CarouselSlide, Checkbox, Chip, Collapsible, ContactPopover, ContentSlot, ContentState, ContentViewer, DEFAULT_PAGE_SIZE_OPTIONS, DataTable, Divider, DocumentRow, Drawer, Dropdown, DropdownItem, DropdownPanel, EmptyState, EventTimeline, FeatureCard, Field, FieldLabel, Filter, FilterChips, FolderTabs, FolderTabsPanel, Footer, FormatInputDirective, Icon, Input, JourneyStepper, LIST_TOOLBAR_HYSTERESIS, Link, ListToolbar, MetricCard, Modal, MultiSelect, NoteBlock, OptionList, PageHeader, Pagination, Panel, PortalHeader, Progress, PulseDot, RowActions, SectionHeader, SectionNav, SectionNavItem, SectionNavSubsection, Segmented, Select, SelectableCardGroup, SidebarShell, Skeleton, Spinner, StatCard, StatTile, StatusPill, Tabs, Text, Textarea, TileGrid, TimeSlotPicker, ToastHost, ToastService, TooltipDirective, ViewToggle, dataTableViewSignal, resolveListToolbarMode };
4275
+ export type { AlertRowTone, AlertTone, BreadcrumbItem, BucketProgressItem, CalendarDay, ContentStateKind, ContentStateLoad, ContentViewerType, DataTableCardRole, DataTableColumn, DataTableDensity, DataTableSort, DataTableView, DividerOrientation, DocumentRowTone, DrawerRole, DrawerSide, DropdownItemTone, DropdownPanelVariant, EmptyStateTone, EventTimelineItem, EventTimelineTone, FilterChipOption, FilterFieldChange, FilterFieldConfig, FilterFieldType, FilterValue, FolderTabOption, FooterLink, IconName, IconRotation, IconSize, JourneyStep, JourneyStepStatus, JourneyStepperColorScheme, JourneyStepperSurface, LinkVariant, ListToolbarMode, MultiSelectOption, NoteBlockTone, OptionListOption, PageHeaderTitleSize, PaginationItem, PortalHeaderAdminAction, PortalHeaderNavItem, ProgressTone, RowAction, RowActionKind, RowActionsMode, SectionNavSubsectionData, SegmentedOption, SelectOption, SelectableCardOption, SidebarNavItemData, SkeletonVariant, SpinnerSize, StatCardTone, StatTileTone, StatTileVariant, TabOption, TextAs, TextTone, TextVariant, TimeSlot, ToastAction, ToastOptions, ToastPromiseMessages, ToastState, ToastTone, TooltipPosition, ViewToggleOption };