@kaufmann-lab/ui 0.1.14 → 0.1.15

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,12 +1,12 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, booleanAttribute, inject, computed, effect, Component, model, output, Directive, contentChild, numberAttribute, contentChildren, signal, DestroyRef, viewChild, forwardRef, ViewContainerRef, afterRenderEffect, HostListener, afterNextRender, TemplateRef, ElementRef, ChangeDetectionStrategy, InjectionToken, Injector, Input as Input$1, linkedSignal, EnvironmentInjector, createComponent, ViewEncapsulation, viewChildren, Injectable } from '@angular/core';
2
+ import { input, booleanAttribute, inject, computed, effect, Component, model, output, Directive, contentChild, numberAttribute, contentChildren, signal, DestroyRef, viewChild, forwardRef, ViewContainerRef, afterRenderEffect, HostListener, TemplateRef, ElementRef, afterNextRender, ChangeDetectionStrategy, InjectionToken, Injector, Input as Input$1, linkedSignal, EnvironmentInjector, createComponent, ViewEncapsulation, viewChildren, Injectable } from '@angular/core';
3
3
  import { DOCUMENT, NgTemplateOutlet } from '@angular/common';
4
4
  import * as i1 from '@angular/forms';
5
5
  import { NG_VALUE_ACCESSOR, FormsModule, NgControl } from '@angular/forms';
6
6
  import { Overlay, OverlayContainer } from '@angular/cdk/overlay';
7
7
  import { TemplatePortal, ComponentPortal } from '@angular/cdk/portal';
8
- import { FocusTrapFactory } from '@angular/cdk/a11y';
9
8
  import { Subscription } from 'rxjs';
9
+ import { FocusTrapFactory } from '@angular/cdk/a11y';
10
10
  import mapboxgl from 'mapbox-gl';
11
11
 
12
12
  /** Símbolo de trazo "activity" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
@@ -395,6 +395,15 @@ const logOut = {
395
395
  `,
396
396
  };
397
397
 
398
+ /** Símbolo de trazo "mail" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
399
+ const mail = {
400
+ viewBox: '0 0 24 24',
401
+ markup: `
402
+ <rect x="2" y="4" width="20" height="16" rx="2" />
403
+ <path d="m3 6 9 6 9-6" />
404
+ `,
405
+ };
406
+
398
407
  /** Símbolo de trazo "map" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
399
408
  const map = {
400
409
  viewBox: '0 0 24 24',
@@ -750,6 +759,7 @@ const ICON_DEFINITIONS = {
750
759
  layers: layers,
751
760
  list: list,
752
761
  'log-out': logOut,
762
+ mail: mail,
753
763
  map: map,
754
764
  'map-pin': mapPin,
755
765
  maximize: maximize,
@@ -1241,7 +1251,7 @@ class Divider {
1241
1251
  orientation = input('horizontal', /* @ts-ignore */
1242
1252
  ...(ngDevMode ? [{ debugName: "orientation" }] : /* istanbul ignore next */ []));
1243
1253
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Divider, deps: [], target: i0.ɵɵFactoryTarget.Component });
1244
- 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;border:none;background:var(--kf-color-border)}:host,:host([orientation=\"horizontal\"]){flex:1 1 0;width:100%;height:1px}:host([orientation=\"vertical\"]){flex:none;align-self:stretch;width:1px;height:auto}\n"] });
1254
+ 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;border:none;background:var(--kf-color-border)}:host,:host([orientation=\"horizontal\"]){flex:none;width:100%;height:1px}:host([orientation=\"vertical\"]){flex:none;align-self:stretch;width:1px;height:auto}\n"] });
1245
1255
  }
1246
1256
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Divider, decorators: [{
1247
1257
  type: Component,
@@ -1249,7 +1259,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
1249
1259
  role: 'separator',
1250
1260
  '[attr.orientation]': 'orientation()',
1251
1261
  '[attr.aria-orientation]': 'orientation()',
1252
- }, styles: [":host{display:block;border:none;background:var(--kf-color-border)}:host,:host([orientation=\"horizontal\"]){flex:1 1 0;width:100%;height:1px}:host([orientation=\"vertical\"]){flex:none;align-self:stretch;width:1px;height:auto}\n"] }]
1262
+ }, styles: [":host{display:block;border:none;background:var(--kf-color-border)}:host,:host([orientation=\"horizontal\"]){flex:none;width:100%;height:1px}:host([orientation=\"vertical\"]){flex:none;align-self:stretch;width:1px;height:auto}\n"] }]
1253
1263
  }], propDecorators: { orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }] } });
1254
1264
 
1255
1265
  /**
@@ -1372,13 +1382,13 @@ class AuthLayout {
1372
1382
  ssoContent = contentChild(AuthLayoutSso, /* @ts-ignore */
1373
1383
  ...(ngDevMode ? [{ debugName: "ssoContent" }] : /* istanbul ignore next */ []));
1374
1384
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: AuthLayout, deps: [], target: i0.ɵɵFactoryTarget.Component });
1375
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: AuthLayout, isStandalone: true, selector: "kf-auth-layout", inputs: { brand: { classPropertyName: "brand", publicName: "brand", isSignal: true, isRequired: true, transformFunction: null }, brandLogoSrc: { classPropertyName: "brandLogoSrc", publicName: "brandLogoSrc", isSignal: true, isRequired: false, transformFunction: null }, brandTagline: { classPropertyName: "brandTagline", publicName: "brandTagline", isSignal: true, isRequired: false, transformFunction: null }, brandLogoMobileSrc: { classPropertyName: "brandLogoMobileSrc", publicName: "brandLogoMobileSrc", isSignal: true, isRequired: false, transformFunction: null }, backgroundVariant: { classPropertyName: "backgroundVariant", publicName: "backgroundVariant", isSignal: true, isRequired: false, transformFunction: null }, card: { classPropertyName: "card", publicName: "card", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, subtitle: { classPropertyName: "subtitle", publicName: "subtitle", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-background": "backgroundVariant()" } }, queries: [{ propertyName: "ssoContent", first: true, predicate: AuthLayoutSso, descendants: true, isSignal: true }], ngImport: i0, template: "<aside class=\"kf-auth-layout__brand\">\n @if (brandLogoSrc()) {\n <img class=\"kf-auth-layout__logo\" [src]=\"brandLogoSrc()\" [alt]=\"brand()\" />\n } @else {\n <span class=\"kf-auth-layout__wordmark\">{{ brand() }}</span>\n }\n @if (brandTagline()) {\n <p class=\"kf-auth-layout__tagline\">{{ brandTagline() }}</p>\n }\n</aside>\n\n<main class=\"kf-auth-layout__content\">\n <div class=\"kf-auth-layout__panel\" [class.kf-auth-layout__panel--card]=\"card()\">\n <div class=\"kf-auth-layout__mobile-brand\">\n @if (brandLogoMobileSrc() || brandLogoSrc()) {\n <img\n class=\"kf-auth-layout__logo kf-auth-layout__logo--mobile\"\n [src]=\"brandLogoMobileSrc() || brandLogoSrc()\"\n [alt]=\"brand()\"\n />\n } @else {\n <span class=\"kf-auth-layout__wordmark kf-auth-layout__wordmark--mobile\">{{ brand() }}</span>\n }\n </div>\n\n @if (title(); as headerTitle) {\n <div class=\"kf-auth-layout__header\">\n <kf-text as=\"h1\" variant=\"title-lg\">{{ headerTitle }}</kf-text>\n @if (subtitle(); as headerSubtitle) {\n <kf-text as=\"p\" variant=\"body-muted\">{{ headerSubtitle }}</kf-text>\n }\n </div>\n }\n\n <div class=\"kf-auth-layout__sso\">\n <ng-content select=\"[kfAuthLayoutSso]\" />\n </div>\n\n @if (ssoContent()) {\n <div class=\"kf-auth-layout__divider\">\n <kf-divider />\n <kf-text as=\"span\" variant=\"caption\" class=\"kf-auth-layout__divider-label\">o</kf-text>\n <kf-divider />\n </div>\n }\n\n <div class=\"kf-auth-layout__body\">\n <ng-content />\n </div>\n </div>\n</main>\n", styles: ["@charset \"UTF-8\";:host{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.15fr);min-height:100dvh}.kf-auth-layout__brand{display:flex;flex-direction:column;justify-content:center;gap:var(--kf-space-md);padding:var(--kf-space-xl);background:linear-gradient(190deg,var(--kf-color-primary-active) 0%,var(--kf-color-tertiary-deep) 100%);color:var(--kf-color-white)}.kf-auth-layout__logo{height:1.75rem;width:fit-content}.kf-auth-layout__wordmark{font-family:var(--kf-font-title);font-weight:var(--kf-font-weight-title);font-size:var(--kf-font-size-title-lg);letter-spacing:-.01em}.kf-auth-layout__tagline{max-width:34ch;margin:0;font-family:var(--kf-font-body);font-size:var(--kf-font-size-body);color:#ffffffb8}.kf-auth-layout__content{display:flex;align-items:center;justify-content:center;padding:var(--kf-space-xl);background:var(--kf-color-surface)}:host([data-background=gradient]) .kf-auth-layout__content{background:linear-gradient(165deg,color-mix(in srgb,var(--kf-color-primary) 6%,var(--kf-color-surface)) 0%,var(--kf-color-surface) 55%,color-mix(in srgb,var(--kf-color-primary) 10%,var(--kf-color-surface)) 100%)}:host([data-background=pattern]) .kf-auth-layout__content{background-color:var(--kf-color-surface);background-image:radial-gradient(circle at 1px 1px,color-mix(in srgb,var(--kf-color-primary) 16%,transparent) 1px,transparent 0),linear-gradient(165deg,color-mix(in srgb,var(--kf-color-primary) 6%,var(--kf-color-surface)) 0%,var(--kf-color-surface) 55%,color-mix(in srgb,var(--kf-color-primary) 10%,var(--kf-color-surface)) 100%);background-size:18px 18px,100% 100%}.kf-auth-layout__panel{width:100%;max-width:360px;display:flex;flex-direction:column;gap:var(--kf-space-xl)}.kf-auth-layout__header{display:flex;flex-direction:column;gap:var(--kf-space-xs)}.kf-auth-layout__sso,.kf-auth-layout__body{display:flex;flex-direction:column;gap:var(--kf-space-sm)}.kf-auth-layout__panel--card{padding:var(--kf-space-xl);background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md);box-shadow:var(--kf-shadow-md)}.kf-auth-layout__divider{display:flex;align-items:center;gap:var(--kf-space-sm)}.kf-auth-layout__divider-label{flex:none}.kf-auth-layout__mobile-brand{display:none;text-align:center}.kf-auth-layout__logo--mobile{margin-inline:auto;height:1.5rem}.kf-auth-layout__wordmark--mobile{color:var(--kf-color-ink)}@media(max-width:900px){:host{grid-template-columns:1fr}.kf-auth-layout__brand{display:none}.kf-auth-layout__content{padding:var(--kf-space-lg) var(--kf-space-md)}.kf-auth-layout__mobile-brand{display:block;margin-bottom:var(--kf-space-md)}}@media(max-width:480px){.kf-auth-layout__panel{max-width:none}}\n"], dependencies: [{ kind: "component", type: Divider, selector: "kf-divider", inputs: ["orientation"] }, { kind: "component", type: Text, selector: "kf-text", inputs: ["as", "variant", "tone"] }] });
1385
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: AuthLayout, isStandalone: true, selector: "kf-auth-layout", inputs: { brand: { classPropertyName: "brand", publicName: "brand", isSignal: true, isRequired: true, transformFunction: null }, brandLogoSrc: { classPropertyName: "brandLogoSrc", publicName: "brandLogoSrc", isSignal: true, isRequired: false, transformFunction: null }, brandTagline: { classPropertyName: "brandTagline", publicName: "brandTagline", isSignal: true, isRequired: false, transformFunction: null }, brandLogoMobileSrc: { classPropertyName: "brandLogoMobileSrc", publicName: "brandLogoMobileSrc", isSignal: true, isRequired: false, transformFunction: null }, backgroundVariant: { classPropertyName: "backgroundVariant", publicName: "backgroundVariant", isSignal: true, isRequired: false, transformFunction: null }, card: { classPropertyName: "card", publicName: "card", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, subtitle: { classPropertyName: "subtitle", publicName: "subtitle", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.data-background": "backgroundVariant()" } }, queries: [{ propertyName: "ssoContent", first: true, predicate: AuthLayoutSso, descendants: true, isSignal: true }], ngImport: i0, template: "<aside class=\"kf-auth-layout__brand\">\n @if (brandLogoSrc()) {\n <img class=\"kf-auth-layout__logo\" [src]=\"brandLogoSrc()\" [alt]=\"brand()\" />\n } @else {\n <span class=\"kf-auth-layout__wordmark\">{{ brand() }}</span>\n }\n @if (brandTagline()) {\n <p class=\"kf-auth-layout__tagline\">{{ brandTagline() }}</p>\n }\n</aside>\n\n<main class=\"kf-auth-layout__content\">\n <div class=\"kf-auth-layout__panel\" [class.kf-auth-layout__panel--card]=\"card()\">\n <div class=\"kf-auth-layout__mobile-brand\">\n @if (brandLogoMobileSrc() || brandLogoSrc()) {\n <img\n class=\"kf-auth-layout__logo kf-auth-layout__logo--mobile\"\n [src]=\"brandLogoMobileSrc() || brandLogoSrc()\"\n [alt]=\"brand()\"\n />\n } @else {\n <span class=\"kf-auth-layout__wordmark kf-auth-layout__wordmark--mobile\">{{ brand() }}</span>\n }\n </div>\n\n @if (title(); as headerTitle) {\n <div class=\"kf-auth-layout__header\">\n <kf-text as=\"h1\" variant=\"title-lg\">{{ headerTitle }}</kf-text>\n @if (subtitle(); as headerSubtitle) {\n <kf-text as=\"p\" variant=\"body-muted\">{{ headerSubtitle }}</kf-text>\n }\n </div>\n }\n\n <div class=\"kf-auth-layout__sso\">\n <ng-content select=\"[kfAuthLayoutSso]\" />\n </div>\n\n @if (ssoContent()) {\n <div class=\"kf-auth-layout__divider\">\n <kf-divider />\n <kf-text as=\"span\" variant=\"caption\" class=\"kf-auth-layout__divider-label\">o</kf-text>\n <kf-divider />\n </div>\n }\n\n <div class=\"kf-auth-layout__body\">\n <ng-content />\n </div>\n </div>\n</main>\n", styles: ["@charset \"UTF-8\";:host{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.15fr);min-height:100dvh}.kf-auth-layout__brand{display:flex;flex-direction:column;justify-content:center;gap:var(--kf-space-md);padding:var(--kf-space-xl);background:linear-gradient(190deg,var(--kf-color-primary-active) 0%,var(--kf-color-tertiary-deep) 100%);color:var(--kf-color-white)}.kf-auth-layout__logo{height:1.75rem;width:fit-content}.kf-auth-layout__wordmark{font-family:var(--kf-font-title);font-weight:var(--kf-font-weight-title);font-size:var(--kf-font-size-title-lg);letter-spacing:-.01em}.kf-auth-layout__tagline{max-width:34ch;margin:0;font-family:var(--kf-font-body);font-size:var(--kf-font-size-body);color:#ffffffb8}.kf-auth-layout__content{display:flex;align-items:center;justify-content:center;padding:var(--kf-space-xl);background:var(--kf-color-surface)}:host([data-background=gradient]) .kf-auth-layout__content{background:linear-gradient(165deg,color-mix(in srgb,var(--kf-color-primary) 6%,var(--kf-color-surface)) 0%,var(--kf-color-surface) 55%,color-mix(in srgb,var(--kf-color-primary) 10%,var(--kf-color-surface)) 100%)}:host([data-background=pattern]) .kf-auth-layout__content{background-color:var(--kf-color-surface);background-image:radial-gradient(circle at 1px 1px,color-mix(in srgb,var(--kf-color-primary) 16%,transparent) 1px,transparent 0),linear-gradient(165deg,color-mix(in srgb,var(--kf-color-primary) 6%,var(--kf-color-surface)) 0%,var(--kf-color-surface) 55%,color-mix(in srgb,var(--kf-color-primary) 10%,var(--kf-color-surface)) 100%);background-size:18px 18px,100% 100%}.kf-auth-layout__panel{width:100%;max-width:360px;display:flex;flex-direction:column;gap:var(--kf-space-xl)}.kf-auth-layout__header{display:flex;flex-direction:column;gap:var(--kf-space-xs)}.kf-auth-layout__sso,.kf-auth-layout__body{display:flex;flex-direction:column;gap:var(--kf-space-sm)}.kf-auth-layout__panel--card{padding:var(--kf-space-xl);background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md);box-shadow:var(--kf-shadow-md)}.kf-auth-layout__divider{display:flex;align-items:center;gap:var(--kf-space-sm)}.kf-auth-layout__divider kf-divider{flex:1 1 0}.kf-auth-layout__divider-label{flex:none}.kf-auth-layout__mobile-brand{display:none;text-align:center}.kf-auth-layout__logo--mobile{margin-inline:auto;height:1.5rem}.kf-auth-layout__wordmark--mobile{color:var(--kf-color-ink)}@media(max-width:900px){:host{grid-template-columns:1fr}.kf-auth-layout__brand{display:none}.kf-auth-layout__content{padding:var(--kf-space-lg) var(--kf-space-md)}.kf-auth-layout__mobile-brand{display:block;margin-bottom:var(--kf-space-md)}}@media(max-width:480px){.kf-auth-layout__panel{max-width:none}}\n"], dependencies: [{ kind: "component", type: Divider, selector: "kf-divider", inputs: ["orientation"] }, { kind: "component", type: Text, selector: "kf-text", inputs: ["as", "variant", "tone"] }] });
1376
1386
  }
1377
1387
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: AuthLayout, decorators: [{
1378
1388
  type: Component,
1379
1389
  args: [{ selector: 'kf-auth-layout', imports: [Divider, Text], host: {
1380
1390
  '[attr.data-background]': 'backgroundVariant()',
1381
- }, template: "<aside class=\"kf-auth-layout__brand\">\n @if (brandLogoSrc()) {\n <img class=\"kf-auth-layout__logo\" [src]=\"brandLogoSrc()\" [alt]=\"brand()\" />\n } @else {\n <span class=\"kf-auth-layout__wordmark\">{{ brand() }}</span>\n }\n @if (brandTagline()) {\n <p class=\"kf-auth-layout__tagline\">{{ brandTagline() }}</p>\n }\n</aside>\n\n<main class=\"kf-auth-layout__content\">\n <div class=\"kf-auth-layout__panel\" [class.kf-auth-layout__panel--card]=\"card()\">\n <div class=\"kf-auth-layout__mobile-brand\">\n @if (brandLogoMobileSrc() || brandLogoSrc()) {\n <img\n class=\"kf-auth-layout__logo kf-auth-layout__logo--mobile\"\n [src]=\"brandLogoMobileSrc() || brandLogoSrc()\"\n [alt]=\"brand()\"\n />\n } @else {\n <span class=\"kf-auth-layout__wordmark kf-auth-layout__wordmark--mobile\">{{ brand() }}</span>\n }\n </div>\n\n @if (title(); as headerTitle) {\n <div class=\"kf-auth-layout__header\">\n <kf-text as=\"h1\" variant=\"title-lg\">{{ headerTitle }}</kf-text>\n @if (subtitle(); as headerSubtitle) {\n <kf-text as=\"p\" variant=\"body-muted\">{{ headerSubtitle }}</kf-text>\n }\n </div>\n }\n\n <div class=\"kf-auth-layout__sso\">\n <ng-content select=\"[kfAuthLayoutSso]\" />\n </div>\n\n @if (ssoContent()) {\n <div class=\"kf-auth-layout__divider\">\n <kf-divider />\n <kf-text as=\"span\" variant=\"caption\" class=\"kf-auth-layout__divider-label\">o</kf-text>\n <kf-divider />\n </div>\n }\n\n <div class=\"kf-auth-layout__body\">\n <ng-content />\n </div>\n </div>\n</main>\n", styles: ["@charset \"UTF-8\";:host{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.15fr);min-height:100dvh}.kf-auth-layout__brand{display:flex;flex-direction:column;justify-content:center;gap:var(--kf-space-md);padding:var(--kf-space-xl);background:linear-gradient(190deg,var(--kf-color-primary-active) 0%,var(--kf-color-tertiary-deep) 100%);color:var(--kf-color-white)}.kf-auth-layout__logo{height:1.75rem;width:fit-content}.kf-auth-layout__wordmark{font-family:var(--kf-font-title);font-weight:var(--kf-font-weight-title);font-size:var(--kf-font-size-title-lg);letter-spacing:-.01em}.kf-auth-layout__tagline{max-width:34ch;margin:0;font-family:var(--kf-font-body);font-size:var(--kf-font-size-body);color:#ffffffb8}.kf-auth-layout__content{display:flex;align-items:center;justify-content:center;padding:var(--kf-space-xl);background:var(--kf-color-surface)}:host([data-background=gradient]) .kf-auth-layout__content{background:linear-gradient(165deg,color-mix(in srgb,var(--kf-color-primary) 6%,var(--kf-color-surface)) 0%,var(--kf-color-surface) 55%,color-mix(in srgb,var(--kf-color-primary) 10%,var(--kf-color-surface)) 100%)}:host([data-background=pattern]) .kf-auth-layout__content{background-color:var(--kf-color-surface);background-image:radial-gradient(circle at 1px 1px,color-mix(in srgb,var(--kf-color-primary) 16%,transparent) 1px,transparent 0),linear-gradient(165deg,color-mix(in srgb,var(--kf-color-primary) 6%,var(--kf-color-surface)) 0%,var(--kf-color-surface) 55%,color-mix(in srgb,var(--kf-color-primary) 10%,var(--kf-color-surface)) 100%);background-size:18px 18px,100% 100%}.kf-auth-layout__panel{width:100%;max-width:360px;display:flex;flex-direction:column;gap:var(--kf-space-xl)}.kf-auth-layout__header{display:flex;flex-direction:column;gap:var(--kf-space-xs)}.kf-auth-layout__sso,.kf-auth-layout__body{display:flex;flex-direction:column;gap:var(--kf-space-sm)}.kf-auth-layout__panel--card{padding:var(--kf-space-xl);background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md);box-shadow:var(--kf-shadow-md)}.kf-auth-layout__divider{display:flex;align-items:center;gap:var(--kf-space-sm)}.kf-auth-layout__divider-label{flex:none}.kf-auth-layout__mobile-brand{display:none;text-align:center}.kf-auth-layout__logo--mobile{margin-inline:auto;height:1.5rem}.kf-auth-layout__wordmark--mobile{color:var(--kf-color-ink)}@media(max-width:900px){:host{grid-template-columns:1fr}.kf-auth-layout__brand{display:none}.kf-auth-layout__content{padding:var(--kf-space-lg) var(--kf-space-md)}.kf-auth-layout__mobile-brand{display:block;margin-bottom:var(--kf-space-md)}}@media(max-width:480px){.kf-auth-layout__panel{max-width:none}}\n"] }]
1391
+ }, template: "<aside class=\"kf-auth-layout__brand\">\n @if (brandLogoSrc()) {\n <img class=\"kf-auth-layout__logo\" [src]=\"brandLogoSrc()\" [alt]=\"brand()\" />\n } @else {\n <span class=\"kf-auth-layout__wordmark\">{{ brand() }}</span>\n }\n @if (brandTagline()) {\n <p class=\"kf-auth-layout__tagline\">{{ brandTagline() }}</p>\n }\n</aside>\n\n<main class=\"kf-auth-layout__content\">\n <div class=\"kf-auth-layout__panel\" [class.kf-auth-layout__panel--card]=\"card()\">\n <div class=\"kf-auth-layout__mobile-brand\">\n @if (brandLogoMobileSrc() || brandLogoSrc()) {\n <img\n class=\"kf-auth-layout__logo kf-auth-layout__logo--mobile\"\n [src]=\"brandLogoMobileSrc() || brandLogoSrc()\"\n [alt]=\"brand()\"\n />\n } @else {\n <span class=\"kf-auth-layout__wordmark kf-auth-layout__wordmark--mobile\">{{ brand() }}</span>\n }\n </div>\n\n @if (title(); as headerTitle) {\n <div class=\"kf-auth-layout__header\">\n <kf-text as=\"h1\" variant=\"title-lg\">{{ headerTitle }}</kf-text>\n @if (subtitle(); as headerSubtitle) {\n <kf-text as=\"p\" variant=\"body-muted\">{{ headerSubtitle }}</kf-text>\n }\n </div>\n }\n\n <div class=\"kf-auth-layout__sso\">\n <ng-content select=\"[kfAuthLayoutSso]\" />\n </div>\n\n @if (ssoContent()) {\n <div class=\"kf-auth-layout__divider\">\n <kf-divider />\n <kf-text as=\"span\" variant=\"caption\" class=\"kf-auth-layout__divider-label\">o</kf-text>\n <kf-divider />\n </div>\n }\n\n <div class=\"kf-auth-layout__body\">\n <ng-content />\n </div>\n </div>\n</main>\n", styles: ["@charset \"UTF-8\";:host{display:grid;grid-template-columns:minmax(0,1fr) minmax(0,1.15fr);min-height:100dvh}.kf-auth-layout__brand{display:flex;flex-direction:column;justify-content:center;gap:var(--kf-space-md);padding:var(--kf-space-xl);background:linear-gradient(190deg,var(--kf-color-primary-active) 0%,var(--kf-color-tertiary-deep) 100%);color:var(--kf-color-white)}.kf-auth-layout__logo{height:1.75rem;width:fit-content}.kf-auth-layout__wordmark{font-family:var(--kf-font-title);font-weight:var(--kf-font-weight-title);font-size:var(--kf-font-size-title-lg);letter-spacing:-.01em}.kf-auth-layout__tagline{max-width:34ch;margin:0;font-family:var(--kf-font-body);font-size:var(--kf-font-size-body);color:#ffffffb8}.kf-auth-layout__content{display:flex;align-items:center;justify-content:center;padding:var(--kf-space-xl);background:var(--kf-color-surface)}:host([data-background=gradient]) .kf-auth-layout__content{background:linear-gradient(165deg,color-mix(in srgb,var(--kf-color-primary) 6%,var(--kf-color-surface)) 0%,var(--kf-color-surface) 55%,color-mix(in srgb,var(--kf-color-primary) 10%,var(--kf-color-surface)) 100%)}:host([data-background=pattern]) .kf-auth-layout__content{background-color:var(--kf-color-surface);background-image:radial-gradient(circle at 1px 1px,color-mix(in srgb,var(--kf-color-primary) 16%,transparent) 1px,transparent 0),linear-gradient(165deg,color-mix(in srgb,var(--kf-color-primary) 6%,var(--kf-color-surface)) 0%,var(--kf-color-surface) 55%,color-mix(in srgb,var(--kf-color-primary) 10%,var(--kf-color-surface)) 100%);background-size:18px 18px,100% 100%}.kf-auth-layout__panel{width:100%;max-width:360px;display:flex;flex-direction:column;gap:var(--kf-space-xl)}.kf-auth-layout__header{display:flex;flex-direction:column;gap:var(--kf-space-xs)}.kf-auth-layout__sso,.kf-auth-layout__body{display:flex;flex-direction:column;gap:var(--kf-space-sm)}.kf-auth-layout__panel--card{padding:var(--kf-space-xl);background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md);box-shadow:var(--kf-shadow-md)}.kf-auth-layout__divider{display:flex;align-items:center;gap:var(--kf-space-sm)}.kf-auth-layout__divider kf-divider{flex:1 1 0}.kf-auth-layout__divider-label{flex:none}.kf-auth-layout__mobile-brand{display:none;text-align:center}.kf-auth-layout__logo--mobile{margin-inline:auto;height:1.5rem}.kf-auth-layout__wordmark--mobile{color:var(--kf-color-ink)}@media(max-width:900px){:host{grid-template-columns:1fr}.kf-auth-layout__brand{display:none}.kf-auth-layout__content{padding:var(--kf-space-lg) var(--kf-space-md)}.kf-auth-layout__mobile-brand{display:block;margin-bottom:var(--kf-space-md)}}@media(max-width:480px){.kf-auth-layout__panel{max-width:none}}\n"] }]
1382
1392
  }], propDecorators: { brand: [{ type: i0.Input, args: [{ isSignal: true, alias: "brand", required: true }] }], brandLogoSrc: [{ type: i0.Input, args: [{ isSignal: true, alias: "brandLogoSrc", required: false }] }], brandTagline: [{ type: i0.Input, args: [{ isSignal: true, alias: "brandTagline", required: false }] }], brandLogoMobileSrc: [{ type: i0.Input, args: [{ isSignal: true, alias: "brandLogoMobileSrc", required: false }] }], backgroundVariant: [{ type: i0.Input, args: [{ isSignal: true, alias: "backgroundVariant", required: false }] }], card: [{ type: i0.Input, args: [{ isSignal: true, alias: "card", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], subtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "subtitle", required: false }] }], ssoContent: [{ type: i0.ContentChild, args: [i0.forwardRef(() => AuthLayoutSso), { isSignal: true }] }] } });
