@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.
@@ -1,7 +1,9 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, inject, computed, effect, Component, booleanAttribute, model, output, numberAttribute, contentChildren, signal, DestroyRef, viewChild, forwardRef, ElementRef, HostListener, ChangeDetectionStrategy, afterRenderEffect, afterNextRender, Injectable } from '@angular/core';
2
+ import { input, inject, computed, effect, Component, booleanAttribute, model, output, numberAttribute, contentChildren, signal, DestroyRef, viewChild, forwardRef, ViewContainerRef, HostListener, TemplateRef, contentChild, ElementRef, ChangeDetectionStrategy, afterRenderEffect, afterNextRender, Injectable } from '@angular/core';
3
3
  import { DOCUMENT, NgTemplateOutlet } from '@angular/common';
4
4
  import { NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';
5
+ import { Overlay } from '@angular/cdk/overlay';
6
+ import { TemplatePortal } from '@angular/cdk/portal';
5
7
 
6
8
  /** Símbolo de trazo "activity" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
7
9
  const activity = {
@@ -849,6 +851,55 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
849
851
  }, styles: [":host{display:inline-flex;align-items:center;gap:var(--kf-space-xs);padding:var(--kf-space-xs) var(--kf-space-sm);border-radius:var(--kf-radius-pill);background:var(--kf-color-warning-soft);border:1px solid var(--kf-color-warning);color:var(--kf-color-warning-strong);font-size:.75rem;font-weight:700;letter-spacing:.02em;text-transform:uppercase;line-height:1}:host(.kf-admin-badge--inline){background:transparent;border-color:#e2b93b80;color:var(--kf-color-warning)}\n"] }]
850
852
  }], propDecorators: { inline: [{ type: i0.Input, args: [{ isSignal: true, alias: "inline", required: false }] }] } });
851
853
 
854
+ /**
855
+ * Indicador de carga indeterminado: inline, dentro de un botón, o como único
856
+ * contenido de una sección mientras llega la data.
857
+ *
858
+ * Cuando se conoce la forma del contenido que viene, preferir `kf-skeleton`
859
+ * (dibuja la silueta). El spinner es para "algo está pasando" sin forma
860
+ * conocida — guardar, enviar, reintentar.
861
+ *
862
+ * Lleva `role="status"`, así que el lector de pantalla anuncia el `label`
863
+ * cuando aparece. Sin `label` cae en un texto accesible por defecto.
864
+ *
865
+ * <kf-spinner />
866
+ * <kf-spinner size="sm" label="Cargando…" />
867
+ * <kf-spinner onDark />
868
+ */
869
+ class Spinner {
870
+ size = input('md', /* @ts-ignore */
871
+ ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
872
+ /** Texto al lado del anillo. Vacío deja solo el anillo (y el texto para lectores). */
873
+ label = input('', /* @ts-ignore */
874
+ ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
875
+ /** Invierte los colores para fondos oscuros. */
876
+ onDark = input(false, { ...(ngDevMode ? { debugName: "onDark" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
877
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Spinner, deps: [], target: i0.ɵɵFactoryTarget.Component });
878
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Spinner, isStandalone: true, selector: "kf-spinner", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, onDark: { classPropertyName: "onDark", publicName: "onDark", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
879
+ <span class="kf-spinner" [class.kf-spinner--on-dark]="onDark()" role="status">
880
+ <span class="kf-spinner__ring" [class]="'kf-spinner__ring--' + size()"></span>
881
+ @if (label()) {
882
+ <span class="kf-spinner__label">{{ label() }}</span>
883
+ } @else {
884
+ <span class="kf-spinner__sr">Cargando</span>
885
+ }
886
+ </span>
887
+ `, isInline: true, styles: [":host{display:inline-block}.kf-spinner{display:inline-flex;align-items:center;gap:var(--kf-space-sm);font-family:var(--kf-font-body)}.kf-spinner__ring{display:inline-block;flex:none;border-radius:var(--kf-radius-pill);border:2px solid var(--kf-color-border);border-top-color:var(--kf-color-primary);animation:kf-spinner-turn .7s linear infinite}.kf-spinner__ring--sm{width:16px;height:16px}.kf-spinner__ring--md{width:24px;height:24px;border-width:3px}.kf-spinner__ring--lg{width:32px;height:32px;border-width:3px}.kf-spinner__label{font-size:.875rem;color:var(--kf-color-text-muted)}.kf-spinner--on-dark .kf-spinner__ring{border-color:#ffffff47;border-top-color:var(--kf-color-white)}.kf-spinner--on-dark .kf-spinner__label{color:var(--kf-color-white)}@keyframes kf-spinner-turn{to{transform:rotate(360deg)}}.kf-spinner__sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}@media(prefers-reduced-motion:reduce){.kf-spinner__ring{animation-duration:2s}}\n"] });
888
+ }
889
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Spinner, decorators: [{
890
+ type: Component,
891
+ args: [{ selector: 'kf-spinner', template: `
892
+ <span class="kf-spinner" [class.kf-spinner--on-dark]="onDark()" role="status">
893
+ <span class="kf-spinner__ring" [class]="'kf-spinner__ring--' + size()"></span>
894
+ @if (label()) {
895
+ <span class="kf-spinner__label">{{ label() }}</span>
896
+ } @else {
897
+ <span class="kf-spinner__sr">Cargando</span>
898
+ }
899
+ </span>
900
+ `, styles: [":host{display:inline-block}.kf-spinner{display:inline-flex;align-items:center;gap:var(--kf-space-sm);font-family:var(--kf-font-body)}.kf-spinner__ring{display:inline-block;flex:none;border-radius:var(--kf-radius-pill);border:2px solid var(--kf-color-border);border-top-color:var(--kf-color-primary);animation:kf-spinner-turn .7s linear infinite}.kf-spinner__ring--sm{width:16px;height:16px}.kf-spinner__ring--md{width:24px;height:24px;border-width:3px}.kf-spinner__ring--lg{width:32px;height:32px;border-width:3px}.kf-spinner__label{font-size:.875rem;color:var(--kf-color-text-muted)}.kf-spinner--on-dark .kf-spinner__ring{border-color:#ffffff47;border-top-color:var(--kf-color-white)}.kf-spinner--on-dark .kf-spinner__label{color:var(--kf-color-white)}@keyframes kf-spinner-turn{to{transform:rotate(360deg)}}.kf-spinner__sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}@media(prefers-reduced-motion:reduce){.kf-spinner__ring{animation-duration:2s}}\n"] }]
901
+ }], propDecorators: { size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], onDark: [{ type: i0.Input, args: [{ isSignal: true, alias: "onDark", required: false }] }] } });
902
+
852
903
  /**
853
904
  * Wraps a native `<button>`, so keyboard handling, focus and form submission
854
905
  * keep working. The cost of the wrapper is forwarding `disabled` and `type` by
@@ -864,10 +915,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
864
915
  *
865
916
  * `(click)` is not forwarded — it bubbles from the inner button to the host.
866
917
  *
918
+ * `loading` replaces the projected content with `kf-spinner` and forces
919
+ * `disabled` — for the common "Ingresando…"/"Enviando…" submit pattern,
920
+ * which before this prop existed as plain text swaps with no visual
921
+ * indicator (see `web-portal-latam-frontend`'s login submit buttons).
922
+ *
867
923
  * <kf-button (click)="save()">Save</kf-button>
868
924
  * <kf-button variant="ghost" disabled>Cancel</kf-button>
869
925
  * <kf-button variant="danger" (click)="remove()">Delete</kf-button>
870
926
  * <kf-button size="sm" icon="calendar" block>Reservar hora</kf-button>
927
+ * <kf-button type="submit" [loading]="submitting()">Ingresar</kf-button>
871
928
  */
872
929
  class Button {
873
930
  variant = input('primary', /* @ts-ignore */
@@ -883,11 +940,19 @@ class Button {
883
940
  ...(ngDevMode ? [undefined, { debugName: "icon" }] : /* istanbul ignore next */ []));
884
941
  /** Stretches the button to the full width of its container. */
885
942
  block = input(false, { ...(ngDevMode ? { debugName: "block" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
943
+ /** Replaces the projected content with `kf-spinner` and forces `disabled`. */
944
+ loading = input(false, { ...(ngDevMode ? { debugName: "loading" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
945
+ /** `ghost`/`outline` have no solid background — the default spinner ring already reads fine there. */
946
+ hasSolidFill = computed(() => {
947
+ const variant = this.variant();
948
+ return variant !== 'ghost' && variant !== 'outline';
949
+ }, /* @ts-ignore */
950
+ ...(ngDevMode ? [{ debugName: "hasSolidFill" }] : /* istanbul ignore next */ []));
886
951
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Button, deps: [], target: i0.ɵɵFactoryTarget.Component });
887
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Button, isStandalone: true, selector: "kf-button", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, block: { classPropertyName: "block", publicName: "block", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.block": "block() ? '' : null" } }, ngImport: i0, template: `
952
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Button, isStandalone: true, selector: "kf-button", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, block: { classPropertyName: "block", publicName: "block", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.block": "block() ? '' : null" } }, ngImport: i0, template: `
888
953
  <button
889
954
  [type]="type()"
890
- [disabled]="disabled()"
955
+ [disabled]="disabled() || loading()"
891
956
  [class.kf-button--secondary]="variant() === 'secondary'"
892
957
  [class.kf-button--ghost]="variant() === 'ghost'"
893
958
  [class.kf-button--outline]="variant() === 'outline'"
@@ -895,19 +960,27 @@ class Button {
895
960
  [class.kf-button--sm]="size() === 'sm'"
896
961
  [class.kf-button--lg]="size() === 'lg'"
897
962
  >
898
- @if (icon(); as iconName) {
899
- <kf-icon class="kf-button__icon" [name]="iconName" [size]="size() === 'lg' ? 'md' : 'sm'" />
963
+ @if (loading()) {
964
+ <kf-spinner size="sm" [onDark]="hasSolidFill()" />
965
+ } @else {
966
+ @if (icon(); as iconName) {
967
+ <kf-icon
968
+ class="kf-button__icon"
969
+ [name]="iconName"
970
+ [size]="size() === 'lg' ? 'md' : 'sm'"
971
+ />
972
+ }
973
+ <ng-content />
900
974
  }
901
- <ng-content />
902
975
  </button>
903
- `, isInline: true, styles: [":host{display:inline-block}:host([block]){display:block}button{display:inline-flex;align-items:center;justify-content:center;gap:var(--kf-space-xs);width:100%;font:inherit;font-weight:600;padding:var(--kf-space-sm) var(--kf-space-md);border:1px solid var(--kf-color-primary);border-radius:var(--kf-radius-sm);background:var(--kf-color-primary);color:var(--kf-color-white);cursor:pointer;transition:background .14s,border-color .14s,color .14s}button:hover:not(:disabled){background:var(--kf-color-primary-hover);border-color:var(--kf-color-primary-hover)}button:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}button:disabled{opacity:.5;cursor:not-allowed}button.kf-button--secondary{border-color:var(--kf-color-secondary);background:var(--kf-color-secondary)}button.kf-button--secondary:hover:not(:disabled){background:var(--kf-color-secondary-hover);border-color:var(--kf-color-secondary-hover)}button.kf-button--ghost{background:transparent;border-color:transparent;color:var(--kf-color-primary)}button.kf-button--ghost:hover:not(:disabled){background:var(--kf-color-primary-soft)}button.kf-button--outline{background:transparent;border-color:var(--kf-color-border-strong);color:var(--kf-color-text)}button.kf-button--outline:hover:not(:disabled){background:var(--kf-color-surface);border-color:var(--kf-color-text-muted)}button.kf-button--danger{border-color:var(--kf-color-error);background:var(--kf-color-error)}button.kf-button--danger:hover:not(:disabled){background:var(--kf-color-error-hover);border-color:var(--kf-color-error-hover)}button.kf-button--sm{padding:6px var(--kf-space-sm);font-size:.85rem}button.kf-button--lg{padding:var(--kf-space-sm) var(--kf-space-lg);font-size:1.05rem}\n"], dependencies: [{ kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size"] }] });
976
+ `, isInline: true, styles: [":host{display:inline-block}:host([block]){display:block}button{display:inline-flex;align-items:center;justify-content:center;gap:var(--kf-space-xs);width:100%;font:inherit;font-weight:600;padding:var(--kf-space-sm) var(--kf-space-md);border:1px solid var(--kf-color-primary);border-radius:var(--kf-radius-sm);background:var(--kf-color-primary);color:var(--kf-color-white);cursor:pointer;transition:background .14s,border-color .14s,color .14s}button:hover:not(:disabled){background:var(--kf-color-primary-hover);border-color:var(--kf-color-primary-hover)}button:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}button:disabled{opacity:.5;cursor:not-allowed}button.kf-button--secondary{border-color:var(--kf-color-secondary);background:var(--kf-color-secondary)}button.kf-button--secondary:hover:not(:disabled){background:var(--kf-color-secondary-hover);border-color:var(--kf-color-secondary-hover)}button.kf-button--ghost{background:transparent;border-color:transparent;color:var(--kf-color-primary)}button.kf-button--ghost:hover:not(:disabled){background:var(--kf-color-primary-soft)}button.kf-button--outline{background:transparent;border-color:var(--kf-color-border-strong);color:var(--kf-color-text)}button.kf-button--outline:hover:not(:disabled){background:var(--kf-color-surface);border-color:var(--kf-color-text-muted)}button.kf-button--danger{border-color:var(--kf-color-error);background:var(--kf-color-error)}button.kf-button--danger:hover:not(:disabled){background:var(--kf-color-error-hover);border-color:var(--kf-color-error-hover)}button.kf-button--sm{padding:6px var(--kf-space-sm);font-size:.85rem}button.kf-button--lg{padding:var(--kf-space-sm) var(--kf-space-lg);font-size:1.05rem}\n"], dependencies: [{ kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size"] }, { kind: "component", type: Spinner, selector: "kf-spinner", inputs: ["size", "label", "onDark"] }] });
904
977
  }
905
978
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Button, decorators: [{
906
979
  type: Component,
907
- args: [{ selector: 'kf-button', imports: [Icon], template: `
980
+ args: [{ selector: 'kf-button', imports: [Icon, Spinner], template: `
908
981
  <button
909
982
  [type]="type()"
910
- [disabled]="disabled()"
983
+ [disabled]="disabled() || loading()"
911
984
  [class.kf-button--secondary]="variant() === 'secondary'"
912
985
  [class.kf-button--ghost]="variant() === 'ghost'"
913
986
  [class.kf-button--outline]="variant() === 'outline'"
@@ -915,15 +988,23 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
915
988
  [class.kf-button--sm]="size() === 'sm'"
916
989
  [class.kf-button--lg]="size() === 'lg'"
917
990
  >
918
- @if (icon(); as iconName) {
919
- <kf-icon class="kf-button__icon" [name]="iconName" [size]="size() === 'lg' ? 'md' : 'sm'" />
991
+ @if (loading()) {
992
+ <kf-spinner size="sm" [onDark]="hasSolidFill()" />
993
+ } @else {
994
+ @if (icon(); as iconName) {
995
+ <kf-icon
996
+ class="kf-button__icon"
997
+ [name]="iconName"
998
+ [size]="size() === 'lg' ? 'md' : 'sm'"
999
+ />
1000
+ }
1001
+ <ng-content />
920
1002
  }
921
- <ng-content />
922
1003
  </button>
923
1004
  `, host: {
924
1005
  '[attr.block]': "block() ? '' : null",
925
1006
  }, styles: [":host{display:inline-block}:host([block]){display:block}button{display:inline-flex;align-items:center;justify-content:center;gap:var(--kf-space-xs);width:100%;font:inherit;font-weight:600;padding:var(--kf-space-sm) var(--kf-space-md);border:1px solid var(--kf-color-primary);border-radius:var(--kf-radius-sm);background:var(--kf-color-primary);color:var(--kf-color-white);cursor:pointer;transition:background .14s,border-color .14s,color .14s}button:hover:not(:disabled){background:var(--kf-color-primary-hover);border-color:var(--kf-color-primary-hover)}button:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}button:disabled{opacity:.5;cursor:not-allowed}button.kf-button--secondary{border-color:var(--kf-color-secondary);background:var(--kf-color-secondary)}button.kf-button--secondary:hover:not(:disabled){background:var(--kf-color-secondary-hover);border-color:var(--kf-color-secondary-hover)}button.kf-button--ghost{background:transparent;border-color:transparent;color:var(--kf-color-primary)}button.kf-button--ghost:hover:not(:disabled){background:var(--kf-color-primary-soft)}button.kf-button--outline{background:transparent;border-color:var(--kf-color-border-strong);color:var(--kf-color-text)}button.kf-button--outline:hover:not(:disabled){background:var(--kf-color-surface);border-color:var(--kf-color-text-muted)}button.kf-button--danger{border-color:var(--kf-color-error);background:var(--kf-color-error)}button.kf-button--danger:hover:not(:disabled){background:var(--kf-color-error-hover);border-color:var(--kf-color-error-hover)}button.kf-button--sm{padding:6px var(--kf-space-sm);font-size:.85rem}button.kf-button--lg{padding:var(--kf-space-sm) var(--kf-space-lg);font-size:1.05rem}\n"] }]
926
- }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], block: [{ type: i0.Input, args: [{ isSignal: true, alias: "block", required: false }] }] } });
1007
+ }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], block: [{ type: i0.Input, args: [{ isSignal: true, alias: "block", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }] } });
927
1008
 
928
1009
  /**
929
1010
  * Banner contextual que vive dentro del flujo: validaciones, avisos de
@@ -1463,7 +1544,7 @@ class Carousel {
1463
1544
  aria-label="Slide anterior"
1464
1545
  (click)="previous()"
1465
1546
  >
1466
- ‹
1547
+ <kf-icon name="chevron-left" size="sm" />
1467
1548
  </button>
1468
1549
  <button
1469
1550
  type="button"
@@ -1471,16 +1552,16 @@ class Carousel {
1471
1552
  aria-label="Slide siguiente"
1472
1553
  (click)="next()"
1473
1554
  >
1474
- ›
1555
+ <kf-icon name="chevron-right" size="sm" />
1475
1556
  </button>
1476
1557
  </div>
1477
1558
  }
1478
1559
  </div>
1479
- `, isInline: true, styles: [":host{display:block;height:100%;font-family:var(--kf-font-body)}.kf-carousel{position:relative;height:100%}.kf-carousel__viewport{height:100%;overflow:hidden;border-radius:var(--kf-radius-md)}.kf-carousel__track{display:flex;height:100%;transition:transform .32s ease}.kf-carousel__controls{position:absolute;z-index:2;right:var(--kf-space-sm);bottom:var(--kf-space-sm);display:flex;align-items:center;gap:var(--kf-space-sm)}.kf-carousel__arrow{display:inline-flex;align-items:center;justify-content:center;width:1.75rem;height:1.75rem;border:1px solid rgb(255 255 255 / 22%);border-radius:var(--kf-radius-pill);background:#ffffff1f;color:#fff;font-size:1rem;line-height:1;cursor:pointer;transition:background .14s}.kf-carousel__arrow:hover{background:#ffffff3d}.kf-carousel__arrow:focus-visible{outline:none;box-shadow:0 0 0 2px #fff9}.kf-carousel__dots{display:flex;gap:var(--kf-space-xs)}.kf-carousel__dot{width:7px;height:7px;padding:0;border:none;border-radius:var(--kf-radius-pill);background:#ffffff59;cursor:pointer;transition:background .14s,width .14s}.kf-carousel__dot:focus-visible{outline:none;box-shadow:0 0 0 2px #fff9}.kf-carousel__dot--active{width:1.125rem;border-radius:var(--kf-radius-xs);background:#fff}\n"] });
1560
+ `, isInline: true, styles: [":host{display:block;height:100%;font-family:var(--kf-font-body)}.kf-carousel{position:relative;height:100%}.kf-carousel__viewport{height:100%;overflow:hidden;border-radius:var(--kf-radius-md)}.kf-carousel__track{display:flex;height:100%;transition:transform .32s ease}.kf-carousel__controls{position:absolute;z-index:2;right:var(--kf-space-sm);bottom:var(--kf-space-sm);display:flex;align-items:center;gap:var(--kf-space-sm)}.kf-carousel__arrow{display:inline-flex;align-items:center;justify-content:center;width:1.75rem;height:1.75rem;border:1px solid rgb(255 255 255 / 22%);border-radius:var(--kf-radius-pill);background:#ffffff1f;color:#fff;cursor:pointer;transition:background .14s}.kf-carousel__arrow:hover{background:#ffffff3d}.kf-carousel__arrow:focus-visible{outline:none;box-shadow:0 0 0 2px #fff9}.kf-carousel__dots{display:flex;gap:var(--kf-space-xs)}.kf-carousel__dot{width:7px;height:7px;padding:0;border:none;border-radius:var(--kf-radius-pill);background:#ffffff59;cursor:pointer;transition:background .14s,width .14s}.kf-carousel__dot:focus-visible{outline:none;box-shadow:0 0 0 2px #fff9}.kf-carousel__dot--active{width:1.125rem;border-radius:var(--kf-radius-xs);background:#fff}\n"], dependencies: [{ kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size"] }] });
1480
1561
  }
1481
1562
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Carousel, decorators: [{
1482
1563
  type: Component,
1483
- args: [{ selector: 'kf-carousel', template: `
1564
+ args: [{ selector: 'kf-carousel', imports: [Icon], template: `
1484
1565
  <div
1485
1566
  class="kf-carousel"
1486
1567
  (mouseenter)="pauseAutoplay()"
@@ -1518,7 +1599,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
1518
1599
  aria-label="Slide anterior"
1519
1600
  (click)="previous()"
1520
1601
  >
1521
- ‹
1602
+ <kf-icon name="chevron-left" size="sm" />
1522
1603
  </button>
1523
1604
  <button
1524
1605
  type="button"
@@ -1526,12 +1607,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
1526
1607
  aria-label="Slide siguiente"
1527
1608
  (click)="next()"
1528
1609
  >
1529
- ›
1610
+ <kf-icon name="chevron-right" size="sm" />
1530
1611
  </button>
1531
1612
  </div>
1532
1613
  }
1533
1614
  </div>
1534
- `, styles: [":host{display:block;height:100%;font-family:var(--kf-font-body)}.kf-carousel{position:relative;height:100%}.kf-carousel__viewport{height:100%;overflow:hidden;border-radius:var(--kf-radius-md)}.kf-carousel__track{display:flex;height:100%;transition:transform .32s ease}.kf-carousel__controls{position:absolute;z-index:2;right:var(--kf-space-sm);bottom:var(--kf-space-sm);display:flex;align-items:center;gap:var(--kf-space-sm)}.kf-carousel__arrow{display:inline-flex;align-items:center;justify-content:center;width:1.75rem;height:1.75rem;border:1px solid rgb(255 255 255 / 22%);border-radius:var(--kf-radius-pill);background:#ffffff1f;color:#fff;font-size:1rem;line-height:1;cursor:pointer;transition:background .14s}.kf-carousel__arrow:hover{background:#ffffff3d}.kf-carousel__arrow:focus-visible{outline:none;box-shadow:0 0 0 2px #fff9}.kf-carousel__dots{display:flex;gap:var(--kf-space-xs)}.kf-carousel__dot{width:7px;height:7px;padding:0;border:none;border-radius:var(--kf-radius-pill);background:#ffffff59;cursor:pointer;transition:background .14s,width .14s}.kf-carousel__dot:focus-visible{outline:none;box-shadow:0 0 0 2px #fff9}.kf-carousel__dot--active{width:1.125rem;border-radius:var(--kf-radius-xs);background:#fff}\n"] }]
1615
+ `, styles: [":host{display:block;height:100%;font-family:var(--kf-font-body)}.kf-carousel{position:relative;height:100%}.kf-carousel__viewport{height:100%;overflow:hidden;border-radius:var(--kf-radius-md)}.kf-carousel__track{display:flex;height:100%;transition:transform .32s ease}.kf-carousel__controls{position:absolute;z-index:2;right:var(--kf-space-sm);bottom:var(--kf-space-sm);display:flex;align-items:center;gap:var(--kf-space-sm)}.kf-carousel__arrow{display:inline-flex;align-items:center;justify-content:center;width:1.75rem;height:1.75rem;border:1px solid rgb(255 255 255 / 22%);border-radius:var(--kf-radius-pill);background:#ffffff1f;color:#fff;cursor:pointer;transition:background .14s}.kf-carousel__arrow:hover{background:#ffffff3d}.kf-carousel__arrow:focus-visible{outline:none;box-shadow:0 0 0 2px #fff9}.kf-carousel__dots{display:flex;gap:var(--kf-space-xs)}.kf-carousel__dot{width:7px;height:7px;padding:0;border:none;border-radius:var(--kf-radius-pill);background:#ffffff59;cursor:pointer;transition:background .14s,width .14s}.kf-carousel__dot:focus-visible{outline:none;box-shadow:0 0 0 2px #fff9}.kf-carousel__dot--active{width:1.125rem;border-radius:var(--kf-radius-xs);background:#fff}\n"] }]
1535
1616
  }], propDecorators: { autoplay: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoplay", required: false }] }], interval: [{ type: i0.Input, args: [{ isSignal: true, alias: "interval", required: false }] }], indexChange: [{ type: i0.Output, args: ["indexChange"] }], slides: [{ type: i0.ContentChildren, args: [i0.forwardRef(() => CarouselSlide), { isSignal: true }] }] } });
1536
1617
 
1537
1618
  /**
@@ -1700,10 +1781,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
1700
1781
  }], propDecorators: { tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "tone", required: false }] }] } });
1701
1782
 
1702
1783
  /**
1703
- * Línea separadora decorativa entre bloques de contenido (ej. columnas de
1704
- * metadata en `kf-company-selector`). `vertical` necesita que el contenedor
1705
- * padre sea flex/grid con altura definida — se estira con `align-self:
1706
- * stretch`; sin eso no tiene alto propio y no se ve.
1784
+ * Separador visual simple entre bloques de contenido. `orientation`
1785
+ * controla si es una línea horizontal (para separar secciones apiladas) o
1786
+ * vertical (para separar elementos en una fila, ej. acciones en una toolbar,
1787
+ * o las columnas de metadata en `kf-company-selector`).
1788
+ *
1789
+ * En `vertical`, el divisor necesita una altura del contexto donde vive —
1790
+ * `:host { align-self: stretch }` lo hace ocupar la altura del contenedor
1791
+ * flex/grid padre sin que el consumidor tenga que fijarla a mano. `flex:
1792
+ * none` evita que el propio divisor se achique a 0 dentro de ese contenedor
1793
+ * flex cuando hay contenido hermano compitiendo por espacio.
1707
1794
  *
1708
1795
  * <kf-divider />
1709
1796
  * <kf-divider orientation="vertical" />
@@ -1712,15 +1799,15 @@ class Divider {
1712
1799
  orientation = input('horizontal', /* @ts-ignore */
1713
1800
  ...(ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []));
1714
1801
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Divider, deps: [], target: i0.ɵɵFactoryTarget.Component });
1715
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.0", type: Divider, isStandalone: true, selector: "kf-divider", inputs: { orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "separator" }, properties: { "attr.aria-orientation": "orientation()", "class.kf-divider--vertical": "orientation() === 'vertical'" } }, ngImport: i0, template: '', isInline: true, styles: [":host{display:block;flex:none;background:var(--kf-color-border)}:host(.kf-divider--vertical){width:1px;align-self:stretch}:host(:not(.kf-divider--vertical)){width:100%;height:1px}\n"] });
1802
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.0", type: Divider, isStandalone: true, selector: "kf-divider", inputs: { orientation: { classPropertyName: "orientation", publicName: "orientation", isSignal: true, isRequired: false, transformFunction: null } }, host: { attributes: { "role": "separator" }, properties: { "attr.orientation": "orientation()", "attr.aria-orientation": "orientation()" } }, ngImport: i0, template: '', isInline: true, styles: [":host{display:block;flex:none;border:none;background:var(--kf-color-border)}:host,:host([orientation=\"horizontal\"]){width:100%;height:1px}:host([orientation=\"vertical\"]){align-self:stretch;width:1px;height:auto}\n"] });
1716
1803
  }
1717
1804
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Divider, decorators: [{
1718
1805
  type: Component,
1719
1806
  args: [{ selector: 'kf-divider', template: '', host: {
1720
1807
  role: 'separator',
1808
+ '[attr.orientation]': 'orientation()',
1721
1809
  '[attr.aria-orientation]': 'orientation()',
1722
- '[class.kf-divider--vertical]': "orientation() === 'vertical'",
1723
- }, styles: [":host{display:block;flex:none;background:var(--kf-color-border)}:host(.kf-divider--vertical){width:1px;align-self:stretch}:host(:not(.kf-divider--vertical)){width:100%;height:1px}\n"] }]
1810
+ }, styles: [":host{display:block;flex:none;border:none;background:var(--kf-color-border)}:host,:host([orientation=\"horizontal\"]){width:100%;height:1px}:host([orientation=\"vertical\"]){align-self:stretch;width:1px;height:auto}\n"] }]
1724
1811
  }], propDecorators: { orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }] } });
1725
1812
 
1726
1813
  /**
@@ -1771,23 +1858,30 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
1771
1858
  `, styles: [":host{display:contents}.kf-field-label{font-family:var(--kf-font-title);font-size:.75rem;font-weight:600;letter-spacing:.04em;text-transform:uppercase;color:var(--kf-color-text-muted)}.kf-field-label__required{color:var(--kf-color-error);margin-inline-start:2px}\n"] }]
1772
1859
  }], propDecorators: { for: [{ type: i0.Input, args: [{ isSignal: true, alias: "for", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }] } });
1773
1860
 
1774
- let nextId$2 = 0;
1861
+ let nextId$1 = 0;
1775
1862
  /**
1776
1863
  * Dropdown de selección simple con panel propio — NUNCA usa el `<select>`
1777
1864
  * nativo del navegador. Un `<select>` real siempre delega el popup de
1778
1865
  * opciones al sistema operativo, y ningún CSS puede controlar cómo se ve
1779
- * ese popup (ver el mismo problema ya resuelto en `SelectMenu`, del que
1780
- * este componente reutiliza el patrón completo: trigger + panel flotante
1781
- * propio, cerrado con click-afuera y Escape).
1866
+ * ese popup.
1782
1867
  *
1783
- * Pensado para sets simples y cortos. Para listas más ricas o con más
1784
- * opciones, es el mismo componente que `SelectMenu` — la diferencia es
1785
- * solo de estilo (label uppercase, fondo gris que pasa a blanco al foco,
1786
- * a tono con `Input`) y que acepta `options` como `string[]` además de
1787
- * `{ value, label }[]`.
1868
+ * Hasta la `0.1.5` existía además `kf-select-menu`, un segundo componente
1869
+ * prácticamente idéntico (mismo trigger, mismo panel, mismo CSS) que solo
1870
+ * se diferenciaba en aceptar `options` únicamente como `{ value, label }[]`
1871
+ * (sin el atajo `string[]` que `Select` ya tenía). Se fusionaron en este
1872
+ * componente en la `0.2.0` — mantener dos componentes con la misma
1873
+ * interacción y el mismo aspecto visual, cuya única diferencia real era la
1874
+ * forma de un input, no se sostenía como dos piezas separadas del catálogo.
1875
+ *
1876
+ * El panel flotante se conecta al trigger vía `@angular/cdk/overlay` — el
1877
+ * mismo mecanismo que `Dropdown` (ver `../dropdown/Dropdown.ts`).
1788
1878
  *
1789
1879
  * <kf-select label="Sede" [options]="['Lima', 'Arequipa', 'Trujillo']" placeholder="Selecciona…" />
1790
1880
  * <kf-select [options]="[{ value: 'pe', label: 'Perú' }]" (valueChange)="onCountry($event)" />
1881
+ *
1882
+ * Pendiente: el prop `icon` de otros componentes del design system requiere
1883
+ * un componente Icon / sprite SVG que esta librería todavía no tiene — este
1884
+ * componente no usa iconos, así que no aplica acá.
1791
1885
  */
1792
1886
  class Select {
1793
1887
  label = input(/* @ts-ignore */
@@ -1800,7 +1894,7 @@ class Select {
1800
1894
  ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
1801
1895
  disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1802
1896
  valueChange = output();
1803
- instanceId = `kf-select-${nextId$2++}`;
1897
+ instanceId = `kf-select-${nextId$1++}`;
1804
1898
  triggerId = `${this.instanceId}-trigger`;
1805
1899
  listboxId = `${this.instanceId}-listbox`;
1806
1900
  isOpen = signal(false, /* @ts-ignore */
@@ -1810,7 +1904,34 @@ class Select {
1810
1904
  ...(ngDevMode ? [{ debugName: "normalizedOptions" }] : /* istanbul ignore next */ []));
1811
1905
  selectedOption = computed(() => this.normalizedOptions().find((option) => option.value === this.value()), /* @ts-ignore */
1812
1906
  ...(ngDevMode ? [{ debugName: "selectedOption" }] : /* istanbul ignore next */ []));
1813
- host = inject((ElementRef));
1907
+ triggerRef = viewChild.required('trigger', /* @ts-ignore */
1908
+ ...(ngDevMode ? [{ debugName: "triggerRef" }] : /* istanbul ignore next */ []));
1909
+ panelTemplate = viewChild.required('panelTemplate', /* @ts-ignore */
1910
+ ...(ngDevMode ? [{ debugName: "panelTemplate" }] : /* istanbul ignore next */ []));
1911
+ overlayService = inject(Overlay);
1912
+ viewContainerRef = inject(ViewContainerRef);
1913
+ destroyRef = inject(DestroyRef);
1914
+ overlayRef = null;
1915
+ constructor() {
1916
+ effect(() => {
1917
+ if (this.isOpen()) {
1918
+ this.attach();
1919
+ }
1920
+ else {
1921
+ this.detach();
1922
+ }
1923
+ });
1924
+ this.destroyRef.onDestroy(() => this.overlayRef?.dispose());
1925
+ }
1926
+ /**
1927
+ * Respaldo de `overlayRef.keydownEvents()`: ese observable del CDK solo ve
1928
+ * teclas presionadas con el foco DENTRO del pane del overlay. Si el foco
1929
+ * sigue en el trigger (o en cualquier otro lado de la página) mientras el
1930
+ * panel está abierto, `keydownEvents()` nunca dispara.
1931
+ */
1932
+ onDocumentEscape() {
1933
+ this.isOpen.set(false);
1934
+ }
1814
1935
  toggle() {
1815
1936
  if (this.disabled()) {
1816
1937
  return;
@@ -1823,16 +1944,53 @@ class Select {
1823
1944
  this.valueChange.emit(option.value);
1824
1945
  }
1825
1946
  }
1826
- onDocumentClick(event) {
1827
- if (this.isOpen() && !this.host.nativeElement.contains(event.target)) {
1828
- this.isOpen.set(false);
1947
+ attach() {
1948
+ if (this.overlayRef?.hasAttached()) {
1949
+ return;
1829
1950
  }
1951
+ const overlayRef = this.getOrCreateOverlayRef();
1952
+ // Se remide el ancho del trigger en cada apertura, no solo la primera —
1953
+ // ver la misma nota en SelectMenu: cachear el ancho en la creación del
1954
+ // overlay lo deja desincronizado si el trigger cambia de tamaño entre
1955
+ // una apertura y la siguiente.
1956
+ overlayRef.updateSize({ width: this.triggerRef().nativeElement.getBoundingClientRect().width });
1957
+ const portal = new TemplatePortal(this.panelTemplate(), this.viewContainerRef);
1958
+ overlayRef.attach(portal);
1959
+ overlayRef.outsidePointerEvents().subscribe(() => this.isOpen.set(false));
1960
+ overlayRef.keydownEvents().subscribe((event) => {
1961
+ if (event.key === 'Escape') {
1962
+ this.isOpen.set(false);
1963
+ }
1964
+ });
1830
1965
  }
1831
- onEscape() {
1832
- this.isOpen.set(false);
1966
+ detach() {
1967
+ this.overlayRef?.detach();
1968
+ }
1969
+ getOrCreateOverlayRef() {
1970
+ if (this.overlayRef) {
1971
+ return this.overlayRef;
1972
+ }
1973
+ const positionStrategy = this.overlayService
1974
+ .position()
1975
+ .flexibleConnectedTo(this.triggerRef())
1976
+ .withPositions([
1977
+ { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4 },
1978
+ { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', offsetY: -4 },
1979
+ { originX: 'end', originY: 'bottom', overlayX: 'end', overlayY: 'top', offsetY: 4 },
1980
+ { originX: 'end', originY: 'top', overlayX: 'end', overlayY: 'bottom', offsetY: -4 },
1981
+ ])
1982
+ .withViewportMargin(8)
1983
+ .withPush(true);
1984
+ this.overlayRef = this.overlayService.create({
1985
+ positionStrategy,
1986
+ scrollStrategy: this.overlayService.scrollStrategies.reposition(),
1987
+ width: this.triggerRef().nativeElement.getBoundingClientRect().width,
1988
+ maxWidth: '90vw',
1989
+ });
1990
+ return this.overlayRef;
1833
1991
  }
1834
1992
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Select, deps: [], target: i0.ɵɵFactoryTarget.Component });
1835
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Select, isStandalone: true, selector: "kf-select", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, host: { listeners: { "document:click": "onDocumentClick($event)", "document:keydown.escape": "onEscape()" } }, ngImport: i0, template: `
1993
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Select, isStandalone: true, selector: "kf-select", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, host: { listeners: { "document:keydown.escape": "onDocumentEscape()" } }, viewQueries: [{ propertyName: "triggerRef", first: true, predicate: ["trigger"], descendants: true, isSignal: true }, { propertyName: "panelTemplate", first: true, predicate: ["panelTemplate"], descendants: true, isSignal: true }], ngImport: i0, template: `
1836
1994
  @if (label()) {
1837
1995
  <kf-field-label [for]="triggerId">{{ label() }}</kf-field-label>
1838
1996
  }
@@ -1857,7 +2015,7 @@ class Select {
1857
2015
  aria-hidden="true"
1858
2016
  ></span>
1859
2017
  </button>
1860
- @if (isOpen()) {
2018
+ <ng-template #panelTemplate>
1861
2019
  <ul
1862
2020
  class="kf-select__panel"
1863
2021
  [id]="listboxId"
@@ -1881,8 +2039,8 @@ class Select {
1881
2039
  <li class="kf-select__empty">Sin opciones disponibles</li>
1882
2040
  }
1883
2041
  </ul>
1884
- }
1885
- `, isInline: true, styles: [":host{display:flex;flex-direction:column;gap:var(--kf-space-xs);position:relative;font-family:var(--kf-font-body)}.kf-select__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);width:100%;font:inherit;color:var(--kf-color-text);padding:var(--kf-space-sm) var(--kf-space-md);background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);cursor:pointer;transition:border-color .14s,box-shadow .14s}.kf-select__trigger:hover:not(:disabled){border-color:var(--kf-color-border-strong)}.kf-select__trigger:focus-visible{outline:none;border-color:var(--kf-color-primary);box-shadow:var(--kf-shadow-focus)}.kf-select__trigger--disabled{opacity:.5;cursor:not-allowed}.kf-select__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kf-select__value--placeholder{color:var(--kf-color-text-muted)}.kf-select__caret{flex-shrink:0;width:8px;height:8px;border-right:2px solid var(--kf-color-text-muted);border-bottom:2px solid var(--kf-color-text-muted);transform:rotate(45deg);transition:transform .14s;margin-top:-4px}.kf-select__caret--open{transform:rotate(-135deg);margin-top:4px}.kf-select__panel{position:absolute;top:100%;left:0;right:0;z-index:10;margin:var(--kf-space-xs) 0 0;padding:var(--kf-space-xs) 0;list-style:none;max-height:240px;overflow-y:auto;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-md)}.kf-select__option{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-select__option:hover{background:var(--kf-color-primary-soft)}.kf-select__option--selected{background:var(--kf-color-primary-soft);color:var(--kf-color-primary);font-weight:600}.kf-select__empty{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text-muted)}\n"], dependencies: [{ kind: "component", type: FieldLabel, selector: "kf-field-label", inputs: ["for", "required"] }] });
2042
+ </ng-template>
2043
+ `, isInline: true, styles: [":host{display:flex;flex-direction:column;gap:var(--kf-space-xs);width:240px;font-family:var(--kf-font-body)}.kf-select__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);width:100%;font:inherit;color:var(--kf-color-text);padding:var(--kf-space-sm) var(--kf-space-md);background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);cursor:pointer;transition:border-color .14s,box-shadow .14s}.kf-select__trigger:hover:not(:disabled){border-color:var(--kf-color-border-strong)}.kf-select__trigger:focus-visible{outline:none;border-color:var(--kf-color-primary);box-shadow:var(--kf-shadow-focus)}.kf-select__trigger--disabled{opacity:.5;cursor:not-allowed}.kf-select__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kf-select__value--placeholder{color:var(--kf-color-text-muted)}.kf-select__caret{flex-shrink:0;width:8px;height:8px;border-right:2px solid var(--kf-color-text-muted);border-bottom:2px solid var(--kf-color-text-muted);transform:rotate(45deg);transition:transform .14s;margin-top:-4px}.kf-select__caret--open{transform:rotate(-135deg);margin-top:4px}.kf-select__panel{box-sizing:border-box;width:100%;margin:0;padding:var(--kf-space-xs) 0;list-style:none;max-height:240px;overflow-y:auto;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-md)}.kf-select__option{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-select__option:hover{background:var(--kf-color-primary-soft)}.kf-select__option--selected{background:var(--kf-color-primary-soft);color:var(--kf-color-primary);font-weight:600}.kf-select__empty{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text-muted)}\n"], dependencies: [{ kind: "component", type: FieldLabel, selector: "kf-field-label", inputs: ["for", "required"] }] });
1886
2044
  }
1887
2045
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Select, decorators: [{
1888
2046
  type: Component,
@@ -1911,7 +2069,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
1911
2069
  aria-hidden="true"
1912
2070
  ></span>
1913
2071
  </button>
1914
- @if (isOpen()) {
2072
+ <ng-template #panelTemplate>
1915
2073
  <ul
1916
2074
  class="kf-select__panel"
1917
2075
  [id]="listboxId"
@@ -1935,12 +2093,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
1935
2093
  <li class="kf-select__empty">Sin opciones disponibles</li>
1936
2094
  }
1937
2095
  </ul>
1938
- }
1939
- `, styles: [":host{display:flex;flex-direction:column;gap:var(--kf-space-xs);position:relative;font-family:var(--kf-font-body)}.kf-select__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);width:100%;font:inherit;color:var(--kf-color-text);padding:var(--kf-space-sm) var(--kf-space-md);background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);cursor:pointer;transition:border-color .14s,box-shadow .14s}.kf-select__trigger:hover:not(:disabled){border-color:var(--kf-color-border-strong)}.kf-select__trigger:focus-visible{outline:none;border-color:var(--kf-color-primary);box-shadow:var(--kf-shadow-focus)}.kf-select__trigger--disabled{opacity:.5;cursor:not-allowed}.kf-select__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kf-select__value--placeholder{color:var(--kf-color-text-muted)}.kf-select__caret{flex-shrink:0;width:8px;height:8px;border-right:2px solid var(--kf-color-text-muted);border-bottom:2px solid var(--kf-color-text-muted);transform:rotate(45deg);transition:transform .14s;margin-top:-4px}.kf-select__caret--open{transform:rotate(-135deg);margin-top:4px}.kf-select__panel{position:absolute;top:100%;left:0;right:0;z-index:10;margin:var(--kf-space-xs) 0 0;padding:var(--kf-space-xs) 0;list-style:none;max-height:240px;overflow-y:auto;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-md)}.kf-select__option{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-select__option:hover{background:var(--kf-color-primary-soft)}.kf-select__option--selected{background:var(--kf-color-primary-soft);color:var(--kf-color-primary);font-weight:600}.kf-select__empty{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text-muted)}\n"] }]
1940
- }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], onDocumentClick: [{
1941
- type: HostListener,
1942
- args: ['document:click', ['$event']]
1943
- }], onEscape: [{
2096
+ </ng-template>
2097
+ `, styles: [":host{display:flex;flex-direction:column;gap:var(--kf-space-xs);width:240px;font-family:var(--kf-font-body)}.kf-select__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);width:100%;font:inherit;color:var(--kf-color-text);padding:var(--kf-space-sm) var(--kf-space-md);background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);cursor:pointer;transition:border-color .14s,box-shadow .14s}.kf-select__trigger:hover:not(:disabled){border-color:var(--kf-color-border-strong)}.kf-select__trigger:focus-visible{outline:none;border-color:var(--kf-color-primary);box-shadow:var(--kf-shadow-focus)}.kf-select__trigger--disabled{opacity:.5;cursor:not-allowed}.kf-select__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kf-select__value--placeholder{color:var(--kf-color-text-muted)}.kf-select__caret{flex-shrink:0;width:8px;height:8px;border-right:2px solid var(--kf-color-text-muted);border-bottom:2px solid var(--kf-color-text-muted);transform:rotate(45deg);transition:transform .14s;margin-top:-4px}.kf-select__caret--open{transform:rotate(-135deg);margin-top:4px}.kf-select__panel{box-sizing:border-box;width:100%;margin:0;padding:var(--kf-space-xs) 0;list-style:none;max-height:240px;overflow-y:auto;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-md)}.kf-select__option{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-select__option:hover{background:var(--kf-color-primary-soft)}.kf-select__option--selected{background:var(--kf-color-primary-soft);color:var(--kf-color-primary);font-weight:600}.kf-select__empty{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text-muted)}\n"] }]
2098
+ }], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], triggerRef: [{ type: i0.ViewChild, args: ['trigger', { isSignal: true }] }], panelTemplate: [{ type: i0.ViewChild, args: ['panelTemplate', { isSignal: true }] }], onDocumentEscape: [{
1944
2099
  type: HostListener,
1945
2100
  args: ['document:keydown.escape']
1946
2101
  }] } });
@@ -2179,55 +2334,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
2179
2334
  `, styles: [":host{display:block}.kf-skeleton{display:block;background:linear-gradient(90deg,var(--kf-color-border) 25%,var(--kf-color-surface) 37%,var(--kf-color-border) 63%);background-size:400% 100%;animation:kf-skeleton-shimmer 1.4s ease infinite}.kf-skeleton--text{height:14px;border-radius:var(--kf-radius-xs)}.kf-skeleton--rect{height:80px;border-radius:var(--kf-radius-sm)}.kf-skeleton--circle{aspect-ratio:1 / 1;border-radius:var(--kf-radius-pill)}@keyframes kf-skeleton-shimmer{0%{background-position:100% 0}to{background-position:0 0}}@media(prefers-reduced-motion:reduce){.kf-skeleton{animation:none;background:var(--kf-color-border)}}\n"] }]
2180
2335
  }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], height: [{ type: i0.Input, args: [{ isSignal: true, alias: "height", required: false }] }] } });
2181
2336
 
2182
- /**
2183
- * Indicador de carga indeterminado: inline, dentro de un botón, o como único
2184
- * contenido de una sección mientras llega la data.
2185
- *
2186
- * Cuando se conoce la forma del contenido que viene, preferir `kf-skeleton`
2187
- * (dibuja la silueta). El spinner es para "algo está pasando" sin forma
2188
- * conocida — guardar, enviar, reintentar.
2189
- *
2190
- * Lleva `role="status"`, así que el lector de pantalla anuncia el `label`
2191
- * cuando aparece. Sin `label` cae en un texto accesible por defecto.
2192
- *
2193
- * <kf-spinner />
2194
- * <kf-spinner size="sm" label="Cargando…" />
2195
- * <kf-spinner onDark />
2196
- */
2197
- class Spinner {
2198
- size = input('md', /* @ts-ignore */
2199
- ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
2200
- /** Texto al lado del anillo. Vacío deja solo el anillo (y el texto para lectores). */
2201
- label = input('', /* @ts-ignore */
2202
- ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
2203
- /** Invierte los colores para fondos oscuros. */
2204
- onDark = input(false, { ...(ngDevMode ? { debugName: "onDark" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
2205
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Spinner, deps: [], target: i0.ɵɵFactoryTarget.Component });
2206
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Spinner, isStandalone: true, selector: "kf-spinner", inputs: { size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, onDark: { classPropertyName: "onDark", publicName: "onDark", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
2207
- <span class="kf-spinner" [class.kf-spinner--on-dark]="onDark()" role="status">
2208
- <span class="kf-spinner__ring" [class]="'kf-spinner__ring--' + size()"></span>
2209
- @if (label()) {
2210
- <span class="kf-spinner__label">{{ label() }}</span>
2211
- } @else {
2212
- <span class="kf-spinner__sr">Cargando</span>
2213
- }
2214
- </span>
2215
- `, isInline: true, styles: [":host{display:inline-block}.kf-spinner{display:inline-flex;align-items:center;gap:var(--kf-space-sm);font-family:var(--kf-font-body)}.kf-spinner__ring{display:inline-block;flex:none;border-radius:var(--kf-radius-pill);border:2px solid var(--kf-color-border);border-top-color:var(--kf-color-primary);animation:kf-spinner-turn .7s linear infinite}.kf-spinner__ring--sm{width:16px;height:16px}.kf-spinner__ring--md{width:24px;height:24px;border-width:3px}.kf-spinner__ring--lg{width:32px;height:32px;border-width:3px}.kf-spinner__label{font-size:.875rem;color:var(--kf-color-text-muted)}.kf-spinner--on-dark .kf-spinner__ring{border-color:#ffffff47;border-top-color:var(--kf-color-white)}.kf-spinner--on-dark .kf-spinner__label{color:var(--kf-color-white)}@keyframes kf-spinner-turn{to{transform:rotate(360deg)}}.kf-spinner__sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}@media(prefers-reduced-motion:reduce){.kf-spinner__ring{animation-duration:2s}}\n"] });
2216
- }
2217
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Spinner, decorators: [{
2218
- type: Component,
2219
- args: [{ selector: 'kf-spinner', template: `
2220
- <span class="kf-spinner" [class.kf-spinner--on-dark]="onDark()" role="status">
2221
- <span class="kf-spinner__ring" [class]="'kf-spinner__ring--' + size()"></span>
2222
- @if (label()) {
2223
- <span class="kf-spinner__label">{{ label() }}</span>
2224
- } @else {
2225
- <span class="kf-spinner__sr">Cargando</span>
2226
- }
2227
- </span>
2228
- `, styles: [":host{display:inline-block}.kf-spinner{display:inline-flex;align-items:center;gap:var(--kf-space-sm);font-family:var(--kf-font-body)}.kf-spinner__ring{display:inline-block;flex:none;border-radius:var(--kf-radius-pill);border:2px solid var(--kf-color-border);border-top-color:var(--kf-color-primary);animation:kf-spinner-turn .7s linear infinite}.kf-spinner__ring--sm{width:16px;height:16px}.kf-spinner__ring--md{width:24px;height:24px;border-width:3px}.kf-spinner__ring--lg{width:32px;height:32px;border-width:3px}.kf-spinner__label{font-size:.875rem;color:var(--kf-color-text-muted)}.kf-spinner--on-dark .kf-spinner__ring{border-color:#ffffff47;border-top-color:var(--kf-color-white)}.kf-spinner--on-dark .kf-spinner__label{color:var(--kf-color-white)}@keyframes kf-spinner-turn{to{transform:rotate(360deg)}}.kf-spinner__sr{position:absolute;width:1px;height:1px;overflow:hidden;clip-path:inset(50%);white-space:nowrap}@media(prefers-reduced-motion:reduce){.kf-spinner__ring{animation-duration:2s}}\n"] }]
2229
- }], propDecorators: { size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], onDark: [{ type: i0.Input, args: [{ isSignal: true, alias: "onDark", required: false }] }] } });
2230
-
2231
2337
  /**
2232
2338
  * Wrapper de los cuatro estados que toda sección tiene: cargando, con datos,
2233
2339
  * vacía y con error. Un componente en vez de tres `@if` sueltos, porque es
@@ -2788,6 +2894,229 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
2788
2894
  `, styles: [":host{display:block;font-family:var(--kf-font-body)}.kf-data-table{background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md);box-shadow:var(--kf-shadow-sm);overflow:hidden}.kf-data-table--flush{border:none;box-shadow:none;border-radius:0}.kf-data-table__toolbar{display:flex;justify-content:flex-end;padding:var(--kf-space-sm) var(--kf-space-md);border-bottom:1px solid var(--kf-color-border)}.kf-data-table__view-toggle{display:inline-flex;gap:2px;padding:2px;background:var(--kf-color-surface);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm)}.kf-data-table__view-option{font:inherit;font-size:.875em;padding:var(--kf-space-xs) var(--kf-space-sm);border:none;background:transparent;color:var(--kf-color-text-muted);border-radius:var(--kf-radius-xs);cursor:pointer;transition:background .14s,color .14s}.kf-data-table__view-option:hover:not(.kf-data-table__view-option--active){color:var(--kf-color-text)}.kf-data-table__view-option--active{background:var(--kf-color-white);color:var(--kf-color-ink);box-shadow:var(--kf-shadow-xs)}.kf-data-table__view-option:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-data-table__scroll{overflow-x:auto}.kf-data-table__table{width:100%;border-collapse:collapse;text-align:left}.kf-data-table__table th,.kf-data-table__table td{padding:var(--kf-space-sm) var(--kf-space-md);border-bottom:1px solid var(--kf-color-border);white-space:nowrap}.kf-data-table__table th{font-family:var(--kf-font-title);font-size:.8125em;text-transform:uppercase;letter-spacing:.02em;color:var(--kf-color-text-muted);background:var(--kf-color-surface)}.kf-data-table__table td{color:var(--kf-color-text)}.kf-data-table__table tbody tr:last-child td{border-bottom:none}.kf-data-table__row{cursor:pointer;transition:background .14s}.kf-data-table__row:hover{background:var(--kf-color-primary-soft)}.kf-data-table__cell--right{text-align:right}.kf-data-table__empty{padding:var(--kf-space-md)}.kf-data-table__footer{padding:var(--kf-space-sm) var(--kf-space-md);border-top:1px solid var(--kf-color-border)}.kf-data-table__footer:empty{display:none}.kf-data-table__cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:var(--kf-space-md);padding:var(--kf-space-md)}.kf-data-table__card{display:flex;flex-direction:column;border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);overflow:hidden;transition:border-color .14s,box-shadow .14s}.kf-data-table__card--interactive{cursor:pointer}.kf-data-table__card:hover{border-color:var(--kf-color-border-strong);box-shadow:var(--kf-shadow-xs)}.kf-data-table__card--interactive:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-data-table__card-header{display:flex;align-items:center;gap:var(--kf-space-sm);padding:var(--kf-space-md)}.kf-data-table__card-media{flex:none}.kf-data-table__card-media img,.kf-data-table__thumb{display:block;width:48px;height:48px;border-radius:var(--kf-radius-xs);object-fit:cover;background:var(--kf-color-surface)}.kf-data-table__card-heading{display:flex;flex-direction:column;gap:var(--kf-space-xs);min-width:0}.kf-data-table__card-title{font-family:var(--kf-font-title);font-weight:600;color:var(--kf-color-ink);overflow-wrap:anywhere}.kf-data-table__card-statuses,.kf-data-table__card-actions{display:flex;flex-wrap:wrap;gap:var(--kf-space-xs)}.kf-data-table__card-fields{display:flex;flex-direction:column;gap:var(--kf-space-xs);padding:var(--kf-space-md)}.kf-data-table__card-header+.kf-data-table__card-fields{padding-top:0}.kf-data-table__card-field{display:flex;justify-content:space-between;gap:var(--kf-space-sm)}.kf-data-table__card-actions{justify-content:flex-end;margin-top:auto;padding:var(--kf-space-sm) var(--kf-space-md);border-top:1px solid var(--kf-color-border);background:var(--kf-color-surface)}.kf-data-table__thumb{width:32px;height:32px}.kf-data-table__card-label{font-family:var(--kf-font-title);font-size:.8125em;text-transform:uppercase;letter-spacing:.02em;color:var(--kf-color-text-muted)}.kf-data-table__card-value{color:var(--kf-color-text);text-align:right}\n"] }]
2789
2895
  }], propDecorators: { columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: true }] }], rowKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowKey", required: false }] }], flush: [{ type: i0.Input, args: [{ isSignal: true, alias: "flush", required: false }] }], cardable: [{ type: i0.Input, args: [{ isSignal: true, alias: "cardable", required: false }] }], defaultView: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultView", required: false }] }], render: [{ type: i0.Input, args: [{ isSignal: true, alias: "render", required: false }] }], cellTemplates: [{ type: i0.Input, args: [{ isSignal: true, alias: "cellTemplates", required: false }] }], rowClick: [{ type: i0.Output, args: ["rowClick"] }] } });
2790
2896
 
2897
+ /**
2898
+ * Ítem de acción dentro de un `kf-dropdown-panel`. Es un `<button>` nativo
2899
+ * por dentro (no un `<a>`): las acciones de un menú de este tipo disparan
2900
+ * comportamiento en la pantalla actual (cerrar sesión, ejecutar un
2901
+ * comando), no navegan a otra URL.
2902
+ *
2903
+ * <kf-dropdown-item (click)="onLogout()" tone="error">Cerrar sesión</kf-dropdown-item>
2904
+ */
2905
+ class DropdownItem {
2906
+ tone = input('neutral', /* @ts-ignore */
2907
+ ...(ngDevMode ? [{ debugName: "tone" }] : /* istanbul ignore next */ []));
2908
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DropdownItem, deps: [], target: i0.ɵɵFactoryTarget.Component });
2909
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.0", type: DropdownItem, isStandalone: true, selector: "kf-dropdown-item", inputs: { tone: { classPropertyName: "tone", publicName: "tone", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
2910
+ <button
2911
+ type="button"
2912
+ class="kf-dropdown-item"
2913
+ [class.kf-dropdown-item--error]="tone() === 'error'"
2914
+ role="menuitem"
2915
+ >
2916
+ <ng-content />
2917
+ </button>
2918
+ `, isInline: true, styles: [":host{display:block}.kf-dropdown-item{display:block;width:100%;padding:var(--kf-space-sm) var(--kf-space-md);border:none;background:transparent;font:inherit;text-align:left;color:var(--kf-color-text);cursor:pointer}.kf-dropdown-item:hover{background:var(--kf-color-surface)}.kf-dropdown-item:focus-visible{outline:none;box-shadow:inset var(--kf-shadow-focus)}.kf-dropdown-item--error{color:var(--kf-color-error-strong)}.kf-dropdown-item--error:hover{background:var(--kf-color-error-soft)}\n"] });
2919
+ }
2920
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DropdownItem, decorators: [{
2921
+ type: Component,
2922
+ args: [{ selector: 'kf-dropdown-item', template: `
2923
+ <button
2924
+ type="button"
2925
+ class="kf-dropdown-item"
2926
+ [class.kf-dropdown-item--error]="tone() === 'error'"
2927
+ role="menuitem"
2928
+ >
2929
+ <ng-content />
2930
+ </button>
2931
+ `, styles: [":host{display:block}.kf-dropdown-item{display:block;width:100%;padding:var(--kf-space-sm) var(--kf-space-md);border:none;background:transparent;font:inherit;text-align:left;color:var(--kf-color-text);cursor:pointer}.kf-dropdown-item:hover{background:var(--kf-color-surface)}.kf-dropdown-item:focus-visible{outline:none;box-shadow:inset var(--kf-shadow-focus)}.kf-dropdown-item--error{color:var(--kf-color-error-strong)}.kf-dropdown-item--error:hover{background:var(--kf-color-error-soft)}\n"] }]
2932
+ }], propDecorators: { tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "tone", required: false }] }] } });
2933
+ /**
2934
+ * Contenido del panel flotante de un `kf-dropdown`. Es solo una plantilla
2935
+ * (`<ng-template>` por dentro, capturada vía `TemplateRef`) — `Dropdown` la
2936
+ * conecta al overlay real cuando `open` pasa a `true`, no la renderiza en su
2937
+ * propio lugar del DOM. Esto es lo que permite que el panel aparezca pegado
2938
+ * al host de `Dropdown` sin importar dónde esté ese host en la página.
2939
+ *
2940
+ * <kf-dropdown [(open)]="menuOpen">
2941
+ * <kf-dropdown-panel>
2942
+ * <kf-dropdown-item (click)="onLogout()">Cerrar sesión</kf-dropdown-item>
2943
+ * </kf-dropdown-panel>
2944
+ * </kf-dropdown>
2945
+ */
2946
+ class DropdownPanel {
2947
+ templateRef = viewChild.required(TemplateRef, /* @ts-ignore */
2948
+ ...(ngDevMode ? [{ debugName: "templateRef" }] : /* istanbul ignore next */ []));
2949
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DropdownPanel, deps: [], target: i0.ɵɵFactoryTarget.Component });
2950
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.0", type: DropdownPanel, isStandalone: true, selector: "kf-dropdown-panel", viewQueries: [{ propertyName: "templateRef", first: true, predicate: TemplateRef, descendants: true, isSignal: true }], ngImport: i0, template: `
2951
+ <ng-template>
2952
+ <div class="kf-dropdown-panel">
2953
+ <div class="kf-dropdown-panel__inner" role="menu">
2954
+ <ng-content />
2955
+ </div>
2956
+ </div>
2957
+ </ng-template>
2958
+ `, isInline: true, styles: [".kf-dropdown-panel{min-width:220px;max-width:100%;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md);box-shadow:var(--kf-shadow-lg);font-family:var(--kf-font-body);animation:kf-dropdown-panel-enter .14s ease-out}.kf-dropdown-panel__inner{padding:var(--kf-space-xs) 0;overflow:hidden;border-radius:inherit}@media(prefers-reduced-motion:reduce){.kf-dropdown-panel{animation:none}}@keyframes kf-dropdown-panel-enter{0%{opacity:0;transform:translateY(-4px) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}\n"] });
2959
+ }
2960
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DropdownPanel, decorators: [{
2961
+ type: Component,
2962
+ args: [{ selector: 'kf-dropdown-panel', template: `
2963
+ <ng-template>
2964
+ <div class="kf-dropdown-panel">
2965
+ <div class="kf-dropdown-panel__inner" role="menu">
2966
+ <ng-content />
2967
+ </div>
2968
+ </div>
2969
+ </ng-template>
2970
+ `, styles: [".kf-dropdown-panel{min-width:220px;max-width:100%;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md);box-shadow:var(--kf-shadow-lg);font-family:var(--kf-font-body);animation:kf-dropdown-panel-enter .14s ease-out}.kf-dropdown-panel__inner{padding:var(--kf-space-xs) 0;overflow:hidden;border-radius:inherit}@media(prefers-reduced-motion:reduce){.kf-dropdown-panel{animation:none}}@keyframes kf-dropdown-panel-enter{0%{opacity:0;transform:translateY(-4px) scale(.98)}to{opacity:1;transform:translateY(0) scale(1)}}\n"] }]
2971
+ }], propDecorators: { templateRef: [{ type: i0.ViewChild, args: [i0.forwardRef(() => TemplateRef), { isSignal: true }] }] } });
2972
+ /**
2973
+ * Menú de acciones flotante, conectado a un elemento disparador vía
2974
+ * `@angular/cdk/overlay` — la misma pieza que usa Angular Material para
2975
+ * `mat-menu`/`mat-select`. Resuelve el patrón que antes se reimplementaba a
2976
+ * mano en cada consumidor (ver el session-menu de `web-portal-latam-frontend`,
2977
+ * ~80 líneas de HTML/TS/SCSS para toggle + click-afuera + Escape + animación
2978
+ * de entrada, acopladas a una clase interna no pública de `PortalHeader`).
2979
+ *
2980
+ * Se apoya en el CDK en vez de un `position: absolute` casero (como sí hace
2981
+ * `SelectMenu`, que puede permitírselo porque su trigger vive siempre dentro
2982
+ * del propio componente) porque acá el disparador real puede vivir en OTRO
2983
+ * componente por completo — ej. el avatar dentro de `kf-portal-header` — y
2984
+ * el panel tiene que aparecer pegado a ESE elemento sin importar dónde el
2985
+ * consumidor ubique al `Dropdown` en su HTML. El Overlay CDK calcula esa
2986
+ * posición (con fallback si no entra en pantalla) en vez de que cada
2987
+ * consumidor lo recalcule a mano.
2988
+ *
2989
+ * `open` es two-way, igual que `Alert`/`Modal`: el consumidor decide qué
2990
+ * dispara la apertura (puede ser un `(click)` en un botón propio, o un
2991
+ * evento de otro componente como `PortalHeader.sessionClick`) — `Dropdown`
2992
+ * en sí no renderiza ningún trigger, su propio host es el punto de anclaje
2993
+ * visual del panel.
2994
+ *
2995
+ * <kf-dropdown [(open)]="menuOpen">
2996
+ * <kf-dropdown-panel>
2997
+ * <div class="mi-header">Sesión iniciada como {{ email() }}</div>
2998
+ * <kf-dropdown-item (click)="onLogout()" tone="error">Cerrar sesión</kf-dropdown-item>
2999
+ * </kf-dropdown-panel>
3000
+ * </kf-dropdown>
3001
+ */
3002
+ class Dropdown {
3003
+ /** Visibilidad del panel, two-way: `[(open)]="menuOpen"`. */
3004
+ open = model(false, /* @ts-ignore */
3005
+ ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
3006
+ /**
3007
+ * Elemento al que se conecta el overlay, si es distinto del propio host
3008
+ * de `Dropdown`. Sin esto, `Dropdown` asume que ÉL MISMO es visualmente
3009
+ * el disparador (caso normal: el consumidor arma su propio botón como
3010
+ * contenido, y `Dropdown` envuelve ese contenido). Cuando el disparador
3011
+ * real vive dentro de OTRO componente que no se puede envolver sin
3012
+ * ensanchar el área de click a algo más grande que el botón (ej. el
3013
+ * avatar de sesión dentro de `PortalHeader`, que convive con logo y
3014
+ * navegación en el mismo host), `anchor` permite apuntar el overlay a la
3015
+ * referencia real de ese componente en vez de al host vacío de
3016
+ * `Dropdown`.
3017
+ *
3018
+ * <kf-dropdown [(open)]="menuOpen" [anchor]="headerRef()">
3019
+ */
3020
+ anchor = input(/* @ts-ignore */
3021
+ ...(ngDevMode ? [undefined, { debugName: "anchor" }] : /* istanbul ignore next */ []));
3022
+ panel = contentChild(DropdownPanel, /* @ts-ignore */
3023
+ ...(ngDevMode ? [{ debugName: "panel" }] : /* istanbul ignore next */ []));
3024
+ overlayService = inject(Overlay);
3025
+ host = inject((ElementRef));
3026
+ viewContainerRef = inject(ViewContainerRef);
3027
+ destroyRef = inject(DestroyRef);
3028
+ overlayRef = null;
3029
+ constructor() {
3030
+ effect(() => {
3031
+ if (this.open()) {
3032
+ this.attach();
3033
+ }
3034
+ else {
3035
+ this.detach();
3036
+ }
3037
+ });
3038
+ this.destroyRef.onDestroy(() => this.overlayRef?.dispose());
3039
+ }
3040
+ /**
3041
+ * Respaldo de `overlayRef.keydownEvents()`: ese observable del CDK solo
3042
+ * ve teclas presionadas con el foco DENTRO del pane del overlay. Si el
3043
+ * usuario abre el menú y presiona Escape con el foco en otro lado de la
3044
+ * página (caso común — el foco puede seguir en el botón disparador, que
3045
+ * vive fuera del overlay), `keydownEvents()` nunca dispara. Este listener
3046
+ * a nivel de documento cubre ese caso; el de `keydownEvents()` sigue
3047
+ * activo para cuando el foco sí está dentro del panel.
3048
+ */
3049
+ onDocumentEscape() {
3050
+ this.open.set(false);
3051
+ }
3052
+ attach() {
3053
+ const panel = this.panel();
3054
+ if (!panel || this.overlayRef?.hasAttached()) {
3055
+ return;
3056
+ }
3057
+ const overlayRef = this.getOrCreateOverlayRef();
3058
+ const portal = new TemplatePortal(panel.templateRef(), this.viewContainerRef);
3059
+ overlayRef.attach(portal);
3060
+ overlayRef.outsidePointerEvents().subscribe(() => this.open.set(false));
3061
+ overlayRef.keydownEvents().subscribe((event) => {
3062
+ if (event.key === 'Escape') {
3063
+ this.open.set(false);
3064
+ }
3065
+ });
3066
+ }
3067
+ detach() {
3068
+ this.overlayRef?.detach();
3069
+ }
3070
+ getOrCreateOverlayRef() {
3071
+ if (this.overlayRef) {
3072
+ return this.overlayRef;
3073
+ }
3074
+ const positionStrategy = this.overlayService
3075
+ .position()
3076
+ .flexibleConnectedTo(this.anchor() ?? this.host)
3077
+ // 4 posiciones, no 2: además de abajo/arriba (alineado al borde
3078
+ // derecho del anchor, el caso común), agrega el mismo par alineado al
3079
+ // borde IZQUIERDO. Con solo el par "end" — como estaba antes — un
3080
+ // trigger cerca del borde derecho de la pantalla no tenía ningún
3081
+ // fallback horizontal: el panel se salía del viewport en vez de
3082
+ // reacomodarse hacia la izquierda. El CDK prueba cada posición en
3083
+ // orden y usa la primera que entra completa en el viewport.
3084
+ .withPositions([
3085
+ { originX: 'end', originY: 'bottom', overlayX: 'end', overlayY: 'top', offsetY: 4 },
3086
+ { originX: 'end', originY: 'top', overlayX: 'end', overlayY: 'bottom', offsetY: -4 },
3087
+ { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4 },
3088
+ { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', offsetY: -4 },
3089
+ ])
3090
+ // Margen de seguridad contra los bordes del viewport — sin esto, el
3091
+ // CDK puede considerar una posición "válida" aunque quede pegada al
3092
+ // borde con 0px de aire.
3093
+ .withViewportMargin(8)
3094
+ // Si ninguna de las 4 posiciones entra completa (viewport muy
3095
+ // angosto), la última se ajusta con push en vez de desbordarse fuera
3096
+ // de pantalla sin control.
3097
+ .withPush(true);
3098
+ this.overlayRef = this.overlayService.create({
3099
+ positionStrategy,
3100
+ scrollStrategy: this.overlayService.scrollStrategies.reposition(),
3101
+ // Sin esto, el panel puede quedar más angosto que su propio
3102
+ // `min-width` en pantallas chicas si el CDK decide recortar — fija un
3103
+ // techo razonable en vez de dejarlo crecer sin límite ni encogerse
3104
+ // por debajo de lo legible.
3105
+ maxWidth: '90vw',
3106
+ });
3107
+ return this.overlayRef;
3108
+ }
3109
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Dropdown, deps: [], target: i0.ɵɵFactoryTarget.Component });
3110
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.0", type: Dropdown, isStandalone: true, selector: "kf-dropdown", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, anchor: { classPropertyName: "anchor", publicName: "anchor", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange" }, host: { listeners: { "document:keydown.escape": "onDocumentEscape()" } }, queries: [{ propertyName: "panel", first: true, predicate: DropdownPanel, descendants: true, isSignal: true }], ngImport: i0, template: `<ng-content />`, isInline: true, styles: [":host{display:inline-block}\n"] });
3111
+ }
3112
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Dropdown, decorators: [{
3113
+ type: Component,
3114
+ args: [{ selector: 'kf-dropdown', template: `<ng-content />`, styles: [":host{display:inline-block}\n"] }]
3115
+ }], ctorParameters: () => [], propDecorators: { open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], anchor: [{ type: i0.Input, args: [{ isSignal: true, alias: "anchor", required: false }] }], panel: [{ type: i0.ContentChild, args: [i0.forwardRef(() => DropdownPanel), { isSignal: true }] }], onDocumentEscape: [{
3116
+ type: HostListener,
3117
+ args: ['document:keydown.escape']
3118
+ }] } });
3119
+
2791
3120
  /**
2792
3121
  * Bloque promocional sobre fondo oscuro de marca (página 37) — campañas,
2793
3122
  * accesos directos o el portal de autoatención. Usa `--kf-color-tertiary-deep`
@@ -3369,6 +3698,58 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
3369
3698
  `, styles: [":host{display:flex;flex-direction:column;gap:var(--kf-space-xs);font-family:var(--kf-font-body)}.kf-input__field{display:flex;align-items:center;gap:var(--kf-space-sm);padding:0 var(--kf-space-sm);background:var(--kf-color-surface);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);transition:background .14s,border-color .14s,box-shadow .14s}.kf-input__field--focused{background:var(--kf-color-white);border-color:var(--kf-color-primary);box-shadow:var(--kf-shadow-focus)}.kf-input__field--invalid{border-color:var(--kf-color-error)}.kf-input__field--invalid.kf-input__field--focused{box-shadow:0 0 0 3px #eb575738}.kf-input__field--disabled{opacity:.5}.kf-input__icon{color:var(--kf-color-text-muted)}.kf-input__field--invalid .kf-input__icon{color:var(--kf-color-error)}.kf-input__control{flex:1;min-width:0;border:none;outline:none;background:transparent;font:inherit;color:var(--kf-color-text);padding:var(--kf-space-sm) 0}.kf-input__control::placeholder{color:var(--kf-color-text-muted)}.kf-input__control:disabled{cursor:not-allowed}.kf-input__suffix{flex:none;color:var(--kf-color-text-muted);font-size:.875rem}.kf-input__toggle{flex:none;display:inline-flex;align-items:center;justify-content:center;border:none;background:transparent;padding:var(--kf-space-xs);margin:0 calc(var(--kf-space-xs) * -1);color:var(--kf-color-text-muted);cursor:pointer;border-radius:var(--kf-radius-xs);transition:color .14s}.kf-input__toggle:hover{color:var(--kf-color-text)}.kf-input__toggle:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-input__hint{font-size:.75rem;color:var(--kf-color-text-muted)}.kf-input__error{display:flex;align-items:center;gap:var(--kf-space-xs);font-size:.75rem;font-weight:600;color:var(--kf-color-error-strong)}\n"] }]
