@kaufmann-lab/ui 0.1.2 → 0.1.8

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaufmann-lab/ui",
3
- "version": "0.1.2",
3
+ "version": "0.1.8",
4
4
  "description": "Shared Angular component library for the Kaufmann frontends",
5
5
  "license": "MIT",
6
6
  "author": "Kaufmann Lab",
@@ -8,6 +8,7 @@
8
8
  "access": "public"
9
9
  },
10
10
  "peerDependencies": {
11
+ "@angular/cdk": "^22.1.0",
11
12
  "@angular/common": "^22.1.0",
12
13
  "@angular/core": "^22.1.0"
13
14
  },
@@ -106,6 +106,26 @@
106
106
  --kf-font-body: 'Roboto', system-ui, -apple-system, sans-serif;
107
107
  --kf-font-mono: 'Roboto Mono', ui-monospace, 'SFMono-Regular', Menlo, monospace;
108
108
 
109
+ /*
110
+ * Escala de tamaño (usada por `kf-text`, ver variant `title-lg|md|sm` y
111
+ * `body|body-muted|caption`). Antes de esta escala, cada pantalla que
112
+ * consume la librería definía su propio `font-size` suelto para el mismo
113
+ * rol semántico — auditoría sobre `web-portal-latam-frontend` encontró 9
114
+ * valores distintos entre login y home para títulos/subtítulos/texto
115
+ * secundario (0.6875rem a 2.125rem), sin ningún paso de escala en común.
116
+ * Estos 6 pasos cubren esos casos reales, no son arbitrarios.
117
+ */
118
+ --kf-font-size-title-lg: 1.75rem;
119
+ --kf-font-size-title-md: 1.5rem;
120
+ --kf-font-size-title-sm: 1.125rem;
121
+ --kf-font-size-body: 1rem;
122
+ --kf-font-size-body-sm: 0.875rem;
123
+ --kf-font-size-caption: 0.75rem;
124
+
125
+ /* ---------- Peso de fuente ---------- */
126
+ --kf-font-weight-title: 700;
127
+ --kf-font-weight-body: 400;
128
+
109
129
  /* ---------- Espaciado (escala 4/8px) ---------- */
110
130
  --kf-space-xs: 4px;
111
131
  --kf-space-sm: 8px;
@@ -1,5 +1,5 @@
1
1
  import * as _angular_core from '@angular/core';
2
- import { AfterContentInit, TemplateRef, AfterViewInit } from '@angular/core';
2
+ import { AfterContentInit, TemplateRef, ElementRef, AfterViewInit } from '@angular/core';
3
3
  import { ControlValueAccessor } from '@angular/forms';
4
4
 
5
5
  /**
@@ -8,11 +8,17 @@ import { ControlValueAccessor } from '@angular/forms';
8
8
  * el toggle de mostrar/ocultar contraseña de `Input` — no hay un ícono de
9
9
  * "ojo tachado" en la spec original porque esa interacción no estaba
10
10
  * contemplada ahí.
11
+ *
12
+ * Separado de `Icon.ts` a propósito: este type es el contrato público que
13
+ * comparten `Icon`, `icons/index.ts` (registro) y cualquier componente que
14
+ * reciba un `IconName` (`Button`, `MetricCard`, etc.) — no debería requerir
15
+ * importar el componente `Icon` completo solo para tipar una prop.
11
16
  */
12
17
  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' | 'download' | 'edit' | 'external-link' | 'eye' | 'eye-off' | 'file' | 'filter' | 'gauge' | 'gift' | 'grid' | 'history' | 'inbox' | 'layers' | 'list' | 'map' | 'map-pin' | 'menu' | 'mic' | 'mic-off' | 'more-vertical' | 'pause' | 'phone' | 'play' | 'plus' | 'search' | 'settings' | 'shield-check' | 'sort' | 'star' | 'ticket' | 'tool' | 'trash' | 'truck' | 'upload-cloud' | 'user' | 'user-search' | 'video' | 'x';
18
+
13
19
  type IconSize = 'sm' | 'md' | 'lg';
