@kaufmann-lab/ui 0.1.11 → 0.1.14
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.mjs +5561 -4440
- package/fesm2022/kaufmann-lab-ui.mjs.map +1 -1
- package/fesm2022/mapbox-gl-csp.worker.js +6 -0
- package/package.json +2 -1
- package/src/tokens/base.css +51 -0
- package/src/tokens/themes/andes-maq.css +7 -0
- package/src/tokens/themes/divemotor-star.css +16 -3
- package/types/kaufmann-lab-ui.d.ts +2736 -538
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as _angular_core from '@angular/core';
|
|
2
|
-
import { AfterContentInit, TemplateRef, ElementRef, AfterViewInit } from '@angular/core';
|
|
2
|
+
import { AfterContentInit, TemplateRef, Signal, ElementRef, AfterViewInit, InjectionToken, Provider } from '@angular/core';
|
|
3
3
|
import { ControlValueAccessor } from '@angular/forms';
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -13,12 +13,36 @@ import { ControlValueAccessor } from '@angular/forms';
|
|
|
13
13
|
* comparten `Icon`, `icons/index.ts` (registro) y cualquier componente que
|
|
14
14
|
* reciba un `IconName` (`Button`, `MetricCard`, etc.) — no debería requerir
|
|
15
15
|
* importar el componente `Icon` completo solo para tipar una prop.
|
|
16
|
+
*
|
|
17
|
+
* `minus`, `maximize`, `minimize`, `crosshair` y `navigation` son una
|
|
18
|
+
* extensión propia (mismo criterio que `eye-off`): página 5 del PDF no las
|
|
19
|
+
* incluye, pero los controles flotantes de `kf-map` (zoom, fullscreen,
|
|
20
|
+
* recenter, follow) los necesitan y ninguno tiene un glyph equivalente ya
|
|
21
|
+
* en el set — antes de agregarlos se confirmó que no había forma de resolver
|
|
22
|
+
* esas 5 acciones con un ícono ya existente.
|
|
23
|
+
*
|
|
24
|
+
* `message-circle`, `monitor`, `zap` y `pin` son otra extensión propia, mismo
|
|
25
|
+
* criterio: el selector de canal de entrada de ticket (Llamada/WhatsApp/
|
|
26
|
+
* Portal/API·agente) de `apps/operations` necesita "burbuja de chat",
|
|
27
|
+
* "pantalla" y "rayo" y ninguno tiene glyph equivalente en el set (`phone`
|
|
28
|
+
* ya cubre "Llamada", no hizo falta agregarlo). `pin` es la acción de
|
|
29
|
+
* fijar/soltar un subheader — se confirmó que no alcanza con reusar
|
|
30
|
+
* `map-pin`, que en este mismo dominio representa ubicación geográfica real
|
|
31
|
+
* de vehículos; reusarlo para "fijar" generaría ambigüedad con los markers
|
|
32
|
+
* de mapa.
|
|
16
33
|
*/
|
|
17
|
-
type IconName = 'activity' | 'alert-circle' | 'alert-octagon' | 'alert-triangle' | 'award' | 'bar-chart' | 'bell' | 'bus' | 'calculator' | 'calendar' | 'car' | 'cart' | 'check' | 'check-circle' | 'check-square' | 'chevron-down' | 'chevron-left' | 'chevron-left-bar' | 'chevron-right' | 'clipboard' | 'clock' | 'credit-card' | 'download' | 'edit' | 'external-link' | 'eye' | 'eye-off' | 'file' | 'filter' | 'gauge' | 'gift' | 'grid' | 'history' | 'home' | 'inbox' | 'layers' | 'list' | 'log-out' | 'map' | 'map-pin' | 'menu' | 'mic' | 'mic-off' | 'more-vertical' | 'pause' | 'phone' | 'play' | 'plus' | 'search' | 'settings' | 'shield-check' | 'sort' | 'star' | 'ticket' | 'tool' | 'trash' | 'truck' | 'upload-cloud' | 'user' | 'user-search' | 'video' | 'x';
|
|
34
|
+
type IconName = 'activity' | 'alert-circle' | 'alert-octagon' | 'alert-triangle' | 'award' | 'bar-chart' | 'bell' | 'bus' | 'calculator' | 'calendar' | 'car' | 'cart' | 'check' | 'check-circle' | 'check-square' | 'chevron-down' | 'chevron-left' | 'chevron-left-bar' | 'chevron-right' | 'clipboard' | 'clock' | 'credit-card' | 'crosshair' | 'download' | 'edit' | 'external-link' | 'eye' | 'eye-off' | 'file' | 'filter' | 'gauge' | 'gift' | 'grid' | 'history' | 'home' | 'inbox' | 'layers' | 'list' | 'log-out' | 'map' | 'map-pin' | 'maximize' | 'menu' | 'message-circle' | 'mic' | 'mic-off' | 'minimize' | 'minus' | 'monitor' | 'more-vertical' | 'navigation' | 'pause' | 'phone' | 'pin' | 'play' | 'plus' | 'search' | 'settings' | 'shield-check' | 'sort' | 'star' | 'ticket' | 'tool' | 'trash' | 'truck' | 'upload-cloud' | 'user' | 'user-search' | 'video' | 'x' | 'zap';
|
|
18
35
|
|
|
19
36
|
type IconSize = 'sm' | 'md' | 'lg';
|
|
37
|
+
/** Ángulos de rotación soportados por `rotate` — cubre los casos reales del
|
|
38
|
+
* design system (un chevron que apunta en 4 direcciones posibles según
|
|
39
|
+
* estado, ej. orden ascendente/descendente o un panel abierto/cerrado);
|
|
40
|
+
* no un `number` libre, para no habilitar ángulos arbitrarios sin caso de
|
|
41
|
+
* uso ni animación pensada. */
|
|
42
|
+
type IconRotation = 0 | 90 | 180 | 270;
|
|
20
43
|
/**
|
|
21
|
-
* Ícono de trazo del design system —
|
|
44
|
+
* Ícono de trazo del design system — 71 glyphs (59 de la página 5 + extensiones
|
|
45
|
+
* propias documentadas en `icon-name.ts`), grilla 24×24,
|
|
22
46
|
* grosor de trazo 1.9px. Referencia un sprite SVG embebido (ver
|
|
23
47
|
* `icon-sprite.ts`) vía `<use>`, así que no hay peso de red adicional por
|
|
24
48
|
* cada instancia: el sprite se inyecta al DOM una única vez, la primera
|
|
@@ -29,17 +53,36 @@ type IconSize = 'sm' | 'md' | 'lg';
|
|
|
29
53
|
* sigue siempre `currentColor` — se hereda del texto del elemento
|
|
30
54
|
* contenedor, nunca se fija en el propio ícono.
|
|
31
55
|
*
|
|
56
|
+
* `rotate` gira el mismo glyph en vez de requerir un ícono aparte por
|
|
57
|
+
* dirección — útil para un chevron que representa un estado con más de una
|
|
58
|
+
* orientación (ej. orden ascendente/descendente de una columna, un panel
|
|
59
|
+
* abierto/cerrado). La rotación transiciona con la misma duración que el
|
|
60
|
+
* resto de las transiciones cortas del design system.
|
|
61
|
+
*
|
|
32
62
|
* <kf-icon name="alert-triangle" size="sm" />
|
|
63
|
+
* <kf-icon name="chevron-down" [rotate]="isOpen() ? 180 : 0" />
|
|
33
64
|
* <kf-button icon="calendar">Reservar hora</kf-button> <!-- ver Button -->
|
|
34
65
|
*/
|
|
35
66
|
declare class Icon {
|
|
36
67
|
readonly name: _angular_core.InputSignal<IconName>;
|
|
37
68
|
readonly size: _angular_core.InputSignal<IconSize>;
|
|
69
|
+
readonly rotate: _angular_core.InputSignal<IconRotation>;
|
|
70
|
+
/**
|
|
71
|
+
* Rellena el glyph (`fill: currentColor`) en vez de dejarlo solo de trazo.
|
|
72
|
+
* Pensado para un estado activo/marcado de un ícono usado como toggle (ej.
|
|
73
|
+
* "fijar" un panel) — un cambio de COLOR únicamente puede no distinguirse
|
|
74
|
+
* lo suficiente en marcas cuyo `--kf-color-secondary` queda cerca del color
|
|
75
|
+
* de texto por default; rellenar el área cambia más píxeles y se percibe
|
|
76
|
+
* con más contraste aunque el tono sea similar. No es el default del
|
|
77
|
+
* design system (sigue siendo de trazo, página 6 del PDF) — es opt-in por
|
|
78
|
+
* componente para ese caso puntual.
|
|
79
|
+
*/
|
|
80
|
+
readonly filled: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
38
81
|
private readonly document;
|
|
39
82
|
protected readonly pixelSize: _angular_core.Signal<string>;
|
|
40
83
|
constructor();
|
|
41
84
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<Icon, never>;
|
|
42
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<Icon, "kf-icon", never, { "name": { "alias": "name"; "required": true; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
85
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<Icon, "kf-icon", never, { "name": { "alias": "name"; "required": true; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "rotate": { "alias": "rotate"; "required": false; "isSignal": true; }; "filled": { "alias": "filled"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
43
86
|
}
|
|
44
87
|
|
|
45
88
|
/**
|
|
@@ -81,47 +124,73 @@ declare class AdminBadge {
|
|
|
81
124
|
}
|
|
82
125
|
|
|
83
126
|
/**
|
|
84
|
-
*
|
|
85
|
-
*
|
|
86
|
-
*
|
|
127
|
+
* Envuelve un `<button>` nativo: mantiene teclado, foco y submit de formulario.
|
|
128
|
+
* Como es un elemento personalizado, ningún atributo ARIA puesto en el host
|
|
129
|
+
* llega al `<button>` interno — por eso `disabled`, `type`, `ariaLabel`,
|
|
130
|
+
* `ariaExpanded` y `ariaPressed` se reenvían a mano.
|
|
87
131
|
*
|
|
88
|
-
*
|
|
89
|
-
* `
|
|
90
|
-
* `
|
|
91
|
-
* confirmations — red, see the Confirmation Modal pattern on page 30). Three
|
|
92
|
-
* sizes (`sm`/`md`/`lg`). `icon` renders a leading glyph (`kf-icon`) before
|
|
93
|
-
* the projected label; `block` stretches the button to the full width of its
|
|
94
|
-
* container.
|
|
132
|
+
* Seis variantes: `primary`, `secondary`, `ghost`, `outline`, `danger`,
|
|
133
|
+
* `surface`. Tres tamaños (`sm`/`md`/`lg`). `icon` agrega un glifo antes del
|
|
134
|
+
* label proyectado; `block` estira el botón a todo el ancho de su contenedor.
|
|
95
135
|
*
|
|
96
|
-
* `
|
|
136
|
+
* `surface` es la variante neutra pensada para flotar SOBRE contenido
|
|
137
|
+
* variable (ej. los controles de `kf-map` sobre un mapa que cambia de tema:
|
|
138
|
+
* calles, satélite, oscuro) — fondo sólido de `--kf-color-surface` con
|
|
139
|
+
* sombra, en vez de depender de contraste contra lo que haya debajo (que
|
|
140
|
+
* `ghost`/`outline`, transparentes, no pueden garantizar ahí).
|
|
97
141
|
*
|
|
98
|
-
* `
|
|
99
|
-
*
|
|
100
|
-
*
|
|
101
|
-
*
|
|
142
|
+
* `shape="circle"` vuelve el botón cuadrado con esquina `pill` (mismo ancho
|
|
143
|
+
* y alto, sin padding horizontal extra) — pensado para uso solo-ícono
|
|
144
|
+
* (`icon` sin contenido proyectado): combinarlo con un label visible
|
|
145
|
+
* descentra el ícono, no es el caso de uso.
|
|
102
146
|
*
|
|
103
|
-
*
|
|
104
|
-
*
|
|
105
|
-
*
|
|
106
|
-
*
|
|
147
|
+
* `(click)` no se reenvía: burbujea del botón interno al host solo.
|
|
148
|
+
*
|
|
149
|
+
* `loading` reemplaza el contenido proyectado por `kf-spinner` y fuerza
|
|
150
|
+
* `disabled`.
|
|
151
|
+
*
|
|
152
|
+
* Botón solo-ícono (sin label visible): pasar `ariaLabel` en vez de dejar el
|
|
153
|
+
* botón sin nombre accesible.
|
|
154
|
+
*
|
|
155
|
+
* <kf-button (click)="save()">Guardar</kf-button>
|
|
156
|
+
* <kf-button variant="danger" (click)="remove()">Eliminar</kf-button>
|
|
157
|
+
* <kf-button size="sm" icon="external-link" ariaLabel="Expandir" (click)="expand()" />
|
|
107
158
|
* <kf-button type="submit" [loading]="submitting()">Ingresar</kf-button>
|
|
159
|
+
* <kf-button variant="surface" shape="circle" icon="plus" ariaLabel="Acercar" [ariaPressed]="false" />
|
|
108
160
|
*/
|
|
109
161
|
declare class Button {
|
|
110
|
-
readonly variant: _angular_core.InputSignal<"primary" | "secondary" | "ghost" | "outline" | "danger">;
|
|
162
|
+
readonly variant: _angular_core.InputSignal<"primary" | "secondary" | "ghost" | "outline" | "danger" | "surface">;
|
|
111
163
|
readonly size: _angular_core.InputSignal<"sm" | "md" | "lg">;
|
|
164
|
+
readonly shape: _angular_core.InputSignal<"rect" | "circle">;
|
|
112
165
|
readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
113
|
-
/**
|
|
166
|
+
/** Igual al default nativo: hace submit dentro de un `<form>`. */
|
|
114
167
|
readonly type: _angular_core.InputSignal<"button" | "submit" | "reset">;
|
|
115
|
-
/**
|
|
168
|
+
/** Ícono líder, renderizado con `kf-icon` antes del label proyectado. */
|
|
116
169
|
readonly icon: _angular_core.InputSignal<IconName | undefined>;
|
|
117
|
-
/**
|
|
170
|
+
/** Estira el botón a todo el ancho de su contenedor. */
|
|
118
171
|
readonly block: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
119
|
-
/**
|
|
172
|
+
/** Reemplaza el contenido proyectado por `kf-spinner` y fuerza `disabled`. */
|
|
120
173
|
readonly loading: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
121
|
-
/**
|
|
174
|
+
/** Nombre accesible del botón interno — obligatorio quien no proyecta
|
|
175
|
+
* ningún texto visible (botón solo-ícono), porque el host no lo reenvía. */
|
|
176
|
+
readonly ariaLabel: _angular_core.InputSignal<string | undefined>;
|
|
177
|
+
/** Estado expandido/colapsado para un botón toggle — mismo motivo que
|
|
178
|
+
* `ariaLabel`, el host no reenvía atributos ARIA al `<button>` interno. */
|
|
179
|
+
readonly ariaExpanded: _angular_core.InputSignal<boolean | undefined>;
|
|
180
|
+
/** Estado presionado/no presionado para un botón toggle real (ej. "seguir
|
|
181
|
+
* vehículo" activo/inactivo) — distinto semánticamente de `ariaExpanded`
|
|
182
|
+
* (que es para mostrar/ocultar un panel), mismo motivo de reenvío manual.
|
|
183
|
+
* Con `icon`, presionado (`true`) rellena el glyph (`kf-icon[filled]`) en
|
|
184
|
+
* vez de solo cambiar de color — un toggle solo-ícono necesita esa señal
|
|
185
|
+
* más fuerte para leerse activo, y así ningún consumidor tiene que
|
|
186
|
+
* acordarse de pedirlo aparte. */
|
|
187
|
+
readonly ariaPressed: _angular_core.InputSignal<boolean | undefined>;
|
|
188
|
+
/** `ghost`/`outline`/`surface` no tienen fondo oscuro sólido — el anillo
|
|
189
|
+
* del spinner por defecto ya se lee bien ahí sin variante propia
|
|
190
|
+
* (`surface` es fondo claro con texto oscuro, mismo motivo que `outline`). */
|
|
122
191
|
protected readonly hasSolidFill: _angular_core.Signal<boolean>;
|
|
123
192
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<Button, never>;
|
|
124
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<Button, "kf-button", never, { "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "type": { "alias": "type"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "block": { "alias": "block"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
|
|
193
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<Button, "kf-button", never, { "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "shape": { "alias": "shape"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "type": { "alias": "type"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "block": { "alias": "block"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "ariaExpanded": { "alias": "ariaExpanded"; "required": false; "isSignal": true; }; "ariaPressed": { "alias": "ariaPressed"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
|
|
125
194
|
}
|
|
126
195
|
|
|
127
196
|
type AlertTone = 'info' | 'ok' | 'warn' | 'error';
|
|
@@ -134,10 +203,6 @@ type AlertTone = 'info' | 'ok' | 'warn' | 'error';
|
|
|
134
203
|
* deja de renderizar. El consumidor puede volver a abrirlo poniéndolo en
|
|
135
204
|
* `true` (ej. cuando la validación falla otra vez).
|
|
136
205
|
*
|
|
137
|
-
* ponytail: los glifos van inline en vez de venir de un componente Icon —
|
|
138
|
-
* son 4 paths fijos, uno por tono, y no existe todavía `kf-icon` en la
|
|
139
|
-
* librería. Cuando exista, se reemplaza este `@switch` por `<kf-icon>`.
|
|
140
|
-
*
|
|
141
206
|
* <kf-alert tone="warn" title="SOAT por vencer">Vence el 15/08.</kf-alert>
|
|
142
207
|
* <kf-alert tone="ok" [(open)]="saved" dismissible>Pago registrado.</kf-alert>
|
|
143
208
|
*/
|
|
@@ -153,42 +218,58 @@ declare class Alert {
|
|
|
153
218
|
* Los avisos informativos se anuncian sin cortar lo que se esté leyendo.
|
|
154
219
|
*/
|
|
155
220
|
protected readonly ariaRole: _angular_core.Signal<"alert" | "status">;
|
|
221
|
+
protected readonly toneIcon: _angular_core.Signal<IconName>;
|
|
156
222
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<Alert, never>;
|
|
157
223
|
static ɵcmp: _angular_core.ɵɵComponentDeclaration<Alert, "kf-alert", never, { "tone": { "alias": "tone"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; "dismissible": { "alias": "dismissible"; "required": false; "isSignal": true; }; "open": { "alias": "open"; "required": false; "isSignal": true; }; }, { "open": "openChange"; }, never, ["*"], true, never>;
|
|
158
224
|
}
|
|
159
225
|
|
|
226
|
+
type AlertRowTone = 'warn' | 'error';
|
|
227
|
+
/**
|
|
228
|
+
* Fila clickeable de una lista de pendientes/alertas: ícono circular con
|
|
229
|
+
* tono semántico, texto de una línea, chevron al final. Pensada para
|
|
230
|
+
* agrupar varias dentro de un `kf-card` (ej. "Tus pendientes" del
|
|
231
|
+
* dashboard) — no incluye el contenedor en sí.
|
|
232
|
+
*
|
|
233
|
+
* <kf-alert-row icon="file" tone="warn" text="2 cotizaciones esperan tu aprobación" (rowClick)="onNavigate('cotizaciones')" />
|
|
234
|
+
*/
|
|
235
|
+
declare class AlertRow {
|
|
236
|
+
readonly icon: _angular_core.InputSignal<IconName>;
|
|
237
|
+
readonly tone: _angular_core.InputSignal<AlertRowTone>;
|
|
238
|
+
readonly text: _angular_core.InputSignal<string>;
|
|
239
|
+
readonly rowClick: _angular_core.OutputEmitterRef<void>;
|
|
240
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<AlertRow, never>;
|
|
241
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<AlertRow, "kf-alert-row", never, { "icon": { "alias": "icon"; "required": true; "isSignal": true; }; "tone": { "alias": "tone"; "required": false; "isSignal": true; }; "text": { "alias": "text"; "required": true; "isSignal": true; }; }, { "rowClick": "rowClick"; }, never, never, true, never>;
|
|
242
|
+
}
|
|
243
|
+
|
|
244
|
+
/**
|
|
245
|
+
* Marca el contenido que va en el slot de SSO de `kf-auth-layout` (típico:
|
|
246
|
+
* un `kf-sso-button`, o varios si el ecosistema suma más de un proveedor).
|
|
247
|
+
* `AuthLayout` la detecta vía `contentChild` para decidir si renderiza el
|
|
248
|
+
* separador "o" entre el bloque de SSO y el formulario tradicional
|
|
249
|
+
* proyectado en el slot por defecto — sin este marcador, `AuthLayout`
|
|
250
|
+
* tendría que adivinar si hubo proyección leyendo el DOM a mano.
|
|
251
|
+
*
|
|
252
|
+
* <kf-auth-layout brand="Mi Portal">
|
|
253
|
+
* <div kfAuthLayoutSso>
|
|
254
|
+
* <kf-sso-button provider="microsoft" (click)="loginWithMicrosoft()" />
|
|
255
|
+
* </div>
|
|
256
|
+
* <form ...>...</form>
|
|
257
|
+
* </kf-auth-layout>
|
|
258
|
+
*/
|
|
259
|
+
declare class AuthLayoutSso {
|
|
260
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<AuthLayoutSso, never>;
|
|
261
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<AuthLayoutSso, "[kfAuthLayoutSso]", never, {}, {}, never, never, true, never>;
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
type AuthLayoutBackground = 'plain' | 'gradient' | 'pattern';
|
|
160
265
|
/**
|
|
161
|
-
* Layout a pantalla completa para
|
|
162
|
-
*
|
|
163
|
-
* izquierda
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
168
|
-
* pierde de vista qué portal está usando, mismo criterio que ya sigue el
|
|
169
|
-
* resto del ecosistema (ver la topbar mobile de `css/otd.css`, que muestra
|
|
170
|
-
* el logo aunque el sidebar completo se oculte).
|
|
171
|
-
*
|
|
172
|
-
* Este componente resuelve el "cascarón" — nunca el formulario en sí. Cada
|
|
173
|
-
* portal arma su propio `<form>` con los componentes de la librería
|
|
174
|
-
* (`kf-input`, `kf-checkbox`, `kf-button`, `kf-alert`) y lo proyecta en el
|
|
175
|
-
* slot por defecto. Es la misma separación de responsabilidad que ya usa
|
|
176
|
-
* `kf-card`: un componente da la estructura visual, el consumidor decide
|
|
177
|
-
* qué va adentro.
|
|
178
|
-
*
|
|
179
|
-
* Antes de este componente, cada portal (LATAM primero, ahora OTD) hubiera
|
|
180
|
-
* tenido que reimplementar este mismo split desde cero — la librería
|
|
181
|
-
* había pospuesto esta abstracción a propósito hasta tener un segundo
|
|
182
|
-
* consumidor real que confirmara qué parte es realmente compartida (el
|
|
183
|
-
* cascarón) y qué parte es de cada portal (el formulario). OTD es ese
|
|
184
|
-
* segundo consumidor.
|
|
185
|
-
*
|
|
186
|
-
* Ningún input tiene valor por defecto propio de ningún portal — `brand`
|
|
187
|
-
* es obligatorio (`input.required`) y el resto de props de marca son
|
|
188
|
-
* opcionales sin fallback hardcodeado, precisamente para que cada
|
|
189
|
-
* consumidor (LATAM, OTD, Star, o el ERP de Asistencia en Ruta con sus
|
|
190
|
-
* propios temas) traiga su propio logo/tagline sin que la librería asuma
|
|
191
|
-
* cuál.
|
|
266
|
+
* Layout a pantalla completa para pantallas de autenticación (login,
|
|
267
|
+
* recuperar contraseña, confirmación de envío) — panel de marca a la
|
|
268
|
+
* izquierda, contenido libre a la derecha en desktop; en mobile el panel de
|
|
269
|
+
* marca se oculta y un logo compacto queda visible arriba del formulario.
|
|
270
|
+
*
|
|
271
|
+
* Resuelve solo el "cascarón": cada portal arma su propio `<form>` con los
|
|
272
|
+
* componentes de la librería y lo proyecta en el slot por defecto.
|
|
192
273
|
*
|
|
193
274
|
* <kf-auth-layout
|
|
194
275
|
* brand="Mi Portal"
|
|
@@ -202,24 +283,72 @@ declare class Alert {
|
|
|
202
283
|
* <kf-button type="submit">Ingresar</kf-button>
|
|
203
284
|
* </form>
|
|
204
285
|
* </kf-auth-layout>
|
|
286
|
+
*
|
|
287
|
+
* `backgroundVariant` controla la superficie del panel derecho:
|
|
288
|
+
* `'plain'` (fondo liso, comportamiento histórico), `'gradient'` (default —
|
|
289
|
+
* degradé sutil derivado de `--kf-color-primary`/`--kf-color-surface` vía
|
|
290
|
+
* `color-mix()`, ningún color hardcodeado) o `'pattern'` (el mismo degradé
|
|
291
|
+
* más una textura de puntos, también resuelta con tokens). Los tres se
|
|
292
|
+
* resuelven contra los tokens de tema ACTIVOS — cambiar de tema
|
|
293
|
+
* (`andes-maq`, `divemotor-star`, `kaufmann-ar`, futuros) no requiere tocar
|
|
294
|
+
* este componente ni pasarle un color a mano.
|
|
295
|
+
*
|
|
296
|
+
* `card`, en `false` por defecto (comportamiento histórico: el contenido
|
|
297
|
+
* proyectado flota sin superficie propia), envuelve el slot por defecto con
|
|
298
|
+
* la misma superficie de `kf-card` (fondo, borde, sombra, radio) para que
|
|
299
|
+
* el formulario deje de verse suelto sobre el fondo del panel.
|
|
300
|
+
*
|
|
301
|
+
* `title`/`subtitle` renderizan un encabezado de bienvenida DENTRO de la
|
|
302
|
+
* card, antes que cualquier otro contenido (jerarquía ya resuelta:
|
|
303
|
+
* `title-lg` + `body-muted`, ver `Text.ts`) — así ningún consumidor tiene
|
|
304
|
+
* que adivinar la variante de `kf-text` correcta ni terminar con un
|
|
305
|
+
* "Iniciar sesión" repetido y suelto dentro de su propio `<form>`. Sin
|
|
306
|
+
* `title`, no se renderiza ningún encabezado (comportamiento histórico).
|
|
307
|
+
*
|
|
308
|
+
* El slot `[kfAuthLayoutSso]` (ver `AuthLayoutSso`) es para el patrón
|
|
309
|
+
* "SSO primero, credenciales como alternativa secundaria" — típicamente un
|
|
310
|
+
* `kf-sso-button`. Cuando hay contenido ahí, `AuthLayout` resuelve el
|
|
311
|
+
* separador "o" automáticamente entre ese bloque y el contenido por
|
|
312
|
+
* defecto; sin ese slot, nada cambia frente al comportamiento actual.
|
|
313
|
+
*
|
|
314
|
+
* <kf-auth-layout
|
|
315
|
+
* brand="Mi Portal"
|
|
316
|
+
* backgroundVariant="pattern"
|
|
317
|
+
* card
|
|
318
|
+
* title="Iniciar sesión"
|
|
319
|
+
* subtitle="Usa tu cuenta corporativa o ingresa con contraseña"
|
|
320
|
+
* >
|
|
321
|
+
* <div kfAuthLayoutSso>
|
|
322
|
+
* <kf-sso-button provider="microsoft" (click)="loginWithMicrosoft()" />
|
|
323
|
+
* </div>
|
|
324
|
+
* <form (ngSubmit)="onSubmit()">
|
|
325
|
+
* <kf-input label="Correo" type="email" />
|
|
326
|
+
* <kf-input label="Contraseña" type="password" toggleable />
|
|
327
|
+
* <kf-button type="submit">Ingresar</kf-button>
|
|
328
|
+
* </form>
|
|
329
|
+
* </kf-auth-layout>
|
|
205
330
|
*/
|
|
206
331
|
declare class AuthLayout {
|
|
207
|
-
/** Nombre de marca/portal
|
|
332
|
+
/** Nombre de marca/portal — wordmark de respaldo si no hay logo, y `alt` de ambos logos. */
|
|
208
333
|
readonly brand: _angular_core.InputSignal<string>;
|
|
209
|
-
/** Logo en blanco para el panel oscuro de desktop
|
|
334
|
+
/** Logo en blanco para el panel oscuro de desktop. Sin esto, se muestra `brand()` como wordmark. */
|
|
210
335
|
readonly brandLogoSrc: _angular_core.InputSignal<string | undefined>;
|
|
211
|
-
/** Frase corta bajo el logo
|
|
336
|
+
/** Frase corta bajo el logo. */
|
|
212
337
|
readonly brandTagline: _angular_core.InputSignal<string | undefined>;
|
|
213
|
-
/**
|
|
214
|
-
* Logo compacto para mobile, sobre el fondo claro del panel de contenido
|
|
215
|
-
* — normalmente la variante oscura/negra del mismo logo, ya que
|
|
216
|
-
* `brandLogoSrc` suele ser la variante blanca pensada para el gradiente
|
|
217
|
-
* oscuro. Si no se pasa, reutiliza `brandLogoSrc`; si tampoco hay logo en
|
|
218
|
-
* absoluto, cae al wordmark tipográfico igual que en desktop.
|
|
219
|
-
*/
|
|
338
|
+
/** Logo compacto para mobile, sobre fondo claro. Sin esto, reutiliza `brandLogoSrc`. */
|
|
220
339
|
readonly brandLogoMobileSrc: _angular_core.InputSignal<string | undefined>;
|
|
340
|
+
/** Superficie del panel derecho. Default `'gradient'`; `'plain'` preserva el fondo liso histórico. */
|
|
341
|
+
readonly backgroundVariant: _angular_core.InputSignal<AuthLayoutBackground>;
|
|
342
|
+
/** Envuelve el contenido proyectado con la superficie de `kf-card` (fondo, borde, sombra). Default `false` — preserva el comportamiento histórico. */
|
|
343
|
+
readonly card: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
344
|
+
/** Título del encabezado dentro de la card (`title-lg`). Sin este input, no se renderiza ningún encabezado. */
|
|
345
|
+
readonly title: _angular_core.InputSignal<string | undefined>;
|
|
346
|
+
/** Subtítulo del encabezado (`body-muted`), debajo de `title`. Sin efecto si `title` no está presente. */
|
|
347
|
+
readonly subtitle: _angular_core.InputSignal<string | undefined>;
|
|
348
|
+
/** Presencia de contenido en el slot `[kfAuthLayoutSso]` — controla si se renderiza el separador "o". */
|
|
349
|
+
protected readonly ssoContent: _angular_core.Signal<AuthLayoutSso | undefined>;
|
|
221
350
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<AuthLayout, never>;
|
|
222
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<AuthLayout, "kf-auth-layout", never, { "brand": { "alias": "brand"; "required": true; "isSignal": true; }; "brandLogoSrc": { "alias": "brandLogoSrc"; "required": false; "isSignal": true; }; "brandTagline": { "alias": "brandTagline"; "required": false; "isSignal": true; }; "brandLogoMobileSrc": { "alias": "brandLogoMobileSrc"; "required": false; "isSignal": true; }; }, {},
|
|
351
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<AuthLayout, "kf-auth-layout", never, { "brand": { "alias": "brand"; "required": true; "isSignal": true; }; "brandLogoSrc": { "alias": "brandLogoSrc"; "required": false; "isSignal": true; }; "brandTagline": { "alias": "brandTagline"; "required": false; "isSignal": true; }; "brandLogoMobileSrc": { "alias": "brandLogoMobileSrc"; "required": false; "isSignal": true; }; "backgroundVariant": { "alias": "backgroundVariant"; "required": false; "isSignal": true; }; "card": { "alias": "card"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; "subtitle": { "alias": "subtitle"; "required": false; "isSignal": true; }; }, {}, ["ssoContent"], ["[kfAuthLayoutSso]", "*"], true, never>;
|
|
223
352
|
}
|
|
224
353
|
|
|
225
354
|
/**
|
|
@@ -234,10 +363,6 @@ declare class AuthLayout {
|
|
|
234
363
|
* <kf-avatar initials="JD" />
|
|
235
364
|
* <kf-avatar src="https://…/foto.jpg" [round]="false" />
|
|
236
365
|
* <kf-avatar initials="TS" pill />
|
|
237
|
-
*
|
|
238
|
-
* Pendiente: si en el futuro se agrega un ícono de fallback (persona
|
|
239
|
-
* genérica) para cuando no hay ni `src` ni `initials`, depende del
|
|
240
|
-
* componente Icon / sprite SVG que todavía no existe en esta librería.
|
|
241
366
|
*/
|
|
242
367
|
declare class Avatar {
|
|
243
368
|
readonly initials: _angular_core.InputSignal<string | undefined>;
|
|
@@ -252,29 +377,24 @@ declare class Avatar {
|
|
|
252
377
|
}
|
|
253
378
|
|
|
254
379
|
/**
|
|
255
|
-
* Pill chica de fondo suave
|
|
256
|
-
*
|
|
257
|
-
*
|
|
258
|
-
*
|
|
259
|
-
*
|
|
260
|
-
*
|
|
261
|
-
*
|
|
262
|
-
*
|
|
263
|
-
*
|
|
264
|
-
*
|
|
265
|
-
*
|
|
266
|
-
* Mapeo de `tone` a los seis conceptos de la página 10 y sus tokens
|
|
267
|
-
* semánticos en projects/ui/src/tokens/base.css:
|
|
268
|
-
* neutral -> Neutral (border/text-muted), info -> Información (info-soft/-strong),
|
|
269
|
-
* ok -> Activo (success-soft/-strong), warn -> Pendiente (warning-soft/-strong),
|
|
270
|
-
* error -> Vencido (error-soft/-strong), service -> Servicio (secondary-soft,
|
|
271
|
-
* no existe un `-strong` para secondary todavía — usa el color base).
|
|
380
|
+
* Pill chica de fondo suave para un conteo, tag o estado liviano puntual —
|
|
381
|
+
* no un proceso en vivo (para eso, `kf-status-pill`, con borde en vez de
|
|
382
|
+
* relleno). Fondo soft y texto del tono fuerte según `tone`; `service` usa
|
|
383
|
+
* el color base porque `secondary` todavía no tiene variante `-strong`.
|
|
384
|
+
* `variant` es un eje ortogonal a `tone` (mismo patrón que `kf-button`):
|
|
385
|
+
* `soft` (default) es el fondo de color sólido de siempre; `glass` es
|
|
386
|
+
* translúcido con desenfoque, pensado para un badge sobre una superficie
|
|
387
|
+
* de marca (ej. un header navy), no para un banner suelto. En `glass` el
|
|
388
|
+
* punto (`dot`) usa el color BASE de cada tono, no `-strong` — ese último
|
|
389
|
+
* está pensado para texto legible sobre el fondo pastel de `soft` y se ve
|
|
390
|
+
* apagado sobre una superficie translúcida/oscura.
|
|
272
391
|
*/
|
|
273
392
|
declare class Badge {
|
|
274
|
-
readonly tone: _angular_core.InputSignal<"
|
|
393
|
+
readonly tone: _angular_core.InputSignal<"neutral" | "ok" | "warn" | "error" | "info" | "service">;
|
|
394
|
+
readonly variant: _angular_core.InputSignal<"soft" | "glass">;
|
|
275
395
|
readonly dot: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
276
396
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<Badge, never>;
|
|
277
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<Badge, "kf-badge", never, { "tone": { "alias": "tone"; "required": false; "isSignal": true; }; "dot": { "alias": "dot"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
|
|
397
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<Badge, "kf-badge", never, { "tone": { "alias": "tone"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "dot": { "alias": "dot"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
|
|
278
398
|
}
|
|
279
399
|
|
|
280
400
|
interface BreadcrumbItem {
|
|
@@ -300,10 +420,9 @@ declare class Breadcrumb {
|
|
|
300
420
|
readonly items: _angular_core.InputSignal<BreadcrumbItem[]>;
|
|
301
421
|
/**
|
|
302
422
|
* Click en un nivel con `href`. Incluye el `MouseEvent` para que un
|
|
303
|
-
* consumidor con router pueda cortar la navegación nativa.
|
|
304
|
-
*
|
|
305
|
-
*
|
|
306
|
-
* nuevo en la librería para lo que el consumidor resuelve en una línea.
|
|
423
|
+
* consumidor con router pueda cortar la navegación nativa. No se depende
|
|
424
|
+
* de `@angular/router` acá — sería un peer dep nuevo en la librería para
|
|
425
|
+
* lo que el consumidor resuelve en una línea.
|
|
307
426
|
*/
|
|
308
427
|
readonly itemClick: _angular_core.OutputEmitterRef<{
|
|
309
428
|
item: BreadcrumbItem;
|
|
@@ -315,6 +434,64 @@ declare class Breadcrumb {
|
|
|
315
434
|
static ɵcmp: _angular_core.ɵɵComponentDeclaration<Breadcrumb, "kf-breadcrumb", never, { "items": { "alias": "items"; "required": true; "isSignal": true; }; }, { "itemClick": "itemClick"; }, never, never, true, never>;
|
|
316
435
|
}
|
|
317
436
|
|
|
437
|
+
/**
|
|
438
|
+
* Una categoría de la barra — el componente no sabe qué significa "pagada"
|
|
439
|
+
* o "vencida", solo dibuja proporciones: `count` decide el ancho relativo
|
|
440
|
+
* del segmento (`flex-grow`) y de la fila de leyenda que lo acompaña.
|
|
441
|
+
* `color` es cualquier valor CSS válido (pensado para tokens `var(--kf-*)`,
|
|
442
|
+
* nunca hex suelto desde el consumidor).
|
|
443
|
+
*/
|
|
444
|
+
interface BucketProgressItem {
|
|
445
|
+
key: string;
|
|
446
|
+
label: string;
|
|
447
|
+
count: number;
|
|
448
|
+
color: string;
|
|
449
|
+
}
|
|
450
|
+
/**
|
|
451
|
+
* Barra de progreso segmentada en categorías proporcionales — generaliza
|
|
452
|
+
* el resumen de "Progreso del plan de pagos" de Pagos y Facturas de OTD
|
|
453
|
+
* (cuotas pagadas/vencidas/próximas/pendientes) sin acoplarse a esas 4
|
|
454
|
+
* categorías puntuales: el consumidor pasa sus propios `items` con
|
|
455
|
+
* `{key, label, count, color}`.
|
|
456
|
+
*
|
|
457
|
+
* Los segmentos son proporcionales por CANTIDAD (`flex-grow: count`), no
|
|
458
|
+
* uno por unidad — con 36 cuotas y 6 pagadas, la barra dibuja 2 anchos
|
|
459
|
+
* distintos (pagado ~17% / resto ~83%), nunca 36 rectángulos idénticos
|
|
460
|
+
* (hallazgo real de UX: con volumen alto, un segmento por unidad se vuelve
|
|
461
|
+
* ilegible). Los `items` con `count: 0` no se dibujan (ni segmento ni
|
|
462
|
+
* fila de leyenda) — no tiene sentido mostrar una categoría vacía.
|
|
463
|
+
*
|
|
464
|
+
* La leyenda debajo reproduce el mismo orden y color que la barra, para
|
|
465
|
+
* que una categoría se pueda mapear a su cantidad de un vistazo (pedido
|
|
466
|
+
* explícito de UX: "posicionar correctamente los labels").
|
|
467
|
+
*
|
|
468
|
+
* <kf-bucket-progress
|
|
469
|
+
* title="Progreso del plan de pagos"
|
|
470
|
+
* [summary]="'6 de 30 cuotas pagadas'"
|
|
471
|
+
* [items]="[
|
|
472
|
+
* { key: 'paid', label: 'pagadas', count: 6, color: 'var(--kf-color-success)' },
|
|
473
|
+
* { key: 'overdue', label: 'vencidas', count: 2, color: 'var(--kf-color-error)' },
|
|
474
|
+
* { key: 'upcoming', label: 'próxima', count: 1, color: 'var(--kf-color-warning)' },
|
|
475
|
+
* { key: 'pending', label: 'pendientes', count: 21, color: 'var(--kf-color-gray-200)' },
|
|
476
|
+
* ]"
|
|
477
|
+
* />
|
|
478
|
+
*/
|
|
479
|
+
declare class BucketProgress {
|
|
480
|
+
readonly title: _angular_core.InputSignal<string>;
|
|
481
|
+
/** Texto libre a la derecha del título (ej. "6 de 30 cuotas pagadas") —
|
|
482
|
+
* el componente no sabe calcular ese resumen, lo recibe hecho. */
|
|
483
|
+
readonly summary: _angular_core.InputSignal<string>;
|
|
484
|
+
readonly items: _angular_core.InputSignal<BucketProgressItem[]>;
|
|
485
|
+
protected readonly visibleItems: _angular_core.Signal<BucketProgressItem[]>;
|
|
486
|
+
protected readonly total: _angular_core.Signal<number>;
|
|
487
|
+
/** Descripción accesible de la barra completa — un solo `role="img"`
|
|
488
|
+
* con el desglose en texto, en vez de que cada segmento individual
|
|
489
|
+
* compita por foco/lectura de screen reader. */
|
|
490
|
+
protected readonly ariaLabel: _angular_core.Signal<string>;
|
|
491
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<BucketProgress, never>;
|
|
492
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<BucketProgress, "kf-bucket-progress", never, { "title": { "alias": "title"; "required": true; "isSignal": true; }; "summary": { "alias": "summary"; "required": false; "isSignal": true; }; "items": { "alias": "items"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
493
|
+
}
|
|
494
|
+
|
|
318
495
|
/**
|
|
319
496
|
* Contenedor base con esquinas redondeadas y sombra sutil — la base visual
|
|
320
497
|
* de paneles, tarjetas de métrica, tablas y bloques de contenido de todo el
|
|
@@ -328,7 +505,16 @@ declare class Breadcrumb {
|
|
|
328
505
|
* cualquier composición (una métrica, una lista, un formulario) pueda vivir
|
|
329
506
|
* dentro sin pelear con estilos propios del `Card`.
|
|
330
507
|
*
|
|
331
|
-
*
|
|
508
|
+
* `gap` controla el espacio entre los hijos directos del cuerpo — `md`
|
|
509
|
+
* (default) separa bloques con peso propio (un `kf-section-header`
|
|
510
|
+
* seguido de datos, por ejemplo); `xs`/`sm` para texto corrido o bloques
|
|
511
|
+
* más compactos, `lg`/`xl` para composiciones con más aire entre secciones.
|
|
512
|
+
*
|
|
513
|
+
* <kf-card>
|
|
514
|
+
* <kf-section-header title="Cliente" />
|
|
515
|
+
* <dl>…</dl>
|
|
516
|
+
* </kf-card>
|
|
517
|
+
* <kf-card gap="xs">Texto corrido, sin bloques que separar</kf-card>
|
|
332
518
|
* <kf-card flush>Sin sombra — para superficies ya delimitadas por otra card</kf-card>
|
|
333
519
|
* <kf-card>
|
|
334
520
|
* <img kf-card-media src="..." alt="" />
|
|
@@ -343,8 +529,10 @@ declare class Breadcrumb {
|
|
|
343
529
|
declare class Card {
|
|
344
530
|
/** Quita la sombra — para cards anidadas dentro de otra superficie ya elevada. */
|
|
345
531
|
readonly flush: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
532
|
+
/** Espacio entre los hijos directos del cuerpo. `md` es el default. */
|
|
533
|
+
readonly gap: _angular_core.InputSignal<"sm" | "md" | "lg" | "xs" | "xl">;
|
|
346
534
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<Card, never>;
|
|
347
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<Card, "kf-card", never, { "flush": { "alias": "flush"; "required": false; "isSignal": true; }; }, {}, never, ["[kf-card-media]", "*", "[kf-card-actions]"], true, never>;
|
|
535
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<Card, "kf-card", never, { "flush": { "alias": "flush"; "required": false; "isSignal": true; }; "gap": { "alias": "gap"; "required": false; "isSignal": true; }; }, {}, never, ["[kf-card-media]", "*", "[kf-card-actions]"], true, never>;
|
|
348
536
|
}
|
|
349
537
|
|
|
350
538
|
/**
|
|
@@ -359,6 +547,7 @@ declare class CarouselSlide {
|
|
|
359
547
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<CarouselSlide, never>;
|
|
360
548
|
static ɵcmp: _angular_core.ɵɵComponentDeclaration<CarouselSlide, "kf-carousel-slide", never, {}, {}, never, ["*"], true, never>;
|
|
361
549
|
}
|
|
550
|
+
|
|
362
551
|
/**
|
|
363
552
|
* Carrusel de slides con flechas, dots y autoplay opcional.
|
|
364
553
|
*
|
|
@@ -449,6 +638,13 @@ declare class Checkbox implements ControlValueAccessor {
|
|
|
449
638
|
*
|
|
450
639
|
* <kf-chip tone="warn">Pendiente</kf-chip>
|
|
451
640
|
* <kf-chip tone="ok">Aprobado</kf-chip>
|
|
641
|
+
* <kf-chip [dot]="false">4 Unidades</kf-chip>
|
|
642
|
+
*
|
|
643
|
+
* `dot` (default `true`) oculta el punto cuando el chip muestra un DATO
|
|
644
|
+
* (ej. una cantidad — "4 Unidades") y no un ESTADO — antes forzaba a cada
|
|
645
|
+
* portal a reimplementar el mismo patrón "pill con ícono + texto sin punto
|
|
646
|
+
* de estado" a mano (visto 2-3 veces idéntico en `web-otd-frontend`:
|
|
647
|
+
* `quotes-grid.html`/`quote-detail.html` para el conteo de unidades).
|
|
452
648
|
*
|
|
453
649
|
* Nota: la spec original define la unión de tonos como "ok" | "warn" |
|
|
454
650
|
* "error" | "neutral". Internamente se mapean a los tokens semánticos de
|
|
@@ -458,9 +654,41 @@ declare class Checkbox implements ControlValueAccessor {
|
|
|
458
654
|
* de superficie/borde/texto-muted más cercanos).
|
|
459
655
|
*/
|
|
460
656
|
declare class Chip {
|
|
461
|
-
readonly tone: _angular_core.InputSignal<"
|
|
657
|
+
readonly tone: _angular_core.InputSignal<"neutral" | "ok" | "warn" | "error">;
|
|
658
|
+
readonly dot: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
462
659
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<Chip, never>;
|
|
463
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<Chip, "kf-chip", never, { "tone": { "alias": "tone"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
|
|
660
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<Chip, "kf-chip", never, { "tone": { "alias": "tone"; "required": false; "isSignal": true; }; "dot": { "alias": "dot"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
|
|
661
|
+
}
|
|
662
|
+
|
|
663
|
+
/**
|
|
664
|
+
* Bloque colapsable genérico: un header clickeable (título + chevron) que
|
|
665
|
+
* expande/colapsa contenido proyectado libre, sin saber nada de lo que hay
|
|
666
|
+
* adentro. Building block para layouts responsive donde un bloque se
|
|
667
|
+
* muestra inline en desktop pero se colapsa en mobile/tablet para ahorrar
|
|
668
|
+
* espacio vertical — el primer caso de uso real es un panel de filtros de
|
|
669
|
+
* tabla que en desktop queda siempre abierto y en mobile arranca cerrado.
|
|
670
|
+
*
|
|
671
|
+
* `expanded` es two-way, mismo patrón que `Drawer.open`: el consumidor
|
|
672
|
+
* decide el estado inicial y puede reaccionar a los cambios, pero el propio
|
|
673
|
+
* click en el header ya alterna el estado sin que el consumidor tenga que
|
|
674
|
+
* cablear un `(click)` manual.
|
|
675
|
+
*
|
|
676
|
+
* <kf-collapsible title="Filtros" [(expanded)]="filtersOpen">
|
|
677
|
+
* ...
|
|
678
|
+
* </kf-collapsible>
|
|
679
|
+
*
|
|
680
|
+
* El ícono chevron es un SVG inline fijo (no una prop `icon` configurable)
|
|
681
|
+
* porque esta librería todavía no tiene un componente `Icon`/sprite
|
|
682
|
+
* compartido — un solo ícono fijo no amerita esperar esa abstracción.
|
|
683
|
+
*/
|
|
684
|
+
declare class Collapsible {
|
|
685
|
+
readonly title: _angular_core.InputSignal<string>;
|
|
686
|
+
readonly expanded: _angular_core.ModelSignal<boolean>;
|
|
687
|
+
private readonly instanceId;
|
|
688
|
+
readonly triggerId: string;
|
|
689
|
+
readonly panelId: string;
|
|
690
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<Collapsible, never>;
|
|
691
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<Collapsible, "kf-collapsible", never, { "title": { "alias": "title"; "required": true; "isSignal": true; }; "expanded": { "alias": "expanded"; "required": false; "isSignal": true; }; }, { "expanded": "expandedChange"; }, never, ["*"], true, never>;
|
|
464
692
|
}
|
|
465
693
|
|
|
466
694
|
/** Metadato clave mostrado junto al selector (RUC, identificación, sucursal). */
|
|
@@ -556,6 +784,53 @@ declare class ContentState {
|
|
|
556
784
|
static ɵcmp: _angular_core.ɵɵComponentDeclaration<ContentState, "kf-content-state", never, { "state": { "alias": "state"; "required": true; "isSignal": true; }; "load": { "alias": "load"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; "subtitle": { "alias": "subtitle"; "required": false; "isSignal": true; }; "loadingLabel": { "alias": "loadingLabel"; "required": false; "isSignal": true; }; }, {}, never, ["*", "[actions]"], true, never>;
|
|
557
785
|
}
|
|
558
786
|
|
|
787
|
+
type ContentViewerType = 'image' | 'video';
|
|
788
|
+
/**
|
|
789
|
+
* Visor a pantalla completa para ampliar una pieza de contenido — hoy
|
|
790
|
+
* imagen, con `video` ya soportado para cuando haya contenido de ese tipo.
|
|
791
|
+
* Mismo mecanismo que `kf-modal` (el `<dialog>` nativo resuelve backdrop,
|
|
792
|
+
* atrapado de foco, top-layer y `Escape`), pero sin panel/título/footer:
|
|
793
|
+
* el contenido ocupa toda la pantalla sobre un backdrop oscuro.
|
|
794
|
+
*
|
|
795
|
+
* `src` acepta lo que venga del backend sin que el consumidor tenga que
|
|
796
|
+
* normalizarlo primero: una URL (`http(s)://`), un data URI ya armado
|
|
797
|
+
* (`data:...`), o base64 crudo (se le antepone el prefijo `data:` según
|
|
798
|
+
* `type`, asumiendo JPEG para imagen y MP4 para video).
|
|
799
|
+
*
|
|
800
|
+
* <kf-content-viewer [(open)]="viewerOpen" [src]="viewerSrc()" alt="Foto del vehículo" />
|
|
801
|
+
* <kf-content-viewer [(open)]="viewerOpen" type="video" [src]="viewerSrc()" title="Inspección" />
|
|
802
|
+
*/
|
|
803
|
+
declare class ContentViewer {
|
|
804
|
+
readonly type: _angular_core.InputSignal<ContentViewerType>;
|
|
805
|
+
/** URL, data URI, o base64 crudo — ver JSDoc de la clase. */
|
|
806
|
+
readonly src: _angular_core.InputSignal<string | null>;
|
|
807
|
+
/** Texto alternativo cuando `type="image"`. */
|
|
808
|
+
readonly alt: _angular_core.InputSignal<string>;
|
|
809
|
+
readonly title: _angular_core.InputSignal<string>;
|
|
810
|
+
/** Visibilidad, two-way: `[(open)]="viewerOpen"`. */
|
|
811
|
+
readonly open: _angular_core.ModelSignal<boolean>;
|
|
812
|
+
/** Permite cerrar con el botón, `Escape` o clic en el backdrop. */
|
|
813
|
+
readonly dismissible: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
814
|
+
protected readonly titleId: string;
|
|
815
|
+
/** Detecta si `src` ya es una URL o un data URI armado; si no, asume
|
|
816
|
+
* base64 crudo y le antepone el prefijo `data:` correspondiente a `type`
|
|
817
|
+
* — el backend a veces manda una cosa, a veces la otra. */
|
|
818
|
+
protected readonly resolvedSrc: _angular_core.Signal<string | null>;
|
|
819
|
+
private readonly dialog;
|
|
820
|
+
private readonly document;
|
|
821
|
+
constructor();
|
|
822
|
+
/** `Escape` dispara `cancel`; cancelarlo lo deja abierto. */
|
|
823
|
+
protected onCancel(event: Event): void;
|
|
824
|
+
/**
|
|
825
|
+
* Clic en el backdrop. Un clic en la zona del backdrop tiene como `target` al
|
|
826
|
+
* propio `<dialog>` (el contenido vive en el panel de adentro), así que basta
|
|
827
|
+
* comparar contra él — no hace falta cortar la propagación en el panel.
|
|
828
|
+
*/
|
|
829
|
+
protected onHostClick(event: MouseEvent): void;
|
|
830
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<ContentViewer, never>;
|
|
831
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<ContentViewer, "kf-content-viewer", never, { "type": { "alias": "type"; "required": false; "isSignal": true; }; "src": { "alias": "src"; "required": false; "isSignal": true; }; "alt": { "alias": "alt"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; "open": { "alias": "open"; "required": false; "isSignal": true; }; "dismissible": { "alias": "dismissible"; "required": false; "isSignal": true; }; }, { "open": "openChange"; }, never, never, true, never>;
|
|
832
|
+
}
|
|
833
|
+
|
|
559
834
|
/**
|
|
560
835
|
* Zona que ocupa la columna en la vista de cards. La vista de tabla ignora
|
|
561
836
|
* esto (salvo `media`, que igual se rinde como thumbnail en vez de imprimir
|
|
@@ -574,9 +849,40 @@ interface DataTableColumn {
|
|
|
574
849
|
right?: boolean;
|
|
575
850
|
className?: string;
|
|
576
851
|
cardRole?: DataTableCardRole;
|
|
852
|
+
/** Habilita ordenar por esta columna clickeando su header (solo vista
|
|
853
|
+
* tabla — en cards no hay header clickeable). Ciclo asc → desc → sin
|
|
854
|
+
* orden, un click a la vez. */
|
|
855
|
+
sortable?: boolean;
|
|
856
|
+
/** Fija la columna a un borde del scroll horizontal — típicamente la
|
|
857
|
+
* primera (identificador) y/o la última (acciones). Columnas `sticky`
|
|
858
|
+
* del mismo lado deben ser consecutivas y estar en ese extremo del
|
|
859
|
+
* array `columns`; el offset de cada una se calcula sumando el ancho
|
|
860
|
+
* real de las que ya quedaron fijas antes, medido en runtime (no hay
|
|
861
|
+
* forma de conocer el ancho de antemano, las columnas son de ancho
|
|
862
|
+
* variable según su contenido). */
|
|
863
|
+
sticky?: 'left' | 'right';
|
|
864
|
+
/** Ancho mínimo forzado — evita que una columna angosta (ej. un chip
|
|
865
|
+
* corto) se comprima más de la cuenta cuando hay poco espacio y otras
|
|
866
|
+
* columnas compiten por él. */
|
|
867
|
+
minWidth?: string;
|
|
577
868
|
}
|
|
578
869
|
/** Vistas disponibles cuando `cardable` está activo. */
|
|
579
870
|
type DataTableView = 'table' | 'cards';
|
|
871
|
+
/** Opciones de "filas por página" estándar del portal — 10 como default
|
|
872
|
+
* cubre bien tanto un dataset chico (ej. Beneficios) como uno con volumen
|
|
873
|
+
* real (ej. 30+ cuotas en Billing), sin sentirse vacío ni insuficiente; 25
|
|
874
|
+
* y 50 escalan ~2.5x cada paso, evitando un salto brusco donde la opción
|
|
875
|
+
* del medio no aporta nada. Cualquier consumidor puede pisar este default
|
|
876
|
+
* con su propio `pageSizeOptions` si su caso lo justifica — pasar `[]`
|
|
877
|
+
* explícitamente desactiva el selector aunque `pageSize` esté activo. */
|
|
878
|
+
declare const DEFAULT_PAGE_SIZE_OPTIONS: number[];
|
|
879
|
+
/** Densidad de fila: `comfortable` (default, el padding actual) o
|
|
880
|
+
* `compact` (menos padding vertical, para tablas con muchas filas donde
|
|
881
|
+
* priorizar cantidad visible sobre aire entre filas). */
|
|
882
|
+
type DataTableDensity = 'comfortable' | 'compact';
|
|
883
|
+
/** `key:direction` — mismo formato que `sort` de `kf-quote-picker`, para
|
|
884
|
+
* que un consumidor que ya conoce ese patrón no aprenda uno nuevo acá. */
|
|
885
|
+
type DataTableSort = `${string}:${'asc' | 'desc'}`;
|
|
580
886
|
/**
|
|
581
887
|
* Tabla estandarizada con soporte opcional de toggle tabla/cards.
|
|
582
888
|
*
|
|
@@ -598,10 +904,7 @@ type DataTableView = 'table' | 'cards';
|
|
|
598
904
|
* pie separado por una línea para las acciones. Sin `cardRole`, todas las
|
|
599
905
|
* columnas caen en el cuerpo, igual que antes.
|
|
600
906
|
*
|
|
601
|
-
* El
|
|
602
|
-
* un control de segmentos mínimo inline (mismo patrón visual que
|
|
603
|
-
* kf-segmented) para no acoplarse a un componente que otro agente puede
|
|
604
|
-
* estar construyendo en paralelo — ver nota en el resumen de la tarea.
|
|
907
|
+
* El toggle tabla/cards es `kf-view-toggle`, componente standalone propio.
|
|
605
908
|
*
|
|
606
909
|
* <kf-data-table [columns]="columns" [rows]="rows" (rowClick)="onRowClick($event)">
|
|
607
910
|
* <kf-empty-state empty>No hay resultados para mostrar.</kf-empty-state>
|
|
@@ -609,6 +912,18 @@ type DataTableView = 'table' | 'cards';
|
|
|
609
912
|
*
|
|
610
913
|
* <kf-data-table [columns]="columns" [rows]="rows" [cardable]="true" defaultView="cards" />
|
|
611
914
|
*
|
|
915
|
+
* Loading nativo: con `loading` en `true`, la tabla se reemplaza por un
|
|
916
|
+
* placeholder (`kf-skeleton` por celda/card) — mismo número de filas que
|
|
917
|
+
* `pageSize`, o 5 si no hay paginación activa. Ignora `rows()`/el slot
|
|
918
|
+
* `[empty]` mientras dura (no sabemos aún si el resultado real estará
|
|
919
|
+
* vacío), y respeta la vista activa (tabla o cards) para no cambiar el
|
|
920
|
+
* layout de golpe cuando los datos reales lleguen. El toggle tabla/cards y
|
|
921
|
+
* el header de columnas siguen visibles — solo el cuerpo se reemplaza:
|
|
922
|
+
*
|
|
923
|
+
* <kf-data-table [columns]="columns" [rows]="rows()" [loading]="isLoading()">
|
|
924
|
+
* <kf-empty-state empty>No hay resultados para mostrar.</kf-empty-state>
|
|
925
|
+
* </kf-data-table>
|
|
926
|
+
*
|
|
612
927
|
* El slot `[footer]` (página 15: "slot de pie para paginación") va siempre
|
|
613
928
|
* al final, en ambas vistas, separado por una línea — pensado para proyectar
|
|
614
929
|
* `<kf-pagination>`, que sigue siendo un componente independiente.
|
|
@@ -616,10 +931,52 @@ type DataTableView = 'table' | 'cards';
|
|
|
616
931
|
* <kf-data-table [columns]="columns" [rows]="rows">
|
|
617
932
|
* <kf-pagination footer [page]="page()" [total]="total()" (pageChange)="page.set($event)" />
|
|
618
933
|
* </kf-data-table>
|
|
934
|
+
*
|
|
935
|
+
* Paginación interna opcional: con `pageSize`, la tabla pagina sola y
|
|
936
|
+
* agrega su propio `kf-pagination` en el footer nativo (el slot
|
|
937
|
+
* `[footer]` proyectado sigue disponible en paralelo). El selector de "N
|
|
938
|
+
* por página" viene activo por defecto con `DEFAULT_PAGE_SIZE_OPTIONS`
|
|
939
|
+
* ([10, 25, 50], estándar del portal) — pasar `pageSizeOptions` propio lo
|
|
940
|
+
* pisa, y pasar `[]` lo desactiva (paginación sin selector). `itemLabel`
|
|
941
|
+
* completa el texto informativo ("1–10 de 42 beneficios").
|
|
942
|
+
*
|
|
943
|
+
* <kf-data-table [columns]="columns" [rows]="rows" [pageSize]="25" itemLabel="beneficios" />
|
|
944
|
+
* <kf-data-table [columns]="columns" [rows]="rows" [pageSize]="25" [pageSizeOptions]="[]" />
|
|
945
|
+
*
|
|
946
|
+
* Ordenamiento por columna: cada `DataTableColumn` con `sortable: true`
|
|
947
|
+
* vuelve su header clickeable, con el mismo formato `key:direction` que
|
|
948
|
+
* usa `sort` de `kf-quote-picker` (two-way manual, no `model()` — el
|
|
949
|
+
* consumidor decide si persiste el valor o lo descarta al desmontar):
|
|
950
|
+
*
|
|
951
|
+
* <kf-data-table [columns]="columns" [rows]="rows" [sort]="sort()" (sortChange)="sort.set($event)" />
|
|
952
|
+
*
|
|
953
|
+
* Columnas fijas: `sticky: 'left' | 'right'` en la columna ancla esa
|
|
954
|
+
* columna a un borde del scroll horizontal — típicamente el identificador
|
|
955
|
+
* (izquierda) y/o las acciones (derecha) en una tabla con muchas columnas.
|
|
956
|
+
*
|
|
957
|
+
* Densidad: `density="compact"` reduce el padding vertical de cada fila,
|
|
958
|
+
* para priorizar cantidad de filas visibles sobre aire entre ellas.
|
|
619
959
|
*/
|
|
620
960
|
declare class DataTable<T = unknown> {
|
|
621
961
|
readonly columns: _angular_core.InputSignal<DataTableColumn[]>;
|
|
622
962
|
readonly rows: _angular_core.InputSignal<T[]>;
|
|
963
|
+
/** Placeholder nativo (`kf-skeleton`) mientras los datos reales cargan —
|
|
964
|
+
* ver el doc-comment de la clase para el detalle. */
|
|
965
|
+
readonly loading: _angular_core.InputSignal<boolean>;
|
|
966
|
+
private readonly scrollEl;
|
|
967
|
+
private readonly destroyRef;
|
|
968
|
+
/**
|
|
969
|
+
* `.kf-data-table__scroll` tiene `overflow-x: auto` sin ningún indicador
|
|
970
|
+
* visual — con varias columnas y poco ancho disponible, la tabla se corta
|
|
971
|
+
* en el borde y nada le avisa al usuario que hay más contenido
|
|
972
|
+
* scrolleando. `canScrollLeft`/`canScrollRight` alimentan un fade en cada
|
|
973
|
+
* borde — visible solo cuando de verdad hay algo que revelar de ese lado,
|
|
974
|
+
* así una tabla que entra completa no muestra fades falsos.
|
|
975
|
+
*/
|
|
976
|
+
protected readonly canScrollLeft: _angular_core.WritableSignal<boolean>;
|
|
977
|
+
protected readonly canScrollRight: _angular_core.WritableSignal<boolean>;
|
|
978
|
+
constructor();
|
|
979
|
+
private updateScrollState;
|
|
623
980
|
/** Default: índice de la fila dentro de `rows`. */
|
|
624
981
|
readonly rowKey: _angular_core.InputSignal<((row: T, index: number) => string | number) | undefined>;
|
|
625
982
|
/** Sin borde/sombra de card — para tablas anidadas dentro de otra card. */
|
|
@@ -637,10 +994,76 @@ declare class DataTable<T = unknown> {
|
|
|
637
994
|
$implicit: T;
|
|
638
995
|
index: number;
|
|
639
996
|
}>> | undefined>;
|
|
997
|
+
/** Densidad de fila — ver `DataTableDensity`. */
|
|
998
|
+
readonly density: _angular_core.InputSignal<DataTableDensity>;
|
|
999
|
+
/** Fuerza si la fila entera dispara `rowClick` en la vista de tabla —
|
|
1000
|
+
* ver `rowInteractive` para el cálculo automático cuando no se define. */
|
|
1001
|
+
readonly rowClickable: _angular_core.InputSignal<boolean | null>;
|
|
640
1002
|
readonly rowClick: _angular_core.OutputEmitterRef<{
|
|
641
1003
|
row: T;
|
|
642
1004
|
index: number;
|
|
643
1005
|
}>;
|
|
1006
|
+
/**
|
|
1007
|
+
* Paginación interna opcional — sin `pageSize`, cero cambio de
|
|
1008
|
+
* comportamiento (el consumidor sigue paginando afuera si quiere, vía el
|
|
1009
|
+
* slot `[footer]` con su propio `kf-pagination`, como ya hace Billing).
|
|
1010
|
+
* Con `pageSize`, la tabla pagina `rows()` sola y agrega su propio
|
|
1011
|
+
* `kf-pagination` en el footer nativo — el slot `[footer]` proyectado
|
|
1012
|
+
* sigue disponible para cualquier otra cosa que el consumidor quiera
|
|
1013
|
+
* agregar ahí (no son mutuamente excluyentes).
|
|
1014
|
+
*/
|
|
1015
|
+
readonly pageSize: _angular_core.InputSignal<number | null>;
|
|
1016
|
+
/** Default `DEFAULT_PAGE_SIZE_OPTIONS` ([10, 25, 50], estándar del
|
|
1017
|
+
* portal) — pasar `[]` explícitamente desactiva el selector de tamaño
|
|
1018
|
+
* aunque `pageSize` esté activo (paginación sin selector). */
|
|
1019
|
+
readonly pageSizeOptions: _angular_core.InputSignal<number[]>;
|
|
1020
|
+
readonly pageChange: _angular_core.OutputEmitterRef<number>;
|
|
1021
|
+
readonly pageSizeChange: _angular_core.OutputEmitterRef<number>;
|
|
1022
|
+
/** Nombre de la entidad listada (ej. "beneficios", "facturas"), en
|
|
1023
|
+
* plural — se agrega al final del texto informativo de la paginación
|
|
1024
|
+
* interna ("1–5 de 7 beneficios"). Sin esto, el texto queda solo con
|
|
1025
|
+
* números ("1–5 de 7"): la librería no impone un idioma ni un nombre
|
|
1026
|
+
* fijo, cada consumidor decide el suyo. */
|
|
1027
|
+
readonly itemLabel: _angular_core.InputSignal<string>;
|
|
1028
|
+
protected readonly page: _angular_core.WritableSignal<number>;
|
|
1029
|
+
private readonly internalPageSize;
|
|
1030
|
+
/** Tamaño de página efectivo: el que el usuario elija en el selector
|
|
1031
|
+
* (si `pageSizeOptions` está activo), o el `pageSize` del consumidor. */
|
|
1032
|
+
protected readonly effectivePageSize: _angular_core.Signal<number | null>;
|
|
1033
|
+
/** Cantidad de filas/cards placeholder mientras `loading` es true — el
|
|
1034
|
+
* `pageSize` efectivo si hay paginación activa (así el placeholder ya
|
|
1035
|
+
* "adivina" cuántas filas reales van a aparecer, sin salto de layout),
|
|
1036
|
+
* o 5 si no hay paginación. */
|
|
1037
|
+
protected readonly skeletonRowIndexes: _angular_core.Signal<number[]>;
|
|
1038
|
+
protected readonly totalPages: _angular_core.Signal<number>;
|
|
1039
|
+
/** Texto informativo del footer de paginación interna ("1–5 de 7
|
|
1040
|
+
* beneficios") — se recalcula con cada cambio de página/tamaño/dataset,
|
|
1041
|
+
* nunca queda desactualizado tras filtrar o pasar de página. */
|
|
1042
|
+
protected readonly paginationSummary: _angular_core.Signal<string>;
|
|
1043
|
+
/**
|
|
1044
|
+
* Ordenamiento por columna — mismo formato `key:direction` que `sort` de
|
|
1045
|
+
* `kf-quote-picker`, para que un consumidor que ya conoce ese patrón no
|
|
1046
|
+
* aprenda uno nuevo. Un click en un header `sortable` avanza el ciclo
|
|
1047
|
+
* asc → desc → sin orden; clickear otra columna reinicia el ciclo en esa
|
|
1048
|
+
* columna nueva.
|
|
1049
|
+
*/
|
|
1050
|
+
readonly sort: _angular_core.InputSignal<`${string}:desc` | `${string}:asc` | null>;
|
|
1051
|
+
readonly sortChange: _angular_core.OutputEmitterRef<`${string}:desc` | `${string}:asc` | null>;
|
|
1052
|
+
protected readonly sortedRows: _angular_core.Signal<T[]>;
|
|
1053
|
+
/** Filas realmente renderizadas: ordenadas y, si `pageSize` está activo,
|
|
1054
|
+
* recortadas a la página actual. */
|
|
1055
|
+
protected readonly displayedRows: _angular_core.Signal<T[]>;
|
|
1056
|
+
protected sortDirectionFor(column: DataTableColumn): 'asc' | 'desc' | null;
|
|
1057
|
+
/** Ciclo asc → desc → sin orden, disparado al clickear un header `sortable`. */
|
|
1058
|
+
protected toggleSort(column: DataTableColumn): void;
|
|
1059
|
+
protected goToPage(target: number): void;
|
|
1060
|
+
protected setPageSize(size: number | null): void;
|
|
1061
|
+
/** Columnas fijas del lado izquierdo/derecho, con el offset acumulado
|
|
1062
|
+
* (medido en runtime, ver `measureStickyOffsets`) para `position: sticky`. */
|
|
1063
|
+
protected readonly stickyLeftColumns: _angular_core.Signal<DataTableColumn[]>;
|
|
1064
|
+
protected readonly stickyRightColumns: _angular_core.Signal<DataTableColumn[]>;
|
|
1065
|
+
protected readonly stickyOffsets: _angular_core.WritableSignal<Map<string, number>>;
|
|
1066
|
+
protected stickyStyle(column: DataTableColumn): Record<string, string> | null;
|
|
644
1067
|
private readonly internalView;
|
|
645
1068
|
/** Vista activa: la elegida por el usuario, o `defaultView` si no tocó el toggle. */
|
|
646
1069
|
readonly view: _angular_core.Signal<DataTableView>;
|
|
@@ -656,14 +1079,45 @@ declare class DataTable<T = unknown> {
|
|
|
656
1079
|
* Con columnas `action`, la interacción la manejan esos botones.
|
|
657
1080
|
*/
|
|
658
1081
|
readonly cardInteractive: _angular_core.Signal<boolean>;
|
|
1082
|
+
/**
|
|
1083
|
+
* Mismo criterio que `cardInteractive`, para la vista de tabla: la fila
|
|
1084
|
+
* entera dispara `rowClick` solo si no hay una columna `action` que ya
|
|
1085
|
+
* resuelva la interacción con su propio botón — sin esto, un click en
|
|
1086
|
+
* "Ver detalle" dispararía TAMBIÉN el click de la fila por burbujeo.
|
|
1087
|
+
* `rowClickable` fuerza el comportamiento en cualquier sentido si el
|
|
1088
|
+
* consumidor lo necesita.
|
|
1089
|
+
*/
|
|
1090
|
+
readonly rowInteractive: _angular_core.Signal<boolean>;
|
|
659
1091
|
setView(next: DataTableView): void;
|
|
660
1092
|
trackRow(row: T, index: number): string | number;
|
|
661
1093
|
cellValue(row: T, column: DataTableColumn): string;
|
|
662
1094
|
onRowClick(row: T, index: number): void;
|
|
1095
|
+
/**
|
|
1096
|
+
* Ancho real de cada columna `sticky` — se mide después de cada render
|
|
1097
|
+
* porque el ancho de una columna depende de su contenido, no hay forma
|
|
1098
|
+
* de conocerlo de antemano. Cada columna fija toma como offset la suma
|
|
1099
|
+
* de anchos de las columnas fijas del MISMO lado que ya quedaron antes
|
|
1100
|
+
* que ella (así "left" apila de izquierda a derecha, "right" de derecha
|
|
1101
|
+
* a izquierda).
|
|
1102
|
+
*/
|
|
1103
|
+
protected measureStickyOffsets(): void;
|
|
663
1104
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<DataTable<any>, never>;
|
|
664
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<DataTable<any>, "kf-data-table", never, { "columns": { "alias": "columns"; "required": true; "isSignal": true; }; "rows": { "alias": "rows"; "required": true; "isSignal": true; }; "rowKey": { "alias": "rowKey"; "required": false; "isSignal": true; }; "flush": { "alias": "flush"; "required": false; "isSignal": true; }; "cardable": { "alias": "cardable"; "required": false; "isSignal": true; }; "defaultView": { "alias": "defaultView"; "required": false; "isSignal": true; }; "render": { "alias": "render"; "required": false; "isSignal": true; }; "cellTemplates": { "alias": "cellTemplates"; "required": false; "isSignal": true; }; }, { "rowClick": "rowClick"; }, never, ["[empty]", "[footer]"], true, never>;
|
|
1105
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<DataTable<any>, "kf-data-table", never, { "columns": { "alias": "columns"; "required": true; "isSignal": true; }; "rows": { "alias": "rows"; "required": true; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "rowKey": { "alias": "rowKey"; "required": false; "isSignal": true; }; "flush": { "alias": "flush"; "required": false; "isSignal": true; }; "cardable": { "alias": "cardable"; "required": false; "isSignal": true; }; "defaultView": { "alias": "defaultView"; "required": false; "isSignal": true; }; "render": { "alias": "render"; "required": false; "isSignal": true; }; "cellTemplates": { "alias": "cellTemplates"; "required": false; "isSignal": true; }; "density": { "alias": "density"; "required": false; "isSignal": true; }; "rowClickable": { "alias": "rowClickable"; "required": false; "isSignal": true; }; "pageSize": { "alias": "pageSize"; "required": false; "isSignal": true; }; "pageSizeOptions": { "alias": "pageSizeOptions"; "required": false; "isSignal": true; }; "itemLabel": { "alias": "itemLabel"; "required": false; "isSignal": true; }; "sort": { "alias": "sort"; "required": false; "isSignal": true; }; }, { "rowClick": "rowClick"; "pageChange": "pageChange"; "pageSizeChange": "pageSizeChange"; "sortChange": "sortChange"; }, never, ["[empty]", "[footer]"], true, never>;
|
|
665
1106
|
}
|
|
666
1107
|
|
|
1108
|
+
/**
|
|
1109
|
+
* Signal con la vista de `kf-data-table` ('table' | 'cards') derivada de un
|
|
1110
|
+
* breakpoint — mismo criterio que cualquier tabla con muchas columnas: bajo
|
|
1111
|
+
* el ancho dado, la vista de tabla no entra sin scroll horizontal, así que
|
|
1112
|
+
* se fuerza `cards`. Centraliza el patrón `matchMediaSignal` +
|
|
1113
|
+
* `computed<DataTableView>` que cada tabla del portal repetía igual.
|
|
1114
|
+
*
|
|
1115
|
+
* protected readonly tableView = dataTableViewSignal();
|
|
1116
|
+
* // con un breakpoint distinto al default de 900px:
|
|
1117
|
+
* protected readonly tableView = dataTableViewSignal('(max-width: 768px)');
|
|
1118
|
+
*/
|
|
1119
|
+
declare function dataTableViewSignal(query?: string): Signal<DataTableView>;
|
|
1120
|
+
|
|
667
1121
|
type DividerOrientation = 'horizontal' | 'vertical';
|
|
668
1122
|
/**
|
|
669
1123
|
* Separador visual simple entre bloques de contenido. `orientation`
|
|
@@ -686,6 +1140,33 @@ declare class Divider {
|
|
|
686
1140
|
static ɵcmp: _angular_core.ɵɵComponentDeclaration<Divider, "kf-divider", never, { "orientation": { "alias": "orientation"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
687
1141
|
}
|
|
688
1142
|
|
|
1143
|
+
/** Color del chip de ícono. Identifica la CLASE de documento de un vistazo
|
|
1144
|
+
* en una lista de nombres de archivo parecidos entre sí. */
|
|
1145
|
+
type DocumentRowTone = 'neutral' | 'ok' | 'warn' | 'error' | 'info';
|
|
1146
|
+
/**
|
|
1147
|
+
* Fila de un documento descargable: chip de ícono coloreado por tipo,
|
|
1148
|
+
* título, metadata y la acción de descarga al final. Sin `href` rinde
|
|
1149
|
+
* `pendingLabel` en vez de un link roto. El contenido proyectado cae entre
|
|
1150
|
+
* la metadata y la acción, como slot para un `kf-badge` de estado.
|
|
1151
|
+
*/
|
|
1152
|
+
declare class DocumentRow {
|
|
1153
|
+
readonly icon: _angular_core.InputSignal<IconName>;
|
|
1154
|
+
readonly tone: _angular_core.InputSignal<DocumentRowTone>;
|
|
1155
|
+
/** Qué documento es. El nombre humano del tipo, no el del archivo. */
|
|
1156
|
+
readonly title: _angular_core.InputSignal<string>;
|
|
1157
|
+
/** Línea secundaria — archivo, fecha, tamaño. El consumidor la arma ya
|
|
1158
|
+
* formateada: la librería no decide en qué unidad se lee un tamaño ni en
|
|
1159
|
+
* qué formato una fecha. */
|
|
1160
|
+
readonly meta: _angular_core.InputSignal<string | undefined>;
|
|
1161
|
+
/** Ausente rinde `pendingLabel` en vez del link de descarga. */
|
|
1162
|
+
readonly href: _angular_core.InputSignal<string | undefined>;
|
|
1163
|
+
readonly downloadLabel: _angular_core.InputSignal<string>;
|
|
1164
|
+
/** Qué se dice cuando no hay `href`. */
|
|
1165
|
+
readonly pendingLabel: _angular_core.InputSignal<string>;
|
|
1166
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<DocumentRow, never>;
|
|
1167
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<DocumentRow, "kf-document-row", never, { "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "tone": { "alias": "tone"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": true; "isSignal": true; }; "meta": { "alias": "meta"; "required": false; "isSignal": true; }; "href": { "alias": "href"; "required": false; "isSignal": true; }; "downloadLabel": { "alias": "downloadLabel"; "required": false; "isSignal": true; }; "pendingLabel": { "alias": "pendingLabel"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
|
|
1168
|
+
}
|
|
1169
|
+
|
|
689
1170
|
type DrawerSide = 'left' | 'right';
|
|
690
1171
|
/**
|
|
691
1172
|
* Panel lateral genérico, agnóstico de contenido — no sabe qué es navegación
|
|
@@ -695,11 +1176,6 @@ type DrawerSide = 'left' | 'right';
|
|
|
695
1176
|
* overlay con blur que cierra al click, `Escape`, atrapado de foco real y
|
|
696
1177
|
* devolución de foco al cerrar.
|
|
697
1178
|
*
|
|
698
|
-
* Extraído de `PortalHeader`, que hasta acá reimplementaba su propio drawer
|
|
699
|
-
* embebido (navegación hardcodeada + slot único al final). Cualquier
|
|
700
|
-
* consumidor que necesite un panel lateral — no solo `PortalHeader` — puede
|
|
701
|
-
* usar este componente directamente.
|
|
702
|
-
*
|
|
703
1179
|
* `open` es two-way, mismo patrón que `Dropdown`/`Modal`: el consumidor
|
|
704
1180
|
* decide qué dispara la apertura (un botón de hamburguesa, por ejemplo) y
|
|
705
1181
|
* qué hace con el cierre.
|
|
@@ -755,14 +1231,9 @@ declare class Drawer {
|
|
|
755
1231
|
* `kf-drawer-session` (que solo muestra identidad: brand, avatar, nombre,
|
|
756
1232
|
* email), no anidado dentro de ella.
|
|
757
1233
|
*
|
|
758
|
-
*
|
|
759
|
-
*
|
|
760
|
-
*
|
|
761
|
-
* en un componente propio porque identidad y logout son responsabilidades
|
|
762
|
-
* distintas — `kf-drawer-session` no debería saber que existe un botón de
|
|
763
|
-
* cerrar sesión, y el footer del drawer ya tiene su propio padding/borde
|
|
764
|
-
* superior (ver `Drawer.ts`) que reemplaza el borde que antes traía el botón
|
|
765
|
-
* mismo.
|
|
1234
|
+
* Ícono `log-out` + label, color `--kf-color-error` con hover en
|
|
1235
|
+
* `--kf-color-error-soft` y foco visible con `--kf-shadow-focus`. Separado
|
|
1236
|
+
* de `kf-drawer-session` (solo identidad) por responsabilidad única.
|
|
766
1237
|
*
|
|
767
1238
|
* No sabe nada de autenticación real — solo emite `logoutClick` cuando el
|
|
768
1239
|
* consumidor debe cerrar la sesión real.
|
|
@@ -806,6 +1277,14 @@ declare class DrawerLogout {
|
|
|
806
1277
|
declare class DrawerSession {
|
|
807
1278
|
readonly name: _angular_core.InputSignal<string>;
|
|
808
1279
|
readonly email: _angular_core.InputSignal<string>;
|
|
1280
|
+
/**
|
|
1281
|
+
* Etiqueta de rol o función del usuario dentro de la sesión (ej.
|
|
1282
|
+
* "Coordinador", "Supervisor"). Se muestra como badge dentro de la
|
|
1283
|
+
* identidad, debajo del email — distinto de `brand`, que es la marca o
|
|
1284
|
+
* cliente mostrada como encabezado de todo el bloque, no un atributo de
|
|
1285
|
+
* la persona.
|
|
1286
|
+
*/
|
|
1287
|
+
readonly role: _angular_core.InputSignal<string>;
|
|
809
1288
|
readonly initials: _angular_core.InputSignal<string>;
|
|
810
1289
|
readonly avatarUrl: _angular_core.InputSignal<string>;
|
|
811
1290
|
/** Label superior opcional, ej. "Portal Kaufmann LATAM". */
|
|
@@ -820,59 +1299,16 @@ declare class DrawerSession {
|
|
|
820
1299
|
* una de las primeras 2 palabras, en mayúscula. */
|
|
821
1300
|
protected readonly displayInitials: _angular_core.Signal<string>;
|
|
822
1301
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<DrawerSession, never>;
|
|
823
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<DrawerSession, "kf-drawer-session", never, { "name": { "alias": "name"; "required": true; "isSignal": true; }; "email": { "alias": "email"; "required": false; "isSignal": true; }; "initials": { "alias": "initials"; "required": false; "isSignal": true; }; "avatarUrl": { "alias": "avatarUrl"; "required": false; "isSignal": true; }; "brand": { "alias": "brand"; "required": false; "isSignal": true; }; "collapsed": { "alias": "collapsed"; "required": false; "isSignal": true; }; "interactive": { "alias": "interactive"; "required": false; "isSignal": true; }; }, { "cardClick": "cardClick"; }, never, never, true, never>;
|
|
1302
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<DrawerSession, "kf-drawer-session", never, { "name": { "alias": "name"; "required": true; "isSignal": true; }; "email": { "alias": "email"; "required": false; "isSignal": true; }; "role": { "alias": "role"; "required": false; "isSignal": true; }; "initials": { "alias": "initials"; "required": false; "isSignal": true; }; "avatarUrl": { "alias": "avatarUrl"; "required": false; "isSignal": true; }; "brand": { "alias": "brand"; "required": false; "isSignal": true; }; "collapsed": { "alias": "collapsed"; "required": false; "isSignal": true; }; "interactive": { "alias": "interactive"; "required": false; "isSignal": true; }; }, { "cardClick": "cardClick"; }, never, never, true, never>;
|
|
824
1303
|
}
|
|
825
1304
|
|
|
826
|
-
type DropdownItemTone = 'neutral' | 'error';
|
|
827
|
-
/**
|
|
828
|
-
* Ítem de acción dentro de un `kf-dropdown-panel`. Es un `<button>` nativo
|
|
829
|
-
* (no un `<a>`): dispara comportamiento en la pantalla actual, no navega.
|
|
830
|
-
*
|
|
831
|
-
* `icon` es opcional — sin él, el ítem es solo texto.
|
|
832
|
-
*
|
|
833
|
-
* <kf-dropdown-item icon="log-out" (click)="onLogout()" tone="error">Cerrar sesión</kf-dropdown-item>
|
|
834
|
-
*/
|
|
835
|
-
declare class DropdownItem {
|
|
836
|
-
readonly tone: _angular_core.InputSignal<DropdownItemTone>;
|
|
837
|
-
/** Ícono opcional al inicio de la fila. */
|
|
838
|
-
readonly icon: _angular_core.InputSignal<IconName | undefined>;
|
|
839
|
-
static ɵfac: _angular_core.ɵɵFactoryDeclaration<DropdownItem, never>;
|
|
840
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<DropdownItem, "kf-dropdown-item", never, { "tone": { "alias": "tone"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
|
|
841
|
-
}
|
|
842
|
-
type DropdownPanelVariant = 'solid' | 'dark';
|
|
843
|
-
/**
|
|
844
|
-
* Contenido del panel flotante de un `kf-dropdown`. Es solo una plantilla
|
|
845
|
-
* (`<ng-template>`, capturada vía `TemplateRef`) — `Dropdown` la conecta al
|
|
846
|
-
* overlay real cuando `open` pasa a `true`, no la renderiza en su propio
|
|
847
|
-
* lugar del DOM. Esto permite que el panel aparezca pegado al host de
|
|
848
|
-
* `Dropdown` sin importar dónde esté ese host en la página.
|
|
849
|
-
*
|
|
850
|
-
* `variant`: `'solid'` (default, fondo blanco) o `'dark'` (fondo sólido
|
|
851
|
-
* oscuro, mismo tono que `[kf-drawer-header]`) para cuando el trigger vive
|
|
852
|
-
* sobre una superficie oscura de marca.
|
|
853
|
-
*
|
|
854
|
-
* `dark` es sólida a propósito, no semitransparente: el CDK Overlay
|
|
855
|
-
* reposiciona el panel según dónde entra en el viewport y puede terminar
|
|
856
|
-
* cayendo sobre fondo blanco en vez de sobre el rail oscuro — un color
|
|
857
|
-
* sólido se ve intencional sin importar dónde lo posicione el CDK.
|
|
858
|
-
*/
|
|
859
|
-
declare class DropdownPanel {
|
|
860
|
-
readonly variant: _angular_core.InputSignal<DropdownPanelVariant>;
|
|
861
|
-
readonly templateRef: _angular_core.Signal<TemplateRef<any>>;
|
|
862
|
-
static ɵfac: _angular_core.ɵɵFactoryDeclaration<DropdownPanel, never>;
|
|
863
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<DropdownPanel, "kf-dropdown-panel", never, { "variant": { "alias": "variant"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
|
|
864
|
-
}
|
|
865
1305
|
/**
|
|
866
1306
|
* Menú de acciones flotante, conectado a un elemento disparador vía
|
|
867
1307
|
* `@angular/cdk/overlay` — la misma pieza que usa Angular Material para
|
|
868
|
-
* `mat-menu`/`mat-select`.
|
|
869
|
-
*
|
|
870
|
-
*
|
|
871
|
-
*
|
|
872
|
-
* dentro del propio componente) porque acá el disparador real puede vivir
|
|
873
|
-
* en OTRO componente por completo — ej. el avatar dentro de
|
|
874
|
-
* `kf-portal-header` — y el panel tiene que aparecer pegado a ESE elemento
|
|
875
|
-
* sin importar dónde el consumidor ubique al `Dropdown` en su HTML.
|
|
1308
|
+
* `mat-menu`/`mat-select`. El disparador real puede vivir en OTRO
|
|
1309
|
+
* componente por completo (ej. el avatar dentro de `kf-portal-header`), y
|
|
1310
|
+
* el panel aparece pegado a ESE elemento sin importar dónde el consumidor
|
|
1311
|
+
* ubique al `Dropdown` en su HTML.
|
|
876
1312
|
*
|
|
877
1313
|
* `open` es two-way: el consumidor decide qué dispara la apertura —
|
|
878
1314
|
* `Dropdown` en sí no renderiza ningún trigger, su propio host es el punto
|
|
@@ -900,15 +1336,10 @@ declare class Dropdown {
|
|
|
900
1336
|
/**
|
|
901
1337
|
* Suscripciones a `outsidePointerEvents()`/`keydownEvents()` del overlay
|
|
902
1338
|
* ACTUALMENTE adjunto. `getOrCreateOverlayRef()` reutiliza el mismo
|
|
903
|
-
* `overlayRef` entre aperturas (no lo recrea cada vez)
|
|
904
|
-
* `
|
|
905
|
-
*
|
|
906
|
-
*
|
|
907
|
-
* "clicks seguidos": al abrir de nuevo, el propio pointerdown que originó
|
|
908
|
-
* la apertura llegaba a más de una suscripción acumulada, disparando
|
|
909
|
-
* `open.set(false)` de forma errática en el mismo tick (patrón observado:
|
|
910
|
-
* abre cada 2 clicks, cierra cada 1). Se cancelan explícitamente en
|
|
911
|
-
* `detach()`, antes de la siguiente `attach()`.
|
|
1339
|
+
* `overlayRef` entre aperturas (no lo recrea cada vez), así que se
|
|
1340
|
+
* cancelan explícitamente en `detach()` antes de la siguiente `attach()`
|
|
1341
|
+
* — de lo contrario se acumularían N suscripciones activas sobre el
|
|
1342
|
+
* mismo overlay persistente.
|
|
912
1343
|
*/
|
|
913
1344
|
private overlaySubscription;
|
|
914
1345
|
constructor();
|
|
@@ -926,10 +1357,8 @@ declare class Dropdown {
|
|
|
926
1357
|
* overlay recién se crea/adjunta cuando `open` pasa a `true`, así que el
|
|
927
1358
|
* CDK no tiene forma de saber que ese elemento externo "es parte" de su
|
|
928
1359
|
* disparador. Sin este filtro, el mismo `pointerdown` que abre el menú
|
|
929
|
-
*
|
|
930
|
-
*
|
|
931
|
-
* abrió — bug real reproducido con Playwright: "abre y cierra" en
|
|
932
|
-
* clicks consecutivos rápidos sobre la tarjeta de sesión del sidebar.
|
|
1360
|
+
* también llega a `outsidePointerEvents()`, cerrando el panel en el
|
|
1361
|
+
* mismo gesto que lo abrió.
|
|
933
1362
|
*/
|
|
934
1363
|
private isEventOnTrigger;
|
|
935
1364
|
private detach;
|
|
@@ -938,6 +1367,47 @@ declare class Dropdown {
|
|
|
938
1367
|
static ɵcmp: _angular_core.ɵɵComponentDeclaration<Dropdown, "kf-dropdown", never, { "open": { "alias": "open"; "required": false; "isSignal": true; }; "anchor": { "alias": "anchor"; "required": false; "isSignal": true; }; }, { "open": "openChange"; }, ["panel"], ["*"], true, never>;
|
|
939
1368
|
}
|
|
940
1369
|
|
|
1370
|
+
type DropdownItemTone = 'neutral' | 'error';
|
|
1371
|
+
/**
|
|
1372
|
+
* Ítem de acción dentro de un `kf-dropdown-panel`. Es un `<button>` nativo
|
|
1373
|
+
* (no un `<a>`): dispara comportamiento en la pantalla actual, no navega.
|
|
1374
|
+
*
|
|
1375
|
+
* `icon` es opcional — sin él, el ítem es solo texto.
|
|
1376
|
+
*
|
|
1377
|
+
* <kf-dropdown-item icon="log-out" (click)="onLogout()" tone="error">Cerrar sesión</kf-dropdown-item>
|
|
1378
|
+
*/
|
|
1379
|
+
declare class DropdownItem {
|
|
1380
|
+
readonly tone: _angular_core.InputSignal<DropdownItemTone>;
|
|
1381
|
+
/** Ícono opcional al inicio de la fila. */
|
|
1382
|
+
readonly icon: _angular_core.InputSignal<IconName | undefined>;
|
|
1383
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<DropdownItem, never>;
|
|
1384
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<DropdownItem, "kf-dropdown-item", never, { "tone": { "alias": "tone"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
|
|
1385
|
+
}
|
|
1386
|
+
|
|
1387
|
+
type DropdownPanelVariant = 'solid' | 'dark';
|
|
1388
|
+
/**
|
|
1389
|
+
* Contenido del panel flotante de un `kf-dropdown`. Es solo una plantilla
|
|
1390
|
+
* (`<ng-template>`, capturada vía `TemplateRef`) — `Dropdown` la conecta al
|
|
1391
|
+
* overlay real cuando `open` pasa a `true`, no la renderiza en su propio
|
|
1392
|
+
* lugar del DOM. Esto permite que el panel aparezca pegado al host de
|
|
1393
|
+
* `Dropdown` sin importar dónde esté ese host en la página.
|
|
1394
|
+
*
|
|
1395
|
+
* `variant`: `'solid'` (default, fondo blanco) o `'dark'` (fondo sólido
|
|
1396
|
+
* oscuro, mismo tono que `[kf-drawer-header]`) para cuando el trigger vive
|
|
1397
|
+
* sobre una superficie oscura de marca.
|
|
1398
|
+
*
|
|
1399
|
+
* `dark` es sólida a propósito, no semitransparente: el CDK Overlay
|
|
1400
|
+
* reposiciona el panel según dónde entra en el viewport y puede terminar
|
|
1401
|
+
* cayendo sobre fondo blanco en vez de sobre el rail oscuro — un color
|
|
1402
|
+
* sólido se ve intencional sin importar dónde lo posicione el CDK.
|
|
1403
|
+
*/
|
|
1404
|
+
declare class DropdownPanel {
|
|
1405
|
+
readonly variant: _angular_core.InputSignal<DropdownPanelVariant>;
|
|
1406
|
+
readonly templateRef: _angular_core.Signal<TemplateRef<any>>;
|
|
1407
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<DropdownPanel, never>;
|
|
1408
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<DropdownPanel, "kf-dropdown-panel", never, { "variant": { "alias": "variant"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
|
|
1409
|
+
}
|
|
1410
|
+
|
|
941
1411
|
/**
|
|
942
1412
|
* Placeholder para una sección sin contenido. Dos tonos, con anatomía
|
|
943
1413
|
* distinta (páginas 27-28 del design system, que los trata como piezas
|
|
@@ -961,11 +1431,38 @@ declare class Dropdown {
|
|
|
961
1431
|
declare class EmptyState {
|
|
962
1432
|
readonly icon: _angular_core.InputSignal<IconName | undefined>;
|
|
963
1433
|
/** `neutral` (default): cero resultados. `error`: fallo real de carga. */
|
|
964
|
-
readonly tone: _angular_core.InputSignal<"
|
|
1434
|
+
readonly tone: _angular_core.InputSignal<"neutral" | "error">;
|
|
965
1435
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<EmptyState, never>;
|
|
966
1436
|
static ɵcmp: _angular_core.ɵɵComponentDeclaration<EmptyState, "kf-empty-state", never, { "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "tone": { "alias": "tone"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
|
|
967
1437
|
}
|
|
968
1438
|
|
|
1439
|
+
/** Color del marcador. Distingue la CLASE de evento de un vistazo en un log
|
|
1440
|
+
* largo — no es un estado de progreso. */
|
|
1441
|
+
type EventTimelineTone = 'neutral' | 'primary' | 'ok' | 'warn' | 'error' | 'info';
|
|
1442
|
+
/** Un evento ya ocurrido. Los tres campos de texto son los tres niveles de
|
|
1443
|
+
* lectura: qué pasó, por qué, y cuándo/quién. */
|
|
1444
|
+
interface EventTimelineItem {
|
|
1445
|
+
/** Clave de `track`. */
|
|
1446
|
+
readonly id: string;
|
|
1447
|
+
readonly title: string;
|
|
1448
|
+
readonly detail?: string | null;
|
|
1449
|
+
/** Hora y autor, ya formateados por el consumidor. */
|
|
1450
|
+
readonly meta?: string | null;
|
|
1451
|
+
readonly icon?: IconName;
|
|
1452
|
+
readonly tone?: EventTimelineTone;
|
|
1453
|
+
}
|
|
1454
|
+
/**
|
|
1455
|
+
* Línea de tiempo de AUDITORÍA: eventos ya ocurridos, en orden, cada uno con
|
|
1456
|
+
* marcador coloreado por CLASE y tres niveles de texto (titular, detalle,
|
|
1457
|
+
* hora + autor). Distinto de `kf-journey-stepper` (de PROGRESO, con pasos
|
|
1458
|
+
* futuros conocidos de antemano): acá todo ya pasó.
|
|
1459
|
+
*/
|
|
1460
|
+
declare class EventTimeline {
|
|
1461
|
+
readonly items: _angular_core.InputSignal<readonly EventTimelineItem[]>;
|
|
1462
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<EventTimeline, never>;
|
|
1463
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<EventTimeline, "kf-event-timeline", never, { "items": { "alias": "items"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
1464
|
+
}
|
|
1465
|
+
|
|
969
1466
|
/**
|
|
970
1467
|
* Bloque promocional sobre fondo oscuro de marca (página 37) — campañas,
|
|
971
1468
|
* accesos directos o el portal de autoatención. Usa `--kf-color-tertiary-deep`
|
|
@@ -1051,129 +1548,196 @@ declare class FieldLabel {
|
|
|
1051
1548
|
static ɵcmp: _angular_core.ɵɵComponentDeclaration<FieldLabel, "kf-field-label", never, { "for": { "alias": "for"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
|
|
1052
1549
|
}
|
|
1053
1550
|
|
|
1054
|
-
|
|
1055
|
-
interface FilterChipOption {
|
|
1551
|
+
interface MultiSelectOption {
|
|
1056
1552
|
value: string;
|
|
1057
1553
|
label: string;
|
|
1058
1554
|
}
|
|
1059
1555
|
/**
|
|
1060
|
-
*
|
|
1556
|
+
* Dropdown de selección múltiple con checkboxes internos. El panel flotante
|
|
1557
|
+
* se conecta al trigger vía `@angular/cdk/overlay` — el mismo mecanismo que
|
|
1558
|
+
* `Select` (ver `../select/Select.ts`) y `Dropdown` (`../dropdown/Dropdown.ts`).
|
|
1061
1559
|
*
|
|
1062
|
-
*
|
|
1063
|
-
*
|
|
1064
|
-
* del
|
|
1560
|
+
* A diferencia de `Select`, el disparador no muestra un único valor sino los
|
|
1561
|
+
* nombres seleccionados junto a un badge circular con el conteo, y cada
|
|
1562
|
+
* opción del panel se marca/desmarca con un checkbox sin cerrar el panel.
|
|
1563
|
+
* Incluye una opción "Todas" al inicio de la lista, con estado
|
|
1564
|
+
* `indeterminate` cuando hay selección parcial — el mismo patrón de
|
|
1565
|
+
* "seleccionar todos" de `Checkbox`. "Todas" nunca se filtra por el buscador:
|
|
1566
|
+
* queda siempre visible y sigue actuando sobre el universo COMPLETO de
|
|
1567
|
+
* `options()`, no sobre el subconjunto filtrado (ver JSDoc de `toggleAll()`).
|
|
1065
1568
|
*
|
|
1066
|
-
*
|
|
1067
|
-
*
|
|
1068
|
-
*
|
|
1569
|
+
* <kf-multi-select
|
|
1570
|
+
* label="Sucursales"
|
|
1571
|
+
* [options]="branches"
|
|
1572
|
+
* [values]="selectedBranches()"
|
|
1573
|
+
* (valuesChange)="selectedBranches.set($event)"
|
|
1574
|
+
* />
|
|
1069
1575
|
*
|
|
1070
|
-
*
|
|
1071
|
-
*
|
|
1072
|
-
* (valueChange)="filtro = $event" />
|
|
1576
|
+
* `width="full"` ocupa el 100% del contenedor — para multi-selects dentro
|
|
1577
|
+
* de layouts flex/grid donde el ancho fijo de `md` no encaja.
|
|
1073
1578
|
*
|
|
1074
|
-
* <kf-
|
|
1075
|
-
* [value]="filtro"
|
|
1076
|
-
* (valueChange)="filtro = $event" />
|
|
1579
|
+
* <kf-multi-select width="full" [options]="branches" [values]="selectedBranches()" (valuesChange)="..." />
|
|
1077
1580
|
*/
|
|
1078
|
-
declare class
|
|
1079
|
-
readonly
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
readonly
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1581
|
+
declare class MultiSelect {
|
|
1582
|
+
readonly label: _angular_core.InputSignal<string | undefined>;
|
|
1583
|
+
readonly options: _angular_core.InputSignal<MultiSelectOption[]>;
|
|
1584
|
+
readonly values: _angular_core.InputSignal<string[]>;
|
|
1585
|
+
readonly placeholder: _angular_core.InputSignal<string>;
|
|
1586
|
+
readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
1587
|
+
/**
|
|
1588
|
+
* Ancho del control. 'md' (240px) es el default histórico y preserva el
|
|
1589
|
+
* comportamiento actual sin cambios para consumidores existentes.
|
|
1590
|
+
* 'full' ocupa el 100% del contenedor — para multi-selects dentro de
|
|
1591
|
+
* layouts flex/grid donde el ancho fijo no encaja (ej. un footer de
|
|
1592
|
+
* sidebar).
|
|
1593
|
+
*/
|
|
1594
|
+
readonly width: _angular_core.InputSignal<"sm" | "md" | "lg" | "full">;
|
|
1595
|
+
/**
|
|
1596
|
+
* Tri-state: si el consumidor no lo especifica (`undefined`), el buscador
|
|
1597
|
+
* se activa solo cuando `options().length` supera `searchThreshold()`. Si
|
|
1598
|
+
* el consumidor define `true`/`false` explícitamente, ese valor manda por
|
|
1599
|
+
* sobre el umbral automático. Mismo criterio que `Select`.
|
|
1600
|
+
*/
|
|
1601
|
+
readonly searchable: _angular_core.InputSignal<boolean | undefined>;
|
|
1602
|
+
readonly searchThreshold: _angular_core.InputSignal<number>;
|
|
1603
|
+
readonly valuesChange: _angular_core.OutputEmitterRef<string[]>;
|
|
1604
|
+
private readonly instanceId;
|
|
1605
|
+
readonly triggerId: string;
|
|
1606
|
+
readonly listboxId: string;
|
|
1607
|
+
readonly isOpen: _angular_core.WritableSignal<boolean>;
|
|
1608
|
+
readonly searchQuery: _angular_core.WritableSignal<string>;
|
|
1609
|
+
readonly activeIndex: _angular_core.WritableSignal<number>;
|
|
1610
|
+
readonly selectedCount: _angular_core.Signal<number>;
|
|
1611
|
+
readonly allSelected: _angular_core.Signal<boolean>;
|
|
1612
|
+
readonly isPartiallySelected: _angular_core.Signal<boolean>;
|
|
1613
|
+
readonly showSearch: _angular_core.Signal<boolean>;
|
|
1614
|
+
/** Opciones visibles tras aplicar el filtro de búsqueda ("Todas" nunca se filtra). */
|
|
1615
|
+
readonly filteredOptions: _angular_core.Signal<MultiSelectOption[]>;
|
|
1616
|
+
/**
|
|
1617
|
+
* Texto del disparador: placeholder si no hay selección, o los nombres
|
|
1618
|
+
* seleccionados unidos por coma (el conteo va aparte, en el badge). Con
|
|
1619
|
+
* muchas opciones el `text-overflow: ellipsis` del `.kf-multi-select__value`
|
|
1620
|
+
* corta el texto — el badge de conteo siempre queda visible al lado.
|
|
1621
|
+
*/
|
|
1622
|
+
readonly summaryText: _angular_core.Signal<string>;
|
|
1623
|
+
private readonly triggerRef;
|
|
1624
|
+
private readonly panelTemplate;
|
|
1625
|
+
private readonly searchInputRef;
|
|
1626
|
+
private readonly overlay;
|
|
1627
|
+
constructor();
|
|
1628
|
+
isSelected(value: string): boolean;
|
|
1629
|
+
toggle(): void;
|
|
1630
|
+
toggleOption(value: string): void;
|
|
1631
|
+
/**
|
|
1632
|
+
* Alterna entre "todas seleccionadas" y "ninguna seleccionada". Actúa
|
|
1633
|
+
* siempre sobre el universo COMPLETO de `options()`, no sobre
|
|
1634
|
+
* `filteredOptions()` — con un buscador activo, "Todas" no se filtra ni
|
|
1635
|
+
* cambia de significado: seguir marcando/desmarcando únicamente lo visible
|
|
1636
|
+
* sería sorprendente para quien ya tenía otras opciones (no visibles en el
|
|
1637
|
+
* filtro actual) seleccionadas de antes.
|
|
1638
|
+
*/
|
|
1639
|
+
toggleAll(): void;
|
|
1640
|
+
onSearchInput(event: Event): void;
|
|
1641
|
+
/** Mueve `activeIndex` sobre las opciones YA FILTRADAS visibles. Clamp simple, sin wraparound. */
|
|
1642
|
+
moveActive(delta: number): void;
|
|
1643
|
+
toggleActive(): void;
|
|
1644
|
+
onEscape(): void;
|
|
1645
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<MultiSelect, never>;
|
|
1646
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<MultiSelect, "kf-multi-select", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": false; "isSignal": true; }; "values": { "alias": "values"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "width": { "alias": "width"; "required": false; "isSignal": true; }; "searchable": { "alias": "searchable"; "required": false; "isSignal": true; }; "searchThreshold": { "alias": "searchThreshold"; "required": false; "isSignal": true; }; }, { "valuesChange": "valuesChange"; }, never, never, true, never>;
|
|
1647
|
+
}
|
|
1648
|
+
|
|
1649
|
+
type FilterFieldType = 'select' | 'multi-select' | 'search';
|
|
1650
|
+
interface FilterFieldConfig {
|
|
1651
|
+
key: string;
|
|
1652
|
+
type: FilterFieldType;
|
|
1653
|
+
label?: string;
|
|
1654
|
+
placeholder?: string;
|
|
1655
|
+
width?: string;
|
|
1656
|
+
icon?: IconName;
|
|
1657
|
+
showDivider?: boolean;
|
|
1658
|
+
}
|
|
1659
|
+
type FilterValue = string | string[];
|
|
1660
|
+
interface FilterFieldChange {
|
|
1661
|
+
key: string;
|
|
1662
|
+
value: FilterValue;
|
|
1096
1663
|
}
|
|
1097
1664
|
/**
|
|
1098
|
-
*
|
|
1099
|
-
*
|
|
1100
|
-
*
|
|
1101
|
-
*
|
|
1102
|
-
*
|
|
1103
|
-
*
|
|
1104
|
-
*
|
|
1105
|
-
*
|
|
1106
|
-
*
|
|
1665
|
+
* Fila de filtros config-driven (select / multi-select / búsqueda) para el
|
|
1666
|
+
* header de una tabla, compuesta con piezas YA existentes de la librería
|
|
1667
|
+
* (`kf-select`, `kf-multi-select`, `kf-input`, `kf-divider`, `kf-collapsible`).
|
|
1668
|
+
*
|
|
1669
|
+
* No tiene lógica de filtrado propia: solo renderiza los controles descritos
|
|
1670
|
+
* por `fields()` y emite `fieldChange` cuando alguno cambia — filtrar filas
|
|
1671
|
+
* sigue siendo responsabilidad de quien lo consume.
|
|
1672
|
+
*
|
|
1673
|
+
* En escritorio los campos se muestran en una fila; en mobile/tablet
|
|
1674
|
+
* (≤768px, mismo breakpoint que `kf-portal-header`) se colapsan dentro de un
|
|
1675
|
+
* `kf-collapsible`.
|
|
1676
|
+
*
|
|
1677
|
+
* <kf-filter
|
|
1678
|
+
* [fields]="[
|
|
1679
|
+
* { key: 'model', type: 'multi-select', label: 'Modelo', placeholder: 'Todos los modelos', showDivider: true },
|
|
1680
|
+
* { key: 'search', type: 'search', label: 'Buscar vehículo', placeholder: 'Ingresa N° de orden o placa', width: '280px' },
|
|
1681
|
+
* ]"
|
|
1682
|
+
* [values]="{ model: selectedModels(), search: searchTerm() }"
|
|
1683
|
+
* [options]="{ model: modelOptions() }"
|
|
1684
|
+
* (fieldChange)="onFieldChange($event)"
|
|
1685
|
+
* />
|
|
1107
1686
|
*/
|
|
1108
|
-
declare class
|
|
1109
|
-
|
|
1110
|
-
|
|
1687
|
+
declare class Filter {
|
|
1688
|
+
readonly fields: _angular_core.InputSignal<FilterFieldConfig[]>;
|
|
1689
|
+
readonly values: _angular_core.InputSignal<Record<string, FilterValue>>;
|
|
1690
|
+
readonly options: _angular_core.InputSignal<Record<string, string[]>>;
|
|
1691
|
+
readonly mobileTitle: _angular_core.InputSignal<string>;
|
|
1692
|
+
readonly fieldChange: _angular_core.OutputEmitterRef<FilterFieldChange>;
|
|
1693
|
+
protected readonly multiOptionsByKey: _angular_core.Signal<Record<string, MultiSelectOption[]>>;
|
|
1694
|
+
protected singleValueFor(key: string): string;
|
|
1695
|
+
protected multiValueFor(key: string): string[];
|
|
1696
|
+
protected fieldWidth(field: FilterFieldConfig): string;
|
|
1697
|
+
protected optionsFor(key: string): string[];
|
|
1698
|
+
protected multiOptionsFor(key: string): MultiSelectOption[];
|
|
1699
|
+
protected onFieldChange(key: string, value: FilterValue): void;
|
|
1700
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<Filter, never>;
|
|
1701
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<Filter, "kf-filter", never, { "fields": { "alias": "fields"; "required": true; "isSignal": true; }; "values": { "alias": "values"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": false; "isSignal": true; }; "mobileTitle": { "alias": "mobileTitle"; "required": false; "isSignal": true; }; }, { "fieldChange": "fieldChange"; }, never, never, true, never>;
|
|
1702
|
+
}
|
|
1703
|
+
|
|
1704
|
+
/** Forma normalizada interna de cada opción, ya resuelta a value/label. */
|
|
1705
|
+
interface FilterChipOption {
|
|
1706
|
+
value: string;
|
|
1707
|
+
label: string;
|
|
1111
1708
|
}
|
|
1112
1709
|
/**
|
|
1113
|
-
*
|
|
1114
|
-
* inferior de la pestaña activa se funde visualmente con el panel de
|
|
1115
|
-
* contenido que va debajo (misma superficie, sin línea divisoria).
|
|
1116
|
-
*
|
|
1117
|
-
* Al cambiar de pestaña, la que se activa se "abre": gira sobre su borde
|
|
1118
|
-
* inferior (hinge 3D) y sube con un pequeño rebote. Es CSS solo: la clase
|
|
1119
|
-
* `--active` se mueve de un botón a otro, y eso vuelve a disparar el keyframe
|
|
1120
|
-
* sin tocar JS.
|
|
1121
|
-
*
|
|
1122
|
-
* El panel de contenido es un `<ng-content>` OPCIONAL — si el consumidor no
|
|
1123
|
-
* proyecta nada, `kf-folder-tabs` renderiza solo la fila de pestañas (mismo
|
|
1124
|
-
* comportamiento que antes de que existiera este slot, detectado con
|
|
1125
|
-
* `contentChild()` sin `.required`, igual patrón que usa `kf-dropdown` para
|
|
1126
|
-
* saber si hay un `kf-dropdown-panel` proyectado). Si SÍ proyecta contenido,
|
|
1127
|
-
* se envuelve en un panel con la esquina superior-izquierda recta (nace ahí
|
|
1128
|
-
* la primera pestaña, siempre pegada al borde izquierdo de la fila — un
|
|
1129
|
-
* radio ahí competiría visualmente con el propio borde redondeado de esa
|
|
1130
|
-
* pestaña) y las otras tres redondeadas. El `border-bottom` de 2px de la
|
|
1131
|
-
* pestaña activa (ver `.kf-folder-tabs__tab--active` más abajo) es la señal
|
|
1132
|
-
* visual de conexión con el panel para CUALQUIER pestaña activa, sin
|
|
1133
|
-
* necesitar mover ni medir nada.
|
|
1134
|
-
*
|
|
1135
|
-
* El borde izquierdo de toda la fila (y por lo tanto de la esquina recta
|
|
1136
|
-
* del panel) depende de que ninguna pestaña se corra al cambiar de estado
|
|
1137
|
-
* — ver `transform-origin: bottom left` en `.kf-folder-tabs__tab`: con el
|
|
1138
|
-
* origen centrado (como estaba antes), el `scale()` de la pestaña inactiva
|
|
1139
|
-
* encogía el borde izquierdo unos px, y ese corrimiento sub-pixel es lo que
|
|
1140
|
-
* hacía que este mismo diseño (esquina fija) se viera "roto" cuando la
|
|
1141
|
-
* pestaña activa no era la primera. Con el origen anclado a la izquierda,
|
|
1142
|
-
* el corrimiento desaparece y la esquina fija vuelve a ser una solución
|
|
1143
|
-
* válida y simple — sin necesitar medir la pestaña activa por JS.
|
|
1144
|
-
*
|
|
1145
|
-
* Antes de este slot, el patrón era que el consumidor pusiera su propia
|
|
1146
|
-
* card como HERMANO de `kf-folder-tabs` y anulara a mano el radio superior
|
|
1147
|
-
* completo (`border-top-left-radius: 0`, `border-top-right-radius: 0` — ver
|
|
1148
|
-
* el caso original de `web-portal-latam-frontend`). Encapsular esto acá
|
|
1149
|
-
* evita que cada consumidor repita esas reglas a mano.
|
|
1710
|
+
* Fila de chips tipo "pill" para filtros de una sola selección.
|
|
1150
1711
|
*
|
|
1151
|
-
*
|
|
1152
|
-
*
|
|
1153
|
-
*
|
|
1712
|
+
* Cada opción puede pasarse como string simple (se usa como value y label
|
|
1713
|
+
* a la vez) o como objeto { value, label } cuando el texto mostrado difiere
|
|
1714
|
+
* del valor interno.
|
|
1154
1715
|
*
|
|
1155
|
-
*
|
|
1156
|
-
*
|
|
1716
|
+
* El chip activo se resalta con `--kf-color-primary` — mismo color de marca
|
|
1717
|
+
* que `kf-tabs`/`kf-select` para su propio estado activo, así el filtro
|
|
1718
|
+
* seleccionado se lee con el color del tema activo, no un negro fijo que
|
|
1719
|
+
* no cambia entre marcas.
|
|
1720
|
+
*
|
|
1721
|
+
* <kf-filter-chips [options]="['Todos', 'Activos', 'Archivados']"
|
|
1722
|
+
* [value]="filtro"
|
|
1723
|
+
* (valueChange)="filtro = $event" />
|
|
1724
|
+
*
|
|
1725
|
+
* <kf-filter-chips [options]="[{ value: 'all', label: 'Todos' }]"
|
|
1726
|
+
* [value]="filtro"
|
|
1727
|
+
* (valueChange)="filtro = $event" />
|
|
1157
1728
|
*/
|
|
1158
|
-
declare class
|
|
1159
|
-
readonly options: _angular_core.InputSignal<
|
|
1729
|
+
declare class FilterChips {
|
|
1730
|
+
readonly options: _angular_core.InputSignal<(string | {
|
|
1731
|
+
value: string;
|
|
1732
|
+
label: string;
|
|
1733
|
+
})[]>;
|
|
1160
1734
|
readonly value: _angular_core.InputSignal<string>;
|
|
1161
1735
|
readonly valueChange: _angular_core.OutputEmitterRef<string>;
|
|
1162
|
-
/**
|
|
1163
|
-
|
|
1164
|
-
* `kfFolderTabsPanel` — sin `.required`, así que es `undefined`
|
|
1165
|
-
* cuando no hay nada proyectado (mismo patrón que usa `kf-dropdown` con
|
|
1166
|
-
* `contentChild(DropdownPanel)` para saber si hay un panel que
|
|
1167
|
-
* attachear). `hasPanel()` decide si el wrapper del panel participa del
|
|
1168
|
-
* layout (`display: contents` si no).
|
|
1169
|
-
*/
|
|
1170
|
-
private readonly projectedPanel;
|
|
1171
|
-
protected readonly hasPanel: _angular_core.Signal<boolean>;
|
|
1736
|
+
/** Normaliza cada opción string/objeto a la forma { value, label }. */
|
|
1737
|
+
readonly normalizedOptions: _angular_core.Signal<FilterChipOption[]>;
|
|
1172
1738
|
select(value: string): void;
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FolderTabs, never>;
|
|
1176
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<FolderTabs, "kf-folder-tabs", never, { "options": { "alias": "options"; "required": true; "isSignal": true; }; "value": { "alias": "value"; "required": true; "isSignal": true; }; }, { "valueChange": "valueChange"; }, ["projectedPanel"], ["[kfFolderTabsPanel]"], true, never>;
|
|
1739
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FilterChips, never>;
|
|
1740
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<FilterChips, "kf-filter-chips", never, { "options": { "alias": "options"; "required": true; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; }, { "valueChange": "valueChange"; }, never, never, true, never>;
|
|
1177
1741
|
}
|
|
1178
1742
|
|
|
1179
1743
|
/** Un link del pie — legales, contacto, soporte. */
|
|
@@ -1208,6 +1772,101 @@ declare class Footer {
|
|
|
1208
1772
|
static ɵcmp: _angular_core.ɵɵComponentDeclaration<Footer, "kf-footer", never, { "logoSrc": { "alias": "logoSrc"; "required": false; "isSignal": true; }; "logoAlt": { "alias": "logoAlt"; "required": false; "isSignal": true; }; "copyright": { "alias": "copyright"; "required": true; "isSignal": true; }; "links": { "alias": "links"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
1209
1773
|
}
|
|
1210
1774
|
|
|
1775
|
+
/** Opciones de `formatNumber` — todas opcionales. */
|
|
1776
|
+
interface FormatNumberOptions {
|
|
1777
|
+
/** Cantidad de decimales a mostrar. Default `0`. */
|
|
1778
|
+
decimals?: number;
|
|
1779
|
+
/** Locale de `Intl`/`toLocaleString` — decide separador de miles y de
|
|
1780
|
+
* decimales. Default `'es'` (punto de miles, coma decimal). */
|
|
1781
|
+
locale?: string;
|
|
1782
|
+
}
|
|
1783
|
+
/**
|
|
1784
|
+
* Separador de miles genérico — sin signo, sin asumir que el valor es
|
|
1785
|
+
* dinero. Cubre casos como kilometraje/horómetro (`formatNumber(45231,
|
|
1786
|
+
* { locale: 'en-US' })` → `'45,231'`), no solo moneda.
|
|
1787
|
+
*
|
|
1788
|
+
* formatNumber(16800); // '16.800'
|
|
1789
|
+
* formatNumber(16800.5, { decimals: 2 }); // '16.800,50'
|
|
1790
|
+
* formatNumber(45231, { locale: 'en-US' }); // '45,231'
|
|
1791
|
+
*/
|
|
1792
|
+
declare function formatNumber(value: number, options?: FormatNumberOptions): string;
|
|
1793
|
+
/** Opciones de `formatCurrency` — extiende `FormatNumberOptions` con el
|
|
1794
|
+
* signo/prefijo de moneda. */
|
|
1795
|
+
interface FormatCurrencyOptions extends FormatNumberOptions {
|
|
1796
|
+
/** Símbolo o prefijo antepuesto al monto. Default `'$'`. */
|
|
1797
|
+
sign?: string;
|
|
1798
|
+
}
|
|
1799
|
+
/**
|
|
1800
|
+
* Formatea un monto de dinero — compone sobre `formatNumber` agregando el
|
|
1801
|
+
* signo. Mismos defaults que la convención de OTD (punto de miles, sin
|
|
1802
|
+
* decimales, prefijo `$`), overrideables para otra moneda/país/formato:
|
|
1803
|
+
*
|
|
1804
|
+
* formatCurrency(16800); // '$16.800'
|
|
1805
|
+
* formatCurrency(16800.5, { decimals: 2 }); // '$16.800,50'
|
|
1806
|
+
* formatCurrency(16800, { sign: '' }); // '16.800'
|
|
1807
|
+
* formatCurrency(16800, { sign: 'US$', locale: 'en-US' }); // 'US$16,800'
|
|
1808
|
+
*/
|
|
1809
|
+
declare function formatCurrency(amount: number, options?: FormatCurrencyOptions): string;
|
|
1810
|
+
/**
|
|
1811
|
+
* Inverso de `formatNumber` — recupera el número crudo de un valor ya
|
|
1812
|
+
* formateado con el mismo `locale`. Solo soporta `locale` cuyo separador de
|
|
1813
|
+
* miles y decimal sean `'.'`/`','` (`es`) o `','`/`'.'` (`en-US`) — son los
|
|
1814
|
+
* 2 casos reales del ecosistema hoy; si aparece un tercer locale con otros
|
|
1815
|
+
* separadores, extender acá antes de usarlo.
|
|
1816
|
+
*/
|
|
1817
|
+
declare function parseNumber(formatted: string, locale?: string): number;
|
|
1818
|
+
/**
|
|
1819
|
+
* Inverso de `formatCurrency` — recupera el número crudo de un monto ya
|
|
1820
|
+
* formateado, descartando cualquier signo/prefijo de moneda.
|
|
1821
|
+
*
|
|
1822
|
+
* parseCurrency('$16.800'); // 16800
|
|
1823
|
+
*/
|
|
1824
|
+
declare function parseCurrency(formatted: string, locale?: string): number;
|
|
1825
|
+
/** "12:40" — hora en formato de 24 horas. */
|
|
1826
|
+
declare function formatClockTime(date: Date): string;
|
|
1827
|
+
/**
|
|
1828
|
+
* Quita espacios (incluido cualquier espacio en blanco intermedio, no solo
|
|
1829
|
+
* al principio/final) y puntos de un identificador de documento — un
|
|
1830
|
+
* espacio invisible (ej. pegado por accidente desde otra fuente) es un
|
|
1831
|
+
* motivo de rechazo silencioso real que un usuario no sabe explicarse.
|
|
1832
|
+
* Genérico: sirve tanto para RUT (`formatChileanRut` ya lo usa por
|
|
1833
|
+
* dentro) como para cualquier otro identificador con el mismo problema.
|
|
1834
|
+
*
|
|
1835
|
+
* sanitizeDocumentId(' 51 00004-8 '); // '5100004-8'
|
|
1836
|
+
* sanitizeDocumentId('12.345.678-9'); // '12345678-9'
|
|
1837
|
+
*/
|
|
1838
|
+
declare function sanitizeDocumentId(raw: string): string;
|
|
1839
|
+
/**
|
|
1840
|
+
* Formatea un RUT chileno insertando el guion automáticamente a medida
|
|
1841
|
+
* que se escribe — igual que un input de tarjeta de crédito, el usuario
|
|
1842
|
+
* solo tipea dígitos (y opcionalmente `k`/`K` como dígito verificador) y
|
|
1843
|
+
* el guion aparece solo antes del último carácter. Solo da forma al
|
|
1844
|
+
* string — no valida si el dígito verificador es matemáticamente
|
|
1845
|
+
* correcto (ver `isValidChileanRut` para eso) ni si la cuenta existe
|
|
1846
|
+
* (eso lo confirma GAM del lado servidor al autenticar).
|
|
1847
|
+
*
|
|
1848
|
+
* formatChileanRut('123456789'); // '12345678-9'
|
|
1849
|
+
* formatChileanRut('5100004-8'); // '5100004-8' (ya formateado, idempotente)
|
|
1850
|
+
* formatChileanRut('5100004k'); // '5100004-k'
|
|
1851
|
+
* formatChileanRut('510000 4-8 '); // '5100004-8' (espacios ignorados)
|
|
1852
|
+
*/
|
|
1853
|
+
declare function formatChileanRut(raw: string): string;
|
|
1854
|
+
/**
|
|
1855
|
+
* Valida un RUT chileno YA formateado (`cuerpo-DV`, ej. `'12345678-9'`)
|
|
1856
|
+
* contra el algoritmo módulo 11 real — a diferencia del `pattern` de
|
|
1857
|
+
* `document-id.config.ts` en cada portal consumidor (que solo exige la
|
|
1858
|
+
* FORMA: dígitos + guion + dígito verificador), esta función confirma
|
|
1859
|
+
* que el dígito verificador sea matemáticamente correcto para ese
|
|
1860
|
+
* cuerpo. Sigue sin confirmar que la cuenta exista — eso solo lo sabe
|
|
1861
|
+
* GAM al autenticar — pero atrapa el error tipográfico más común
|
|
1862
|
+
* (invertir/cambiar un dígito) antes de intentar el login.
|
|
1863
|
+
*
|
|
1864
|
+
* isValidChileanRut('20236276-1'); // true (DV real de ese cuerpo)
|
|
1865
|
+
* isValidChileanRut('20236276-8'); // false (DV real es '1', no '8')
|
|
1866
|
+
* isValidChileanRut('no-es-rut'); // false (ni siquiera tiene la forma)
|
|
1867
|
+
*/
|
|
1868
|
+
declare function isValidChileanRut(rut: string): boolean;
|
|
1869
|
+
|
|
1211
1870
|
/**
|
|
1212
1871
|
* Input de formulario apilado: label arriba (en mayúsculas vía CSS), campo con
|
|
1213
1872
|
* fondo gris que pasa a blanco con anillo de foco azul al enfocar.
|
|
@@ -1239,12 +1898,26 @@ declare class Footer {
|
|
|
1239
1898
|
* de una sola acción.
|
|
1240
1899
|
*
|
|
1241
1900
|
* <kf-input type="password" label="Contraseña" toggleable formControlName="password" />
|
|
1901
|
+
*
|
|
1902
|
+
* `readonly` es distinto de `disabled`: un campo `disabled` no recibe foco
|
|
1903
|
+
* por teclado ni se puede seleccionar/copiar su texto (y se lee como
|
|
1904
|
+
* "deshabilitado" en un lector de pantalla); un campo `readonly` sí — el
|
|
1905
|
+
* valor se puede ver, enfocar y copiar, solo no se puede editar. Usar
|
|
1906
|
+
* `readonly` para mostrar un dato existente que el usuario puede querer
|
|
1907
|
+
* copiar (ej. un documento de identidad en un formulario de perfil), y
|
|
1908
|
+
* `disabled` para un campo genuinamente inactivo (ej. mientras un guardado
|
|
1909
|
+
* está en vuelo).
|
|
1910
|
+
*
|
|
1911
|
+
* <kf-input label="DNI" readonly formControlName="documentId" />
|
|
1242
1912
|
*/
|
|
1243
1913
|
declare class Input implements ControlValueAccessor, AfterViewInit {
|
|
1244
1914
|
private static nextId;
|
|
1245
1915
|
private readonly nativeInput;
|
|
1246
1916
|
readonly label: _angular_core.InputSignal<string | undefined>;
|
|
1247
|
-
/** Marca el label con un asterisco visual
|
|
1917
|
+
/** Marca el label con un asterisco visual y aplica `required` al `<input>`
|
|
1918
|
+
* nativo — el atributo HTML ya expone el estado obligatorio a lectores de
|
|
1919
|
+
* pantalla (mapeo implícito a `aria-required`), sin necesidad de un
|
|
1920
|
+
* `aria-required` explícito redundante. */
|
|
1248
1921
|
readonly required: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
1249
1922
|
/** Ícono líder, renderizado vía `kf-icon`. */
|
|
1250
1923
|
readonly icon: _angular_core.InputSignal<IconName | undefined>;
|
|
@@ -1255,6 +1928,11 @@ declare class Input implements ControlValueAccessor, AfterViewInit {
|
|
|
1255
1928
|
/** Mensaje de error con `role="alert"` — reemplaza a `hint` mientras esté presente. */
|
|
1256
1929
|
readonly errorMessage: _angular_core.InputSignal<string | undefined>;
|
|
1257
1930
|
readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
1931
|
+
/** Ver JSDoc de la clase para la diferencia con `disabled`. No pasa por
|
|
1932
|
+
* Reactive Forms (a diferencia de `disabled`, que también puede llegar
|
|
1933
|
+
* vía `FormControl.disable()`) — es siempre una decisión explícita del
|
|
1934
|
+
* consumidor del componente. */
|
|
1935
|
+
readonly readonly: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
1258
1936
|
readonly type: _angular_core.InputSignal<string>;
|
|
1259
1937
|
readonly placeholder: _angular_core.InputSignal<string>;
|
|
1260
1938
|
/** Agrega el botón de mostrar/ocultar — sin efecto si `type` no es `"password"`. */
|
|
@@ -1295,39 +1973,787 @@ declare class Input implements ControlValueAccessor, AfterViewInit {
|
|
|
1295
1973
|
*/
|
|
1296
1974
|
protected onTogglePasswordVisibility(): void;
|
|
1297
1975
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<Input, never>;
|
|
1298
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<Input, "kf-input", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "suffix": { "alias": "suffix"; "required": false; "isSignal": true; }; "hint": { "alias": "hint"; "required": false; "isSignal": true; }; "invalid": { "alias": "invalid"; "required": false; "isSignal": true; }; "errorMessage": { "alias": "errorMessage"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "type": { "alias": "type"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "toggleable": { "alias": "toggleable"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
1976
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<Input, "kf-input", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "suffix": { "alias": "suffix"; "required": false; "isSignal": true; }; "hint": { "alias": "hint"; "required": false; "isSignal": true; }; "invalid": { "alias": "invalid"; "required": false; "isSignal": true; }; "errorMessage": { "alias": "errorMessage"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "readonly": { "alias": "readonly"; "required": false; "isSignal": true; }; "type": { "alias": "type"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "toggleable": { "alias": "toggleable"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
1977
|
+
}
|
|
1978
|
+
|
|
1979
|
+
type JourneyStepStatus = 'done' | 'current' | 'delayed' | 'pending';
|
|
1980
|
+
type JourneyStepperSurface = 'dark' | 'card';
|
|
1981
|
+
/**
|
|
1982
|
+
* Paleta de color de los estados, independiente de `orientation`:
|
|
1983
|
+
*
|
|
1984
|
+
* - `'brand'` (default): azul de marca para completado/actual, sin
|
|
1985
|
+
* semántica de semáforo. Para cuando horizontal y vertical representan
|
|
1986
|
+
* el mismo journey en distintos tamaños de pantalla.
|
|
1987
|
+
* - `'semantic'`: verde/amarillo/rojo real por paso (solo con
|
|
1988
|
+
* `orientation="vertical"`, la única con 4 estados incluido `delayed`).
|
|
1989
|
+
* Para cuando cada paso comunica su propio estado puntual.
|
|
1990
|
+
*/
|
|
1991
|
+
type JourneyStepperColorScheme = 'brand' | 'semantic';
|
|
1992
|
+
interface JourneyStep {
|
|
1993
|
+
key: string;
|
|
1994
|
+
label: string;
|
|
1995
|
+
icon: IconName;
|
|
1996
|
+
/** Solo con `orientation="vertical"` — en horizontal el status se calcula desde `currentStep`. */
|
|
1997
|
+
status?: JourneyStepStatus;
|
|
1998
|
+
/** Texto bajo el label — solo con `orientation="vertical"`. */
|
|
1999
|
+
date?: string;
|
|
2000
|
+
}
|
|
2001
|
+
/**
|
|
2002
|
+
* Progreso de un flujo de N pasos.
|
|
2003
|
+
*
|
|
2004
|
+
* - `orientation` (`'horizontal'` default | `'vertical'`): horizontal
|
|
2005
|
+
* deriva done/current/pending de `currentStep`; vertical usa
|
|
2006
|
+
* `steps[].status` explícito, con `delayed` como cuarto estado.
|
|
2007
|
+
* - `surface` (`'dark'` default | `'card'`): fondo sólido oscuro de marca,
|
|
2008
|
+
* o superficie clara con borde+sombra.
|
|
2009
|
+
* - `colorScheme` (`'brand'` default | `'semantic'`): ver `JourneyStepperColorScheme`.
|
|
2010
|
+
*
|
|
2011
|
+
* <kf-journey-stepper surface="card" [steps]="steps" [currentStep]="4" (stepClick)="onNavigate($event)" />
|
|
2012
|
+
* <kf-journey-stepper orientation="vertical" [steps]="unitSteps" />
|
|
2013
|
+
* <kf-journey-stepper orientation="vertical" colorScheme="semantic" [steps]="unitStepsWithDelays" />
|
|
2014
|
+
*/
|
|
2015
|
+
declare class JourneyStepper {
|
|
2016
|
+
readonly steps: _angular_core.InputSignal<JourneyStep[]>;
|
|
2017
|
+
readonly orientation: _angular_core.InputSignal<"horizontal" | "vertical">;
|
|
2018
|
+
readonly surface: _angular_core.InputSignal<JourneyStepperSurface>;
|
|
2019
|
+
readonly colorScheme: _angular_core.InputSignal<JourneyStepperColorScheme>;
|
|
2020
|
+
/** Índice del paso actual — solo con `orientation="horizontal"`. */
|
|
2021
|
+
readonly currentStep: _angular_core.InputSignal<number>;
|
|
2022
|
+
/** Se emite con `step.key` al clickear un paso — solo activo con `orientation="horizontal"`. */
|
|
2023
|
+
readonly stepClick: _angular_core.OutputEmitterRef<string>;
|
|
2024
|
+
private readonly track;
|
|
2025
|
+
private readonly destroyRef;
|
|
2026
|
+
/** `true` cuando el contenido desborda el ancho visible — controla el fade de los bordes. */
|
|
2027
|
+
protected readonly hasOverflow: _angular_core.WritableSignal<boolean>;
|
|
2028
|
+
protected readonly clickable: _angular_core.Signal<boolean>;
|
|
2029
|
+
constructor();
|
|
2030
|
+
private measureOverflow;
|
|
2031
|
+
protected readonly resolvedSteps: _angular_core.Signal<(JourneyStep & {
|
|
2032
|
+
status: JourneyStepStatus;
|
|
2033
|
+
})[]>;
|
|
2034
|
+
protected dotIcon(status: JourneyStepStatus, stepIcon: IconName): IconName;
|
|
2035
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<JourneyStepper, never>;
|
|
2036
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<JourneyStepper, "kf-journey-stepper", never, { "steps": { "alias": "steps"; "required": true; "isSignal": true; }; "orientation": { "alias": "orientation"; "required": false; "isSignal": true; }; "surface": { "alias": "surface"; "required": false; "isSignal": true; }; "colorScheme": { "alias": "colorScheme"; "required": false; "isSignal": true; }; "currentStep": { "alias": "currentStep"; "required": false; "isSignal": true; }; }, { "stepClick": "stepClick"; }, never, never, true, never>;
|
|
1299
2037
|
}
|
|
1300
2038
|
|
|
1301
2039
|
type LinkVariant = 'default' | 'muted';
|
|
1302
2040
|
/**
|
|
1303
2041
|
* Acción de bajo énfasis con apariencia de texto — "¿Olvidaste tu
|
|
1304
|
-
* contraseña?", "Volver a iniciar sesión"
|
|
1305
|
-
*
|
|
1306
|
-
*
|
|
1307
|
-
*
|
|
1308
|
-
*
|
|
1309
|
-
*
|
|
1310
|
-
*
|
|
1311
|
-
* misma pantalla (cambiar de vista, someter un flujo). Si en el futuro un
|
|
1312
|
-
* consumidor necesita navegación real con href, esa es una discusión de API
|
|
1313
|
-
* distinta (`kf-link` con `[routerLink]`/`href`), no se fuerza acá sin caso
|
|
1314
|
-
* de uso confirmado.
|
|
2042
|
+
* contraseña?", "Volver a iniciar sesión", o un link real de navegación
|
|
2043
|
+
* como "Ir a pagos →".
|
|
2044
|
+
*
|
|
2045
|
+
* Sin `href` (default) es un `<button type="button">`: dispara una acción
|
|
2046
|
+
* en la misma pantalla, vía `(click)`. Con `href`, es un `<a>` real: navega
|
|
2047
|
+
* a otra URL. `showArrow` agrega un ícono `chevron-right` al final — para
|
|
2048
|
+
* el patrón "texto + flecha" de un link que lleva a otro lado.
|
|
1315
2049
|
*
|
|
1316
2050
|
* `variant="default"` (color primario, subrayado en hover) es para la
|
|
1317
|
-
* acción de mayor jerarquía relativa de la pantalla
|
|
1318
|
-
*
|
|
1319
|
-
* para acciones de retroceso/salida (ej. "← Volver a iniciar sesión").
|
|
2051
|
+
* acción de mayor jerarquía relativa de la pantalla; `variant="muted"`
|
|
2052
|
+
* (gris que pasa a primario en hover) es para acciones de retroceso/salida.
|
|
1320
2053
|
*
|
|
1321
2054
|
* <kf-link (click)="onForgotPassword()">¿Olvidaste tu contraseña?</kf-link>
|
|
1322
2055
|
* <kf-link variant="muted" (click)="onBack()">← Volver a iniciar sesión</kf-link>
|
|
2056
|
+
* <kf-link href="/pagos" showArrow>Ir a pagos</kf-link>
|
|
1323
2057
|
*/
|
|
1324
2058
|
declare class Link {
|
|
1325
2059
|
readonly variant: _angular_core.InputSignal<LinkVariant>;
|
|
1326
2060
|
readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
2061
|
+
/** Si se pasa, renderiza un `<a>` real en vez de `<button>` — navega en vez de disparar `(click)`. */
|
|
2062
|
+
readonly href: _angular_core.InputSignal<string | undefined>;
|
|
2063
|
+
/** Ícono `chevron-right` al final — para el patrón "texto + flecha" de un link que lleva a otro lado. */
|
|
2064
|
+
readonly showArrow: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
1327
2065
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<Link, never>;
|
|
1328
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<Link, "kf-link", never, { "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
|
|
2066
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<Link, "kf-link", never, { "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "href": { "alias": "href"; "required": false; "isSignal": true; }; "showArrow": { "alias": "showArrow"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
|
|
1329
2067
|
}
|
|
1330
2068
|
|
|
2069
|
+
/**
|
|
2070
|
+
* Motor de mapas configurado a nivel app, no por uso — mismo criterio que
|
|
2071
|
+
* `API_CONFIG` en `packages/api-client` de `route-assistance-monorepo`: el
|
|
2072
|
+
* access token de Mapbox es un dato de despliegue (cada app/entorno tiene
|
|
2073
|
+
* el propio), no algo que cada `<kf-map>` deba repetir.
|
|
2074
|
+
*/
|
|
2075
|
+
interface MapConfig {
|
|
2076
|
+
/** Access token público de Mapbox (`pk.*`) de la cuenta del proyecto que consume la librería. */
|
|
2077
|
+
accessToken: string;
|
|
2078
|
+
/**
|
|
2079
|
+
* Estilo de Mapbox Studio propio del cliente, si lo tiene. Cuando se
|
|
2080
|
+
* define, gana siempre sobre `theme()` del componente — un estilo de
|
|
2081
|
+
* marca custom no tiene una variante "light"/"dark" que elegir, ya viene
|
|
2082
|
+
* resuelto. Sin esto, `kf-map` usa el estilo `light-v11`/`dark-v11` de
|
|
2083
|
+
* Mapbox según `theme()`.
|
|
2084
|
+
*/
|
|
2085
|
+
styleUrl?: string;
|
|
2086
|
+
}
|
|
2087
|
+
declare const MAP_CONFIG: InjectionToken<MapConfig>;
|
|
2088
|
+
/**
|
|
2089
|
+
* `provide*` idiomático (mismo patrón que `provideHttpClient`/`provideRouter`)
|
|
2090
|
+
* para inyectar una sola vez en `app.config.ts` de cada app consumidora:
|
|
2091
|
+
*
|
|
2092
|
+
* providers: [provideMapConfig({ accessToken: 'pk.xxx' })]
|
|
2093
|
+
*/
|
|
2094
|
+
declare function provideMapConfig(config: MapConfig): Provider;
|
|
2095
|
+
|
|
2096
|
+
/**
|
|
2097
|
+
* Vocabulario de temas de `kf-map`, separado de `MapView.ts` para que
|
|
2098
|
+
* `MapThemePicker` (el panel flotante del control `mapStyle`) pueda
|
|
2099
|
+
* importarlo sin crear un ciclo `MapView → MapControls → MapThemePicker →
|
|
2100
|
+
* MapView`.
|
|
2101
|
+
*
|
|
2102
|
+
* `streets`/`satellite`/`outdoors` se suman a los `light`/`dark`
|
|
2103
|
+
* originales — los 3 IDs de estilo (`streets-v12`, `satellite-streets-v12`,
|
|
2104
|
+
* `outdoors-v12`) están confirmados contra la cuenta/token real del
|
|
2105
|
+
* ecosistema: la API de estilos de Mapbox es por cuenta, no por versión de
|
|
2106
|
+
* `mapbox-gl`, así que cualquier duda se resuelve probando contra el token
|
|
2107
|
+
* real, no leyendo el número de versión instalado.
|
|
2108
|
+
*/
|
|
2109
|
+
type MapTheme = 'light' | 'dark' | 'streets' | 'satellite' | 'outdoors';
|
|
2110
|
+
declare const STYLE_BY_THEME: Record<MapTheme, string>;
|
|
2111
|
+
/** Orden fijo en que `MapThemePicker` lista las 5 opciones — predecible
|
|
2112
|
+
* (siempre el mismo orden en toda pantalla que use el picker), no importa
|
|
2113
|
+
* cuál esté activa. */
|
|
2114
|
+
declare const THEME_ORDER: MapTheme[];
|
|
2115
|
+
/** Nombre en español de cada tema, para el `aria-label`/label del control
|
|
2116
|
+
* `mapStyle` y las opciones del picker — anuncia qué tema rige HOY o qué
|
|
2117
|
+
* tema se está por elegir, no solo "otro tema distinto". */
|
|
2118
|
+
declare const THEME_LABEL: Record<MapTheme, string>;
|
|
2119
|
+
|
|
2120
|
+
/**
|
|
2121
|
+
* Cada pieza es independiente entre sí — agregar/sacar un `MapControlKind`
|
|
2122
|
+
* de `controls` en `kf-map` no reacomoda ni afecta a los demás. Array vacío
|
|
2123
|
+
* (default de `kf-map`) es "ningún control": opt-in explícito del
|
|
2124
|
+
* consumidor, nunca activado por default (ver JSDoc de `controls` en
|
|
2125
|
+
* `MapView.ts`).
|
|
2126
|
+
*
|
|
2127
|
+
* zoom — botones +/- reales (`map.zoomIn()`/`zoomOut()`).
|
|
2128
|
+
* fullscreen — expandir/contraer el mapa (Fullscreen API nativa).
|
|
2129
|
+
* mapStyle — ciclar entre los temas de `MapTheme`.
|
|
2130
|
+
* recenter — re-ejecuta el encuadre a markers/route bajo demanda.
|
|
2131
|
+
* cluster — togglear agrupamiento nativo de markers cercanos.
|
|
2132
|
+
* follow — la cámara sigue al marker `live` mientras se mueve.
|
|
2133
|
+
*/
|
|
2134
|
+
type MapControlKind = 'zoom' | 'fullscreen' | 'mapStyle' | 'recenter' | 'cluster' | 'follow';
|
|
2135
|
+
/**
|
|
2136
|
+
* Barra de controles flotantes de `kf-map` — pieza puramente presentacional,
|
|
2137
|
+
* sin estado propio: todo lo que sabe se lo pasa `MapView` por input, todo
|
|
2138
|
+
* lo que hace lo reporta por output para que `MapView` (única dueña del
|
|
2139
|
+
* `Map` de Mapbox) decida qué hacer. Vive en la misma carpeta que `MapView`
|
|
2140
|
+
* a propósito (mismo criterio que `DropdownItem`/`DropdownPanel` con
|
|
2141
|
+
* `Dropdown`): el acoplamiento es real, no de nombre — no tiene sentido
|
|
2142
|
+
* fuera de un `kf-map` que la controle.
|
|
2143
|
+
*
|
|
2144
|
+
* Un solo stack vertical, siempre en la esquina superior derecha del mapa
|
|
2145
|
+
* (fijado por `MapView.css`, no acá): es donde ya vivía el `NavigationControl`
|
|
2146
|
+
* de Mapbox que este componente reemplaza, y la atribución obligatoria de
|
|
2147
|
+
* Mapbox (`AttributionControl`, nunca oculta) vive abajo-derecha — un solo
|
|
2148
|
+
* stack evita que dos grupos floten en esquinas distintas y compitan por
|
|
2149
|
+
* espacio en un mapa `size="sm"` de 200px de alto.
|
|
2150
|
+
*
|
|
2151
|
+
* El stack no es una lista continua de botones idénticos: cada grupo lógico
|
|
2152
|
+
* es su propia "card" flotante (`kf-radius-md`+`kf-shadow-md`+fondo blanco,
|
|
2153
|
+
* ver `.kf-map-controls__group` en `MapControls.css`), separadas entre sí
|
|
2154
|
+
* por el `gap` del `:host` — mismo lenguaje ya verificado visualmente en
|
|
2155
|
+
* `route-assistance-monorepo/design/mockups/operations-final.html`
|
|
2156
|
+
* (`.map__ctrl-group`). Orden fijo de arriba a abajo, independiente del
|
|
2157
|
+
* orden en el array `controls` del consumidor (para que dos pantallas que
|
|
2158
|
+
* piden los mismos controles en distinto orden se vean IGUAL):
|
|
2159
|
+
*
|
|
2160
|
+
* 1. zoom — magnificación (par +/-, unido por un divisor, sin gap entre sí).
|
|
2161
|
+
* 2. cámara — recenter + follow (posición de la cámara).
|
|
2162
|
+
* 3. datos — cluster (densidad de los markers, eje distinto de cámara).
|
|
2163
|
+
* 4. chrome — mapStyle + fullscreen (nada del mapa en sí).
|
|
2164
|
+
*
|
|
2165
|
+
* Cada grupo usa `@if` sobre AL MENOS UNO de sus controles (ej. el grupo de
|
|
2166
|
+
* cámara aparece con solo `recenter`, solo `follow`, o ambos) — agregar o
|
|
2167
|
+
* sacar un control nunca reacomoda ni depende de los demás grupos.
|
|
2168
|
+
*
|
|
2169
|
+
* Todos los botones siguen siendo `kf-button[variant=surface][shape=circle]`
|
|
2170
|
+
* — sin tocar `Button.ts`: el fondo/sombra propios de `surface` se anulan
|
|
2171
|
+
* SOLO dentro de un `.kf-map-controls__group` vía una custom property CSS
|
|
2172
|
+
* (ver el comentario junto a esa clase en `MapControls.css` sobre por qué
|
|
2173
|
+
* eso sí atraviesa la encapsulación de Angular y un selector normal no).
|
|
2174
|
+
*/
|
|
2175
|
+
declare class MapControls {
|
|
2176
|
+
readonly controls: _angular_core.InputSignal<MapControlKind[]>;
|
|
2177
|
+
readonly clusterActive: _angular_core.InputSignal<boolean>;
|
|
2178
|
+
readonly followActive: _angular_core.InputSignal<boolean>;
|
|
2179
|
+
/** Sin ningún marker `live`, togglear "seguir" no tendría ningún efecto
|
|
2180
|
+
* visible — se deshabilita en vez de dejar un botón que parece no hacer nada. */
|
|
2181
|
+
readonly canFollow: _angular_core.InputSignal<boolean>;
|
|
2182
|
+
readonly isFullscreen: _angular_core.InputSignal<boolean>;
|
|
2183
|
+
/** Nombre en español del tema actualmente resuelto (ej. "Calles"), para
|
|
2184
|
+
* anunciar en el `aria-label` del botón de estilo qué se está por perder,
|
|
2185
|
+
* no solo que "algo" va a cambiar. */
|
|
2186
|
+
readonly styleLabel: _angular_core.InputSignal<string>;
|
|
2187
|
+
/** Tema resuelto HOY (`light|dark|...`) — a diferencia de `styleLabel`
|
|
2188
|
+
* (ya traducido a texto), esto es lo que `MapThemePicker` necesita para
|
|
2189
|
+
* marcar la opción activa. */
|
|
2190
|
+
readonly activeTheme: _angular_core.InputSignal<MapTheme>;
|
|
2191
|
+
readonly zoomIn: _angular_core.OutputEmitterRef<void>;
|
|
2192
|
+
readonly zoomOut: _angular_core.OutputEmitterRef<void>;
|
|
2193
|
+
readonly recenter: _angular_core.OutputEmitterRef<void>;
|
|
2194
|
+
readonly followToggle: _angular_core.OutputEmitterRef<void>;
|
|
2195
|
+
readonly clusterToggle: _angular_core.OutputEmitterRef<void>;
|
|
2196
|
+
/** Reemplaza al viejo `styleCycle` (ciclado de un click): ahora el botón
|
|
2197
|
+
* `mapStyle` abre `MapThemePicker` y este output reporta la elección real
|
|
2198
|
+
* hecha ahí. */
|
|
2199
|
+
readonly themeSelect: _angular_core.OutputEmitterRef<MapTheme>;
|
|
2200
|
+
readonly fullscreenToggle: _angular_core.OutputEmitterRef<void>;
|
|
2201
|
+
/** Visibilidad del panel de temas — estado puramente de UI (abierto o no),
|
|
2202
|
+
* no de negocio: `MapView` no necesita saberlo, así que vive acá y no
|
|
2203
|
+
* rompe el criterio de "sin estado propio" del resto de la clase (ver
|
|
2204
|
+
* JSDoc). Se cierra solo (click afuera/Escape) vía `kf-dropdown`, y acá
|
|
2205
|
+
* además al elegir un tema. */
|
|
2206
|
+
protected readonly mapStyleOpen: _angular_core.WritableSignal<boolean>;
|
|
2207
|
+
protected onThemeSelect(theme: MapTheme): void;
|
|
2208
|
+
protected readonly hasZoom: _angular_core.Signal<boolean>;
|
|
2209
|
+
protected readonly hasRecenter: _angular_core.Signal<boolean>;
|
|
2210
|
+
protected readonly hasFollow: _angular_core.Signal<boolean>;
|
|
2211
|
+
protected readonly hasCluster: _angular_core.Signal<boolean>;
|
|
2212
|
+
protected readonly hasMapStyle: _angular_core.Signal<boolean>;
|
|
2213
|
+
protected readonly hasFullscreen: _angular_core.Signal<boolean>;
|
|
2214
|
+
/** El grupo "cámara" (card) aparece con solo `recenter`, solo `follow`, o
|
|
2215
|
+
* ambos — ver JSDoc de la clase sobre por qué cada `@if` de grupo cubre
|
|
2216
|
+
* "al menos uno de sus controles" en vez de un control puntual. */
|
|
2217
|
+
protected readonly hasCamera: _angular_core.Signal<boolean>;
|
|
2218
|
+
protected readonly hasChrome: _angular_core.Signal<boolean>;
|
|
2219
|
+
/**
|
|
2220
|
+
* Texto corto que revela cada botón en hover/foco vía `[kfTooltip]` (ver
|
|
2221
|
+
* `MapControls.html` — la librería ya resuelve este patrón con
|
|
2222
|
+
* `TooltipDirective`, no con CSS a mano) — la misma acción que ya describe
|
|
2223
|
+
* `ariaLabel`, condensada para caber en una píldora en vez de leerse como
|
|
2224
|
+
* oración completa. No reemplaza `ariaLabel` (que sigue siendo la fuente
|
|
2225
|
+
* real para lectores de pantalla): esto es SOLO visual, para quien puede
|
|
2226
|
+
* ver el mapa pero no sabe qué hace un ícono suelto.
|
|
2227
|
+
*/
|
|
2228
|
+
protected readonly followLabel: _angular_core.Signal<"Dejar de seguir" | "Seguir">;
|
|
2229
|
+
protected readonly clusterLabel: _angular_core.Signal<"Desagrupar" | "Agrupar">;
|
|
2230
|
+
protected readonly mapStyleLabel: _angular_core.Signal<string>;
|
|
2231
|
+
protected readonly fullscreenLabel: _angular_core.Signal<"Salir de pantalla completa" | "Pantalla completa">;
|
|
2232
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<MapControls, never>;
|
|
2233
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<MapControls, "kf-map-controls", never, { "controls": { "alias": "controls"; "required": false; "isSignal": true; }; "clusterActive": { "alias": "clusterActive"; "required": false; "isSignal": true; }; "followActive": { "alias": "followActive"; "required": false; "isSignal": true; }; "canFollow": { "alias": "canFollow"; "required": false; "isSignal": true; }; "isFullscreen": { "alias": "isFullscreen"; "required": false; "isSignal": true; }; "styleLabel": { "alias": "styleLabel"; "required": false; "isSignal": true; }; "activeTheme": { "alias": "activeTheme"; "required": false; "isSignal": true; }; }, { "zoomIn": "zoomIn"; "zoomOut": "zoomOut"; "recenter": "recenter"; "followToggle": "followToggle"; "clusterToggle": "clusterToggle"; "themeSelect": "themeSelect"; "fullscreenToggle": "fullscreenToggle"; }, never, never, true, never>;
|
|
2234
|
+
}
|
|
2235
|
+
|
|
2236
|
+
type MapSize = 'sm' | 'md' | 'lg' | 'full';
|
|
2237
|
+
type MapVariant = 'interactive' | 'preview';
|
|
2238
|
+
/** Vocabulario semántico único de tono, compartido con `TextTone`/`StatTileTone`. */
|
|
2239
|
+
type MapMarkerTone = 'neutral' | 'ok' | 'warn' | 'error' | 'info';
|
|
2240
|
+
interface MapLatLng {
|
|
2241
|
+
lat: number;
|
|
2242
|
+
lng: number;
|
|
2243
|
+
}
|
|
2244
|
+
/**
|
|
2245
|
+
* Botón de acción real dentro de `MapMarker.popup` — cada click emite
|
|
2246
|
+
* `markerAction` con su `key` (nunca el `label`, ver JSDoc de `popup` más
|
|
2247
|
+
* abajo). `icon` es OPCIONAL a propósito: no romper a los consumidores que
|
|
2248
|
+
* ya usaban `actions` sin él (era el único campo hasta esta ronda).
|
|
2249
|
+
*
|
|
2250
|
+
* La jerarquía visual entre acciones NO es un campo aparte (`variant`) sino
|
|
2251
|
+
* la POSICIÓN en el array: la primera acción se renderiza con estilo
|
|
2252
|
+
* prominente (fill, mismo peso que `kf-button[variant=primary]`, el
|
|
2253
|
+
* default del botón); el resto con estilo discreto (mismo peso que
|
|
2254
|
+
* `kf-button[variant=ghost]` — ver el ejemplo de `Modal` con
|
|
2255
|
+
* `variant="ghost"` para "Cancelar" junto a un primary implícito). Un
|
|
2256
|
+
* consumidor con una sola acción no necesita pensar en esto: esa acción ya
|
|
2257
|
+
* es "la primera".
|
|
2258
|
+
*/
|
|
2259
|
+
interface MapPopupAction {
|
|
2260
|
+
key: string;
|
|
2261
|
+
label: string;
|
|
2262
|
+
/** Ícono líder del catálogo de `kf-icon` — sin esto, la acción se ve solo
|
|
2263
|
+
* con texto (compatibilidad con `actions` ya declarado sin `icon`). */
|
|
2264
|
+
icon?: IconName;
|
|
2265
|
+
}
|
|
2266
|
+
interface MapMarker {
|
|
2267
|
+
position: MapLatLng;
|
|
2268
|
+
/** Sin `icon`, cae en `map-pin` — el pin genérico del catálogo. Se
|
|
2269
|
+
* ignora cuando el control `cluster` está activo (ver JSDoc de
|
|
2270
|
+
* `controls` — el clustering nativo resuelve por capas WebGL, no por
|
|
2271
|
+
* DOM, y ahí no hay dónde montar un `kf-icon`). */
|
|
2272
|
+
icon?: IconName;
|
|
2273
|
+
tone?: MapMarkerTone;
|
|
2274
|
+
/** Nombre accesible del marker (`aria-label`/`title`) — no abre un popup. */
|
|
2275
|
+
label?: string;
|
|
2276
|
+
/**
|
|
2277
|
+
* Identificador propio del consumidor (ej. el id del ticket/vehículo que
|
|
2278
|
+
* representa) — sin esto el marker no es clickeable: `kf-map` no tiene
|
|
2279
|
+
* forma de reportar "se hizo click en ESTE marker" sin algo que
|
|
2280
|
+
* identificarlo de vuelta hacia `markerSelect`. Opcional a propósito: un
|
|
2281
|
+
* mapa puramente decorativo (ej. la vista previa de `variant="preview"`)
|
|
2282
|
+
* no necesita que sus markers respondan a nada.
|
|
2283
|
+
*/
|
|
2284
|
+
id?: string;
|
|
2285
|
+
/**
|
|
2286
|
+
* Marca este marker como el punto que sigue la cámara cuando el control
|
|
2287
|
+
* `follow` está activo — ej. la posición en tiempo real de un vehículo o
|
|
2288
|
+
* técnico. Genérico a propósito (no `isVehicle` ni ningún nombre atado al
|
|
2289
|
+
* dominio de un portal): `@kaufmann-lab/ui` no sabe qué es un "vehículo",
|
|
2290
|
+
* solo que ALGÚN marker es el que se sigue. Si más de un marker lo marca,
|
|
2291
|
+
* se sigue el primero en `markers()`. Distinto de la geolocalización del
|
|
2292
|
+
* propio dispositivo (`navigator.geolocation`, fuera de alcance): esto
|
|
2293
|
+
* sigue un dato que el consumidor ya tiene, no ubica al usuario.
|
|
2294
|
+
*/
|
|
2295
|
+
live?: boolean;
|
|
2296
|
+
/**
|
|
2297
|
+
* Mini-card que aparece al hover/foco de este marker — solo si el marker
|
|
2298
|
+
* tiene `id` (ver JSDoc de arriba): sin `id` el marker no es clickeable ni
|
|
2299
|
+
* enfocable, así que no habría forma de revelarla por teclado.
|
|
2300
|
+
*
|
|
2301
|
+
* `actions` son botones REALES (no el viejo `ctaLabel` de texto plano sin
|
|
2302
|
+
* acción): cada click emite `markerAction` con su `key` — nunca el
|
|
2303
|
+
* `label`, que es solo texto de UI y puede repetirse con significados
|
|
2304
|
+
* distintos entre consumidores — y detiene la propagación del click para
|
|
2305
|
+
* no disparar además `markerSelect` sobre el marker completo (ver
|
|
2306
|
+
* `buildMarkerPopup()`).
|
|
2307
|
+
*
|
|
2308
|
+
* Vocabulario genérico a propósito (`title`/`description`/`details`/
|
|
2309
|
+
* `actions`, no `folio`/`cliente`/`verTicket`): esta librería no sabe qué
|
|
2310
|
+
* es un "ticket" — el caso de uso real (folio+cliente+estado+SLA+acciones)
|
|
2311
|
+
* es responsabilidad de quien arma cada `MapMarker`.
|
|
2312
|
+
*/
|
|
2313
|
+
popup?: {
|
|
2314
|
+
title: string;
|
|
2315
|
+
description?: string;
|
|
2316
|
+
/** Pares clave/valor extra (ej. estado + SLA) — se listan debajo de `description`. */
|
|
2317
|
+
details?: {
|
|
2318
|
+
label: string;
|
|
2319
|
+
value: string;
|
|
2320
|
+
}[];
|
|
2321
|
+
/** Botones de acción reales — ver JSDoc de `MapPopupAction`. */
|
|
2322
|
+
actions?: MapPopupAction[];
|
|
2323
|
+
};
|
|
2324
|
+
}
|
|
2325
|
+
/**
|
|
2326
|
+
* Mapa con motor real de Mapbox GL JS — decisión de producto ya cerrada
|
|
2327
|
+
* (comparación de 6 proveedores). El access token vive a nivel app, no por
|
|
2328
|
+
* uso: ver `provideMapConfig` en `map-config.ts` (mismo criterio que
|
|
2329
|
+
* `API_CONFIG` de `packages/api-client`), inyectado una vez en
|
|
2330
|
+
* `app.config.ts` de cada app consumidora.
|
|
2331
|
+
*
|
|
2332
|
+
* `size` (`sm|md|lg` alturas fijas, `full` llena el contenedor padre) NO
|
|
2333
|
+
* hace step-down por viewport como `kf-page-header`: ese mecanismo existe
|
|
2334
|
+
* ahí porque un tamaño de FUENTE fijo se corta en pantallas chicas; una
|
|
2335
|
+
* caja de mapa no tiene ese problema — el canvas de Mapbox ya es
|
|
2336
|
+
* responsive dentro de cualquier tamaño de contenedor. Lo que sí hace
|
|
2337
|
+
* falta (y que el header no necesita) es un `ResizeObserver` real: Mapbox
|
|
2338
|
+
* no se entera solo de que su contenedor cambió de tamaño (un layout que
|
|
2339
|
+
* colapsa un sidebar, un cambio de orientación) — hay que llamar
|
|
2340
|
+
* `map.resize()` a mano cuando el contenedor cambia (mismo motivo por el
|
|
2341
|
+
* que `fullscreenchange` también llama `resize()`, ver `onFullscreenChange`).
|
|
2342
|
+
*
|
|
2343
|
+
* `variant` es un eje ortogonal a `size` (mismo criterio que `variant` +
|
|
2344
|
+
* `tone` en `kf-badge`): `interactive` trae la posibilidad de controles
|
|
2345
|
+
* flotantes reales (ver `controls`); `preview` (`interactive: false` de
|
|
2346
|
+
* Mapbox GL) es para una card de lista, sin chrome y sin foco por teclado —
|
|
2347
|
+
* no hace nada al recibir foco, así que no debe formar parte del tab order,
|
|
2348
|
+
* y por eso NUNCA renderiza `controls` aunque el consumidor los pida:
|
|
2349
|
+
* togglear "seguir" o abrir pantalla completa no tiene sentido en un mapa
|
|
2350
|
+
* que no acepta interacción. Ninguna de las dos variantes oculta la
|
|
2351
|
+
* atribución: es requisito de licencia de Mapbox, no cosmético.
|
|
2352
|
+
*
|
|
2353
|
+
* `theme` (`light|dark|streets|satellite|outdoors`) resuelve el estilo real
|
|
2354
|
+
* de Mapbox — eje totalmente distinto del `[data-theme]` de
|
|
2355
|
+
* `tokens/themes/*` (paleta de marca de la librería): ninguno de los 5
|
|
2356
|
+
* temas de `tokens/` es oscuro todavía, así que no hay ninguna señal real
|
|
2357
|
+
* de la que derivar un default "según el tema de marca activo" — construir
|
|
2358
|
+
* esa detección ahora sería simular una señal que no existe.
|
|
2359
|
+
*
|
|
2360
|
+
* `controls` (`MapControlKind[]`, default `[]`) es siempre opt-in: un mapa
|
|
2361
|
+
* sin `controls` no dibuja ningún botón flotante, ni siquiera en
|
|
2362
|
+
* `interactive` (antes de esta versión, `interactive` agregaba el
|
|
2363
|
+
* `NavigationControl` default de Mapbox gratis — se sacó a propósito: era
|
|
2364
|
+
* el look genérico de Mapbox, no el de esta librería, exactamente la queja
|
|
2365
|
+
* que motivó este rediseño. Un consumidor que quería esos botones de zoom
|
|
2366
|
+
* ahora los pide con `[controls]="['zoom']"`). Cada kind es una pieza
|
|
2367
|
+
* independiente entre sí (ver `MapControls.ts` para el detalle de cada
|
|
2368
|
+
* una) — agregar una no reacomoda ni depende de las demás.
|
|
2369
|
+
*
|
|
2370
|
+
* <kf-map label="Ubicación del vehículo y del destino" [markers]="markers()" />
|
|
2371
|
+
* <kf-map size="sm" variant="preview" label="Vista previa de la ruta" [route]="route()" />
|
|
2372
|
+
* <kf-map label="Flota completa" [markers]="fleet()" [controls]="['zoom', 'recenter', 'cluster', 'follow']" />
|
|
2373
|
+
*/
|
|
2374
|
+
declare class MapView {
|
|
2375
|
+
readonly size: _angular_core.InputSignal<MapSize>;
|
|
2376
|
+
readonly variant: _angular_core.InputSignal<MapVariant>;
|
|
2377
|
+
readonly markers: _angular_core.InputSignal<MapMarker[]>;
|
|
2378
|
+
readonly route: _angular_core.InputSignal<MapLatLng[] | undefined>;
|
|
2379
|
+
readonly fitToMarkers: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
2380
|
+
readonly center: _angular_core.InputSignal<MapLatLng | undefined>;
|
|
2381
|
+
readonly zoom: _angular_core.InputSignal<number | undefined>;
|
|
2382
|
+
/**
|
|
2383
|
+
* Default `'streets'`, no `'light'` ni `'dark'`. `'dark'` se probó
|
|
2384
|
+
* primero por coherencia con la paleta navy de marca, pero una
|
|
2385
|
+
* verificación visual real sobre una ubicación de servicio típica (una
|
|
2386
|
+
* faena/zona rural, no un centro urbano denso) mostró el problema real:
|
|
2387
|
+
* `dark-v11` casi no tiene datos que pintar fuera de ciudades grandes —
|
|
2388
|
+
* el mapa se ve prácticamente todo negro, ilegible, exactamente donde
|
|
2389
|
+
* más importa que se lea bien (ubicaciones de terreno, no de oficina).
|
|
2390
|
+
* `'streets'` SIEMPRE tiene cartografía base visible (calles, nombres,
|
|
2391
|
+
* uso de suelo) sin importar la densidad del lugar — es el único de los
|
|
2392
|
+
* 5 estilos sin ese riesgo. Sigue siendo overrideable por el consumidor. */
|
|
2393
|
+
readonly theme: _angular_core.InputSignal<MapTheme>;
|
|
2394
|
+
/** Controles flotantes a mostrar — ver JSDoc de la clase y `MapControls.ts`. */
|
|
2395
|
+
readonly controls: _angular_core.InputSignal<MapControlKind[]>;
|
|
2396
|
+
/**
|
|
2397
|
+
* Descripción accesible real del mapa — el `<div>` donde Mapbox renderiza
|
|
2398
|
+
* su canvas queda vacío de semántica para un lector de pantalla. Requerido
|
|
2399
|
+
* a propósito: un default genérico ("Mapa") no dice qué está mostrando
|
|
2400
|
+
* ESTE mapa; cada consumidor conoce su propio caso real (ej. "Ubicación
|
|
2401
|
+
* del vehículo y del destino").
|
|
2402
|
+
*/
|
|
2403
|
+
readonly label: _angular_core.InputSignal<string>;
|
|
2404
|
+
/** Se emite al hacer click sobre un marker que declaró `id` (ver JSDoc de
|
|
2405
|
+
* `MapMarker.id`) — un marker SIN `id` no es clickeable, no emite nada.
|
|
2406
|
+
* No dispara en modo `cluster` (los markers pasan a ser capas WebGL sin
|
|
2407
|
+
* DOM propio donde colgar un listener) ni en `variant="preview"` (el
|
|
2408
|
+
* mapa entero no es interactivo ahí). */
|
|
2409
|
+
readonly markerSelect: _angular_core.OutputEmitterRef<string>;
|
|
2410
|
+
/** Se emite al clickear un botón de `MapMarker.popup.actions` — ver el
|
|
2411
|
+
* JSDoc de `popup` para por qué reporta `actionKey` (nunca el `label`) y
|
|
2412
|
+
* por qué el click no dispara además `markerSelect`. */
|
|
2413
|
+
readonly markerAction: _angular_core.OutputEmitterRef<{
|
|
2414
|
+
markerId: string;
|
|
2415
|
+
actionKey: string;
|
|
2416
|
+
}>;
|
|
2417
|
+
private readonly mapConfig;
|
|
2418
|
+
private readonly environmentInjector;
|
|
2419
|
+
private readonly document;
|
|
2420
|
+
private readonly destroyRef;
|
|
2421
|
+
private readonly hostElementRef;
|
|
2422
|
+
private readonly containerRef;
|
|
2423
|
+
/** `preview` no es foco-navegable ni se anuncia como región interactiva. */
|
|
2424
|
+
protected readonly isPreview: _angular_core.Signal<boolean>;
|
|
2425
|
+
protected readonly containerRole: _angular_core.Signal<"img" | "region">;
|
|
2426
|
+
/** Ver estado de carga/error real, más abajo (`status`). */
|
|
2427
|
+
protected readonly status: _angular_core.WritableSignal<"error" | "ready" | "loading">;
|
|
2428
|
+
/** Elección manual del consumidor final vía el control `mapStyle` — pisa
|
|
2429
|
+
* `theme()` de forma sostenida (no vuelve a `theme()` sola) hasta que se
|
|
2430
|
+
* destruya este `kf-map`; ver `resolvedTheme()`. */
|
|
2431
|
+
private readonly userTheme;
|
|
2432
|
+
protected readonly styleLabel: _angular_core.Signal<string>;
|
|
2433
|
+
/** Igual que `resolvedTheme()` pero público para el template — lo necesita
|
|
2434
|
+
* `MapThemePicker` (vía `MapControls`) para marcar la opción activa. */
|
|
2435
|
+
protected readonly activeTheme: _angular_core.Signal<MapTheme>;
|
|
2436
|
+
protected readonly clusterActive: _angular_core.WritableSignal<boolean>;
|
|
2437
|
+
protected readonly followActive: _angular_core.WritableSignal<boolean>;
|
|
2438
|
+
protected readonly hasLiveMarker: _angular_core.Signal<boolean>;
|
|
2439
|
+
protected readonly isFullscreen: _angular_core.WritableSignal<boolean>;
|
|
2440
|
+
/** Sin estilo de marca propio (Mapbox Studio) no hay nada que ciclar con
|
|
2441
|
+
* `mapStyle` — se filtra en vez de dejar un botón que no cambiaría nada. */
|
|
2442
|
+
protected readonly effectiveControls: _angular_core.Signal<MapControlKind[]>;
|
|
2443
|
+
protected readonly showControls: _angular_core.Signal<boolean>;
|
|
2444
|
+
private map;
|
|
2445
|
+
/** `extraIconRefs`: íconos de las acciones del popup (ver `buildMarkerPopup()`)
|
|
2446
|
+
* — se crean con `createComponent()` igual que `iconRef` (el pin), así que
|
|
2447
|
+
* necesitan el mismo `.destroy()` en `clearMarkers()`. */
|
|
2448
|
+
private markerHandles;
|
|
2449
|
+
/** Markers DOM reales sobre cada cluster real (punto 1, a11y) — separados
|
|
2450
|
+
* de `markerHandles` porque se reconstruyen en su propio ciclo (ver
|
|
2451
|
+
* `syncClusterMarkers()`) y no tienen `iconRef` propio (sin `kf-icon`
|
|
2452
|
+
* adentro, ver JSDoc de `addClusterMarkerToMap()`). */
|
|
2453
|
+
private clusterMarkerHandles;
|
|
2454
|
+
private resizeObserver;
|
|
2455
|
+
private currentStyle;
|
|
2456
|
+
private mapReady;
|
|
2457
|
+
/** Popup resumen de un cluster real (ver `renderClusterPopup()`) — a
|
|
2458
|
+
* diferencia del popup de marker individual (siempre en el DOM, mostrado
|
|
2459
|
+
* por CSS `:hover`/`:focus-within`), este se crea/destruye a demanda: no
|
|
2460
|
+
* cuelga de ningún elemento con estado hover propio, lo abre un click
|
|
2461
|
+
* sobre una capa WebGL sin DOM. */
|
|
2462
|
+
private clusterPopupElement;
|
|
2463
|
+
private clusterPopupIconRefs;
|
|
2464
|
+
/** Coordenadas del cluster abierto — necesarias para volver a proyectar
|
|
2465
|
+
* contra pantalla en cada `'move'` (ver `repositionClusterPopup()`); el
|
|
2466
|
+
* cluster en sí no se mueve, pero su posición EN PANTALLA sí cuando se
|
|
2467
|
+
* hace pan. */
|
|
2468
|
+
private clusterPopupCoordinates;
|
|
2469
|
+
private readonly onFullscreenChange;
|
|
2470
|
+
constructor();
|
|
2471
|
+
/**
|
|
2472
|
+
* El bundle default de `mapbox-gl` arma su Web Worker serializando el
|
|
2473
|
+
* código del worker como string y cargándolo por Blob URL — un bundler
|
|
2474
|
+
* ESM-nativo (esbuild, el builder `application` de Angular) minifica ESE
|
|
2475
|
+
* string también, sin saber que corre verbatim en otro contexto, y el
|
|
2476
|
+
* worker termina con un identificador roto en runtime
|
|
2477
|
+
* (`ReferenceError: <letra> is not defined`, ver mapbox/mapbox-gl-js#13700
|
|
2478
|
+
* y angular/angular-cli#27378). `workerClass` reemplaza ese mecanismo por
|
|
2479
|
+
* un `Worker` real armado con el patrón literal
|
|
2480
|
+
* `new Worker(new URL(..., import.meta.url), { type: 'module' })` — el
|
|
2481
|
+
* único que esbuild reconoce de forma ESTÁTICA y separa en su propio
|
|
2482
|
+
* chunk (esbuild lo detecta por análisis sintáctico puro, dondequiera que
|
|
2483
|
+
* el literal aparezca en el archivo — no hace falta que esté a nivel de
|
|
2484
|
+
* módulo). `mapbox-gl-csp.worker.js` es un asset copiado tal cual, nunca
|
|
2485
|
+
* TypeScript compilado, así que `import.meta.url` sigue apuntando a un
|
|
2486
|
+
* archivo real en cualquiera de los dos contextos donde esbuild procesa
|
|
2487
|
+
* este literal: en `ng test` (bundlea el `.ts` fuente tal cual, el asset
|
|
2488
|
+
* está al lado en `src/lib/map/`) y en el build real de un consumidor
|
|
2489
|
+
* (bundlea el FESM publicado — `ng-package.json` copia este mismo asset a
|
|
2490
|
+
* `dist/ui/fesm2022/`, junto al bundle aplanado, con esa carpeta EXACTA a
|
|
2491
|
+
* propósito). Ambos casos comparten la misma ruta relativa
|
|
2492
|
+
* (`./mapbox-gl-csp.worker.js`) porque el asset se coloca deliberadamente
|
|
2493
|
+
* al lado del consumidor en los dos. Si un cambio de Angular Package
|
|
2494
|
+
* Format renombra `fesm2022`, hay que actualizar el `output` en
|
|
2495
|
+
* `ng-package.json` a la par.
|
|
2496
|
+
*
|
|
2497
|
+
* A propósito NO es un side-effect de módulo (no corre con solo
|
|
2498
|
+
* `import`ar este archivo): un `<kf-map>` real en jsdom construye un
|
|
2499
|
+
* `Worker` real vía esta misma expresión, y jsdom no lo soporta — revienta
|
|
2500
|
+
* el proceso de Vitest entero, no solo el test, y se lleva puesto
|
|
2501
|
+
* CUALQUIER spec que solo importe algo del barrel de la librería (nada
|
|
2502
|
+
* que ver con mapas). Por eso vive acá, dentro de `initializeMap()`, que
|
|
2503
|
+
* únicamente corre cuando alguien de verdad instancia y renderiza este
|
|
2504
|
+
* componente — `MapView.spec.ts` mockea `mapbox-gl` entero, así que ahí
|
|
2505
|
+
* la asignación cae sobre el mock, no sobre el `mapboxgl` real.
|
|
2506
|
+
*/
|
|
2507
|
+
private assignWorkerClass;
|
|
2508
|
+
private initializeMap;
|
|
2509
|
+
private resolvedTheme;
|
|
2510
|
+
private resolvedStyle;
|
|
2511
|
+
private toLngLat;
|
|
2512
|
+
/**
|
|
2513
|
+
* `matchMedia` (no una media query CSS): esta preferencia decide la
|
|
2514
|
+
* `duration` que se le pasa a la API de cámara de Mapbox, que corre en
|
|
2515
|
+
* JS/WebGL — CSS no tiene nada que animar acá. `document.defaultView` (no
|
|
2516
|
+
* el `window` global) para que quede mockeable en test igual que el resto
|
|
2517
|
+
* de la clase usa `DOCUMENT` inyectado.
|
|
2518
|
+
*/
|
|
2519
|
+
private prefersReducedMotion;
|
|
2520
|
+
/** Colapsa cualquier duración a 0 (instantáneo, sin animar) si el usuario
|
|
2521
|
+
* pidió `prefers-reduced-motion: reduce` — un único punto de verdad para
|
|
2522
|
+
* que ningún movimiento de cámara se salga de esa preferencia. */
|
|
2523
|
+
private cameraDuration;
|
|
2524
|
+
private syncCamera;
|
|
2525
|
+
/**
|
|
2526
|
+
* Encuadra `bounds` con `fitBounds()`, salvo que sea una caja degenerada
|
|
2527
|
+
* (ver `DEGENERATE_BOUNDS_THRESHOLD_DEG`) — un solo punto total (un único
|
|
2528
|
+
* marker sin ruta, o vehículo+destino prácticamente en el mismo sitio) le
|
|
2529
|
+
* da a `fitBounds()` un ancho y alto ~cero, que Mapbox resuelve con un
|
|
2530
|
+
* `padding`/zoom que en la práctica deja el mapa mayormente en blanco
|
|
2531
|
+
* ("se ve todo blanco", el bug real que esto corrige). Un `easeTo` con
|
|
2532
|
+
* zoom fijo sensato muestra contexto real de calle en ese caso, animado
|
|
2533
|
+
* igual que el resto (antes era un `jumpTo` seco).
|
|
2534
|
+
*/
|
|
2535
|
+
private frameBounds;
|
|
2536
|
+
private isDegenerateBounds;
|
|
2537
|
+
/**
|
|
2538
|
+
* Extiende sobre markers Y route: el nombre del input sigue el caso de
|
|
2539
|
+
* uso principal (encuadrar los markers), pero un mapa con solo `route` y
|
|
2540
|
+
* sin `markers` tampoco debería quedar en el encuadre default inútil.
|
|
2541
|
+
*/
|
|
2542
|
+
private computeBounds;
|
|
2543
|
+
private syncMarkers;
|
|
2544
|
+
/**
|
|
2545
|
+
* Crea el marker de Mapbox (DOM + `kf-icon` + wiring de click/teclado/
|
|
2546
|
+
* popup) para UN `MapMarker` y lo agrega a `markerHandles` — compartido
|
|
2547
|
+
* entre el modo normal (`syncMarkers()`, todos los markers) y el modo
|
|
2548
|
+
* cluster (`syncClusterMarkers()`, solo los que quedan sueltos): un punto
|
|
2549
|
+
* suelto en modo cluster tiene que verse y comportarse EXACTAMENTE igual
|
|
2550
|
+
* que en modo normal, así que no puede haber dos implementaciones.
|
|
2551
|
+
*/
|
|
2552
|
+
private addMarkerToMap;
|
|
2553
|
+
/**
|
|
2554
|
+
* Mini-card hija del `<div>` del marker — mismo patrón que
|
|
2555
|
+
* `.kf-map-controls__label` (ver `MapControls.css`): un elemento SIEMPRE
|
|
2556
|
+
* presente en el DOM, oculto por defecto vía `opacity`/`pointer-events` en
|
|
2557
|
+
* `MapView.css` y revelado por CSS puro en `:hover`/`:focus-within` del
|
|
2558
|
+
* marker padre — sin JS de show/hide, y sin `mapboxgl.Popup` (evita una
|
|
2559
|
+
* segunda gestión de overlay/posicionamiento cuando el CSS ya alcanza acá,
|
|
2560
|
+
* igual que ya decidió `.kf-map-controls__label`).
|
|
2561
|
+
*
|
|
2562
|
+
* Todo el subárbol (título, descripción, `details`, `actions`) se arma con
|
|
2563
|
+
* `document.createElement` puro, nunca `kf-text`/`kf-button`: este marker
|
|
2564
|
+
* entero vive fuera del template de Angular (ver el comentario grande de
|
|
2565
|
+
* `ViewEncapsulation.None` más arriba) — meter un componente real acá
|
|
2566
|
+
* significaría un `createComponent()` propio por cada línea de texto, el
|
|
2567
|
+
* mismo costo que ya paga `kf-icon` en `syncMarkers()` pero sin ninguna
|
|
2568
|
+
* necesidad real (no hay nada dinámico que un componente resuelva mejor
|
|
2569
|
+
* que texto plano estilado por clase). Es la MISMA decisión que ya regía
|
|
2570
|
+
* para `title`/`description` antes de esta ronda — se extiende acá para no
|
|
2571
|
+
* mezclar dos convenciones distintas dentro del mismo fragmento de DOM.
|
|
2572
|
+
*
|
|
2573
|
+
* `actions` sí necesita wiring propio (a diferencia del resto de la card,
|
|
2574
|
+
* que burbujea su click al listener ya cableado sobre `element` en
|
|
2575
|
+
* `syncMarkers()`): cada botón corta esa burbuja con `stopPropagation()`
|
|
2576
|
+
* para reportar `markerAction` en vez de (además) `markerSelect`.
|
|
2577
|
+
*
|
|
2578
|
+
* Jerarquía visual de `actions` (ver JSDoc de `MapPopupAction`): la
|
|
2579
|
+
* PRIMERA acción del array es la principal (`--primary`, fill); el resto
|
|
2580
|
+
* es secundaria (`--ghost`, transparente) — mismo peso que ya usan
|
|
2581
|
+
* `kf-button[variant=primary]`/`[variant=ghost]` en el resto de la
|
|
2582
|
+
* librería, sin inventar un esquema de color nuevo acá.
|
|
2583
|
+
*
|
|
2584
|
+
* El punto de color junto al título (`kf-map__marker-popup-tone`) reusa
|
|
2585
|
+
* el `tone` del propio marker — no el de cada `details` (que son pares
|
|
2586
|
+
* label/value genéricos, sin un campo "cuál de estos es el estado"): es
|
|
2587
|
+
* la misma señal que YA pinta el círculo del marker en el mapa, mostrada
|
|
2588
|
+
* de nuevo acá para que el popup se lea sin tener que mirar el marker.
|
|
2589
|
+
*/
|
|
2590
|
+
private buildMarkerPopup;
|
|
2591
|
+
private clearMarkers;
|
|
2592
|
+
/**
|
|
2593
|
+
* Recorte de borde para el popup de marker individual (punto 2) —
|
|
2594
|
+
* `getBoundingClientRect()` del marker y del propio contenedor del mapa,
|
|
2595
|
+
* recalculado en el evento que dispara la apertura (`mouseenter`/
|
|
2596
|
+
* `focusin`, ver `addMarkerToMap()`). El mecanismo de mostrar/ocultar
|
|
2597
|
+
* sigue siendo 100% CSS; esto solo decide 3 clases modificadoras que
|
|
2598
|
+
* `MapView.css` traduce en alineación horizontal/vertical:
|
|
2599
|
+
*
|
|
2600
|
+
* `--align-start` — el popup se desborda por la izquierda del
|
|
2601
|
+
* contenedor si se centra sobre el marker: se alinea su borde
|
|
2602
|
+
* izquierdo con el del marker en vez de centrarlo.
|
|
2603
|
+
* `--align-end` — mismo caso, contra el borde derecho.
|
|
2604
|
+
* `--below` — no entra arriba del marker (cerca del borde superior del
|
|
2605
|
+
* contenedor): se muestra debajo en vez de arriba.
|
|
2606
|
+
*
|
|
2607
|
+
* El popup YA está en el DOM en este punto (oculto por `opacity`, no por
|
|
2608
|
+
* `display:none` — ver `MapView.css`), así que `getBoundingClientRect()`
|
|
2609
|
+
* devuelve su tamaño real aunque todavía no sea visible.
|
|
2610
|
+
*/
|
|
2611
|
+
private positionMarkerPopup;
|
|
2612
|
+
/**
|
|
2613
|
+
* Clustering nativo de Mapbox (`cluster: true` en la fuente GeoJSON, no un
|
|
2614
|
+
* algoritmo armado a mano) — 2 capas, solo para clusters REALES (2+
|
|
2615
|
+
* puntos agrupados, `has: point_count`): círculo agregado y su label de
|
|
2616
|
+
* cantidad. Un punto que Mapbox deja suelto (sin `point_count`) YA NO
|
|
2617
|
+
* tiene capa WebGL propia — ver `syncClusterMarkers()`, que lo renderiza
|
|
2618
|
+
* como el mismo marker DOM de siempre (con ícono/tono/popup reales, cosa
|
|
2619
|
+
* que una capa `circle` no puede mostrar).
|
|
2620
|
+
*/
|
|
2621
|
+
private syncClusterSource;
|
|
2622
|
+
/**
|
|
2623
|
+
* Contraparte en DOM de la capa WebGL: mientras esa capa solo dibuja
|
|
2624
|
+
* clusters reales, ESTE método decide qué puntos quedan sueltos al zoom
|
|
2625
|
+
* ACTUAL (`querySourceFeatures` con el mismo filtro `!has(point_count)`
|
|
2626
|
+
* que ya usa Mapbox para separar cluster de leaf) y les arma el mismo
|
|
2627
|
+
* marker DOM que tendrían sin clustering — vía `addMarkerToMap()`, la
|
|
2628
|
+
* MISMA función que usa el modo normal, no una copia.
|
|
2629
|
+
*
|
|
2630
|
+
* `querySourceFeatures` puede devolver una feature repetida si cae cerca
|
|
2631
|
+
* de un borde interno de tile — de ahí el `Set` sobre `index` (la
|
|
2632
|
+
* property que identifica al `MapMarker` de origen, ver
|
|
2633
|
+
* `markersToFeatureCollection()`), no una lista con posibles duplicados.
|
|
2634
|
+
* Mismo motivo (y mismo `Set`, esta vez sobre `cluster_id`) para los
|
|
2635
|
+
* clusters reales que se resuelven acá abajo.
|
|
2636
|
+
*
|
|
2637
|
+
* Se llama solo cuando `clusterActive()` — ver los listeners `'data'`/
|
|
2638
|
+
* `'moveend'` en `initializeMap()` sobre CUÁNDO se llama esto.
|
|
2639
|
+
*/
|
|
2640
|
+
private syncClusterMarkers;
|
|
2641
|
+
/**
|
|
2642
|
+
* Marker DOM real sobre un cluster (punto 1, a11y) — MISMO patrón de
|
|
2643
|
+
* wiring que `addMarkerToMap()` (`role="button"`, `tabindex="0"`,
|
|
2644
|
+
* `click`+`keydown` de Enter/Space), aplicado acá porque el círculo que
|
|
2645
|
+
* pinta `CLUSTER_CIRCLE_LAYER_ID` es una capa WebGL sin nodo DOM propio
|
|
2646
|
+
* —inalcanzable por teclado (WCAG SC 2.1.1 Keyboard). `mapboxgl.Marker`
|
|
2647
|
+
* posiciona y reproyecta este `<div>` solo en cada pan/zoom, igual que ya
|
|
2648
|
+
* hace con cualquier marker individual — no hace falta reposicionarlo a
|
|
2649
|
+
* mano como sí necesita el popup resumen (ver `repositionClusterPopup()`).
|
|
2650
|
+
*
|
|
2651
|
+
* Sin `kf-icon`/texto propio adentro a propósito: el círculo+conteo que ya
|
|
2652
|
+
* pinta la capa WebGL sigue siendo la representación visual (mismo
|
|
2653
|
+
* `'circle-radius'`, ver `clusterMarkerRadiusPx()`); este `<div>` es
|
|
2654
|
+
* transparente y se superpone exacto encima, aportando SOLO la
|
|
2655
|
+
* interactividad real que la capa WebGL no puede tener.
|
|
2656
|
+
*/
|
|
2657
|
+
private addClusterMarkerToMap;
|
|
2658
|
+
private clearClusterMarkers;
|
|
2659
|
+
/**
|
|
2660
|
+
* Activación de un cluster real (click O Enter/Space sobre su marker DOM,
|
|
2661
|
+
* ver `addClusterMarkerToMap()`): ANTES hacía zoom directo
|
|
2662
|
+
* (`getClusterExpansionZoom()` + `easeTo`) — ahora abre el popup resumen
|
|
2663
|
+
* (`renderClusterPopup()`) con la lista de tickets agrupados, y ESE zoom
|
|
2664
|
+
* pasa a ser la acción explícita "Acercar para separar" dentro del popup
|
|
2665
|
+
* (ver `expandCluster()`).
|
|
2666
|
+
*/
|
|
2667
|
+
private activateCluster;
|
|
2668
|
+
/** Dispara el zoom que antes hacía el click directo sobre el círculo —
|
|
2669
|
+
* ahora vive detrás del botón "Acercar para separar" del popup resumen. */
|
|
2670
|
+
private expandCluster;
|
|
2671
|
+
/**
|
|
2672
|
+
* `index` identifica de vuelta al `MapMarker` de origen (ver
|
|
2673
|
+
* `syncClusterMarkers()`); `title`/`description`/`markerId` son las
|
|
2674
|
+
* mismas properties que ya arma `buildMarkerPopup()` (folio/cliente/id
|
|
2675
|
+
* del caso real) — se serializan acá TAMBIÉN para que
|
|
2676
|
+
* `getClusterLeaves()` pueda listarlas en el popup resumen sin tener que
|
|
2677
|
+
* ir a buscar cada `MapMarker` original por separado. Los campos son
|
|
2678
|
+
* `string` (no `undefined`): las properties de una `Feature` de GeoJSON
|
|
2679
|
+
* no viajan bien como `undefined` — `''` significa "sin dato" en los 3.
|
|
2680
|
+
*/
|
|
2681
|
+
private markersToFeatureCollection;
|
|
2682
|
+
private clearClusterLayers;
|
|
2683
|
+
/**
|
|
2684
|
+
* Popup resumen de un cluster real (punto 4) — a diferencia del popup de
|
|
2685
|
+
* marker individual (siempre en el DOM, `:hover`/`:focus-within` puro),
|
|
2686
|
+
* este no cuelga de ningún elemento con estado hover propio: lo abre un
|
|
2687
|
+
* click sobre una capa WebGL sin DOM, así que se crea/destruye a demanda
|
|
2688
|
+
* y su visibilidad la maneja JS, no CSS. Se agrega como hijo del HOST
|
|
2689
|
+
* (`kf-map`, no `.kf-map__container`): ambos comparten exactamente la
|
|
2690
|
+
* misma caja (`.kf-map__container` es `width/height:100%` sin padding/
|
|
2691
|
+
* borde dentro de un host `position:relative`), así que las coordenadas
|
|
2692
|
+
* de `map.project()` (relativas al contenedor) valen también acá — mismo
|
|
2693
|
+
* ancestro posicionado que ya usa `.kf-map__overlay`.
|
|
2694
|
+
*
|
|
2695
|
+
* `title`/`description` de cada leaf son `popup.title`/`popup.description`
|
|
2696
|
+
* del `MapMarker` original (folio/cliente en el caso real) — ver
|
|
2697
|
+
* `markersToFeatureCollection()`. El punto de color reusa `tone`, MISMO
|
|
2698
|
+
* criterio que el punto de color del popup individual (ver JSDoc de
|
|
2699
|
+
* `buildMarkerPopup()`): no se inventa un texto de "estado" que la
|
|
2700
|
+
* librería no tiene, se reusa la señal de color que sí existe.
|
|
2701
|
+
*/
|
|
2702
|
+
private renderClusterPopup;
|
|
2703
|
+
/** Reproyecta el popup de cluster contra pantalla — se llama en cada
|
|
2704
|
+
* `'move'` (ver `initializeMap()`): el punto geográfico no se mueve, pero
|
|
2705
|
+
* su posición EN PANTALLA sí cuando se hace pan. Mismo criterio de
|
|
2706
|
+
* recorte de borde que `positionMarkerPopup()` (punto 2), pero en
|
|
2707
|
+
* píxeles calculados a mano en vez de clases CSS: este popup no cuelga
|
|
2708
|
+
* de un ancestro posicionado por CSS relativo al marker, su posición
|
|
2709
|
+
* ENTERA viene de `map.project()`. */
|
|
2710
|
+
private repositionClusterPopup;
|
|
2711
|
+
private closeClusterPopup;
|
|
2712
|
+
private syncRoute;
|
|
2713
|
+
private resolveColorToken;
|
|
2714
|
+
protected handleZoomIn(): void;
|
|
2715
|
+
protected handleZoomOut(): void;
|
|
2716
|
+
/** Reencuadra a demanda, ignorando `fitToMarkers()` (tiene sentido pedirlo
|
|
2717
|
+
* justo cuando `fitToMarkers` está en `false`) y sin apagar `follow`: si
|
|
2718
|
+
* está activo, la próxima posición nueva vuelve a pegar la cámara al
|
|
2719
|
+
* marker `live` — este reencuadre es una corrección puntual, no un modo. */
|
|
2720
|
+
protected handleRecenter(): void;
|
|
2721
|
+
protected handleFollowToggle(): void;
|
|
2722
|
+
protected handleClusterToggle(): void;
|
|
2723
|
+
/** Reemplaza al viejo ciclado de un click (`handleStyleCycle`): la
|
|
2724
|
+
* elección real llega desde `MapThemePicker`, vía `MapControls`. */
|
|
2725
|
+
protected handleThemeSelect(theme: MapTheme): void;
|
|
2726
|
+
protected handleFullscreenToggle(): void;
|
|
2727
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<MapView, never>;
|
|
2728
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<MapView, "kf-map", never, { "size": { "alias": "size"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "markers": { "alias": "markers"; "required": false; "isSignal": true; }; "route": { "alias": "route"; "required": false; "isSignal": true; }; "fitToMarkers": { "alias": "fitToMarkers"; "required": false; "isSignal": true; }; "center": { "alias": "center"; "required": false; "isSignal": true; }; "zoom": { "alias": "zoom"; "required": false; "isSignal": true; }; "theme": { "alias": "theme"; "required": false; "isSignal": true; }; "controls": { "alias": "controls"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": true; "isSignal": true; }; }, { "markerSelect": "markerSelect"; "markerAction": "markerAction"; }, never, never, true, never>;
|
|
2729
|
+
}
|
|
2730
|
+
|
|
2731
|
+
/**
|
|
2732
|
+
* Signal reactivo con el estado actual de un media query — para cuando un
|
|
2733
|
+
* componente necesita SABER en qué lado de un breakpoint está (no solo
|
|
2734
|
+
* mostrar/ocultar con CSS). Un `@media` en `styles` no alcanza cuando la
|
|
2735
|
+
* decisión no es puramente visual: por ejemplo, un componente hijo que
|
|
2736
|
+
* recibe un input booleano (`[collapsed]`) no puede "leer" un breakpoint
|
|
2737
|
+
* CSS del padre — necesita que alguien en TypeScript se lo diga.
|
|
2738
|
+
*
|
|
2739
|
+
* Encontrado al construir `SidebarShell`: el estado `collapsed` (colapso
|
|
2740
|
+
* del rail en desktop) se pasaba tal cual a cada `kf-sidebar-nav-item`, sin
|
|
2741
|
+
* importar el viewport. En mobile, donde el rail deslizado siempre muestra
|
|
2742
|
+
* etiquetas completas (ver estilos de `SidebarShell`), los ítems seguían
|
|
2743
|
+
* ocultando su label si el usuario había dejado el rail colapsado en
|
|
2744
|
+
* desktop antes de achicar la ventana — un CSS que dice "en mobile se ve
|
|
2745
|
+
* ancho" no cambia el valor del input que ya llegó al hijo.
|
|
2746
|
+
*
|
|
2747
|
+
* Debe llamarse dentro de un contexto de inyección (el `constructor` de un
|
|
2748
|
+
* componente, o un campo de clase inicializado ahí — mismo requisito que
|
|
2749
|
+
* cualquier función que use `inject()`).
|
|
2750
|
+
*
|
|
2751
|
+
* protected readonly isMobile = matchMediaSignal('(max-width: 900px)');
|
|
2752
|
+
* // ...
|
|
2753
|
+
* [collapsed]="collapsed() && !isMobile()"
|
|
2754
|
+
*/
|
|
2755
|
+
declare function matchMediaSignal(query: string): Signal<boolean>;
|
|
2756
|
+
|
|
1331
2757
|
/**
|
|
1332
2758
|
* Card de estadística/KPI: label, valor grande, ícono opcional en la esquina
|
|
1333
2759
|
* superior derecha, badge de esquina opcional (pegado al valor) y una nota
|
|
@@ -1338,8 +2764,17 @@ declare class Link {
|
|
|
1338
2764
|
* en toda la librería, tal como pide el design system (página 14: "Card...
|
|
1339
2765
|
* es la base de paneles, tarjetas de métrica, tablas...").
|
|
1340
2766
|
*
|
|
2767
|
+
* Slot `[actions]` opcional (proyectado al pie, después de `foot`) para un
|
|
2768
|
+
* CTA dentro de la card — ej. "Saldo Pendiente" con botón "Pagar Ahora" en
|
|
2769
|
+
* Fase 6 de OTD. Mismo mecanismo de proyección por selector que `[footer]`
|
|
2770
|
+
* en `kf-modal`, no un `<ng-content>` default: así una card sin acciones no
|
|
2771
|
+
* paga ningún espacio extra.
|
|
2772
|
+
*
|
|
1341
2773
|
* <kf-metric-card label="Tickets abiertos" value="11" icon="ticket" foot="+2 hoy" />
|
|
1342
2774
|
* <kf-metric-card label="Tasa de resolución" value="94%" badge="+2%" value-color="var(--kf-color-success)" />
|
|
2775
|
+
* <kf-metric-card label="Saldo Pendiente" value="$45,000">
|
|
2776
|
+
* <kf-button actions variant="primary" size="sm">Pagar Ahora</kf-button>
|
|
2777
|
+
* </kf-metric-card>
|
|
1343
2778
|
*/
|
|
1344
2779
|
declare class MetricCard {
|
|
1345
2780
|
readonly label: _angular_core.InputSignal<string>;
|
|
@@ -1353,7 +2788,57 @@ declare class MetricCard {
|
|
|
1353
2788
|
/** Badge de esquina opcional, pegado al valor (ej. "+2%"). */
|
|
1354
2789
|
readonly badge: _angular_core.InputSignal<string | undefined>;
|
|
1355
2790
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<MetricCard, never>;
|
|
1356
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<MetricCard, "kf-metric-card", never, { "label": { "alias": "label"; "required": true; "isSignal": true; }; "value": { "alias": "value"; "required": true; "isSignal": true; }; "unit": { "alias": "unit"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "foot": { "alias": "foot"; "required": false; "isSignal": true; }; "valueColor": { "alias": "valueColor"; "required": false; "isSignal": true; }; "badge": { "alias": "badge"; "required": false; "isSignal": true; }; }, {}, never,
|
|
2791
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<MetricCard, "kf-metric-card", never, { "label": { "alias": "label"; "required": true; "isSignal": true; }; "value": { "alias": "value"; "required": true; "isSignal": true; }; "unit": { "alias": "unit"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "foot": { "alias": "foot"; "required": false; "isSignal": true; }; "valueColor": { "alias": "valueColor"; "required": false; "isSignal": true; }; "badge": { "alias": "badge"; "required": false; "isSignal": true; }; }, {}, never, ["[actions]"], true, never>;
|
|
2792
|
+
}
|
|
2793
|
+
|
|
2794
|
+
/**
|
|
2795
|
+
* Franja fija en la base de una pantalla completa para apps mobile del
|
|
2796
|
+
* ecosistema: encapsula el padding con `safe-area-inset-bottom` (para no
|
|
2797
|
+
* invadir la zona del home indicator), el borde superior y el fondo —
|
|
2798
|
+
* cualquier acción, chip de estado o hint que necesite vivir pegado al
|
|
2799
|
+
* borde inferior del dispositivo se proyecta acá vía `<ng-content>`.
|
|
2800
|
+
*
|
|
2801
|
+
* <kf-mobile-footer>
|
|
2802
|
+
* <kf-button>Cerrar orden</kf-button>
|
|
2803
|
+
* </kf-mobile-footer>
|
|
2804
|
+
*/
|
|
2805
|
+
declare class MobileFooter {
|
|
2806
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<MobileFooter, never>;
|
|
2807
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<MobileFooter, "kf-mobile-footer", never, {}, {}, never, ["*"], true, never>;
|
|
2808
|
+
}
|
|
2809
|
+
|
|
2810
|
+
/**
|
|
2811
|
+
* Encabezado de pantalla completa (fondo navy) para apps mobile del
|
|
2812
|
+
* ecosistema — cubre tanto la raíz de un tab (sin volver) como el detalle
|
|
2813
|
+
* de un flujo (con volver).
|
|
2814
|
+
*
|
|
2815
|
+
* <kf-mobile-header title="Mis asignaciones" />
|
|
2816
|
+
*
|
|
2817
|
+
* <kf-mobile-header title="STAR-20841" subtitle="Cerrado hoy · 15:18" [showBack]="true" (back)="onBack()" />
|
|
2818
|
+
*
|
|
2819
|
+
* Sin `kf-text` para `eyebrow`/`title`/`subtitle`: su `tone` es un set
|
|
2820
|
+
* cerrado sin blanco/inverso, y sus variantes fijan `color` explícito sobre
|
|
2821
|
+
* el elemento interno — un `color: white` heredado desde afuera nunca le
|
|
2822
|
+
* gana a esa declaración directa. Markup propio con los tokens reales.
|
|
2823
|
+
*
|
|
2824
|
+
* El botón "volver" tampoco es `kf-button`: su `variant="ghost"` fija el
|
|
2825
|
+
* mismo token de color que este fondo navy (el ícono quedaría invisible).
|
|
2826
|
+
*/
|
|
2827
|
+
declare class MobileHeader {
|
|
2828
|
+
readonly title: _angular_core.InputSignal<string>;
|
|
2829
|
+
/** Línea chica y translúcida ARRIBA del título — ej. un folio o un saludo. */
|
|
2830
|
+
readonly eyebrow: _angular_core.InputSignal<string | undefined>;
|
|
2831
|
+
/** `true` cuando `eyebrow` es un código (folio) — usa tipografía monoespaciada. */
|
|
2832
|
+
readonly eyebrowMono: _angular_core.InputSignal<boolean>;
|
|
2833
|
+
/** Línea chica y translúcida DEBAJO del título — ej. "Cerrado hoy · 15:18". */
|
|
2834
|
+
readonly subtitle: _angular_core.InputSignal<string | undefined>;
|
|
2835
|
+
readonly showBack: _angular_core.InputSignal<boolean>;
|
|
2836
|
+
/** El componente no navega por sí solo: cada pantalla decide qué significa
|
|
2837
|
+
* "volver" (`Location.back()`, retroceder un paso del flujo, etc.). */
|
|
2838
|
+
readonly back: _angular_core.OutputEmitterRef<void>;
|
|
2839
|
+
protected onBackClick(): void;
|
|
2840
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<MobileHeader, never>;
|
|
2841
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<MobileHeader, "kf-mobile-header", never, { "title": { "alias": "title"; "required": true; "isSignal": true; }; "eyebrow": { "alias": "eyebrow"; "required": false; "isSignal": true; }; "eyebrowMono": { "alias": "eyebrowMono"; "required": false; "isSignal": true; }; "subtitle": { "alias": "subtitle"; "required": false; "isSignal": true; }; "showBack": { "alias": "showBack"; "required": false; "isSignal": true; }; }, { "back": "back"; }, never, ["[kf-mobile-header-trailing]"], true, never>;
|
|
1357
2842
|
}
|
|
1358
2843
|
|
|
1359
2844
|
/**
|
|
@@ -1369,146 +2854,79 @@ declare class MetricCard {
|
|
|
1369
2854
|
* destructiva está en vuelo) se cancelan `Escape` y el clic en el backdrop:
|
|
1370
2855
|
* solo cierra el consumidor poniendo `open` en `false`.
|
|
1371
2856
|
*
|
|
1372
|
-
* Las acciones van en el slot `footer
|
|
2857
|
+
* Las acciones van en el slot `footer` — el atributo `footer` se pone
|
|
2858
|
+
* DIRECTO en cada `kf-button`, sin envolverlos en un `<div footer>`:
|
|
2859
|
+
* `.kf-modal__footer` ya es `display:flex` con `gap` propio entre sus
|
|
2860
|
+
* hijos DIRECTOS. Envolver los botones en un `<div>` intermedio dejaría
|
|
2861
|
+
* ese `<div>` como único hijo del footer — el gap nunca llegaría a
|
|
2862
|
+
* separar los botones entre sí (bug real encontrado en `web-otd-frontend`:
|
|
2863
|
+
* los 2 botones quedaban pegados, con 0px de separación medida, pese a que
|
|
2864
|
+
* el CSS del footer declaraba `gap: var(--kf-space-md)`).
|
|
1373
2865
|
*
|
|
1374
2866
|
* <kf-modal [(open)]="confirmOpen" title="Eliminar unidad">
|
|
1375
2867
|
* <p>Esta acción no se puede deshacer.</p>
|
|
1376
|
-
* <
|
|
1377
|
-
*
|
|
1378
|
-
* <kf-button (click)="remove()">Eliminar</kf-button>
|
|
1379
|
-
* </div>
|
|
2868
|
+
* <kf-button footer variant="ghost" (click)="confirmOpen.set(false)">Cancelar</kf-button>
|
|
2869
|
+
* <kf-button footer (click)="remove()">Eliminar</kf-button>
|
|
1380
2870
|
* </kf-modal>
|
|
1381
|
-
*/
|
|
1382
|
-
declare class Modal {
|
|
1383
|
-
readonly title: _angular_core.InputSignal<string>;
|
|
1384
|
-
/** Visibilidad, two-way: `[(open)]="confirmOpen"`. */
|
|
1385
|
-
readonly open: _angular_core.ModelSignal<boolean>;
|
|
1386
|
-
/** Permite cerrar con el botón, `Escape` o clic en el backdrop. */
|
|
1387
|
-
readonly dismissible: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
1388
|
-
protected readonly titleId: string;
|
|
1389
|
-
private readonly dialog;
|
|
1390
|
-
private readonly document;
|
|
1391
|
-
constructor();
|
|
1392
|
-
/** `Escape` dispara `cancel`; cancelarlo lo deja abierto. */
|
|
1393
|
-
protected onCancel(event: Event): void;
|
|
1394
|
-
/**
|
|
1395
|
-
* Clic en el backdrop. Un clic en la zona del backdrop tiene como `target` al
|
|
1396
|
-
* propio `<dialog>` (el contenido vive en el panel de adentro), así que basta
|
|
1397
|
-
* comparar contra él — no hace falta cortar la propagación en el panel.
|
|
1398
|
-
*
|
|
1399
|
-
* Va como HostListener y no como binding en el template porque un `(click)`
|
|
1400
|
-
* sobre el `<dialog>` dispara las reglas a11y de click-sin-teclado; acá el
|
|
1401
|
-
* equivalente de teclado es `Escape`, que el `<dialog>` nativo ya maneja.
|
|
1402
|
-
*/
|
|
1403
|
-
protected onHostClick(event: MouseEvent): void;
|
|
1404
|
-
static ɵfac: _angular_core.ɵɵFactoryDeclaration<Modal, never>;
|
|
1405
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<Modal, "kf-modal", never, { "title": { "alias": "title"; "required": false; "isSignal": true; }; "open": { "alias": "open"; "required": false; "isSignal": true; }; "dismissible": { "alias": "dismissible"; "required": false; "isSignal": true; }; }, { "open": "openChange"; }, never, ["*", "[footer]"], true, never>;
|
|
1406
|
-
}
|
|
1407
|
-
|
|
1408
|
-
interface MultiSelectOption {
|
|
1409
|
-
value: string;
|
|
1410
|
-
label: string;
|
|
1411
|
-
}
|
|
1412
|
-
/**
|
|
1413
|
-
* Dropdown de selección múltiple con checkboxes internos. El panel flotante
|
|
1414
|
-
* se conecta al trigger vía `@angular/cdk/overlay` — el mismo mecanismo que
|
|
1415
|
-
* `Select` (ver `../select/Select.ts`) y `Dropdown` (`../dropdown/Dropdown.ts`).
|
|
1416
|
-
*
|
|
1417
|
-
* Hasta la `0.1.9` este panel vivía con `position: absolute` manual sobre el
|
|
1418
|
-
* propio host, sin depender del CDK Overlay. Se migró para unificar el
|
|
1419
|
-
* mecanismo de posicionamiento/cierre con el resto de los dropdowns flotantes
|
|
1420
|
-
* de la librería (4 posiciones de fallback, `withViewportMargin`, `withPush`,
|
|
1421
|
-
* cierre por `outsidePointerEvents()`/`keydownEvents()` en vez de un
|
|
1422
|
-
* `@HostListener('document:click')` manual).
|
|
1423
2871
|
*
|
|
1424
|
-
*
|
|
1425
|
-
*
|
|
1426
|
-
*
|
|
1427
|
-
*
|
|
1428
|
-
*
|
|
1429
|
-
*
|
|
1430
|
-
* queda siempre visible y sigue actuando sobre el universo COMPLETO de
|
|
1431
|
-
* `options()`, no sobre el subconjunto filtrado (ver JSDoc de `toggleAll()`).
|
|
1432
|
-
*
|
|
1433
|
-
* <kf-multi-select
|
|
1434
|
-
* label="Sucursales"
|
|
1435
|
-
* [options]="branches"
|
|
1436
|
-
* [values]="selectedBranches()"
|
|
1437
|
-
* (valuesChange)="selectedBranches.set($event)"
|
|
1438
|
-
* />
|
|
2872
|
+
* El footer agrupa todo a la derecha por defecto (`justify-content:
|
|
2873
|
+
* flex-end`) — para una acción secundaria/informativa que debe quedar
|
|
2874
|
+
* separada a la izquierda (ej. un link "Ver documento" junto a un botón
|
|
2875
|
+
* "Cerrar"), agregar la clase `kf-modal__footer-start` AL MISMO elemento
|
|
2876
|
+
* que ya lleva `footer`, no a un wrapper (mismo motivo que el `footer`
|
|
2877
|
+
* directo: un `<div>` intermedio rompe el `gap`):
|
|
1439
2878
|
*
|
|
1440
|
-
*
|
|
1441
|
-
*
|
|
1442
|
-
* componente no usa iconos, así que no aplica acá.
|
|
2879
|
+
* <kf-link footer class="kf-modal__footer-start" disabled>Ver documento</kf-link>
|
|
2880
|
+
* <kf-button footer (click)="open.set(false)">Cerrar</kf-button>
|
|
1443
2881
|
*/
|
|
1444
|
-
declare class
|
|
1445
|
-
readonly
|
|
1446
|
-
|
|
1447
|
-
readonly
|
|
1448
|
-
|
|
1449
|
-
readonly
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
* sobre el umbral automático. Mismo criterio que `Select`.
|
|
1455
|
-
*/
|
|
1456
|
-
readonly searchable: _angular_core.InputSignal<boolean | undefined>;
|
|
1457
|
-
readonly searchThreshold: _angular_core.InputSignal<number>;
|
|
1458
|
-
readonly valuesChange: _angular_core.OutputEmitterRef<string[]>;
|
|
1459
|
-
private readonly instanceId;
|
|
1460
|
-
readonly triggerId: string;
|
|
1461
|
-
readonly listboxId: string;
|
|
1462
|
-
readonly isOpen: _angular_core.WritableSignal<boolean>;
|
|
1463
|
-
readonly searchQuery: _angular_core.WritableSignal<string>;
|
|
1464
|
-
readonly activeIndex: _angular_core.WritableSignal<number>;
|
|
1465
|
-
readonly selectedCount: _angular_core.Signal<number>;
|
|
1466
|
-
readonly allSelected: _angular_core.Signal<boolean>;
|
|
1467
|
-
readonly isPartiallySelected: _angular_core.Signal<boolean>;
|
|
1468
|
-
readonly showSearch: _angular_core.Signal<boolean>;
|
|
1469
|
-
/** Opciones visibles tras aplicar el filtro de búsqueda ("Todas" nunca se filtra). */
|
|
1470
|
-
readonly filteredOptions: _angular_core.Signal<MultiSelectOption[]>;
|
|
1471
|
-
/**
|
|
1472
|
-
* Texto del disparador: placeholder si no hay selección, o los nombres
|
|
1473
|
-
* seleccionados unidos por coma (el conteo va aparte, en el badge). Con
|
|
1474
|
-
* muchas opciones el `text-overflow: ellipsis` del `.kf-multi-select__value`
|
|
1475
|
-
* corta el texto — el badge de conteo siempre queda visible al lado.
|
|
1476
|
-
*/
|
|
1477
|
-
readonly summaryText: _angular_core.Signal<string>;
|
|
1478
|
-
private readonly triggerRef;
|
|
1479
|
-
private readonly panelTemplate;
|
|
1480
|
-
private readonly searchInputRef;
|
|
1481
|
-
private readonly overlayService;
|
|
1482
|
-
private readonly viewContainerRef;
|
|
1483
|
-
private readonly destroyRef;
|
|
1484
|
-
private overlayRef;
|
|
2882
|
+
declare class Modal {
|
|
2883
|
+
readonly title: _angular_core.InputSignal<string>;
|
|
2884
|
+
/** Visibilidad, two-way: `[(open)]="confirmOpen"`. */
|
|
2885
|
+
readonly open: _angular_core.ModelSignal<boolean>;
|
|
2886
|
+
/** Permite cerrar con el botón, `Escape` o clic en el backdrop. */
|
|
2887
|
+
readonly dismissible: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
2888
|
+
protected readonly titleId: string;
|
|
2889
|
+
private readonly dialog;
|
|
2890
|
+
private readonly document;
|
|
2891
|
+
private readonly overlayContainer;
|
|
1485
2892
|
constructor();
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
toggleOption(value: string): void;
|
|
2893
|
+
/** `Escape` dispara `cancel`; cancelarlo lo deja abierto. */
|
|
2894
|
+
protected onCancel(event: Event): void;
|
|
1489
2895
|
/**
|
|
1490
|
-
*
|
|
1491
|
-
*
|
|
1492
|
-
*
|
|
1493
|
-
*
|
|
1494
|
-
*
|
|
1495
|
-
*
|
|
2896
|
+
* Clic en el backdrop. Un clic en la zona del backdrop tiene como `target` al
|
|
2897
|
+
* propio `<dialog>` (el contenido vive en el panel de adentro), así que basta
|
|
2898
|
+
* comparar contra él — no hace falta cortar la propagación en el panel.
|
|
2899
|
+
*
|
|
2900
|
+
* Va como HostListener y no como binding en el template porque un `(click)`
|
|
2901
|
+
* sobre el `<dialog>` dispara las reglas a11y de click-sin-teclado; acá el
|
|
2902
|
+
* equivalente de teclado es `Escape`, que el `<dialog>` nativo ya maneja.
|
|
1496
2903
|
*/
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
2904
|
+
protected onHostClick(event: MouseEvent): void;
|
|
2905
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<Modal, never>;
|
|
2906
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<Modal, "kf-modal", never, { "title": { "alias": "title"; "required": false; "isSignal": true; }; "open": { "alias": "open"; "required": false; "isSignal": true; }; "dismissible": { "alias": "dismissible"; "required": false; "isSignal": true; }; }, { "open": "openChange"; }, never, ["*", "[footer]"], true, never>;
|
|
2907
|
+
}
|
|
2908
|
+
|
|
2909
|
+
/** Color de la franja izquierda y del fondo. */
|
|
2910
|
+
type NoteBlockTone = 'neutral' | 'ok' | 'warn' | 'error' | 'info';
|
|
2911
|
+
/**
|
|
2912
|
+
* Bloque de texto atribuido a una PERSONA: encabezado, prosa y firma al pie,
|
|
2913
|
+
* con fondo suave y franja de color — la anatomía de una cita, distinta de
|
|
2914
|
+
* `kf-alert` (que interrumpe el flujo con `role="alert"`). El texto va como
|
|
2915
|
+
* contenido proyectado y respeta los saltos de línea del autor.
|
|
2916
|
+
*/
|
|
2917
|
+
declare class NoteBlock {
|
|
2918
|
+
readonly title: _angular_core.InputSignal<string | undefined>;
|
|
2919
|
+
/** Segunda pieza del encabezado, en la misma línea del `title`. */
|
|
2920
|
+
readonly subtitle: _angular_core.InputSignal<string | undefined>;
|
|
2921
|
+
/** Pie de atribución — quién escribió y cuándo, ya formateado. */
|
|
2922
|
+
readonly signature: _angular_core.InputSignal<string | undefined>;
|
|
2923
|
+
readonly tone: _angular_core.InputSignal<NoteBlockTone>;
|
|
2924
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<NoteBlock, never>;
|
|
2925
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<NoteBlock, "kf-note-block", never, { "title": { "alias": "title"; "required": false; "isSignal": true; }; "subtitle": { "alias": "subtitle"; "required": false; "isSignal": true; }; "signature": { "alias": "signature"; "required": false; "isSignal": true; }; "tone": { "alias": "tone"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
|
|
1508
2926
|
}
|
|
1509
2927
|
|
|
1510
2928
|
type TextAs = 'h1' | 'h2' | 'h3' | 'p' | 'span';
|
|
1511
|
-
type TextVariant = 'title-xxl' | 'title-xl' | 'title-lg' | 'title-md' | 'title-sm' | 'body' | 'body-muted' | 'caption';
|
|
2929
|
+
type TextVariant = 'title-xxl' | 'title-xl' | 'title-lg' | 'title-md' | 'title-sm' | 'body' | 'body-muted' | 'caption' | 'overline' | 'overline-strong';
|
|
1512
2930
|
type TextTone = 'neutral' | 'ok' | 'warn' | 'error' | 'info';
|
|
1513
2931
|
/**
|
|
1514
2932
|
* Tipografía unificada — separa la etiqueta semántica (`as`, qué es el
|
|
@@ -1529,9 +2947,20 @@ type TextTone = 'neutral' | 'ok' | 'warn' | 'error' | 'info';
|
|
|
1529
2947
|
* títulos protagonistas de pantalla completa (ej. bienvenida del home vía
|
|
1530
2948
|
* `kf-page-header[size=xl|xxl]`) — ver tokens/base.css.
|
|
1531
2949
|
*
|
|
2950
|
+
* `overline`/`overline-strong` son para el patrón "texto chico en mayúsculas
|
|
2951
|
+
* junto a un dato" (ej. el label "RUC" de `kf-company-selector`, el badge de
|
|
2952
|
+
* `kf-admin-badge`) — antes de estas variantes, ese patrón se reimplementaba
|
|
2953
|
+
* suelto en al menos 7 componentes con tamaños/pesos ligeramente distintos
|
|
2954
|
+
* entre sí. `overline` (peso 600) es para labels sin chrome propio;
|
|
2955
|
+
* `overline-strong` (peso 700) es para el texto de un badge/pill que ya trae
|
|
2956
|
+
* su propio fondo. Ninguna de las dos fija color — como el resto de los
|
|
2957
|
+
* variants, el color se resuelve con `tone`.
|
|
2958
|
+
*
|
|
1532
2959
|
* <kf-text as="h1" variant="title-lg">Bienvenido de nuevo</kf-text>
|
|
1533
2960
|
* <kf-text as="p" variant="body-muted">Ingresá con tu cuenta corporativa.</kf-text>
|
|
1534
2961
|
* <kf-text as="span" variant="caption" tone="error">Campo obligatorio</kf-text>
|
|
2962
|
+
* <kf-text as="span" variant="overline">RUC</kf-text>
|
|
2963
|
+
* <kf-text as="span" variant="overline-strong" tone="warn">Vista administrador</kf-text>
|
|
1535
2964
|
*/
|
|
1536
2965
|
declare class Text {
|
|
1537
2966
|
/** Etiqueta semántica real que se renderiza — nunca es solo un `div`. */
|
|
@@ -1551,17 +2980,16 @@ declare class Text {
|
|
|
1551
2980
|
}
|
|
1552
2981
|
|
|
1553
2982
|
/**
|
|
1554
|
-
* Tamaño del título
|
|
1555
|
-
*
|
|
1556
|
-
*
|
|
1557
|
-
*
|
|
2983
|
+
* Tamaño del título. Cada valor mapea 1:1 a una variante `title-*` de
|
|
2984
|
+
* `kf-text`; `xl`/`xxl` son para títulos protagonistas (ej. bienvenida del
|
|
2985
|
+
* home). Bajo los 900px, el tamaño efectivo baja un escalón (ver
|
|
2986
|
+
* `MOBILE_STEP_DOWN`).
|
|
1558
2987
|
*/
|
|
1559
2988
|
type PageHeaderTitleSize = 'sm' | 'md' | 'lg' | 'xl' | 'xxl';
|
|
1560
2989
|
/**
|
|
1561
2990
|
* Bloque de título al tope de una página: chip de ícono opcional, título (h1),
|
|
1562
2991
|
* subtítulo opcional y un área de acciones opcional proyectada vía
|
|
1563
|
-
* `<ng-content select="[actions]"
|
|
1564
|
-
* fijo de botones.
|
|
2992
|
+
* `<ng-content select="[actions]">`.
|
|
1565
2993
|
*
|
|
1566
2994
|
* <kf-page-header title="Vehículos" subtitle="Gestión de la flota activa">
|
|
1567
2995
|
* <button actions>Nuevo vehículo</button>
|
|
@@ -1576,7 +3004,8 @@ declare class PageHeader {
|
|
|
1576
3004
|
readonly title: _angular_core.InputSignal<string>;
|
|
1577
3005
|
readonly subtitle: _angular_core.InputSignal<string | undefined>;
|
|
1578
3006
|
readonly size: _angular_core.InputSignal<PageHeaderTitleSize>;
|
|
1579
|
-
|
|
3007
|
+
private readonly isMobile;
|
|
3008
|
+
protected readonly resolvedTitleVariant: _angular_core.Signal<TextVariant>;
|
|
1580
3009
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<PageHeader, never>;
|
|
1581
3010
|
static ɵcmp: _angular_core.ɵɵComponentDeclaration<PageHeader, "kf-page-header", never, { "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": true; "isSignal": true; }; "subtitle": { "alias": "subtitle"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; }, {}, never, ["[actions]"], true, never>;
|
|
1582
3011
|
}
|
|
@@ -1603,16 +3032,47 @@ type PaginationItem = {
|
|
|
1603
3032
|
* <kf-pagination [page]="page" [pages]="totalPages" (pageChange)="page = $event">
|
|
1604
3033
|
* 21–40 de 273
|
|
1605
3034
|
* </kf-pagination>
|
|
3035
|
+
*
|
|
3036
|
+
* Con `pageSizeOptions`, agrega un select de "N por página" entre el texto
|
|
3037
|
+
* informativo y los controles de navegación — el consumidor decide el
|
|
3038
|
+
* tamaño real vía `pageSize`/`pageSizeChange` (two-way opcional, ninguno de
|
|
3039
|
+
* los dos es requerido si no se usa el selector):
|
|
3040
|
+
*
|
|
3041
|
+
* <kf-pagination [page]="page" [pages]="totalPages" (pageChange)="page = $event"
|
|
3042
|
+
* [pageSizeOptions]="[10, 25, 50]" [(pageSize)]="pageSize">
|
|
3043
|
+
* 21–40 de 273
|
|
3044
|
+
* </kf-pagination>
|
|
1606
3045
|
*/
|
|
1607
3046
|
declare class Pagination {
|
|
1608
3047
|
readonly page: _angular_core.InputSignal<number>;
|
|
1609
3048
|
readonly pages: _angular_core.InputSignal<number>;
|
|
1610
3049
|
readonly pageChange: _angular_core.OutputEmitterRef<number>;
|
|
3050
|
+
/** Opciones del selector "N por página" — sin esto, el selector no se
|
|
3051
|
+
* renderiza (mismo criterio que `[footer]`/`[actions]` de otros
|
|
3052
|
+
* componentes: ausente por default, nunca forzado). `kf-select` en
|
|
3053
|
+
* `variant="compact"` no impone un máximo de opciones, a diferencia de
|
|
3054
|
+
* `kf-segmented` (pensado solo para 2-3). */
|
|
3055
|
+
readonly pageSizeOptions: _angular_core.InputSignal<number[]>;
|
|
3056
|
+
readonly pageSize: _angular_core.ModelSignal<number | null>;
|
|
1611
3057
|
/** Lista de números de página y separadores a renderizar, ya resuelta por el algoritmo de elipsis. */
|
|
1612
3058
|
readonly items: _angular_core.Signal<PaginationItem[]>;
|
|
3059
|
+
/** Con una sola página (o ninguna), el `<nav>` de prev/next+números no
|
|
3060
|
+
* aporta nada navegable — antes se dejaba en el DOM igual (oculto solo
|
|
3061
|
+
* por un hack `::ng-deep` del consumidor, ver `route-assistance-monorepo`)
|
|
3062
|
+
* y el footer quedaba con el selector "Filas" descoordinado, como si le
|
|
3063
|
+
* faltara una pieza. Sacar el `<nav>` del DOM (no `display:none`) resuelve
|
|
3064
|
+
* esto para cualquier consumidor de `kf-pagination`, no solo para quien ya
|
|
3065
|
+
* tenía el hack. El `divider` reusa el mismo criterio: sin `nav` no hay
|
|
3066
|
+
* nada del otro lado que separar, así que tampoco corresponde dejarlo. */
|
|
3067
|
+
protected readonly hasNav: _angular_core.Signal<boolean>;
|
|
3068
|
+
/** `kf-select` trabaja con `value: string` — cada tamaño numérico se
|
|
3069
|
+
* mapea a su representación en texto, ida y vuelta. */
|
|
3070
|
+
protected readonly pageSizeSelectOptions: _angular_core.Signal<string[]>;
|
|
3071
|
+
protected readonly pageSizeValue: _angular_core.Signal<string>;
|
|
3072
|
+
protected setPageSize(value: string): void;
|
|
1613
3073
|
goTo(target: number): void;
|
|
1614
3074
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<Pagination, never>;
|
|
1615
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<Pagination, "kf-pagination", never, { "page": { "alias": "page"; "required": false; "isSignal": true; }; "pages": { "alias": "pages"; "required": false; "isSignal": true; }; }, { "pageChange": "pageChange"; }, never, ["*"], true, never>;
|
|
3075
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<Pagination, "kf-pagination", never, { "page": { "alias": "page"; "required": false; "isSignal": true; }; "pages": { "alias": "pages"; "required": false; "isSignal": true; }; "pageSizeOptions": { "alias": "pageSizeOptions"; "required": false; "isSignal": true; }; "pageSize": { "alias": "pageSize"; "required": false; "isSignal": true; }; }, { "pageChange": "pageChange"; "pageSize": "pageSizeChange"; }, never, ["*"], true, never>;
|
|
1616
3076
|
}
|
|
1617
3077
|
|
|
1618
3078
|
/**
|
|
@@ -1683,22 +3143,20 @@ interface PortalHeaderAdminAction {
|
|
|
1683
3143
|
* `Drawer.ts`) que `kf-drawer-session` espera como fondo (no trae uno
|
|
1684
3144
|
* propio, ver `DrawerSession.ts`).
|
|
1685
3145
|
* - `<ng-content select="[kf-portal-header-footer]">`: acciones separadas del
|
|
1686
|
-
* resto (ej. `kf-drawer-logout`). Cae en `[kf-drawer-footer]` de
|
|
1687
|
-
* `kf-drawer`, mismo criterio de "no reimplementar nada del drawer": el
|
|
1688
|
-
* footer ya trae su propio padding/separador (ver `Drawer.ts`).
|
|
3146
|
+
* resto (ej. `kf-drawer-logout`). Cae en `[kf-drawer-footer]` de `kf-drawer`.
|
|
1689
3147
|
*
|
|
1690
|
-
* `PortalHeader` no sabe qué es ninguno de los dos contenidos
|
|
1691
|
-
*
|
|
1692
|
-
* `kf-drawer` resuelve overlay con blur, cierre con click-afuera/`Escape`,
|
|
1693
|
-
* atrapado de foco real y devolución de foco al cerrar — `PortalHeader` no
|
|
1694
|
-
* reimplementa nada de eso, solo controla su `open` desde el botón de
|
|
1695
|
-
* hamburguesa.
|
|
3148
|
+
* `PortalHeader` no sabe qué es ninguno de los dos contenidos, solo les da
|
|
3149
|
+
* el lugar correcto dentro del drawer ya desplegado (ver `Drawer.ts`).
|
|
1696
3150
|
*
|
|
1697
3151
|
* <kf-portal-header
|
|
1698
3152
|
* [isAdmin]="true"
|
|
1699
3153
|
* [adminActions]="[{ label: 'Consultar deuda clientes', href: '/deuda' }, { label: 'Cambiar DNI', href: '/dni' }]"
|
|
1700
3154
|
* [navItems]="[{ label: 'Home', href: '/', active: true, icon: 'grid' }, { label: 'Vehículos', href: '/vehiculos', icon: 'truck' }]"
|
|
3155
|
+
* tagline="Portal privado"
|
|
1701
3156
|
* userInitials="TS"
|
|
3157
|
+
* userName="Tomás Soto"
|
|
3158
|
+
* [showCart]="true"
|
|
3159
|
+
* (cartClick)="onCartClick()"
|
|
1702
3160
|
* >
|
|
1703
3161
|
* <kf-drawer-session
|
|
1704
3162
|
* kf-portal-header-extra
|
|
@@ -1711,11 +3169,25 @@ interface PortalHeaderAdminAction {
|
|
|
1711
3169
|
*/
|
|
1712
3170
|
declare class PortalHeader {
|
|
1713
3171
|
readonly brand: _angular_core.InputSignal<string>;
|
|
3172
|
+
/** URL del logo como imagen. Sin ella (default), se mantiene el texto de `brand`. */
|
|
3173
|
+
readonly logoSrc: _angular_core.InputSignal<string | undefined>;
|
|
3174
|
+
/** `alt` del logo cuando se usa `logoSrc`. Sin ella, cae en `brand()`. */
|
|
3175
|
+
readonly logoAlt: _angular_core.InputSignal<string | undefined>;
|
|
1714
3176
|
readonly isAdmin: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
1715
3177
|
readonly adminActions: _angular_core.InputSignal<PortalHeaderAdminAction[]>;
|
|
1716
3178
|
readonly navItems: _angular_core.InputSignal<PortalHeaderNavItem[]>;
|
|
1717
3179
|
readonly userInitials: _angular_core.InputSignal<string>;
|
|
3180
|
+
/** Texto junto al logo (ej. "Portal privado"), con separador vertical. Sin
|
|
3181
|
+
* separador ni texto si queda vacío. */
|
|
3182
|
+
readonly tagline: _angular_core.InputSignal<string>;
|
|
3183
|
+
/** Nombre completo para el bloque "HOLA / {nombre}" dentro de la pastilla
|
|
3184
|
+
* de sesión. Vacío (default) mantiene la pastilla como hoy: solo avatar,
|
|
3185
|
+
* sin bloque de texto ni chevron — retrocompatible con cualquier
|
|
3186
|
+
* consumidor que no pase este input. */
|
|
3187
|
+
readonly userName: _angular_core.InputSignal<string>;
|
|
3188
|
+
readonly showCart: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
1718
3189
|
readonly sessionClick: _angular_core.OutputEmitterRef<void>;
|
|
3190
|
+
readonly cartClick: _angular_core.OutputEmitterRef<void>;
|
|
1719
3191
|
/** Visibilidad del drawer de navegación mobile (bajo los 768px). Se pasa a
|
|
1720
3192
|
* `kf-drawer` como `[(open)]` — `PortalHeader` ya no implementa overlay,
|
|
1721
3193
|
* `Escape` ni atrapado de foco por su cuenta, eso vive en `Drawer`. */
|
|
@@ -1731,8 +3203,276 @@ declare class PortalHeader {
|
|
|
1731
3203
|
* <kf-dropdown [(open)]="menuOpen" [anchor]="header().sessionButtonRef()">
|
|
1732
3204
|
*/
|
|
1733
3205
|
readonly sessionButtonRef: _angular_core.Signal<ElementRef<any>>;
|
|
3206
|
+
/**
|
|
3207
|
+
* Ancla real de cada `<a>` del `<nav>` horizontal (una por `navItems()`,
|
|
3208
|
+
* mismo orden — `@for` trackea por `item.label`, así que la lista no se
|
|
3209
|
+
* recrea en cada cambio de ruta, solo cambia qué índice trae
|
|
3210
|
+
* `active: true`). Sirve para medir dónde poner la cápsula deslizante
|
|
3211
|
+
* (`thumbStyle`, ver abajo) sin duplicar el layout del nav en JS.
|
|
3212
|
+
*/
|
|
3213
|
+
private readonly navItemAnchors;
|
|
3214
|
+
/** Fuerza recalcular `thumbStyle` en un resize — un resize de ventana no
|
|
3215
|
+
* cambia ningún signal de los que `thumbStyle` ya lee (`navItems`,
|
|
3216
|
+
* `navItemAnchors`), pero sí puede correr el layout de los `<a>` bajo un
|
|
3217
|
+
* ancho de columna distinto (texto largo pasando a otra línea, etc.). */
|
|
3218
|
+
private readonly resizeTick;
|
|
3219
|
+
protected onWindowResize(): void;
|
|
3220
|
+
/**
|
|
3221
|
+
* Posición/ancho de la cápsula blanca del ítem activo del `<nav>`, como
|
|
3222
|
+
* un elemento propio (`.kf-portal-header__nav-thumb`) que se desliza con
|
|
3223
|
+
* `transition` en vez de que cada `<a>` traiga su propio fondo blanco
|
|
3224
|
+
* (eso hacía que el fondo "saltara" de un ítem a otro sin animación,
|
|
3225
|
+
* porque son elementos DOM distintos — no hay nada que Angular pueda
|
|
3226
|
+
* animar entre ellos).
|
|
3227
|
+
*/
|
|
3228
|
+
protected readonly thumbStyle: _angular_core.Signal<Record<string, string>>;
|
|
1734
3229
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<PortalHeader, never>;
|
|
1735
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<PortalHeader, "kf-portal-header", never, { "brand": { "alias": "brand"; "required": false; "isSignal": true; }; "isAdmin": { "alias": "isAdmin"; "required": false; "isSignal": true; }; "adminActions": { "alias": "adminActions"; "required": false; "isSignal": true; }; "navItems": { "alias": "navItems"; "required": false; "isSignal": true; }; "userInitials": { "alias": "userInitials"; "required": true; "isSignal": true; }; }, { "sessionClick": "sessionClick"; }, never, ["[kf-portal-header-extra]", "[kf-portal-header-footer]"], true, never>;
|
|
3230
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<PortalHeader, "kf-portal-header", never, { "brand": { "alias": "brand"; "required": false; "isSignal": true; }; "logoSrc": { "alias": "logoSrc"; "required": false; "isSignal": true; }; "logoAlt": { "alias": "logoAlt"; "required": false; "isSignal": true; }; "isAdmin": { "alias": "isAdmin"; "required": false; "isSignal": true; }; "adminActions": { "alias": "adminActions"; "required": false; "isSignal": true; }; "navItems": { "alias": "navItems"; "required": false; "isSignal": true; }; "userInitials": { "alias": "userInitials"; "required": true; "isSignal": true; }; "tagline": { "alias": "tagline"; "required": false; "isSignal": true; }; "userName": { "alias": "userName"; "required": false; "isSignal": true; }; "showCart": { "alias": "showCart"; "required": false; "isSignal": true; }; }, { "sessionClick": "sessionClick"; "cartClick": "cartClick"; }, never, ["[kf-portal-header-extra]", "[kf-portal-header-footer]"], true, never>;
|
|
3231
|
+
}
|
|
3232
|
+
|
|
3233
|
+
/** Mismo vocabulario que `Badge.tone`/`TextTone` (menos `info`/`service`,
|
|
3234
|
+
* que no aplican a un valor de progreso): `neutral` es el color de marca
|
|
3235
|
+
* por defecto (sin implicar buen/mal estado), `ok`/`warn`/`error` sí
|
|
3236
|
+
* comunican severidad. La librería no conoce el dominio que produce ese
|
|
3237
|
+
* valor (SLA, storage, lo que sea) — el consumidor decide qué tone usar. */
|
|
3238
|
+
type ProgressTone = 'neutral' | 'ok' | 'warn' | 'error';
|
|
3239
|
+
/**
|
|
3240
|
+
* Barra de progreso de un solo valor (0-100%) con color por severidad —
|
|
3241
|
+
* para un porcentaje simple, a diferencia de `kf-bucket-progress`, que
|
|
3242
|
+
* dibuja varias categorías proporcionales en la misma barra.
|
|
3243
|
+
*
|
|
3244
|
+
* <kf-progress [value]="80" tone="ok" label="80%" />
|
|
3245
|
+
* <kf-progress [value]="ratio()" [tone]="severityTone()" />
|
|
3246
|
+
*
|
|
3247
|
+
* `value` se clampea a [0, 100] — un consumidor que calcule un ratio fuera
|
|
3248
|
+
* de rango (ej. tiempo transcurrido sobre un SLA ya vencido) no rompe el
|
|
3249
|
+
* ancho de la barra. `label` es texto libre opcional sobre la barra (ej.
|
|
3250
|
+
* "80%"); sin él, no se renderiza ningún texto, solo la barra.
|
|
3251
|
+
*/
|
|
3252
|
+
declare class Progress {
|
|
3253
|
+
readonly value: _angular_core.InputSignal<number>;
|
|
3254
|
+
readonly tone: _angular_core.InputSignal<ProgressTone>;
|
|
3255
|
+
readonly label: _angular_core.InputSignal<string | undefined>;
|
|
3256
|
+
protected readonly clampedValue: _angular_core.Signal<number>;
|
|
3257
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<Progress, never>;
|
|
3258
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<Progress, "kf-progress", never, { "value": { "alias": "value"; "required": true; "isSignal": true; }; "tone": { "alias": "tone"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
3259
|
+
}
|
|
3260
|
+
|
|
3261
|
+
/**
|
|
3262
|
+
* Punto que emite ondas concéntricas en bucle, para marcar un dato que
|
|
3263
|
+
* necesita atención inmediata sin depender de que el operador lo vea por
|
|
3264
|
+
* casualidad — el mismo rol que un punto rojo estático ya cumple en varios
|
|
3265
|
+
* lugares del producto (ver `.now__d`/`.board-alerts__dot` en
|
|
3266
|
+
* `operations-final.html`), llevado a movimiento porque el color solo
|
|
3267
|
+
* compite por atención con todo lo demás en pantalla; el movimiento no.
|
|
3268
|
+
*
|
|
3269
|
+
* Es una primitiva de énfasis, no un indicador de estado — para eso está
|
|
3270
|
+
* `kf-status-pill` (punto estático + texto). Este componente no tiene
|
|
3271
|
+
* texto ni tono semántico de "ok/warn/error": una sola apariencia (rojo,
|
|
3272
|
+
* el color reservado para lo urgente en el design system), porque emitir
|
|
3273
|
+
* ondas por algo que no es urgente banalizaría la señal la próxima vez que
|
|
3274
|
+
* sí lo sea.
|
|
3275
|
+
*
|
|
3276
|
+
* <kf-pulse-dot />
|
|
3277
|
+
* <kf-pulse-dot size="lg" />
|
|
3278
|
+
*
|
|
3279
|
+
* Respeta `prefers-reduced-motion` (ver PulseDot.css): la onda deja de
|
|
3280
|
+
* animarse y el punto queda como un punto estático, sin perder la
|
|
3281
|
+
* información de "esto es urgente" que da el color.
|
|
3282
|
+
*/
|
|
3283
|
+
declare class PulseDot {
|
|
3284
|
+
readonly size: _angular_core.InputSignal<"sm" | "md" | "lg">;
|
|
3285
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<PulseDot, never>;
|
|
3286
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<PulseDot, "kf-pulse-dot", never, { "size": { "alias": "size"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
3287
|
+
}
|
|
3288
|
+
|
|
3289
|
+
/** Chip de filtro genérico con contador — nunca hardcodeado a un dominio
|
|
3290
|
+
* concreto (ej. estado de una cotización); el consumidor decide qué
|
|
3291
|
+
* representa cada `value` y cómo mapea sus items contra él vía `predicate`.
|
|
3292
|
+
*
|
|
3293
|
+
* Bug real corregido: la primera versión leía `item.status`/`item.chip` por
|
|
3294
|
+
* nombre de campo mágico en vez de un `predicate` — colisionaba con
|
|
3295
|
+
* dominios donde `status` ya significa otra cosa (ej. `Quote.status` en
|
|
3296
|
+
* `web-otd-frontend` es 'ok'|'error'|'warn' de aprobación, no "tiene saldo
|
|
3297
|
+
* pendiente"). Un chip "Con saldo pendiente" sobre cotizaciones terminaba
|
|
3298
|
+
* filtrando por un campo que no tenía nada que ver, mostrando 0 resultados
|
|
3299
|
+
* siempre. `predicate` es explícito y no depende de convenciones de nombre. */
|
|
3300
|
+
interface QuotePickerChip<T = unknown> {
|
|
3301
|
+
value: string;
|
|
3302
|
+
label: string;
|
|
3303
|
+
count: number;
|
|
3304
|
+
predicate: (item: T) => boolean;
|
|
3305
|
+
}
|
|
3306
|
+
/** Una opción del dropdown "Ordenar por", visible solo en `variant="modal"`. */
|
|
3307
|
+
interface QuotePickerSortOption {
|
|
3308
|
+
value: string;
|
|
3309
|
+
label: string;
|
|
3310
|
+
}
|
|
3311
|
+
/**
|
|
3312
|
+
* Selector de un ítem "rico" de una lista buscable/filtrable — generalización
|
|
3313
|
+
* del patrón "elegir una cotización" de Pagos y Facturas de OTD, pero sin
|
|
3314
|
+
* ningún acoplamiento a `Quote`: todo el contenido de cada fila/card llega
|
|
3315
|
+
* por `columns` (mismo `DataTableColumn` de `kf-data-table`, reexportado acá
|
|
3316
|
+
* en vez de duplicar el concepto) + `cellTemplates` para contenido custom
|
|
3317
|
+
* (thumbnail, monto formateado, etc. — mismo mecanismo `TemplateRef` +
|
|
3318
|
+
* `ngTemplateOutlet` que ya usa `kf-data-table`).
|
|
3319
|
+
*
|
|
3320
|
+
* Un solo componente, dos contenedores (`variant: 'inline' | 'modal'`), UNA
|
|
3321
|
+
* sola lógica de búsqueda/filtro/orden interna. Nunca dos implementaciones
|
|
3322
|
+
* paralelas: el riesgo real de eso es que un fix de filtro se aplique en un
|
|
3323
|
+
* modo y se olvide en el otro. Los `computed()` de más abajo
|
|
3324
|
+
* (`filteredItems`, `sortedItems`) son la ÚNICA fuente de verdad para ambos
|
|
3325
|
+
* `variant`; lo que cambia entre inline y modal es solo qué contenedor
|
|
3326
|
+
* envuelve esos datos ya resueltos.
|
|
3327
|
+
*
|
|
3328
|
+
* ## Modo `inline` (default)
|
|
3329
|
+
* Trigger a ANCHO COMPLETO (nunca una caja angosta de ancho fijo) que
|
|
3330
|
+
* muestra el ítem activo colapsado — thumbnail, id + modelo, unidades +
|
|
3331
|
+
* fecha + monto, CTA "Cambiar cotización" con chevron-down. Al click, el
|
|
3332
|
+
* panel se expande INLINE (empuja el contenido de abajo, nunca flota
|
|
3333
|
+
* encima) con buscador propio + chips de filtro + un grid de 2 columnas (1
|
|
3334
|
+
* en mobile, breakpoint `max-width: 900px` vía `matchMediaSignal` — mismo
|
|
3335
|
+
* mecanismo que ya usa el resto de la librería para decisiones de layout en
|
|
3336
|
+
* TypeScript, no solo CSS). Por encima de `expandThreshold` resultados
|
|
3337
|
+
* visibles, el panel recorta a un preview de `PREVIEW_ROWS` filas con un
|
|
3338
|
+
* fade inferior y un botón dashed "Ver las N en pantalla completa" que
|
|
3339
|
+
* emite `expandToModal` — el picker no decide qué pasa después (típicamente
|
|
3340
|
+
* el consumidor pone `variant` en `'modal'` y `open` en `true`).
|
|
3341
|
+
*
|
|
3342
|
+
* ## Modo `modal`
|
|
3343
|
+
* Mismo buscador, mismos chips, misma lógica de filtrado — compone
|
|
3344
|
+
* `kf-modal` (proyectando el contenido) en vez de reimplementar un
|
|
3345
|
+
* `<dialog>` a mano: la API de `Modal` (`[(open)]`, cierre con
|
|
3346
|
+
* Escape/backdrop/botón vía `<dialog>` nativo) ya cubre exactamente lo que
|
|
3347
|
+
* este modo necesita. Compone también `kf-data-table` para la tabla
|
|
3348
|
+
* completa (columnas configurables, fade de overflow horizontal ya
|
|
3349
|
+
* resuelto ahí) y `kf-pagination` para paginar — ninguno de los tres se
|
|
3350
|
+
* reimplementa. Agrega un dropdown propio de "Ordenar por" (calibrado
|
|
3351
|
+
* visualmente contra el trigger de `kf-select`, pero compacto: `kf-select`
|
|
3352
|
+
* fuerza un ancho fijo de 240px pensado para formularios, no para una
|
|
3353
|
+
* barra de herramientas de tabla — ver JSDoc de `sortOpen` más abajo).
|
|
3354
|
+
*
|
|
3355
|
+
* En mobile, `kf-data-table[cardable]` con `defaultView` forzado a
|
|
3356
|
+
* `'cards'` decide la vista (mismo criterio que `matchMediaSignal` ya
|
|
3357
|
+
* resolvió en otros consumidores): dentro de un modal a pantalla completa
|
|
3358
|
+
* no tiene sentido ofrecer un toggle tabla/cards que en mobile daría una
|
|
3359
|
+
* tabla angosta con scroll horizontal.
|
|
3360
|
+
*
|
|
3361
|
+
* DEUDA TÉCNICA CONOCIDA (confirmada con el usuario, ver CHANGELOG.md): el
|
|
3362
|
+
* diseño validado con mockups pedía que en mobile el modal ocupe el 100%
|
|
3363
|
+
* de la pantalla (botón "atrás", sin overlay centrado) — `kf-modal` no
|
|
3364
|
+
* tiene ese modo, así que hoy en mobile se ve como un diálogo centrado
|
|
3365
|
+
* angosto (`width: min(480px, ...)`). Es funcional (cards legibles,
|
|
3366
|
+
* búsqueda, chips, paginación), solo visualmente distinto del mockup.
|
|
3367
|
+
*
|
|
3368
|
+
* <kf-quote-picker
|
|
3369
|
+
* [items]="quotes()"
|
|
3370
|
+
* [columns]="columns"
|
|
3371
|
+
* [selectedId]="selectedQuoteId()"
|
|
3372
|
+
* [idKey]="(q) => q.id"
|
|
3373
|
+
* [chips]="statusChips()"
|
|
3374
|
+
* [cellTemplates]="{ photo: photoCell(), amount: amountCell() }"
|
|
3375
|
+
* (selectedIdChange)="selectedQuoteId.set($event)"
|
|
3376
|
+
* (expandToModal)="variant.set('modal'); open.set(true)"
|
|
3377
|
+
* />
|
|
3378
|
+
*/
|
|
3379
|
+
declare class QuotePicker<T = unknown> {
|
|
3380
|
+
readonly items: _angular_core.InputSignal<T[]>;
|
|
3381
|
+
readonly columns: _angular_core.InputSignal<DataTableColumn[]>;
|
|
3382
|
+
readonly selectedId: _angular_core.InputSignal<string | null>;
|
|
3383
|
+
readonly idKey: _angular_core.InputSignal<(item: T) => string>;
|
|
3384
|
+
/**
|
|
3385
|
+
* Mapa `columnKey -> TemplateRef`, mismo mecanismo que `kf-data-table`
|
|
3386
|
+
* (`ngTemplateOutlet` + contexto `{ $implicit: item, index }`) — se
|
|
3387
|
+
* reenvía tal cual a la tabla interna del modo `modal`, y se usa
|
|
3388
|
+
* directamente en el grid de cards del modo `inline`. Un solo mapa para
|
|
3389
|
+
* ambos modos, consistente con la regla de "una sola lógica interna".
|
|
3390
|
+
*/
|
|
3391
|
+
readonly cellTemplates: _angular_core.InputSignal<Record<string, TemplateRef<{
|
|
3392
|
+
$implicit: T;
|
|
3393
|
+
index: number;
|
|
3394
|
+
}>> | undefined>;
|
|
3395
|
+
readonly chips: _angular_core.InputSignal<QuotePickerChip<T>[]>;
|
|
3396
|
+
readonly sortOptions: _angular_core.InputSignal<QuotePickerSortOption[]>;
|
|
3397
|
+
readonly sort: _angular_core.ModelSignal<string | null>;
|
|
3398
|
+
/**
|
|
3399
|
+
* Umbral de volumen alto — el consumidor decide el número exacto, nunca
|
|
3400
|
+
* hardcodeado a "cotizaciones" ni a ningún caso de uso puntual. Default
|
|
3401
|
+
* 15 según el diseño validado con mockups.
|
|
3402
|
+
*/
|
|
3403
|
+
readonly expandThreshold: _angular_core.InputSignal<number>;
|
|
3404
|
+
readonly variant: _angular_core.InputSignal<"inline" | "modal">;
|
|
3405
|
+
/** Visibilidad del modo `modal`, two-way — mismo patrón que `[(open)]` de `kf-modal`. */
|
|
3406
|
+
readonly open: _angular_core.ModelSignal<boolean>;
|
|
3407
|
+
readonly title: _angular_core.InputSignal<string>;
|
|
3408
|
+
readonly searchPlaceholder: _angular_core.InputSignal<string>;
|
|
3409
|
+
/** Ícono de placeholder cuando ninguna columna `cardRole: 'media'` resuelve
|
|
3410
|
+
* un thumbnail real vía `cellTemplates` — el picker no asume qué dominio
|
|
3411
|
+
* consume (cotización, vehículo, cliente...), así que no hardcodea un
|
|
3412
|
+
* ícono fijo. Default `'file'` solo por no dejar el input sin valor. */
|
|
3413
|
+
readonly placeholderIcon: _angular_core.InputSignal<IconName>;
|
|
3414
|
+
readonly selectedIdChange: _angular_core.OutputEmitterRef<string>;
|
|
3415
|
+
/** Disparado al superar `expandThreshold` en modo inline; el consumidor decide pasar a `modal`. */
|
|
3416
|
+
readonly expandToModal: _angular_core.OutputEmitterRef<void>;
|
|
3417
|
+
protected readonly searchQuery: _angular_core.WritableSignal<string>;
|
|
3418
|
+
protected readonly activeChip: _angular_core.WritableSignal<string | null>;
|
|
3419
|
+
protected readonly expanded: _angular_core.WritableSignal<boolean>;
|
|
3420
|
+
protected readonly page: _angular_core.WritableSignal<number>;
|
|
3421
|
+
protected readonly sortOpen: _angular_core.WritableSignal<boolean>;
|
|
3422
|
+
/**
|
|
3423
|
+
* Mismo breakpoint (`max-width: 900px`) que el resto de la librería usa
|
|
3424
|
+
* para decisiones de layout en TypeScript (no solo CSS) — ver
|
|
3425
|
+
* `matchMediaSignal`. Decide 1 vs. 2 columnas en el grid inline, y tabla
|
|
3426
|
+
* vs. cards apiladas dentro del modal.
|
|
3427
|
+
*/
|
|
3428
|
+
protected readonly isMobile: _angular_core.Signal<boolean>;
|
|
3429
|
+
private readonly destroyRef;
|
|
3430
|
+
private readonly previewScrollEl;
|
|
3431
|
+
/** Mismo mecanismo de fade que `kf-data-table` — ver comentario en QuotePicker.css. */
|
|
3432
|
+
protected readonly canFadeBottom: _angular_core.WritableSignal<boolean>;
|
|
3433
|
+
readonly selectedItem: _angular_core.Signal<T | undefined>;
|
|
3434
|
+
/** Filtro por texto (buscador propio) + filtro por chip activo, en ese orden. */
|
|
3435
|
+
protected readonly filteredItems: _angular_core.Signal<T[]>;
|
|
3436
|
+
/**
|
|
3437
|
+
* Orden solo tiene efecto visible en modo `modal` (el diseño validado no
|
|
3438
|
+
* pide dropdown de orden en inline) — se calcula igual en ambos casos
|
|
3439
|
+
* porque es la MISMA lógica interna; en inline `sort()` simplemente se
|
|
3440
|
+
* queda en `null` salvo que el consumidor lo setee explícitamente.
|
|
3441
|
+
*/
|
|
3442
|
+
protected readonly sortedItems: _angular_core.Signal<T[]>;
|
|
3443
|
+
/** Por encima del umbral, el preview inline se recorta salvo que el usuario ya haya expandido. */
|
|
3444
|
+
protected readonly isHighVolume: _angular_core.Signal<boolean>;
|
|
3445
|
+
protected readonly previewItems: _angular_core.Signal<T[]>;
|
|
3446
|
+
protected readonly pages: _angular_core.Signal<number>;
|
|
3447
|
+
protected readonly pagedItems: _angular_core.Signal<T[]>;
|
|
3448
|
+
protected readonly sortLabel: _angular_core.Signal<string>;
|
|
3449
|
+
constructor();
|
|
3450
|
+
private updateFadeState;
|
|
3451
|
+
private cellText;
|
|
3452
|
+
toggleExpandedPanel(): void;
|
|
3453
|
+
onSearchInput(event: Event): void;
|
|
3454
|
+
selectChip(value: string): void;
|
|
3455
|
+
select(item: T): void;
|
|
3456
|
+
onItemKeydown(event: KeyboardEvent, item: T): void;
|
|
3457
|
+
onSeeAll(): void;
|
|
3458
|
+
onTableRowClick(event: {
|
|
3459
|
+
row: T;
|
|
3460
|
+
index: number;
|
|
3461
|
+
}): void;
|
|
3462
|
+
toggleSort(): void;
|
|
3463
|
+
setSort(value: string): void;
|
|
3464
|
+
trackItem(item: T, index: number): string | number;
|
|
3465
|
+
/** Arrow function como campo (no un método) para que `[rowKey]` reciba
|
|
3466
|
+
* una referencia ESTABLE entre change detections — `trackItem.bind(this)`
|
|
3467
|
+
* en el template recrearía una función nueva en cada ciclo, invalidando
|
|
3468
|
+
* cualquier optimización de `kf-data-table` que dependa de identidad. */
|
|
3469
|
+
protected readonly rowKeyFn: (item: T, index: number) => string | number;
|
|
3470
|
+
protected cellValue(item: T, column: DataTableColumn): string;
|
|
3471
|
+
protected readonly mediaColumn: _angular_core.Signal<DataTableColumn | undefined>;
|
|
3472
|
+
protected readonly titleColumn: _angular_core.Signal<DataTableColumn | undefined>;
|
|
3473
|
+
protected readonly fieldColumns: _angular_core.Signal<DataTableColumn[]>;
|
|
3474
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<QuotePicker<any>, never>;
|
|
3475
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<QuotePicker<any>, "kf-quote-picker", never, { "items": { "alias": "items"; "required": true; "isSignal": true; }; "columns": { "alias": "columns"; "required": true; "isSignal": true; }; "selectedId": { "alias": "selectedId"; "required": false; "isSignal": true; }; "idKey": { "alias": "idKey"; "required": true; "isSignal": true; }; "cellTemplates": { "alias": "cellTemplates"; "required": false; "isSignal": true; }; "chips": { "alias": "chips"; "required": false; "isSignal": true; }; "sortOptions": { "alias": "sortOptions"; "required": false; "isSignal": true; }; "sort": { "alias": "sort"; "required": false; "isSignal": true; }; "expandThreshold": { "alias": "expandThreshold"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "open": { "alias": "open"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; "searchPlaceholder": { "alias": "searchPlaceholder"; "required": false; "isSignal": true; }; "placeholderIcon": { "alias": "placeholderIcon"; "required": false; "isSignal": true; }; }, { "sort": "sortChange"; "open": "openChange"; "selectedIdChange": "selectedIdChange"; "expandToModal": "expandToModal"; }, never, never, true, never>;
|
|
1736
3476
|
}
|
|
1737
3477
|
|
|
1738
3478
|
/**
|
|
@@ -1780,14 +3520,10 @@ declare class ReserveBlock {
|
|
|
1780
3520
|
* `size` sigue la misma escala `'sm'|'md'|'lg'` que `Button`/`Icon`/`Spinner`
|
|
1781
3521
|
* (`md` es el default): `sm` reduce tipografía y espaciado para usarse
|
|
1782
3522
|
* dentro de cards ya compactas, `lg` los agranda para encabezar un bloque
|
|
1783
|
-
* más prominente.
|
|
1784
|
-
*
|
|
1785
|
-
*
|
|
1786
|
-
*
|
|
1787
|
-
* patrón real de uso dentro de cards). `solid` rellena el chip de ícono con
|
|
1788
|
-
* el color primario sólido en vez del tratamiento "soft" (fondo tenue) —
|
|
1789
|
-
* pensado para superficies que ya tienen un tinte de color propio, donde el
|
|
1790
|
-
* chip "soft" perdería contraste.
|
|
3523
|
+
* más prominente. `solid` rellena el chip de ícono con el color primario
|
|
3524
|
+
* sólido en vez del tratamiento "soft" (fondo tenue) — pensado para
|
|
3525
|
+
* superficies que ya tienen un tinte de color propio, donde el chip "soft"
|
|
3526
|
+
* perdería contraste.
|
|
1791
3527
|
*
|
|
1792
3528
|
* <kf-section-header icon="settings" title="Preferencias"
|
|
1793
3529
|
* subtitle="Configuración general de la cuenta">
|
|
@@ -1808,6 +3544,118 @@ declare class SectionHeader {
|
|
|
1808
3544
|
static ɵcmp: _angular_core.ɵɵComponentDeclaration<SectionHeader, "kf-section-header", never, { "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": true; "isSignal": true; }; "subtitle": { "alias": "subtitle"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "solid": { "alias": "solid"; "required": false; "isSignal": true; }; }, {}, never, ["[actions]"], true, never>;
|
|
1809
3545
|
}
|
|
1810
3546
|
|
|
3547
|
+
/**
|
|
3548
|
+
* Contenedor de navegación por secciones dentro de una misma pantalla —
|
|
3549
|
+
* agrupa `kf-section-nav-item` y resuelve su layout fusionado según
|
|
3550
|
+
* viewport, con un breakpoint PROPIO de 1024px (no el $mobile:900px
|
|
3551
|
+
* compartido del resto de la librería — ver el comentario en
|
|
3552
|
+
* `SectionNav.scss`):
|
|
3553
|
+
*
|
|
3554
|
+
* - Desktop (≥1024px): columna de botones a la izquierda + panel de
|
|
3555
|
+
* contenido separado a la derecha, mostrando el contenido del ítem (o
|
|
3556
|
+
* sub-ítem) activo.
|
|
3557
|
+
* - Mobile y tablet (<1024px): acordeón — el contenido del ítem activo se
|
|
3558
|
+
* despliega inline, justo debajo de su propio botón.
|
|
3559
|
+
*
|
|
3560
|
+
* El cambio entre ambos layouts es solo CSS (`SectionNav.scss` +
|
|
3561
|
+
* `SectionNavItem.scss`, ver comentarios ahí): cada `kf-section-nav-item`
|
|
3562
|
+
* proyecta su contenido una única vez y es la grilla la que lo reposiciona
|
|
3563
|
+
* visualmente, sin recrearlo ni duplicarlo en el DOM.
|
|
3564
|
+
*
|
|
3565
|
+
* A diferencia de `kf-tabs` (selector de vista plano, sin contenido propio)
|
|
3566
|
+
* y de `kf-sidebar-shell` (rail persistente ENTRE rutas, colapsable, fondo
|
|
3567
|
+
* oscuro): esto es navegación DENTRO de una sola pantalla, con paneles de
|
|
3568
|
+
* contenido rico por sección.
|
|
3569
|
+
*
|
|
3570
|
+
* La exclusividad entre ítems (y entre sub-ítems, dentro de cada uno) la
|
|
3571
|
+
* decide quien consume el componente, igual que cada `kf-section-nav-item`
|
|
3572
|
+
* por separado — `kf-section-nav` no guarda ningún estado de selección.
|
|
3573
|
+
*
|
|
3574
|
+
* <kf-section-nav>
|
|
3575
|
+
* <kf-section-nav-item label="Mis datos personales" icon="user"
|
|
3576
|
+
* [active]="openSection() === 'datos'" (toggled)="openSection.set('datos')">
|
|
3577
|
+
* <app-personal-data />
|
|
3578
|
+
* </kf-section-nav-item>
|
|
3579
|
+
* <kf-section-nav-item label="Mis empresas" icon="layers"
|
|
3580
|
+
* [active]="openSection() === 'empresas'" (toggled)="openSection.set('empresas')">
|
|
3581
|
+
* <app-my-companies />
|
|
3582
|
+
* </kf-section-nav-item>
|
|
3583
|
+
* </kf-section-nav>
|
|
3584
|
+
*/
|
|
3585
|
+
declare class SectionNav {
|
|
3586
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<SectionNav, never>;
|
|
3587
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<SectionNav, "kf-section-nav", never, {}, {}, never, ["*"], true, never>;
|
|
3588
|
+
}
|
|
3589
|
+
|
|
3590
|
+
interface SectionNavSubsectionData {
|
|
3591
|
+
key: string;
|
|
3592
|
+
label: string;
|
|
3593
|
+
icon: IconName;
|
|
3594
|
+
disabled?: boolean;
|
|
3595
|
+
}
|
|
3596
|
+
/**
|
|
3597
|
+
* Ítem de nivel superior de `kf-section-nav`: header clickeable (ícono
|
|
3598
|
+
* identificador opcional + label + chevron) que expande/colapsa contenido
|
|
3599
|
+
* proyectado. La exclusividad entre ítems la decide quien consume el
|
|
3600
|
+
* componente — cada click solo emite `toggled` y el padre resuelve qué
|
|
3601
|
+
* ítem queda `active`, igual que `kf-folder-tabs` con `value`.
|
|
3602
|
+
*
|
|
3603
|
+
* `subsections` agrega un segundo nivel de navegación DENTRO del ítem
|
|
3604
|
+
* (solo visible cuando el ítem está `active`), cada uno con su propio
|
|
3605
|
+
* estado activo (`activeSubsection`) independiente del padre. El contenido
|
|
3606
|
+
* de cada sub-ítem se proyecta envolviéndolo en `<kf-section-nav-subsection
|
|
3607
|
+
* key="...">` dentro del mismo slot por defecto — ver esa clase.
|
|
3608
|
+
*
|
|
3609
|
+
* El contenido proyectado usa `[hidden]`, no `@if`: un ítem puede envolver
|
|
3610
|
+
* un formulario con estado que el usuario ya completó, y colapsar/expandir
|
|
3611
|
+
* (o cruzar el breakpoint propio de 1024px, ver `SectionNav.scss`) no debe
|
|
3612
|
+
* destruir ese subárbol.
|
|
3613
|
+
*
|
|
3614
|
+
* <kf-section-nav-item
|
|
3615
|
+
* label="Mis datos personales"
|
|
3616
|
+
* icon="user"
|
|
3617
|
+
* [active]="openSection() === 'datos'"
|
|
3618
|
+
* (toggled)="openSection.set('datos')"
|
|
3619
|
+
* >
|
|
3620
|
+
* ...
|
|
3621
|
+
* </kf-section-nav-item>
|
|
3622
|
+
*/
|
|
3623
|
+
declare class SectionNavItem {
|
|
3624
|
+
readonly label: _angular_core.InputSignal<string>;
|
|
3625
|
+
readonly icon: _angular_core.InputSignal<IconName | undefined>;
|
|
3626
|
+
readonly active: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
3627
|
+
readonly toggled: _angular_core.OutputEmitterRef<void>;
|
|
3628
|
+
/** Sub-ítems navegables de este ítem, visibles solo mientras `active()`. */
|
|
3629
|
+
readonly subsections: _angular_core.InputSignal<SectionNavSubsectionData[]>;
|
|
3630
|
+
/** `key` del sub-ítem actualmente activo — ver `SectionNavSubsectionData`. */
|
|
3631
|
+
readonly activeSubsection: _angular_core.InputSignal<string | undefined>;
|
|
3632
|
+
readonly subsectionSelected: _angular_core.OutputEmitterRef<string>;
|
|
3633
|
+
protected readonly showSubsections: _angular_core.Signal<boolean>;
|
|
3634
|
+
protected onSubsectionClick(key: string, disabled: boolean | undefined, event: MouseEvent): void;
|
|
3635
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<SectionNavItem, never>;
|
|
3636
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<SectionNavItem, "kf-section-nav-item", never, { "label": { "alias": "label"; "required": true; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "active": { "alias": "active"; "required": false; "isSignal": true; }; "subsections": { "alias": "subsections"; "required": false; "isSignal": true; }; "activeSubsection": { "alias": "activeSubsection"; "required": false; "isSignal": true; }; }, { "toggled": "toggled"; "subsectionSelected": "subsectionSelected"; }, never, ["*"], true, never>;
|
|
3637
|
+
}
|
|
3638
|
+
|
|
3639
|
+
/**
|
|
3640
|
+
* Envoltorio de contenido para UN sub-ítem de `kf-section-nav-item`. Varios
|
|
3641
|
+
* pueden proyectarse juntos, uno por `key` de `subsections()` del padre —
|
|
3642
|
+
* cada uno queda montado siempre (mismo criterio `[hidden]` que el propio
|
|
3643
|
+
* `kf-section-nav-item`), y solo se muestra el que coincide con
|
|
3644
|
+
* `activeSubsection()` del padre.
|
|
3645
|
+
*
|
|
3646
|
+
* <kf-section-nav-item [subsections]="subs" [activeSubsection]="active()" ...>
|
|
3647
|
+
* <kf-section-nav-subsection key="general"><app-general /></kf-section-nav-subsection>
|
|
3648
|
+
* <kf-section-nav-subsection key="seguridad"><app-seguridad /></kf-section-nav-subsection>
|
|
3649
|
+
* </kf-section-nav-item>
|
|
3650
|
+
*/
|
|
3651
|
+
declare class SectionNavSubsection {
|
|
3652
|
+
readonly key: _angular_core.InputSignal<string>;
|
|
3653
|
+
private readonly parent;
|
|
3654
|
+
protected readonly isActive: _angular_core.Signal<boolean>;
|
|
3655
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<SectionNavSubsection, never>;
|
|
3656
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<SectionNavSubsection, "kf-section-nav-subsection", never, { "key": { "alias": "key"; "required": true; "isSignal": true; }; }, {}, never, ["*"], true, never>;
|
|
3657
|
+
}
|
|
3658
|
+
|
|
1811
3659
|
interface SegmentedOption {
|
|
1812
3660
|
value: string;
|
|
1813
3661
|
label: string;
|
|
@@ -1844,23 +3692,27 @@ interface SelectOption {
|
|
|
1844
3692
|
* opciones al sistema operativo, y ningún CSS puede controlar cómo se ve
|
|
1845
3693
|
* ese popup.
|
|
1846
3694
|
*
|
|
1847
|
-
* Hasta la `0.1.5` existía además `kf-select-menu`, un segundo componente
|
|
1848
|
-
* prácticamente idéntico (mismo trigger, mismo panel, mismo CSS) que solo
|
|
1849
|
-
* se diferenciaba en aceptar `options` únicamente como `{ value, label }[]`
|
|
1850
|
-
* (sin el atajo `string[]` que `Select` ya tenía). Se fusionaron en este
|
|
1851
|
-
* componente en la `0.2.0` — mantener dos componentes con la misma
|
|
1852
|
-
* interacción y el mismo aspecto visual, cuya única diferencia real era la
|
|
1853
|
-
* forma de un input, no se sostenía como dos piezas separadas del catálogo.
|
|
1854
|
-
*
|
|
1855
3695
|
* El panel flotante se conecta al trigger vía `@angular/cdk/overlay` — el
|
|
1856
3696
|
* mismo mecanismo que `Dropdown` (ver `../dropdown/Dropdown.ts`).
|
|
1857
3697
|
*
|
|
1858
3698
|
* <kf-select label="Sede" [options]="['Lima', 'Arequipa', 'Trujillo']" placeholder="Selecciona…" />
|
|
1859
3699
|
* <kf-select [options]="[{ value: 'pe', label: 'Perú' }]" (valueChange)="onCountry($event)" />
|
|
1860
3700
|
*
|
|
1861
|
-
*
|
|
1862
|
-
*
|
|
1863
|
-
*
|
|
3701
|
+
* `variant="compact"` cambia el ancho de `240px` fijo a intrínseco con un
|
|
3702
|
+
* techo de `160px` (se ajusta al contenido de la opción seleccionada hasta
|
|
3703
|
+
* ese máximo, truncando con ellipsis si no entra) y reduce padding/tamaño de
|
|
3704
|
+
* fuente — pensado para selectores cortos embebidos en una barra (ej. "Filas
|
|
3705
|
+
* por página" en el footer de `kf-data-table`), nunca para un campo de
|
|
3706
|
+
* formulario largo, que sigue usando el `variant="field"` por defecto. El
|
|
3707
|
+
* panel flotante siempre mide el ancho real del trigger en cada apertura
|
|
3708
|
+
* (`SelectOverlay.attach`), así que se angosta solo sin cambios adicionales.
|
|
3709
|
+
*
|
|
3710
|
+
* <kf-select variant="compact" [options]="['5', '10', '25']" [value]="pageSize()" (valueChange)="..." />
|
|
3711
|
+
*
|
|
3712
|
+
* `width="full"` ocupa el 100% del contenedor — para selects dentro de
|
|
3713
|
+
* layouts flex/grid donde el ancho fijo de `md` no encaja.
|
|
3714
|
+
*
|
|
3715
|
+
* <kf-select width="full" [options]="branches" [value]="branch()" (valueChange)="..." />
|
|
1864
3716
|
*/
|
|
1865
3717
|
declare class Select {
|
|
1866
3718
|
readonly label: _angular_core.InputSignal<string | undefined>;
|
|
@@ -1868,6 +3720,18 @@ declare class Select {
|
|
|
1868
3720
|
readonly value: _angular_core.InputSignal<string | undefined>;
|
|
1869
3721
|
readonly placeholder: _angular_core.InputSignal<string>;
|
|
1870
3722
|
readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
3723
|
+
/** Borde en rojo y `aria-invalid` en el trigger. Ver `errorMessage` para el mensaje asociado — mismo contrato que `kf-input`. */
|
|
3724
|
+
readonly invalid: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
3725
|
+
/** Mensaje de error con `role="alert"`, debajo del trigger. */
|
|
3726
|
+
readonly errorMessage: _angular_core.InputSignal<string | undefined>;
|
|
3727
|
+
readonly variant: _angular_core.InputSignal<"field" | "compact">;
|
|
3728
|
+
/**
|
|
3729
|
+
* Ancho del control. 'md' (240px) es el default histórico y preserva el
|
|
3730
|
+
* comportamiento actual sin cambios para consumidores existentes.
|
|
3731
|
+
* 'full' ocupa el 100% del contenedor — para selects dentro de layouts
|
|
3732
|
+
* flex/grid donde el ancho fijo no encaja (ej. un footer de sidebar).
|
|
3733
|
+
*/
|
|
3734
|
+
readonly width: _angular_core.InputSignal<"sm" | "md" | "lg" | "full">;
|
|
1871
3735
|
/**
|
|
1872
3736
|
* Tri-state: si el consumidor no lo especifica (`undefined`), el buscador
|
|
1873
3737
|
* se activa solo cuando `options().length` supera `searchThreshold()`. Si
|
|
@@ -1880,6 +3744,7 @@ declare class Select {
|
|
|
1880
3744
|
private readonly instanceId;
|
|
1881
3745
|
readonly triggerId: string;
|
|
1882
3746
|
readonly listboxId: string;
|
|
3747
|
+
readonly errorId: string;
|
|
1883
3748
|
readonly isOpen: _angular_core.WritableSignal<boolean>;
|
|
1884
3749
|
readonly searchQuery: _angular_core.WritableSignal<string>;
|
|
1885
3750
|
readonly activeIndex: _angular_core.WritableSignal<number>;
|
|
@@ -1892,10 +3757,7 @@ declare class Select {
|
|
|
1892
3757
|
private readonly triggerRef;
|
|
1893
3758
|
private readonly panelTemplate;
|
|
1894
3759
|
private readonly searchInputRef;
|
|
1895
|
-
private readonly
|
|
1896
|
-
private readonly viewContainerRef;
|
|
1897
|
-
private readonly destroyRef;
|
|
1898
|
-
private overlayRef;
|
|
3760
|
+
private readonly overlay;
|
|
1899
3761
|
constructor();
|
|
1900
3762
|
/**
|
|
1901
3763
|
* Respaldo de `overlayRef.keydownEvents()`: ese observable del CDK solo ve
|
|
@@ -1915,43 +3777,63 @@ declare class Select {
|
|
|
1915
3777
|
*/
|
|
1916
3778
|
moveActive(delta: number): void;
|
|
1917
3779
|
selectActive(): void;
|
|
1918
|
-
private attach;
|
|
1919
|
-
private detach;
|
|
1920
|
-
private getOrCreateOverlayRef;
|
|
1921
3780
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<Select, never>;
|
|
1922
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<Select, "kf-select", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": true; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "searchable": { "alias": "searchable"; "required": false; "isSignal": true; }; "searchThreshold": { "alias": "searchThreshold"; "required": false; "isSignal": true; }; }, { "valueChange": "valueChange"; }, never, never, true, never>;
|
|
3781
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<Select, "kf-select", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": true; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "invalid": { "alias": "invalid"; "required": false; "isSignal": true; }; "errorMessage": { "alias": "errorMessage"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "width": { "alias": "width"; "required": false; "isSignal": true; }; "searchable": { "alias": "searchable"; "required": false; "isSignal": true; }; "searchThreshold": { "alias": "searchThreshold"; "required": false; "isSignal": true; }; }, { "valueChange": "valueChange"; }, never, never, true, never>;
|
|
3782
|
+
}
|
|
3783
|
+
|
|
3784
|
+
interface SelectableCardOption {
|
|
3785
|
+
value: string;
|
|
3786
|
+
label: string;
|
|
3787
|
+
icon?: IconName;
|
|
3788
|
+
}
|
|
3789
|
+
/**
|
|
3790
|
+
* Radio group real (`role="radiogroup"` + `role="radio"` + `aria-checked`),
|
|
3791
|
+
* con apariencia de cards apiladas verticalmente — para una decisión ÚNICA Y
|
|
3792
|
+
* EXCLUYENTE dentro de un flujo (ej. método de pago, tipo de documento a
|
|
3793
|
+
* subir), no para filtrar una lista.
|
|
3794
|
+
*
|
|
3795
|
+
* Distinto de `kf-filter-chips` a propósito: `FilterChips` es semántica y
|
|
3796
|
+
* visualmente un FILTRO (chips horizontales, se resaltan con `--kf-color-ink`
|
|
3797
|
+
* — pensado para "Todos/Activos/Archivados" sobre una lista, donde elegir
|
|
3798
|
+
* una opción cambia qué se MUESTRA). Acá la elección es la respuesta misma
|
|
3799
|
+
* a una pregunta del flujo ("¿con qué método pagás?"), no un filtro — por
|
|
3800
|
+
* eso necesita affordance de radio (círculo con punto, más peso visual por
|
|
3801
|
+
* opción) en vez de un pill de filtro reciclado.
|
|
3802
|
+
*
|
|
3803
|
+
* Réplica fiel del patrón ya resuelto en el Design System de referencia
|
|
3804
|
+
* (`otd-pagos.jsx` / `otd.css`: `.otd-pay-method` + `.radio`) — apilado
|
|
3805
|
+
* vertical con gap 10px, radio de 18px con punto interno vía `::after`,
|
|
3806
|
+
* borde+fondo+ícono cambian de color al seleccionar. Se agrega manejo de
|
|
3807
|
+
* teclado (flechas arriba/abajo, Home/End) y `:focus-visible` real, ninguno
|
|
3808
|
+
* de los dos presentes en el mockup original — mínimos de accesibilidad que
|
|
3809
|
+
* no dependen de fidelidad visual al mockup.
|
|
3810
|
+
*
|
|
3811
|
+
* <kf-selectable-card-group
|
|
3812
|
+
* [options]="[{ value: 'card', label: 'Tarjeta', icon: 'credit-card' }]"
|
|
3813
|
+
* [value]="method()"
|
|
3814
|
+
* (valueChange)="method.set($event)"
|
|
3815
|
+
* />
|
|
3816
|
+
*/
|
|
3817
|
+
declare class SelectableCardGroup {
|
|
3818
|
+
readonly options: _angular_core.InputSignal<SelectableCardOption[]>;
|
|
3819
|
+
readonly value: _angular_core.InputSignal<string>;
|
|
3820
|
+
/** Accesible: describe qué está eligiendo el usuario (ej. "Método de pago"). */
|
|
3821
|
+
readonly ariaLabel: _angular_core.InputSignal<string>;
|
|
3822
|
+
readonly valueChange: _angular_core.OutputEmitterRef<string>;
|
|
3823
|
+
select(value: string): void;
|
|
3824
|
+
protected onKeydown(event: KeyboardEvent, currentValue: string): void;
|
|
3825
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<SelectableCardGroup, never>;
|
|
3826
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<SelectableCardGroup, "kf-selectable-card-group", never, { "options": { "alias": "options"; "required": true; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": true; "isSignal": true; }; }, { "valueChange": "valueChange"; }, never, never, true, never>;
|
|
1923
3827
|
}
|
|
1924
3828
|
|
|
1925
|
-
/**
|
|
1926
|
-
* sidebar persistente el ícono es parte permanente del diseño. */
|
|
3829
|
+
/** Contrato de datos de `kf-sidebar-shell.navItems`. `icon` es requerido:
|
|
3830
|
+
* en el sidebar persistente el ícono es parte permanente del diseño. */
|
|
1927
3831
|
interface SidebarNavItemData {
|
|
1928
3832
|
icon: IconName;
|
|
1929
3833
|
label: string;
|
|
1930
3834
|
href: string;
|
|
1931
3835
|
active?: boolean;
|
|
1932
3836
|
}
|
|
1933
|
-
/**
|
|
1934
|
-
* Ítem de navegación de `kf-sidebar-shell` — ícono + label en fila, estado
|
|
1935
|
-
* activo, sin agrupación. Es el equivalente vertical/persistente del `<a>`
|
|
1936
|
-
* que arma `kf-portal-header` para su `<nav>` horizontal.
|
|
1937
|
-
*
|
|
1938
|
-
* `collapsed` se recibe como input explícito (no vía `:host-context`): el
|
|
1939
|
-
* componente no necesita saber cómo funciona el estado de colapso del
|
|
1940
|
-
* shell, solo qué hacer cuando `collapsed` es `true`.
|
|
1941
|
-
*
|
|
1942
|
-
* No sabe nada de routing — emite `href` como cualquier link y deja que el
|
|
1943
|
-
* consumidor intercepte el click para integrarlo con el Router.
|
|
1944
|
-
*/
|
|
1945
|
-
declare class SidebarNavItem {
|
|
1946
|
-
readonly icon: _angular_core.InputSignal<IconName>;
|
|
1947
|
-
readonly label: _angular_core.InputSignal<string>;
|
|
1948
|
-
readonly href: _angular_core.InputSignal<string>;
|
|
1949
|
-
readonly active: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
1950
|
-
/** Oculta el label y centra el ícono — lo controla `kf-sidebar-shell` según su propio estado de colapso. */
|
|
1951
|
-
readonly collapsed: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
1952
|
-
static ɵfac: _angular_core.ɵɵFactoryDeclaration<SidebarNavItem, never>;
|
|
1953
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<SidebarNavItem, "kf-sidebar-nav-item", never, { "icon": { "alias": "icon"; "required": true; "isSignal": true; }; "label": { "alias": "label"; "required": true; "isSignal": true; }; "href": { "alias": "href"; "required": true; "isSignal": true; }; "active": { "alias": "active"; "required": false; "isSignal": true; }; "collapsed": { "alias": "collapsed"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
1954
|
-
}
|
|
1955
3837
|
|
|
1956
3838
|
/**
|
|
1957
3839
|
* Shell de layout con navegación lateral persistente — el complemento
|
|
@@ -1980,12 +3862,11 @@ declare class SidebarNavItem {
|
|
|
1980
3862
|
* mientras el panel está cerrado.
|
|
1981
3863
|
*
|
|
1982
3864
|
* A propósito NO reutiliza `kf-drawer` (el panel lateral genérico de la
|
|
1983
|
-
* librería): `kf-drawer` tiene su propio fondo claro fijo
|
|
1984
|
-
*
|
|
1985
|
-
*
|
|
1986
|
-
*
|
|
1987
|
-
*
|
|
1988
|
-
* cambia de posición según el viewport.
|
|
3865
|
+
* librería): `kf-drawer` tiene su propio fondo claro fijo, incompatible
|
|
3866
|
+
* con el texto claro de `kf-sidebar-nav-item` (pensado para el gradiente
|
|
3867
|
+
* oscuro), y duplicaría el árbol de navegación en un segundo lugar del
|
|
3868
|
+
* DOM. Acá hay un solo rail, un solo árbol de nav, que cambia de posición
|
|
3869
|
+
* según el viewport.
|
|
1989
3870
|
*/
|
|
1990
3871
|
declare class SidebarShell {
|
|
1991
3872
|
/** Ítems del menú del rail. */
|
|
@@ -1996,12 +3877,18 @@ declare class SidebarShell {
|
|
|
1996
3877
|
readonly mobileOpen: _angular_core.ModelSignal<boolean>;
|
|
1997
3878
|
/** Se emite al clickear un ítem — el consumidor decide cómo navegar (ej. `router.navigateByUrl`). */
|
|
1998
3879
|
readonly navItemClick: _angular_core.OutputEmitterRef<SidebarNavItemData>;
|
|
1999
|
-
/** `true` bajo los 900px
|
|
2000
|
-
* seguía ocultando el label de cada ítem al abrir el panel en mobile. */
|
|
3880
|
+
/** `true` bajo los 900px. */
|
|
2001
3881
|
private readonly isMobileViewport;
|
|
2002
|
-
/**
|
|
2003
|
-
*
|
|
2004
|
-
|
|
3882
|
+
/**
|
|
3883
|
+
* Estado de colapso EFECTIVO — el que ya considera el viewport, no el
|
|
3884
|
+
* `collapsed` crudo. Es lo que usa el propio rail para sus nav-items y
|
|
3885
|
+
* el botón de colapso; se expone público para que el consumidor le
|
|
3886
|
+
* pase el mismo valor a lo que proyecte en `[kf-sidebar-footer]` (ej.
|
|
3887
|
+
* `kf-drawer-session`) — así un footer proyectado no queda colapsado
|
|
3888
|
+
* dentro del panel deslizado de mobile mientras el resto del rail ya
|
|
3889
|
+
* muestra todo expandido.
|
|
3890
|
+
*/
|
|
3891
|
+
readonly itemsCollapsed: _angular_core.Signal<boolean>;
|
|
2005
3892
|
/** Cierra el panel mobile con Escape — mismo criterio que `Dropdown`/`Modal`/`Drawer`. */
|
|
2006
3893
|
protected onEscape(): void;
|
|
2007
3894
|
protected onNavItemClick(event: Event, item: SidebarNavItemData): void;
|
|
@@ -2061,32 +3948,124 @@ declare class Spinner {
|
|
|
2061
3948
|
static ɵcmp: _angular_core.ɵɵComponentDeclaration<Spinner, "kf-spinner", never, { "size": { "alias": "size"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "onDark": { "alias": "onDark"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
2062
3949
|
}
|
|
2063
3950
|
|
|
3951
|
+
type SsoProvider = 'microsoft';
|
|
3952
|
+
/** Copy y glyph por proveedor — agregar una entrada acá es todo lo que hace
|
|
3953
|
+
* falta para soportar un proveedor nuevo (ej. `google`), sin tocar el
|
|
3954
|
+
* template ni los estilos del componente. */
|
|
3955
|
+
interface SsoProviderConfig {
|
|
3956
|
+
readonly label: string;
|
|
3957
|
+
}
|
|
2064
3958
|
/**
|
|
2065
|
-
*
|
|
2066
|
-
*
|
|
2067
|
-
*
|
|
2068
|
-
*
|
|
2069
|
-
*
|
|
2070
|
-
*
|
|
2071
|
-
*
|
|
2072
|
-
*
|
|
3959
|
+
* Botón de inicio de sesión vía SSO corporativo — pensado como pieza suelta,
|
|
3960
|
+
* no acoplada a `kf-auth-layout`: cualquier consumidor puede usarlo solo,
|
|
3961
|
+
* dentro o fuera del slot `[sso]` de `AuthLayout`.
|
|
3962
|
+
*
|
|
3963
|
+
* `provider` resuelve copy y glyph — hoy solo `'microsoft'` (el método
|
|
3964
|
+
* principal de acceso del ecosistema, cuenta corporativa de Grupo Kaufmann),
|
|
3965
|
+
* con la puerta abierta a sumar otros a futuro sin romper la API existente.
|
|
3966
|
+
*
|
|
3967
|
+
* El glyph es el ícono GENÉRICO de 4 cuadrados de colores que Microsoft
|
|
3968
|
+
* publica en sus propias guías de marca para botones de terceros ("Sign in
|
|
3969
|
+
* with Microsoft") — nunca el logotipo/wordmark oficial completo. No vive en
|
|
3970
|
+
* el sprite de `kf-icon` (`icon-sprite.ts`) a propósito: ese catálogo es
|
|
3971
|
+
* monocromático (`currentColor`, ver `Icon.ts`), pensado para trazos de un
|
|
3972
|
+
* solo color — este glyph necesita sus 4 colores fijos tal como los define
|
|
3973
|
+
* la guía de marca, así que un SVG inline propio es la opción correcta, no
|
|
3974
|
+
* un glyph nuevo forzado al sprite compartido.
|
|
3975
|
+
*
|
|
3976
|
+
* El label por defecto viene de `provider` — pasar contenido propio via
|
|
3977
|
+
* `<ng-content>` lo reemplaza (ej. un texto más corto en un lugar angosto).
|
|
3978
|
+
*
|
|
3979
|
+
* <kf-sso-button provider="microsoft" (ssoClick)="loginWithMicrosoft()" />
|
|
3980
|
+
* <kf-sso-button provider="microsoft" [loading]="loggingIn()">
|
|
3981
|
+
* Ingresar con cuenta corporativa
|
|
3982
|
+
* </kf-sso-button>
|
|
3983
|
+
*/
|
|
3984
|
+
declare class SsoButton {
|
|
3985
|
+
readonly provider: _angular_core.InputSignal<"microsoft">;
|
|
3986
|
+
readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
3987
|
+
/** Reemplaza el glyph + label por `kf-spinner` y fuerza `disabled` — mismo
|
|
3988
|
+
* patrón que `kf-button[loading]`. */
|
|
3989
|
+
readonly loading: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
3990
|
+
/** Igual que `kf-button`: por defecto `submit` dentro de un `<form>`, pero
|
|
3991
|
+
* este botón normalmente vive fuera de cualquier form (dispara un
|
|
3992
|
+
* redirect a Microsoft Entra ID), así que casi siempre conviene `button`. */
|
|
3993
|
+
readonly type: _angular_core.InputSignal<"button" | "submit">;
|
|
3994
|
+
protected readonly config: _angular_core.Signal<SsoProviderConfig>;
|
|
3995
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<SsoButton, never>;
|
|
3996
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<SsoButton, "kf-sso-button", never, { "provider": { "alias": "provider"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "loading": { "alias": "loading"; "required": false; "isSignal": true; }; "type": { "alias": "type"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
|
|
3997
|
+
}
|
|
3998
|
+
|
|
3999
|
+
type StatCardTone = 'primary' | 'success' | 'error' | 'purple';
|
|
4000
|
+
/**
|
|
4001
|
+
* Card de estadística horizontal: ícono circular grande a la izquierda,
|
|
4002
|
+
* valor + label + nota apilados al centro, chevron opcional al final si es
|
|
4003
|
+
* clickeable. Distinta de `kf-metric-card` (layout vertical, label arriba +
|
|
4004
|
+
* ícono a la derecha) — este es el patrón real del dashboard de OTD ("Bonos
|
|
4005
|
+
* otorgados", "Cortesías otorgadas"), no una variante de la misma card.
|
|
2073
4006
|
*
|
|
2074
|
-
* <kf-stat-
|
|
2075
|
-
|
|
2076
|
-
|
|
2077
|
-
|
|
4007
|
+
* <kf-stat-card icon="gift" tone="purple" value="4" label="Bonos otorgados" note="$15,000.00 en beneficios" (cardClick)="onNavigate('beneficios')" />
|
|
4008
|
+
*/
|
|
4009
|
+
declare class StatCard {
|
|
4010
|
+
readonly icon: _angular_core.InputSignal<IconName>;
|
|
4011
|
+
readonly tone: _angular_core.InputSignal<StatCardTone>;
|
|
4012
|
+
readonly value: _angular_core.InputSignal<string | number>;
|
|
4013
|
+
readonly label: _angular_core.InputSignal<string>;
|
|
4014
|
+
readonly note: _angular_core.InputSignal<string | undefined>;
|
|
4015
|
+
/** Si se escucha `cardClick`, la card se renderiza como botón clickeable con chevron. */
|
|
4016
|
+
readonly clickable: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
4017
|
+
readonly cardClick: _angular_core.OutputEmitterRef<void>;
|
|
4018
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<StatCard, never>;
|
|
4019
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<StatCard, "kf-stat-card", never, { "icon": { "alias": "icon"; "required": true; "isSignal": true; }; "tone": { "alias": "tone"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": true; "isSignal": true; }; "label": { "alias": "label"; "required": true; "isSignal": true; }; "note": { "alias": "note"; "required": false; "isSignal": true; }; "clickable": { "alias": "clickable"; "required": false; "isSignal": true; }; }, { "cardClick": "cardClick"; }, never, never, true, never>;
|
|
4020
|
+
}
|
|
4021
|
+
|
|
4022
|
+
/** Vocabulario semántico único de tono, compartido con `TextTone`. */
|
|
4023
|
+
type StatTileTone = 'neutral' | 'ok' | 'warn' | 'error' | 'info';
|
|
4024
|
+
type StatTileVariant = 'inline' | 'card';
|
|
4025
|
+
/**
|
|
4026
|
+
* Indicador numérico compacto: label + valor grande con ícono, para grillas
|
|
4027
|
+
* densas de KPIs. Con `cta` en vez de `value` se convierte en un bloque de
|
|
4028
|
+
* llamada a la acción (título, descripción y link, sin tono ni número).
|
|
4029
|
+
* `variant="inline"` quita fondo/borde para una fila horizontal donde el
|
|
4030
|
+
* consumidor pone sus propios separadores.
|
|
4031
|
+
*
|
|
4032
|
+
* `hasProjectedValue` reemplaza el valor de texto por contenido proyectado
|
|
4033
|
+
* (`[valueContent]`) cuando el dato no es un número/string medido — el
|
|
4034
|
+
* caso típico es un estado con su propio `kf-badge`, que necesita su
|
|
4035
|
+
* tono/dot propios en vez del tono del tile:
|
|
4036
|
+
*
|
|
4037
|
+
* <kf-stat-tile label="Estado" hasProjectedValue>
|
|
4038
|
+
* <kf-badge valueContent [tone]="stateTone()">{{ stateLabel() }}</kf-badge>
|
|
4039
|
+
* </kf-stat-tile>
|
|
2078
4040
|
*/
|
|
2079
4041
|
declare class StatTile {
|
|
2080
4042
|
readonly cta: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
2081
4043
|
readonly label: _angular_core.InputSignal<string | undefined>;
|
|
2082
4044
|
readonly value: _angular_core.InputSignal<string | number | undefined>;
|
|
2083
4045
|
readonly icon: _angular_core.InputSignal<IconName | undefined>;
|
|
4046
|
+
readonly variant: _angular_core.InputSignal<StatTileVariant>;
|
|
4047
|
+
readonly tone: _angular_core.InputSignal<StatTileTone>;
|
|
4048
|
+
/** Tercera línea bajo el valor. Vacía cae en `notePlaceholder`. */
|
|
4049
|
+
readonly note: _angular_core.InputSignal<string | null | undefined>;
|
|
4050
|
+
/** Qué dice la tercera línea cuando no hay nota. Cada consumidor debería
|
|
4051
|
+
* pasar el motivo real de SU dato ausente, no un texto genérico. */
|
|
4052
|
+
readonly notePlaceholder: _angular_core.InputSignal<string | undefined>;
|
|
4053
|
+
/** Rinde el valor como ausencia, no como dato medido. */
|
|
4054
|
+
readonly valueMissing: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
4055
|
+
/** El valor es contenido proyectado (`<ng-content select="[valueContent]">`,
|
|
4056
|
+
* ej. un `kf-badge` de estado) en vez del texto de `value()` — para un
|
|
4057
|
+
* dato cuya representación natural no es un número/string medido. */
|
|
4058
|
+
readonly hasProjectedValue: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
2084
4059
|
readonly title: _angular_core.InputSignal<string | undefined>;
|
|
2085
4060
|
readonly description: _angular_core.InputSignal<string | undefined>;
|
|
2086
4061
|
readonly actionLabel: _angular_core.InputSignal<string | undefined>;
|
|
2087
4062
|
readonly actionHref: _angular_core.InputSignal<string | undefined>;
|
|
4063
|
+
/** Texto real de la nota. `null` significa "no reservar la línea": pasa
|
|
4064
|
+
* solo cuando el consumidor no declaró ni nota ni placeholder. */
|
|
4065
|
+
protected readonly noteText: _angular_core.Signal<string | null>;
|
|
4066
|
+
protected readonly noteIsPlaceholder: _angular_core.Signal<boolean>;
|
|
2088
4067
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<StatTile, never>;
|
|
2089
|
-
static ɵcmp: _angular_core.ɵɵComponentDeclaration<StatTile, "kf-stat-tile", never, { "cta": { "alias": "cta"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; "description": { "alias": "description"; "required": false; "isSignal": true; }; "actionLabel": { "alias": "actionLabel"; "required": false; "isSignal": true; }; "actionHref": { "alias": "actionHref"; "required": false; "isSignal": true; }; }, {}, never,
|
|
4068
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<StatTile, "kf-stat-tile", never, { "cta": { "alias": "cta"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "tone": { "alias": "tone"; "required": false; "isSignal": true; }; "note": { "alias": "note"; "required": false; "isSignal": true; }; "notePlaceholder": { "alias": "notePlaceholder"; "required": false; "isSignal": true; }; "valueMissing": { "alias": "valueMissing"; "required": false; "isSignal": true; }; "hasProjectedValue": { "alias": "hasProjectedValue"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; "description": { "alias": "description"; "required": false; "isSignal": true; }; "actionLabel": { "alias": "actionLabel"; "required": false; "isSignal": true; }; "actionHref": { "alias": "actionHref"; "required": false; "isSignal": true; }; }, {}, never, ["[valueContent]"], true, never>;
|
|
2090
4069
|
}
|
|
2091
4070
|
|
|
2092
4071
|
/**
|
|
@@ -2110,11 +4089,9 @@ interface TabOption {
|
|
|
2110
4089
|
icon?: IconName;
|
|
2111
4090
|
}
|
|
2112
4091
|
/**
|
|
2113
|
-
* Barra de tabs subrayada: un
|
|
2114
|
-
*
|
|
2115
|
-
*
|
|
2116
|
-
* `offsetLeft`/`offsetWidth` de la pestaña activa después de cada render y la
|
|
2117
|
-
* transición del `transform`/`width` queda a cargo de CSS.
|
|
4092
|
+
* Barra de tabs subrayada: un indicador se desliza hasta la pestaña activa.
|
|
4093
|
+
* La posición se mide (`offsetLeft`/`offsetWidth`) después de cada render,
|
|
4094
|
+
* ya que las pestañas tienen ancho de contenido variable.
|
|
2118
4095
|
*
|
|
2119
4096
|
* <kf-tabs [options]="tabs" [value]="active" (valueChange)="active = $event" />
|
|
2120
4097
|
*/
|
|
@@ -2124,19 +4101,178 @@ declare class Tabs {
|
|
|
2124
4101
|
readonly valueChange: _angular_core.OutputEmitterRef<string>;
|
|
2125
4102
|
private readonly track;
|
|
2126
4103
|
private readonly destroyRef;
|
|
2127
|
-
/** Geometría de la pestaña activa; `null` mientras no haya una medición válida. */
|
|
2128
4104
|
private readonly activeRect;
|
|
2129
4105
|
protected readonly indicatorTransform: _angular_core.Signal<string>;
|
|
2130
4106
|
protected readonly indicatorWidth: _angular_core.Signal<number>;
|
|
2131
4107
|
constructor();
|
|
2132
4108
|
private measure;
|
|
2133
4109
|
select(value: string): void;
|
|
2134
|
-
|
|
2135
|
-
onKeydown(event: KeyboardEvent, currentValue: string): void;
|
|
4110
|
+
protected onKeydown(event: KeyboardEvent, currentValue: string): void;
|
|
2136
4111
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<Tabs, never>;
|
|
2137
4112
|
static ɵcmp: _angular_core.ɵɵComponentDeclaration<Tabs, "kf-tabs", never, { "options": { "alias": "options"; "required": true; "isSignal": true; }; "value": { "alias": "value"; "required": true; "isSignal": true; }; }, { "valueChange": "valueChange"; }, never, never, true, never>;
|
|
2138
4113
|
}
|
|
2139
4114
|
|
|
4115
|
+
interface FolderTabOption {
|
|
4116
|
+
value: string;
|
|
4117
|
+
label: string;
|
|
4118
|
+
icon?: IconName;
|
|
4119
|
+
}
|
|
4120
|
+
/**
|
|
4121
|
+
* Marca el contenido que `kf-folder-tabs` debe proyectar dentro de su panel.
|
|
4122
|
+
*
|
|
4123
|
+
* <div kfFolderTabsPanel>...</div>
|
|
4124
|
+
*/
|
|
4125
|
+
declare class FolderTabsPanel {
|
|
4126
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FolderTabsPanel, never>;
|
|
4127
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<FolderTabsPanel, "[kfFolderTabsPanel]", never, {}, {}, never, never, true, never>;
|
|
4128
|
+
}
|
|
4129
|
+
/**
|
|
4130
|
+
* Tabs con forma de "carpeta": borde superior redondeado, la pestaña activa
|
|
4131
|
+
* se funde visualmente con el panel de contenido de abajo. El panel
|
|
4132
|
+
* (`kfFolderTabsPanel`) es opcional — sin él, se renderiza solo la fila de
|
|
4133
|
+
* pestañas.
|
|
4134
|
+
*
|
|
4135
|
+
* <kf-folder-tabs [options]="tabs" [value]="active" (valueChange)="active = $event">
|
|
4136
|
+
* <div kfFolderTabsPanel>contenido de la pestaña activa</div>
|
|
4137
|
+
* </kf-folder-tabs>
|
|
4138
|
+
*
|
|
4139
|
+
* <!-- o, sin contenido: -->
|
|
4140
|
+
* <kf-folder-tabs [options]="tabs" [value]="active" (valueChange)="active = $event" />
|
|
4141
|
+
*/
|
|
4142
|
+
declare class FolderTabs {
|
|
4143
|
+
readonly options: _angular_core.InputSignal<FolderTabOption[]>;
|
|
4144
|
+
readonly value: _angular_core.InputSignal<string>;
|
|
4145
|
+
readonly valueChange: _angular_core.OutputEmitterRef<string>;
|
|
4146
|
+
/** Id del panel único — todos los tabs lo referencian vía `aria-controls`. */
|
|
4147
|
+
protected readonly panelId: string;
|
|
4148
|
+
private readonly projectedPanel;
|
|
4149
|
+
protected readonly hasPanel: _angular_core.Signal<boolean>;
|
|
4150
|
+
select(value: string): void;
|
|
4151
|
+
protected onKeydown(event: KeyboardEvent, currentValue: string): void;
|
|
4152
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FolderTabs, never>;
|
|
4153
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<FolderTabs, "kf-folder-tabs", never, { "options": { "alias": "options"; "required": true; "isSignal": true; }; "value": { "alias": "value"; "required": true; "isSignal": true; }; }, { "valueChange": "valueChange"; }, ["projectedPanel"], ["[kfFolderTabsPanel]"], true, never>;
|
|
4154
|
+
}
|
|
4155
|
+
|
|
4156
|
+
/**
|
|
4157
|
+
* Textarea de formulario apilado: mismo patrón visual y de integración que
|
|
4158
|
+
* `kf-input` (label arriba, `ControlValueAccessor`, funciona con
|
|
4159
|
+
* `[(ngModel)]` y con Reactive Forms) para el caso de texto libre
|
|
4160
|
+
* multilínea (notas, comentarios, descripciones).
|
|
4161
|
+
*
|
|
4162
|
+
* <kf-textarea label="Notas" formControlName="notes" />
|
|
4163
|
+
* <kf-textarea label="Comentario" [(ngModel)]="comment" [minRows]="2" [maxRows]="10" />
|
|
4164
|
+
*
|
|
4165
|
+
* `minRows`/`maxRows` (3 y 6 por defecto) acotan el crecimiento: el campo
|
|
4166
|
+
* crece con el contenido pero nunca pasa de `maxRows` — superado ese tope el
|
|
4167
|
+
* contenido hace scroll interno (`overflow-y: auto`) en vez de seguir
|
|
4168
|
+
* empujando el layout. Reemplaza el `resize: vertical` nativo (deshabilitado
|
|
4169
|
+
* a propósito) porque un resize manual sin tope es exactamente el problema
|
|
4170
|
+
* que motivó este componente: un `<textarea>` a mano podía crecer hasta
|
|
4171
|
+
* tapar los botones del footer de un modal.
|
|
4172
|
+
*
|
|
4173
|
+
* <kf-textarea label="Detalle" formControlName="detail" [maxRows]="12" />
|
|
4174
|
+
*
|
|
4175
|
+
* `invalid` + `errorMessage` y `readonly`/`disabled` siguen el mismo
|
|
4176
|
+
* contrato que `kf-input` — ver su JSDoc para la diferencia entre ambos
|
|
4177
|
+
* estados.
|
|
4178
|
+
*/
|
|
4179
|
+
declare class Textarea implements ControlValueAccessor, AfterViewInit {
|
|
4180
|
+
private static nextId;
|
|
4181
|
+
private readonly nativeTextarea;
|
|
4182
|
+
readonly label: _angular_core.InputSignal<string | undefined>;
|
|
4183
|
+
/** Marca el label con un asterisco visual y aplica `required` al
|
|
4184
|
+
* `<textarea>` nativo — el atributo HTML ya expone el estado obligatorio a
|
|
4185
|
+
* lectores de pantalla (mapeo implícito a `aria-required`), sin necesidad
|
|
4186
|
+
* de un `aria-required` explícito redundante. */
|
|
4187
|
+
readonly required: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
4188
|
+
readonly hint: _angular_core.InputSignal<string | undefined>;
|
|
4189
|
+
/** Borde y aria-invalid en rojo. Ver `errorMessage` para el mensaje asociado. */
|
|
4190
|
+
readonly invalid: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
4191
|
+
/** Mensaje de error con `role="alert"` — reemplaza a `hint` mientras esté presente. */
|
|
4192
|
+
readonly errorMessage: _angular_core.InputSignal<string | undefined>;
|
|
4193
|
+
readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
4194
|
+
/** Ver JSDoc de `kf-input` para la diferencia con `disabled`. */
|
|
4195
|
+
readonly readonly: _angular_core.InputSignalWithTransform<boolean, unknown>;
|
|
4196
|
+
readonly placeholder: _angular_core.InputSignal<string>;
|
|
4197
|
+
/** Filas mínimas visibles en reposo — define la altura de arranque del campo. */
|
|
4198
|
+
readonly minRows: _angular_core.InputSignal<number>;
|
|
4199
|
+
/** Filas máximas antes de que el contenido pase a scroll interno en vez de seguir creciendo. */
|
|
4200
|
+
readonly maxRows: _angular_core.InputSignal<number>;
|
|
4201
|
+
protected readonly textareaId: string;
|
|
4202
|
+
protected readonly hintId: string;
|
|
4203
|
+
protected readonly errorId: string;
|
|
4204
|
+
protected readonly value: _angular_core.WritableSignal<string>;
|
|
4205
|
+
protected readonly focused: _angular_core.WritableSignal<boolean>;
|
|
4206
|
+
/** Deshabilitado por Reactive Forms (formControl.disable()). */
|
|
4207
|
+
private readonly disabledByForm;
|
|
4208
|
+
/** Disabled real: por input() directo o por el FormControl asociado. */
|
|
4209
|
+
protected readonly isDisabled: _angular_core.Signal<boolean>;
|
|
4210
|
+
private onChange;
|
|
4211
|
+
private onTouched;
|
|
4212
|
+
ngAfterViewInit(): void;
|
|
4213
|
+
writeValue(value: string): void;
|
|
4214
|
+
registerOnChange(fn: (value: string) => void): void;
|
|
4215
|
+
registerOnTouched(fn: () => void): void;
|
|
4216
|
+
setDisabledState(isDisabled: boolean): void;
|
|
4217
|
+
protected onInput(rawValue: string): void;
|
|
4218
|
+
protected onBlur(): void;
|
|
4219
|
+
/**
|
|
4220
|
+
* Recalcula la altura según el contenido real (`scrollHeight`), acotada
|
|
4221
|
+
* entre `minRows` y `maxRows` medidos con el line-height ya calculado del
|
|
4222
|
+
* propio elemento — así el tope se mantiene correcto sin importar qué
|
|
4223
|
+
* font-size/line-height termine aplicando cada tema. Superado `maxRows`
|
|
4224
|
+
* el resto del contenido hace scroll interno en vez de seguir creciendo.
|
|
4225
|
+
*/
|
|
4226
|
+
private adjustHeight;
|
|
4227
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<Textarea, never>;
|
|
4228
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<Textarea, "kf-textarea", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "hint": { "alias": "hint"; "required": false; "isSignal": true; }; "invalid": { "alias": "invalid"; "required": false; "isSignal": true; }; "errorMessage": { "alias": "errorMessage"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "readonly": { "alias": "readonly"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "minRows": { "alias": "minRows"; "required": false; "isSignal": true; }; "maxRows": { "alias": "maxRows"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
4229
|
+
}
|
|
4230
|
+
|
|
4231
|
+
/**
|
|
4232
|
+
* Da forma al valor de un campo MIENTRAS el usuario escribe — ej.
|
|
4233
|
+
* inserta el guion de un RUT chileno automáticamente, sin que el
|
|
4234
|
+
* usuario tenga que tipearlo — reposicionando el cursor para que
|
|
4235
|
+
* seguir escribiendo se sienta natural (sin esto, cualquier inserción
|
|
4236
|
+
* de caracteres empuja el cursor al final del campo en cada tecla).
|
|
4237
|
+
*
|
|
4238
|
+
* Se aplica junto a `formControlName`/`[formControl]`/`[(ngModel)]`
|
|
4239
|
+
* sobre cualquier input que implemente `ControlValueAccessor` (incluye
|
|
4240
|
+
* `kf-input`, pero no depende de él — funciona igual sobre un
|
|
4241
|
+
* `<input>` nativo):
|
|
4242
|
+
*
|
|
4243
|
+
* <kf-input formControlName="documentId" [kfFormatInput]="formatChileanRut" />
|
|
4244
|
+
*
|
|
4245
|
+
* La función de formateo recibe el valor crudo tecleado y devuelve el
|
|
4246
|
+
* valor con forma — debe ser determinística e idempotente (aplicarla
|
|
4247
|
+
* dos veces seguidas da el mismo resultado), como `formatChileanRut`
|
|
4248
|
+
* en este mismo archivo (`./format.ts`).
|
|
4249
|
+
*/
|
|
4250
|
+
declare class FormatInputDirective {
|
|
4251
|
+
private readonly ngControl;
|
|
4252
|
+
/** La función de formateo a aplicar — sin valor, el directive no
|
|
4253
|
+
* hace nada (permite pasar `undefined` condicionalmente sin tener
|
|
4254
|
+
* que quitar el directive del template entero). */
|
|
4255
|
+
readonly kfFormatInput: _angular_core.InputSignal<((value: string) => string) | undefined>;
|
|
4256
|
+
protected onInput(event: Event): void;
|
|
4257
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<FormatInputDirective, never>;
|
|
4258
|
+
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<FormatInputDirective, "[kfFormatInput]", never, { "kfFormatInput": { "alias": "kfFormatInput"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
|
|
4259
|
+
}
|
|
4260
|
+
|
|
4261
|
+
/**
|
|
4262
|
+
* Grilla de tiles que escalona columnas solo entre DIVISORES de `columns`
|
|
4263
|
+
* (4 → 2 → 1, nunca 3), para que ninguna fila quede huérfana. Mide el
|
|
4264
|
+
* contenedor (`container-type: inline-size`), no el viewport, porque el
|
|
4265
|
+
* mismo bloque se rinde tanto en un drawer angosto como en una página
|
|
4266
|
+
* completa bajo el mismo viewport.
|
|
4267
|
+
*/
|
|
4268
|
+
declare class TileGrid {
|
|
4269
|
+
/** Columnas en el ancho máximo. Se acota a 1-6: por encima de 6 un tile
|
|
4270
|
+
* deja de tener ancho para su propio label. */
|
|
4271
|
+
readonly columns: _angular_core.InputSignalWithTransform<number, unknown>;
|
|
4272
|
+
static ɵfac: _angular_core.ɵɵFactoryDeclaration<TileGrid, never>;
|
|
4273
|
+
static ɵcmp: _angular_core.ɵɵComponentDeclaration<TileGrid, "kf-tile-grid", never, { "columns": { "alias": "columns"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
|
|
4274
|
+
}
|
|
4275
|
+
|
|
2140
4276
|
/**
|
|
2141
4277
|
* Visual tone of a toast. Maps to the state tokens in
|
|
2142
4278
|
* projects/ui/src/tokens/base.css. `loading` has no auto-dismiss and shows an
|
|
@@ -2145,12 +4281,21 @@ declare class Tabs {
|
|
|
2145
4281
|
* semantic role to "work in progress".
|
|
2146
4282
|
*/
|
|
2147
4283
|
type ToastTone = 'success' | 'error' | 'warning' | 'info' | 'loading';
|
|
4284
|
+
/** Acción opcional (call-to-action) renderizada dentro del toast, junto al botón de cerrar. */
|
|
4285
|
+
interface ToastAction {
|
|
4286
|
+
/** Label corto, ej. "Ver ticket" — 1-3 palabras, el toast mide solo 360px de ancho. */
|
|
4287
|
+
label: string;
|
|
4288
|
+
/** Se invoca al hacer click; el toast se cierra solo después, sin que el llamador tenga que llamar `dismiss()`. */
|
|
4289
|
+
onClick: () => void;
|
|
4290
|
+
}
|
|
2148
4291
|
/** Options for {@link ToastService.show}. */
|
|
2149
4292
|
interface ToastOptions {
|
|
2150
4293
|
/** Color tone. Defaults to `'info'`. */
|
|
2151
4294
|
tone?: ToastTone;
|
|
2152
4295
|
/** Milliseconds before auto-dismiss. Ignored when `tone` is `'loading'`. Defaults to `2800`. */
|
|
2153
4296
|
duration?: number;
|
|
4297
|
+
/** Botón de acción opcional, ej. ir al registro sobre el que avisa el toast. */
|
|
4298
|
+
action?: ToastAction;
|
|
2154
4299
|
}
|
|
2155
4300
|
/** Messages for each leg of {@link ToastService.promise}. */
|
|
2156
4301
|
interface ToastPromiseMessages<T = unknown> {
|
|
@@ -2165,7 +4310,12 @@ interface ToastState {
|
|
|
2165
4310
|
tone: ToastTone;
|
|
2166
4311
|
duration: number;
|
|
2167
4312
|
leaving: boolean;
|
|
4313
|
+
action?: ToastAction;
|
|
2168
4314
|
}
|
|
4315
|
+
/** What's currently holding the auto-dismiss timer paused. Tracked as a set so hover and
|
|
4316
|
+
* focus can overlap (e.g. mouse leaves while focus is still on the action button) without
|
|
4317
|
+
* resuming the timer early. */
|
|
4318
|
+
type ToastPauseSource = 'hover' | 'focus';
|
|
2169
4319
|
/**
|
|
2170
4320
|
* Shows one toast at a time — a new `show()` call replaces whatever is
|
|
2171
4321
|
* currently up (this mirrors a global status line, not a notification stack).
|
|
@@ -2199,10 +4349,31 @@ declare class ToastService {
|
|
|
2199
4349
|
private nextId;
|
|
2200
4350
|
private dismissTimer?;
|
|
2201
4351
|
private removeTimer?;
|
|
4352
|
+
/** Wall-clock time the running `dismissTimer` is due to fire; used to compute how much
|
|
4353
|
+
* time is left when {@link pause} interrupts it. */
|
|
4354
|
+
private dismissDueAt?;
|
|
4355
|
+
/** Time left on the auto-dismiss countdown while paused. `undefined` when no countdown
|
|
4356
|
+
* is currently paused. */
|
|
4357
|
+
private remainingMs?;
|
|
4358
|
+
private readonly pauseSources;
|
|
2202
4359
|
/** The single active toast, or `null`. */
|
|
2203
4360
|
readonly current: _angular_core.Signal<ToastState | null>;
|
|
2204
4361
|
/** Shows a toast, replacing whatever is currently shown. Returns its id. */
|
|
2205
4362
|
show(message: string, options?: ToastOptions): number;
|
|
4363
|
+
private scheduleDismiss;
|
|
4364
|
+
/**
|
|
4365
|
+
* Pauses the auto-dismiss countdown for `id` while `source` (pointer hover or focus) is
|
|
4366
|
+
* active on it, so a user reaching for the action/dismiss button never loses the toast
|
|
4367
|
+
* mid-interaction (WCAG 2.2.1). No-op when `id` isn't the active toast, it's leaving, or
|
|
4368
|
+
* it has no running countdown (persistent or `loading` toasts, or already paused by
|
|
4369
|
+
* another source).
|
|
4370
|
+
*/
|
|
4371
|
+
pause(id: number, source: ToastPauseSource): void;
|
|
4372
|
+
/**
|
|
4373
|
+
* Resumes a countdown paused by {@link pause}, honoring the time already elapsed before
|
|
4374
|
+
* the pause. No-op while another pause `source` is still active, or nothing is paused.
|
|
4375
|
+
*/
|
|
4376
|
+
resume(id: number, source: ToastPauseSource): void;
|
|
2206
4377
|
/**
|
|
2207
4378
|
* Starts the exit animation, then removes the toast. No-op when `id` is no
|
|
2208
4379
|
* longer the active toast, so a stale timer never kills a newer toast.
|
|
@@ -2230,6 +4401,13 @@ declare class ToastHost {
|
|
|
2230
4401
|
protected readonly toastList: _angular_core.Signal<ToastState[]>;
|
|
2231
4402
|
protected toastIcon(tone: ToastTone): IconName;
|
|
2232
4403
|
protected dismiss(id: number): void;
|
|
4404
|
+
/** Pauses the toast's auto-dismiss timer while the pointer or keyboard focus is on it. */
|
|
4405
|
+
protected pause(t: ToastState, source: ToastPauseSource): void;
|
|
4406
|
+
/** Resumes the toast's auto-dismiss timer once neither the pointer nor focus is on it. */
|
|
4407
|
+
protected resume(t: ToastState, source: ToastPauseSource): void;
|
|
4408
|
+
/** Ejecuta la acción del toast (ej. "Ver ticket") y lo cierra — el
|
|
4409
|
+
* llamador no tiene que acordarse de descartarlo desde su propio handler. */
|
|
4410
|
+
protected runAction(t: ToastState): void;
|
|
2233
4411
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<ToastHost, never>;
|
|
2234
4412
|
static ɵcmp: _angular_core.ɵɵComponentDeclaration<ToastHost, "kf-toast-host", never, {}, {}, never, never, true, never>;
|
|
2235
4413
|
}
|
|
@@ -2254,9 +4432,29 @@ declare class TooltipDirective {
|
|
|
2254
4432
|
private readonly overlayService;
|
|
2255
4433
|
private readonly viewContainerRef;
|
|
2256
4434
|
private readonly injector;
|
|
4435
|
+
private readonly contentId;
|
|
2257
4436
|
private overlayRef;
|
|
4437
|
+
/**
|
|
4438
|
+
* `focusin` (no `focus`) a propósito: `focus` no burbujea, así que un
|
|
4439
|
+
* disparador que ENVUELVE al elemento realmente enfocable (ej.
|
|
4440
|
+
* `<kf-button>`, cuyo `<button>` nativo vive en el template del hijo, fuera
|
|
4441
|
+
* de alcance de este host) nunca vería el evento — el tooltip quedaría
|
|
4442
|
+
* mudo por teclado justo en el caso de uso que más lo necesita (un botón
|
|
4443
|
+
* solo-ícono). `focusin` SÍ burbujea y sigue disparando igual para el caso
|
|
4444
|
+
* ya cubierto (un `<a>`/`<button>` nativo que es él mismo el host, ver
|
|
4445
|
+
* `Tooltip.spec.ts`) — sin regresión ahí.
|
|
4446
|
+
*/
|
|
2258
4447
|
protected show(): void;
|
|
2259
4448
|
protected hide(): void;
|
|
4449
|
+
/**
|
|
4450
|
+
* `focusout` (no `blur`, mismo motivo que `show()` usa `focusin`): además,
|
|
4451
|
+
* a diferencia de `mouseleave`/Escape, acá SÍ hace falta revisar
|
|
4452
|
+
* `relatedTarget` — si el host envuelve más de un elemento enfocable, un
|
|
4453
|
+
* foco que se mueve de uno a otro DENTRO del mismo host no debería cerrar
|
|
4454
|
+
* el tooltip (no "salió" del disparador). `Node` en vez de `EventTarget`:
|
|
4455
|
+
* `Node.contains()` es lo que existe para chequear pertenencia al árbol.
|
|
4456
|
+
*/
|
|
4457
|
+
protected onFocusOut(event: FocusEvent): void;
|
|
2260
4458
|
private positionsFor;
|
|
2261
4459
|
static ɵfac: _angular_core.ɵɵFactoryDeclaration<TooltipDirective, never>;
|
|
2262
4460
|
static ɵdir: _angular_core.ɵɵDirectiveDeclaration<TooltipDirective, "[kfTooltip]", never, { "text": { "alias": "kfTooltip"; "required": false; }; "kfTooltipPosition": { "alias": "kfTooltipPosition"; "required": false; }; }, {}, never, never, true, never>;
|
|
@@ -2320,5 +4518,5 @@ declare class ViewToggle {
|
|
|
2320
4518
|
static ɵcmp: _angular_core.ɵɵComponentDeclaration<ViewToggle, "kf-view-toggle", never, { "view": { "alias": "view"; "required": true; "isSignal": true; }; }, { "viewChange": "viewChange"; }, never, never, true, never>;
|
|
2321
4519
|
}
|
|
2322
4520
|
|
|
2323
|
-
export { ActionTile, AdminBadge, Alert, AuthLayout, Avatar, Badge, Breadcrumb, Button, Card, Carousel, CarouselSlide, Checkbox, Chip, CompanySelector, ContentSlot, ContentState, DataTable, Divider, Drawer, DrawerLogout, DrawerSession, Dropdown, DropdownItem, DropdownPanel, EmptyState, FeatureCard, Field, FieldLabel, FilterChips, FolderTabs, FolderTabsPanel, Footer, Icon, Input, Link, MetricCard, Modal, MultiSelect, PageHeader, Pagination, Panel, PortalHeader, ReserveBlock, SectionHeader, Segmented, Select,
|
|
2324
|
-
export type { AlertTone, BreadcrumbItem, CompanySelectorMeta, ContentStateKind, ContentStateLoad, DataTableCardRole, DataTableColumn, DataTableView, DividerOrientation, DrawerSide, DropdownItemTone, DropdownPanelVariant, FolderTabOption, FooterLink, IconName, IconSize, LinkVariant, MultiSelectOption, PageHeaderTitleSize, PaginationItem, PortalHeaderAdminAction, PortalHeaderNavItem, SegmentedOption, SidebarNavItemData, SkeletonVariant, SpinnerSize, TabOption, TextAs, TextTone, TextVariant, ToastOptions, ToastPromiseMessages, ToastState, ToastTone, TooltipPosition, VehicleHeaderStatus, ViewToggleOption };
|
|
4521
|
+
export { ActionTile, AdminBadge, Alert, AlertRow, AuthLayout, AuthLayoutSso, Avatar, Badge, Breadcrumb, BucketProgress, Button, Card, Carousel, CarouselSlide, Checkbox, Chip, Collapsible, CompanySelector, ContentSlot, ContentState, ContentViewer, DEFAULT_PAGE_SIZE_OPTIONS, DataTable, Divider, DocumentRow, Drawer, DrawerLogout, DrawerSession, Dropdown, DropdownItem, DropdownPanel, EmptyState, EventTimeline, FeatureCard, Field, FieldLabel, Filter, FilterChips, FolderTabs, FolderTabsPanel, Footer, FormatInputDirective, Icon, Input, JourneyStepper, Link, MAP_CONFIG, MapControls, MapView, MetricCard, MobileFooter, MobileHeader, Modal, MultiSelect, NoteBlock, PageHeader, Pagination, Panel, PortalHeader, Progress, PulseDot, QuotePicker, ReserveBlock, STYLE_BY_THEME, SectionHeader, SectionNav, SectionNavItem, SectionNavSubsection, Segmented, Select, SelectableCardGroup, SidebarShell, Skeleton, Spinner, SsoButton, StatCard, StatTile, StatusPill, THEME_LABEL, THEME_ORDER, Tabs, Text, Textarea, TileGrid, ToastHost, ToastService, TooltipDirective, VehicleHeader, ViewToggle, dataTableViewSignal, formatChileanRut, formatClockTime, formatCurrency, formatNumber, isValidChileanRut, matchMediaSignal, parseCurrency, parseNumber, provideMapConfig, sanitizeDocumentId };
|
|
4522
|
+
export type { AlertRowTone, AlertTone, AuthLayoutBackground, BreadcrumbItem, BucketProgressItem, CompanySelectorMeta, ContentStateKind, ContentStateLoad, ContentViewerType, DataTableCardRole, DataTableColumn, DataTableDensity, DataTableSort, DataTableView, DividerOrientation, DocumentRowTone, DrawerSide, DropdownItemTone, DropdownPanelVariant, EventTimelineItem, EventTimelineTone, FilterFieldChange, FilterFieldConfig, FilterFieldType, FilterValue, FolderTabOption, FooterLink, FormatCurrencyOptions, FormatNumberOptions, IconName, IconRotation, IconSize, JourneyStep, JourneyStepStatus, JourneyStepperColorScheme, JourneyStepperSurface, LinkVariant, MapConfig, MapControlKind, MapLatLng, MapMarker, MapMarkerTone, MapPopupAction, MapSize, MapTheme, MapVariant, MultiSelectOption, NoteBlockTone, PageHeaderTitleSize, PaginationItem, PortalHeaderAdminAction, PortalHeaderNavItem, ProgressTone, QuotePickerChip, QuotePickerSortOption, SectionNavSubsectionData, SegmentedOption, SelectableCardOption, SidebarNavItemData, SkeletonVariant, SpinnerSize, SsoProvider, StatCardTone, StatTileTone, StatTileVariant, TabOption, TextAs, TextTone, TextVariant, ToastAction, ToastOptions, ToastPromiseMessages, ToastState, ToastTone, TooltipPosition, VehicleHeaderStatus, ViewToggleOption };
|