1383
1393
 
1384
1394
  /**
@@ -2211,6 +2221,240 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
2211
2221
  args: [{ selector: 'kf-company-selector', imports: [Divider, Select, Text], template: "<kf-select\n class=\"kf-company-selector__select\"\n [label]=\"label()\"\n [options]=\"companies()\"\n [value]=\"value()\"\n (valueChange)=\"valueChange.emit($event)\"\n/>\n@if (meta().length) {\n <div class=\"kf-company-selector__meta\">\n @for (item of meta(); track item.label; let last = $last) {\n <div class=\"kf-company-selector__meta-item\">\n <kf-text as=\"span\" variant=\"overline\">{{ item.label }}</kf-text>\n <span class=\"kf-company-selector__meta-value\">{{ item.value }}</span>\n </div>\n @if (!last) {\n <kf-divider orientation=\"vertical\" />\n }\n }\n </div>\n}\n", styles: [":host{display:flex;align-items:center;gap:var(--kf-space-lg);flex-wrap:wrap;padding:var(--kf-space-md);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md);background:var(--kf-color-white);font-family:var(--kf-font-body)}.kf-company-selector__select{flex:1 1 260px;min-width:0}.kf-company-selector__meta{display:flex;gap:var(--kf-space-lg);flex-wrap:wrap}.kf-company-selector__meta-item{display:flex;flex-direction:column;gap:2px}.kf-company-selector__meta-value{font-family:var(--kf-font-mono);font-size:.875rem;color:var(--kf-color-ink)}\n"] }]
2212
2222
  }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], companies: [{ type: i0.Input, args: [{ isSignal: true, alias: "companies", required: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], meta: [{ type: i0.Input, args: [{ isSignal: true, alias: "meta", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }] } });
2213
2223
 
2224
+ /**
2225
+ * Contenido del panel flotante de un `kf-dropdown`. Es solo una plantilla
2226
+ * (`<ng-template>`, capturada vía `TemplateRef`) — `Dropdown` la conecta al
2227
+ * overlay real cuando `open` pasa a `true`, no la renderiza en su propio
2228
+ * lugar del DOM. Esto permite que el panel aparezca pegado al host de
2229
+ * `Dropdown` sin importar dónde esté ese host en la página.
2230
+ *
2231
+ * `variant`: `'solid'` (default, fondo blanco) o `'dark'` (fondo sólido
2232
+ * oscuro, mismo tono que `[kf-drawer-header]`) para cuando el trigger vive
2233
+ * sobre una superficie oscura de marca.
2234
+ *
2235
+ * `dark` es sólida a propósito, no semitransparente: el CDK Overlay
2236
+ * reposiciona el panel según dónde entra en el viewport y puede terminar
2237
+ * cayendo sobre fondo blanco en vez de sobre el rail oscuro — un color
2238
+ * sólido se ve intencional sin importar dónde lo posicione el CDK.
2239
+ */
2240
+ class DropdownPanel {
2241
+ variant = input('solid', /* @ts-ignore */
2242
+ ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
2243
+ templateRef = viewChild.required(TemplateRef, /* @ts-ignore */
2244
+ ...(ngDevMode ? [{ debugName: "templateRef" }] : /* istanbul ignore next */ []));
2245
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DropdownPanel, deps: [], target: i0.ɵɵFactoryTarget.Component });
2246
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.0", type: DropdownPanel, isStandalone: true, selector: "kf-dropdown-panel", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "templateRef", first: true, predicate: TemplateRef, descendants: true, isSignal: true }], ngImport: i0, template: "<ng-template>\n <div class=\"kf-dropdown-panel\" [class.kf-dropdown-panel--dark]=\"variant() === 'dark'\">\n <div class=\"kf-dropdown-panel__inner\" role=\"menu\">\n <ng-content />\n </div>\n </div>\n</ng-template>\n", styles: [".kf-dropdown-panel{min-width:220px;max-width:min(280px,90vw);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--dark{background:var(--kf-color-tertiary-deep);border-color:#ffffff24}.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"] });
2247
+ }
2248
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DropdownPanel, decorators: [{
2249
+ type: Component,
2250
+ args: [{ selector: 'kf-dropdown-panel', template: "<ng-template>\n <div class=\"kf-dropdown-panel\" [class.kf-dropdown-panel--dark]=\"variant() === 'dark'\">\n <div class=\"kf-dropdown-panel__inner\" role=\"menu\">\n <ng-content />\n </div>\n </div>\n</ng-template>\n", styles: [".kf-dropdown-panel{min-width:220px;max-width:min(280px,90vw);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--dark{background:var(--kf-color-tertiary-deep);border-color:#ffffff24}.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"] }]
2251
+ }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], templateRef: [{ type: i0.ViewChild, args: [i0.forwardRef(() => TemplateRef), { isSignal: true }] }] } });
2252
+
2253
+ /**
2254
+ * Menú de acciones flotante, conectado a un elemento disparador vía
2255
+ * `@angular/cdk/overlay` — la misma pieza que usa Angular Material para
2256
+ * `mat-menu`/`mat-select`. El disparador real puede vivir en OTRO
2257
+ * componente por completo (ej. el avatar dentro de `kf-portal-header`), y
2258
+ * el panel aparece pegado a ESE elemento sin importar dónde el consumidor
2259
+ * ubique al `Dropdown` en su HTML.
2260
+ *
2261
+ * `open` es two-way: el consumidor decide qué dispara la apertura —
2262
+ * `Dropdown` en sí no renderiza ningún trigger, su propio host es el punto
2263
+ * de anclaje visual del panel.
2264
+ *
2265
+ * <kf-dropdown [(open)]="menuOpen"><kf-dropdown-panel>...</kf-dropdown-panel></kf-dropdown>
2266
+ */
2267
+ class Dropdown {
2268
+ /** Visibilidad del panel, two-way: `[(open)]="menuOpen"`. */
2269
+ open = model(false, /* @ts-ignore */
2270
+ ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
2271
+ /**
2272
+ * Elemento al que se conecta el overlay, si es distinto del propio host
2273
+ * de `Dropdown`. Sin esto, `Dropdown` asume que ÉL MISMO es el
2274
+ * disparador. Útil cuando el disparador real vive dentro de OTRO
2275
+ * componente que no se puede envolver sin ensanchar el área de click
2276
+ * (ej. el avatar de sesión dentro de `PortalHeader`).
2277
+ */
2278
+ anchor = input(/* @ts-ignore */
2279
+ ...(ngDevMode ? [undefined, { debugName: "anchor" }] : /* istanbul ignore next */ []));
2280
+ panel = contentChild(DropdownPanel, /* @ts-ignore */
2281
+ ...(ngDevMode ? [{ debugName: "panel" }] : /* istanbul ignore next */ []));
2282
+ overlayService = inject(Overlay);
2283
+ host = inject((ElementRef));
2284
+ viewContainerRef = inject(ViewContainerRef);
2285
+ destroyRef = inject(DestroyRef);
2286
+ overlayRef = null;
2287
+ /**
2288
+ * Suscripciones a `outsidePointerEvents()`/`keydownEvents()` del overlay
2289
+ * ACTUALMENTE adjunto. `getOrCreateOverlayRef()` reutiliza el mismo
2290
+ * `overlayRef` entre aperturas (no lo recrea cada vez), así que se
2291
+ * cancelan explícitamente en `detach()` antes de la siguiente `attach()`
2292
+ * — de lo contrario se acumularían N suscripciones activas sobre el
2293
+ * mismo overlay persistente.
2294
+ */
2295
+ overlaySubscription = null;
2296
+ constructor() {
2297
+ effect(() => {
2298
+ if (this.open()) {
2299
+ this.attach();
2300
+ }
2301
+ else {
2302
+ this.detach();
2303
+ }
2304
+ });
2305
+ this.destroyRef.onDestroy(() => {
2306
+ this.overlaySubscription?.unsubscribe();
2307
+ this.overlayRef?.dispose();
2308
+ });
2309
+ }
2310
+ /**
2311
+ * Respaldo de `overlayRef.keydownEvents()`: ese observable del CDK solo
2312
+ * ve teclas con el foco DENTRO del panel. Si el foco queda en el botón
2313
+ * disparador (caso común), `keydownEvents()` nunca dispara — este
2314
+ * listener a nivel de documento cubre ese caso.
2315
+ */
2316
+ onDocumentEscape() {
2317
+ this.open.set(false);
2318
+ }
2319
+ attach() {
2320
+ const panel = this.panel();
2321
+ if (!panel || this.overlayRef?.hasAttached()) {
2322
+ return;
2323
+ }
2324
+ const overlayRef = this.getOrCreateOverlayRef();
2325
+ const portal = new TemplatePortal(panel.templateRef(), this.viewContainerRef);
2326
+ overlayRef.attach(portal);
2327
+ this.overlaySubscription = new Subscription();
2328
+ this.overlaySubscription.add(overlayRef.outsidePointerEvents().subscribe((event) => {
2329
+ if (this.isEventOnTrigger(event)) {
2330
+ return;
2331
+ }
2332
+ this.open.set(false);
2333
+ }));
2334
+ this.overlaySubscription.add(overlayRef.keydownEvents().subscribe((event) => {
2335
+ if (event.key === 'Escape') {
2336
+ this.open.set(false);
2337
+ }
2338
+ }));
2339
+ }
2340
+ /**
2341
+ * El trigger real (el `anchor`, o el propio host si no se pasó ninguno —
2342
+ * ver comentario de la clase) vive FUERA del overlay a propósito: el
2343
+ * overlay recién se crea/adjunta cuando `open` pasa a `true`, así que el
2344
+ * CDK no tiene forma de saber que ese elemento externo "es parte" de su
2345
+ * disparador. Sin este filtro, el mismo `pointerdown` que abre el menú
2346
+ * también llega a `outsidePointerEvents()`, cerrando el panel en el
2347
+ * mismo gesto que lo abrió.
2348
+ */
2349
+ isEventOnTrigger(event) {
2350
+ const trigger = this.anchor()?.nativeElement ?? this.host.nativeElement;
2351
+ const target = event.target;
2352
+ return target instanceof Node && trigger.contains(target);
2353
+ }
2354
+ detach() {
2355
+ this.overlaySubscription?.unsubscribe();
2356
+ this.overlaySubscription = null;
2357
+ this.overlayRef?.detach();
2358
+ }
2359
+ getOrCreateOverlayRef() {
2360
+ if (this.overlayRef) {
2361
+ return this.overlayRef;
2362
+ }
2363
+ const positionStrategy = this.overlayService
2364
+ .position()
2365
+ .flexibleConnectedTo(this.anchor() ?? this.host)
2366
+ // 4 posiciones, no 2: además del par alineado al borde derecho del
2367
+ // anchor, agrega el mismo par alineado al borde IZQUIERDO — sin esto,
2368
+ // un trigger cerca del borde derecho de pantalla no tenía fallback
2369
+ // horizontal y el panel se salía del viewport.
2370
+ .withPositions([
2371
+ { originX: 'end', originY: 'bottom', overlayX: 'end', overlayY: 'top', offsetY: 4 },
2372
+ { originX: 'end', originY: 'top', overlayX: 'end', overlayY: 'bottom', offsetY: -4 },
2373
+ { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4 },
2374
+ { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', offsetY: -4 },
2375
+ ])
2376
+ .withViewportMargin(8)
2377
+ // Si ninguna de las 4 posiciones entra completa, la última se ajusta
2378
+ // con push en vez de desbordarse fuera de pantalla.
2379
+ .withPush(true);
2380
+ this.overlayRef = this.overlayService.create({
2381
+ positionStrategy,
2382
+ scrollStrategy: this.overlayService.scrollStrategies.reposition(),
2383
+ maxWidth: '90vw',
2384
+ });
2385
+ return this.overlayRef;
2386
+ }
2387
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Dropdown, deps: [], target: i0.ɵɵFactoryTarget.Component });
2388
+ 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"] });
2389
+ }
2390
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Dropdown, decorators: [{
2391
+ type: Component,
2392
+ args: [{ selector: 'kf-dropdown', template: `<ng-content />`, styles: [":host{display:inline-block}\n"] }]
2393
+ }], 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: [{
2394
+ type: HostListener,
2395
+ args: ['document:keydown.escape']
2396
+ }] } });
2397
+
2398
+ /**
2399
+ * Popover de "Datos de contacto" de una persona (asesor, ejecutivo — nombre
2400
+ * genérico a propósito, sin lógica de negocio de ninguna pantalla concreta
2401
+ * adentro). Cada canal (`whatsapp`/`phone`/`email`) se muestra SOLO si
2402
+ * viene con dato — nunca los 3 fijos — y si ninguno viene, un estado vacío
2403
+ * explícito en vez de un panel a medio llenar. Mismo criterio que ya
2404
+ * resolvía `sitio-privado-one-way-frontend` (repo hermano de Chile) para
2405
+ * el asesor de taller, ahora como pieza compartida del design system.
2406
+ *
2407
+ * <kf-contact-popover
2408
+ * [name]="advisor.name"
2409
+ * [whatsapp]="advisor.whatsapp"
2410
+ * [phone]="advisor.phone"
2411
+ * [email]="advisor.email"
2412
+ * />
2413
+ */
2414
+ class ContactPopover {
2415
+ name = input.required(/* @ts-ignore */
2416
+ ...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
2417
+ title = input('Datos de contacto', /* @ts-ignore */
2418
+ ...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
2419
+ whatsapp = input(null, /* @ts-ignore */
2420
+ ...(ngDevMode ? [{ debugName: "whatsapp" }] : /* istanbul ignore next */ []));
2421
+ phone = input(null, /* @ts-ignore */
2422
+ ...(ngDevMode ? [{ debugName: "phone" }] : /* istanbul ignore next */ []));
2423
+ email = input(null, /* @ts-ignore */
2424
+ ...(ngDevMode ? [{ debugName: "email" }] : /* istanbul ignore next */ []));
2425
+ open = signal(false, /* @ts-ignore */
2426
+ ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
2427
+ whatsappHref = computed(() => {
2428
+ const digits = (this.whatsapp() ?? '').replace(/[^\d]/g, '');
2429
+ return digits.length > 0 ? `https://wa.me/${digits}` : null;
2430
+ }, /* @ts-ignore */
2431
+ ...(ngDevMode ? [{ debugName: "whatsappHref" }] : /* istanbul ignore next */ []));
2432
+ phoneHref = computed(() => {
2433
+ const trimmed = (this.phone() ?? '').trim();
2434
+ return trimmed.length > 0 ? `tel:${trimmed}` : null;
2435
+ }, /* @ts-ignore */
2436
+ ...(ngDevMode ? [{ debugName: "phoneHref" }] : /* istanbul ignore next */ []));
2437
+ emailHref = computed(() => {
2438
+ const trimmed = (this.email() ?? '').trim();
2439
+ return trimmed.length > 0 ? `mailto:${trimmed}` : null;
2440
+ }, /* @ts-ignore */
2441
+ ...(ngDevMode ? [{ debugName: "emailHref" }] : /* istanbul ignore next */ []));
2442
+ hasContactMethods = computed(() => this.whatsappHref() !== null || this.phoneHref() !== null || this.emailHref() !== null, /* @ts-ignore */
2443
+ ...(ngDevMode ? [{ debugName: "hasContactMethods" }] : /* istanbul ignore next */ []));
2444
+ onToggle() {
2445
+ this.open.update((value) => !value);
2446
+ }
2447
+ onClose() {
2448
+ this.open.set(false);
2449
+ }
2450
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: ContactPopover, deps: [], target: i0.ɵɵFactoryTarget.Component });
2451
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: ContactPopover, isStandalone: true, selector: "kf-contact-popover", inputs: { name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: true, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, whatsapp: { classPropertyName: "whatsapp", publicName: "whatsapp", isSignal: true, isRequired: false, transformFunction: null }, phone: { classPropertyName: "phone", publicName: "phone", isSignal: true, isRequired: false, transformFunction: null }, email: { classPropertyName: "email", publicName: "email", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<kf-dropdown class=\"kf-contact-popover\" [(open)]=\"open\">\n <button\n type=\"button\"\n class=\"kf-contact-popover__trigger\"\n [attr.aria-expanded]=\"open()\"\n (click)=\"onToggle()\"\n >\n {{ name() }}\n <kf-icon name=\"chevron-down\" size=\"sm\" [rotate]=\"open() ? 180 : 0\" />\n </button>\n <kf-dropdown-panel>\n <div class=\"kf-contact-popover__header\">\n <span class=\"kf-contact-popover__title\">{{ title() }}</span>\n <button\n type=\"button\"\n class=\"kf-contact-popover__close\"\n aria-label=\"Cerrar\"\n (click)=\"onClose()\"\n >\n <kf-icon name=\"x\" size=\"sm\" />\n </button>\n </div>\n <hr class=\"kf-contact-popover__hr\" />\n @if (hasContactMethods()) {\n @if (whatsappHref()) {\n <a class=\"kf-contact-popover__link\" [href]=\"whatsappHref()!\">\n <kf-icon name=\"message-circle\" size=\"sm\" />\n WhatsApp\n </a>\n }\n @if (phoneHref()) {\n <a class=\"kf-contact-popover__link\" [href]=\"phoneHref()!\">\n <kf-icon name=\"phone\" size=\"sm\" />\n Tel\u00E9fono\n </a>\n }\n @if (emailHref()) {\n <a class=\"kf-contact-popover__link\" [href]=\"emailHref()!\">\n <kf-icon name=\"mail\" size=\"sm\" />\n Correo\n </a>\n }\n } @else {\n <div class=\"kf-contact-popover__empty\">\n <kf-icon name=\"alert-circle\" size=\"sm\" />\n No registra datos de contacto\n </div>\n }\n </kf-dropdown-panel>\n</kf-dropdown>\n", styles: [":host{display:inline-flex}.kf-contact-popover{display:inline-flex}.kf-contact-popover__trigger{display:inline-flex;align-items:center;gap:4px;border:none;background:none;padding:0;font:inherit;font-weight:600;color:var(--kf-color-primary);cursor:pointer}.kf-contact-popover__trigger:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus);border-radius:var(--kf-radius-xs)}.kf-contact-popover__header{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);padding:var(--kf-space-sm) var(--kf-space-md)}.kf-contact-popover__title{font-size:.8125rem;font-weight:700;color:var(--kf-color-ink)}.kf-contact-popover__close{display:inline-flex;border:none;background:none;padding:0;color:var(--kf-color-text-muted);cursor:pointer}.kf-contact-popover__hr{height:1px;margin:0;border:none;background:var(--kf-color-border)}.kf-contact-popover__link{display:flex;align-items:center;gap:var(--kf-space-sm);padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);text-decoration:none}.kf-contact-popover__link kf-icon{flex:none;color:var(--kf-color-text-muted)}.kf-contact-popover__link:hover{background:var(--kf-color-surface)}.kf-contact-popover__link:focus-visible{outline:none;box-shadow:inset var(--kf-shadow-focus)}.kf-contact-popover__empty{display:flex;align-items:center;gap:var(--kf-space-sm);padding:var(--kf-space-md);color:var(--kf-color-text-muted);font-size:.875rem}\n"], dependencies: [{ kind: "component", type: Dropdown, selector: "kf-dropdown", inputs: ["open", "anchor"], outputs: ["openChange"] }, { kind: "component", type: DropdownPanel, selector: "kf-dropdown-panel", inputs: ["variant"] }, { kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size", "rotate", "filled"] }] });
2452
+ }
2453
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: ContactPopover, decorators: [{
2454
+ type: Component,
2455
+ args: [{ selector: 'kf-contact-popover', imports: [Dropdown, DropdownPanel, Icon], template: "<kf-dropdown class=\"kf-contact-popover\" [(open)]=\"open\">\n <button\n type=\"button\"\n class=\"kf-contact-popover__trigger\"\n [attr.aria-expanded]=\"open()\"\n (click)=\"onToggle()\"\n >\n {{ name() }}\n <kf-icon name=\"chevron-down\" size=\"sm\" [rotate]=\"open() ? 180 : 0\" />\n </button>\n <kf-dropdown-panel>\n <div class=\"kf-contact-popover__header\">\n <span class=\"kf-contact-popover__title\">{{ title() }}</span>\n <button\n type=\"button\"\n class=\"kf-contact-popover__close\"\n aria-label=\"Cerrar\"\n (click)=\"onClose()\"\n >\n <kf-icon name=\"x\" size=\"sm\" />\n </button>\n </div>\n <hr class=\"kf-contact-popover__hr\" />\n @if (hasContactMethods()) {\n @if (whatsappHref()) {\n <a class=\"kf-contact-popover__link\" [href]=\"whatsappHref()!\">\n <kf-icon name=\"message-circle\" size=\"sm\" />\n WhatsApp\n </a>\n }\n @if (phoneHref()) {\n <a class=\"kf-contact-popover__link\" [href]=\"phoneHref()!\">\n <kf-icon name=\"phone\" size=\"sm\" />\n Tel\u00E9fono\n </a>\n }\n @if (emailHref()) {\n <a class=\"kf-contact-popover__link\" [href]=\"emailHref()!\">\n <kf-icon name=\"mail\" size=\"sm\" />\n Correo\n </a>\n }\n } @else {\n <div class=\"kf-contact-popover__empty\">\n <kf-icon name=\"alert-circle\" size=\"sm\" />\n No registra datos de contacto\n </div>\n }\n </kf-dropdown-panel>\n</kf-dropdown>\n", styles: [":host{display:inline-flex}.kf-contact-popover{display:inline-flex}.kf-contact-popover__trigger{display:inline-flex;align-items:center;gap:4px;border:none;background:none;padding:0;font:inherit;font-weight:600;color:var(--kf-color-primary);cursor:pointer}.kf-contact-popover__trigger:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus);border-radius:var(--kf-radius-xs)}.kf-contact-popover__header{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);padding:var(--kf-space-sm) var(--kf-space-md)}.kf-contact-popover__title{font-size:.8125rem;font-weight:700;color:var(--kf-color-ink)}.kf-contact-popover__close{display:inline-flex;border:none;background:none;padding:0;color:var(--kf-color-text-muted);cursor:pointer}.kf-contact-popover__hr{height:1px;margin:0;border:none;background:var(--kf-color-border)}.kf-contact-popover__link{display:flex;align-items:center;gap:var(--kf-space-sm);padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);text-decoration:none}.kf-contact-popover__link kf-icon{flex:none;color:var(--kf-color-text-muted)}.kf-contact-popover__link:hover{background:var(--kf-color-surface)}.kf-contact-popover__link:focus-visible{outline:none;box-shadow:inset var(--kf-shadow-focus)}.kf-contact-popover__empty{display:flex;align-items:center;gap:var(--kf-space-sm);padding:var(--kf-space-md);color:var(--kf-color-text-muted);font-size:.875rem}\n"] }]
2456
+ }], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], whatsapp: [{ type: i0.Input, args: [{ isSignal: true, alias: "whatsapp", required: false }] }], phone: [{ type: i0.Input, args: [{ isSignal: true, alias: "phone", required: false }] }], email: [{ type: i0.Input, args: [{ isSignal: true, alias: "email", required: false }] }] } });
2457
+
2214
2458
  /**
2215
2459
  * Marcador genérico para contenido aún por definir (página 38) — comunica
2216
2460
  * "contenido disponible para uso" sin parecer un error, a diferencia de
@@ -3356,180 +3600,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
3356
3600
  args: [{ selector: 'kf-drawer-session', imports: [Avatar, Icon], template: "@if (brand() && !collapsed()) {\n <span class=\"kf-drawer-session__brand\">{{ brand() }}</span>\n}\n\n@if (interactive()) {\n <button\n type=\"button\"\n class=\"kf-drawer-session__card kf-drawer-session__card--interactive\"\n [class.kf-drawer-session__card--collapsed]=\"collapsed()\"\n (click)=\"cardClick.emit()\"\n >\n <kf-avatar [initials]=\"displayInitials()\" [src]=\"avatarUrl()\" />\n @if (!collapsed()) {\n <div class=\"kf-drawer-session__identity\">\n <span class=\"kf-drawer-session__name\">{{ name() }}</span>\n @if (email()) {\n <span class=\"kf-drawer-session__email\">{{ email() }}</span>\n }\n @if (role()) {\n <span class=\"kf-drawer-session__role\">{{ role() }}</span>\n }\n </div>\n <kf-icon name=\"more-vertical\" size=\"sm\" />\n }\n </button>\n} @else {\n <div class=\"kf-drawer-session__card\" [class.kf-drawer-session__card--collapsed]=\"collapsed()\">\n <kf-avatar [initials]=\"displayInitials()\" [src]=\"avatarUrl()\" />\n @if (!collapsed()) {\n <div class=\"kf-drawer-session__identity\">\n <span class=\"kf-drawer-session__name\">{{ name() }}</span>\n @if (email()) {\n <span class=\"kf-drawer-session__email\">{{ email() }}</span>\n }\n @if (role()) {\n <span class=\"kf-drawer-session__role\">{{ role() }}</span>\n }\n </div>\n }\n </div>\n}\n", styles: [":host{display:block;font-family:var(--kf-font-body)}.kf-drawer-session__brand{display:block;margin-bottom:var(--kf-space-sm);font-family:var(--kf-font-mono);font-size:.6875rem;font-weight:600;letter-spacing:.08em;text-transform:uppercase;color:#ffffffa6}.kf-drawer-session__card{box-sizing:border-box;display:flex;align-items:center;gap:var(--kf-space-sm);width:100%;padding:var(--kf-space-sm);border:1px solid rgba(255,255,255,.14);border-radius:var(--kf-radius-md);background:#ffffff0a}.kf-drawer-session__card--interactive{font:inherit;color:inherit;cursor:pointer;text-align:left;transition:background .14s}.kf-drawer-session__card--interactive:hover{background:#ffffff17}.kf-drawer-session__card--interactive:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-drawer-session__card--interactive kf-icon{flex:none;color:#ffffff73}.kf-drawer-session__card--collapsed{justify-content:center;width:40px;height:40px;padding:0;border-color:transparent;background:transparent}.kf-drawer-session__card--collapsed:hover{background:#ffffff17}.kf-drawer-session__identity{display:flex;flex-direction:column;gap:2px;min-width:0;flex:1 1 auto}.kf-drawer-session__name{font-size:.9375rem;font-weight:600;color:var(--kf-color-white);white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-drawer-session__email{font-size:.8125rem;color:#ffffffa6;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-drawer-session__role{display:inline-block;margin-top:2px;padding:2px var(--kf-space-xs);border:1px solid rgba(255,255,255,.16);border-radius:var(--kf-radius-pill);font-family:var(--kf-font-mono);font-size:.625rem;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:#ffffffd9;background:#ffffff14;width:fit-content}\n"] }]
3357
3601
  }], propDecorators: { name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: true }] }], email: [{ type: i0.Input, args: [{ isSignal: true, alias: "email", required: false }] }], role: [{ type: i0.Input, args: [{ isSignal: true, alias: "role", required: false }] }], initials: [{ type: i0.Input, args: [{ isSignal: true, alias: "initials", required: false }] }], avatarUrl: [{ type: i0.Input, args: [{ isSignal: true, alias: "avatarUrl", required: false }] }], brand: [{ type: i0.Input, args: [{ isSignal: true, alias: "brand", required: false }] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }], interactive: [{ type: i0.Input, args: [{ isSignal: true, alias: "interactive", required: false }] }], cardClick: [{ type: i0.Output, args: ["cardClick"] }] } });
3358
3602
 
3359
- /**
3360
- * Contenido del panel flotante de un `kf-dropdown`. Es solo una plantilla
3361
- * (`<ng-template>`, capturada vía `TemplateRef`) — `Dropdown` la conecta al
3362
- * overlay real cuando `open` pasa a `true`, no la renderiza en su propio
3363
- * lugar del DOM. Esto permite que el panel aparezca pegado al host de
3364
- * `Dropdown` sin importar dónde esté ese host en la página.
3365
- *
3366
- * `variant`: `'solid'` (default, fondo blanco) o `'dark'` (fondo sólido
3367
- * oscuro, mismo tono que `[kf-drawer-header]`) para cuando el trigger vive
3368
- * sobre una superficie oscura de marca.
3369
- *
3370
- * `dark` es sólida a propósito, no semitransparente: el CDK Overlay
3371
- * reposiciona el panel según dónde entra en el viewport y puede terminar
3372
- * cayendo sobre fondo blanco en vez de sobre el rail oscuro — un color
3373
- * sólido se ve intencional sin importar dónde lo posicione el CDK.
3374
- */
3375
- class DropdownPanel {
3376
- variant = input('solid', /* @ts-ignore */
3377
- ...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
3378
- templateRef = viewChild.required(TemplateRef, /* @ts-ignore */
3379
- ...(ngDevMode ? [{ debugName: "templateRef" }] : /* istanbul ignore next */ []));
3380
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DropdownPanel, deps: [], target: i0.ɵɵFactoryTarget.Component });
3381
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.2.0", version: "22.1.0", type: DropdownPanel, isStandalone: true, selector: "kf-dropdown-panel", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "templateRef", first: true, predicate: TemplateRef, descendants: true, isSignal: true }], ngImport: i0, template: "<ng-template>\n <div class=\"kf-dropdown-panel\" [class.kf-dropdown-panel--dark]=\"variant() === 'dark'\">\n <div class=\"kf-dropdown-panel__inner\" role=\"menu\">\n <ng-content />\n </div>\n </div>\n</ng-template>\n", styles: [".kf-dropdown-panel{min-width:220px;max-width:min(280px,90vw);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--dark{background:var(--kf-color-tertiary-deep);border-color:#ffffff24}.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"] });
3382
- }
3383
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DropdownPanel, decorators: [{
3384
- type: Component,
3385
- args: [{ selector: 'kf-dropdown-panel', template: "<ng-template>\n <div class=\"kf-dropdown-panel\" [class.kf-dropdown-panel--dark]=\"variant() === 'dark'\">\n <div class=\"kf-dropdown-panel__inner\" role=\"menu\">\n <ng-content />\n </div>\n </div>\n</ng-template>\n", styles: [".kf-dropdown-panel{min-width:220px;max-width:min(280px,90vw);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--dark{background:var(--kf-color-tertiary-deep);border-color:#ffffff24}.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"] }]
3386
- }], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], templateRef: [{ type: i0.ViewChild, args: [i0.forwardRef(() => TemplateRef), { isSignal: true }] }] } });
3387
-
3388
- /**
3389
- * Menú de acciones flotante, conectado a un elemento disparador vía
3390
- * `@angular/cdk/overlay` — la misma pieza que usa Angular Material para
3391
- * `mat-menu`/`mat-select`. El disparador real puede vivir en OTRO
3392
- * componente por completo (ej. el avatar dentro de `kf-portal-header`), y
3393
- * el panel aparece pegado a ESE elemento sin importar dónde el consumidor
3394
- * ubique al `Dropdown` en su HTML.
3395
- *
3396
- * `open` es two-way: el consumidor decide qué dispara la apertura —
3397
- * `Dropdown` en sí no renderiza ningún trigger, su propio host es el punto
3398
- * de anclaje visual del panel.
3399
- *
3400
- * <kf-dropdown [(open)]="menuOpen"><kf-dropdown-panel>...</kf-dropdown-panel></kf-dropdown>
3401
- */
3402
- class Dropdown {
3403
- /** Visibilidad del panel, two-way: `[(open)]="menuOpen"`. */
3404
- open = model(false, /* @ts-ignore */
3405
- ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
3406
- /**
3407
- * Elemento al que se conecta el overlay, si es distinto del propio host
3408
- * de `Dropdown`. Sin esto, `Dropdown` asume que ÉL MISMO es el
3409
- * disparador. Útil cuando el disparador real vive dentro de OTRO
3410
- * componente que no se puede envolver sin ensanchar el área de click
3411
- * (ej. el avatar de sesión dentro de `PortalHeader`).
3412
- */
3413
- anchor = input(/* @ts-ignore */
3414
- ...(ngDevMode ? [undefined, { debugName: "anchor" }] : /* istanbul ignore next */ []));
3415
- panel = contentChild(DropdownPanel, /* @ts-ignore */
3416
- ...(ngDevMode ? [{ debugName: "panel" }] : /* istanbul ignore next */ []));
3417
- overlayService = inject(Overlay);
3418
- host = inject((ElementRef));
3419
- viewContainerRef = inject(ViewContainerRef);
3420
- destroyRef = inject(DestroyRef);
3421
- overlayRef = null;
3422
- /**
3423
- * Suscripciones a `outsidePointerEvents()`/`keydownEvents()` del overlay
3424
- * ACTUALMENTE adjunto. `getOrCreateOverlayRef()` reutiliza el mismo
3425
- * `overlayRef` entre aperturas (no lo recrea cada vez), así que se
3426
- * cancelan explícitamente en `detach()` antes de la siguiente `attach()`
3427
- * — de lo contrario se acumularían N suscripciones activas sobre el
3428
- * mismo overlay persistente.
3429
- */
3430
- overlaySubscription = null;
3431
- constructor() {
3432
- effect(() => {
3433
- if (this.open()) {
3434
- this.attach();
3435
- }
3436
- else {
3437
- this.detach();
3438
- }
3439
- });
3440
- this.destroyRef.onDestroy(() => {
3441
- this.overlaySubscription?.unsubscribe();
3442
- this.overlayRef?.dispose();
3443
- });
3444
- }
3445
- /**
3446
- * Respaldo de `overlayRef.keydownEvents()`: ese observable del CDK solo
3447
- * ve teclas con el foco DENTRO del panel. Si el foco queda en el botón
3448
- * disparador (caso común), `keydownEvents()` nunca dispara — este
3449
- * listener a nivel de documento cubre ese caso.
3450
- */
3451
- onDocumentEscape() {
3452
- this.open.set(false);
3453
- }
3454
- attach() {
3455
- const panel = this.panel();
3456
- if (!panel || this.overlayRef?.hasAttached()) {
3457
- return;
3458
- }
3459
- const overlayRef = this.getOrCreateOverlayRef();
3460
- const portal = new TemplatePortal(panel.templateRef(), this.viewContainerRef);
3461
- overlayRef.attach(portal);
3462
- this.overlaySubscription = new Subscription();
3463
- this.overlaySubscription.add(overlayRef.outsidePointerEvents().subscribe((event) => {
3464
- if (this.isEventOnTrigger(event)) {
3465
- return;
3466
- }
3467
- this.open.set(false);
3468
- }));
3469
- this.overlaySubscription.add(overlayRef.keydownEvents().subscribe((event) => {
3470
- if (event.key === 'Escape') {
3471
- this.open.set(false);
3472
- }
3473
- }));
3474
- }
3475
- /**
3476
- * El trigger real (el `anchor`, o el propio host si no se pasó ninguno —
3477
- * ver comentario de la clase) vive FUERA del overlay a propósito: el
3478
- * overlay recién se crea/adjunta cuando `open` pasa a `true`, así que el
3479
- * CDK no tiene forma de saber que ese elemento externo "es parte" de su
3480
- * disparador. Sin este filtro, el mismo `pointerdown` que abre el menú
3481
- * también llega a `outsidePointerEvents()`, cerrando el panel en el
3482
- * mismo gesto que lo abrió.
3483
- */
3484
- isEventOnTrigger(event) {
3485
- const trigger = this.anchor()?.nativeElement ?? this.host.nativeElement;
3486
- const target = event.target;
3487
- return target instanceof Node && trigger.contains(target);
3488
- }
3489
- detach() {
3490
- this.overlaySubscription?.unsubscribe();
3491
- this.overlaySubscription = null;
3492
- this.overlayRef?.detach();
3493
- }
3494
- getOrCreateOverlayRef() {
3495
- if (this.overlayRef) {
3496
- return this.overlayRef;
3497
- }
3498
- const positionStrategy = this.overlayService
3499
- .position()
3500
- .flexibleConnectedTo(this.anchor() ?? this.host)
3501
- // 4 posiciones, no 2: además del par alineado al borde derecho del
3502
- // anchor, agrega el mismo par alineado al borde IZQUIERDO — sin esto,
3503
- // un trigger cerca del borde derecho de pantalla no tenía fallback
3504
- // horizontal y el panel se salía del viewport.
3505
- .withPositions([
3506
- { originX: 'end', originY: 'bottom', overlayX: 'end', overlayY: 'top', offsetY: 4 },
3507
- { originX: 'end', originY: 'top', overlayX: 'end', overlayY: 'bottom', offsetY: -4 },
3508
- { originX: 'start', originY: 'bottom', overlayX: 'start', overlayY: 'top', offsetY: 4 },
3509
- { originX: 'start', originY: 'top', overlayX: 'start', overlayY: 'bottom', offsetY: -4 },
3510
- ])
3511
- .withViewportMargin(8)
3512
- // Si ninguna de las 4 posiciones entra completa, la última se ajusta
3513
- // con push en vez de desbordarse fuera de pantalla.
3514
- .withPush(true);
3515
- this.overlayRef = this.overlayService.create({
3516
- positionStrategy,
3517
- scrollStrategy: this.overlayService.scrollStrategies.reposition(),
3518
- maxWidth: '90vw',
3519
- });
3520
- return this.overlayRef;
3521
- }
3522
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Dropdown, deps: [], target: i0.ɵɵFactoryTarget.Component });
3523
- 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"] });
3524
- }
3525
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Dropdown, decorators: [{
3526
- type: Component,
3527
- args: [{ selector: 'kf-dropdown', template: `<ng-content />`, styles: [":host{display:inline-block}\n"] }]
3528
- }], 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: [{
3529
- type: HostListener,
3530
- args: ['document:keydown.escape']
3531
- }] } });
3532
-
3533
3603
  /**
3534
3604
  * Ítem de acción dentro de un `kf-dropdown-panel`. Es un `<button>` nativo
3535
3605
  * (no un `<a>`): dispara comportamiento en la pantalla actual, no navega.
@@ -3544,13 +3614,20 @@ class DropdownItem {
3544
3614
  /** Ícono opcional al inicio de la fila. */
