@kaufmann-lab/ui 0.1.15 → 0.1.17

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.
@@ -6,6 +6,7 @@ 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
8
  import { Subscription } from 'rxjs';
9
+ import { DomSanitizer } from '@angular/platform-browser';
9
10
  import { FocusTrapFactory } from '@angular/cdk/a11y';
10
11
  import mapboxgl from 'mapbox-gl';
11
12
 
@@ -2596,10 +2597,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
2596
2597
  args: [{ selector: 'kf-content-state', imports: [EmptyState, Skeleton, Spinner], template: "@switch (state()) {\n @case ('ready') {\n <ng-content />\n }\n @case ('loading') {\n <div class=\"kf-content-state__loading\" aria-busy=\"true\">\n @if (load() === 'spinner') {\n <kf-spinner [label]=\"loadingLabel()\" />\n } @else {\n <kf-skeleton />\n <kf-skeleton width=\"80%\" />\n <kf-skeleton width=\"60%\" />\n }\n </div>\n }\n @default {\n <kf-empty-state [tone]=\"state() === 'error' ? 'error' : 'neutral'\">\n @if (title()) {\n <p class=\"kf-content-state__title\">{{ title() }}</p>\n }\n @if (subtitle()) {\n <p class=\"kf-content-state__subtitle\">{{ subtitle() }}</p>\n }\n <div class=\"kf-content-state__actions\"><ng-content select=\"[actions]\" /></div>\n </kf-empty-state>\n }\n}\n", styles: [":host{display:block;font-family:var(--kf-font-body)}.kf-content-state__loading{display:flex;flex-direction:column;gap:var(--kf-space-sm)}.kf-content-state__title{margin:0;font-family:var(--kf-font-title);font-weight:700;color:var(--kf-color-ink)}.kf-content-state__subtitle{margin:0;color:var(--kf-color-text-muted)}.kf-content-state__actions:empty{display:none}.kf-content-state__actions{display:flex;flex-wrap:wrap;justify-content:center;gap:var(--kf-space-sm);margin-top:var(--kf-space-sm)}\n"] }]
2597
2598
  }], propDecorators: { state: [{ type: i0.Input, args: [{ isSignal: true, alias: "state", required: true }] }], load: [{ type: i0.Input, args: [{ isSignal: true, alias: "load", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], subtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "subtitle", required: false }] }], loadingLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "loadingLabel", required: false }] }] } });
2598
2599
 
2600
+ const RAW_BASE64_MIME = {
2601
+ image: 'image/jpeg',
2602
+ video: 'video/mp4',
2603
+ pdf: 'application/pdf',
2604
+ };
2605
+ /** Solo contenido ya en memoria: un `<iframe>` con una URL remota arbitraria podría cargar cualquier página. */
2606
+ const LOCAL_PDF_SRC = /^(blob:|data:application\/pdf[;,])/i;
2599
2607
  let contentViewerId = 0;
2600
2608
  /**
2601
- * Visor a pantalla completa para ampliar una pieza de contenido — hoy
2602
- * imagen, con `video` ya soportado para cuando haya contenido de ese tipo.
2609
+ * Visor a pantalla completa para ampliar una pieza de contenido: imagen,
2610
+ * video o PDF (`type="pdf"`, renderizado por el visor nativo del navegador).
2603
2611
  * Mismo mecanismo que `kf-modal` (el `<dialog>` nativo resuelve backdrop,
2604
2612
  * atrapado de foco, top-layer y `Escape`), pero sin panel/título/footer:
2605
2613
  * el contenido ocupa toda la pantalla sobre un backdrop oscuro.
@@ -2607,10 +2615,17 @@ let contentViewerId = 0;
2607
2615
  * `src` acepta lo que venga del backend sin que el consumidor tenga que
2608
2616
  * normalizarlo primero: una URL (`http(s)://`), un data URI ya armado
2609
2617
  * (`data:...`), o base64 crudo (se le antepone el prefijo `data:` según
2610
- * `type`, asumiendo JPEG para imagen y MP4 para video).
2618
+ * `type`, asumiendo JPEG para imagen, MP4 para video y PDF para pdf). Un PDF
2619
+ * solo acepta contenido local (`blob:`, data URI o base64), nunca una URL remota.
2620
+ *
2621
+ * Lo proyectado con el atributo `actions` (p. ej. un botón de descarga) va en
2622
+ * la barra superior, junto al título.
2611
2623
  *
2612
2624
  * <kf-content-viewer [(open)]="viewerOpen" [src]="viewerSrc()" alt="Foto del vehículo" />
2613
2625
  * <kf-content-viewer [(open)]="viewerOpen" type="video" [src]="viewerSrc()" title="Inspección" />
2626
+ * <kf-content-viewer [(open)]="viewerOpen" type="pdf" [src]="blobUrl()" title="Contrato">
2627
+ * <kf-button actions variant="surface" icon="download" (click)="save()">Descargar</kf-button>
2628
+ * </kf-content-viewer>
2614
2629
  */
