@kaufmann-lab/ui 0.1.3 → 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.3",
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
  /**
@@ -95,10 +95,16 @@ declare class AdminBadge {
95
95
  *
96
96
  * `(click)` is not forwarded — it bubbles from the inner button to the host.
97
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
+ *
98
103
  * <kf-button (click)="save()">Save</kf-button>
99
104
  * <kf-button variant="ghost" disabled>Cancel</kf-button>
100
105
  * <kf-button variant="danger" (click)="remove()">Delete</kf-button>
101
106
  * <kf-button size="sm" icon="calendar" block>Reservar hora</kf-button>
107
+ * <kf-button type="submit" [loading]="submitting()">Ingresar</kf-button>
102
108
  */
103
109
  declare class Button {
104
110
  readonly variant: _angular_core.InputSignal<"primary" | "secondary" | "ghost" | "outline" | "danger">;
@@ -110,8 +116,12 @@ declare class Button {
110
116
  readonly icon: _angular_core.InputSignal<IconName | undefined>;
111
117
  /** Stretches the button to the full width of its container. */
112
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>;
113
123
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<Button, never>;
114
- 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>;
115
125
  }
116
126
 
117
127
  type AlertTone = 'info' | 'ok' | 'warn' | 'error';
@@ -196,7 +206,7 @@ declare class Avatar {
196
206
  * no existe un `-strong` para secondary todavía — usa el color base).
197
207
  */
198
208
  declare class Badge {
199
- readonly tone: _angular_core.InputSignal<"info" | "ok" | "warn" | "error" | "neutral" | "service">;
209
+ readonly tone: _angular_core.InputSignal<"error" | "info" | "ok" | "warn" | "neutral" | "service">;
200
210
  readonly dot: _angular_core.InputSignalWithTransform<boolean, unknown>;
201
211
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<Badge, never>;
202
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>;
@@ -383,7 +393,7 @@ declare class Checkbox implements ControlValueAccessor {
383
393
  * de superficie/borde/texto-muted más cercanos).
384
394
  */
385
395
  declare class Chip {
386
- readonly tone: _angular_core.InputSignal<"ok" | "warn" | "error" | "neutral">;
396
+ readonly tone: _angular_core.InputSignal<"error" | "ok" | "warn" | "neutral">;
387
397
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<Chip, never>;
388
398
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<Chip, "kf-chip", never, { "tone": { "alias": "tone"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
389
399
  }
@@ -591,10 +601,16 @@ declare class DataTable<T = unknown> {
591
601
 
592
602
  type DividerOrientation = 'horizontal' | 'vertical';
593
603
  /**
594
- * Línea separadora decorativa entre bloques de contenido (ej. columnas de
595
- * metadata en `kf-company-selector`). `vertical` necesita que el contenedor
596
- * padre sea flex/grid con altura definida — se estira con `align-self:
597
- * stretch`; sin eso no tiene alto propio y no se ve.
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.
598
614
  *
599
615
  * <kf-divider />
600
616
  * <kf-divider orientation="vertical" />
@@ -605,6 +621,110 @@ declare class Divider {
605
621
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<Divider, "kf-divider", never, { "orientation": { "alias": "orientation"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
606
622
  }
607
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
+
608
728
  /**
609
729
  * Placeholder para una sección sin contenido. Dos tonos, con anatomía
610
730
  * distinta (páginas 27-28 del design system, que los trata como piezas
@@ -877,6 +997,36 @@ declare class Input implements ControlValueAccessor, AfterViewInit {
877
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>;
878
998
  }
879
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
+
880
1030
  /**
881
1031
  * Card de estadística/KPI: label, valor grande, ícono opcional en la esquina
882
1032
  * superior derecha, badge de esquina opcional (pegado al valor) y una nota
@@ -1161,10 +1311,17 @@ declare class ReserveBlock {
1161
1311
  * `<ng-content select="[actions]">` para no acoplar el componente a un set
1162
1312
  * fijo de botones.
1163
1313
  *
1164
- * `size="sm"` reduce tipografía y espaciado para usarse dentro de cards ya
1165
- * compactas. `solid` rellena el chip de ícono con el color primario sólido
1166
- * en vez del tratamiento "soft" (fondo tenue) — pensado para superficies que
1167
- * 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.
1168
1325
  *
1169
1326
  * <kf-section-header icon="settings" title="Preferencias"
1170
1327
  * subtitle="Configuración general de la cuenta">
@@ -1172,13 +1329,15 @@ declare class ReserveBlock {
1172
1329
  * </kf-section-header>
1173
1330
  *
1174
1331
  * <kf-section-header title="Detalle" size="sm" [solid]="true" />
1332
+ * <kf-section-header title="Resumen de flota" size="lg" />
1175
1333
  */
1176
1334
  declare class SectionHeader {
1177
1335
  readonly icon: _angular_core.InputSignal<IconName | undefined>;
1178
1336
  readonly title: _angular_core.InputSignal<string>;
1179
1337
  readonly subtitle: _angular_core.InputSignal<string | undefined>;
1180
- readonly size: _angular_core.InputSignal<"sm" | undefined>;
1338
+ readonly size: _angular_core.InputSignal<"sm" | "md" | "lg">;
1181
1339
  readonly solid: _angular_core.InputSignalWithTransform<boolean, unknown>;
1340
+ protected readonly iconSize: _angular_core.Signal<"sm" | "md" | "lg">;
1182
1341
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<SectionHeader, never>;
1183
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>;
1184
1343
  }
@@ -1217,18 +1376,25 @@ interface SelectOption {
1217
1376
  * Dropdown de selección simple con panel propio — NUNCA usa el `<select>`
1218
1377
  * nativo del navegador. Un `<select>` real siempre delega el popup de
1219
1378
  * opciones al sistema operativo, y ningún CSS puede controlar cómo se ve
1220
- * ese popup (ver el mismo problema ya resuelto en `SelectMenu`, del que
1221
- * este componente reutiliza el patrón completo: trigger + panel flotante
1222
- * 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.
1223
1388
  *
1224
- * Pensado para sets simples y cortos. Para listas más ricas o con más
1225
- * opciones, es el mismo componente que `SelectMenu` — la diferencia es
1226
- * solo de estilo (label uppercase, fondo gris que pasa a blanco al foco,
1227
- * a tono con `Input`) y que acepta `options` como `string[]` además de
1228
- * `{ 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`).
1229
1391
  *
1230
1392
  * <kf-select label="Sede" [options]="['Lima', 'Arequipa', 'Trujillo']" placeholder="Selecciona…" />
1231
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á.
1232
1398
  */
1233
1399
  declare class Select {
1234
1400
  readonly label: _angular_core.InputSignal<string | undefined>;
@@ -1244,60 +1410,29 @@ declare class Select {
1244
1410
  /** Resolves the union (`string[] | { value; label }[]`) into a single shape for the template. */
1245
1411
  readonly normalizedOptions: _angular_core.Signal<SelectOption[]>;
1246
1412
  readonly selectedOption: _angular_core.Signal<SelectOption | undefined>;
1247
- 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;
1248
1427
  toggle(): void;
1249
1428
  select(option: SelectOption): void;
1250
- onDocumentClick(event: MouseEvent): void;
1251
- onEscape(): void;
1429
+ private attach;
1430
+ private detach;
1431
+ private getOrCreateOverlayRef;
1252
1432
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<Select, never>;
1253
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>;
1254
1434
  }
1255
1435
 
1256
- interface SelectMenuOption {
1257
- value: string;
1258
- label: string;
1259
- }
1260
- /**
1261
- * Dropdown de selección simple pero con panel propio (no usa `<select>` nativo),
1262
- * para cuando se necesita más control visual que un `<select>` estándar permite.
1263
- *
1264
- * El panel flotante se implementa con `position: absolute` sobre el host
1265
- * (sin Angular CDK Overlay, que no es dependencia del proyecto). Se abre y
1266
- * cierra por click en el disparador, y se cierra al hacer click afuera o al
1267
- * presionar Escape.
1268
- *
1269
- * <kf-select-menu
1270
- * label="País"
1271
- * [options]="countries"
1272
- * [value]="selectedCountry()"
1273
- * (valueChange)="selectedCountry.set($event)"
1274
- * />
1275
- *
1276
- * Pendiente: el prop `icon` de otros componentes del design system requiere
1277
- * un componente Icon / sprite SVG que esta librería todavía no tiene — este
1278
- * componente no usa iconos, así que no aplica acá.
1279
- */
1280
- declare class SelectMenu {
1281
- readonly label: _angular_core.InputSignal<string | undefined>;
1282
- readonly options: _angular_core.InputSignal<SelectMenuOption[]>;
1283
- readonly value: _angular_core.InputSignal<string | undefined>;
1284
- readonly placeholder: _angular_core.InputSignal<string>;
1285
- readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
1286
- readonly valueChange: _angular_core.OutputEmitterRef<string>;
1287
- private readonly instanceId;
1288
- readonly triggerId: string;
1289
- readonly listboxId: string;
1290
- readonly isOpen: _angular_core.WritableSignal<boolean>;
1291
- readonly selectedOption: _angular_core.Signal<SelectMenuOption | undefined>;
1292
- private readonly host;
1293
- toggle(): void;
1294
- select(option: SelectMenuOption): void;
1295
- onDocumentClick(event: MouseEvent): void;
1296
- onEscape(): void;
1297
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<SelectMenu, never>;
1298
- 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>;
1299
- }
1300
-
1301
1436
  /** Forma del placeholder. */
1302
1437
  type SkeletonVariant = 'text' | 'circle' | 'rect';
1303
1438
  /**
@@ -1387,7 +1522,7 @@ declare class StatTile {
1387
1522
  * <kf-status-pill tone="error" dot>Fuera de servicio</kf-status-pill>
1388
1523
  */
1389
1524
  declare class StatusPill {
1390
- readonly tone: _angular_core.InputSignal<"ok" | "warn" | "error" | "blue">;
1525
+ readonly tone: _angular_core.InputSignal<"error" | "ok" | "warn" | "blue">;
1391
1526
  readonly dot: _angular_core.InputSignalWithTransform<boolean, unknown>;
1392
1527
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<StatusPill, never>;
1393
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>;
@@ -1426,6 +1561,44 @@ declare class Tabs {
1426
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>;
1427
1562
  }
1428
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
+
1429
1602
  /**
1430
1603
  * Visual tone of a toast. Maps to the state tokens in
1431
1604
  * projects/ui/src/tokens/base.css. `loading` has no auto-dismiss and shows an
@@ -1581,5 +1754,5 @@ declare class ViewToggle {
1581
1754
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<ViewToggle, "kf-view-toggle", never, { "view": { "alias": "view"; "required": true; "isSignal": true; }; }, { "viewChange": "viewChange"; }, never, never, true, never>;
1582
1755
  }
1583
1756
 
1584
- export { ActionTile, AdminBadge, Alert, Avatar, Badge, Breadcrumb, Button, Card, Carousel, CarouselSlide, Checkbox, Chip, CompanySelector, ContentSlot, ContentState, DataTable, Divider, 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 };
1585
- export type { AlertTone, BreadcrumbItem, CompanySelectorMeta, ContentStateKind, ContentStateLoad, DataTableCardRole, DataTableColumn, DataTableView, DividerOrientation, 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 };