@kaufmann-lab/ui 0.1.20 → 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 +257 -17
- package/fesm2022/kaufmann-lab-ui-business.mjs.map +1 -1
- package/fesm2022/kaufmann-lab-ui-core.mjs +1408 -99
- 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 +150 -13
- package/types/kaufmann-lab-ui-core.d.ts +859 -52
|
@@ -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
|
|
@@ -2666,6 +2793,25 @@ const COMPACT_PANEL_MIN_WIDTH = 72;
|
|
|
2666
2793
|
* layouts flex/grid donde el ancho fijo de `md` no encaja.
|
|
2667
2794
|
*
|
|
2668
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"`).
|
|
2669
2815
|
*/
|
|
2670
2816
|
class Select {
|
|
2671
2817
|
label = input(/* @ts-ignore */
|
|
@@ -2702,8 +2848,32 @@ class Select {
|
|
|
2702
2848
|
...(ngDevMode ? [{ debugName: "searchable" }] : /* istanbul ignore next */ []));
|
|
2703
2849
|
searchThreshold = input(7, /* @ts-ignore */
|
|
2704
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 */ []));
|
|
2705
2875
|
valueChange = output();
|
|
2706
|
-
instanceId = `kf-select-${nextId$
|
|
2876
|
+
instanceId = `kf-select-${nextId$3++}`;
|
|
2707
2877
|
triggerId = `${this.instanceId}-trigger`;
|
|
2708
2878
|
listboxId = `${this.instanceId}-listbox`;
|
|
2709
2879
|
errorId = `${this.instanceId}-error`;
|
|
@@ -2718,6 +2888,25 @@ class Select {
|
|
|
2718
2888
|
...(ngDevMode ? [{ debugName: "normalizedOptions" }] : /* istanbul ignore next */ []));
|
|
2719
2889
|
selectedOption = computed(() => this.normalizedOptions().find((option) => option.value === this.value()), /* @ts-ignore */
|
|
2720
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 */ []));
|
|
2721
2910
|
showSearch = computed(() => this.searchable() ?? this.normalizedOptions().length > this.searchThreshold(), /* @ts-ignore */
|
|
2722
2911
|
...(ngDevMode ? [{ debugName: "showSearch" }] : /* istanbul ignore next */ []));
|
|
2723
2912
|
/** Opciones visibles tras aplicar el filtro de búsqueda (case/acento-insensitive). */
|
|
@@ -2735,11 +2924,14 @@ class Select {
|
|
|
2735
2924
|
...(ngDevMode ? [{ debugName: "panelTemplate" }] : /* istanbul ignore next */ []));
|
|
2736
2925
|
searchInputRef = viewChild('searchInput', /* @ts-ignore */
|
|
2737
2926
|
...(ngDevMode ? [{ debugName: "searchInputRef" }] : /* istanbul ignore next */ []));
|
|
2738
|
-
|
|
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'));
|
|
2739
2931
|
constructor() {
|
|
2740
2932
|
effect(() => {
|
|
2741
2933
|
if (this.isOpen()) {
|
|
2742
|
-
this.overlay.attach(this.panelTemplate(), () => this.
|
|
2934
|
+
this.overlay.attach(this.panelTemplate(), () => this.close());
|
|
2743
2935
|
}
|
|
2744
2936
|
else {
|
|
2745
2937
|
this.overlay.detach();
|
|
@@ -2751,9 +2943,20 @@ class Select {
|
|
|
2751
2943
|
// dentro de un effect() (NG0203: pierde el contexto de inyección) — hay
|
|
2752
2944
|
// que usar afterRenderEffect(), que sí es reactivo a signals y corre
|
|
2753
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.
|
|
2754
2950
|
afterRenderEffect(() => {
|
|
2755
|
-
if (this.isOpen()
|
|
2951
|
+
if (!this.isOpen()) {
|
|
2952
|
+
return;
|
|
2953
|
+
}
|
|
2954
|
+
if (this.showSearch()) {
|
|
2756
2955
|
this.searchInputRef()?.nativeElement.focus();
|
|
2956
|
+
return;
|
|
2957
|
+
}
|
|
2958
|
+
if (this.panelRef()) {
|
|
2959
|
+
untracked(() => this.focusOption(this.activeIndex()));
|
|
2757
2960
|
}
|
|
2758
2961
|
});
|
|
2759
2962
|
inject(DestroyRef).onDestroy(() => this.overlay.dispose());
|
|
@@ -2765,13 +2968,125 @@ class Select {
|
|
|
2765
2968
|
* panel está abierto, `keydownEvents()` nunca dispara.
|
|
2766
2969
|
*/
|
|
2767
2970
|
onDocumentEscape() {
|
|
2768
|
-
this.
|
|
2971
|
+
this.close();
|
|
2769
2972
|
}
|
|
2770
2973
|
toggle() {
|
|
2771
2974
|
if (this.disabled()) {
|
|
2772
2975
|
return;
|
|
2773
2976
|
}
|
|
2774
|
-
this.isOpen
|
|
2977
|
+
if (this.isOpen()) {
|
|
2978
|
+
this.close();
|
|
2979
|
+
}
|
|
2980
|
+
else {
|
|
2981
|
+
this.open();
|
|
2982
|
+
}
|
|
2983
|
+
}
|
|
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
|
+
}
|
|
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}`;
|
|
2775
3090
|
}
|
|
2776
3091
|
/** Nombre accesible explícito solo para opciones con `tag`/`detail`, para separar las partes al leerlas. */
|
|
2777
3092
|
optionAriaLabel(option) {
|
|
@@ -2781,7 +3096,7 @@ class Select {
|
|
|
2781
3096
|
return [option.label, option.tag, option.detail].filter(Boolean).join(', ');
|
|
2782
3097
|
}
|
|
2783
3098
|
select(option) {
|
|
2784
|
-
this.
|
|
3099
|
+
this.close(true);
|
|
2785
3100
|
if (option.value !== this.value()) {
|
|
2786
3101
|
this.valueChange.emit(option.value);
|
|
2787
3102
|
}
|
|
@@ -2803,6 +3118,17 @@ class Select {
|
|
|
2803
3118
|
return;
|
|
2804
3119
|
}
|
|
2805
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' });
|
|
2806
3132
|
}
|
|
2807
3133
|
selectActive() {
|
|
2808
3134
|
const option = this.filteredOptions()[this.activeIndex()];
|
|
@@ -2811,17 +3137,17 @@ class Select {
|
|
|
2811
3137
|
}
|
|
2812
3138
|
}
|
|
2813
3139
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Select, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2814
|
-
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 [class.kf-select__trigger--tagged]=\"!!selectedOption()?.tag\"\n (click)=\"toggle()\"\n>\n <span class=\"kf-select__value\" [class.kf-select__value--placeholder]=\"!selectedOption()\">\n {{ selectedOption()?.label ?? placeholder() }}\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 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 [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.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 @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)}: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__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;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__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__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"] }, { 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"] }] });
|
|
2815
3141
|
}
|
|
2816
3142
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Select, decorators: [{
|
|
2817
3143
|
type: Component,
|
|
2818
|
-
args: [{ selector: 'kf-select', imports: [Badge, FieldLabel], host: {
|
|
3144
|
+
args: [{ selector: 'kf-select', imports: [Badge, FieldLabel, Icon], host: {
|
|
2819
3145
|
'[class.kf-select--compact]': "variant() === 'compact'",
|
|
2820
3146
|
'[class.kf-select--width-sm]': "width() === 'sm'",
|
|
2821
3147
|
'[class.kf-select--width-lg]': "width() === 'lg'",
|
|
2822
3148
|
'[class.kf-select--width-full]': "width() === 'full'",
|
|
2823
|
-
}, 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 [class.kf-select__trigger--tagged]=\"!!selectedOption()?.tag\"\n (click)=\"toggle()\"\n>\n <span class=\"kf-select__value\" [class.kf-select__value--placeholder]=\"!selectedOption()\">\n {{
|
|
2824
|
-
}], 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: [{
|
|
2825
3151
|
type: HostListener,
|
|
2826
3152
|
args: ['document:keydown.escape']
|
|
2827
3153
|
}] } });
|
|
@@ -2898,11 +3224,11 @@ class Pagination {
|
|
|
2898
3224
|
this.pageChange.emit(clamped);
|
|
2899
3225
|
}
|
|
2900
3226
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Pagination, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2901
|
-
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"] }] });
|
|
2902
3228
|
}
|
|
2903
3229
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Pagination, decorators: [{
|
|
2904
3230
|
type: Component,
|
|
2905
|
-
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"] }]
|
|
2906
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"] }] } });
|
|
2907
3233
|
/**
|
|
2908
3234
|
* Construye la lista de items visibles con el algoritmo de elipsis estándar:
|
|
@@ -2947,11 +3273,28 @@ function buildPaginationItems(current, total) {
|
|
|
2947
3273
|
* navegación con flechas izquierda/derecha, igual que un <input type="radio">
|
|
2948
3274
|
* nativo agrupado.
|
|
2949
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
|
+
*
|
|
2950
3281
|
* <kf-view-toggle [view]="view" (viewChange)="view = $event" />
|
|
3282
|
+
* <kf-view-toggle iconOnly [view]="view" (viewChange)="view = $event" />
|
|
2951
3283
|
*/
|
|
2952
3284
|
class ViewToggle {
|
|
2953
3285
|
view = input.required(/* @ts-ignore */
|
|
2954
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 */ []));
|
|
2955
3298
|
viewChange = output();
|
|
2956
3299
|
select(next) {
|
|
2957
3300
|
if (next === this.view()) {
|
|
@@ -2973,13 +3316,17 @@ class ViewToggle {
|
|
|
2973
3316
|
nextButton?.focus();
|
|
2974
3317
|
}
|
|
2975
3318
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: ViewToggle, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2976
|
-
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"] }] });
|
|
2977
3320
|
}
|
|
2978
3321
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: ViewToggle, decorators: [{
|
|
2979
3322
|
type: Component,
|
|
2980
|
-
args: [{ selector: 'kf-view-toggle', template: "<div class=\"kf-view-toggle\" role=\"radiogroup\" aria-label=\"
|
|
2981
|
-
}], 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"] }] } });
|
|
2982
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';
|
|
2983
3330
|
/** Opciones de "filas por página" estándar del portal — 10 como default
|
|
2984
3331
|
* cubre bien tanto un dataset chico (ej. Beneficios) como uno con volumen
|
|
2985
3332
|
* real (ej. 30+ cuotas en Billing), sin sentirse vacío ni insuficiente; 25
|
|
@@ -3048,6 +3395,12 @@ const DEFAULT_PAGE_SIZE_OPTIONS = [10, 25, 50];
|
|
|
3048
3395
|
* <kf-data-table [columns]="columns" [rows]="rows" [pageSize]="25" itemLabel="beneficios" />
|
|
3049
3396
|
* <kf-data-table [columns]="columns" [rows]="rows" [pageSize]="25" [pageSizeOptions]="[]" />
|
|
3050
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
|
+
*
|
|
3051
3404
|
* Ordenamiento por columna: cada `DataTableColumn` con `sortable: true`
|
|
3052
3405
|
* vuelve su header clickeable, con el mismo formato `key:direction` que
|
|
3053
3406
|
* usa `sort` de `kf-quote-picker` (two-way manual, no `model()` — el
|
|
@@ -3061,6 +3414,55 @@ const DEFAULT_PAGE_SIZE_OPTIONS = [10, 25, 50];
|
|
|
3061
3414
|
*
|
|
3062
3415
|
* Densidad: `density="compact"` reduce el padding vertical de cada fila,
|
|
3063
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)" />
|
|
3064
3466
|
*/
|
|
3065
3467
|
class DataTable {
|
|
3066
3468
|
columns = input.required(/* @ts-ignore */
|
|
@@ -3086,6 +3488,14 @@ class DataTable {
|
|
|
3086
3488
|
...(ngDevMode ? [{ debugName: "canScrollLeft" }] : /* istanbul ignore next */ []));
|
|
3087
3489
|
canScrollRight = signal(false, /* @ts-ignore */
|
|
3088
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 */ []));
|
|
3089
3499
|
constructor() {
|
|
3090
3500
|
afterNextRender(() => {
|
|
3091
3501
|
const el = this.scrollEl()?.nativeElement;
|
|
@@ -3111,15 +3521,45 @@ class DataTable {
|
|
|
3111
3521
|
this.measureStickyOffsets();
|
|
3112
3522
|
});
|
|
3113
3523
|
// `rows()` cambia de identidad cuando el consumidor filtra el dataset
|
|
3114
|
-
// base (ej. cambiar de tab)
|
|
3115
|
-
//
|
|
3116
|
-
//
|
|
3117
|
-
//
|
|
3118
|
-
// solo a `rows()
|
|
3119
|
-
|
|
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;
|
|
3120
3530
|
effect(() => {
|
|
3121
3531
|
this.rows();
|
|
3122
|
-
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));
|
|
3123
3563
|
});
|
|
3124
3564
|
}
|
|
3125
3565
|
updateScrollState(el) {
|
|
@@ -3128,6 +3568,7 @@ class DataTable {
|
|
|
3128
3568
|
// al final (o nunca tuvo overflow real).
|
|
3129
3569
|
this.canScrollLeft.set(el.scrollLeft > 1);
|
|
3130
3570
|
this.canScrollRight.set(el.scrollWidth - el.clientWidth - el.scrollLeft > 1);
|
|
3571
|
+
this.hasOverflow.set(el.scrollWidth - el.clientWidth > 1);
|
|
3131
3572
|
}
|
|
3132
3573
|
/** Default: índice de la fila dentro de `rows`. */
|
|
3133
3574
|
rowKey = input(/* @ts-ignore */
|
|
@@ -3136,10 +3577,16 @@ class DataTable {
|
|
|
3136
3577
|
flush = input(false, /* @ts-ignore */
|
|
3137
3578
|
...(ngDevMode ? [{ debugName: "flush" }] : /* istanbul ignore next */ []));
|
|
3138
3579
|
/** Habilita el toggle de vista tabla/cards. */
|
|
3139
|
-
cardable = input(false, /*
|
|
3140
|
-
...(ngDevMode ? [{ debugName: "cardable" }] : /* istanbul ignore next */ []));
|
|
3580
|
+
cardable = input(false, { ...(ngDevMode ? { debugName: "cardable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
3141
3581
|
defaultView = input('table', /* @ts-ignore */
|
|
3142
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 */ []));
|
|
3143
3590
|
/**
|
|
3144
3591
|
* Formateo simple de celda como texto: `(row) => string`. Para marcado
|
|
3145
3592
|
* real (badges, links, botones) usar `cellTemplates`.
|
|
@@ -3157,6 +3604,17 @@ class DataTable {
|
|
|
3157
3604
|
rowClickable = input(null, /* @ts-ignore */
|
|
3158
3605
|
...(ngDevMode ? [{ debugName: "rowClickable" }] : /* istanbul ignore next */ []));
|
|
3159
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 */ []));
|
|
3160
3618
|
/**
|
|
3161
3619
|
* Paginación interna opcional — sin `pageSize`, cero cambio de
|
|
3162
3620
|
* comportamiento (el consumidor sigue paginando afuera si quiere, vía el
|
|
@@ -3173,7 +3631,17 @@ class DataTable {
|
|
|
3173
3631
|
* aunque `pageSize` esté activo (paginación sin selector). */
|
|
3174
3632
|
pageSizeOptions = input(DEFAULT_PAGE_SIZE_OPTIONS, /* @ts-ignore */
|
|
3175
3633
|
...(ngDevMode ? [{ debugName: "pageSizeOptions" }] : /* istanbul ignore next */ []));
|
|
3176
|
-
|
|
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. */
|
|
3177
3645
|
pageSizeChange = output();
|
|
3178
3646
|
/** Nombre de la entidad listada (ej. "beneficios", "facturas"), en
|
|
3179
3647
|
* plural — se agrega al final del texto informativo de la paginación
|
|
@@ -3182,8 +3650,6 @@ class DataTable {
|
|
|
3182
3650
|
* fijo, cada consumidor decide el suyo. */
|
|
3183
3651
|
itemLabel = input('', /* @ts-ignore */
|
|
3184
3652
|
...(ngDevMode ? [{ debugName: "itemLabel" }] : /* istanbul ignore next */ []));
|
|
3185
|
-
page = signal(1, /* @ts-ignore */
|
|
3186
|
-
...(ngDevMode ? [{ debugName: "page" }] : /* istanbul ignore next */ []));
|
|
3187
3653
|
internalPageSize = signal(null, /* @ts-ignore */
|
|
3188
3654
|
...(ngDevMode ? [{ debugName: "internalPageSize" }] : /* istanbul ignore next */ []));
|
|
3189
3655
|
/** Tamaño de página efectivo: el que el usuario elija en el selector
|
|
@@ -3288,15 +3754,82 @@ class DataTable {
|
|
|
3288
3754
|
return;
|
|
3289
3755
|
}
|
|
3290
3756
|
this.page.set(clamped);
|
|
3291
|
-
this.pageChange.emit(clamped);
|
|
3292
3757
|
}
|
|
3293
3758
|
setPageSize(size) {
|
|
3294
3759
|
if (size === null) {
|
|
3295
3760
|
return;
|
|
3296
3761
|
}
|
|
3297
3762
|
this.internalPageSize.set(size);
|
|
3298
|
-
|
|
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.
|
|
3299
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;
|
|
3300
3833
|
}
|
|
3301
3834
|
/** Columnas fijas del lado izquierdo/derecho, con el offset acumulado
|
|
3302
3835
|
* (medido en runtime, ver `measureStickyOffsets`) para `position: sticky`. */
|
|
@@ -3317,11 +3850,18 @@ class DataTable {
|
|
|
3317
3850
|
zIndex: '1',
|
|
3318
3851
|
};
|
|
3319
3852
|
}
|
|
3320
|
-
|
|
3321
|
-
|
|
3322
|
-
|
|
3323
|
-
|
|
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 */
|
|
3324
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 */ []));
|
|
3325
3865
|
/** Primera columna de cada rol único; el resto se ignora en el encabezado. */
|
|
3326
3866
|
cardMediaColumn = computed(() => this.columns().find((c) => c.cardRole === 'media'), /* @ts-ignore */
|
|
3327
3867
|
...(ngDevMode ? [{ debugName: "cardMediaColumn" }] : /* istanbul ignore next */ []));
|
|
@@ -3331,14 +3871,20 @@ class DataTable {
|
|
|
3331
3871
|
...(ngDevMode ? [{ debugName: "cardStatusColumns" }] : /* istanbul ignore next */ []));
|
|
3332
3872
|
cardActionColumns = computed(() => this.columns().filter((c) => c.cardRole === 'action'), /* @ts-ignore */
|
|
3333
3873
|
...(ngDevMode ? [{ debugName: "cardActionColumns" }] : /* istanbul ignore next */ []));
|
|
3334
|
-
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 */
|
|
3335
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 */ []));
|
|
3336
3882
|
/**
|
|
3337
3883
|
* La card entera es clickeable solo si no tiene acciones propias: un
|
|
3338
3884
|
* `role="button"` con botones adentro es inválido y deja el foco ambiguo.
|
|
3339
3885
|
* Con columnas `action`, la interacción la manejan esos botones.
|
|
3340
3886
|
*/
|
|
3341
|
-
cardInteractive = computed(() => this.cardActionColumns().length === 0, /* @ts-ignore */
|
|
3887
|
+
cardInteractive = computed(() => this.rowClickable() ?? this.cardActionColumns().length === 0, /* @ts-ignore */
|
|
3342
3888
|
...(ngDevMode ? [{ debugName: "cardInteractive" }] : /* istanbul ignore next */ []));
|
|
3343
3889
|
/**
|
|
3344
3890
|
* Mismo criterio que `cardInteractive`, para la vista de tabla: la fila
|
|
@@ -3351,12 +3897,23 @@ class DataTable {
|
|
|
3351
3897
|
rowInteractive = computed(() => this.rowClickable() ?? this.cardActionColumns().length === 0, /* @ts-ignore */
|
|
3352
3898
|
...(ngDevMode ? [{ debugName: "rowInteractive" }] : /* istanbul ignore next */ []));
|
|
3353
3899
|
setView(next) {
|
|
3354
|
-
this.
|
|
3900
|
+
this.view.set(next);
|
|
3355
3901
|
}
|
|
3356
3902
|
trackRow(row, index) {
|
|
3357
3903
|
const keyFn = this.rowKey();
|
|
3358
3904
|
return keyFn ? keyFn(row, index) : index;
|
|
3359
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
|
+
}
|
|
3360
3917
|
cellValue(row, column) {
|
|
3361
3918
|
const renderFn = this.render();
|
|
3362
3919
|
if (renderFn) {
|
|
@@ -3365,12 +3922,45 @@ class DataTable {
|
|
|
3365
3922
|
const value = row[column.key];
|
|
3366
3923
|
return value === null || value === undefined ? '' : String(value);
|
|
3367
3924
|
}
|
|
3368
|
-
|
|
3369
|
-
|
|
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)) {
|
|
3370
3951
|
return;
|
|
3371
3952
|
}
|
|
3372
3953
|
this.rowClick.emit({ row, index });
|
|
3373
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
|
+
}
|
|
3374
3964
|
/**
|
|
3375
3965
|
* Ancho real de cada columna `sticky` — se mide después de cada render
|
|
3376
3966
|
* porque el ancho de una columna depende de su contenido, no hay forma
|
|
@@ -3400,12 +3990,12 @@ class DataTable {
|
|
|
3400
3990
|
this.stickyOffsets.set(offsets);
|
|
3401
3991
|
}
|
|
3402
3992
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DataTable, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3403
|
-
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"] }] });
|
|
3404
3994
|
}
|
|
3405
3995
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: DataTable, decorators: [{
|
|
3406
3996
|
type: Component,
|
|
3407
|
-
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"] }]
|
|
3408
|
-
}], 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"] }] } });
|
|
3409
3999
|
|
|
3410
4000
|
/**
|
|
3411
4001
|
* Signal con la vista de `kf-data-table` ('table' | 'cards') derivada de un
|
|
@@ -3525,6 +4115,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
|
|
|
3525
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"] }]
|
|
3526
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 }] }] } });
|
|
3527
4117
|
|
|
4118
|
+
let nextId$2 = 0;
|
|
3528
4119
|
/**
|
|
3529
4120
|
* Panel lateral genérico, agnóstico de contenido — no sabe qué es navegación
|
|
3530
4121
|
* ni qué es sesión, solo da tres lugares para proyectar contenido
|
|
@@ -3557,6 +4148,46 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
|
|
|
3557
4148
|
* <a href="/">Home</a>
|
|
3558
4149
|
* <a href="/vehiculos">Vehículos</a>
|
|
3559
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.
|
|
3560
4191
|
*/
|
|
3561
4192
|
class Drawer {
|
|
3562
4193
|
/** Visibilidad del panel, two-way: `[(open)]="menuOpen"`. */
|
|
@@ -3564,13 +4195,32 @@ class Drawer {
|
|
|
3564
4195
|
...(ngDevMode ? [{ debugName: "open" }] : /* istanbul ignore next */ []));
|
|
3565
4196
|
/** Borde de anclaje del panel. `left` (default) es la convención dominante
|
|
3566
4197
|
* (Material/iOS/Android): el botón que abre el drawer y el propio panel
|
|
3567
|
-
* quedan del mismo lado. */
|
|
4198
|
+
* quedan del mismo lado. `bottom` lo convierte en hoja inferior. */
|
|
3568
4199
|
side = input('left', /* @ts-ignore */
|
|
3569
4200
|
...(ngDevMode ? [{ debugName: "side" }] : /* istanbul ignore next */ []));
|
|
3570
|
-
/** `
|
|
3571
|
-
|
|
3572
|
-
|
|
3573
|
-
|
|
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++}`;
|
|
3574
4224
|
panelRef = viewChild.required('panel', /* @ts-ignore */
|
|
3575
4225
|
...(ngDevMode ? [{ debugName: "panelRef" }] : /* istanbul ignore next */ []));
|
|
3576
4226
|
focusTrapFactory = inject(FocusTrapFactory);
|
|
@@ -3587,14 +4237,26 @@ class Drawer {
|
|
|
3587
4237
|
else {
|
|
3588
4238
|
this.focusTrap?.destroy();
|
|
3589
4239
|
this.focusTrap = null;
|
|
3590
|
-
this.lastFocusedElement?.
|
|
4240
|
+
if (this.lastFocusedElement?.isConnected) {
|
|
4241
|
+
this.lastFocusedElement.focus();
|
|
4242
|
+
}
|
|
3591
4243
|
this.lastFocusedElement = null;
|
|
3592
4244
|
}
|
|
3593
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
|
+
}
|
|
3594
4253
|
inject(DestroyRef).onDestroy(() => this.focusTrap?.destroy());
|
|
3595
4254
|
}
|
|
3596
4255
|
/** Cierra el drawer con Escape — mismo patrón que `Dropdown`/`Modal`. */
|
|
3597
|
-
onEscape() {
|
|
4256
|
+
onEscape(event) {
|
|
4257
|
+
if (!this.open() || event.defaultPrevented) {
|
|
4258
|
+
return;
|
|
4259
|
+
}
|
|
3598
4260
|
this.open.set(false);
|
|
3599
4261
|
}
|
|
3600
4262
|
getOrCreateFocusTrap() {
|
|
@@ -3604,14 +4266,21 @@ class Drawer {
|
|
|
3604
4266
|
return this.focusTrap;
|
|
3605
4267
|
}
|
|
3606
4268
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Drawer, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3607
|
-
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"] }] });
|
|
3608
4270
|
}
|
|
3609
4271
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Drawer, decorators: [{
|
|
3610
4272
|
type: Component,
|
|
3611
|
-
args: [{ selector: 'kf-drawer',
|
|
3612
|
-
|
|
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: [{
|
|
3613
4282
|
type: HostListener,
|
|
3614
|
-
args: ['document:keydown.escape']
|
|
4283
|
+
args: ['document:keydown.escape', ['$event']]
|
|
3615
4284
|
}] } });
|
|
3616
4285
|
|
|
3617
4286
|
/**
|
|
@@ -3782,6 +4451,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
|
|
|
3782
4451
|
* está en vuelo).
|
|
3783
4452
|
*
|
|
3784
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" />
|
|
3785
4475
|
*/
|
|
3786
4476
|
class Input {
|
|
3787
4477
|
static nextId = 0;
|
|
@@ -3816,12 +4506,34 @@ class Input {
|
|
|
3816
4506
|
...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
|
|
3817
4507
|
placeholder = input('', /* @ts-ignore */
|
|
3818
4508
|
...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
|
|
3819
|
-
/**
|
|
3820
|
-
*
|
|
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. */
|
|
3821
4522
|
inputmode = input(/* @ts-ignore */
|
|
3822
4523
|
...(ngDevMode ? [undefined, { debugName: "inputmode" }] : /* istanbul ignore next */ []));
|
|
3823
4524
|
/** Agrega el botón de mostrar/ocultar — sin efecto si `type` no es `"password"`. */
|
|
3824
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();
|
|
3825
4537
|
inputId = `kf-input-${Input.nextId++}`;
|
|
3826
4538
|
hintId = `${this.inputId}-hint`;
|
|
3827
4539
|
errorId = `${this.inputId}-error`;
|
|
@@ -3841,6 +4553,9 @@ class Input {
|
|
|
3841
4553
|
/** El botón solo tiene sentido si el campo realmente es de contraseña. */
|
|
3842
4554
|
showPasswordToggle = computed(() => this.toggleable() && this.type() === 'password', /* @ts-ignore */
|
|
3843
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 */ []));
|
|
3844
4559
|
/**
|
|
3845
4560
|
* `type` real que recibe el `<input>` nativo: igual a `type()` salvo
|
|
3846
4561
|
* cuando el toggle está activo y visible, donde pasa a `text` para
|
|
@@ -3859,6 +4574,10 @@ class Input {
|
|
|
3859
4574
|
// ya cargado (ngModel/formControl con valor inicial) se refleje siempre.
|
|
3860
4575
|
this.nativeInput().nativeElement.value = this.value();
|
|
3861
4576
|
}
|
|
4577
|
+
/** Enfoca el `<input>` nativo (mismas opciones que `HTMLElement.focus`). */
|
|
4578
|
+
focus(options) {
|
|
4579
|
+
this.nativeInput().nativeElement.focus(options);
|
|
4580
|
+
}
|
|
3862
4581
|
writeValue(value) {
|
|
3863
4582
|
const nextValue = value ?? '';
|
|
3864
4583
|
this.value.set(nextValue);
|
|
@@ -3888,6 +4607,20 @@ class Input {
|
|
|
3888
4607
|
this.focused.set(false);
|
|
3889
4608
|
this.onTouched();
|
|
3890
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
|
+
}
|
|
3891
4624
|
/**
|
|
3892
4625
|
* Alterna la visibilidad del valor. No dispara `onBlur`/pierde el foco:
|
|
3893
4626
|
* el botón vive dentro del mismo `.kf-input__field`, así que un click acá
|
|
@@ -3897,13 +4630,13 @@ class Input {
|
|
|
3897
4630
|
this.passwordVisible.update((visible) => !visible);
|
|
3898
4631
|
}
|
|
3899
4632
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Input, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
3900
|
-
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: [
|
|
3901
4634
|
{
|
|
3902
4635
|
provide: NG_VALUE_ACCESSOR,
|
|
3903
4636
|
useExisting: forwardRef(() => Input),
|
|
3904
4637
|
multi: true,
|
|
3905
4638
|
},
|
|
3906
|
-
], 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 });
|
|
3907
4640
|
}
|
|
3908
4641
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Input, decorators: [{
|
|
3909
4642
|
type: Component,
|
|
@@ -3913,10 +4646,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
|
|
|
3913
4646
|
useExisting: forwardRef(() => Input),
|
|
3914
4647
|
multi: true,
|
|
3915
4648
|
},
|
|
3916
|
-
], 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"] }]
|
|
3917
|
-
}], 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"] }] } });
|
|
3918
4651
|
|
|
3919
|
-
let nextId = 0;
|
|
4652
|
+
let nextId$1 = 0;
|
|
3920
4653
|
/**
|
|
3921
4654
|
* Dropdown de selección múltiple con checkboxes internos. El panel flotante
|
|
3922
4655
|
* se conecta al trigger vía `@angular/cdk/overlay` — el mismo mecanismo que
|
|
@@ -3973,7 +4706,7 @@ class MultiSelect {
|
|
|
3973
4706
|
searchThreshold = input(7, /* @ts-ignore */
|
|
3974
4707
|
...(ngDevMode ? [{ debugName: "searchThreshold" }] : /* istanbul ignore next */ []));
|
|
3975
4708
|
valuesChange = output();
|
|
3976
|
-
instanceId = `kf-multi-select-${nextId++}`;
|
|
4709
|
+
instanceId = `kf-multi-select-${nextId$1++}`;
|
|
3977
4710
|
triggerId = `${this.instanceId}-trigger`;
|
|
3978
4711
|
listboxId = `${this.instanceId}-listbox`;
|
|
3979
4712
|
isOpen = signal(false, /* @ts-ignore */
|
|
@@ -4170,7 +4903,7 @@ class Filter {
|
|
|
4170
4903
|
this.fieldChange.emit({ key, value });
|
|
4171
4904
|
}
|
|
4172
4905
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Filter, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4173
|
-
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"] }] });
|
|
4174
4907
|
}
|
|
4175
4908
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Filter, decorators: [{
|
|
4176
4909
|
type: Component,
|
|
@@ -4181,19 +4914,31 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
|
|
|
4181
4914
|
* Fila de chips tipo "pill" para filtros de una sola selección.
|
|
4182
4915
|
*
|
|
4183
4916
|
* Cada opción puede pasarse como string simple (se usa como value y label
|
|
4184
|
-
* a la vez) o como objeto { value, label } cuando el texto
|
|
4185
|
-
* 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.
|
|
4186
4919
|
*
|
|
4187
4920
|
* El chip activo se resalta con `--kf-color-primary` — mismo color de marca
|
|
4188
4921
|
* que `kf-tabs`/`kf-select` para su propio estado activo, así el filtro
|
|
4189
4922
|
* seleccionado se lee con el color del tema activo, no un negro fijo que
|
|
4190
4923
|
* no cambia entre marcas.
|
|
4191
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
|
+
*
|
|
4192
4930
|
* <kf-filter-chips [options]="['Todos', 'Activos', 'Archivados']"
|
|
4193
4931
|
* [value]="filtro"
|
|
4194
4932
|
* (valueChange)="filtro = $event" />
|
|
4195
4933
|
*
|
|
4196
|
-
*
|
|
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 }]"
|
|
4197
4942
|
* [value]="filtro"
|
|
4198
4943
|
* (valueChange)="filtro = $event" />
|
|
4199
4944
|
*/
|
|
@@ -4202,23 +4947,34 @@ class FilterChips {
|
|
|
4202
4947
|
...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
|
|
4203
4948
|
value = input('', /* @ts-ignore */
|
|
4204
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 });
|
|
4205
4958
|
valueChange = output();
|
|
4206
|
-
/** Normaliza cada opción string/objeto a la forma { value, label }
|
|
4959
|
+
/** Normaliza cada opción string/objeto a la forma `{ value, label, count? }`. */
|
|
4207
4960
|
normalizedOptions = computed(() => this.options().map((option) => typeof option === 'string' ? { value: option, label: option } : option), /* @ts-ignore */
|
|
4208
4961
|
...(ngDevMode ? [{ debugName: "normalizedOptions" }] : /* istanbul ignore next */ []));
|
|
4209
4962
|
select(value) {
|
|
4210
4963
|
if (value === this.value()) {
|
|
4964
|
+
if (this.deselectable() && value !== '') {
|
|
4965
|
+
this.valueChange.emit('');
|
|
4966
|
+
}
|
|
4211
4967
|
return;
|
|
4212
4968
|
}
|
|
4213
4969
|
this.valueChange.emit(value);
|
|
4214
4970
|
}
|
|
4215
4971
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: FilterChips, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4216
|
-
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"] });
|
|
4217
4973
|
}
|
|
4218
4974
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: FilterChips, decorators: [{
|
|
4219
4975
|
type: Component,
|
|
4220
|
-
args: [{ selector: 'kf-filter-chips', template: "<div
|
|
4221
|
-
}], 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"] }] } });
|
|
4222
4978
|
|
|
4223
4979
|
/**
|
|
4224
4980
|
* Pie de página general del portal (página 38): logo + copyright a la
|
|
@@ -4335,6 +5091,408 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
|
|
|
4335
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"] }]
|
|
4336
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 }] }] } });
|
|
4337
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
|
+
|
|
4338
5496
|
/**
|
|
4339
5497
|
* Card de estadística/KPI: label, valor grande, ícono opcional en la esquina
|
|
4340
5498
|
* superior derecha, badge de esquina opcional (pegado al valor) y una nota
|
|
@@ -4464,6 +5622,8 @@ function installModalAwareOverlayContainer(container) {
|
|
|
4464
5622
|
}
|
|
4465
5623
|
|
|
4466
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"])';
|
|
4467
5627
|
/**
|
|
4468
5628
|
* Diálogo centrado sobre un backdrop: confirmaciones destructivas, formularios
|
|
4469
5629
|
* cortos, decisiones que bloquean el flujo.
|
|
@@ -4501,6 +5661,27 @@ let modalId = 0;
|
|
|
4501
5661
|
*
|
|
4502
5662
|
* <kf-link footer class="kf-modal__footer-start" disabled>Ver documento</kf-link>
|
|
4503
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>
|
|
4504
5685
|
*/
|
|
4505
5686
|
class Modal {
|
|
4506
5687
|
title = input('', /* @ts-ignore */
|
|
@@ -4516,9 +5697,20 @@ class Modal {
|
|
|
4516
5697
|
* angostas, mismo `min()` de siempre. */
|
|
4517
5698
|
width = input(null, /* @ts-ignore */
|
|
4518
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 */ []));
|
|
4519
5709
|
titleId = `kf-modal-title-${modalId++}`;
|
|
4520
5710
|
dialog = viewChild('dialog', /* @ts-ignore */
|
|
4521
5711
|
...(ngDevMode ? [{ debugName: "dialog" }] : /* istanbul ignore next */ []));
|
|
5712
|
+
panel = viewChild('panel', /* @ts-ignore */
|
|
5713
|
+
...(ngDevMode ? [{ debugName: "panel" }] : /* istanbul ignore next */ []));
|
|
4522
5714
|
document = inject(DOCUMENT);
|
|
4523
5715
|
overlayContainer = inject(OverlayContainer);
|
|
4524
5716
|
constructor() {
|
|
@@ -4542,6 +5734,7 @@ class Modal {
|
|
|
4542
5734
|
else {
|
|
4543
5735
|
el.open = true;
|
|
4544
5736
|
}
|
|
5737
|
+
this.focusInitial();
|
|
4545
5738
|
}
|
|
4546
5739
|
else if (!this.open() && el.open) {
|
|
4547
5740
|
if (typeof el.close === 'function') {
|
|
@@ -4573,6 +5766,24 @@ class Modal {
|
|
|
4573
5766
|
}
|
|
4574
5767
|
});
|
|
4575
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
|
+
}
|
|
4576
5787
|
/** `Escape` dispara `cancel`; cancelarlo lo deja abierto. */
|
|
4577
5788
|
onCancel(event) {
|
|
4578
5789
|
if (!this.dismissible()) {
|
|
@@ -4594,12 +5805,12 @@ class Modal {
|
|
|
4594
5805
|
}
|
|
4595
5806
|
}
|
|
4596
5807
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Modal, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4597
|
-
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"] }] });
|
|
4598
5809
|
}
|
|
4599
5810
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Modal, decorators: [{
|
|
4600
5811
|
type: Component,
|
|
4601
|
-
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
|
|
4602
|
-
}], 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: [{
|
|
4603
5814
|
type: HostListener,
|
|
4604
5815
|
args: ['click', ['$event']]
|
|
4605
5816
|
}] } });
|
|
@@ -4631,6 +5842,101 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
|
|
|
4631
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"] }]
|
|
4632
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 }] }] } });
|
|
4633
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
|
+
|
|
4634
5940
|
const MOBILE_STEP_DOWN = {
|
|
4635
5941
|
xxl: 'xl',
|
|
4636
5942
|
xl: 'lg',
|
|
@@ -4863,7 +6169,7 @@ class PortalHeader {
|
|
|
4863
6169
|
}, /* @ts-ignore */
|
|
4864
6170
|
...(ngDevMode ? [{ debugName: "thumbStyle" }] : /* istanbul ignore next */ []));
|
|
4865
6171
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: PortalHeader, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
4866
|
-
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"] }] });
|
|
4867
6173
|
}
|
|
4868
6174
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: PortalHeader, decorators: [{
|
|
4869
6175
|
type: Component,
|
|
@@ -5378,11 +6684,11 @@ class Segmented {
|
|
|
5378
6684
|
this.valueChange.emit(nextValue);
|
|
5379
6685
|
}
|
|
5380
6686
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Segmented, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5381
|
-
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"] });
|
|
5382
6688
|
}
|
|
5383
6689
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Segmented, decorators: [{
|
|
5384
6690
|
type: Component,
|
|
5385
|
-
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"] }]
|
|
5386
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"] }] } });
|
|
5387
6693
|
|
|
5388
6694
|
/**
|
|
@@ -5444,6 +6750,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
|
|
|
5444
6750
|
* estado activo. `collapsed` oculta el label y centra el ícono, controlado
|
|
5445
6751
|
* por el propio shell. No sabe nada de routing: emite `href` como
|
|
5446
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).
|
|
5447
6756
|
*/
|
|
5448
6757
|
class SidebarNavItem {
|
|
5449
6758
|
icon = input.required(/* @ts-ignore */
|
|
@@ -5455,11 +6764,11 @@ class SidebarNavItem {
|
|
|
5455
6764
|
active = input(false, { ...(ngDevMode ? { debugName: "active" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
5456
6765
|
collapsed = input(false, { ...(ngDevMode ? { debugName: "collapsed" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
|
|
5457
6766
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: SidebarNavItem, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
5458
|
-
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"] }] });
|
|
5459
6768
|
}
|
|
5460
6769
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: SidebarNavItem, decorators: [{
|
|
5461
6770
|
type: Component,
|
|
5462
|
-
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"] }]
|
|
5463
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 }] }] } });
|
|
5464
6773
|
|
|
5465
6774
|
const MOBILE_BREAKPOINT_QUERY = '(max-width: 900px)';
|
|
@@ -6342,5 +7651,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
|
|
|
6342
7651
|
* Generated bundle index. Do not edit.
|
|
6343
7652
|
*/
|
|
6344
7653
|
|
|
6345
|
-
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 };
|
|
6346
7655
|
//# sourceMappingURL=kaufmann-lab-ui-core.mjs.map
|