3545
3615
  icon = input(/* @ts-ignore */
3546
3616
  ...(ngDevMode ? [undefined, { debugName: "icon" }] : /* istanbul ignore next */ []));
3617
+ /**
3618
+ * Deshabilita el ítem (ej. una acción cuyo endpoint todavía no existe,
3619
+ * caso real de "Factura" en `kf-row-actions` de Taller). Default `false`
3620
+ * a propósito — ningún consumidor existente (ej. el menú de sesión del
3621
+ * Shell) pasa esta prop hoy, así que no cambia su comportamiento.
3622
+ */
3623
+ disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
3547
3624
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DropdownItem, deps: [], target: i0.ɵɵFactoryTarget.Component });
3548
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.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 }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<button\n type=\"button\"\n class=\"kf-dropdown-item\"\n [class.kf-dropdown-item--error]=\"tone() === 'error'\"\n role=\"menuitem\"\n>\n @if (icon()) {\n <kf-icon [name]=\"icon()!\" size=\"sm\" />\n }\n <span class=\"kf-dropdown-item__label\"><ng-content /></span>\n</button>\n", styles: [":host{display:block}.kf-dropdown-item{display:flex;align-items:center;gap:var(--kf-space-sm);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 kf-icon{flex:none;color:var(--kf-color-text-muted)}.kf-dropdown-item__label{flex:1 1 auto}.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 kf-icon{color:var(--kf-color-error-strong)}.kf-dropdown-item--error:hover{background:var(--kf-color-error-soft)}:host-context(.kf-dropdown-panel--dark) .kf-dropdown-item{color:#ffffffd9}:host-context(.kf-dropdown-panel--dark) .kf-dropdown-item kf-icon{color:#ffffff8c}:host-context(.kf-dropdown-panel--dark) .kf-dropdown-item:hover{background:#ffffff14}:host-context(.kf-dropdown-panel--dark) .kf-dropdown-item--error{color:var(--kf-color-error)}:host-context(.kf-dropdown-panel--dark) .kf-dropdown-item--error kf-icon{color:var(--kf-color-error)}:host-context(.kf-dropdown-panel--dark) .kf-dropdown-item--error:hover{background:#bc454529}\n"], dependencies: [{ kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size", "rotate", "filled"] }] });
3625
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.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 }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<button\n type=\"button\"\n class=\"kf-dropdown-item\"\n [class.kf-dropdown-item--error]=\"tone() === 'error'\"\n [disabled]=\"disabled()\"\n role=\"menuitem\"\n>\n @if (icon()) {\n <kf-icon [name]=\"icon()!\" size=\"sm\" />\n }\n <span class=\"kf-dropdown-item__label\"><ng-content /></span>\n</button>\n", styles: [":host{display:block}.kf-dropdown-item{display:flex;align-items:center;gap:var(--kf-space-sm);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 kf-icon{flex:none;color:var(--kf-color-text-muted)}.kf-dropdown-item__label{flex:1 1 auto}.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:disabled{opacity:.55;cursor:not-allowed;pointer-events:none}.kf-dropdown-item--error{color:var(--kf-color-error-strong)}.kf-dropdown-item--error kf-icon{color:var(--kf-color-error-strong)}.kf-dropdown-item--error:hover{background:var(--kf-color-error-soft)}:host-context(.kf-dropdown-panel--dark) .kf-dropdown-item{color:#ffffffd9}:host-context(.kf-dropdown-panel--dark) .kf-dropdown-item kf-icon{color:#ffffff8c}:host-context(.kf-dropdown-panel--dark) .kf-dropdown-item:hover{background:#ffffff14}:host-context(.kf-dropdown-panel--dark) .kf-dropdown-item--error{color:var(--kf-color-error)}:host-context(.kf-dropdown-panel--dark) .kf-dropdown-item--error kf-icon{color:var(--kf-color-error)}:host-context(.kf-dropdown-panel--dark) .kf-dropdown-item--error:hover{background:#bc454529}\n"], dependencies: [{ kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size", "rotate", "filled"] }] });
3549
3626
  }
3550
3627
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DropdownItem, decorators: [{
3551
3628
  type: Component,
3552
- args: [{ selector: 'kf-dropdown-item', imports: [Icon], template: "<button\n type=\"button\"\n class=\"kf-dropdown-item\"\n [class.kf-dropdown-item--error]=\"tone() === 'error'\"\n role=\"menuitem\"\n>\n @if (icon()) {\n <kf-icon [name]=\"icon()!\" size=\"sm\" />\n }\n <span class=\"kf-dropdown-item__label\"><ng-content /></span>\n</button>\n", styles: [":host{display:block}.kf-dropdown-item{display:flex;align-items:center;gap:var(--kf-space-sm);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 kf-icon{flex:none;color:var(--kf-color-text-muted)}.kf-dropdown-item__label{flex:1 1 auto}.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 kf-icon{color:var(--kf-color-error-strong)}.kf-dropdown-item--error:hover{background:var(--kf-color-error-soft)}:host-context(.kf-dropdown-panel--dark) .kf-dropdown-item{color:#ffffffd9}:host-context(.kf-dropdown-panel--dark) .kf-dropdown-item kf-icon{color:#ffffff8c}:host-context(.kf-dropdown-panel--dark) .kf-dropdown-item:hover{background:#ffffff14}:host-context(.kf-dropdown-panel--dark) .kf-dropdown-item--error{color:var(--kf-color-error)}:host-context(.kf-dropdown-panel--dark) .kf-dropdown-item--error kf-icon{color:var(--kf-color-error)}:host-context(.kf-dropdown-panel--dark) .kf-dropdown-item--error:hover{background:#bc454529}\n"] }]
3553
- }], propDecorators: { tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "tone", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }] } });
3629
+ args: [{ selector: 'kf-dropdown-item', imports: [Icon], template: "<button\n type=\"button\"\n class=\"kf-dropdown-item\"\n [class.kf-dropdown-item--error]=\"tone() === 'error'\"\n [disabled]=\"disabled()\"\n role=\"menuitem\"\n>\n @if (icon()) {\n <kf-icon [name]=\"icon()!\" size=\"sm\" />\n }\n <span class=\"kf-dropdown-item__label\"><ng-content /></span>\n</button>\n", styles: [":host{display:block}.kf-dropdown-item{display:flex;align-items:center;gap:var(--kf-space-sm);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 kf-icon{flex:none;color:var(--kf-color-text-muted)}.kf-dropdown-item__label{flex:1 1 auto}.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:disabled{opacity:.55;cursor:not-allowed;pointer-events:none}.kf-dropdown-item--error{color:var(--kf-color-error-strong)}.kf-dropdown-item--error kf-icon{color:var(--kf-color-error-strong)}.kf-dropdown-item--error:hover{background:var(--kf-color-error-soft)}:host-context(.kf-dropdown-panel--dark) .kf-dropdown-item{color:#ffffffd9}:host-context(.kf-dropdown-panel--dark) .kf-dropdown-item kf-icon{color:#ffffff8c}:host-context(.kf-dropdown-panel--dark) .kf-dropdown-item:hover{background:#ffffff14}:host-context(.kf-dropdown-panel--dark) .kf-dropdown-item--error{color:var(--kf-color-error)}:host-context(.kf-dropdown-panel--dark) .kf-dropdown-item--error kf-icon{color:var(--kf-color-error)}:host-context(.kf-dropdown-panel--dark) .kf-dropdown-item--error:hover{background:#bc454529}\n"] }]
3630
+ }], propDecorators: { tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "tone", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }] } });
3554
3631
 