14
20
  /**
15
- * Ícono de trazo del design system — 59 glyphs (página 5), grilla 24×24,
21
+ * Ícono de trazo del design system — 60 glyphs (59 de la página 5 + `eye-off`), grilla 24×24,
16
22
  * grosor de trazo 1.9px. Referencia un sprite SVG embebido (ver
17
23
  * `icon-sprite.ts`) vía `<use>`, así que no hay peso de red adicional por
18
24
  * cada instancia: el sprite se inyecta al DOM una única vez, la primera
@@ -89,10 +95,16 @@ declare class AdminBadge {
89
95
  *
90
96
  * `(click)` is not forwarded — it bubbles from the inner button to the host.
91
97
  *
98
+ * `loading` replaces the projected content with `kf-spinner` and forces
99
+ * `disabled` — for the common "Ingresando…"/"Enviando…" submit pattern,
100
+ * which before this prop existed as plain text swaps with no visual
101
+ * indicator (see `web-portal-latam-frontend`'s login submit buttons).
102
+ *
92
103
  * <kf-button (click)="save()">Save</kf-button>
93
104
  * <kf-button variant="ghost" disabled>Cancel</kf-button>
94
105
  * <kf-button variant="danger" (click)="remove()">Delete</kf-button>
95
106
  * <kf-button size="sm" icon="calendar" block>Reservar hora</kf-button>
107
+ * <kf-button type="submit" [loading]="submitting()">Ingresar</kf-button>
96
108
  */
97
109
  declare class Button {
98
110
  readonly variant: _angular_core.InputSignal<"primary" | "secondary" | "ghost" | "outline" | "danger">;
@@ -104,8 +116,12 @@ declare class Button {
104
116
  readonly icon: _angular_core.InputSignal<IconName | undefined>;
105
117
  /** Stretches the button to the full width of its container. */
106
118
  readonly block: _angular_core.InputSignalWithTransform<boolean, unknown>;
119
+ /** Replaces the projected content with `kf-spinner` and forces `disabled`. */
120
+ readonly loading: _angular_core.InputSignalWithTransform<boolean, unknown>;
121
+ /** `ghost`/`outline` have no solid background — the default spinner ring already reads fine there. */
122
+ protected readonly hasSolidFill: _angular_core.Signal<boolean>;
107
123
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<Button, never>;
108
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<Button, "kf-button", never, { "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "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; }; }, {}, never, ["*"], true, never>;
124
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<Button, "kf-button", never, { "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "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; }; }, {}, never, ["*"], true, never>;
109
125
  }
110
126
 
111
127
  type AlertTone = 'info' | 'ok' | 'warn' | 'error';
@@ -190,7 +206,7 @@ declare class Avatar {
190
206
  * no existe un `-strong` para secondary todavía — usa el color base).
191
207
  */
