@kaufmann-lab/ui 0.1.19 → 0.1.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/kaufmann-lab-ui-business.mjs +269 -17
- package/fesm2022/kaufmann-lab-ui-business.mjs.map +1 -1
- package/fesm2022/kaufmann-lab-ui-core.mjs +1418 -101
- package/fesm2022/kaufmann-lab-ui-core.mjs.map +1 -1
- package/fesm2022/kaufmann-lab-ui-map.mjs +1 -1
- package/fesm2022/kaufmann-lab-ui-map.mjs.map +1 -1
- package/package.json +1 -1
- package/types/kaufmann-lab-ui-business.d.ts +175 -14
- package/types/kaufmann-lab-ui-core.d.ts +871 -55
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { input, booleanAttribute, inject, computed, effect, Component, model, output, ElementRef, Injector, signal, afterNextRender, ChangeDetectionStrategy, numberAttribute, contentChildren, DestroyRef,
|
|
2
|
+
import { input, booleanAttribute, inject, computed, effect, Component, viewChild, model, output, ElementRef, Injector, signal, afterNextRender, ChangeDetectionStrategy, numberAttribute, contentChildren, DestroyRef, forwardRef, TemplateRef, contentChild, ViewContainerRef, HostListener, afterRenderEffect, untracked, isDevMode, viewChildren, Input as Input$1, Directive, 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';
|
|
@@ -363,6 +363,16 @@ const inbox = {
|
|
|
363
363
|
`,
|
|
364
364
|
};
|
|
365
365
|
|
|
366
|
+
/** Símbolo de trazo "info" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
|
|
367
|
+
const info = {
|
|
368
|
+
viewBox: '0 0 24 24',
|
|
369
|
+
markup: `
|
|
370
|
+
<circle cx="12" cy="12" r="10" />
|
|
371
|
+
<path d="M12 16v-4" />
|
|
372
|
+
<path d="M12 8h.01" />
|
|
373
|
+
`,
|
|
374
|
+
};
|
|
375
|
+
|
|
366
376
|
/** Símbolo de trazo "layers" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
|
|
367
377
|
const layers = {
|
|
368
378
|
viewBox: '0 0 24 24',
|
|
@@ -373,6 +383,17 @@ const layers = {
|
|
|
373
383
|
`,
|
|
374
384
|
};
|
|
375
385
|
|
|
386
|
+
/** Símbolo de trazo "layout-grid" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
|
|
387
|
+
const layoutGrid = {
|
|
388
|
+
viewBox: '0 0 24 24',
|
|
389
|
+
markup: `
|
|
390
|
+
<rect width="7" height="7" x="3" y="3" rx="1" />
|
|
391
|
+
<rect width="7" height="7" x="14" y="3" rx="1" />
|
|
392
|
+
<rect width="7" height="7" x="14" y="14" rx="1" />
|
|
393
|
+
<rect width="7" height="7" x="3" y="14" rx="1" />
|
|
394
|
+
`,
|
|
395
|
+
};
|
|
396
|
+
|
|
376
397
|
/** Símbolo de trazo "list" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
|
|
377
398
|
const list = {
|
|
378
399
|
viewBox: '0 0 24 24',
|
|
@@ -566,6 +587,17 @@ const plus = {
|
|
|
566
587
|
`,
|
|
567
588
|
};
|
|
568
589
|
|
|
590
|
+
/** Símbolo de trazo "refresh" (grilla 24×24; `refresh-cw` de Lucide) — ver `icons/index.ts` para el registro completo. */
|
|
591
|
+
const refresh = {
|
|
592
|
+
viewBox: '0 0 24 24',
|
|
593
|
+
markup: `
|
|
594
|
+
<path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8" />
|
|
595
|
+
<path d="M21 3v5h-5" />
|
|
596
|
+
<path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16" />
|
|
597
|
+
<path d="M8 16H3v5" />
|
|
598
|
+
`,
|
|
599
|
+
};
|
|
600
|
+
|
|
569
601
|
/** Símbolo de trazo "search" (grilla 24×24) — ver `icons/index.ts` para el registro completo. */
|
|
570
602
|
const search = {
|
|
571
603
|
viewBox: '0 0 24 24',
|
|
@@ -757,7 +789,9 @@ const ICON_DEFINITIONS = {
|
|
|
757
789
|
history: history,
|
|
758
790
|
home: home,
|
|
759
791
|
inbox: inbox,
|
|
792
|
+
info: info,
|
|
760
793
|
layers: layers,
|
|
794
|
+
'layout-grid': layoutGrid,
|
|
761
795
|
list: list,
|
|
762
796
|
'log-out': logOut,
|
|
763
797
|
mail: mail,
|
|
@@ -778,6 +812,7 @@ const ICON_DEFINITIONS = {
|
|
|
778
812
|
pin: pin,
|
|
779
813
|
play: play,
|
|
780
814
|
plus: plus,
|
|
815
|
+
refresh: refresh,
|
|
781
816
|
search: search,
|
|
782
817
|
settings: settings,
|
|
783
818
|
'shield-check': shieldCheck,
|
|
@@ -796,7 +831,7 @@ const ICON_DEFINITIONS = {
|
|
|
796
831
|
};
|
|
797
832
|
|
|
798
833
|
/**
|
|
799
|
-
* Sprite SVG con los
|
|
834
|
+
* Sprite SVG con los 74 íconos de trazo del design system (página 5:
|
|
800
835
|
* "Set de 59 iconos de trazo (1.9px, grilla 24×24)", más las extensiones
|
|
801
836
|
* propias documentadas en `icon-name.ts` — `eye-off`, `log-out`, los 5 de
|
|
802
837
|
* `kf-map` y los 4 del selector de canal/fijar de `apps/operations`).
|
|
@@ -1081,7 +1116,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
|
|
|
1081
1116
|
* Envuelve un `<button>` nativo: mantiene teclado, foco y submit de formulario.
|
|
1082
1117
|
* Como es un elemento personalizado, ningún atributo ARIA puesto en el host
|
|
1083
1118
|
* llega al `<button>` interno — por eso `disabled`, `type`, `ariaLabel`,
|
|
1084
|
-
* `ariaExpanded` y `
|
|
1119
|
+
* `ariaExpanded`, `ariaPressed` y `ariaHaspopup` se reenvían a mano.
|
|
1085
1120
|
*
|
|
1086
1121
|
* Seis variantes: `primary`, `secondary`, `ghost`, `outline`, `danger`,
|
|
1087
1122
|
* `surface`. Tres tamaños (`sm`/`md`/`lg`). `icon` agrega un glifo antes del
|
|
@@ -1100,6 +1135,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
|
|
|
1100
1135
|
*
|
|
1101
1136
|
* `(click)` no se reenvía: burbujea del botón interno al host solo.
|
|
1102
1137
|
*
|
|
1138
|
+
* Objetivo táctil: el alto y el ancho mínimos del `<button>` interno salen
|
|
1139
|
+
* de `--kf-button-min-height` y `--kf-button-min-width` (default `auto`,
|
|
1140
|
+
* sin cambio). Un contenedor los sube (ej. a 44px en pantallas táctiles,
|
|
1141
|
+
* sobre todo para un botón solo-ícono) declarando las variables en su
|
|
1142
|
+
* propio CSS — heredan a través del límite de encapsulación.
|
|
1143
|
+
*
|
|
1144
|
+
* `focus()` enfoca el `<button>` interno: el consumidor no necesita
|
|
1145
|
+
* buscarlo con `querySelector` en el DOM del componente.
|
|
1146
|
+
*
|
|
1103
1147
|
* `loading` reemplaza el contenido proyectado por `kf-spinner` y fuerza
|
|
1104
1148
|
* `disabled`.
|
|
1105
1149
|
*
|
|
@@ -1147,6 +1191,16 @@ class Button {
|
|
|
1147
1191
|
* acordarse de pedirlo aparte. */
|
|
1148
1192
|
ariaPressed = input(/* @ts-ignore */
|
|
1149
1193
|
...(ngDevMode ? [undefined, { debugName: "ariaPressed" }] : /* istanbul ignore next */ []));
|
|
1194
|
+
/** Tipo de elemento emergente que abre el botón (ej. `'dialog'` para un
|
|
1195
|
+
* panel de filtros, `'listbox'`) — mismo motivo de reenvío manual. */
|
|
1196
|
+
ariaHaspopup = input(/* @ts-ignore */
|
|
1197
|
+
...(ngDevMode ? [undefined, { debugName: "ariaHaspopup" }] : /* istanbul ignore next */ []));
|
|
1198
|
+
nativeButton = viewChild.required('nativeButton', /* @ts-ignore */
|
|
1199
|
+
...(ngDevMode ? [{ debugName: "nativeButton" }] : /* istanbul ignore next */ []));
|
|
1200
|
+
/** Enfoca el `<button>` interno (mismas opciones que `HTMLElement.focus`). */
|
|
1201
|
+
focus(options) {
|
|
1202
|
+
this.nativeButton().nativeElement.focus(options);
|
|
1203
|
+
}
|
|
1150
1204
|
/** `ghost`/`outline`/`surface` no tienen fondo oscuro sólido — el anillo
|
|
1151
1205
|
* del spinner por defecto ya se lee bien ahí sin variante propia
|
|
1152
1206
|
* (`surface` es fondo claro con texto oscuro, mismo motivo que `outline`). */
|
|
@@ -1156,14 +1210,14 @@ class Button {
|
|
|
1156
1210
|
}, /* @ts-ignore */
|
|
1157
1211
|
...(ngDevMode ? [{ debugName: "hasSolidFill" }] : /* istanbul ignore next */ []));
|
|
1158
1212
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Button, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1159
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Button, isStandalone: true, selector: "kf-button", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, shape: { classPropertyName: "shape", publicName: "shape", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, block: { classPropertyName: "block", publicName: "block", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, ariaExpanded: { classPropertyName: "ariaExpanded", publicName: "ariaExpanded", isSignal: true, isRequired: false, transformFunction: null }, ariaPressed: { classPropertyName: "ariaPressed", publicName: "ariaPressed", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.block": "block() ? '' : null" } }, ngImport: i0, template: "<button\n [type]=\"type()\"\n [disabled]=\"disabled() || loading()\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-expanded]=\"ariaExpanded() === undefined ? null : ariaExpanded()\"\n [attr.aria-pressed]=\"ariaPressed() === undefined ? null : ariaPressed()\"\n [class.kf-button--secondary]=\"variant() === 'secondary'\"\n [class.kf-button--ghost]=\"variant() === 'ghost'\"\n [class.kf-button--outline]=\"variant() === 'outline'\"\n [class.kf-button--danger]=\"variant() === 'danger'\"\n [class.kf-button--surface]=\"variant() === 'surface'\"\n [class.kf-button--circle]=\"shape() === 'circle'\"\n [class.kf-button--sm]=\"size() === 'sm'\"\n [class.kf-button--lg]=\"size() === 'lg'\"\n>\n @if (loading()) {\n <kf-spinner size=\"sm\" [onDark]=\"hasSolidFill()\" />\n } @else {\n @if (icon(); as iconName) {\n <kf-icon\n class=\"kf-button__icon\"\n [name]=\"iconName\"\n [size]=\"size() === 'lg' ? 'md' : 'sm'\"\n [filled]=\"ariaPressed() === true\"\n />\n }\n <ng-content />\n }\n</button>\n", styles: [":host{display:inline-block}:host([block]){display:block}button{display:inline-flex;align-items:center;justify-content:center;gap:var(--kf-space-xs);width:100%;font:inherit;font-weight:600;padding:var(--kf-space-sm) var(--kf-space-md);border:1px solid var(--kf-color-primary);border-radius:var(--kf-radius-sm);background:var(--kf-color-primary);color:var(--kf-color-white);cursor:pointer;transition:background .14s,border-color .14s,color .14s}button:hover:not(:disabled){background:var(--kf-color-primary-hover);border-color:var(--kf-color-primary-hover)}button:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}button:disabled{opacity:.5;cursor:not-allowed}button.kf-button--secondary{border-color:var(--kf-color-secondary);background:var(--kf-color-secondary)}button.kf-button--secondary:hover:not(:disabled){background:var(--kf-color-secondary-hover);border-color:var(--kf-color-secondary-hover)}button.kf-button--ghost{background:transparent;border-color:transparent;color:var(--kf-color-primary)}button.kf-button--ghost:hover:not(:disabled){background:var(--kf-color-primary-soft)}button.kf-button--ghost[aria-pressed=true]{color:var(--kf-color-secondary)}.kf-button__icon{transition:transform .14s ease}button[aria-pressed=true] .kf-button__icon{transform:scale(1.12)}@media(prefers-reduced-motion:reduce){.kf-button__icon{transition:none}}button.kf-button--outline{background:transparent;border-color:var(--kf-color-border-strong);color:var(--kf-color-text)}button.kf-button--outline:hover:not(:disabled){background:var(--kf-color-surface);border-color:var(--kf-color-text-muted)}button.kf-button--danger{border-color:var(--kf-color-error);background:var(--kf-color-error)}button.kf-button--danger:hover:not(:disabled){background:var(--kf-color-error-hover);border-color:var(--kf-color-error-hover)}button.kf-button--surface{border-color:var(--kf-color-border);background:var(--kf-color-surface);color:var(--kf-color-text);box-shadow:var(--kf-shadow-md)}button.kf-button--surface:hover:not(:disabled){background:var(--kf-color-white);border-color:var(--kf-color-border-strong)}button.kf-button--circle{aspect-ratio:1;padding:var(--kf-space-sm);border-radius:var(--kf-radius-pill)}button.kf-button--circle.kf-button--sm{padding:6px}button.kf-button--circle.kf-button--lg{padding:var(--kf-space-md)}button.kf-button--sm{padding:6px var(--kf-space-sm);font-size:.85rem}button.kf-button--lg{padding:var(--kf-space-sm) var(--kf-space-lg);font-size:1.05rem}\n"], dependencies: [{ kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size", "rotate", "filled"] }, { kind: "component", type: Spinner, selector: "kf-spinner", inputs: ["size", "label", "onDark"] }] });
|
|
1213
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Button, isStandalone: true, selector: "kf-button", inputs: { variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, shape: { classPropertyName: "shape", publicName: "shape", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, block: { classPropertyName: "block", publicName: "block", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, ariaExpanded: { classPropertyName: "ariaExpanded", publicName: "ariaExpanded", isSignal: true, isRequired: false, transformFunction: null }, ariaPressed: { classPropertyName: "ariaPressed", publicName: "ariaPressed", isSignal: true, isRequired: false, transformFunction: null }, ariaHaspopup: { classPropertyName: "ariaHaspopup", publicName: "ariaHaspopup", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.block": "block() ? '' : null" } }, viewQueries: [{ propertyName: "nativeButton", first: true, predicate: ["nativeButton"], descendants: true, isSignal: true }], ngImport: i0, template: "<button\n #nativeButton\n [type]=\"type()\"\n [disabled]=\"disabled() || loading()\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-expanded]=\"ariaExpanded() === undefined ? null : ariaExpanded()\"\n [attr.aria-pressed]=\"ariaPressed() === undefined ? null : ariaPressed()\"\n [attr.aria-haspopup]=\"ariaHaspopup() ?? null\"\n [class.kf-button--secondary]=\"variant() === 'secondary'\"\n [class.kf-button--ghost]=\"variant() === 'ghost'\"\n [class.kf-button--outline]=\"variant() === 'outline'\"\n [class.kf-button--danger]=\"variant() === 'danger'\"\n [class.kf-button--surface]=\"variant() === 'surface'\"\n [class.kf-button--circle]=\"shape() === 'circle'\"\n [class.kf-button--sm]=\"size() === 'sm'\"\n [class.kf-button--lg]=\"size() === 'lg'\"\n>\n @if (loading()) {\n <kf-spinner size=\"sm\" [onDark]=\"hasSolidFill()\" />\n } @else {\n @if (icon(); as iconName) {\n <kf-icon\n class=\"kf-button__icon\"\n [name]=\"iconName\"\n [size]=\"size() === 'lg' ? 'md' : 'sm'\"\n [filled]=\"ariaPressed() === true\"\n />\n }\n <ng-content />\n }\n</button>\n", styles: [":host{display:inline-block}:host([block]){display:block}button{display:inline-flex;align-items:center;justify-content:center;gap:var(--kf-space-xs);width:100%;min-height:var(--kf-button-min-height, auto);min-width:var(--kf-button-min-width, auto);font:inherit;font-weight:600;padding:var(--kf-space-sm) var(--kf-space-md);border:1px solid var(--kf-color-primary);border-radius:var(--kf-radius-sm);background:var(--kf-color-primary);color:var(--kf-color-white);cursor:pointer;transition:background .14s,border-color .14s,color .14s}button:hover:not(:disabled){background:var(--kf-color-primary-hover);border-color:var(--kf-color-primary-hover)}button:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}button:disabled{opacity:.5;cursor:not-allowed}button.kf-button--secondary{border-color:var(--kf-color-secondary);background:var(--kf-color-secondary)}button.kf-button--secondary:hover:not(:disabled){background:var(--kf-color-secondary-hover);border-color:var(--kf-color-secondary-hover)}button.kf-button--ghost{background:transparent;border-color:transparent;color:var(--kf-color-primary)}button.kf-button--ghost:hover:not(:disabled){background:var(--kf-color-primary-soft)}button.kf-button--ghost[aria-pressed=true]{color:var(--kf-color-secondary)}.kf-button__icon{transition:transform .14s ease}button[aria-pressed=true] .kf-button__icon{transform:scale(1.12)}@media(prefers-reduced-motion:reduce){.kf-button__icon{transition:none}}button.kf-button--outline{background:transparent;border-color:var(--kf-color-border-strong);color:var(--kf-color-text)}button.kf-button--outline:hover:not(:disabled){background:var(--kf-color-surface);border-color:var(--kf-color-text-muted)}button.kf-button--danger{border-color:var(--kf-color-error);background:var(--kf-color-error)}button.kf-button--danger:hover:not(:disabled){background:var(--kf-color-error-hover);border-color:var(--kf-color-error-hover)}button.kf-button--surface{border-color:var(--kf-color-border);background:var(--kf-color-surface);color:var(--kf-color-text);box-shadow:var(--kf-shadow-md)}button.kf-button--surface:hover:not(:disabled){background:var(--kf-color-white);border-color:var(--kf-color-border-strong)}button.kf-button--circle{aspect-ratio:1;padding:var(--kf-space-sm);border-radius:var(--kf-radius-pill)}button.kf-button--circle.kf-button--sm{padding:6px}button.kf-button--circle.kf-button--lg{padding:var(--kf-space-md)}button.kf-button--sm{padding:6px var(--kf-space-sm);font-size:.85rem}button.kf-button--lg{padding:var(--kf-space-sm) var(--kf-space-lg);font-size:1.05rem}\n"], dependencies: [{ kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size", "rotate", "filled"] }, { kind: "component", type: Spinner, selector: "kf-spinner", inputs: ["size", "label", "onDark"] }] });
|
|
1160
1214
|
}
|
|
1161
1215
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Button, decorators: [{
|
|
1162
1216
|
type: Component,
|
|
1163
1217
|
args: [{ selector: 'kf-button', imports: [Icon, Spinner], host: {
|
|
1164
1218
|
'[attr.block]': "block() ? '' : null",
|
|
1165
|
-
}, template: "<button\n [type]=\"type()\"\n [disabled]=\"disabled() || loading()\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-expanded]=\"ariaExpanded() === undefined ? null : ariaExpanded()\"\n [attr.aria-pressed]=\"ariaPressed() === undefined ? null : ariaPressed()\"\n [class.kf-button--secondary]=\"variant() === 'secondary'\"\n [class.kf-button--ghost]=\"variant() === 'ghost'\"\n [class.kf-button--outline]=\"variant() === 'outline'\"\n [class.kf-button--danger]=\"variant() === 'danger'\"\n [class.kf-button--surface]=\"variant() === 'surface'\"\n [class.kf-button--circle]=\"shape() === 'circle'\"\n [class.kf-button--sm]=\"size() === 'sm'\"\n [class.kf-button--lg]=\"size() === 'lg'\"\n>\n @if (loading()) {\n <kf-spinner size=\"sm\" [onDark]=\"hasSolidFill()\" />\n } @else {\n @if (icon(); as iconName) {\n <kf-icon\n class=\"kf-button__icon\"\n [name]=\"iconName\"\n [size]=\"size() === 'lg' ? 'md' : 'sm'\"\n [filled]=\"ariaPressed() === true\"\n />\n }\n <ng-content />\n }\n</button>\n", styles: [":host{display:inline-block}:host([block]){display:block}button{display:inline-flex;align-items:center;justify-content:center;gap:var(--kf-space-xs);width:100%;font:inherit;font-weight:600;padding:var(--kf-space-sm) var(--kf-space-md);border:1px solid var(--kf-color-primary);border-radius:var(--kf-radius-sm);background:var(--kf-color-primary);color:var(--kf-color-white);cursor:pointer;transition:background .14s,border-color .14s,color .14s}button:hover:not(:disabled){background:var(--kf-color-primary-hover);border-color:var(--kf-color-primary-hover)}button:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}button:disabled{opacity:.5;cursor:not-allowed}button.kf-button--secondary{border-color:var(--kf-color-secondary);background:var(--kf-color-secondary)}button.kf-button--secondary:hover:not(:disabled){background:var(--kf-color-secondary-hover);border-color:var(--kf-color-secondary-hover)}button.kf-button--ghost{background:transparent;border-color:transparent;color:var(--kf-color-primary)}button.kf-button--ghost:hover:not(:disabled){background:var(--kf-color-primary-soft)}button.kf-button--ghost[aria-pressed=true]{color:var(--kf-color-secondary)}.kf-button__icon{transition:transform .14s ease}button[aria-pressed=true] .kf-button__icon{transform:scale(1.12)}@media(prefers-reduced-motion:reduce){.kf-button__icon{transition:none}}button.kf-button--outline{background:transparent;border-color:var(--kf-color-border-strong);color:var(--kf-color-text)}button.kf-button--outline:hover:not(:disabled){background:var(--kf-color-surface);border-color:var(--kf-color-text-muted)}button.kf-button--danger{border-color:var(--kf-color-error);background:var(--kf-color-error)}button.kf-button--danger:hover:not(:disabled){background:var(--kf-color-error-hover);border-color:var(--kf-color-error-hover)}button.kf-button--surface{border-color:var(--kf-color-border);background:var(--kf-color-surface);color:var(--kf-color-text);box-shadow:var(--kf-shadow-md)}button.kf-button--surface:hover:not(:disabled){background:var(--kf-color-white);border-color:var(--kf-color-border-strong)}button.kf-button--circle{aspect-ratio:1;padding:var(--kf-space-sm);border-radius:var(--kf-radius-pill)}button.kf-button--circle.kf-button--sm{padding:6px}button.kf-button--circle.kf-button--lg{padding:var(--kf-space-md)}button.kf-button--sm{padding:6px var(--kf-space-sm);font-size:.85rem}button.kf-button--lg{padding:var(--kf-space-sm) var(--kf-space-lg);font-size:1.05rem}\n"] }]
|
|
1166
|
-
}], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], shape: [{ type: i0.Input, args: [{ isSignal: true, alias: "shape", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], block: [{ type: i0.Input, args: [{ isSignal: true, alias: "block", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], ariaExpanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaExpanded", required: false }] }], ariaPressed: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaPressed", required: false }] }] } });
|
|
1219
|
+
}, template: "<button\n #nativeButton\n [type]=\"type()\"\n [disabled]=\"disabled() || loading()\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-expanded]=\"ariaExpanded() === undefined ? null : ariaExpanded()\"\n [attr.aria-pressed]=\"ariaPressed() === undefined ? null : ariaPressed()\"\n [attr.aria-haspopup]=\"ariaHaspopup() ?? null\"\n [class.kf-button--secondary]=\"variant() === 'secondary'\"\n [class.kf-button--ghost]=\"variant() === 'ghost'\"\n [class.kf-button--outline]=\"variant() === 'outline'\"\n [class.kf-button--danger]=\"variant() === 'danger'\"\n [class.kf-button--surface]=\"variant() === 'surface'\"\n [class.kf-button--circle]=\"shape() === 'circle'\"\n [class.kf-button--sm]=\"size() === 'sm'\"\n [class.kf-button--lg]=\"size() === 'lg'\"\n>\n @if (loading()) {\n <kf-spinner size=\"sm\" [onDark]=\"hasSolidFill()\" />\n } @else {\n @if (icon(); as iconName) {\n <kf-icon\n class=\"kf-button__icon\"\n [name]=\"iconName\"\n [size]=\"size() === 'lg' ? 'md' : 'sm'\"\n [filled]=\"ariaPressed() === true\"\n />\n }\n <ng-content />\n }\n</button>\n", styles: [":host{display:inline-block}:host([block]){display:block}button{display:inline-flex;align-items:center;justify-content:center;gap:var(--kf-space-xs);width:100%;min-height:var(--kf-button-min-height, auto);min-width:var(--kf-button-min-width, auto);font:inherit;font-weight:600;padding:var(--kf-space-sm) var(--kf-space-md);border:1px solid var(--kf-color-primary);border-radius:var(--kf-radius-sm);background:var(--kf-color-primary);color:var(--kf-color-white);cursor:pointer;transition:background .14s,border-color .14s,color .14s}button:hover:not(:disabled){background:var(--kf-color-primary-hover);border-color:var(--kf-color-primary-hover)}button:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}button:disabled{opacity:.5;cursor:not-allowed}button.kf-button--secondary{border-color:var(--kf-color-secondary);background:var(--kf-color-secondary)}button.kf-button--secondary:hover:not(:disabled){background:var(--kf-color-secondary-hover);border-color:var(--kf-color-secondary-hover)}button.kf-button--ghost{background:transparent;border-color:transparent;color:var(--kf-color-primary)}button.kf-button--ghost:hover:not(:disabled){background:var(--kf-color-primary-soft)}button.kf-button--ghost[aria-pressed=true]{color:var(--kf-color-secondary)}.kf-button__icon{transition:transform .14s ease}button[aria-pressed=true] .kf-button__icon{transform:scale(1.12)}@media(prefers-reduced-motion:reduce){.kf-button__icon{transition:none}}button.kf-button--outline{background:transparent;border-color:var(--kf-color-border-strong);color:var(--kf-color-text)}button.kf-button--outline:hover:not(:disabled){background:var(--kf-color-surface);border-color:var(--kf-color-text-muted)}button.kf-button--danger{border-color:var(--kf-color-error);background:var(--kf-color-error)}button.kf-button--danger:hover:not(:disabled){background:var(--kf-color-error-hover);border-color:var(--kf-color-error-hover)}button.kf-button--surface{border-color:var(--kf-color-border);background:var(--kf-color-surface);color:var(--kf-color-text);box-shadow:var(--kf-shadow-md)}button.kf-button--surface:hover:not(:disabled){background:var(--kf-color-white);border-color:var(--kf-color-border-strong)}button.kf-button--circle{aspect-ratio:1;padding:var(--kf-space-sm);border-radius:var(--kf-radius-pill)}button.kf-button--circle.kf-button--sm{padding:6px}button.kf-button--circle.kf-button--lg{padding:var(--kf-space-md)}button.kf-button--sm{padding:6px var(--kf-space-sm);font-size:.85rem}button.kf-button--lg{padding:var(--kf-space-sm) var(--kf-space-lg);font-size:1.05rem}\n"] }]
|
|
1220
|
+
}], propDecorators: { variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], shape: [{ type: i0.Input, args: [{ isSignal: true, alias: "shape", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], block: [{ type: i0.Input, args: [{ isSignal: true, alias: "block", required: false }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], ariaExpanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaExpanded", required: false }] }], ariaPressed: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaPressed", required: false }] }], ariaHaspopup: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaHaspopup", required: false }] }], nativeButton: [{ type: i0.ViewChild, args: ['nativeButton', { isSignal: true }] }] } });
|
|
1167
1221
|
|
|
1168
1222
|
const TONE_ICON = {
|
|
1169
1223
|
info: 'alert-circle',
|
|
@@ -1278,6 +1332,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
|
|
|
1278
1332
|
* punto (`dot`) usa el color BASE de cada tono, no `-strong` — ese último
|
|
1279
1333
|
* está pensado para texto legible sobre el fondo pastel de `soft` y se ve
|
|
1280
1334
|
* apagado sobre una superficie translúcida/oscura.
|
|
1335
|
+
*
|
|
1336
|
+
* `truncate` corta un texto largo con elipsis (ej. un estado de SAP como
|
|
1337
|
+
* "WF Aprobación Política descuento") en vez de ensanchar la fila que lo
|
|
1338
|
+
* contiene: el badge se limita al ancho de su contenedor (`max-width: 100%`,
|
|
1339
|
+
* `min-width: 0`) y el punto no se recorta. El texto completo lo pone el
|
|
1340
|
+
* consumidor en `title` (el badge no puede leer su propio contenido
|
|
1341
|
+
* proyectado para generarlo):
|
|
1342
|
+
*
|
|
1343
|
+
* <kf-badge tone="warn" dot truncate [title]="q.statusLabel">{{ q.statusLabel }}</kf-badge>
|
|
1281
1344
|
*/
|
|
1282
1345
|
class Badge {
|
|
1283
1346
|
tone = input('neutral', /* @ts-ignore */
|
|
@@ -1285,13 +1348,17 @@ class Badge {
|
|
|
1285
1348
|
variant = input('soft', /* @ts-ignore */
|
|
1286
1349
|
...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
|
|
1287
1350
|
dot = input(false, { ...(ngDevMode ? { debugName: "dot" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1351
|
+
/** Corta el texto con elipsis al ancho del contenedor. Ver JSDoc de la clase. */
|
|
1352
|
+
truncate = input(false, { ...(ngDevMode ? { debugName: "truncate" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
1288
1353
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Badge, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1289
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Badge, isStandalone: true, selector: "kf-badge", inputs: { tone: { classPropertyName: "tone", publicName: "tone", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, dot: { classPropertyName: "dot", publicName: "dot", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<span\n class=\"kf-badge\"\n [class]=\"'kf-badge--' + tone()\"\n [class.kf-badge--glass]=\"variant() === 'glass'\"\n>\n @if (dot()) {\n <span class=\"kf-badge__dot\"></span>\n }\n <ng-content
|
|
1354
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Badge, isStandalone: true, selector: "kf-badge", inputs: { tone: { classPropertyName: "tone", publicName: "tone", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, dot: { classPropertyName: "dot", publicName: "dot", isSignal: true, isRequired: false, transformFunction: null }, truncate: { classPropertyName: "truncate", publicName: "truncate", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.kf-badge-host--truncate": "truncate()" } }, ngImport: i0, template: "<span\n class=\"kf-badge\"\n [class]=\"'kf-badge--' + tone()\"\n [class.kf-badge--glass]=\"variant() === 'glass'\"\n>\n @if (dot()) {\n <span class=\"kf-badge__dot\"></span>\n }\n <!-- `display: contents` salvo con `truncate`: sin truncar, el contenido\n proyectado sigue siendo hijo directo del flex (mismo `gap` de siempre). -->\n <span class=\"kf-badge__label\"><ng-content /></span>\n</span>\n", styles: [":host{display:inline-block}.kf-badge{display:inline-flex;align-items:center;gap:var(--kf-space-xs);padding:var(--kf-space-xs) var(--kf-space-sm);border-radius:var(--kf-radius-pill);font:inherit;font-size:var(--kf-font-size-caption);font-weight:600;line-height:1;white-space:nowrap}.kf-badge__label{display:contents}:host(.kf-badge-host--truncate){display:inline-flex;max-width:100%;min-width:0;vertical-align:middle}:host(.kf-badge-host--truncate) .kf-badge{box-sizing:border-box;max-width:100%;min-width:0}:host(.kf-badge-host--truncate) .kf-badge__label{display:block;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.25;margin-block:-.125em}.kf-badge__dot{inline-size:6px;block-size:6px;border-radius:var(--kf-radius-pill);flex:none;background:var(--kf-badge-accent, currentColor)}.kf-badge--neutral{background:var(--kf-color-gray-200);color:var(--kf-color-text);--kf-badge-accent: var(--kf-color-text)}.kf-badge--ok{background:var(--kf-color-success-soft);color:var(--kf-color-success-strong);--kf-badge-accent: var(--kf-color-success-strong)}.kf-badge--warn{background:var(--kf-color-warning-soft);color:var(--kf-color-warning-strong);--kf-badge-accent: var(--kf-color-warning-strong)}.kf-badge--error{background:var(--kf-color-error-soft);color:var(--kf-color-error-strong);--kf-badge-accent: var(--kf-color-error-strong)}.kf-badge--info{background:var(--kf-color-info-soft);color:var(--kf-color-info-strong);--kf-badge-accent: var(--kf-color-info-strong)}.kf-badge--service{background:var(--kf-color-secondary-soft);color:var(--kf-color-secondary-strong);--kf-badge-accent: var(--kf-color-secondary-strong)}.kf-badge--glass{background:#ffffff24;backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);color:var(--kf-color-white)}.kf-badge--ok.kf-badge--glass{--kf-badge-accent: var(--kf-color-success)}.kf-badge--warn.kf-badge--glass{--kf-badge-accent: var(--kf-color-warning)}.kf-badge--error.kf-badge--glass{--kf-badge-accent: var(--kf-color-error)}.kf-badge--info.kf-badge--glass{--kf-badge-accent: var(--kf-color-info)}.kf-badge--service.kf-badge--glass{--kf-badge-accent: var(--kf-color-secondary)}\n"] });
|
|
1290
1355
|
}
|
|
1291
1356
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Badge, decorators: [{
|
|
1292
1357
|
type: Component,
|
|
1293
|
-
args: [{ selector: 'kf-badge',
|
|
1294
|
-
|
|
1358
|
+
args: [{ selector: 'kf-badge', host: {
|
|
1359
|
+
'[class.kf-badge-host--truncate]': 'truncate()',
|
|
1360
|
+
}, template: "<span\n class=\"kf-badge\"\n [class]=\"'kf-badge--' + tone()\"\n [class.kf-badge--glass]=\"variant() === 'glass'\"\n>\n @if (dot()) {\n <span class=\"kf-badge__dot\"></span>\n }\n <!-- `display: contents` salvo con `truncate`: sin truncar, el contenido\n proyectado sigue siendo hijo directo del flex (mismo `gap` de siempre). -->\n <span class=\"kf-badge__label\"><ng-content /></span>\n</span>\n", styles: [":host{display:inline-block}.kf-badge{display:inline-flex;align-items:center;gap:var(--kf-space-xs);padding:var(--kf-space-xs) var(--kf-space-sm);border-radius:var(--kf-radius-pill);font:inherit;font-size:var(--kf-font-size-caption);font-weight:600;line-height:1;white-space:nowrap}.kf-badge__label{display:contents}:host(.kf-badge-host--truncate){display:inline-flex;max-width:100%;min-width:0;vertical-align:middle}:host(.kf-badge-host--truncate) .kf-badge{box-sizing:border-box;max-width:100%;min-width:0}:host(.kf-badge-host--truncate) .kf-badge__label{display:block;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap;line-height:1.25;margin-block:-.125em}.kf-badge__dot{inline-size:6px;block-size:6px;border-radius:var(--kf-radius-pill);flex:none;background:var(--kf-badge-accent, currentColor)}.kf-badge--neutral{background:var(--kf-color-gray-200);color:var(--kf-color-text);--kf-badge-accent: var(--kf-color-text)}.kf-badge--ok{background:var(--kf-color-success-soft);color:var(--kf-color-success-strong);--kf-badge-accent: var(--kf-color-success-strong)}.kf-badge--warn{background:var(--kf-color-warning-soft);color:var(--kf-color-warning-strong);--kf-badge-accent: var(--kf-color-warning-strong)}.kf-badge--error{background:var(--kf-color-error-soft);color:var(--kf-color-error-strong);--kf-badge-accent: var(--kf-color-error-strong)}.kf-badge--info{background:var(--kf-color-info-soft);color:var(--kf-color-info-strong);--kf-badge-accent: var(--kf-color-info-strong)}.kf-badge--service{background:var(--kf-color-secondary-soft);color:var(--kf-color-secondary-strong);--kf-badge-accent: var(--kf-color-secondary-strong)}.kf-badge--glass{background:#ffffff24;backdrop-filter:blur(6px);-webkit-backdrop-filter:blur(6px);color:var(--kf-color-white)}.kf-badge--ok.kf-badge--glass{--kf-badge-accent: var(--kf-color-success)}.kf-badge--warn.kf-badge--glass{--kf-badge-accent: var(--kf-color-warning)}.kf-badge--error.kf-badge--glass{--kf-badge-accent: var(--kf-color-error)}.kf-badge--info.kf-badge--glass{--kf-badge-accent: var(--kf-color-info)}.kf-badge--service.kf-badge--glass{--kf-badge-accent: var(--kf-color-secondary)}\n"] }]
|
|
1361
|
+
}], propDecorators: { tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "tone", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], dot: [{ type: i0.Input, args: [{ isSignal: true, alias: "dot", required: false }] }], truncate: [{ type: i0.Input, args: [{ isSignal: true, alias: "truncate", required: false }] }] } });
|
|
1295
1362
|
|
|
1296
1363
|
/**
|
|
1297
1364
|
* Ruta de navegación de las vistas internas. El último nivel es la página
|
|
@@ -1620,7 +1687,7 @@ class Calendar {
|
|
|
1620
1687
|
};
|
|
1621
1688
|
}
|
|
1622
1689
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Calendar, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
1623
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Calendar, isStandalone: true, selector: "kf-calendar", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, month: { classPropertyName: "month", publicName: "month", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, availableDates: { classPropertyName: "availableDates", publicName: "availableDates", isSignal: true, isRequired: false, transformFunction: null }, markedDates: { classPropertyName: "markedDates", publicName: "markedDates", isSignal: true, isRequired: false, transformFunction: null }, today: { classPropertyName: "today", publicName: "today", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, monthNames: { classPropertyName: "monthNames", publicName: "monthNames", isSignal: true, isRequired: false, transformFunction: null }, weekdayLabels: { classPropertyName: "weekdayLabels", publicName: "weekdayLabels", isSignal: true, isRequired: false, transformFunction: null }, weekdayNames: { classPropertyName: "weekdayNames", publicName: "weekdayNames", isSignal: true, isRequired: false, transformFunction: null }, markedLabel: { classPropertyName: "markedLabel", publicName: "markedLabel", isSignal: true, isRequired: false, transformFunction: null }, previousMonthLabel: { classPropertyName: "previousMonthLabel", publicName: "previousMonthLabel", isSignal: true, isRequired: false, transformFunction: null }, nextMonthLabel: { classPropertyName: "nextMonthLabel", publicName: "nextMonthLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", month: "monthChange" }, ngImport: i0, template: "<div class=\"kf-calendar\" role=\"group\" [attr.aria-label]=\"ariaLabel()\">\n <div class=\"kf-calendar__header\">\n <kf-button\n variant=\"ghost\"\n shape=\"circle\"\n size=\"lg\"\n type=\"button\"\n icon=\"chevron-left\"\n [ariaLabel]=\"previousMonthLabel()\"\n [disabled]=\"!canGoPrevious()\"\n (click)=\"previousMonth()\"\n />\n <kf-text\n class=\"kf-calendar__title\"\n as=\"span\"\n variant=\"title-sm\"\n aria-live=\"polite\"\n [attr.id]=\"titleId\"\n >{{ title() }}</kf-text\n >\n <kf-button\n variant=\"ghost\"\n shape=\"circle\"\n size=\"lg\"\n type=\"button\"\n icon=\"chevron-right\"\n [ariaLabel]=\"nextMonthLabel()\"\n [disabled]=\"!canGoNext()\"\n (click)=\"nextMonth()\"\n />\n </div>\n\n <div\n class=\"kf-calendar__grid\"\n role=\"grid\"\n [class.kf-calendar__grid--loading]=\"loading()\"\n [attr.aria-labelledby]=\"titleId\"\n [attr.aria-busy]=\"loading() || null\"\n >\n <div class=\"kf-calendar__row\" role=\"row\">\n @for (label of weekdayLabels(); track $index) {\n <span\n class=\"kf-calendar__weekday kf-overline-text\"\n role=\"columnheader\"\n [attr.aria-label]=\"weekdayNames()[$index]\"\n >{{ label }}</span\n >\n }\n </div>\n\n @for (week of weeks(); track week[0].iso) {\n <div class=\"kf-calendar__row\" role=\"row\">\n @for (day of week; track day.iso) {\n @if (day.inMonth) {\n <button\n type=\"button\"\n role=\"gridcell\"\n class=\"kf-calendar__day\"\n [class.kf-calendar__day--selectable]=\"day.selectable\"\n [class.kf-calendar__day--selected]=\"day.selected\"\n [class.kf-calendar__day--today]=\"day.today\"\n [class.kf-calendar__day--marked]=\"day.marked\"\n [attr.data-date]=\"day.iso\"\n [attr.aria-label]=\"day.ariaLabel\"\n [attr.aria-selected]=\"day.selected\"\n [attr.aria-disabled]=\"!day.selectable\"\n [attr.aria-current]=\"day.today ? 'date' : null\"\n [attr.tabindex]=\"day.iso === activeDate() ? 0 : -1\"\n (click)=\"selectDay(day)\"\n (focus)=\"onDayFocus(day.iso)\"\n (keydown)=\"onDayKeydown($event, day.iso)\"\n >\n {{ day.day }}\n </button>\n } @else {\n <span role=\"gridcell\" class=\"kf-calendar__day kf-calendar__day--outside\">\n <span aria-hidden=\"true\">{{ day.day }}</span>\n </span>\n }\n }\n </div>\n }\n </div>\n\n <ng-content select=\"[footer]\" />\n</div>\n", styles: [".kf-overline-text{font-family:var(--kf-font-title);font-size:var(--kf-font-size-caption);font-weight:var(--kf-font-weight-medium);letter-spacing:.04em;text-transform:uppercase;color:var(--kf-color-text-muted)}:host{display:block}.kf-calendar{display:flex;flex-direction:column;gap:var(--kf-space-md)}.kf-calendar__header{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm)}.kf-calendar__title{flex:1;text-align:center}.kf-calendar__grid{display:flex;flex-direction:column;gap:2px;transition:opacity .2s ease}.kf-calendar__grid--loading{opacity:.5;cursor:progress}.kf-calendar__row{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:2px}.kf-calendar__weekday{display:flex;align-items:center;justify-content:center;min-height:32px}.kf-calendar__day{position:relative;display:flex;align-items:center;justify-content:center;min-width:0;min-height:44px;padding:0;box-sizing:border-box;border:none;border-radius:var(--kf-radius-sm);background:transparent;font:inherit;font-size:var(--kf-font-size-body-sm);font-weight:var(--kf-font-weight-body);color:var(--kf-color-text-muted);cursor:default;transition:background-color .14s,color .14s}.kf-calendar__day--selectable{font-weight:var(--kf-font-weight-medium);color:var(--kf-color-ink);cursor:pointer}.kf-calendar__grid--loading .kf-calendar__day--selectable{cursor:progress}.kf-calendar__day--selectable:hover:not(.kf-calendar__day--selected){background:var(--kf-color-primary-soft)}.kf-calendar__day:focus-visible{z-index:1;outline:2px solid var(--kf-color-primary);outline-offset:1px}.kf-calendar__day--today{box-shadow:inset 0 0 0 1.5px var(--kf-color-primary)}.kf-calendar__day--selected{background:var(--kf-color-primary);color:var(--kf-color-white)}.kf-calendar__day--selected:hover{background:var(--kf-color-primary-hover)}.kf-calendar__day--outside{color:var(--kf-color-gray-400)}.kf-calendar__day--marked:after{content:\"\";position:absolute;bottom:6px;left:50%;width:4px;height:4px;margin-left:-2px;border-radius:var(--kf-radius-pill);background:var(--kf-color-primary)}.kf-calendar__day--selected.kf-calendar__day--marked:after{background:var(--kf-color-white)}@media(prefers-reduced-motion:reduce){.kf-calendar__grid,.kf-calendar__day{transition:none}}\n"], dependencies: [{ kind: "component", type: Button, selector: "kf-button", inputs: ["variant", "size", "shape", "disabled", "type", "icon", "block", "loading", "ariaLabel", "ariaExpanded", "ariaPressed"] }, { kind: "component", type: Text, selector: "kf-text", inputs: ["as", "variant", "tone"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1690
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Calendar, isStandalone: true, selector: "kf-calendar", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, month: { classPropertyName: "month", publicName: "month", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, availableDates: { classPropertyName: "availableDates", publicName: "availableDates", isSignal: true, isRequired: false, transformFunction: null }, markedDates: { classPropertyName: "markedDates", publicName: "markedDates", isSignal: true, isRequired: false, transformFunction: null }, today: { classPropertyName: "today", publicName: "today", isSignal: true, isRequired: false, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, monthNames: { classPropertyName: "monthNames", publicName: "monthNames", isSignal: true, isRequired: false, transformFunction: null }, weekdayLabels: { classPropertyName: "weekdayLabels", publicName: "weekdayLabels", isSignal: true, isRequired: false, transformFunction: null }, weekdayNames: { classPropertyName: "weekdayNames", publicName: "weekdayNames", isSignal: true, isRequired: false, transformFunction: null }, markedLabel: { classPropertyName: "markedLabel", publicName: "markedLabel", isSignal: true, isRequired: false, transformFunction: null }, previousMonthLabel: { classPropertyName: "previousMonthLabel", publicName: "previousMonthLabel", isSignal: true, isRequired: false, transformFunction: null }, nextMonthLabel: { classPropertyName: "nextMonthLabel", publicName: "nextMonthLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", month: "monthChange" }, ngImport: i0, template: "<div class=\"kf-calendar\" role=\"group\" [attr.aria-label]=\"ariaLabel()\">\n <div class=\"kf-calendar__header\">\n <kf-button\n variant=\"ghost\"\n shape=\"circle\"\n size=\"lg\"\n type=\"button\"\n icon=\"chevron-left\"\n [ariaLabel]=\"previousMonthLabel()\"\n [disabled]=\"!canGoPrevious()\"\n (click)=\"previousMonth()\"\n />\n <kf-text\n class=\"kf-calendar__title\"\n as=\"span\"\n variant=\"title-sm\"\n aria-live=\"polite\"\n [attr.id]=\"titleId\"\n >{{ title() }}</kf-text\n >\n <kf-button\n variant=\"ghost\"\n shape=\"circle\"\n size=\"lg\"\n type=\"button\"\n icon=\"chevron-right\"\n [ariaLabel]=\"nextMonthLabel()\"\n [disabled]=\"!canGoNext()\"\n (click)=\"nextMonth()\"\n />\n </div>\n\n <div\n class=\"kf-calendar__grid\"\n role=\"grid\"\n [class.kf-calendar__grid--loading]=\"loading()\"\n [attr.aria-labelledby]=\"titleId\"\n [attr.aria-busy]=\"loading() || null\"\n >\n <div class=\"kf-calendar__row\" role=\"row\">\n @for (label of weekdayLabels(); track $index) {\n <span\n class=\"kf-calendar__weekday kf-overline-text\"\n role=\"columnheader\"\n [attr.aria-label]=\"weekdayNames()[$index]\"\n >{{ label }}</span\n >\n }\n </div>\n\n @for (week of weeks(); track week[0].iso) {\n <div class=\"kf-calendar__row\" role=\"row\">\n @for (day of week; track day.iso) {\n @if (day.inMonth) {\n <button\n type=\"button\"\n role=\"gridcell\"\n class=\"kf-calendar__day\"\n [class.kf-calendar__day--selectable]=\"day.selectable\"\n [class.kf-calendar__day--selected]=\"day.selected\"\n [class.kf-calendar__day--today]=\"day.today\"\n [class.kf-calendar__day--marked]=\"day.marked\"\n [attr.data-date]=\"day.iso\"\n [attr.aria-label]=\"day.ariaLabel\"\n [attr.aria-selected]=\"day.selected\"\n [attr.aria-disabled]=\"!day.selectable\"\n [attr.aria-current]=\"day.today ? 'date' : null\"\n [attr.tabindex]=\"day.iso === activeDate() ? 0 : -1\"\n (click)=\"selectDay(day)\"\n (focus)=\"onDayFocus(day.iso)\"\n (keydown)=\"onDayKeydown($event, day.iso)\"\n >\n {{ day.day }}\n </button>\n } @else {\n <span role=\"gridcell\" class=\"kf-calendar__day kf-calendar__day--outside\">\n <span aria-hidden=\"true\">{{ day.day }}</span>\n </span>\n }\n }\n </div>\n }\n </div>\n\n <ng-content select=\"[footer]\" />\n</div>\n", styles: [".kf-overline-text{font-family:var(--kf-font-title);font-size:var(--kf-font-size-caption);font-weight:var(--kf-font-weight-medium);letter-spacing:.04em;text-transform:uppercase;color:var(--kf-color-text-muted)}:host{display:block}.kf-calendar{display:flex;flex-direction:column;gap:var(--kf-space-md)}.kf-calendar__header{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm)}.kf-calendar__title{flex:1;text-align:center}.kf-calendar__grid{display:flex;flex-direction:column;gap:2px;transition:opacity .2s ease}.kf-calendar__grid--loading{opacity:.5;cursor:progress}.kf-calendar__row{display:grid;grid-template-columns:repeat(7,minmax(0,1fr));gap:2px}.kf-calendar__weekday{display:flex;align-items:center;justify-content:center;min-height:32px}.kf-calendar__day{position:relative;display:flex;align-items:center;justify-content:center;min-width:0;min-height:44px;padding:0;box-sizing:border-box;border:none;border-radius:var(--kf-radius-sm);background:transparent;font:inherit;font-size:var(--kf-font-size-body-sm);font-weight:var(--kf-font-weight-body);color:var(--kf-color-text-muted);cursor:default;transition:background-color .14s,color .14s}.kf-calendar__day--selectable{font-weight:var(--kf-font-weight-medium);color:var(--kf-color-ink);cursor:pointer}.kf-calendar__grid--loading .kf-calendar__day--selectable{cursor:progress}.kf-calendar__day--selectable:hover:not(.kf-calendar__day--selected){background:var(--kf-color-primary-soft)}.kf-calendar__day:focus-visible{z-index:1;outline:2px solid var(--kf-color-primary);outline-offset:1px}.kf-calendar__day--today{box-shadow:inset 0 0 0 1.5px var(--kf-color-primary)}.kf-calendar__day--selected{background:var(--kf-color-primary);color:var(--kf-color-white)}.kf-calendar__day--selected:hover{background:var(--kf-color-primary-hover)}.kf-calendar__day--outside{color:var(--kf-color-gray-400)}.kf-calendar__day--marked:after{content:\"\";position:absolute;bottom:6px;left:50%;width:4px;height:4px;margin-left:-2px;border-radius:var(--kf-radius-pill);background:var(--kf-color-primary)}.kf-calendar__day--selected.kf-calendar__day--marked:after{background:var(--kf-color-white)}@media(prefers-reduced-motion:reduce){.kf-calendar__grid,.kf-calendar__day{transition:none}}\n"], dependencies: [{ kind: "component", type: Button, selector: "kf-button", inputs: ["variant", "size", "shape", "disabled", "type", "icon", "block", "loading", "ariaLabel", "ariaExpanded", "ariaPressed", "ariaHaspopup"] }, { kind: "component", type: Text, selector: "kf-text", inputs: ["as", "variant", "tone"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
1624
1691
|
}
|
|
1625
1692
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Calendar, decorators: [{
|
|
1626
1693
|
type: Component,
|
|
@@ -1962,7 +2029,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
|
|
|
1962
2029
|
}, template: "@if (dot()) {\n <span\n class=\"kf-chip__dot\"\n [class.kf-chip__dot--ok]=\"tone() === 'ok'\"\n [class.kf-chip__dot--warn]=\"tone() === 'warn'\"\n [class.kf-chip__dot--error]=\"tone() === 'error'\"\n [class.kf-chip__dot--neutral]=\"tone() === 'neutral'\"\n ></span>\n}\n<span class=\"kf-chip__label\"><ng-content /></span>\n", styles: [":host{display:inline-flex;align-items:center;gap:var(--kf-space-xs);padding:var(--kf-space-xs) var(--kf-space-sm);border-radius:var(--kf-radius-pill);border:1px solid transparent;font:inherit;font-size:.875em;line-height:1;color:var(--kf-color-text);background:var(--kf-color-surface);border-color:var(--kf-color-border);white-space:nowrap}:host(.kf-chip--ok){background:var(--kf-color-success-soft);border-color:var(--kf-color-success-soft);color:var(--kf-color-text)}:host(.kf-chip--warn){background:var(--kf-color-warning-soft);border-color:var(--kf-color-warning-soft);color:var(--kf-color-text)}:host(.kf-chip--error){background:var(--kf-color-error-soft);border-color:var(--kf-color-error-soft);color:var(--kf-color-text)}:host(.kf-chip--neutral){background:var(--kf-color-surface);border-color:var(--kf-color-border);color:var(--kf-color-text)}.kf-chip__dot{inline-size:.5em;block-size:.5em;border-radius:var(--kf-radius-pill);flex:none;background:var(--kf-color-text-muted)}.kf-chip__label{display:inline-block}.kf-chip__dot--ok{background:var(--kf-color-success)}.kf-chip__dot--warn{background:var(--kf-color-warning)}.kf-chip__dot--error{background:var(--kf-color-error)}.kf-chip__dot--neutral{background:var(--kf-color-text-muted)}\n"] }]
|
|
1963
2030
|
}], propDecorators: { tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "tone", required: false }] }], dot: [{ type: i0.Input, args: [{ isSignal: true, alias: "dot", required: false }] }] } });
|
|
1964
2031
|
|
|
1965
|
-
let nextId$
|
|
2032
|
+
let nextId$4 = 0;
|
|
1966
2033
|
/**
|
|
1967
2034
|
* Bloque colapsable genérico: un header clickeable (título + chevron) que
|
|
1968
2035
|
* expande/colapsa contenido proyectado libre, sin saber nada de lo que hay
|
|
@@ -1989,7 +2056,7 @@ class Collapsible {
|
|
|
1989
2056
|
...(ngDevMode ? [{ debugName: "title" }] : /* istanbul ignore next */ []));
|
|
1990
2057
|
expanded = model(false, /* @ts-ignore */
|
|
1991
2058
|
...(ngDevMode ? [{ debugName: "expanded" }] : /* istanbul ignore next */ []));
|
|
1992
|
-
instanceId = `kf-collapsible-${nextId$
|
|
2059
|
+
instanceId = `kf-collapsible-${nextId$4++}`;
|
|
1993
2060
|
triggerId = `${this.instanceId}-trigger`;
|
|
1994
2061
|
panelId = `${this.instanceId}-panel`;
|
|
1995
2062
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Collapsible, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
@@ -2286,22 +2353,63 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
|
|
|
2286
2353
|
* No pudimos cargar tus vehículos
|
|
2287
2354
|
* <kf-button (click)="retry()">Reintentar</kf-button>
|
|
2288
2355
|
* </kf-empty-state>
|
|
2356
|
+
*
|
|
2357
|
+
* ## Variante `card`
|
|
2358
|
+
* Tarjeta centrada (fondo blanco, borde, sombra, ancho máximo 480px) para
|
|
2359
|
+
* un estado que REEMPLAZA a un contenido grande — ej. un iframe que no
|
|
2360
|
+
* cargó o un documento que aún no existe. Ícono en un círculo tonal de
|
|
2361
|
+
* 56px según `tone` (`warn`, `info`, `error` o `neutral`), `title` como
|
|
2362
|
+
* título, el contenido proyectado como texto (puede incluir una lista de
|
|
2363
|
+
* pasos) y `[kf-empty-state-actions]` para los botones. Los roles
|
|
2364
|
+
* (`role="alert"` para un error) los pone el consumidor en el host: no
|
|
2365
|
+
* todo estado de esta forma debe interrumpir al lector de pantalla.
|
|
2366
|
+
*
|
|
2367
|
+
* <kf-empty-state variant="card" tone="warn" icon="alert-triangle"
|
|
2368
|
+
* title="No pudimos cargar la propuesta" role="alert">
|
|
2369
|
+
* <p>Este contenido solo está disponible dentro de la red de la empresa.</p>
|
|
2370
|
+
* <ol>…pasos…</ol>
|
|
2371
|
+
* <div kf-empty-state-actions>
|
|
2372
|
+
* <kf-button (click)="retry()">Reintentar</kf-button>
|
|
2373
|
+
* </div>
|
|
2374
|
+
* </kf-empty-state>
|
|
2375
|
+
*
|
|
2376
|
+
* `[kf-empty-state-footer]` va DESPUÉS de las acciones, para pistas y
|
|
2377
|
+
* metadatos debajo de los botones ("¿Ya estás conectado? …", "Intento 2 ·
|
|
2378
|
+
* 10:42"). Vacío, no ocupa lugar.
|
|
2379
|
+
*
|
|
2380
|
+
* `title` se dibuja como texto destacado, no como encabezado (`<h*>`): el
|
|
2381
|
+
* nivel de encabezado depende de la página que lo contiene, y un estado
|
|
2382
|
+
* vacío no debe alterar su estructura.
|
|
2289
2383
|
*/
|
|
2290
2384
|
class EmptyState {
|
|
2291
2385
|
icon = input(undefined, /* @ts-ignore */
|
|
2292
2386
|
...(ngDevMode ? [{ debugName: "icon" }] : /* istanbul ignore next */ []));
|
|
2293
|
-
/** `neutral` (default): cero resultados. `error`: fallo real de carga. */
|
|
2387
|
+
/** `neutral` (default): cero resultados. `error`: fallo real de carga. `warn`/`info`: tiñen el chip del ícono. */
|
|
2294
2388
|
tone = input('neutral', /* @ts-ignore */
|
|
2295
2389
|
...(ngDevMode ? [{ debugName: "tone" }] : /* istanbul ignore next */ []));
|
|
2390
|
+
/** `inline` (default, histórico) o `card` (tarjeta centrada). Ver JSDoc de la clase. */
|
|
2391
|
+
variant = input('inline', /* @ts-ignore */
|
|
2392
|
+
...(ngDevMode ? [{ debugName: "variant" }] : /* istanbul ignore next */ []));
|
|
2393
|
+
/** Título destacado sobre el contenido. Opcional en ambas variantes. */
|
|
2394
|
+
title = input(/* @ts-ignore */
|
|
2395
|
+
...(ngDevMode ? [undefined, { debugName: "title" }] : /* istanbul ignore next */ []));
|
|
2396
|
+
iconSize = computed(() => this.variant() === 'card' || this.tone() === 'error' ? 'lg' : 'md', /* @ts-ignore */
|
|
2397
|
+
...(ngDevMode ? [{ debugName: "iconSize" }] : /* istanbul ignore next */ []));
|
|
2296
2398
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: EmptyState, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2297
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: EmptyState, isStandalone: true, selector: "kf-empty-state", inputs: { icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, tone: { classPropertyName: "tone", publicName: "tone", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.kf-empty-state--error": "tone() === 'error'" } }, ngImport: i0, template: "@if (icon(); as iconName) {\n <span class=\"kf-empty-state__icon\">\n <kf-icon [name]=\"iconName\" [size]=\"
|
|
2399
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: EmptyState, isStandalone: true, selector: "kf-empty-state", inputs: { icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, tone: { classPropertyName: "tone", publicName: "tone", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class.kf-empty-state--error": "tone() === 'error'", "class.kf-empty-state--warn": "tone() === 'warn'", "class.kf-empty-state--info": "tone() === 'info'", "class.kf-empty-state--card": "variant() === 'card'", "attr.title": "null" } }, ngImport: i0, template: "@if (icon(); as iconName) {\n <span class=\"kf-empty-state__icon\">\n <kf-icon [name]=\"iconName\" [size]=\"iconSize()\" />\n </span>\n}\n@if (title(); as titleText) {\n <p class=\"kf-empty-state__title\">{{ titleText }}</p>\n}\n<div class=\"kf-empty-state__content\">\n <ng-content />\n</div>\n<div class=\"kf-empty-state__actions\"><ng-content select=\"[kf-empty-state-actions]\" /></div>\n<div class=\"kf-empty-state__footer\"><ng-content select=\"[kf-empty-state-footer]\" /></div>\n", styles: [":host{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--kf-space-sm);padding:var(--kf-space-xl);border:1px dashed var(--kf-color-border-strong);border-radius:var(--kf-radius-md);background:var(--kf-color-surface);color:var(--kf-color-text-muted);font:inherit;text-align:center}:host(.kf-empty-state--error){border:none;background:var(--kf-color-error-soft)}.kf-empty-state__content{color:var(--kf-color-text-muted);font-family:var(--kf-font-body)}:host(.kf-empty-state--error) .kf-empty-state__content{color:var(--kf-color-error-strong)}.kf-empty-state__title{margin:0;font-family:var(--kf-font-title);font-size:var(--kf-font-size-title-sm);line-height:1.25;color:var(--kf-color-ink)}.kf-empty-state__actions{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:var(--kf-space-sm)}.kf-empty-state__actions>::ng-deep [kf-empty-state-actions]{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:var(--kf-space-sm)}.kf-empty-state__actions:empty,.kf-empty-state__footer:empty{display:none}.kf-empty-state__footer{display:flex;flex-direction:column;align-items:center;gap:var(--kf-space-xs);font-size:var(--kf-font-size-caption);color:var(--kf-color-text)}:host(.kf-empty-state--warn) .kf-empty-state__icon{color:var(--kf-color-warning-strong)}:host(.kf-empty-state--info) .kf-empty-state__icon{color:var(--kf-color-info-strong)}:host(.kf-empty-state--card){box-sizing:border-box;width:min(480px,100%);gap:var(--kf-space-md);padding:var(--kf-space-xl) var(--kf-space-lg);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-lg);background:var(--kf-color-white);box-shadow:var(--kf-shadow-md);color:var(--kf-color-text)}:host(.kf-empty-state--card) .kf-empty-state__title+.kf-empty-state__content{margin-top:calc(var(--kf-space-sm) - var(--kf-space-md))}:host(.kf-empty-state--card) .kf-empty-state__content>::ng-deep :is(p,ol,ul),:host(.kf-empty-state--card) .kf-empty-state__footer>::ng-deep [kf-empty-state-footer] :is(p),:host(.kf-empty-state--card) .kf-empty-state__footer>::ng-deep p[kf-empty-state-footer]{margin:0}:host(.kf-empty-state--card) .kf-empty-state__content:empty{display:none}:host(.kf-empty-state--card) .kf-empty-state__content{display:flex;flex-direction:column;align-items:stretch;gap:var(--kf-space-md);align-self:stretch;color:var(--kf-color-text);font-size:var(--kf-font-size-body-sm);line-height:1.5}:host(.kf-empty-state--card.kf-empty-state--error) .kf-empty-state__content{color:var(--kf-color-text)}:host(.kf-empty-state--card) .kf-empty-state__icon{display:flex;align-items:center;justify-content:center;width:56px;height:56px;border-radius:50%;background:var(--kf-color-surface);color:var(--kf-color-text-muted)}:host(.kf-empty-state--card.kf-empty-state--warn) .kf-empty-state__icon{background:var(--kf-color-warning-soft);color:var(--kf-color-warning-strong)}:host(.kf-empty-state--card.kf-empty-state--info) .kf-empty-state__icon{background:var(--kf-color-info-soft);color:var(--kf-color-info-strong)}:host(.kf-empty-state--card.kf-empty-state--error) .kf-empty-state__icon{background:var(--kf-color-error-soft);color:var(--kf-color-error-strong)}\n"], dependencies: [{ kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size", "rotate", "filled"] }] });
|
|
2298
2400
|
}
|
|
2299
2401
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: EmptyState, decorators: [{
|
|
2300
2402
|
type: Component,
|
|
2301
2403
|
args: [{ selector: 'kf-empty-state', imports: [Icon], host: {
|
|
2302
2404
|
'[class.kf-empty-state--error]': "tone() === 'error'",
|
|
2303
|
-
|
|
2304
|
-
|
|
2405
|
+
'[class.kf-empty-state--warn]': "tone() === 'warn'",
|
|
2406
|
+
'[class.kf-empty-state--info]': "tone() === 'info'",
|
|
2407
|
+
'[class.kf-empty-state--card]': "variant() === 'card'",
|
|
2408
|
+
// `title` es un input con nombre de atributo nativo: escrito estático,
|
|
2409
|
+
// Angular también lo dejaría en el host como tooltip sobre todo el bloque.
|
|
2410
|
+
'[attr.title]': 'null',
|
|
2411
|
+
}, template: "@if (icon(); as iconName) {\n <span class=\"kf-empty-state__icon\">\n <kf-icon [name]=\"iconName\" [size]=\"iconSize()\" />\n </span>\n}\n@if (title(); as titleText) {\n <p class=\"kf-empty-state__title\">{{ titleText }}</p>\n}\n<div class=\"kf-empty-state__content\">\n <ng-content />\n</div>\n<div class=\"kf-empty-state__actions\"><ng-content select=\"[kf-empty-state-actions]\" /></div>\n<div class=\"kf-empty-state__footer\"><ng-content select=\"[kf-empty-state-footer]\" /></div>\n", styles: [":host{display:flex;flex-direction:column;align-items:center;justify-content:center;gap:var(--kf-space-sm);padding:var(--kf-space-xl);border:1px dashed var(--kf-color-border-strong);border-radius:var(--kf-radius-md);background:var(--kf-color-surface);color:var(--kf-color-text-muted);font:inherit;text-align:center}:host(.kf-empty-state--error){border:none;background:var(--kf-color-error-soft)}.kf-empty-state__content{color:var(--kf-color-text-muted);font-family:var(--kf-font-body)}:host(.kf-empty-state--error) .kf-empty-state__content{color:var(--kf-color-error-strong)}.kf-empty-state__title{margin:0;font-family:var(--kf-font-title);font-size:var(--kf-font-size-title-sm);line-height:1.25;color:var(--kf-color-ink)}.kf-empty-state__actions{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:var(--kf-space-sm)}.kf-empty-state__actions>::ng-deep [kf-empty-state-actions]{display:flex;flex-wrap:wrap;align-items:center;justify-content:center;gap:var(--kf-space-sm)}.kf-empty-state__actions:empty,.kf-empty-state__footer:empty{display:none}.kf-empty-state__footer{display:flex;flex-direction:column;align-items:center;gap:var(--kf-space-xs);font-size:var(--kf-font-size-caption);color:var(--kf-color-text)}:host(.kf-empty-state--warn) .kf-empty-state__icon{color:var(--kf-color-warning-strong)}:host(.kf-empty-state--info) .kf-empty-state__icon{color:var(--kf-color-info-strong)}:host(.kf-empty-state--card){box-sizing:border-box;width:min(480px,100%);gap:var(--kf-space-md);padding:var(--kf-space-xl) var(--kf-space-lg);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-lg);background:var(--kf-color-white);box-shadow:var(--kf-shadow-md);color:var(--kf-color-text)}:host(.kf-empty-state--card) .kf-empty-state__title+.kf-empty-state__content{margin-top:calc(var(--kf-space-sm) - var(--kf-space-md))}:host(.kf-empty-state--card) .kf-empty-state__content>::ng-deep :is(p,ol,ul),:host(.kf-empty-state--card) .kf-empty-state__footer>::ng-deep [kf-empty-state-footer] :is(p),:host(.kf-empty-state--card) .kf-empty-state__footer>::ng-deep p[kf-empty-state-footer]{margin:0}:host(.kf-empty-state--card) .kf-empty-state__content:empty{display:none}:host(.kf-empty-state--card) .kf-empty-state__content{display:flex;flex-direction:column;align-items:stretch;gap:var(--kf-space-md);align-self:stretch;color:var(--kf-color-text);font-size:var(--kf-font-size-body-sm);line-height:1.5}:host(.kf-empty-state--card.kf-empty-state--error) .kf-empty-state__content{color:var(--kf-color-text)}:host(.kf-empty-state--card) .kf-empty-state__icon{display:flex;align-items:center;justify-content:center;width:56px;height:56px;border-radius:50%;background:var(--kf-color-surface);color:var(--kf-color-text-muted)}:host(.kf-empty-state--card.kf-empty-state--warn) .kf-empty-state__icon{background:var(--kf-color-warning-soft);color:var(--kf-color-warning-strong)}:host(.kf-empty-state--card.kf-empty-state--info) .kf-empty-state__icon{background:var(--kf-color-info-soft);color:var(--kf-color-info-strong)}:host(.kf-empty-state--card.kf-empty-state--error) .kf-empty-state__icon{background:var(--kf-color-error-soft);color:var(--kf-color-error-strong)}\n"] }]
|
|
2412
|
+
}], propDecorators: { icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "tone", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }] } });
|
|
2305
2413
|
|
|
2306
2414
|
/**
|
|
2307
2415
|
* Bloque placeholder con shimmer para contenido que todavía carga: una línea
|
|
@@ -2368,7 +2476,7 @@ class ContentState {
|
|
|
2368
2476
|
loadingLabel = input('Cargando…', /* @ts-ignore */
|
|
2369
2477
|
...(ngDevMode ? [{ debugName: "loadingLabel" }] : /* istanbul ignore next */ []));
|
|
2370
2478
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: ContentState, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2371
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: ContentState, isStandalone: true, selector: "kf-content-state", inputs: { state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: true, transformFunction: null }, load: { classPropertyName: "load", publicName: "load", 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 }, loadingLabel: { classPropertyName: "loadingLabel", publicName: "loadingLabel", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, 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"], dependencies: [{ kind: "component", type: EmptyState, selector: "kf-empty-state", inputs: ["icon", "tone"] }, { kind: "component", type: Skeleton, selector: "kf-skeleton", inputs: ["variant", "width", "height"] }, { kind: "component", type: Spinner, selector: "kf-spinner", inputs: ["size", "label", "onDark"] }] });
|
|
2479
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: ContentState, isStandalone: true, selector: "kf-content-state", inputs: { state: { classPropertyName: "state", publicName: "state", isSignal: true, isRequired: true, transformFunction: null }, load: { classPropertyName: "load", publicName: "load", 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 }, loadingLabel: { classPropertyName: "loadingLabel", publicName: "loadingLabel", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, 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"], dependencies: [{ kind: "component", type: EmptyState, selector: "kf-empty-state", inputs: ["icon", "tone", "variant", "title"] }, { kind: "component", type: Skeleton, selector: "kf-skeleton", inputs: ["variant", "width", "height"] }, { kind: "component", type: Spinner, selector: "kf-spinner", inputs: ["size", "label", "onDark"] }] });
|
|
2372
2480
|
}
|
|
2373
2481
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: ContentState, decorators: [{
|
|
2374
2482
|
type: Component,
|
|
@@ -2557,6 +2665,7 @@ class SelectOverlay {
|
|
|
2557
2665
|
viewContainerRef;
|
|
2558
2666
|
triggerRef;
|
|
2559
2667
|
minWidth;
|
|
2668
|
+
sizing;
|
|
2560
2669
|
overlayRef = null;
|
|
2561
2670
|
constructor(overlayService, viewContainerRef, triggerRef,
|
|
2562
2671
|
/**
|
|
@@ -2567,18 +2676,28 @@ class SelectOverlay {
|
|
|
2567
2676
|
* trigger normal (240px fijo) ya es más ancho que cualquier
|
|
2568
2677
|
* `minWidth` razonable, así que este parámetro no le cambia nada.
|
|
2569
2678
|
*/
|
|
2570
|
-
minWidth = () => 0
|
|
2679
|
+
minWidth = () => 0,
|
|
2680
|
+
/**
|
|
2681
|
+
* `'match'` (default): el panel mide exactamente el ancho del trigger.
|
|
2682
|
+
* `'content'`: el trigger es solo el piso y el panel crece hasta el
|
|
2683
|
+
* ancho natural de sus opciones (con techo `maxWidth: 90vw`) — para un
|
|
2684
|
+
* trigger compacto de barra de herramientas ("Más recientes") cuyas
|
|
2685
|
+
* opciones son más largas ("Más recientes primero") y no deben
|
|
2686
|
+
* truncarse.
|
|
2687
|
+
*/
|
|
2688
|
+
sizing = () => 'match') {
|
|
2571
2689
|
this.overlayService = overlayService;
|
|
2572
2690
|
this.viewContainerRef = viewContainerRef;
|
|
2573
2691
|
this.triggerRef = triggerRef;
|
|
2574
2692
|
this.minWidth = minWidth;
|
|
2693
|
+
this.sizing = sizing;
|
|
2575
2694
|
}
|
|
2576
2695
|
attach(panelTemplate, onClose) {
|
|
2577
2696
|
if (this.overlayRef?.hasAttached()) {
|
|
2578
2697
|
return;
|
|
2579
2698
|
}
|
|
2580
2699
|
const overlayRef = this.getOrCreate();
|
|
2581
|
-
overlayRef.updateSize(
|
|
2700
|
+
overlayRef.updateSize(this.resolveSize());
|
|
2582
2701
|
const portal = new TemplatePortal(panelTemplate, this.viewContainerRef);
|
|
2583
2702
|
overlayRef.attach(portal);
|
|
2584
2703
|
overlayRef.outsidePointerEvents().subscribe((event) => {
|
|
@@ -2618,17 +2737,25 @@ class SelectOverlay {
|
|
|
2618
2737
|
this.overlayRef = this.overlayService.create({
|
|
2619
2738
|
positionStrategy,
|
|
2620
2739
|
scrollStrategy: this.overlayService.scrollStrategies.reposition(),
|
|
2621
|
-
|
|
2740
|
+
...this.resolveSize(),
|
|
2622
2741
|
maxWidth: '90vw',
|
|
2623
2742
|
});
|
|
2624
2743
|
return this.overlayRef;
|
|
2625
2744
|
}
|
|
2745
|
+
resolveSize() {
|
|
2746
|
+
const width = this.resolveWidth();
|
|
2747
|
+
// `undefined` (no omitir la clave): `updateSize` mezcla con la config
|
|
2748
|
+
// previa, y solo una clave presente con `undefined` limpia el estilo.
|
|
2749
|
+
return this.sizing() === 'content'
|
|
2750
|
+
? { width: undefined, minWidth: width }
|
|
2751
|
+
: { width, minWidth: undefined };
|
|
2752
|
+
}
|
|
2626
2753
|
resolveWidth() {
|
|
2627
2754
|
return Math.max(this.triggerRef().nativeElement.getBoundingClientRect().width, this.minWidth());
|
|
2628
2755
|
}
|
|
2629
2756
|
}
|
|
2630
2757
|
|
|
2631
|
-
let nextId$
|
|
2758
|
+
let nextId$3 = 0;
|
|
2632
2759
|
/** Ancho mínimo del panel flotante en `variant="compact"` — el trigger de
|
|
2633
2760
|
* ese modo puede medir menos de 45px (ej. "10" + caret), y sin este piso
|
|
2634
2761
|
* la opción quedaría apretada contra ese mismo ancho angosto. 72px es lo
|
|
@@ -2649,6 +2776,7 @@ const COMPACT_PANEL_MIN_WIDTH = 72;
|
|
|
2649
2776
|
*
|
|
2650
2777
|
* <kf-select label="Sede" [options]="['Lima', 'Arequipa', 'Trujillo']" placeholder="Selecciona…" />
|
|
2651
2778
|
* <kf-select [options]="[{ value: 'pe', label: 'Perú' }]" (valueChange)="onCountry($event)" />
|
|
2779
|
+
* <kf-select [options]="[{ value: '1002099', label: 'BBVA', tag: 'Empresa', detail: 'RUC 20100130204' }]" />
|
|
2652
2780
|
*
|
|
2653
2781
|
* `variant="compact"` cambia el ancho de `240px` fijo a intrínseco con un
|
|
2654
2782
|
* techo de `160px` (se ajusta al contenido de la opción seleccionada hasta
|
|
@@ -2665,6 +2793,25 @@ const COMPACT_PANEL_MIN_WIDTH = 72;
|
|
|
2665
2793
|
* layouts flex/grid donde el ancho fijo de `md` no encaja.
|
|
2666
2794
|
*
|
|
2667
2795
|
* <kf-select width="full" [options]="branches" [value]="branch()" (valueChange)="..." />
|
|
2796
|
+
*
|
|
2797
|
+
* Control de orden de una barra de herramientas (ícono + valor + caret, sin
|
|
2798
|
+
* label visible): `icon` agrega un glifo `sm` a la izquierda del valor,
|
|
2799
|
+
* `ariaLabel` da el nombre accesible (el trigger se anuncia como
|
|
2800
|
+
* `"<ariaLabel>: <valor actual>"`), `panelTitle` agrega un título chico en
|
|
2801
|
+
* mayúsculas dentro del panel (decorativo, `aria-hidden`) y
|
|
2802
|
+
* `panelWidth="content"` deja que el panel crezca hasta el ancho de sus
|
|
2803
|
+
* opciones en vez de truncarlas al ancho del trigger compacto.
|
|
2804
|
+
*
|
|
2805
|
+
* <kf-select variant="compact" icon="sort" ariaLabel="Ordenar por"
|
|
2806
|
+
* panelTitle="Ordenar por" panelWidth="content"
|
|
2807
|
+
* [options]="sortOptions" [value]="sort()" (valueChange)="sort.set($event)" />
|
|
2808
|
+
*
|
|
2809
|
+
* Teclado (patrón listbox de WAI-ARIA): ArrowDown/ArrowUp sobre el trigger
|
|
2810
|
+
* cerrado lo abren; al abrir, el foco pasa a la opción seleccionada (o al
|
|
2811
|
+
* buscador, si está visible). En el panel, flechas y Home/End mueven el
|
|
2812
|
+
* foco, Enter/Espacio eligen, Escape cierra y devuelve el foco al trigger,
|
|
2813
|
+
* y Tab cierra el panel y continúa la tabulación desde el trigger. Las
|
|
2814
|
+
* opciones nunca son paradas de Tab (`tabindex="-1"`).
|
|
2668
2815
|
*/
|
|
2669
2816
|
class Select {
|
|
2670
2817
|
label = input(/* @ts-ignore */
|
|
@@ -2701,8 +2848,32 @@ class Select {
|
|
|
2701
2848
|
...(ngDevMode ? [{ debugName: "searchable" }] : /* istanbul ignore next */ []));
|
|
2702
2849
|
searchThreshold = input(7, /* @ts-ignore */
|
|
2703
2850
|
...(ngDevMode ? [{ debugName: "searchThreshold" }] : /* istanbul ignore next */ []));
|
|
2851
|
+
/**
|
|
2852
|
+
* Nombre accesible del control cuando no hay `label` visible (ej. "Ordenar
|
|
2853
|
+
* por"). El trigger se anuncia como `"<ariaLabel>: <valor visible>"` (el
|
|
2854
|
+
* mismo texto que muestra el trigger, `shortLabel` si la opción lo trae),
|
|
2855
|
+
* así el nombre contiene el texto visible (WCAG 2.5.3) y además dice qué se
|
|
2856
|
+
* está eligiendo. Ignorado si hay `label`: un `aria-label` pisaría al
|
|
2857
|
+
* `<label for>` visible.
|
|
2858
|
+
*/
|
|
2859
|
+
ariaLabel = input(/* @ts-ignore */
|
|
2860
|
+
...(ngDevMode ? [undefined, { debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
2861
|
+
/** Ícono `sm` a la izquierda del valor en el trigger (ej. `'sort'`). */
|
|
2862
|
+
icon = input(/* @ts-ignore */
|
|
2863
|
+
...(ngDevMode ? [undefined, { debugName: "icon" }] : /* istanbul ignore next */ []));
|
|
2864
|
+
/** Título chico en mayúsculas dentro del panel, sobre las opciones. Decorativo (`aria-hidden`): el nombre del listbox ya lo da `ariaLabel`/`label`. */
|
|
2865
|
+
panelTitle = input(/* @ts-ignore */
|
|
2866
|
+
...(ngDevMode ? [undefined, { debugName: "panelTitle" }] : /* istanbul ignore next */ []));
|
|
2867
|
+
/**
|
|
2868
|
+
* `'trigger'` (default): el panel mide lo mismo que el trigger.
|
|
2869
|
+
* `'content'`: el trigger es el ancho mínimo y el panel crece hasta el
|
|
2870
|
+
* ancho de la opción más larga — para triggers compactos cuyas opciones
|
|
2871
|
+
* son más largas que el valor resumido.
|
|
2872
|
+
*/
|
|
2873
|
+
panelWidth = input('trigger', /* @ts-ignore */
|
|
2874
|
+
...(ngDevMode ? [{ debugName: "panelWidth" }] : /* istanbul ignore next */ []));
|
|
2704
2875
|
valueChange = output();
|
|
2705
|
-
instanceId = `kf-select-${nextId$
|
|
2876
|
+
instanceId = `kf-select-${nextId$3++}`;
|
|
2706
2877
|
triggerId = `${this.instanceId}-trigger`;
|
|
2707
2878
|
listboxId = `${this.instanceId}-listbox`;
|
|
2708
2879
|
errorId = `${this.instanceId}-error`;
|
|
@@ -2712,11 +2883,30 @@ class Select {
|
|
|
2712
2883
|
...(ngDevMode ? [{ debugName: "searchQuery" }] : /* istanbul ignore next */ []));
|
|
2713
2884
|
activeIndex = signal(0, /* @ts-ignore */
|
|
2714
2885
|
...(ngDevMode ? [{ debugName: "activeIndex" }] : /* istanbul ignore next */ []));
|
|
2715
|
-
/** Resolves
|
|
2886
|
+
/** Resolves `string | SelectOption` entries into a single shape for the template. */
|
|
2716
2887
|
normalizedOptions = computed(() => this.options().map((option) => typeof option === 'string' ? { value: option, label: option } : option), /* @ts-ignore */
|
|
2717
2888
|
...(ngDevMode ? [{ debugName: "normalizedOptions" }] : /* istanbul ignore next */ []));
|
|
2718
2889
|
selectedOption = computed(() => this.normalizedOptions().find((option) => option.value === this.value()), /* @ts-ignore */
|
|
2719
2890
|
...(ngDevMode ? [{ debugName: "selectedOption" }] : /* istanbul ignore next */ []));
|
|
2891
|
+
/** Texto que muestra el trigger: `shortLabel` o `label` de la opción elegida, o el placeholder. */
|
|
2892
|
+
triggerText = computed(() => {
|
|
2893
|
+
const option = this.selectedOption();
|
|
2894
|
+
return option?.shortLabel ?? option?.label ?? this.placeholder();
|
|
2895
|
+
}, /* @ts-ignore */
|
|
2896
|
+
...(ngDevMode ? [{ debugName: "triggerText" }] : /* istanbul ignore next */ []));
|
|
2897
|
+
/**
|
|
2898
|
+
* `aria-label` del trigger: solo con `ariaLabel` y SIN `label` visible. Un
|
|
2899
|
+
* `aria-label` tiene prioridad sobre el `<label for>` en el cálculo del
|
|
2900
|
+
* nombre accesible, así que con label visible no se emite.
|
|
2901
|
+
*/
|
|
2902
|
+
triggerAriaLabel = computed(() => {
|
|
2903
|
+
const name = this.ariaLabel();
|
|
2904
|
+
if (!name || this.label()) {
|
|
2905
|
+
return null;
|
|
2906
|
+
}
|
|
2907
|
+
return `${name}: ${this.triggerText()}`;
|
|
2908
|
+
}, /* @ts-ignore */
|
|
2909
|
+
...(ngDevMode ? [{ debugName: "triggerAriaLabel" }] : /* istanbul ignore next */ []));
|
|
2720
2910
|
showSearch = computed(() => this.searchable() ?? this.normalizedOptions().length > this.searchThreshold(), /* @ts-ignore */
|
|
2721
2911
|
...(ngDevMode ? [{ debugName: "showSearch" }] : /* istanbul ignore next */ []));
|
|
2722
2912
|
/** Opciones visibles tras aplicar el filtro de búsqueda (case/acento-insensitive). */
|
|
@@ -2725,7 +2915,7 @@ class Select {
|
|
|
2725
2915
|
if (!query) {
|
|
2726
2916
|
return this.normalizedOptions();
|
|
2727
2917
|
}
|
|
2728
|
-
return this.normalizedOptions().filter((option) =>
|
|
2918
|
+
return this.normalizedOptions().filter((option) => [option.label, option.tag, option.detail].some((text) => !!text && normalizeForSearch(text).includes(query)));
|
|
2729
2919
|
}, /* @ts-ignore */
|
|
2730
2920
|
...(ngDevMode ? [{ debugName: "filteredOptions" }] : /* istanbul ignore next */ []));
|
|
2731
2921
|
triggerRef = viewChild.required('trigger', /* @ts-ignore */
|
|
@@ -2734,11 +2924,14 @@ class Select {
|
|
|
2734
2924
|
...(ngDevMode ? [{ debugName: "panelTemplate" }] : /* istanbul ignore next */ []));
|
|
2735
2925
|
searchInputRef = viewChild('searchInput', /* @ts-ignore */
|
|
2736
2926
|
...(ngDevMode ? [{ debugName: "searchInputRef" }] : /* istanbul ignore next */ []));
|
|
2737
|
-
|
|
2927
|
+
panelRef = viewChild('panel', /* @ts-ignore */
|
|
2928
|
+
...(ngDevMode ? [{ debugName: "panelRef" }] : /* istanbul ignore next */ []));
|
|
2929
|
+
document = inject(DOCUMENT);
|
|
2930
|
+
overlay = new SelectOverlay(inject(Overlay), inject(ViewContainerRef), () => this.triggerRef(), () => (this.variant() === 'compact' ? COMPACT_PANEL_MIN_WIDTH : 0), () => (this.panelWidth() === 'content' ? 'content' : 'match'));
|
|
2738
2931
|
constructor() {
|
|
2739
2932
|
effect(() => {
|
|
2740
2933
|
if (this.isOpen()) {
|
|
2741
|
-
this.overlay.attach(this.panelTemplate(), () => this.
|
|
2934
|
+
this.overlay.attach(this.panelTemplate(), () => this.close());
|
|
2742
2935
|
}
|
|
2743
2936
|
else {
|
|
2744
2937
|
this.overlay.detach();
|
|
@@ -2750,9 +2943,20 @@ class Select {
|
|
|
2750
2943
|
// dentro de un effect() (NG0203: pierde el contexto de inyección) — hay
|
|
2751
2944
|
// que usar afterRenderEffect(), que sí es reactivo a signals y corre
|
|
2752
2945
|
// después de que Angular actualizó el DOM del panel recién adjuntado.
|
|
2946
|
+
//
|
|
2947
|
+
// Sin buscador, el foco pasa a la opción activa (la seleccionada, ver
|
|
2948
|
+
// `open()`): `activeIndex` se lee `untracked` para que mover el índice
|
|
2949
|
+
// con el mouse (`mouseenter`) no robe el foco en cada render.
|
|
2753
2950
|
afterRenderEffect(() => {
|
|
2754
|
-
if (this.isOpen()
|
|
2951
|
+
if (!this.isOpen()) {
|
|
2952
|
+
return;
|
|
2953
|
+
}
|
|
2954
|
+
if (this.showSearch()) {
|
|
2755
2955
|
this.searchInputRef()?.nativeElement.focus();
|
|
2956
|
+
return;
|
|
2957
|
+
}
|
|
2958
|
+
if (this.panelRef()) {
|
|
2959
|
+
untracked(() => this.focusOption(this.activeIndex()));
|
|
2756
2960
|
}
|
|
2757
2961
|
});
|
|
2758
2962
|
inject(DestroyRef).onDestroy(() => this.overlay.dispose());
|
|
@@ -2764,16 +2968,135 @@ class Select {
|
|
|
2764
2968
|
* panel está abierto, `keydownEvents()` nunca dispara.
|
|
2765
2969
|
*/
|
|
2766
2970
|
onDocumentEscape() {
|
|
2767
|
-
this.
|
|
2971
|
+
this.close();
|
|
2768
2972
|
}
|
|
2769
2973
|
toggle() {
|
|
2770
2974
|
if (this.disabled()) {
|
|
2771
2975
|
return;
|
|
2772
2976
|
}
|
|
2773
|
-
this.isOpen
|
|
2977
|
+
if (this.isOpen()) {
|
|
2978
|
+
this.close();
|
|
2979
|
+
}
|
|
2980
|
+
else {
|
|
2981
|
+
this.open();
|
|
2982
|
+
}
|
|
2774
2983
|
}
|
|
2775
|
-
|
|
2984
|
+
/** Abre el panel con la opción seleccionada como activa (o la primera). */
|
|
2985
|
+
open() {
|
|
2986
|
+
if (this.disabled() || this.isOpen()) {
|
|
2987
|
+
return;
|
|
2988
|
+
}
|
|
2989
|
+
const selectedIndex = this.filteredOptions().findIndex((option) => option.value === this.value());
|
|
2990
|
+
this.activeIndex.set(Math.max(selectedIndex, 0));
|
|
2991
|
+
this.isOpen.set(true);
|
|
2992
|
+
}
|
|
2993
|
+
/**
|
|
2994
|
+
* Cierra el panel. `restoreFocus` (default: solo si el foco está dentro
|
|
2995
|
+
* del panel) devuelve el foco al trigger ANTES de desmontar el panel —
|
|
2996
|
+
* si no, el elemento enfocado desaparece y el foco cae al `<body>`. Un
|
|
2997
|
+
* click afuera no lo restaura: el foco ya se fue a donde se hizo click.
|
|
2998
|
+
*/
|
|
2999
|
+
close(restoreFocus = this.isFocusInPanel()) {
|
|
3000
|
+
if (!this.isOpen()) {
|
|
3001
|
+
return;
|
|
3002
|
+
}
|
|
3003
|
+
if (restoreFocus) {
|
|
3004
|
+
this.triggerRef().nativeElement.focus();
|
|
3005
|
+
}
|
|
2776
3006
|
this.isOpen.set(false);
|
|
3007
|
+
}
|
|
3008
|
+
/** ArrowDown/ArrowUp con el panel cerrado lo abren (patrón listbox). */
|
|
3009
|
+
onTriggerKeydown(event) {
|
|
3010
|
+
if (this.isOpen() || (event.key !== 'ArrowDown' && event.key !== 'ArrowUp')) {
|
|
3011
|
+
return;
|
|
3012
|
+
}
|
|
3013
|
+
event.preventDefault();
|
|
3014
|
+
this.open();
|
|
3015
|
+
}
|
|
3016
|
+
/**
|
|
3017
|
+
* Teclado con el foco en una opción del panel. Las flechas parten SIEMPRE
|
|
3018
|
+
* de la opción que recibió el evento (`index`), no de `activeIndex`: el
|
|
3019
|
+
* `mouseenter` mueve `activeIndex` sin mover el foco, y sin esto, con el
|
|
3020
|
+
* foco en la opción 1 y el mouse sobre la 4, ArrowDown saltaría a la 5.
|
|
3021
|
+
*/
|
|
3022
|
+
onOptionKeydown(event, option, index) {
|
|
3023
|
+
this.activeIndex.set(index);
|
|
3024
|
+
switch (event.key) {
|
|
3025
|
+
case 'ArrowDown':
|
|
3026
|
+
event.preventDefault();
|
|
3027
|
+
this.moveActive(1);
|
|
3028
|
+
this.focusOption(this.activeIndex());
|
|
3029
|
+
break;
|
|
3030
|
+
case 'ArrowUp':
|
|
3031
|
+
event.preventDefault();
|
|
3032
|
+
this.moveActive(-1);
|
|
3033
|
+
this.focusOption(this.activeIndex());
|
|
3034
|
+
break;
|
|
3035
|
+
case 'Home':
|
|
3036
|
+
event.preventDefault();
|
|
3037
|
+
this.activeIndex.set(0);
|
|
3038
|
+
this.focusOption(0);
|
|
3039
|
+
this.scrollActiveIntoView();
|
|
3040
|
+
break;
|
|
3041
|
+
case 'End': {
|
|
3042
|
+
event.preventDefault();
|
|
3043
|
+
const last = this.filteredOptions().length - 1;
|
|
3044
|
+
this.activeIndex.set(last);
|
|
3045
|
+
this.focusOption(last);
|
|
3046
|
+
this.scrollActiveIntoView();
|
|
3047
|
+
break;
|
|
3048
|
+
}
|
|
3049
|
+
case 'Enter':
|
|
3050
|
+
case ' ':
|
|
3051
|
+
event.preventDefault();
|
|
3052
|
+
this.select(option);
|
|
3053
|
+
break;
|
|
3054
|
+
default:
|
|
3055
|
+
this.handleExitKeys(event);
|
|
3056
|
+
}
|
|
3057
|
+
}
|
|
3058
|
+
/** Teclas de salida del buscador: Escape y Tab, igual que desde una opción. */
|
|
3059
|
+
onSearchKeydown(event) {
|
|
3060
|
+
this.handleExitKeys(event);
|
|
3061
|
+
}
|
|
3062
|
+
/**
|
|
3063
|
+
* Escape cierra y devuelve el foco al trigger; `preventDefault` le avisa a
|
|
3064
|
+
* un contenedor que también escucha Escape (ej. `kf-drawer`) que la tecla
|
|
3065
|
+
* ya se consumió. Tab cierra y mueve el foco al trigger SIN
|
|
3066
|
+
* `preventDefault`: la tabulación nativa continúa desde el trigger, así
|
|
3067
|
+
* Tab/Shift+Tab salen del panel hacia el control siguiente/anterior.
|
|
3068
|
+
*/
|
|
3069
|
+
handleExitKeys(event) {
|
|
3070
|
+
if (event.key === 'Escape') {
|
|
3071
|
+
event.preventDefault();
|
|
3072
|
+
this.close(true);
|
|
3073
|
+
}
|
|
3074
|
+
else if (event.key === 'Tab') {
|
|
3075
|
+
this.close(true);
|
|
3076
|
+
}
|
|
3077
|
+
}
|
|
3078
|
+
focusOption(index) {
|
|
3079
|
+
const options = this.panelRef()?.nativeElement.querySelectorAll('.kf-select__option');
|
|
3080
|
+
options?.[index]?.focus();
|
|
3081
|
+
}
|
|
3082
|
+
isFocusInPanel() {
|
|
3083
|
+
const active = this.document.activeElement;
|
|
3084
|
+
const panel = this.panelRef()?.nativeElement;
|
|
3085
|
+
return !!active && !!panel && panel.contains(active);
|
|
3086
|
+
}
|
|
3087
|
+
/** Id de cada opción, para `aria-activedescendant` del buscador. */
|
|
3088
|
+
optionId(index) {
|
|
3089
|
+
return `${this.listboxId}-option-${index}`;
|
|
3090
|
+
}
|
|
3091
|
+
/** Nombre accesible explícito solo para opciones con `tag`/`detail`, para separar las partes al leerlas. */
|
|
3092
|
+
optionAriaLabel(option) {
|
|
3093
|
+
if (!option.tag && !option.detail) {
|
|
3094
|
+
return null;
|
|
3095
|
+
}
|
|
3096
|
+
return [option.label, option.tag, option.detail].filter(Boolean).join(', ');
|
|
3097
|
+
}
|
|
3098
|
+
select(option) {
|
|
3099
|
+
this.close(true);
|
|
2777
3100
|
if (option.value !== this.value()) {
|
|
2778
3101
|
this.valueChange.emit(option.value);
|
|
2779
3102
|
}
|
|
@@ -2795,6 +3118,17 @@ class Select {
|
|
|
2795
3118
|
return;
|
|
2796
3119
|
}
|
|
2797
3120
|
this.activeIndex.update((current) => Math.min(Math.max(current + delta, 0), count - 1));
|
|
3121
|
+
this.scrollActiveIntoView();
|
|
3122
|
+
}
|
|
3123
|
+
/**
|
|
3124
|
+
* Con el foco en el buscador, la opción activa se marca con
|
|
3125
|
+
* `aria-activedescendant` pero no recibe foco, así que el navegador no la
|
|
3126
|
+
* desplaza a la vista: se hace a mano. (`scrollIntoView` puede no existir
|
|
3127
|
+
* en entornos sin layout.)
|
|
3128
|
+
*/
|
|
3129
|
+
scrollActiveIntoView() {
|
|
3130
|
+
const option = this.panelRef()?.nativeElement.querySelectorAll('.kf-select__option')[this.activeIndex()];
|
|
3131
|
+
option?.scrollIntoView?.({ block: 'nearest' });
|
|
2798
3132
|
}
|
|
2799
3133
|
selectActive() {
|
|
2800
3134
|
const option = this.filteredOptions()[this.activeIndex()];
|
|
@@ -2803,17 +3137,17 @@ class Select {
|
|
|
2803
3137
|
}
|
|
2804
3138
|
}
|
|
2805
3139
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Select, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2806
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Select, isStandalone: true, selector: "kf-select", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, errorMessage: { classPropertyName: "errorMessage", publicName: "errorMessage", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, searchable: { classPropertyName: "searchable", publicName: "searchable", isSignal: true, isRequired: false, transformFunction: null }, searchThreshold: { classPropertyName: "searchThreshold", publicName: "searchThreshold", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, host: { listeners: { "document:keydown.escape": "onDocumentEscape()" }, properties: { "class.kf-select--compact": "variant() === 'compact'", "class.kf-select--width-sm": "width() === 'sm'", "class.kf-select--width-lg": "width() === 'lg'", "class.kf-select--width-full": "width() === 'full'" } }, viewQueries: [{ propertyName: "triggerRef", first: true, predicate: ["trigger"], descendants: true, isSignal: true }, { propertyName: "panelTemplate", first: true, predicate: ["panelTemplate"], descendants: true, isSignal: true }, { propertyName: "searchInputRef", first: true, predicate: ["searchInput"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (label()) {\n <kf-field-label [for]=\"triggerId\">{{ label() }}</kf-field-label>\n}\n<button\n #trigger\n type=\"button\"\n [id]=\"triggerId\"\n class=\"kf-select__trigger\"\n [class.kf-select__trigger--disabled]=\"disabled()\"\n [class.kf-select__trigger--invalid]=\"invalid()\"\n [disabled]=\"disabled()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-controls]=\"isOpen() ? listboxId : null\"\n [attr.aria-describedby]=\"errorMessage() ? errorId : null\"\n [attr.aria-invalid]=\"invalid() ? true : null\"\n (click)=\"toggle()\"\n>\n <span class=\"kf-select__value\" [class.kf-select__value--placeholder]=\"!selectedOption()\">\n {{ selectedOption()?.label ?? placeholder() }}\n </span>\n <span\n class=\"kf-select__caret kf-caret\"\n [class.kf-select__caret--open]=\"isOpen()\"\n [class.kf-caret--open]=\"isOpen()\"\n aria-hidden=\"true\"\n ></span>\n</button>\n@if (errorMessage(); as message) {\n <span class=\"kf-select__error kf-field-error\" [id]=\"errorId\" role=\"alert\">{{ message }}</span>\n}\n<ng-template #panelTemplate>\n <div class=\"kf-select__panel\" [class.kf-select__panel--compact]=\"variant() === 'compact'\">\n @if (showSearch()) {\n <input\n #searchInput\n type=\"text\"\n class=\"kf-select__search\"\n placeholder=\"Buscar\u2026\"\n [value]=\"searchQuery()\"\n (input)=\"onSearchInput($event)\"\n (keydown.arrowdown)=\"$event.preventDefault(); moveActive(1)\"\n (keydown.arrowup)=\"$event.preventDefault(); moveActive(-1)\"\n (keydown.enter)=\"$event.preventDefault(); selectActive()\"\n />\n }\n <ul class=\"kf-select__list\" [id]=\"listboxId\" role=\"listbox\" [attr.aria-labelledby]=\"triggerId\">\n @for (option of filteredOptions(); track option.value; let i = $index) {\n <li\n class=\"kf-select__option\"\n [class.kf-select__option--selected]=\"option.value === value()\"\n [class.kf-select__option--active]=\"i === activeIndex()\"\n role=\"option\"\n tabindex=\"0\"\n [attr.aria-selected]=\"option.value === value()\"\n (click)=\"select(option)\"\n (mouseenter)=\"activeIndex.set(i)\"\n (keydown.enter)=\"select(option)\"\n (keydown.space)=\"$event.preventDefault(); select(option)\"\n (keydown.arrowdown)=\"$event.preventDefault(); moveActive(1)\"\n (keydown.arrowup)=\"$event.preventDefault(); moveActive(-1)\"\n >\n {{ option.label }}\n </li>\n } @empty {\n <li class=\"kf-select__empty\">\n {{ searchQuery() ? 'Sin resultados' : 'Sin opciones disponibles' }}\n </li>\n }\n </ul>\n </div>\n</ng-template>\n", styles: [".kf-caret{flex-shrink:0;width:8px;height:8px;border-right:2px solid var(--kf-color-text-muted);border-bottom:2px solid var(--kf-color-text-muted);transform:rotate(45deg);transition:transform .14s;margin-top:-4px}.kf-caret--open{transform:rotate(-135deg);margin-top:4px}.kf-field-hint{font-size:.75rem;color:var(--kf-color-text-muted)}.kf-field-error{display:flex;align-items:center;gap:var(--kf-space-xs);font-size:.75rem;font-weight:600;color:var(--kf-color-error-strong)}:host{display:flex;flex-direction:column;gap:var(--kf-space-xs);width:var(--kf-select-width-md);font-family:var(--kf-font-body)}:host(.kf-select--compact){width:max-content;max-width:160px}:host(.kf-select--width-sm){width:var(--kf-select-width-sm)}:host(.kf-select--width-lg){width:var(--kf-select-width-lg)}:host(.kf-select--width-full){width:100%}:host(.kf-select--compact) .kf-select__trigger{padding:6px var(--kf-space-sm);font-size:.85rem;gap:var(--kf-space-xs)}:host(.kf-select--compact) .kf-select__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kf-select__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);width:100%;font:inherit;color:var(--kf-color-text);padding:var(--kf-space-sm) var(--kf-space-md);background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);cursor:pointer;transition:border-color .14s,box-shadow .14s}.kf-select__trigger:hover:not(:disabled){border-color:var(--kf-color-border-strong)}.kf-select__trigger:focus-visible{outline:none;border-color:var(--kf-color-primary);box-shadow:var(--kf-shadow-focus)}.kf-select__trigger--disabled{opacity:.5;cursor:not-allowed}.kf-select__trigger--invalid{border-color:var(--kf-color-error)}.kf-select__trigger--invalid:focus-visible{border-color:var(--kf-color-error);box-shadow:var(--kf-shadow-focus-error)}.kf-select__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kf-select__value--placeholder{color:var(--kf-color-text-muted)}.kf-select__panel{box-sizing:border-box;width:100%;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-md);overflow:hidden}.kf-select__search{box-sizing:border-box;width:100%;font:inherit;font-family:var(--kf-font-body);color:var(--kf-color-text);padding:var(--kf-space-sm) var(--kf-space-md);border:none;border-bottom:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm) var(--kf-radius-sm) 0 0;outline:none}.kf-select__search:focus-visible{box-shadow:inset 0 0 0 2px var(--kf-color-primary)}.kf-select__list{box-sizing:border-box;width:100%;margin:0;padding:var(--kf-space-xs) 0;list-style:none;max-height:240px;overflow-y:auto;scrollbar-width:thin;scrollbar-color:var(--kf-color-gray-400) transparent}.kf-select__list::-webkit-scrollbar{width:6px}.kf-select__list::-webkit-scrollbar-track{background:transparent}.kf-select__list::-webkit-scrollbar-thumb{background-color:var(--kf-color-gray-400);border-radius:var(--kf-radius-pill)}.kf-select__list::-webkit-scrollbar-thumb:hover{background-color:var(--kf-color-gray-500)}.kf-select__option{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-select__panel--compact .kf-select__option{padding:6px var(--kf-space-sm);font-size:.85rem}.kf-select__option:hover{background:var(--kf-color-primary-soft)}.kf-select__option--selected{background:var(--kf-color-primary-soft);color:var(--kf-color-primary);font-weight:600}.kf-select__option--active{background:var(--kf-color-primary-soft)}.kf-select__empty{text-align:center;padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text-muted)}\n"], dependencies: [{ kind: "component", type: FieldLabel, selector: "kf-field-label", inputs: ["for", "required"] }] });
|
|
3140
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Select, isStandalone: true, selector: "kf-select", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, errorMessage: { classPropertyName: "errorMessage", publicName: "errorMessage", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, width: { classPropertyName: "width", publicName: "width", isSignal: true, isRequired: false, transformFunction: null }, searchable: { classPropertyName: "searchable", publicName: "searchable", isSignal: true, isRequired: false, transformFunction: null }, searchThreshold: { classPropertyName: "searchThreshold", publicName: "searchThreshold", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, panelTitle: { classPropertyName: "panelTitle", publicName: "panelTitle", isSignal: true, isRequired: false, transformFunction: null }, panelWidth: { classPropertyName: "panelWidth", publicName: "panelWidth", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, host: { listeners: { "document:keydown.escape": "onDocumentEscape()" }, properties: { "class.kf-select--compact": "variant() === 'compact'", "class.kf-select--width-sm": "width() === 'sm'", "class.kf-select--width-lg": "width() === 'lg'", "class.kf-select--width-full": "width() === 'full'" } }, viewQueries: [{ propertyName: "triggerRef", first: true, predicate: ["trigger"], descendants: true, isSignal: true }, { propertyName: "panelTemplate", first: true, predicate: ["panelTemplate"], descendants: true, isSignal: true }, { propertyName: "searchInputRef", first: true, predicate: ["searchInput"], descendants: true, isSignal: true }, { propertyName: "panelRef", first: true, predicate: ["panel"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (label()) {\n <kf-field-label [for]=\"triggerId\">{{ label() }}</kf-field-label>\n}\n<button\n #trigger\n type=\"button\"\n [id]=\"triggerId\"\n class=\"kf-select__trigger\"\n [class.kf-select__trigger--disabled]=\"disabled()\"\n [class.kf-select__trigger--invalid]=\"invalid()\"\n [class.kf-select__trigger--with-icon]=\"!!icon()\"\n [disabled]=\"disabled()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-controls]=\"isOpen() ? listboxId : null\"\n [attr.aria-label]=\"triggerAriaLabel()\"\n [attr.aria-describedby]=\"errorMessage() ? errorId : null\"\n [attr.aria-invalid]=\"invalid() ? true : null\"\n [class.kf-select__trigger--tagged]=\"!!selectedOption()?.tag\"\n (click)=\"toggle()\"\n (keydown)=\"onTriggerKeydown($event)\"\n>\n @if (icon(); as iconName) {\n <kf-icon class=\"kf-select__icon\" [name]=\"iconName\" size=\"sm\" />\n }\n <span class=\"kf-select__value\" [class.kf-select__value--placeholder]=\"!selectedOption()\">\n {{ triggerText() }}\n </span>\n @if (selectedOption()?.tag; as tag) {\n <kf-badge class=\"kf-select__tag\">{{ tag }}</kf-badge>\n }\n <span\n class=\"kf-select__caret kf-caret\"\n [class.kf-select__caret--open]=\"isOpen()\"\n [class.kf-caret--open]=\"isOpen()\"\n aria-hidden=\"true\"\n ></span>\n</button>\n@if (errorMessage(); as message) {\n <span class=\"kf-select__error kf-field-error\" [id]=\"errorId\" role=\"alert\">{{ message }}</span>\n}\n<ng-template #panelTemplate>\n <div #panel class=\"kf-select__panel\" [class.kf-select__panel--compact]=\"variant() === 'compact'\">\n @if (panelTitle(); as title) {\n <div class=\"kf-select__panel-title kf-overline-text\" aria-hidden=\"true\">{{ title }}</div>\n }\n @if (showSearch()) {\n <input\n #searchInput\n type=\"text\"\n class=\"kf-select__search\"\n placeholder=\"Buscar\u2026\"\n aria-label=\"Buscar opci\u00F3n\"\n role=\"combobox\"\n aria-autocomplete=\"list\"\n aria-expanded=\"true\"\n [attr.aria-controls]=\"listboxId\"\n [attr.aria-activedescendant]=\"filteredOptions().length ? optionId(activeIndex()) : null\"\n [value]=\"searchQuery()\"\n (input)=\"onSearchInput($event)\"\n (keydown.arrowdown)=\"$event.preventDefault(); moveActive(1)\"\n (keydown.arrowup)=\"$event.preventDefault(); moveActive(-1)\"\n (keydown.enter)=\"$event.preventDefault(); selectActive()\"\n (keydown)=\"onSearchKeydown($event)\"\n />\n }\n <ul\n class=\"kf-select__list\"\n [id]=\"listboxId\"\n role=\"listbox\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-labelledby]=\"ariaLabel() ? null : triggerId\"\n >\n @for (option of filteredOptions(); track option.value; let i = $index) {\n <li\n class=\"kf-select__option\"\n [id]=\"optionId(i)\"\n [class.kf-select__option--selected]=\"option.value === value()\"\n [class.kf-select__option--active]=\"i === activeIndex()\"\n role=\"option\"\n tabindex=\"-1\"\n [attr.aria-selected]=\"option.value === value()\"\n [attr.aria-label]=\"optionAriaLabel(option)\"\n [class.kf-select__option--rich]=\"!!(option.tag || option.detail)\"\n (click)=\"select(option)\"\n (mouseenter)=\"activeIndex.set(i)\"\n (keydown)=\"onOptionKeydown($event, option, i)\"\n >\n @if (option.tag || option.detail) {\n <span class=\"kf-select__option-main\">\n <span class=\"kf-select__option-label\">{{ option.label }}</span>\n @if (option.tag) {\n <kf-badge class=\"kf-select__tag\">{{ option.tag }}</kf-badge>\n }\n </span>\n @if (option.detail) {\n <span class=\"kf-select__option-detail\">{{ option.detail }}</span>\n }\n } @else {\n {{ option.label }}\n }\n </li>\n } @empty {\n <li class=\"kf-select__empty\">\n {{ searchQuery() ? 'Sin resultados' : 'Sin opciones disponibles' }}\n </li>\n }\n </ul>\n </div>\n</ng-template>\n", styles: [".kf-caret{flex-shrink:0;width:8px;height:8px;border-right:2px solid var(--kf-color-text-muted);border-bottom:2px solid var(--kf-color-text-muted);transform:rotate(45deg);transition:transform .14s;margin-top:-4px}.kf-caret--open{transform:rotate(-135deg);margin-top:4px}.kf-field-hint{font-size:.75rem;color:var(--kf-color-text-muted)}.kf-field-error{display:flex;align-items:center;gap:var(--kf-space-xs);font-size:.75rem;font-weight:600;color:var(--kf-color-error-strong)}.kf-overline-text{font-family:var(--kf-font-title);font-size:var(--kf-font-size-caption);font-weight:var(--kf-font-weight-medium);letter-spacing:.04em;text-transform:uppercase;color:var(--kf-color-text-muted)}:host{display:flex;flex-direction:column;gap:var(--kf-space-xs);width:var(--kf-select-width-md);font-family:var(--kf-font-body)}:host(.kf-select--compact){width:max-content;max-width:160px}:host(.kf-select--width-sm){width:var(--kf-select-width-sm)}:host(.kf-select--width-lg){width:var(--kf-select-width-lg)}:host(.kf-select--width-full){width:100%}:host(.kf-select--compact) .kf-select__trigger{padding:6px var(--kf-space-sm);font-size:.85rem;gap:var(--kf-space-xs)}:host(.kf-select--compact) .kf-select__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kf-select__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);width:100%;font:inherit;color:var(--kf-color-text);padding:var(--kf-space-sm) var(--kf-space-md);background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);cursor:pointer;transition:border-color .14s,box-shadow .14s}.kf-select__trigger:hover:not(:disabled){border-color:var(--kf-color-border-strong)}.kf-select__trigger:focus-visible{outline:none;border-color:var(--kf-color-primary);box-shadow:var(--kf-shadow-focus)}.kf-select__trigger--disabled{opacity:.5;cursor:not-allowed}.kf-select__trigger--invalid{border-color:var(--kf-color-error)}.kf-select__trigger--invalid:focus-visible{border-color:var(--kf-color-error);box-shadow:var(--kf-shadow-focus-error)}.kf-select__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kf-select__icon{flex:none;color:var(--kf-color-text-muted)}.kf-select__trigger--with-icon .kf-select__value{flex:1 1 auto;min-width:0;text-align:start}:host(.kf-select--compact) .kf-select__trigger--with-icon .kf-select__value{color:var(--kf-color-ink);font-weight:600}.kf-select__trigger--tagged .kf-select__value{flex:1 1 auto;min-width:0;text-align:start}.kf-select__tag{flex:none}.kf-select__trigger .kf-select__tag{margin-block:-2px}.kf-select__value--placeholder{color:var(--kf-color-text-muted)}.kf-select__panel{box-sizing:border-box;font-family:var(--kf-font-body);width:100%;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-md);overflow:hidden}.kf-select__panel-title{padding:var(--kf-space-sm) var(--kf-space-md) var(--kf-space-xs)}.kf-select__panel--compact .kf-select__panel-title{padding:var(--kf-space-xs) var(--kf-space-sm) 2px}.kf-select__search{box-sizing:border-box;width:100%;font:inherit;font-family:var(--kf-font-body);color:var(--kf-color-text);padding:var(--kf-space-sm) var(--kf-space-md);border:none;border-bottom:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm) var(--kf-radius-sm) 0 0;outline:none}.kf-select__search:focus-visible{box-shadow:inset 0 0 0 2px var(--kf-color-primary)}.kf-select__list{box-sizing:border-box;width:100%;margin:0;padding:var(--kf-space-xs) 0;list-style:none;max-height:240px;overflow-y:auto;scrollbar-width:thin;scrollbar-color:var(--kf-color-gray-400) transparent}.kf-select__list::-webkit-scrollbar{width:6px}.kf-select__list::-webkit-scrollbar-track{background:transparent}.kf-select__list::-webkit-scrollbar-thumb{background-color:var(--kf-color-gray-400);border-radius:var(--kf-radius-pill)}.kf-select__list::-webkit-scrollbar-thumb:hover{background-color:var(--kf-color-gray-500)}.kf-select__option{padding:var(--kf-space-sm) var(--kf-space-md);outline:none;color:var(--kf-color-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-select__option--rich{display:flex;flex-direction:column;gap:2px}.kf-select__option-main{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);min-width:0}.kf-select__option-label,.kf-select__option-detail{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.kf-select__option-detail{font-size:var(--kf-font-size-body-sm);font-weight:400;color:var(--kf-color-text-muted)}.kf-select__panel--compact .kf-select__option{padding:6px var(--kf-space-sm);font-size:.85rem}.kf-select__option:hover{background:var(--kf-color-primary-soft)}.kf-select__option--selected{background:var(--kf-color-primary-soft);color:var(--kf-color-primary);font-weight:600}.kf-select__option--active{background:var(--kf-color-primary-soft)}.kf-select__option:focus-visible{background:var(--kf-color-primary-soft);box-shadow:inset 0 0 0 2px var(--kf-color-primary)}.kf-select__empty{text-align:center;padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text-muted)}\n"], dependencies: [{ kind: "component", type: Badge, selector: "kf-badge", inputs: ["tone", "variant", "dot", "truncate"] }, { kind: "component", type: FieldLabel, selector: "kf-field-label", inputs: ["for", "required"] }, { kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size", "rotate", "filled"] }] });
|
|
2807
3141
|
}
|
|
2808
3142
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Select, decorators: [{
|
|
2809
3143
|
type: Component,
|
|
2810
|
-
args: [{ selector: 'kf-select', imports: [FieldLabel], host: {
|
|
3144
|
+
args: [{ selector: 'kf-select', imports: [Badge, FieldLabel, Icon], host: {
|
|
2811
3145
|
'[class.kf-select--compact]': "variant() === 'compact'",
|
|
2812
3146
|
'[class.kf-select--width-sm]': "width() === 'sm'",
|
|
2813
3147
|
'[class.kf-select--width-lg]': "width() === 'lg'",
|
|
2814
3148
|
'[class.kf-select--width-full]': "width() === 'full'",
|
|
2815
|
-
}, template: "@if (label()) {\n <kf-field-label [for]=\"triggerId\">{{ label() }}</kf-field-label>\n}\n<button\n #trigger\n type=\"button\"\n [id]=\"triggerId\"\n class=\"kf-select__trigger\"\n [class.kf-select__trigger--disabled]=\"disabled()\"\n [class.kf-select__trigger--invalid]=\"invalid()\"\n [disabled]=\"disabled()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-controls]=\"isOpen() ? listboxId : null\"\n [attr.aria-describedby]=\"errorMessage() ? errorId : null\"\n [attr.aria-invalid]=\"invalid() ? true : null\"\n (click)=\"toggle()\"\n>\n <span class=\"kf-select__value\" [class.kf-select__value--placeholder]=\"!selectedOption()\">\n {{ selectedOption()?.
|
|
2816
|
-
}], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], errorMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMessage", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], searchable: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchable", required: false }] }], searchThreshold: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchThreshold", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], triggerRef: [{ type: i0.ViewChild, args: ['trigger', { isSignal: true }] }], panelTemplate: [{ type: i0.ViewChild, args: ['panelTemplate', { isSignal: true }] }], searchInputRef: [{ type: i0.ViewChild, args: ['searchInput', { isSignal: true }] }], onDocumentEscape: [{
|
|
3149
|
+
}, template: "@if (label()) {\n <kf-field-label [for]=\"triggerId\">{{ label() }}</kf-field-label>\n}\n<button\n #trigger\n type=\"button\"\n [id]=\"triggerId\"\n class=\"kf-select__trigger\"\n [class.kf-select__trigger--disabled]=\"disabled()\"\n [class.kf-select__trigger--invalid]=\"invalid()\"\n [class.kf-select__trigger--with-icon]=\"!!icon()\"\n [disabled]=\"disabled()\"\n [attr.aria-haspopup]=\"'listbox'\"\n [attr.aria-expanded]=\"isOpen()\"\n [attr.aria-controls]=\"isOpen() ? listboxId : null\"\n [attr.aria-label]=\"triggerAriaLabel()\"\n [attr.aria-describedby]=\"errorMessage() ? errorId : null\"\n [attr.aria-invalid]=\"invalid() ? true : null\"\n [class.kf-select__trigger--tagged]=\"!!selectedOption()?.tag\"\n (click)=\"toggle()\"\n (keydown)=\"onTriggerKeydown($event)\"\n>\n @if (icon(); as iconName) {\n <kf-icon class=\"kf-select__icon\" [name]=\"iconName\" size=\"sm\" />\n }\n <span class=\"kf-select__value\" [class.kf-select__value--placeholder]=\"!selectedOption()\">\n {{ triggerText() }}\n </span>\n @if (selectedOption()?.tag; as tag) {\n <kf-badge class=\"kf-select__tag\">{{ tag }}</kf-badge>\n }\n <span\n class=\"kf-select__caret kf-caret\"\n [class.kf-select__caret--open]=\"isOpen()\"\n [class.kf-caret--open]=\"isOpen()\"\n aria-hidden=\"true\"\n ></span>\n</button>\n@if (errorMessage(); as message) {\n <span class=\"kf-select__error kf-field-error\" [id]=\"errorId\" role=\"alert\">{{ message }}</span>\n}\n<ng-template #panelTemplate>\n <div #panel class=\"kf-select__panel\" [class.kf-select__panel--compact]=\"variant() === 'compact'\">\n @if (panelTitle(); as title) {\n <div class=\"kf-select__panel-title kf-overline-text\" aria-hidden=\"true\">{{ title }}</div>\n }\n @if (showSearch()) {\n <input\n #searchInput\n type=\"text\"\n class=\"kf-select__search\"\n placeholder=\"Buscar\u2026\"\n aria-label=\"Buscar opci\u00F3n\"\n role=\"combobox\"\n aria-autocomplete=\"list\"\n aria-expanded=\"true\"\n [attr.aria-controls]=\"listboxId\"\n [attr.aria-activedescendant]=\"filteredOptions().length ? optionId(activeIndex()) : null\"\n [value]=\"searchQuery()\"\n (input)=\"onSearchInput($event)\"\n (keydown.arrowdown)=\"$event.preventDefault(); moveActive(1)\"\n (keydown.arrowup)=\"$event.preventDefault(); moveActive(-1)\"\n (keydown.enter)=\"$event.preventDefault(); selectActive()\"\n (keydown)=\"onSearchKeydown($event)\"\n />\n }\n <ul\n class=\"kf-select__list\"\n [id]=\"listboxId\"\n role=\"listbox\"\n [attr.aria-label]=\"ariaLabel() || null\"\n [attr.aria-labelledby]=\"ariaLabel() ? null : triggerId\"\n >\n @for (option of filteredOptions(); track option.value; let i = $index) {\n <li\n class=\"kf-select__option\"\n [id]=\"optionId(i)\"\n [class.kf-select__option--selected]=\"option.value === value()\"\n [class.kf-select__option--active]=\"i === activeIndex()\"\n role=\"option\"\n tabindex=\"-1\"\n [attr.aria-selected]=\"option.value === value()\"\n [attr.aria-label]=\"optionAriaLabel(option)\"\n [class.kf-select__option--rich]=\"!!(option.tag || option.detail)\"\n (click)=\"select(option)\"\n (mouseenter)=\"activeIndex.set(i)\"\n (keydown)=\"onOptionKeydown($event, option, i)\"\n >\n @if (option.tag || option.detail) {\n <span class=\"kf-select__option-main\">\n <span class=\"kf-select__option-label\">{{ option.label }}</span>\n @if (option.tag) {\n <kf-badge class=\"kf-select__tag\">{{ option.tag }}</kf-badge>\n }\n </span>\n @if (option.detail) {\n <span class=\"kf-select__option-detail\">{{ option.detail }}</span>\n }\n } @else {\n {{ option.label }}\n }\n </li>\n } @empty {\n <li class=\"kf-select__empty\">\n {{ searchQuery() ? 'Sin resultados' : 'Sin opciones disponibles' }}\n </li>\n }\n </ul>\n </div>\n</ng-template>\n", styles: [".kf-caret{flex-shrink:0;width:8px;height:8px;border-right:2px solid var(--kf-color-text-muted);border-bottom:2px solid var(--kf-color-text-muted);transform:rotate(45deg);transition:transform .14s;margin-top:-4px}.kf-caret--open{transform:rotate(-135deg);margin-top:4px}.kf-field-hint{font-size:.75rem;color:var(--kf-color-text-muted)}.kf-field-error{display:flex;align-items:center;gap:var(--kf-space-xs);font-size:.75rem;font-weight:600;color:var(--kf-color-error-strong)}.kf-overline-text{font-family:var(--kf-font-title);font-size:var(--kf-font-size-caption);font-weight:var(--kf-font-weight-medium);letter-spacing:.04em;text-transform:uppercase;color:var(--kf-color-text-muted)}:host{display:flex;flex-direction:column;gap:var(--kf-space-xs);width:var(--kf-select-width-md);font-family:var(--kf-font-body)}:host(.kf-select--compact){width:max-content;max-width:160px}:host(.kf-select--width-sm){width:var(--kf-select-width-sm)}:host(.kf-select--width-lg){width:var(--kf-select-width-lg)}:host(.kf-select--width-full){width:100%}:host(.kf-select--compact) .kf-select__trigger{padding:6px var(--kf-space-sm);font-size:.85rem;gap:var(--kf-space-xs)}:host(.kf-select--compact) .kf-select__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kf-select__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);width:100%;font:inherit;color:var(--kf-color-text);padding:var(--kf-space-sm) var(--kf-space-md);background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);cursor:pointer;transition:border-color .14s,box-shadow .14s}.kf-select__trigger:hover:not(:disabled){border-color:var(--kf-color-border-strong)}.kf-select__trigger:focus-visible{outline:none;border-color:var(--kf-color-primary);box-shadow:var(--kf-shadow-focus)}.kf-select__trigger--disabled{opacity:.5;cursor:not-allowed}.kf-select__trigger--invalid{border-color:var(--kf-color-error)}.kf-select__trigger--invalid:focus-visible{border-color:var(--kf-color-error);box-shadow:var(--kf-shadow-focus-error)}.kf-select__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kf-select__icon{flex:none;color:var(--kf-color-text-muted)}.kf-select__trigger--with-icon .kf-select__value{flex:1 1 auto;min-width:0;text-align:start}:host(.kf-select--compact) .kf-select__trigger--with-icon .kf-select__value{color:var(--kf-color-ink);font-weight:600}.kf-select__trigger--tagged .kf-select__value{flex:1 1 auto;min-width:0;text-align:start}.kf-select__tag{flex:none}.kf-select__trigger .kf-select__tag{margin-block:-2px}.kf-select__value--placeholder{color:var(--kf-color-text-muted)}.kf-select__panel{box-sizing:border-box;font-family:var(--kf-font-body);width:100%;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-md);overflow:hidden}.kf-select__panel-title{padding:var(--kf-space-sm) var(--kf-space-md) var(--kf-space-xs)}.kf-select__panel--compact .kf-select__panel-title{padding:var(--kf-space-xs) var(--kf-space-sm) 2px}.kf-select__search{box-sizing:border-box;width:100%;font:inherit;font-family:var(--kf-font-body);color:var(--kf-color-text);padding:var(--kf-space-sm) var(--kf-space-md);border:none;border-bottom:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm) var(--kf-radius-sm) 0 0;outline:none}.kf-select__search:focus-visible{box-shadow:inset 0 0 0 2px var(--kf-color-primary)}.kf-select__list{box-sizing:border-box;width:100%;margin:0;padding:var(--kf-space-xs) 0;list-style:none;max-height:240px;overflow-y:auto;scrollbar-width:thin;scrollbar-color:var(--kf-color-gray-400) transparent}.kf-select__list::-webkit-scrollbar{width:6px}.kf-select__list::-webkit-scrollbar-track{background:transparent}.kf-select__list::-webkit-scrollbar-thumb{background-color:var(--kf-color-gray-400);border-radius:var(--kf-radius-pill)}.kf-select__list::-webkit-scrollbar-thumb:hover{background-color:var(--kf-color-gray-500)}.kf-select__option{padding:var(--kf-space-sm) var(--kf-space-md);outline:none;color:var(--kf-color-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-select__option--rich{display:flex;flex-direction:column;gap:2px}.kf-select__option-main{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);min-width:0}.kf-select__option-label,.kf-select__option-detail{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;min-width:0}.kf-select__option-detail{font-size:var(--kf-font-size-body-sm);font-weight:400;color:var(--kf-color-text-muted)}.kf-select__panel--compact .kf-select__option{padding:6px var(--kf-space-sm);font-size:.85rem}.kf-select__option:hover{background:var(--kf-color-primary-soft)}.kf-select__option--selected{background:var(--kf-color-primary-soft);color:var(--kf-color-primary);font-weight:600}.kf-select__option--active{background:var(--kf-color-primary-soft)}.kf-select__option:focus-visible{background:var(--kf-color-primary-soft);box-shadow:inset 0 0 0 2px var(--kf-color-primary)}.kf-select__empty{text-align:center;padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text-muted)}\n"] }]
|
|
3150
|
+
}], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], errorMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMessage", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], width: [{ type: i0.Input, args: [{ isSignal: true, alias: "width", required: false }] }], searchable: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchable", required: false }] }], searchThreshold: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchThreshold", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], panelTitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "panelTitle", required: false }] }], panelWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "panelWidth", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }], triggerRef: [{ type: i0.ViewChild, args: ['trigger', { isSignal: true }] }], panelTemplate: [{ type: i0.ViewChild, args: ['panelTemplate', { isSignal: true }] }], searchInputRef: [{ type: i0.ViewChild, args: ['searchInput', { isSignal: true }] }], panelRef: [{ type: i0.ViewChild, args: ['panel', { isSignal: true }] }], onDocumentEscape: [{
|
|
2817
3151
|
type: HostListener,
|
|
2818
3152
|
args: ['document:keydown.escape']
|
|
2819
3153
|
}] } });
|
|
@@ -2890,11 +3224,11 @@ class Pagination {
|
|
|
2890
3224
|
this.pageChange.emit(clamped);
|
|
2891
3225
|
}
|
|
2892
3226
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Pagination, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2893
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Pagination, isStandalone: true, selector: "kf-pagination", inputs: { page: { classPropertyName: "page", publicName: "page", isSignal: true, isRequired: false, transformFunction: null }, pages: { classPropertyName: "pages", publicName: "pages", isSignal: true, isRequired: false, transformFunction: null }, pageSizeOptions: { classPropertyName: "pageSizeOptions", publicName: "pageSizeOptions", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pageChange: "pageChange", pageSize: "pageSizeChange" }, ngImport: i0, template: "<div class=\"kf-pagination\">\n <div class=\"kf-pagination__info kf-caption-text\">\n <ng-content />\n </div>\n\n <div class=\"kf-pagination__controls\">\n @if (pageSizeOptions().length > 0) {\n <div class=\"kf-pagination__size\">\n <span class=\"kf-pagination__size-label kf-caption-text\">Filas</span>\n <kf-select\n variant=\"compact\"\n [options]=\"pageSizeSelectOptions()\"\n [value]=\"pageSizeValue()\"\n (valueChange)=\"setPageSize($event)\"\n />\n </div>\n @if (hasNav()) {\n <div class=\"kf-pagination__divider\" aria-hidden=\"true\"></div>\n }\n }\n\n @if (hasNav()) {\n <nav class=\"kf-pagination__nav\" aria-label=\"Paginaci\u00F3n\">\n <button\n type=\"button\"\n class=\"kf-pagination__control\"\n [disabled]=\"page() <= 1\"\n aria-label=\"P\u00E1gina anterior\"\n (click)=\"goTo(page() - 1)\"\n >\n \u2039\n </button>\n\n @for (item of items(); track $index) {\n @if (item.type === 'page') {\n <button\n type=\"button\"\n class=\"kf-pagination__page\"\n [class.kf-pagination__page--active]=\"item.page === page()\"\n [attr.aria-current]=\"item.page === page() ? 'page' : null\"\n [attr.aria-label]=\"'Ir a la p\u00E1gina ' + item.page\"\n (click)=\"goTo(item.page)\"\n >\n {{ item.page }}\n </button>\n } @else {\n <span class=\"kf-pagination__ellipsis\" aria-hidden=\"true\">\u2026</span>\n }\n }\n\n <button\n type=\"button\"\n class=\"kf-pagination__control\"\n [disabled]=\"page() >= pages()\"\n aria-label=\"P\u00E1gina siguiente\"\n (click)=\"goTo(page() + 1)\"\n >\n \u203A\n </button>\n </nav>\n }\n </div>\n</div>\n", styles: [".kf-caption-text{font-size:var(--kf-font-size-caption);font-weight:var(--kf-font-weight-body);color:var(--kf-color-text-muted)}:host{display:block;font-family:var(--kf-font-body)}.kf-pagination{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-md);flex-wrap:wrap}.kf-pagination__info:empty{display:none}.kf-pagination__controls{display:flex;align-items:center;gap:var(--kf-space-md)}.kf-pagination__size{display:flex;align-items:center;gap:var(--kf-space-sm)}.kf-pagination__size-label{white-space:nowrap}.kf-pagination__divider{width:1px;height:1.25rem;background:var(--kf-color-border)}.kf-pagination__nav{display:flex;align-items:center;gap:var(--kf-space-xs)}.kf-pagination__control,.kf-pagination__page{display:inline-flex;align-items:center;justify-content:center;min-width:2rem;height:2rem;padding:0 var(--kf-space-xs);font:inherit;font-size:.85rem;color:var(--kf-color-text);background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);cursor:pointer;transition:background .14s,border-color .14s,color .14s}.kf-pagination__control:hover:not(:disabled),.kf-pagination__page:hover:not(.kf-pagination__page--active){background:var(--kf-color-surface);border-color:var(--kf-color-border-strong)}.kf-pagination__control:focus-visible,.kf-pagination__page:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-pagination__control:disabled{opacity:.5;cursor:not-allowed}.kf-pagination__page--active{color:var(--kf-color-white);background:var(--kf-color-primary);border-color:var(--kf-color-primary);cursor:default}.kf-pagination__ellipsis{display:inline-flex;align-items:center;justify-content:center;min-width:2rem;height:2rem;color:var(--kf-color-text-muted)}\n"], dependencies: [{ kind: "component", type: Select, selector: "kf-select", inputs: ["label", "options", "value", "placeholder", "disabled", "invalid", "errorMessage", "variant", "width", "searchable", "searchThreshold"], outputs: ["valueChange"] }] });
|
|
3227
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Pagination, isStandalone: true, selector: "kf-pagination", inputs: { page: { classPropertyName: "page", publicName: "page", isSignal: true, isRequired: false, transformFunction: null }, pages: { classPropertyName: "pages", publicName: "pages", isSignal: true, isRequired: false, transformFunction: null }, pageSizeOptions: { classPropertyName: "pageSizeOptions", publicName: "pageSizeOptions", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { pageChange: "pageChange", pageSize: "pageSizeChange" }, ngImport: i0, template: "<div class=\"kf-pagination\">\n <div class=\"kf-pagination__info kf-caption-text\" aria-live=\"polite\">\n <ng-content />\n </div>\n\n <div class=\"kf-pagination__controls\">\n @if (pageSizeOptions().length > 0) {\n <div class=\"kf-pagination__size\">\n <span class=\"kf-pagination__size-label kf-caption-text\">Filas</span>\n <kf-select\n variant=\"compact\"\n ariaLabel=\"Filas por p\u00E1gina\"\n [options]=\"pageSizeSelectOptions()\"\n [value]=\"pageSizeValue()\"\n (valueChange)=\"setPageSize($event)\"\n />\n </div>\n @if (hasNav()) {\n <div class=\"kf-pagination__divider\" aria-hidden=\"true\"></div>\n }\n }\n\n @if (hasNav()) {\n <nav class=\"kf-pagination__nav\" aria-label=\"Paginaci\u00F3n\">\n <button\n type=\"button\"\n class=\"kf-pagination__control\"\n [disabled]=\"page() <= 1\"\n aria-label=\"P\u00E1gina anterior\"\n (click)=\"goTo(page() - 1)\"\n >\n \u2039\n </button>\n\n @for (item of items(); track $index) {\n @if (item.type === 'page') {\n <button\n type=\"button\"\n class=\"kf-pagination__page\"\n [class.kf-pagination__page--active]=\"item.page === page()\"\n [attr.aria-current]=\"item.page === page() ? 'page' : null\"\n [attr.aria-label]=\"'Ir a la p\u00E1gina ' + item.page\"\n (click)=\"goTo(item.page)\"\n >\n {{ item.page }}\n </button>\n } @else {\n <span class=\"kf-pagination__ellipsis\" aria-hidden=\"true\">\u2026</span>\n }\n }\n\n <button\n type=\"button\"\n class=\"kf-pagination__control\"\n [disabled]=\"page() >= pages()\"\n aria-label=\"P\u00E1gina siguiente\"\n (click)=\"goTo(page() + 1)\"\n >\n \u203A\n </button>\n </nav>\n }\n </div>\n</div>\n", styles: [".kf-caption-text{font-size:var(--kf-font-size-caption);font-weight:var(--kf-font-weight-body);color:var(--kf-color-text-muted)}:host{display:block;font-family:var(--kf-font-body)}.kf-pagination{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-md);flex-wrap:wrap}.kf-pagination__info:empty{display:none}.kf-pagination__controls{display:flex;flex-wrap:wrap;align-items:center;gap:var(--kf-space-sm) var(--kf-space-md)}.kf-pagination__size{display:flex;align-items:center;gap:var(--kf-space-sm)}.kf-pagination__size-label{white-space:nowrap}.kf-pagination__divider{width:1px;height:1.25rem;background:var(--kf-color-border)}.kf-pagination__nav{display:flex;align-items:center;gap:var(--kf-space-xs)}.kf-pagination__control,.kf-pagination__page{display:inline-flex;align-items:center;justify-content:center;min-width:2rem;height:2rem;padding:0 var(--kf-space-xs);font:inherit;font-size:.85rem;color:var(--kf-color-text);background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);cursor:pointer;transition:background .14s,border-color .14s,color .14s}.kf-pagination__control:hover:not(:disabled),.kf-pagination__page:hover:not(.kf-pagination__page--active){background:var(--kf-color-surface);border-color:var(--kf-color-border-strong)}.kf-pagination__control:focus-visible,.kf-pagination__page:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-pagination__control:disabled{opacity:.5;cursor:not-allowed}.kf-pagination__page--active{color:var(--kf-color-white);background:var(--kf-color-primary);border-color:var(--kf-color-primary);cursor:default}.kf-pagination__ellipsis{display:inline-flex;align-items:center;justify-content:center;min-width:2rem;height:2rem;color:var(--kf-color-text-muted)}@media(max-width:480px){.kf-pagination__controls{width:100%}.kf-pagination__divider{display:none}}\n"], dependencies: [{ kind: "component", type: Select, selector: "kf-select", inputs: ["label", "options", "value", "placeholder", "disabled", "invalid", "errorMessage", "variant", "width", "searchable", "searchThreshold", "ariaLabel", "icon", "panelTitle", "panelWidth"], outputs: ["valueChange"] }] });
|
|
2894
3228
|
}
|
|
2895
3229
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Pagination, decorators: [{
|
|
2896
3230
|
type: Component,
|
|
2897
|
-
args: [{ selector: 'kf-pagination', imports: [Select], template: "<div class=\"kf-pagination\">\n <div class=\"kf-pagination__info kf-caption-text\">\n <ng-content />\n </div>\n\n <div class=\"kf-pagination__controls\">\n @if (pageSizeOptions().length > 0) {\n <div class=\"kf-pagination__size\">\n <span class=\"kf-pagination__size-label kf-caption-text\">Filas</span>\n <kf-select\n variant=\"compact\"\n [options]=\"pageSizeSelectOptions()\"\n [value]=\"pageSizeValue()\"\n (valueChange)=\"setPageSize($event)\"\n />\n </div>\n @if (hasNav()) {\n <div class=\"kf-pagination__divider\" aria-hidden=\"true\"></div>\n }\n }\n\n @if (hasNav()) {\n <nav class=\"kf-pagination__nav\" aria-label=\"Paginaci\u00F3n\">\n <button\n type=\"button\"\n class=\"kf-pagination__control\"\n [disabled]=\"page() <= 1\"\n aria-label=\"P\u00E1gina anterior\"\n (click)=\"goTo(page() - 1)\"\n >\n \u2039\n </button>\n\n @for (item of items(); track $index) {\n @if (item.type === 'page') {\n <button\n type=\"button\"\n class=\"kf-pagination__page\"\n [class.kf-pagination__page--active]=\"item.page === page()\"\n [attr.aria-current]=\"item.page === page() ? 'page' : null\"\n [attr.aria-label]=\"'Ir a la p\u00E1gina ' + item.page\"\n (click)=\"goTo(item.page)\"\n >\n {{ item.page }}\n </button>\n } @else {\n <span class=\"kf-pagination__ellipsis\" aria-hidden=\"true\">\u2026</span>\n }\n }\n\n <button\n type=\"button\"\n class=\"kf-pagination__control\"\n [disabled]=\"page() >= pages()\"\n aria-label=\"P\u00E1gina siguiente\"\n (click)=\"goTo(page() + 1)\"\n >\n \u203A\n </button>\n </nav>\n }\n </div>\n</div>\n", styles: [".kf-caption-text{font-size:var(--kf-font-size-caption);font-weight:var(--kf-font-weight-body);color:var(--kf-color-text-muted)}:host{display:block;font-family:var(--kf-font-body)}.kf-pagination{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-md);flex-wrap:wrap}.kf-pagination__info:empty{display:none}.kf-pagination__controls{display:flex;align-items:center;gap:var(--kf-space-md)}.kf-pagination__size{display:flex;align-items:center;gap:var(--kf-space-sm)}.kf-pagination__size-label{white-space:nowrap}.kf-pagination__divider{width:1px;height:1.25rem;background:var(--kf-color-border)}.kf-pagination__nav{display:flex;align-items:center;gap:var(--kf-space-xs)}.kf-pagination__control,.kf-pagination__page{display:inline-flex;align-items:center;justify-content:center;min-width:2rem;height:2rem;padding:0 var(--kf-space-xs);font:inherit;font-size:.85rem;color:var(--kf-color-text);background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);cursor:pointer;transition:background .14s,border-color .14s,color .14s}.kf-pagination__control:hover:not(:disabled),.kf-pagination__page:hover:not(.kf-pagination__page--active){background:var(--kf-color-surface);border-color:var(--kf-color-border-strong)}.kf-pagination__control:focus-visible,.kf-pagination__page:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-pagination__control:disabled{opacity:.5;cursor:not-allowed}.kf-pagination__page--active{color:var(--kf-color-white);background:var(--kf-color-primary);border-color:var(--kf-color-primary);cursor:default}.kf-pagination__ellipsis{display:inline-flex;align-items:center;justify-content:center;min-width:2rem;height:2rem;color:var(--kf-color-text-muted)}\n"] }]
|
|
3231
|
+
args: [{ selector: 'kf-pagination', imports: [Select], template: "<div class=\"kf-pagination\">\n <div class=\"kf-pagination__info kf-caption-text\" aria-live=\"polite\">\n <ng-content />\n </div>\n\n <div class=\"kf-pagination__controls\">\n @if (pageSizeOptions().length > 0) {\n <div class=\"kf-pagination__size\">\n <span class=\"kf-pagination__size-label kf-caption-text\">Filas</span>\n <kf-select\n variant=\"compact\"\n ariaLabel=\"Filas por p\u00E1gina\"\n [options]=\"pageSizeSelectOptions()\"\n [value]=\"pageSizeValue()\"\n (valueChange)=\"setPageSize($event)\"\n />\n </div>\n @if (hasNav()) {\n <div class=\"kf-pagination__divider\" aria-hidden=\"true\"></div>\n }\n }\n\n @if (hasNav()) {\n <nav class=\"kf-pagination__nav\" aria-label=\"Paginaci\u00F3n\">\n <button\n type=\"button\"\n class=\"kf-pagination__control\"\n [disabled]=\"page() <= 1\"\n aria-label=\"P\u00E1gina anterior\"\n (click)=\"goTo(page() - 1)\"\n >\n \u2039\n </button>\n\n @for (item of items(); track $index) {\n @if (item.type === 'page') {\n <button\n type=\"button\"\n class=\"kf-pagination__page\"\n [class.kf-pagination__page--active]=\"item.page === page()\"\n [attr.aria-current]=\"item.page === page() ? 'page' : null\"\n [attr.aria-label]=\"'Ir a la p\u00E1gina ' + item.page\"\n (click)=\"goTo(item.page)\"\n >\n {{ item.page }}\n </button>\n } @else {\n <span class=\"kf-pagination__ellipsis\" aria-hidden=\"true\">\u2026</span>\n }\n }\n\n <button\n type=\"button\"\n class=\"kf-pagination__control\"\n [disabled]=\"page() >= pages()\"\n aria-label=\"P\u00E1gina siguiente\"\n (click)=\"goTo(page() + 1)\"\n >\n \u203A\n </button>\n </nav>\n }\n </div>\n</div>\n", styles: [".kf-caption-text{font-size:var(--kf-font-size-caption);font-weight:var(--kf-font-weight-body);color:var(--kf-color-text-muted)}:host{display:block;font-family:var(--kf-font-body)}.kf-pagination{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-md);flex-wrap:wrap}.kf-pagination__info:empty{display:none}.kf-pagination__controls{display:flex;flex-wrap:wrap;align-items:center;gap:var(--kf-space-sm) var(--kf-space-md)}.kf-pagination__size{display:flex;align-items:center;gap:var(--kf-space-sm)}.kf-pagination__size-label{white-space:nowrap}.kf-pagination__divider{width:1px;height:1.25rem;background:var(--kf-color-border)}.kf-pagination__nav{display:flex;align-items:center;gap:var(--kf-space-xs)}.kf-pagination__control,.kf-pagination__page{display:inline-flex;align-items:center;justify-content:center;min-width:2rem;height:2rem;padding:0 var(--kf-space-xs);font:inherit;font-size:.85rem;color:var(--kf-color-text);background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);cursor:pointer;transition:background .14s,border-color .14s,color .14s}.kf-pagination__control:hover:not(:disabled),.kf-pagination__page:hover:not(.kf-pagination__page--active){background:var(--kf-color-surface);border-color:var(--kf-color-border-strong)}.kf-pagination__control:focus-visible,.kf-pagination__page:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-pagination__control:disabled{opacity:.5;cursor:not-allowed}.kf-pagination__page--active{color:var(--kf-color-white);background:var(--kf-color-primary);border-color:var(--kf-color-primary);cursor:default}.kf-pagination__ellipsis{display:inline-flex;align-items:center;justify-content:center;min-width:2rem;height:2rem;color:var(--kf-color-text-muted)}@media(max-width:480px){.kf-pagination__controls{width:100%}.kf-pagination__divider{display:none}}\n"] }]
|
|
2898
3232
|
}], propDecorators: { page: [{ type: i0.Input, args: [{ isSignal: true, alias: "page", required: false }] }], pages: [{ type: i0.Input, args: [{ isSignal: true, alias: "pages", required: false }] }], pageChange: [{ type: i0.Output, args: ["pageChange"] }], pageSizeOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSizeOptions", required: false }] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }, { type: i0.Output, args: ["pageSizeChange"] }] } });
|
|
2899
3233
|
/**
|
|
2900
3234
|
* Construye la lista de items visibles con el algoritmo de elipsis estándar:
|
|
@@ -2939,11 +3273,28 @@ function buildPaginationItems(current, total) {
|
|
|
2939
3273
|
* navegación con flechas izquierda/derecha, igual que un <input type="radio">
|
|
2940
3274
|
* nativo agrupado.
|
|
2941
3275
|
*
|
|
3276
|
+
* Con `iconOnly` cada opción muestra solo su ícono (`list` / `grid`); el
|
|
3277
|
+
* nombre accesible y el tooltip salen de `tableLabel` / `cardsLabel`. El
|
|
3278
|
+
* objetivo táctil mide 32px en escritorio y 44px en pantallas táctiles o de
|
|
3279
|
+
* hasta 640px.
|
|
3280
|
+
*
|
|
2942
3281
|
* <kf-view-toggle [view]="view" (viewChange)="view = $event" />
|
|
3282
|
+
* <kf-view-toggle iconOnly [view]="view" (viewChange)="view = $event" />
|
|
2943
3283
|
*/
|
|
2944
3284
|
class ViewToggle {
|
|
2945
3285
|
view = input.required(/* @ts-ignore */
|
|
2946
3286
|
...(ngDevMode ? [{ debugName: "view" }] : /* istanbul ignore next */ []));
|
|
3287
|
+
/** Solo íconos (el rótulo queda como nombre accesible y `title`). */
|
|
3288
|
+
iconOnly = input(false, { ...(ngDevMode ? { debugName: "iconOnly" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
3289
|
+
/** Rótulo de la opción de tabla. */
|
|
3290
|
+
tableLabel = input('Tabla', /* @ts-ignore */
|
|
3291
|
+
...(ngDevMode ? [{ debugName: "tableLabel" }] : /* istanbul ignore next */ []));
|
|
3292
|
+
/** Rótulo de la opción de tarjetas. */
|
|
3293
|
+
cardsLabel = input('Tarjetas', /* @ts-ignore */
|
|
3294
|
+
...(ngDevMode ? [{ debugName: "cardsLabel" }] : /* istanbul ignore next */ []));
|
|
3295
|
+
/** Nombre accesible del grupo. */
|
|
3296
|
+
ariaLabel = input('Alternar tipo de vista', /* @ts-ignore */
|
|
3297
|
+
...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
2947
3298
|
viewChange = output();
|
|
2948
3299
|
select(next) {
|
|
2949
3300
|
if (next === this.view()) {
|
|
@@ -2965,13 +3316,17 @@ class ViewToggle {
|
|
|
2965
3316
|
nextButton?.focus();
|
|
2966
3317
|
}
|
|
2967
3318
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: ViewToggle, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2968
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.
|
|
3319
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: ViewToggle, isStandalone: true, selector: "kf-view-toggle", inputs: { view: { classPropertyName: "view", publicName: "view", isSignal: true, isRequired: true, transformFunction: null }, iconOnly: { classPropertyName: "iconOnly", publicName: "iconOnly", isSignal: true, isRequired: false, transformFunction: null }, tableLabel: { classPropertyName: "tableLabel", publicName: "tableLabel", isSignal: true, isRequired: false, transformFunction: null }, cardsLabel: { classPropertyName: "cardsLabel", publicName: "cardsLabel", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { viewChange: "viewChange" }, ngImport: i0, template: "<div class=\"kf-view-toggle\" role=\"radiogroup\" [attr.aria-label]=\"ariaLabel()\">\n <button\n type=\"button\"\n role=\"radio\"\n class=\"kf-view-toggle__option\"\n [class.kf-view-toggle__option--icon]=\"iconOnly()\"\n [class.kf-view-toggle__option--active]=\"view() === 'table'\"\n [attr.aria-checked]=\"view() === 'table'\"\n [attr.aria-label]=\"iconOnly() ? tableLabel() : null\"\n [attr.title]=\"iconOnly() ? tableLabel() : null\"\n [attr.tabindex]=\"view() === 'table' ? 0 : -1\"\n (click)=\"select('table')\"\n (keydown)=\"onKeydown($event)\"\n >\n @if (iconOnly()) {\n <kf-icon name=\"list\" size=\"md\" />\n } @else {\n {{ tableLabel() }}\n }\n </button>\n <button\n type=\"button\"\n role=\"radio\"\n class=\"kf-view-toggle__option\"\n [class.kf-view-toggle__option--icon]=\"iconOnly()\"\n [class.kf-view-toggle__option--active]=\"view() === 'cards'\"\n [attr.aria-checked]=\"view() === 'cards'\"\n [attr.aria-label]=\"iconOnly() ? cardsLabel() : null\"\n [attr.title]=\"iconOnly() ? cardsLabel() : null\"\n [attr.tabindex]=\"view() === 'cards' ? 0 : -1\"\n (click)=\"select('cards')\"\n (keydown)=\"onKeydown($event)\"\n >\n @if (iconOnly()) {\n <kf-icon name=\"layout-grid\" size=\"md\" />\n } @else {\n {{ cardsLabel() }}\n }\n </button>\n</div>\n", styles: [":host{display:inline-block;font-family:var(--kf-font-body)}.kf-view-toggle{display:inline-flex;align-items:stretch;gap:2px;padding:2px;background:var(--kf-color-surface);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm)}.kf-view-toggle__option{font:inherit;font-size:.85rem;line-height:1;color:var(--kf-color-text-muted);background:transparent;border:none;border-radius:var(--kf-radius-xs);padding:var(--kf-space-xs) var(--kf-space-sm);cursor:pointer;transition:background .14s,color .14s,box-shadow .14s}.kf-view-toggle__option:hover:not(.kf-view-toggle__option--active){color:var(--kf-color-text)}.kf-view-toggle__option:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-view-toggle__option--active{color:var(--kf-color-ink);background:var(--kf-color-white);box-shadow:var(--kf-shadow-xs)}.kf-view-toggle__option--icon{display:inline-flex;align-items:center;justify-content:center;min-width:32px;min-height:32px;padding:var(--kf-space-xs)}@media(max-width:640px),(pointer:coarse){.kf-view-toggle__option--icon{min-width:44px;min-height:44px}}@media(prefers-reduced-motion:reduce){.kf-view-toggle__option{transition:none}}\n"], dependencies: [{ kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size", "rotate", "filled"] }] });
|
|
2969
3320
|
}
|
|
2970
3321
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: ViewToggle, decorators: [{
|
|
2971
3322
|
type: Component,
|
|
2972
|
-
args: [{ selector: 'kf-view-toggle', template: "<div class=\"kf-view-toggle\" role=\"radiogroup\" aria-label=\"
|
|
2973
|
-
}], propDecorators: { view: [{ type: i0.Input, args: [{ isSignal: true, alias: "view", required: true }] }], viewChange: [{ type: i0.Output, args: ["viewChange"] }] } });
|
|
3323
|
+
args: [{ selector: 'kf-view-toggle', imports: [Icon], template: "<div class=\"kf-view-toggle\" role=\"radiogroup\" [attr.aria-label]=\"ariaLabel()\">\n <button\n type=\"button\"\n role=\"radio\"\n class=\"kf-view-toggle__option\"\n [class.kf-view-toggle__option--icon]=\"iconOnly()\"\n [class.kf-view-toggle__option--active]=\"view() === 'table'\"\n [attr.aria-checked]=\"view() === 'table'\"\n [attr.aria-label]=\"iconOnly() ? tableLabel() : null\"\n [attr.title]=\"iconOnly() ? tableLabel() : null\"\n [attr.tabindex]=\"view() === 'table' ? 0 : -1\"\n (click)=\"select('table')\"\n (keydown)=\"onKeydown($event)\"\n >\n @if (iconOnly()) {\n <kf-icon name=\"list\" size=\"md\" />\n } @else {\n {{ tableLabel() }}\n }\n </button>\n <button\n type=\"button\"\n role=\"radio\"\n class=\"kf-view-toggle__option\"\n [class.kf-view-toggle__option--icon]=\"iconOnly()\"\n [class.kf-view-toggle__option--active]=\"view() === 'cards'\"\n [attr.aria-checked]=\"view() === 'cards'\"\n [attr.aria-label]=\"iconOnly() ? cardsLabel() : null\"\n [attr.title]=\"iconOnly() ? cardsLabel() : null\"\n [attr.tabindex]=\"view() === 'cards' ? 0 : -1\"\n (click)=\"select('cards')\"\n (keydown)=\"onKeydown($event)\"\n >\n @if (iconOnly()) {\n <kf-icon name=\"layout-grid\" size=\"md\" />\n } @else {\n {{ cardsLabel() }}\n }\n </button>\n</div>\n", styles: [":host{display:inline-block;font-family:var(--kf-font-body)}.kf-view-toggle{display:inline-flex;align-items:stretch;gap:2px;padding:2px;background:var(--kf-color-surface);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm)}.kf-view-toggle__option{font:inherit;font-size:.85rem;line-height:1;color:var(--kf-color-text-muted);background:transparent;border:none;border-radius:var(--kf-radius-xs);padding:var(--kf-space-xs) var(--kf-space-sm);cursor:pointer;transition:background .14s,color .14s,box-shadow .14s}.kf-view-toggle__option:hover:not(.kf-view-toggle__option--active){color:var(--kf-color-text)}.kf-view-toggle__option:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-view-toggle__option--active{color:var(--kf-color-ink);background:var(--kf-color-white);box-shadow:var(--kf-shadow-xs)}.kf-view-toggle__option--icon{display:inline-flex;align-items:center;justify-content:center;min-width:32px;min-height:32px;padding:var(--kf-space-xs)}@media(max-width:640px),(pointer:coarse){.kf-view-toggle__option--icon{min-width:44px;min-height:44px}}@media(prefers-reduced-motion:reduce){.kf-view-toggle__option{transition:none}}\n"] }]
|
|
3324
|
+
}], propDecorators: { view: [{ type: i0.Input, args: [{ isSignal: true, alias: "view", required: true }] }], iconOnly: [{ type: i0.Input, args: [{ isSignal: true, alias: "iconOnly", required: false }] }], tableLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "tableLabel", required: false }] }], cardsLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "cardsLabel", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], viewChange: [{ type: i0.Output, args: ["viewChange"] }] } });
|
|
2974
3325
|
|
|
3326
|
+
/** Controles cuyo clic/Enter NO debe activar además la fila o tarjeta que los contiene. */
|
|
3327
|
+
const INTERACTIVE_CONTROL_SELECTOR = 'a[href], button, input, select, textarea, summary, label, [role="button"], [role="link"], [role="menuitem"], [role="checkbox"], [role="switch"], [role="tab"], [contenteditable="true"]';
|
|
3328
|
+
/** Piso de ancho de una columna `grow` que no declara `minWidth` ni `width`. */
|
|
3329
|
+
const GROW_MIN_WIDTH = '8rem';
|
|
2975
3330
|
/** Opciones de "filas por página" estándar del portal — 10 como default
|
|
2976
3331
|
* cubre bien tanto un dataset chico (ej. Beneficios) como uno con volumen
|
|
2977
3332
|
* real (ej. 30+ cuotas en Billing), sin sentirse vacío ni insuficiente; 25
|
|
@@ -3040,6 +3395,12 @@ const DEFAULT_PAGE_SIZE_OPTIONS = [10, 25, 50];
|
|
|
3040
3395
|
* <kf-data-table [columns]="columns" [rows]="rows" [pageSize]="25" itemLabel="beneficios" />
|
|
3041
3396
|
* <kf-data-table [columns]="columns" [rows]="rows" [pageSize]="25" [pageSizeOptions]="[]" />
|
|
3042
3397
|
*
|
|
3398
|
+
* La página es `[(page)]` (el consumidor puede llevarla a la URL) y se
|
|
3399
|
+
* corrige sola si queda fuera de rango. Por defecto un cambio de `rows` la
|
|
3400
|
+
* devuelve a 1; con `keepPageOnRowsChange` la conserva (el consumidor decide
|
|
3401
|
+
* cuándo volver a 1). Un `pageSize` nuevo del consumidor pisa el elegido en
|
|
3402
|
+
* el selector, y `pageSizeChange` avisa cuando el usuario lo cambia.
|
|
3403
|
+
*
|
|
3043
3404
|
* Ordenamiento por columna: cada `DataTableColumn` con `sortable: true`
|
|
3044
3405
|
* vuelve su header clickeable, con el mismo formato `key:direction` que
|
|
3045
3406
|
* usa `sort` de `kf-quote-picker` (two-way manual, no `model()` — el
|
|
@@ -3053,6 +3414,55 @@ const DEFAULT_PAGE_SIZE_OPTIONS = [10, 25, 50];
|
|
|
3053
3414
|
*
|
|
3054
3415
|
* Densidad: `density="compact"` reduce el padding vertical de cada fila,
|
|
3055
3416
|
* para priorizar cantidad de filas visibles sobre aire entre ellas.
|
|
3417
|
+
*
|
|
3418
|
+
* Listados largos con textos largos (ej. cotizaciones con el modelo del
|
|
3419
|
+
* vehículo): la columna de texto largo lleva `truncate` (elipsis + `title`),
|
|
3420
|
+
* las de dato corto `width`/`align: 'end'`, y la de acciones solo ícono
|
|
3421
|
+
* `width` angosto + `hideLabel` + `sticky: 'right'`. Así la acción queda
|
|
3422
|
+
* siempre a la vista sin scroll horizontal:
|
|
3423
|
+
*
|
|
3424
|
+
* columns = [
|
|
3425
|
+
* { key: 'id', label: 'Cotización', width: '9rem' },
|
|
3426
|
+
* { key: 'model', label: 'Vehículo', truncate: true, minWidth: '10rem' },
|
|
3427
|
+
* { key: 'amount', label: 'Monto', align: 'end', width: '8rem' },
|
|
3428
|
+
* { key: 'open', label: 'Abrir', hideLabel: true, width: '56px', sticky: 'right' },
|
|
3429
|
+
* ];
|
|
3430
|
+
* <kf-data-table [columns]="columns" [rows]="quotes" [rowClickable]="true"
|
|
3431
|
+
* (rowClick)="open($event.row)" [cellTemplates]="{ open: openCell }" />
|
|
3432
|
+
*
|
|
3433
|
+
* Reparto de anchos: con `grow` en alguna columna, esa(s) se reparten TODO el
|
|
3434
|
+
* espacio sobrante según su peso, las de `width` lo mantienen y las que no
|
|
3435
|
+
* traen `width` ni `grow` se ajustan a su contenido (con `maxWidth` como
|
|
3436
|
+
* techo). Así una columna de dato corto (estado, precio, fecha) no acapara
|
|
3437
|
+
* espacio y el texto largo (el vehículo) lo aprovecha:
|
|
3438
|
+
*
|
|
3439
|
+
* columns = [
|
|
3440
|
+
* { key: 'id', label: 'Cotización', width: '8.5rem', mono: true },
|
|
3441
|
+
* { key: 'model', label: 'Vehículo', grow: 1, truncate: true },
|
|
3442
|
+
* { key: 'issuedAt', label: 'Emisión' },
|
|
3443
|
+
* { key: 'price', label: 'Precio', align: 'end', emphasis: true },
|
|
3444
|
+
* { key: 'status', label: 'Estado', truncate: true, maxWidth: '14rem' },
|
|
3445
|
+
* ];
|
|
3446
|
+
*
|
|
3447
|
+
* Listado completo con vista tarjetas (ej. cotizaciones): la vista es
|
|
3448
|
+
* two-way (`[(view)]`) y el toggle interno se oculta con
|
|
3449
|
+
* `[showViewToggle]="false"` cuando el consumidor lo pone en su barra. En
|
|
3450
|
+
* tarjetas: título (`mono`) y estado en una fila, el campo principal a
|
|
3451
|
+
* ancho completo en 2 líneas (`cardClamp: 2`), y fecha/monto en una fila
|
|
3452
|
+
* inferior (`cardRole: 'meta'`, el monto con `emphasis`). Las tarjetas de
|
|
3453
|
+
* una fila miden lo mismo; `cardMinWidth` fija el ancho mínimo. Con
|
|
3454
|
+
* `rowClickable` la tarjeta entera es un botón (Enter/Espacio):
|
|
3455
|
+
*
|
|
3456
|
+
* columns = [
|
|
3457
|
+
* { key: 'id', label: 'Cotización', cardRole: 'title', mono: true },
|
|
3458
|
+
* { key: 'status', label: 'Estado', cardRole: 'status' },
|
|
3459
|
+
* { key: 'model', label: 'Vehículo', truncate: true, cardClamp: 2 },
|
|
3460
|
+
* { key: 'date', label: 'Fecha', cardRole: 'meta', width: '7rem' },
|
|
3461
|
+
* { key: 'amount', label: 'Monto', cardRole: 'meta', align: 'end', emphasis: true },
|
|
3462
|
+
* ];
|
|
3463
|
+
* <kf-data-table [columns]="columns" [rows]="quotes" cardable [(view)]="view"
|
|
3464
|
+
* [showViewToggle]="false" cardMinWidth="300px" [rowClickable]="true"
|
|
3465
|
+
* (rowClick)="open($event.row)" />
|
|
3056
3466
|
*/
|
|
3057
3467
|
class DataTable {
|
|
3058
3468
|
columns = input.required(/* @ts-ignore */
|
|
@@ -3078,6 +3488,14 @@ class DataTable {
|
|
|
3078
3488
|
...(ngDevMode ? [{ debugName: "canScrollLeft" }] : /* istanbul ignore next */ []));
|
|
3079
3489
|
canScrollRight = signal(false, /* @ts-ignore */
|
|
3080
3490
|
...(ngDevMode ? [{ debugName: "canScrollRight" }] : /* istanbul ignore next */ []));
|
|
3491
|
+
/**
|
|
3492
|
+
* Hay contenido oculto por scroll horizontal. Solo entonces el contenedor
|
|
3493
|
+
* es una región nombrada y enfocable (`tabindex="0"`, `role="region"`):
|
|
3494
|
+
* sin foco, quien navega con teclado no puede desplazarlo (WCAG 2.1.1).
|
|
3495
|
+
* Una tabla que entra completa no suma una parada de Tab vacía.
|
|
3496
|
+
*/
|
|
3497
|
+
hasOverflow = signal(false, /* @ts-ignore */
|
|
3498
|
+
...(ngDevMode ? [{ debugName: "hasOverflow" }] : /* istanbul ignore next */ []));
|
|
3081
3499
|
constructor() {
|
|
3082
3500
|
afterNextRender(() => {
|
|
3083
3501
|
const el = this.scrollEl()?.nativeElement;
|
|
@@ -3103,15 +3521,45 @@ class DataTable {
|
|
|
3103
3521
|
this.measureStickyOffsets();
|
|
3104
3522
|
});
|
|
3105
3523
|
// `rows()` cambia de identidad cuando el consumidor filtra el dataset
|
|
3106
|
-
// base (ej. cambiar de tab)
|
|
3107
|
-
//
|
|
3108
|
-
//
|
|
3109
|
-
//
|
|
3110
|
-
// solo a `rows()
|
|
3111
|
-
|
|
3524
|
+
// base (ej. cambiar de tab): la página vuelve a 1, salvo con
|
|
3525
|
+
// `keepPageOnRowsChange`, donde el consumidor lleva la página (ej. en la
|
|
3526
|
+
// URL) y solo se corrige si queda fuera de rango (ver abajo). La primera
|
|
3527
|
+
// ejecución no cuenta: respeta la página inicial de `[(page)]`. Reacciona
|
|
3528
|
+
// solo a `rows()` y a la bandera, no a `page()`/`sort()`.
|
|
3529
|
+
let firstRowsRun = true;
|
|
3112
3530
|
effect(() => {
|
|
3113
3531
|
this.rows();
|
|
3114
|
-
this.
|
|
3532
|
+
const keep = this.keepPageOnRowsChange();
|
|
3533
|
+
if (firstRowsRun) {
|
|
3534
|
+
firstRowsRun = false;
|
|
3535
|
+
return;
|
|
3536
|
+
}
|
|
3537
|
+
if (!keep) {
|
|
3538
|
+
this.page.set(1);
|
|
3539
|
+
}
|
|
3540
|
+
});
|
|
3541
|
+
// Una página fuera de rango (ej. `?page=9` con 3 páginas, o un filtro que
|
|
3542
|
+
// deja menos filas) se corrige a la más cercana válida y se notifica por
|
|
3543
|
+
// `pageChange`. Sin filas o cargando no se corrige: el total aún no es
|
|
3544
|
+
// real y se perdería la página pedida.
|
|
3545
|
+
effect(() => {
|
|
3546
|
+
if (this.loading() || this.rows().length === 0 || !this.effectivePageSize()) {
|
|
3547
|
+
return;
|
|
3548
|
+
}
|
|
3549
|
+
const total = this.totalPages();
|
|
3550
|
+
const current = this.page();
|
|
3551
|
+
if (current > total) {
|
|
3552
|
+
this.page.set(total);
|
|
3553
|
+
}
|
|
3554
|
+
else if (current < 1) {
|
|
3555
|
+
this.page.set(1);
|
|
3556
|
+
}
|
|
3557
|
+
});
|
|
3558
|
+
// Un `pageSize` nuevo desde el consumidor (ej. leído de la URL) manda
|
|
3559
|
+
// sobre el tamaño que el usuario eligió antes en el selector.
|
|
3560
|
+
effect(() => {
|
|
3561
|
+
this.pageSize();
|
|
3562
|
+
untracked(() => this.internalPageSize.set(null));
|
|
3115
3563
|
});
|
|
3116
3564
|
}
|
|
3117
3565
|
updateScrollState(el) {
|
|
@@ -3120,6 +3568,7 @@ class DataTable {
|
|
|
3120
3568
|
// al final (o nunca tuvo overflow real).
|
|
3121
3569
|
this.canScrollLeft.set(el.scrollLeft > 1);
|
|
3122
3570
|
this.canScrollRight.set(el.scrollWidth - el.clientWidth - el.scrollLeft > 1);
|
|
3571
|
+
this.hasOverflow.set(el.scrollWidth - el.clientWidth > 1);
|
|
3123
3572
|
}
|
|
3124
3573
|
/** Default: índice de la fila dentro de `rows`. */
|
|
3125
3574
|
rowKey = input(/* @ts-ignore */
|
|
@@ -3128,10 +3577,16 @@ class DataTable {
|
|
|
3128
3577
|
flush = input(false, /* @ts-ignore */
|
|
3129
3578
|
...(ngDevMode ? [{ debugName: "flush" }] : /* istanbul ignore next */ []));
|
|
3130
3579
|
/** Habilita el toggle de vista tabla/cards. */
|
|
3131
|
-
cardable = input(false, /*
|
|
3132
|
-
...(ngDevMode ? [{ debugName: "cardable" }] : /* istanbul ignore next */ []));
|
|
3580
|
+
cardable = input(false, { ...(ngDevMode ? { debugName: "cardable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
3133
3581
|
defaultView = input('table', /* @ts-ignore */
|
|
3134
3582
|
...(ngDevMode ? [{ debugName: "defaultView" }] : /* istanbul ignore next */ []));
|
|
3583
|
+
/** Muestra el toggle tabla/tarjetas interno (con `cardable`). En `false`,
|
|
3584
|
+
* el consumidor controla la vista con `[(view)]` desde su propio control. */
|
|
3585
|
+
showViewToggle = input(true, { ...(ngDevMode ? { debugName: "showViewToggle" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
3586
|
+
/** Ancho mínimo de cada tarjeta en la grilla (default `220px`, histórico).
|
|
3587
|
+
* Nunca supera el ancho disponible: en pantallas angostas, una columna. */
|
|
3588
|
+
cardMinWidth = input('220px', /* @ts-ignore */
|
|
3589
|
+
...(ngDevMode ? [{ debugName: "cardMinWidth" }] : /* istanbul ignore next */ []));
|
|
3135
3590
|
/**
|
|
3136
3591
|
* Formateo simple de celda como texto: `(row) => string`. Para marcado
|
|
3137
3592
|
* real (badges, links, botones) usar `cellTemplates`.
|
|
@@ -3149,6 +3604,17 @@ class DataTable {
|
|
|
3149
3604
|
rowClickable = input(null, /* @ts-ignore */
|
|
3150
3605
|
...(ngDevMode ? [{ debugName: "rowClickable" }] : /* istanbul ignore next */ []));
|
|
3151
3606
|
rowClick = output();
|
|
3607
|
+
/** Nombre accesible del contenedor con scroll horizontal (solo se aplica cuando hay overflow). */
|
|
3608
|
+
ariaLabel = input('Tabla de datos', /* @ts-ignore */
|
|
3609
|
+
...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
3610
|
+
/**
|
|
3611
|
+
* Clave (la misma que devuelve `rowKey`, o el índice sin él) de la fila
|
|
3612
|
+
* "actual" — ej. el registro que se está viendo en un selector. Esa fila
|
|
3613
|
+
* o card lleva `aria-current="true"` y la clase `--current` (fondo
|
|
3614
|
+
* `primary-soft`). `null` (default): ninguna.
|
|
3615
|
+
*/
|
|
3616
|
+
currentRowKey = input(null, /* @ts-ignore */
|
|
3617
|
+
...(ngDevMode ? [{ debugName: "currentRowKey" }] : /* istanbul ignore next */ []));
|
|
3152
3618
|
/**
|
|
3153
3619
|
* Paginación interna opcional — sin `pageSize`, cero cambio de
|
|
3154
3620
|
* comportamiento (el consumidor sigue paginando afuera si quiere, vía el
|
|
@@ -3165,7 +3631,17 @@ class DataTable {
|
|
|
3165
3631
|
* aunque `pageSize` esté activo (paginación sin selector). */
|
|
3166
3632
|
pageSizeOptions = input(DEFAULT_PAGE_SIZE_OPTIONS, /* @ts-ignore */
|
|
3167
3633
|
...(ngDevMode ? [{ debugName: "pageSizeOptions" }] : /* istanbul ignore next */ []));
|
|
3168
|
-
|
|
3634
|
+
/** Página actual (desde 1), bidireccional: `[(page)]="page"`. El consumidor
|
|
3635
|
+
* puede llevarla fuera de la tabla (ej. en la URL) y la tabla la corrige
|
|
3636
|
+
* si queda fuera de rango. `pageChange` emite en cada cambio, también los
|
|
3637
|
+
* automáticos (volver a 1 al cambiar de tamaño de página). */
|
|
3638
|
+
page = model(1, /* @ts-ignore */
|
|
3639
|
+
...(ngDevMode ? [{ debugName: "page" }] : /* istanbul ignore next */ []));
|
|
3640
|
+
/** Con `true`, un cambio de `rows` NO devuelve la página a 1: el
|
|
3641
|
+
* consumidor decide cuándo (ej. al filtrar) y la tabla solo la corrige si
|
|
3642
|
+
* queda fuera de rango. Default `false`: vuelve a 1, como siempre. */
|
|
3643
|
+
keepPageOnRowsChange = input(false, { ...(ngDevMode ? { debugName: "keepPageOnRowsChange" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
3644
|
+
/** Emite el tamaño elegido en el selector de la paginación interna. */
|
|
3169
3645
|
pageSizeChange = output();
|
|
3170
3646
|
/** Nombre de la entidad listada (ej. "beneficios", "facturas"), en
|
|
3171
3647
|
* plural — se agrega al final del texto informativo de la paginación
|
|
@@ -3174,8 +3650,6 @@ class DataTable {
|
|
|
3174
3650
|
* fijo, cada consumidor decide el suyo. */
|
|
3175
3651
|
itemLabel = input('', /* @ts-ignore */
|
|
3176
3652
|
...(ngDevMode ? [{ debugName: "itemLabel" }] : /* istanbul ignore next */ []));
|
|
3177
|
-
page = signal(1, /* @ts-ignore */
|
|
3178
|
-
...(ngDevMode ? [{ debugName: "page" }] : /* istanbul ignore next */ []));
|
|
3179
3653
|
internalPageSize = signal(null, /* @ts-ignore */
|
|
3180
3654
|
...(ngDevMode ? [{ debugName: "internalPageSize" }] : /* istanbul ignore next */ []));
|
|
3181
3655
|
/** Tamaño de página efectivo: el que el usuario elija en el selector
|
|
@@ -3280,15 +3754,82 @@ class DataTable {
|
|
|
3280
3754
|
return;
|
|
3281
3755
|
}
|
|
3282
3756
|
this.page.set(clamped);
|
|
3283
|
-
this.pageChange.emit(clamped);
|
|
3284
3757
|
}
|
|
3285
3758
|
setPageSize(size) {
|
|
3286
3759
|
if (size === null) {
|
|
3287
3760
|
return;
|
|
3288
3761
|
}
|
|
3289
3762
|
this.internalPageSize.set(size);
|
|
3290
|
-
|
|
3763
|
+
// Primero el tamaño y después la página: un consumidor que lleva ambos en
|
|
3764
|
+
// la URL los escribe en una sola navegación.
|
|
3291
3765
|
this.pageSizeChange.emit(size);
|
|
3766
|
+
this.page.set(1);
|
|
3767
|
+
}
|
|
3768
|
+
/**
|
|
3769
|
+
* Reparto de anchos activo: hay al menos una columna `grow`. Sin ella, los
|
|
3770
|
+
* anchos son los de siempre (`width`/`minWidth`/`maxWidth` tal cual).
|
|
3771
|
+
*/
|
|
3772
|
+
weighted = computed(() => this.columns().some((column) => this.growWeight(column) > 0), /* @ts-ignore */
|
|
3773
|
+
...(ngDevMode ? [{ debugName: "weighted" }] : /* istanbul ignore next */ []));
|
|
3774
|
+
/** Porción (%) del espacio sobrante de cada columna `grow`, según su peso. */
|
|
3775
|
+
growShares = computed(() => {
|
|
3776
|
+
const weights = this.columns()
|
|
3777
|
+
.map((column) => [column.key, this.growWeight(column)])
|
|
3778
|
+
.filter(([, weight]) => weight > 0);
|
|
3779
|
+
const total = weights.reduce((sum, [, weight]) => sum + weight, 0);
|
|
3780
|
+
return new Map(weights.map(([key, weight]) => [key, Math.round((weight / total) * 10000) / 100]));
|
|
3781
|
+
}, /* @ts-ignore */
|
|
3782
|
+
...(ngDevMode ? [{ debugName: "growShares" }] : /* istanbul ignore next */ []));
|
|
3783
|
+
growWeight(column) {
|
|
3784
|
+
const weight = column.grow ?? 0;
|
|
3785
|
+
return Number.isFinite(weight) && weight > 0 ? weight : 0;
|
|
3786
|
+
}
|
|
3787
|
+
/**
|
|
3788
|
+
* `width` de la celda. En reparto de anchos: la columna `grow` toma su
|
|
3789
|
+
* porción en porcentaje (las porciones suman 100% y el navegador las escala
|
|
3790
|
+
* al espacio que dejan las demás), la de `width` conserva su ancho y la que
|
|
3791
|
+
* no trae ni `width` ni `grow` pide 1% — con `nowrap` queda en el ancho de
|
|
3792
|
+
* su contenido.
|
|
3793
|
+
*/
|
|
3794
|
+
widthFor(column) {
|
|
3795
|
+
if (!this.weighted()) {
|
|
3796
|
+
return column.width ?? null;
|
|
3797
|
+
}
|
|
3798
|
+
const share = this.growShares().get(column.key);
|
|
3799
|
+
if (share !== undefined) {
|
|
3800
|
+
return `${share}%`;
|
|
3801
|
+
}
|
|
3802
|
+
return column.width ?? '1%';
|
|
3803
|
+
}
|
|
3804
|
+
/**
|
|
3805
|
+
* `min-width` de la celda. En reparto de anchos, el `width` de una columna
|
|
3806
|
+
* (también de una `grow`) funciona como piso: ni las porciones en
|
|
3807
|
+
* porcentaje ni el contenido de las demás pueden comprimirla.
|
|
3808
|
+
*/
|
|
3809
|
+
minWidthFor(column) {
|
|
3810
|
+
if (!this.weighted()) {
|
|
3811
|
+
return column.minWidth ?? null;
|
|
3812
|
+
}
|
|
3813
|
+
// Una columna `grow` sin piso propio nunca baja de `GROW_MIN_WIDTH`: con
|
|
3814
|
+
// poco espacio la tabla desplaza en horizontal en vez de dejar sin ancho
|
|
3815
|
+
// a la columna principal.
|
|
3816
|
+
const fallback = this.growShares().has(column.key) ? GROW_MIN_WIDTH : null;
|
|
3817
|
+
return column.minWidth ?? column.width ?? fallback;
|
|
3818
|
+
}
|
|
3819
|
+
/**
|
|
3820
|
+
* `max-width` de la celda. En reparto de anchos, una columna que se ajusta
|
|
3821
|
+
* a su contenido y es `truncate` SIN `maxWidth` no debe heredar el techo de
|
|
3822
|
+
* 0 de `truncate` (quedaría sin ancho): se libera con `none`.
|
|
3823
|
+
*/
|
|
3824
|
+
maxWidthFor(column) {
|
|
3825
|
+
if (this.weighted() &&
|
|
3826
|
+
column.truncate &&
|
|
3827
|
+
!column.maxWidth &&
|
|
3828
|
+
!column.width &&
|
|
3829
|
+
!this.growShares().has(column.key)) {
|
|
3830
|
+
return 'none';
|
|
3831
|
+
}
|
|
3832
|
+
return column.maxWidth ?? null;
|
|
3292
3833
|
}
|
|
3293
3834
|
/** Columnas fijas del lado izquierdo/derecho, con el offset acumulado
|
|
3294
3835
|
* (medido en runtime, ver `measureStickyOffsets`) para `position: sticky`. */
|
|
@@ -3309,11 +3850,18 @@ class DataTable {
|
|
|
3309
3850
|
zIndex: '1',
|
|
3310
3851
|
};
|
|
3311
3852
|
}
|
|
3312
|
-
|
|
3313
|
-
|
|
3314
|
-
|
|
3315
|
-
|
|
3853
|
+
/**
|
|
3854
|
+
* Vista elegida, two-way: `[(view)]="view"`. Sin valor (default) manda
|
|
3855
|
+
* `defaultView`; el toggle interno la escribe. Para controlarla desde
|
|
3856
|
+
* afuera (ej. con el toggle en `kf-list-toolbar` y el estado en la URL),
|
|
3857
|
+
* enlazar `[(view)]` y ocultar el interno con `[showViewToggle]="false"`.
|
|
3858
|
+
* La vista efectivamente renderizada es `activeView()`.
|
|
3859
|
+
*/
|
|
3860
|
+
view = model(undefined, /* @ts-ignore */
|
|
3316
3861
|
...(ngDevMode ? [{ debugName: "view" }] : /* istanbul ignore next */ []));
|
|
3862
|
+
/** Vista activa: `view` si está definida, si no `defaultView`. */
|
|
3863
|
+
activeView = computed(() => this.view() ?? this.defaultView(), /* @ts-ignore */
|
|
3864
|
+
...(ngDevMode ? [{ debugName: "activeView" }] : /* istanbul ignore next */ []));
|
|
3317
3865
|
/** Primera columna de cada rol único; el resto se ignora en el encabezado. */
|
|
3318
3866
|
cardMediaColumn = computed(() => this.columns().find((c) => c.cardRole === 'media'), /* @ts-ignore */
|
|
3319
3867
|
...(ngDevMode ? [{ debugName: "cardMediaColumn" }] : /* istanbul ignore next */ []));
|
|
@@ -3323,14 +3871,20 @@ class DataTable {
|
|
|
3323
3871
|
...(ngDevMode ? [{ debugName: "cardStatusColumns" }] : /* istanbul ignore next */ []));
|
|
3324
3872
|
cardActionColumns = computed(() => this.columns().filter((c) => c.cardRole === 'action'), /* @ts-ignore */
|
|
3325
3873
|
...(ngDevMode ? [{ debugName: "cardActionColumns" }] : /* istanbul ignore next */ []));
|
|
3326
|
-
cardFieldColumns = computed(() => this.columns().filter((c) => !c.cardRole || c.cardRole === 'field'), /* @ts-ignore */
|
|
3874
|
+
cardFieldColumns = computed(() => this.columns().filter((c) => (!c.cardRole || c.cardRole === 'field') && !c.cardClamp), /* @ts-ignore */
|
|
3327
3875
|
...(ngDevMode ? [{ debugName: "cardFieldColumns" }] : /* istanbul ignore next */ []));
|
|
3876
|
+
/** Campos a ancho completo y recortados (`cardClamp`), antes del resto. */
|
|
3877
|
+
cardLeadColumns = computed(() => this.columns().filter((c) => (!c.cardRole || c.cardRole === 'field') && !!c.cardClamp), /* @ts-ignore */
|
|
3878
|
+
...(ngDevMode ? [{ debugName: "cardLeadColumns" }] : /* istanbul ignore next */ []));
|
|
3879
|
+
/** Fila inferior de datos secundarios (`cardRole: 'meta'`). */
|
|
3880
|
+
cardMetaColumns = computed(() => this.columns().filter((c) => c.cardRole === 'meta'), /* @ts-ignore */
|
|
3881
|
+
...(ngDevMode ? [{ debugName: "cardMetaColumns" }] : /* istanbul ignore next */ []));
|
|
3328
3882
|
/**
|
|
3329
3883
|
* La card entera es clickeable solo si no tiene acciones propias: un
|
|
3330
3884
|
* `role="button"` con botones adentro es inválido y deja el foco ambiguo.
|
|
3331
3885
|
* Con columnas `action`, la interacción la manejan esos botones.
|
|
3332
3886
|
*/
|
|
3333
|
-
cardInteractive = computed(() => this.cardActionColumns().length === 0, /* @ts-ignore */
|
|
3887
|
+
cardInteractive = computed(() => this.rowClickable() ?? this.cardActionColumns().length === 0, /* @ts-ignore */
|
|
3334
3888
|
...(ngDevMode ? [{ debugName: "cardInteractive" }] : /* istanbul ignore next */ []));
|
|
3335
3889
|
/**
|
|
3336
3890
|
* Mismo criterio que `cardInteractive`, para la vista de tabla: la fila
|
|
@@ -3343,12 +3897,23 @@ class DataTable {
|
|
|
3343
3897
|
rowInteractive = computed(() => this.rowClickable() ?? this.cardActionColumns().length === 0, /* @ts-ignore */
|
|
3344
3898
|
...(ngDevMode ? [{ debugName: "rowInteractive" }] : /* istanbul ignore next */ []));
|
|
3345
3899
|
setView(next) {
|
|
3346
|
-
this.
|
|
3900
|
+
this.view.set(next);
|
|
3347
3901
|
}
|
|
3348
3902
|
trackRow(row, index) {
|
|
3349
3903
|
const keyFn = this.rowKey();
|
|
3350
3904
|
return keyFn ? keyFn(row, index) : index;
|
|
3351
3905
|
}
|
|
3906
|
+
/** Alineada al final: `align: 'end'` o el histórico `right: true`. */
|
|
3907
|
+
isEndAligned(column) {
|
|
3908
|
+
return column.align === 'end' || !!column.right;
|
|
3909
|
+
}
|
|
3910
|
+
/** `title` de una celda `truncate`: el valor completo como texto. */
|
|
3911
|
+
truncateTitle(row, column) {
|
|
3912
|
+
if (!column.truncate) {
|
|
3913
|
+
return null;
|
|
3914
|
+
}
|
|
3915
|
+
return this.cellValue(row, column) || null;
|
|
3916
|
+
}
|
|
3352
3917
|
cellValue(row, column) {
|
|
3353
3918
|
const renderFn = this.render();
|
|
3354
3919
|
if (renderFn) {
|
|
@@ -3357,12 +3922,45 @@ class DataTable {
|
|
|
3357
3922
|
const value = row[column.key];
|
|
3358
3923
|
return value === null || value === undefined ? '' : String(value);
|
|
3359
3924
|
}
|
|
3360
|
-
|
|
3361
|
-
|
|
3925
|
+
isCurrentRow(row, index) {
|
|
3926
|
+
const current = this.currentRowKey();
|
|
3927
|
+
return current !== null && this.trackRow(row, index) === current;
|
|
3928
|
+
}
|
|
3929
|
+
/**
|
|
3930
|
+
* Espacio activa la fila/card igual que Enter, como un botón. Solo si el
|
|
3931
|
+
* foco está en la fila misma (no en un control dentro de ella): ahí
|
|
3932
|
+
* `preventDefault` evita el scroll de página; en cualquier otro caso
|
|
3933
|
+
* Espacio conserva su comportamiento nativo.
|
|
3934
|
+
*/
|
|
3935
|
+
onRowSpace(event, row, index) {
|
|
3936
|
+
if (event.target !== event.currentTarget || !this.rowInteractive()) {
|
|
3937
|
+
return;
|
|
3938
|
+
}
|
|
3939
|
+
event.preventDefault();
|
|
3940
|
+
this.onRowClick(row, index);
|
|
3941
|
+
}
|
|
3942
|
+
/**
|
|
3943
|
+
* Emite `rowClick` salvo que el evento nazca dentro de un control
|
|
3944
|
+
* interactivo de la celda (enlace, botón, campo…): ese control ya resuelve
|
|
3945
|
+
* su propia acción. Así una celda con un `<a href>` real (clic derecho →
|
|
3946
|
+
* "abrir en nueva pestaña", Ctrl/Cmd+clic) no navega dos veces. El parámetro
|
|
3947
|
+
* `event` es opcional para no romper llamadas programáticas existentes.
|
|
3948
|
+
*/
|
|
3949
|
+
onRowClick(row, index, event) {
|
|
3950
|
+
if (!this.rowInteractive() || this.originatesInInteractiveControl(event)) {
|
|
3362
3951
|
return;
|
|
3363
3952
|
}
|
|
3364
3953
|
this.rowClick.emit({ row, index });
|
|
3365
3954
|
}
|
|
3955
|
+
originatesInInteractiveControl(event) {
|
|
3956
|
+
const target = event?.target;
|
|
3957
|
+
const host = event?.currentTarget;
|
|
3958
|
+
if (!(target instanceof Element) || !(host instanceof Element) || target === host) {
|
|
3959
|
+
return false;
|
|
3960
|
+
}
|
|
3961
|
+
const control = target.closest(INTERACTIVE_CONTROL_SELECTOR);
|
|
3962
|
+
return control !== null && control !== host && host.contains(control);
|
|
3963
|
+
}
|
|
3366
3964
|
/**
|
|
3367
3965
|
* Ancho real de cada columna `sticky` — se mide después de cada render
|
|
3368
3966
|
* porque el ancho de una columna depende de su contenido, no hay forma
|
|
@@ -3392,12 +3990,12 @@ class DataTable {
|
|
|
3392
3990
|
this.stickyOffsets.set(offsets);
|
|
3393
3991
|
}
|
|
3394
3992
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DataTable, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3395
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: DataTable, isStandalone: true, selector: "kf-data-table", inputs: { columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: true, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, rowKey: { classPropertyName: "rowKey", publicName: "rowKey", isSignal: true, isRequired: false, transformFunction: null }, flush: { classPropertyName: "flush", publicName: "flush", isSignal: true, isRequired: false, transformFunction: null }, cardable: { classPropertyName: "cardable", publicName: "cardable", isSignal: true, isRequired: false, transformFunction: null }, defaultView: { classPropertyName: "defaultView", publicName: "defaultView", isSignal: true, isRequired: false, transformFunction: null }, render: { classPropertyName: "render", publicName: "render", isSignal: true, isRequired: false, transformFunction: null }, cellTemplates: { classPropertyName: "cellTemplates", publicName: "cellTemplates", isSignal: true, isRequired: false, transformFunction: null }, density: { classPropertyName: "density", publicName: "density", isSignal: true, isRequired: false, transformFunction: null }, rowClickable: { classPropertyName: "rowClickable", publicName: "rowClickable", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, pageSizeOptions: { classPropertyName: "pageSizeOptions", publicName: "pageSizeOptions", isSignal: true, isRequired: false, transformFunction: null }, itemLabel: { classPropertyName: "itemLabel", publicName: "itemLabel", isSignal: true, isRequired: false, transformFunction: null }, sort: { classPropertyName: "sort", publicName: "sort", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { rowClick: "rowClick", pageChange: "pageChange", pageSizeChange: "pageSizeChange", sortChange: "sortChange" }, viewQueries: [{ propertyName: "scrollEl", first: true, predicate: ["scroll"], descendants: true, isSignal: true }], ngImport: i0, template: "<div\n class=\"kf-data-table\"\n [class.kf-data-table--flush]=\"flush()\"\n [class.kf-data-table--compact]=\"density() === 'compact'\"\n>\n @if (cardable()) {\n <div class=\"kf-data-table__toolbar\">\n <kf-view-toggle [view]=\"view()\" (viewChange)=\"setView($event)\" />\n </div>\n }\n\n @if (loading()) {\n @if (cardable() && view() === 'cards') {\n <div class=\"kf-data-table__cards\" aria-busy=\"true\">\n @for (i of skeletonRowIndexes(); track i) {\n <div class=\"kf-data-table__card\">\n <kf-skeleton variant=\"rect\" height=\"120px\" />\n </div>\n }\n </div>\n } @else {\n <div class=\"kf-data-table__scroll\" aria-busy=\"true\">\n <table class=\"kf-data-table__table\">\n <thead>\n <tr>\n @for (column of columns(); track column.key) {\n <th\n class=\"kf-overline-text\"\n [class]=\"column.className\"\n [class.kf-data-table__cell--right]=\"column.right\"\n >\n {{ column.label }}\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @for (i of skeletonRowIndexes(); track i) {\n <tr class=\"kf-data-table__row\">\n @for (column of columns(); track column.key) {\n <td [class]=\"column.className\" [class.kf-data-table__cell--right]=\"column.right\">\n <kf-skeleton />\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n } @else if (rows().length === 0) {\n <div class=\"kf-data-table__empty\">\n <ng-content select=\"[empty]\" />\n </div>\n } @else if (view() === 'table') {\n <div\n #scroll\n class=\"kf-data-table__scroll\"\n [class.kf-data-table__scroll--fade-left]=\"canScrollLeft()\"\n [class.kf-data-table__scroll--fade-right]=\"canScrollRight()\"\n >\n <table class=\"kf-data-table__table\">\n <thead>\n <tr>\n @for (column of columns(); track column.key) {\n <th\n class=\"kf-overline-text\"\n [attr.data-column-key]=\"column.key\"\n [class]=\"column.className\"\n [class.kf-data-table__cell--right]=\"column.right\"\n [class.kf-data-table__cell--sortable]=\"column.sortable\"\n [class.kf-data-table__cell--sticky]=\"column.sticky\"\n [class.kf-data-table__cell--sort-active]=\"sortDirectionFor(column) !== null\"\n [style]=\"stickyStyle(column)\"\n [style.min-width]=\"column.minWidth\"\n (click)=\"toggleSort(column)\"\n >\n <span class=\"kf-data-table__th-content\">\n {{ column.label }}\n @if (column.sortable) {\n <kf-icon\n name=\"chevron-down\"\n size=\"sm\"\n [rotate]=\"sortDirectionFor(column) === 'asc' ? 180 : 0\"\n class=\"kf-data-table__sort-icon\"\n [class.kf-data-table__sort-icon--active]=\"sortDirectionFor(column) !== null\"\n />\n }\n </span>\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @for (row of displayedRows(); track trackRow(row, $index); let i = $index) {\n <tr\n class=\"kf-data-table__row\"\n [class.kf-data-table__row--interactive]=\"rowInteractive()\"\n [attr.tabindex]=\"rowInteractive() ? 0 : null\"\n (click)=\"onRowClick(row, i)\"\n (keydown.enter)=\"onRowClick(row, i)\"\n >\n @for (column of columns(); track column.key) {\n <td\n [attr.data-column-key]=\"column.key\"\n [class]=\"column.className\"\n [class.kf-data-table__cell--right]=\"column.right\"\n [class.kf-data-table__cell--sticky]=\"column.sticky\"\n [style]=\"stickyStyle(column)\"\n [style.min-width]=\"column.minWidth\"\n >\n @if (cellTemplates()?.[column.key]; as template) {\n <ng-container\n [ngTemplateOutlet]=\"template\"\n [ngTemplateOutletContext]=\"{ $implicit: row, index: i }\"\n />\n } @else if (column.cardRole === 'media') {\n <img class=\"kf-data-table__thumb\" [src]=\"cellValue(row, column)\" alt=\"\" />\n } @else {\n {{ cellValue(row, column) }}\n }\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n } @else {\n <div class=\"kf-data-table__cards\">\n @for (row of displayedRows(); track trackRow(row, $index); let i = $index) {\n <div\n class=\"kf-data-table__card\"\n [class.kf-data-table__card--interactive]=\"cardInteractive()\"\n [attr.role]=\"cardInteractive() ? 'button' : null\"\n [attr.tabindex]=\"cardInteractive() ? 0 : null\"\n (click)=\"cardInteractive() && onRowClick(row, i)\"\n (keydown.enter)=\"cardInteractive() && onRowClick(row, i)\"\n >\n @if (cardMediaColumn() || cardTitleColumn() || cardStatusColumns().length) {\n <div class=\"kf-data-table__card-header\">\n @if (cardMediaColumn(); as media) {\n <div class=\"kf-data-table__card-media\">\n @if (cellTemplates()?.[media.key]; as template) {\n <ng-container\n [ngTemplateOutlet]=\"template\"\n [ngTemplateOutletContext]=\"{ $implicit: row, index: i }\"\n />\n } @else {\n <img [src]=\"cellValue(row, media)\" alt=\"\" />\n }\n </div>\n }\n <div class=\"kf-data-table__card-heading\">\n @if (cardTitleColumn(); as title) {\n <span class=\"kf-data-table__card-title\">\n @if (cellTemplates()?.[title.key]; as template) {\n <ng-container\n [ngTemplateOutlet]=\"template\"\n [ngTemplateOutletContext]=\"{ $implicit: row, index: i }\"\n />\n } @else {\n {{ cellValue(row, title) }}\n }\n </span>\n }\n @if (cardStatusColumns().length) {\n <span class=\"kf-data-table__card-statuses\">\n @for (column of cardStatusColumns(); track column.key) {\n @if (cellTemplates()?.[column.key]; as template) {\n <ng-container\n [ngTemplateOutlet]=\"template\"\n [ngTemplateOutletContext]=\"{ $implicit: row, index: i }\"\n />\n } @else {\n {{ cellValue(row, column) }}\n }\n }\n </span>\n }\n </div>\n </div>\n }\n\n @if (cardFieldColumns().length) {\n <div class=\"kf-data-table__card-fields\">\n @for (column of cardFieldColumns(); track column.key) {\n <div class=\"kf-data-table__card-field\">\n <span class=\"kf-data-table__card-label kf-overline-text\">{{ column.label }}</span>\n <span class=\"kf-data-table__card-value\">\n @if (cellTemplates()?.[column.key]; as template) {\n <ng-container\n [ngTemplateOutlet]=\"template\"\n [ngTemplateOutletContext]=\"{ $implicit: row, index: i }\"\n />\n } @else {\n {{ cellValue(row, column) }}\n }\n </span>\n </div>\n }\n </div>\n }\n\n @if (cardActionColumns().length) {\n <div class=\"kf-data-table__card-actions\">\n @for (column of cardActionColumns(); track column.key) {\n @if (cellTemplates()?.[column.key]; as template) {\n <ng-container\n [ngTemplateOutlet]=\"template\"\n [ngTemplateOutletContext]=\"{ $implicit: row, index: i }\"\n />\n } @else {\n {{ cellValue(row, column) }}\n }\n }\n </div>\n }\n </div>\n }\n </div>\n }\n\n @if (!loading() && rows().length > 0) {\n <div class=\"kf-data-table__footer\">\n @if (effectivePageSize()) {\n <kf-pagination\n [page]=\"page()\"\n [pages]=\"totalPages()\"\n [pageSizeOptions]=\"pageSizeOptions()\"\n [pageSize]=\"effectivePageSize()\"\n (pageChange)=\"goToPage($event)\"\n (pageSizeChange)=\"setPageSize($event)\"\n >\n {{ paginationSummary() }}\n </kf-pagination>\n }\n <ng-content select=\"[footer]\" />\n </div>\n }\n</div>\n", styles: [".kf-overline-text{font-family:var(--kf-font-title);font-size:var(--kf-font-size-caption);font-weight:var(--kf-font-weight-medium);letter-spacing:.04em;text-transform:uppercase;color:var(--kf-color-text-muted)}:host{display:block;font-family:var(--kf-font-body)}.kf-data-table{background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md);box-shadow:var(--kf-shadow-sm);overflow:hidden}.kf-data-table--flush{border:none;box-shadow:none;border-radius:0}.kf-data-table__toolbar{display:flex;justify-content:flex-end;padding:var(--kf-space-sm) var(--kf-space-md);border-bottom:1px solid var(--kf-color-border)}.kf-data-table__scroll{position:relative;overflow-x:auto}.kf-data-table__scroll:before,.kf-data-table__scroll:after{content:\"\";position:absolute;top:0;bottom:0;width:24px;pointer-events:none;opacity:0;transition:opacity .14s}.kf-data-table__scroll:before{left:0;background:linear-gradient(to right,var(--kf-color-white),transparent)}.kf-data-table__scroll:after{right:0;background:linear-gradient(to left,var(--kf-color-white),transparent)}.kf-data-table__scroll--fade-left:before{opacity:1}.kf-data-table__scroll--fade-right:after{opacity:1}.kf-data-table__table{width:100%;border-collapse:collapse;text-align:left}.kf-data-table__table th,.kf-data-table__table td{padding:var(--kf-space-sm) var(--kf-space-md);border-bottom:1px solid var(--kf-color-border);white-space:nowrap}.kf-data-table--compact .kf-data-table__table th,.kf-data-table--compact .kf-data-table__table td{padding:var(--kf-space-xs) var(--kf-space-md)}.kf-data-table__table th{background:var(--kf-color-surface)}.kf-data-table__table td{color:var(--kf-color-text)}.kf-data-table__table tbody tr:last-child td{border-bottom:none}.kf-data-table__row{transition:background .14s}.kf-data-table__row--interactive{cursor:pointer}.kf-data-table__row--interactive:hover{background:var(--kf-color-primary-soft)}.kf-data-table__row--interactive:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--kf-color-primary)}.kf-data-table__cell--right{text-align:right}.kf-data-table__cell--sticky{background:var(--kf-color-white)}.kf-data-table__table th.kf-data-table__cell--sticky{background:var(--kf-color-surface);z-index:2}.kf-data-table__cell--sortable{cursor:pointer;-webkit-user-select:none;user-select:none}.kf-data-table__cell--sortable:hover{color:var(--kf-color-ink)}.kf-data-table__table th.kf-data-table__cell--sort-active{color:var(--kf-color-primary);font-weight:700}.kf-data-table__th-content{display:inline-flex;align-items:center;gap:var(--kf-space-xs)}.kf-data-table__sort-icon{transition:opacity .14s}.kf-data-table__cell--sortable:hover .kf-data-table__sort-icon{opacity:.5}.kf-data-table__sort-icon--active,.kf-data-table__cell--sortable:hover .kf-data-table__sort-icon--active{opacity:1}.kf-data-table__empty{padding:var(--kf-space-md)}.kf-data-table__footer{padding:var(--kf-space-sm) var(--kf-space-md);border-top:1px solid var(--kf-color-border)}.kf-data-table__footer:empty{display:none}.kf-data-table__cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:var(--kf-space-md);padding:var(--kf-space-md)}.kf-data-table__card{display:flex;flex-direction:column;border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);overflow:hidden;transition:border-color .14s,box-shadow .14s}.kf-data-table__card--interactive{cursor:pointer}.kf-data-table__card:hover{border-color:var(--kf-color-border-strong);box-shadow:var(--kf-shadow-xs)}.kf-data-table__card--interactive:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-data-table__card-header{display:flex;align-items:center;gap:var(--kf-space-sm);padding:var(--kf-space-md)}.kf-data-table__card-media{flex:none}.kf-data-table__card-media img,.kf-data-table__thumb{display:block;width:48px;height:48px;border-radius:var(--kf-radius-xs);object-fit:cover;background:var(--kf-color-surface)}.kf-data-table__card-heading{display:flex;flex-direction:column;gap:var(--kf-space-xs);min-width:0}.kf-data-table__card-title{font-family:var(--kf-font-title);font-weight:600;color:var(--kf-color-ink);overflow-wrap:anywhere}.kf-data-table__card-statuses,.kf-data-table__card-actions{display:flex;flex-wrap:wrap;gap:var(--kf-space-xs)}.kf-data-table__card-fields{display:flex;flex-direction:column;gap:var(--kf-space-xs);padding:var(--kf-space-md)}.kf-data-table__card-header+.kf-data-table__card-fields{padding-top:0}.kf-data-table__card-field{display:flex;justify-content:space-between;gap:var(--kf-space-sm)}.kf-data-table__card-actions{justify-content:flex-end;margin-top:auto;padding:var(--kf-space-sm) var(--kf-space-md);border-top:1px solid var(--kf-color-border);background:var(--kf-color-surface)}.kf-data-table__thumb{width:32px;height:32px}.kf-data-table__card-value{color:var(--kf-color-text);text-align:right}\n"], dependencies: [{ kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size", "rotate", "filled"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: Pagination, selector: "kf-pagination", inputs: ["page", "pages", "pageSizeOptions", "pageSize"], outputs: ["pageChange", "pageSizeChange"] }, { kind: "component", type: Skeleton, selector: "kf-skeleton", inputs: ["variant", "width", "height"] }, { kind: "component", type: ViewToggle, selector: "kf-view-toggle", inputs: ["view"], outputs: ["viewChange"] }] });
|
|
3993
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: DataTable, isStandalone: true, selector: "kf-data-table", inputs: { columns: { classPropertyName: "columns", publicName: "columns", isSignal: true, isRequired: true, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: true, transformFunction: null }, loading: { classPropertyName: "loading", publicName: "loading", isSignal: true, isRequired: false, transformFunction: null }, rowKey: { classPropertyName: "rowKey", publicName: "rowKey", isSignal: true, isRequired: false, transformFunction: null }, flush: { classPropertyName: "flush", publicName: "flush", isSignal: true, isRequired: false, transformFunction: null }, cardable: { classPropertyName: "cardable", publicName: "cardable", isSignal: true, isRequired: false, transformFunction: null }, defaultView: { classPropertyName: "defaultView", publicName: "defaultView", isSignal: true, isRequired: false, transformFunction: null }, showViewToggle: { classPropertyName: "showViewToggle", publicName: "showViewToggle", isSignal: true, isRequired: false, transformFunction: null }, cardMinWidth: { classPropertyName: "cardMinWidth", publicName: "cardMinWidth", isSignal: true, isRequired: false, transformFunction: null }, render: { classPropertyName: "render", publicName: "render", isSignal: true, isRequired: false, transformFunction: null }, cellTemplates: { classPropertyName: "cellTemplates", publicName: "cellTemplates", isSignal: true, isRequired: false, transformFunction: null }, density: { classPropertyName: "density", publicName: "density", isSignal: true, isRequired: false, transformFunction: null }, rowClickable: { classPropertyName: "rowClickable", publicName: "rowClickable", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, currentRowKey: { classPropertyName: "currentRowKey", publicName: "currentRowKey", isSignal: true, isRequired: false, transformFunction: null }, pageSize: { classPropertyName: "pageSize", publicName: "pageSize", isSignal: true, isRequired: false, transformFunction: null }, pageSizeOptions: { classPropertyName: "pageSizeOptions", publicName: "pageSizeOptions", isSignal: true, isRequired: false, transformFunction: null }, page: { classPropertyName: "page", publicName: "page", isSignal: true, isRequired: false, transformFunction: null }, keepPageOnRowsChange: { classPropertyName: "keepPageOnRowsChange", publicName: "keepPageOnRowsChange", isSignal: true, isRequired: false, transformFunction: null }, itemLabel: { classPropertyName: "itemLabel", publicName: "itemLabel", isSignal: true, isRequired: false, transformFunction: null }, sort: { classPropertyName: "sort", publicName: "sort", isSignal: true, isRequired: false, transformFunction: null }, view: { classPropertyName: "view", publicName: "view", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { rowClick: "rowClick", page: "pageChange", pageSizeChange: "pageSizeChange", sortChange: "sortChange", view: "viewChange" }, viewQueries: [{ propertyName: "scrollEl", first: true, predicate: ["scroll"], descendants: true, isSignal: true }], ngImport: i0, template: "<div\n class=\"kf-data-table\"\n [class.kf-data-table--flush]=\"flush()\"\n [class.kf-data-table--compact]=\"density() === 'compact'\"\n>\n @if (cardable() && showViewToggle()) {\n <div class=\"kf-data-table__toolbar\">\n <kf-view-toggle [view]=\"activeView()\" (viewChange)=\"setView($event)\" />\n </div>\n }\n\n @if (loading()) {\n @if (cardable() && activeView() === 'cards') {\n <div\n class=\"kf-data-table__cards\"\n aria-busy=\"true\"\n [style.--kf-data-table-card-min]=\"cardMinWidth()\"\n >\n @for (i of skeletonRowIndexes(); track i) {\n <div class=\"kf-data-table__card\">\n <kf-skeleton variant=\"rect\" height=\"120px\" />\n </div>\n }\n </div>\n } @else {\n <div class=\"kf-data-table__scroll\" aria-busy=\"true\">\n <table class=\"kf-data-table__table\">\n <thead>\n <tr>\n @for (column of columns(); track column.key) {\n <th\n class=\"kf-overline-text\"\n [class]=\"column.className\"\n [class.kf-data-table__cell--right]=\"isEndAligned(column)\"\n [style.width]=\"column.width\"\n >\n @if (column.hideLabel) {\n <span class=\"kf-data-table__sr-only\">{{ column.label }}</span>\n } @else {\n {{ column.label }}\n }\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @for (i of skeletonRowIndexes(); track i) {\n <tr class=\"kf-data-table__row\">\n @for (column of columns(); track column.key) {\n <td\n [class]=\"column.className\"\n [class.kf-data-table__cell--right]=\"isEndAligned(column)\"\n >\n <kf-skeleton />\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n } @else if (rows().length === 0) {\n <div class=\"kf-data-table__empty\">\n <ng-content select=\"[empty]\" />\n </div>\n } @else if (activeView() === 'table') {\n <div\n #scroll\n class=\"kf-data-table__scroll\"\n [class.kf-data-table__scroll--fade-left]=\"canScrollLeft()\"\n [class.kf-data-table__scroll--fade-right]=\"canScrollRight()\"\n [class.kf-data-table__scroll--weighted]=\"weighted()\"\n [attr.tabindex]=\"hasOverflow() ? 0 : null\"\n [attr.role]=\"hasOverflow() ? 'region' : null\"\n [attr.aria-label]=\"hasOverflow() ? ariaLabel() : null\"\n >\n <table class=\"kf-data-table__table\" [class.kf-data-table__table--weighted]=\"weighted()\">\n <thead>\n <tr>\n @for (column of columns(); track column.key) {\n <th\n class=\"kf-overline-text\"\n [attr.data-column-key]=\"column.key\"\n [class]=\"column.className\"\n [class.kf-data-table__cell--right]=\"isEndAligned(column)\"\n [class.kf-data-table__cell--center]=\"column.align === 'center'\"\n [class.kf-data-table__cell--truncate]=\"column.truncate\"\n [class.kf-data-table__cell--sortable]=\"column.sortable\"\n [class.kf-data-table__cell--sticky]=\"column.sticky\"\n [class.kf-data-table__cell--sort-active]=\"sortDirectionFor(column) !== null\"\n [style]=\"stickyStyle(column)\"\n [style.min-width]=\"minWidthFor(column)\"\n [style.width]=\"widthFor(column)\"\n [style.max-width]=\"maxWidthFor(column)\"\n (click)=\"toggleSort(column)\"\n >\n <span class=\"kf-data-table__th-content\">\n @if (column.hideLabel) {\n <span class=\"kf-data-table__sr-only\">{{ column.label }}</span>\n } @else {\n {{ column.label }}\n }\n @if (column.sortable) {\n <kf-icon\n name=\"chevron-down\"\n size=\"sm\"\n [rotate]=\"sortDirectionFor(column) === 'asc' ? 180 : 0\"\n class=\"kf-data-table__sort-icon\"\n [class.kf-data-table__sort-icon--active]=\"sortDirectionFor(column) !== null\"\n />\n }\n </span>\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @for (row of displayedRows(); track trackRow(row, $index); let i = $index) {\n <tr\n class=\"kf-data-table__row\"\n [class.kf-data-table__row--interactive]=\"rowInteractive()\"\n [class.kf-data-table__row--current]=\"isCurrentRow(row, i)\"\n [attr.aria-current]=\"isCurrentRow(row, i) ? 'true' : null\"\n [attr.tabindex]=\"rowInteractive() ? 0 : null\"\n (click)=\"onRowClick(row, i, $event)\"\n (keydown.enter)=\"onRowClick(row, i, $event)\"\n (keydown.space)=\"onRowSpace($event, row, i)\"\n >\n @for (column of columns(); track column.key) {\n <td\n [attr.data-column-key]=\"column.key\"\n [class]=\"column.className\"\n [class.kf-data-table__cell--right]=\"isEndAligned(column)\"\n [class.kf-data-table__cell--center]=\"column.align === 'center'\"\n [class.kf-data-table__cell--truncate]=\"column.truncate\"\n [class.kf-data-table__cell--mono]=\"column.mono\"\n [class.kf-data-table__cell--emphasis]=\"column.emphasis\"\n [class.kf-data-table__cell--sticky]=\"column.sticky\"\n [style]=\"stickyStyle(column)\"\n [style.min-width]=\"minWidthFor(column)\"\n [style.width]=\"widthFor(column)\"\n [style.max-width]=\"maxWidthFor(column)\"\n >\n @if (column.truncate) {\n <span class=\"kf-data-table__truncate\" [attr.title]=\"truncateTitle(row, column)\">\n <ng-container\n [ngTemplateOutlet]=\"tableCell\"\n [ngTemplateOutletContext]=\"{ $implicit: row, column: column, index: i }\"\n />\n </span>\n } @else {\n <ng-container\n [ngTemplateOutlet]=\"tableCell\"\n [ngTemplateOutletContext]=\"{ $implicit: row, column: column, index: i }\"\n />\n }\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n } @else {\n <div class=\"kf-data-table__cards\" [style.--kf-data-table-card-min]=\"cardMinWidth()\">\n @for (row of displayedRows(); track trackRow(row, $index); let i = $index) {\n <div\n class=\"kf-data-table__card\"\n [class.kf-data-table__card--interactive]=\"cardInteractive()\"\n [class.kf-data-table__card--current]=\"isCurrentRow(row, i)\"\n [attr.aria-current]=\"isCurrentRow(row, i) ? 'true' : null\"\n [attr.role]=\"cardInteractive() ? 'button' : null\"\n [attr.tabindex]=\"cardInteractive() ? 0 : null\"\n (click)=\"cardInteractive() && onRowClick(row, i, $event)\"\n (keydown.enter)=\"cardInteractive() && onRowClick(row, i, $event)\"\n (keydown.space)=\"cardInteractive() && onRowSpace($event, row, i)\"\n >\n @if (cardMediaColumn() || cardTitleColumn() || cardStatusColumns().length) {\n <div class=\"kf-data-table__card-header\">\n @if (cardMediaColumn(); as media) {\n <div class=\"kf-data-table__card-media\">\n @if (cellTemplates()?.[media.key]; as template) {\n <ng-container\n [ngTemplateOutlet]=\"template\"\n [ngTemplateOutletContext]=\"{ $implicit: row, index: i }\"\n />\n } @else {\n <img [src]=\"cellValue(row, media)\" alt=\"\" />\n }\n </div>\n }\n <div class=\"kf-data-table__card-heading\">\n @if (cardTitleColumn(); as title) {\n <span\n class=\"kf-data-table__card-title\"\n [class.kf-data-table__card-title--mono]=\"title.mono\"\n >\n <ng-container\n [ngTemplateOutlet]=\"cardCell\"\n [ngTemplateOutletContext]=\"{ $implicit: row, column: title, index: i }\"\n />\n </span>\n }\n @if (cardStatusColumns().length) {\n <span class=\"kf-data-table__card-statuses\">\n @for (column of cardStatusColumns(); track column.key) {\n <ng-container\n [ngTemplateOutlet]=\"cardCell\"\n [ngTemplateOutletContext]=\"{ $implicit: row, column: column, index: i }\"\n />\n }\n </span>\n }\n </div>\n </div>\n }\n\n @if (cardLeadColumns().length || cardFieldColumns().length) {\n <div class=\"kf-data-table__card-fields\">\n @for (column of cardLeadColumns(); track column.key) {\n <div\n class=\"kf-data-table__card-lead\"\n [style.--kf-data-table-card-clamp]=\"column.cardClamp\"\n [attr.title]=\"cellValue(row, column) || null\"\n >\n <span class=\"kf-data-table__sr-only\">{{ column.label }}: </span>\n <ng-container\n [ngTemplateOutlet]=\"cardCell\"\n [ngTemplateOutletContext]=\"{ $implicit: row, column: column, index: i }\"\n />\n </div>\n }\n @for (column of cardFieldColumns(); track column.key) {\n <div class=\"kf-data-table__card-field\">\n <span class=\"kf-data-table__card-label kf-overline-text\">{{ column.label }}</span>\n <span\n class=\"kf-data-table__card-value\"\n [class.kf-data-table__card-value--emphasis]=\"column.emphasis\"\n >\n <ng-container\n [ngTemplateOutlet]=\"cardCell\"\n [ngTemplateOutletContext]=\"{ $implicit: row, column: column, index: i }\"\n />\n </span>\n </div>\n }\n </div>\n }\n\n @if (cardMetaColumns().length) {\n <div class=\"kf-data-table__card-meta\">\n @for (column of cardMetaColumns(); track column.key) {\n <div\n class=\"kf-data-table__card-meta-item\"\n [class.kf-data-table__card-meta-item--end]=\"\n isEndAligned(column) || column.emphasis\n \"\n [class.kf-data-table__card-meta-item--emphasis]=\"column.emphasis\"\n [style.flex-basis]=\"column.width\"\n >\n <span class=\"kf-data-table__card-label kf-overline-text\">{{ column.label }}</span>\n <span class=\"kf-data-table__card-meta-value\">\n <ng-container\n [ngTemplateOutlet]=\"cardCell\"\n [ngTemplateOutletContext]=\"{ $implicit: row, column: column, index: i }\"\n />\n </span>\n </div>\n }\n </div>\n }\n\n @if (cardActionColumns().length) {\n <div class=\"kf-data-table__card-actions\">\n @for (column of cardActionColumns(); track column.key) {\n <ng-container\n [ngTemplateOutlet]=\"cardCell\"\n [ngTemplateOutletContext]=\"{ $implicit: row, column: column, index: i }\"\n />\n }\n </div>\n }\n </div>\n }\n </div>\n }\n\n @if (!loading() && rows().length > 0) {\n <div class=\"kf-data-table__footer\">\n @if (effectivePageSize()) {\n <kf-pagination\n [page]=\"page()\"\n [pages]=\"totalPages()\"\n [pageSizeOptions]=\"pageSizeOptions()\"\n [pageSize]=\"effectivePageSize()\"\n (pageChange)=\"goToPage($event)\"\n (pageSizeChange)=\"setPageSize($event)\"\n >\n {{ paginationSummary() }}\n </kf-pagination>\n }\n <ng-content select=\"[footer]\" />\n </div>\n }\n</div>\n\n<!-- Contenido de una celda de la vista tabla (template del consumidor,\n thumbnail o texto) \u2014 compartido por la celda normal y la `truncate`. -->\n<ng-template #tableCell let-row let-column=\"column\" let-i=\"index\">\n @if (cellTemplates()?.[column.key]; as template) {\n <ng-container\n [ngTemplateOutlet]=\"template\"\n [ngTemplateOutletContext]=\"{ $implicit: row, index: i }\"\n />\n } @else if (column.cardRole === 'media') {\n <img class=\"kf-data-table__thumb\" [src]=\"cellValue(row, column)\" alt=\"\" />\n } @else {\n {{ cellValue(row, column) }}\n }\n</ng-template>\n\n<!-- Contenido de una zona de la tarjeta: template del consumidor o el valor como texto. -->\n<ng-template #cardCell let-row let-column=\"column\" let-i=\"index\">\n @if (cellTemplates()?.[column.key]; as template) {\n <ng-container\n [ngTemplateOutlet]=\"template\"\n [ngTemplateOutletContext]=\"{ $implicit: row, index: i }\"\n />\n } @else {\n {{ cellValue(row, column) }}\n }\n</ng-template>\n", styles: [".kf-overline-text{font-family:var(--kf-font-title);font-size:var(--kf-font-size-caption);font-weight:var(--kf-font-weight-medium);letter-spacing:.04em;text-transform:uppercase;color:var(--kf-color-text-muted)}:host{display:block;font-family:var(--kf-font-body)}.kf-data-table{background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md);box-shadow:var(--kf-shadow-sm);overflow:hidden}.kf-data-table--flush{border:none;box-shadow:none;border-radius:0}.kf-data-table__toolbar{display:flex;justify-content:flex-end;padding:var(--kf-space-sm) var(--kf-space-md);border-bottom:1px solid var(--kf-color-border)}.kf-data-table__scroll{position:relative;overflow-x:auto}.kf-data-table__scroll--weighted{container-type:inline-size}.kf-data-table__scroll:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--kf-color-primary)}.kf-data-table__scroll:before,.kf-data-table__scroll:after{content:\"\";position:absolute;top:0;bottom:0;width:24px;pointer-events:none;opacity:0;transition:opacity .14s}.kf-data-table__scroll:before{left:0;background:linear-gradient(to right,var(--kf-color-white),transparent)}.kf-data-table__scroll:after{right:0;background:linear-gradient(to left,var(--kf-color-white),transparent)}.kf-data-table__scroll--fade-left:before{opacity:1}.kf-data-table__scroll--fade-right:after{opacity:1}.kf-data-table__table{width:100%;border-collapse:collapse;text-align:left}.kf-data-table__table th,.kf-data-table__table td{padding:var(--kf-space-sm) var(--kf-space-md);border-bottom:1px solid var(--kf-color-border);white-space:nowrap}.kf-data-table--compact .kf-data-table__table th,.kf-data-table--compact .kf-data-table__table td{padding:var(--kf-space-xs) var(--kf-space-md)}.kf-data-table__table th{background:var(--kf-color-surface)}.kf-data-table__table td{color:var(--kf-color-text)}.kf-data-table__table tbody tr:last-child td{border-bottom:none}.kf-data-table__row{transition:background .14s}.kf-data-table__row--interactive{cursor:pointer}.kf-data-table__row--interactive:hover{background:var(--kf-color-primary-soft)}.kf-data-table__row--interactive:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--kf-color-primary)}.kf-data-table__row--current{background:var(--kf-color-primary-soft)}.kf-data-table__cell--right{text-align:right}.kf-data-table__cell--center{text-align:center}.kf-data-table__table .kf-data-table__cell--truncate{max-width:0}.kf-data-table__truncate{display:block;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kf-data-table__table td.kf-data-table__cell--mono{font-family:var(--kf-font-mono);font-size:var(--kf-font-size-body-sm)}.kf-data-table__table td.kf-data-table__cell--emphasis{font-weight:700;color:var(--kf-color-ink);font-variant-numeric:tabular-nums}.kf-data-table__sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}.kf-data-table__cell--sticky{background:var(--kf-color-white)}.kf-data-table__table th.kf-data-table__cell--sticky{background:var(--kf-color-surface);z-index:2}.kf-data-table__cell--sortable{cursor:pointer;-webkit-user-select:none;user-select:none}.kf-data-table__cell--sortable:hover{color:var(--kf-color-ink)}.kf-data-table__table th.kf-data-table__cell--sort-active{color:var(--kf-color-primary);font-weight:700}.kf-data-table__th-content{display:inline-flex;align-items:center;gap:var(--kf-space-xs)}.kf-data-table__sort-icon{transition:opacity .14s}.kf-data-table__cell--sortable:hover .kf-data-table__sort-icon{opacity:.5}.kf-data-table__sort-icon--active,.kf-data-table__cell--sortable:hover .kf-data-table__sort-icon--active{opacity:1}.kf-data-table__empty{padding:var(--kf-space-md)}.kf-data-table__footer{padding:var(--kf-space-sm) var(--kf-space-md);border-top:1px solid var(--kf-color-border)}.kf-data-table__footer:empty{display:none}.kf-data-table__cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(var(--kf-data-table-card-min, 220px),100%),1fr));grid-auto-rows:1fr;gap:var(--kf-space-md);padding:var(--kf-space-md)}.kf-data-table__card{position:relative;display:flex;flex-direction:column;min-width:0;border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);background:var(--kf-color-white);overflow:hidden;transition:border-color .14s,box-shadow .14s,background .14s}.kf-data-table__card--interactive{cursor:pointer}.kf-data-table__card:hover{border-color:var(--kf-color-border-strong);box-shadow:var(--kf-shadow-xs)}.kf-data-table__card--interactive:hover{background:var(--kf-color-primary-soft)}.kf-data-table__card--interactive:focus-visible{outline:2px solid transparent;outline-offset:2px;box-shadow:0 0 0 2px var(--kf-color-primary)}.kf-data-table__card--current{border-color:var(--kf-color-primary);background:var(--kf-color-primary-soft)}.kf-data-table__card-header{display:flex;align-items:center;gap:var(--kf-space-sm);min-width:0;padding:var(--kf-space-md) var(--kf-space-md) var(--kf-space-sm)}.kf-data-table__card-media{flex:none}.kf-data-table__card-media img,.kf-data-table__thumb{display:block;width:48px;height:48px;border-radius:var(--kf-radius-xs);object-fit:cover;background:var(--kf-color-surface)}.kf-data-table__card-heading{display:flex;flex:1;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);min-width:0}.kf-data-table__card-title{flex:0 1 auto;min-width:0;font-family:var(--kf-font-title);font-weight:600;color:var(--kf-color-ink);overflow-wrap:anywhere}.kf-data-table__card-title--mono{flex:none;font-family:var(--kf-font-mono);font-size:var(--kf-font-size-body-sm);white-space:nowrap}.kf-data-table__card-statuses{display:flex;flex:0 1 auto;flex-wrap:nowrap;justify-content:flex-end;gap:var(--kf-space-xs);min-width:0;overflow:hidden}.kf-data-table__card-actions{display:flex;flex-wrap:wrap;gap:var(--kf-space-xs)}.kf-data-table__card-fields{display:flex;flex-direction:column;gap:var(--kf-space-xs);min-width:0;padding:var(--kf-space-md)}.kf-data-table__card-header+.kf-data-table__card-fields{padding-top:0}.kf-data-table__card-lead{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:var(--kf-data-table-card-clamp, 2);line-clamp:var(--kf-data-table-card-clamp, 2);min-width:0;overflow:hidden;overflow-wrap:anywhere;color:var(--kf-color-ink);font-weight:500;line-height:1.35}.kf-data-table__card-field{display:flex;justify-content:space-between;gap:var(--kf-space-sm);min-width:0}.kf-data-table__card-label{flex:none}.kf-data-table__card-value{min-width:0;color:var(--kf-color-text);text-align:right;overflow-wrap:anywhere}.kf-data-table__card-value--emphasis,.kf-data-table__card-meta-item--emphasis .kf-data-table__card-meta-value{font-weight:700;color:var(--kf-color-ink);font-variant-numeric:tabular-nums}.kf-data-table__card-meta{display:flex;flex-wrap:wrap;align-items:flex-end;justify-content:space-between;gap:var(--kf-space-xs) var(--kf-space-md);margin-top:auto;padding:var(--kf-space-sm) var(--kf-space-md) var(--kf-space-md);border-top:1px solid var(--kf-color-border)}.kf-data-table__card-meta-item{display:flex;flex-direction:column;gap:2px;min-width:0}.kf-data-table__card-meta-item--end{margin-left:auto;align-items:flex-end;text-align:right}.kf-data-table__card-meta-value{color:var(--kf-color-text);font-variant-numeric:tabular-nums}.kf-data-table__card-meta-item--emphasis .kf-data-table__card-meta-value{font-size:var(--kf-font-size-body)}.kf-data-table__card-actions{justify-content:flex-end;margin-top:auto;padding:var(--kf-space-sm) var(--kf-space-md);border-top:1px solid var(--kf-color-border);background:var(--kf-color-surface)}.kf-data-table__card-meta~.kf-data-table__card-actions{margin-top:0}.kf-data-table__thumb{width:32px;height:32px}@media(prefers-reduced-motion:reduce){.kf-data-table__card,.kf-data-table__row{transition:none}}\n"], dependencies: [{ kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size", "rotate", "filled"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: Pagination, selector: "kf-pagination", inputs: ["page", "pages", "pageSizeOptions", "pageSize"], outputs: ["pageChange", "pageSizeChange"] }, { kind: "component", type: Skeleton, selector: "kf-skeleton", inputs: ["variant", "width", "height"] }, { kind: "component", type: ViewToggle, selector: "kf-view-toggle", inputs: ["view", "iconOnly", "tableLabel", "cardsLabel", "ariaLabel"], outputs: ["viewChange"] }] });
|
|
3396
3994
|
}
|
|
3397
3995
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DataTable, decorators: [{
|
|
3398
3996
|
type: Component,
|
|
3399
|
-
args: [{ selector: 'kf-data-table', imports: [Icon, NgTemplateOutlet, Pagination, Skeleton, ViewToggle], template: "<div\n class=\"kf-data-table\"\n [class.kf-data-table--flush]=\"flush()\"\n [class.kf-data-table--compact]=\"density() === 'compact'\"\n>\n @if (cardable()) {\n <div class=\"kf-data-table__toolbar\">\n <kf-view-toggle [view]=\"view()\" (viewChange)=\"setView($event)\" />\n </div>\n }\n\n @if (loading()) {\n @if (cardable() && view() === 'cards') {\n <div class=\"kf-data-table__cards\" aria-busy=\"true\">\n @for (i of skeletonRowIndexes(); track i) {\n <div class=\"kf-data-table__card\">\n <kf-skeleton variant=\"rect\" height=\"120px\" />\n </div>\n }\n </div>\n } @else {\n <div class=\"kf-data-table__scroll\" aria-busy=\"true\">\n <table class=\"kf-data-table__table\">\n <thead>\n <tr>\n @for (column of columns(); track column.key) {\n <th\n class=\"kf-overline-text\"\n [class]=\"column.className\"\n [class.kf-data-table__cell--right]=\"column.right\"\n >\n {{ column.label }}\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @for (i of skeletonRowIndexes(); track i) {\n <tr class=\"kf-data-table__row\">\n @for (column of columns(); track column.key) {\n <td [class]=\"column.className\" [class.kf-data-table__cell--right]=\"column.right\">\n <kf-skeleton />\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n } @else if (rows().length === 0) {\n <div class=\"kf-data-table__empty\">\n <ng-content select=\"[empty]\" />\n </div>\n } @else if (view() === 'table') {\n <div\n #scroll\n class=\"kf-data-table__scroll\"\n [class.kf-data-table__scroll--fade-left]=\"canScrollLeft()\"\n [class.kf-data-table__scroll--fade-right]=\"canScrollRight()\"\n >\n <table class=\"kf-data-table__table\">\n <thead>\n <tr>\n @for (column of columns(); track column.key) {\n <th\n class=\"kf-overline-text\"\n [attr.data-column-key]=\"column.key\"\n [class]=\"column.className\"\n [class.kf-data-table__cell--right]=\"column.right\"\n [class.kf-data-table__cell--sortable]=\"column.sortable\"\n [class.kf-data-table__cell--sticky]=\"column.sticky\"\n [class.kf-data-table__cell--sort-active]=\"sortDirectionFor(column) !== null\"\n [style]=\"stickyStyle(column)\"\n [style.min-width]=\"column.minWidth\"\n (click)=\"toggleSort(column)\"\n >\n <span class=\"kf-data-table__th-content\">\n {{ column.label }}\n @if (column.sortable) {\n <kf-icon\n name=\"chevron-down\"\n size=\"sm\"\n [rotate]=\"sortDirectionFor(column) === 'asc' ? 180 : 0\"\n class=\"kf-data-table__sort-icon\"\n [class.kf-data-table__sort-icon--active]=\"sortDirectionFor(column) !== null\"\n />\n }\n </span>\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @for (row of displayedRows(); track trackRow(row, $index); let i = $index) {\n <tr\n class=\"kf-data-table__row\"\n [class.kf-data-table__row--interactive]=\"rowInteractive()\"\n [attr.tabindex]=\"rowInteractive() ? 0 : null\"\n (click)=\"onRowClick(row, i)\"\n (keydown.enter)=\"onRowClick(row, i)\"\n >\n @for (column of columns(); track column.key) {\n <td\n [attr.data-column-key]=\"column.key\"\n [class]=\"column.className\"\n [class.kf-data-table__cell--right]=\"column.right\"\n [class.kf-data-table__cell--sticky]=\"column.sticky\"\n [style]=\"stickyStyle(column)\"\n [style.min-width]=\"column.minWidth\"\n >\n @if (cellTemplates()?.[column.key]; as template) {\n <ng-container\n [ngTemplateOutlet]=\"template\"\n [ngTemplateOutletContext]=\"{ $implicit: row, index: i }\"\n />\n } @else if (column.cardRole === 'media') {\n <img class=\"kf-data-table__thumb\" [src]=\"cellValue(row, column)\" alt=\"\" />\n } @else {\n {{ cellValue(row, column) }}\n }\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n } @else {\n <div class=\"kf-data-table__cards\">\n @for (row of displayedRows(); track trackRow(row, $index); let i = $index) {\n <div\n class=\"kf-data-table__card\"\n [class.kf-data-table__card--interactive]=\"cardInteractive()\"\n [attr.role]=\"cardInteractive() ? 'button' : null\"\n [attr.tabindex]=\"cardInteractive() ? 0 : null\"\n (click)=\"cardInteractive() && onRowClick(row, i)\"\n (keydown.enter)=\"cardInteractive() && onRowClick(row, i)\"\n >\n @if (cardMediaColumn() || cardTitleColumn() || cardStatusColumns().length) {\n <div class=\"kf-data-table__card-header\">\n @if (cardMediaColumn(); as media) {\n <div class=\"kf-data-table__card-media\">\n @if (cellTemplates()?.[media.key]; as template) {\n <ng-container\n [ngTemplateOutlet]=\"template\"\n [ngTemplateOutletContext]=\"{ $implicit: row, index: i }\"\n />\n } @else {\n <img [src]=\"cellValue(row, media)\" alt=\"\" />\n }\n </div>\n }\n <div class=\"kf-data-table__card-heading\">\n @if (cardTitleColumn(); as title) {\n <span class=\"kf-data-table__card-title\">\n @if (cellTemplates()?.[title.key]; as template) {\n <ng-container\n [ngTemplateOutlet]=\"template\"\n [ngTemplateOutletContext]=\"{ $implicit: row, index: i }\"\n />\n } @else {\n {{ cellValue(row, title) }}\n }\n </span>\n }\n @if (cardStatusColumns().length) {\n <span class=\"kf-data-table__card-statuses\">\n @for (column of cardStatusColumns(); track column.key) {\n @if (cellTemplates()?.[column.key]; as template) {\n <ng-container\n [ngTemplateOutlet]=\"template\"\n [ngTemplateOutletContext]=\"{ $implicit: row, index: i }\"\n />\n } @else {\n {{ cellValue(row, column) }}\n }\n }\n </span>\n }\n </div>\n </div>\n }\n\n @if (cardFieldColumns().length) {\n <div class=\"kf-data-table__card-fields\">\n @for (column of cardFieldColumns(); track column.key) {\n <div class=\"kf-data-table__card-field\">\n <span class=\"kf-data-table__card-label kf-overline-text\">{{ column.label }}</span>\n <span class=\"kf-data-table__card-value\">\n @if (cellTemplates()?.[column.key]; as template) {\n <ng-container\n [ngTemplateOutlet]=\"template\"\n [ngTemplateOutletContext]=\"{ $implicit: row, index: i }\"\n />\n } @else {\n {{ cellValue(row, column) }}\n }\n </span>\n </div>\n }\n </div>\n }\n\n @if (cardActionColumns().length) {\n <div class=\"kf-data-table__card-actions\">\n @for (column of cardActionColumns(); track column.key) {\n @if (cellTemplates()?.[column.key]; as template) {\n <ng-container\n [ngTemplateOutlet]=\"template\"\n [ngTemplateOutletContext]=\"{ $implicit: row, index: i }\"\n />\n } @else {\n {{ cellValue(row, column) }}\n }\n }\n </div>\n }\n </div>\n }\n </div>\n }\n\n @if (!loading() && rows().length > 0) {\n <div class=\"kf-data-table__footer\">\n @if (effectivePageSize()) {\n <kf-pagination\n [page]=\"page()\"\n [pages]=\"totalPages()\"\n [pageSizeOptions]=\"pageSizeOptions()\"\n [pageSize]=\"effectivePageSize()\"\n (pageChange)=\"goToPage($event)\"\n (pageSizeChange)=\"setPageSize($event)\"\n >\n {{ paginationSummary() }}\n </kf-pagination>\n }\n <ng-content select=\"[footer]\" />\n </div>\n }\n</div>\n", styles: [".kf-overline-text{font-family:var(--kf-font-title);font-size:var(--kf-font-size-caption);font-weight:var(--kf-font-weight-medium);letter-spacing:.04em;text-transform:uppercase;color:var(--kf-color-text-muted)}:host{display:block;font-family:var(--kf-font-body)}.kf-data-table{background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md);box-shadow:var(--kf-shadow-sm);overflow:hidden}.kf-data-table--flush{border:none;box-shadow:none;border-radius:0}.kf-data-table__toolbar{display:flex;justify-content:flex-end;padding:var(--kf-space-sm) var(--kf-space-md);border-bottom:1px solid var(--kf-color-border)}.kf-data-table__scroll{position:relative;overflow-x:auto}.kf-data-table__scroll:before,.kf-data-table__scroll:after{content:\"\";position:absolute;top:0;bottom:0;width:24px;pointer-events:none;opacity:0;transition:opacity .14s}.kf-data-table__scroll:before{left:0;background:linear-gradient(to right,var(--kf-color-white),transparent)}.kf-data-table__scroll:after{right:0;background:linear-gradient(to left,var(--kf-color-white),transparent)}.kf-data-table__scroll--fade-left:before{opacity:1}.kf-data-table__scroll--fade-right:after{opacity:1}.kf-data-table__table{width:100%;border-collapse:collapse;text-align:left}.kf-data-table__table th,.kf-data-table__table td{padding:var(--kf-space-sm) var(--kf-space-md);border-bottom:1px solid var(--kf-color-border);white-space:nowrap}.kf-data-table--compact .kf-data-table__table th,.kf-data-table--compact .kf-data-table__table td{padding:var(--kf-space-xs) var(--kf-space-md)}.kf-data-table__table th{background:var(--kf-color-surface)}.kf-data-table__table td{color:var(--kf-color-text)}.kf-data-table__table tbody tr:last-child td{border-bottom:none}.kf-data-table__row{transition:background .14s}.kf-data-table__row--interactive{cursor:pointer}.kf-data-table__row--interactive:hover{background:var(--kf-color-primary-soft)}.kf-data-table__row--interactive:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--kf-color-primary)}.kf-data-table__cell--right{text-align:right}.kf-data-table__cell--sticky{background:var(--kf-color-white)}.kf-data-table__table th.kf-data-table__cell--sticky{background:var(--kf-color-surface);z-index:2}.kf-data-table__cell--sortable{cursor:pointer;-webkit-user-select:none;user-select:none}.kf-data-table__cell--sortable:hover{color:var(--kf-color-ink)}.kf-data-table__table th.kf-data-table__cell--sort-active{color:var(--kf-color-primary);font-weight:700}.kf-data-table__th-content{display:inline-flex;align-items:center;gap:var(--kf-space-xs)}.kf-data-table__sort-icon{transition:opacity .14s}.kf-data-table__cell--sortable:hover .kf-data-table__sort-icon{opacity:.5}.kf-data-table__sort-icon--active,.kf-data-table__cell--sortable:hover .kf-data-table__sort-icon--active{opacity:1}.kf-data-table__empty{padding:var(--kf-space-md)}.kf-data-table__footer{padding:var(--kf-space-sm) var(--kf-space-md);border-top:1px solid var(--kf-color-border)}.kf-data-table__footer:empty{display:none}.kf-data-table__cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(220px,1fr));gap:var(--kf-space-md);padding:var(--kf-space-md)}.kf-data-table__card{display:flex;flex-direction:column;border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);overflow:hidden;transition:border-color .14s,box-shadow .14s}.kf-data-table__card--interactive{cursor:pointer}.kf-data-table__card:hover{border-color:var(--kf-color-border-strong);box-shadow:var(--kf-shadow-xs)}.kf-data-table__card--interactive:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-data-table__card-header{display:flex;align-items:center;gap:var(--kf-space-sm);padding:var(--kf-space-md)}.kf-data-table__card-media{flex:none}.kf-data-table__card-media img,.kf-data-table__thumb{display:block;width:48px;height:48px;border-radius:var(--kf-radius-xs);object-fit:cover;background:var(--kf-color-surface)}.kf-data-table__card-heading{display:flex;flex-direction:column;gap:var(--kf-space-xs);min-width:0}.kf-data-table__card-title{font-family:var(--kf-font-title);font-weight:600;color:var(--kf-color-ink);overflow-wrap:anywhere}.kf-data-table__card-statuses,.kf-data-table__card-actions{display:flex;flex-wrap:wrap;gap:var(--kf-space-xs)}.kf-data-table__card-fields{display:flex;flex-direction:column;gap:var(--kf-space-xs);padding:var(--kf-space-md)}.kf-data-table__card-header+.kf-data-table__card-fields{padding-top:0}.kf-data-table__card-field{display:flex;justify-content:space-between;gap:var(--kf-space-sm)}.kf-data-table__card-actions{justify-content:flex-end;margin-top:auto;padding:var(--kf-space-sm) var(--kf-space-md);border-top:1px solid var(--kf-color-border);background:var(--kf-color-surface)}.kf-data-table__thumb{width:32px;height:32px}.kf-data-table__card-value{color:var(--kf-color-text);text-align:right}\n"] }]
|
|
3400
|
-
}], ctorParameters: () => [], propDecorators: { columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: true }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], scrollEl: [{ type: i0.ViewChild, args: ['scroll', { isSignal: true }] }], rowKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowKey", required: false }] }], flush: [{ type: i0.Input, args: [{ isSignal: true, alias: "flush", required: false }] }], cardable: [{ type: i0.Input, args: [{ isSignal: true, alias: "cardable", required: false }] }], defaultView: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultView", required: false }] }], render: [{ type: i0.Input, args: [{ isSignal: true, alias: "render", required: false }] }], cellTemplates: [{ type: i0.Input, args: [{ isSignal: true, alias: "cellTemplates", required: false }] }], density: [{ type: i0.Input, args: [{ isSignal: true, alias: "density", required: false }] }], rowClickable: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowClickable", required: false }] }], rowClick: [{ type: i0.Output, args: ["rowClick"] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }], pageSizeOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSizeOptions", required: false }] }],
|
|
3997
|
+
args: [{ selector: 'kf-data-table', imports: [Icon, NgTemplateOutlet, Pagination, Skeleton, ViewToggle], template: "<div\n class=\"kf-data-table\"\n [class.kf-data-table--flush]=\"flush()\"\n [class.kf-data-table--compact]=\"density() === 'compact'\"\n>\n @if (cardable() && showViewToggle()) {\n <div class=\"kf-data-table__toolbar\">\n <kf-view-toggle [view]=\"activeView()\" (viewChange)=\"setView($event)\" />\n </div>\n }\n\n @if (loading()) {\n @if (cardable() && activeView() === 'cards') {\n <div\n class=\"kf-data-table__cards\"\n aria-busy=\"true\"\n [style.--kf-data-table-card-min]=\"cardMinWidth()\"\n >\n @for (i of skeletonRowIndexes(); track i) {\n <div class=\"kf-data-table__card\">\n <kf-skeleton variant=\"rect\" height=\"120px\" />\n </div>\n }\n </div>\n } @else {\n <div class=\"kf-data-table__scroll\" aria-busy=\"true\">\n <table class=\"kf-data-table__table\">\n <thead>\n <tr>\n @for (column of columns(); track column.key) {\n <th\n class=\"kf-overline-text\"\n [class]=\"column.className\"\n [class.kf-data-table__cell--right]=\"isEndAligned(column)\"\n [style.width]=\"column.width\"\n >\n @if (column.hideLabel) {\n <span class=\"kf-data-table__sr-only\">{{ column.label }}</span>\n } @else {\n {{ column.label }}\n }\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @for (i of skeletonRowIndexes(); track i) {\n <tr class=\"kf-data-table__row\">\n @for (column of columns(); track column.key) {\n <td\n [class]=\"column.className\"\n [class.kf-data-table__cell--right]=\"isEndAligned(column)\"\n >\n <kf-skeleton />\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n }\n } @else if (rows().length === 0) {\n <div class=\"kf-data-table__empty\">\n <ng-content select=\"[empty]\" />\n </div>\n } @else if (activeView() === 'table') {\n <div\n #scroll\n class=\"kf-data-table__scroll\"\n [class.kf-data-table__scroll--fade-left]=\"canScrollLeft()\"\n [class.kf-data-table__scroll--fade-right]=\"canScrollRight()\"\n [class.kf-data-table__scroll--weighted]=\"weighted()\"\n [attr.tabindex]=\"hasOverflow() ? 0 : null\"\n [attr.role]=\"hasOverflow() ? 'region' : null\"\n [attr.aria-label]=\"hasOverflow() ? ariaLabel() : null\"\n >\n <table class=\"kf-data-table__table\" [class.kf-data-table__table--weighted]=\"weighted()\">\n <thead>\n <tr>\n @for (column of columns(); track column.key) {\n <th\n class=\"kf-overline-text\"\n [attr.data-column-key]=\"column.key\"\n [class]=\"column.className\"\n [class.kf-data-table__cell--right]=\"isEndAligned(column)\"\n [class.kf-data-table__cell--center]=\"column.align === 'center'\"\n [class.kf-data-table__cell--truncate]=\"column.truncate\"\n [class.kf-data-table__cell--sortable]=\"column.sortable\"\n [class.kf-data-table__cell--sticky]=\"column.sticky\"\n [class.kf-data-table__cell--sort-active]=\"sortDirectionFor(column) !== null\"\n [style]=\"stickyStyle(column)\"\n [style.min-width]=\"minWidthFor(column)\"\n [style.width]=\"widthFor(column)\"\n [style.max-width]=\"maxWidthFor(column)\"\n (click)=\"toggleSort(column)\"\n >\n <span class=\"kf-data-table__th-content\">\n @if (column.hideLabel) {\n <span class=\"kf-data-table__sr-only\">{{ column.label }}</span>\n } @else {\n {{ column.label }}\n }\n @if (column.sortable) {\n <kf-icon\n name=\"chevron-down\"\n size=\"sm\"\n [rotate]=\"sortDirectionFor(column) === 'asc' ? 180 : 0\"\n class=\"kf-data-table__sort-icon\"\n [class.kf-data-table__sort-icon--active]=\"sortDirectionFor(column) !== null\"\n />\n }\n </span>\n </th>\n }\n </tr>\n </thead>\n <tbody>\n @for (row of displayedRows(); track trackRow(row, $index); let i = $index) {\n <tr\n class=\"kf-data-table__row\"\n [class.kf-data-table__row--interactive]=\"rowInteractive()\"\n [class.kf-data-table__row--current]=\"isCurrentRow(row, i)\"\n [attr.aria-current]=\"isCurrentRow(row, i) ? 'true' : null\"\n [attr.tabindex]=\"rowInteractive() ? 0 : null\"\n (click)=\"onRowClick(row, i, $event)\"\n (keydown.enter)=\"onRowClick(row, i, $event)\"\n (keydown.space)=\"onRowSpace($event, row, i)\"\n >\n @for (column of columns(); track column.key) {\n <td\n [attr.data-column-key]=\"column.key\"\n [class]=\"column.className\"\n [class.kf-data-table__cell--right]=\"isEndAligned(column)\"\n [class.kf-data-table__cell--center]=\"column.align === 'center'\"\n [class.kf-data-table__cell--truncate]=\"column.truncate\"\n [class.kf-data-table__cell--mono]=\"column.mono\"\n [class.kf-data-table__cell--emphasis]=\"column.emphasis\"\n [class.kf-data-table__cell--sticky]=\"column.sticky\"\n [style]=\"stickyStyle(column)\"\n [style.min-width]=\"minWidthFor(column)\"\n [style.width]=\"widthFor(column)\"\n [style.max-width]=\"maxWidthFor(column)\"\n >\n @if (column.truncate) {\n <span class=\"kf-data-table__truncate\" [attr.title]=\"truncateTitle(row, column)\">\n <ng-container\n [ngTemplateOutlet]=\"tableCell\"\n [ngTemplateOutletContext]=\"{ $implicit: row, column: column, index: i }\"\n />\n </span>\n } @else {\n <ng-container\n [ngTemplateOutlet]=\"tableCell\"\n [ngTemplateOutletContext]=\"{ $implicit: row, column: column, index: i }\"\n />\n }\n </td>\n }\n </tr>\n }\n </tbody>\n </table>\n </div>\n } @else {\n <div class=\"kf-data-table__cards\" [style.--kf-data-table-card-min]=\"cardMinWidth()\">\n @for (row of displayedRows(); track trackRow(row, $index); let i = $index) {\n <div\n class=\"kf-data-table__card\"\n [class.kf-data-table__card--interactive]=\"cardInteractive()\"\n [class.kf-data-table__card--current]=\"isCurrentRow(row, i)\"\n [attr.aria-current]=\"isCurrentRow(row, i) ? 'true' : null\"\n [attr.role]=\"cardInteractive() ? 'button' : null\"\n [attr.tabindex]=\"cardInteractive() ? 0 : null\"\n (click)=\"cardInteractive() && onRowClick(row, i, $event)\"\n (keydown.enter)=\"cardInteractive() && onRowClick(row, i, $event)\"\n (keydown.space)=\"cardInteractive() && onRowSpace($event, row, i)\"\n >\n @if (cardMediaColumn() || cardTitleColumn() || cardStatusColumns().length) {\n <div class=\"kf-data-table__card-header\">\n @if (cardMediaColumn(); as media) {\n <div class=\"kf-data-table__card-media\">\n @if (cellTemplates()?.[media.key]; as template) {\n <ng-container\n [ngTemplateOutlet]=\"template\"\n [ngTemplateOutletContext]=\"{ $implicit: row, index: i }\"\n />\n } @else {\n <img [src]=\"cellValue(row, media)\" alt=\"\" />\n }\n </div>\n }\n <div class=\"kf-data-table__card-heading\">\n @if (cardTitleColumn(); as title) {\n <span\n class=\"kf-data-table__card-title\"\n [class.kf-data-table__card-title--mono]=\"title.mono\"\n >\n <ng-container\n [ngTemplateOutlet]=\"cardCell\"\n [ngTemplateOutletContext]=\"{ $implicit: row, column: title, index: i }\"\n />\n </span>\n }\n @if (cardStatusColumns().length) {\n <span class=\"kf-data-table__card-statuses\">\n @for (column of cardStatusColumns(); track column.key) {\n <ng-container\n [ngTemplateOutlet]=\"cardCell\"\n [ngTemplateOutletContext]=\"{ $implicit: row, column: column, index: i }\"\n />\n }\n </span>\n }\n </div>\n </div>\n }\n\n @if (cardLeadColumns().length || cardFieldColumns().length) {\n <div class=\"kf-data-table__card-fields\">\n @for (column of cardLeadColumns(); track column.key) {\n <div\n class=\"kf-data-table__card-lead\"\n [style.--kf-data-table-card-clamp]=\"column.cardClamp\"\n [attr.title]=\"cellValue(row, column) || null\"\n >\n <span class=\"kf-data-table__sr-only\">{{ column.label }}: </span>\n <ng-container\n [ngTemplateOutlet]=\"cardCell\"\n [ngTemplateOutletContext]=\"{ $implicit: row, column: column, index: i }\"\n />\n </div>\n }\n @for (column of cardFieldColumns(); track column.key) {\n <div class=\"kf-data-table__card-field\">\n <span class=\"kf-data-table__card-label kf-overline-text\">{{ column.label }}</span>\n <span\n class=\"kf-data-table__card-value\"\n [class.kf-data-table__card-value--emphasis]=\"column.emphasis\"\n >\n <ng-container\n [ngTemplateOutlet]=\"cardCell\"\n [ngTemplateOutletContext]=\"{ $implicit: row, column: column, index: i }\"\n />\n </span>\n </div>\n }\n </div>\n }\n\n @if (cardMetaColumns().length) {\n <div class=\"kf-data-table__card-meta\">\n @for (column of cardMetaColumns(); track column.key) {\n <div\n class=\"kf-data-table__card-meta-item\"\n [class.kf-data-table__card-meta-item--end]=\"\n isEndAligned(column) || column.emphasis\n \"\n [class.kf-data-table__card-meta-item--emphasis]=\"column.emphasis\"\n [style.flex-basis]=\"column.width\"\n >\n <span class=\"kf-data-table__card-label kf-overline-text\">{{ column.label }}</span>\n <span class=\"kf-data-table__card-meta-value\">\n <ng-container\n [ngTemplateOutlet]=\"cardCell\"\n [ngTemplateOutletContext]=\"{ $implicit: row, column: column, index: i }\"\n />\n </span>\n </div>\n }\n </div>\n }\n\n @if (cardActionColumns().length) {\n <div class=\"kf-data-table__card-actions\">\n @for (column of cardActionColumns(); track column.key) {\n <ng-container\n [ngTemplateOutlet]=\"cardCell\"\n [ngTemplateOutletContext]=\"{ $implicit: row, column: column, index: i }\"\n />\n }\n </div>\n }\n </div>\n }\n </div>\n }\n\n @if (!loading() && rows().length > 0) {\n <div class=\"kf-data-table__footer\">\n @if (effectivePageSize()) {\n <kf-pagination\n [page]=\"page()\"\n [pages]=\"totalPages()\"\n [pageSizeOptions]=\"pageSizeOptions()\"\n [pageSize]=\"effectivePageSize()\"\n (pageChange)=\"goToPage($event)\"\n (pageSizeChange)=\"setPageSize($event)\"\n >\n {{ paginationSummary() }}\n </kf-pagination>\n }\n <ng-content select=\"[footer]\" />\n </div>\n }\n</div>\n\n<!-- Contenido de una celda de la vista tabla (template del consumidor,\n thumbnail o texto) \u2014 compartido por la celda normal y la `truncate`. -->\n<ng-template #tableCell let-row let-column=\"column\" let-i=\"index\">\n @if (cellTemplates()?.[column.key]; as template) {\n <ng-container\n [ngTemplateOutlet]=\"template\"\n [ngTemplateOutletContext]=\"{ $implicit: row, index: i }\"\n />\n } @else if (column.cardRole === 'media') {\n <img class=\"kf-data-table__thumb\" [src]=\"cellValue(row, column)\" alt=\"\" />\n } @else {\n {{ cellValue(row, column) }}\n }\n</ng-template>\n\n<!-- Contenido de una zona de la tarjeta: template del consumidor o el valor como texto. -->\n<ng-template #cardCell let-row let-column=\"column\" let-i=\"index\">\n @if (cellTemplates()?.[column.key]; as template) {\n <ng-container\n [ngTemplateOutlet]=\"template\"\n [ngTemplateOutletContext]=\"{ $implicit: row, index: i }\"\n />\n } @else {\n {{ cellValue(row, column) }}\n }\n</ng-template>\n", styles: [".kf-overline-text{font-family:var(--kf-font-title);font-size:var(--kf-font-size-caption);font-weight:var(--kf-font-weight-medium);letter-spacing:.04em;text-transform:uppercase;color:var(--kf-color-text-muted)}:host{display:block;font-family:var(--kf-font-body)}.kf-data-table{background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md);box-shadow:var(--kf-shadow-sm);overflow:hidden}.kf-data-table--flush{border:none;box-shadow:none;border-radius:0}.kf-data-table__toolbar{display:flex;justify-content:flex-end;padding:var(--kf-space-sm) var(--kf-space-md);border-bottom:1px solid var(--kf-color-border)}.kf-data-table__scroll{position:relative;overflow-x:auto}.kf-data-table__scroll--weighted{container-type:inline-size}.kf-data-table__scroll:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--kf-color-primary)}.kf-data-table__scroll:before,.kf-data-table__scroll:after{content:\"\";position:absolute;top:0;bottom:0;width:24px;pointer-events:none;opacity:0;transition:opacity .14s}.kf-data-table__scroll:before{left:0;background:linear-gradient(to right,var(--kf-color-white),transparent)}.kf-data-table__scroll:after{right:0;background:linear-gradient(to left,var(--kf-color-white),transparent)}.kf-data-table__scroll--fade-left:before{opacity:1}.kf-data-table__scroll--fade-right:after{opacity:1}.kf-data-table__table{width:100%;border-collapse:collapse;text-align:left}.kf-data-table__table th,.kf-data-table__table td{padding:var(--kf-space-sm) var(--kf-space-md);border-bottom:1px solid var(--kf-color-border);white-space:nowrap}.kf-data-table--compact .kf-data-table__table th,.kf-data-table--compact .kf-data-table__table td{padding:var(--kf-space-xs) var(--kf-space-md)}.kf-data-table__table th{background:var(--kf-color-surface)}.kf-data-table__table td{color:var(--kf-color-text)}.kf-data-table__table tbody tr:last-child td{border-bottom:none}.kf-data-table__row{transition:background .14s}.kf-data-table__row--interactive{cursor:pointer}.kf-data-table__row--interactive:hover{background:var(--kf-color-primary-soft)}.kf-data-table__row--interactive:focus-visible{outline:none;box-shadow:inset 0 0 0 2px var(--kf-color-primary)}.kf-data-table__row--current{background:var(--kf-color-primary-soft)}.kf-data-table__cell--right{text-align:right}.kf-data-table__cell--center{text-align:center}.kf-data-table__table .kf-data-table__cell--truncate{max-width:0}.kf-data-table__truncate{display:block;min-width:0;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kf-data-table__table td.kf-data-table__cell--mono{font-family:var(--kf-font-mono);font-size:var(--kf-font-size-body-sm)}.kf-data-table__table td.kf-data-table__cell--emphasis{font-weight:700;color:var(--kf-color-ink);font-variant-numeric:tabular-nums}.kf-data-table__sr-only{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0}.kf-data-table__cell--sticky{background:var(--kf-color-white)}.kf-data-table__table th.kf-data-table__cell--sticky{background:var(--kf-color-surface);z-index:2}.kf-data-table__cell--sortable{cursor:pointer;-webkit-user-select:none;user-select:none}.kf-data-table__cell--sortable:hover{color:var(--kf-color-ink)}.kf-data-table__table th.kf-data-table__cell--sort-active{color:var(--kf-color-primary);font-weight:700}.kf-data-table__th-content{display:inline-flex;align-items:center;gap:var(--kf-space-xs)}.kf-data-table__sort-icon{transition:opacity .14s}.kf-data-table__cell--sortable:hover .kf-data-table__sort-icon{opacity:.5}.kf-data-table__sort-icon--active,.kf-data-table__cell--sortable:hover .kf-data-table__sort-icon--active{opacity:1}.kf-data-table__empty{padding:var(--kf-space-md)}.kf-data-table__footer{padding:var(--kf-space-sm) var(--kf-space-md);border-top:1px solid var(--kf-color-border)}.kf-data-table__footer:empty{display:none}.kf-data-table__cards{display:grid;grid-template-columns:repeat(auto-fill,minmax(min(var(--kf-data-table-card-min, 220px),100%),1fr));grid-auto-rows:1fr;gap:var(--kf-space-md);padding:var(--kf-space-md)}.kf-data-table__card{position:relative;display:flex;flex-direction:column;min-width:0;border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);background:var(--kf-color-white);overflow:hidden;transition:border-color .14s,box-shadow .14s,background .14s}.kf-data-table__card--interactive{cursor:pointer}.kf-data-table__card:hover{border-color:var(--kf-color-border-strong);box-shadow:var(--kf-shadow-xs)}.kf-data-table__card--interactive:hover{background:var(--kf-color-primary-soft)}.kf-data-table__card--interactive:focus-visible{outline:2px solid transparent;outline-offset:2px;box-shadow:0 0 0 2px var(--kf-color-primary)}.kf-data-table__card--current{border-color:var(--kf-color-primary);background:var(--kf-color-primary-soft)}.kf-data-table__card-header{display:flex;align-items:center;gap:var(--kf-space-sm);min-width:0;padding:var(--kf-space-md) var(--kf-space-md) var(--kf-space-sm)}.kf-data-table__card-media{flex:none}.kf-data-table__card-media img,.kf-data-table__thumb{display:block;width:48px;height:48px;border-radius:var(--kf-radius-xs);object-fit:cover;background:var(--kf-color-surface)}.kf-data-table__card-heading{display:flex;flex:1;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);min-width:0}.kf-data-table__card-title{flex:0 1 auto;min-width:0;font-family:var(--kf-font-title);font-weight:600;color:var(--kf-color-ink);overflow-wrap:anywhere}.kf-data-table__card-title--mono{flex:none;font-family:var(--kf-font-mono);font-size:var(--kf-font-size-body-sm);white-space:nowrap}.kf-data-table__card-statuses{display:flex;flex:0 1 auto;flex-wrap:nowrap;justify-content:flex-end;gap:var(--kf-space-xs);min-width:0;overflow:hidden}.kf-data-table__card-actions{display:flex;flex-wrap:wrap;gap:var(--kf-space-xs)}.kf-data-table__card-fields{display:flex;flex-direction:column;gap:var(--kf-space-xs);min-width:0;padding:var(--kf-space-md)}.kf-data-table__card-header+.kf-data-table__card-fields{padding-top:0}.kf-data-table__card-lead{display:-webkit-box;-webkit-box-orient:vertical;-webkit-line-clamp:var(--kf-data-table-card-clamp, 2);line-clamp:var(--kf-data-table-card-clamp, 2);min-width:0;overflow:hidden;overflow-wrap:anywhere;color:var(--kf-color-ink);font-weight:500;line-height:1.35}.kf-data-table__card-field{display:flex;justify-content:space-between;gap:var(--kf-space-sm);min-width:0}.kf-data-table__card-label{flex:none}.kf-data-table__card-value{min-width:0;color:var(--kf-color-text);text-align:right;overflow-wrap:anywhere}.kf-data-table__card-value--emphasis,.kf-data-table__card-meta-item--emphasis .kf-data-table__card-meta-value{font-weight:700;color:var(--kf-color-ink);font-variant-numeric:tabular-nums}.kf-data-table__card-meta{display:flex;flex-wrap:wrap;align-items:flex-end;justify-content:space-between;gap:var(--kf-space-xs) var(--kf-space-md);margin-top:auto;padding:var(--kf-space-sm) var(--kf-space-md) var(--kf-space-md);border-top:1px solid var(--kf-color-border)}.kf-data-table__card-meta-item{display:flex;flex-direction:column;gap:2px;min-width:0}.kf-data-table__card-meta-item--end{margin-left:auto;align-items:flex-end;text-align:right}.kf-data-table__card-meta-value{color:var(--kf-color-text);font-variant-numeric:tabular-nums}.kf-data-table__card-meta-item--emphasis .kf-data-table__card-meta-value{font-size:var(--kf-font-size-body)}.kf-data-table__card-actions{justify-content:flex-end;margin-top:auto;padding:var(--kf-space-sm) var(--kf-space-md);border-top:1px solid var(--kf-color-border);background:var(--kf-color-surface)}.kf-data-table__card-meta~.kf-data-table__card-actions{margin-top:0}.kf-data-table__thumb{width:32px;height:32px}@media(prefers-reduced-motion:reduce){.kf-data-table__card,.kf-data-table__row{transition:none}}\n"] }]
|
|
3998
|
+
}], ctorParameters: () => [], propDecorators: { columns: [{ type: i0.Input, args: [{ isSignal: true, alias: "columns", required: true }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: true }] }], loading: [{ type: i0.Input, args: [{ isSignal: true, alias: "loading", required: false }] }], scrollEl: [{ type: i0.ViewChild, args: ['scroll', { isSignal: true }] }], rowKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowKey", required: false }] }], flush: [{ type: i0.Input, args: [{ isSignal: true, alias: "flush", required: false }] }], cardable: [{ type: i0.Input, args: [{ isSignal: true, alias: "cardable", required: false }] }], defaultView: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultView", required: false }] }], showViewToggle: [{ type: i0.Input, args: [{ isSignal: true, alias: "showViewToggle", required: false }] }], cardMinWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "cardMinWidth", required: false }] }], render: [{ type: i0.Input, args: [{ isSignal: true, alias: "render", required: false }] }], cellTemplates: [{ type: i0.Input, args: [{ isSignal: true, alias: "cellTemplates", required: false }] }], density: [{ type: i0.Input, args: [{ isSignal: true, alias: "density", required: false }] }], rowClickable: [{ type: i0.Input, args: [{ isSignal: true, alias: "rowClickable", required: false }] }], rowClick: [{ type: i0.Output, args: ["rowClick"] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], currentRowKey: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentRowKey", required: false }] }], pageSize: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSize", required: false }] }], pageSizeOptions: [{ type: i0.Input, args: [{ isSignal: true, alias: "pageSizeOptions", required: false }] }], page: [{ type: i0.Input, args: [{ isSignal: true, alias: "page", required: false }] }, { type: i0.Output, args: ["pageChange"] }], keepPageOnRowsChange: [{ type: i0.Input, args: [{ isSignal: true, alias: "keepPageOnRowsChange", required: false }] }], pageSizeChange: [{ type: i0.Output, args: ["pageSizeChange"] }], itemLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "itemLabel", required: false }] }], sort: [{ type: i0.Input, args: [{ isSignal: true, alias: "sort", required: false }] }], sortChange: [{ type: i0.Output, args: ["sortChange"] }], view: [{ type: i0.Input, args: [{ isSignal: true, alias: "view", required: false }] }, { type: i0.Output, args: ["viewChange"] }] } });
|
|
3401
3999
|
|
|
3402
4000
|
/**
|
|
3403
4001
|
* Signal con la vista de `kf-data-table` ('table' | 'cards') derivada de un
|
|
@@ -3517,6 +4115,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
|
|
|
3517
4115
|
}, template: "<span class=\"kf-document-row__icon\" aria-hidden=\"true\">\n <kf-icon [name]=\"icon()\" size=\"sm\" />\n</span>\n\n<span class=\"kf-document-row__body\">\n <span class=\"kf-document-row__title\">{{ title() }}</span>\n @if (meta(); as line) {\n <span class=\"kf-document-row__meta kf-caption-text\">{{ line }}</span>\n }\n</span>\n\n<span class=\"kf-document-row__actions\">\n <ng-content />\n @if (href(); as url) {\n <kf-link [href]=\"url\">{{ downloadLabel() }}</kf-link>\n } @else {\n <span class=\"kf-document-row__pending kf-caption-text\">{{ pendingLabel() }}</span>\n }\n</span>\n", styles: [".kf-caption-text{font-size:var(--kf-font-size-caption);font-weight:var(--kf-font-weight-body);color:var(--kf-color-text-muted)}:host{display:flex;align-items:center;gap:var(--kf-space-sm);padding:var(--kf-space-sm);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);background:var(--kf-color-white);min-width:0}.kf-document-row__icon{display:inline-flex;align-items:center;justify-content:center;flex:0 0 auto;width:32px;height:32px;border-radius:var(--kf-radius-xs);background:var(--kf-color-gray-200);color:var(--kf-color-text)}:host(.kf-document-row--info) .kf-document-row__icon{background:var(--kf-color-info-soft);color:var(--kf-color-info-strong)}:host(.kf-document-row--ok) .kf-document-row__icon{background:var(--kf-color-success-soft);color:var(--kf-color-success-strong)}:host(.kf-document-row--warn) .kf-document-row__icon{background:var(--kf-color-warning-soft);color:var(--kf-color-warning-strong)}:host(.kf-document-row--error) .kf-document-row__icon{background:var(--kf-color-error-soft);color:var(--kf-color-error-strong)}.kf-document-row__body{display:flex;flex-direction:column;gap:2px;flex:1 1 auto;min-width:0}.kf-document-row__title{font-family:var(--kf-font-body);font-size:var(--kf-font-size-body);font-weight:var(--kf-font-weight-medium);color:var(--kf-color-ink)}.kf-document-row__meta{overflow-wrap:anywhere}.kf-document-row__actions{display:flex;align-items:center;gap:var(--kf-space-sm);flex:0 0 auto;margin-left:auto}@container (max-width: 420px){:host{flex-wrap:wrap}}\n"] }]
|
|
3518
4116
|
}], propDecorators: { icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "tone", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: true }] }], meta: [{ type: i0.Input, args: [{ isSignal: true, alias: "meta", required: false }] }], href: [{ type: i0.Input, args: [{ isSignal: true, alias: "href", required: false }] }], downloadLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "downloadLabel", required: false }] }], pendingLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "pendingLabel", required: false }] }] } });
|
|
3519
4117
|
|
|
4118
|
+
let nextId$2 = 0;
|
|
3520
4119
|
/**
|
|
3521
4120
|
* Panel lateral genérico, agnóstico de contenido — no sabe qué es navegación
|
|
3522
4121
|
* ni qué es sesión, solo da tres lugares para proyectar contenido
|
|
@@ -3549,6 +4148,46 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
|
|
|
3549
4148
|
* <a href="/">Home</a>
|
|
3550
4149
|
* <a href="/vehiculos">Vehículos</a>
|
|
3551
4150
|
* </kf-drawer>
|
|
4151
|
+
*
|
|
4152
|
+
* ## Panel de tarea (`role="dialog"`)
|
|
4153
|
+
*
|
|
4154
|
+
* Para un panel modal que no es navegación (filtros de un listado, resumen
|
|
4155
|
+
* de un registro): `role="dialog"` expone `role="dialog"` + `aria-modal`,
|
|
4156
|
+
* `title` dibuja una barra clara con el título (que nombra el diálogo vía
|
|
4157
|
+
* `aria-labelledby`) y un botón de cierre con `closeLabel`. En pantallas de
|
|
4158
|
+
* hasta 640px el panel ocupa el ancho completo; arriba de eso mide
|
|
4159
|
+
* `--kf-drawer-width` (default 480px). El pie (`[kf-drawer-footer]`) queda
|
|
4160
|
+
* fijo abajo mientras el cuerpo hace scroll.
|
|
4161
|
+
*
|
|
4162
|
+
* <kf-drawer [(open)]="filtersOpen" role="dialog" side="right"
|
|
4163
|
+
* title="Filtros" closeLabel="Cerrar filtros">
|
|
4164
|
+
* <kf-option-list label="Estado" ... />
|
|
4165
|
+
* <div kf-drawer-footer> ...Limpiar / Ver 12 resultados... </div>
|
|
4166
|
+
* </kf-drawer>
|
|
4167
|
+
*
|
|
4168
|
+
* `side="bottom"` lo convierte en hoja inferior (grip, esquinas superiores
|
|
4169
|
+
* redondeadas, alto máximo 80vh) — pensado para un resumen en mobile.
|
|
4170
|
+
*
|
|
4171
|
+
* Escape cierra el panel salvo que un control interno ya haya consumido la
|
|
4172
|
+
* tecla (`event.defaultPrevented`, ej. un `kf-select` abierto dentro del
|
|
4173
|
+
* drawer cierra solo su propio panel).
|
|
4174
|
+
*
|
|
4175
|
+
* Nombres por defecto: con `role="navigation"` el panel se llama "Menú" y
|
|
4176
|
+
* el cierre "Cerrar menú" (comportamiento histórico). Con `role="dialog"`
|
|
4177
|
+
* los defaults son neutros ("Diálogo" / "Cerrar") y en modo desarrollo se
|
|
4178
|
+
* avisa por consola si no hay `title` ni `ariaLabel`: un diálogo sin nombre
|
|
4179
|
+
* propio no le dice nada al lector de pantalla.
|
|
4180
|
+
*
|
|
4181
|
+
* Limitación conocida: NO colocar un `kf-select` (ni otro control con panel
|
|
4182
|
+
* flotante del CDK) dentro de un drawer `role="dialog"`. Su listbox se
|
|
4183
|
+
* monta en `.cdk-overlay-container`, fuera del diálogo con `aria-modal`: un
|
|
4184
|
+
* lector de pantalla puede tratarlo como contenido inerte y la trampa de
|
|
4185
|
+
* foco no lo incluye. Para elegir una opción dentro del panel usar
|
|
4186
|
+
* `kf-option-list`.
|
|
4187
|
+
*
|
|
4188
|
+
* Al cerrar, el foco vuelve al elemento que lo tenía al abrir solo si sigue
|
|
4189
|
+
* en el documento (`isConnected`); si fue desmontado mientras el drawer
|
|
4190
|
+
* estaba abierto, no se fuerza ningún destino.
|
|
3552
4191
|
*/
|
|
3553
4192
|
class Drawer {
|
|
3554
4193
|
/** Visibilidad del panel, two-way: `[(open)]="menuOpen"`. */
|
|
@@ -3556,13 +4195,32 @@ class Drawer {
|
|
|
3556
4195
|
...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
|
|
3557
4196
|
/** Borde de anclaje del panel. `left` (default) es la convención dominante
|
|
3558
4197
|
* (Material/iOS/Android): el botón que abre el drawer y el propio panel
|
|
3559
|
-
* quedan del mismo lado. */
|
|
4198
|
+
* quedan del mismo lado. `bottom` lo convierte en hoja inferior. */
|
|
3560
4199
|
side = input('left', /* @ts-ignore */
|
|
3561
4200
|
...(ngDevMode ? [{ debugName: "side" }] : /* istanbul ignore next */ []));
|
|
3562
|
-
/** `
|
|
3563
|
-
|
|
3564
|
-
|
|
3565
|
-
|
|
4201
|
+
/** `navigation` (default, menú) o `dialog` (panel modal de tarea). Ver JSDoc de la clase. */
|
|
4202
|
+
role = input('navigation', /* @ts-ignore */
|
|
4203
|
+
...(ngDevMode ? [{ debugName: "role" }] : /* istanbul ignore next */ []));
|
|
4204
|
+
/** `aria-label` del panel — describe su propósito para lectores de
|
|
4205
|
+
* pantalla (ej. "Menú de navegación"). Ignorado cuando hay `title`, que
|
|
4206
|
+
* nombra el panel con su texto visible. Default: "Menú" (`navigation`) o
|
|
4207
|
+
* "Diálogo" (`dialog`). */
|
|
4208
|
+
ariaLabel = input(/* @ts-ignore */
|
|
4209
|
+
...(ngDevMode ? [undefined, { debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
4210
|
+
/** Título visible en una barra clara arriba del panel, con botón de cierre. Sin él no hay barra (comportamiento histórico). */
|
|
4211
|
+
title = input(/* @ts-ignore */
|
|
4212
|
+
...(ngDevMode ? [undefined, { debugName: "title" }] : /* istanbul ignore next */ []));
|
|
4213
|
+
/** Nombre accesible del botón de cierre de la barra de título y del
|
|
4214
|
+
* overlay. Default: "Cerrar menú" (`navigation`) o "Cerrar" (`dialog`). */
|
|
4215
|
+
closeLabel = input(/* @ts-ignore */
|
|
4216
|
+
...(ngDevMode ? [undefined, { debugName: "closeLabel" }] : /* istanbul ignore next */ []));
|
|
4217
|
+
/** `ariaLabel` o, sin él, el default según el rol. */
|
|
4218
|
+
resolvedAriaLabel = computed(() => this.ariaLabel() ?? (this.role() === 'dialog' ? 'Diálogo' : 'Menú'), /* @ts-ignore */
|
|
4219
|
+
...(ngDevMode ? [{ debugName: "resolvedAriaLabel" }] : /* istanbul ignore next */ []));
|
|
4220
|
+
/** `closeLabel` o, sin él, el default según el rol. */
|
|
4221
|
+
resolvedCloseLabel = computed(() => this.closeLabel() ?? (this.role() === 'dialog' ? 'Cerrar' : 'Cerrar menú'), /* @ts-ignore */
|
|
4222
|
+
...(ngDevMode ? [{ debugName: "resolvedCloseLabel" }] : /* istanbul ignore next */ []));
|
|
4223
|
+
titleId = `kf-drawer-title-${nextId$2++}`;
|
|
3566
4224
|
panelRef = viewChild.required('panel', /* @ts-ignore */
|
|
3567
4225
|
...(ngDevMode ? [{ debugName: "panelRef" }] : /* istanbul ignore next */ []));
|
|
3568
4226
|
focusTrapFactory = inject(FocusTrapFactory);
|
|
@@ -3579,14 +4237,26 @@ class Drawer {
|
|
|
3579
4237
|
else {
|
|
3580
4238
|
this.focusTrap?.destroy();
|
|
3581
4239
|
this.focusTrap = null;
|
|
3582
|
-
this.lastFocusedElement?.
|
|
4240
|
+
if (this.lastFocusedElement?.isConnected) {
|
|
4241
|
+
this.lastFocusedElement.focus();
|
|
4242
|
+
}
|
|
3583
4243
|
this.lastFocusedElement = null;
|
|
3584
4244
|
}
|
|
3585
4245
|
});
|
|
4246
|
+
if (isDevMode()) {
|
|
4247
|
+
effect(() => {
|
|
4248
|
+
if (this.role() === 'dialog' && !this.title() && !this.ariaLabel()) {
|
|
4249
|
+
console.warn('[kf-drawer] role="dialog" sin `title` ni `ariaLabel`: el diálogo se anuncia con el nombre genérico "Diálogo".');
|
|
4250
|
+
}
|
|
4251
|
+
});
|
|
4252
|
+
}
|
|
3586
4253
|
inject(DestroyRef).onDestroy(() => this.focusTrap?.destroy());
|
|
3587
4254
|
}
|
|
3588
4255
|
/** Cierra el drawer con Escape — mismo patrón que `Dropdown`/`Modal`. */
|
|
3589
|
-
onEscape() {
|
|
4256
|
+
onEscape(event) {
|
|
4257
|
+
if (!this.open() || event.defaultPrevented) {
|
|
4258
|
+
return;
|
|
4259
|
+
}
|
|
3590
4260
|
this.open.set(false);
|
|
3591
4261
|
}
|
|
3592
4262
|
getOrCreateFocusTrap() {
|
|
@@ -3596,14 +4266,21 @@ class Drawer {
|
|
|
3596
4266
|
return this.focusTrap;
|
|
3597
4267
|
}
|
|
3598
4268
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Drawer, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3599
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Drawer, isStandalone: true, selector: "kf-drawer", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, side: { classPropertyName: "side", publicName: "side", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange" }, host: { listeners: { "document:keydown.escape": "onEscape()" } }, viewQueries: [{ propertyName: "panelRef", first: true, predicate: ["panel"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (open()) {\n <button\n type=\"button\"\n class=\"kf-drawer__overlay\"\n aria-label=\"
|
|
4269
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Drawer, isStandalone: true, selector: "kf-drawer", inputs: { open: { classPropertyName: "open", publicName: "open", isSignal: true, isRequired: false, transformFunction: null }, side: { classPropertyName: "side", publicName: "side", isSignal: true, isRequired: false, transformFunction: null }, role: { classPropertyName: "role", publicName: "role", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, title: { classPropertyName: "title", publicName: "title", isSignal: true, isRequired: false, transformFunction: null }, closeLabel: { classPropertyName: "closeLabel", publicName: "closeLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { open: "openChange" }, host: { listeners: { "document:keydown.escape": "onEscape($event)" }, properties: { "attr.role": "null", "attr.title": "null" } }, viewQueries: [{ propertyName: "panelRef", first: true, predicate: ["panel"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (open()) {\n <button\n type=\"button\"\n class=\"kf-drawer__overlay\"\n [attr.aria-label]=\"resolvedCloseLabel()\"\n [attr.tabindex]=\"role() === 'dialog' ? -1 : null\"\n (click)=\"open.set(false)\"\n ></button>\n}\n\n<!-- Un <div> con rol expl\u00EDcito (no <nav>): un mismo panel puede ser\n navegaci\u00F3n o di\u00E1logo, y <nav> no admite role=\"dialog\". -->\n<div\n #panel\n class=\"kf-drawer__panel\"\n [class.kf-drawer__panel--open]=\"open()\"\n [class.kf-drawer__panel--right]=\"side() === 'right'\"\n [class.kf-drawer__panel--bottom]=\"side() === 'bottom'\"\n [class.kf-drawer__panel--dialog]=\"role() === 'dialog'\"\n [attr.role]=\"role()\"\n [attr.aria-modal]=\"role() === 'dialog' ? 'true' : null\"\n [attr.aria-label]=\"title() ? null : resolvedAriaLabel()\"\n [attr.aria-labelledby]=\"title() ? titleId : null\"\n [attr.aria-hidden]=\"!open()\"\n>\n @if (side() === 'bottom') {\n <div class=\"kf-drawer__grip\" aria-hidden=\"true\"></div>\n }\n @if (title(); as titleText) {\n <div class=\"kf-drawer__titlebar\">\n <h2 class=\"kf-drawer__title\" [id]=\"titleId\">{{ titleText }}</h2>\n <kf-button\n class=\"kf-drawer__close\"\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n icon=\"x\"\n [ariaLabel]=\"resolvedCloseLabel()\"\n (click)=\"open.set(false)\"\n />\n </div>\n }\n <div class=\"kf-drawer__header\"><ng-content select=\"[kf-drawer-header]\" /></div>\n <div class=\"kf-drawer__body\"><ng-content /></div>\n <div class=\"kf-drawer__footer\"><ng-content select=\"[kf-drawer-footer]\" /></div>\n</div>\n", styles: [".kf-drawer__overlay{position:fixed;inset:0;border:none;padding:0;cursor:pointer;background:#171d2566;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);z-index:39}.kf-drawer__panel{position:fixed;top:0;left:0;bottom:0;z-index:40;display:flex;flex-direction:column;width:82%;max-width:320px;background:var(--kf-color-white);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-drawer__panel--right{left:auto;right:0;box-shadow:-8px 0 24px -8px #1018284d;transform:translate(100%)}.kf-drawer__panel--dialog{width:min(var(--kf-drawer-width, 480px),100vw);max-width:none}@media(max-width:640px){.kf-drawer__panel--dialog{width:100vw}}.kf-drawer__panel--bottom{top:auto;right:0;width:100%;max-width:none;max-height:80vh;border-radius:var(--kf-radius-lg) var(--kf-radius-lg) 0 0;box-shadow:var(--kf-shadow-lg);padding-bottom:env(safe-area-inset-bottom,0px);transform:translateY(100%)}.kf-drawer__panel--open{transform:translate(0);visibility:visible;transition:transform .24s cubic-bezier(.32,.72,0,1),visibility 0s linear 0s}.kf-drawer__panel--bottom.kf-drawer__panel--open{transform:translateY(0)}@media(prefers-reduced-motion:reduce){.kf-drawer__panel,.kf-drawer__panel--open{transition:none}}.kf-drawer__grip{flex:none;align-self:center;width:36px;height:4px;margin-top:var(--kf-space-sm);border-radius:var(--kf-radius-pill);background:var(--kf-color-border-strong)}.kf-drawer__titlebar{flex:none;display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);padding:var(--kf-space-sm) var(--kf-space-sm) var(--kf-space-sm) var(--kf-space-lg);border-bottom:1px solid var(--kf-color-border)}.kf-drawer__title{margin:0;min-width:0;font-family:var(--kf-font-title);font-size:var(--kf-font-size-title-sm);font-weight:700;line-height:1.25;color:var(--kf-color-ink)}.kf-drawer__close{flex:none}@media(pointer:coarse),(max-width:760px){.kf-drawer__close{--kf-button-min-height: 44px;min-width:44px}}.kf-drawer__header:empty,.kf-drawer__footer:empty{display:none}.kf-drawer__header{padding:var(--kf-space-lg);background:var(--kf-color-tertiary-deep)}.kf-drawer__body{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;gap:var(--kf-space-xs);padding:var(--kf-space-lg);overflow-y:auto}.kf-drawer__panel--dialog .kf-drawer__body{gap:var(--kf-space-lg)}.kf-drawer__footer{flex:none;padding:var(--kf-space-lg);border-top:1px solid var(--kf-color-border)}.kf-drawer__panel--dialog .kf-drawer__footer{padding:var(--kf-space-md) var(--kf-space-lg)}\n"], dependencies: [{ kind: "component", type: Button, selector: "kf-button", inputs: ["variant", "size", "shape", "disabled", "type", "icon", "block", "loading", "ariaLabel", "ariaExpanded", "ariaPressed", "ariaHaspopup"] }] });
|
|
3600
4270
|
}
|
|
3601
4271
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Drawer, decorators: [{
|
|
3602
4272
|
type: Component,
|
|
3603
|
-
args: [{ selector: 'kf-drawer',
|
|
3604
|
-
|
|
4273
|
+
args: [{ selector: 'kf-drawer', imports: [Button], host: {
|
|
4274
|
+
// `role` y `title` son inputs con nombre de atributo nativo: escritos
|
|
4275
|
+
// como atributo estático (`role="dialog"`, `title="Filtros"`) Angular
|
|
4276
|
+
// también los deja en el host, lo que anidaría dos diálogos y mostraría
|
|
4277
|
+
// un tooltip nativo sobre todo el panel. El rol real vive en el panel.
|
|
4278
|
+
'[attr.role]': 'null',
|
|
4279
|
+
'[attr.title]': 'null',
|
|
4280
|
+
}, template: "@if (open()) {\n <button\n type=\"button\"\n class=\"kf-drawer__overlay\"\n [attr.aria-label]=\"resolvedCloseLabel()\"\n [attr.tabindex]=\"role() === 'dialog' ? -1 : null\"\n (click)=\"open.set(false)\"\n ></button>\n}\n\n<!-- Un <div> con rol expl\u00EDcito (no <nav>): un mismo panel puede ser\n navegaci\u00F3n o di\u00E1logo, y <nav> no admite role=\"dialog\". -->\n<div\n #panel\n class=\"kf-drawer__panel\"\n [class.kf-drawer__panel--open]=\"open()\"\n [class.kf-drawer__panel--right]=\"side() === 'right'\"\n [class.kf-drawer__panel--bottom]=\"side() === 'bottom'\"\n [class.kf-drawer__panel--dialog]=\"role() === 'dialog'\"\n [attr.role]=\"role()\"\n [attr.aria-modal]=\"role() === 'dialog' ? 'true' : null\"\n [attr.aria-label]=\"title() ? null : resolvedAriaLabel()\"\n [attr.aria-labelledby]=\"title() ? titleId : null\"\n [attr.aria-hidden]=\"!open()\"\n>\n @if (side() === 'bottom') {\n <div class=\"kf-drawer__grip\" aria-hidden=\"true\"></div>\n }\n @if (title(); as titleText) {\n <div class=\"kf-drawer__titlebar\">\n <h2 class=\"kf-drawer__title\" [id]=\"titleId\">{{ titleText }}</h2>\n <kf-button\n class=\"kf-drawer__close\"\n type=\"button\"\n variant=\"ghost\"\n size=\"sm\"\n icon=\"x\"\n [ariaLabel]=\"resolvedCloseLabel()\"\n (click)=\"open.set(false)\"\n />\n </div>\n }\n <div class=\"kf-drawer__header\"><ng-content select=\"[kf-drawer-header]\" /></div>\n <div class=\"kf-drawer__body\"><ng-content /></div>\n <div class=\"kf-drawer__footer\"><ng-content select=\"[kf-drawer-footer]\" /></div>\n</div>\n", styles: [".kf-drawer__overlay{position:fixed;inset:0;border:none;padding:0;cursor:pointer;background:#171d2566;backdrop-filter:blur(4px);-webkit-backdrop-filter:blur(4px);z-index:39}.kf-drawer__panel{position:fixed;top:0;left:0;bottom:0;z-index:40;display:flex;flex-direction:column;width:82%;max-width:320px;background:var(--kf-color-white);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-drawer__panel--right{left:auto;right:0;box-shadow:-8px 0 24px -8px #1018284d;transform:translate(100%)}.kf-drawer__panel--dialog{width:min(var(--kf-drawer-width, 480px),100vw);max-width:none}@media(max-width:640px){.kf-drawer__panel--dialog{width:100vw}}.kf-drawer__panel--bottom{top:auto;right:0;width:100%;max-width:none;max-height:80vh;border-radius:var(--kf-radius-lg) var(--kf-radius-lg) 0 0;box-shadow:var(--kf-shadow-lg);padding-bottom:env(safe-area-inset-bottom,0px);transform:translateY(100%)}.kf-drawer__panel--open{transform:translate(0);visibility:visible;transition:transform .24s cubic-bezier(.32,.72,0,1),visibility 0s linear 0s}.kf-drawer__panel--bottom.kf-drawer__panel--open{transform:translateY(0)}@media(prefers-reduced-motion:reduce){.kf-drawer__panel,.kf-drawer__panel--open{transition:none}}.kf-drawer__grip{flex:none;align-self:center;width:36px;height:4px;margin-top:var(--kf-space-sm);border-radius:var(--kf-radius-pill);background:var(--kf-color-border-strong)}.kf-drawer__titlebar{flex:none;display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);padding:var(--kf-space-sm) var(--kf-space-sm) var(--kf-space-sm) var(--kf-space-lg);border-bottom:1px solid var(--kf-color-border)}.kf-drawer__title{margin:0;min-width:0;font-family:var(--kf-font-title);font-size:var(--kf-font-size-title-sm);font-weight:700;line-height:1.25;color:var(--kf-color-ink)}.kf-drawer__close{flex:none}@media(pointer:coarse),(max-width:760px){.kf-drawer__close{--kf-button-min-height: 44px;min-width:44px}}.kf-drawer__header:empty,.kf-drawer__footer:empty{display:none}.kf-drawer__header{padding:var(--kf-space-lg);background:var(--kf-color-tertiary-deep)}.kf-drawer__body{flex:1 1 auto;min-height:0;display:flex;flex-direction:column;gap:var(--kf-space-xs);padding:var(--kf-space-lg);overflow-y:auto}.kf-drawer__panel--dialog .kf-drawer__body{gap:var(--kf-space-lg)}.kf-drawer__footer{flex:none;padding:var(--kf-space-lg);border-top:1px solid var(--kf-color-border)}.kf-drawer__panel--dialog .kf-drawer__footer{padding:var(--kf-space-md) var(--kf-space-lg)}\n"] }]
|
|
4281
|
+
}], ctorParameters: () => [], propDecorators: { open: [{ type: i0.Input, args: [{ isSignal: true, alias: "open", required: false }] }, { type: i0.Output, args: ["openChange"] }], side: [{ type: i0.Input, args: [{ isSignal: true, alias: "side", required: false }] }], role: [{ type: i0.Input, args: [{ isSignal: true, alias: "role", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], closeLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "closeLabel", required: false }] }], panelRef: [{ type: i0.ViewChild, args: ['panel', { isSignal: true }] }], onEscape: [{
|
|
3605
4282
|
type: HostListener,
|
|
3606
|
-
args: ['document:keydown.escape']
|
|
4283
|
+
args: ['document:keydown.escape', ['$event']]
|
|
3607
4284
|
}] } });
|
|
3608
4285
|
|
|
3609
4286
|
/**
|
|
@@ -3774,6 +4451,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
|
|
|
3774
4451
|
* está en vuelo).
|
|
3775
4452
|
*
|
|
3776
4453
|
* <kf-input label="DNI" readonly formControlName="documentId" />
|
|
4454
|
+
*
|
|
4455
|
+
* Buscador sin label visible: `ariaLabel` da el nombre accesible del
|
|
4456
|
+
* `<input>` (el placeholder no lo reemplaza: desaparece al escribir) y
|
|
4457
|
+
* `clearable` agrega un botón "×" mientras haya texto. Borrar limpia el
|
|
4458
|
+
* valor, notifica al formulario (`ngModel`/`formControl`), dispara un
|
|
4459
|
+
* evento `input` nativo (para quien escucha `(input)`), emite `cleared` y
|
|
4460
|
+
* devuelve el foco al campo.
|
|
4461
|
+
*
|
|
4462
|
+
* <kf-input type="search" icon="search" ariaLabel="Buscar cotizaciones"
|
|
4463
|
+
* placeholder="Buscar cotización" clearable [(ngModel)]="query" />
|
|
4464
|
+
*
|
|
4465
|
+
* `focus()` enfoca el `<input>` nativo, sin que el consumidor busque el
|
|
4466
|
+
* control interno con `querySelector`.
|
|
4467
|
+
*
|
|
4468
|
+
* `autocomplete`, `name` e `inputmode` se reenvían al `<input>` nativo (un
|
|
4469
|
+
* atributo en el host no llega adentro) para que el navegador y los gestores
|
|
4470
|
+
* de contraseñas identifiquen el campo; sin valor no se renderiza ningún
|
|
4471
|
+
* atributo. Funcionan igual con `type="password"` y `toggleable`.
|
|
4472
|
+
*
|
|
4473
|
+
* <kf-input label="RUT" autocomplete="username" name="rut" inputmode="text" formControlName="rut" />
|
|
4474
|
+
* <kf-input type="password" label="Contraseña" toggleable autocomplete="current-password" name="password" formControlName="password" />
|
|
3777
4475
|
*/
|
|
3778
4476
|
class Input {
|
|
3779
4477
|
static nextId = 0;
|
|
@@ -3808,12 +4506,34 @@ class Input {
|
|
|
3808
4506
|
...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
|
|
3809
4507
|
placeholder = input('', /* @ts-ignore */
|
|
3810
4508
|
...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
|
|
3811
|
-
/**
|
|
3812
|
-
*
|
|
4509
|
+
/** Token de autocompletado HTML (`username`, `email`, `current-password`,
|
|
4510
|
+
* `new-password`, `off`, etc.) que se reenvía al `<input>` nativo para que
|
|
4511
|
+
* el navegador y los gestores de contraseñas identifiquen el propósito del
|
|
4512
|
+
* campo (WCAG 1.3.5 y 3.3.8). Sin valor, el atributo no se renderiza. */
|
|
4513
|
+
autocomplete = input(/* @ts-ignore */
|
|
4514
|
+
...(ngDevMode ? [undefined, { debugName: "autocomplete" }] : /* istanbul ignore next */ []));
|
|
4515
|
+
/** `name` del `<input>` nativo; los gestores de contraseñas y el envío
|
|
4516
|
+
* nativo de formularios lo usan para identificar el campo. Sin valor, el
|
|
4517
|
+
* atributo no se renderiza. */
|
|
4518
|
+
name = input(/* @ts-ignore */
|
|
4519
|
+
...(ngDevMode ? [undefined, { debugName: "name" }] : /* istanbul ignore next */ []));
|
|
4520
|
+
/** Sugiere el teclado virtual (`numeric`, `email`, `tel`, etc.) sin cambiar
|
|
4521
|
+
* el `type`. Sin valor, el atributo no se renderiza. */
|
|
3813
4522
|
inputmode = input(/* @ts-ignore */
|
|
3814
4523
|
...(ngDevMode ? [undefined, { debugName: "inputmode" }] : /* istanbul ignore next */ []));
|
|
3815
4524
|
/** Agrega el botón de mostrar/ocultar — sin efecto si `type` no es `"password"`. */
|
|
3816
4525
|
toggleable = input(false, { ...(ngDevMode ? { debugName: "toggleable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
4526
|
+
/** Nombre accesible del `<input>` cuando no hay `label` visible. Ignorado
|
|
4527
|
+
* si hay `label`: un `aria-label` pisaría al `<label for>` visible. */
|
|
4528
|
+
ariaLabel = input(/* @ts-ignore */
|
|
4529
|
+
...(ngDevMode ? [undefined, { debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
4530
|
+
/** Agrega un botón "×" para borrar el valor mientras haya texto (sin efecto con `disabled`/`readonly`). */
|
|
4531
|
+
clearable = input(false, { ...(ngDevMode ? { debugName: "clearable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
4532
|
+
/** Nombre accesible del botón de borrar. */
|
|
4533
|
+
clearLabel = input('Borrar texto', /* @ts-ignore */
|
|
4534
|
+
...(ngDevMode ? [{ debugName: "clearLabel" }] : /* istanbul ignore next */ []));
|
|
4535
|
+
/** Emitido después de borrar el valor con el botón "×". */
|
|
4536
|
+
cleared = output();
|
|
3817
4537
|
inputId = `kf-input-${Input.nextId++}`;
|
|
3818
4538
|
hintId = `${this.inputId}-hint`;
|
|
3819
4539
|
errorId = `${this.inputId}-error`;
|
|
@@ -3833,6 +4553,9 @@ class Input {
|
|
|
3833
4553
|
/** El botón solo tiene sentido si el campo realmente es de contraseña. */
|
|
3834
4554
|
showPasswordToggle = computed(() => this.toggleable() && this.type() === 'password', /* @ts-ignore */
|
|
3835
4555
|
...(ngDevMode ? [{ debugName: "showPasswordToggle" }] : /* istanbul ignore next */ []));
|
|
4556
|
+
/** El botón de borrar solo aparece con texto y con el campo editable. */
|
|
4557
|
+
showClear = computed(() => this.clearable() && !!this.value() && !this.isDisabled() && !this.readonly(), /* @ts-ignore */
|
|
4558
|
+
...(ngDevMode ? [{ debugName: "showClear" }] : /* istanbul ignore next */ []));
|
|
3836
4559
|
/**
|
|
3837
4560
|
* `type` real que recibe el `<input>` nativo: igual a `type()` salvo
|
|
3838
4561
|
* cuando el toggle está activo y visible, donde pasa a `text` para
|
|
@@ -3851,6 +4574,10 @@ class Input {
|
|
|
3851
4574
|
// ya cargado (ngModel/formControl con valor inicial) se refleje siempre.
|
|
3852
4575
|
this.nativeInput().nativeElement.value = this.value();
|
|
3853
4576
|
}
|
|
4577
|
+
/** Enfoca el `<input>` nativo (mismas opciones que `HTMLElement.focus`). */
|
|
4578
|
+
focus(options) {
|
|
4579
|
+
this.nativeInput().nativeElement.focus(options);
|
|
4580
|
+
}
|
|
3854
4581
|
writeValue(value) {
|
|
3855
4582
|
const nextValue = value ?? '';
|
|
3856
4583
|
this.value.set(nextValue);
|
|
@@ -3880,6 +4607,20 @@ class Input {
|
|
|
3880
4607
|
this.focused.set(false);
|
|
3881
4608
|
this.onTouched();
|
|
3882
4609
|
}
|
|
4610
|
+
/**
|
|
4611
|
+
* Borra el valor reutilizando el mismo camino que el tipeo: un evento
|
|
4612
|
+
* `input` nativo pasa por `onInput` (notifica al formulario) y además
|
|
4613
|
+
* llega a cualquier `(input)` del consumidor. Con el mouse no hay blur:
|
|
4614
|
+
* el `mousedown` del botón hace `preventDefault` (ver Input.html), así el
|
|
4615
|
+
* campo no se marca `touched` por usar su propio "×".
|
|
4616
|
+
*/
|
|
4617
|
+
onClear() {
|
|
4618
|
+
const native = this.nativeInput().nativeElement;
|
|
4619
|
+
native.value = '';
|
|
4620
|
+
native.dispatchEvent(new Event('input', { bubbles: true }));
|
|
4621
|
+
native.focus();
|
|
4622
|
+
this.cleared.emit();
|
|
4623
|
+
}
|
|
3883
4624
|
/**
|
|
3884
4625
|
* Alterna la visibilidad del valor. No dispara `onBlur`/pierde el foco:
|
|
3885
4626
|
* el botón vive dentro del mismo `.kf-input__field`, así que un click acá
|
|
@@ -3889,13 +4630,13 @@ class Input {
|
|
|
3889
4630
|
this.passwordVisible.update((visible) => !visible);
|
|
3890
4631
|
}
|
|
3891
4632
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Input, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3892
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Input, isStandalone: true, selector: "kf-input", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, suffix: { classPropertyName: "suffix", publicName: "suffix", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, errorMessage: { classPropertyName: "errorMessage", publicName: "errorMessage", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, inputmode: { classPropertyName: "inputmode", publicName: "inputmode", isSignal: true, isRequired: false, transformFunction: null }, toggleable: { classPropertyName: "toggleable", publicName: "toggleable", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
|
|
4633
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Input, isStandalone: true, selector: "kf-input", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, suffix: { classPropertyName: "suffix", publicName: "suffix", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, errorMessage: { classPropertyName: "errorMessage", publicName: "errorMessage", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, autocomplete: { classPropertyName: "autocomplete", publicName: "autocomplete", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, inputmode: { classPropertyName: "inputmode", publicName: "inputmode", isSignal: true, isRequired: false, transformFunction: null }, toggleable: { classPropertyName: "toggleable", publicName: "toggleable", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, clearable: { classPropertyName: "clearable", publicName: "clearable", isSignal: true, isRequired: false, transformFunction: null }, clearLabel: { classPropertyName: "clearLabel", publicName: "clearLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { cleared: "cleared" }, providers: [
|
|
3893
4634
|
{
|
|
3894
4635
|
provide: NG_VALUE_ACCESSOR,
|
|
3895
4636
|
useExisting: forwardRef(() => Input),
|
|
3896
4637
|
multi: true,
|
|
3897
4638
|
},
|
|
3898
|
-
], viewQueries: [{ propertyName: "nativeInput", first: true, predicate: ["nativeInput"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (label()) {\n <kf-field-label [for]=\"inputId\" [required]=\"required()\">{{ label() }}</kf-field-label>\n}\n<div\n class=\"kf-input__field\"\n [class.kf-input__field--disabled]=\"isDisabled()\"\n [class.kf-input__field--readonly]=\"readonly()\"\n [class.kf-input__field--focused]=\"focused()\"\n [class.kf-input__field--invalid]=\"invalid()\"\n>\n @if (icon(); as iconName) {\n <kf-icon class=\"kf-input__icon\" [name]=\"iconName\" size=\"sm\" />\n }\n <input\n #nativeInput\n [id]=\"inputId\"\n class=\"kf-input__control\"\n [type]=\"effectiveType()\"\n [placeholder]=\"placeholder()\"\n [attr.inputmode]=\"inputmode() || null\"\n [disabled]=\"isDisabled()\"\n [required]=\"required()\"\n [attr.readonly]=\"readonly() ? true : null\"\n [attr.aria-describedby]=\"errorMessage() ? errorId : hint() ? hintId : null\"\n [attr.aria-invalid]=\"invalid() ? true : null\"\n [value]=\"value()\"\n (input)=\"onInput(nativeInput.value)\"\n (blur)=\"onBlur()\"\n (focus)=\"focused.set(true)\"\n />\n @if (showPasswordToggle()) {\n <button\n type=\"button\"\n class=\"kf-input__toggle\"\n [attr.aria-label]=\"passwordVisible() ? 'Ocultar contrase\u00F1a' : 'Mostrar contrase\u00F1a'\"\n [attr.aria-pressed]=\"passwordVisible()\"\n (click)=\"onTogglePasswordVisibility()\"\n >\n <kf-icon [name]=\"passwordVisible() ? 'eye-off' : 'eye'\" size=\"sm\" />\n </button>\n } @else if (suffix()) {\n <span class=\"kf-input__suffix\">{{ suffix() }}</span>\n }\n</div>\n@if (errorMessage(); as message) {\n <span class=\"kf-input__error kf-field-error\" [id]=\"errorId\" role=\"alert\">{{ message }}</span>\n} @else if (hint()) {\n <span class=\"kf-input__hint kf-field-hint\" [id]=\"hintId\">{{ hint() }}</span>\n}\n", styles: [".kf-field-hint{font-size:.75rem;color:var(--kf-color-text-muted)}.kf-field-error{display:flex;align-items:center;gap:var(--kf-space-xs);font-size:.75rem;font-weight:600;color:var(--kf-color-error-strong)}:host{display:flex;flex-direction:column;gap:var(--kf-space-xs);font-family:var(--kf-font-body)}.kf-input__field{display:flex;align-items:center;gap:var(--kf-space-sm);padding:0 var(--kf-space-sm);background:var(--kf-color-surface);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);transition:background .14s,border-color .14s,box-shadow .14s}.kf-input__field--focused{background:var(--kf-color-white);border-color:var(--kf-color-primary);box-shadow:var(--kf-shadow-focus)}.kf-input__field--invalid{border-color:var(--kf-color-error)}.kf-input__field--invalid.kf-input__field--focused{box-shadow:var(--kf-shadow-focus-error)}.kf-input__field--disabled{opacity:.5}.kf-input__field--readonly{background:var(--kf-color-gray-100)}.kf-input__control:read-only{cursor:default}.kf-input__icon{color:var(--kf-color-text-muted)}.kf-input__field--invalid .kf-input__icon{color:var(--kf-color-error)}.kf-input__control{flex:1;min-width:0;border:none;outline:none;background:transparent;font:inherit;color:var(--kf-color-text);padding:var(--kf-space-sm) 0}.kf-input__control::placeholder{color:var(--kf-color-text-muted)}.kf-input__control:disabled{cursor:not-allowed}.kf-input__suffix{flex:none;color:var(--kf-color-text-muted);font-size:.875rem}.kf-input__toggle{flex:none;display:inline-flex;align-items:center;justify-content:center;border:none;background:transparent;padding:var(--kf-space-xs);margin:0 calc(var(--kf-space-xs) * -1);color:var(--kf-color-text-muted);cursor:pointer;border-radius:var(--kf-radius-xs);transition:color .14s}.kf-input__toggle:hover{color:var(--kf-color-text)}.kf-input__toggle:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "component", type: FieldLabel, selector: "kf-field-label", inputs: ["for", "required"] }, { kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size", "rotate", "filled"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
4639
|
+
], viewQueries: [{ propertyName: "nativeInput", first: true, predicate: ["nativeInput"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (label()) {\n <kf-field-label [for]=\"inputId\" [required]=\"required()\">{{ label() }}</kf-field-label>\n}\n<div\n class=\"kf-input__field\"\n [class.kf-input__field--disabled]=\"isDisabled()\"\n [class.kf-input__field--readonly]=\"readonly()\"\n [class.kf-input__field--focused]=\"focused()\"\n [class.kf-input__field--invalid]=\"invalid()\"\n>\n @if (icon(); as iconName) {\n <kf-icon class=\"kf-input__icon\" [name]=\"iconName\" size=\"sm\" />\n }\n <input\n #nativeInput\n [id]=\"inputId\"\n class=\"kf-input__control\"\n [type]=\"effectiveType()\"\n [placeholder]=\"placeholder()\"\n [attr.autocomplete]=\"autocomplete() || null\"\n [attr.name]=\"name() || null\"\n [attr.inputmode]=\"inputmode() || null\"\n [disabled]=\"isDisabled()\"\n [required]=\"required()\"\n [attr.readonly]=\"readonly() ? true : null\"\n [attr.aria-describedby]=\"errorMessage() ? errorId : hint() ? hintId : null\"\n [attr.aria-invalid]=\"invalid() ? true : null\"\n [attr.aria-label]=\"label() ? null : ariaLabel() || null\"\n [value]=\"value()\"\n (input)=\"onInput(nativeInput.value)\"\n (blur)=\"onBlur()\"\n (focus)=\"focused.set(true)\"\n />\n @if (showClear()) {\n <button\n type=\"button\"\n class=\"kf-input__clear\"\n [attr.aria-label]=\"clearLabel()\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"onClear()\"\n >\n <kf-icon name=\"x\" size=\"sm\" />\n </button>\n }\n @if (showPasswordToggle()) {\n <button\n type=\"button\"\n class=\"kf-input__toggle\"\n [attr.aria-label]=\"passwordVisible() ? 'Ocultar contrase\u00F1a' : 'Mostrar contrase\u00F1a'\"\n [attr.aria-pressed]=\"passwordVisible()\"\n (click)=\"onTogglePasswordVisibility()\"\n >\n <kf-icon [name]=\"passwordVisible() ? 'eye-off' : 'eye'\" size=\"sm\" />\n </button>\n } @else if (suffix()) {\n <span class=\"kf-input__suffix\">{{ suffix() }}</span>\n }\n</div>\n@if (errorMessage(); as message) {\n <span class=\"kf-input__error kf-field-error\" [id]=\"errorId\" role=\"alert\">{{ message }}</span>\n} @else if (hint()) {\n <span class=\"kf-input__hint kf-field-hint\" [id]=\"hintId\">{{ hint() }}</span>\n}\n", styles: [".kf-field-hint{font-size:.75rem;color:var(--kf-color-text-muted)}.kf-field-error{display:flex;align-items:center;gap:var(--kf-space-xs);font-size:.75rem;font-weight:600;color:var(--kf-color-error-strong)}:host{display:flex;flex-direction:column;gap:var(--kf-space-xs);font-family:var(--kf-font-body)}.kf-input__field{display:flex;align-items:center;gap:var(--kf-space-sm);padding:0 var(--kf-space-sm);background:var(--kf-color-surface);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);transition:background .14s,border-color .14s,box-shadow .14s}.kf-input__field--focused{background:var(--kf-color-white);border-color:var(--kf-color-primary);box-shadow:var(--kf-shadow-focus)}.kf-input__field--invalid{border-color:var(--kf-color-error)}.kf-input__field--invalid.kf-input__field--focused{box-shadow:var(--kf-shadow-focus-error)}.kf-input__field--disabled{opacity:.5}.kf-input__field--readonly{background:var(--kf-color-gray-100)}.kf-input__control:read-only{cursor:default}.kf-input__icon{color:var(--kf-color-text-muted)}.kf-input__field--invalid .kf-input__icon{color:var(--kf-color-error)}.kf-input__control{flex:1;min-width:0;border:none;outline:none;background:transparent;font:inherit;color:var(--kf-color-text);padding:var(--kf-space-sm) 0}.kf-input__control::placeholder{color:var(--kf-color-text-muted)}.kf-input__control:disabled{cursor:not-allowed}.kf-input__suffix{flex:none;color:var(--kf-color-text-muted);font-size:.875rem}.kf-input__control::-webkit-search-cancel-button{-webkit-appearance:none;appearance:none}.kf-input__clear,.kf-input__toggle{flex:none;display:inline-flex;align-items:center;justify-content:center;border:none;background:transparent;padding:var(--kf-space-xs);margin:0 calc(var(--kf-space-xs) * -1);color:var(--kf-color-text-muted);cursor:pointer;border-radius:var(--kf-radius-xs);transition:color .14s}.kf-input__clear:hover,.kf-input__toggle:hover{color:var(--kf-color-text)}.kf-input__clear:focus-visible,.kf-input__toggle:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "component", type: FieldLabel, selector: "kf-field-label", inputs: ["for", "required"] }, { kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size", "rotate", "filled"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
3899
4640
|
}
|
|
3900
4641
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Input, decorators: [{
|
|
3901
4642
|
type: Component,
|
|
@@ -3905,10 +4646,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
|
|
|
3905
4646
|
useExisting: forwardRef(() => Input),
|
|
3906
4647
|
multi: true,
|
|
3907
4648
|
},
|
|
3908
|
-
], template: "@if (label()) {\n <kf-field-label [for]=\"inputId\" [required]=\"required()\">{{ label() }}</kf-field-label>\n}\n<div\n class=\"kf-input__field\"\n [class.kf-input__field--disabled]=\"isDisabled()\"\n [class.kf-input__field--readonly]=\"readonly()\"\n [class.kf-input__field--focused]=\"focused()\"\n [class.kf-input__field--invalid]=\"invalid()\"\n>\n @if (icon(); as iconName) {\n <kf-icon class=\"kf-input__icon\" [name]=\"iconName\" size=\"sm\" />\n }\n <input\n #nativeInput\n [id]=\"inputId\"\n class=\"kf-input__control\"\n [type]=\"effectiveType()\"\n [placeholder]=\"placeholder()\"\n [attr.inputmode]=\"inputmode() || null\"\n [disabled]=\"isDisabled()\"\n [required]=\"required()\"\n [attr.readonly]=\"readonly() ? true : null\"\n [attr.aria-describedby]=\"errorMessage() ? errorId : hint() ? hintId : null\"\n [attr.aria-invalid]=\"invalid() ? true : null\"\n [value]=\"value()\"\n (input)=\"onInput(nativeInput.value)\"\n (blur)=\"onBlur()\"\n (focus)=\"focused.set(true)\"\n />\n @if (showPasswordToggle()) {\n <button\n type=\"button\"\n class=\"kf-input__toggle\"\n [attr.aria-label]=\"passwordVisible() ? 'Ocultar contrase\u00F1a' : 'Mostrar contrase\u00F1a'\"\n [attr.aria-pressed]=\"passwordVisible()\"\n (click)=\"onTogglePasswordVisibility()\"\n >\n <kf-icon [name]=\"passwordVisible() ? 'eye-off' : 'eye'\" size=\"sm\" />\n </button>\n } @else if (suffix()) {\n <span class=\"kf-input__suffix\">{{ suffix() }}</span>\n }\n</div>\n@if (errorMessage(); as message) {\n <span class=\"kf-input__error kf-field-error\" [id]=\"errorId\" role=\"alert\">{{ message }}</span>\n} @else if (hint()) {\n <span class=\"kf-input__hint kf-field-hint\" [id]=\"hintId\">{{ hint() }}</span>\n}\n", styles: [".kf-field-hint{font-size:.75rem;color:var(--kf-color-text-muted)}.kf-field-error{display:flex;align-items:center;gap:var(--kf-space-xs);font-size:.75rem;font-weight:600;color:var(--kf-color-error-strong)}:host{display:flex;flex-direction:column;gap:var(--kf-space-xs);font-family:var(--kf-font-body)}.kf-input__field{display:flex;align-items:center;gap:var(--kf-space-sm);padding:0 var(--kf-space-sm);background:var(--kf-color-surface);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);transition:background .14s,border-color .14s,box-shadow .14s}.kf-input__field--focused{background:var(--kf-color-white);border-color:var(--kf-color-primary);box-shadow:var(--kf-shadow-focus)}.kf-input__field--invalid{border-color:var(--kf-color-error)}.kf-input__field--invalid.kf-input__field--focused{box-shadow:var(--kf-shadow-focus-error)}.kf-input__field--disabled{opacity:.5}.kf-input__field--readonly{background:var(--kf-color-gray-100)}.kf-input__control:read-only{cursor:default}.kf-input__icon{color:var(--kf-color-text-muted)}.kf-input__field--invalid .kf-input__icon{color:var(--kf-color-error)}.kf-input__control{flex:1;min-width:0;border:none;outline:none;background:transparent;font:inherit;color:var(--kf-color-text);padding:var(--kf-space-sm) 0}.kf-input__control::placeholder{color:var(--kf-color-text-muted)}.kf-input__control:disabled{cursor:not-allowed}.kf-input__suffix{flex:none;color:var(--kf-color-text-muted);font-size:.875rem}.kf-input__toggle{flex:none;display:inline-flex;align-items:center;justify-content:center;border:none;background:transparent;padding:var(--kf-space-xs);margin:0 calc(var(--kf-space-xs) * -1);color:var(--kf-color-text-muted);cursor:pointer;border-radius:var(--kf-radius-xs);transition:color .14s}.kf-input__toggle:hover{color:var(--kf-color-text)}.kf-input__toggle:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}\n"] }]
|
|
3909
|
-
}], propDecorators: { nativeInput: [{ type: i0.ViewChild, args: ['nativeInput', { isSignal: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], suffix: [{ type: i0.Input, args: [{ isSignal: true, alias: "suffix", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], errorMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMessage", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], inputmode: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputmode", required: false }] }], toggleable: [{ type: i0.Input, args: [{ isSignal: true, alias: "toggleable", required: false }] }] } });
|
|
4649
|
+
], template: "@if (label()) {\n <kf-field-label [for]=\"inputId\" [required]=\"required()\">{{ label() }}</kf-field-label>\n}\n<div\n class=\"kf-input__field\"\n [class.kf-input__field--disabled]=\"isDisabled()\"\n [class.kf-input__field--readonly]=\"readonly()\"\n [class.kf-input__field--focused]=\"focused()\"\n [class.kf-input__field--invalid]=\"invalid()\"\n>\n @if (icon(); as iconName) {\n <kf-icon class=\"kf-input__icon\" [name]=\"iconName\" size=\"sm\" />\n }\n <input\n #nativeInput\n [id]=\"inputId\"\n class=\"kf-input__control\"\n [type]=\"effectiveType()\"\n [placeholder]=\"placeholder()\"\n [attr.autocomplete]=\"autocomplete() || null\"\n [attr.name]=\"name() || null\"\n [attr.inputmode]=\"inputmode() || null\"\n [disabled]=\"isDisabled()\"\n [required]=\"required()\"\n [attr.readonly]=\"readonly() ? true : null\"\n [attr.aria-describedby]=\"errorMessage() ? errorId : hint() ? hintId : null\"\n [attr.aria-invalid]=\"invalid() ? true : null\"\n [attr.aria-label]=\"label() ? null : ariaLabel() || null\"\n [value]=\"value()\"\n (input)=\"onInput(nativeInput.value)\"\n (blur)=\"onBlur()\"\n (focus)=\"focused.set(true)\"\n />\n @if (showClear()) {\n <button\n type=\"button\"\n class=\"kf-input__clear\"\n [attr.aria-label]=\"clearLabel()\"\n (mousedown)=\"$event.preventDefault()\"\n (click)=\"onClear()\"\n >\n <kf-icon name=\"x\" size=\"sm\" />\n </button>\n }\n @if (showPasswordToggle()) {\n <button\n type=\"button\"\n class=\"kf-input__toggle\"\n [attr.aria-label]=\"passwordVisible() ? 'Ocultar contrase\u00F1a' : 'Mostrar contrase\u00F1a'\"\n [attr.aria-pressed]=\"passwordVisible()\"\n (click)=\"onTogglePasswordVisibility()\"\n >\n <kf-icon [name]=\"passwordVisible() ? 'eye-off' : 'eye'\" size=\"sm\" />\n </button>\n } @else if (suffix()) {\n <span class=\"kf-input__suffix\">{{ suffix() }}</span>\n }\n</div>\n@if (errorMessage(); as message) {\n <span class=\"kf-input__error kf-field-error\" [id]=\"errorId\" role=\"alert\">{{ message }}</span>\n} @else if (hint()) {\n <span class=\"kf-input__hint kf-field-hint\" [id]=\"hintId\">{{ hint() }}</span>\n}\n", styles: [".kf-field-hint{font-size:.75rem;color:var(--kf-color-text-muted)}.kf-field-error{display:flex;align-items:center;gap:var(--kf-space-xs);font-size:.75rem;font-weight:600;color:var(--kf-color-error-strong)}:host{display:flex;flex-direction:column;gap:var(--kf-space-xs);font-family:var(--kf-font-body)}.kf-input__field{display:flex;align-items:center;gap:var(--kf-space-sm);padding:0 var(--kf-space-sm);background:var(--kf-color-surface);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);transition:background .14s,border-color .14s,box-shadow .14s}.kf-input__field--focused{background:var(--kf-color-white);border-color:var(--kf-color-primary);box-shadow:var(--kf-shadow-focus)}.kf-input__field--invalid{border-color:var(--kf-color-error)}.kf-input__field--invalid.kf-input__field--focused{box-shadow:var(--kf-shadow-focus-error)}.kf-input__field--disabled{opacity:.5}.kf-input__field--readonly{background:var(--kf-color-gray-100)}.kf-input__control:read-only{cursor:default}.kf-input__icon{color:var(--kf-color-text-muted)}.kf-input__field--invalid .kf-input__icon{color:var(--kf-color-error)}.kf-input__control{flex:1;min-width:0;border:none;outline:none;background:transparent;font:inherit;color:var(--kf-color-text);padding:var(--kf-space-sm) 0}.kf-input__control::placeholder{color:var(--kf-color-text-muted)}.kf-input__control:disabled{cursor:not-allowed}.kf-input__suffix{flex:none;color:var(--kf-color-text-muted);font-size:.875rem}.kf-input__control::-webkit-search-cancel-button{-webkit-appearance:none;appearance:none}.kf-input__clear,.kf-input__toggle{flex:none;display:inline-flex;align-items:center;justify-content:center;border:none;background:transparent;padding:var(--kf-space-xs);margin:0 calc(var(--kf-space-xs) * -1);color:var(--kf-color-text-muted);cursor:pointer;border-radius:var(--kf-radius-xs);transition:color .14s}.kf-input__clear:hover,.kf-input__toggle:hover{color:var(--kf-color-text)}.kf-input__clear:focus-visible,.kf-input__toggle:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}\n"] }]
|
|
4650
|
+
}], propDecorators: { nativeInput: [{ type: i0.ViewChild, args: ['nativeInput', { isSignal: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], suffix: [{ type: i0.Input, args: [{ isSignal: true, alias: "suffix", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], errorMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMessage", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], autocomplete: [{ type: i0.Input, args: [{ isSignal: true, alias: "autocomplete", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], inputmode: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputmode", required: false }] }], toggleable: [{ type: i0.Input, args: [{ isSignal: true, alias: "toggleable", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], clearable: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearable", required: false }] }], clearLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "clearLabel", required: false }] }], cleared: [{ type: i0.Output, args: ["cleared"] }] } });
|
|
3910
4651
|
|
|
3911
|
-
let nextId = 0;
|
|
4652
|
+
let nextId$1 = 0;
|
|
3912
4653
|
/**
|
|
3913
4654
|
* Dropdown de selección múltiple con checkboxes internos. El panel flotante
|
|
3914
4655
|
* se conecta al trigger vía `@angular/cdk/overlay` — el mismo mecanismo que
|
|
@@ -3965,7 +4706,7 @@ class MultiSelect {
|
|
|
3965
4706
|
searchThreshold = input(7, /* @ts-ignore */
|
|
3966
4707
|
...(ngDevMode ? [{ debugName: "searchThreshold" }] : /* istanbul ignore next */ []));
|
|
3967
4708
|
valuesChange = output();
|
|
3968
|
-
instanceId = `kf-multi-select-${nextId++}`;
|
|
4709
|
+
instanceId = `kf-multi-select-${nextId$1++}`;
|
|
3969
4710
|
triggerId = `${this.instanceId}-trigger`;
|
|
3970
4711
|
listboxId = `${this.instanceId}-listbox`;
|
|
3971
4712
|
isOpen = signal(false, /* @ts-ignore */
|
|
@@ -4162,7 +4903,7 @@ class Filter {
|
|
|
4162
4903
|
this.fieldChange.emit({ key, value });
|
|
4163
4904
|
}
|
|
4164
4905
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Filter, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4165
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Filter, isStandalone: true, selector: "kf-filter", inputs: { fields: { classPropertyName: "fields", publicName: "fields", isSignal: true, isRequired: true, transformFunction: null }, values: { classPropertyName: "values", publicName: "values", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, mobileTitle: { classPropertyName: "mobileTitle", publicName: "mobileTitle", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { fieldChange: "fieldChange" }, ngImport: i0, template: "<ng-template #fieldsTpl let-stacked=\"stacked\">\n @for (field of fields(); track field.key) {\n @switch (field.type) {\n @case ('select') {\n <kf-select\n [label]=\"field.label ?? ''\"\n [options]=\"optionsFor(field.key)\"\n [value]=\"singleValueFor(field.key)\"\n [placeholder]=\"field.placeholder ?? ''\"\n [style.width]=\"stacked ? '100%' : fieldWidth(field)\"\n (valueChange)=\"onFieldChange(field.key, $event)\"\n />\n }\n @case ('multi-select') {\n <kf-multi-select\n [label]=\"field.label ?? ''\"\n [options]=\"multiOptionsFor(field.key)\"\n [values]=\"multiValueFor(field.key)\"\n [placeholder]=\"field.placeholder ?? ''\"\n [style.width]=\"stacked ? '100%' : fieldWidth(field)\"\n (valuesChange)=\"onFieldChange(field.key, $event)\"\n />\n }\n @case ('search') {\n <kf-input\n [label]=\"field.label ?? ''\"\n [icon]=\"field.icon ?? 'search'\"\n [placeholder]=\"field.placeholder ?? ''\"\n [style.width]=\"stacked ? '100%' : fieldWidth(field)\"\n [ngModel]=\"singleValueFor(field.key)\"\n (ngModelChange)=\"onFieldChange(field.key, $event)\"\n />\n }\n }\n @if (!stacked && field.showDivider) {\n <kf-divider orientation=\"vertical\" />\n }\n }\n</ng-template>\n\n<div class=\"kf-filter kf-filter--row\">\n <ng-container [ngTemplateOutlet]=\"fieldsTpl\" [ngTemplateOutletContext]=\"{ stacked: false }\" />\n</div>\n\n<kf-collapsible class=\"kf-filter__collapsible\" [title]=\"mobileTitle()\">\n <div class=\"kf-filter kf-filter--stack\">\n <ng-container [ngTemplateOutlet]=\"fieldsTpl\" [ngTemplateOutletContext]=\"{ stacked: true }\" />\n </div>\n</kf-collapsible>\n", styles: ["@charset \"UTF-8\";:host{display:block}.kf-filter--row{display:flex;flex-wrap:wrap;align-items:flex-end;gap:var(--kf-space-md)}.kf-filter--stack{display:flex;flex-direction:column;gap:var(--kf-space-md)}.kf-filter__collapsible{display:none}@media(max-width:768px){.kf-filter--row{display:none}.kf-filter__collapsible{display:block}}\n"], dependencies: [{ kind: "component", type: Collapsible, selector: "kf-collapsible", inputs: ["title", "expanded"], outputs: ["expandedChange"] }, { kind: "component", type: Divider, selector: "kf-divider", inputs: ["orientation"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: Input, selector: "kf-input", inputs: ["label", "required", "icon", "suffix", "hint", "invalid", "errorMessage", "disabled", "readonly", "type", "placeholder", "inputmode", "toggleable"] }, { kind: "component", type: MultiSelect, selector: "kf-multi-select", inputs: ["label", "options", "values", "placeholder", "disabled", "width", "searchable", "searchThreshold"], outputs: ["valuesChange"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: Select, selector: "kf-select", inputs: ["label", "options", "value", "placeholder", "disabled", "invalid", "errorMessage", "variant", "width", "searchable", "searchThreshold"], outputs: ["valueChange"] }] });
|
|
4906
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Filter, isStandalone: true, selector: "kf-filter", inputs: { fields: { classPropertyName: "fields", publicName: "fields", isSignal: true, isRequired: true, transformFunction: null }, values: { classPropertyName: "values", publicName: "values", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, mobileTitle: { classPropertyName: "mobileTitle", publicName: "mobileTitle", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { fieldChange: "fieldChange" }, ngImport: i0, template: "<ng-template #fieldsTpl let-stacked=\"stacked\">\n @for (field of fields(); track field.key) {\n @switch (field.type) {\n @case ('select') {\n <kf-select\n [label]=\"field.label ?? ''\"\n [options]=\"optionsFor(field.key)\"\n [value]=\"singleValueFor(field.key)\"\n [placeholder]=\"field.placeholder ?? ''\"\n [style.width]=\"stacked ? '100%' : fieldWidth(field)\"\n (valueChange)=\"onFieldChange(field.key, $event)\"\n />\n }\n @case ('multi-select') {\n <kf-multi-select\n [label]=\"field.label ?? ''\"\n [options]=\"multiOptionsFor(field.key)\"\n [values]=\"multiValueFor(field.key)\"\n [placeholder]=\"field.placeholder ?? ''\"\n [style.width]=\"stacked ? '100%' : fieldWidth(field)\"\n (valuesChange)=\"onFieldChange(field.key, $event)\"\n />\n }\n @case ('search') {\n <kf-input\n [label]=\"field.label ?? ''\"\n [icon]=\"field.icon ?? 'search'\"\n [placeholder]=\"field.placeholder ?? ''\"\n [style.width]=\"stacked ? '100%' : fieldWidth(field)\"\n [ngModel]=\"singleValueFor(field.key)\"\n (ngModelChange)=\"onFieldChange(field.key, $event)\"\n />\n }\n }\n @if (!stacked && field.showDivider) {\n <kf-divider orientation=\"vertical\" />\n }\n }\n</ng-template>\n\n<div class=\"kf-filter kf-filter--row\">\n <ng-container [ngTemplateOutlet]=\"fieldsTpl\" [ngTemplateOutletContext]=\"{ stacked: false }\" />\n</div>\n\n<kf-collapsible class=\"kf-filter__collapsible\" [title]=\"mobileTitle()\">\n <div class=\"kf-filter kf-filter--stack\">\n <ng-container [ngTemplateOutlet]=\"fieldsTpl\" [ngTemplateOutletContext]=\"{ stacked: true }\" />\n </div>\n</kf-collapsible>\n", styles: ["@charset \"UTF-8\";:host{display:block}.kf-filter--row{display:flex;flex-wrap:wrap;align-items:flex-end;gap:var(--kf-space-md)}.kf-filter--stack{display:flex;flex-direction:column;gap:var(--kf-space-md)}.kf-filter__collapsible{display:none}@media(max-width:768px){.kf-filter--row{display:none}.kf-filter__collapsible{display:block}}\n"], dependencies: [{ kind: "component", type: Collapsible, selector: "kf-collapsible", inputs: ["title", "expanded"], outputs: ["expandedChange"] }, { kind: "component", type: Divider, selector: "kf-divider", inputs: ["orientation"] }, { kind: "ngmodule", type: FormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.NgModel, selector: "[ngModel]:not([formControlName]):not([formControl])", inputs: ["name", "disabled", "ngModel", "ngModelOptions"], outputs: ["ngModelChange"], exportAs: ["ngModel"] }, { kind: "component", type: Input, selector: "kf-input", inputs: ["label", "required", "icon", "suffix", "hint", "invalid", "errorMessage", "disabled", "readonly", "type", "placeholder", "autocomplete", "name", "inputmode", "toggleable", "ariaLabel", "clearable", "clearLabel"], outputs: ["cleared"] }, { kind: "component", type: MultiSelect, selector: "kf-multi-select", inputs: ["label", "options", "values", "placeholder", "disabled", "width", "searchable", "searchThreshold"], outputs: ["valuesChange"] }, { kind: "directive", type: NgTemplateOutlet, selector: "[ngTemplateOutlet]", inputs: ["ngTemplateOutletContext", "ngTemplateOutlet", "ngTemplateOutletInjector"] }, { kind: "component", type: Select, selector: "kf-select", inputs: ["label", "options", "value", "placeholder", "disabled", "invalid", "errorMessage", "variant", "width", "searchable", "searchThreshold", "ariaLabel", "icon", "panelTitle", "panelWidth"], outputs: ["valueChange"] }] });
|
|
4166
4907
|
}
|
|
4167
4908
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Filter, decorators: [{
|
|
4168
4909
|
type: Component,
|
|
@@ -4173,19 +4914,31 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
|
|
|
4173
4914
|
* Fila de chips tipo "pill" para filtros de una sola selección.
|
|
4174
4915
|
*
|
|
4175
4916
|
* Cada opción puede pasarse como string simple (se usa como value y label
|
|
4176
|
-
* a la vez) o como objeto { value, label } cuando el texto
|
|
4177
|
-
* del valor interno.
|
|
4917
|
+
* a la vez) o como objeto `{ value, label, count? }` cuando el texto
|
|
4918
|
+
* mostrado difiere del valor interno o cuando cada chip lleva un contador.
|
|
4178
4919
|
*
|
|
4179
4920
|
* El chip activo se resalta con `--kf-color-primary` — mismo color de marca
|
|
4180
4921
|
* que `kf-tabs`/`kf-select` para su propio estado activo, así el filtro
|
|
4181
4922
|
* seleccionado se lee con el color del tema activo, no un negro fijo que
|
|
4182
4923
|
* no cambia entre marcas.
|
|
4183
4924
|
*
|
|
4925
|
+
* Densidad: el padding horizontal de cada chip sale de
|
|
4926
|
+
* `--kf-filter-chips-padding-inline` (default `--kf-space-md`). Un
|
|
4927
|
+
* contenedor puede reducirlo sin tocar este componente — `kf-list-toolbar`
|
|
4928
|
+
* lo baja a 12px en su modo compacto.
|
|
4929
|
+
*
|
|
4184
4930
|
* <kf-filter-chips [options]="['Todos', 'Activos', 'Archivados']"
|
|
4185
4931
|
* [value]="filtro"
|
|
4186
4932
|
* (valueChange)="filtro = $event" />
|
|
4187
4933
|
*
|
|
4188
|
-
*
|
|
4934
|
+
* `layout="scroll"` deja todos los chips en UNA línea con desplazamiento
|
|
4935
|
+
* horizontal (para mobile: los filtros no ocupan más alto que una fila); el
|
|
4936
|
+
* default `wrap` los reparte en varias líneas. `deselectable` permite
|
|
4937
|
+
* quitar el filtro volviendo a tocar el chip activo (emite `''`), para el
|
|
4938
|
+
* caso "ningún chip = sin filtro".
|
|
4939
|
+
*
|
|
4940
|
+
* <kf-filter-chips ariaLabel="Filtrar por estado"
|
|
4941
|
+
* [options]="[{ value: '', label: 'Todas', count: 12 }]"
|
|
4189
4942
|
* [value]="filtro"
|
|
4190
4943
|
* (valueChange)="filtro = $event" />
|
|
4191
4944
|
*/
|
|
@@ -4194,23 +4947,34 @@ class FilterChips {
|
|
|
4194
4947
|
...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
|
|
4195
4948
|
value = input('', /* @ts-ignore */
|
|
4196
4949
|
...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
4950
|
+
/** Nombre accesible del grupo (ej. "Filtrar por estado"). Sin él, el grupo se anuncia sin nombre. */
|
|
4951
|
+
ariaLabel = input(/* @ts-ignore */
|
|
4952
|
+
...(ngDevMode ? [undefined, { debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
4953
|
+
/** `wrap` (default): varias líneas. `scroll`: una sola línea con desplazamiento horizontal. */
|
|
4954
|
+
layout = input('wrap', /* @ts-ignore */
|
|
4955
|
+
...(ngDevMode ? [{ debugName: "layout" }] : /* istanbul ignore next */ []));
|
|
4956
|
+
/** Tocar el chip activo lo desactiva y emite `''`. Default `false` (comportamiento histórico). */
|
|
4957
|
+
deselectable = input(false, { ...(ngDevMode ? { debugName: "deselectable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
4197
4958
|
valueChange = output();
|
|
4198
|
-
/** Normaliza cada opción string/objeto a la forma { value, label }
|
|
4959
|
+
/** Normaliza cada opción string/objeto a la forma `{ value, label, count? }`. */
|
|
4199
4960
|
normalizedOptions = computed(() => this.options().map((option) => typeof option === 'string' ? { value: option, label: option } : option), /* @ts-ignore */
|
|
4200
4961
|
...(ngDevMode ? [{ debugName: "normalizedOptions" }] : /* istanbul ignore next */ []));
|
|
4201
4962
|
select(value) {
|
|
4202
4963
|
if (value === this.value()) {
|
|
4964
|
+
if (this.deselectable() && value !== '') {
|
|
4965
|
+
this.valueChange.emit('');
|
|
4966
|
+
}
|
|
4203
4967
|
return;
|
|
4204
4968
|
}
|
|
4205
4969
|
this.valueChange.emit(value);
|
|
4206
4970
|
}
|
|
4207
4971
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: FilterChips, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4208
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: FilterChips, isStandalone: true, selector: "kf-filter-chips", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<div
|
|
4972
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: FilterChips, isStandalone: true, selector: "kf-filter-chips", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, layout: { classPropertyName: "layout", publicName: "layout", isSignal: true, isRequired: false, transformFunction: null }, deselectable: { classPropertyName: "deselectable", publicName: "deselectable", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "<div\n class=\"kf-filter-chips\"\n [class.kf-filter-chips--scroll]=\"layout() === 'scroll'\"\n role=\"group\"\n [attr.aria-label]=\"ariaLabel() || null\"\n>\n @for (option of normalizedOptions(); track option.value) {\n <button\n type=\"button\"\n class=\"kf-filter-chips__chip\"\n [class.kf-filter-chips__chip--active]=\"option.value === value()\"\n [attr.aria-pressed]=\"option.value === value()\"\n (click)=\"select(option.value)\"\n >\n {{ option.label }}\n @if (option.count !== undefined) {\n <span class=\"kf-filter-chips__count\">{{ option.count }}</span>\n }\n </button>\n }\n</div>\n", styles: [":host{display:block}.kf-filter-chips{display:flex;flex-wrap:wrap;gap:var(--kf-space-xs)}.kf-filter-chips--scroll{flex-wrap:nowrap;overflow-x:auto;overscroll-behavior-x:contain;scrollbar-width:thin;padding:3px;margin:-3px}.kf-filter-chips__chip{display:inline-flex;align-items:center;gap:var(--kf-space-xs);font:inherit;flex:none;white-space:nowrap;padding:var(--kf-space-xs) var(--kf-filter-chips-padding-inline, 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-primary);border-color:var(--kf-color-primary);color:var(--kf-color-white)}.kf-filter-chips__count{font-size:var(--kf-font-size-caption);font-variant-numeric:tabular-nums}@media(prefers-reduced-motion:reduce){.kf-filter-chips__chip{transition:none}}\n"] });
|
|
4209
4973
|
}
|
|
4210
4974
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: FilterChips, decorators: [{
|
|
4211
4975
|
type: Component,
|
|
4212
|
-
args: [{ selector: 'kf-filter-chips', template: "<div
|
|
4213
|
-
}], 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"] }] } });
|
|
4976
|
+
args: [{ selector: 'kf-filter-chips', template: "<div\n class=\"kf-filter-chips\"\n [class.kf-filter-chips--scroll]=\"layout() === 'scroll'\"\n role=\"group\"\n [attr.aria-label]=\"ariaLabel() || null\"\n>\n @for (option of normalizedOptions(); track option.value) {\n <button\n type=\"button\"\n class=\"kf-filter-chips__chip\"\n [class.kf-filter-chips__chip--active]=\"option.value === value()\"\n [attr.aria-pressed]=\"option.value === value()\"\n (click)=\"select(option.value)\"\n >\n {{ option.label }}\n @if (option.count !== undefined) {\n <span class=\"kf-filter-chips__count\">{{ option.count }}</span>\n }\n </button>\n }\n</div>\n", styles: [":host{display:block}.kf-filter-chips{display:flex;flex-wrap:wrap;gap:var(--kf-space-xs)}.kf-filter-chips--scroll{flex-wrap:nowrap;overflow-x:auto;overscroll-behavior-x:contain;scrollbar-width:thin;padding:3px;margin:-3px}.kf-filter-chips__chip{display:inline-flex;align-items:center;gap:var(--kf-space-xs);font:inherit;flex:none;white-space:nowrap;padding:var(--kf-space-xs) var(--kf-filter-chips-padding-inline, 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-primary);border-color:var(--kf-color-primary);color:var(--kf-color-white)}.kf-filter-chips__count{font-size:var(--kf-font-size-caption);font-variant-numeric:tabular-nums}@media(prefers-reduced-motion:reduce){.kf-filter-chips__chip{transition:none}}\n"] }]
|
|
4977
|
+
}], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], layout: [{ type: i0.Input, args: [{ isSignal: true, alias: "layout", required: false }] }], deselectable: [{ type: i0.Input, args: [{ isSignal: true, alias: "deselectable", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }] } });
|
|
4214
4978
|
|
|
4215
4979
|
/**
|
|
4216
4980
|
* Pie de página general del portal (página 38): logo + copyright a la
|
|
@@ -4327,6 +5091,408 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
|
|
|
4327
5091
|
args: [{ selector: 'kf-journey-stepper', imports: [Icon], template: "<div\n #track\n class=\"kf-journey-stepper\"\n [class.kf-journey-stepper--vertical]=\"orientation() === 'vertical'\"\n [class.kf-journey-stepper--card]=\"surface() === 'card'\"\n [class.kf-journey-stepper--semantic]=\"colorScheme() === 'semantic'\"\n [class.kf-journey-stepper--overflowing]=\"hasOverflow()\"\n>\n @for (step of resolvedSteps(); track step.key; let last = $last) {\n <div class=\"kf-journey-stepper__step\" [class]=\"'kf-journey-stepper__step--' + step.status\">\n @if (clickable()) {\n <button type=\"button\" class=\"kf-journey-stepper__dot\" (click)=\"stepClick.emit(step.key)\">\n <kf-icon [name]=\"dotIcon(step.status, step.icon)\" size=\"sm\" />\n </button>\n } @else {\n <span class=\"kf-journey-stepper__dot\">\n <kf-icon [name]=\"dotIcon(step.status, step.icon)\" size=\"sm\" />\n </span>\n }\n\n <div class=\"kf-journey-stepper__text\">\n <span class=\"kf-journey-stepper__label\">{{ step.label }}</span>\n @if (step.date) {\n <span class=\"kf-journey-stepper__date\">{{ step.date }}</span>\n }\n </div>\n </div>\n\n @if (!last && orientation() === 'horizontal') {\n <span\n class=\"kf-journey-stepper__line\"\n [class.kf-journey-stepper__line--done]=\"step.status === 'done'\"\n ></span>\n }\n }\n</div>\n", styles: [":host{display:block;min-width:0}.kf-journey-stepper--overflowing:not(.kf-journey-stepper--vertical){-webkit-mask-image:linear-gradient(to right,transparent 0,black 24px,black calc(100% - 24px),transparent 100%);mask-image:linear-gradient(to right,transparent 0,black 24px,black calc(100% - 24px),transparent 100%)}.kf-journey-stepper{display:flex;align-items:center;overflow-x:auto;padding-bottom:var(--kf-space-xs);font-family:var(--kf-font-body);scrollbar-width:thin;scrollbar-color:var(--kf-color-gray-400) transparent}.kf-journey-stepper::-webkit-scrollbar{height:6px}.kf-journey-stepper::-webkit-scrollbar-track{background:transparent}.kf-journey-stepper::-webkit-scrollbar-thumb{background-color:var(--kf-color-gray-400);border-radius:var(--kf-radius-pill)}.kf-journey-stepper::-webkit-scrollbar-thumb:hover{background-color:var(--kf-color-gray-500)}.kf-journey-stepper:not(.kf-journey-stepper--card){scrollbar-color:rgba(255,255,255,.3) transparent}.kf-journey-stepper:not(.kf-journey-stepper--card)::-webkit-scrollbar-thumb{background-color:#ffffff4d}.kf-journey-stepper:not(.kf-journey-stepper--card)::-webkit-scrollbar-thumb:hover{background-color:#ffffff73}.kf-journey-stepper--vertical{flex-direction:column;align-items:stretch;overflow-x:visible}.kf-journey-stepper__step{display:flex;align-items:center;flex-direction:column;gap:var(--kf-space-sm);flex:none}.kf-journey-stepper--vertical .kf-journey-stepper__step{flex-direction:row;align-items:flex-start;position:relative;padding-bottom:var(--kf-space-lg)}.kf-journey-stepper--vertical .kf-journey-stepper__step:last-child{padding-bottom:0}.kf-journey-stepper--vertical .kf-journey-stepper__step:before{content:\"\";position:absolute;left:13px;top:28px;bottom:0;width:2px;background:var(--kf-color-gray-200)}.kf-journey-stepper--vertical .kf-journey-stepper__step:last-child:before{display:none}.kf-journey-stepper--vertical:not(.kf-journey-stepper--semantic) .kf-journey-stepper__step--done:before{background:var(--kf-color-primary)}.kf-journey-stepper--semantic .kf-journey-stepper__step--done:before{background:var(--kf-color-success)}.kf-journey-stepper__dot{width:36px;height:36px;border-radius:var(--kf-radius-pill);display:grid;place-items:center;flex:none;z-index:1;border:2px solid transparent;background:transparent;cursor:default;font:inherit}.kf-journey-stepper--vertical .kf-journey-stepper__dot{width:28px;height:28px;border:none}button.kf-journey-stepper__dot{cursor:pointer}.kf-journey-stepper__step--pending .kf-journey-stepper__dot{background:#ffffff14;color:#ffffff80;border-color:#ffffff24}.kf-journey-stepper__step--done .kf-journey-stepper__dot{background:var(--kf-color-primary);color:var(--kf-color-white);border-color:var(--kf-color-primary)}.kf-journey-stepper__step--current .kf-journey-stepper__dot{background:var(--kf-color-white);color:var(--kf-color-primary);border-color:var(--kf-color-white);box-shadow:0 0 0 4px #ffffff29}.kf-journey-stepper__label{font-size:.72rem;font-weight:700;color:#ffffff8c;white-space:nowrap}.kf-journey-stepper__step--done .kf-journey-stepper__label,.kf-journey-stepper__step--current .kf-journey-stepper__label{color:var(--kf-color-white)}.kf-journey-stepper__line{flex:1;height:2px;background:#ffffff24;margin:0 var(--kf-space-xs);margin-bottom:20px;min-width:24px}.kf-journey-stepper__line--done{background:var(--kf-color-primary)}.kf-journey-stepper--card .kf-journey-stepper__step--pending .kf-journey-stepper__dot{background:var(--kf-color-surface);color:var(--kf-color-gray-400);border-color:var(--kf-color-border)}.kf-journey-stepper--card .kf-journey-stepper__step--current .kf-journey-stepper__dot{background:var(--kf-color-primary-soft);color:var(--kf-color-primary);border-color:var(--kf-color-primary);box-shadow:0 0 0 4px var(--kf-color-primary-soft)}.kf-journey-stepper--card .kf-journey-stepper__label{color:var(--kf-color-text-muted)}.kf-journey-stepper--card .kf-journey-stepper__step--done .kf-journey-stepper__label,.kf-journey-stepper--card .kf-journey-stepper__step--current .kf-journey-stepper__label{color:var(--kf-color-ink)}.kf-journey-stepper--card .kf-journey-stepper__line{background:var(--kf-color-border)}.kf-journey-stepper--vertical .kf-journey-stepper__text{display:flex;flex-direction:column;gap:2px;padding-top:3px}.kf-journey-stepper--vertical .kf-journey-stepper__label{font-size:.875rem;font-weight:700;color:var(--kf-color-gray-500);white-space:normal}.kf-journey-stepper--vertical .kf-journey-stepper__date{font-size:.78rem;color:var(--kf-color-gray-500)}.kf-journey-stepper--vertical .kf-journey-stepper__step--pending .kf-journey-stepper__dot{background:var(--kf-color-gray-100);color:var(--kf-color-gray-400)}.kf-journey-stepper--vertical .kf-journey-stepper__step--done .kf-journey-stepper__label,.kf-journey-stepper--vertical .kf-journey-stepper__step--current .kf-journey-stepper__label,.kf-journey-stepper--vertical .kf-journey-stepper__step--delayed .kf-journey-stepper__label{color:var(--kf-color-ink)}.kf-journey-stepper--vertical:not(.kf-journey-stepper--semantic) .kf-journey-stepper__step--done .kf-journey-stepper__dot{background:var(--kf-color-primary);color:var(--kf-color-white);box-shadow:none}.kf-journey-stepper--vertical:not(.kf-journey-stepper--semantic) .kf-journey-stepper__step--current .kf-journey-stepper__dot{background:var(--kf-color-primary-soft);color:var(--kf-color-primary);border-color:transparent;box-shadow:0 0 0 3px var(--kf-color-primary-soft)}.kf-journey-stepper--vertical:not(.kf-journey-stepper--semantic) .kf-journey-stepper__step--delayed .kf-journey-stepper__dot{background:var(--kf-color-gray-100);color:var(--kf-color-gray-400)}.kf-journey-stepper--vertical:not(.kf-journey-stepper--semantic) .kf-journey-stepper__step--current .kf-journey-stepper__date{color:var(--kf-color-primary);font-weight:600}.kf-journey-stepper--semantic .kf-journey-stepper__step--done .kf-journey-stepper__dot{background:var(--kf-color-success);color:var(--kf-color-white);box-shadow:none}.kf-journey-stepper--semantic .kf-journey-stepper__step--current .kf-journey-stepper__dot{background:var(--kf-color-warning-soft);color:var(--kf-color-warning-strong);border-color:transparent;box-shadow:0 0 0 3px var(--kf-color-warning-soft)}.kf-journey-stepper--semantic .kf-journey-stepper__step--delayed .kf-journey-stepper__dot{background:var(--kf-color-error-soft);color:var(--kf-color-error);border-color:transparent;box-shadow:0 0 0 3px var(--kf-color-error-soft)}.kf-journey-stepper--semantic .kf-journey-stepper__step--delayed .kf-journey-stepper__label{color:var(--kf-color-ink)}.kf-journey-stepper--vertical .kf-journey-stepper__step--done .kf-journey-stepper__date{color:var(--kf-color-gray-500)}.kf-journey-stepper--semantic .kf-journey-stepper__step--current .kf-journey-stepper__date{color:var(--kf-color-warning-strong);font-weight:600}.kf-journey-stepper--semantic .kf-journey-stepper__step--delayed .kf-journey-stepper__date{color:var(--kf-color-error);font-weight:600}\n"] }]
|
|
4328
5092
|
}], ctorParameters: () => [], propDecorators: { steps: [{ type: i0.Input, args: [{ isSignal: true, alias: "steps", required: true }] }], orientation: [{ type: i0.Input, args: [{ isSignal: true, alias: "orientation", required: false }] }], surface: [{ type: i0.Input, args: [{ isSignal: true, alias: "surface", required: false }] }], colorScheme: [{ type: i0.Input, args: [{ isSignal: true, alias: "colorScheme", required: false }] }], currentStep: [{ type: i0.Input, args: [{ isSignal: true, alias: "currentStep", required: false }] }], stepClick: [{ type: i0.Output, args: ["stepClick"] }], track: [{ type: i0.ViewChild, args: ['track', { isSignal: true }] }] } });
|
|
4329
5093
|
|
|
5094
|
+
/**
|
|
5095
|
+
* Margen de histéresis (px) para SUBIR de modo. Bajar es inmediato (si no
|
|
5096
|
+
* cabe, no cabe); subir exige este margen extra, así un ancho que oscila
|
|
5097
|
+
* justo en el umbral (scrollbar que aparece y desaparece, un contador que
|
|
5098
|
+
* pasa de 9 a 10) no hace parpadear la barra entre dos modos.
|
|
5099
|
+
*/
|
|
5100
|
+
const LIST_TOOLBAR_HYSTERESIS = 24;
|
|
5101
|
+
/**
|
|
5102
|
+
* Decide el modo a partir del modo actual y los anchos medidos. Función pura
|
|
5103
|
+
* para poder probar la histéresis sin layout real.
|
|
5104
|
+
*/
|
|
5105
|
+
function resolveListToolbarMode(widths, current, hysteresis = LIST_TOOLBAR_HYSTERESIS) {
|
|
5106
|
+
const { available, full, compact } = widths;
|
|
5107
|
+
const fullThreshold = current === 'full' ? full : full + hysteresis;
|
|
5108
|
+
if (available >= fullThreshold) {
|
|
5109
|
+
return 'full';
|
|
5110
|
+
}
|
|
5111
|
+
const compactThreshold = current === 'panel' ? compact + hysteresis : compact;
|
|
5112
|
+
if (available >= compactThreshold) {
|
|
5113
|
+
return 'compact';
|
|
5114
|
+
}
|
|
5115
|
+
return 'panel';
|
|
5116
|
+
}
|
|
5117
|
+
|
|
5118
|
+
/** Selector de elementos que pueden recibir foco por teclado dentro de un slot. */
|
|
5119
|
+
const FOCUSABLE$1 = 'button:not([disabled]), [href], input:not([disabled]), select:not([disabled]), textarea:not([disabled]), [tabindex]:not([tabindex="-1"])';
|
|
5120
|
+
/**
|
|
5121
|
+
* Barra de herramientas de un listado: buscador, filtros y acciones finales
|
|
5122
|
+
* (orden, cambio de vista) en UNA fila, que se adapta al ancho REAL que
|
|
5123
|
+
* tiene disponible — no al del viewport. El mismo listado puede renderizarse
|
|
5124
|
+
* con el menú lateral abierto o cerrado, dentro de un panel o a pantalla
|
|
5125
|
+
* completa, y una `@media` no distingue esos casos; la barra mide su propio
|
|
5126
|
+
* ancho con `ResizeObserver`.
|
|
5127
|
+
*
|
|
5128
|
+
* ## Cuándo usarla y cuándo `kf-filter`
|
|
5129
|
+
* - `kf-list-toolbar`: chips con contador (`kf-filter-chips`) + orden + cambio
|
|
5130
|
+
* de vista + panel de filtros en modo estrecho, fija al hacer scroll y
|
|
5131
|
+
* guiada por el ancho del contenedor.
|
|
5132
|
+
* - `kf-filter`: selects, multi-select y buscador de texto en una fila de
|
|
5133
|
+
* formulario, que se pliega en un `kf-collapsible` a ≤768px de viewport.
|
|
5134
|
+
* Si el listado se filtra con campos, usar `kf-filter`; no se combinan ni
|
|
5135
|
+
* se reimplementan entre sí.
|
|
5136
|
+
*
|
|
5137
|
+
* ## Slots
|
|
5138
|
+
* - `[kf-toolbar-search]`: el buscador (ej. `kf-input icon="search"`). Siempre visible; ocupa el espacio libre.
|
|
5139
|
+
* - `[kf-toolbar-filters]`: filtros rápidos (ej. `kf-filter-chips`).
|
|
5140
|
+
* - `[kf-toolbar-end]`: acciones al final de la fila (ej. `kf-select` de orden, `kf-view-toggle`).
|
|
5141
|
+
*
|
|
5142
|
+
* Cada slot acepta VARIOS elementos con el mismo atributo y los ubica en
|
|
5143
|
+
* fila con `gap`; marcar cada control (no un `<div>` envoltorio sin
|
|
5144
|
+
* `display: flex`, que apilaría sus hijos en bloque).
|
|
5145
|
+
*
|
|
5146
|
+
* La barra ocupa el ancho de su contenedor (`display: block`). Dentro de un
|
|
5147
|
+
* contenedor flex que ajusta su ancho al contenido, darle un ancho
|
|
5148
|
+
* explícito: si el contenedor mide lo que la barra pide, la barra mide lo
|
|
5149
|
+
* que el contenedor le da y el modo nunca puede subir de `panel`.
|
|
5150
|
+
* - default: contenido no visual que debe quedar SIEMPRE presente, como una
|
|
5151
|
+
* región `aria-live` con el conteo de resultados (nunca dentro de un slot
|
|
5152
|
+
* que se oculta).
|
|
5153
|
+
*
|
|
5154
|
+
* ## Modos (`mode`, `modeChange`, atributo `data-mode`)
|
|
5155
|
+
* - `full`: todo cabe en una fila.
|
|
5156
|
+
* - `compact`: cabe en una fila solo con chips más angostos. El host expone
|
|
5157
|
+
* `data-density="compact"` y redefine `--kf-filter-chips-padding-inline`
|
|
5158
|
+
* (12px), que `kf-filter-chips` adopta sin configuración extra.
|
|
5159
|
+
* - `panel`: no cabe. Se ocultan `filters` y `end` y aparece, junto al
|
|
5160
|
+
* buscador, un botón "Filtros" (`filtersLabel`) con un badge de
|
|
5161
|
+
* `activeCount` (filtros/orden distintos del valor por defecto) que emite
|
|
5162
|
+
* `filtersClick` — el consumidor abre su propio panel (típicamente
|
|
5163
|
+
* `kf-drawer role="dialog"` con `kf-option-list`).
|
|
5164
|
+
*
|
|
5165
|
+
* Contrato con el panel de filtros: el consumidor DEBE pasar
|
|
5166
|
+
* `[filtersExpanded]` con el estado de su panel y DEBE cerrarlo al recibir
|
|
5167
|
+
* un `modeChange` distinto de `'panel'`. Al salir de `panel` con el panel
|
|
5168
|
+
* abierto, la barra mueve el foco al primer filtro visible (el botón
|
|
5169
|
+
* "Filtros" ya no existe para recibirlo); si el panel modal siguiera
|
|
5170
|
+
* abierto, ese foco quedaría fuera del diálogo.
|
|
5171
|
+
*
|
|
5172
|
+
* El modo sale de comparar el ancho disponible con el ancho natural de los
|
|
5173
|
+
* slots en cada densidad (más `searchMinWidth` para el buscador), con
|
|
5174
|
+
* histéresis al subir de modo (`LIST_TOOLBAR_HYSTERESIS`) para que no
|
|
5175
|
+
* parpadee en el umbral. Los slots ocultos en modo `panel` se siguen
|
|
5176
|
+
* midiendo (quedan fuera de vista con `visibility: hidden`, sin foco ni
|
|
5177
|
+
* árbol de accesibilidad), así un contador que crece se refleja igual.
|
|
5178
|
+
*
|
|
5179
|
+
* ## Fija (`sticky`)
|
|
5180
|
+
* Con `sticky`, la barra queda pegada arriba al hacer scroll (`stickyTop`
|
|
5181
|
+
* compensa una cabecera fija del shell). Cuando efectivamente queda pegada,
|
|
5182
|
+
* el host recibe la clase `kf-list-toolbar--stuck`: sombra más marcada y
|
|
5183
|
+
* menos alto. "Pegada" = el contenedor con scroll (el ancestro más cercano
|
|
5184
|
+
* con `overflow-y: auto|scroll`, o la ventana) ya se desplazó Y la barra
|
|
5185
|
+
* está en su posición fija. Sin centinela: un centinela por encima de la
|
|
5186
|
+
* barra quedaría recortado cuando la barra es el primer elemento del
|
|
5187
|
+
* contenedor, y la marcaría pegada en reposo. El fondo alrededor de
|
|
5188
|
+
* la barra fija sale de `--kf-list-toolbar-backdrop` (default
|
|
5189
|
+
* `--kf-color-surface`, el fondo habitual del área de contenido).
|
|
5190
|
+
*
|
|
5191
|
+
* Foco tapado (WCAG 2.4.11): una barra fija puede quedar encima del control
|
|
5192
|
+
* que recibe el foco al tabular hacia arriba. El consumidor debe aplicar
|
|
5193
|
+
* `scroll-padding-top` = `stickyTop` + alto de la barra al contenedor que
|
|
5194
|
+
* hace scroll (o a `html` si es la ventana), así el navegador deja ese
|
|
5195
|
+
* margen al llevar un elemento enfocado a la vista. El alto vigente se
|
|
5196
|
+
* publica como señal `height` (px), output `heightChange` y variable CSS
|
|
5197
|
+
* `--kf-list-toolbar-height` en el host (esta última solo la heredan los
|
|
5198
|
+
* descendientes; un ancestro debe usar la señal):
|
|
5199
|
+
*
|
|
5200
|
+
* <main class="content" [style.scroll-padding-top.px]="56 + toolbar.height()">
|
|
5201
|
+
* <kf-list-toolbar #toolbar sticky [stickyTop]="56" ... />
|
|
5202
|
+
* </main>
|
|
5203
|
+
*
|
|
5204
|
+
* <kf-list-toolbar sticky ariaLabel="Filtrar cotizaciones" [activeCount]="activeFilters()"
|
|
5205
|
+
* (filtersClick)="filtersOpen.set(true)" (modeChange)="mode.set($event)">
|
|
5206
|
+
* <span class="sr-only" aria-live="polite">{{ resultsLabel() }}</span>
|
|
5207
|
+
* <kf-input kf-toolbar-search icon="search" ... />
|
|
5208
|
+
* <kf-filter-chips kf-toolbar-filters ... />
|
|
5209
|
+
* <kf-select kf-toolbar-end variant="compact" icon="sort" ariaLabel="Ordenar por" ... />
|
|
5210
|
+
* <kf-view-toggle kf-toolbar-end ... />
|
|
5211
|
+
* </kf-list-toolbar>
|
|
5212
|
+
*/
|
|
5213
|
+
class ListToolbar {
|
|
5214
|
+
/** Nombre accesible del landmark de búsqueda (`role="search"`). */
|
|
5215
|
+
ariaLabel = input('Filtrar resultados', /* @ts-ignore */
|
|
5216
|
+
...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
5217
|
+
/** Fija la barra arriba al hacer scroll. */
|
|
5218
|
+
sticky = input(false, { ...(ngDevMode ? { debugName: "sticky" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
5219
|
+
/** Offset superior de la barra fija: número (px) o longitud CSS (ej. `'var(--shell-header-height)'`). */
|
|
5220
|
+
stickyTop = input(0, /* @ts-ignore */
|
|
5221
|
+
...(ngDevMode ? [{ debugName: "stickyTop" }] : /* istanbul ignore next */ []));
|
|
5222
|
+
/** Ancho mínimo útil del buscador (px) — entra en el cálculo de si todo cabe en una fila. */
|
|
5223
|
+
searchMinWidth = input(200, /* @ts-ignore */
|
|
5224
|
+
...(ngDevMode ? [{ debugName: "searchMinWidth" }] : /* istanbul ignore next */ []));
|
|
5225
|
+
/** Texto del botón del modo `panel`. */
|
|
5226
|
+
filtersLabel = input('Filtros', /* @ts-ignore */
|
|
5227
|
+
...(ngDevMode ? [{ debugName: "filtersLabel" }] : /* istanbul ignore next */ []));
|
|
5228
|
+
/** Cantidad de filtros/orden distintos del valor por defecto; se muestra como badge en el botón del modo `panel`. */
|
|
5229
|
+
activeCount = input(0, /* @ts-ignore */
|
|
5230
|
+
...(ngDevMode ? [{ debugName: "activeCount" }] : /* istanbul ignore next */ []));
|
|
5231
|
+
/**
|
|
5232
|
+
* `aria-expanded` del botón del modo `panel`: el estado del panel que abre
|
|
5233
|
+
* el consumidor. Además, si es `true` al salir de `panel`, el foco pasa al
|
|
5234
|
+
* primer filtro visible — por eso el consumidor DEBE cerrar su panel en
|
|
5235
|
+
* ese mismo `modeChange` (ver "Contrato con el panel de filtros").
|
|
5236
|
+
*/
|
|
5237
|
+
filtersExpanded = input(/* @ts-ignore */
|
|
5238
|
+
...(ngDevMode ? [undefined, { debugName: "filtersExpanded" }] : /* istanbul ignore next */ []));
|
|
5239
|
+
filtersClick = output();
|
|
5240
|
+
/**
|
|
5241
|
+
* Nuevo modo. Al recibir un valor distinto de `'panel'`, el consumidor
|
|
5242
|
+
* DEBE cerrar su panel de filtros (ver "Contrato con el panel de filtros").
|
|
5243
|
+
*/
|
|
5244
|
+
modeChange = output();
|
|
5245
|
+
/** Alto del host (px) cada vez que cambia — ver "Foco tapado" en el JSDoc de la clase. */
|
|
5246
|
+
heightChange = output();
|
|
5247
|
+
modeState = signal('full', /* @ts-ignore */
|
|
5248
|
+
...(ngDevMode ? [{ debugName: "modeState" }] : /* istanbul ignore next */ []));
|
|
5249
|
+
/** Modo vigente (también disponible como `data-mode` en el host). */
|
|
5250
|
+
mode = this.modeState.asReadonly();
|
|
5251
|
+
stuckState = signal(false, /* @ts-ignore */
|
|
5252
|
+
...(ngDevMode ? [{ debugName: "stuckState" }] : /* istanbul ignore next */ []));
|
|
5253
|
+
/** `true` mientras la barra fija está efectivamente pegada arriba. */
|
|
5254
|
+
stuck = this.stuckState.asReadonly();
|
|
5255
|
+
heightState = signal(0, /* @ts-ignore */
|
|
5256
|
+
...(ngDevMode ? [{ debugName: "heightState" }] : /* istanbul ignore next */ []));
|
|
5257
|
+
/** Alto vigente del host en px (barra + padding de la variante fija). */
|
|
5258
|
+
height = this.heightState.asReadonly();
|
|
5259
|
+
stickyTopCss = computed(() => {
|
|
5260
|
+
const top = this.stickyTop();
|
|
5261
|
+
return typeof top === 'number' ? `${top}px` : top;
|
|
5262
|
+
}, /* @ts-ignore */
|
|
5263
|
+
...(ngDevMode ? [{ debugName: "stickyTopCss" }] : /* istanbul ignore next */ []));
|
|
5264
|
+
filtersAriaLabel = computed(() => {
|
|
5265
|
+
const count = this.activeCount();
|
|
5266
|
+
if (count <= 0) {
|
|
5267
|
+
return undefined;
|
|
5268
|
+
}
|
|
5269
|
+
return `${this.filtersLabel()}, ${count} ${count === 1 ? 'activo' : 'activos'}`;
|
|
5270
|
+
}, /* @ts-ignore */
|
|
5271
|
+
...(ngDevMode ? [{ debugName: "filtersAriaLabel" }] : /* istanbul ignore next */ []));
|
|
5272
|
+
hostRef = inject(ElementRef);
|
|
5273
|
+
document = inject(DOCUMENT);
|
|
5274
|
+
destroyRef = inject(DestroyRef);
|
|
5275
|
+
barRef = viewChild.required('bar', /* @ts-ignore */
|
|
5276
|
+
...(ngDevMode ? [{ debugName: "barRef" }] : /* istanbul ignore next */ []));
|
|
5277
|
+
filtersRef = viewChild.required('filters', /* @ts-ignore */
|
|
5278
|
+
...(ngDevMode ? [{ debugName: "filtersRef" }] : /* istanbul ignore next */ []));
|
|
5279
|
+
endRef = viewChild.required('end', /* @ts-ignore */
|
|
5280
|
+
...(ngDevMode ? [{ debugName: "endRef" }] : /* istanbul ignore next */ []));
|
|
5281
|
+
filtersButtonRef = viewChild('filtersButton', { ...(ngDevMode ? { debugName: "filtersButtonRef" } : /* istanbul ignore next */ {}), read: ElementRef });
|
|
5282
|
+
/** A dónde mover el foco tras un cambio de modo que desmonta/oculta el elemento enfocado. */
|
|
5283
|
+
pendingFocus = null;
|
|
5284
|
+
/** `requestAnimationFrame` pendiente de `measure()` (coalesce de notificaciones). */
|
|
5285
|
+
measureFrame = null;
|
|
5286
|
+
stuckFrame = null;
|
|
5287
|
+
removeScrollListener = null;
|
|
5288
|
+
constructor() {
|
|
5289
|
+
afterNextRender(() => {
|
|
5290
|
+
// Primera medición síncrona: sin parpadeo del modo inicial.
|
|
5291
|
+
this.measure();
|
|
5292
|
+
this.observeSize();
|
|
5293
|
+
});
|
|
5294
|
+
// `stuck` solo se calcula con `sticky`; se (des)engancha si cambia en runtime.
|
|
5295
|
+
afterRenderEffect(() => {
|
|
5296
|
+
const sticky = this.sticky();
|
|
5297
|
+
untracked(() => this.watchScroll(sticky));
|
|
5298
|
+
});
|
|
5299
|
+
// Conserva el foco cuando el cambio de modo oculta lo que lo tenía.
|
|
5300
|
+
afterRenderEffect(() => {
|
|
5301
|
+
this.mode();
|
|
5302
|
+
untracked(() => this.restorePendingFocus());
|
|
5303
|
+
});
|
|
5304
|
+
this.destroyRef.onDestroy(() => {
|
|
5305
|
+
this.removeScrollListener?.();
|
|
5306
|
+
if (this.measureFrame !== null) {
|
|
5307
|
+
cancelFrame(this.measureFrame);
|
|
5308
|
+
}
|
|
5309
|
+
if (this.stuckFrame !== null) {
|
|
5310
|
+
cancelFrame(this.stuckFrame);
|
|
5311
|
+
}
|
|
5312
|
+
});
|
|
5313
|
+
}
|
|
5314
|
+
onFiltersClick() {
|
|
5315
|
+
this.filtersClick.emit();
|
|
5316
|
+
}
|
|
5317
|
+
/**
|
|
5318
|
+
* Mide el ancho disponible y el natural de cada slot en ambas
|
|
5319
|
+
* densidades, y actualiza el modo. La densidad de medición se fuerza con
|
|
5320
|
+
* `data-measure` (que gana sobre `data-density` en el CSS) y se retira en
|
|
5321
|
+
* el mismo frame: el navegador nunca pinta ese estado intermedio.
|
|
5322
|
+
*/
|
|
5323
|
+
measure() {
|
|
5324
|
+
const host = this.hostRef.nativeElement;
|
|
5325
|
+
this.updateHeight(host);
|
|
5326
|
+
const bar = this.barRef().nativeElement;
|
|
5327
|
+
const style = getComputedStyle(bar);
|
|
5328
|
+
const padding = (parseFloat(style.paddingLeft) || 0) + (parseFloat(style.paddingRight) || 0);
|
|
5329
|
+
const available = bar.clientWidth - padding;
|
|
5330
|
+
if (available <= 0) {
|
|
5331
|
+
return;
|
|
5332
|
+
}
|
|
5333
|
+
const gap = parseFloat(style.columnGap) || 0;
|
|
5334
|
+
const required = (density) => {
|
|
5335
|
+
host.setAttribute('data-measure', density);
|
|
5336
|
+
const slots = [this.filtersRef(), this.endRef()].map((ref) => ref.nativeElement.getBoundingClientRect().width);
|
|
5337
|
+
return slots.reduce((total, width) => (width > 0 ? total + gap + width : total), this.searchMinWidth());
|
|
5338
|
+
};
|
|
5339
|
+
const full = required('full');
|
|
5340
|
+
const compact = required('compact');
|
|
5341
|
+
host.removeAttribute('data-measure');
|
|
5342
|
+
this.applyMode(resolveListToolbarMode({ available, full, compact }, this.modeState()));
|
|
5343
|
+
}
|
|
5344
|
+
updateHeight(host) {
|
|
5345
|
+
const height = Math.round(host.getBoundingClientRect().height);
|
|
5346
|
+
if (height !== this.heightState()) {
|
|
5347
|
+
this.heightState.set(height);
|
|
5348
|
+
this.heightChange.emit(height);
|
|
5349
|
+
}
|
|
5350
|
+
}
|
|
5351
|
+
applyMode(next) {
|
|
5352
|
+
const current = this.modeState();
|
|
5353
|
+
if (next === current) {
|
|
5354
|
+
return;
|
|
5355
|
+
}
|
|
5356
|
+
const active = this.document.activeElement;
|
|
5357
|
+
if (next === 'panel' && this.slotsContain(active)) {
|
|
5358
|
+
this.pendingFocus = 'filters-button';
|
|
5359
|
+
}
|
|
5360
|
+
else if (current === 'panel' &&
|
|
5361
|
+
// El botón "Filtros" se desmonta al salir de `panel`. Si tenía el foco,
|
|
5362
|
+
// o si el panel que abrió sigue abierto (el drawer devolvería el foco
|
|
5363
|
+
// a ese botón ya desmontado y caería en <body>), el foco pasa al
|
|
5364
|
+
// primer filtro visible.
|
|
5365
|
+
((active && this.filtersButtonRef()?.nativeElement.contains(active)) ||
|
|
5366
|
+
this.filtersExpanded() === true)) {
|
|
5367
|
+
this.pendingFocus = 'slots';
|
|
5368
|
+
}
|
|
5369
|
+
this.modeState.set(next);
|
|
5370
|
+
this.modeChange.emit(next);
|
|
5371
|
+
}
|
|
5372
|
+
slotsContain(element) {
|
|
5373
|
+
return (!!element &&
|
|
5374
|
+
(this.filtersRef().nativeElement.contains(element) ||
|
|
5375
|
+
this.endRef().nativeElement.contains(element)));
|
|
5376
|
+
}
|
|
5377
|
+
restorePendingFocus() {
|
|
5378
|
+
const target = this.pendingFocus;
|
|
5379
|
+
this.pendingFocus = null;
|
|
5380
|
+
if (target === 'filters-button') {
|
|
5381
|
+
this.filtersButtonRef()?.nativeElement.querySelector('button')?.focus();
|
|
5382
|
+
}
|
|
5383
|
+
else if (target === 'slots') {
|
|
5384
|
+
const first = this.filtersRef().nativeElement.querySelector(FOCUSABLE$1) ??
|
|
5385
|
+
this.endRef().nativeElement.querySelector(FOCUSABLE$1);
|
|
5386
|
+
first?.focus();
|
|
5387
|
+
}
|
|
5388
|
+
}
|
|
5389
|
+
observeSize() {
|
|
5390
|
+
if (typeof ResizeObserver === 'undefined') {
|
|
5391
|
+
return;
|
|
5392
|
+
}
|
|
5393
|
+
// Varias notificaciones en el mismo frame (barra, slots, host durante la
|
|
5394
|
+
// transición de padding de `stuck`) se resuelven en UNA medición por
|
|
5395
|
+
// frame: `heightChange` no se emite en cada paso intermedio.
|
|
5396
|
+
const observer = new ResizeObserver(() => this.scheduleMeasure());
|
|
5397
|
+
// El host también: su alto cambia con `sticky` (padding) sin que cambie la barra.
|
|
5398
|
+
observer.observe(this.hostRef.nativeElement);
|
|
5399
|
+
observer.observe(this.barRef().nativeElement);
|
|
5400
|
+
observer.observe(this.filtersRef().nativeElement);
|
|
5401
|
+
observer.observe(this.endRef().nativeElement);
|
|
5402
|
+
this.destroyRef.onDestroy(() => observer.disconnect());
|
|
5403
|
+
}
|
|
5404
|
+
scheduleMeasure() {
|
|
5405
|
+
if (this.measureFrame !== null) {
|
|
5406
|
+
return;
|
|
5407
|
+
}
|
|
5408
|
+
this.measureFrame = requestFrame(() => {
|
|
5409
|
+
this.measureFrame = null;
|
|
5410
|
+
this.measure();
|
|
5411
|
+
});
|
|
5412
|
+
}
|
|
5413
|
+
/** Engancha (o suelta) el cálculo de `stuck` al scroll del contenedor. */
|
|
5414
|
+
watchScroll(sticky) {
|
|
5415
|
+
this.removeScrollListener?.();
|
|
5416
|
+
this.removeScrollListener = null;
|
|
5417
|
+
if (!sticky) {
|
|
5418
|
+
this.stuckState.set(false);
|
|
5419
|
+
return;
|
|
5420
|
+
}
|
|
5421
|
+
const container = scrollParent(this.hostRef.nativeElement);
|
|
5422
|
+
const target = container ?? this.document.defaultView ?? window;
|
|
5423
|
+
const onScroll = () => {
|
|
5424
|
+
if (this.stuckFrame === null) {
|
|
5425
|
+
this.stuckFrame = requestFrame(() => {
|
|
5426
|
+
this.stuckFrame = null;
|
|
5427
|
+
this.updateStuck(container);
|
|
5428
|
+
});
|
|
5429
|
+
}
|
|
5430
|
+
};
|
|
5431
|
+
target.addEventListener('scroll', onScroll, { passive: true });
|
|
5432
|
+
this.removeScrollListener = () => target.removeEventListener('scroll', onScroll);
|
|
5433
|
+
this.updateStuck(container);
|
|
5434
|
+
}
|
|
5435
|
+
/**
|
|
5436
|
+
* Pegada = el contenedor ya se desplazó Y la barra está en su posición
|
|
5437
|
+
* fija (`top` resuelto, con medio píxel de tolerancia por subpíxeles).
|
|
5438
|
+
* Sin la primera condición, una barra que es el primer elemento del
|
|
5439
|
+
* contenedor se leería pegada en reposo.
|
|
5440
|
+
*/
|
|
5441
|
+
updateStuck(container) {
|
|
5442
|
+
const host = this.hostRef.nativeElement;
|
|
5443
|
+
const view = this.document.defaultView;
|
|
5444
|
+
const scrolled = container ? container.scrollTop > 0 : (view?.scrollY ?? 0) > 0;
|
|
5445
|
+
const containerTop = container
|
|
5446
|
+
? container.getBoundingClientRect().top + container.clientTop
|
|
5447
|
+
: 0;
|
|
5448
|
+
const stickyTop = parseFloat(getComputedStyle(host).top) || 0;
|
|
5449
|
+
const offset = host.getBoundingClientRect().top - containerTop;
|
|
5450
|
+
this.stuckState.set(scrolled && offset <= stickyTop + 0.5);
|
|
5451
|
+
}
|
|
5452
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: ListToolbar, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5453
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: ListToolbar, isStandalone: true, selector: "kf-list-toolbar", inputs: { ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, sticky: { classPropertyName: "sticky", publicName: "sticky", isSignal: true, isRequired: false, transformFunction: null }, stickyTop: { classPropertyName: "stickyTop", publicName: "stickyTop", isSignal: true, isRequired: false, transformFunction: null }, searchMinWidth: { classPropertyName: "searchMinWidth", publicName: "searchMinWidth", isSignal: true, isRequired: false, transformFunction: null }, filtersLabel: { classPropertyName: "filtersLabel", publicName: "filtersLabel", isSignal: true, isRequired: false, transformFunction: null }, activeCount: { classPropertyName: "activeCount", publicName: "activeCount", isSignal: true, isRequired: false, transformFunction: null }, filtersExpanded: { classPropertyName: "filtersExpanded", publicName: "filtersExpanded", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { filtersClick: "filtersClick", modeChange: "modeChange", heightChange: "heightChange" }, host: { properties: { "attr.data-mode": "mode()", "attr.data-density": "mode() === 'full' ? 'full' : 'compact'", "class.kf-list-toolbar--sticky": "sticky()", "class.kf-list-toolbar--stuck": "sticky() && stuck()", "style.--kf-list-toolbar-top": "stickyTopCss()", "style.--kf-list-toolbar-search-min": "searchMinWidth() + 'px'", "style.--kf-list-toolbar-height": "height() + 'px'" } }, viewQueries: [{ propertyName: "barRef", first: true, predicate: ["bar"], descendants: true, isSignal: true }, { propertyName: "filtersRef", first: true, predicate: ["filters"], descendants: true, isSignal: true }, { propertyName: "endRef", first: true, predicate: ["end"], descendants: true, isSignal: true }, { propertyName: "filtersButtonRef", first: true, predicate: ["filtersButton"], descendants: true, read: ElementRef, isSignal: true }], ngImport: i0, template: "<div #bar class=\"kf-list-toolbar__bar\" role=\"search\" [attr.aria-label]=\"ariaLabel()\">\n <ng-content />\n <div class=\"kf-list-toolbar__search\"><ng-content select=\"[kf-toolbar-search]\" /></div>\n @if (mode() === 'panel') {\n <kf-button\n #filtersButton\n class=\"kf-list-toolbar__filters-button\"\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n icon=\"filter\"\n ariaHaspopup=\"dialog\"\n [ariaExpanded]=\"filtersExpanded()\"\n [ariaLabel]=\"filtersAriaLabel()\"\n (click)=\"onFiltersClick()\"\n >\n {{ filtersLabel() }}\n @if (activeCount() > 0) {\n <kf-badge class=\"kf-list-toolbar__filters-count\" aria-hidden=\"true\">{{\n activeCount()\n }}</kf-badge>\n }\n </kf-button>\n }\n <!-- `display: contents` en full/compact (los slots son \u00EDtems de la fila);\n en panel, caja de 0x0 que recorta: los slots se siguen midiendo sin\n ensanchar la p\u00E1gina ni ser alcanzables. -->\n <div class=\"kf-list-toolbar__collapsible\">\n <div #filters class=\"kf-list-toolbar__filters kf-list-toolbar__slot\">\n <ng-content select=\"[kf-toolbar-filters]\" />\n </div>\n <div #end class=\"kf-list-toolbar__end kf-list-toolbar__slot\">\n <ng-content select=\"[kf-toolbar-end]\" />\n </div>\n </div>\n</div>\n", styles: [":host{display:block;position:relative;font-family:var(--kf-font-body)}:host(.kf-list-toolbar--sticky){position:sticky;top:var(--kf-list-toolbar-top, 0px);z-index:var(--kf-list-toolbar-z-index, 30);padding-block:var(--kf-space-sm);background:var(--kf-list-toolbar-backdrop, var(--kf-color-surface))}.kf-list-toolbar__bar{position:relative;display:flex;align-items:center;gap:var(--kf-space-sm) var(--kf-space-md);padding:var(--kf-space-sm) var(--kf-space-md);background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md);box-shadow:var(--kf-shadow-xs);transition:box-shadow .14s,padding .14s}:host(.kf-list-toolbar--stuck) .kf-list-toolbar__bar{padding-block:6px;box-shadow:var(--kf-shadow-md)}.kf-list-toolbar__search{flex:1 1 auto;min-width:var(--kf-list-toolbar-search-min, 200px)}.kf-list-toolbar__slot{flex:none;width:max-content;display:flex;align-items:center;gap:var(--kf-space-sm)}.kf-list-toolbar__slot:empty{display:none}.kf-list-toolbar__end{margin-inline-start:auto}.kf-list-toolbar__filters-button{flex:none}.kf-list-toolbar__filters-count{margin-inline-start:2px}:host([data-density=\"compact\"]){--kf-filter-chips-padding-inline: 12px}:host([data-measure=\"full\"]){--kf-filter-chips-padding-inline: var(--kf-space-md)}:host([data-measure=\"compact\"]){--kf-filter-chips-padding-inline: 12px}.kf-list-toolbar__collapsible{display:contents}:host([data-mode=\"panel\"]) .kf-list-toolbar__collapsible{display:block;position:absolute;top:0;left:0;width:0;height:0;overflow:hidden;visibility:hidden;pointer-events:none}:host([data-mode=\"panel\"]) .kf-list-toolbar__search{flex:1 1 0;min-width:0}@media(pointer:coarse),(max-width:760px){.kf-list-toolbar__filters-button{--kf-button-min-height: 44px}}@media(prefers-reduced-motion:reduce){.kf-list-toolbar__bar{transition:none}}\n"], dependencies: [{ kind: "component", type: Badge, selector: "kf-badge", inputs: ["tone", "variant", "dot", "truncate"] }, { kind: "component", type: Button, selector: "kf-button", inputs: ["variant", "size", "shape", "disabled", "type", "icon", "block", "loading", "ariaLabel", "ariaExpanded", "ariaPressed", "ariaHaspopup"] }] });
|
|
5454
|
+
}
|
|
5455
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: ListToolbar, decorators: [{
|
|
5456
|
+
type: Component,
|
|
5457
|
+
args: [{ selector: 'kf-list-toolbar', imports: [Badge, Button], host: {
|
|
5458
|
+
'[attr.data-mode]': 'mode()',
|
|
5459
|
+
'[attr.data-density]': "mode() === 'full' ? 'full' : 'compact'",
|
|
5460
|
+
'[class.kf-list-toolbar--sticky]': 'sticky()',
|
|
5461
|
+
'[class.kf-list-toolbar--stuck]': 'sticky() && stuck()',
|
|
5462
|
+
'[style.--kf-list-toolbar-top]': 'stickyTopCss()',
|
|
5463
|
+
'[style.--kf-list-toolbar-search-min]': "searchMinWidth() + 'px'",
|
|
5464
|
+
'[style.--kf-list-toolbar-height]': "height() + 'px'",
|
|
5465
|
+
}, template: "<div #bar class=\"kf-list-toolbar__bar\" role=\"search\" [attr.aria-label]=\"ariaLabel()\">\n <ng-content />\n <div class=\"kf-list-toolbar__search\"><ng-content select=\"[kf-toolbar-search]\" /></div>\n @if (mode() === 'panel') {\n <kf-button\n #filtersButton\n class=\"kf-list-toolbar__filters-button\"\n type=\"button\"\n variant=\"outline\"\n size=\"sm\"\n icon=\"filter\"\n ariaHaspopup=\"dialog\"\n [ariaExpanded]=\"filtersExpanded()\"\n [ariaLabel]=\"filtersAriaLabel()\"\n (click)=\"onFiltersClick()\"\n >\n {{ filtersLabel() }}\n @if (activeCount() > 0) {\n <kf-badge class=\"kf-list-toolbar__filters-count\" aria-hidden=\"true\">{{\n activeCount()\n }}</kf-badge>\n }\n </kf-button>\n }\n <!-- `display: contents` en full/compact (los slots son \u00EDtems de la fila);\n en panel, caja de 0x0 que recorta: los slots se siguen midiendo sin\n ensanchar la p\u00E1gina ni ser alcanzables. -->\n <div class=\"kf-list-toolbar__collapsible\">\n <div #filters class=\"kf-list-toolbar__filters kf-list-toolbar__slot\">\n <ng-content select=\"[kf-toolbar-filters]\" />\n </div>\n <div #end class=\"kf-list-toolbar__end kf-list-toolbar__slot\">\n <ng-content select=\"[kf-toolbar-end]\" />\n </div>\n </div>\n</div>\n", styles: [":host{display:block;position:relative;font-family:var(--kf-font-body)}:host(.kf-list-toolbar--sticky){position:sticky;top:var(--kf-list-toolbar-top, 0px);z-index:var(--kf-list-toolbar-z-index, 30);padding-block:var(--kf-space-sm);background:var(--kf-list-toolbar-backdrop, var(--kf-color-surface))}.kf-list-toolbar__bar{position:relative;display:flex;align-items:center;gap:var(--kf-space-sm) var(--kf-space-md);padding:var(--kf-space-sm) var(--kf-space-md);background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md);box-shadow:var(--kf-shadow-xs);transition:box-shadow .14s,padding .14s}:host(.kf-list-toolbar--stuck) .kf-list-toolbar__bar{padding-block:6px;box-shadow:var(--kf-shadow-md)}.kf-list-toolbar__search{flex:1 1 auto;min-width:var(--kf-list-toolbar-search-min, 200px)}.kf-list-toolbar__slot{flex:none;width:max-content;display:flex;align-items:center;gap:var(--kf-space-sm)}.kf-list-toolbar__slot:empty{display:none}.kf-list-toolbar__end{margin-inline-start:auto}.kf-list-toolbar__filters-button{flex:none}.kf-list-toolbar__filters-count{margin-inline-start:2px}:host([data-density=\"compact\"]){--kf-filter-chips-padding-inline: 12px}:host([data-measure=\"full\"]){--kf-filter-chips-padding-inline: var(--kf-space-md)}:host([data-measure=\"compact\"]){--kf-filter-chips-padding-inline: 12px}.kf-list-toolbar__collapsible{display:contents}:host([data-mode=\"panel\"]) .kf-list-toolbar__collapsible{display:block;position:absolute;top:0;left:0;width:0;height:0;overflow:hidden;visibility:hidden;pointer-events:none}:host([data-mode=\"panel\"]) .kf-list-toolbar__search{flex:1 1 0;min-width:0}@media(pointer:coarse),(max-width:760px){.kf-list-toolbar__filters-button{--kf-button-min-height: 44px}}@media(prefers-reduced-motion:reduce){.kf-list-toolbar__bar{transition:none}}\n"] }]
|
|
5466
|
+
}], ctorParameters: () => [], propDecorators: { ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], sticky: [{ type: i0.Input, args: [{ isSignal: true, alias: "sticky", required: false }] }], stickyTop: [{ type: i0.Input, args: [{ isSignal: true, alias: "stickyTop", required: false }] }], searchMinWidth: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchMinWidth", required: false }] }], filtersLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "filtersLabel", required: false }] }], activeCount: [{ type: i0.Input, args: [{ isSignal: true, alias: "activeCount", required: false }] }], filtersExpanded: [{ type: i0.Input, args: [{ isSignal: true, alias: "filtersExpanded", required: false }] }], filtersClick: [{ type: i0.Output, args: ["filtersClick"] }], modeChange: [{ type: i0.Output, args: ["modeChange"] }], heightChange: [{ type: i0.Output, args: ["heightChange"] }], barRef: [{ type: i0.ViewChild, args: ['bar', { isSignal: true }] }], filtersRef: [{ type: i0.ViewChild, args: ['filters', { isSignal: true }] }], endRef: [{ type: i0.ViewChild, args: ['end', { isSignal: true }] }], filtersButtonRef: [{ type: i0.ViewChild, args: ['filtersButton', { ...{
|
|
5467
|
+
read: ElementRef,
|
|
5468
|
+
}, isSignal: true }] }] } });
|
|
5469
|
+
/** Ancestro más cercano que hace scroll vertical, o `null` si es la ventana. */
|
|
5470
|
+
function scrollParent(element) {
|
|
5471
|
+
let parent = element.parentElement;
|
|
5472
|
+
while (parent && parent !== element.ownerDocument.documentElement) {
|
|
5473
|
+
const overflowY = getComputedStyle(parent).overflowY;
|
|
5474
|
+
if (overflowY === 'auto' || overflowY === 'scroll' || overflowY === 'overlay') {
|
|
5475
|
+
return parent;
|
|
5476
|
+
}
|
|
5477
|
+
parent = parent.parentElement;
|
|
5478
|
+
}
|
|
5479
|
+
return null;
|
|
5480
|
+
}
|
|
5481
|
+
/** `requestAnimationFrame` con respaldo para entornos sin él (SSR, algunos runners de test). */
|
|
5482
|
+
function requestFrame(callback) {
|
|
5483
|
+
return typeof requestAnimationFrame === 'function'
|
|
5484
|
+
? requestAnimationFrame(callback)
|
|
5485
|
+
: setTimeout(callback, 16);
|
|
5486
|
+
}
|
|
5487
|
+
function cancelFrame(handle) {
|
|
5488
|
+
if (typeof cancelAnimationFrame === 'function') {
|
|
5489
|
+
cancelAnimationFrame(handle);
|
|
5490
|
+
}
|
|
5491
|
+
else {
|
|
5492
|
+
clearTimeout(handle);
|
|
5493
|
+
}
|
|
5494
|
+
}
|
|
5495
|
+
|
|
4330
5496
|
/**
|
|
4331
5497
|
* Card de estadística/KPI: label, valor grande, ícono opcional en la esquina
|
|
4332
5498
|
* superior derecha, badge de esquina opcional (pegado al valor) y una nota
|
|
@@ -4456,6 +5622,8 @@ function installModalAwareOverlayContainer(container) {
|
|
|
4456
5622
|
}
|
|
4457
5623
|
|
|
4458
5624
|
let modalId = 0;
|
|
5625
|
+
/** Controles que pueden recibir el foco inicial. */
|
|
5626
|
+
const FOCUSABLE = 'input:not([disabled]), select:not([disabled]), textarea:not([disabled]), button:not([disabled]), a[href], [tabindex]:not([tabindex="-1"])';
|
|
4459
5627
|
/**
|
|
4460
5628
|
* Diálogo centrado sobre un backdrop: confirmaciones destructivas, formularios
|
|
4461
5629
|
* cortos, decisiones que bloquean el flujo.
|
|
@@ -4493,6 +5661,27 @@ let modalId = 0;
|
|
|
4493
5661
|
*
|
|
4494
5662
|
* <kf-link footer class="kf-modal__footer-start" disabled>Ver documento</kf-link>
|
|
4495
5663
|
* <kf-button footer (click)="open.set(false)">Cerrar</kf-button>
|
|
5664
|
+
*
|
|
5665
|
+
* ## Modal de listado (alto acotado, filtros fijos)
|
|
5666
|
+
* Para un diálogo con mucho contenido (ej. elegir un ítem entre cientos):
|
|
5667
|
+
* - `maxHeight` (ej. `"85vh"`) acota el panel; solo `.kf-modal__body` se
|
|
5668
|
+
* desplaza, el encabezado, la zona `[kf-modal-toolbar]` y el footer
|
|
5669
|
+
* quedan fijos.
|
|
5670
|
+
* - `[kf-modal-toolbar]`: zona FIJA entre el encabezado y el cuerpo, para
|
|
5671
|
+
* buscador y filtros.
|
|
5672
|
+
* - `[kf-modal-meta]`: dato corto junto al título (ej. "12 de 86").
|
|
5673
|
+
* - `fullscreenOnMobile`: hasta 640px de viewport ocupa toda la pantalla
|
|
5674
|
+
* (`100dvh`, sin radio).
|
|
5675
|
+
* - `initialFocus`: selector CSS del elemento que recibe el foco al abrir
|
|
5676
|
+
* (si apunta a un contenedor, se enfoca su primer control). Sin él, el
|
|
5677
|
+
* `<dialog>` nativo decide (`autofocus` o el primer control).
|
|
5678
|
+
*
|
|
5679
|
+
* <kf-modal [(open)]="open" title="Cambiar cotización" width="1040px"
|
|
5680
|
+
* maxHeight="85vh" fullscreenOnMobile initialFocus=".search">
|
|
5681
|
+
* <span kf-modal-meta aria-live="polite">12 de 86</span>
|
|
5682
|
+
* <div kf-modal-toolbar><kf-input class="search" … /></div>
|
|
5683
|
+
* <ul>…</ul>
|
|
5684
|
+
* </kf-modal>
|
|
4496
5685
|
*/
|
|
4497
5686
|
class Modal {
|
|
4498
5687
|
title = input('', /* @ts-ignore */
|
|
@@ -4508,9 +5697,20 @@ class Modal {
|
|
|
4508
5697
|
* angostas, mismo `min()` de siempre. */
|
|
4509
5698
|
width = input(null, /* @ts-ignore */
|
|
4510
5699
|
...(ngDevMode ? [{ debugName: "width" }] : /* istanbul ignore next */ []));
|
|
5700
|
+
/** Alto máximo del panel (ej. `"85vh"`); con valor, solo el cuerpo se
|
|
5701
|
+
* desplaza. Sin valor (default) el panel crece con su contenido. */
|
|
5702
|
+
maxHeight = input(null, /* @ts-ignore */
|
|
5703
|
+
...(ngDevMode ? [{ debugName: "maxHeight" }] : /* istanbul ignore next */ []));
|
|
5704
|
+
/** Hasta 640px de viewport, ocupa toda la pantalla (100dvh, sin radio). */
|
|
5705
|
+
fullscreenOnMobile = input(false, { ...(ngDevMode ? { debugName: "fullscreenOnMobile" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
5706
|
+
/** Selector CSS (dentro del panel) del elemento a enfocar al abrir. */
|
|
5707
|
+
initialFocus = input(null, /* @ts-ignore */
|
|
5708
|
+
...(ngDevMode ? [{ debugName: "initialFocus" }] : /* istanbul ignore next */ []));
|
|
4511
5709
|
titleId = `kf-modal-title-${modalId++}`;
|
|
4512
5710
|
dialog = viewChild('dialog', /* @ts-ignore */
|
|
4513
5711
|
...(ngDevMode ? [{ debugName: "dialog" }] : /* istanbul ignore next */ []));
|
|
5712
|
+
panel = viewChild('panel', /* @ts-ignore */
|
|
5713
|
+
...(ngDevMode ? [{ debugName: "panel" }] : /* istanbul ignore next */ []));
|
|
4514
5714
|
document = inject(DOCUMENT);
|
|
4515
5715
|
overlayContainer = inject(OverlayContainer);
|
|
4516
5716
|
constructor() {
|
|
@@ -4534,6 +5734,7 @@ class Modal {
|
|
|
4534
5734
|
else {
|
|
4535
5735
|
el.open = true;
|
|
4536
5736
|
}
|
|
5737
|
+
this.focusInitial();
|
|
4537
5738
|
}
|
|
4538
5739
|
else if (!this.open() && el.open) {
|
|
4539
5740
|
if (typeof el.close === 'function') {
|
|
@@ -4565,6 +5766,24 @@ class Modal {
|
|
|
4565
5766
|
}
|
|
4566
5767
|
});
|
|
4567
5768
|
}
|
|
5769
|
+
/**
|
|
5770
|
+
* Enfoca `initialFocus` DESPUÉS de `showModal()` (que ya movió el foco al
|
|
5771
|
+
* primer control): un elemento dentro de un `<dialog>` cerrado no se puede
|
|
5772
|
+
* enfocar. Si el selector apunta a un host (ej. `kf-input`), se enfoca su
|
|
5773
|
+
* primer control interno.
|
|
5774
|
+
*/
|
|
5775
|
+
focusInitial() {
|
|
5776
|
+
const selector = this.initialFocus();
|
|
5777
|
+
const panel = this.panel()?.nativeElement;
|
|
5778
|
+
if (!selector || !panel) {
|
|
5779
|
+
return;
|
|
5780
|
+
}
|
|
5781
|
+
const target = panel.querySelector(selector);
|
|
5782
|
+
const focusable = target?.matches(FOCUSABLE)
|
|
5783
|
+
? target
|
|
5784
|
+
: target?.querySelector(FOCUSABLE);
|
|
5785
|
+
focusable?.focus();
|
|
5786
|
+
}
|
|
4568
5787
|
/** `Escape` dispara `cancel`; cancelarlo lo deja abierto. */
|
|
4569
5788
|
onCancel(event) {
|
|
4570
5789
|
if (!this.dismissible()) {
|
|
@@ -4586,12 +5805,12 @@ class Modal {
|
|
|
4586
5805
|
}
|
|
4587
5806
|
}
|
|
4588
5807
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Modal, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4589
|
-
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
|
|
5808
|
+
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 }, maxHeight: { classPropertyName: "maxHeight", publicName: "maxHeight", isSignal: true, isRequired: false, transformFunction: null }, fullscreenOnMobile: { classPropertyName: "fullscreenOnMobile", publicName: "fullscreenOnMobile", isSignal: true, isRequired: false, transformFunction: null }, initialFocus: { classPropertyName: "initialFocus", publicName: "initialFocus", 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 }, { propertyName: "panel", first: true, predicate: ["panel"], descendants: true, isSignal: true }], ngImport: i0, template: "<dialog\n #dialog\n class=\"kf-modal\"\n [class.kf-modal--bounded]=\"!!maxHeight()\"\n [class.kf-modal--fullscreen-mobile]=\"fullscreenOnMobile()\"\n [style.--kf-modal-width]=\"width()\"\n [style.--kf-modal-max-height]=\"maxHeight()\"\n [attr.aria-labelledby]=\"title() ? titleId : null\"\n (cancel)=\"onCancel($event)\"\n (close)=\"open.set(false)\"\n>\n @if (open()) {\n <div #panel class=\"kf-modal__panel\">\n <div class=\"kf-modal__head\">\n <div class=\"kf-modal__heading\">\n @if (title()) {\n <h2 class=\"kf-modal__title\" [id]=\"titleId\">{{ title() }}</h2>\n }\n <ng-content select=\"[kf-modal-meta]\" />\n </div>\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__toolbar\"><ng-content select=\"[kf-modal-toolbar]\" /></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__heading{flex:1;min-width:0;display:flex;flex-wrap:wrap;align-items:baseline;column-gap:var(--kf-space-sm);row-gap:var(--kf-space-xs)}.kf-modal__title{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;align-items:center;justify-content:center;min-width:32px;min-height:32px;margin:-4px -6px 0 0;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);background:var(--kf-color-surface)}.kf-modal__close:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-modal__toolbar:empty{display:none}.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}.kf-modal--bounded{max-height:min(var(--kf-modal-max-height),calc(100dvh - 2 * var(--kf-space-md)))}.kf-modal--bounded .kf-modal__panel{box-sizing:border-box;max-height:inherit}.kf-modal--bounded .kf-modal__body{flex:1 1 auto;min-height:0;overscroll-behavior:contain}@media(max-width:640px){.kf-modal--fullscreen-mobile{width:100vw;max-width:100vw;height:100dvh;max-height:100dvh;margin:0}.kf-modal--fullscreen-mobile .kf-modal__panel{box-sizing:border-box;height:100%;max-height:100%;padding:var(--kf-space-md);gap:var(--kf-space-sm);border-radius:0;box-shadow:none}.kf-modal--fullscreen-mobile .kf-modal__body{flex:1 1 auto;min-height:0}.kf-modal--fullscreen-mobile .kf-modal__title{font-size:var(--kf-font-size-title-sm)}}\n"], dependencies: [{ kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size", "rotate", "filled"] }] });
|
|
4590
5809
|
}
|
|
4591
5810
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Modal, decorators: [{
|
|
4592
5811
|
type: Component,
|
|
4593
|
-
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
|
|
4594
|
-
}], 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: [{
|
|
5812
|
+
args: [{ selector: 'kf-modal', imports: [Icon], template: "<dialog\n #dialog\n class=\"kf-modal\"\n [class.kf-modal--bounded]=\"!!maxHeight()\"\n [class.kf-modal--fullscreen-mobile]=\"fullscreenOnMobile()\"\n [style.--kf-modal-width]=\"width()\"\n [style.--kf-modal-max-height]=\"maxHeight()\"\n [attr.aria-labelledby]=\"title() ? titleId : null\"\n (cancel)=\"onCancel($event)\"\n (close)=\"open.set(false)\"\n>\n @if (open()) {\n <div #panel class=\"kf-modal__panel\">\n <div class=\"kf-modal__head\">\n <div class=\"kf-modal__heading\">\n @if (title()) {\n <h2 class=\"kf-modal__title\" [id]=\"titleId\">{{ title() }}</h2>\n }\n <ng-content select=\"[kf-modal-meta]\" />\n </div>\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__toolbar\"><ng-content select=\"[kf-modal-toolbar]\" /></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__heading{flex:1;min-width:0;display:flex;flex-wrap:wrap;align-items:baseline;column-gap:var(--kf-space-sm);row-gap:var(--kf-space-xs)}.kf-modal__title{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;align-items:center;justify-content:center;min-width:32px;min-height:32px;margin:-4px -6px 0 0;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);background:var(--kf-color-surface)}.kf-modal__close:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-modal__toolbar:empty{display:none}.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}.kf-modal--bounded{max-height:min(var(--kf-modal-max-height),calc(100dvh - 2 * var(--kf-space-md)))}.kf-modal--bounded .kf-modal__panel{box-sizing:border-box;max-height:inherit}.kf-modal--bounded .kf-modal__body{flex:1 1 auto;min-height:0;overscroll-behavior:contain}@media(max-width:640px){.kf-modal--fullscreen-mobile{width:100vw;max-width:100vw;height:100dvh;max-height:100dvh;margin:0}.kf-modal--fullscreen-mobile .kf-modal__panel{box-sizing:border-box;height:100%;max-height:100%;padding:var(--kf-space-md);gap:var(--kf-space-sm);border-radius:0;box-shadow:none}.kf-modal--fullscreen-mobile .kf-modal__body{flex:1 1 auto;min-height:0}.kf-modal--fullscreen-mobile .kf-modal__title{font-size:var(--kf-font-size-title-sm)}}\n"] }]
|
|
5813
|
+
}], 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 }] }], maxHeight: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxHeight", required: false }] }], fullscreenOnMobile: [{ type: i0.Input, args: [{ isSignal: true, alias: "fullscreenOnMobile", required: false }] }], initialFocus: [{ type: i0.Input, args: [{ isSignal: true, alias: "initialFocus", required: false }] }], dialog: [{ type: i0.ViewChild, args: ['dialog', { isSignal: true }] }], panel: [{ type: i0.ViewChild, args: ['panel', { isSignal: true }] }], onHostClick: [{
|
|
4595
5814
|
type: HostListener,
|
|
4596
5815
|
args: ['click', ['$event']]
|
|
4597
5816
|
}] } });
|
|
@@ -4623,6 +5842,101 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
|
|
|
4623
5842
|
}, template: "@if (title() || subtitle()) {\n <span class=\"kf-note-block__head\">\n @if (title(); as heading) {\n <span class=\"kf-note-block__title kf-overline-text\">{{ heading }}</span>\n }\n @if (subtitle(); as sub) {\n <span class=\"kf-note-block__subtitle kf-caption-text\">{{ sub }}</span>\n }\n </span>\n}\n\n<span class=\"kf-note-block__text\"><ng-content /></span>\n\n@if (signature(); as author) {\n <span class=\"kf-note-block__signature kf-caption-text\">{{ author }}</span>\n}\n", styles: [".kf-overline-text{font-family:var(--kf-font-title);font-size:var(--kf-font-size-caption);font-weight:var(--kf-font-weight-medium);letter-spacing:.04em;text-transform:uppercase;color:var(--kf-color-text-muted)}.kf-caption-text{font-size:var(--kf-font-size-caption);font-weight:var(--kf-font-weight-body);color:var(--kf-color-text-muted)}:host{display:flex;flex-direction:column;gap:var(--kf-space-xs);padding:var(--kf-space-sm) var(--kf-space-md);border-radius:var(--kf-radius-sm);border-left:3px solid var(--kf-color-border-strong);background:var(--kf-color-surface);min-width:0}:host(.kf-note-block--info){border-left-color:var(--kf-color-info);background:var(--kf-color-info-soft)}:host(.kf-note-block--ok){border-left-color:var(--kf-color-success);background:var(--kf-color-success-soft)}:host(.kf-note-block--warn){border-left-color:var(--kf-color-warning);background:var(--kf-color-warning-soft)}:host(.kf-note-block--error){border-left-color:var(--kf-color-error);background:var(--kf-color-error-soft)}.kf-note-block__head{display:flex;align-items:baseline;flex-wrap:wrap;gap:var(--kf-space-sm)}.kf-note-block__title{font-weight:var(--kf-font-weight-title);color:var(--kf-color-ink)}.kf-note-block__text{font-family:var(--kf-font-body);font-size:var(--kf-font-size-body);color:var(--kf-color-text);white-space:pre-line;overflow-wrap:anywhere}\n"] }]
|
|
4624
5843
|
}], propDecorators: { title: [{ type: i0.Input, args: [{ isSignal: true, alias: "title", required: false }] }], subtitle: [{ type: i0.Input, args: [{ isSignal: true, alias: "subtitle", required: false }] }], signature: [{ type: i0.Input, args: [{ isSignal: true, alias: "signature", required: false }] }], tone: [{ type: i0.Input, args: [{ isSignal: true, alias: "tone", required: false }] }] } });
|
|
4625
5844
|
|
|
5845
|
+
let nextId = 0;
|
|
5846
|
+
/**
|
|
5847
|
+
* Lista vertical de opciones de UNA sola selección para paneles (ej. las
|
|
5848
|
+
* secciones "Estado", "Ordenar por" y "Vista" de un panel de filtros): una
|
|
5849
|
+
* fila de al menos 44px por opción, contador opcional a la derecha y la
|
|
5850
|
+
* opción elegida resaltada con `--kf-color-primary-soft`.
|
|
5851
|
+
*
|
|
5852
|
+
* Es la contraparte en panel de `kf-filter-chips`: misma función (filtrar u
|
|
5853
|
+
* ordenar lo que se muestra) cuando no hay ancho para una fila de chips. No
|
|
5854
|
+
* es `kf-selectable-card-group`: aquel responde una pregunta de un flujo
|
|
5855
|
+
* ("¿con qué método pagas?") con affordance de radio y cards grandes; esta
|
|
5856
|
+
* lista ajusta una vista, con filas densas y contador.
|
|
5857
|
+
*
|
|
5858
|
+
* Semántica: `role="radiogroup"` + `role="radio"` con `aria-checked`, no
|
|
5859
|
+
* botones con `aria-pressed`. Las opciones son mutuamente excluyentes (eso
|
|
5860
|
+
* es un radio; `aria-pressed` describe toggles independientes), el lector
|
|
5861
|
+
* de pantalla anuncia la posición ("2 de 4") y cada grupo es UNA sola
|
|
5862
|
+
* parada de Tab: un panel con tres secciones se recorre con 3 Tabs en vez
|
|
5863
|
+
* de uno por opción. Dentro del grupo, flechas arriba/abajo y Home/End
|
|
5864
|
+
* mueven el foco y seleccionan (`handleArrowKeyNavigation`, mismo helper que
|
|
5865
|
+
* `kf-tabs`/`kf-selectable-card-group`).
|
|
5866
|
+
*
|
|
5867
|
+
* La opción elegida lleva además un check (no solo color), para que el
|
|
5868
|
+
* estado no dependa de distinguir el tinte `primary-soft` (WCAG 1.4.1).
|
|
5869
|
+
*
|
|
5870
|
+
* El `@for` hace `track` por índice: cuando el padre re-renderiza (ej. los
|
|
5871
|
+
* contadores cambian al elegir), los botones existentes se reutilizan y el
|
|
5872
|
+
* foco no se pierde. Por índice y no por `value` para no fallar (NG0955)
|
|
5873
|
+
* si dos opciones repiten `value`; aun así los values deben ser únicos:
|
|
5874
|
+
* con repetidos, ambas filas se marcan elegidas.
|
|
5875
|
+
*
|
|
5876
|
+
* `label` dibuja el título de la sección (chico, en mayúsculas) y nombra el
|
|
5877
|
+
* grupo vía `aria-labelledby`; sin título visible, pasar `ariaLabel`.
|
|
5878
|
+
*
|
|
5879
|
+
* <kf-option-list label="Estado"
|
|
5880
|
+
* [options]="[{ value: '', label: 'Todas', count: 12 },
|
|
5881
|
+
* { value: 'pending', label: 'Pendientes', count: 3 }]"
|
|
5882
|
+
* [value]="status()"
|
|
5883
|
+
* (valueChange)="status.set($event)" />
|
|
5884
|
+
*/
|
|
5885
|
+
class OptionList {
|
|
5886
|
+
options = input.required(/* @ts-ignore */
|
|
5887
|
+
...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
|
|
5888
|
+
value = input('', /* @ts-ignore */
|
|
5889
|
+
...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
|
|
5890
|
+
/** Título visible de la sección; nombra el grupo. */
|
|
5891
|
+
label = input(/* @ts-ignore */
|
|
5892
|
+
...(ngDevMode ? [undefined, { debugName: "label" }] : /* istanbul ignore next */ []));
|
|
5893
|
+
/** Nombre accesible del grupo cuando no hay `label` visible. */
|
|
5894
|
+
ariaLabel = input(/* @ts-ignore */
|
|
5895
|
+
...(ngDevMode ? [undefined, { debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
|
|
5896
|
+
valueChange = output();
|
|
5897
|
+
instanceId = `kf-option-list-${nextId++}`;
|
|
5898
|
+
labelId = `${this.instanceId}-label`;
|
|
5899
|
+
/**
|
|
5900
|
+
* Prefijo de id de cada opción — único por instancia (dos listas con los
|
|
5901
|
+
* mismos values no colisionan). El id termina en el ÍNDICE, no en el
|
|
5902
|
+
* value: un value con espacios u otros caracteres daría un id inválido.
|
|
5903
|
+
*/
|
|
5904
|
+
optionIdPrefix = `${this.instanceId}-option-`;
|
|
5905
|
+
normalizedOptions = computed(() => this.options().map((option) => typeof option === 'string' ? { value: option, label: option } : option), /* @ts-ignore */
|
|
5906
|
+
...(ngDevMode ? [{ debugName: "normalizedOptions" }] : /* istanbul ignore next */ []));
|
|
5907
|
+
/**
|
|
5908
|
+
* Opción que recibe `tabindex="0"`: la seleccionada o, si ninguna lo
|
|
5909
|
+
* está, la primera — el grupo siempre debe ser alcanzable con Tab.
|
|
5910
|
+
*/
|
|
5911
|
+
tabStopValue = computed(() => {
|
|
5912
|
+
const options = this.normalizedOptions();
|
|
5913
|
+
const selected = options.find((option) => option.value === this.value());
|
|
5914
|
+
return (selected ?? options[0])?.value;
|
|
5915
|
+
}, /* @ts-ignore */
|
|
5916
|
+
...(ngDevMode ? [{ debugName: "tabStopValue" }] : /* istanbul ignore next */ []));
|
|
5917
|
+
select(value) {
|
|
5918
|
+
if (value === this.value()) {
|
|
5919
|
+
return;
|
|
5920
|
+
}
|
|
5921
|
+
this.valueChange.emit(value);
|
|
5922
|
+
}
|
|
5923
|
+
/**
|
|
5924
|
+
* `handleArrowKeyNavigation` ubica el botón destino por `idPrefix + value`;
|
|
5925
|
+
* como los ids usan el índice, se le pasa la lista con el índice como
|
|
5926
|
+
* value y se traduce de vuelta al value real al seleccionar.
|
|
5927
|
+
*/
|
|
5928
|
+
onKeydown(event, index) {
|
|
5929
|
+
const options = this.normalizedOptions();
|
|
5930
|
+
handleArrowKeyNavigation(event, options.map((_, i) => ({ value: String(i) })), String(index), this.optionIdPrefix, (next) => this.select(options[Number(next)].value), 'vertical');
|
|
5931
|
+
}
|
|
5932
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: OptionList, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5933
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: OptionList, isStandalone: true, selector: "kf-option-list", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, ngImport: i0, template: "@if (label(); as labelText) {\n <span class=\"kf-option-list__title kf-overline-text\" [id]=\"labelId\">{{ labelText }}</span>\n}\n<div\n class=\"kf-option-list\"\n role=\"radiogroup\"\n [attr.aria-labelledby]=\"label() ? labelId : null\"\n [attr.aria-label]=\"label() ? null : ariaLabel() || null\"\n>\n @for (option of normalizedOptions(); track $index; let i = $index) {\n <button\n type=\"button\"\n role=\"radio\"\n class=\"kf-option-list__option\"\n [id]=\"optionIdPrefix + i\"\n [class.kf-option-list__option--selected]=\"option.value === value()\"\n [attr.aria-checked]=\"option.value === value()\"\n [attr.tabindex]=\"option.value === tabStopValue() ? 0 : -1\"\n (click)=\"select(option.value)\"\n (keydown)=\"onKeydown($event, i)\"\n >\n <span class=\"kf-option-list__label\">{{ option.label }}</span>\n <span class=\"kf-option-list__end\">\n @if (option.count !== undefined) {\n <span class=\"kf-option-list__count\"> {{ option.count }}</span>\n }\n <span class=\"kf-option-list__check\">\n @if (option.value === value()) {\n <kf-icon name=\"check\" size=\"sm\" />\n }\n </span>\n </span>\n </button>\n }\n</div>\n", styles: [".kf-overline-text{font-family:var(--kf-font-title);font-size:var(--kf-font-size-caption);font-weight:var(--kf-font-weight-medium);letter-spacing:.04em;text-transform:uppercase;color:var(--kf-color-text-muted)}:host{display:flex;flex-direction:column;gap:var(--kf-space-sm);font-family:var(--kf-font-body)}.kf-option-list__title{color:var(--kf-color-text)}.kf-option-list{display:flex;flex-direction:column;gap:var(--kf-space-sm)}.kf-option-list__option{display:flex;align-items:center;gap:var(--kf-space-sm);box-sizing:border-box;width:100%;min-height:44px;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);font:inherit;font-size:var(--kf-font-size-body);color:var(--kf-color-text);text-align:start;cursor:pointer;transition:background .14s,border-color .14s,color .14s}.kf-option-list__option:hover:not(.kf-option-list__option--selected){border-color:var(--kf-color-border-strong)}.kf-option-list__option:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-option-list__option--selected{border-color:var(--kf-color-primary);background:var(--kf-color-primary-soft);color:var(--kf-color-primary-active);font-weight:600}.kf-option-list__label{min-width:0;overflow-wrap:anywhere}.kf-option-list__end{display:inline-flex;align-items:center;gap:var(--kf-space-sm);margin-inline-start:auto;flex:none}.kf-option-list__check{display:inline-flex;width:16px;height:16px}.kf-option-list__count{font-size:var(--kf-font-size-caption);font-variant-numeric:tabular-nums}@media(prefers-reduced-motion:reduce){.kf-option-list__option{transition:none}}\n"], dependencies: [{ kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size", "rotate", "filled"] }] });
|
|
5934
|
+
}
|
|
5935
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: OptionList, decorators: [{
|
|
5936
|
+
type: Component,
|
|
5937
|
+
args: [{ selector: 'kf-option-list', imports: [Icon], template: "@if (label(); as labelText) {\n <span class=\"kf-option-list__title kf-overline-text\" [id]=\"labelId\">{{ labelText }}</span>\n}\n<div\n class=\"kf-option-list\"\n role=\"radiogroup\"\n [attr.aria-labelledby]=\"label() ? labelId : null\"\n [attr.aria-label]=\"label() ? null : ariaLabel() || null\"\n>\n @for (option of normalizedOptions(); track $index; let i = $index) {\n <button\n type=\"button\"\n role=\"radio\"\n class=\"kf-option-list__option\"\n [id]=\"optionIdPrefix + i\"\n [class.kf-option-list__option--selected]=\"option.value === value()\"\n [attr.aria-checked]=\"option.value === value()\"\n [attr.tabindex]=\"option.value === tabStopValue() ? 0 : -1\"\n (click)=\"select(option.value)\"\n (keydown)=\"onKeydown($event, i)\"\n >\n <span class=\"kf-option-list__label\">{{ option.label }}</span>\n <span class=\"kf-option-list__end\">\n @if (option.count !== undefined) {\n <span class=\"kf-option-list__count\"> {{ option.count }}</span>\n }\n <span class=\"kf-option-list__check\">\n @if (option.value === value()) {\n <kf-icon name=\"check\" size=\"sm\" />\n }\n </span>\n </span>\n </button>\n }\n</div>\n", styles: [".kf-overline-text{font-family:var(--kf-font-title);font-size:var(--kf-font-size-caption);font-weight:var(--kf-font-weight-medium);letter-spacing:.04em;text-transform:uppercase;color:var(--kf-color-text-muted)}:host{display:flex;flex-direction:column;gap:var(--kf-space-sm);font-family:var(--kf-font-body)}.kf-option-list__title{color:var(--kf-color-text)}.kf-option-list{display:flex;flex-direction:column;gap:var(--kf-space-sm)}.kf-option-list__option{display:flex;align-items:center;gap:var(--kf-space-sm);box-sizing:border-box;width:100%;min-height:44px;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);font:inherit;font-size:var(--kf-font-size-body);color:var(--kf-color-text);text-align:start;cursor:pointer;transition:background .14s,border-color .14s,color .14s}.kf-option-list__option:hover:not(.kf-option-list__option--selected){border-color:var(--kf-color-border-strong)}.kf-option-list__option:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-option-list__option--selected{border-color:var(--kf-color-primary);background:var(--kf-color-primary-soft);color:var(--kf-color-primary-active);font-weight:600}.kf-option-list__label{min-width:0;overflow-wrap:anywhere}.kf-option-list__end{display:inline-flex;align-items:center;gap:var(--kf-space-sm);margin-inline-start:auto;flex:none}.kf-option-list__check{display:inline-flex;width:16px;height:16px}.kf-option-list__count{font-size:var(--kf-font-size-caption);font-variant-numeric:tabular-nums}@media(prefers-reduced-motion:reduce){.kf-option-list__option{transition:none}}\n"] }]
|
|
5938
|
+
}], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }] } });
|
|
5939
|
+
|
|
4626
5940
|
const MOBILE_STEP_DOWN = {
|
|
4627
5941
|
xxl: 'xl',
|
|
4628
5942
|
xl: 'lg',
|
|
@@ -4855,7 +6169,7 @@ class PortalHeader {
|
|
|
4855
6169
|
}, /* @ts-ignore */
|
|
4856
6170
|
...(ngDevMode ? [{ debugName: "thumbStyle" }] : /* istanbul ignore next */ []));
|
|
4857
6171
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: PortalHeader, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4858
|
-
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 }, logoSrc: { classPropertyName: "logoSrc", publicName: "logoSrc", isSignal: true, isRequired: false, transformFunction: null }, logoAlt: { classPropertyName: "logoAlt", publicName: "logoAlt", 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 }, tagline: { classPropertyName: "tagline", publicName: "tagline", isSignal: true, isRequired: false, transformFunction: null }, userName: { classPropertyName: "userName", publicName: "userName", isSignal: true, isRequired: false, transformFunction: null }, showCart: { classPropertyName: "showCart", publicName: "showCart", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sessionClick: "sessionClick", cartClick: "cartClick" }, host: { listeners: { "window:resize": "onWindowResize()" } }, viewQueries: [{ propertyName: "sessionButtonRef", first: true, predicate: ["sessionButton"], descendants: true, read: ElementRef, isSignal: true }, { propertyName: "navItemAnchors", predicate: ["navItemAnchor"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (isAdmin()) {\n <div class=\"kf-portal-header__admin-bar\">\n <div class=\"kf-portal-header__admin-inner\">\n <kf-admin-badge inline />\n <div class=\"kf-portal-header__admin-actions\">\n @for (action of adminActions(); track action.label) {\n <a class=\"kf-portal-header__admin-action\" [href]=\"action.href\">{{ action.label }}</a>\n }\n </div>\n </div>\n </div>\n}\n<div class=\"kf-portal-header__brand-bar\">\n <div class=\"kf-portal-header__brand-inner\">\n <div class=\"kf-portal-header__top-row\">\n <div class=\"kf-portal-header__logo-group\">\n @if (logoSrc()) {\n <img class=\"kf-portal-header__logo-img\" [src]=\"logoSrc()\" [alt]=\"logoAlt() || brand()\" />\n } @else {\n <span class=\"kf-portal-header__logo\">{{ brand() }}</span>\n }\n @if (tagline()) {\n <span class=\"kf-portal-header__logo-sep\" aria-hidden=\"true\"></span>\n <span class=\"kf-portal-header__logo-tag\">{{ tagline() }}</span>\n }\n </div>\n\n <div class=\"kf-portal-header__actions\">\n @if (showCart()) {\n <button\n type=\"button\"\n class=\"kf-portal-header__cart\"\n aria-label=\"Carrito de compras\"\n (click)=\"cartClick.emit()\"\n >\n <kf-icon name=\"cart\" size=\"md\" />\n </button>\n }\n\n <button\n #sessionButton\n type=\"button\"\n class=\"kf-portal-header__session\"\n [class.kf-portal-header__session--expanded]=\"userName()\"\n (click)=\"sessionClick.emit()\"\n >\n @if (userName()) {\n <kf-avatar [initials]=\"userInitials()\" />\n <span class=\"kf-portal-header__session-who\">\n <span class=\"kf-portal-header__session-greeting\">Hola</span>\n <span class=\"kf-portal-header__session-name\">{{ userName() }}</span>\n </span>\n <kf-icon name=\"chevron-down\" size=\"sm\" class=\"kf-portal-header__session-chevron\" />\n } @else {\n <kf-avatar [initials]=\"userInitials()\" pill />\n }\n </button>\n\n <button\n type=\"button\"\n class=\"kf-portal-header__menu-toggle\"\n [attr.aria-expanded]=\"mobileMenuOpen()\"\n aria-label=\"Abrir men\u00FA de navegaci\u00F3n\"\n (click)=\"mobileMenuOpen.set(!mobileMenuOpen())\"\n >\n <kf-icon [name]=\"mobileMenuOpen() ? 'x' : 'menu'\" size=\"md\" />\n </button>\n </div>\n </div>\n\n <nav class=\"kf-portal-header__nav\">\n <span class=\"kf-portal-header__nav-thumb\" [style]=\"thumbStyle()\" aria-hidden=\"true\"></span>\n @for (item of navItems(); track item.label) {\n <a\n #navItemAnchor\n class=\"kf-portal-header__nav-item\"\n [class.kf-portal-header__nav-item--active]=\"item.active\"\n [href]=\"item.href\"\n >\n {{ item.label }}\n </a>\n }\n </nav>\n </div>\n</div>\n\n<kf-drawer [(open)]=\"mobileMenuOpen\" ariaLabel=\"Men\u00FA de navegaci\u00F3n\">\n <div kf-drawer-header>\n <ng-content select=\"[kf-portal-header-extra]\" />\n </div>\n @for (item of navItems(); track item.label) {\n <a\n class=\"kf-portal-header__mobile-nav-item\"\n [class.kf-portal-header__mobile-nav-item--active]=\"item.active\"\n [href]=\"item.href\"\n (click)=\"mobileMenuOpen.set(false)\"\n >\n @if (item.icon) {\n <kf-icon [name]=\"item.icon\" size=\"sm\" />\n }\n <span class=\"kf-portal-header__mobile-nav-item-label\">{{ item.label }}</span>\n <kf-icon name=\"chevron-right\" size=\"sm\" />\n </a>\n }\n <div kf-drawer-footer>\n <ng-content select=\"[kf-portal-header-footer]\" />\n </div>\n</kf-drawer>\n", styles: [":host{display:block;position:sticky;top:0;z-index:30;font-family:var(--kf-font-body)}.kf-portal-header__admin-bar{padding-block:var(--kf-space-xs);background:var(--kf-color-tertiary-deep)}.kf-portal-header__admin-inner{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-md);max-width:var(--kf-header-max-width, none);margin:0 auto;padding:0 var(--kf-space-lg)}.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{background:var(--kf-color-tertiary-deep);border-top:1px solid rgba(255,255,255,.1)}.kf-portal-header__brand-inner{display:flex;flex-direction:column;max-width:var(--kf-header-max-width, none);margin:0 auto;padding:0 var(--kf-space-lg)}.kf-portal-header__top-row{display:flex;align-items:center;gap:var(--kf-space-lg);min-height:60px;padding-block:var(--kf-space-sm) 4px}.kf-portal-header__logo-group{display:flex;align-items:center;gap:var(--kf-space-md);flex:1 1 auto;min-width:0}.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__logo-img{height:28px;width:auto;display:block}.kf-portal-header__logo-sep{flex:none;width:1px;height:30px;background:#fff3}.kf-portal-header__logo-tag{font-family:var(--kf-font-title);font-weight:400;font-size:1.1875rem;color:#ffffffc7}.kf-portal-header__actions{margin-left:auto;display:flex;align-items:center;gap:var(--kf-space-sm)}.kf-portal-header__cart{flex:none;width:46px;height:46px;border-radius:var(--kf-radius-pill);border:1px solid rgba(255,255,255,.18);background:transparent;color:var(--kf-color-white);display:grid;place-items:center;cursor:pointer}.kf-portal-header__cart:hover{background:#ffffff1a;border-color:#ffffff4d}.kf-portal-header__nav{display:flex;align-items:center;gap:2px;padding:4px;margin-bottom:var(--kf-space-sm);background:#ffffff14;border-radius:var(--kf-radius-pill);width:fit-content;max-width:100%;position:relative}.kf-portal-header__nav-thumb{position:absolute;top:4px;bottom:4px;left:0;background:var(--kf-color-white);border-radius:var(--kf-radius-pill);transition:transform .25s cubic-bezier(.4,0,.2,1),width .25s cubic-bezier(.4,0,.2,1),opacity .15s linear;pointer-events:none}.kf-portal-header__nav-item{position:relative;padding:var(--kf-space-sm) var(--kf-space-md);border-radius:var(--kf-radius-pill);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);background:#ffffff14}.kf-portal-header__nav-item--active{color:var(--kf-color-ink)}.kf-portal-header__session{flex:none;border:none;background:transparent;padding:0;cursor:pointer}.kf-portal-header__session--expanded{display:flex;align-items:center;gap:var(--kf-space-sm);padding:7px 12px 7px 8px;background:var(--kf-color-primary);border-radius:var(--kf-radius-pill)}.kf-portal-header__session--expanded:hover{background:var(--kf-color-primary-active)}.kf-portal-header__session-who{display:flex;flex-direction:column;line-height:1.2;color:var(--kf-color-white)}.kf-portal-header__session-greeting{font-size:.65625rem;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:#ffffffb3}.kf-portal-header__session-name{font-size:.875rem;font-weight:700;max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kf-portal-header__session-chevron{color:#ffffffd9}.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__mobile-nav-item{display:flex;align-items:center;gap:var(--kf-space-sm);padding:var(--kf-space-sm) var(--kf-space-xs);color:var(--kf-color-text);font-size:.9375rem;font-weight:600;text-decoration:none;border-radius:var(--kf-radius-sm)}.kf-portal-header__mobile-nav-item:hover{background:var(--kf-color-surface)}.kf-portal-header__mobile-nav-item--active{color:var(--kf-color-primary-active);background:var(--kf-color-primary-soft)}.kf-portal-header__mobile-nav-item-label{flex:1 1 auto}.kf-portal-header__mobile-nav-item kf-icon:last-child{color:#171d2559}@media(max-width:768px){.kf-portal-header__nav,.kf-portal-header__session{display:none}.kf-portal-header__menu-toggle{display:inline-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", "rotate", "filled"] }, { kind: "component", type: Drawer, selector: "kf-drawer", inputs: ["open", "side", "ariaLabel"], outputs: ["openChange"] }] });
|
|
6172
|
+
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 }, logoSrc: { classPropertyName: "logoSrc", publicName: "logoSrc", isSignal: true, isRequired: false, transformFunction: null }, logoAlt: { classPropertyName: "logoAlt", publicName: "logoAlt", 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 }, tagline: { classPropertyName: "tagline", publicName: "tagline", isSignal: true, isRequired: false, transformFunction: null }, userName: { classPropertyName: "userName", publicName: "userName", isSignal: true, isRequired: false, transformFunction: null }, showCart: { classPropertyName: "showCart", publicName: "showCart", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { sessionClick: "sessionClick", cartClick: "cartClick" }, host: { listeners: { "window:resize": "onWindowResize()" } }, viewQueries: [{ propertyName: "sessionButtonRef", first: true, predicate: ["sessionButton"], descendants: true, read: ElementRef, isSignal: true }, { propertyName: "navItemAnchors", predicate: ["navItemAnchor"], descendants: true, isSignal: true }], ngImport: i0, template: "@if (isAdmin()) {\n <div class=\"kf-portal-header__admin-bar\">\n <div class=\"kf-portal-header__admin-inner\">\n <kf-admin-badge inline />\n <div class=\"kf-portal-header__admin-actions\">\n @for (action of adminActions(); track action.label) {\n <a class=\"kf-portal-header__admin-action\" [href]=\"action.href\">{{ action.label }}</a>\n }\n </div>\n </div>\n </div>\n}\n<div class=\"kf-portal-header__brand-bar\">\n <div class=\"kf-portal-header__brand-inner\">\n <div class=\"kf-portal-header__top-row\">\n <div class=\"kf-portal-header__logo-group\">\n @if (logoSrc()) {\n <img class=\"kf-portal-header__logo-img\" [src]=\"logoSrc()\" [alt]=\"logoAlt() || brand()\" />\n } @else {\n <span class=\"kf-portal-header__logo\">{{ brand() }}</span>\n }\n @if (tagline()) {\n <span class=\"kf-portal-header__logo-sep\" aria-hidden=\"true\"></span>\n <span class=\"kf-portal-header__logo-tag\">{{ tagline() }}</span>\n }\n </div>\n\n <div class=\"kf-portal-header__actions\">\n @if (showCart()) {\n <button\n type=\"button\"\n class=\"kf-portal-header__cart\"\n aria-label=\"Carrito de compras\"\n (click)=\"cartClick.emit()\"\n >\n <kf-icon name=\"cart\" size=\"md\" />\n </button>\n }\n\n <button\n #sessionButton\n type=\"button\"\n class=\"kf-portal-header__session\"\n [class.kf-portal-header__session--expanded]=\"userName()\"\n (click)=\"sessionClick.emit()\"\n >\n @if (userName()) {\n <kf-avatar [initials]=\"userInitials()\" />\n <span class=\"kf-portal-header__session-who\">\n <span class=\"kf-portal-header__session-greeting\">Hola</span>\n <span class=\"kf-portal-header__session-name\">{{ userName() }}</span>\n </span>\n <kf-icon name=\"chevron-down\" size=\"sm\" class=\"kf-portal-header__session-chevron\" />\n } @else {\n <kf-avatar [initials]=\"userInitials()\" pill />\n }\n </button>\n\n <button\n type=\"button\"\n class=\"kf-portal-header__menu-toggle\"\n [attr.aria-expanded]=\"mobileMenuOpen()\"\n aria-label=\"Abrir men\u00FA de navegaci\u00F3n\"\n (click)=\"mobileMenuOpen.set(!mobileMenuOpen())\"\n >\n <kf-icon [name]=\"mobileMenuOpen() ? 'x' : 'menu'\" size=\"md\" />\n </button>\n </div>\n </div>\n\n <nav class=\"kf-portal-header__nav\">\n <span class=\"kf-portal-header__nav-thumb\" [style]=\"thumbStyle()\" aria-hidden=\"true\"></span>\n @for (item of navItems(); track item.label) {\n <a\n #navItemAnchor\n class=\"kf-portal-header__nav-item\"\n [class.kf-portal-header__nav-item--active]=\"item.active\"\n [href]=\"item.href\"\n >\n {{ item.label }}\n </a>\n }\n </nav>\n </div>\n</div>\n\n<kf-drawer [(open)]=\"mobileMenuOpen\" ariaLabel=\"Men\u00FA de navegaci\u00F3n\">\n <div kf-drawer-header>\n <ng-content select=\"[kf-portal-header-extra]\" />\n </div>\n @for (item of navItems(); track item.label) {\n <a\n class=\"kf-portal-header__mobile-nav-item\"\n [class.kf-portal-header__mobile-nav-item--active]=\"item.active\"\n [href]=\"item.href\"\n (click)=\"mobileMenuOpen.set(false)\"\n >\n @if (item.icon) {\n <kf-icon [name]=\"item.icon\" size=\"sm\" />\n }\n <span class=\"kf-portal-header__mobile-nav-item-label\">{{ item.label }}</span>\n <kf-icon name=\"chevron-right\" size=\"sm\" />\n </a>\n }\n <div kf-drawer-footer>\n <ng-content select=\"[kf-portal-header-footer]\" />\n </div>\n</kf-drawer>\n", styles: [":host{display:block;position:sticky;top:0;z-index:30;font-family:var(--kf-font-body)}.kf-portal-header__admin-bar{padding-block:var(--kf-space-xs);background:var(--kf-color-tertiary-deep)}.kf-portal-header__admin-inner{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-md);max-width:var(--kf-header-max-width, none);margin:0 auto;padding:0 var(--kf-space-lg)}.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{background:var(--kf-color-tertiary-deep);border-top:1px solid rgba(255,255,255,.1)}.kf-portal-header__brand-inner{display:flex;flex-direction:column;max-width:var(--kf-header-max-width, none);margin:0 auto;padding:0 var(--kf-space-lg)}.kf-portal-header__top-row{display:flex;align-items:center;gap:var(--kf-space-lg);min-height:60px;padding-block:var(--kf-space-sm) 4px}.kf-portal-header__logo-group{display:flex;align-items:center;gap:var(--kf-space-md);flex:1 1 auto;min-width:0}.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__logo-img{height:28px;width:auto;display:block}.kf-portal-header__logo-sep{flex:none;width:1px;height:30px;background:#fff3}.kf-portal-header__logo-tag{font-family:var(--kf-font-title);font-weight:400;font-size:1.1875rem;color:#ffffffc7}.kf-portal-header__actions{margin-left:auto;display:flex;align-items:center;gap:var(--kf-space-sm)}.kf-portal-header__cart{flex:none;width:46px;height:46px;border-radius:var(--kf-radius-pill);border:1px solid rgba(255,255,255,.18);background:transparent;color:var(--kf-color-white);display:grid;place-items:center;cursor:pointer}.kf-portal-header__cart:hover{background:#ffffff1a;border-color:#ffffff4d}.kf-portal-header__nav{display:flex;align-items:center;gap:2px;padding:4px;margin-bottom:var(--kf-space-sm);background:#ffffff14;border-radius:var(--kf-radius-pill);width:fit-content;max-width:100%;position:relative}.kf-portal-header__nav-thumb{position:absolute;top:4px;bottom:4px;left:0;background:var(--kf-color-white);border-radius:var(--kf-radius-pill);transition:transform .25s cubic-bezier(.4,0,.2,1),width .25s cubic-bezier(.4,0,.2,1),opacity .15s linear;pointer-events:none}.kf-portal-header__nav-item{position:relative;padding:var(--kf-space-sm) var(--kf-space-md);border-radius:var(--kf-radius-pill);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);background:#ffffff14}.kf-portal-header__nav-item--active{color:var(--kf-color-ink)}.kf-portal-header__session{flex:none;border:none;background:transparent;padding:0;cursor:pointer}.kf-portal-header__session--expanded{display:flex;align-items:center;gap:var(--kf-space-sm);padding:7px 12px 7px 8px;background:var(--kf-color-primary);border-radius:var(--kf-radius-pill)}.kf-portal-header__session--expanded:hover{background:var(--kf-color-primary-active)}.kf-portal-header__session-who{display:flex;flex-direction:column;line-height:1.2;color:var(--kf-color-white)}.kf-portal-header__session-greeting{font-size:.65625rem;font-weight:600;letter-spacing:.06em;text-transform:uppercase;color:#ffffffb3}.kf-portal-header__session-name{font-size:.875rem;font-weight:700;max-width:150px;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kf-portal-header__session-chevron{color:#ffffffd9}.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__mobile-nav-item{display:flex;align-items:center;gap:var(--kf-space-sm);padding:var(--kf-space-sm) var(--kf-space-xs);color:var(--kf-color-text);font-size:.9375rem;font-weight:600;text-decoration:none;border-radius:var(--kf-radius-sm)}.kf-portal-header__mobile-nav-item:hover{background:var(--kf-color-surface)}.kf-portal-header__mobile-nav-item--active{color:var(--kf-color-primary-active);background:var(--kf-color-primary-soft)}.kf-portal-header__mobile-nav-item-label{flex:1 1 auto}.kf-portal-header__mobile-nav-item kf-icon:last-child{color:#171d2559}@media(max-width:768px){.kf-portal-header__nav,.kf-portal-header__session{display:none}.kf-portal-header__menu-toggle{display:inline-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", "rotate", "filled"] }, { kind: "component", type: Drawer, selector: "kf-drawer", inputs: ["open", "side", "role", "ariaLabel", "title", "closeLabel"], outputs: ["openChange"] }] });
|
|
4859
6173
|
}
|
|
4860
6174
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: PortalHeader, decorators: [{
|
|
4861
6175
|
type: Component,
|
|
@@ -5370,11 +6684,11 @@ class Segmented {
|
|
|
5370
6684
|
this.valueChange.emit(nextValue);
|
|
5371
6685
|
}
|
|
5372
6686
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Segmented, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5373
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Segmented, isStandalone: true, selector: "kf-segmented", 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: "<div class=\"kf-segmented__track\" [style.--kf-segmented-count]=\"options().length\">\n <div\n class=\"kf-segmented__thumb\"\n [style.--kf-segmented-index]=\"selectedIndex()\"\n [style.visibility]=\"selectedIndex() === -1 ? 'hidden' : 'visible'\"\n ></div>\n @for (option of options(); track option.value) {\n <button\n type=\"button\"\n class=\"kf-segmented__option\"\n [class.kf-segmented__option--active]=\"option.value === value()\"\n [attr.aria-pressed]=\"option.value === value()\"\n (click)=\"select(option.value)\"\n >\n {{ option.label }}\n </button>\n }\n</div>\n", styles: [":host{display:inline-block}.kf-segmented__track{position:relative;display:grid;grid-template-columns:repeat(var(--kf-segmented-count),1fr);gap:2px;padding:2px;background:var(--kf-color-surface);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md)}.kf-segmented__thumb{position:absolute;top:2px;left:2px;width:calc((100% - 4px) / var(--kf-segmented-count));height:calc(100% - 4px);background:var(--kf-color-
|
|
6687
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Segmented, isStandalone: true, selector: "kf-segmented", 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: "<div class=\"kf-segmented__track\" [style.--kf-segmented-count]=\"options().length\">\n <div\n class=\"kf-segmented__thumb\"\n [style.--kf-segmented-index]=\"selectedIndex()\"\n [style.visibility]=\"selectedIndex() === -1 ? 'hidden' : 'visible'\"\n ></div>\n @for (option of options(); track option.value) {\n <button\n type=\"button\"\n class=\"kf-segmented__option\"\n [class.kf-segmented__option--active]=\"option.value === value()\"\n [attr.aria-pressed]=\"option.value === value()\"\n (click)=\"select(option.value)\"\n >\n {{ option.label }}\n </button>\n }\n</div>\n", styles: [":host{display:inline-block}.kf-segmented__track{position:relative;display:grid;grid-template-columns:repeat(var(--kf-segmented-count),1fr);gap:2px;padding:2px;background:var(--kf-color-surface);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md)}.kf-segmented__thumb{position:absolute;top:2px;left:2px;width:calc((100% - 4px) / var(--kf-segmented-count));height:calc(100% - 4px);background:var(--kf-color-primary);border-radius:var(--kf-radius-sm);transform:translate(calc(var(--kf-segmented-index, 0) * 100%));transition:transform .2s ease,background .14s}.kf-segmented__option{position:relative;z-index:1;font:inherit;padding:var(--kf-space-xs) var(--kf-space-md);border:none;background:transparent;color:var(--kf-color-text-muted);border-radius:var(--kf-radius-sm);cursor:pointer;transition:color .14s}.kf-segmented__option:hover:not(.kf-segmented__option--active){color:var(--kf-color-text)}.kf-segmented__option--active{color:var(--kf-color-white)}.kf-segmented__option:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}\n"] });
|
|
5374
6688
|
}
|
|
5375
6689
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Segmented, decorators: [{
|
|
5376
6690
|
type: Component,
|
|
5377
|
-
args: [{ selector: 'kf-segmented', template: "<div class=\"kf-segmented__track\" [style.--kf-segmented-count]=\"options().length\">\n <div\n class=\"kf-segmented__thumb\"\n [style.--kf-segmented-index]=\"selectedIndex()\"\n [style.visibility]=\"selectedIndex() === -1 ? 'hidden' : 'visible'\"\n ></div>\n @for (option of options(); track option.value) {\n <button\n type=\"button\"\n class=\"kf-segmented__option\"\n [class.kf-segmented__option--active]=\"option.value === value()\"\n [attr.aria-pressed]=\"option.value === value()\"\n (click)=\"select(option.value)\"\n >\n {{ option.label }}\n </button>\n }\n</div>\n", styles: [":host{display:inline-block}.kf-segmented__track{position:relative;display:grid;grid-template-columns:repeat(var(--kf-segmented-count),1fr);gap:2px;padding:2px;background:var(--kf-color-surface);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md)}.kf-segmented__thumb{position:absolute;top:2px;left:2px;width:calc((100% - 4px) / var(--kf-segmented-count));height:calc(100% - 4px);background:var(--kf-color-
|
|
6691
|
+
args: [{ selector: 'kf-segmented', template: "<div class=\"kf-segmented__track\" [style.--kf-segmented-count]=\"options().length\">\n <div\n class=\"kf-segmented__thumb\"\n [style.--kf-segmented-index]=\"selectedIndex()\"\n [style.visibility]=\"selectedIndex() === -1 ? 'hidden' : 'visible'\"\n ></div>\n @for (option of options(); track option.value) {\n <button\n type=\"button\"\n class=\"kf-segmented__option\"\n [class.kf-segmented__option--active]=\"option.value === value()\"\n [attr.aria-pressed]=\"option.value === value()\"\n (click)=\"select(option.value)\"\n >\n {{ option.label }}\n </button>\n }\n</div>\n", styles: [":host{display:inline-block}.kf-segmented__track{position:relative;display:grid;grid-template-columns:repeat(var(--kf-segmented-count),1fr);gap:2px;padding:2px;background:var(--kf-color-surface);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-md)}.kf-segmented__thumb{position:absolute;top:2px;left:2px;width:calc((100% - 4px) / var(--kf-segmented-count));height:calc(100% - 4px);background:var(--kf-color-primary);border-radius:var(--kf-radius-sm);transform:translate(calc(var(--kf-segmented-index, 0) * 100%));transition:transform .2s ease,background .14s}.kf-segmented__option{position:relative;z-index:1;font:inherit;padding:var(--kf-space-xs) var(--kf-space-md);border:none;background:transparent;color:var(--kf-color-text-muted);border-radius:var(--kf-radius-sm);cursor:pointer;transition:color .14s}.kf-segmented__option:hover:not(.kf-segmented__option--active){color:var(--kf-color-text)}.kf-segmented__option--active{color:var(--kf-color-white)}.kf-segmented__option:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}\n"] }]
|
|
5378
6692
|
}], 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"] }] } });
|
|
5379
6693
|
|
|
5380
6694
|
/**
|
|
@@ -5436,6 +6750,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
|
|
|
5436
6750
|
* estado activo. `collapsed` oculta el label y centra el ícono, controlado
|
|
5437
6751
|
* por el propio shell. No sabe nada de routing: emite `href` como
|
|
5438
6752
|
* cualquier link, el consumidor intercepta el click.
|
|
6753
|
+
*
|
|
6754
|
+
* Colapsado, el link solo muestra el ícono: su nombre accesible sale de
|
|
6755
|
+
* `aria-label` = `label` (el tooltip es solo visual y no nombra el link).
|
|
5439
6756
|
*/
|
|
5440
6757
|
class SidebarNavItem {
|
|
5441
6758
|
icon = input.required(/* @ts-ignore */
|
|
@@ -5447,11 +6764,11 @@ class SidebarNavItem {
|
|
|
5447
6764
|
active = input(false, { ...(ngDevMode ? { debugName: "active" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
5448
6765
|
collapsed = input(false, { ...(ngDevMode ? { debugName: "collapsed" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
5449
6766
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: SidebarNavItem, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5450
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: SidebarNavItem, isStandalone: true, selector: "kf-sidebar-nav-item", inputs: { icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, href: { classPropertyName: "href", publicName: "href", isSignal: true, isRequired: true, transformFunction: null }, active: { classPropertyName: "active", publicName: "active", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<a\n class=\"kf-sidebar-nav-item\"\n [class.kf-sidebar-nav-item--active]=\"active()\"\n [class.kf-sidebar-nav-item--collapsed]=\"collapsed()\"\n [href]=\"href()\"\n [attr.aria-current]=\"active() ? 'page' : null\"\n [kfTooltip]=\"collapsed() ? label() : ''\"\n>\n <kf-icon [name]=\"icon()\" size=\"md\" />\n @if (!collapsed()) {\n <span class=\"kf-sidebar-nav-item__label\">{{ label() }}</span>\n }\n</a>\n", styles: [".kf-sidebar-nav-item{box-sizing:border-box;position:relative;display:flex;align-items:center;gap:var(--kf-space-sm);padding:var(--kf-space-sm) var(--kf-space-md);border-radius:var(--kf-radius-sm);color:#ffffffb3;font-family:var(--kf-font-body);font-size:.875rem;font-weight:600;text-decoration:none;white-space:nowrap;transition:background .14s,color .14s}.kf-sidebar-nav-item:hover:not(.kf-sidebar-nav-item--active){background:#ffffff0f;color:var(--kf-color-white)}.kf-sidebar-nav-item--collapsed:hover:not(.kf-sidebar-nav-item--active){background:#ffffff1f}.kf-sidebar-nav-item--active{background:#ffffff1f;color:var(--kf-color-white)}.kf-sidebar-nav-item--active:before{content:\"\";position:absolute;left:calc(-1 * var(--kf-space-xs));top:20%;bottom:20%;width:3px;border-radius:var(--kf-radius-pill);background:var(--kf-color-primary-hover)}.kf-sidebar-nav-item--collapsed{justify-content:center;width:40px;height:40px;padding-inline:0}.kf-sidebar-nav-item--collapsed.kf-sidebar-nav-item--active:before{left:calc(-1 * var(--kf-space-sm))}\n"], dependencies: [{ kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size", "rotate", "filled"] }, { kind: "directive", type: TooltipDirective, selector: "[kfTooltip]", inputs: ["kfTooltip", "kfTooltipPosition"] }] });
|
|
6767
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: SidebarNavItem, isStandalone: true, selector: "kf-sidebar-nav-item", inputs: { icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, href: { classPropertyName: "href", publicName: "href", isSignal: true, isRequired: true, transformFunction: null }, active: { classPropertyName: "active", publicName: "active", isSignal: true, isRequired: false, transformFunction: null }, collapsed: { classPropertyName: "collapsed", publicName: "collapsed", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: "<a\n class=\"kf-sidebar-nav-item\"\n [class.kf-sidebar-nav-item--active]=\"active()\"\n [class.kf-sidebar-nav-item--collapsed]=\"collapsed()\"\n [href]=\"href()\"\n [attr.aria-current]=\"active() ? 'page' : null\"\n [attr.aria-label]=\"collapsed() ? label() : null\"\n [kfTooltip]=\"collapsed() ? label() : ''\"\n>\n <kf-icon [name]=\"icon()\" size=\"md\" />\n @if (!collapsed()) {\n <span class=\"kf-sidebar-nav-item__label\">{{ label() }}</span>\n }\n</a>\n", styles: [".kf-sidebar-nav-item{box-sizing:border-box;position:relative;display:flex;align-items:center;gap:var(--kf-space-sm);padding:var(--kf-space-sm) var(--kf-space-md);border-radius:var(--kf-radius-sm);color:#ffffffb3;font-family:var(--kf-font-body);font-size:.875rem;font-weight:600;text-decoration:none;white-space:nowrap;transition:background .14s,color .14s}.kf-sidebar-nav-item:hover:not(.kf-sidebar-nav-item--active){background:#ffffff0f;color:var(--kf-color-white)}.kf-sidebar-nav-item--collapsed:hover:not(.kf-sidebar-nav-item--active){background:#ffffff1f}.kf-sidebar-nav-item--active{background:#ffffff1f;color:var(--kf-color-white)}.kf-sidebar-nav-item--active:before{content:\"\";position:absolute;left:calc(-1 * var(--kf-space-xs));top:20%;bottom:20%;width:3px;border-radius:var(--kf-radius-pill);background:var(--kf-color-primary-hover)}.kf-sidebar-nav-item--collapsed{justify-content:center;width:40px;height:40px;padding-inline:0}.kf-sidebar-nav-item--collapsed.kf-sidebar-nav-item--active:before{left:calc(-1 * var(--kf-space-sm))}\n"], dependencies: [{ kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size", "rotate", "filled"] }, { kind: "directive", type: TooltipDirective, selector: "[kfTooltip]", inputs: ["kfTooltip", "kfTooltipPosition"] }] });
|
|
5451
6768
|
}
|
|
5452
6769
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: SidebarNavItem, decorators: [{
|
|
5453
6770
|
type: Component,
|
|
5454
|
-
args: [{ selector: 'kf-sidebar-nav-item', imports: [Icon, TooltipDirective], template: "<a\n class=\"kf-sidebar-nav-item\"\n [class.kf-sidebar-nav-item--active]=\"active()\"\n [class.kf-sidebar-nav-item--collapsed]=\"collapsed()\"\n [href]=\"href()\"\n [attr.aria-current]=\"active() ? 'page' : null\"\n [kfTooltip]=\"collapsed() ? label() : ''\"\n>\n <kf-icon [name]=\"icon()\" size=\"md\" />\n @if (!collapsed()) {\n <span class=\"kf-sidebar-nav-item__label\">{{ label() }}</span>\n }\n</a>\n", styles: [".kf-sidebar-nav-item{box-sizing:border-box;position:relative;display:flex;align-items:center;gap:var(--kf-space-sm);padding:var(--kf-space-sm) var(--kf-space-md);border-radius:var(--kf-radius-sm);color:#ffffffb3;font-family:var(--kf-font-body);font-size:.875rem;font-weight:600;text-decoration:none;white-space:nowrap;transition:background .14s,color .14s}.kf-sidebar-nav-item:hover:not(.kf-sidebar-nav-item--active){background:#ffffff0f;color:var(--kf-color-white)}.kf-sidebar-nav-item--collapsed:hover:not(.kf-sidebar-nav-item--active){background:#ffffff1f}.kf-sidebar-nav-item--active{background:#ffffff1f;color:var(--kf-color-white)}.kf-sidebar-nav-item--active:before{content:\"\";position:absolute;left:calc(-1 * var(--kf-space-xs));top:20%;bottom:20%;width:3px;border-radius:var(--kf-radius-pill);background:var(--kf-color-primary-hover)}.kf-sidebar-nav-item--collapsed{justify-content:center;width:40px;height:40px;padding-inline:0}.kf-sidebar-nav-item--collapsed.kf-sidebar-nav-item--active:before{left:calc(-1 * var(--kf-space-sm))}\n"] }]
|
|
6771
|
+
args: [{ selector: 'kf-sidebar-nav-item', imports: [Icon, TooltipDirective], template: "<a\n class=\"kf-sidebar-nav-item\"\n [class.kf-sidebar-nav-item--active]=\"active()\"\n [class.kf-sidebar-nav-item--collapsed]=\"collapsed()\"\n [href]=\"href()\"\n [attr.aria-current]=\"active() ? 'page' : null\"\n [attr.aria-label]=\"collapsed() ? label() : null\"\n [kfTooltip]=\"collapsed() ? label() : ''\"\n>\n <kf-icon [name]=\"icon()\" size=\"md\" />\n @if (!collapsed()) {\n <span class=\"kf-sidebar-nav-item__label\">{{ label() }}</span>\n }\n</a>\n", styles: [".kf-sidebar-nav-item{box-sizing:border-box;position:relative;display:flex;align-items:center;gap:var(--kf-space-sm);padding:var(--kf-space-sm) var(--kf-space-md);border-radius:var(--kf-radius-sm);color:#ffffffb3;font-family:var(--kf-font-body);font-size:.875rem;font-weight:600;text-decoration:none;white-space:nowrap;transition:background .14s,color .14s}.kf-sidebar-nav-item:hover:not(.kf-sidebar-nav-item--active){background:#ffffff0f;color:var(--kf-color-white)}.kf-sidebar-nav-item--collapsed:hover:not(.kf-sidebar-nav-item--active){background:#ffffff1f}.kf-sidebar-nav-item--active{background:#ffffff1f;color:var(--kf-color-white)}.kf-sidebar-nav-item--active:before{content:\"\";position:absolute;left:calc(-1 * var(--kf-space-xs));top:20%;bottom:20%;width:3px;border-radius:var(--kf-radius-pill);background:var(--kf-color-primary-hover)}.kf-sidebar-nav-item--collapsed{justify-content:center;width:40px;height:40px;padding-inline:0}.kf-sidebar-nav-item--collapsed.kf-sidebar-nav-item--active:before{left:calc(-1 * var(--kf-space-sm))}\n"] }]
|
|
5455
6772
|
}], propDecorators: { icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], href: [{ type: i0.Input, args: [{ isSignal: true, alias: "href", required: true }] }], active: [{ type: i0.Input, args: [{ isSignal: true, alias: "active", required: false }] }], collapsed: [{ type: i0.Input, args: [{ isSignal: true, alias: "collapsed", required: false }] }] } });
|
|
5456
6773
|
|
|
5457
6774
|
const MOBILE_BREAKPOINT_QUERY = '(max-width: 900px)';
|
|
@@ -6334,5 +7651,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
|
|
|
6334
7651
|
* Generated bundle index. Do not edit.
|
|
6335
7652
|
*/
|
|
6336
7653
|
|
|
6337
|
-
export { ActionTile, AdminBadge, Alert, AlertRow, Avatar, Badge, Breadcrumb, BucketProgress, Button, Calendar, Card, Carousel, CarouselSlide, Checkbox, Chip, Collapsible, ContactPopover, ContentSlot, ContentState, ContentViewer, DEFAULT_PAGE_SIZE_OPTIONS, DataTable, Divider, DocumentRow, Drawer, Dropdown, DropdownItem, DropdownPanel, EmptyState, EventTimeline, FeatureCard, Field, FieldLabel, Filter, FilterChips, FolderTabs, FolderTabsPanel, Footer, FormatInputDirective, Icon, Input, JourneyStepper, Link, MetricCard, Modal, MultiSelect, NoteBlock, PageHeader, Pagination, Panel, PortalHeader, Progress, PulseDot, RowActions, SectionHeader, SectionNav, SectionNavItem, SectionNavSubsection, Segmented, Select, SelectableCardGroup, SidebarShell, Skeleton, Spinner, StatCard, StatTile, StatusPill, Tabs, Text, Textarea, TileGrid, TimeSlotPicker, ToastHost, ToastService, TooltipDirective, ViewToggle, dataTableViewSignal };
|
|
7654
|
+
export { ActionTile, AdminBadge, Alert, AlertRow, Avatar, Badge, Breadcrumb, BucketProgress, Button, Calendar, Card, Carousel, CarouselSlide, Checkbox, Chip, Collapsible, ContactPopover, ContentSlot, ContentState, ContentViewer, DEFAULT_PAGE_SIZE_OPTIONS, DataTable, Divider, DocumentRow, Drawer, Dropdown, DropdownItem, DropdownPanel, EmptyState, EventTimeline, FeatureCard, Field, FieldLabel, Filter, FilterChips, FolderTabs, FolderTabsPanel, Footer, FormatInputDirective, Icon, Input, JourneyStepper, LIST_TOOLBAR_HYSTERESIS, Link, ListToolbar, MetricCard, Modal, MultiSelect, NoteBlock, OptionList, PageHeader, Pagination, Panel, PortalHeader, Progress, PulseDot, RowActions, SectionHeader, SectionNav, SectionNavItem, SectionNavSubsection, Segmented, Select, SelectableCardGroup, SidebarShell, Skeleton, Spinner, StatCard, StatTile, StatusPill, Tabs, Text, Textarea, TileGrid, TimeSlotPicker, ToastHost, ToastService, TooltipDirective, ViewToggle, dataTableViewSignal, resolveListToolbarMode };
|
|
6338
7655
|
//# sourceMappingURL=kaufmann-lab-ui-core.mjs.map
|