3370
3699
  }], propDecorators: { nativeInput: [{ type: i0.ViewChild, args: ['nativeInput', { isSignal: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], suffix: [{ type: i0.Input, args: [{ isSignal: true, alias: "suffix", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], errorMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMessage", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], toggleable: [{ type: i0.Input, args: [{ isSignal: true, alias: "toggleable", required: false }] }] } });
3371
3700
 
3701
+ /**
3702
+ * Acción de bajo énfasis con apariencia de texto — "¿Olvidaste tu
3703
+ * contraseña?", "Volver a iniciar sesión". Antes de este componente,
3704
+ * `web-portal-latam-frontend` reimplementaba esto dos veces (`.login__forgot`
3705
+ * como `<a href="#">` con `preventDefault()`, `.login__back` como
3706
+ * `<button>`), cada una con su propio `:focus-visible` copiado literal.
3707
+ *
3708
+ * Es siempre un `<button type="button">` por dentro, nunca un `<a>`: ninguno
3709
+ * de los dos casos reales navega a otra URL, ambos disparan una acción en la
3710
+ * misma pantalla (cambiar de vista, someter un flujo). Si en el futuro un
3711
+ * consumidor necesita navegación real con href, esa es una discusión de API
3712
+ * distinta (`kf-link` con `[routerLink]`/`href`), no se fuerza acá sin caso
3713
+ * de uso confirmado.
3714
+ *
3715
+ * `variant="default"` (color primario, subrayado en hover) es para la
3716
+ * acción de mayor jerarquía relativa de la pantalla (ej. "¿Olvidaste tu
3717
+ * contraseña?"); `variant="muted"` (gris que pasa a primario en hover) es
3718
+ * para acciones de retroceso/salida (ej. "← Volver a iniciar sesión").
3719
+ *
3720
+ * <kf-link (click)="onForgotPassword()">¿Olvidaste tu contraseña?</kf-link>
3721
+ * <kf-link variant="muted" (click)="onBack()">← Volver a iniciar sesión</kf-link>
3722
+ */
3723
+ class Link {
3724
+ variant = input('default', /* @ts-ignore */
3725
+ ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
3726
+ disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
3727
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Link, deps: [], target: i0.ɵɵFactoryTarget.Component });
3728
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.0", type: Link, isStandalone: true, selector: "kf-link", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
3729
+ <button
3730
+ type="button"
3731
+ class="kf-link"
3732
+ [class.kf-link--muted]="variant() === 'muted'"
3733
+ [disabled]="disabled()"
3734
+ >
3735
+ <ng-content />
3736
+ </button>
3737
+ `, isInline: true, styles: [":host{display:inline-block}.kf-link{padding:0;border:none;background:transparent;font:inherit;font-weight:500;color:var(--kf-color-primary);text-decoration:none;cursor:pointer}.kf-link:hover:not(:disabled){text-decoration:underline}.kf-link:focus-visible{outline:2px solid var(--kf-color-primary);outline-offset:2px;border-radius:var(--kf-radius-xs)}.kf-link:disabled{opacity:.5;cursor:not-allowed}.kf-link--muted{color:var(--kf-color-text-muted)}.kf-link--muted:hover:not(:disabled){color:var(--kf-color-primary);text-decoration:none}\n"] });
3738
+ }
3739
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Link, decorators: [{
3740
+ type: Component,
3741
+ args: [{ selector: 'kf-link', template: `
3742
+ <button
3743
+ type="button"
3744
+ class="kf-link"
3745
+ [class.kf-link--muted]="variant() === 'muted'"
3746
+ [disabled]="disabled()"
3747
+ >
3748
+ <ng-content />
3749
+ </button>
3750
+ `, styles: [":host{display:inline-block}.kf-link{padding:0;border:none;background:transparent;font:inherit;font-weight:500;color:var(--kf-color-primary);text-decoration:none;cursor:pointer}.kf-link:hover:not(:disabled){text-decoration:underline}.kf-link:focus-visible{outline:2px solid var(--kf-color-primary);outline-offset:2px;border-radius:var(--kf-radius-xs)}.kf-link:disabled{opacity:.5;cursor:not-allowed}.kf-link--muted{color:var(--kf-color-text-muted)}.kf-link--muted:hover:not(:disabled){color:var(--kf-color-primary);text-decoration:none}\n"] }]
3751
+ }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
3752
+
3372
3753
  /**
3373
3754
  * Card de estadística/KPI: label, valor grande, ícono opcional en la esquina
3374
3755
  * superior derecha, badge de esquina opcional (pegado al valor) y una nota
@@ -3620,7 +4001,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
3620
4001
  args: ['click', ['$event']]
3621
4002
  }] } });
3622
4003
 
3623
- let nextId$1 = 0;
4004
+ let nextId = 0;
3624
4005
  /**
3625
4006
  * Dropdown de selección múltiple con checkboxes internos, siguiendo el mismo
3626
4007
  * patrón de panel flotante que `SelectMenu` (panel con `position: absolute`
@@ -3657,7 +4038,7 @@ class MultiSelect {
3657
4038
  ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
3658
4039
  disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
3659
4040
  valuesChange = output();
3660
- instanceId = `kf-multi-select-${nextId$1++}`;
4041
+ instanceId = `kf-multi-select-${nextId++}`;
3661
4042
  triggerId = `${this.instanceId}-trigger`;
3662
4043
  listboxId = `${this.instanceId}-listbox`;
3663
4044
  isOpen = signal(false, /* @ts-ignore */
@@ -3793,7 +4174,7 @@ class MultiSelect {
3793
4174
  }
3794
4175
  </ul>
3795
4176
  }
3796
- `, isInline: true, styles: [":host{display:inline-flex;flex-direction:column;gap:var(--kf-space-xs);position:relative;font:inherit;min-width:200px}.kf-multi-select__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);width:100%;font:inherit;font-family:var(--kf-font-body);padding:var(--kf-space-sm) var(--kf-space-md);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);background:var(--kf-color-white);color:var(--kf-color-text);cursor:pointer;transition:border-color .14s,box-shadow .14s}.kf-multi-select__trigger:hover:not(:disabled){border-color:var(--kf-color-border-strong)}.kf-multi-select__trigger:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-multi-select__trigger:disabled{opacity:.5;cursor:not-allowed}.kf-multi-select__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;text-align:left}.kf-multi-select__value--placeholder{color:var(--kf-color-text-muted)}.kf-multi-select__count{flex:none;display:inline-flex;align-items:center;justify-content:center;min-width:1.25rem;height:1.25rem;padding:0 5px;border-radius:var(--kf-radius-pill);background:var(--kf-color-primary);color:var(--kf-color-white);font-size:.72rem;font-weight:700}.kf-multi-select__caret{flex-shrink:0;width:8px;height:8px;border-right:2px solid var(--kf-color-text-muted);border-bottom:2px solid var(--kf-color-text-muted);transform:rotate(45deg);transition:transform .14s;margin-top:-4px}.kf-multi-select__caret--open{transform:rotate(-135deg);margin-top:4px}.kf-multi-select__panel{position:absolute;top:100%;left:0;right:0;z-index:10;margin:var(--kf-space-xs) 0 0;padding:var(--kf-space-xs) 0;list-style:none;max-height:240px;overflow-y:auto;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-md)}.kf-multi-select__option{display:flex;align-items:center;gap:var(--kf-space-sm);padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-multi-select__option:hover{background:var(--kf-color-primary-soft)}.kf-multi-select__option--selected{color:var(--kf-color-primary);font-weight:600}.kf-multi-select__checkbox{display:inline-flex;flex-shrink:0;width:var(--kf-space-md);height:var(--kf-space-md);border:1px solid var(--kf-color-border-strong);border-radius:var(--kf-radius-xs);background:var(--kf-color-white);transition:background .14s,border-color .14s;position:relative}.kf-multi-select__checkbox--checked{background:var(--kf-color-primary);border-color:var(--kf-color-primary)}.kf-multi-select__checkbox--checked:after{content:\"\";position:absolute;left:4px;top:1px;width:4px;height:8px;border:solid var(--kf-color-white);border-width:0 2px 2px 0;transform:rotate(45deg)}.kf-multi-select__checkbox--indeterminate{background:var(--kf-color-primary);border-color:var(--kf-color-primary)}.kf-multi-select__checkbox--indeterminate:after{content:\"\";position:absolute;left:3px;top:6px;width:8px;height:2px;background:var(--kf-color-white);border:none;transform:none}.kf-multi-select__option-label{overflow:hidden;text-overflow:ellipsis}.kf-multi-select__empty{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text-muted)}\n"], dependencies: [{ kind: "component", type: FieldLabel, selector: "kf-field-label", inputs: ["for", "required"] }] });
4177
+ `, isInline: true, styles: [":host{display:inline-flex;flex-direction:column;gap:var(--kf-space-xs);position:relative;font:inherit;width:240px}.kf-multi-select__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);width:100%;font:inherit;font-family:var(--kf-font-body);padding:var(--kf-space-sm) var(--kf-space-md);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);background:var(--kf-color-white);color:var(--kf-color-text);cursor:pointer;transition:border-color .14s,box-shadow .14s}.kf-multi-select__trigger:hover:not(:disabled){border-color:var(--kf-color-border-strong)}.kf-multi-select__trigger:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-multi-select__trigger:disabled{opacity:.5;cursor:not-allowed}.kf-multi-select__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;text-align:left}.kf-multi-select__value--placeholder{color:var(--kf-color-text-muted)}.kf-multi-select__count{flex:none;display:inline-flex;align-items:center;justify-content:center;min-width:1.25rem;height:1.25rem;padding:0 5px;border-radius:var(--kf-radius-pill);background:var(--kf-color-primary);color:var(--kf-color-white);font-size:.72rem;font-weight:700}.kf-multi-select__caret{flex-shrink:0;width:8px;height:8px;border-right:2px solid var(--kf-color-text-muted);border-bottom:2px solid var(--kf-color-text-muted);transform:rotate(45deg);transition:transform .14s;margin-top:-4px}.kf-multi-select__caret--open{transform:rotate(-135deg);margin-top:4px}.kf-multi-select__panel{position:absolute;top:100%;left:0;right:0;z-index:10;margin:var(--kf-space-xs) 0 0;padding:var(--kf-space-xs) 0;list-style:none;max-height:240px;overflow-y:auto;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-md)}.kf-multi-select__option{display:flex;align-items:center;gap:var(--kf-space-sm);padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-multi-select__option:hover{background:var(--kf-color-primary-soft)}.kf-multi-select__option--selected{color:var(--kf-color-primary);font-weight:600}.kf-multi-select__checkbox{display:inline-flex;flex-shrink:0;width:var(--kf-space-md);height:var(--kf-space-md);border:1px solid var(--kf-color-border-strong);border-radius:var(--kf-radius-xs);background:var(--kf-color-white);transition:background .14s,border-color .14s;position:relative}.kf-multi-select__checkbox--checked{background:var(--kf-color-primary);border-color:var(--kf-color-primary)}.kf-multi-select__checkbox--checked:after{content:\"\";position:absolute;left:4px;top:1px;width:4px;height:8px;border:solid var(--kf-color-white);border-width:0 2px 2px 0;transform:rotate(45deg)}.kf-multi-select__checkbox--indeterminate{background:var(--kf-color-primary);border-color:var(--kf-color-primary)}.kf-multi-select__checkbox--indeterminate:after{content:\"\";position:absolute;left:3px;top:6px;width:8px;height:2px;background:var(--kf-color-white);border:none;transform:none}.kf-multi-select__option-label{overflow:hidden;text-overflow:ellipsis}.kf-multi-select__empty{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text-muted)}\n"], dependencies: [{ kind: "component", type: FieldLabel, selector: "kf-field-label", inputs: ["for", "required"] }] });
3797
4178
  }
3798
4179
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: MultiSelect, decorators: [{
3799
4180
  type: Component,
@@ -3875,7 +4256,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
3875
4256
  }
3876
4257
  </ul>
3877
4258
  }
3878
- `, styles: [":host{display:inline-flex;flex-direction:column;gap:var(--kf-space-xs);position:relative;font:inherit;min-width:200px}.kf-multi-select__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);width:100%;font:inherit;font-family:var(--kf-font-body);padding:var(--kf-space-sm) var(--kf-space-md);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);background:var(--kf-color-white);color:var(--kf-color-text);cursor:pointer;transition:border-color .14s,box-shadow .14s}.kf-multi-select__trigger:hover:not(:disabled){border-color:var(--kf-color-border-strong)}.kf-multi-select__trigger:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-multi-select__trigger:disabled{opacity:.5;cursor:not-allowed}.kf-multi-select__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;text-align:left}.kf-multi-select__value--placeholder{color:var(--kf-color-text-muted)}.kf-multi-select__count{flex:none;display:inline-flex;align-items:center;justify-content:center;min-width:1.25rem;height:1.25rem;padding:0 5px;border-radius:var(--kf-radius-pill);background:var(--kf-color-primary);color:var(--kf-color-white);font-size:.72rem;font-weight:700}.kf-multi-select__caret{flex-shrink:0;width:8px;height:8px;border-right:2px solid var(--kf-color-text-muted);border-bottom:2px solid var(--kf-color-text-muted);transform:rotate(45deg);transition:transform .14s;margin-top:-4px}.kf-multi-select__caret--open{transform:rotate(-135deg);margin-top:4px}.kf-multi-select__panel{position:absolute;top:100%;left:0;right:0;z-index:10;margin:var(--kf-space-xs) 0 0;padding:var(--kf-space-xs) 0;list-style:none;max-height:240px;overflow-y:auto;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-md)}.kf-multi-select__option{display:flex;align-items:center;gap:var(--kf-space-sm);padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-multi-select__option:hover{background:var(--kf-color-primary-soft)}.kf-multi-select__option--selected{color:var(--kf-color-primary);font-weight:600}.kf-multi-select__checkbox{display:inline-flex;flex-shrink:0;width:var(--kf-space-md);height:var(--kf-space-md);border:1px solid var(--kf-color-border-strong);border-radius:var(--kf-radius-xs);background:var(--kf-color-white);transition:background .14s,border-color .14s;position:relative}.kf-multi-select__checkbox--checked{background:var(--kf-color-primary);border-color:var(--kf-color-primary)}.kf-multi-select__checkbox--checked:after{content:\"\";position:absolute;left:4px;top:1px;width:4px;height:8px;border:solid var(--kf-color-white);border-width:0 2px 2px 0;transform:rotate(45deg)}.kf-multi-select__checkbox--indeterminate{background:var(--kf-color-primary);border-color:var(--kf-color-primary)}.kf-multi-select__checkbox--indeterminate:after{content:\"\";position:absolute;left:3px;top:6px;width:8px;height:2px;background:var(--kf-color-white);border:none;transform:none}.kf-multi-select__option-label{overflow:hidden;text-overflow:ellipsis}.kf-multi-select__empty{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text-muted)}\n"] }]
4259
+ `, styles: [":host{display:inline-flex;flex-direction:column;gap:var(--kf-space-xs);position:relative;font:inherit;width:240px}.kf-multi-select__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);width:100%;font:inherit;font-family:var(--kf-font-body);padding:var(--kf-space-sm) var(--kf-space-md);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);background:var(--kf-color-white);color:var(--kf-color-text);cursor:pointer;transition:border-color .14s,box-shadow .14s}.kf-multi-select__trigger:hover:not(:disabled){border-color:var(--kf-color-border-strong)}.kf-multi-select__trigger:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-multi-select__trigger:disabled{opacity:.5;cursor:not-allowed}.kf-multi-select__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;text-align:left}.kf-multi-select__value--placeholder{color:var(--kf-color-text-muted)}.kf-multi-select__count{flex:none;display:inline-flex;align-items:center;justify-content:center;min-width:1.25rem;height:1.25rem;padding:0 5px;border-radius:var(--kf-radius-pill);background:var(--kf-color-primary);color:var(--kf-color-white);font-size:.72rem;font-weight:700}.kf-multi-select__caret{flex-shrink:0;width:8px;height:8px;border-right:2px solid var(--kf-color-text-muted);border-bottom:2px solid var(--kf-color-text-muted);transform:rotate(45deg);transition:transform .14s;margin-top:-4px}.kf-multi-select__caret--open{transform:rotate(-135deg);margin-top:4px}.kf-multi-select__panel{position:absolute;top:100%;left:0;right:0;z-index:10;margin:var(--kf-space-xs) 0 0;padding:var(--kf-space-xs) 0;list-style:none;max-height:240px;overflow-y:auto;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-md)}.kf-multi-select__option{display:flex;align-items:center;gap:var(--kf-space-sm);padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-multi-select__option:hover{background:var(--kf-color-primary-soft)}.kf-multi-select__option--selected{color:var(--kf-color-primary);font-weight:600}.kf-multi-select__checkbox{display:inline-flex;flex-shrink:0;width:var(--kf-space-md);height:var(--kf-space-md);border:1px solid var(--kf-color-border-strong);border-radius:var(--kf-radius-xs);background:var(--kf-color-white);transition:background .14s,border-color .14s;position:relative}.kf-multi-select__checkbox--checked{background:var(--kf-color-primary);border-color:var(--kf-color-primary)}.kf-multi-select__checkbox--checked:after{content:\"\";position:absolute;left:4px;top:1px;width:4px;height:8px;border:solid var(--kf-color-white);border-width:0 2px 2px 0;transform:rotate(45deg)}.kf-multi-select__checkbox--indeterminate{background:var(--kf-color-primary);border-color:var(--kf-color-primary)}.kf-multi-select__checkbox--indeterminate:after{content:\"\";position:absolute;left:3px;top:6px;width:8px;height:2px;background:var(--kf-color-white);border:none;transform:none}.kf-multi-select__option-label{overflow:hidden;text-overflow:ellipsis}.kf-multi-select__empty{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text-muted)}\n"] }]
3879
4260
  }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], values: [{ type: i0.Input, args: [{ isSignal: true, alias: "values", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], valuesChange: [{ type: i0.Output, args: ["valuesChange"] }], onDocumentClick: [{
3880
4261
  type: HostListener,
3881
4262
  args: ['document:click', ['$event']]
@@ -4347,10 +4728,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
4347
4728
  * `<ng-content select="[actions]">` para no acoplar el componente a un set
4348
4729
  * fijo de botones.
4349
4730
  *
4350
- * `size="sm"` reduce tipografía y espaciado para usarse dentro de cards ya
4351
- * compactas. `solid` rellena el chip de ícono con el color primario sólido
4352
- * en vez del tratamiento "soft" (fondo tenue) — pensado para superficies que
4353
- * ya tienen un tinte de color propio, donde el chip "soft" perdería contraste.
4731
+ * `size` sigue la misma escala `'sm'|'md'|'lg'` que `Button`/`Icon`/`Spinner`
4732
+ * (`md` es el default): `sm` reduce tipografía y espaciado para usarse
4733
+ * dentro de cards ya compactas, `lg` los agranda para encabezar un bloque
4734
+ * más prominente. Antes de la `0.2.0`, `size` solo aceptaba `'sm'` —
4735
+ * placeholder incompleto de esta misma escala; los valores de `lg` son una
4736
+ * extrapolación proporcional de la escala existente (sin un caso de uso
4737
+ * real todavía que los valide, a diferencia de `sm`, que sí viene de un
4738
+ * patrón real de uso dentro de cards). `solid` rellena el chip de ícono con
4739
+ * el color primario sólido en vez del tratamiento "soft" (fondo tenue) —
4740
+ * pensado para superficies que ya tienen un tinte de color propio, donde el
4741
+ * chip "soft" perdería contraste.
4354
4742
  *
4355
4743
  * <kf-section-header icon="settings" title="Preferencias"
4356
4744
  * subtitle="Configuración general de la cuenta">
@@ -4358,6 +4746,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
4358
4746
  * </kf-section-header>
4359
4747
  *
4360
4748
  * <kf-section-header title="Detalle" size="sm" [solid]="true" />
4749
+ * <kf-section-header title="Resumen de flota" size="lg" />
4361
4750
  */
4362
4751
  class SectionHeader {
4363
4752
  icon = input(/* @ts-ignore */
@@ -4366,14 +4755,24 @@ class SectionHeader {
4366
4755
  ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
4367
4756
  subtitle = input(/* @ts-ignore */
4368
4757
  ...(ngDevMode ? [undefined, { debugName: "subtitle" }] : /* istanbul ignore next */ []));
4369
- size = input(/* @ts-ignore */
4370
- ...(ngDevMode ? [undefined, { debugName: "size" }] : /* istanbul ignore next */ []));
4758
+ size = input('md', /* @ts-ignore */
4759
+ ...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
4371
4760
  solid = input(false, { ...(ngDevMode ? { debugName: "solid" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
4761
+ iconSize = computed(() => {
4762
+ if (this.size() === 'sm') {
4763
+ return 'sm';
4764
+ }
4765
+ if (this.size() === 'lg') {
4766
+ return 'lg';
4767
+ }
4768
+ return 'md';
4769
+ }, /* @ts-ignore */
4770
+ ...(ngDevMode ? [{ debugName: "iconSize" }] : /* istanbul ignore next */ []));
4372
4771
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: SectionHeader, deps: [], target: i0.ɵɵFactoryTarget.Component });
4373
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: SectionHeader, isStandalone: true, selector: "kf-section-header", inputs: { icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, subtitle: { classPropertyName: "subtitle", publicName: "subtitle", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, solid: { classPropertyName: "solid", publicName: "solid", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.kf-section-header--sm": "size() === 'sm'" } }, ngImport: i0, template: `
4772
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: SectionHeader, isStandalone: true, selector: "kf-section-header", inputs: { icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: true, transformFunction: null }, subtitle: { classPropertyName: "subtitle", publicName: "subtitle", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, solid: { classPropertyName: "solid", publicName: "solid", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.kf-section-header--sm": "size() === 'sm'", "class.kf-section-header--lg": "size() === 'lg'" } }, ngImport: i0, template: `
4374
4773
  @if (icon(); as iconName) {
4375
4774
  <span class="kf-section-header__icon" [class.kf-section-header__icon--solid]="solid()">
4376
- <kf-icon [name]="iconName" [size]="size() === 'sm' ? 'sm' : 'md'" />
4775
+ <kf-icon [name]="iconName" [size]="iconSize()" />
4377
4776
  </span>
4378
4777
  }
4379
4778
 
@@ -4387,14 +4786,14 @@ class SectionHeader {
4387
4786
  <span class="kf-section-header__actions">
4388
4787
  <ng-content select="[actions]" />
4389
4788
  </span>
4390
- `, isInline: true, styles: [":host{display:flex;align-items:center;gap:var(--kf-space-sm);font-family:var(--kf-font-body)}:host(.kf-section-header--sm){gap:var(--kf-space-xs)}.kf-section-header__icon{display:inline-flex;align-items:center;justify-content:center;flex:none;inline-size:2.5rem;block-size:2.5rem;border-radius:var(--kf-radius-sm);background:var(--kf-color-primary-soft);color:var(--kf-color-primary)}:host(.kf-section-header--sm) .kf-section-header__icon{inline-size:1.75rem;block-size:1.75rem;border-radius:var(--kf-radius-xs)}.kf-section-header__icon--solid{background:var(--kf-color-primary);color:var(--kf-color-white)}.kf-section-header__text{display:flex;flex-direction:column;min-width:0;flex:1 1 auto}.kf-section-header__title{font-family:var(--kf-font-title);font-size:1rem;font-weight:700;color:var(--kf-color-ink);line-height:1.3}:host(.kf-section-header--sm) .kf-section-header__title{font-size:.875rem}.kf-section-header__subtitle{font-size:.8125rem;color:var(--kf-color-text-muted);line-height:1.3}:host(.kf-section-header--sm) .kf-section-header__subtitle{font-size:.75rem}.kf-section-header__actions{flex:none;display:flex;align-items:center;gap:var(--kf-space-sm)}\n"], dependencies: [{ kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size"] }] });
4789
+ `, isInline: true, styles: [":host{display:flex;align-items:center;gap:var(--kf-space-sm);font-family:var(--kf-font-body)}:host(.kf-section-header--sm){gap:var(--kf-space-xs)}:host(.kf-section-header--lg){gap:var(--kf-space-md)}.kf-section-header__icon{display:inline-flex;align-items:center;justify-content:center;flex:none;inline-size:2.5rem;block-size:2.5rem;border-radius:var(--kf-radius-sm);background:var(--kf-color-primary-soft);color:var(--kf-color-primary)}:host(.kf-section-header--sm) .kf-section-header__icon{inline-size:1.75rem;block-size:1.75rem;border-radius:var(--kf-radius-xs)}:host(.kf-section-header--lg) .kf-section-header__icon{inline-size:3.25rem;block-size:3.25rem;border-radius:var(--kf-radius-md)}.kf-section-header__icon--solid{background:var(--kf-color-primary);color:var(--kf-color-white)}.kf-section-header__text{display:flex;flex-direction:column;min-width:0;flex:1 1 auto}.kf-section-header__title{font-family:var(--kf-font-title);font-size:1rem;font-weight:700;color:var(--kf-color-ink);line-height:1.3}:host(.kf-section-header--sm) .kf-section-header__title{font-size:.875rem}:host(.kf-section-header--lg) .kf-section-header__title{font-size:1.25rem}.kf-section-header__subtitle{font-size:.8125rem;color:var(--kf-color-text-muted);line-height:1.3}:host(.kf-section-header--sm) .kf-section-header__subtitle{font-size:.75rem}:host(.kf-section-header--lg) .kf-section-header__subtitle{font-size:.9375rem}.kf-section-header__actions{flex:none;display:flex;align-items:center;gap:var(--kf-space-sm)}\n"], dependencies: [{ kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size"] }] });
4391
4790
  }
4392
4791
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: SectionHeader, decorators: [{
4393
4792
  type: Component,
4394
4793
  args: [{ selector: 'kf-section-header', imports: [Icon], template: `
4395
4794
  @if (icon(); as iconName) {
4396
4795
  <span class="kf-section-header__icon" [class.kf-section-header__icon--solid]="solid()">
4397
- <kf-icon [name]="iconName" [size]="size() === 'sm' ? 'sm' : 'md'" />
4796
+ <kf-icon [name]="iconName" [size]="iconSize()" />
4398
4797
  </span>
4399
4798
  }
4400
4799
 
@@ -4410,7 +4809,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
4410
4809
  </span>
4411
4810
  `, host: {
4412
4811
  '[class.kf-section-header--sm]': "size() === 'sm'",
4413
- }, styles: [":host{display:flex;align-items:center;gap:var(--kf-space-sm);font-family:var(--kf-font-body)}:host(.kf-section-header--sm){gap:var(--kf-space-xs)}.kf-section-header__icon{display:inline-flex;align-items:center;justify-content:center;flex:none;inline-size:2.5rem;block-size:2.5rem;border-radius:var(--kf-radius-sm);background:var(--kf-color-primary-soft);color:var(--kf-color-primary)}:host(.kf-section-header--sm) .kf-section-header__icon{inline-size:1.75rem;block-size:1.75rem;border-radius:var(--kf-radius-xs)}.kf-section-header__icon--solid{background:var(--kf-color-primary);color:var(--kf-color-white)}.kf-section-header__text{display:flex;flex-direction:column;min-width:0;flex:1 1 auto}.kf-section-header__title{font-family:var(--kf-font-title);font-size:1rem;font-weight:700;color:var(--kf-color-ink);line-height:1.3}:host(.kf-section-header--sm) .kf-section-header__title{font-size:.875rem}.kf-section-header__subtitle{font-size:.8125rem;color:var(--kf-color-text-muted);line-height:1.3}:host(.kf-section-header--sm) .kf-section-header__subtitle{font-size:.75rem}.kf-section-header__actions{flex:none;display:flex;align-items:center;gap:var(--kf-space-sm)}\n"] }]
4812
+ '[class.kf-section-header--lg]': "size() === 'lg'",
4813
+ }, styles: [":host{display:flex;align-items:center;gap:var(--kf-space-sm);font-family:var(--kf-font-body)}:host(.kf-section-header--sm){gap:var(--kf-space-xs)}:host(.kf-section-header--lg){gap:var(--kf-space-md)}.kf-section-header__icon{display:inline-flex;align-items:center;justify-content:center;flex:none;inline-size:2.5rem;block-size:2.5rem;border-radius:var(--kf-radius-sm);background:var(--kf-color-primary-soft);color:var(--kf-color-primary)}:host(.kf-section-header--sm) .kf-section-header__icon{inline-size:1.75rem;block-size:1.75rem;border-radius:var(--kf-radius-xs)}:host(.kf-section-header--lg) .kf-section-header__icon{inline-size:3.25rem;block-size:3.25rem;border-radius:var(--kf-radius-md)}.kf-section-header__icon--solid{background:var(--kf-color-primary);color:var(--kf-color-white)}.kf-section-header__text{display:flex;flex-direction:column;min-width:0;flex:1 1 auto}.kf-section-header__title{font-family:var(--kf-font-title);font-size:1rem;font-weight:700;color:var(--kf-color-ink);line-height:1.3}:host(.kf-section-header--sm) .kf-section-header__title{font-size:.875rem}:host(.kf-section-header--lg) .kf-section-header__title{font-size:1.25rem}.kf-section-header__subtitle{font-size:.8125rem;color:var(--kf-color-text-muted);line-height:1.3}:host(.kf-section-header--sm) .kf-section-header__subtitle{font-size:.75rem}:host(.kf-section-header--lg) .kf-section-header__subtitle{font-size:.9375rem}.kf-section-header__actions{flex:none;display:flex;align-items:center;gap:var(--kf-space-sm)}\n"] }]
4414
4814
  }], propDecorators: { icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], subtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "subtitle", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], solid: [{ type: i0.Input, args: [{ isSignal: true, alias: "solid", required: false }] }] } });