192
208
  declare class Badge {
193
- readonly tone: _angular_core.InputSignal<"info" | "ok" | "warn" | "error" | "neutral" | "service">;
209
+ readonly tone: _angular_core.InputSignal<"error" | "info" | "ok" | "warn" | "neutral" | "service">;
194
210
  readonly dot: _angular_core.InputSignalWithTransform<boolean, unknown>;
195
211
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<Badge, never>;
196
212
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<Badge, "kf-badge", never, { "tone": { "alias": "tone"; "required": false; "isSignal": true; }; "dot": { "alias": "dot"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
@@ -377,7 +393,7 @@ declare class Checkbox implements ControlValueAccessor {
377
393
  * de superficie/borde/texto-muted más cercanos).
378
394
  */
379
395
  declare class Chip {
380
- readonly tone: _angular_core.InputSignal<"ok" | "warn" | "error" | "neutral">;
396
+ readonly tone: _angular_core.InputSignal<"error" | "ok" | "warn" | "neutral">;
381
397
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<Chip, never>;
382
398
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<Chip, "kf-chip", never, { "tone": { "alias": "tone"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
383
399
  }
@@ -583,6 +599,132 @@ declare class DataTable<T = unknown> {
583
599
  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; }; "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; }; }, { "rowClick": "rowClick"; }, never, ["[empty]", "[footer]"], true, never>;
584
600
  }
585
601
 
602
+ type DividerOrientation = 'horizontal' | 'vertical';
603
+ /**
604
+ * Separador visual simple entre bloques de contenido. `orientation`
605
+ * controla si es una línea horizontal (para separar secciones apiladas) o
606
+ * vertical (para separar elementos en una fila, ej. acciones en una toolbar,
607
+ * o las columnas de metadata en `kf-company-selector`).
608
+ *
609
+ * En `vertical`, el divisor necesita una altura del contexto donde vive —
610
+ * `:host { align-self: stretch }` lo hace ocupar la altura del contenedor
611
+ * flex/grid padre sin que el consumidor tenga que fijarla a mano. `flex:
612
+ * none` evita que el propio divisor se achique a 0 dentro de ese contenedor
613
+ * flex cuando hay contenido hermano compitiendo por espacio.
614
+ *
615
+ * <kf-divider />
616
+ * <kf-divider orientation="vertical" />
617
+ */
618
+ declare class Divider {
619
+ readonly orientation: _angular_core.InputSignal<DividerOrientation>;
620
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<Divider, never>;
621
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<Divider, "kf-divider", never, { "orientation": { "alias": "orientation"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
622
+ }
623
+
624
+ type DropdownItemTone = 'neutral' | 'error';
625
+ /**
626
+ * Ítem de acción dentro de un `kf-dropdown-panel`. Es un `<button>` nativo
627
+ * por dentro (no un `<a>`): las acciones de un menú de este tipo disparan
628
+ * comportamiento en la pantalla actual (cerrar sesión, ejecutar un
629
+ * comando), no navegan a otra URL.
630
+ *
631
+ * <kf-dropdown-item (click)="onLogout()" tone="error">Cerrar sesión</kf-dropdown-item>
632
+ */
633
+ declare class DropdownItem {
634
+ readonly tone: _angular_core.InputSignal<DropdownItemTone>;
635
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<DropdownItem, never>;
636
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<DropdownItem, "kf-dropdown-item", never, { "tone": { "alias": "tone"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
637
+ }
638
+ /**
639
+ * Contenido del panel flotante de un `kf-dropdown`. Es solo una plantilla
640
+ * (`<ng-template>` por dentro, capturada vía `TemplateRef`) — `Dropdown` la
641
+ * conecta al overlay real cuando `open` pasa a `true`, no la renderiza en su
642
+ * propio lugar del DOM. Esto es lo que permite que el panel aparezca pegado
643
+ * al host de `Dropdown` sin importar dónde esté ese host en la página.
644
+ *
645
+ * <kf-dropdown [(open)]="menuOpen">
646
+ * <kf-dropdown-panel>
647
+ * <kf-dropdown-item (click)="onLogout()">Cerrar sesión</kf-dropdown-item>
648
+ * </kf-dropdown-panel>
649
+ * </kf-dropdown>
650
+ */
651
+ declare class DropdownPanel {
652
+ readonly templateRef: _angular_core.Signal<TemplateRef<any>>;
653
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<DropdownPanel, never>;
654
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<DropdownPanel, "kf-dropdown-panel", never, {}, {}, never, ["*"], true, never>;
655
+ }
656
+ /**
657
+ * Menú de acciones flotante, conectado a un elemento disparador vía
658
+ * `@angular/cdk/overlay` — la misma pieza que usa Angular Material para
659
+ * `mat-menu`/`mat-select`. Resuelve el patrón que antes se reimplementaba a
660
+ * mano en cada consumidor (ver el session-menu de `web-portal-latam-frontend`,
661
+ * ~80 líneas de HTML/TS/SCSS para toggle + click-afuera + Escape + animación
662
+ * de entrada, acopladas a una clase interna no pública de `PortalHeader`).
663
+ *
664
+ * Se apoya en el CDK en vez de un `position: absolute` casero (como sí hace
665
+ * `SelectMenu`, que puede permitírselo porque su trigger vive siempre dentro
666
+ * del propio componente) porque acá el disparador real puede vivir en OTRO
667
+ * componente por completo — ej. el avatar dentro de `kf-portal-header` — y
668
+ * el panel tiene que aparecer pegado a ESE elemento sin importar dónde el
669
+ * consumidor ubique al `Dropdown` en su HTML. El Overlay CDK calcula esa
670
+ * posición (con fallback si no entra en pantalla) en vez de que cada
671
+ * consumidor lo recalcule a mano.
672
+ *
673
+ * `open` es two-way, igual que `Alert`/`Modal`: el consumidor decide qué
674
+ * dispara la apertura (puede ser un `(click)` en un botón propio, o un
675
+ * evento de otro componente como `PortalHeader.sessionClick`) — `Dropdown`
676
+ * en sí no renderiza ningún trigger, su propio host es el punto de anclaje
677
+ * visual del panel.
678
+ *
679
+ * <kf-dropdown [(open)]="menuOpen">
680
+ * <kf-dropdown-panel>
681
+ * <div class="mi-header">Sesión iniciada como {{ email() }}</div>
682
+ * <kf-dropdown-item (click)="onLogout()" tone="error">Cerrar sesión</kf-dropdown-item>
683
+ * </kf-dropdown-panel>
684
+ * </kf-dropdown>
685
+ */
686
+ declare class Dropdown {
687
+ /** Visibilidad del panel, two-way: `[(open)]="menuOpen"`. */
688
+ readonly open: _angular_core.ModelSignal<boolean>;
689
+ /**
690
+ * Elemento al que se conecta el overlay, si es distinto del propio host
691
+ * de `Dropdown`. Sin esto, `Dropdown` asume que ÉL MISMO es visualmente
692
+ * el disparador (caso normal: el consumidor arma su propio botón como
693
+ * contenido, y `Dropdown` envuelve ese contenido). Cuando el disparador
694
+ * real vive dentro de OTRO componente que no se puede envolver sin
695
+ * ensanchar el área de click a algo más grande que el botón (ej. el
696
+ * avatar de sesión dentro de `PortalHeader`, que convive con logo y
697
+ * navegación en el mismo host), `anchor` permite apuntar el overlay a la
698
+ * referencia real de ese componente en vez de al host vacío de
699
+ * `Dropdown`.
700
+ *
701
+ * <kf-dropdown [(open)]="menuOpen" [anchor]="headerRef()">
702
+ */
703
+ readonly anchor: _angular_core.InputSignal<ElementRef<HTMLElement> | undefined>;
704
+ private readonly panel;
705
+ private readonly overlayService;
706
+ private readonly host;
707
+ private readonly viewContainerRef;
708
+ private readonly destroyRef;
709
+ private overlayRef;
710
+ constructor();
711
+ /**
712
+ * Respaldo de `overlayRef.keydownEvents()`: ese observable del CDK solo
713
+ * ve teclas presionadas con el foco DENTRO del pane del overlay. Si el
714
+ * usuario abre el menú y presiona Escape con el foco en otro lado de la
715
+ * página (caso común — el foco puede seguir en el botón disparador, que
716
+ * vive fuera del overlay), `keydownEvents()` nunca dispara. Este listener
717
+ * a nivel de documento cubre ese caso; el de `keydownEvents()` sigue
718
+ * activo para cuando el foco sí está dentro del panel.
719
+ */
720
+ onDocumentEscape(): void;
721
+ private attach;
722
+ private detach;
723
+ private getOrCreateOverlayRef;
724
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<Dropdown, never>;
725
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<Dropdown, "kf-dropdown", never, { "open": { "alias": "open"; "required": false; "isSignal": true; }; "anchor": { "alias": "anchor"; "required": false; "isSignal": true; }; }, { "open": "openChange"; }, ["panel"], ["*"], true, never>;
726
+ }
727
+
586
728
  /**
587
729
  * Placeholder para una sección sin contenido. Dos tonos, con anatomía
588
730
  * distinta (páginas 27-28 del design system, que los trata como piezas
@@ -855,6 +997,36 @@ declare class Input implements ControlValueAccessor, AfterViewInit {
855
997
  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; }; "type": { "alias": "type"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "toggleable": { "alias": "toggleable"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
856
998
  }
857
999
 
1000
+ type LinkVariant = 'default' | 'muted';
1001
+ /**
1002
+ * Acción de bajo énfasis con apariencia de texto — "¿Olvidaste tu
1003
+ * contraseña?", "Volver a iniciar sesión". Antes de este componente,
1004
+ * `web-portal-latam-frontend` reimplementaba esto dos veces (`.login__forgot`
1005
+ * como `<a href="#">` con `preventDefault()`, `.login__back` como
1006
+ * `<button>`), cada una con su propio `:focus-visible` copiado literal.
1007
+ *
1008
+ * Es siempre un `<button type="button">` por dentro, nunca un `<a>`: ninguno
1009
+ * de los dos casos reales navega a otra URL, ambos disparan una acción en la
1010
+ * misma pantalla (cambiar de vista, someter un flujo). Si en el futuro un
1011
+ * consumidor necesita navegación real con href, esa es una discusión de API
1012
+ * distinta (`kf-link` con `[routerLink]`/`href`), no se fuerza acá sin caso
1013
+ * de uso confirmado.
1014
+ *
1015
+ * `variant="default"` (color primario, subrayado en hover) es para la
1016
+ * acción de mayor jerarquía relativa de la pantalla (ej. "¿Olvidaste tu
1017
+ * contraseña?"); `variant="muted"` (gris que pasa a primario en hover) es
1018
+ * para acciones de retroceso/salida (ej. "← Volver a iniciar sesión").
1019
+ *
1020
+ * <kf-link (click)="onForgotPassword()">¿Olvidaste tu contraseña?</kf-link>
1021
+ * <kf-link variant="muted" (click)="onBack()">← Volver a iniciar sesión</kf-link>
1022
+ */
1023
+ declare class Link {
1024
+ readonly variant: _angular_core.InputSignal<LinkVariant>;
1025
+ readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
1026
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<Link, never>;
1027
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<Link, "kf-link", never, { "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
1028
+ }
1029
+
858
1030
  /**
859
1031
  * Card de estadística/KPI: label, valor grande, ícono opcional en la esquina
860
1032
  * superior derecha, badge de esquina opcional (pegado al valor) y una nota
@@ -1139,10 +1311,17 @@ declare class ReserveBlock {
1139
1311
  * `<ng-content select="[actions]">` para no acoplar el componente a un set
1140
1312
  * fijo de botones.
1141
1313
  *
1142
- * `size="sm"` reduce tipografía y espaciado para usarse dentro de cards ya
1143
- * compactas. `solid` rellena el chip de ícono con el color primario sólido
1144
- * en vez del tratamiento "soft" (fondo tenue) — pensado para superficies que
1145
- * ya tienen un tinte de color propio, donde el chip "soft" perdería contraste.
1314
+ * `size` sigue la misma escala `'sm'|'md'|'lg'` que `Button`/`Icon`/`Spinner`
1315
+ * (`md` es el default): `sm` reduce tipografía y espaciado para usarse
1316
+ * dentro de cards ya compactas, `lg` los agranda para encabezar un bloque
1317
+ * más prominente. Antes de la `0.2.0`, `size` solo aceptaba `'sm'` —
1318
+ * placeholder incompleto de esta misma escala; los valores de `lg` son una
1319
+ * extrapolación proporcional de la escala existente (sin un caso de uso
1320
+ * real todavía que los valide, a diferencia de `sm`, que sí viene de un
1321
+ * patrón real de uso dentro de cards). `solid` rellena el chip de ícono con
1322
+ * el color primario sólido en vez del tratamiento "soft" (fondo tenue) —
1323
+ * pensado para superficies que ya tienen un tinte de color propio, donde el
1324
+ * chip "soft" perdería contraste.
1146
1325
  *
1147
1326
  * <kf-section-header icon="settings" title="Preferencias"
1148
1327
  * subtitle="Configuración general de la cuenta">
@@ -1150,13 +1329,15 @@ declare class ReserveBlock {
1150
1329
  * </kf-section-header>
1151
1330
  *
1152
1331
  * <kf-section-header title="Detalle" size="sm" [solid]="true" />
1332
+ * <kf-section-header title="Resumen de flota" size="lg" />
1153
1333
  */
1154
1334
  declare class SectionHeader {
1155
1335
  readonly icon: _angular_core.InputSignal<IconName | undefined>;
1156
1336
  readonly title: _angular_core.InputSignal<string>;
1157
1337
  readonly subtitle: _angular_core.InputSignal<string | undefined>;
1158
- readonly size: _angular_core.InputSignal<"sm" | undefined>;
1338
+ readonly size: _angular_core.InputSignal<"sm" | "md" | "lg">;
1159
1339
  readonly solid: _angular_core.InputSignalWithTransform<boolean, unknown>;
1340
+ protected readonly iconSize: _angular_core.Signal<"sm" | "md" | "lg">;
1160
1341
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<SectionHeader, never>;
1161
1342
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<SectionHeader, "kf-section-header", never, { "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": true; "isSignal": true; }; "subtitle": { "alias": "subtitle"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "solid": { "alias": "solid"; "required": false; "isSignal": true; }; }, {}, never, ["[actions]"], true, never>;
1162
1343
  }
@@ -1195,18 +1376,25 @@ interface SelectOption {
1195
1376
  * Dropdown de selección simple con panel propio — NUNCA usa el `<select>`
1196
1377
  * nativo del navegador. Un `<select>` real siempre delega el popup de
1197
1378
  * opciones al sistema operativo, y ningún CSS puede controlar cómo se ve
1198
- * ese popup (ver el mismo problema ya resuelto en `SelectMenu`, del que
1199
- * este componente reutiliza el patrón completo: trigger + panel flotante
1200
- * propio, cerrado con click-afuera y Escape).
1379
+ * ese popup.
1380
+ *
1381
+ * Hasta la `0.1.5` existía además `kf-select-menu`, un segundo componente
1382
+ * prácticamente idéntico (mismo trigger, mismo panel, mismo CSS) que solo
1383
+ * se diferenciaba en aceptar `options` únicamente como `{ value, label }[]`
1384
+ * (sin el atajo `string[]` que `Select` ya tenía). Se fusionaron en este
1385
+ * componente en la `0.2.0` — mantener dos componentes con la misma
1386
+ * interacción y el mismo aspecto visual, cuya única diferencia real era la
1387
+ * forma de un input, no se sostenía como dos piezas separadas del catálogo.
1201
1388
  *
1202
- * Pensado para sets simples y cortos. Para listas más ricas o con más
1203
- * opciones, es el mismo componente que `SelectMenu` — la diferencia es
1204
- * solo de estilo (label uppercase, fondo gris que pasa a blanco al foco,
1205
- * a tono con `Input`) y que acepta `options` como `string[]` además de
1206
- * `{ value, label }[]`.
1389
+ * El panel flotante se conecta al trigger vía `@angular/cdk/overlay` — el
1390
+ * mismo mecanismo que `Dropdown` (ver `../dropdown/Dropdown.ts`).
1207
1391
  *
1208
1392
  * <kf-select label="Sede" [options]="['Lima', 'Arequipa', 'Trujillo']" placeholder="Selecciona…" />
1209
1393
  * <kf-select [options]="[{ value: 'pe', label: 'Perú' }]" (valueChange)="onCountry($event)" />
1394
+ *
1395
+ * Pendiente: el prop `icon` de otros componentes del design system requiere
1396
+ * un componente Icon / sprite SVG que esta librería todavía no tiene — este
1397
+ * componente no usa iconos, así que no aplica acá.
1210
1398
  */
1211
1399
  declare class Select {
1212
1400
  readonly label: _angular_core.InputSignal<string | undefined>;
@@ -1222,60 +1410,29 @@ declare class Select {
1222
1410
  /** Resolves the union (`string[] | { value; label }[]`) into a single shape for the template. */
1223
1411
  readonly normalizedOptions: _angular_core.Signal<SelectOption[]>;
1224
1412
  readonly selectedOption: _angular_core.Signal<SelectOption | undefined>;
1225
- private readonly host;
1413
+ private readonly triggerRef;
1414
+ private readonly panelTemplate;
1415
+ private readonly overlayService;
1416
+ private readonly viewContainerRef;
1417
+ private readonly destroyRef;
1418
+ private overlayRef;
1419
+ constructor();
1420
+ /**
1421
+ * Respaldo de `overlayRef.keydownEvents()`: ese observable del CDK solo ve
1422
+ * teclas presionadas con el foco DENTRO del pane del overlay. Si el foco
1423
+ * sigue en el trigger (o en cualquier otro lado de la página) mientras el
1424
+ * panel está abierto, `keydownEvents()` nunca dispara.
1425
+ */
1426
+ onDocumentEscape(): void;
1226
1427
  toggle(): void;
1227
1428
  select(option: SelectOption): void;
1228
- onDocumentClick(event: MouseEvent): void;
1229
- onEscape(): void;
1429
+ private attach;
1430
+ private detach;
1431
+ private getOrCreateOverlayRef;
1230
1432
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<Select, never>;
1231
1433
  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; }; }, { "valueChange": "valueChange"; }, never, never, true, never>;
1232
1434
  }
1233
1435
 
1234
- interface SelectMenuOption {
1235
- value: string;
1236
- label: string;
1237
- }
1238
- /**
1239
- * Dropdown de selección simple pero con panel propio (no usa `<select>` nativo),
1240
- * para cuando se necesita más control visual que un `<select>` estándar permite.
1241
- *
1242
- * El panel flotante se implementa con `position: absolute` sobre el host
1243
- * (sin Angular CDK Overlay, que no es dependencia del proyecto). Se abre y
1244
- * cierra por click en el disparador, y se cierra al hacer click afuera o al
1245
- * presionar Escape.
1246
- *
1247
- * <kf-select-menu
1248
- * label="País"
1249
- * [options]="countries"
1250
- * [value]="selectedCountry()"
1251
- * (valueChange)="selectedCountry.set($event)"
1252
- * />
1253
- *
1254
- * Pendiente: el prop `icon` de otros componentes del design system requiere
1255
- * un componente Icon / sprite SVG que esta librería todavía no tiene — este
1256
- * componente no usa iconos, así que no aplica acá.
1257
- */
1258
- declare class SelectMenu {
1259
- readonly label: _angular_core.InputSignal<string | undefined>;
1260
- readonly options: _angular_core.InputSignal<SelectMenuOption[]>;
1261
- readonly value: _angular_core.InputSignal<string | undefined>;
1262
- readonly placeholder: _angular_core.InputSignal<string>;
1263
- readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
1264
- readonly valueChange: _angular_core.OutputEmitterRef<string>;
1265
- private readonly instanceId;
1266
- readonly triggerId: string;
1267
- readonly listboxId: string;
1268
- readonly isOpen: _angular_core.WritableSignal<boolean>;
1269
- readonly selectedOption: _angular_core.Signal<SelectMenuOption | undefined>;
1270
- private readonly host;
1271
- toggle(): void;
1272
- select(option: SelectMenuOption): void;
1273
- onDocumentClick(event: MouseEvent): void;
1274
- onEscape(): void;
1275
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<SelectMenu, never>;
1276
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<SelectMenu, "kf-select-menu", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; }, { "valueChange": "valueChange"; }, never, never, true, never>;
1277
- }
1278
-
1279
1436
  /** Forma del placeholder. */
1280
1437
  type SkeletonVariant = 'text' | 'circle' | 'rect';
1281
1438
  /**
@@ -1365,7 +1522,7 @@ declare class StatTile {
1365
1522
  * <kf-status-pill tone="error" dot>Fuera de servicio</kf-status-pill>
1366
1523
  */
1367
1524
  declare class StatusPill {
1368
- readonly tone: _angular_core.InputSignal<"ok" | "warn" | "error" | "blue">;
1525
+ readonly tone: _angular_core.InputSignal<"error" | "ok" | "warn" | "blue">;
1369
1526
  readonly dot: _angular_core.InputSignalWithTransform<boolean, unknown>;
1370
1527
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<StatusPill, never>;
1371
1528
  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>;
@@ -1404,6 +1561,44 @@ declare class Tabs {
1404
1561
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<Tabs, "kf-tabs", never, { "options": { "alias": "options"; "required": true; "isSignal": true; }; "value": { "alias": "value"; "required": true; "isSignal": true; }; }, { "valueChange": "valueChange"; }, never, never, true, never>;
1405
1562
  }
1406
1563
 
1564
+ type TextAs = 'h1' | 'h2' | 'h3' | 'p' | 'span';
1565
+ type TextVariant = 'title-lg' | 'title-md' | 'title-sm' | 'body' | 'body-muted' | 'caption';
1566
+ type TextTone = 'neutral' | 'ok' | 'warn' | 'error' | 'info';
1567
+ /**
1568
+ * Tipografía unificada — separa la etiqueta semántica (`as`, qué es el
1569
+ * texto para el DOM/accesibilidad) del estilo visual (`variant`, cómo se
1570
+ * ve). Antes de este componente, cada pantalla que consume la librería
1571
+ * definía su propio `h1`/`p` con `font-size` suelto para el mismo rol
1572
+ * (título de página, subtítulo, texto secundario) — auditoría sobre
1573
+ * `web-portal-latam-frontend` encontró 9 tamaños distintos entre login y
1574
+ * home sin ningún paso de escala en común. `variant` resuelve eso contra
1575
+ * la escala de `tokens/base.css` (`--kf-font-size-*`).
1576
+ *
1577
+ * `tone` sigue la misma convención cerrada que `Alert`/`Badge`/`Chip`: sin
1578
+ * escape hatch de color libre. Si un caso necesita un color que `tone` no
1579
+ * cubre, es una discusión de design system, no una prop nueva acá.
1580
+ *
1581
+ * <kf-text as="h1" variant="title-lg">Bienvenido de nuevo</kf-text>
1582
+ * <kf-text as="p" variant="body-muted">Ingresá con tu cuenta corporativa.</kf-text>
1583
+ * <kf-text as="span" variant="caption" tone="error">Campo obligatorio</kf-text>
1584
+ */
1585
+ declare class Text {
1586
+ /** Etiqueta semántica real que se renderiza — nunca es solo un `div`. */
1587
+ readonly as: _angular_core.InputSignal<TextAs>;
1588
+ readonly variant: _angular_core.InputSignal<TextVariant>;
1589
+ readonly tone: _angular_core.InputSignal<TextTone>;
1590
+ /**
1591
+ * Copia de `as` para uso dentro del template: `as` es palabra reservada
1592
+ * en las expresiones de Angular (`@if (x; as y)`), así que `@switch (as())`
1593
+ * no parsea — NG5002. La prop pública sigue llamándose `as` (es solo un
1594
+ * atributo HTML ahí, `<kf-text as="h1">`, no una expresión).
1595
+ */
1596
+ protected readonly tag: _angular_core.Signal<TextAs>;
1597
+ protected hostClass(): string;
1598
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<Text, never>;
1599
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<Text, "kf-text", never, { "as": { "alias": "as"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "tone": { "alias": "tone"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
1600
+ }
1601
+
1407
1602
  /**
1408
1603
  * Visual tone of a toast. Maps to the state tokens in
1409
1604
  * projects/ui/src/tokens/base.css. `loading` has no auto-dismiss and shows an
@@ -1559,5 +1754,5 @@ declare class ViewToggle {
1559
1754
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<ViewToggle, "kf-view-toggle", never, { "view": { "alias": "view"; "required": true; "isSignal": true; }; }, { "viewChange": "viewChange"; }, never, never, true, never>;
1560
1755
  }
1561
1756
 
1562
- export { ActionTile, AdminBadge, Alert, Avatar, Badge, Breadcrumb, Button, Card, Carousel, CarouselSlide, Checkbox, Chip, CompanySelector, ContentSlot, ContentState, DataTable, EmptyState, FeatureCard, Field, FieldLabel, FilterChips, FolderTabs, Icon, Input, MetricCard, Modal, MultiSelect, PageHeader, Pagination, Panel, PortalHeader, ReserveBlock, SectionHeader, Segmented, Select, SelectMenu, Skeleton, Spinner, StatTile, StatusPill, Tabs, ToastHost, ToastService, VehicleHeader, ViewToggle };
1563
- export type { AlertTone, BreadcrumbItem, CompanySelectorMeta, ContentStateKind, ContentStateLoad, DataTableCardRole, DataTableColumn, DataTableView, FolderTabOption, IconName, IconSize, MultiSelectOption, PaginationItem, PortalHeaderAdminAction, PortalHeaderNavItem, SegmentedOption, SelectMenuOption, SkeletonVariant, SpinnerSize, TabOption, ToastOptions, ToastPromiseMessages, ToastState, ToastTone, VehicleHeaderStatus, ViewToggleOption };
1757
+ export { ActionTile, AdminBadge, Alert, Avatar, Badge, Breadcrumb, Button, Card, Carousel, CarouselSlide, Checkbox, Chip, CompanySelector, ContentSlot, ContentState, DataTable, Divider, Dropdown, DropdownItem, DropdownPanel, EmptyState, FeatureCard, Field, FieldLabel, FilterChips, FolderTabs, Icon, Input, Link, MetricCard, Modal, MultiSelect, PageHeader, Pagination, Panel, PortalHeader, ReserveBlock, SectionHeader, Segmented, Select, Skeleton, Spinner, StatTile, StatusPill, Tabs, Text, ToastHost, ToastService, VehicleHeader, ViewToggle };
1758
+ export type { AlertTone, BreadcrumbItem, CompanySelectorMeta, ContentStateKind, ContentStateLoad, DataTableCardRole, DataTableColumn, DataTableView, DividerOrientation, DropdownItemTone, FolderTabOption, IconName, IconSize, LinkVariant, MultiSelectOption, PaginationItem, PortalHeaderAdminAction, PortalHeaderNavItem, SegmentedOption, SkeletonVariant, SpinnerSize, TabOption, TextAs, TextTone, TextVariant, ToastOptions, ToastPromiseMessages, ToastState, ToastTone, VehicleHeaderStatus, ViewToggleOption };