2615
2630
  class ContentViewer {
2616
2631
  type = input('image', /* @ts-ignore */
@@ -2638,13 +2653,22 @@ class ContentViewer {
2638
2653
  return null;
2639
2654
  if (/^(https?:|data:|blob:)/i.test(raw))
2640
2655
  return raw;
2641
- const mime = this.type() === 'video' ? 'video/mp4' : 'image/jpeg';
2642
- return `data:${mime};base64,${raw}`;
2656
+ return `data:${RAW_BASE64_MIME[this.type()]};base64,${raw}`;
2643
2657
  }, /* @ts-ignore */
2644
2658
  ...(ngDevMode ? [{ debugName: "resolvedSrc" }] : /* istanbul ignore next */ []));
2659
+ pdfSrc = computed(() => {
2660
+ const src = this.resolvedSrc();
2661
+ if (this.type() !== 'pdf' || !src || !LOCAL_PDF_SRC.test(src))
2662
+ return null;
2663
+ return this.sanitizer.bypassSecurityTrustResourceUrl(src);
2664
+ }, /* @ts-ignore */
2665
+ ...(ngDevMode ? [{ debugName: "pdfSrc" }] : /* istanbul ignore next */ []));
2666
+ fallbackLabel = computed(() => ({ image: 'Visor de imagen', video: 'Visor de video', pdf: 'Visor de documento' })[this.type()], /* @ts-ignore */
2667
+ ...(ngDevMode ? [{ debugName: "fallbackLabel" }] : /* istanbul ignore next */ []));
2645
2668
  dialog = viewChild('dialog', /* @ts-ignore */
2646
2669
  ...(ngDevMode ? [{ debugName: "dialog" }] : /* istanbul ignore next */ []));
2647
2670
  document = inject(DOCUMENT);
2671
+ sanitizer = inject(DomSanitizer);
2648
2672
  constructor() {
2649
2673
  afterRenderEffect(() => {
2650
2674
  const el = this.dialog()?.nativeElement;
@@ -2693,11 +2717,11 @@ class ContentViewer {
2693
2717
  }
2694
2718
  }
2695
2719
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: ContentViewer, deps: [], target: i0.ɵɵFactoryTarget.Component });
2696
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: ContentViewer, isStandalone: true, selector: "kf-content-viewer", inputs: { type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: false, transformFunction: null }, alt: { classPropertyName: "alt", publicName: "alt", isSignal: true, isRequired: false, transformFunction: null }, 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-content-viewer\"\n [attr.aria-labelledby]=\"title() ? titleId : null\"\n [attr.aria-label]=\"title() ? null : type() === 'video' ? 'Visor de video' : 'Visor de imagen'\"\n (cancel)=\"onCancel($event)\"\n (close)=\"open.set(false)\"\n>\n @if (open()) {\n <div class=\"kf-content-viewer__panel\">\n @if (dismissible()) {\n <button\n type=\"button\"\n class=\"kf-content-viewer__close\"\n aria-label=\"Cerrar visor\"\n (click)=\"open.set(false)\"\n >\n <kf-icon name=\"x\" size=\"md\" />\n </button>\n }\n @if (title()) {\n <span class=\"kf-content-viewer__title\" [id]=\"titleId\">{{ title() }}</span>\n }\n <div class=\"kf-content-viewer__stage\">\n @if (resolvedSrc(); as src) {\n @if (type() === 'video') {\n <video class=\"kf-content-viewer__media\" [src]=\"src\" controls autoplay></video>\n } @else {\n <img class=\"kf-content-viewer__media\" [src]=\"src\" [alt]=\"alt()\" />\n }\n }\n </div>\n </div>\n }\n</dialog>\n", styles: [".kf-content-viewer{width:100vw;height:100vh;max-width:100vw;max-height:100vh;margin:0;padding:0;border:none;background:transparent;overflow:visible;font-family:var(--kf-font-body)}.kf-content-viewer::backdrop{background:#0a0c10eb}.kf-content-viewer__panel{position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--kf-space-md);width:100%;height:100%;padding:var(--kf-space-xl);box-sizing:border-box}.kf-content-viewer__close{position:absolute;top:var(--kf-space-lg);right:var(--kf-space-lg);display:inline-flex;padding:var(--kf-space-xs);border:none;border-radius:var(--kf-radius-pill);background:#ffffff1f;color:var(--kf-color-white);cursor:pointer}.kf-content-viewer__close:hover{background:#ffffff38}.kf-content-viewer__close:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-content-viewer__title{font-size:.9rem;font-weight:600;color:var(--kf-color-white);text-align:center}.kf-content-viewer__stage{flex:1;display:flex;align-items:center;justify-content:center;min-height:0;width:100%}.kf-content-viewer__media{max-width:100%;max-height:100%;object-fit:contain;border-radius:var(--kf-radius-sm)}\n"], dependencies: [{ kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size", "rotate", "filled"] }] });
2720
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: ContentViewer, isStandalone: true, selector: "kf-content-viewer", inputs: { type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, src: { classPropertyName: "src", publicName: "src", isSignal: true, isRequired: false, transformFunction: null }, alt: { classPropertyName: "alt", publicName: "alt", isSignal: true, isRequired: false, transformFunction: null }, 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-content-viewer\"\n [attr.aria-labelledby]=\"title() ? titleId : null\"\n [attr.aria-label]=\"title() ? null : fallbackLabel()\"\n (cancel)=\"onCancel($event)\"\n (close)=\"open.set(false)\"\n>\n @if (open()) {\n <div class=\"kf-content-viewer__panel\">\n @if (dismissible()) {\n <button\n type=\"button\"\n class=\"kf-content-viewer__close\"\n aria-label=\"Cerrar visor\"\n (click)=\"open.set(false)\"\n >\n <kf-icon name=\"x\" size=\"md\" />\n </button>\n }\n <div class=\"kf-content-viewer__bar\">\n @if (title()) {\n <span class=\"kf-content-viewer__title\" [id]=\"titleId\">{{ title() }}</span>\n }\n <div class=\"kf-content-viewer__actions\"><ng-content select=\"[actions]\" /></div>\n </div>\n <div class=\"kf-content-viewer__stage\">\n @if (type() === 'pdf') {\n @if (pdfSrc(); as src) {\n <iframe\n class=\"kf-content-viewer__document\"\n [src]=\"src\"\n [title]=\"title() || fallbackLabel()\"\n ></iframe>\n }\n } @else if (resolvedSrc(); as src) {\n @if (type() === 'video') {\n <video class=\"kf-content-viewer__media\" [src]=\"src\" controls autoplay></video>\n } @else {\n <img class=\"kf-content-viewer__media\" [src]=\"src\" [alt]=\"alt()\" />\n }\n }\n </div>\n </div>\n }\n</dialog>\n", styles: [".kf-content-viewer{width:100vw;height:100vh;max-width:100vw;max-height:100vh;margin:0;padding:0;border:none;background:transparent;overflow:visible;font-family:var(--kf-font-body)}.kf-content-viewer::backdrop{background:#0a0c10eb}.kf-content-viewer__panel{position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--kf-space-md);width:100%;height:100%;padding:var(--kf-space-xl);box-sizing:border-box}.kf-content-viewer__close{position:absolute;top:var(--kf-space-lg);right:var(--kf-space-lg);display:inline-flex;padding:var(--kf-space-xs);border:none;border-radius:var(--kf-radius-pill);background:#ffffff1f;color:var(--kf-color-white);cursor:pointer}.kf-content-viewer__close:hover{background:#ffffff38}.kf-content-viewer__close:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-content-viewer__bar{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:var(--kf-space-md);max-width:calc(100% - 2 * var(--kf-space-xl))}.kf-content-viewer__actions{display:contents}.kf-content-viewer__title{font-size:.9rem;font-weight:600;color:var(--kf-color-white);text-align:center}.kf-content-viewer__stage{flex:1;display:flex;align-items:center;justify-content:center;min-height:0;width:100%}.kf-content-viewer__document{width:min(100%,1100px);height:100%;border:none;border-radius:var(--kf-radius-sm);background:var(--kf-color-white)}.kf-content-viewer__media{max-width:100%;max-height:100%;object-fit:contain;border-radius:var(--kf-radius-sm)}\n"], dependencies: [{ kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size", "rotate", "filled"] }] });
2697
2721
  }
2698
2722
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: ContentViewer, decorators: [{
2699
2723
  type: Component,
2700
- args: [{ selector: 'kf-content-viewer', imports: [Icon], template: "<dialog\n #dialog\n class=\"kf-content-viewer\"\n [attr.aria-labelledby]=\"title() ? titleId : null\"\n [attr.aria-label]=\"title() ? null : type() === 'video' ? 'Visor de video' : 'Visor de imagen'\"\n (cancel)=\"onCancel($event)\"\n (close)=\"open.set(false)\"\n>\n @if (open()) {\n <div class=\"kf-content-viewer__panel\">\n @if (dismissible()) {\n <button\n type=\"button\"\n class=\"kf-content-viewer__close\"\n aria-label=\"Cerrar visor\"\n (click)=\"open.set(false)\"\n >\n <kf-icon name=\"x\" size=\"md\" />\n </button>\n }\n @if (title()) {\n <span class=\"kf-content-viewer__title\" [id]=\"titleId\">{{ title() }}</span>\n }\n <div class=\"kf-content-viewer__stage\">\n @if (resolvedSrc(); as src) {\n @if (type() === 'video') {\n <video class=\"kf-content-viewer__media\" [src]=\"src\" controls autoplay></video>\n } @else {\n <img class=\"kf-content-viewer__media\" [src]=\"src\" [alt]=\"alt()\" />\n }\n }\n </div>\n </div>\n }\n</dialog>\n", styles: [".kf-content-viewer{width:100vw;height:100vh;max-width:100vw;max-height:100vh;margin:0;padding:0;border:none;background:transparent;overflow:visible;font-family:var(--kf-font-body)}.kf-content-viewer::backdrop{background:#0a0c10eb}.kf-content-viewer__panel{position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--kf-space-md);width:100%;height:100%;padding:var(--kf-space-xl);box-sizing:border-box}.kf-content-viewer__close{position:absolute;top:var(--kf-space-lg);right:var(--kf-space-lg);display:inline-flex;padding:var(--kf-space-xs);border:none;border-radius:var(--kf-radius-pill);background:#ffffff1f;color:var(--kf-color-white);cursor:pointer}.kf-content-viewer__close:hover{background:#ffffff38}.kf-content-viewer__close:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-content-viewer__title{font-size:.9rem;font-weight:600;color:var(--kf-color-white);text-align:center}.kf-content-viewer__stage{flex:1;display:flex;align-items:center;justify-content:center;min-height:0;width:100%}.kf-content-viewer__media{max-width:100%;max-height:100%;object-fit:contain;border-radius:var(--kf-radius-sm)}\n"] }]
2724
+ args: [{ selector: 'kf-content-viewer', imports: [Icon], template: "<dialog\n #dialog\n class=\"kf-content-viewer\"\n [attr.aria-labelledby]=\"title() ? titleId : null\"\n [attr.aria-label]=\"title() ? null : fallbackLabel()\"\n (cancel)=\"onCancel($event)\"\n (close)=\"open.set(false)\"\n>\n @if (open()) {\n <div class=\"kf-content-viewer__panel\">\n @if (dismissible()) {\n <button\n type=\"button\"\n class=\"kf-content-viewer__close\"\n aria-label=\"Cerrar visor\"\n (click)=\"open.set(false)\"\n >\n <kf-icon name=\"x\" size=\"md\" />\n </button>\n }\n <div class=\"kf-content-viewer__bar\">\n @if (title()) {\n <span class=\"kf-content-viewer__title\" [id]=\"titleId\">{{ title() }}</span>\n }\n <div class=\"kf-content-viewer__actions\"><ng-content select=\"[actions]\" /></div>\n </div>\n <div class=\"kf-content-viewer__stage\">\n @if (type() === 'pdf') {\n @if (pdfSrc(); as src) {\n <iframe\n class=\"kf-content-viewer__document\"\n [src]=\"src\"\n [title]=\"title() || fallbackLabel()\"\n ></iframe>\n }\n } @else if (resolvedSrc(); as src) {\n @if (type() === 'video') {\n <video class=\"kf-content-viewer__media\" [src]=\"src\" controls autoplay></video>\n } @else {\n <img class=\"kf-content-viewer__media\" [src]=\"src\" [alt]=\"alt()\" />\n }\n }\n </div>\n </div>\n }\n</dialog>\n", styles: [".kf-content-viewer{width:100vw;height:100vh;max-width:100vw;max-height:100vh;margin:0;padding:0;border:none;background:transparent;overflow:visible;font-family:var(--kf-font-body)}.kf-content-viewer::backdrop{background:#0a0c10eb}.kf-content-viewer__panel{position:relative;display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--kf-space-md);width:100%;height:100%;padding:var(--kf-space-xl);box-sizing:border-box}.kf-content-viewer__close{position:absolute;top:var(--kf-space-lg);right:var(--kf-space-lg);display:inline-flex;padding:var(--kf-space-xs);border:none;border-radius:var(--kf-radius-pill);background:#ffffff1f;color:var(--kf-color-white);cursor:pointer}.kf-content-viewer__close:hover{background:#ffffff38}.kf-content-viewer__close:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-content-viewer__bar{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:var(--kf-space-md);max-width:calc(100% - 2 * var(--kf-space-xl))}.kf-content-viewer__actions{display:contents}.kf-content-viewer__title{font-size:.9rem;font-weight:600;color:var(--kf-color-white);text-align:center}.kf-content-viewer__stage{flex:1;display:flex;align-items:center;justify-content:center;min-height:0;width:100%}.kf-content-viewer__document{width:min(100%,1100px);height:100%;border:none;border-radius:var(--kf-radius-sm);background:var(--kf-color-white)}.kf-content-viewer__media{max-width:100%;max-height:100%;object-fit:contain;border-radius:var(--kf-radius-sm)}\n"] }]
2701
2725
  }], ctorParameters: () => [], propDecorators: { type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], src: [{ type: i0.Input, args: [{ isSignal: true, alias: "src", required: false }] }], alt: [{ type: i0.Input, args: [{ isSignal: true, alias: "alt", required: false }] }], 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: [{
2702
2726
  type: HostListener,
2703
2727
  args: ['click', ['$event']]
@@ -7428,11 +7452,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
7428
7452
  */
7429
7453
  class SectionNav {
7430
7454
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: SectionNav, deps: [], target: i0.ɵɵFactoryTarget.Component });
7431
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.0", type: SectionNav, isStandalone: true, selector: "kf-section-nav", ngImport: i0, template: "<div class=\"kf-section-nav\">\n <ng-content />\n</div>\n", styles: [":host{display:block}.kf-section-nav{display:grid;grid-template-columns:minmax(220px,280px) 1fr;column-gap:var(--kf-space-lg);align-items:start}@media(max-width:1023px){.kf-section-nav{display:flex;flex-direction:column;gap:var(--kf-space-sm);align-items:stretch}}\n"] });
7455
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "22.1.0", type: SectionNav, isStandalone: true, selector: "kf-section-nav", ngImport: i0, template: "<div class=\"kf-section-nav\">\n <ng-content />\n</div>\n", styles: [":host{display:block}.kf-section-nav{display:grid;grid-template-columns:minmax(220px,280px) minmax(0,1fr);column-gap:var(--kf-space-lg);align-items:start}@media(max-width:1023px){.kf-section-nav{display:flex;flex-direction:column;gap:var(--kf-space-sm);align-items:stretch}}\n"] });
7432
7456
  }
7433
7457
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: SectionNav, decorators: [{
7434
7458
  type: Component,
7435
- args: [{ selector: 'kf-section-nav', template: "<div class=\"kf-section-nav\">\n <ng-content />\n</div>\n", styles: [":host{display:block}.kf-section-nav{display:grid;grid-template-columns:minmax(220px,280px) 1fr;column-gap:var(--kf-space-lg);align-items:start}@media(max-width:1023px){.kf-section-nav{display:flex;flex-direction:column;gap:var(--kf-space-sm);align-items:stretch}}\n"] }]
7459
+ args: [{ selector: 'kf-section-nav', template: "<div class=\"kf-section-nav\">\n <ng-content />\n</div>\n", styles: [":host{display:block}.kf-section-nav{display:grid;grid-template-columns:minmax(220px,280px) minmax(0,1fr);column-gap:var(--kf-space-lg);align-items:start}@media(max-width:1023px){.kf-section-nav{display:flex;flex-direction:column;gap:var(--kf-space-sm);align-items:stretch}}\n"] }]
7436
7460
  }] });