4415
4815
 
4416
4816
  /**
@@ -4484,184 +4884,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
4484
4884
  `, styles: [":host{display:inline-block}.kf-segmented__track{position:relative;display:grid;grid-template-columns:repeat(var(--kf-segmented-count),1fr);gap:2px;padding:2px;background:var(--kf-color-surface);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md)}.kf-segmented__thumb{position:absolute;top:2px;left:2px;width:calc((100% - 4px) / var(--kf-segmented-count));height:calc(100% - 4px);background:var(--kf-color-white);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-xs);transform:translate(calc(var(--kf-segmented-index, 0) * 100%));transition:transform .2s ease}.kf-segmented__option{position:relative;z-index:1;font:inherit;padding:var(--kf-space-xs) var(--kf-space-md);border:none;background:transparent;color:var(--kf-color-text-muted);border-radius:var(--kf-radius-sm);cursor:pointer;transition:color .14s}.kf-segmented__option:hover:not(.kf-segmented__option--active){color:var(--kf-color-text)}.kf-segmented__option--active{color:var(--kf-color-ink)}.kf-segmented__option:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}\n"] }]
4485
4885
  }], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }] } });
4486
4886
 
4487
- let nextId = 0;
4488
- /**
4489
- * Dropdown de selección simple pero con panel propio (no usa `<select>` nativo),
4490
- * para cuando se necesita más control visual que un `<select>` estándar permite.
4491
- *
4492
- * El panel flotante se implementa con `position: absolute` sobre el host
4493
- * (sin Angular CDK Overlay, que no es dependencia del proyecto). Se abre y
4494
- * cierra por click en el disparador, y se cierra al hacer click afuera o al
4495
- * presionar Escape.
4496
- *
4497
- * <kf-select-menu
4498
- * label="País"
4499
- * [options]="countries"
4500
- * [value]="selectedCountry()"
4501
- * (valueChange)="selectedCountry.set($event)"
4502
- * />
4503
- *
4504
- * Pendiente: el prop `icon` de otros componentes del design system requiere
4505
- * un componente Icon / sprite SVG que esta librería todavía no tiene — este
4506
- * componente no usa iconos, así que no aplica acá.
4507
- */
4508
- class SelectMenu {
4509
- label = input(/* @ts-ignore */
4510
- ...(ngDevMode ? [undefined, { debugName: "label" }] : /* istanbul ignore next */ []));
4511
- options = input([], /* @ts-ignore */
4512
- ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
4513
- value = input(/* @ts-ignore */
4514
- ...(ngDevMode ? [undefined, { debugName: "value" }] : /* istanbul ignore next */ []));
4515
- placeholder = input('Seleccionar', /* @ts-ignore */
4516
- ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
4517
- disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
4518
- valueChange = output();
4519
- instanceId = `kf-select-menu-${nextId++}`;
4520
- triggerId = `${this.instanceId}-trigger`;
4521
- listboxId = `${this.instanceId}-listbox`;
4522
- isOpen = signal(false, /* @ts-ignore */
4523
- ...(ngDevMode ? [{ debugName: "isOpen" }] : /* istanbul ignore next */ []));
4524
- selectedOption = computed(() => this.options().find((option) => option.value === this.value()), /* @ts-ignore */
4525
- ...(ngDevMode ? [{ debugName: "selectedOption" }] : /* istanbul ignore next */ []));
4526
- host = inject((ElementRef));
4527
- toggle() {
4528
- if (this.disabled()) {
4529
- return;
4530
- }
4531
- this.isOpen.update((open) => !open);
4532
- }
4533
- select(option) {
4534
- this.isOpen.set(false);
4535
- if (option.value !== this.value()) {
4536
- this.valueChange.emit(option.value);
4537
- }
4538
- }
4539
- onDocumentClick(event) {
4540
- if (this.isOpen() && !this.host.nativeElement.contains(event.target)) {
4541
- this.isOpen.set(false);
4542
- }
4543
- }
4544
- onEscape() {
4545
- this.isOpen.set(false);
4546
- }
4547
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: SelectMenu, deps: [], target: i0.ɵɵFactoryTarget.Component });
4548
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: SelectMenu, isStandalone: true, selector: "kf-select-menu", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, host: { listeners: { "document:click": "onDocumentClick($event)", "document:keydown.escape": "onEscape()" } }, ngImport: i0, template: `
4549
- @if (label()) {
4550
- <kf-field-label [for]="triggerId">{{ label() }}</kf-field-label>
4551
- }
4552
- <button
4553
- #trigger
4554
- type="button"
4555
- [id]="triggerId"
4556
- class="kf-select-menu__trigger"
4557
- [disabled]="disabled()"
4558
- [attr.aria-haspopup]="'listbox'"
4559
- [attr.aria-expanded]="isOpen()"
4560
- [attr.aria-controls]="isOpen() ? listboxId : null"
4561
- (click)="toggle()"
4562
- >
4563
- <span
4564
- class="kf-select-menu__value"
4565
- [class.kf-select-menu__value--placeholder]="!selectedOption()"
4566
- >
4567
- {{ selectedOption()?.label ?? placeholder() }}
4568
- </span>
4569
- <span
4570
- class="kf-select-menu__caret"
4571
- [class.kf-select-menu__caret--open]="isOpen()"
4572
- aria-hidden="true"
4573
- ></span>
4574
- </button>
4575
- @if (isOpen()) {
4576
- <ul
4577
- class="kf-select-menu__panel"
4578
- [id]="listboxId"
4579
- role="listbox"
4580
- [attr.aria-labelledby]="triggerId"
4581
- >
4582
- @for (option of options(); track option.value) {
4583
- <li
4584
- class="kf-select-menu__option"
4585
- [class.kf-select-menu__option--selected]="option.value === value()"
4586
- role="option"
4587
- tabindex="0"
4588
- [attr.aria-selected]="option.value === value()"
4589
- (click)="select(option)"
4590
- (keydown.enter)="select(option)"
4591
- (keydown.space)="$event.preventDefault(); select(option)"
4592
- >
4593
- {{ option.label }}
4594
- </li>
4595
- } @empty {
4596
- <li class="kf-select-menu__empty">Sin opciones disponibles</li>
4597
- }
4598
- </ul>
4599
- }
4600
- `, isInline: true, styles: [":host{display:inline-flex;flex-direction:column;gap:var(--kf-space-xs);position:relative;font:inherit;min-width:200px}.kf-select-menu__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);width:100%;font:inherit;font-family:var(--kf-font-body);padding:var(--kf-space-sm) var(--kf-space-md);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);background:var(--kf-color-white);color:var(--kf-color-text);cursor:pointer;transition:border-color .14s,box-shadow .14s}.kf-select-menu__trigger:hover:not(:disabled){border-color:var(--kf-color-border-strong)}.kf-select-menu__trigger:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-select-menu__trigger:disabled{opacity:.5;cursor:not-allowed}.kf-select-menu__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kf-select-menu__value--placeholder{color:var(--kf-color-text-muted)}.kf-select-menu__caret{flex-shrink:0;width:8px;height:8px;border-right:2px solid var(--kf-color-text-muted);border-bottom:2px solid var(--kf-color-text-muted);transform:rotate(45deg);transition:transform .14s;margin-top:-4px}.kf-select-menu__caret--open{transform:rotate(-135deg);margin-top:4px}.kf-select-menu__panel{position:absolute;top:100%;left:0;right:0;z-index:10;margin:var(--kf-space-xs) 0 0;padding:var(--kf-space-xs) 0;list-style:none;max-height:240px;overflow-y:auto;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-md)}.kf-select-menu__option{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-select-menu__option:hover{background:var(--kf-color-primary-soft)}.kf-select-menu__option--selected{background:var(--kf-color-primary-soft);color:var(--kf-color-primary);font-weight:600}.kf-select-menu__empty{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text-muted)}\n"], dependencies: [{ kind: "component", type: FieldLabel, selector: "kf-field-label", inputs: ["for", "required"] }] });
4601
- }
4602
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: SelectMenu, decorators: [{
4603
- type: Component,
4604
- args: [{ selector: 'kf-select-menu', imports: [FieldLabel], template: `
4605
- @if (label()) {
4606
- <kf-field-label [for]="triggerId">{{ label() }}</kf-field-label>
4607
- }
4608
- <button
4609
- #trigger
4610
- type="button"
4611
- [id]="triggerId"
4612
- class="kf-select-menu__trigger"
4613
- [disabled]="disabled()"
4614
- [attr.aria-haspopup]="'listbox'"
4615
- [attr.aria-expanded]="isOpen()"
4616
- [attr.aria-controls]="isOpen() ? listboxId : null"
4617
- (click)="toggle()"
4618
- >
4619
- <span
4620
- class="kf-select-menu__value"
4621
- [class.kf-select-menu__value--placeholder]="!selectedOption()"
4622
- >
4623
- {{ selectedOption()?.label ?? placeholder() }}
4624
- </span>
4625
- <span
4626
- class="kf-select-menu__caret"
4627
- [class.kf-select-menu__caret--open]="isOpen()"
4628
- aria-hidden="true"
4629
- ></span>
4630
- </button>
4631
- @if (isOpen()) {
4632
- <ul
4633
- class="kf-select-menu__panel"
4634
- [id]="listboxId"
4635
- role="listbox"
4636
- [attr.aria-labelledby]="triggerId"
4637
- >
4638
- @for (option of options(); track option.value) {
4639
- <li
4640
- class="kf-select-menu__option"
4641
- [class.kf-select-menu__option--selected]="option.value === value()"
4642
- role="option"
4643
- tabindex="0"
4644
- [attr.aria-selected]="option.value === value()"
4645
- (click)="select(option)"
4646
- (keydown.enter)="select(option)"
4647
- (keydown.space)="$event.preventDefault(); select(option)"
4648
- >
4649
- {{ option.label }}
4650
- </li>
4651
- } @empty {
4652
- <li class="kf-select-menu__empty">Sin opciones disponibles</li>
4653
- }
4654
- </ul>
4655
- }
4656
- `, styles: [":host{display:inline-flex;flex-direction:column;gap:var(--kf-space-xs);position:relative;font:inherit;min-width:200px}.kf-select-menu__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);width:100%;font:inherit;font-family:var(--kf-font-body);padding:var(--kf-space-sm) var(--kf-space-md);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);background:var(--kf-color-white);color:var(--kf-color-text);cursor:pointer;transition:border-color .14s,box-shadow .14s}.kf-select-menu__trigger:hover:not(:disabled){border-color:var(--kf-color-border-strong)}.kf-select-menu__trigger:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-select-menu__trigger:disabled{opacity:.5;cursor:not-allowed}.kf-select-menu__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kf-select-menu__value--placeholder{color:var(--kf-color-text-muted)}.kf-select-menu__caret{flex-shrink:0;width:8px;height:8px;border-right:2px solid var(--kf-color-text-muted);border-bottom:2px solid var(--kf-color-text-muted);transform:rotate(45deg);transition:transform .14s;margin-top:-4px}.kf-select-menu__caret--open{transform:rotate(-135deg);margin-top:4px}.kf-select-menu__panel{position:absolute;top:100%;left:0;right:0;z-index:10;margin:var(--kf-space-xs) 0 0;padding:var(--kf-space-xs) 0;list-style:none;max-height:240px;overflow-y:auto;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-md)}.kf-select-menu__option{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-select-menu__option:hover{background:var(--kf-color-primary-soft)}.kf-select-menu__option--selected{background:var(--kf-color-primary-soft);color:var(--kf-color-primary);font-weight:600}.kf-select-menu__empty{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text-muted)}\n"] }]
4657
- }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], onDocumentClick: [{
4658
- type: HostListener,
4659
- args: ['document:click', ['$event']]
4660
- }], onEscape: [{
4661
- type: HostListener,
4662
- args: ['document:keydown.escape']
4663
- }] } });
4664
-
4665
4887
  /**
4666
4888
  * Indicador numérico compacto (página 34): label + valor grande, con un
4667
4889
  * ícono a la derecha del label — más chico y sin badge de esquina que
@@ -4930,6 +5152,109 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
4930
5152
  `, styles: [":host{display:block;font-family:var(--kf-font-body)}.kf-tabs{position:relative;display:flex;align-items:stretch;gap:var(--kf-space-md);border-bottom:1px solid var(--kf-color-border)}.kf-tabs__tab{display:inline-flex;align-items:center;gap:var(--kf-space-xs);font:inherit;font-size:.9rem;color:var(--kf-color-text-muted);background:transparent;border:none;padding:var(--kf-space-sm) 0;cursor:pointer;transition:color .14s}.kf-tabs__tab:hover:not(.kf-tabs__tab--active){color:var(--kf-color-text)}.kf-tabs__tab:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus);border-radius:var(--kf-radius-xs)}.kf-tabs__tab--active{color:var(--kf-color-primary);font-size:1rem;font-weight:700}.kf-tabs__indicator{position:absolute;bottom:-1px;left:0;height:2px;background:var(--kf-color-primary);border-radius:2px 2px 0 0;pointer-events:none;transition:transform .28s cubic-bezier(.32,.72,0,1),width .28s cubic-bezier(.32,.72,0,1),opacity .14s}@media(prefers-reduced-motion:reduce){.kf-tabs__indicator{transition:opacity .14s}}\n"] }]
4931
5153
  }], ctorParameters: () => [], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], track: [{ type: i0.ViewChild, args: ['track', { isSignal: true }] }] } });
4932
5154
 
5155
+ /**
5156
+ * Tipografía unificada — separa la etiqueta semántica (`as`, qué es el
5157
+ * texto para el DOM/accesibilidad) del estilo visual (`variant`, cómo se
5158
+ * ve). Antes de este componente, cada pantalla que consume la librería
5159
+ * definía su propio `h1`/`p` con `font-size` suelto para el mismo rol
5160
+ * (título de página, subtítulo, texto secundario) — auditoría sobre
5161
+ * `web-portal-latam-frontend` encontró 9 tamaños distintos entre login y
5162
+ * home sin ningún paso de escala en común. `variant` resuelve eso contra
5163
+ * la escala de `tokens/base.css` (`--kf-font-size-*`).
5164
+ *
5165
+ * `tone` sigue la misma convención cerrada que `Alert`/`Badge`/`Chip`: sin
5166
+ * escape hatch de color libre. Si un caso necesita un color que `tone` no
5167
+ * cubre, es una discusión de design system, no una prop nueva acá.
5168
+ *
5169
+ * <kf-text as="h1" variant="title-lg">Bienvenido de nuevo</kf-text>
5170
+ * <kf-text as="p" variant="body-muted">Ingresá con tu cuenta corporativa.</kf-text>
5171
+ * <kf-text as="span" variant="caption" tone="error">Campo obligatorio</kf-text>
5172
+ */
5173
+ class Text {
5174
+ /** Etiqueta semántica real que se renderiza — nunca es solo un `div`. */
5175
+ as = input('p', /* @ts-ignore */
5176
+ ...(ngDevMode ? [{ debugName: "as" }] : /* istanbul ignore next */ []));
5177
+ variant = input('body', /* @ts-ignore */
5178
+ ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
5179
+ tone = input('neutral', /* @ts-ignore */
5180
+ ...(ngDevMode ? [{ debugName: "tone" }] : /* istanbul ignore next */ []));
5181
+ /**
5182
+ * Copia de `as` para uso dentro del template: `as` es palabra reservada
5183
+ * en las expresiones de Angular (`@if (x; as y)`), así que `@switch (as())`
5184
+ * no parsea — NG5002. La prop pública sigue llamándose `as` (es solo un
5185
+ * atributo HTML ahí, `<kf-text as="h1">`, no una expresión).
5186
+ */
5187
+ tag = computed(() => this.as(), /* @ts-ignore */
5188
+ ...(ngDevMode ? [{ debugName: "tag" }] : /* istanbul ignore next */ []));
5189
+ hostClass() {
5190
+ const classes = [`kf-text--${this.variant()}`];
5191
+ if (this.tone() !== 'neutral') {
5192
+ classes.push(`kf-text--${this.tone()}`);
5193
+ }
5194
+ return classes.join(' ');
5195
+ }
5196
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Text, deps: [], target: i0.ɵɵFactoryTarget.Component });
5197
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Text, isStandalone: true, selector: "kf-text", inputs: { as: { classPropertyName: "as", publicName: "as", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, tone: { classPropertyName: "tone", publicName: "tone", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
5198
+ <!--
5199
+ Una sola proyección compartida (ng-template + ngTemplateOutlet) en vez
5200
+ de un <ng-content> por cada rama del @switch: Angular solo conecta la
5201
+ proyección real del consumidor a la PRIMERA instancia de <ng-content>
5202
+ que encuentra en el árbol de compilación — con un <ng-content> por
5203
+ rama, todas menos una quedan vacías en runtime (bug detectado por
5204
+ Text.spec.ts, no algo documentado a simple vista en los docs de Angular).
5205
+ -->
5206
+ <ng-template #projected><ng-content /></ng-template>
5207
+ @switch (tag()) {
5208
+ @case ('h1') {
5209
+ <h1 [class]="hostClass()"><ng-container [ngTemplateOutlet]="projected" /></h1>
5210
+ }
5211
+ @case ('h2') {
5212
+ <h2 [class]="hostClass()"><ng-container [ngTemplateOutlet]="projected" /></h2>
5213
+ }
5214
+ @case ('h3') {
5215
+ <h3 [class]="hostClass()"><ng-container [ngTemplateOutlet]="projected" /></h3>
5216
+ }
5217
+ @case ('span') {
5218
+ <span [class]="hostClass()"><ng-container [ngTemplateOutlet]="projected" /></span>
5219
+ }
5220
+ @default {
5221
+ <p [class]="hostClass()"><ng-container [ngTemplateOutlet]="projected" /></p>
5222
+ }
5223
+ }
5224
+ `, isInline: true, styles: [":host{display:contents}h1,h2,h3,p,span{margin:0;font-family:var(--kf-font-body);color:var(--kf-color-text)}.kf-text--title-lg{font-family:var(--kf-font-title);font-size:var(--kf-font-size-title-lg);font-weight:var(--kf-font-weight-title);color:var(--kf-color-ink)}.kf-text--title-md{font-family:var(--kf-font-title);font-size:var(--kf-font-size-title-md);font-weight:var(--kf-font-weight-title);color:var(--kf-color-ink)}.kf-text--title-sm{font-family:var(--kf-font-title);font-size:var(--kf-font-size-title-sm);font-weight:var(--kf-font-weight-title);color:var(--kf-color-ink)}.kf-text--body{font-size:var(--kf-font-size-body);font-weight:var(--kf-font-weight-body)}.kf-text--body-muted{font-size:var(--kf-font-size-body);font-weight:var(--kf-font-weight-body);color:var(--kf-color-text-muted)}.kf-text--caption{font-size:var(--kf-font-size-caption);font-weight:var(--kf-font-weight-body);color:var(--kf-color-text-muted)}.kf-text--ok{color:var(--kf-color-success-strong)}.kf-text--warn{color:var(--kf-color-warning-strong)}.kf-text--error{color:var(--kf-color-error-strong)}.kf-text--info{color:var(--kf-color-info-strong)}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }] });
5225
+ }
5226
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Text, decorators: [{
5227
+ type: Component,
5228
+ args: [{ selector: 'kf-text', imports: [NgTemplateOutlet], template: `
5229
+ <!--
5230
+ Una sola proyección compartida (ng-template + ngTemplateOutlet) en vez
5231
+ de un <ng-content> por cada rama del @switch: Angular solo conecta la
5232
+ proyección real del consumidor a la PRIMERA instancia de <ng-content>
5233
+ que encuentra en el árbol de compilación — con un <ng-content> por
5234
+ rama, todas menos una quedan vacías en runtime (bug detectado por
5235
+ Text.spec.ts, no algo documentado a simple vista en los docs de Angular).
5236
+ -->
5237
+ <ng-template #projected><ng-content /></ng-template>
5238
+ @switch (tag()) {
5239
+ @case ('h1') {
5240
+ <h1 [class]="hostClass()"><ng-container [ngTemplateOutlet]="projected" /></h1>
5241
+ }
5242
+ @case ('h2') {
5243
+ <h2 [class]="hostClass()"><ng-container [ngTemplateOutlet]="projected" /></h2>
5244
+ }
5245
+ @case ('h3') {
5246
+ <h3 [class]="hostClass()"><ng-container [ngTemplateOutlet]="projected" /></h3>
5247
+ }
5248
+ @case ('span') {
5249
+ <span [class]="hostClass()"><ng-container [ngTemplateOutlet]="projected" /></span>
5250
+ }
5251
+ @default {
5252
+ <p [class]="hostClass()"><ng-container [ngTemplateOutlet]="projected" /></p>
5253
+ }
5254
+ }
5255
+ `, styles: [":host{display:contents}h1,h2,h3,p,span{margin:0;font-family:var(--kf-font-body);color:var(--kf-color-text)}.kf-text--title-lg{font-family:var(--kf-font-title);font-size:var(--kf-font-size-title-lg);font-weight:var(--kf-font-weight-title);color:var(--kf-color-ink)}.kf-text--title-md{font-family:var(--kf-font-title);font-size:var(--kf-font-size-title-md);font-weight:var(--kf-font-weight-title);color:var(--kf-color-ink)}.kf-text--title-sm{font-family:var(--kf-font-title);font-size:var(--kf-font-size-title-sm);font-weight:var(--kf-font-weight-title);color:var(--kf-color-ink)}.kf-text--body{font-size:var(--kf-font-size-body);font-weight:var(--kf-font-weight-body)}.kf-text--body-muted{font-size:var(--kf-font-size-body);font-weight:var(--kf-font-weight-body);color:var(--kf-color-text-muted)}.kf-text--caption{font-size:var(--kf-font-size-caption);font-weight:var(--kf-font-weight-body);color:var(--kf-color-text-muted)}.kf-text--ok{color:var(--kf-color-success-strong)}.kf-text--warn{color:var(--kf-color-warning-strong)}.kf-text--error{color:var(--kf-color-error-strong)}.kf-text--info{color:var(--kf-color-info-strong)}\n"] }]
5256
+ }], propDecorators: { as: [{ type: i0.Input, args: [{ isSignal: true, alias: "as", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "tone", required: false }] }] } });
5257
+
4933
5258
  /** Fixed icon per tone (page 20 of the design system) — not configurable per call. */
4934
5259
  const TOAST_ICON = {
4935
5260
  success: 'check-circle',
@@ -5279,5 +5604,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
5279
5604
  * Generated bundle index. Do not edit.
5280
5605
  */
5281
5606
 
5282
- 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 };
5607
+ 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 };
5283
5608
  //# sourceMappingURL=kaufmann-lab-ui.mjs.map