3555
3632
  /**
3556
3633
  * Línea de tiempo de AUDITORÍA: eventos ya ocurridos, en orden, cada uno con
@@ -6439,6 +6516,12 @@ class Modal {
6439
6516
  ...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
6440
6517
  /** Permite cerrar con el botón, `Escape` o clic en el backdrop. */
6441
6518
  dismissible = input(true, { ...(ngDevMode ? { debugName: "dismissible" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
6519
+ /** Ancho por defecto: `480px` (sin pasar nada). Un modal con contenido
6520
+ * tabular/ancho (ej. una lista de varios campos por fila) puede pedir
6521
+ * más, ej. `width="720px"` — sigue topeado por `100vw` en pantallas
6522
+ * angostas, mismo `min()` de siempre. */
6523
+ width = input(null, /* @ts-ignore */
6524
+ ...(ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []));
6442
6525
  titleId = `kf-modal-title-${modalId++}`;
6443
6526
  dialog = viewChild('dialog', /* @ts-ignore */
6444
6527
  ...(ngDevMode ? [{ debugName: "dialog" }] : /* istanbul ignore next */ []));
@@ -6517,12 +6600,12 @@ class Modal {
6517
6600
  }
6518
6601
  }
6519
6602
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Modal, deps: [], target: i0.ɵɵFactoryTarget.Component });
6520
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Modal, isStandalone: true, selector: "kf-modal", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, dismissible: { classPropertyName: "dismissible", publicName: "dismissible", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange" }, host: { listeners: { "click": "onHostClick($event)" } }, viewQueries: [{ propertyName: "dialog", first: true, predicate: ["dialog"], descendants: true, isSignal: true }], ngImport: i0, template: "<dialog\n #dialog\n class=\"kf-modal\"\n [attr.aria-labelledby]=\"title() ? titleId : null\"\n (cancel)=\"onCancel($event)\"\n (close)=\"open.set(false)\"\n>\n @if (open()) {\n <div class=\"kf-modal__panel\">\n <div class=\"kf-modal__head\">\n @if (title()) {\n <h2 class=\"kf-modal__title\" [id]=\"titleId\">{{ title() }}</h2>\n }\n @if (dismissible()) {\n <button\n type=\"button\"\n class=\"kf-modal__close\"\n aria-label=\"Cerrar di\u00E1logo\"\n (click)=\"open.set(false)\"\n >\n <kf-icon name=\"x\" size=\"sm\" />\n </button>\n }\n </div>\n <div class=\"kf-modal__body\"><ng-content /></div>\n <div class=\"kf-modal__footer\"><ng-content select=\"[footer]\" /></div>\n </div>\n }\n</dialog>\n", styles: [".kf-modal{width:min(480px,calc(100vw - 2 * var(--kf-space-md)));max-height:calc(100vh - 2 * var(--kf-space-xl));padding:0;border:none;background:transparent;overflow:visible;font-family:var(--kf-font-body)}.kf-modal::backdrop{background:#171d2573}.kf-modal__panel{display:flex;flex-direction:column;gap:var(--kf-space-md);padding:var(--kf-space-lg);background:var(--kf-color-white);border-radius:var(--kf-radius-md);box-shadow:var(--kf-shadow-lg)}.kf-modal__head{display:flex;align-items:flex-start;gap:var(--kf-space-sm)}.kf-modal__title{flex:1;margin:0;font-family:var(--kf-font-title);font-size:1.375rem;font-weight:700;color:var(--kf-color-ink)}.kf-modal__close{flex:none;display:inline-flex;padding:2px;border:none;background:transparent;color:var(--kf-color-text-muted);border-radius:var(--kf-radius-xs);cursor:pointer}.kf-modal__close:hover{color:var(--kf-color-ink)}.kf-modal__close:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-modal__body{color:var(--kf-color-text);overflow-y:auto}.kf-modal__footer:empty{display:none}.kf-modal__footer{display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:var(--kf-space-sm)}.kf-modal__footer-start{margin-right:auto}\n"], dependencies: [{ kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size", "rotate", "filled"] }] });
6603
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Modal, isStandalone: true, selector: "kf-modal", inputs: { title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, dismissible: { classPropertyName: "dismissible", publicName: "dismissible", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange" }, host: { listeners: { "click": "onHostClick($event)" } }, viewQueries: [{ propertyName: "dialog", first: true, predicate: ["dialog"], descendants: true, isSignal: true }], ngImport: i0, template: "<dialog\n #dialog\n class=\"kf-modal\"\n [style.--kf-modal-width]=\"width()\"\n [attr.aria-labelledby]=\"title() ? titleId : null\"\n (cancel)=\"onCancel($event)\"\n (close)=\"open.set(false)\"\n>\n @if (open()) {\n <div class=\"kf-modal__panel\">\n <div class=\"kf-modal__head\">\n @if (title()) {\n <h2 class=\"kf-modal__title\" [id]=\"titleId\">{{ title() }}</h2>\n }\n @if (dismissible()) {\n <button\n type=\"button\"\n class=\"kf-modal__close\"\n aria-label=\"Cerrar di\u00E1logo\"\n (click)=\"open.set(false)\"\n >\n <kf-icon name=\"x\" size=\"sm\" />\n </button>\n }\n </div>\n <div class=\"kf-modal__body\"><ng-content /></div>\n <div class=\"kf-modal__footer\"><ng-content select=\"[footer]\" /></div>\n </div>\n }\n</dialog>\n", styles: [".kf-modal{width:min(var(--kf-modal-width, 480px),calc(100vw - 2 * var(--kf-space-md)));max-height:calc(100vh - 2 * var(--kf-space-xl));padding:0;border:none;background:transparent;overflow:visible;font-family:var(--kf-font-body)}.kf-modal::backdrop{background:#171d2573}.kf-modal__panel{display:flex;flex-direction:column;gap:var(--kf-space-md);padding:var(--kf-space-lg);background:var(--kf-color-white);border-radius:var(--kf-radius-md);box-shadow:var(--kf-shadow-lg)}.kf-modal__head{display:flex;align-items:flex-start;gap:var(--kf-space-sm)}.kf-modal__title{flex:1;margin:0;font-family:var(--kf-font-title);font-size:1.375rem;font-weight:700;color:var(--kf-color-ink)}.kf-modal__close{flex:none;display:inline-flex;padding:2px;border:none;background:transparent;color:var(--kf-color-text-muted);border-radius:var(--kf-radius-xs);cursor:pointer}.kf-modal__close:hover{color:var(--kf-color-ink)}.kf-modal__close:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-modal__body{color:var(--kf-color-text);overflow-y:auto}.kf-modal__footer:empty{display:none}.kf-modal__footer{display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:var(--kf-space-sm)}.kf-modal__footer-start{margin-right:auto}\n"], dependencies: [{ kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size", "rotate", "filled"] }] });
6521
6604
  }
6522
6605
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Modal, decorators: [{
6523
6606
  type: Component,
6524
- args: [{ selector: 'kf-modal', imports: [Icon], template: "<dialog\n #dialog\n class=\"kf-modal\"\n [attr.aria-labelledby]=\"title() ? titleId : null\"\n (cancel)=\"onCancel($event)\"\n (close)=\"open.set(false)\"\n>\n @if (open()) {\n <div class=\"kf-modal__panel\">\n <div class=\"kf-modal__head\">\n @if (title()) {\n <h2 class=\"kf-modal__title\" [id]=\"titleId\">{{ title() }}</h2>\n }\n @if (dismissible()) {\n <button\n type=\"button\"\n class=\"kf-modal__close\"\n aria-label=\"Cerrar di\u00E1logo\"\n (click)=\"open.set(false)\"\n >\n <kf-icon name=\"x\" size=\"sm\" />\n </button>\n }\n </div>\n <div class=\"kf-modal__body\"><ng-content /></div>\n <div class=\"kf-modal__footer\"><ng-content select=\"[footer]\" /></div>\n </div>\n }\n</dialog>\n", styles: [".kf-modal{width:min(480px,calc(100vw - 2 * var(--kf-space-md)));max-height:calc(100vh - 2 * var(--kf-space-xl));padding:0;border:none;background:transparent;overflow:visible;font-family:var(--kf-font-body)}.kf-modal::backdrop{background:#171d2573}.kf-modal__panel{display:flex;flex-direction:column;gap:var(--kf-space-md);padding:var(--kf-space-lg);background:var(--kf-color-white);border-radius:var(--kf-radius-md);box-shadow:var(--kf-shadow-lg)}.kf-modal__head{display:flex;align-items:flex-start;gap:var(--kf-space-sm)}.kf-modal__title{flex:1;margin:0;font-family:var(--kf-font-title);font-size:1.375rem;font-weight:700;color:var(--kf-color-ink)}.kf-modal__close{flex:none;display:inline-flex;padding:2px;border:none;background:transparent;color:var(--kf-color-text-muted);border-radius:var(--kf-radius-xs);cursor:pointer}.kf-modal__close:hover{color:var(--kf-color-ink)}.kf-modal__close:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-modal__body{color:var(--kf-color-text);overflow-y:auto}.kf-modal__footer:empty{display:none}.kf-modal__footer{display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:var(--kf-space-sm)}.kf-modal__footer-start{margin-right:auto}\n"] }]
6525
- }], ctorParameters: () => [], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], dismissible: [{ type: i0.Input, args: [{ isSignal: true, alias: "dismissible", required: false }] }], dialog: [{ type: i0.ViewChild, args: ['dialog', { isSignal: true }] }], onHostClick: [{
6607
+ args: [{ selector: 'kf-modal', imports: [Icon], template: "<dialog\n #dialog\n class=\"kf-modal\"\n [style.--kf-modal-width]=\"width()\"\n [attr.aria-labelledby]=\"title() ? titleId : null\"\n (cancel)=\"onCancel($event)\"\n (close)=\"open.set(false)\"\n>\n @if (open()) {\n <div class=\"kf-modal__panel\">\n <div class=\"kf-modal__head\">\n @if (title()) {\n <h2 class=\"kf-modal__title\" [id]=\"titleId\">{{ title() }}</h2>\n }\n @if (dismissible()) {\n <button\n type=\"button\"\n class=\"kf-modal__close\"\n aria-label=\"Cerrar di\u00E1logo\"\n (click)=\"open.set(false)\"\n >\n <kf-icon name=\"x\" size=\"sm\" />\n </button>\n }\n </div>\n <div class=\"kf-modal__body\"><ng-content /></div>\n <div class=\"kf-modal__footer\"><ng-content select=\"[footer]\" /></div>\n </div>\n }\n</dialog>\n", styles: [".kf-modal{width:min(var(--kf-modal-width, 480px),calc(100vw - 2 * var(--kf-space-md)));max-height:calc(100vh - 2 * var(--kf-space-xl));padding:0;border:none;background:transparent;overflow:visible;font-family:var(--kf-font-body)}.kf-modal::backdrop{background:#171d2573}.kf-modal__panel{display:flex;flex-direction:column;gap:var(--kf-space-md);padding:var(--kf-space-lg);background:var(--kf-color-white);border-radius:var(--kf-radius-md);box-shadow:var(--kf-shadow-lg)}.kf-modal__head{display:flex;align-items:flex-start;gap:var(--kf-space-sm)}.kf-modal__title{flex:1;margin:0;font-family:var(--kf-font-title);font-size:1.375rem;font-weight:700;color:var(--kf-color-ink)}.kf-modal__close{flex:none;display:inline-flex;padding:2px;border:none;background:transparent;color:var(--kf-color-text-muted);border-radius:var(--kf-radius-xs);cursor:pointer}.kf-modal__close:hover{color:var(--kf-color-ink)}.kf-modal__close:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-modal__body{color:var(--kf-color-text);overflow-y:auto}.kf-modal__footer:empty{display:none}.kf-modal__footer{display:flex;flex-wrap:wrap;align-items:center;justify-content:flex-end;gap:var(--kf-space-sm)}.kf-modal__footer-start{margin-right:auto}\n"] }]
6608
+ }], ctorParameters: () => [], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], dismissible: [{ type: i0.Input, args: [{ isSignal: true, alias: "dismissible", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], dialog: [{ type: i0.ViewChild, args: ['dialog', { isSignal: true }] }], onHostClick: [{
6526
6609
  type: HostListener,
6527
6610
  args: ['click', ['$event']]
6528
6611
  }] } });
@@ -7146,7 +7229,7 @@ class QuotePicker {
7146
7229
  fieldColumns = computed(() => this.columns().filter((c) => !c.cardRole || c.cardRole === 'field'), /* @ts-ignore */
7147
7230
  ...(ngDevMode ? [{ debugName: "fieldColumns" }] : /* istanbul ignore next */ []));
7148
7231
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: QuotePicker, deps: [], target: i0.ɵɵFactoryTarget.Component });
7149
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: QuotePicker, isStandalone: true, selector: "kf-quote-picker", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, selectedId: { classPropertyName: "selectedId", publicName: "selectedId", isSignal: true, isRequired: false, transformFunction: null }, idKey: { classPropertyName: "idKey", publicName: "idKey", isSignal: true, isRequired: true, transformFunction: null }, cellTemplates: { classPropertyName: "cellTemplates", publicName: "cellTemplates", isSignal: true, isRequired: false, transformFunction: null }, chips: { classPropertyName: "chips", publicName: "chips", isSignal: true, isRequired: false, transformFunction: null }, sortOptions: { classPropertyName: "sortOptions", publicName: "sortOptions", isSignal: true, isRequired: false, transformFunction: null }, sort: { classPropertyName: "sort", publicName: "sort", isSignal: true, isRequired: false, transformFunction: null }, expandThreshold: { classPropertyName: "expandThreshold", publicName: "expandThreshold", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, searchPlaceholder: { classPropertyName: "searchPlaceholder", publicName: "searchPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, placeholderIcon: { classPropertyName: "placeholderIcon", publicName: "placeholderIcon", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sort: "sortChange", open: "openChange", selectedIdChange: "selectedIdChange", expandToModal: "expandToModal" }, viewQueries: [{ propertyName: "previewScrollEl", first: true, predicate: ["previewScroll"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (variant() === 'inline') {\n <div class=\"kf-quote-picker__inline\">\n <!-- Trigger colapsado (default): ancho completo, nunca una caja angosta. -->\n <button\n type=\"button\"\n class=\"kf-quote-picker__trigger\"\n [attr.aria-expanded]=\"expanded()\"\n (click)=\"toggleExpandedPanel()\"\n >\n <span class=\"kf-quote-picker__thumb\">\n @if (mediaColumn(); as media) {\n @if (cellTemplates()?.[media.key]; as template) {\n <ng-container\n [ngTemplateOutlet]=\"template\"\n [ngTemplateOutletContext]=\"{ $implicit: selectedItem(), index: 0 }\"\n />\n }\n } @else {\n <kf-icon [name]=\"placeholderIcon()\" size=\"md\" />\n }\n </span>\n @if (selectedItem(); as item) {\n <span class=\"kf-quote-picker__trigger-summary\">\n @if (titleColumn(); as title) {\n <span class=\"kf-quote-picker__trigger-field kf-quote-picker__trigger-field--title\">{{\n cellValue(item, title)\n }}</span>\n }\n <!--\n En mobile solo se muestra el t\u00EDtulo (ID) \u2014 mostrar los 5 campos\n en una sola l\u00EDnea de ~90px de ancho real corta el texto sin\n elipsis a mitad del segundo campo (bug real medido:\n scrollWidth=519 vs. clientWidth=87 en 390px). El resto de los\n campos siguen disponibles en el panel expandido y en el modal,\n donde s\u00ED hay espacio horizontal.\n -->\n @if (!isMobile()) {\n @for (col of fieldColumns(); track col.key) {\n <span class=\"kf-quote-picker__trigger-sep\" aria-hidden=\"true\">\u00B7</span>\n <span class=\"kf-quote-picker__trigger-field\">{{ cellValue(item, col) }}</span>\n }\n }\n </span>\n } @else {\n <span class=\"kf-quote-picker__trigger-summary kf-quote-picker__trigger-summary--empty\"\n >Sin selecci\u00F3n</span\n >\n }\n <span class=\"kf-quote-picker__trigger-action\">\n {{ expanded() ? 'Cerrar' : title() }}\n <kf-icon\n name=\"chevron-down\"\n size=\"sm\"\n class=\"kf-quote-picker__chevron\"\n [class.kf-quote-picker__chevron--open]=\"expanded()\"\n />\n </span>\n </button>\n\n @if (expanded()) {\n <div class=\"kf-quote-picker__panel\">\n <div class=\"kf-quote-picker__toolbar\">\n <div class=\"kf-quote-picker__search\">\n <kf-icon name=\"search\" size=\"sm\" />\n <input\n type=\"text\"\n class=\"kf-quote-picker__search-input\"\n [placeholder]=\"searchPlaceholder()\"\n [value]=\"searchQuery()\"\n (input)=\"onSearchInput($event)\"\n />\n </div>\n @for (chip of chips(); track chip.value) {\n <button\n type=\"button\"\n class=\"kf-quote-picker__chip\"\n [class.kf-quote-picker__chip--active]=\"activeChip() === chip.value\"\n (click)=\"selectChip(chip.value)\"\n >\n {{ chip.label }} ({{ chip.count }})\n </button>\n }\n </div>\n\n <div class=\"kf-quote-picker__results-wrap\">\n <div #previewScroll class=\"kf-quote-picker__results\" role=\"listbox\">\n @for (item of previewItems(); track trackItem(item, $index)) {\n <button\n type=\"button\"\n class=\"kf-quote-picker__result\"\n [class.kf-quote-picker__result--active]=\"idKey()(item) === selectedId()\"\n role=\"option\"\n [attr.aria-selected]=\"idKey()(item) === selectedId()\"\n (click)=\"select(item)\"\n (keydown)=\"onItemKeydown($event, item)\"\n >\n <span class=\"kf-quote-picker__thumb kf-quote-picker__thumb--sm\">\n @if (mediaColumn(); as media) {\n @if (cellTemplates()?.[media.key]; as template) {\n <ng-container\n [ngTemplateOutlet]=\"template\"\n [ngTemplateOutletContext]=\"{ $implicit: item, index: $index }\"\n />\n }\n } @else {\n <kf-icon [name]=\"placeholderIcon()\" size=\"sm\" />\n }\n </span>\n @if (titleColumn(); as title) {\n <span\n class=\"kf-quote-picker__result-field kf-quote-picker__result-field--title\"\n >{{ cellValue(item, title) }}</span\n >\n }\n <!--\n Wrapper propio (no los spans sueltos) para los campos\n secundarios: en mobile necesita saltar de l\u00EDnea COMPLETO\n (mismo criterio que \"unidades\" no debe competir con\n \"modelo\" en la primera l\u00EDnea) \u2014 `flex-basis: 100%` en un\n solo elemento fuerza ese salto sin tener que asumir cu\u00E1l\n de los N campos gen\u00E9ricos es el m\u00E1s corto o el m\u00E1s largo.\n -->\n <span class=\"kf-quote-picker__result-fields\">\n @for (col of fieldColumns(); track col.key; let first = $first) {\n <span\n class=\"kf-quote-picker__result-field\"\n [class.kf-quote-picker__result-field--primary]=\"first\"\n >{{ cellValue(item, col) }}</span\n >\n }\n </span>\n </button>\n } @empty {\n <p class=\"kf-quote-picker__empty\">Sin resultados para esta b\u00FAsqueda.</p>\n }\n </div>\n @if (canFadeBottom()) {\n <div class=\"kf-quote-picker__fade\" aria-hidden=\"true\"></div>\n }\n </div>\n\n @if (isHighVolume()) {\n <button type=\"button\" class=\"kf-quote-picker__see-all\" (click)=\"onSeeAll()\">\n Ver las {{ sortedItems().length }} en pantalla completa\n </button>\n }\n </div>\n }\n </div>\n} @else {\n <kf-modal [(open)]=\"open\" [title]=\"title()\" [dismissible]=\"true\">\n <div class=\"kf-quote-picker__modal-toolbar\">\n <div class=\"kf-quote-picker__search\">\n <kf-icon name=\"search\" size=\"sm\" />\n <input\n type=\"text\"\n class=\"kf-quote-picker__search-input\"\n [placeholder]=\"searchPlaceholder()\"\n [value]=\"searchQuery()\"\n (input)=\"onSearchInput($event)\"\n />\n </div>\n <div class=\"kf-quote-picker__modal-filters\">\n <div class=\"kf-quote-picker__modal-chips\">\n @for (chip of chips(); track chip.value) {\n <button\n type=\"button\"\n class=\"kf-quote-picker__chip\"\n [class.kf-quote-picker__chip--active]=\"activeChip() === chip.value\"\n (click)=\"selectChip(chip.value)\"\n >\n {{ chip.label }} ({{ chip.count }})\n </button>\n }\n </div>\n @if (sortOptions().length > 0) {\n <div class=\"kf-quote-picker__sort\">\n <button\n type=\"button\"\n class=\"kf-quote-picker__sort-trigger\"\n [attr.aria-expanded]=\"sortOpen()\"\n (click)=\"toggleSort()\"\n >\n <kf-icon name=\"sort\" size=\"sm\" />\n {{ sortLabel() }}\n <kf-icon name=\"chevron-down\" size=\"sm\" />\n </button>\n @if (sortOpen()) {\n <div class=\"kf-quote-picker__sort-panel\">\n @for (option of sortOptions(); track option.value) {\n <button\n type=\"button\"\n class=\"kf-quote-picker__sort-option\"\n [class.kf-quote-picker__sort-option--active]=\"sort() === option.value\"\n (click)=\"setSort(option.value)\"\n >\n {{ option.label }}\n </button>\n }\n </div>\n }\n </div>\n }\n </div>\n </div>\n\n <kf-data-table\n [columns]=\"columns()\"\n [rows]=\"pagedItems()\"\n [cardable]=\"true\"\n [defaultView]=\"isMobile() ? 'cards' : 'table'\"\n [cellTemplates]=\"cellTemplates()\"\n [rowKey]=\"rowKeyFn\"\n (rowClick)=\"onTableRowClick($event)\"\n >\n <kf-empty-state empty icon=\"file\">Sin resultados para esta b\u00FAsqueda.</kf-empty-state>\n @if (sortedItems().length > 0) {\n <kf-pagination footer [page]=\"page()\" [pages]=\"pages()\" (pageChange)=\"page.set($event)\">\n {{ sortedItems().length }} resultados\n </kf-pagination>\n }\n </kf-data-table>\n </kf-modal>\n}\n", styles: [":host{display:block;font-family:var(--kf-font-body)}.kf-quote-picker__trigger{box-sizing:border-box;display:flex;align-items:center;gap:var(--kf-space-md);width:100%;padding:var(--kf-space-sm) var(--kf-space-md);background:var(--kf-color-white);border:1.5px solid var(--kf-color-primary);border-radius:var(--kf-radius-md);box-shadow:var(--kf-shadow-sm);cursor:pointer;font:inherit;text-align:left}.kf-quote-picker__trigger:hover{box-shadow:var(--kf-shadow-md)}.kf-quote-picker__trigger:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-quote-picker__thumb{display:flex;align-items:center;justify-content:center;width:40px;height:40px;flex:none;border-radius:var(--kf-radius-sm);background:var(--kf-color-surface);color:var(--kf-color-gray-400)}.kf-quote-picker__thumb--sm{width:36px;height:36px}.kf-quote-picker__result--active .kf-quote-picker__thumb{background:var(--kf-color-white);color:var(--kf-color-primary)}.kf-quote-picker__trigger-summary{flex:1;min-width:0;display:flex;align-items:baseline;gap:var(--kf-space-xs);overflow:hidden;white-space:nowrap;color:var(--kf-color-ink)}.kf-quote-picker__trigger-summary--empty{color:var(--kf-color-text-muted)}.kf-quote-picker__trigger-field--title{font-family:var(--kf-font-title);font-weight:700;font-size:1.05rem;color:var(--kf-color-ink)}.kf-quote-picker__trigger-field{font-size:.85rem;color:var(--kf-color-text)}.kf-quote-picker__trigger-sep{color:var(--kf-color-border-strong)}.kf-quote-picker__trigger-action{flex:none;display:flex;align-items:center;gap:6px;color:var(--kf-color-primary);font-size:.85rem;font-weight:600}.kf-quote-picker__chevron{transition:transform .14s}.kf-quote-picker__chevron--open{transform:rotate(180deg)}.kf-quote-picker__panel{display:flex;flex-direction:column;gap:var(--kf-space-md);margin-top:var(--kf-space-sm);padding:var(--kf-space-md) var(--kf-space-lg);background:var(--kf-color-surface);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md)}.kf-quote-picker__toolbar{display:flex;flex-wrap:wrap;gap:var(--kf-space-sm)}.kf-quote-picker__search{flex:1;min-width:200px;display:flex;align-items:center;gap:var(--kf-space-sm);padding:var(--kf-space-sm) var(--kf-space-md);background:var(--kf-color-white);border:1.5px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);color:var(--kf-color-text-muted)}.kf-quote-picker__search:focus-within{border-color:var(--kf-color-primary);box-shadow:var(--kf-shadow-focus)}.kf-quote-picker__search-input{flex:1;border:none;outline:none;background:transparent;font:inherit;color:var(--kf-color-ink)}.kf-quote-picker__search-input::placeholder{color:var(--kf-color-text-muted)}.kf-quote-picker__chip{flex:none;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);font:inherit;font-size:.8rem;font-weight:600;cursor:pointer}.kf-quote-picker__chip--active{background:var(--kf-color-primary);border-color:var(--kf-color-primary);color:var(--kf-color-white)}.kf-quote-picker__results-wrap{position:relative}.kf-quote-picker__results{display:grid;grid-template-columns:repeat(2,1fr);gap:var(--kf-space-sm);max-height:340px;overflow-y:auto}@media(max-width:900px){.kf-quote-picker__results{grid-template-columns:1fr}}.kf-quote-picker__result{box-sizing:border-box;display:flex;align-items:center;gap:var(--kf-space-sm);padding:var(--kf-space-sm) var(--kf-space-md);border:1px solid transparent;border-radius:var(--kf-radius-sm);background:var(--kf-color-white);cursor:pointer;text-align:left;font:inherit;min-width:0;flex-wrap:wrap;row-gap:2px}.kf-quote-picker__result-fields{display:flex;align-items:center;gap:var(--kf-space-sm);min-width:0;flex:1}.kf-quote-picker__result:hover{border-color:var(--kf-color-border)}.kf-quote-picker__result--active{border-color:var(--kf-color-primary);background:var(--kf-color-primary-soft)}.kf-quote-picker__result-field{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:.85rem;color:var(--kf-color-text)}.kf-quote-picker__result-field--title{flex:none;min-width:92px;font-weight:700;color:var(--kf-color-ink)}.kf-quote-picker__result-field--primary{flex:1;min-width:0}@media(max-width:900px){.kf-quote-picker__result-fields{flex-basis:100%;flex-wrap:wrap;row-gap:2px;padding-left:calc(36px + var(--kf-space-sm))}.kf-quote-picker__result-field--primary{flex:none;min-width:0;max-width:100%}}.kf-quote-picker__fade{position:absolute;left:0;right:0;bottom:0;height:36px;background:linear-gradient(to top,var(--kf-color-surface),transparent);pointer-events:none}.kf-quote-picker__see-all{box-sizing:border-box;display:flex;align-items:center;justify-content:center;gap:var(--kf-space-sm);width:100%;margin-top:var(--kf-space-sm);padding:var(--kf-space-sm);border:1.5px dashed var(--kf-color-primary);border-radius:var(--kf-radius-sm);background:var(--kf-color-white);color:var(--kf-color-primary);font:inherit;font-size:.85rem;font-weight:600;cursor:pointer}.kf-quote-picker__see-all:hover{background:var(--kf-color-primary-soft)}.kf-quote-picker__empty{grid-column:1 / -1;padding:var(--kf-space-md);text-align:center;color:var(--kf-color-text-muted);font-size:.85rem;margin:0}.kf-quote-picker__modal-toolbar{display:flex;flex-direction:column;gap:var(--kf-space-sm);margin-bottom:var(--kf-space-md)}.kf-quote-picker__modal-filters{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);flex-wrap:wrap}.kf-quote-picker__modal-chips{display:flex;gap:var(--kf-space-sm);flex-wrap:wrap}.kf-quote-picker__sort{position:relative;flex:none}.kf-quote-picker__sort-trigger{display:flex;align-items:center;gap:var(--kf-space-sm);padding:6px 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);font:inherit;font-size:.8rem;font-weight:600;cursor:pointer}.kf-quote-picker__sort-trigger:hover{border-color:var(--kf-color-border-strong)}.kf-quote-picker__sort-panel{position:absolute;top:calc(100% + 4px);right:0;z-index:1;min-width:180px;padding:var(--kf-space-xs) 0;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-quote-picker__sort-option{display:block;width:100%;padding:var(--kf-space-sm) var(--kf-space-md);border:none;background:transparent;color:var(--kf-color-text);font:inherit;font-size:.85rem;text-align:left;cursor:pointer}.kf-quote-picker__sort-option:hover{background:var(--kf-color-primary-soft)}.kf-quote-picker__sort-option--active{color:var(--kf-color-primary);font-weight:600}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size", "rotate", "filled"] }, { kind: "component", type: Modal, selector: "kf-modal", inputs: ["title", "open", "dismissible"], outputs: ["openChange"] }, { kind: "component", type: DataTable, selector: "kf-data-table", inputs: ["columns", "rows", "loading", "rowKey", "flush", "cardable", "defaultView", "render", "cellTemplates", "density", "rowClickable", "pageSize", "pageSizeOptions", "itemLabel", "sort"], outputs: ["rowClick", "pageChange", "pageSizeChange", "sortChange"] }, { kind: "component", type: EmptyState, selector: "kf-empty-state", inputs: ["icon", "tone"] }, { kind: "component", type: Pagination, selector: "kf-pagination", inputs: ["page", "pages", "pageSizeOptions", "pageSize"], outputs: ["pageChange", "pageSizeChange"] }] });
7232
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: QuotePicker, isStandalone: true, selector: "kf-quote-picker", inputs: { items: { classPropertyName: "items", publicName: "items", isSignal: true, isRequired: true, transformFunction: null }, columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, selectedId: { classPropertyName: "selectedId", publicName: "selectedId", isSignal: true, isRequired: false, transformFunction: null }, idKey: { classPropertyName: "idKey", publicName: "idKey", isSignal: true, isRequired: true, transformFunction: null }, cellTemplates: { classPropertyName: "cellTemplates", publicName: "cellTemplates", isSignal: true, isRequired: false, transformFunction: null }, chips: { classPropertyName: "chips", publicName: "chips", isSignal: true, isRequired: false, transformFunction: null }, sortOptions: { classPropertyName: "sortOptions", publicName: "sortOptions", isSignal: true, isRequired: false, transformFunction: null }, sort: { classPropertyName: "sort", publicName: "sort", isSignal: true, isRequired: false, transformFunction: null }, expandThreshold: { classPropertyName: "expandThreshold", publicName: "expandThreshold", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, searchPlaceholder: { classPropertyName: "searchPlaceholder", publicName: "searchPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, placeholderIcon: { classPropertyName: "placeholderIcon", publicName: "placeholderIcon", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sort: "sortChange", open: "openChange", selectedIdChange: "selectedIdChange", expandToModal: "expandToModal" }, viewQueries: [{ propertyName: "previewScrollEl", first: true, predicate: ["previewScroll"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (variant() === 'inline') {\n <div class=\"kf-quote-picker__inline\">\n <!-- Trigger colapsado (default): ancho completo, nunca una caja angosta. -->\n <button\n type=\"button\"\n class=\"kf-quote-picker__trigger\"\n [attr.aria-expanded]=\"expanded()\"\n (click)=\"toggleExpandedPanel()\"\n >\n <span class=\"kf-quote-picker__thumb\">\n @if (mediaColumn(); as media) {\n @if (cellTemplates()?.[media.key]; as template) {\n <ng-container\n [ngTemplateOutlet]=\"template\"\n [ngTemplateOutletContext]=\"{ $implicit: selectedItem(), index: 0 }\"\n />\n }\n } @else {\n <kf-icon [name]=\"placeholderIcon()\" size=\"md\" />\n }\n </span>\n @if (selectedItem(); as item) {\n <span class=\"kf-quote-picker__trigger-summary\">\n @if (titleColumn(); as title) {\n <span class=\"kf-quote-picker__trigger-field kf-quote-picker__trigger-field--title\">{{\n cellValue(item, title)\n }}</span>\n }\n <!--\n En mobile solo se muestra el t\u00EDtulo (ID) \u2014 mostrar los 5 campos\n en una sola l\u00EDnea de ~90px de ancho real corta el texto sin\n elipsis a mitad del segundo campo (bug real medido:\n scrollWidth=519 vs. clientWidth=87 en 390px). El resto de los\n campos siguen disponibles en el panel expandido y en el modal,\n donde s\u00ED hay espacio horizontal.\n -->\n @if (!isMobile()) {\n @for (col of fieldColumns(); track col.key) {\n <span class=\"kf-quote-picker__trigger-sep\" aria-hidden=\"true\">\u00B7</span>\n <span class=\"kf-quote-picker__trigger-field\">{{ cellValue(item, col) }}</span>\n }\n }\n </span>\n } @else {\n <span class=\"kf-quote-picker__trigger-summary kf-quote-picker__trigger-summary--empty\"\n >Sin selecci\u00F3n</span\n >\n }\n <span class=\"kf-quote-picker__trigger-action\">\n {{ expanded() ? 'Cerrar' : title() }}\n <kf-icon\n name=\"chevron-down\"\n size=\"sm\"\n class=\"kf-quote-picker__chevron\"\n [class.kf-quote-picker__chevron--open]=\"expanded()\"\n />\n </span>\n </button>\n\n @if (expanded()) {\n <div class=\"kf-quote-picker__panel\">\n <div class=\"kf-quote-picker__toolbar\">\n <div class=\"kf-quote-picker__search\">\n <kf-icon name=\"search\" size=\"sm\" />\n <input\n type=\"text\"\n class=\"kf-quote-picker__search-input\"\n [placeholder]=\"searchPlaceholder()\"\n [value]=\"searchQuery()\"\n (input)=\"onSearchInput($event)\"\n />\n </div>\n @for (chip of chips(); track chip.value) {\n <button\n type=\"button\"\n class=\"kf-quote-picker__chip\"\n [class.kf-quote-picker__chip--active]=\"activeChip() === chip.value\"\n (click)=\"selectChip(chip.value)\"\n >\n {{ chip.label }} ({{ chip.count }})\n </button>\n }\n </div>\n\n <div class=\"kf-quote-picker__results-wrap\">\n <div #previewScroll class=\"kf-quote-picker__results\" role=\"listbox\">\n @for (item of previewItems(); track trackItem(item, $index)) {\n <button\n type=\"button\"\n class=\"kf-quote-picker__result\"\n [class.kf-quote-picker__result--active]=\"idKey()(item) === selectedId()\"\n role=\"option\"\n [attr.aria-selected]=\"idKey()(item) === selectedId()\"\n (click)=\"select(item)\"\n (keydown)=\"onItemKeydown($event, item)\"\n >\n <span class=\"kf-quote-picker__thumb kf-quote-picker__thumb--sm\">\n @if (mediaColumn(); as media) {\n @if (cellTemplates()?.[media.key]; as template) {\n <ng-container\n [ngTemplateOutlet]=\"template\"\n [ngTemplateOutletContext]=\"{ $implicit: item, index: $index }\"\n />\n }\n } @else {\n <kf-icon [name]=\"placeholderIcon()\" size=\"sm\" />\n }\n </span>\n @if (titleColumn(); as title) {\n <span\n class=\"kf-quote-picker__result-field kf-quote-picker__result-field--title\"\n >{{ cellValue(item, title) }}</span\n >\n }\n <!--\n Wrapper propio (no los spans sueltos) para los campos\n secundarios: en mobile necesita saltar de l\u00EDnea COMPLETO\n (mismo criterio que \"unidades\" no debe competir con\n \"modelo\" en la primera l\u00EDnea) \u2014 `flex-basis: 100%` en un\n solo elemento fuerza ese salto sin tener que asumir cu\u00E1l\n de los N campos gen\u00E9ricos es el m\u00E1s corto o el m\u00E1s largo.\n -->\n <span class=\"kf-quote-picker__result-fields\">\n @for (col of fieldColumns(); track col.key; let first = $first) {\n <span\n class=\"kf-quote-picker__result-field\"\n [class.kf-quote-picker__result-field--primary]=\"first\"\n >{{ cellValue(item, col) }}</span\n >\n }\n </span>\n </button>\n } @empty {\n <p class=\"kf-quote-picker__empty\">Sin resultados para esta b\u00FAsqueda.</p>\n }\n </div>\n @if (canFadeBottom()) {\n <div class=\"kf-quote-picker__fade\" aria-hidden=\"true\"></div>\n }\n </div>\n\n @if (isHighVolume()) {\n <button type=\"button\" class=\"kf-quote-picker__see-all\" (click)=\"onSeeAll()\">\n Ver las {{ sortedItems().length }} en pantalla completa\n </button>\n }\n </div>\n }\n </div>\n} @else {\n <kf-modal [(open)]=\"open\" [title]=\"title()\" [dismissible]=\"true\">\n <div class=\"kf-quote-picker__modal-toolbar\">\n <div class=\"kf-quote-picker__search\">\n <kf-icon name=\"search\" size=\"sm\" />\n <input\n type=\"text\"\n class=\"kf-quote-picker__search-input\"\n [placeholder]=\"searchPlaceholder()\"\n [value]=\"searchQuery()\"\n (input)=\"onSearchInput($event)\"\n />\n </div>\n <div class=\"kf-quote-picker__modal-filters\">\n <div class=\"kf-quote-picker__modal-chips\">\n @for (chip of chips(); track chip.value) {\n <button\n type=\"button\"\n class=\"kf-quote-picker__chip\"\n [class.kf-quote-picker__chip--active]=\"activeChip() === chip.value\"\n (click)=\"selectChip(chip.value)\"\n >\n {{ chip.label }} ({{ chip.count }})\n </button>\n }\n </div>\n @if (sortOptions().length > 0) {\n <div class=\"kf-quote-picker__sort\">\n <button\n type=\"button\"\n class=\"kf-quote-picker__sort-trigger\"\n [attr.aria-expanded]=\"sortOpen()\"\n (click)=\"toggleSort()\"\n >\n <kf-icon name=\"sort\" size=\"sm\" />\n {{ sortLabel() }}\n <kf-icon name=\"chevron-down\" size=\"sm\" />\n </button>\n @if (sortOpen()) {\n <div class=\"kf-quote-picker__sort-panel\">\n @for (option of sortOptions(); track option.value) {\n <button\n type=\"button\"\n class=\"kf-quote-picker__sort-option\"\n [class.kf-quote-picker__sort-option--active]=\"sort() === option.value\"\n (click)=\"setSort(option.value)\"\n >\n {{ option.label }}\n </button>\n }\n </div>\n }\n </div>\n }\n </div>\n </div>\n\n <kf-data-table\n [columns]=\"columns()\"\n [rows]=\"pagedItems()\"\n [cardable]=\"true\"\n [defaultView]=\"isMobile() ? 'cards' : 'table'\"\n [cellTemplates]=\"cellTemplates()\"\n [rowKey]=\"rowKeyFn\"\n (rowClick)=\"onTableRowClick($event)\"\n >\n <kf-empty-state empty icon=\"file\">Sin resultados para esta b\u00FAsqueda.</kf-empty-state>\n @if (sortedItems().length > 0) {\n <kf-pagination footer [page]=\"page()\" [pages]=\"pages()\" (pageChange)=\"page.set($event)\">\n {{ sortedItems().length }} resultados\n </kf-pagination>\n }\n </kf-data-table>\n </kf-modal>\n}\n", styles: [":host{display:block;font-family:var(--kf-font-body)}.kf-quote-picker__trigger{box-sizing:border-box;display:flex;align-items:center;gap:var(--kf-space-md);width:100%;padding:var(--kf-space-sm) var(--kf-space-md);background:var(--kf-color-white);border:1.5px solid var(--kf-color-primary);border-radius:var(--kf-radius-md);box-shadow:var(--kf-shadow-sm);cursor:pointer;font:inherit;text-align:left}.kf-quote-picker__trigger:hover{box-shadow:var(--kf-shadow-md)}.kf-quote-picker__trigger:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-quote-picker__thumb{display:flex;align-items:center;justify-content:center;width:40px;height:40px;flex:none;border-radius:var(--kf-radius-sm);background:var(--kf-color-surface);color:var(--kf-color-gray-400)}.kf-quote-picker__thumb--sm{width:36px;height:36px}.kf-quote-picker__result--active .kf-quote-picker__thumb{background:var(--kf-color-white);color:var(--kf-color-primary)}.kf-quote-picker__trigger-summary{flex:1;min-width:0;display:flex;align-items:baseline;gap:var(--kf-space-xs);overflow:hidden;white-space:nowrap;color:var(--kf-color-ink)}.kf-quote-picker__trigger-summary--empty{color:var(--kf-color-text-muted)}.kf-quote-picker__trigger-field--title{font-family:var(--kf-font-title);font-weight:700;font-size:1.05rem;color:var(--kf-color-ink)}.kf-quote-picker__trigger-field{font-size:.85rem;color:var(--kf-color-text)}.kf-quote-picker__trigger-sep{color:var(--kf-color-border-strong)}.kf-quote-picker__trigger-action{flex:none;display:flex;align-items:center;gap:6px;color:var(--kf-color-primary);font-size:.85rem;font-weight:600}.kf-quote-picker__chevron{transition:transform .14s}.kf-quote-picker__chevron--open{transform:rotate(180deg)}.kf-quote-picker__panel{display:flex;flex-direction:column;gap:var(--kf-space-md);margin-top:var(--kf-space-sm);padding:var(--kf-space-md) var(--kf-space-lg);background:var(--kf-color-surface);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md)}.kf-quote-picker__toolbar{display:flex;flex-wrap:wrap;gap:var(--kf-space-sm)}.kf-quote-picker__search{flex:1;min-width:200px;display:flex;align-items:center;gap:var(--kf-space-sm);padding:var(--kf-space-sm) var(--kf-space-md);background:var(--kf-color-white);border:1.5px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);color:var(--kf-color-text-muted)}.kf-quote-picker__search:focus-within{border-color:var(--kf-color-primary);box-shadow:var(--kf-shadow-focus)}.kf-quote-picker__search-input{flex:1;border:none;outline:none;background:transparent;font:inherit;color:var(--kf-color-ink)}.kf-quote-picker__search-input::placeholder{color:var(--kf-color-text-muted)}.kf-quote-picker__chip{flex:none;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);font:inherit;font-size:.8rem;font-weight:600;cursor:pointer}.kf-quote-picker__chip--active{background:var(--kf-color-primary);border-color:var(--kf-color-primary);color:var(--kf-color-white)}.kf-quote-picker__results-wrap{position:relative}.kf-quote-picker__results{display:grid;grid-template-columns:repeat(2,1fr);gap:var(--kf-space-sm);max-height:340px;overflow-y:auto}@media(max-width:900px){.kf-quote-picker__results{grid-template-columns:1fr}}.kf-quote-picker__result{box-sizing:border-box;display:flex;align-items:center;gap:var(--kf-space-sm);padding:var(--kf-space-sm) var(--kf-space-md);border:1px solid transparent;border-radius:var(--kf-radius-sm);background:var(--kf-color-white);cursor:pointer;text-align:left;font:inherit;min-width:0;flex-wrap:wrap;row-gap:2px}.kf-quote-picker__result-fields{display:flex;align-items:center;gap:var(--kf-space-sm);min-width:0;flex:1}.kf-quote-picker__result:hover{border-color:var(--kf-color-border)}.kf-quote-picker__result--active{border-color:var(--kf-color-primary);background:var(--kf-color-primary-soft)}.kf-quote-picker__result-field{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;font-size:.85rem;color:var(--kf-color-text)}.kf-quote-picker__result-field--title{flex:none;min-width:92px;font-weight:700;color:var(--kf-color-ink)}.kf-quote-picker__result-field--primary{flex:1;min-width:0}@media(max-width:900px){.kf-quote-picker__result-fields{flex-basis:100%;flex-wrap:wrap;row-gap:2px;padding-left:calc(36px + var(--kf-space-sm))}.kf-quote-picker__result-field--primary{flex:none;min-width:0;max-width:100%}}.kf-quote-picker__fade{position:absolute;left:0;right:0;bottom:0;height:36px;background:linear-gradient(to top,var(--kf-color-surface),transparent);pointer-events:none}.kf-quote-picker__see-all{box-sizing:border-box;display:flex;align-items:center;justify-content:center;gap:var(--kf-space-sm);width:100%;margin-top:var(--kf-space-sm);padding:var(--kf-space-sm);border:1.5px dashed var(--kf-color-primary);border-radius:var(--kf-radius-sm);background:var(--kf-color-white);color:var(--kf-color-primary);font:inherit;font-size:.85rem;font-weight:600;cursor:pointer}.kf-quote-picker__see-all:hover{background:var(--kf-color-primary-soft)}.kf-quote-picker__empty{grid-column:1 / -1;padding:var(--kf-space-md);text-align:center;color:var(--kf-color-text-muted);font-size:.85rem;margin:0}.kf-quote-picker__modal-toolbar{display:flex;flex-direction:column;gap:var(--kf-space-sm);margin-bottom:var(--kf-space-md)}.kf-quote-picker__modal-filters{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);flex-wrap:wrap}.kf-quote-picker__modal-chips{display:flex;gap:var(--kf-space-sm);flex-wrap:wrap}.kf-quote-picker__sort{position:relative;flex:none}.kf-quote-picker__sort-trigger{display:flex;align-items:center;gap:var(--kf-space-sm);padding:6px 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);font:inherit;font-size:.8rem;font-weight:600;cursor:pointer}.kf-quote-picker__sort-trigger:hover{border-color:var(--kf-color-border-strong)}.kf-quote-picker__sort-panel{position:absolute;top:calc(100% + 4px);right:0;z-index:1;min-width:180px;padding:var(--kf-space-xs) 0;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-quote-picker__sort-option{display:block;width:100%;padding:var(--kf-space-sm) var(--kf-space-md);border:none;background:transparent;color:var(--kf-color-text);font:inherit;font-size:.85rem;text-align:left;cursor:pointer}.kf-quote-picker__sort-option:hover{background:var(--kf-color-primary-soft)}.kf-quote-picker__sort-option--active{color:var(--kf-color-primary);font-weight:600}\n"], dependencies: [{ kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size", "rotate", "filled"] }, { kind: "component", type: Modal, selector: "kf-modal", inputs: ["title", "open", "dismissible", "width"], outputs: ["openChange"] }, { kind: "component", type: DataTable, selector: "kf-data-table", inputs: ["columns", "rows", "loading", "rowKey", "flush", "cardable", "defaultView", "render", "cellTemplates", "density", "rowClickable", "pageSize", "pageSizeOptions", "itemLabel", "sort"], outputs: ["rowClick", "pageChange", "pageSizeChange", "sortChange"] }, { kind: "component", type: EmptyState, selector: "kf-empty-state", inputs: ["icon", "tone"] }, { kind: "component", type: Pagination, selector: "kf-pagination", inputs: ["page", "pages", "pageSizeOptions", "pageSize"], outputs: ["pageChange", "pageSizeChange"] }] });
7150
7233
  }
7151
7234
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: QuotePicker, decorators: [{
7152
7235
  type: Component,
@@ -7197,6 +7280,60 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
7197
7280
  args: [{ selector: 'kf-reserve-block', imports: [Icon, Link], template: "<div class=\"kf-reserve-block__empty\">\n <kf-icon name=\"calendar\" size=\"sm\" />\n <span>{{ emptyLabel() }}</span>\n</div>\n<div class=\"kf-reserve-block__invite\" [class.kf-reserve-block__invite--grow]=\"grow()\">\n <span class=\"kf-reserve-block__invite-icon\">\n <kf-icon name=\"tool\" size=\"md\" />\n </span>\n <div class=\"kf-reserve-block__invite-text\">\n <span class=\"kf-reserve-block__invite-title\">{{ title() }}</span>\n @if (description()) {\n <span class=\"kf-reserve-block__invite-description\">{{ description() }}</span>\n }\n @if (actionLabel()) {\n <kf-link class=\"kf-reserve-block__invite-action\" [href]=\"actionHref() || '#'\" showArrow>\n {{ actionLabel() }}\n </kf-link>\n }\n </div>\n</div>\n<div class=\"kf-reserve-block__actions\">\n <ng-content select=\"[actions]\" />\n</div>\n", styles: [":host{display:flex;flex-direction:column;gap:var(--kf-space-sm);padding:var(--kf-space-md);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md);background:var(--kf-color-white);font-family:var(--kf-font-body)}.kf-reserve-block__empty{display:flex;align-items:center;gap:var(--kf-space-xs);padding-bottom:var(--kf-space-sm);border-bottom:1px solid var(--kf-color-border);color:var(--kf-color-text-muted);font-size:.875rem}.kf-reserve-block__invite{display:flex;align-items:center;gap:var(--kf-space-sm);padding:var(--kf-space-sm);border-radius:var(--kf-radius-sm);background:var(--kf-color-primary-soft)}.kf-reserve-block__invite--grow{flex:1}.kf-reserve-block__invite-icon{display:inline-flex;align-items:center;justify-content:center;flex:none;width:2.25rem;height:2.25rem;border-radius:var(--kf-radius-pill);background:var(--kf-color-primary);color:var(--kf-color-white)}.kf-reserve-block__invite-text{display:flex;flex-direction:column;gap:2px;min-width:0}.kf-reserve-block__invite-title{font-family:var(--kf-font-title);font-weight:700;color:var(--kf-color-ink)}.kf-reserve-block__invite-description{font-size:.8125rem;color:var(--kf-color-text-muted)}.kf-reserve-block__invite-action{margin-top:var(--kf-space-xs);font-size:.8125rem;font-weight:600}.kf-reserve-block__actions{display:flex;flex-wrap:wrap;gap:var(--kf-space-sm)}\n"] }]
7198
7281
  }], propDecorators: { emptyLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyLabel", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], description: [{ type: i0.Input, args: [{ isSignal: true, alias: "description", required: false }] }], actionLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "actionLabel", required: false }] }], actionHref: [{ type: i0.Input, args: [{ isSignal: true, alias: "actionHref", required: false }] }], grow: [{ type: i0.Input, args: [{ isSignal: true, alias: "grow", required: false }] }] } });
7199
7282
 
7283
+ /**
7284
+ * Columna "Acciones" de cualquier tabla del ecosistema. Decide su propia
7285
+ * forma según cuántas acciones recibe — no hace falta que cada tabla elija
7286
+ * a mano entre "botones sueltos" y "menú":
7287
+ *
7288
+ * - `inline` (`actions().length <= inlineLimit()`, default 2): un
7289
+ * ícono-botón de 32px por acción, sin texto — el `label` vive en un
7290
+ * `kfTooltip`. Todas las acciones quedan visibles sin clic extra.
7291
+ * - `menu` (más de `inlineLimit()`): un único botón "kebab" que abre un
7292
+ * `kf-dropdown-panel` con una fila por acción.
7293
+ *
7294
+ * `mode` fuerza uno de los dos modos sin importar cuántas acciones haya
7295
+ * (ej. una tabla que prefiere el kebab siempre, aunque tenga una sola
7296
+ * acción, por consistencia visual con otra columna).
7297
+ *
7298
+ * <kf-row-actions [actions]="rowActions(row)" />
7299
+ * <kf-row-actions [actions]="rowActions(row)" mode="menu" />
7300
+ */
7301
+ class RowActions {
7302
+ actions = input.required(/* @ts-ignore */
7303
+ ...(ngDevMode ? [{ debugName: "actions" }] : /* istanbul ignore next */ []));
7304
+ mode = input('auto', /* @ts-ignore */
7305
+ ...(ngDevMode ? [{ debugName: "mode" }] : /* istanbul ignore next */ []));
7306
+ /** Desde cuántas acciones el modo `auto` pasa de `inline` a `menu`. */
7307
+ inlineLimit = input(2, /* @ts-ignore */
7308
+ ...(ngDevMode ? [{ debugName: "inlineLimit" }] : /* istanbul ignore next */ []));
7309
+ menuOpen = signal(false, /* @ts-ignore */
7310
+ ...(ngDevMode ? [{ debugName: "menuOpen" }] : /* istanbul ignore next */ []));
7311
+ effectiveMode = computed(() => {
7312
+ const explicit = this.mode();
7313
+ if (explicit !== 'auto') {
7314
+ return explicit;
7315
+ }
7316
+ return this.actions().length <= this.inlineLimit() ? 'inline' : 'menu';
7317
+ }, /* @ts-ignore */
7318
+ ...(ngDevMode ? [{ debugName: "effectiveMode" }] : /* istanbul ignore next */ []));
7319
+ onToggleMenu() {
7320
+ this.menuOpen.update((open) => !open);
7321
+ }
7322
+ onButtonAction(action) {
7323
+ action.onClick?.();
7324
+ this.menuOpen.set(false);
7325
+ }
7326
+ onMenuLinkClick() {
7327
+ this.menuOpen.set(false);
7328
+ }
7329
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: RowActions, deps: [], target: i0.ɵɵFactoryTarget.Component });
7330
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: RowActions, isStandalone: true, selector: "kf-row-actions", inputs: { actions: { classPropertyName: "actions", publicName: "actions", isSignal: true, isRequired: true, transformFunction: null }, mode: { classPropertyName: "mode", publicName: "mode", isSignal: true, isRequired: false, transformFunction: null }, inlineLimit: { classPropertyName: "inlineLimit", publicName: "inlineLimit", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "@if (effectiveMode() === 'inline') {\n <div class=\"kf-row-actions kf-row-actions--inline\">\n @for (action of actions(); track action.label) {\n @if (action.kind === 'link') {\n @if (action.href && !action.disabled) {\n <a\n class=\"kf-row-actions__icon-btn\"\n [href]=\"action.href\"\n [kfTooltip]=\"action.label\"\n kfTooltipPosition=\"top\"\n >\n <kf-icon [name]=\"action.icon\" size=\"sm\" />\n </a>\n } @else {\n <span\n class=\"kf-row-actions__icon-btn kf-row-actions__icon-btn--disabled\"\n [kfTooltip]=\"action.label\"\n kfTooltipPosition=\"top\"\n >\n <kf-icon [name]=\"action.icon\" size=\"sm\" />\n </span>\n }\n } @else {\n <button\n type=\"button\"\n class=\"kf-row-actions__icon-btn\"\n [disabled]=\"action.disabled ?? false\"\n [kfTooltip]=\"action.label\"\n kfTooltipPosition=\"top\"\n (click)=\"action.onClick?.()\"\n >\n <kf-icon [name]=\"action.icon\" size=\"sm\" />\n </button>\n }\n }\n </div>\n} @else {\n <kf-dropdown class=\"kf-row-actions\" [(open)]=\"menuOpen\">\n <button\n type=\"button\"\n class=\"kf-row-actions__trigger\"\n [class.kf-row-actions__trigger--open]=\"menuOpen()\"\n [attr.aria-expanded]=\"menuOpen()\"\n aria-label=\"M\u00E1s acciones\"\n (click)=\"onToggleMenu()\"\n >\n <kf-icon name=\"more-vertical\" size=\"md\" />\n </button>\n <kf-dropdown-panel>\n @for (action of actions(); track action.label) {\n @if (action.kind === 'link') {\n @if (action.href && !action.disabled) {\n <a class=\"kf-row-actions__menu-link\" [href]=\"action.href\" (click)=\"onMenuLinkClick()\">\n <kf-icon [name]=\"action.icon\" size=\"sm\" />\n <span class=\"kf-row-actions__menu-link-label\">{{ action.label }}</span>\n </a>\n } @else {\n <span class=\"kf-row-actions__menu-link kf-row-actions__menu-link--disabled\">\n <kf-icon [name]=\"action.icon\" size=\"sm\" />\n <span class=\"kf-row-actions__menu-link-label\">{{ action.label }}</span>\n </span>\n }\n } @else {\n <kf-dropdown-item\n [icon]=\"action.icon\"\n [disabled]=\"action.disabled ?? false\"\n (click)=\"onButtonAction(action)\"\n >\n {{ action.label }}\n </kf-dropdown-item>\n }\n }\n </kf-dropdown-panel>\n </kf-dropdown>\n}\n", styles: [":host{display:inline-flex}.kf-row-actions{display:inline-flex}.kf-row-actions--inline{align-items:center;gap:2px}.kf-row-actions__icon-btn{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border:none;border-radius:var(--kf-radius-sm);background:transparent;color:var(--kf-color-text-muted);cursor:pointer;transition:background-color .14s,color .14s}.kf-row-actions__icon-btn:hover{background:var(--kf-color-primary-soft);color:var(--kf-color-primary)}.kf-row-actions__icon-btn:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-row-actions__icon-btn--disabled,.kf-row-actions__icon-btn:disabled{opacity:.45;cursor:not-allowed;pointer-events:none}.kf-row-actions__trigger{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-pill);background:var(--kf-color-white);color:var(--kf-color-text-muted);cursor:pointer;transition:background-color .14s,border-color .14s,color .14s}.kf-row-actions__trigger:hover{border-color:var(--kf-color-border-strong)}.kf-row-actions__trigger:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-row-actions__trigger--open{background:var(--kf-color-primary-soft);border-color:var(--kf-color-primary);color:var(--kf-color-primary)}.kf-row-actions__menu-link{display:flex;align-items:center;gap:var(--kf-space-sm);width:100%;padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);text-decoration:none;cursor:pointer}.kf-row-actions__menu-link kf-icon{flex:none;color:var(--kf-color-text-muted)}.kf-row-actions__menu-link-label{flex:1 1 auto}.kf-row-actions__menu-link:hover{background:var(--kf-color-surface)}.kf-row-actions__menu-link:focus-visible{outline:none;box-shadow:inset var(--kf-shadow-focus)}.kf-row-actions__menu-link--disabled{opacity:.55;cursor:not-allowed;pointer-events:none}\n"], dependencies: [{ kind: "component", type: Dropdown, selector: "kf-dropdown", inputs: ["open", "anchor"], outputs: ["openChange"] }, { kind: "component", type: DropdownPanel, selector: "kf-dropdown-panel", inputs: ["variant"] }, { kind: "component", type: DropdownItem, selector: "kf-dropdown-item", inputs: ["tone", "icon", "disabled"] }, { kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size", "rotate", "filled"] }, { kind: "directive", type: TooltipDirective, selector: "[kfTooltip]", inputs: ["kfTooltip", "kfTooltipPosition"] }] });
7331
+ }
7332
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: RowActions, decorators: [{
7333
+ type: Component,
7334
+ args: [{ selector: 'kf-row-actions', imports: [Dropdown, DropdownPanel, DropdownItem, Icon, TooltipDirective], template: "@if (effectiveMode() === 'inline') {\n <div class=\"kf-row-actions kf-row-actions--inline\">\n @for (action of actions(); track action.label) {\n @if (action.kind === 'link') {\n @if (action.href && !action.disabled) {\n <a\n class=\"kf-row-actions__icon-btn\"\n [href]=\"action.href\"\n [kfTooltip]=\"action.label\"\n kfTooltipPosition=\"top\"\n >\n <kf-icon [name]=\"action.icon\" size=\"sm\" />\n </a>\n } @else {\n <span\n class=\"kf-row-actions__icon-btn kf-row-actions__icon-btn--disabled\"\n [kfTooltip]=\"action.label\"\n kfTooltipPosition=\"top\"\n >\n <kf-icon [name]=\"action.icon\" size=\"sm\" />\n </span>\n }\n } @else {\n <button\n type=\"button\"\n class=\"kf-row-actions__icon-btn\"\n [disabled]=\"action.disabled ?? false\"\n [kfTooltip]=\"action.label\"\n kfTooltipPosition=\"top\"\n (click)=\"action.onClick?.()\"\n >\n <kf-icon [name]=\"action.icon\" size=\"sm\" />\n </button>\n }\n }\n </div>\n} @else {\n <kf-dropdown class=\"kf-row-actions\" [(open)]=\"menuOpen\">\n <button\n type=\"button\"\n class=\"kf-row-actions__trigger\"\n [class.kf-row-actions__trigger--open]=\"menuOpen()\"\n [attr.aria-expanded]=\"menuOpen()\"\n aria-label=\"M\u00E1s acciones\"\n (click)=\"onToggleMenu()\"\n >\n <kf-icon name=\"more-vertical\" size=\"md\" />\n </button>\n <kf-dropdown-panel>\n @for (action of actions(); track action.label) {\n @if (action.kind === 'link') {\n @if (action.href && !action.disabled) {\n <a class=\"kf-row-actions__menu-link\" [href]=\"action.href\" (click)=\"onMenuLinkClick()\">\n <kf-icon [name]=\"action.icon\" size=\"sm\" />\n <span class=\"kf-row-actions__menu-link-label\">{{ action.label }}</span>\n </a>\n } @else {\n <span class=\"kf-row-actions__menu-link kf-row-actions__menu-link--disabled\">\n <kf-icon [name]=\"action.icon\" size=\"sm\" />\n <span class=\"kf-row-actions__menu-link-label\">{{ action.label }}</span>\n </span>\n }\n } @else {\n <kf-dropdown-item\n [icon]=\"action.icon\"\n [disabled]=\"action.disabled ?? false\"\n (click)=\"onButtonAction(action)\"\n >\n {{ action.label }}\n </kf-dropdown-item>\n }\n }\n </kf-dropdown-panel>\n </kf-dropdown>\n}\n", styles: [":host{display:inline-flex}.kf-row-actions{display:inline-flex}.kf-row-actions--inline{align-items:center;gap:2px}.kf-row-actions__icon-btn{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border:none;border-radius:var(--kf-radius-sm);background:transparent;color:var(--kf-color-text-muted);cursor:pointer;transition:background-color .14s,color .14s}.kf-row-actions__icon-btn:hover{background:var(--kf-color-primary-soft);color:var(--kf-color-primary)}.kf-row-actions__icon-btn:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-row-actions__icon-btn--disabled,.kf-row-actions__icon-btn:disabled{opacity:.45;cursor:not-allowed;pointer-events:none}.kf-row-actions__trigger{display:inline-flex;align-items:center;justify-content:center;width:32px;height:32px;border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-pill);background:var(--kf-color-white);color:var(--kf-color-text-muted);cursor:pointer;transition:background-color .14s,border-color .14s,color .14s}.kf-row-actions__trigger:hover{border-color:var(--kf-color-border-strong)}.kf-row-actions__trigger:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-row-actions__trigger--open{background:var(--kf-color-primary-soft);border-color:var(--kf-color-primary);color:var(--kf-color-primary)}.kf-row-actions__menu-link{display:flex;align-items:center;gap:var(--kf-space-sm);width:100%;padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);text-decoration:none;cursor:pointer}.kf-row-actions__menu-link kf-icon{flex:none;color:var(--kf-color-text-muted)}.kf-row-actions__menu-link-label{flex:1 1 auto}.kf-row-actions__menu-link:hover{background:var(--kf-color-surface)}.kf-row-actions__menu-link:focus-visible{outline:none;box-shadow:inset var(--kf-shadow-focus)}.kf-row-actions__menu-link--disabled{opacity:.55;cursor:not-allowed;pointer-events:none}\n"] }]
7335
+ }], propDecorators: { actions: [{ type: i0.Input, args: [{ isSignal: true, alias: "actions", required: true }] }], mode: [{ type: i0.Input, args: [{ isSignal: true, alias: "mode", required: false }] }], inlineLimit: [{ type: i0.Input, args: [{ isSignal: true, alias: "inlineLimit", required: false }] }] } });
7336
+
7200
7337
  /**
7201
7338
  * Título de sub-sección con chip de ícono, pensado para anidarse dentro de
7202
7339
  * cards o paneles (a diferencia de `PageHeader`, que encabeza una página
@@ -8317,5 +8454,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
8317
8454
  * Generated bundle index. Do not edit.
8318
8455
  */
8319
8456
 
8320
- export { ActionTile, AdminBadge, Alert, AlertRow, AuthLayout, AuthLayoutSso, Avatar, Badge, Breadcrumb, BucketProgress, Button, Card, Carousel, CarouselSlide, Checkbox, Chip, Collapsible, CompanySelector, ContentSlot, ContentState, ContentViewer, DEFAULT_PAGE_SIZE_OPTIONS, DataTable, Divider, DocumentRow, Drawer, DrawerLogout, DrawerSession, Dropdown, DropdownItem, DropdownPanel, EmptyState, EventTimeline, FeatureCard, Field, FieldLabel, Filter, FilterChips, FolderTabs, FolderTabsPanel, Footer, FormatInputDirective, Icon, Input, JourneyStepper, Link, MAP_CONFIG, MapControls, MapView, MetricCard, MobileFooter, MobileHeader, Modal, MultiSelect, NoteBlock, PageHeader, Pagination, Panel, PortalHeader, Progress, PulseDot, QuotePicker, ReserveBlock, STYLE_BY_THEME, SectionHeader, SectionNav, SectionNavItem, SectionNavSubsection, Segmented, Select, SelectableCardGroup, SidebarShell, Skeleton, Spinner, SsoButton, StatCard, StatTile, StatusPill, THEME_LABEL, THEME_ORDER, Tabs, Text, Textarea, TileGrid, ToastHost, ToastService, TooltipDirective, VehicleHeader, ViewToggle, dataTableViewSignal, formatChileanRut, formatClockTime, formatCurrency, formatNumber, isValidChileanRut, matchMediaSignal, parseCurrency, parseNumber, provideMapConfig, sanitizeDocumentId };
8457
+ export { ActionTile, AdminBadge, Alert, AlertRow, AuthLayout, AuthLayoutSso, Avatar, Badge, Breadcrumb, BucketProgress, Button, Card, Carousel, CarouselSlide, Checkbox, Chip, Collapsible, CompanySelector, ContactPopover, ContentSlot, ContentState, ContentViewer, DEFAULT_PAGE_SIZE_OPTIONS, DataTable, Divider, DocumentRow, Drawer, DrawerLogout, DrawerSession, Dropdown, DropdownItem, DropdownPanel, EmptyState, EventTimeline, FeatureCard, Field, FieldLabel, Filter, FilterChips, FolderTabs, FolderTabsPanel, Footer, FormatInputDirective, Icon, Input, JourneyStepper, Link, MAP_CONFIG, MapControls, MapView, MetricCard, MobileFooter, MobileHeader, Modal, MultiSelect, NoteBlock, PageHeader, Pagination, Panel, PortalHeader, Progress, PulseDot, QuotePicker, ReserveBlock, RowActions, STYLE_BY_THEME, SectionHeader, SectionNav, SectionNavItem, SectionNavSubsection, Segmented, Select, SelectableCardGroup, SidebarShell, Skeleton, Spinner, SsoButton, StatCard, StatTile, StatusPill, THEME_LABEL, THEME_ORDER, Tabs, Text, Textarea, TileGrid, ToastHost, ToastService, TooltipDirective, VehicleHeader, ViewToggle, dataTableViewSignal, formatChileanRut, formatClockTime, formatCurrency, formatNumber, isValidChileanRut, matchMediaSignal, parseCurrency, parseNumber, provideMapConfig, sanitizeDocumentId };
8321
8458
  //# sourceMappingURL=kaufmann-lab-ui.mjs.map