7437
7461
 
7438
7462
  /**
@@ -7486,11 +7510,11 @@ class SectionNavItem {
7486
7510
  this.subsectionSelected.emit(key);
7487
7511
  }
7488
7512
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: SectionNavItem, deps: [], target: i0.ɵɵFactoryTarget.Component });
7489
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: SectionNavItem, isStandalone: true, selector: "kf-section-nav-item", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, active: { classPropertyName: "active", publicName: "active", isSignal: true, isRequired: false, transformFunction: null }, subsections: { classPropertyName: "subsections", publicName: "subsections", isSignal: true, isRequired: false, transformFunction: null }, activeSubsection: { classPropertyName: "activeSubsection", publicName: "activeSubsection", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { toggled: "toggled", subsectionSelected: "subsectionSelected" }, ngImport: i0, template: "<button\n type=\"button\"\n class=\"kf-section-nav-item__header\"\n [class.kf-section-nav-item__header--active]=\"active()\"\n [attr.aria-expanded]=\"active()\"\n (click)=\"toggled.emit()\"\n>\n <kf-icon\n class=\"kf-section-nav-item__chevron kf-section-nav-item__chevron--desktop\"\n name=\"chevron-right\"\n size=\"sm\"\n />\n @if (icon(); as iconName) {\n <kf-icon class=\"kf-section-nav-item__icon\" [name]=\"iconName\" size=\"sm\" />\n }\n <span class=\"kf-section-nav-item__label\">{{ label() }}</span>\n <kf-icon\n class=\"kf-section-nav-item__chevron kf-section-nav-item__chevron--mobile\"\n name=\"chevron-down\"\n size=\"sm\"\n />\n</button>\n\n@if (showSubsections()) {\n <div class=\"kf-section-nav-item__subsections\" role=\"group\" [attr.aria-label]=\"label()\">\n @for (sub of subsections(); track sub.key) {\n <button\n type=\"button\"\n class=\"kf-section-nav-item__subsection\"\n [class.kf-section-nav-item__subsection--active]=\"activeSubsection() === sub.key\"\n [disabled]=\"!!sub.disabled\"\n [attr.aria-current]=\"activeSubsection() === sub.key ? 'true' : null\"\n (click)=\"onSubsectionClick(sub.key, sub.disabled, $event)\"\n >\n <kf-icon [name]=\"sub.icon\" size=\"sm\" />\n <span>{{ sub.label }}</span>\n </button>\n }\n </div>\n}\n\n<div class=\"kf-section-nav-item__content\" [hidden]=\"!active()\">\n <ng-content />\n</div>\n", styles: [":host{display:contents}.kf-section-nav-item__header{grid-column:1;display:flex;align-items:center;gap:var(--kf-space-sm);width:100%;font:inherit;font-weight:600;color:var(--kf-color-text);background:none;border:none;border-radius:var(--kf-radius-sm);padding:var(--kf-space-sm) var(--kf-space-md);cursor:pointer;text-align:left}.kf-section-nav-item__header:hover:not(.kf-section-nav-item__header--active){background:var(--kf-color-surface)}.kf-section-nav-item__header:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-section-nav-item__header--active{background:var(--kf-color-primary-soft);color:var(--kf-color-primary-active)}.kf-section-nav-item__icon{flex:none}.kf-section-nav-item__label{flex:1}.kf-section-nav-item__chevron{flex:none;color:var(--kf-color-text-muted)}.kf-section-nav-item__header--active .kf-section-nav-item__icon,.kf-section-nav-item__header--active .kf-section-nav-item__chevron{color:var(--kf-color-primary-active)}.kf-section-nav-item__chevron--mobile{display:none}.kf-section-nav-item__subsections{grid-column:1;display:flex;flex-direction:column;gap:2px;margin:2px 0 var(--kf-space-xs) var(--kf-space-lg)}.kf-section-nav-item__subsection{display:flex;align-items:center;gap:var(--kf-space-sm);border:none;border-radius:var(--kf-radius-sm);background:none;color:var(--kf-color-text-muted);font:inherit;font-size:.875rem;font-weight:600;padding:var(--kf-space-xs) var(--kf-space-md);cursor:pointer;text-align:left}.kf-section-nav-item__subsection:hover:not(.kf-section-nav-item__subsection--active):not(:disabled){background:var(--kf-color-surface);color:var(--kf-color-text)}.kf-section-nav-item__subsection:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-section-nav-item__subsection:disabled{opacity:.5;cursor:not-allowed}.kf-section-nav-item__subsection--active{background:var(--kf-color-primary-soft);color:var(--kf-color-primary-active)}.kf-section-nav-item__content{grid-column:2;grid-row:1/span 20;align-self:start;padding:var(--kf-space-lg);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md);background:var(--kf-color-white)}@media(max-width:1023px){:host{display:block;border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);background:var(--kf-color-white)}.kf-section-nav-item__header{justify-content:space-between;padding:var(--kf-space-md)}.kf-section-nav-item__chevron--desktop{display:none}.kf-section-nav-item__chevron--mobile{display:flex;transition:transform .18s ease}.kf-section-nav-item__header--active .kf-section-nav-item__chevron--mobile{transform:rotate(180deg)}.kf-section-nav-item__subsections{margin-inline:var(--kf-space-md)}.kf-section-nav-item__content{border:none;border-radius:0;padding:var(--kf-space-md)}}\n"], dependencies: [{ kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size", "rotate", "filled"] }] });
7513
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: SectionNavItem, isStandalone: true, selector: "kf-section-nav-item", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, active: { classPropertyName: "active", publicName: "active", isSignal: true, isRequired: false, transformFunction: null }, subsections: { classPropertyName: "subsections", publicName: "subsections", isSignal: true, isRequired: false, transformFunction: null }, activeSubsection: { classPropertyName: "activeSubsection", publicName: "activeSubsection", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { toggled: "toggled", subsectionSelected: "subsectionSelected" }, ngImport: i0, template: "<button\n type=\"button\"\n class=\"kf-section-nav-item__header\"\n [class.kf-section-nav-item__header--active]=\"active()\"\n [attr.aria-expanded]=\"active()\"\n (click)=\"toggled.emit()\"\n>\n <kf-icon\n class=\"kf-section-nav-item__chevron kf-section-nav-item__chevron--desktop\"\n name=\"chevron-right\"\n size=\"sm\"\n />\n @if (icon(); as iconName) {\n <kf-icon class=\"kf-section-nav-item__icon\" [name]=\"iconName\" size=\"sm\" />\n }\n <span class=\"kf-section-nav-item__label\">{{ label() }}</span>\n <kf-icon\n class=\"kf-section-nav-item__chevron kf-section-nav-item__chevron--mobile\"\n name=\"chevron-down\"\n size=\"sm\"\n />\n</button>\n\n@if (showSubsections()) {\n <div class=\"kf-section-nav-item__subsections\" role=\"group\" [attr.aria-label]=\"label()\">\n @for (sub of subsections(); track sub.key) {\n <button\n type=\"button\"\n class=\"kf-section-nav-item__subsection\"\n [class.kf-section-nav-item__subsection--active]=\"activeSubsection() === sub.key\"\n [disabled]=\"!!sub.disabled\"\n [attr.aria-current]=\"activeSubsection() === sub.key ? 'true' : null\"\n (click)=\"onSubsectionClick(sub.key, sub.disabled, $event)\"\n >\n <kf-icon [name]=\"sub.icon\" size=\"sm\" />\n <span>{{ sub.label }}</span>\n </button>\n }\n </div>\n}\n\n<div class=\"kf-section-nav-item__content\" [hidden]=\"!active()\">\n <ng-content />\n</div>\n", styles: [":host{display:contents}.kf-section-nav-item__header{grid-column:1;display:flex;align-items:center;gap:var(--kf-space-sm);width:100%;font:inherit;font-weight:600;color:var(--kf-color-text);background:none;border:none;border-radius:var(--kf-radius-sm);padding:var(--kf-space-sm) var(--kf-space-md);cursor:pointer;text-align:left}.kf-section-nav-item__header:hover:not(.kf-section-nav-item__header--active){background:var(--kf-color-surface)}.kf-section-nav-item__header:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-section-nav-item__header--active{background:var(--kf-color-primary-soft);color:var(--kf-color-primary-active)}.kf-section-nav-item__icon{flex:none}.kf-section-nav-item__label{flex:1}.kf-section-nav-item__chevron{flex:none;color:var(--kf-color-text-muted)}.kf-section-nav-item__header--active .kf-section-nav-item__icon,.kf-section-nav-item__header--active .kf-section-nav-item__chevron{color:var(--kf-color-primary-active)}.kf-section-nav-item__chevron--mobile{display:none}.kf-section-nav-item__subsections{grid-column:1;display:flex;flex-direction:column;gap:2px;margin:2px 0 var(--kf-space-xs) var(--kf-space-lg)}.kf-section-nav-item__subsection{display:flex;align-items:center;gap:var(--kf-space-sm);border:none;border-radius:var(--kf-radius-sm);background:none;color:var(--kf-color-text-muted);font:inherit;font-size:.875rem;font-weight:600;padding:var(--kf-space-xs) var(--kf-space-md);cursor:pointer;text-align:left}.kf-section-nav-item__subsection:hover:not(.kf-section-nav-item__subsection--active):not(:disabled){background:var(--kf-color-surface);color:var(--kf-color-text)}.kf-section-nav-item__subsection:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-section-nav-item__subsection:disabled{opacity:.5;cursor:not-allowed}.kf-section-nav-item__subsection--active{background:var(--kf-color-primary-soft);color:var(--kf-color-primary-active)}.kf-section-nav-item__content{grid-column:2;grid-row:1/span 20;min-width:0;align-self:start;padding:var(--kf-space-lg);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md);background:var(--kf-color-white);animation:kf-section-nav-item-reveal .16s ease-out}@keyframes kf-section-nav-item-reveal{0%{opacity:0;transform:translateY(-6px)}}@media(max-width:1023px){:host{display:block;border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);background:var(--kf-color-white)}.kf-section-nav-item__header{justify-content:space-between;padding:var(--kf-space-md)}.kf-section-nav-item__chevron--desktop{display:none}.kf-section-nav-item__chevron--mobile{display:flex;transition:transform .18s ease}.kf-section-nav-item__header--active .kf-section-nav-item__chevron--mobile{transform:rotate(180deg)}.kf-section-nav-item__subsections{margin-inline:var(--kf-space-md)}.kf-section-nav-item__content{border:none;border-radius:0;padding:var(--kf-space-md)}}\n"], dependencies: [{ kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size", "rotate", "filled"] }] });
7490
7514
  }
7491
7515
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: SectionNavItem, decorators: [{
7492
7516
  type: Component,
7493
- args: [{ selector: 'kf-section-nav-item', imports: [Icon], template: "<button\n type=\"button\"\n class=\"kf-section-nav-item__header\"\n [class.kf-section-nav-item__header--active]=\"active()\"\n [attr.aria-expanded]=\"active()\"\n (click)=\"toggled.emit()\"\n>\n <kf-icon\n class=\"kf-section-nav-item__chevron kf-section-nav-item__chevron--desktop\"\n name=\"chevron-right\"\n size=\"sm\"\n />\n @if (icon(); as iconName) {\n <kf-icon class=\"kf-section-nav-item__icon\" [name]=\"iconName\" size=\"sm\" />\n }\n <span class=\"kf-section-nav-item__label\">{{ label() }}</span>\n <kf-icon\n class=\"kf-section-nav-item__chevron kf-section-nav-item__chevron--mobile\"\n name=\"chevron-down\"\n size=\"sm\"\n />\n</button>\n\n@if (showSubsections()) {\n <div class=\"kf-section-nav-item__subsections\" role=\"group\" [attr.aria-label]=\"label()\">\n @for (sub of subsections(); track sub.key) {\n <button\n type=\"button\"\n class=\"kf-section-nav-item__subsection\"\n [class.kf-section-nav-item__subsection--active]=\"activeSubsection() === sub.key\"\n [disabled]=\"!!sub.disabled\"\n [attr.aria-current]=\"activeSubsection() === sub.key ? 'true' : null\"\n (click)=\"onSubsectionClick(sub.key, sub.disabled, $event)\"\n >\n <kf-icon [name]=\"sub.icon\" size=\"sm\" />\n <span>{{ sub.label }}</span>\n </button>\n }\n </div>\n}\n\n<div class=\"kf-section-nav-item__content\" [hidden]=\"!active()\">\n <ng-content />\n</div>\n", styles: [":host{display:contents}.kf-section-nav-item__header{grid-column:1;display:flex;align-items:center;gap:var(--kf-space-sm);width:100%;font:inherit;font-weight:600;color:var(--kf-color-text);background:none;border:none;border-radius:var(--kf-radius-sm);padding:var(--kf-space-sm) var(--kf-space-md);cursor:pointer;text-align:left}.kf-section-nav-item__header:hover:not(.kf-section-nav-item__header--active){background:var(--kf-color-surface)}.kf-section-nav-item__header:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-section-nav-item__header--active{background:var(--kf-color-primary-soft);color:var(--kf-color-primary-active)}.kf-section-nav-item__icon{flex:none}.kf-section-nav-item__label{flex:1}.kf-section-nav-item__chevron{flex:none;color:var(--kf-color-text-muted)}.kf-section-nav-item__header--active .kf-section-nav-item__icon,.kf-section-nav-item__header--active .kf-section-nav-item__chevron{color:var(--kf-color-primary-active)}.kf-section-nav-item__chevron--mobile{display:none}.kf-section-nav-item__subsections{grid-column:1;display:flex;flex-direction:column;gap:2px;margin:2px 0 var(--kf-space-xs) var(--kf-space-lg)}.kf-section-nav-item__subsection{display:flex;align-items:center;gap:var(--kf-space-sm);border:none;border-radius:var(--kf-radius-sm);background:none;color:var(--kf-color-text-muted);font:inherit;font-size:.875rem;font-weight:600;padding:var(--kf-space-xs) var(--kf-space-md);cursor:pointer;text-align:left}.kf-section-nav-item__subsection:hover:not(.kf-section-nav-item__subsection--active):not(:disabled){background:var(--kf-color-surface);color:var(--kf-color-text)}.kf-section-nav-item__subsection:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-section-nav-item__subsection:disabled{opacity:.5;cursor:not-allowed}.kf-section-nav-item__subsection--active{background:var(--kf-color-primary-soft);color:var(--kf-color-primary-active)}.kf-section-nav-item__content{grid-column:2;grid-row:1/span 20;align-self:start;padding:var(--kf-space-lg);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md);background:var(--kf-color-white)}@media(max-width:1023px){:host{display:block;border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);background:var(--kf-color-white)}.kf-section-nav-item__header{justify-content:space-between;padding:var(--kf-space-md)}.kf-section-nav-item__chevron--desktop{display:none}.kf-section-nav-item__chevron--mobile{display:flex;transition:transform .18s ease}.kf-section-nav-item__header--active .kf-section-nav-item__chevron--mobile{transform:rotate(180deg)}.kf-section-nav-item__subsections{margin-inline:var(--kf-space-md)}.kf-section-nav-item__content{border:none;border-radius:0;padding:var(--kf-space-md)}}\n"] }]
7517
+ args: [{ selector: 'kf-section-nav-item', imports: [Icon], template: "<button\n type=\"button\"\n class=\"kf-section-nav-item__header\"\n [class.kf-section-nav-item__header--active]=\"active()\"\n [attr.aria-expanded]=\"active()\"\n (click)=\"toggled.emit()\"\n>\n <kf-icon\n class=\"kf-section-nav-item__chevron kf-section-nav-item__chevron--desktop\"\n name=\"chevron-right\"\n size=\"sm\"\n />\n @if (icon(); as iconName) {\n <kf-icon class=\"kf-section-nav-item__icon\" [name]=\"iconName\" size=\"sm\" />\n }\n <span class=\"kf-section-nav-item__label\">{{ label() }}</span>\n <kf-icon\n class=\"kf-section-nav-item__chevron kf-section-nav-item__chevron--mobile\"\n name=\"chevron-down\"\n size=\"sm\"\n />\n</button>\n\n@if (showSubsections()) {\n <div class=\"kf-section-nav-item__subsections\" role=\"group\" [attr.aria-label]=\"label()\">\n @for (sub of subsections(); track sub.key) {\n <button\n type=\"button\"\n class=\"kf-section-nav-item__subsection\"\n [class.kf-section-nav-item__subsection--active]=\"activeSubsection() === sub.key\"\n [disabled]=\"!!sub.disabled\"\n [attr.aria-current]=\"activeSubsection() === sub.key ? 'true' : null\"\n (click)=\"onSubsectionClick(sub.key, sub.disabled, $event)\"\n >\n <kf-icon [name]=\"sub.icon\" size=\"sm\" />\n <span>{{ sub.label }}</span>\n </button>\n }\n </div>\n}\n\n<div class=\"kf-section-nav-item__content\" [hidden]=\"!active()\">\n <ng-content />\n</div>\n", styles: [":host{display:contents}.kf-section-nav-item__header{grid-column:1;display:flex;align-items:center;gap:var(--kf-space-sm);width:100%;font:inherit;font-weight:600;color:var(--kf-color-text);background:none;border:none;border-radius:var(--kf-radius-sm);padding:var(--kf-space-sm) var(--kf-space-md);cursor:pointer;text-align:left}.kf-section-nav-item__header:hover:not(.kf-section-nav-item__header--active){background:var(--kf-color-surface)}.kf-section-nav-item__header:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-section-nav-item__header--active{background:var(--kf-color-primary-soft);color:var(--kf-color-primary-active)}.kf-section-nav-item__icon{flex:none}.kf-section-nav-item__label{flex:1}.kf-section-nav-item__chevron{flex:none;color:var(--kf-color-text-muted)}.kf-section-nav-item__header--active .kf-section-nav-item__icon,.kf-section-nav-item__header--active .kf-section-nav-item__chevron{color:var(--kf-color-primary-active)}.kf-section-nav-item__chevron--mobile{display:none}.kf-section-nav-item__subsections{grid-column:1;display:flex;flex-direction:column;gap:2px;margin:2px 0 var(--kf-space-xs) var(--kf-space-lg)}.kf-section-nav-item__subsection{display:flex;align-items:center;gap:var(--kf-space-sm);border:none;border-radius:var(--kf-radius-sm);background:none;color:var(--kf-color-text-muted);font:inherit;font-size:.875rem;font-weight:600;padding:var(--kf-space-xs) var(--kf-space-md);cursor:pointer;text-align:left}.kf-section-nav-item__subsection:hover:not(.kf-section-nav-item__subsection--active):not(:disabled){background:var(--kf-color-surface);color:var(--kf-color-text)}.kf-section-nav-item__subsection:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-section-nav-item__subsection:disabled{opacity:.5;cursor:not-allowed}.kf-section-nav-item__subsection--active{background:var(--kf-color-primary-soft);color:var(--kf-color-primary-active)}.kf-section-nav-item__content{grid-column:2;grid-row:1/span 20;min-width:0;align-self:start;padding:var(--kf-space-lg);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md);background:var(--kf-color-white);animation:kf-section-nav-item-reveal .16s ease-out}@keyframes kf-section-nav-item-reveal{0%{opacity:0;transform:translateY(-6px)}}@media(max-width:1023px){:host{display:block;border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);background:var(--kf-color-white)}.kf-section-nav-item__header{justify-content:space-between;padding:var(--kf-space-md)}.kf-section-nav-item__chevron--desktop{display:none}.kf-section-nav-item__chevron--mobile{display:flex;transition:transform .18s ease}.kf-section-nav-item__header--active .kf-section-nav-item__chevron--mobile{transform:rotate(180deg)}.kf-section-nav-item__subsections{margin-inline:var(--kf-space-md)}.kf-section-nav-item__content{border:none;border-radius:0;padding:var(--kf-space-md)}}\n"] }]
7494
7518
  }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], active: [{ type: i0.Input, args: [{ isSignal: true, alias: "active", required: false }] }], toggled: [{ type: i0.Output, args: ["toggled"] }], subsections: [{ type: i0.Input, args: [{ isSignal: true, alias: "subsections", required: false }] }], activeSubsection: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeSubsection", required: false }] }], subsectionSelected: [{ type: i0.Output, args: ["subsectionSelected"] }] } });
7495
7519
 
7496
7520
  /**