@kaufmann-lab/ui 0.1.8 → 0.1.9

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,5 +1,5 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, inject, computed, effect, Component, booleanAttribute, model, output, numberAttribute, contentChildren, signal, DestroyRef, viewChild, forwardRef, ViewContainerRef, HostListener, TemplateRef, contentChild, ElementRef, ChangeDetectionStrategy, afterRenderEffect, afterNextRender, Injectable } from '@angular/core';
2
+ import { input, inject, computed, effect, Component, booleanAttribute, model, output, numberAttribute, contentChildren, signal, DestroyRef, viewChild, forwardRef, ViewContainerRef, HostListener, TemplateRef, contentChild, ElementRef, Directive, ChangeDetectionStrategy, afterRenderEffect, afterNextRender, Injectable } from '@angular/core';
3
3
  import { DOCUMENT, NgTemplateOutlet } from '@angular/common';
4
4
  import { NG_VALUE_ACCESSOR, FormsModule } from '@angular/forms';
5
5
  import { Overlay } from '@angular/cdk/overlay';
@@ -3345,6 +3345,25 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
3345
3345
  `, styles: [":host{display:block}.kf-filter-chips{display:flex;flex-wrap:wrap;gap:var(--kf-space-xs)}.kf-filter-chips__chip{font:inherit;padding:var(--kf-space-xs) var(--kf-space-md);border-radius:var(--kf-radius-pill);border:1px solid var(--kf-color-border);background:var(--kf-color-surface);color:var(--kf-color-text);cursor:pointer;transition:background .14s,border-color .14s,color .14s}.kf-filter-chips__chip:hover:not(.kf-filter-chips__chip--active){border-color:var(--kf-color-border-strong)}.kf-filter-chips__chip:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-filter-chips__chip--active{background:var(--kf-color-ink);border-color:var(--kf-color-ink);color:var(--kf-color-white)}\n"] }]
3346
3346
  }], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }] } });
3347
3347
 
3348
+ /**
3349
+ * Marca el contenido que `kf-folder-tabs` debe proyectar dentro de su
3350
+ * panel. Sin lógica propia — existe solo como token de tipo para que
3351
+ * `contentChild(FolderTabsPanel)` pueda detectar si el consumidor proyectó
3352
+ * algo (mismo mecanismo que `kf-dropdown` usa con `contentChild(DropdownPanel)`;
3353
+ * acá no hace falta un componente completo, alcanza con una directiva de
3354
+ * atributo porque el panel no necesita comportamiento propio, solo marcar
3355
+ * su presencia).
3356
+ *
3357
+ * <div kf-folder-tabs-panel>...</div>
3358
+ */
3359
+ class FolderTabsPanel {
3360
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: FolderTabsPanel, deps: [], target: i0.ɵɵFactoryTarget.Directive });
3361
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.0", type: FolderTabsPanel, isStandalone: true, selector: "[kf-folder-tabs-panel]", ngImport: i0 });
3362
+ }
3363
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: FolderTabsPanel, decorators: [{
3364
+ type: Directive,
3365
+ args: [{ selector: '[kf-folder-tabs-panel]' }]
3366
+ }] });
3348
3367
  /**
3349
3368
  * Tabs con forma de "carpeta": el borde superior queda redondeado y el borde
3350
3369
  * inferior de la pestaña activa se funde visualmente con el panel de
@@ -3355,9 +3374,40 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
3355
3374
  * `--active` se mueve de un botón a otro, y eso vuelve a disparar el keyframe
3356
3375
  * sin tocar JS.
3357
3376
  *
3358
- * El consumidor es responsable de renderizar el panel de contenido debajo;
3359
- * este componente solo resuelve la fila de pestañas y emite `valueChange`.
3360
- *
3377
+ * El panel de contenido es un `<ng-content>` OPCIONAL — si el consumidor no
3378
+ * proyecta nada, `kf-folder-tabs` renderiza solo la fila de pestañas (mismo
3379
+ * comportamiento que antes de que existiera este slot, detectado con
3380
+ * `contentChild()` sin `.required`, igual patrón que usa `kf-dropdown` para
3381
+ * saber si hay un `kf-dropdown-panel` proyectado). Si SÍ proyecta contenido,
3382
+ * se envuelve en un panel con la esquina superior-izquierda recta (nace ahí
3383
+ * la primera pestaña, siempre pegada al borde izquierdo de la fila — un
3384
+ * radio ahí competiría visualmente con el propio borde redondeado de esa
3385
+ * pestaña) y las otras tres redondeadas. El `border-bottom` de 2px de la
3386
+ * pestaña activa (ver `.kf-folder-tabs__tab--active` más abajo) es la señal
3387
+ * visual de conexión con el panel para CUALQUIER pestaña activa, sin
3388
+ * necesitar mover ni medir nada.
3389
+ *
3390
+ * El borde izquierdo de toda la fila (y por lo tanto de la esquina recta
3391
+ * del panel) depende de que ninguna pestaña se corra al cambiar de estado
3392
+ * — ver `transform-origin: bottom left` en `.kf-folder-tabs__tab`: con el
3393
+ * origen centrado (como estaba antes), el `scale()` de la pestaña inactiva
3394
+ * encogía el borde izquierdo unos px, y ese corrimiento sub-pixel es lo que
3395
+ * hacía que este mismo diseño (esquina fija) se viera "roto" cuando la
3396
+ * pestaña activa no era la primera. Con el origen anclado a la izquierda,
3397
+ * el corrimiento desaparece y la esquina fija vuelve a ser una solución
3398
+ * válida y simple — sin necesitar medir la pestaña activa por JS.
3399
+ *
3400
+ * Antes de este slot, el patrón era que el consumidor pusiera su propia
3401
+ * card como HERMANO de `kf-folder-tabs` y anulara a mano el radio superior
3402
+ * completo (`border-top-left-radius: 0`, `border-top-right-radius: 0` — ver
3403
+ * el caso original de `web-portal-latam-frontend`). Encapsular esto acá
3404
+ * evita que cada consumidor repita esas reglas a mano.
3405
+ *
3406
+ * <kf-folder-tabs [options]="tabs" [value]="active" (valueChange)="active = $event">
3407
+ * <div kf-folder-tabs-panel>contenido de la pestaña activa</div>
3408
+ * </kf-folder-tabs>
3409
+ *
3410
+ * <!-- o, sin contenido, como antes: -->
3361
3411
  * <kf-folder-tabs [options]="tabs" [value]="active" (valueChange)="active = $event" />
3362
3412
  */
3363
3413
  class FolderTabs {
@@ -3366,6 +3416,18 @@ class FolderTabs {
3366
3416
  value = input.required(/* @ts-ignore */
3367
3417
  ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
3368
3418
  valueChange = output();
3419
+ /**
3420
+ * Detecta si el consumidor proyectó contenido marcado con
3421
+ * `kf-folder-tabs-panel` — sin `.required`, así que es `undefined`
3422
+ * cuando no hay nada proyectado (mismo patrón que usa `kf-dropdown` con
3423
+ * `contentChild(DropdownPanel)` para saber si hay un panel que
3424
+ * attachear). `hasPanel()` decide si el wrapper del panel participa del
3425
+ * layout (`display: contents` si no).
3426
+ */
3427
+ projectedPanel = contentChild(FolderTabsPanel, /* @ts-ignore */
3428
+ ...(ngDevMode ? [{ debugName: "projectedPanel" }] : /* istanbul ignore next */ []));
3429
+ hasPanel = computed(() => this.projectedPanel() !== undefined, /* @ts-ignore */
3430
+ ...(ngDevMode ? [{ debugName: "hasPanel" }] : /* istanbul ignore next */ []));
3369
3431
  select(value) {
3370
3432
  if (value === this.value()) {
3371
3433
  return;
@@ -3402,7 +3464,7 @@ class FolderTabs {
3402
3464
  nextButton?.focus();
3403
3465
  }
3404
3466
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: FolderTabs, deps: [], target: i0.ɵɵFactoryTarget.Component });
3405
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: FolderTabs, isStandalone: true, selector: "kf-folder-tabs", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: `
3467
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: FolderTabs, isStandalone: true, selector: "kf-folder-tabs", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { valueChange: "valueChange" }, queries: [{ propertyName: "projectedPanel", first: true, predicate: FolderTabsPanel, descendants: true, isSignal: true }], ngImport: i0, template: `
3406
3468
  <div class="kf-folder-tabs" role="tablist">
3407
3469
  @for (option of options(); track option.value) {
3408
3470
  <button
@@ -3424,7 +3486,11 @@ class FolderTabs {
3424
3486
  </button>
3425
3487
  }
3426
3488
  </div>
3427
- `, isInline: true, styles: [":host{display:block;font-family:var(--kf-font-body)}.kf-folder-tabs{display:flex;align-items:flex-end;gap:var(--kf-space-xs);perspective:900px}.kf-folder-tabs__tab{position:relative;display:inline-flex;align-items:center;gap:var(--kf-space-xs);font:inherit;font-size:.9rem;color:var(--kf-color-text-muted);background:var(--kf-color-surface);border:1px solid var(--kf-color-border);border-bottom-color:var(--kf-color-border-strong);border-radius:var(--kf-radius-sm) var(--kf-radius-sm) 0 0;padding:var(--kf-space-sm) var(--kf-space-md);cursor:pointer;transform-origin:bottom center;transform:translateY(5px) scale(.985);transition:background .14s,color .14s,border-color .14s,transform .22s cubic-bezier(.32,.72,0,1)}.kf-folder-tabs__tab:hover:not(.kf-folder-tabs__tab--active){color:var(--kf-color-text);background:var(--kf-color-white);transform:translateY(2px) scale(.99)}.kf-folder-tabs__tab:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus);z-index:1}.kf-folder-tabs__tab--active{z-index:1;color:var(--kf-color-primary);font-weight:700;background:var(--kf-color-white);border-color:var(--kf-color-border);border-bottom:2px solid var(--kf-color-primary);box-shadow:0 -6px 14px -10px #10182866;transform:translateY(1px);animation:kf-folder-tabs-pull .34s cubic-bezier(.22,1.2,.36,1) both}@keyframes kf-folder-tabs-pull{0%{transform:translateY(7px) rotateX(-24deg) scale(.95)}60%{transform:translateY(-2px) rotateX(3deg) scale(1.02)}to{transform:translateY(1px) rotateX(0) scale(1)}}@media(prefers-reduced-motion:reduce){.kf-folder-tabs__tab,.kf-folder-tabs__tab--active{animation:none;transition:background .14s,color .14s,border-color .14s}}\n"], dependencies: [{ kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size"] }] });
3489
+
3490
+ <div class="kf-folder-tabs__panel" [class.kf-folder-tabs__panel--empty]="!hasPanel()">
3491
+ <ng-content select="[kf-folder-tabs-panel]" />
3492
+ </div>
3493
+ `, isInline: true, styles: [":host{display:block;font-family:var(--kf-font-body)}.kf-folder-tabs{display:flex;align-items:flex-end;gap:var(--kf-space-xs);perspective:900px}.kf-folder-tabs__tab{position:relative;display:inline-flex;align-items:center;gap:var(--kf-space-xs);font:inherit;font-size:.9rem;color:var(--kf-color-text-muted);background:var(--kf-color-surface);border:1px solid var(--kf-color-border);border-bottom-color:var(--kf-color-border-strong);border-radius:var(--kf-radius-sm) var(--kf-radius-sm) 0 0;padding:var(--kf-space-sm) var(--kf-space-md);cursor:pointer;transform-origin:bottom left;transform:translateY(5px) scale(.985);transition:background .14s,color .14s,border-color .14s,transform .22s cubic-bezier(.32,.72,0,1)}.kf-folder-tabs__tab:hover:not(.kf-folder-tabs__tab--active){color:var(--kf-color-text);background:var(--kf-color-white);transform:translateY(2px) scale(.99)}.kf-folder-tabs__tab:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus);z-index:1}.kf-folder-tabs__tab--active{z-index:1;color:var(--kf-color-primary);font-weight:700;background:var(--kf-color-white);border-color:var(--kf-color-border);border-bottom:2px solid var(--kf-color-primary);box-shadow:0 -6px 14px -10px #10182866;transform:translateY(1px);animation:kf-folder-tabs-pull .34s cubic-bezier(.22,1.2,.36,1) both}@keyframes kf-folder-tabs-pull{0%{transform:translateY(7px) rotateX(-10deg) scale(.97)}60%{transform:translateY(1px) rotateX(2deg) scale(1.01)}to{transform:translateY(1px) rotateX(0) scale(1)}}@media(prefers-reduced-motion:reduce){.kf-folder-tabs__tab,.kf-folder-tabs__tab--active{animation:none;transition:background .14s,color .14s,border-color .14s}}.kf-folder-tabs__panel--empty{display:contents}.kf-folder-tabs__panel:not(.kf-folder-tabs__panel--empty){background:var(--kf-color-white);border-right:1px solid var(--kf-color-border);border-bottom:1px solid var(--kf-color-border);border-left:1px solid var(--kf-color-border);border-top:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md);border-top-left-radius:0;overflow:hidden}\n"], dependencies: [{ kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size"] }] });
3428
3494
  }
3429
3495
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: FolderTabs, decorators: [{
3430
3496
  type: Component,
@@ -3450,8 +3516,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
3450
3516
  </button>
3451
3517
  }
3452
3518
  </div>
3453
- `, styles: [":host{display:block;font-family:var(--kf-font-body)}.kf-folder-tabs{display:flex;align-items:flex-end;gap:var(--kf-space-xs);perspective:900px}.kf-folder-tabs__tab{position:relative;display:inline-flex;align-items:center;gap:var(--kf-space-xs);font:inherit;font-size:.9rem;color:var(--kf-color-text-muted);background:var(--kf-color-surface);border:1px solid var(--kf-color-border);border-bottom-color:var(--kf-color-border-strong);border-radius:var(--kf-radius-sm) var(--kf-radius-sm) 0 0;padding:var(--kf-space-sm) var(--kf-space-md);cursor:pointer;transform-origin:bottom center;transform:translateY(5px) scale(.985);transition:background .14s,color .14s,border-color .14s,transform .22s cubic-bezier(.32,.72,0,1)}.kf-folder-tabs__tab:hover:not(.kf-folder-tabs__tab--active){color:var(--kf-color-text);background:var(--kf-color-white);transform:translateY(2px) scale(.99)}.kf-folder-tabs__tab:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus);z-index:1}.kf-folder-tabs__tab--active{z-index:1;color:var(--kf-color-primary);font-weight:700;background:var(--kf-color-white);border-color:var(--kf-color-border);border-bottom:2px solid var(--kf-color-primary);box-shadow:0 -6px 14px -10px #10182866;transform:translateY(1px);animation:kf-folder-tabs-pull .34s cubic-bezier(.22,1.2,.36,1) both}@keyframes kf-folder-tabs-pull{0%{transform:translateY(7px) rotateX(-24deg) scale(.95)}60%{transform:translateY(-2px) rotateX(3deg) scale(1.02)}to{transform:translateY(1px) rotateX(0) scale(1)}}@media(prefers-reduced-motion:reduce){.kf-folder-tabs__tab,.kf-folder-tabs__tab--active{animation:none;transition:background .14s,color .14s,border-color .14s}}\n"] }]
3454
- }], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }] } });
3519
+
3520
+ <div class="kf-folder-tabs__panel" [class.kf-folder-tabs__panel--empty]="!hasPanel()">
3521
+ <ng-content select="[kf-folder-tabs-panel]" />
3522
+ </div>
3523
+ `, styles: [":host{display:block;font-family:var(--kf-font-body)}.kf-folder-tabs{display:flex;align-items:flex-end;gap:var(--kf-space-xs);perspective:900px}.kf-folder-tabs__tab{position:relative;display:inline-flex;align-items:center;gap:var(--kf-space-xs);font:inherit;font-size:.9rem;color:var(--kf-color-text-muted);background:var(--kf-color-surface);border:1px solid var(--kf-color-border);border-bottom-color:var(--kf-color-border-strong);border-radius:var(--kf-radius-sm) var(--kf-radius-sm) 0 0;padding:var(--kf-space-sm) var(--kf-space-md);cursor:pointer;transform-origin:bottom left;transform:translateY(5px) scale(.985);transition:background .14s,color .14s,border-color .14s,transform .22s cubic-bezier(.32,.72,0,1)}.kf-folder-tabs__tab:hover:not(.kf-folder-tabs__tab--active){color:var(--kf-color-text);background:var(--kf-color-white);transform:translateY(2px) scale(.99)}.kf-folder-tabs__tab:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus);z-index:1}.kf-folder-tabs__tab--active{z-index:1;color:var(--kf-color-primary);font-weight:700;background:var(--kf-color-white);border-color:var(--kf-color-border);border-bottom:2px solid var(--kf-color-primary);box-shadow:0 -6px 14px -10px #10182866;transform:translateY(1px);animation:kf-folder-tabs-pull .34s cubic-bezier(.22,1.2,.36,1) both}@keyframes kf-folder-tabs-pull{0%{transform:translateY(7px) rotateX(-10deg) scale(.97)}60%{transform:translateY(1px) rotateX(2deg) scale(1.01)}to{transform:translateY(1px) rotateX(0) scale(1)}}@media(prefers-reduced-motion:reduce){.kf-folder-tabs__tab,.kf-folder-tabs__tab--active{animation:none;transition:background .14s,color .14s,border-color .14s}}.kf-folder-tabs__panel--empty{display:contents}.kf-folder-tabs__panel:not(.kf-folder-tabs__panel--empty){background:var(--kf-color-white);border-right:1px solid var(--kf-color-border);border-bottom:1px solid var(--kf-color-border);border-left:1px solid var(--kf-color-border);border-top:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md);border-top-left-radius:0;overflow:hidden}\n"] }]
3524
+ }], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: true }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], projectedPanel: [{ type: i0.ContentChild, args: [i0.forwardRef(() => FolderTabsPanel), { isSignal: true }] }] } });
3455
3525
 
3456
3526
  /**
3457
3527
  * Input de formulario apilado: label arriba (en mayúsculas vía CSS), campo con
@@ -4552,12 +4622,29 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
4552
4622
  * Es distinto de `kf-page-header`/`kf-page-head` (título de una vista
4553
4623
  * interna) — este es el chrome global del portal, se monta una sola vez.
4554
4624
  *
4625
+ * `:host` es `position: sticky; top: 0` — el header queda pegado arriba al
4626
+ * hacer scroll. No es `fixed`: como no se superpone al contenido en el
4627
+ * primer render, no hace falta que el consumidor compense con
4628
+ * `padding-top`.
4629
+ *
4630
+ * Bajo los 768px, el `<nav>` deja de mostrarse en línea junto al logo y
4631
+ * colapsa detrás de un botón de menú (ícono `menu`/`x`) que despliega un
4632
+ * drawer lateral (desde el borde derecho, `position: fixed` + overlay de
4633
+ * fondo) con los mismos `navItems` en columna. Ese drawer expone un slot
4634
+ * (`<ng-content select="[kf-portal-header-extra]">`) al final, pensado
4635
+ * para que el consumidor proyecte contenido propio que en escritorio vive
4636
+ * en otro lugar (ej. el menú de sesión) — `PortalHeader` no sabe qué es
4637
+ * ese contenido ni cómo se abre, solo le da un lugar dentro del drawer ya
4638
+ * desplegado. El overlay cierra el drawer al tocarlo, y `Escape` también.
4639
+ *
4555
4640
  * <kf-portal-header
4556
4641
  * [isAdmin]="true"
4557
4642
  * [adminActions]="[{ label: 'Consultar deuda clientes', href: '/deuda' }, { label: 'Cambiar DNI', href: '/dni' }]"
4558
4643
  * [navItems]="[{ label: 'Home', href: '/', active: true }, { label: 'Vehículos', href: '/vehiculos' }]"
4559
4644
  * userInitials="TS"
4560
- * />
4645
+ * >
4646
+ * <div kf-portal-header-extra>contenido propio, solo visible en el panel mobile</div>
4647
+ * </kf-portal-header>
4561
4648
  */
4562
4649
  class PortalHeader {
4563
4650
  brand = input('DIVEMOTOR', /* @ts-ignore */
@@ -4570,8 +4657,26 @@ class PortalHeader {
4570
4657
  userInitials = input.required(/* @ts-ignore */
4571
4658
  ...(ngDevMode ? [{ debugName: "userInitials" }] : /* istanbul ignore next */ []));
4572
4659
  sessionClick = output();
4660
+ /** Visibilidad del drawer de navegación mobile (bajo los 768px). */
4661
+ mobileMenuOpen = signal(false, /* @ts-ignore */
4662
+ ...(ngDevMode ? [{ debugName: "mobileMenuOpen" }] : /* istanbul ignore next */ []));
4663
+ /** Cierra el drawer con Escape — mismo patrón que cualquier drawer/modal. */
4664
+ onEscape() {
4665
+ this.mobileMenuOpen.set(false);
4666
+ }
4667
+ /**
4668
+ * `ElementRef` real del botón de sesión (el avatar en la esquina
4669
+ * derecha), no de todo el header. Pensado para anclar un `kf-dropdown`
4670
+ * externo — sin esto, el único `ElementRef` disponible desde afuera es el
4671
+ * del host de `PortalHeader`, que ocupa el ancho completo de la pantalla
4672
+ * (logo + nav + sesión), y el overlay del CDK termina "pegado" a la
4673
+ * esquina del header entero en vez de al botón que el usuario clickeó.
4674
+ *
4675
+ * <kf-dropdown [(open)]="menuOpen" [anchor]="header().sessionButtonRef()">
4676
+ */
4677
+ sessionButtonRef = viewChild.required('sessionButton', { ...(ngDevMode ? { debugName: "sessionButtonRef" } : /* istanbul ignore next */ {}), read: ElementRef });
4573
4678
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: PortalHeader, deps: [], target: i0.ɵɵFactoryTarget.Component });
4574
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: PortalHeader, isStandalone: true, selector: "kf-portal-header", inputs: { brand: { classPropertyName: "brand", publicName: "brand", isSignal: true, isRequired: false, transformFunction: null }, isAdmin: { classPropertyName: "isAdmin", publicName: "isAdmin", isSignal: true, isRequired: false, transformFunction: null }, adminActions: { classPropertyName: "adminActions", publicName: "adminActions", isSignal: true, isRequired: false, transformFunction: null }, navItems: { classPropertyName: "navItems", publicName: "navItems", isSignal: true, isRequired: false, transformFunction: null }, userInitials: { classPropertyName: "userInitials", publicName: "userInitials", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { sessionClick: "sessionClick" }, ngImport: i0, template: `
4679
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: PortalHeader, isStandalone: true, selector: "kf-portal-header", inputs: { brand: { classPropertyName: "brand", publicName: "brand", isSignal: true, isRequired: false, transformFunction: null }, isAdmin: { classPropertyName: "isAdmin", publicName: "isAdmin", isSignal: true, isRequired: false, transformFunction: null }, adminActions: { classPropertyName: "adminActions", publicName: "adminActions", isSignal: true, isRequired: false, transformFunction: null }, navItems: { classPropertyName: "navItems", publicName: "navItems", isSignal: true, isRequired: false, transformFunction: null }, userInitials: { classPropertyName: "userInitials", publicName: "userInitials", isSignal: true, isRequired: true, transformFunction: null } }, outputs: { sessionClick: "sessionClick" }, host: { listeners: { "document:keydown.escape": "onEscape()" } }, viewQueries: [{ propertyName: "sessionButtonRef", first: true, predicate: ["sessionButton"], descendants: true, read: ElementRef, isSignal: true }], ngImport: i0, template: `
4575
4680
  @if (isAdmin()) {
4576
4681
  <div class="kf-portal-header__admin-bar">
4577
4682
  <kf-admin-badge inline />
@@ -4584,6 +4689,7 @@ class PortalHeader {
4584
4689
  }
4585
4690
  <div class="kf-portal-header__brand-bar">
4586
4691
  <span class="kf-portal-header__logo">{{ brand() }}</span>
4692
+
4587
4693
  <nav class="kf-portal-header__nav">
4588
4694
  @for (item of navItems(); track item.label) {
4589
4695
  <a
@@ -4595,15 +4701,54 @@ class PortalHeader {
4595
4701
  </a>
4596
4702
  }
4597
4703
  </nav>
4598
- <button type="button" class="kf-portal-header__session" (click)="sessionClick.emit()">
4704
+
4705
+ <button
4706
+ #sessionButton
4707
+ type="button"
4708
+ class="kf-portal-header__session"
4709
+ (click)="sessionClick.emit()"
4710
+ >
4599
4711
  <kf-avatar [initials]="userInitials()" pill />
4600
4712
  </button>
4713
+
4714
+ <button
4715
+ type="button"
4716
+ class="kf-portal-header__menu-toggle"
4717
+ [attr.aria-expanded]="mobileMenuOpen()"
4718
+ aria-label="Abrir menú de navegación"
4719
+ (click)="mobileMenuOpen.set(!mobileMenuOpen())"
4720
+ >
4721
+ <kf-icon [name]="mobileMenuOpen() ? 'x' : 'menu'" size="md" />
4722
+ </button>
4601
4723
  </div>
4602
- `, isInline: true, styles: [":host{display:block;font-family:var(--kf-font-body)}.kf-portal-header__admin-bar{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-md);padding:var(--kf-space-xs) var(--kf-space-lg);background:var(--kf-color-tertiary-deep)}.kf-portal-header__admin-actions{display:flex;gap:var(--kf-space-md)}.kf-portal-header__admin-action{font-size:.8125rem;font-weight:600;color:#ffffffd9;text-decoration:none}.kf-portal-header__admin-action:hover{color:var(--kf-color-white)}.kf-portal-header__brand-bar{display:flex;align-items:center;gap:var(--kf-space-lg);padding:var(--kf-space-sm) var(--kf-space-lg);background:var(--kf-color-tertiary-deep);border-top:1px solid rgba(255,255,255,.1)}.kf-portal-header__logo{font-family:var(--kf-font-title);font-size:1.25rem;font-weight:700;letter-spacing:.02em;color:var(--kf-color-white);text-transform:uppercase}.kf-portal-header__nav{display:flex;align-items:center;gap:var(--kf-space-xs);flex:1 1 auto}.kf-portal-header__nav-item{padding:var(--kf-space-xs) var(--kf-space-md);border-radius:var(--kf-radius-sm);color:#ffffffbf;font-size:.875rem;font-weight:600;text-decoration:none;transition:background .14s,color .14s}.kf-portal-header__nav-item:hover:not(.kf-portal-header__nav-item--active){color:var(--kf-color-white)}.kf-portal-header__nav-item--active{background:var(--kf-color-white);color:var(--kf-color-ink)}.kf-portal-header__session{flex:none;border:none;background:transparent;padding:0;cursor:pointer}\n"], dependencies: [{ kind: "component", type: AdminBadge, selector: "kf-admin-badge", inputs: ["inline"] }, { kind: "component", type: Avatar, selector: "kf-avatar", inputs: ["initials", "src", "round", "pill"] }] });
4724
+
4725
+ @if (mobileMenuOpen()) {
4726
+ <div class="kf-portal-header__overlay" (click)="mobileMenuOpen.set(false)"></div>
4727
+ }
4728
+
4729
+ <nav
4730
+ class="kf-portal-header__drawer"
4731
+ [class.kf-portal-header__drawer--open]="mobileMenuOpen()"
4732
+ role="menu"
4733
+ [attr.aria-hidden]="!mobileMenuOpen()"
4734
+ >
4735
+ @for (item of navItems(); track item.label) {
4736
+ <a
4737
+ class="kf-portal-header__mobile-nav-item"
4738
+ [class.kf-portal-header__mobile-nav-item--active]="item.active"
4739
+ [href]="item.href"
4740
+ (click)="mobileMenuOpen.set(false)"
4741
+ >
4742
+ {{ item.label }}
4743
+ </a>
4744
+ }
4745
+ <ng-content select="[kf-portal-header-extra]" />
4746
+ </nav>
4747
+ `, isInline: true, styles: [":host{display:block;position:sticky;top:0;z-index:30;font-family:var(--kf-font-body)}.kf-portal-header__admin-bar{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-md);padding:var(--kf-space-xs) var(--kf-space-lg);background:var(--kf-color-tertiary-deep)}.kf-portal-header__admin-actions{display:flex;gap:var(--kf-space-md)}.kf-portal-header__admin-action{font-size:.8125rem;font-weight:600;color:#ffffffd9;text-decoration:none}.kf-portal-header__admin-action:hover{color:var(--kf-color-white)}.kf-portal-header__brand-bar{display:flex;align-items:center;gap:var(--kf-space-lg);padding:var(--kf-space-sm) var(--kf-space-lg);background:var(--kf-color-tertiary-deep);border-top:1px solid rgba(255,255,255,.1)}.kf-portal-header__logo{font-family:var(--kf-font-title);font-size:1.25rem;font-weight:700;letter-spacing:.02em;color:var(--kf-color-white);text-transform:uppercase;flex:1 1 auto;min-width:0}.kf-portal-header__nav{display:flex;align-items:center;gap:var(--kf-space-xs);flex:1 1 auto}.kf-portal-header__nav-item{padding:var(--kf-space-xs) var(--kf-space-md);border-radius:var(--kf-radius-sm);color:#ffffffbf;font-size:.875rem;font-weight:600;text-decoration:none;white-space:nowrap;transition:background .14s,color .14s}.kf-portal-header__nav-item:hover:not(.kf-portal-header__nav-item--active){color:var(--kf-color-white)}.kf-portal-header__nav-item--active{background:var(--kf-color-white);color:var(--kf-color-ink)}.kf-portal-header__session{flex:none;border:none;background:transparent;padding:0;cursor:pointer}.kf-portal-header__menu-toggle{display:none;flex:none;align-items:center;justify-content:center;width:36px;height:36px;border:none;border-radius:var(--kf-radius-sm);background:transparent;color:var(--kf-color-white);cursor:pointer}.kf-portal-header__menu-toggle:hover{background:#ffffff1a}.kf-portal-header__overlay{position:fixed;inset:0;background:#10182866;z-index:39}.kf-portal-header__drawer{position:fixed;top:0;right:0;bottom:0;z-index:40;display:none;flex-direction:column;width:min(80vw,320px);padding:var(--kf-space-lg);background:var(--kf-color-tertiary-deep);box-shadow:-8px 0 24px -8px #1018284d;transform:translate(100%);visibility:hidden;transition:transform .24s cubic-bezier(.32,.72,0,1),visibility 0s linear .24s}.kf-portal-header__drawer--open{transform:translate(0);visibility:visible;transition:transform .24s cubic-bezier(.32,.72,0,1),visibility 0s linear 0s}.kf-portal-header__mobile-nav-item{padding:var(--kf-space-sm) var(--kf-space-xs);color:#ffffffd9;font-size:.9375rem;font-weight:600;text-decoration:none;border-radius:var(--kf-radius-sm)}.kf-portal-header__mobile-nav-item:hover{background:#ffffff14}.kf-portal-header__mobile-nav-item--active{color:var(--kf-color-white);background:#ffffff24}@media(max-width:768px){.kf-portal-header__nav,.kf-portal-header__session{display:none}.kf-portal-header__menu-toggle{display:inline-flex}.kf-portal-header__drawer{display:flex}}\n"], dependencies: [{ kind: "component", type: AdminBadge, selector: "kf-admin-badge", inputs: ["inline"] }, { kind: "component", type: Avatar, selector: "kf-avatar", inputs: ["initials", "src", "round", "pill"] }, { kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size"] }] });
4603
4748
  }
4604
4749
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: PortalHeader, decorators: [{
4605
4750
  type: Component,
4606
- args: [{ selector: 'kf-portal-header', imports: [AdminBadge, Avatar], template: `
4751
+ args: [{ selector: 'kf-portal-header', imports: [AdminBadge, Avatar, Icon], template: `
4607
4752
  @if (isAdmin()) {
4608
4753
  <div class="kf-portal-header__admin-bar">
4609
4754
  <kf-admin-badge inline />
@@ -4616,6 +4761,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
4616
4761
  }
4617
4762
  <div class="kf-portal-header__brand-bar">
4618
4763
  <span class="kf-portal-header__logo">{{ brand() }}</span>
4764
+
4619
4765
  <nav class="kf-portal-header__nav">
4620
4766
  @for (item of navItems(); track item.label) {
4621
4767
  <a
@@ -4627,12 +4773,54 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
4627
4773
  </a>
4628
4774
  }
4629
4775
  </nav>
4630
- <button type="button" class="kf-portal-header__session" (click)="sessionClick.emit()">
4776
+
4777
+ <button
4778
+ #sessionButton
4779
+ type="button"
4780
+ class="kf-portal-header__session"
4781
+ (click)="sessionClick.emit()"
4782
+ >
4631
4783
  <kf-avatar [initials]="userInitials()" pill />
4632
4784
  </button>
4785
+
4786
+ <button
4787
+ type="button"
4788
+ class="kf-portal-header__menu-toggle"
4789
+ [attr.aria-expanded]="mobileMenuOpen()"
4790
+ aria-label="Abrir menú de navegación"
4791
+ (click)="mobileMenuOpen.set(!mobileMenuOpen())"
4792
+ >
4793
+ <kf-icon [name]="mobileMenuOpen() ? 'x' : 'menu'" size="md" />
4794
+ </button>
4633
4795
  </div>
4634
- `, styles: [":host{display:block;font-family:var(--kf-font-body)}.kf-portal-header__admin-bar{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-md);padding:var(--kf-space-xs) var(--kf-space-lg);background:var(--kf-color-tertiary-deep)}.kf-portal-header__admin-actions{display:flex;gap:var(--kf-space-md)}.kf-portal-header__admin-action{font-size:.8125rem;font-weight:600;color:#ffffffd9;text-decoration:none}.kf-portal-header__admin-action:hover{color:var(--kf-color-white)}.kf-portal-header__brand-bar{display:flex;align-items:center;gap:var(--kf-space-lg);padding:var(--kf-space-sm) var(--kf-space-lg);background:var(--kf-color-tertiary-deep);border-top:1px solid rgba(255,255,255,.1)}.kf-portal-header__logo{font-family:var(--kf-font-title);font-size:1.25rem;font-weight:700;letter-spacing:.02em;color:var(--kf-color-white);text-transform:uppercase}.kf-portal-header__nav{display:flex;align-items:center;gap:var(--kf-space-xs);flex:1 1 auto}.kf-portal-header__nav-item{padding:var(--kf-space-xs) var(--kf-space-md);border-radius:var(--kf-radius-sm);color:#ffffffbf;font-size:.875rem;font-weight:600;text-decoration:none;transition:background .14s,color .14s}.kf-portal-header__nav-item:hover:not(.kf-portal-header__nav-item--active){color:var(--kf-color-white)}.kf-portal-header__nav-item--active{background:var(--kf-color-white);color:var(--kf-color-ink)}.kf-portal-header__session{flex:none;border:none;background:transparent;padding:0;cursor:pointer}\n"] }]
4635
- }], propDecorators: { brand: [{ type: i0.Input, args: [{ isSignal: true, alias: "brand", required: false }] }], isAdmin: [{ type: i0.Input, args: [{ isSignal: true, alias: "isAdmin", required: false }] }], adminActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "adminActions", required: false }] }], navItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "navItems", required: false }] }], userInitials: [{ type: i0.Input, args: [{ isSignal: true, alias: "userInitials", required: true }] }], sessionClick: [{ type: i0.Output, args: ["sessionClick"] }] } });
4796
+
4797
+ @if (mobileMenuOpen()) {
4798
+ <div class="kf-portal-header__overlay" (click)="mobileMenuOpen.set(false)"></div>
4799
+ }
4800
+
4801
+ <nav
4802
+ class="kf-portal-header__drawer"
4803
+ [class.kf-portal-header__drawer--open]="mobileMenuOpen()"
4804
+ role="menu"
4805
+ [attr.aria-hidden]="!mobileMenuOpen()"
4806
+ >
4807
+ @for (item of navItems(); track item.label) {
4808
+ <a
4809
+ class="kf-portal-header__mobile-nav-item"
4810
+ [class.kf-portal-header__mobile-nav-item--active]="item.active"
4811
+ [href]="item.href"
4812
+ (click)="mobileMenuOpen.set(false)"
4813
+ >
4814
+ {{ item.label }}
4815
+ </a>
4816
+ }
4817
+ <ng-content select="[kf-portal-header-extra]" />
4818
+ </nav>
4819
+ `, styles: [":host{display:block;position:sticky;top:0;z-index:30;font-family:var(--kf-font-body)}.kf-portal-header__admin-bar{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-md);padding:var(--kf-space-xs) var(--kf-space-lg);background:var(--kf-color-tertiary-deep)}.kf-portal-header__admin-actions{display:flex;gap:var(--kf-space-md)}.kf-portal-header__admin-action{font-size:.8125rem;font-weight:600;color:#ffffffd9;text-decoration:none}.kf-portal-header__admin-action:hover{color:var(--kf-color-white)}.kf-portal-header__brand-bar{display:flex;align-items:center;gap:var(--kf-space-lg);padding:var(--kf-space-sm) var(--kf-space-lg);background:var(--kf-color-tertiary-deep);border-top:1px solid rgba(255,255,255,.1)}.kf-portal-header__logo{font-family:var(--kf-font-title);font-size:1.25rem;font-weight:700;letter-spacing:.02em;color:var(--kf-color-white);text-transform:uppercase;flex:1 1 auto;min-width:0}.kf-portal-header__nav{display:flex;align-items:center;gap:var(--kf-space-xs);flex:1 1 auto}.kf-portal-header__nav-item{padding:var(--kf-space-xs) var(--kf-space-md);border-radius:var(--kf-radius-sm);color:#ffffffbf;font-size:.875rem;font-weight:600;text-decoration:none;white-space:nowrap;transition:background .14s,color .14s}.kf-portal-header__nav-item:hover:not(.kf-portal-header__nav-item--active){color:var(--kf-color-white)}.kf-portal-header__nav-item--active{background:var(--kf-color-white);color:var(--kf-color-ink)}.kf-portal-header__session{flex:none;border:none;background:transparent;padding:0;cursor:pointer}.kf-portal-header__menu-toggle{display:none;flex:none;align-items:center;justify-content:center;width:36px;height:36px;border:none;border-radius:var(--kf-radius-sm);background:transparent;color:var(--kf-color-white);cursor:pointer}.kf-portal-header__menu-toggle:hover{background:#ffffff1a}.kf-portal-header__overlay{position:fixed;inset:0;background:#10182866;z-index:39}.kf-portal-header__drawer{position:fixed;top:0;right:0;bottom:0;z-index:40;display:none;flex-direction:column;width:min(80vw,320px);padding:var(--kf-space-lg);background:var(--kf-color-tertiary-deep);box-shadow:-8px 0 24px -8px #1018284d;transform:translate(100%);visibility:hidden;transition:transform .24s cubic-bezier(.32,.72,0,1),visibility 0s linear .24s}.kf-portal-header__drawer--open{transform:translate(0);visibility:visible;transition:transform .24s cubic-bezier(.32,.72,0,1),visibility 0s linear 0s}.kf-portal-header__mobile-nav-item{padding:var(--kf-space-sm) var(--kf-space-xs);color:#ffffffd9;font-size:.9375rem;font-weight:600;text-decoration:none;border-radius:var(--kf-radius-sm)}.kf-portal-header__mobile-nav-item:hover{background:#ffffff14}.kf-portal-header__mobile-nav-item--active{color:var(--kf-color-white);background:#ffffff24}@media(max-width:768px){.kf-portal-header__nav,.kf-portal-header__session{display:none}.kf-portal-header__menu-toggle{display:inline-flex}.kf-portal-header__drawer{display:flex}}\n"] }]
4820
+ }], propDecorators: { brand: [{ type: i0.Input, args: [{ isSignal: true, alias: "brand", required: false }] }], isAdmin: [{ type: i0.Input, args: [{ isSignal: true, alias: "isAdmin", required: false }] }], adminActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "adminActions", required: false }] }], navItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "navItems", required: false }] }], userInitials: [{ type: i0.Input, args: [{ isSignal: true, alias: "userInitials", required: true }] }], sessionClick: [{ type: i0.Output, args: ["sessionClick"] }], onEscape: [{
4821
+ type: HostListener,
4822
+ args: ['document:keydown.escape']
4823
+ }], sessionButtonRef: [{ type: i0.ViewChild, args: ['sessionButton', { ...{ read: ElementRef }, isSignal: true }] }] } });
4636
4824
 
4637
4825
  /**
4638
4826
  * Estado vacío de reservas en taller con llamada a agendar (página 36):
@@ -5604,5 +5792,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
5604
5792
  * Generated bundle index. Do not edit.
5605
5793
  */
5606
5794
 
5607
- export { ActionTile, AdminBadge, Alert, Avatar, Badge, Breadcrumb, Button, Card, Carousel, CarouselSlide, Checkbox, Chip, CompanySelector, ContentSlot, ContentState, DataTable, Divider, Dropdown, DropdownItem, DropdownPanel, EmptyState, FeatureCard, Field, FieldLabel, FilterChips, FolderTabs, Icon, Input, Link, MetricCard, Modal, MultiSelect, PageHeader, Pagination, Panel, PortalHeader, ReserveBlock, SectionHeader, Segmented, Select, Skeleton, Spinner, StatTile, StatusPill, Tabs, Text, ToastHost, ToastService, VehicleHeader, ViewToggle };
5795
+ export { ActionTile, AdminBadge, Alert, Avatar, Badge, Breadcrumb, Button, Card, Carousel, CarouselSlide, Checkbox, Chip, CompanySelector, ContentSlot, ContentState, DataTable, Divider, Dropdown, DropdownItem, DropdownPanel, EmptyState, FeatureCard, Field, FieldLabel, FilterChips, FolderTabs, FolderTabsPanel, Icon, Input, Link, MetricCard, Modal, MultiSelect, PageHeader, Pagination, Panel, PortalHeader, ReserveBlock, SectionHeader, Segmented, Select, Skeleton, Spinner, StatTile, StatusPill, Tabs, Text, ToastHost, ToastService, VehicleHeader, ViewToggle };
5608
5796
  //# sourceMappingURL=kaufmann-lab-ui.mjs.map