@kaufmann-lab/ui 0.1.16 → 0.1.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,4620 +1,5 @@
1
- import * as _angular_core from '@angular/core';
2
- import { AfterContentInit, TemplateRef, Signal, ElementRef, AfterViewInit, InjectionToken, Provider } from '@angular/core';
3
- import { ControlValueAccessor } from '@angular/forms';
4
-
5
- /**
6
- * Nombre de cada ícono del design system. Página 5 del PDF fija un set de
7
- * 59; `eye-off` es una extensión propia por fuera de ese set, agregada para
8
- * el toggle de mostrar/ocultar contraseña de `Input` — no hay un ícono de
9
- * "ojo tachado" en la spec original porque esa interacción no estaba
10
- * contemplada ahí.
11
- *
12
- * Separado de `Icon.ts` a propósito: este type es el contrato público que
13
- * comparten `Icon`, `icons/index.ts` (registro) y cualquier componente que
14
- * reciba un `IconName` (`Button`, `MetricCard`, etc.) — no debería requerir
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.
33
- */
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' | 'mail' | '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';
35
-
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;
43
- /**
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,
46
- * grosor de trazo 1.9px. Referencia un sprite SVG embebido (ver
47
- * `icon-sprite.ts`) vía `<use>`, así que no hay peso de red adicional por
48
- * cada instancia: el sprite se inyecta al DOM una única vez, la primera
49
- * vez que se renderiza cualquier `<kf-icon>` de toda la aplicación.
50
- *
51
- * El tamaño se controla con `size` (página 6: "el tamaño se controla con la
52
- * prop size"): `sm` (16px), `md` (20px, default) o `lg` (24px). El color
53
- * sigue siempre `currentColor` — se hereda del texto del elemento
54
- * contenedor, nunca se fija en el propio ícono.
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
- *
62
- * <kf-icon name="alert-triangle" size="sm" />
63
- * <kf-icon name="chevron-down" [rotate]="isOpen() ? 180 : 0" />
64
- * <kf-button icon="calendar">Reservar hora</kf-button> <!-- ver Button -->
65
- */
66
- declare class Icon {
67
- readonly name: _angular_core.InputSignal<IconName>;
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>;
81
- private readonly document;
82
- protected readonly pixelSize: _angular_core.Signal<string>;
83
- constructor();
84
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<Icon, 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>;
86
- }
87
-
88
- /**
89
- * Dos variantes del mismo concepto — acceso rápido a una sección (página
90
- * 35): "Tile de acceso destacado" (`variant="tile"`, default) es un bloque
91
- * centrado con ícono grande arriba y título abajo, pensado para una
92
- * cuadrícula de accesos (ej. "Todos los certificados"). "Fila de documento"
93
- * (`variant="row"`) es horizontal, con ícono a la izquierda y
94
- * título+descripción a la derecha, pensada para listas verticales dentro de
95
- * un `kf-panel` (ej. "Pagos" / "Revisa tu estado de cuenta y realiza pagos.").
96
- *
97
- * <kf-action-tile icon="shield-check" title="Todos los certificados" />
98
- * <kf-action-tile variant="row" icon="download" title="Pagos"
99
- * description="Revisa tu estado de cuenta y realiza pagos." />
100
- */
101
- declare class ActionTile {
102
- readonly variant: _angular_core.InputSignal<"tile" | "row">;
103
- readonly icon: _angular_core.InputSignal<IconName>;
104
- readonly title: _angular_core.InputSignal<string>;
105
- readonly description: _angular_core.InputSignal<string | undefined>;
106
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<ActionTile, never>;
107
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<ActionTile, "kf-action-tile", never, { "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": true; "isSignal": true; }; "title": { "alias": "title"; "required": true; "isSignal": true; }; "description": { "alias": "description"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
108
- }
109
-
110
- /**
111
- * Marca la barra superior como chrome exclusivo de administradores (página
112
- * 18) — acento ámbar para diferenciar contexto privilegiado del resto del
113
- * portal. Dos variantes: etiqueta aislada (pill), o `inline` para usarse
114
- * dentro de la franja de `kf-portal-header` (fondo oscuro, texto ámbar).
115
- *
116
- * <kf-admin-badge />
117
- * <kf-admin-badge inline />
118
- */
119
- declare class AdminBadge {
120
- /** Para usarse dentro de una superficie oscura (ej. la franja de `kf-portal-header`). */
121
- readonly inline: _angular_core.InputSignalWithTransform<boolean, unknown>;
122
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<AdminBadge, never>;
123
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<AdminBadge, "kf-admin-badge", never, { "inline": { "alias": "inline"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
124
- }
125
-
126
- /**
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.
131
- *
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.
135
- *
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í).
141
- *
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.
146
- *
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()" />
158
- * <kf-button type="submit" [loading]="submitting()">Ingresar</kf-button>
159
- * <kf-button variant="surface" shape="circle" icon="plus" ariaLabel="Acercar" [ariaPressed]="false" />
160
- */
161
- declare class Button {
162
- readonly variant: _angular_core.InputSignal<"primary" | "secondary" | "ghost" | "outline" | "danger" | "surface">;
163
- readonly size: _angular_core.InputSignal<"sm" | "md" | "lg">;
164
- readonly shape: _angular_core.InputSignal<"rect" | "circle">;
165
- readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
166
- /** Igual al default nativo: hace submit dentro de un `<form>`. */
167
- readonly type: _angular_core.InputSignal<"button" | "submit" | "reset">;
168
- /** Ícono líder, renderizado con `kf-icon` antes del label proyectado. */
169
- readonly icon: _angular_core.InputSignal<IconName | undefined>;
170
- /** Estira el botón a todo el ancho de su contenedor. */
171
- readonly block: _angular_core.InputSignalWithTransform<boolean, unknown>;
172
- /** Reemplaza el contenido proyectado por `kf-spinner` y fuerza `disabled`. */
173
- readonly loading: _angular_core.InputSignalWithTransform<boolean, unknown>;
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`). */
191
- protected readonly hasSolidFill: _angular_core.Signal<boolean>;
192
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<Button, 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>;
194
- }
195
-
196
- type AlertTone = 'info' | 'ok' | 'warn' | 'error';
197
- /**
198
- * Banner contextual que vive dentro del flujo: validaciones, avisos de
199
- * vencimiento, confirmaciones. Persiste hasta que se descarta o hasta que la
200
- * condición que lo causó desaparece — para feedback efímero está el toast.
201
- *
202
- * `open` es two-way: el botón de cerrar lo pone en `false` y el componente
203
- * deja de renderizar. El consumidor puede volver a abrirlo poniéndolo en
204
- * `true` (ej. cuando la validación falla otra vez).
205
- *
206
- * <kf-alert tone="warn" title="SOAT por vencer">Vence el 15/08.</kf-alert>
207
- * <kf-alert tone="ok" [(open)]="saved" dismissible>Pago registrado.</kf-alert>
208
- */
209
- declare class Alert {
210
- readonly tone: _angular_core.InputSignal<AlertTone>;
211
- /** Línea en negrita arriba del mensaje. Vacío deja solo el contenido proyectado. */
212
- readonly title: _angular_core.InputSignal<string>;
213
- readonly dismissible: _angular_core.InputSignalWithTransform<boolean, unknown>;
214
- /** Visibilidad, two-way: `[(open)]="hasError"`. */
215
- readonly open: _angular_core.ModelSignal<boolean>;
216
- /**
217
- * `alert` interrumpe al lector de pantalla; se reserva para error/warn.
218
- * Los avisos informativos se anuncian sin cortar lo que se esté leyendo.
219
- */
220
- protected readonly ariaRole: _angular_core.Signal<"alert" | "status">;
221
- protected readonly toneIcon: _angular_core.Signal<IconName>;
222
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<Alert, never>;
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>;
224
- }
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';
265
- /**
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.
273
- *
274
- * <kf-auth-layout
275
- * brand="Mi Portal"
276
- * brandLogoSrc="images/mi-portal-logo-white.png"
277
- * brandLogoMobileSrc="images/mi-portal-logo-black.png"
278
- * brandTagline="Frase corta de marca, propia de cada portal."
279
- * >
280
- * <form (ngSubmit)="onSubmit()">
281
- * <kf-input label="Correo" type="email" />
282
- * <kf-input label="Contraseña" type="password" toggleable />
283
- * <kf-button type="submit">Ingresar</kf-button>
284
- * </form>
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>
330
- */
331
- declare class AuthLayout {
332
- /** Nombre de marca/portal — wordmark de respaldo si no hay logo, y `alt` de ambos logos. */
333
- readonly brand: _angular_core.InputSignal<string>;
334
- /** Logo en blanco para el panel oscuro de desktop. Sin esto, se muestra `brand()` como wordmark. */
335
- readonly brandLogoSrc: _angular_core.InputSignal<string | undefined>;
336
- /** Frase corta bajo el logo. */
337
- readonly brandTagline: _angular_core.InputSignal<string | undefined>;
338
- /** Logo compacto para mobile, sobre fondo claro. Sin esto, reutiliza `brandLogoSrc`. */
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>;
350
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<AuthLayout, never>;
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>;
352
- }
353
-
354
- /**
355
- * Avatar de usuario: muestra una foto (`src`) o, si no hay foto, las
356
- * iniciales (`initials`) sobre un fondo con gradiente de marca. `round`
357
- * controla la forma: circular (por defecto) o cuadrada con radio.
358
- *
359
- * Con `pill`, el avatar se envuelve en una pastilla azul con un chevron a la
360
- * derecha — la "pill de usuario (header)" de la página 18 del design system,
361
- * usada en la barra de sesión del Portal Header.
362
- *
363
- * <kf-avatar initials="JD" />
364
- * <kf-avatar src="https://…/foto.jpg" [round]="false" />
365
- * <kf-avatar initials="TS" pill />
366
- */
367
- declare class Avatar {
368
- readonly initials: _angular_core.InputSignal<string | undefined>;
369
- readonly src: _angular_core.InputSignal<string | undefined>;
370
- readonly round: _angular_core.InputSignal<boolean>;
371
- /** Envuelve el avatar en la pastilla de sesión del header, con chevron. */
372
- readonly pill: _angular_core.InputSignalWithTransform<boolean, unknown>;
373
- /** Limita a 2 caracteres para que quepan cómodos dentro del círculo. */
374
- readonly displayInitials: _angular_core.Signal<string>;
375
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<Avatar, never>;
376
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<Avatar, "kf-avatar", never, { "initials": { "alias": "initials"; "required": false; "isSignal": true; }; "src": { "alias": "src"; "required": false; "isSignal": true; }; "round": { "alias": "round"; "required": false; "isSignal": true; }; "pill": { "alias": "pill"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
377
- }
378
-
379
- /**
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.
391
- */
392
- declare class Badge {
393
- readonly tone: _angular_core.InputSignal<"neutral" | "ok" | "warn" | "error" | "info" | "service">;
394
- readonly variant: _angular_core.InputSignal<"soft" | "glass">;
395
- readonly dot: _angular_core.InputSignalWithTransform<boolean, unknown>;
396
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<Badge, 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>;
398
- }
399
-
400
- interface BreadcrumbItem {
401
- label: string;
402
- /** Destino del link. Sin `href` el nivel se rinde como texto plano. */
403
- href?: string;
404
- }
405
- /**
406
- * Ruta de navegación de las vistas internas. El último nivel es la página
407
- * actual: nunca es link y lleva `aria-current="page"`, aunque traiga `href`.
408
- *
409
- * Se rinde como `<nav><ol>` — es una lista ordenada de ancestros, y eso es lo
410
- * que los lectores de pantalla anuncian ("navegación, lista de 3 elementos").
411
- *
412
- * Para apps con router: escuchar `itemClick` y llamar
413
- * `event.preventDefault()` antes de `router.navigate(...)`. El `href` se
414
- * mantiene igual para que click-medio, "abrir en pestaña nueva" y el hover
415
- * de la barra de estado sigan funcionando.
416
- *
417
- * <kf-breadcrumb [items]="[{ label: 'Inicio', href: '/' }, { label: 'Mis vehículos' }]" />
418
- */
419
- declare class Breadcrumb {
420
- readonly items: _angular_core.InputSignal<BreadcrumbItem[]>;
421
- /**
422
- * Click en un nivel con `href`. Incluye el `MouseEvent` para que un
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.
426
- */
427
- readonly itemClick: _angular_core.OutputEmitterRef<{
428
- item: BreadcrumbItem;
429
- index: number;
430
- event: MouseEvent;
431
- }>;
432
- protected readonly lastIndex: _angular_core.Signal<number>;
433
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<Breadcrumb, never>;
434
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<Breadcrumb, "kf-breadcrumb", never, { "items": { "alias": "items"; "required": true; "isSignal": true; }; }, { "itemClick": "itemClick"; }, never, never, true, never>;
435
- }
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
-
495
- /**
496
- * Contenedor base con esquinas redondeadas y sombra sutil — la base visual
497
- * de paneles, tarjetas de métrica, tablas y bloques de contenido de todo el
498
- * portal (ver página 14 del design system).
499
- *
500
- * Piensa este componente como el "cimiento": no impone estructura interna
501
- * más allá de dos slots opcionales alrededor del contenido libre —
502
- * `[media]` para una imagen/ícono destacado arriba, y `[actions]` para una
503
- * fila de botones al pie, separada por una línea. El contenido por defecto
504
- * (sin selector) queda en el medio, sin ningún layout forzado, para que
505
- * cualquier composición (una métrica, una lista, un formulario) pueda vivir
506
- * dentro sin pelear con estilos propios del `Card`.
507
- *
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>
518
- * <kf-card flush>Sin sombra — para superficies ya delimitadas por otra card</kf-card>
519
- * <kf-card>
520
- * <img kf-card-media src="..." alt="" />
521
- * <h3>Título</h3>
522
- * <p>Descripción</p>
523
- * <div kf-card-actions>
524
- * <kf-button variant="ghost">Cancelar</kf-button>
525
- * <kf-button>Guardar</kf-button>
526
- * </div>
527
- * </kf-card>
528
- */
529
- declare class Card {
530
- /** Quita la sombra — para cards anidadas dentro de otra superficie ya elevada. */
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">;
534
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<Card, 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>;
536
- }
537
-
538
- /**
539
- * Slide individual de un `kf-carousel`. Es un contenedor mínimo: solo existe
540
- * para que el carrusel pueda detectar, vía `contentChildren`, cuántos slides
541
- * se proyectaron y en qué orden — el contenido real es responsabilidad total
542
- * de quien lo consume.
543
- *
544
- * <kf-carousel-slide>...</kf-carousel-slide>
545
- */
546
- declare class CarouselSlide {
547
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<CarouselSlide, never>;
548
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<CarouselSlide, "kf-carousel-slide", never, {}, {}, never, ["*"], true, never>;
549
- }
550
-
551
- /**
552
- * Carrusel de slides con flechas, dots y autoplay opcional.
553
- *
554
- * La spec original de React recibe `slides` como un array de nodos React.
555
- * En Angular eso no tiene equivalente directo: en vez de forzar un array de
556
- * datos crudos, cada slide se proyecta como contenido declarativo mediante
557
- * un `kf-carousel-slide` hijo, y el carrusel los detecta con
558
- * `contentChildren(CarouselSlide)`. Esto evita depender de un renderer de
559
- * nodos y deja que cada slide use plantillas Angular normales (bindings,
560
- * `@if`, `@for`, etc.) tal como se espera en este ecosistema.
561
- *
562
- * <kf-carousel [autoplay]="true" [interval]="4000" (indexChange)="onIndex($event)">
563
- * <kf-carousel-slide>...</kf-carousel-slide>
564
- * <kf-carousel-slide>...</kf-carousel-slide>
565
- * </kf-carousel>
566
- */
567
- declare class Carousel implements AfterContentInit {
568
- readonly autoplay: _angular_core.InputSignal<boolean>;
569
- readonly interval: _angular_core.InputSignalWithTransform<number, unknown>;
570
- readonly indexChange: _angular_core.OutputEmitterRef<number>;
571
- readonly slides: _angular_core.Signal<readonly CarouselSlide[]>;
572
- private readonly activeIndexSignal;
573
- readonly activeIndex: _angular_core.Signal<number>;
574
- private readonly destroyRef;
575
- private timer;
576
- private autoplayPaused;
577
- ngAfterContentInit(): void;
578
- next(): void;
579
- previous(): void;
580
- goTo(index: number): void;
581
- pauseAutoplay(): void;
582
- resumeAutoplay(): void;
583
- private startAutoplay;
584
- private stopAutoplay;
585
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<Carousel, never>;
586
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<Carousel, "kf-carousel", never, { "autoplay": { "alias": "autoplay"; "required": false; "isSignal": true; }; "interval": { "alias": "interval"; "required": false; "isSignal": true; }; }, { "indexChange": "indexChange"; }, ["slides"], ["*"], true, never>;
587
- }
588
-
589
- /**
590
- * Checkbox accesible con soporte de estado "indeterminate".
591
- *
592
- * `indeterminate` no existe como atributo HTML: el navegador solo lo expone
593
- * como propiedad del elemento nativo, así que se aplica a mano sobre el
594
- * `<input>` interno vía `viewChild` + `effect` cada vez que cambia el input.
595
- *
596
- * Implementa `ControlValueAccessor` para poder usarse con `formControlName` /
597
- * `ngModel` igual que un checkbox nativo. En ese modo, `writeValue` controla
598
- * el DOM directamente y el input `checked` (pensado para binding manual sin
599
- * forms) queda en su valor por defecto — igual que `disabled` combina el
600
- * input signal con `setDisabledState` en el resto de la librería.
601
- *
602
- * `invalid` pinta el recuadro en rojo y setea `aria-invalid` en el `<input>`
603
- * nativo — mismo mecanismo que `Input.invalid`, para el caso típico de un
604
- * checkbox obligatorio (ej. "Acepto términos y condiciones") sin marcar.
605
- *
606
- * <kf-checkbox label="Aceptar términos" [(checked)]="accepted" />
607
- * <kf-checkbox label="Seleccionar todo" [indeterminate]="isPartial()" />
608
- * <kf-checkbox label="Notificaciones" formControlName="notifications" />
609
- * <kf-checkbox label="Acepto los términos y condiciones" [invalid]="submitted() && !accepted()" />
610
- */
611
- declare class Checkbox implements ControlValueAccessor {
612
- readonly label: _angular_core.InputSignal<string | undefined>;
613
- readonly checked: _angular_core.InputSignalWithTransform<boolean, unknown>;
614
- readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
615
- readonly indeterminate: _angular_core.InputSignalWithTransform<boolean, unknown>;
616
- /** Recuadro en rojo + aria-invalid — ej. checkbox obligatorio sin marcar. */
617
- readonly invalid: _angular_core.InputSignalWithTransform<boolean, unknown>;
618
- readonly checkedChange: _angular_core.OutputEmitterRef<boolean>;
619
- private readonly inputRef;
620
- /** Callbacks registrados por Forms; no-op hasta que registerOnChange/Touched las reemplacen. */
621
- private onChangeFn;
622
- private onTouchedFn;
623
- constructor();
624
- onChange(event: Event): void;
625
- onTouched(): void;
626
- writeValue(value: boolean): void;
627
- registerOnChange(fn: (value: boolean) => void): void;
628
- registerOnTouched(fn: () => void): void;
629
- setDisabledState?(isDisabled: boolean): void;
630
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<Checkbox, never>;
631
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<Checkbox, "kf-checkbox", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "checked": { "alias": "checked"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "indeterminate": { "alias": "indeterminate"; "required": false; "isSignal": true; }; "invalid": { "alias": "invalid"; "required": false; "isSignal": true; }; }, { "checkedChange": "checkedChange"; }, never, never, true, never>;
632
- }
633
-
634
- /**
635
- * Chip de fondo suave con un punto líder que indica estado. Pensado
636
- * principalmente para celdas de tabla (estado de fila), pero utilizable
637
- * en cualquier lugar donde se necesite una etiqueta de estado compacta.
638
- *
639
- * <kf-chip tone="warn">Pendiente</kf-chip>
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).
648
- *
649
- * Nota: la spec original define la unión de tonos como "ok" | "warn" |
650
- * "error" | "neutral". Internamente se mapean a los tokens semánticos de
651
- * projects/ui/src/tokens/base.css: ok -> success, warn -> warning,
652
- * error -> error, neutral -> surface/border/text-muted (no existe un
653
- * token "neutral" dedicado en base.css, así que se arma con los tokens
654
- * de superficie/borde/texto-muted más cercanos).
655
- */
656
- declare class Chip {
657
- readonly tone: _angular_core.InputSignal<"neutral" | "ok" | "warn" | "error">;
658
- readonly dot: _angular_core.InputSignalWithTransform<boolean, unknown>;
659
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<Chip, 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>;
692
- }
693
-
694
- /** Metadato clave mostrado junto al selector (RUC, identificación, sucursal). */
695
- interface CompanySelectorMeta {
696
- label: string;
697
- value: string;
698
- }
699
- /**
700
- * Conmutador de empresa activa con metadatos clave (página 32) — encabeza
701
- * la pantalla de inicio del portal. Compone `kf-select` para el propio
702
- * selector (mismo panel propio, sin `<select>` nativo) y proyecta hasta 3
703
- * pares label/valor (RUC, Identificación, Sucursal en el ejemplo del PDF).
704
- *
705
- * <kf-company-selector
706
- * label="Selecciona empresa"
707
- * [companies]="['TRANSPORTES DEL SUR S.A.C.', 'ANDES MAQ LTDA.']"
708
- * [value]="activeCompany()"
709
- * (valueChange)="activeCompany.set($event)"
710
- * [meta]="[
711
- * { label: 'RUC', value: '20448712359' },
712
- * { label: 'Identificación', value: '0004002232' },
713
- * { label: 'Sucursal', value: 'Sucursal Norte' },
714
- * ]"
715
- * />
716
- */
717
- declare class CompanySelector {
718
- readonly label: _angular_core.InputSignal<string>;
719
- readonly companies: _angular_core.InputSignal<string[]>;
720
- readonly value: _angular_core.InputSignal<string | undefined>;
721
- readonly meta: _angular_core.InputSignal<CompanySelectorMeta[]>;
722
- readonly valueChange: _angular_core.OutputEmitterRef<string>;
723
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<CompanySelector, never>;
724
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<CompanySelector, "kf-company-selector", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "companies": { "alias": "companies"; "required": true; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "meta": { "alias": "meta"; "required": false; "isSignal": true; }; }, { "valueChange": "valueChange"; }, never, never, true, never>;
725
- }
726
-
727
- /**
728
- * Popover de "Datos de contacto" de una persona (asesor, ejecutivo — nombre
729
- * genérico a propósito, sin lógica de negocio de ninguna pantalla concreta
730
- * adentro). Cada canal (`whatsapp`/`phone`/`email`) se muestra SOLO si
731
- * viene con dato — nunca los 3 fijos — y si ninguno viene, un estado vacío
732
- * explícito en vez de un panel a medio llenar. Mismo criterio que ya
733
- * resolvía `sitio-privado-one-way-frontend` (repo hermano de Chile) para
734
- * el asesor de taller, ahora como pieza compartida del design system.
735
- *
736
- * <kf-contact-popover
737
- * [name]="advisor.name"
738
- * [whatsapp]="advisor.whatsapp"
739
- * [phone]="advisor.phone"
740
- * [email]="advisor.email"
741
- * />
742
- */
743
- declare class ContactPopover {
744
- readonly name: _angular_core.InputSignal<string>;
745
- readonly title: _angular_core.InputSignal<string>;
746
- readonly whatsapp: _angular_core.InputSignal<string | null>;
747
- readonly phone: _angular_core.InputSignal<string | null>;
748
- readonly email: _angular_core.InputSignal<string | null>;
749
- protected readonly open: _angular_core.WritableSignal<boolean>;
750
- protected readonly whatsappHref: _angular_core.Signal<string | null>;
751
- protected readonly phoneHref: _angular_core.Signal<string | null>;
752
- protected readonly emailHref: _angular_core.Signal<string | null>;
753
- protected readonly hasContactMethods: _angular_core.Signal<boolean>;
754
- protected onToggle(): void;
755
- protected onClose(): void;
756
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<ContactPopover, never>;
757
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<ContactPopover, "kf-contact-popover", never, { "name": { "alias": "name"; "required": true; "isSignal": true; }; "title": { "alias": "title"; "required": false; "isSignal": true; }; "whatsapp": { "alias": "whatsapp"; "required": false; "isSignal": true; }; "phone": { "alias": "phone"; "required": false; "isSignal": true; }; "email": { "alias": "email"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
758
- }
759
-
760
- /**
761
- * Marcador genérico para contenido aún por definir (página 38) — comunica
762
- * "contenido disponible para uso" sin parecer un error, a diferencia de
763
- * `kf-empty-state` (que comunica ausencia real de datos del usuario).
764
- * Pensado para secciones del portal que un negocio todavía no completó.
765
- *
766
- * `compact` reduce el tamaño de la imagen placeholder y omite el badge de
767
- * pie (segunda variante de la página 38, sin la etiqueta "Contenido
768
- * disponible para uso" al final).
769
- *
770
- * <kf-content-slot title="Contenido disponible"
771
- * description="Espacio reservado para contenido de Divemotor Perú. Disponible para uso." />
772
- * <kf-content-slot compact icon="star" title="Contenido disponible"
773
- * description="Espacio reservado para contenido de Divemotor Perú." />
774
- */
775
- declare class ContentSlot {
776
- readonly title: _angular_core.InputSignal<string>;
777
- readonly description: _angular_core.InputSignal<string | undefined>;
778
- readonly icon: _angular_core.InputSignal<IconName>;
779
- readonly compact: _angular_core.InputSignalWithTransform<boolean, unknown>;
780
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<ContentSlot, never>;
781
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<ContentSlot, "kf-content-slot", never, { "title": { "alias": "title"; "required": false; "isSignal": true; }; "description": { "alias": "description"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "compact": { "alias": "compact"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
782
- }
783
-
784
- /** En qué estado está la sección. */
785
- type ContentStateKind = 'ready' | 'loading' | 'empty' | 'error';
786
- /** Cómo se dibuja `loading`. Default `'skeleton'`. */
787
- type ContentStateLoad = 'skeleton' | 'spinner';
788
- /**
789
- * Wrapper de los cuatro estados que toda sección tiene: cargando, con datos,
790
- * vacía y con error. Un componente en vez de tres `@if` sueltos, porque es
791
- * siempre el mismo slot el que va rotando entre los cuatro.
792
- *
793
- * Con `state="ready"` proyecta el contenido real y desaparece; en los otros
794
- * tres dibuja el placeholder correspondiente y el contenido no se instancia.
795
- * No inventa visuales propios: `loading` usa `kf-skeleton` (o `kf-spinner` con
796
- * `load="spinner"`), y `empty`/`error` usan `kf-empty-state`.
797
- *
798
- * Las acciones (reintentar, crear) van en el slot `actions` — se ignoran
799
- * mientras carga, porque no hay nada que reintentar todavía.
800
- *
801
- * <kf-content-state [state]="state()" title="No pudimos cargar tus vehículos"
802
- * subtitle="Revisá tu conexión e intentá de nuevo.">
803
- * <kf-button actions (click)="retry()">Reintentar</kf-button>
804
- * <kf-data-table [columns]="cols" [rows]="rows()" />
805
- * </kf-content-state>
806
- */
807
- declare class ContentState {
808
- readonly state: _angular_core.InputSignal<ContentStateKind>;
809
- /** Forma del estado de carga: silueta (default) o spinner. */
810
- readonly load: _angular_core.InputSignal<ContentStateLoad>;
811
- /** Título de `empty`/`error`. Ignorado mientras carga. */
812
- readonly title: _angular_core.InputSignal<string>;
813
- readonly subtitle: _angular_core.InputSignal<string>;
814
- /** Texto al lado del spinner cuando `load="spinner"`. */
815
- readonly loadingLabel: _angular_core.InputSignal<string>;
816
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<ContentState, never>;
817
- 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>;
818
- }
819
-
820
- type ContentViewerType = 'image' | 'video';
821
- /**
822
- * Visor a pantalla completa para ampliar una pieza de contenido — hoy
823
- * imagen, con `video` ya soportado para cuando haya contenido de ese tipo.
824
- * Mismo mecanismo que `kf-modal` (el `<dialog>` nativo resuelve backdrop,
825
- * atrapado de foco, top-layer y `Escape`), pero sin panel/título/footer:
826
- * el contenido ocupa toda la pantalla sobre un backdrop oscuro.
827
- *
828
- * `src` acepta lo que venga del backend sin que el consumidor tenga que
829
- * normalizarlo primero: una URL (`http(s)://`), un data URI ya armado
830
- * (`data:...`), o base64 crudo (se le antepone el prefijo `data:` según
831
- * `type`, asumiendo JPEG para imagen y MP4 para video).
832
- *
833
- * <kf-content-viewer [(open)]="viewerOpen" [src]="viewerSrc()" alt="Foto del vehículo" />
834
- * <kf-content-viewer [(open)]="viewerOpen" type="video" [src]="viewerSrc()" title="Inspección" />
835
- */
836
- declare class ContentViewer {
837
- readonly type: _angular_core.InputSignal<ContentViewerType>;
838
- /** URL, data URI, o base64 crudo — ver JSDoc de la clase. */
839
- readonly src: _angular_core.InputSignal<string | null>;
840
- /** Texto alternativo cuando `type="image"`. */
841
- readonly alt: _angular_core.InputSignal<string>;
842
- readonly title: _angular_core.InputSignal<string>;
843
- /** Visibilidad, two-way: `[(open)]="viewerOpen"`. */
844
- readonly open: _angular_core.ModelSignal<boolean>;
845
- /** Permite cerrar con el botón, `Escape` o clic en el backdrop. */
846
- readonly dismissible: _angular_core.InputSignalWithTransform<boolean, unknown>;
847
- protected readonly titleId: string;
848
- /** Detecta si `src` ya es una URL o un data URI armado; si no, asume
849
- * base64 crudo y le antepone el prefijo `data:` correspondiente a `type`
850
- * — el backend a veces manda una cosa, a veces la otra. */
851
- protected readonly resolvedSrc: _angular_core.Signal<string | null>;
852
- private readonly dialog;
853
- private readonly document;
854
- constructor();
855
- /** `Escape` dispara `cancel`; cancelarlo lo deja abierto. */
856
- protected onCancel(event: Event): void;
857
- /**
858
- * Clic en el backdrop. Un clic en la zona del backdrop tiene como `target` al
859
- * propio `<dialog>` (el contenido vive en el panel de adentro), así que basta
860
- * comparar contra él — no hace falta cortar la propagación en el panel.
861
- */
862
- protected onHostClick(event: MouseEvent): void;
863
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<ContentViewer, never>;
864
- 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>;
865
- }
866
-
867
- /**
868
- * Zona que ocupa la columna en la vista de cards. La vista de tabla ignora
869
- * esto (salvo `media`, que igual se rinde como thumbnail en vez de imprimir
870
- * la URL):
871
- * - `media`: thumbnail a la izquierda del encabezado; el valor de la celda se
872
- * usa como `src` salvo que la columna traiga su `cellTemplates`.
873
- * - `title`: título del encabezado (ej. la patente/unidad).
874
- * - `status`: chip/estado al lado del título.
875
- * - `field` (default): par label/valor en el cuerpo.
876
- * - `action`: va al pie, separado del cuerpo por una línea.
877
- */
878
- type DataTableCardRole = 'media' | 'title' | 'status' | 'field' | 'action';
879
- interface DataTableColumn {
880
- key: string;
881
- label: string;
882
- right?: boolean;
883
- className?: string;
884
- cardRole?: DataTableCardRole;
885
- /** Habilita ordenar por esta columna clickeando su header (solo vista
886
- * tabla — en cards no hay header clickeable). Ciclo asc → desc → sin
887
- * orden, un click a la vez. */
888
- sortable?: boolean;
889
- /** Fija la columna a un borde del scroll horizontal — típicamente la
890
- * primera (identificador) y/o la última (acciones). Columnas `sticky`
891
- * del mismo lado deben ser consecutivas y estar en ese extremo del
892
- * array `columns`; el offset de cada una se calcula sumando el ancho
893
- * real de las que ya quedaron fijas antes, medido en runtime (no hay
894
- * forma de conocer el ancho de antemano, las columnas son de ancho
895
- * variable según su contenido). */
896
- sticky?: 'left' | 'right';
897
- /** Ancho mínimo forzado — evita que una columna angosta (ej. un chip
898
- * corto) se comprima más de la cuenta cuando hay poco espacio y otras
899
- * columnas compiten por él. */
900
- minWidth?: string;
901
- }
902
- /** Vistas disponibles cuando `cardable` está activo. */
903
- type DataTableView = 'table' | 'cards';
904
- /** Opciones de "filas por página" estándar del portal — 10 como default
905
- * cubre bien tanto un dataset chico (ej. Beneficios) como uno con volumen
906
- * real (ej. 30+ cuotas en Billing), sin sentirse vacío ni insuficiente; 25
907
- * y 50 escalan ~2.5x cada paso, evitando un salto brusco donde la opción
908
- * del medio no aporta nada. Cualquier consumidor puede pisar este default
909
- * con su propio `pageSizeOptions` si su caso lo justifica — pasar `[]`
910
- * explícitamente desactiva el selector aunque `pageSize` esté activo. */
911
- declare const DEFAULT_PAGE_SIZE_OPTIONS: number[];
912
- /** Densidad de fila: `comfortable` (default, el padding actual) o
913
- * `compact` (menos padding vertical, para tablas con muchas filas donde
914
- * priorizar cantidad visible sobre aire entre filas). */
915
- type DataTableDensity = 'comfortable' | 'compact';
916
- /** `key:direction` — mismo formato que `sort` de `kf-quote-picker`, para
917
- * que un consumidor que ya conoce ese patrón no aprenda uno nuevo acá. */
918
- type DataTableSort = `${string}:${'asc' | 'desc'}`;
919
- /**
920
- * Tabla estandarizada con soporte opcional de toggle tabla/cards.
921
- *
922
- * El "render" custom por columna de la spec original (una función que
923
- * devuelve JSX) se resuelve acá de dos formas, de la más simple a la más
924
- * flexible:
925
- * - `render`: función `(row) => string` para el caso típico de mostrar un
926
- * valor derivado/formateado de la fila como texto plano.
927
- * - `cellTemplates`: mapa `key -> TemplateRef` para cuando un consumidor
928
- * necesita marcado real dentro de una celda (badges, links, botones). Se
929
- * proyecta con `ngTemplateOutlet` y recibe `{ $implicit: row, index }`.
930
- * Se eligió esta combinación en vez de content-projection por columna
931
- * (ng-content con selector) porque las columnas son dinámicas (vienen de
932
- * `columns()`), y `ngTemplateOutlet` es el mecanismo estándar de Angular
933
- * para "insertar esta plantilla por cada fila de este array".
934
- *
935
- * En la vista de cards, `cardRole` por columna reparte el contenido en zonas
936
- * — encabezado (thumbnail + título + estado), cuerpo (pares label/valor) y
937
- * pie separado por una línea para las acciones. Sin `cardRole`, todas las
938
- * columnas caen en el cuerpo, igual que antes.
939
- *
940
- * El toggle tabla/cards es `kf-view-toggle`, componente standalone propio.
941
- *
942
- * <kf-data-table [columns]="columns" [rows]="rows" (rowClick)="onRowClick($event)">
943
- * <kf-empty-state empty>No hay resultados para mostrar.</kf-empty-state>
944
- * </kf-data-table>
945
- *
946
- * <kf-data-table [columns]="columns" [rows]="rows" [cardable]="true" defaultView="cards" />
947
- *
948
- * Loading nativo: con `loading` en `true`, la tabla se reemplaza por un
949
- * placeholder (`kf-skeleton` por celda/card) — mismo número de filas que
950
- * `pageSize`, o 5 si no hay paginación activa. Ignora `rows()`/el slot
951
- * `[empty]` mientras dura (no sabemos aún si el resultado real estará
952
- * vacío), y respeta la vista activa (tabla o cards) para no cambiar el
953
- * layout de golpe cuando los datos reales lleguen. El toggle tabla/cards y
954
- * el header de columnas siguen visibles — solo el cuerpo se reemplaza:
955
- *
956
- * <kf-data-table [columns]="columns" [rows]="rows()" [loading]="isLoading()">
957
- * <kf-empty-state empty>No hay resultados para mostrar.</kf-empty-state>
958
- * </kf-data-table>
959
- *
960
- * El slot `[footer]` (página 15: "slot de pie para paginación") va siempre
961
- * al final, en ambas vistas, separado por una línea — pensado para proyectar
962
- * `<kf-pagination>`, que sigue siendo un componente independiente.
963
- *
964
- * <kf-data-table [columns]="columns" [rows]="rows">
965
- * <kf-pagination footer [page]="page()" [total]="total()" (pageChange)="page.set($event)" />
966
- * </kf-data-table>
967
- *
968
- * Paginación interna opcional: con `pageSize`, la tabla pagina sola y
969
- * agrega su propio `kf-pagination` en el footer nativo (el slot
970
- * `[footer]` proyectado sigue disponible en paralelo). El selector de "N
971
- * por página" viene activo por defecto con `DEFAULT_PAGE_SIZE_OPTIONS`
972
- * ([10, 25, 50], estándar del portal) — pasar `pageSizeOptions` propio lo
973
- * pisa, y pasar `[]` lo desactiva (paginación sin selector). `itemLabel`
974
- * completa el texto informativo ("1–10 de 42 beneficios").
975
- *
976
- * <kf-data-table [columns]="columns" [rows]="rows" [pageSize]="25" itemLabel="beneficios" />
977
- * <kf-data-table [columns]="columns" [rows]="rows" [pageSize]="25" [pageSizeOptions]="[]" />
978
- *
979
- * Ordenamiento por columna: cada `DataTableColumn` con `sortable: true`
980
- * vuelve su header clickeable, con el mismo formato `key:direction` que
981
- * usa `sort` de `kf-quote-picker` (two-way manual, no `model()` — el
982
- * consumidor decide si persiste el valor o lo descarta al desmontar):
983
- *
984
- * <kf-data-table [columns]="columns" [rows]="rows" [sort]="sort()" (sortChange)="sort.set($event)" />
985
- *
986
- * Columnas fijas: `sticky: 'left' | 'right'` en la columna ancla esa
987
- * columna a un borde del scroll horizontal — típicamente el identificador
988
- * (izquierda) y/o las acciones (derecha) en una tabla con muchas columnas.
989
- *
990
- * Densidad: `density="compact"` reduce el padding vertical de cada fila,
991
- * para priorizar cantidad de filas visibles sobre aire entre ellas.
992
- */
993
- declare class DataTable<T = unknown> {
994
- readonly columns: _angular_core.InputSignal<DataTableColumn[]>;
995
- readonly rows: _angular_core.InputSignal<T[]>;
996
- /** Placeholder nativo (`kf-skeleton`) mientras los datos reales cargan —
997
- * ver el doc-comment de la clase para el detalle. */
998
- readonly loading: _angular_core.InputSignal<boolean>;
999
- private readonly scrollEl;
1000
- private readonly destroyRef;
1001
- /**
1002
- * `.kf-data-table__scroll` tiene `overflow-x: auto` sin ningún indicador
1003
- * visual — con varias columnas y poco ancho disponible, la tabla se corta
1004
- * en el borde y nada le avisa al usuario que hay más contenido
1005
- * scrolleando. `canScrollLeft`/`canScrollRight` alimentan un fade en cada
1006
- * borde — visible solo cuando de verdad hay algo que revelar de ese lado,
1007
- * así una tabla que entra completa no muestra fades falsos.
1008
- */
1009
- protected readonly canScrollLeft: _angular_core.WritableSignal<boolean>;
1010
- protected readonly canScrollRight: _angular_core.WritableSignal<boolean>;
1011
- constructor();
1012
- private updateScrollState;
1013
- /** Default: índice de la fila dentro de `rows`. */
1014
- readonly rowKey: _angular_core.InputSignal<((row: T, index: number) => string | number) | undefined>;
1015
- /** Sin borde/sombra de card — para tablas anidadas dentro de otra card. */
1016
- readonly flush: _angular_core.InputSignal<boolean>;
1017
- /** Habilita el toggle de vista tabla/cards. */
1018
- readonly cardable: _angular_core.InputSignal<boolean>;
1019
- readonly defaultView: _angular_core.InputSignal<DataTableView>;
1020
- /**
1021
- * Formateo simple de celda como texto: `(row) => string`. Para marcado
1022
- * real (badges, links, botones) usar `cellTemplates`.
1023
- */
1024
- readonly render: _angular_core.InputSignal<((row: T) => string) | undefined>;
1025
- /** Mapa `columnKey -> TemplateRef` para celdas con contenido custom. */
1026
- readonly cellTemplates: _angular_core.InputSignal<Record<string, TemplateRef<{
1027
- $implicit: T;
1028
- index: number;
1029
- }>> | undefined>;
1030
- /** Densidad de fila — ver `DataTableDensity`. */
1031
- readonly density: _angular_core.InputSignal<DataTableDensity>;
1032
- /** Fuerza si la fila entera dispara `rowClick` en la vista de tabla —
1033
- * ver `rowInteractive` para el cálculo automático cuando no se define. */
1034
- readonly rowClickable: _angular_core.InputSignal<boolean | null>;
1035
- readonly rowClick: _angular_core.OutputEmitterRef<{
1036
- row: T;
1037
- index: number;
1038
- }>;
1039
- /**
1040
- * Paginación interna opcional — sin `pageSize`, cero cambio de
1041
- * comportamiento (el consumidor sigue paginando afuera si quiere, vía el
1042
- * slot `[footer]` con su propio `kf-pagination`, como ya hace Billing).
1043
- * Con `pageSize`, la tabla pagina `rows()` sola y agrega su propio
1044
- * `kf-pagination` en el footer nativo — el slot `[footer]` proyectado
1045
- * sigue disponible para cualquier otra cosa que el consumidor quiera
1046
- * agregar ahí (no son mutuamente excluyentes).
1047
- */
1048
- readonly pageSize: _angular_core.InputSignal<number | null>;
1049
- /** Default `DEFAULT_PAGE_SIZE_OPTIONS` ([10, 25, 50], estándar del
1050
- * portal) — pasar `[]` explícitamente desactiva el selector de tamaño
1051
- * aunque `pageSize` esté activo (paginación sin selector). */
1052
- readonly pageSizeOptions: _angular_core.InputSignal<number[]>;
1053
- readonly pageChange: _angular_core.OutputEmitterRef<number>;
1054
- readonly pageSizeChange: _angular_core.OutputEmitterRef<number>;
1055
- /** Nombre de la entidad listada (ej. "beneficios", "facturas"), en
1056
- * plural — se agrega al final del texto informativo de la paginación
1057
- * interna ("1–5 de 7 beneficios"). Sin esto, el texto queda solo con
1058
- * números ("1–5 de 7"): la librería no impone un idioma ni un nombre
1059
- * fijo, cada consumidor decide el suyo. */
1060
- readonly itemLabel: _angular_core.InputSignal<string>;
1061
- protected readonly page: _angular_core.WritableSignal<number>;
1062
- private readonly internalPageSize;
1063
- /** Tamaño de página efectivo: el que el usuario elija en el selector
1064
- * (si `pageSizeOptions` está activo), o el `pageSize` del consumidor. */
1065
- protected readonly effectivePageSize: _angular_core.Signal<number | null>;
1066
- /** Cantidad de filas/cards placeholder mientras `loading` es true — el
1067
- * `pageSize` efectivo si hay paginación activa (así el placeholder ya
1068
- * "adivina" cuántas filas reales van a aparecer, sin salto de layout),
1069
- * o 5 si no hay paginación. */
1070
- protected readonly skeletonRowIndexes: _angular_core.Signal<number[]>;
1071
- protected readonly totalPages: _angular_core.Signal<number>;
1072
- /** Texto informativo del footer de paginación interna ("1–5 de 7
1073
- * beneficios") — se recalcula con cada cambio de página/tamaño/dataset,
1074
- * nunca queda desactualizado tras filtrar o pasar de página. */
1075
- protected readonly paginationSummary: _angular_core.Signal<string>;
1076
- /**
1077
- * Ordenamiento por columna — mismo formato `key:direction` que `sort` de
1078
- * `kf-quote-picker`, para que un consumidor que ya conoce ese patrón no
1079
- * aprenda uno nuevo. Un click en un header `sortable` avanza el ciclo
1080
- * asc → desc → sin orden; clickear otra columna reinicia el ciclo en esa
1081
- * columna nueva.
1082
- */
1083
- readonly sort: _angular_core.InputSignal<`${string}:desc` | `${string}:asc` | null>;
1084
- readonly sortChange: _angular_core.OutputEmitterRef<`${string}:desc` | `${string}:asc` | null>;
1085
- protected readonly sortedRows: _angular_core.Signal<T[]>;
1086
- /** Filas realmente renderizadas: ordenadas y, si `pageSize` está activo,
1087
- * recortadas a la página actual. */
1088
- protected readonly displayedRows: _angular_core.Signal<T[]>;
1089
- protected sortDirectionFor(column: DataTableColumn): 'asc' | 'desc' | null;
1090
- /** Ciclo asc → desc → sin orden, disparado al clickear un header `sortable`. */
1091
- protected toggleSort(column: DataTableColumn): void;
1092
- protected goToPage(target: number): void;
1093
- protected setPageSize(size: number | null): void;
1094
- /** Columnas fijas del lado izquierdo/derecho, con el offset acumulado
1095
- * (medido en runtime, ver `measureStickyOffsets`) para `position: sticky`. */
1096
- protected readonly stickyLeftColumns: _angular_core.Signal<DataTableColumn[]>;
1097
- protected readonly stickyRightColumns: _angular_core.Signal<DataTableColumn[]>;
1098
- protected readonly stickyOffsets: _angular_core.WritableSignal<Map<string, number>>;
1099
- protected stickyStyle(column: DataTableColumn): Record<string, string> | null;
1100
- private readonly internalView;
1101
- /** Vista activa: la elegida por el usuario, o `defaultView` si no tocó el toggle. */
1102
- readonly view: _angular_core.Signal<DataTableView>;
1103
- /** Primera columna de cada rol único; el resto se ignora en el encabezado. */
1104
- readonly cardMediaColumn: _angular_core.Signal<DataTableColumn | undefined>;
1105
- readonly cardTitleColumn: _angular_core.Signal<DataTableColumn | undefined>;
1106
- readonly cardStatusColumns: _angular_core.Signal<DataTableColumn[]>;
1107
- readonly cardActionColumns: _angular_core.Signal<DataTableColumn[]>;
1108
- readonly cardFieldColumns: _angular_core.Signal<DataTableColumn[]>;
1109
- /**
1110
- * La card entera es clickeable solo si no tiene acciones propias: un
1111
- * `role="button"` con botones adentro es inválido y deja el foco ambiguo.
1112
- * Con columnas `action`, la interacción la manejan esos botones.
1113
- */
1114
- readonly cardInteractive: _angular_core.Signal<boolean>;
1115
- /**
1116
- * Mismo criterio que `cardInteractive`, para la vista de tabla: la fila
1117
- * entera dispara `rowClick` solo si no hay una columna `action` que ya
1118
- * resuelva la interacción con su propio botón — sin esto, un click en
1119
- * "Ver detalle" dispararía TAMBIÉN el click de la fila por burbujeo.
1120
- * `rowClickable` fuerza el comportamiento en cualquier sentido si el
1121
- * consumidor lo necesita.
1122
- */
1123
- readonly rowInteractive: _angular_core.Signal<boolean>;
1124
- setView(next: DataTableView): void;
1125
- trackRow(row: T, index: number): string | number;
1126
- cellValue(row: T, column: DataTableColumn): string;
1127
- onRowClick(row: T, index: number): void;
1128
- /**
1129
- * Ancho real de cada columna `sticky` — se mide después de cada render
1130
- * porque el ancho de una columna depende de su contenido, no hay forma
1131
- * de conocerlo de antemano. Cada columna fija toma como offset la suma
1132
- * de anchos de las columnas fijas del MISMO lado que ya quedaron antes
1133
- * que ella (así "left" apila de izquierda a derecha, "right" de derecha
1134
- * a izquierda).
1135
- */
1136
- protected measureStickyOffsets(): void;
1137
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<DataTable<any>, never>;
1138
- 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>;
1139
- }
1140
-
1141
- /**
1142
- * Signal con la vista de `kf-data-table` ('table' | 'cards') derivada de un
1143
- * breakpoint — mismo criterio que cualquier tabla con muchas columnas: bajo
1144
- * el ancho dado, la vista de tabla no entra sin scroll horizontal, así que
1145
- * se fuerza `cards`. Centraliza el patrón `matchMediaSignal` +
1146
- * `computed<DataTableView>` que cada tabla del portal repetía igual.
1147
- *
1148
- * protected readonly tableView = dataTableViewSignal();
1149
- * // con un breakpoint distinto al default de 900px:
1150
- * protected readonly tableView = dataTableViewSignal('(max-width: 768px)');
1151
- */
1152
- declare function dataTableViewSignal(query?: string): Signal<DataTableView>;
1153
-
1154
- type DividerOrientation = 'horizontal' | 'vertical';
1155
- /**
1156
- * Separador visual simple entre bloques de contenido. `orientation`
1157
- * controla si es una línea horizontal (para separar secciones apiladas) o
1158
- * vertical (para separar elementos en una fila, ej. acciones en una toolbar,
1159
- * o las columnas de metadata en `kf-company-selector`).
1160
- *
1161
- * En `vertical`, el divisor necesita una altura del contexto donde vive —
1162
- * `:host { align-self: stretch }` lo hace ocupar la altura del contenedor
1163
- * flex/grid padre sin que el consumidor tenga que fijarla a mano. `flex:
1164
- * none` evita que el propio divisor se achique a 0 dentro de ese contenedor
1165
- * flex cuando hay contenido hermano compitiendo por espacio.
1166
- *
1167
- * <kf-divider />
1168
- * <kf-divider orientation="vertical" />
1169
- */
1170
- declare class Divider {
1171
- readonly orientation: _angular_core.InputSignal<DividerOrientation>;
1172
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<Divider, never>;
1173
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<Divider, "kf-divider", never, { "orientation": { "alias": "orientation"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
1174
- }
1175
-
1176
- /** Color del chip de ícono. Identifica la CLASE de documento de un vistazo
1177
- * en una lista de nombres de archivo parecidos entre sí. */
1178
- type DocumentRowTone = 'neutral' | 'ok' | 'warn' | 'error' | 'info';
1179
- /**
1180
- * Fila de un documento descargable: chip de ícono coloreado por tipo,
1181
- * título, metadata y la acción de descarga al final. Sin `href` rinde
1182
- * `pendingLabel` en vez de un link roto. El contenido proyectado cae entre
1183
- * la metadata y la acción, como slot para un `kf-badge` de estado.
1184
- */
1185
- declare class DocumentRow {
1186
- readonly icon: _angular_core.InputSignal<IconName>;
1187
- readonly tone: _angular_core.InputSignal<DocumentRowTone>;
1188
- /** Qué documento es. El nombre humano del tipo, no el del archivo. */
1189
- readonly title: _angular_core.InputSignal<string>;
1190
- /** Línea secundaria — archivo, fecha, tamaño. El consumidor la arma ya
1191
- * formateada: la librería no decide en qué unidad se lee un tamaño ni en
1192
- * qué formato una fecha. */
1193
- readonly meta: _angular_core.InputSignal<string | undefined>;
1194
- /** Ausente rinde `pendingLabel` en vez del link de descarga. */
1195
- readonly href: _angular_core.InputSignal<string | undefined>;
1196
- readonly downloadLabel: _angular_core.InputSignal<string>;
1197
- /** Qué se dice cuando no hay `href`. */
1198
- readonly pendingLabel: _angular_core.InputSignal<string>;
1199
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<DocumentRow, never>;
1200
- 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>;
1201
- }
1202
-
1203
- type DrawerSide = 'left' | 'right';
1204
- /**
1205
- * Panel lateral genérico, agnóstico de contenido — no sabe qué es navegación
1206
- * ni qué es sesión, solo da tres lugares para proyectar contenido
1207
- * (`[kf-drawer-header]`, default, `[kf-drawer-footer]`) y resuelve lo que
1208
- * cualquier drawer necesita resolver igual sin importar qué haya adentro:
1209
- * overlay con blur que cierra al click, `Escape`, atrapado de foco real y
1210
- * devolución de foco al cerrar.
1211
- *
1212
- * `open` es two-way, mismo patrón que `Dropdown`/`Modal`: el consumidor
1213
- * decide qué dispara la apertura (un botón de hamburguesa, por ejemplo) y
1214
- * qué hace con el cierre.
1215
- *
1216
- * Foco: al abrir, se crea un `FocusTrap` del CDK sobre el panel (la misma
1217
- * pieza que usa Angular Material para `mat-sidenav`/`mat-dialog`) — Tab y
1218
- * Shift+Tab ciclan solo entre los elementos focuseables del drawer, sin
1219
- * escapar al contenido de fondo. Al cerrar, el foco vuelve al elemento que
1220
- * tenía el foco antes de abrir (guardado en `document.activeElement`), no
1221
- * se pierde en el `<body>`.
1222
- *
1223
- * `[kf-drawer-header]` tiene fondo oscuro propio (`--kf-color-tertiary-deep`),
1224
- * distinto del body (`--kf-color-white`) — pensado para contener
1225
- * `kf-drawer-session`, que asume ese fondo y no lo trae él mismo (ver
1226
- * `DrawerSession.ts`). Si el slot queda vacío, el contenedor colapsa
1227
- * (`display: none`) y no deja una franja oscura sin contenido.
1228
- *
1229
- * <kf-drawer [(open)]="menuOpen" ariaLabel="Menú de navegación">
1230
- * <div kf-drawer-header>
1231
- * <kf-drawer-session name="Tomás Soto" email="tomas@kaufmann.com" (logoutClick)="onLogout()" />
1232
- * </div>
1233
- * <a href="/">Home</a>
1234
- * <a href="/vehiculos">Vehículos</a>
1235
- * </kf-drawer>
1236
- */
1237
- declare class Drawer {
1238
- /** Visibilidad del panel, two-way: `[(open)]="menuOpen"`. */
1239
- readonly open: _angular_core.ModelSignal<boolean>;
1240
- /** Borde de anclaje del panel. `left` (default) es la convención dominante
1241
- * (Material/iOS/Android): el botón que abre el drawer y el propio panel
1242
- * quedan del mismo lado. */
1243
- readonly side: _angular_core.InputSignal<DrawerSide>;
1244
- /** `aria-label` del `<nav>` interno — describe el propósito del panel para
1245
- * lectores de pantalla (ej. "Menú de navegación"). */
1246
- readonly ariaLabel: _angular_core.InputSignal<string>;
1247
- private readonly panelRef;
1248
- private readonly focusTrapFactory;
1249
- private readonly document;
1250
- private focusTrap;
1251
- private lastFocusedElement;
1252
- constructor();
1253
- /** Cierra el drawer con Escape — mismo patrón que `Dropdown`/`Modal`. */
1254
- protected onEscape(): void;
1255
- private getOrCreateFocusTrap;
1256
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<Drawer, never>;
1257
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<Drawer, "kf-drawer", never, { "open": { "alias": "open"; "required": false; "isSignal": true; }; "side": { "alias": "side"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; }, { "open": "openChange"; }, never, ["[kf-drawer-header]", "*", "[kf-drawer-footer]"], true, never>;
1258
- }
1259
-
1260
- /**
1261
- * Botón de cerrar sesión, pensado para proyectarse dentro de
1262
- * `[kf-drawer-footer]` de `kf-drawer`, o junto a `kf-drawer-session` dentro
1263
- * de `[kf-sidebar-footer]` de `kf-sidebar-shell` — separado de
1264
- * `kf-drawer-session` (que solo muestra identidad: brand, avatar, nombre,
1265
- * email), no anidado dentro de ella.
1266
- *
1267
- * Ícono `log-out` + label, color `--kf-color-error` con hover en
1268
- * `--kf-color-error-soft` y foco visible con `--kf-shadow-focus`. Separado
1269
- * de `kf-drawer-session` (solo identidad) por responsabilidad única.
1270
- *
1271
- * No sabe nada de autenticación real — solo emite `logoutClick` cuando el
1272
- * consumidor debe cerrar la sesión real.
1273
- *
1274
- * `collapsed` oculta el label y centra el ícono, exponiéndolo vía `title`
1275
- * — mismo criterio que `SidebarNavItem.collapsed` y
1276
- * `DrawerSession.collapsed`, para cuando este botón vive dentro de un
1277
- * `kf-sidebar-shell` colapsado.
1278
- *
1279
- * <kf-drawer-logout kf-portal-header-footer (logoutClick)="onLogout()" />
1280
- */
1281
- declare class DrawerLogout {
1282
- /** Label del botón, en español neutro por defecto. Input opcional por si
1283
- * algún consumidor futuro necesita otro texto. */
1284
- readonly label: _angular_core.InputSignal<string>;
1285
- /** Oculta el label y centra el ícono — ver comentario de la clase. */
1286
- readonly collapsed: _angular_core.InputSignalWithTransform<boolean, unknown>;
1287
- readonly logoutClick: _angular_core.OutputEmitterRef<void>;
1288
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<DrawerLogout, never>;
1289
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<DrawerLogout, "kf-drawer-logout", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "collapsed": { "alias": "collapsed"; "required": false; "isSignal": true; }; }, { "logoutClick": "logoutClick"; }, never, never, true, never>;
1290
- }
1291
-
1292
- /**
1293
- * Bloque de identidad para `[kf-drawer-header]` de `kf-drawer` o
1294
- * `[kf-sidebar-footer]` de `kf-sidebar-shell`: avatar + nombre + email,
1295
- * brand opcional arriba. Solo identidad — el logout vive aparte, en
1296
- * `kf-drawer-logout`.
1297
- *
1298
- * `collapsed` oculta brand/nombre/email y deja solo el avatar (para un
1299
- * `kf-sidebar-shell` colapsado). `interactive` convierte la tarjeta en un
1300
- * botón real que emite `cardClick`, para cuando la identidad es el
1301
- * disparador de un menú de acciones (ej. envuelta en `kf-dropdown`).
1302
- *
1303
- * <kf-dropdown [(open)]="menuOpen">
1304
- * <kf-drawer-session interactive name="Juan Pérez" email="juan@empresa.com" (cardClick)="menuOpen.set(!menuOpen())" />
1305
- * <kf-dropdown-panel variant="dark">
1306
- * <kf-dropdown-item icon="log-out" tone="error" (click)="onLogout()">Cerrar sesión</kf-dropdown-item>
1307
- * </kf-dropdown-panel>
1308
- * </kf-dropdown>
1309
- */
1310
- declare class DrawerSession {
1311
- readonly name: _angular_core.InputSignal<string>;
1312
- readonly email: _angular_core.InputSignal<string>;
1313
- /**
1314
- * Etiqueta de rol o función del usuario dentro de la sesión (ej.
1315
- * "Coordinador", "Supervisor"). Se muestra como badge dentro de la
1316
- * identidad, debajo del email — distinto de `brand`, que es la marca o
1317
- * cliente mostrada como encabezado de todo el bloque, no un atributo de
1318
- * la persona.
1319
- */
1320
- readonly role: _angular_core.InputSignal<string>;
1321
- readonly initials: _angular_core.InputSignal<string>;
1322
- readonly avatarUrl: _angular_core.InputSignal<string>;
1323
- /** Label superior opcional, ej. "Portal Kaufmann LATAM". */
1324
- readonly brand: _angular_core.InputSignal<string>;
1325
- /** Oculta brand/nombre/email y centra el avatar — ver comentario de la clase. */
1326
- readonly collapsed: _angular_core.InputSignalWithTransform<boolean, unknown>;
1327
- /** Convierte la tarjeta en un botón real con ícono de kebab — ver comentario de la clase. */
1328
- readonly interactive: _angular_core.InputSignalWithTransform<boolean, unknown>;
1329
- /** Se emite al clickear la tarjeta, solo cuando `interactive` es `true`. */
1330
- readonly cardClick: _angular_core.OutputEmitterRef<void>;
1331
- /** Si no se pasa `initials`, se derivan de `name`: primera letra de cada
1332
- * una de las primeras 2 palabras, en mayúscula. */
1333
- protected readonly displayInitials: _angular_core.Signal<string>;
1334
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<DrawerSession, never>;
1335
- 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>;
1336
- }
1337
-
1338
- /**
1339
- * Menú de acciones flotante, conectado a un elemento disparador vía
1340
- * `@angular/cdk/overlay` — la misma pieza que usa Angular Material para
1341
- * `mat-menu`/`mat-select`. El disparador real puede vivir en OTRO
1342
- * componente por completo (ej. el avatar dentro de `kf-portal-header`), y
1343
- * el panel aparece pegado a ESE elemento sin importar dónde el consumidor
1344
- * ubique al `Dropdown` en su HTML.
1345
- *
1346
- * `open` es two-way: el consumidor decide qué dispara la apertura —
1347
- * `Dropdown` en sí no renderiza ningún trigger, su propio host es el punto
1348
- * de anclaje visual del panel.
1349
- *
1350
- * <kf-dropdown [(open)]="menuOpen"><kf-dropdown-panel>...</kf-dropdown-panel></kf-dropdown>
1351
- */
1352
- declare class Dropdown {
1353
- /** Visibilidad del panel, two-way: `[(open)]="menuOpen"`. */
1354
- readonly open: _angular_core.ModelSignal<boolean>;
1355
- /**
1356
- * Elemento al que se conecta el overlay, si es distinto del propio host
1357
- * de `Dropdown`. Sin esto, `Dropdown` asume que ÉL MISMO es el
1358
- * disparador. Útil cuando el disparador real vive dentro de OTRO
1359
- * componente que no se puede envolver sin ensanchar el área de click
1360
- * (ej. el avatar de sesión dentro de `PortalHeader`).
1361
- */
1362
- readonly anchor: _angular_core.InputSignal<ElementRef<HTMLElement> | undefined>;
1363
- private readonly panel;
1364
- private readonly overlayService;
1365
- private readonly host;
1366
- private readonly viewContainerRef;
1367
- private readonly destroyRef;
1368
- private overlayRef;
1369
- /**
1370
- * Suscripciones a `outsidePointerEvents()`/`keydownEvents()` del overlay
1371
- * ACTUALMENTE adjunto. `getOrCreateOverlayRef()` reutiliza el mismo
1372
- * `overlayRef` entre aperturas (no lo recrea cada vez), así que se
1373
- * cancelan explícitamente en `detach()` antes de la siguiente `attach()`
1374
- * — de lo contrario se acumularían N suscripciones activas sobre el
1375
- * mismo overlay persistente.
1376
- */
1377
- private overlaySubscription;
1378
- constructor();
1379
- /**
1380
- * Respaldo de `overlayRef.keydownEvents()`: ese observable del CDK solo
1381
- * ve teclas con el foco DENTRO del panel. Si el foco queda en el botón
1382
- * disparador (caso común), `keydownEvents()` nunca dispara — este
1383
- * listener a nivel de documento cubre ese caso.
1384
- */
1385
- onDocumentEscape(): void;
1386
- private attach;
1387
- /**
1388
- * El trigger real (el `anchor`, o el propio host si no se pasó ninguno —
1389
- * ver comentario de la clase) vive FUERA del overlay a propósito: el
1390
- * overlay recién se crea/adjunta cuando `open` pasa a `true`, así que el
1391
- * CDK no tiene forma de saber que ese elemento externo "es parte" de su
1392
- * disparador. Sin este filtro, el mismo `pointerdown` que abre el menú
1393
- * también llega a `outsidePointerEvents()`, cerrando el panel en el
1394
- * mismo gesto que lo abrió.
1395
- */
1396
- private isEventOnTrigger;
1397
- private detach;
1398
- private getOrCreateOverlayRef;
1399
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<Dropdown, never>;
1400
- 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>;
1401
- }
1402
-
1403
- type DropdownItemTone = 'neutral' | 'error';
1404
- /**
1405
- * Ítem de acción dentro de un `kf-dropdown-panel`. Es un `<button>` nativo
1406
- * (no un `<a>`): dispara comportamiento en la pantalla actual, no navega.
1407
- *
1408
- * `icon` es opcional — sin él, el ítem es solo texto.
1409
- *
1410
- * <kf-dropdown-item icon="log-out" (click)="onLogout()" tone="error">Cerrar sesión</kf-dropdown-item>
1411
- */
1412
- declare class DropdownItem {
1413
- readonly tone: _angular_core.InputSignal<DropdownItemTone>;
1414
- /** Ícono opcional al inicio de la fila. */
1415
- readonly icon: _angular_core.InputSignal<IconName | undefined>;
1416
- /**
1417
- * Deshabilita el ítem (ej. una acción cuyo endpoint todavía no existe,
1418
- * caso real de "Factura" en `kf-row-actions` de Taller). Default `false`
1419
- * a propósito — ningún consumidor existente (ej. el menú de sesión del
1420
- * Shell) pasa esta prop hoy, así que no cambia su comportamiento.
1421
- */
1422
- readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
1423
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<DropdownItem, never>;
1424
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<DropdownItem, "kf-dropdown-item", never, { "tone": { "alias": "tone"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
1425
- }
1426
-
1427
- type DropdownPanelVariant = 'solid' | 'dark';
1428
- /**
1429
- * Contenido del panel flotante de un `kf-dropdown`. Es solo una plantilla
1430
- * (`<ng-template>`, capturada vía `TemplateRef`) — `Dropdown` la conecta al
1431
- * overlay real cuando `open` pasa a `true`, no la renderiza en su propio
1432
- * lugar del DOM. Esto permite que el panel aparezca pegado al host de
1433
- * `Dropdown` sin importar dónde esté ese host en la página.
1434
- *
1435
- * `variant`: `'solid'` (default, fondo blanco) o `'dark'` (fondo sólido
1436
- * oscuro, mismo tono que `[kf-drawer-header]`) para cuando el trigger vive
1437
- * sobre una superficie oscura de marca.
1438
- *
1439
- * `dark` es sólida a propósito, no semitransparente: el CDK Overlay
1440
- * reposiciona el panel según dónde entra en el viewport y puede terminar
1441
- * cayendo sobre fondo blanco en vez de sobre el rail oscuro — un color
1442
- * sólido se ve intencional sin importar dónde lo posicione el CDK.
1443
- */
1444
- declare class DropdownPanel {
1445
- readonly variant: _angular_core.InputSignal<DropdownPanelVariant>;
1446
- readonly templateRef: _angular_core.Signal<TemplateRef<any>>;
1447
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<DropdownPanel, never>;
1448
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<DropdownPanel, "kf-dropdown-panel", never, { "variant": { "alias": "variant"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
1449
- }
1450
-
1451
- /**
1452
- * Placeholder para una sección sin contenido. Dos tonos, con anatomía
1453
- * distinta (páginas 27-28 del design system, que los trata como piezas
1454
- * separadas — "Error State" no es una variante de color de "Empty State",
1455
- * es un estado semánticamente distinto):
1456
- *
1457
- * - `neutral` (default): "cero resultados" — cuadro con borde punteado
1458
- * discreto, para listas vacías o búsquedas sin coincidencias. No es un
1459
- * error, es la ausencia normal de datos.
1460
- * - `error`: fallo real de carga — sin borde punteado, con un ícono de
1461
- * alerta más prominente y texto en el tono de error. Pensado para usarse
1462
- * junto a un botón "Reintentar" en el slot por defecto.
1463
- *
1464
- * <kf-empty-state>No se encontraron resultados.</kf-empty-state>
1465
- * <kf-empty-state icon="search">Sin coincidencias para tu búsqueda.</kf-empty-state>
1466
- * <kf-empty-state tone="error" icon="alert-triangle">
1467
- * No pudimos cargar tus vehículos
1468
- * <kf-button (click)="retry()">Reintentar</kf-button>
1469
- * </kf-empty-state>
1470
- */
1471
- declare class EmptyState {
1472
- readonly icon: _angular_core.InputSignal<IconName | undefined>;
1473
- /** `neutral` (default): cero resultados. `error`: fallo real de carga. */
1474
- readonly tone: _angular_core.InputSignal<"neutral" | "error">;
1475
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<EmptyState, never>;
1476
- 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>;
1477
- }
1478
-
1479
- /** Color del marcador. Distingue la CLASE de evento de un vistazo en un log
1480
- * largo — no es un estado de progreso. */
1481
- type EventTimelineTone = 'neutral' | 'primary' | 'ok' | 'warn' | 'error' | 'info';
1482
- /** Un evento ya ocurrido. Los tres campos de texto son los tres niveles de
1483
- * lectura: qué pasó, por qué, y cuándo/quién. */
1484
- interface EventTimelineItem {
1485
- /** Clave de `track`. */
1486
- readonly id: string;
1487
- readonly title: string;
1488
- readonly detail?: string | null;
1489
- /** Hora y autor, ya formateados por el consumidor. */
1490
- readonly meta?: string | null;
1491
- readonly icon?: IconName;
1492
- readonly tone?: EventTimelineTone;
1493
- }
1494
- /**
1495
- * Línea de tiempo de AUDITORÍA: eventos ya ocurridos, en orden, cada uno con
1496
- * marcador coloreado por CLASE y tres niveles de texto (titular, detalle,
1497
- * hora + autor). Distinto de `kf-journey-stepper` (de PROGRESO, con pasos
1498
- * futuros conocidos de antemano): acá todo ya pasó.
1499
- */
1500
- declare class EventTimeline {
1501
- readonly items: _angular_core.InputSignal<readonly EventTimelineItem[]>;
1502
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<EventTimeline, never>;
1503
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<EventTimeline, "kf-event-timeline", never, { "items": { "alias": "items"; "required": true; "isSignal": true; }; }, {}, never, never, true, never>;
1504
- }
1505
-
1506
- /**
1507
- * Bloque promocional sobre fondo oscuro de marca (página 37) — campañas,
1508
- * accesos directos o el portal de autoatención. Usa `--kf-color-tertiary-deep`
1509
- * como base del degradado, el mismo azul profundo que el slide destacado de
1510
- * `kf-carousel` y la barra de `kf-portal-header`.
1511
- *
1512
- * <kf-feature-card badge="Autoatención" badgeIcon="tool" title="Portal de Autoatención"
1513
- * description="Con tu patente o VIN accede a soluciones para tu vehículo: mantenciones, repuestos y conectividad en un solo lugar."
1514
- * actionLabel="Ir al portal" actionHref="/autoatencion" />
1515
- */
1516
- declare class FeatureCard {
1517
- readonly badge: _angular_core.InputSignal<string | undefined>;
1518
- readonly badgeIcon: _angular_core.InputSignal<IconName | undefined>;
1519
- readonly title: _angular_core.InputSignal<string>;
1520
- readonly description: _angular_core.InputSignal<string | undefined>;
1521
- readonly actionLabel: _angular_core.InputSignal<string | undefined>;
1522
- readonly actionHref: _angular_core.InputSignal<string | undefined>;
1523
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<FeatureCard, never>;
1524
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<FeatureCard, "kf-feature-card", never, { "badge": { "alias": "badge"; "required": false; "isSignal": true; }; "badgeIcon": { "alias": "badgeIcon"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": true; "isSignal": true; }; "description": { "alias": "description"; "required": false; "isSignal": true; }; "actionLabel": { "alias": "actionLabel"; "required": false; "isSignal": true; }; "actionHref": { "alias": "actionHref"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
1525
- }
1526
-
1527
- /**
1528
- * Chip de solo-lectura inline (label/valor) dentro de una caja bordeada.
1529
- * Se usa en barras de filtro y filas de resumen para mostrar información,
1530
- * NO para entrada de datos — para eso existe `Input`.
1531
- *
1532
- * El valor admite dos formas, igual que en el design system de referencia:
1533
- * - `value` (string): caso simple, ej. `[value]="'Jul 2026'"`.
1534
- * - contenido proyectado (`<ng-content>`): cuando se necesita algo más rico
1535
- * que un string (un badge, texto con formato, etc.).
1536
- * `value` tiene prioridad cuando está definido; si no se pasa `value`, se
1537
- * renderiza el contenido proyectado y, si tampoco hay contenido, `placeholder`.
1538
- *
1539
- * <kf-field icon="calendar" label="Periodo" value="Jul 2026" [select]="true" />
1540
- * <kf-field label="Sucursal"><strong>Casa Central</strong></kf-field>
1541
- */
1542
- declare class Field {
1543
- readonly label: _angular_core.InputSignal<string | undefined>;
1544
- readonly value: _angular_core.InputSignal<string | undefined>;
1545
- readonly icon: _angular_core.InputSignal<IconName | undefined>;
1546
- readonly placeholder: _angular_core.InputSignal<string | undefined>;
1547
- readonly select: _angular_core.InputSignalWithTransform<boolean, unknown>;
1548
- /** Estilos inline opcionales aplicados directamente al host. */
1549
- readonly style: _angular_core.InputSignal<string | undefined>;
1550
- /**
1551
- * El placeholder solo tiene sentido cuando no hay `value` ni contenido
1552
- * proyectado. Angular no expone de forma simple si `<ng-content>` recibió
1553
- * hijos reales, así que la regla adoptada es puramente declarativa: se
1554
- * muestra el placeholder cuando hay texto de placeholder y no hay `value`
1555
- * (ver comentario de clase sobre la prioridad value > proyectado > placeholder).
1556
- */
1557
- protected isPlaceholderVisible(): boolean;
1558
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<Field, never>;
1559
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<Field, "kf-field", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "select": { "alias": "select"; "required": false; "isSignal": true; }; "style": { "alias": "style"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
1560
- }
1561
-
1562
- /**
1563
- * Label superior compartido por todos los campos de formulario del design
1564
- * system (páginas 11-12: Text Field, Select, Multiselect) — uppercase,
1565
- * chico, gris, con letter-spacing amplio. El PDF fija un único tratamiento
1566
- * ("PATENTE O VIN", "SUCURSAL", "SUCURSALES" usan exactamente el mismo
1567
- * estilo), así que este componente es la única fuente de verdad: antes vivía
1568
- * copiado por separado en `Input`, `Select`, `SelectMenu` y `MultiSelect`,
1569
- * y dos de esas copias ya habían divergido del estilo real del PDF.
1570
- *
1571
- * No reemplaza al `<label>` nativo — lo envuelve, para no perder la
1572
- * asociación de accesibilidad `for`/`id` con el control real.
1573
- *
1574
- * `required` agrega un asterisco visual en el tono de error, separado del
1575
- * texto por un espacio y marcado `aria-hidden` — el estado obligatorio real
1576
- * para lectores de pantalla lo comunica el propio control (`aria-required`
1577
- * / `required` en el `<input>`/`<select>` asociado), no este label.
1578
- *
1579
- * <kf-field-label [for]="inputId">Patente o VIN</kf-field-label>
1580
- * <kf-field-label [for]="inputId" required>Correo electrónico</kf-field-label>
1581
- */
1582
- declare class FieldLabel {
1583
- /** `id` del control asociado — se refleja como `for` en el `<label>` interno. */
1584
- readonly for: _angular_core.InputSignal<string | undefined>;
1585
- /** Muestra un asterisco visual indicando que el campo es obligatorio. */
1586
- readonly required: _angular_core.InputSignalWithTransform<boolean, unknown>;
1587
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<FieldLabel, never>;
1588
- 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>;
1589
- }
1590
-
1591
- interface MultiSelectOption {
1592
- value: string;
1593
- label: string;
1594
- }
1595
- /**
1596
- * Dropdown de selección múltiple con checkboxes internos. El panel flotante
1597
- * se conecta al trigger vía `@angular/cdk/overlay` — el mismo mecanismo que
1598
- * `Select` (ver `../select/Select.ts`) y `Dropdown` (`../dropdown/Dropdown.ts`).
1599
- *
1600
- * A diferencia de `Select`, el disparador no muestra un único valor sino los
1601
- * nombres seleccionados junto a un badge circular con el conteo, y cada
1602
- * opción del panel se marca/desmarca con un checkbox sin cerrar el panel.
1603
- * Incluye una opción "Todas" al inicio de la lista, con estado
1604
- * `indeterminate` cuando hay selección parcial — el mismo patrón de
1605
- * "seleccionar todos" de `Checkbox`. "Todas" nunca se filtra por el buscador:
1606
- * queda siempre visible y sigue actuando sobre el universo COMPLETO de
1607
- * `options()`, no sobre el subconjunto filtrado (ver JSDoc de `toggleAll()`).
1608
- *
1609
- * <kf-multi-select
1610
- * label="Sucursales"
1611
- * [options]="branches"
1612
- * [values]="selectedBranches()"
1613
- * (valuesChange)="selectedBranches.set($event)"
1614
- * />
1615
- *
1616
- * `width="full"` ocupa el 100% del contenedor — para multi-selects dentro
1617
- * de layouts flex/grid donde el ancho fijo de `md` no encaja.
1618
- *
1619
- * <kf-multi-select width="full" [options]="branches" [values]="selectedBranches()" (valuesChange)="..." />
1620
- */
1621
- declare class MultiSelect {
1622
- readonly label: _angular_core.InputSignal<string | undefined>;
1623
- readonly options: _angular_core.InputSignal<MultiSelectOption[]>;
1624
- readonly values: _angular_core.InputSignal<string[]>;
1625
- readonly placeholder: _angular_core.InputSignal<string>;
1626
- readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
1627
- /**
1628
- * Ancho del control. 'md' (240px) es el default histórico y preserva el
1629
- * comportamiento actual sin cambios para consumidores existentes.
1630
- * 'full' ocupa el 100% del contenedor — para multi-selects dentro de
1631
- * layouts flex/grid donde el ancho fijo no encaja (ej. un footer de
1632
- * sidebar).
1633
- */
1634
- readonly width: _angular_core.InputSignal<"sm" | "md" | "lg" | "full">;
1635
- /**
1636
- * Tri-state: si el consumidor no lo especifica (`undefined`), el buscador
1637
- * se activa solo cuando `options().length` supera `searchThreshold()`. Si
1638
- * el consumidor define `true`/`false` explícitamente, ese valor manda por
1639
- * sobre el umbral automático. Mismo criterio que `Select`.
1640
- */
1641
- readonly searchable: _angular_core.InputSignal<boolean | undefined>;
1642
- readonly searchThreshold: _angular_core.InputSignal<number>;
1643
- readonly valuesChange: _angular_core.OutputEmitterRef<string[]>;
1644
- private readonly instanceId;
1645
- readonly triggerId: string;
1646
- readonly listboxId: string;
1647
- readonly isOpen: _angular_core.WritableSignal<boolean>;
1648
- readonly searchQuery: _angular_core.WritableSignal<string>;
1649
- readonly activeIndex: _angular_core.WritableSignal<number>;
1650
- readonly selectedCount: _angular_core.Signal<number>;
1651
- readonly allSelected: _angular_core.Signal<boolean>;
1652
- readonly isPartiallySelected: _angular_core.Signal<boolean>;
1653
- readonly showSearch: _angular_core.Signal<boolean>;
1654
- /** Opciones visibles tras aplicar el filtro de búsqueda ("Todas" nunca se filtra). */
1655
- readonly filteredOptions: _angular_core.Signal<MultiSelectOption[]>;
1656
- /**
1657
- * Texto del disparador: placeholder si no hay selección, o los nombres
1658
- * seleccionados unidos por coma (el conteo va aparte, en el badge). Con
1659
- * muchas opciones el `text-overflow: ellipsis` del `.kf-multi-select__value`
1660
- * corta el texto — el badge de conteo siempre queda visible al lado.
1661
- */
1662
- readonly summaryText: _angular_core.Signal<string>;
1663
- private readonly triggerRef;
1664
- private readonly panelTemplate;
1665
- private readonly searchInputRef;
1666
- private readonly overlay;
1667
- constructor();
1668
- isSelected(value: string): boolean;
1669
- toggle(): void;
1670
- toggleOption(value: string): void;
1671
- /**
1672
- * Alterna entre "todas seleccionadas" y "ninguna seleccionada". Actúa
1673
- * siempre sobre el universo COMPLETO de `options()`, no sobre
1674
- * `filteredOptions()` — con un buscador activo, "Todas" no se filtra ni
1675
- * cambia de significado: seguir marcando/desmarcando únicamente lo visible
1676
- * sería sorprendente para quien ya tenía otras opciones (no visibles en el
1677
- * filtro actual) seleccionadas de antes.
1678
- */
1679
- toggleAll(): void;
1680
- onSearchInput(event: Event): void;
1681
- /** Mueve `activeIndex` sobre las opciones YA FILTRADAS visibles. Clamp simple, sin wraparound. */
1682
- moveActive(delta: number): void;
1683
- toggleActive(): void;
1684
- onEscape(): void;
1685
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<MultiSelect, never>;
1686
- 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>;
1687
- }
1688
-
1689
- type FilterFieldType = 'select' | 'multi-select' | 'search';
1690
- interface FilterFieldConfig {
1691
- key: string;
1692
- type: FilterFieldType;
1693
- label?: string;
1694
- placeholder?: string;
1695
- width?: string;
1696
- icon?: IconName;
1697
- showDivider?: boolean;
1698
- }
1699
- type FilterValue = string | string[];
1700
- interface FilterFieldChange {
1701
- key: string;
1702
- value: FilterValue;
1703
- }
1704
- /**
1705
- * Fila de filtros config-driven (select / multi-select / búsqueda) para el
1706
- * header de una tabla, compuesta con piezas YA existentes de la librería
1707
- * (`kf-select`, `kf-multi-select`, `kf-input`, `kf-divider`, `kf-collapsible`).
1708
- *
1709
- * No tiene lógica de filtrado propia: solo renderiza los controles descritos
1710
- * por `fields()` y emite `fieldChange` cuando alguno cambia — filtrar filas
1711
- * sigue siendo responsabilidad de quien lo consume.
1712
- *
1713
- * En escritorio los campos se muestran en una fila; en mobile/tablet
1714
- * (≤768px, mismo breakpoint que `kf-portal-header`) se colapsan dentro de un
1715
- * `kf-collapsible`.
1716
- *
1717
- * <kf-filter
1718
- * [fields]="[
1719
- * { key: 'model', type: 'multi-select', label: 'Modelo', placeholder: 'Todos los modelos', showDivider: true },
1720
- * { key: 'search', type: 'search', label: 'Buscar vehículo', placeholder: 'Ingresa N° de orden o placa', width: '280px' },
1721
- * ]"
1722
- * [values]="{ model: selectedModels(), search: searchTerm() }"
1723
- * [options]="{ model: modelOptions() }"
1724
- * (fieldChange)="onFieldChange($event)"
1725
- * />
1726
- */
1727
- declare class Filter {
1728
- readonly fields: _angular_core.InputSignal<FilterFieldConfig[]>;
1729
- readonly values: _angular_core.InputSignal<Record<string, FilterValue>>;
1730
- readonly options: _angular_core.InputSignal<Record<string, string[]>>;
1731
- readonly mobileTitle: _angular_core.InputSignal<string>;
1732
- readonly fieldChange: _angular_core.OutputEmitterRef<FilterFieldChange>;
1733
- protected readonly multiOptionsByKey: _angular_core.Signal<Record<string, MultiSelectOption[]>>;
1734
- protected singleValueFor(key: string): string;
1735
- protected multiValueFor(key: string): string[];
1736
- protected fieldWidth(field: FilterFieldConfig): string;
1737
- protected optionsFor(key: string): string[];
1738
- protected multiOptionsFor(key: string): MultiSelectOption[];
1739
- protected onFieldChange(key: string, value: FilterValue): void;
1740
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<Filter, never>;
1741
- 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>;
1742
- }
1743
-
1744
- /** Forma normalizada interna de cada opción, ya resuelta a value/label. */
1745
- interface FilterChipOption {
1746
- value: string;
1747
- label: string;
1748
- }
1749
- /**
1750
- * Fila de chips tipo "pill" para filtros de una sola selección.
1751
- *
1752
- * Cada opción puede pasarse como string simple (se usa como value y label
1753
- * a la vez) o como objeto { value, label } cuando el texto mostrado difiere
1754
- * del valor interno.
1755
- *
1756
- * El chip activo se resalta con `--kf-color-primary` — mismo color de marca
1757
- * que `kf-tabs`/`kf-select` para su propio estado activo, así el filtro
1758
- * seleccionado se lee con el color del tema activo, no un negro fijo que
1759
- * no cambia entre marcas.
1760
- *
1761
- * <kf-filter-chips [options]="['Todos', 'Activos', 'Archivados']"
1762
- * [value]="filtro"
1763
- * (valueChange)="filtro = $event" />
1764
- *
1765
- * <kf-filter-chips [options]="[{ value: 'all', label: 'Todos' }]"
1766
- * [value]="filtro"
1767
- * (valueChange)="filtro = $event" />
1768
- */
1769
- declare class FilterChips {
1770
- readonly options: _angular_core.InputSignal<(string | {
1771
- value: string;
1772
- label: string;
1773
- })[]>;
1774
- readonly value: _angular_core.InputSignal<string>;
1775
- readonly valueChange: _angular_core.OutputEmitterRef<string>;
1776
- /** Normaliza cada opción string/objeto a la forma { value, label }. */
1777
- readonly normalizedOptions: _angular_core.Signal<FilterChipOption[]>;
1778
- select(value: string): void;
1779
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<FilterChips, never>;
1780
- 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>;
1781
- }
1782
-
1783
- /** Un link del pie — legales, contacto, soporte. */
1784
- interface FooterLink {
1785
- label: string;
1786
- href?: string;
1787
- }
1788
- /**
1789
- * Pie de página general del portal (página 38): logo + copyright a la
1790
- * izquierda, links legales/soporte empujados a la derecha. Logo y copyright
1791
- * son inputs (cada portal del ecosistema — LATAM, OTD, Star — trae su
1792
- * propia marca y texto legal); `links` es la lista de accesos, típicamente
1793
- * placeholders hasta que existan destinos reales.
1794
- *
1795
- * <kf-footer
1796
- * logoSrc="images/logo-kaufmann-dark.png"
1797
- * logoAlt="Grupo Kaufmann"
1798
- * copyright="© 2026 Kaufmann LATAM · Todos los derechos reservados"
1799
- * [links]="[
1800
- * { label: 'Políticas de privacidad' },
1801
- * { label: 'Contacto' },
1802
- * { label: 'Sugerencias y reclamos' },
1803
- * ]"
1804
- * />
1805
- */
1806
- declare class Footer {
1807
- readonly logoSrc: _angular_core.InputSignal<string | undefined>;
1808
- readonly logoAlt: _angular_core.InputSignal<string>;
1809
- readonly copyright: _angular_core.InputSignal<string>;
1810
- readonly links: _angular_core.InputSignal<FooterLink[]>;
1811
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<Footer, never>;
1812
- 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>;
1813
- }
1814
-
1815
- /** Opciones de `formatNumber` — todas opcionales. */
1816
- interface FormatNumberOptions {
1817
- /** Cantidad de decimales a mostrar. Default `0`. */
1818
- decimals?: number;
1819
- /** Locale de `Intl`/`toLocaleString` — decide separador de miles y de
1820
- * decimales. Default `'es'` (punto de miles, coma decimal). */
1821
- locale?: string;
1822
- }
1823
- /**
1824
- * Separador de miles genérico — sin signo, sin asumir que el valor es
1825
- * dinero. Cubre casos como kilometraje/horómetro (`formatNumber(45231,
1826
- * { locale: 'en-US' })` → `'45,231'`), no solo moneda.
1827
- *
1828
- * formatNumber(16800); // '16.800'
1829
- * formatNumber(16800.5, { decimals: 2 }); // '16.800,50'
1830
- * formatNumber(45231, { locale: 'en-US' }); // '45,231'
1831
- */
1832
- declare function formatNumber(value: number, options?: FormatNumberOptions): string;
1833
- /** Opciones de `formatCurrency` — extiende `FormatNumberOptions` con el
1834
- * signo/prefijo de moneda. */
1835
- interface FormatCurrencyOptions extends FormatNumberOptions {
1836
- /** Símbolo o prefijo antepuesto al monto. Default `'$'`. */
1837
- sign?: string;
1838
- }
1839
- /**
1840
- * Formatea un monto de dinero — compone sobre `formatNumber` agregando el
1841
- * signo. Mismos defaults que la convención de OTD (punto de miles, sin
1842
- * decimales, prefijo `$`), overrideables para otra moneda/país/formato:
1843
- *
1844
- * formatCurrency(16800); // '$16.800'
1845
- * formatCurrency(16800.5, { decimals: 2 }); // '$16.800,50'
1846
- * formatCurrency(16800, { sign: '' }); // '16.800'
1847
- * formatCurrency(16800, { sign: 'US$', locale: 'en-US' }); // 'US$16,800'
1848
- */
1849
- declare function formatCurrency(amount: number, options?: FormatCurrencyOptions): string;
1850
- /**
1851
- * Inverso de `formatNumber` — recupera el número crudo de un valor ya
1852
- * formateado con el mismo `locale`. Solo soporta `locale` cuyo separador de
1853
- * miles y decimal sean `'.'`/`','` (`es`) o `','`/`'.'` (`en-US`) — son los
1854
- * 2 casos reales del ecosistema hoy; si aparece un tercer locale con otros
1855
- * separadores, extender acá antes de usarlo.
1856
- */
1857
- declare function parseNumber(formatted: string, locale?: string): number;
1858
- /**
1859
- * Inverso de `formatCurrency` — recupera el número crudo de un monto ya
1860
- * formateado, descartando cualquier signo/prefijo de moneda.
1861
- *
1862
- * parseCurrency('$16.800'); // 16800
1863
- */
1864
- declare function parseCurrency(formatted: string, locale?: string): number;
1865
- /** "12:40" — hora en formato de 24 horas. */
1866
- declare function formatClockTime(date: Date): string;
1867
- /**
1868
- * Quita espacios (incluido cualquier espacio en blanco intermedio, no solo
1869
- * al principio/final) y puntos de un identificador de documento — un
1870
- * espacio invisible (ej. pegado por accidente desde otra fuente) es un
1871
- * motivo de rechazo silencioso real que un usuario no sabe explicarse.
1872
- * Genérico: sirve tanto para RUT (`formatChileanRut` ya lo usa por
1873
- * dentro) como para cualquier otro identificador con el mismo problema.
1874
- *
1875
- * sanitizeDocumentId(' 51 00004-8 '); // '5100004-8'
1876
- * sanitizeDocumentId('12.345.678-9'); // '12345678-9'
1877
- */
1878
- declare function sanitizeDocumentId(raw: string): string;
1879
- /**
1880
- * Formatea un RUT chileno insertando el guion automáticamente a medida
1881
- * que se escribe — igual que un input de tarjeta de crédito, el usuario
1882
- * solo tipea dígitos (y opcionalmente `k`/`K` como dígito verificador) y
1883
- * el guion aparece solo antes del último carácter. Solo da forma al
1884
- * string — no valida si el dígito verificador es matemáticamente
1885
- * correcto (ver `isValidChileanRut` para eso) ni si la cuenta existe
1886
- * (eso lo confirma GAM del lado servidor al autenticar).
1887
- *
1888
- * formatChileanRut('123456789'); // '12345678-9'
1889
- * formatChileanRut('5100004-8'); // '5100004-8' (ya formateado, idempotente)
1890
- * formatChileanRut('5100004k'); // '5100004-k'
1891
- * formatChileanRut('510000 4-8 '); // '5100004-8' (espacios ignorados)
1892
- */
1893
- declare function formatChileanRut(raw: string): string;
1894
- /**
1895
- * Valida un RUT chileno YA formateado (`cuerpo-DV`, ej. `'12345678-9'`)
1896
- * contra el algoritmo módulo 11 real — a diferencia del `pattern` de
1897
- * `document-id.config.ts` en cada portal consumidor (que solo exige la
1898
- * FORMA: dígitos + guion + dígito verificador), esta función confirma
1899
- * que el dígito verificador sea matemáticamente correcto para ese
1900
- * cuerpo. Sigue sin confirmar que la cuenta exista — eso solo lo sabe
1901
- * GAM al autenticar — pero atrapa el error tipográfico más común
1902
- * (invertir/cambiar un dígito) antes de intentar el login.
1903
- *
1904
- * isValidChileanRut('20236276-1'); // true (DV real de ese cuerpo)
1905
- * isValidChileanRut('20236276-8'); // false (DV real es '1', no '8')
1906
- * isValidChileanRut('no-es-rut'); // false (ni siquiera tiene la forma)
1907
- */
1908
- declare function isValidChileanRut(rut: string): boolean;
1909
-
1910
- /**
1911
- * Input de formulario apilado: label arriba (en mayúsculas vía CSS), campo con
1912
- * fondo gris que pasa a blanco con anillo de foco azul al enfocar.
1913
- *
1914
- * Implementa ControlValueAccessor, así que funciona con [(ngModel)] y con
1915
- * Reactive Forms (formControl / formControlName), no es solo un wrapper visual.
1916
- *
1917
- * <kf-input label="Placa" icon="car" placeholder="ABC-123" hint="Formato: 3 letras + 3 números" />
1918
- * <input kf-input formControlName="email" type="email" />
1919
- *
1920
- * `invalid` + `errorMessage` cubren el estado de error: con `invalid` el
1921
- * borde pasa a rojo (`--kf-color-error`) y se setea `aria-invalid="true"` en
1922
- * el `<input>` nativo; `errorMessage` reemplaza el `hint` neutro por un
1923
- * mensaje en rojo con `role="alert"`, para que un lector de pantalla lo
1924
- * anuncie apenas aparece (a diferencia de `hint`, que es solo informativo y
1925
- * nunca interrumpe). Si se pasan ambos, `errorMessage` tiene prioridad
1926
- * visual sobre `hint` — no tiene sentido mostrar los dos a la vez.
1927
- *
1928
- * <kf-input label="Correo" formControlName="email"
1929
- * [invalid]="email.invalid && email.touched"
1930
- * [errorMessage]="email.touched && email.hasError('required') ? 'Ingresa tu correo.' : undefined" />
1931
- *
1932
- * `toggleable` solo tiene efecto cuando `type="password"`: agrega un botón
1933
- * de ojo al final del campo que alterna la visibilidad del valor tipeado
1934
- * (cambia el `type` real del `<input>` nativo entre `password` y `text`,
1935
- * nunca expone el valor en el DOM de otra forma). El botón es `type="button"`
1936
- * — nunca dispara submit del form contenedor — y anuncia su acción con
1937
- * `aria-label` dinámico y `aria-pressed`, ya que es un toggle, no un botón
1938
- * de una sola acción.
1939
- *
1940
- * <kf-input type="password" label="Contraseña" toggleable formControlName="password" />
1941
- *
1942
- * `readonly` es distinto de `disabled`: un campo `disabled` no recibe foco
1943
- * por teclado ni se puede seleccionar/copiar su texto (y se lee como
1944
- * "deshabilitado" en un lector de pantalla); un campo `readonly` sí — el
1945
- * valor se puede ver, enfocar y copiar, solo no se puede editar. Usar
1946
- * `readonly` para mostrar un dato existente que el usuario puede querer
1947
- * copiar (ej. un documento de identidad en un formulario de perfil), y
1948
- * `disabled` para un campo genuinamente inactivo (ej. mientras un guardado
1949
- * está en vuelo).
1950
- *
1951
- * <kf-input label="DNI" readonly formControlName="documentId" />
1952
- */
1953
- declare class Input implements ControlValueAccessor, AfterViewInit {
1954
- private static nextId;
1955
- private readonly nativeInput;
1956
- readonly label: _angular_core.InputSignal<string | undefined>;
1957
- /** Marca el label con un asterisco visual y aplica `required` al `<input>`
1958
- * nativo — el atributo HTML ya expone el estado obligatorio a lectores de
1959
- * pantalla (mapeo implícito a `aria-required`), sin necesidad de un
1960
- * `aria-required` explícito redundante. */
1961
- readonly required: _angular_core.InputSignalWithTransform<boolean, unknown>;
1962
- /** Ícono líder, renderizado vía `kf-icon`. */
1963
- readonly icon: _angular_core.InputSignal<IconName | undefined>;
1964
- readonly suffix: _angular_core.InputSignal<string | undefined>;
1965
- readonly hint: _angular_core.InputSignal<string | undefined>;
1966
- /** Borde y aria-invalid en rojo. Ver `errorMessage` para el mensaje asociado. */
1967
- readonly invalid: _angular_core.InputSignalWithTransform<boolean, unknown>;
1968
- /** Mensaje de error con `role="alert"` — reemplaza a `hint` mientras esté presente. */
1969
- readonly errorMessage: _angular_core.InputSignal<string | undefined>;
1970
- readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
1971
- /** Ver JSDoc de la clase para la diferencia con `disabled`. No pasa por
1972
- * Reactive Forms (a diferencia de `disabled`, que también puede llegar
1973
- * vía `FormControl.disable()`) — es siempre una decisión explícita del
1974
- * consumidor del componente. */
1975
- readonly readonly: _angular_core.InputSignalWithTransform<boolean, unknown>;
1976
- readonly type: _angular_core.InputSignal<string>;
1977
- readonly placeholder: _angular_core.InputSignal<string>;
1978
- /** Agrega el botón de mostrar/ocultar — sin efecto si `type` no es `"password"`. */
1979
- readonly toggleable: _angular_core.InputSignalWithTransform<boolean, unknown>;
1980
- protected readonly inputId: string;
1981
- protected readonly hintId: string;
1982
- protected readonly errorId: string;
1983
- protected readonly value: _angular_core.WritableSignal<string>;
1984
- protected readonly focused: _angular_core.WritableSignal<boolean>;
1985
- /** Deshabilitado por Reactive Forms (formControl.disable()). */
1986
- private readonly disabledByForm;
1987
- /** Disabled real: por input() directo o por el FormControl asociado. */
1988
- protected readonly isDisabled: _angular_core.Signal<boolean>;
1989
- /** Estado interno del toggle — irrelevante si `showPasswordToggle()` es false. */
1990
- protected readonly passwordVisible: _angular_core.WritableSignal<boolean>;
1991
- /** El botón solo tiene sentido si el campo realmente es de contraseña. */
1992
- protected readonly showPasswordToggle: _angular_core.Signal<boolean>;
1993
- /**
1994
- * `type` real que recibe el `<input>` nativo: igual a `type()` salvo
1995
- * cuando el toggle está activo y visible, donde pasa a `text` para
1996
- * mostrar el valor en claro sin cambiar `type()` en sí (evita que un
1997
- * consumidor que lee `type()` desde afuera vea un valor inconsistente).
1998
- */
1999
- protected readonly effectiveType: _angular_core.Signal<string>;
2000
- private onChange;
2001
- private onTouched;
2002
- ngAfterViewInit(): void;
2003
- writeValue(value: string): void;
2004
- registerOnChange(fn: (value: string) => void): void;
2005
- registerOnTouched(fn: () => void): void;
2006
- setDisabledState(isDisabled: boolean): void;
2007
- protected onInput(rawValue: string): void;
2008
- protected onBlur(): void;
2009
- /**
2010
- * Alterna la visibilidad del valor. No dispara `onBlur`/pierde el foco:
2011
- * el botón vive dentro del mismo `.kf-input__field`, así que un click acá
2012
- * no debe tratarse como si el usuario hubiese abandonado el campo.
2013
- */
2014
- protected onTogglePasswordVisibility(): void;
2015
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<Input, never>;
2016
- 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>;
2017
- }
2018
-
2019
- type JourneyStepStatus = 'done' | 'current' | 'delayed' | 'pending';
2020
- type JourneyStepperSurface = 'dark' | 'card';
2021
- /**
2022
- * Paleta de color de los estados, independiente de `orientation`:
2023
- *
2024
- * - `'brand'` (default): azul de marca para completado/actual, sin
2025
- * semántica de semáforo. Para cuando horizontal y vertical representan
2026
- * el mismo journey en distintos tamaños de pantalla.
2027
- * - `'semantic'`: verde/amarillo/rojo real por paso (solo con
2028
- * `orientation="vertical"`, la única con 4 estados incluido `delayed`).
2029
- * Para cuando cada paso comunica su propio estado puntual.
2030
- */
2031
- type JourneyStepperColorScheme = 'brand' | 'semantic';
2032
- interface JourneyStep {
2033
- key: string;
2034
- label: string;
2035
- icon: IconName;
2036
- /** Solo con `orientation="vertical"` — en horizontal el status se calcula desde `currentStep`. */
2037
- status?: JourneyStepStatus;
2038
- /** Texto bajo el label — solo con `orientation="vertical"`. */
2039
- date?: string;
2040
- }
2041
- /**
2042
- * Progreso de un flujo de N pasos.
2043
- *
2044
- * - `orientation` (`'horizontal'` default | `'vertical'`): horizontal
2045
- * deriva done/current/pending de `currentStep`; vertical usa
2046
- * `steps[].status` explícito, con `delayed` como cuarto estado.
2047
- * - `surface` (`'dark'` default | `'card'`): fondo sólido oscuro de marca,
2048
- * o superficie clara con borde+sombra.
2049
- * - `colorScheme` (`'brand'` default | `'semantic'`): ver `JourneyStepperColorScheme`.
2050
- *
2051
- * <kf-journey-stepper surface="card" [steps]="steps" [currentStep]="4" (stepClick)="onNavigate($event)" />
2052
- * <kf-journey-stepper orientation="vertical" [steps]="unitSteps" />
2053
- * <kf-journey-stepper orientation="vertical" colorScheme="semantic" [steps]="unitStepsWithDelays" />
2054
- */
2055
- declare class JourneyStepper {
2056
- readonly steps: _angular_core.InputSignal<JourneyStep[]>;
2057
- readonly orientation: _angular_core.InputSignal<"horizontal" | "vertical">;
2058
- readonly surface: _angular_core.InputSignal<JourneyStepperSurface>;
2059
- readonly colorScheme: _angular_core.InputSignal<JourneyStepperColorScheme>;
2060
- /** Índice del paso actual — solo con `orientation="horizontal"`. */
2061
- readonly currentStep: _angular_core.InputSignal<number>;
2062
- /** Se emite con `step.key` al clickear un paso — solo activo con `orientation="horizontal"`. */
2063
- readonly stepClick: _angular_core.OutputEmitterRef<string>;
2064
- private readonly track;
2065
- private readonly destroyRef;
2066
- /** `true` cuando el contenido desborda el ancho visible — controla el fade de los bordes. */
2067
- protected readonly hasOverflow: _angular_core.WritableSignal<boolean>;
2068
- protected readonly clickable: _angular_core.Signal<boolean>;
2069
- constructor();
2070
- private measureOverflow;
2071
- protected readonly resolvedSteps: _angular_core.Signal<(JourneyStep & {
2072
- status: JourneyStepStatus;
2073
- })[]>;
2074
- protected dotIcon(status: JourneyStepStatus, stepIcon: IconName): IconName;
2075
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<JourneyStepper, never>;
2076
- 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>;
2077
- }
2078
-
2079
- type LinkVariant = 'default' | 'muted';
2080
- /**
2081
- * Acción de bajo énfasis con apariencia de texto — "¿Olvidaste tu
2082
- * contraseña?", "Volver a iniciar sesión", o un link real de navegación
2083
- * como "Ir a pagos →".
2084
- *
2085
- * Sin `href` (default) es un `<button type="button">`: dispara una acción
2086
- * en la misma pantalla, vía `(click)`. Con `href`, es un `<a>` real: navega
2087
- * a otra URL. `showArrow` agrega un ícono `chevron-right` al final — para
2088
- * el patrón "texto + flecha" de un link que lleva a otro lado.
2089
- *
2090
- * `variant="default"` (color primario, subrayado en hover) es para la
2091
- * acción de mayor jerarquía relativa de la pantalla; `variant="muted"`
2092
- * (gris que pasa a primario en hover) es para acciones de retroceso/salida.
2093
- *
2094
- * <kf-link (click)="onForgotPassword()">¿Olvidaste tu contraseña?</kf-link>
2095
- * <kf-link variant="muted" (click)="onBack()">← Volver a iniciar sesión</kf-link>
2096
- * <kf-link href="/pagos" showArrow>Ir a pagos</kf-link>
2097
- */
2098
- declare class Link {
2099
- readonly variant: _angular_core.InputSignal<LinkVariant>;
2100
- readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
2101
- /** Si se pasa, renderiza un `<a>` real en vez de `<button>` — navega en vez de disparar `(click)`. */
2102
- readonly href: _angular_core.InputSignal<string | undefined>;
2103
- /** Ícono `chevron-right` al final — para el patrón "texto + flecha" de un link que lleva a otro lado. */
2104
- readonly showArrow: _angular_core.InputSignalWithTransform<boolean, unknown>;
2105
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<Link, never>;
2106
- 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>;
2107
- }
2108
-
2109
- /**
2110
- * Motor de mapas configurado a nivel app, no por uso — mismo criterio que
2111
- * `API_CONFIG` en `packages/api-client` de `route-assistance-monorepo`: el
2112
- * access token de Mapbox es un dato de despliegue (cada app/entorno tiene
2113
- * el propio), no algo que cada `<kf-map>` deba repetir.
2114
- */
2115
- interface MapConfig {
2116
- /** Access token público de Mapbox (`pk.*`) de la cuenta del proyecto que consume la librería. */
2117
- accessToken: string;
2118
- /**
2119
- * Estilo de Mapbox Studio propio del cliente, si lo tiene. Cuando se
2120
- * define, gana siempre sobre `theme()` del componente — un estilo de
2121
- * marca custom no tiene una variante "light"/"dark" que elegir, ya viene
2122
- * resuelto. Sin esto, `kf-map` usa el estilo `light-v11`/`dark-v11` de
2123
- * Mapbox según `theme()`.
2124
- */
2125
- styleUrl?: string;
2126
- }
2127
- declare const MAP_CONFIG: InjectionToken<MapConfig>;
2128
- /**
2129
- * `provide*` idiomático (mismo patrón que `provideHttpClient`/`provideRouter`)
2130
- * para inyectar una sola vez en `app.config.ts` de cada app consumidora:
2131
- *
2132
- * providers: [provideMapConfig({ accessToken: 'pk.xxx' })]
2133
- */
2134
- declare function provideMapConfig(config: MapConfig): Provider;
2135
-
2136
- /**
2137
- * Vocabulario de temas de `kf-map`, separado de `MapView.ts` para que
2138
- * `MapThemePicker` (el panel flotante del control `mapStyle`) pueda
2139
- * importarlo sin crear un ciclo `MapView → MapControls → MapThemePicker →
2140
- * MapView`.
2141
- *
2142
- * `streets`/`satellite`/`outdoors` se suman a los `light`/`dark`
2143
- * originales — los 3 IDs de estilo (`streets-v12`, `satellite-streets-v12`,
2144
- * `outdoors-v12`) están confirmados contra la cuenta/token real del
2145
- * ecosistema: la API de estilos de Mapbox es por cuenta, no por versión de
2146
- * `mapbox-gl`, así que cualquier duda se resuelve probando contra el token
2147
- * real, no leyendo el número de versión instalado.
2148
- */
2149
- type MapTheme = 'light' | 'dark' | 'streets' | 'satellite' | 'outdoors';
2150
- declare const STYLE_BY_THEME: Record<MapTheme, string>;
2151
- /** Orden fijo en que `MapThemePicker` lista las 5 opciones — predecible
2152
- * (siempre el mismo orden en toda pantalla que use el picker), no importa
2153
- * cuál esté activa. */
2154
- declare const THEME_ORDER: MapTheme[];
2155
- /** Nombre en español de cada tema, para el `aria-label`/label del control
2156
- * `mapStyle` y las opciones del picker — anuncia qué tema rige HOY o qué
2157
- * tema se está por elegir, no solo "otro tema distinto". */
2158
- declare const THEME_LABEL: Record<MapTheme, string>;
2159
-
2160
- /**
2161
- * Cada pieza es independiente entre sí — agregar/sacar un `MapControlKind`
2162
- * de `controls` en `kf-map` no reacomoda ni afecta a los demás. Array vacío
2163
- * (default de `kf-map`) es "ningún control": opt-in explícito del
2164
- * consumidor, nunca activado por default (ver JSDoc de `controls` en
2165
- * `MapView.ts`).
2166
- *
2167
- * zoom — botones +/- reales (`map.zoomIn()`/`zoomOut()`).
2168
- * fullscreen — expandir/contraer el mapa (Fullscreen API nativa).
2169
- * mapStyle — ciclar entre los temas de `MapTheme`.
2170
- * recenter — re-ejecuta el encuadre a markers/route bajo demanda.
2171
- * cluster — togglear agrupamiento nativo de markers cercanos.
2172
- * follow — la cámara sigue al marker `live` mientras se mueve.
2173
- */
2174
- type MapControlKind = 'zoom' | 'fullscreen' | 'mapStyle' | 'recenter' | 'cluster' | 'follow';
2175
- /**
2176
- * Barra de controles flotantes de `kf-map` — pieza puramente presentacional,
2177
- * sin estado propio: todo lo que sabe se lo pasa `MapView` por input, todo
2178
- * lo que hace lo reporta por output para que `MapView` (única dueña del
2179
- * `Map` de Mapbox) decida qué hacer. Vive en la misma carpeta que `MapView`
2180
- * a propósito (mismo criterio que `DropdownItem`/`DropdownPanel` con
2181
- * `Dropdown`): el acoplamiento es real, no de nombre — no tiene sentido
2182
- * fuera de un `kf-map` que la controle.
2183
- *
2184
- * Un solo stack vertical, siempre en la esquina superior derecha del mapa
2185
- * (fijado por `MapView.css`, no acá): es donde ya vivía el `NavigationControl`
2186
- * de Mapbox que este componente reemplaza, y la atribución obligatoria de
2187
- * Mapbox (`AttributionControl`, nunca oculta) vive abajo-derecha — un solo
2188
- * stack evita que dos grupos floten en esquinas distintas y compitan por
2189
- * espacio en un mapa `size="sm"` de 200px de alto.
2190
- *
2191
- * El stack no es una lista continua de botones idénticos: cada grupo lógico
2192
- * es su propia "card" flotante (`kf-radius-md`+`kf-shadow-md`+fondo blanco,
2193
- * ver `.kf-map-controls__group` en `MapControls.css`), separadas entre sí
2194
- * por el `gap` del `:host` — mismo lenguaje ya verificado visualmente en
2195
- * `route-assistance-monorepo/design/mockups/operations-final.html`
2196
- * (`.map__ctrl-group`). Orden fijo de arriba a abajo, independiente del
2197
- * orden en el array `controls` del consumidor (para que dos pantallas que
2198
- * piden los mismos controles en distinto orden se vean IGUAL):
2199
- *
2200
- * 1. zoom — magnificación (par +/-, unido por un divisor, sin gap entre sí).
2201
- * 2. cámara — recenter + follow (posición de la cámara).
2202
- * 3. datos — cluster (densidad de los markers, eje distinto de cámara).
2203
- * 4. chrome — mapStyle + fullscreen (nada del mapa en sí).
2204
- *
2205
- * Cada grupo usa `@if` sobre AL MENOS UNO de sus controles (ej. el grupo de
2206
- * cámara aparece con solo `recenter`, solo `follow`, o ambos) — agregar o
2207
- * sacar un control nunca reacomoda ni depende de los demás grupos.
2208
- *
2209
- * Todos los botones siguen siendo `kf-button[variant=surface][shape=circle]`
2210
- * — sin tocar `Button.ts`: el fondo/sombra propios de `surface` se anulan
2211
- * SOLO dentro de un `.kf-map-controls__group` vía una custom property CSS
2212
- * (ver el comentario junto a esa clase en `MapControls.css` sobre por qué
2213
- * eso sí atraviesa la encapsulación de Angular y un selector normal no).
2214
- */
2215
- declare class MapControls {
2216
- readonly controls: _angular_core.InputSignal<MapControlKind[]>;
2217
- readonly clusterActive: _angular_core.InputSignal<boolean>;
2218
- readonly followActive: _angular_core.InputSignal<boolean>;
2219
- /** Sin ningún marker `live`, togglear "seguir" no tendría ningún efecto
2220
- * visible — se deshabilita en vez de dejar un botón que parece no hacer nada. */
2221
- readonly canFollow: _angular_core.InputSignal<boolean>;
2222
- readonly isFullscreen: _angular_core.InputSignal<boolean>;
2223
- /** Nombre en español del tema actualmente resuelto (ej. "Calles"), para
2224
- * anunciar en el `aria-label` del botón de estilo qué se está por perder,
2225
- * no solo que "algo" va a cambiar. */
2226
- readonly styleLabel: _angular_core.InputSignal<string>;
2227
- /** Tema resuelto HOY (`light|dark|...`) — a diferencia de `styleLabel`
2228
- * (ya traducido a texto), esto es lo que `MapThemePicker` necesita para
2229
- * marcar la opción activa. */
2230
- readonly activeTheme: _angular_core.InputSignal<MapTheme>;
2231
- readonly zoomIn: _angular_core.OutputEmitterRef<void>;
2232
- readonly zoomOut: _angular_core.OutputEmitterRef<void>;
2233
- readonly recenter: _angular_core.OutputEmitterRef<void>;
2234
- readonly followToggle: _angular_core.OutputEmitterRef<void>;
2235
- readonly clusterToggle: _angular_core.OutputEmitterRef<void>;
2236
- /** Reemplaza al viejo `styleCycle` (ciclado de un click): ahora el botón
2237
- * `mapStyle` abre `MapThemePicker` y este output reporta la elección real
2238
- * hecha ahí. */
2239
- readonly themeSelect: _angular_core.OutputEmitterRef<MapTheme>;
2240
- readonly fullscreenToggle: _angular_core.OutputEmitterRef<void>;
2241
- /** Visibilidad del panel de temas — estado puramente de UI (abierto o no),
2242
- * no de negocio: `MapView` no necesita saberlo, así que vive acá y no
2243
- * rompe el criterio de "sin estado propio" del resto de la clase (ver
2244
- * JSDoc). Se cierra solo (click afuera/Escape) vía `kf-dropdown`, y acá
2245
- * además al elegir un tema. */
2246
- protected readonly mapStyleOpen: _angular_core.WritableSignal<boolean>;
2247
- protected onThemeSelect(theme: MapTheme): void;
2248
- protected readonly hasZoom: _angular_core.Signal<boolean>;
2249
- protected readonly hasRecenter: _angular_core.Signal<boolean>;
2250
- protected readonly hasFollow: _angular_core.Signal<boolean>;
2251
- protected readonly hasCluster: _angular_core.Signal<boolean>;
2252
- protected readonly hasMapStyle: _angular_core.Signal<boolean>;
2253
- protected readonly hasFullscreen: _angular_core.Signal<boolean>;
2254
- /** El grupo "cámara" (card) aparece con solo `recenter`, solo `follow`, o
2255
- * ambos — ver JSDoc de la clase sobre por qué cada `@if` de grupo cubre
2256
- * "al menos uno de sus controles" en vez de un control puntual. */
2257
- protected readonly hasCamera: _angular_core.Signal<boolean>;
2258
- protected readonly hasChrome: _angular_core.Signal<boolean>;
2259
- /**
2260
- * Texto corto que revela cada botón en hover/foco vía `[kfTooltip]` (ver
2261
- * `MapControls.html` — la librería ya resuelve este patrón con
2262
- * `TooltipDirective`, no con CSS a mano) — la misma acción que ya describe
2263
- * `ariaLabel`, condensada para caber en una píldora en vez de leerse como
2264
- * oración completa. No reemplaza `ariaLabel` (que sigue siendo la fuente
2265
- * real para lectores de pantalla): esto es SOLO visual, para quien puede
2266
- * ver el mapa pero no sabe qué hace un ícono suelto.
2267
- */
2268
- protected readonly followLabel: _angular_core.Signal<"Dejar de seguir" | "Seguir">;
2269
- protected readonly clusterLabel: _angular_core.Signal<"Desagrupar" | "Agrupar">;
2270
- protected readonly mapStyleLabel: _angular_core.Signal<string>;
2271
- protected readonly fullscreenLabel: _angular_core.Signal<"Salir de pantalla completa" | "Pantalla completa">;
2272
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<MapControls, never>;
2273
- 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>;
2274
- }
2275
-
2276
- type MapSize = 'sm' | 'md' | 'lg' | 'full';
2277
- type MapVariant = 'interactive' | 'preview';
2278
- /** Vocabulario semántico único de tono, compartido con `TextTone`/`StatTileTone`. */
2279
- type MapMarkerTone = 'neutral' | 'ok' | 'warn' | 'error' | 'info';
2280
- interface MapLatLng {
2281
- lat: number;
2282
- lng: number;
2283
- }
2284
- /**
2285
- * Botón de acción real dentro de `MapMarker.popup` — cada click emite
2286
- * `markerAction` con su `key` (nunca el `label`, ver JSDoc de `popup` más
2287
- * abajo). `icon` es OPCIONAL a propósito: no romper a los consumidores que
2288
- * ya usaban `actions` sin él (era el único campo hasta esta ronda).
2289
- *
2290
- * La jerarquía visual entre acciones NO es un campo aparte (`variant`) sino
2291
- * la POSICIÓN en el array: la primera acción se renderiza con estilo
2292
- * prominente (fill, mismo peso que `kf-button[variant=primary]`, el
2293
- * default del botón); el resto con estilo discreto (mismo peso que
2294
- * `kf-button[variant=ghost]` — ver el ejemplo de `Modal` con
2295
- * `variant="ghost"` para "Cancelar" junto a un primary implícito). Un
2296
- * consumidor con una sola acción no necesita pensar en esto: esa acción ya
2297
- * es "la primera".
2298
- */
2299
- interface MapPopupAction {
2300
- key: string;
2301
- label: string;
2302
- /** Ícono líder del catálogo de `kf-icon` — sin esto, la acción se ve solo
2303
- * con texto (compatibilidad con `actions` ya declarado sin `icon`). */
2304
- icon?: IconName;
2305
- }
2306
- interface MapMarker {
2307
- position: MapLatLng;
2308
- /** Sin `icon`, cae en `map-pin` — el pin genérico del catálogo. Se
2309
- * ignora cuando el control `cluster` está activo (ver JSDoc de
2310
- * `controls` — el clustering nativo resuelve por capas WebGL, no por
2311
- * DOM, y ahí no hay dónde montar un `kf-icon`). */
2312
- icon?: IconName;
2313
- tone?: MapMarkerTone;
2314
- /** Nombre accesible del marker (`aria-label`/`title`) — no abre un popup. */
2315
- label?: string;
2316
- /**
2317
- * Identificador propio del consumidor (ej. el id del ticket/vehículo que
2318
- * representa) — sin esto el marker no es clickeable: `kf-map` no tiene
2319
- * forma de reportar "se hizo click en ESTE marker" sin algo que
2320
- * identificarlo de vuelta hacia `markerSelect`. Opcional a propósito: un
2321
- * mapa puramente decorativo (ej. la vista previa de `variant="preview"`)
2322
- * no necesita que sus markers respondan a nada.
2323
- */
2324
- id?: string;
2325
- /**
2326
- * Marca este marker como el punto que sigue la cámara cuando el control
2327
- * `follow` está activo — ej. la posición en tiempo real de un vehículo o
2328
- * técnico. Genérico a propósito (no `isVehicle` ni ningún nombre atado al
2329
- * dominio de un portal): `@kaufmann-lab/ui` no sabe qué es un "vehículo",
2330
- * solo que ALGÚN marker es el que se sigue. Si más de un marker lo marca,
2331
- * se sigue el primero en `markers()`. Distinto de la geolocalización del
2332
- * propio dispositivo (`navigator.geolocation`, fuera de alcance): esto
2333
- * sigue un dato que el consumidor ya tiene, no ubica al usuario.
2334
- */
2335
- live?: boolean;
2336
- /**
2337
- * Mini-card que aparece al hover/foco de este marker — solo si el marker
2338
- * tiene `id` (ver JSDoc de arriba): sin `id` el marker no es clickeable ni
2339
- * enfocable, así que no habría forma de revelarla por teclado.
2340
- *
2341
- * `actions` son botones REALES (no el viejo `ctaLabel` de texto plano sin
2342
- * acción): cada click emite `markerAction` con su `key` — nunca el
2343
- * `label`, que es solo texto de UI y puede repetirse con significados
2344
- * distintos entre consumidores — y detiene la propagación del click para
2345
- * no disparar además `markerSelect` sobre el marker completo (ver
2346
- * `buildMarkerPopup()`).
2347
- *
2348
- * Vocabulario genérico a propósito (`title`/`description`/`details`/
2349
- * `actions`, no `folio`/`cliente`/`verTicket`): esta librería no sabe qué
2350
- * es un "ticket" — el caso de uso real (folio+cliente+estado+SLA+acciones)
2351
- * es responsabilidad de quien arma cada `MapMarker`.
2352
- */
2353
- popup?: {
2354
- title: string;
2355
- description?: string;
2356
- /** Pares clave/valor extra (ej. estado + SLA) — se listan debajo de `description`. */
2357
- details?: {
2358
- label: string;
2359
- value: string;
2360
- }[];
2361
- /** Botones de acción reales — ver JSDoc de `MapPopupAction`. */
2362
- actions?: MapPopupAction[];
2363
- };
2364
- }
2365
- /**
2366
- * Mapa con motor real de Mapbox GL JS — decisión de producto ya cerrada
2367
- * (comparación de 6 proveedores). El access token vive a nivel app, no por
2368
- * uso: ver `provideMapConfig` en `map-config.ts` (mismo criterio que
2369
- * `API_CONFIG` de `packages/api-client`), inyectado una vez en
2370
- * `app.config.ts` de cada app consumidora.
2371
- *
2372
- * `size` (`sm|md|lg` alturas fijas, `full` llena el contenedor padre) NO
2373
- * hace step-down por viewport como `kf-page-header`: ese mecanismo existe
2374
- * ahí porque un tamaño de FUENTE fijo se corta en pantallas chicas; una
2375
- * caja de mapa no tiene ese problema — el canvas de Mapbox ya es
2376
- * responsive dentro de cualquier tamaño de contenedor. Lo que sí hace
2377
- * falta (y que el header no necesita) es un `ResizeObserver` real: Mapbox
2378
- * no se entera solo de que su contenedor cambió de tamaño (un layout que
2379
- * colapsa un sidebar, un cambio de orientación) — hay que llamar
2380
- * `map.resize()` a mano cuando el contenedor cambia (mismo motivo por el
2381
- * que `fullscreenchange` también llama `resize()`, ver `onFullscreenChange`).
2382
- *
2383
- * `variant` es un eje ortogonal a `size` (mismo criterio que `variant` +
2384
- * `tone` en `kf-badge`): `interactive` trae la posibilidad de controles
2385
- * flotantes reales (ver `controls`); `preview` (`interactive: false` de
2386
- * Mapbox GL) es para una card de lista, sin chrome y sin foco por teclado —
2387
- * no hace nada al recibir foco, así que no debe formar parte del tab order,
2388
- * y por eso NUNCA renderiza `controls` aunque el consumidor los pida:
2389
- * togglear "seguir" o abrir pantalla completa no tiene sentido en un mapa
2390
- * que no acepta interacción. Ninguna de las dos variantes oculta la
2391
- * atribución: es requisito de licencia de Mapbox, no cosmético.
2392
- *
2393
- * `theme` (`light|dark|streets|satellite|outdoors`) resuelve el estilo real
2394
- * de Mapbox — eje totalmente distinto del `[data-theme]` de
2395
- * `tokens/themes/*` (paleta de marca de la librería): ninguno de los 5
2396
- * temas de `tokens/` es oscuro todavía, así que no hay ninguna señal real
2397
- * de la que derivar un default "según el tema de marca activo" — construir
2398
- * esa detección ahora sería simular una señal que no existe.
2399
- *
2400
- * `controls` (`MapControlKind[]`, default `[]`) es siempre opt-in: un mapa
2401
- * sin `controls` no dibuja ningún botón flotante, ni siquiera en
2402
- * `interactive` (antes de esta versión, `interactive` agregaba el
2403
- * `NavigationControl` default de Mapbox gratis — se sacó a propósito: era
2404
- * el look genérico de Mapbox, no el de esta librería, exactamente la queja
2405
- * que motivó este rediseño. Un consumidor que quería esos botones de zoom
2406
- * ahora los pide con `[controls]="['zoom']"`). Cada kind es una pieza
2407
- * independiente entre sí (ver `MapControls.ts` para el detalle de cada
2408
- * una) — agregar una no reacomoda ni depende de las demás.
2409
- *
2410
- * <kf-map label="Ubicación del vehículo y del destino" [markers]="markers()" />
2411
- * <kf-map size="sm" variant="preview" label="Vista previa de la ruta" [route]="route()" />
2412
- * <kf-map label="Flota completa" [markers]="fleet()" [controls]="['zoom', 'recenter', 'cluster', 'follow']" />
2413
- */
2414
- declare class MapView {
2415
- readonly size: _angular_core.InputSignal<MapSize>;
2416
- readonly variant: _angular_core.InputSignal<MapVariant>;
2417
- readonly markers: _angular_core.InputSignal<MapMarker[]>;
2418
- readonly route: _angular_core.InputSignal<MapLatLng[] | undefined>;
2419
- readonly fitToMarkers: _angular_core.InputSignalWithTransform<boolean, unknown>;
2420
- readonly center: _angular_core.InputSignal<MapLatLng | undefined>;
2421
- readonly zoom: _angular_core.InputSignal<number | undefined>;
2422
- /**
2423
- * Default `'streets'`, no `'light'` ni `'dark'`. `'dark'` se probó
2424
- * primero por coherencia con la paleta navy de marca, pero una
2425
- * verificación visual real sobre una ubicación de servicio típica (una
2426
- * faena/zona rural, no un centro urbano denso) mostró el problema real:
2427
- * `dark-v11` casi no tiene datos que pintar fuera de ciudades grandes —
2428
- * el mapa se ve prácticamente todo negro, ilegible, exactamente donde
2429
- * más importa que se lea bien (ubicaciones de terreno, no de oficina).
2430
- * `'streets'` SIEMPRE tiene cartografía base visible (calles, nombres,
2431
- * uso de suelo) sin importar la densidad del lugar — es el único de los
2432
- * 5 estilos sin ese riesgo. Sigue siendo overrideable por el consumidor. */
2433
- readonly theme: _angular_core.InputSignal<MapTheme>;
2434
- /** Controles flotantes a mostrar — ver JSDoc de la clase y `MapControls.ts`. */
2435
- readonly controls: _angular_core.InputSignal<MapControlKind[]>;
2436
- /**
2437
- * Descripción accesible real del mapa — el `<div>` donde Mapbox renderiza
2438
- * su canvas queda vacío de semántica para un lector de pantalla. Requerido
2439
- * a propósito: un default genérico ("Mapa") no dice qué está mostrando
2440
- * ESTE mapa; cada consumidor conoce su propio caso real (ej. "Ubicación
2441
- * del vehículo y del destino").
2442
- */
2443
- readonly label: _angular_core.InputSignal<string>;
2444
- /** Se emite al hacer click sobre un marker que declaró `id` (ver JSDoc de
2445
- * `MapMarker.id`) — un marker SIN `id` no es clickeable, no emite nada.
2446
- * No dispara en modo `cluster` (los markers pasan a ser capas WebGL sin
2447
- * DOM propio donde colgar un listener) ni en `variant="preview"` (el
2448
- * mapa entero no es interactivo ahí). */
2449
- readonly markerSelect: _angular_core.OutputEmitterRef<string>;
2450
- /** Se emite al clickear un botón de `MapMarker.popup.actions` — ver el
2451
- * JSDoc de `popup` para por qué reporta `actionKey` (nunca el `label`) y
2452
- * por qué el click no dispara además `markerSelect`. */
2453
- readonly markerAction: _angular_core.OutputEmitterRef<{
2454
- markerId: string;
2455
- actionKey: string;
2456
- }>;
2457
- private readonly mapConfig;
2458
- private readonly environmentInjector;
2459
- private readonly document;
2460
- private readonly destroyRef;
2461
- private readonly hostElementRef;
2462
- private readonly containerRef;
2463
- /** `preview` no es foco-navegable ni se anuncia como región interactiva. */
2464
- protected readonly isPreview: _angular_core.Signal<boolean>;
2465
- protected readonly containerRole: _angular_core.Signal<"img" | "region">;
2466
- /** Ver estado de carga/error real, más abajo (`status`). */
2467
- protected readonly status: _angular_core.WritableSignal<"error" | "ready" | "loading">;
2468
- /** Elección manual del consumidor final vía el control `mapStyle` — pisa
2469
- * `theme()` de forma sostenida (no vuelve a `theme()` sola) hasta que se
2470
- * destruya este `kf-map`; ver `resolvedTheme()`. */
2471
- private readonly userTheme;
2472
- protected readonly styleLabel: _angular_core.Signal<string>;
2473
- /** Igual que `resolvedTheme()` pero público para el template — lo necesita
2474
- * `MapThemePicker` (vía `MapControls`) para marcar la opción activa. */
2475
- protected readonly activeTheme: _angular_core.Signal<MapTheme>;
2476
- protected readonly clusterActive: _angular_core.WritableSignal<boolean>;
2477
- protected readonly followActive: _angular_core.WritableSignal<boolean>;
2478
- protected readonly hasLiveMarker: _angular_core.Signal<boolean>;
2479
- protected readonly isFullscreen: _angular_core.WritableSignal<boolean>;
2480
- /** Sin estilo de marca propio (Mapbox Studio) no hay nada que ciclar con
2481
- * `mapStyle` — se filtra en vez de dejar un botón que no cambiaría nada. */
2482
- protected readonly effectiveControls: _angular_core.Signal<MapControlKind[]>;
2483
- protected readonly showControls: _angular_core.Signal<boolean>;
2484
- private map;
2485
- /** `extraIconRefs`: íconos de las acciones del popup (ver `buildMarkerPopup()`)
2486
- * — se crean con `createComponent()` igual que `iconRef` (el pin), así que
2487
- * necesitan el mismo `.destroy()` en `clearMarkers()`. */
2488
- private markerHandles;
2489
- /** Markers DOM reales sobre cada cluster real (punto 1, a11y) — separados
2490
- * de `markerHandles` porque se reconstruyen en su propio ciclo (ver
2491
- * `syncClusterMarkers()`) y no tienen `iconRef` propio (sin `kf-icon`
2492
- * adentro, ver JSDoc de `addClusterMarkerToMap()`). */
2493
- private clusterMarkerHandles;
2494
- private resizeObserver;
2495
- private currentStyle;
2496
- private mapReady;
2497
- /** Popup resumen de un cluster real (ver `renderClusterPopup()`) — a
2498
- * diferencia del popup de marker individual (siempre en el DOM, mostrado
2499
- * por CSS `:hover`/`:focus-within`), este se crea/destruye a demanda: no
2500
- * cuelga de ningún elemento con estado hover propio, lo abre un click
2501
- * sobre una capa WebGL sin DOM. */
2502
- private clusterPopupElement;
2503
- private clusterPopupIconRefs;
2504
- /** Coordenadas del cluster abierto — necesarias para volver a proyectar
2505
- * contra pantalla en cada `'move'` (ver `repositionClusterPopup()`); el
2506
- * cluster en sí no se mueve, pero su posición EN PANTALLA sí cuando se
2507
- * hace pan. */
2508
- private clusterPopupCoordinates;
2509
- private readonly onFullscreenChange;
2510
- constructor();
2511
- /**
2512
- * El bundle default de `mapbox-gl` arma su Web Worker serializando el
2513
- * código del worker como string y cargándolo por Blob URL — un bundler
2514
- * ESM-nativo (esbuild, el builder `application` de Angular) minifica ESE
2515
- * string también, sin saber que corre verbatim en otro contexto, y el
2516
- * worker termina con un identificador roto en runtime
2517
- * (`ReferenceError: <letra> is not defined`, ver mapbox/mapbox-gl-js#13700
2518
- * y angular/angular-cli#27378). `workerClass` reemplaza ese mecanismo por
2519
- * un `Worker` real armado con el patrón literal
2520
- * `new Worker(new URL(..., import.meta.url), { type: 'module' })` — el
2521
- * único que esbuild reconoce de forma ESTÁTICA y separa en su propio
2522
- * chunk (esbuild lo detecta por análisis sintáctico puro, dondequiera que
2523
- * el literal aparezca en el archivo — no hace falta que esté a nivel de
2524
- * módulo). `mapbox-gl-csp.worker.js` es un asset copiado tal cual, nunca
2525
- * TypeScript compilado, así que `import.meta.url` sigue apuntando a un
2526
- * archivo real en cualquiera de los dos contextos donde esbuild procesa
2527
- * este literal: en `ng test` (bundlea el `.ts` fuente tal cual, el asset
2528
- * está al lado en `src/lib/map/`) y en el build real de un consumidor
2529
- * (bundlea el FESM publicado — `ng-package.json` copia este mismo asset a
2530
- * `dist/ui/fesm2022/`, junto al bundle aplanado, con esa carpeta EXACTA a
2531
- * propósito). Ambos casos comparten la misma ruta relativa
2532
- * (`./mapbox-gl-csp.worker.js`) porque el asset se coloca deliberadamente
2533
- * al lado del consumidor en los dos. Si un cambio de Angular Package
2534
- * Format renombra `fesm2022`, hay que actualizar el `output` en
2535
- * `ng-package.json` a la par.
2536
- *
2537
- * A propósito NO es un side-effect de módulo (no corre con solo
2538
- * `import`ar este archivo): un `<kf-map>` real en jsdom construye un
2539
- * `Worker` real vía esta misma expresión, y jsdom no lo soporta — revienta
2540
- * el proceso de Vitest entero, no solo el test, y se lleva puesto
2541
- * CUALQUIER spec que solo importe algo del barrel de la librería (nada
2542
- * que ver con mapas). Por eso vive acá, dentro de `initializeMap()`, que
2543
- * únicamente corre cuando alguien de verdad instancia y renderiza este
2544
- * componente — `MapView.spec.ts` mockea `mapbox-gl` entero, así que ahí
2545
- * la asignación cae sobre el mock, no sobre el `mapboxgl` real.
2546
- */
2547
- private assignWorkerClass;
2548
- private initializeMap;
2549
- private resolvedTheme;
2550
- private resolvedStyle;
2551
- private toLngLat;
2552
- /**
2553
- * `matchMedia` (no una media query CSS): esta preferencia decide la
2554
- * `duration` que se le pasa a la API de cámara de Mapbox, que corre en
2555
- * JS/WebGL — CSS no tiene nada que animar acá. `document.defaultView` (no
2556
- * el `window` global) para que quede mockeable en test igual que el resto
2557
- * de la clase usa `DOCUMENT` inyectado.
2558
- */
2559
- private prefersReducedMotion;
2560
- /** Colapsa cualquier duración a 0 (instantáneo, sin animar) si el usuario
2561
- * pidió `prefers-reduced-motion: reduce` — un único punto de verdad para
2562
- * que ningún movimiento de cámara se salga de esa preferencia. */
2563
- private cameraDuration;
2564
- private syncCamera;
2565
- /**
2566
- * Encuadra `bounds` con `fitBounds()`, salvo que sea una caja degenerada
2567
- * (ver `DEGENERATE_BOUNDS_THRESHOLD_DEG`) — un solo punto total (un único
2568
- * marker sin ruta, o vehículo+destino prácticamente en el mismo sitio) le
2569
- * da a `fitBounds()` un ancho y alto ~cero, que Mapbox resuelve con un
2570
- * `padding`/zoom que en la práctica deja el mapa mayormente en blanco
2571
- * ("se ve todo blanco", el bug real que esto corrige). Un `easeTo` con
2572
- * zoom fijo sensato muestra contexto real de calle en ese caso, animado
2573
- * igual que el resto (antes era un `jumpTo` seco).
2574
- */
2575
- private frameBounds;
2576
- private isDegenerateBounds;
2577
- /**
2578
- * Extiende sobre markers Y route: el nombre del input sigue el caso de
2579
- * uso principal (encuadrar los markers), pero un mapa con solo `route` y
2580
- * sin `markers` tampoco debería quedar en el encuadre default inútil.
2581
- */
2582
- private computeBounds;
2583
- private syncMarkers;
2584
- /**
2585
- * Crea el marker de Mapbox (DOM + `kf-icon` + wiring de click/teclado/
2586
- * popup) para UN `MapMarker` y lo agrega a `markerHandles` — compartido
2587
- * entre el modo normal (`syncMarkers()`, todos los markers) y el modo
2588
- * cluster (`syncClusterMarkers()`, solo los que quedan sueltos): un punto
2589
- * suelto en modo cluster tiene que verse y comportarse EXACTAMENTE igual
2590
- * que en modo normal, así que no puede haber dos implementaciones.
2591
- */
2592
- private addMarkerToMap;
2593
- /**
2594
- * Mini-card hija del `<div>` del marker — mismo patrón que
2595
- * `.kf-map-controls__label` (ver `MapControls.css`): un elemento SIEMPRE
2596
- * presente en el DOM, oculto por defecto vía `opacity`/`pointer-events` en
2597
- * `MapView.css` y revelado por CSS puro en `:hover`/`:focus-within` del
2598
- * marker padre — sin JS de show/hide, y sin `mapboxgl.Popup` (evita una
2599
- * segunda gestión de overlay/posicionamiento cuando el CSS ya alcanza acá,
2600
- * igual que ya decidió `.kf-map-controls__label`).
2601
- *
2602
- * Todo el subárbol (título, descripción, `details`, `actions`) se arma con
2603
- * `document.createElement` puro, nunca `kf-text`/`kf-button`: este marker
2604
- * entero vive fuera del template de Angular (ver el comentario grande de
2605
- * `ViewEncapsulation.None` más arriba) — meter un componente real acá
2606
- * significaría un `createComponent()` propio por cada línea de texto, el
2607
- * mismo costo que ya paga `kf-icon` en `syncMarkers()` pero sin ninguna
2608
- * necesidad real (no hay nada dinámico que un componente resuelva mejor
2609
- * que texto plano estilado por clase). Es la MISMA decisión que ya regía
2610
- * para `title`/`description` antes de esta ronda — se extiende acá para no
2611
- * mezclar dos convenciones distintas dentro del mismo fragmento de DOM.
2612
- *
2613
- * `actions` sí necesita wiring propio (a diferencia del resto de la card,
2614
- * que burbujea su click al listener ya cableado sobre `element` en
2615
- * `syncMarkers()`): cada botón corta esa burbuja con `stopPropagation()`
2616
- * para reportar `markerAction` en vez de (además) `markerSelect`.
2617
- *
2618
- * Jerarquía visual de `actions` (ver JSDoc de `MapPopupAction`): la
2619
- * PRIMERA acción del array es la principal (`--primary`, fill); el resto
2620
- * es secundaria (`--ghost`, transparente) — mismo peso que ya usan
2621
- * `kf-button[variant=primary]`/`[variant=ghost]` en el resto de la
2622
- * librería, sin inventar un esquema de color nuevo acá.
2623
- *
2624
- * El punto de color junto al título (`kf-map__marker-popup-tone`) reusa
2625
- * el `tone` del propio marker — no el de cada `details` (que son pares
2626
- * label/value genéricos, sin un campo "cuál de estos es el estado"): es
2627
- * la misma señal que YA pinta el círculo del marker en el mapa, mostrada
2628
- * de nuevo acá para que el popup se lea sin tener que mirar el marker.
2629
- */
2630
- private buildMarkerPopup;
2631
- private clearMarkers;
2632
- /**
2633
- * Recorte de borde para el popup de marker individual (punto 2) —
2634
- * `getBoundingClientRect()` del marker y del propio contenedor del mapa,
2635
- * recalculado en el evento que dispara la apertura (`mouseenter`/
2636
- * `focusin`, ver `addMarkerToMap()`). El mecanismo de mostrar/ocultar
2637
- * sigue siendo 100% CSS; esto solo decide 3 clases modificadoras que
2638
- * `MapView.css` traduce en alineación horizontal/vertical:
2639
- *
2640
- * `--align-start` — el popup se desborda por la izquierda del
2641
- * contenedor si se centra sobre el marker: se alinea su borde
2642
- * izquierdo con el del marker en vez de centrarlo.
2643
- * `--align-end` — mismo caso, contra el borde derecho.
2644
- * `--below` — no entra arriba del marker (cerca del borde superior del
2645
- * contenedor): se muestra debajo en vez de arriba.
2646
- *
2647
- * El popup YA está en el DOM en este punto (oculto por `opacity`, no por
2648
- * `display:none` — ver `MapView.css`), así que `getBoundingClientRect()`
2649
- * devuelve su tamaño real aunque todavía no sea visible.
2650
- */
2651
- private positionMarkerPopup;
2652
- /**
2653
- * Clustering nativo de Mapbox (`cluster: true` en la fuente GeoJSON, no un
2654
- * algoritmo armado a mano) — 2 capas, solo para clusters REALES (2+
2655
- * puntos agrupados, `has: point_count`): círculo agregado y su label de
2656
- * cantidad. Un punto que Mapbox deja suelto (sin `point_count`) YA NO
2657
- * tiene capa WebGL propia — ver `syncClusterMarkers()`, que lo renderiza
2658
- * como el mismo marker DOM de siempre (con ícono/tono/popup reales, cosa
2659
- * que una capa `circle` no puede mostrar).
2660
- */
2661
- private syncClusterSource;
2662
- /**
2663
- * Contraparte en DOM de la capa WebGL: mientras esa capa solo dibuja
2664
- * clusters reales, ESTE método decide qué puntos quedan sueltos al zoom
2665
- * ACTUAL (`querySourceFeatures` con el mismo filtro `!has(point_count)`
2666
- * que ya usa Mapbox para separar cluster de leaf) y les arma el mismo
2667
- * marker DOM que tendrían sin clustering — vía `addMarkerToMap()`, la
2668
- * MISMA función que usa el modo normal, no una copia.
2669
- *
2670
- * `querySourceFeatures` puede devolver una feature repetida si cae cerca
2671
- * de un borde interno de tile — de ahí el `Set` sobre `index` (la
2672
- * property que identifica al `MapMarker` de origen, ver
2673
- * `markersToFeatureCollection()`), no una lista con posibles duplicados.
2674
- * Mismo motivo (y mismo `Set`, esta vez sobre `cluster_id`) para los
2675
- * clusters reales que se resuelven acá abajo.
2676
- *
2677
- * Se llama solo cuando `clusterActive()` — ver los listeners `'data'`/
2678
- * `'moveend'` en `initializeMap()` sobre CUÁNDO se llama esto.
2679
- */
2680
- private syncClusterMarkers;
2681
- /**
2682
- * Marker DOM real sobre un cluster (punto 1, a11y) — MISMO patrón de
2683
- * wiring que `addMarkerToMap()` (`role="button"`, `tabindex="0"`,
2684
- * `click`+`keydown` de Enter/Space), aplicado acá porque el círculo que
2685
- * pinta `CLUSTER_CIRCLE_LAYER_ID` es una capa WebGL sin nodo DOM propio
2686
- * —inalcanzable por teclado (WCAG SC 2.1.1 Keyboard). `mapboxgl.Marker`
2687
- * posiciona y reproyecta este `<div>` solo en cada pan/zoom, igual que ya
2688
- * hace con cualquier marker individual — no hace falta reposicionarlo a
2689
- * mano como sí necesita el popup resumen (ver `repositionClusterPopup()`).
2690
- *
2691
- * Sin `kf-icon`/texto propio adentro a propósito: el círculo+conteo que ya
2692
- * pinta la capa WebGL sigue siendo la representación visual (mismo
2693
- * `'circle-radius'`, ver `clusterMarkerRadiusPx()`); este `<div>` es
2694
- * transparente y se superpone exacto encima, aportando SOLO la
2695
- * interactividad real que la capa WebGL no puede tener.
2696
- */
2697
- private addClusterMarkerToMap;
2698
- private clearClusterMarkers;
2699
- /**
2700
- * Activación de un cluster real (click O Enter/Space sobre su marker DOM,
2701
- * ver `addClusterMarkerToMap()`): ANTES hacía zoom directo
2702
- * (`getClusterExpansionZoom()` + `easeTo`) — ahora abre el popup resumen
2703
- * (`renderClusterPopup()`) con la lista de tickets agrupados, y ESE zoom
2704
- * pasa a ser la acción explícita "Acercar para separar" dentro del popup
2705
- * (ver `expandCluster()`).
2706
- */
2707
- private activateCluster;
2708
- /** Dispara el zoom que antes hacía el click directo sobre el círculo —
2709
- * ahora vive detrás del botón "Acercar para separar" del popup resumen. */
2710
- private expandCluster;
2711
- /**
2712
- * `index` identifica de vuelta al `MapMarker` de origen (ver
2713
- * `syncClusterMarkers()`); `title`/`description`/`markerId` son las
2714
- * mismas properties que ya arma `buildMarkerPopup()` (folio/cliente/id
2715
- * del caso real) — se serializan acá TAMBIÉN para que
2716
- * `getClusterLeaves()` pueda listarlas en el popup resumen sin tener que
2717
- * ir a buscar cada `MapMarker` original por separado. Los campos son
2718
- * `string` (no `undefined`): las properties de una `Feature` de GeoJSON
2719
- * no viajan bien como `undefined` — `''` significa "sin dato" en los 3.
2720
- */
2721
- private markersToFeatureCollection;
2722
- private clearClusterLayers;
2723
- /**
2724
- * Popup resumen de un cluster real (punto 4) — a diferencia del popup de
2725
- * marker individual (siempre en el DOM, `:hover`/`:focus-within` puro),
2726
- * este no cuelga de ningún elemento con estado hover propio: lo abre un
2727
- * click sobre una capa WebGL sin DOM, así que se crea/destruye a demanda
2728
- * y su visibilidad la maneja JS, no CSS. Se agrega como hijo del HOST
2729
- * (`kf-map`, no `.kf-map__container`): ambos comparten exactamente la
2730
- * misma caja (`.kf-map__container` es `width/height:100%` sin padding/
2731
- * borde dentro de un host `position:relative`), así que las coordenadas
2732
- * de `map.project()` (relativas al contenedor) valen también acá — mismo
2733
- * ancestro posicionado que ya usa `.kf-map__overlay`.
2734
- *
2735
- * `title`/`description` de cada leaf son `popup.title`/`popup.description`
2736
- * del `MapMarker` original (folio/cliente en el caso real) — ver
2737
- * `markersToFeatureCollection()`. El punto de color reusa `tone`, MISMO
2738
- * criterio que el punto de color del popup individual (ver JSDoc de
2739
- * `buildMarkerPopup()`): no se inventa un texto de "estado" que la
2740
- * librería no tiene, se reusa la señal de color que sí existe.
2741
- */
2742
- private renderClusterPopup;
2743
- /** Reproyecta el popup de cluster contra pantalla — se llama en cada
2744
- * `'move'` (ver `initializeMap()`): el punto geográfico no se mueve, pero
2745
- * su posición EN PANTALLA sí cuando se hace pan. Mismo criterio de
2746
- * recorte de borde que `positionMarkerPopup()` (punto 2), pero en
2747
- * píxeles calculados a mano en vez de clases CSS: este popup no cuelga
2748
- * de un ancestro posicionado por CSS relativo al marker, su posición
2749
- * ENTERA viene de `map.project()`. */
2750
- private repositionClusterPopup;
2751
- private closeClusterPopup;
2752
- private syncRoute;
2753
- private resolveColorToken;
2754
- protected handleZoomIn(): void;
2755
- protected handleZoomOut(): void;
2756
- /** Reencuadra a demanda, ignorando `fitToMarkers()` (tiene sentido pedirlo
2757
- * justo cuando `fitToMarkers` está en `false`) y sin apagar `follow`: si
2758
- * está activo, la próxima posición nueva vuelve a pegar la cámara al
2759
- * marker `live` — este reencuadre es una corrección puntual, no un modo. */
2760
- protected handleRecenter(): void;
2761
- protected handleFollowToggle(): void;
2762
- protected handleClusterToggle(): void;
2763
- /** Reemplaza al viejo ciclado de un click (`handleStyleCycle`): la
2764
- * elección real llega desde `MapThemePicker`, vía `MapControls`. */
2765
- protected handleThemeSelect(theme: MapTheme): void;
2766
- protected handleFullscreenToggle(): void;
2767
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<MapView, never>;
2768
- 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>;
2769
- }
2770
-
2771
- /**
2772
- * Signal reactivo con el estado actual de un media query — para cuando un
2773
- * componente necesita SABER en qué lado de un breakpoint está (no solo
2774
- * mostrar/ocultar con CSS). Un `@media` en `styles` no alcanza cuando la
2775
- * decisión no es puramente visual: por ejemplo, un componente hijo que
2776
- * recibe un input booleano (`[collapsed]`) no puede "leer" un breakpoint
2777
- * CSS del padre — necesita que alguien en TypeScript se lo diga.
2778
- *
2779
- * Encontrado al construir `SidebarShell`: el estado `collapsed` (colapso
2780
- * del rail en desktop) se pasaba tal cual a cada `kf-sidebar-nav-item`, sin
2781
- * importar el viewport. En mobile, donde el rail deslizado siempre muestra
2782
- * etiquetas completas (ver estilos de `SidebarShell`), los ítems seguían
2783
- * ocultando su label si el usuario había dejado el rail colapsado en
2784
- * desktop antes de achicar la ventana — un CSS que dice "en mobile se ve
2785
- * ancho" no cambia el valor del input que ya llegó al hijo.
2786
- *
2787
- * Debe llamarse dentro de un contexto de inyección (el `constructor` de un
2788
- * componente, o un campo de clase inicializado ahí — mismo requisito que
2789
- * cualquier función que use `inject()`).
2790
- *
2791
- * protected readonly isMobile = matchMediaSignal('(max-width: 900px)');
2792
- * // ...
2793
- * [collapsed]="collapsed() && !isMobile()"
2794
- */
2795
- declare function matchMediaSignal(query: string): Signal<boolean>;
2796
-
2797
- /**
2798
- * Card de estadística/KPI: label, valor grande, ícono opcional en la esquina
2799
- * superior derecha, badge de esquina opcional (pegado al valor) y una nota
2800
- * al pie. Pensada para tableros de resumen (ej. "11 Tickets abiertos").
2801
- *
2802
- * Compone sobre `kf-card` en vez de duplicar su estilo de contenedor — así
2803
- * el look de "card" (radio, sombra, borde) tiene una única fuente de verdad
2804
- * en toda la librería, tal como pide el design system (página 14: "Card...
2805
- * es la base de paneles, tarjetas de métrica, tablas...").
2806
- *
2807
- * Slot `[actions]` opcional (proyectado al pie, después de `foot`) para un
2808
- * CTA dentro de la card — ej. "Saldo Pendiente" con botón "Pagar Ahora" en
2809
- * Fase 6 de OTD. Mismo mecanismo de proyección por selector que `[footer]`
2810
- * en `kf-modal`, no un `<ng-content>` default: así una card sin acciones no
2811
- * paga ningún espacio extra.
2812
- *
2813
- * <kf-metric-card label="Tickets abiertos" value="11" icon="ticket" foot="+2 hoy" />
2814
- * <kf-metric-card label="Tasa de resolución" value="94%" badge="+2%" value-color="var(--kf-color-success)" />
2815
- * <kf-metric-card label="Saldo Pendiente" value="$45,000">
2816
- * <kf-button actions variant="primary" size="sm">Pagar Ahora</kf-button>
2817
- * </kf-metric-card>
2818
- */
2819
- declare class MetricCard {
2820
- readonly label: _angular_core.InputSignal<string>;
2821
- readonly value: _angular_core.InputSignal<string | number>;
2822
- readonly unit: _angular_core.InputSignal<string | undefined>;
2823
- /** Ícono en la esquina superior derecha, renderizado vía `kf-icon`. */
2824
- readonly icon: _angular_core.InputSignal<IconName | undefined>;
2825
- readonly foot: _angular_core.InputSignal<string | undefined>;
2826
- /** Tinte opcional para el valor grande (ej. un color semántico de estado). */
2827
- readonly valueColor: _angular_core.InputSignal<string | undefined>;
2828
- /** Badge de esquina opcional, pegado al valor (ej. "+2%"). */
2829
- readonly badge: _angular_core.InputSignal<string | undefined>;
2830
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<MetricCard, never>;
2831
- 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>;
2832
- }
2833
-
2834
- /**
2835
- * Franja fija en la base de una pantalla completa para apps mobile del
2836
- * ecosistema: encapsula el padding con `safe-area-inset-bottom` (para no
2837
- * invadir la zona del home indicator), el borde superior y el fondo —
2838
- * cualquier acción, chip de estado o hint que necesite vivir pegado al
2839
- * borde inferior del dispositivo se proyecta acá vía `<ng-content>`.
2840
- *
2841
- * <kf-mobile-footer>
2842
- * <kf-button>Cerrar orden</kf-button>
2843
- * </kf-mobile-footer>
2844
- */
2845
- declare class MobileFooter {
2846
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<MobileFooter, never>;
2847
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<MobileFooter, "kf-mobile-footer", never, {}, {}, never, ["*"], true, never>;
2848
- }
2849
-
2850
- /**
2851
- * Encabezado de pantalla completa (fondo navy) para apps mobile del
2852
- * ecosistema — cubre tanto la raíz de un tab (sin volver) como el detalle
2853
- * de un flujo (con volver).
2854
- *
2855
- * <kf-mobile-header title="Mis asignaciones" />
2856
- *
2857
- * <kf-mobile-header title="STAR-20841" subtitle="Cerrado hoy · 15:18" [showBack]="true" (back)="onBack()" />
2858
- *
2859
- * Sin `kf-text` para `eyebrow`/`title`/`subtitle`: su `tone` es un set
2860
- * cerrado sin blanco/inverso, y sus variantes fijan `color` explícito sobre
2861
- * el elemento interno — un `color: white` heredado desde afuera nunca le
2862
- * gana a esa declaración directa. Markup propio con los tokens reales.
2863
- *
2864
- * El botón "volver" tampoco es `kf-button`: su `variant="ghost"` fija el
2865
- * mismo token de color que este fondo navy (el ícono quedaría invisible).
2866
- */
2867
- declare class MobileHeader {
2868
- readonly title: _angular_core.InputSignal<string>;
2869
- /** Línea chica y translúcida ARRIBA del título — ej. un folio o un saludo. */
2870
- readonly eyebrow: _angular_core.InputSignal<string | undefined>;
2871
- /** `true` cuando `eyebrow` es un código (folio) — usa tipografía monoespaciada. */
2872
- readonly eyebrowMono: _angular_core.InputSignal<boolean>;
2873
- /** Línea chica y translúcida DEBAJO del título — ej. "Cerrado hoy · 15:18". */
2874
- readonly subtitle: _angular_core.InputSignal<string | undefined>;
2875
- readonly showBack: _angular_core.InputSignal<boolean>;
2876
- /** El componente no navega por sí solo: cada pantalla decide qué significa
2877
- * "volver" (`Location.back()`, retroceder un paso del flujo, etc.). */
2878
- readonly back: _angular_core.OutputEmitterRef<void>;
2879
- protected onBackClick(): void;
2880
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<MobileHeader, never>;
2881
- 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>;
2882
- }
2883
-
2884
- /**
2885
- * Diálogo centrado sobre un backdrop: confirmaciones destructivas, formularios
2886
- * cortos, decisiones que bloquean el flujo.
2887
- *
2888
- * Se apoya en el `<dialog>` nativo (`showModal()`), así que el atrapado de
2889
- * foco, el backdrop, el orden en el top-layer y el cierre con `Escape` los
2890
- * resuelve el navegador — no hay trampa de foco escrita a mano acá. Lo único
2891
- * que se agrega es el lock de scroll del body, que `<dialog>` no hace.
2892
- *
2893
- * `open` es two-way. Con `dismissible` en `false` (ej. mientras una acción
2894
- * destructiva está en vuelo) se cancelan `Escape` y el clic en el backdrop:
2895
- * solo cierra el consumidor poniendo `open` en `false`.
2896
- *
2897
- * Las acciones van en el slot `footer` — el atributo `footer` se pone
2898
- * DIRECTO en cada `kf-button`, sin envolverlos en un `<div footer>`:
2899
- * `.kf-modal__footer` ya es `display:flex` con `gap` propio entre sus
2900
- * hijos DIRECTOS. Envolver los botones en un `<div>` intermedio dejaría
2901
- * ese `<div>` como único hijo del footer — el gap nunca llegaría a
2902
- * separar los botones entre sí (bug real encontrado en `web-otd-frontend`:
2903
- * los 2 botones quedaban pegados, con 0px de separación medida, pese a que
2904
- * el CSS del footer declaraba `gap: var(--kf-space-md)`).
2905
- *
2906
- * <kf-modal [(open)]="confirmOpen" title="Eliminar unidad">
2907
- * <p>Esta acción no se puede deshacer.</p>
2908
- * <kf-button footer variant="ghost" (click)="confirmOpen.set(false)">Cancelar</kf-button>
2909
- * <kf-button footer (click)="remove()">Eliminar</kf-button>
2910
- * </kf-modal>
2911
- *
2912
- * El footer agrupa todo a la derecha por defecto (`justify-content:
2913
- * flex-end`) — para una acción secundaria/informativa que debe quedar
2914
- * separada a la izquierda (ej. un link "Ver documento" junto a un botón
2915
- * "Cerrar"), agregar la clase `kf-modal__footer-start` AL MISMO elemento
2916
- * que ya lleva `footer`, no a un wrapper (mismo motivo que el `footer`
2917
- * directo: un `<div>` intermedio rompe el `gap`):
2918
- *
2919
- * <kf-link footer class="kf-modal__footer-start" disabled>Ver documento</kf-link>
2920
- * <kf-button footer (click)="open.set(false)">Cerrar</kf-button>
2921
- */
2922
- declare class Modal {
2923
- readonly title: _angular_core.InputSignal<string>;
2924
- /** Visibilidad, two-way: `[(open)]="confirmOpen"`. */
2925
- readonly open: _angular_core.ModelSignal<boolean>;
2926
- /** Permite cerrar con el botón, `Escape` o clic en el backdrop. */
2927
- readonly dismissible: _angular_core.InputSignalWithTransform<boolean, unknown>;
2928
- /** Ancho por defecto: `480px` (sin pasar nada). Un modal con contenido
2929
- * tabular/ancho (ej. una lista de varios campos por fila) puede pedir
2930
- * más, ej. `width="720px"` — sigue topeado por `100vw` en pantallas
2931
- * angostas, mismo `min()` de siempre. */
2932
- readonly width: _angular_core.InputSignal<string | null>;
2933
- protected readonly titleId: string;
2934
- private readonly dialog;
2935
- private readonly document;
2936
- private readonly overlayContainer;
2937
- constructor();
2938
- /** `Escape` dispara `cancel`; cancelarlo lo deja abierto. */
2939
- protected onCancel(event: Event): void;
2940
- /**
2941
- * Clic en el backdrop. Un clic en la zona del backdrop tiene como `target` al
2942
- * propio `<dialog>` (el contenido vive en el panel de adentro), así que basta
2943
- * comparar contra él — no hace falta cortar la propagación en el panel.
2944
- *
2945
- * Va como HostListener y no como binding en el template porque un `(click)`
2946
- * sobre el `<dialog>` dispara las reglas a11y de click-sin-teclado; acá el
2947
- * equivalente de teclado es `Escape`, que el `<dialog>` nativo ya maneja.
2948
- */
2949
- protected onHostClick(event: MouseEvent): void;
2950
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<Modal, never>;
2951
- 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; }; "width": { "alias": "width"; "required": false; "isSignal": true; }; }, { "open": "openChange"; }, never, ["*", "[footer]"], true, never>;
2952
- }
2953
-
2954
- /** Color de la franja izquierda y del fondo. */
2955
- type NoteBlockTone = 'neutral' | 'ok' | 'warn' | 'error' | 'info';
2956
- /**
2957
- * Bloque de texto atribuido a una PERSONA: encabezado, prosa y firma al pie,
2958
- * con fondo suave y franja de color — la anatomía de una cita, distinta de
2959
- * `kf-alert` (que interrumpe el flujo con `role="alert"`). El texto va como
2960
- * contenido proyectado y respeta los saltos de línea del autor.
2961
- */
2962
- declare class NoteBlock {
2963
- readonly title: _angular_core.InputSignal<string | undefined>;
2964
- /** Segunda pieza del encabezado, en la misma línea del `title`. */
2965
- readonly subtitle: _angular_core.InputSignal<string | undefined>;
2966
- /** Pie de atribución — quién escribió y cuándo, ya formateado. */
2967
- readonly signature: _angular_core.InputSignal<string | undefined>;
2968
- readonly tone: _angular_core.InputSignal<NoteBlockTone>;
2969
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<NoteBlock, never>;
2970
- 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>;
2971
- }
2972
-
2973
- type TextAs = 'h1' | 'h2' | 'h3' | 'p' | 'span';
2974
- type TextVariant = 'title-xxl' | 'title-xl' | 'title-lg' | 'title-md' | 'title-sm' | 'body' | 'body-muted' | 'caption' | 'overline' | 'overline-strong';
2975
- type TextTone = 'neutral' | 'ok' | 'warn' | 'error' | 'info';
2976
- /**
2977
- * Tipografía unificada — separa la etiqueta semántica (`as`, qué es el
2978
- * texto para el DOM/accesibilidad) del estilo visual (`variant`, cómo se
2979
- * ve). Antes de este componente, cada pantalla que consume la librería
2980
- * definía su propio `h1`/`p` con `font-size` suelto para el mismo rol
2981
- * (título de página, subtítulo, texto secundario) — auditoría sobre
2982
- * `web-portal-latam-frontend` encontró 9 tamaños distintos entre login y
2983
- * home sin ningún paso de escala en común. `variant` resuelve eso contra
2984
- * la escala de `tokens/base.css` (`--kf-font-size-*`).
2985
- *
2986
- * `tone` sigue la misma convención cerrada que `Alert`/`Badge`/`Chip`: sin
2987
- * escape hatch de color libre. Si un caso necesita un color que `tone` no
2988
- * cubre, es una discusión de design system, no una prop nueva acá.
2989
- *
2990
- * `title-xl`/`title-xxl` son un tier aparte de `title-lg|md|sm`: no vienen
2991
- * de la auditoría de contenido de página (esa cerró en 2.125rem), son para
2992
- * títulos protagonistas de pantalla completa (ej. bienvenida del home vía
2993
- * `kf-page-header[size=xl|xxl]`) — ver tokens/base.css.
2994
- *
2995
- * `overline`/`overline-strong` son para el patrón "texto chico en mayúsculas
2996
- * junto a un dato" (ej. el label "RUC" de `kf-company-selector`, el badge de
2997
- * `kf-admin-badge`) — antes de estas variantes, ese patrón se reimplementaba
2998
- * suelto en al menos 7 componentes con tamaños/pesos ligeramente distintos
2999
- * entre sí. `overline` (peso 600) es para labels sin chrome propio;
3000
- * `overline-strong` (peso 700) es para el texto de un badge/pill que ya trae
3001
- * su propio fondo. Ninguna de las dos fija color — como el resto de los
3002
- * variants, el color se resuelve con `tone`.
3003
- *
3004
- * <kf-text as="h1" variant="title-lg">Bienvenido de nuevo</kf-text>
3005
- * <kf-text as="p" variant="body-muted">Ingresá con tu cuenta corporativa.</kf-text>
3006
- * <kf-text as="span" variant="caption" tone="error">Campo obligatorio</kf-text>
3007
- * <kf-text as="span" variant="overline">RUC</kf-text>
3008
- * <kf-text as="span" variant="overline-strong" tone="warn">Vista administrador</kf-text>
3009
- */
3010
- declare class Text {
3011
- /** Etiqueta semántica real que se renderiza — nunca es solo un `div`. */
3012
- readonly as: _angular_core.InputSignal<TextAs>;
3013
- readonly variant: _angular_core.InputSignal<TextVariant>;
3014
- readonly tone: _angular_core.InputSignal<TextTone>;
3015
- /**
3016
- * Copia de `as` para uso dentro del template: `as` es palabra reservada
3017
- * en las expresiones de Angular (`@if (x; as y)`), así que `@switch (as())`
3018
- * no parsea — NG5002. La prop pública sigue llamándose `as` (es solo un
3019
- * atributo HTML ahí, `<kf-text as="h1">`, no una expresión).
3020
- */
3021
- protected readonly tag: _angular_core.Signal<TextAs>;
3022
- protected hostClass(): string;
3023
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<Text, never>;
3024
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<Text, "kf-text", never, { "as": { "alias": "as"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "tone": { "alias": "tone"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
3025
- }
3026
-
3027
- /**
3028
- * Tamaño del título. Cada valor mapea 1:1 a una variante `title-*` de
3029
- * `kf-text`; `xl`/`xxl` son para títulos protagonistas (ej. bienvenida del
3030
- * home). Bajo los 900px, el tamaño efectivo baja un escalón (ver
3031
- * `MOBILE_STEP_DOWN`).
3032
- */
3033
- type PageHeaderTitleSize = 'sm' | 'md' | 'lg' | 'xl' | 'xxl';
3034
- /**
3035
- * Bloque de título al tope de una página: chip de ícono opcional, título (h1),
3036
- * subtítulo opcional y un área de acciones opcional proyectada vía
3037
- * `<ng-content select="[actions]">`.
3038
- *
3039
- * <kf-page-header title="Vehículos" subtitle="Gestión de la flota activa">
3040
- * <button actions>Nuevo vehículo</button>
3041
- * </kf-page-header>
3042
- *
3043
- * <kf-page-header icon="settings" title="Configuración" />
3044
- *
3045
- * <kf-page-header size="xl" title="Bienvenido de nuevo" />
3046
- */
3047
- declare class PageHeader {
3048
- readonly icon: _angular_core.InputSignal<IconName | undefined>;
3049
- readonly title: _angular_core.InputSignal<string>;
3050
- readonly subtitle: _angular_core.InputSignal<string | undefined>;
3051
- readonly size: _angular_core.InputSignal<PageHeaderTitleSize>;
3052
- private readonly isMobile;
3053
- protected readonly resolvedTitleVariant: _angular_core.Signal<TextVariant>;
3054
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<PageHeader, never>;
3055
- 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>;
3056
- }
3057
-
3058
- /** Un número de página clickeable o un separador "…" no interactivo. */
3059
- type PaginationItem = {
3060
- type: 'page';
3061
- page: number;
3062
- } | {
3063
- type: 'ellipsis';
3064
- };
3065
- /**
3066
- * Paginador numerado con elipsis + prev/next.
3067
- *
3068
- * Algoritmo de elipsis estándar: siempre se muestran la primera página, la
3069
- * última, la página actual y un par de vecinas alrededor de la actual; el
3070
- * resto de los huecos se colapsa en un separador "…". Cuando el hueco entre
3071
- * dos números visibles es de exactamente una página, se muestra ese número
3072
- * en vez de un "…" (no tiene sentido colapsar un solo elemento).
3073
- *
3074
- * El contenido proyectado (`<ng-content>`) es para el texto informativo
3075
- * opcional (ej. "21–40 de 273"), a la izquierda de los controles.
3076
- *
3077
- * <kf-pagination [page]="page" [pages]="totalPages" (pageChange)="page = $event">
3078
- * 21–40 de 273
3079
- * </kf-pagination>
3080
- *
3081
- * Con `pageSizeOptions`, agrega un select de "N por página" entre el texto
3082
- * informativo y los controles de navegación — el consumidor decide el
3083
- * tamaño real vía `pageSize`/`pageSizeChange` (two-way opcional, ninguno de
3084
- * los dos es requerido si no se usa el selector):
3085
- *
3086
- * <kf-pagination [page]="page" [pages]="totalPages" (pageChange)="page = $event"
3087
- * [pageSizeOptions]="[10, 25, 50]" [(pageSize)]="pageSize">
3088
- * 21–40 de 273
3089
- * </kf-pagination>
3090
- */
3091
- declare class Pagination {
3092
- readonly page: _angular_core.InputSignal<number>;
3093
- readonly pages: _angular_core.InputSignal<number>;
3094
- readonly pageChange: _angular_core.OutputEmitterRef<number>;
3095
- /** Opciones del selector "N por página" — sin esto, el selector no se
3096
- * renderiza (mismo criterio que `[footer]`/`[actions]` de otros
3097
- * componentes: ausente por default, nunca forzado). `kf-select` en
3098
- * `variant="compact"` no impone un máximo de opciones, a diferencia de
3099
- * `kf-segmented` (pensado solo para 2-3). */
3100
- readonly pageSizeOptions: _angular_core.InputSignal<number[]>;
3101
- readonly pageSize: _angular_core.ModelSignal<number | null>;
3102
- /** Lista de números de página y separadores a renderizar, ya resuelta por el algoritmo de elipsis. */
3103
- readonly items: _angular_core.Signal<PaginationItem[]>;
3104
- /** Con una sola página (o ninguna), el `<nav>` de prev/next+números no
3105
- * aporta nada navegable — antes se dejaba en el DOM igual (oculto solo
3106
- * por un hack `::ng-deep` del consumidor, ver `route-assistance-monorepo`)
3107
- * y el footer quedaba con el selector "Filas" descoordinado, como si le
3108
- * faltara una pieza. Sacar el `<nav>` del DOM (no `display:none`) resuelve
3109
- * esto para cualquier consumidor de `kf-pagination`, no solo para quien ya
3110
- * tenía el hack. El `divider` reusa el mismo criterio: sin `nav` no hay
3111
- * nada del otro lado que separar, así que tampoco corresponde dejarlo. */
3112
- protected readonly hasNav: _angular_core.Signal<boolean>;
3113
- /** `kf-select` trabaja con `value: string` — cada tamaño numérico se
3114
- * mapea a su representación en texto, ida y vuelta. */
3115
- protected readonly pageSizeSelectOptions: _angular_core.Signal<string[]>;
3116
- protected readonly pageSizeValue: _angular_core.Signal<string>;
3117
- protected setPageSize(value: string): void;
3118
- goTo(target: number): void;
3119
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<Pagination, never>;
3120
- 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>;
3121
- }
3122
-
3123
- /**
3124
- * Tarjeta contenedora con título, enlace de acción opcional a la derecha, y
3125
- * cuerpo con contenido libre — página 33: "Base de la mayoría de bloques del
3126
- * portal". Compone sobre `kf-card` para compartir la misma superficie
3127
- * (radio, sombra, borde) que el resto de la librería.
3128
- *
3129
- * <kf-panel title="Mis documentos por pagar" actionLabel="Ir a pagos" actionHref="/pagos">
3130
- * <kf-action-tile-row icon="download" title="Pagos" description="Revisa tu estado de cuenta y realiza pagos." />
3131
- * </kf-panel>
3132
- *
3133
- * El host y el `kf-card` interno son flex column con `flex:1` de punta a
3134
- * punta — si un grid/flex externo estira `kf-panel` a la altura de un
3135
- * hermano más alto, el contenido proyectado hereda esa altura en vez de
3136
- * dejar un hueco vacío bajo el `kf-card`. Sin esa presión externa (altura
3137
- * "auto" del contenedor), `flex:1` no tiene espacio extra que repartir y el
3138
- * panel se comporta igual que antes.
3139
- */
3140
- declare class Panel {
3141
- readonly title: _angular_core.InputSignal<string>;
3142
- readonly actionLabel: _angular_core.InputSignal<string | undefined>;
3143
- readonly actionHref: _angular_core.InputSignal<string | undefined>;
3144
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<Panel, never>;
3145
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<Panel, "kf-panel", never, { "title": { "alias": "title"; "required": true; "isSignal": true; }; "actionLabel": { "alias": "actionLabel"; "required": false; "isSignal": true; }; "actionHref": { "alias": "actionHref"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
3146
- }
3147
-
3148
- /** Un ítem de navegación de la barra de marca. `icon` es opcional y solo se
3149
- * usa en el drawer mobile (columna, ver template) — el `<nav>` horizontal de
3150
- * escritorio se mantiene solo texto, sin ícono ni chevron. */
3151
- interface PortalHeaderNavItem {
3152
- label: string;
3153
- href: string;
3154
- active?: boolean;
3155
- icon?: IconName;
3156
- }
3157
- /** Una acción de la franja de administrador (ej. "Consultar deuda clientes"). */
3158
- interface PortalHeaderAdminAction {
3159
- label: string;
3160
- href: string;
3161
- }
3162
- /**
3163
- * Cabecera completa del portal (página 31) — franja de administrador
3164
- * (acento ámbar, solo admins), barra de marca con logo, navegación y
3165
- * pastilla de sesión. Compone `kf-admin-badge` para la franja y `kf-avatar
3166
- * pill` para la sesión, en vez de reimplementar ambos.
3167
- *
3168
- * Es distinto de `kf-page-header`/`kf-page-head` (título de una vista
3169
- * interna) — este es el chrome global del portal, se monta una sola vez.
3170
- *
3171
- * `:host` es `position: sticky; top: 0` — el header queda pegado arriba al
3172
- * hacer scroll. No es `fixed`: como no se superpone al contenido en el
3173
- * primer render, no hace falta que el consumidor compense con
3174
- * `padding-top`.
3175
- *
3176
- * Bajo los 768px, el `<nav>` deja de mostrarse en línea junto al logo y
3177
- * colapsa detrás de un botón de menú (ícono `menu`/`x`) que despliega un
3178
- * `kf-drawer` (panel lateral genérico, ver `Drawer.ts`) anclado al borde
3179
- * IZQUIERDO — mismo lado que el botón que lo abre, convención dominante
3180
- * Material/iOS/Android — con los mismos `navItems` en columna dentro del
3181
- * body (fondo claro) de ese drawer.
3182
- *
3183
- * `PortalHeader` expone dos slots propios pensados para que el consumidor
3184
- * proyecte contenido que en escritorio vive en otro lugar:
3185
- * - `<ng-content select="[kf-portal-header-extra]">`: identidad de sesión
3186
- * (ej. `kf-drawer-session`). Cae en `[kf-drawer-header]` de `kf-drawer` —
3187
- * el único contenedor con fondo oscuro (`--kf-color-tertiary-deep`, ver
3188
- * `Drawer.ts`) que `kf-drawer-session` espera como fondo (no trae uno
3189
- * propio, ver `DrawerSession.ts`).
3190
- * - `<ng-content select="[kf-portal-header-footer]">`: acciones separadas del
3191
- * resto (ej. `kf-drawer-logout`). Cae en `[kf-drawer-footer]` de `kf-drawer`.
3192
- *
3193
- * `PortalHeader` no sabe qué es ninguno de los dos contenidos, solo les da
3194
- * el lugar correcto dentro del drawer ya desplegado (ver `Drawer.ts`).
3195
- *
3196
- * <kf-portal-header
3197
- * [isAdmin]="true"
3198
- * [adminActions]="[{ label: 'Consultar deuda clientes', href: '/deuda' }, { label: 'Cambiar DNI', href: '/dni' }]"
3199
- * [navItems]="[{ label: 'Home', href: '/', active: true, icon: 'grid' }, { label: 'Vehículos', href: '/vehiculos', icon: 'truck' }]"
3200
- * tagline="Portal privado"
3201
- * userInitials="TS"
3202
- * userName="Tomás Soto"
3203
- * [showCart]="true"
3204
- * (cartClick)="onCartClick()"
3205
- * >
3206
- * <kf-drawer-session
3207
- * kf-portal-header-extra
3208
- * brand="Portal Kaufmann LATAM"
3209
- * name="Tomás Soto"
3210
- * email="tomas.soto@kaufmann.com"
3211
- * />
3212
- * <kf-drawer-logout kf-portal-header-footer (logoutClick)="onLogout()" />
3213
- * </kf-portal-header>
3214
- */
3215
- declare class PortalHeader {
3216
- readonly brand: _angular_core.InputSignal<string>;
3217
- /** URL del logo como imagen. Sin ella (default), se mantiene el texto de `brand`. */
3218
- readonly logoSrc: _angular_core.InputSignal<string | undefined>;
3219
- /** `alt` del logo cuando se usa `logoSrc`. Sin ella, cae en `brand()`. */
3220
- readonly logoAlt: _angular_core.InputSignal<string | undefined>;
3221
- readonly isAdmin: _angular_core.InputSignalWithTransform<boolean, unknown>;
3222
- readonly adminActions: _angular_core.InputSignal<PortalHeaderAdminAction[]>;
3223
- readonly navItems: _angular_core.InputSignal<PortalHeaderNavItem[]>;
3224
- readonly userInitials: _angular_core.InputSignal<string>;
3225
- /** Texto junto al logo (ej. "Portal privado"), con separador vertical. Sin
3226
- * separador ni texto si queda vacío. */
3227
- readonly tagline: _angular_core.InputSignal<string>;
3228
- /** Nombre completo para el bloque "HOLA / {nombre}" dentro de la pastilla
3229
- * de sesión. Vacío (default) mantiene la pastilla como hoy: solo avatar,
3230
- * sin bloque de texto ni chevron — retrocompatible con cualquier
3231
- * consumidor que no pase este input. */
3232
- readonly userName: _angular_core.InputSignal<string>;
3233
- readonly showCart: _angular_core.InputSignalWithTransform<boolean, unknown>;
3234
- readonly sessionClick: _angular_core.OutputEmitterRef<void>;
3235
- readonly cartClick: _angular_core.OutputEmitterRef<void>;
3236
- /** Visibilidad del drawer de navegación mobile (bajo los 768px). Se pasa a
3237
- * `kf-drawer` como `[(open)]` — `PortalHeader` ya no implementa overlay,
3238
- * `Escape` ni atrapado de foco por su cuenta, eso vive en `Drawer`. */
3239
- protected readonly mobileMenuOpen: _angular_core.WritableSignal<boolean>;
3240
- /**
3241
- * `ElementRef` real del botón de sesión (el avatar en la esquina
3242
- * derecha), no de todo el header. Pensado para anclar un `kf-dropdown`
3243
- * externo — sin esto, el único `ElementRef` disponible desde afuera es el
3244
- * del host de `PortalHeader`, que ocupa el ancho completo de la pantalla
3245
- * (logo + nav + sesión), y el overlay del CDK termina "pegado" a la
3246
- * esquina del header entero en vez de al botón que el usuario clickeó.
3247
- *
3248
- * <kf-dropdown [(open)]="menuOpen" [anchor]="header().sessionButtonRef()">
3249
- */
3250
- readonly sessionButtonRef: _angular_core.Signal<ElementRef<any>>;
3251
- /**
3252
- * Ancla real de cada `<a>` del `<nav>` horizontal (una por `navItems()`,
3253
- * mismo orden — `@for` trackea por `item.label`, así que la lista no se
3254
- * recrea en cada cambio de ruta, solo cambia qué índice trae
3255
- * `active: true`). Sirve para medir dónde poner la cápsula deslizante
3256
- * (`thumbStyle`, ver abajo) sin duplicar el layout del nav en JS.
3257
- */
3258
- private readonly navItemAnchors;
3259
- /** Fuerza recalcular `thumbStyle` en un resize — un resize de ventana no
3260
- * cambia ningún signal de los que `thumbStyle` ya lee (`navItems`,
3261
- * `navItemAnchors`), pero sí puede correr el layout de los `<a>` bajo un
3262
- * ancho de columna distinto (texto largo pasando a otra línea, etc.). */
3263
- private readonly resizeTick;
3264
- protected onWindowResize(): void;
3265
- /**
3266
- * Posición/ancho de la cápsula blanca del ítem activo del `<nav>`, como
3267
- * un elemento propio (`.kf-portal-header__nav-thumb`) que se desliza con
3268
- * `transition` en vez de que cada `<a>` traiga su propio fondo blanco
3269
- * (eso hacía que el fondo "saltara" de un ítem a otro sin animación,
3270
- * porque son elementos DOM distintos — no hay nada que Angular pueda
3271
- * animar entre ellos).
3272
- */
3273
- protected readonly thumbStyle: _angular_core.Signal<Record<string, string>>;
3274
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<PortalHeader, never>;
3275
- 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>;
3276
- }
3277
-
3278
- /** Mismo vocabulario que `Badge.tone`/`TextTone` (menos `info`/`service`,
3279
- * que no aplican a un valor de progreso): `neutral` es el color de marca
3280
- * por defecto (sin implicar buen/mal estado), `ok`/`warn`/`error` sí
3281
- * comunican severidad. La librería no conoce el dominio que produce ese
3282
- * valor (SLA, storage, lo que sea) — el consumidor decide qué tone usar. */
3283
- type ProgressTone = 'neutral' | 'ok' | 'warn' | 'error';
3284
- /**
3285
- * Barra de progreso de un solo valor (0-100%) con color por severidad —
3286
- * para un porcentaje simple, a diferencia de `kf-bucket-progress`, que
3287
- * dibuja varias categorías proporcionales en la misma barra.
3288
- *
3289
- * <kf-progress [value]="80" tone="ok" label="80%" />
3290
- * <kf-progress [value]="ratio()" [tone]="severityTone()" />
3291
- *
3292
- * `value` se clampea a [0, 100] — un consumidor que calcule un ratio fuera
3293
- * de rango (ej. tiempo transcurrido sobre un SLA ya vencido) no rompe el
3294
- * ancho de la barra. `label` es texto libre opcional sobre la barra (ej.
3295
- * "80%"); sin él, no se renderiza ningún texto, solo la barra.
3296
- */
3297
- declare class Progress {
3298
- readonly value: _angular_core.InputSignal<number>;
3299
- readonly tone: _angular_core.InputSignal<ProgressTone>;
3300
- readonly label: _angular_core.InputSignal<string | undefined>;
3301
- protected readonly clampedValue: _angular_core.Signal<number>;
3302
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<Progress, never>;
3303
- 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>;
3304
- }
3305
-
3306
- /**
3307
- * Punto que emite ondas concéntricas en bucle, para marcar un dato que
3308
- * necesita atención inmediata sin depender de que el operador lo vea por
3309
- * casualidad — el mismo rol que un punto rojo estático ya cumple en varios
3310
- * lugares del producto (ver `.now__d`/`.board-alerts__dot` en
3311
- * `operations-final.html`), llevado a movimiento porque el color solo
3312
- * compite por atención con todo lo demás en pantalla; el movimiento no.
3313
- *
3314
- * Es una primitiva de énfasis, no un indicador de estado — para eso está
3315
- * `kf-status-pill` (punto estático + texto). Este componente no tiene
3316
- * texto ni tono semántico de "ok/warn/error": una sola apariencia (rojo,
3317
- * el color reservado para lo urgente en el design system), porque emitir
3318
- * ondas por algo que no es urgente banalizaría la señal la próxima vez que
3319
- * sí lo sea.
3320
- *
3321
- * <kf-pulse-dot />
3322
- * <kf-pulse-dot size="lg" />
3323
- *
3324
- * Respeta `prefers-reduced-motion` (ver PulseDot.css): la onda deja de
3325
- * animarse y el punto queda como un punto estático, sin perder la
3326
- * información de "esto es urgente" que da el color.
3327
- */
3328
- declare class PulseDot {
3329
- readonly size: _angular_core.InputSignal<"sm" | "md" | "lg">;
3330
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<PulseDot, never>;
3331
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<PulseDot, "kf-pulse-dot", never, { "size": { "alias": "size"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
3332
- }
3333
-
3334
- /** Chip de filtro genérico con contador — nunca hardcodeado a un dominio
3335
- * concreto (ej. estado de una cotización); el consumidor decide qué
3336
- * representa cada `value` y cómo mapea sus items contra él vía `predicate`.
3337
- *
3338
- * Bug real corregido: la primera versión leía `item.status`/`item.chip` por
3339
- * nombre de campo mágico en vez de un `predicate` — colisionaba con
3340
- * dominios donde `status` ya significa otra cosa (ej. `Quote.status` en
3341
- * `web-otd-frontend` es 'ok'|'error'|'warn' de aprobación, no "tiene saldo
3342
- * pendiente"). Un chip "Con saldo pendiente" sobre cotizaciones terminaba
3343
- * filtrando por un campo que no tenía nada que ver, mostrando 0 resultados
3344
- * siempre. `predicate` es explícito y no depende de convenciones de nombre. */
3345
- interface QuotePickerChip<T = unknown> {
3346
- value: string;
3347
- label: string;
3348
- count: number;
3349
- predicate: (item: T) => boolean;
3350
- }
3351
- /** Una opción del dropdown "Ordenar por", visible solo en `variant="modal"`. */
3352
- interface QuotePickerSortOption {
3353
- value: string;
3354
- label: string;
3355
- }
3356
- /**
3357
- * Selector de un ítem "rico" de una lista buscable/filtrable — generalización
3358
- * del patrón "elegir una cotización" de Pagos y Facturas de OTD, pero sin
3359
- * ningún acoplamiento a `Quote`: todo el contenido de cada fila/card llega
3360
- * por `columns` (mismo `DataTableColumn` de `kf-data-table`, reexportado acá
3361
- * en vez de duplicar el concepto) + `cellTemplates` para contenido custom
3362
- * (thumbnail, monto formateado, etc. — mismo mecanismo `TemplateRef` +
3363
- * `ngTemplateOutlet` que ya usa `kf-data-table`).
3364
- *
3365
- * Un solo componente, dos contenedores (`variant: 'inline' | 'modal'`), UNA
3366
- * sola lógica de búsqueda/filtro/orden interna. Nunca dos implementaciones
3367
- * paralelas: el riesgo real de eso es que un fix de filtro se aplique en un
3368
- * modo y se olvide en el otro. Los `computed()` de más abajo
3369
- * (`filteredItems`, `sortedItems`) son la ÚNICA fuente de verdad para ambos
3370
- * `variant`; lo que cambia entre inline y modal es solo qué contenedor
3371
- * envuelve esos datos ya resueltos.
3372
- *
3373
- * ## Modo `inline` (default)
3374
- * Trigger a ANCHO COMPLETO (nunca una caja angosta de ancho fijo) que
3375
- * muestra el ítem activo colapsado — thumbnail, id + modelo, unidades +
3376
- * fecha + monto, CTA "Cambiar cotización" con chevron-down. Al click, el
3377
- * panel se expande INLINE (empuja el contenido de abajo, nunca flota
3378
- * encima) con buscador propio + chips de filtro + un grid de 2 columnas (1
3379
- * en mobile, breakpoint `max-width: 900px` vía `matchMediaSignal` — mismo
3380
- * mecanismo que ya usa el resto de la librería para decisiones de layout en
3381
- * TypeScript, no solo CSS). Por encima de `expandThreshold` resultados
3382
- * visibles, el panel recorta a un preview de `PREVIEW_ROWS` filas con un
3383
- * fade inferior y un botón dashed "Ver las N en pantalla completa" que
3384
- * emite `expandToModal` — el picker no decide qué pasa después (típicamente
3385
- * el consumidor pone `variant` en `'modal'` y `open` en `true`).
3386
- *
3387
- * ## Modo `modal`
3388
- * Mismo buscador, mismos chips, misma lógica de filtrado — compone
3389
- * `kf-modal` (proyectando el contenido) en vez de reimplementar un
3390
- * `<dialog>` a mano: la API de `Modal` (`[(open)]`, cierre con
3391
- * Escape/backdrop/botón vía `<dialog>` nativo) ya cubre exactamente lo que
3392
- * este modo necesita. Compone también `kf-data-table` para la tabla
3393
- * completa (columnas configurables, fade de overflow horizontal ya
3394
- * resuelto ahí) y `kf-pagination` para paginar — ninguno de los tres se
3395
- * reimplementa. Agrega un dropdown propio de "Ordenar por" (calibrado
3396
- * visualmente contra el trigger de `kf-select`, pero compacto: `kf-select`
3397
- * fuerza un ancho fijo de 240px pensado para formularios, no para una
3398
- * barra de herramientas de tabla — ver JSDoc de `sortOpen` más abajo).
3399
- *
3400
- * En mobile, `kf-data-table[cardable]` con `defaultView` forzado a
3401
- * `'cards'` decide la vista (mismo criterio que `matchMediaSignal` ya
3402
- * resolvió en otros consumidores): dentro de un modal a pantalla completa
3403
- * no tiene sentido ofrecer un toggle tabla/cards que en mobile daría una
3404
- * tabla angosta con scroll horizontal.
3405
- *
3406
- * DEUDA TÉCNICA CONOCIDA (confirmada con el usuario, ver CHANGELOG.md): el
3407
- * diseño validado con mockups pedía que en mobile el modal ocupe el 100%
3408
- * de la pantalla (botón "atrás", sin overlay centrado) — `kf-modal` no
3409
- * tiene ese modo, así que hoy en mobile se ve como un diálogo centrado
3410
- * angosto (`width: min(480px, ...)`). Es funcional (cards legibles,
3411
- * búsqueda, chips, paginación), solo visualmente distinto del mockup.
3412
- *
3413
- * <kf-quote-picker
3414
- * [items]="quotes()"
3415
- * [columns]="columns"
3416
- * [selectedId]="selectedQuoteId()"
3417
- * [idKey]="(q) => q.id"
3418
- * [chips]="statusChips()"
3419
- * [cellTemplates]="{ photo: photoCell(), amount: amountCell() }"
3420
- * (selectedIdChange)="selectedQuoteId.set($event)"
3421
- * (expandToModal)="variant.set('modal'); open.set(true)"
3422
- * />
3423
- */
3424
- declare class QuotePicker<T = unknown> {
3425
- readonly items: _angular_core.InputSignal<T[]>;
3426
- readonly columns: _angular_core.InputSignal<DataTableColumn[]>;
3427
- readonly selectedId: _angular_core.InputSignal<string | null>;
3428
- readonly idKey: _angular_core.InputSignal<(item: T) => string>;
3429
- /**
3430
- * Mapa `columnKey -> TemplateRef`, mismo mecanismo que `kf-data-table`
3431
- * (`ngTemplateOutlet` + contexto `{ $implicit: item, index }`) — se
3432
- * reenvía tal cual a la tabla interna del modo `modal`, y se usa
3433
- * directamente en el grid de cards del modo `inline`. Un solo mapa para
3434
- * ambos modos, consistente con la regla de "una sola lógica interna".
3435
- */
3436
- readonly cellTemplates: _angular_core.InputSignal<Record<string, TemplateRef<{
3437
- $implicit: T;
3438
- index: number;
3439
- }>> | undefined>;
3440
- readonly chips: _angular_core.InputSignal<QuotePickerChip<T>[]>;
3441
- readonly sortOptions: _angular_core.InputSignal<QuotePickerSortOption[]>;
3442
- readonly sort: _angular_core.ModelSignal<string | null>;
3443
- /**
3444
- * Umbral de volumen alto — el consumidor decide el número exacto, nunca
3445
- * hardcodeado a "cotizaciones" ni a ningún caso de uso puntual. Default
3446
- * 15 según el diseño validado con mockups.
3447
- */
3448
- readonly expandThreshold: _angular_core.InputSignal<number>;
3449
- readonly variant: _angular_core.InputSignal<"inline" | "modal">;
3450
- /** Visibilidad del modo `modal`, two-way — mismo patrón que `[(open)]` de `kf-modal`. */
3451
- readonly open: _angular_core.ModelSignal<boolean>;
3452
- readonly title: _angular_core.InputSignal<string>;
3453
- readonly searchPlaceholder: _angular_core.InputSignal<string>;
3454
- /** Ícono de placeholder cuando ninguna columna `cardRole: 'media'` resuelve
3455
- * un thumbnail real vía `cellTemplates` — el picker no asume qué dominio
3456
- * consume (cotización, vehículo, cliente...), así que no hardcodea un
3457
- * ícono fijo. Default `'file'` solo por no dejar el input sin valor. */
3458
- readonly placeholderIcon: _angular_core.InputSignal<IconName>;
3459
- readonly selectedIdChange: _angular_core.OutputEmitterRef<string>;
3460
- /** Disparado al superar `expandThreshold` en modo inline; el consumidor decide pasar a `modal`. */
3461
- readonly expandToModal: _angular_core.OutputEmitterRef<void>;
3462
- protected readonly searchQuery: _angular_core.WritableSignal<string>;
3463
- protected readonly activeChip: _angular_core.WritableSignal<string | null>;
3464
- protected readonly expanded: _angular_core.WritableSignal<boolean>;
3465
- protected readonly page: _angular_core.WritableSignal<number>;
3466
- protected readonly sortOpen: _angular_core.WritableSignal<boolean>;
3467
- /**
3468
- * Mismo breakpoint (`max-width: 900px`) que el resto de la librería usa
3469
- * para decisiones de layout en TypeScript (no solo CSS) — ver
3470
- * `matchMediaSignal`. Decide 1 vs. 2 columnas en el grid inline, y tabla
3471
- * vs. cards apiladas dentro del modal.
3472
- */
3473
- protected readonly isMobile: _angular_core.Signal<boolean>;
3474
- private readonly destroyRef;
3475
- private readonly previewScrollEl;
3476
- /** Mismo mecanismo de fade que `kf-data-table` — ver comentario en QuotePicker.css. */
3477
- protected readonly canFadeBottom: _angular_core.WritableSignal<boolean>;
3478
- readonly selectedItem: _angular_core.Signal<T | undefined>;
3479
- /** Filtro por texto (buscador propio) + filtro por chip activo, en ese orden. */
3480
- protected readonly filteredItems: _angular_core.Signal<T[]>;
3481
- /**
3482
- * Orden solo tiene efecto visible en modo `modal` (el diseño validado no
3483
- * pide dropdown de orden en inline) — se calcula igual en ambos casos
3484
- * porque es la MISMA lógica interna; en inline `sort()` simplemente se
3485
- * queda en `null` salvo que el consumidor lo setee explícitamente.
3486
- */
3487
- protected readonly sortedItems: _angular_core.Signal<T[]>;
3488
- /** Por encima del umbral, el preview inline se recorta salvo que el usuario ya haya expandido. */
3489
- protected readonly isHighVolume: _angular_core.Signal<boolean>;
3490
- protected readonly previewItems: _angular_core.Signal<T[]>;
3491
- protected readonly pages: _angular_core.Signal<number>;
3492
- protected readonly pagedItems: _angular_core.Signal<T[]>;
3493
- protected readonly sortLabel: _angular_core.Signal<string>;
3494
- constructor();
3495
- private updateFadeState;
3496
- private cellText;
3497
- toggleExpandedPanel(): void;
3498
- onSearchInput(event: Event): void;
3499
- selectChip(value: string): void;
3500
- select(item: T): void;
3501
- onItemKeydown(event: KeyboardEvent, item: T): void;
3502
- onSeeAll(): void;
3503
- onTableRowClick(event: {
3504
- row: T;
3505
- index: number;
3506
- }): void;
3507
- toggleSort(): void;
3508
- setSort(value: string): void;
3509
- trackItem(item: T, index: number): string | number;
3510
- /** Arrow function como campo (no un método) para que `[rowKey]` reciba
3511
- * una referencia ESTABLE entre change detections — `trackItem.bind(this)`
3512
- * en el template recrearía una función nueva en cada ciclo, invalidando
3513
- * cualquier optimización de `kf-data-table` que dependa de identidad. */
3514
- protected readonly rowKeyFn: (item: T, index: number) => string | number;
3515
- protected cellValue(item: T, column: DataTableColumn): string;
3516
- protected readonly mediaColumn: _angular_core.Signal<DataTableColumn | undefined>;
3517
- protected readonly titleColumn: _angular_core.Signal<DataTableColumn | undefined>;
3518
- protected readonly fieldColumns: _angular_core.Signal<DataTableColumn[]>;
3519
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<QuotePicker<any>, never>;
3520
- 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>;
3521
- }
3522
-
3523
- /**
3524
- * Estado vacío de reservas en taller con llamada a agendar (página 36):
3525
- * barra de estado vacío arriba ("No tienes reservas de taller"), panel de
3526
- * invitación al medio (ícono en círculo azul, título, descripción, link) y
3527
- * una fila de dos acciones al pie — un slot `[actions]` proyectado, para no
3528
- * acoplar el componente a botones fijos.
3529
- *
3530
- * Distinto de `kf-empty-state` genérico: este combina la barra + la
3531
- * invitación + las acciones como un bloque compuesto propio del dominio
3532
- * "taller", no un placeholder de una sola pieza.
3533
- *
3534
- * `grow` estira el panel de invitación para llenar el espacio vertical
3535
- * disponible — solo cuando el consumidor comparte fila con una tarjeta más
3536
- * alta (ej. junto a "Total de vehículos"); en un lugar sin esa presión
3537
- * (home, debajo del carousel) se deja en su alto natural.
3538
- *
3539
- * <kf-reserve-block emptyLabel="No tienes reservas de taller" grow
3540
- * title="¿Necesitas mantención?" description="Tenemos la mejor red de talleres."
3541
- * actionLabel="Agenda tu reserva aquí" actionHref="/taller/agendar">
3542
- * <kf-button actions icon="calendar">Reservar hora</kf-button>
3543
- * <kf-button actions variant="outline" icon="list">Ver agenda</kf-button>
3544
- * </kf-reserve-block>
3545
- */
3546
- declare class ReserveBlock {
3547
- readonly emptyLabel: _angular_core.InputSignal<string>;
3548
- readonly title: _angular_core.InputSignal<string>;
3549
- readonly description: _angular_core.InputSignal<string | undefined>;
3550
- readonly actionLabel: _angular_core.InputSignal<string | undefined>;
3551
- readonly actionHref: _angular_core.InputSignal<string | undefined>;
3552
- /** La invitación se estira para llenar la tarjeta (ej. junto a "Total de vehículos"). */
3553
- readonly grow: _angular_core.InputSignalWithTransform<boolean, unknown>;
3554
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<ReserveBlock, never>;
3555
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<ReserveBlock, "kf-reserve-block", never, { "emptyLabel": { "alias": "emptyLabel"; "required": false; "isSignal": true; }; "title": { "alias": "title"; "required": true; "isSignal": true; }; "description": { "alias": "description"; "required": false; "isSignal": true; }; "actionLabel": { "alias": "actionLabel"; "required": false; "isSignal": true; }; "actionHref": { "alias": "actionHref"; "required": false; "isSignal": true; }; "grow": { "alias": "grow"; "required": false; "isSignal": true; }; }, {}, never, ["[actions]"], true, never>;
3556
- }
3557
-
3558
- type RowActionKind = 'link' | 'button';
3559
- /**
3560
- * Una acción de fila — mismo shape que ya usaba `WorkshopRowAction` en
3561
- * `web-portal-latam-frontend` antes de este componente (Taller, tab
3562
- * "Trabajos terminados"), ahora compartido: `kind: 'link'` navega (necesita
3563
- * `href`, `onClick` se ignora), `kind: 'button'` dispara `onClick` en la
3564
- * pantalla actual. `disabled` aplica a ambos — un link sin `href` (o
3565
- * explícitamente `disabled`) se renderiza inerte en vez de clickeable, en
3566
- * vez de omitirse del todo: el consumidor decide que la acción EXISTE pero
3567
- * no está disponible todavía (ej. "Factura" mientras no exista el endpoint
3568
- * de descarga).
3569
- */
3570
- interface RowAction {
3571
- readonly kind: RowActionKind;
3572
- readonly icon: IconName;
3573
- readonly label: string;
3574
- readonly href?: string | null;
3575
- readonly disabled?: boolean;
3576
- readonly onClick?: () => void;
3577
- }
3578
- type RowActionsMode = 'auto' | 'inline' | 'menu';
3579
- /**
3580
- * Columna "Acciones" de cualquier tabla del ecosistema. Decide su propia
3581
- * forma según cuántas acciones recibe — no hace falta que cada tabla elija
3582
- * a mano entre "botones sueltos" y "menú":
3583
- *
3584
- * - `inline` (`actions().length <= inlineLimit()`, default 2): un
3585
- * ícono-botón de 32px por acción, sin texto — el `label` vive en un
3586
- * `kfTooltip`. Todas las acciones quedan visibles sin clic extra.
3587
- * - `menu` (más de `inlineLimit()`): un único botón "kebab" que abre un
3588
- * `kf-dropdown-panel` con una fila por acción.
3589
- *
3590
- * `mode` fuerza uno de los dos modos sin importar cuántas acciones haya
3591
- * (ej. una tabla que prefiere el kebab siempre, aunque tenga una sola
3592
- * acción, por consistencia visual con otra columna).
3593
- *
3594
- * <kf-row-actions [actions]="rowActions(row)" />
3595
- * <kf-row-actions [actions]="rowActions(row)" mode="menu" />
3596
- */
3597
- declare class RowActions {
3598
- readonly actions: _angular_core.InputSignal<readonly RowAction[]>;
3599
- readonly mode: _angular_core.InputSignal<RowActionsMode>;
3600
- /** Desde cuántas acciones el modo `auto` pasa de `inline` a `menu`. */
3601
- readonly inlineLimit: _angular_core.InputSignal<number>;
3602
- protected readonly menuOpen: _angular_core.WritableSignal<boolean>;
3603
- protected readonly effectiveMode: _angular_core.Signal<"menu" | "inline">;
3604
- protected onToggleMenu(): void;
3605
- protected onButtonAction(action: RowAction): void;
3606
- protected onMenuLinkClick(): void;
3607
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<RowActions, never>;
3608
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<RowActions, "kf-row-actions", never, { "actions": { "alias": "actions"; "required": true; "isSignal": true; }; "mode": { "alias": "mode"; "required": false; "isSignal": true; }; "inlineLimit": { "alias": "inlineLimit"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
3609
- }
3610
-
3611
- /**
3612
- * Título de sub-sección con chip de ícono, pensado para anidarse dentro de
3613
- * cards o paneles (a diferencia de `PageHeader`, que encabeza una página
3614
- * completa). Un área de acciones opcional se proyecta vía
3615
- * `<ng-content select="[actions]">` para no acoplar el componente a un set
3616
- * fijo de botones.
3617
- *
3618
- * `size` sigue la misma escala `'sm'|'md'|'lg'` que `Button`/`Icon`/`Spinner`
3619
- * (`md` es el default): `sm` reduce tipografía y espaciado para usarse
3620
- * dentro de cards ya compactas, `lg` los agranda para encabezar un bloque
3621
- * más prominente. `solid` rellena el chip de ícono con el color primario
3622
- * sólido en vez del tratamiento "soft" (fondo tenue) — pensado para
3623
- * superficies que ya tienen un tinte de color propio, donde el chip "soft"
3624
- * perdería contraste.
3625
- *
3626
- * <kf-section-header icon="settings" title="Preferencias"
3627
- * subtitle="Configuración general de la cuenta">
3628
- * <button actions>Editar</button>
3629
- * </kf-section-header>
3630
- *
3631
- * <kf-section-header title="Detalle" size="sm" [solid]="true" />
3632
- * <kf-section-header title="Resumen de flota" size="lg" />
3633
- */
3634
- declare class SectionHeader {
3635
- readonly icon: _angular_core.InputSignal<IconName | undefined>;
3636
- readonly title: _angular_core.InputSignal<string>;
3637
- readonly subtitle: _angular_core.InputSignal<string | undefined>;
3638
- readonly size: _angular_core.InputSignal<"sm" | "md" | "lg">;
3639
- readonly solid: _angular_core.InputSignalWithTransform<boolean, unknown>;
3640
- protected readonly iconSize: _angular_core.Signal<"sm" | "md" | "lg">;
3641
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<SectionHeader, never>;
3642
- 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>;
3643
- }
3644
-
3645
- /**
3646
- * Contenedor de navegación por secciones dentro de una misma pantalla —
3647
- * agrupa `kf-section-nav-item` y resuelve su layout fusionado según
3648
- * viewport, con un breakpoint PROPIO de 1024px (no el $mobile:900px
3649
- * compartido del resto de la librería — ver el comentario en
3650
- * `SectionNav.scss`):
3651
- *
3652
- * - Desktop (≥1024px): columna de botones a la izquierda + panel de
3653
- * contenido separado a la derecha, mostrando el contenido del ítem (o
3654
- * sub-ítem) activo.
3655
- * - Mobile y tablet (<1024px): acordeón — el contenido del ítem activo se
3656
- * despliega inline, justo debajo de su propio botón.
3657
- *
3658
- * El cambio entre ambos layouts es solo CSS (`SectionNav.scss` +
3659
- * `SectionNavItem.scss`, ver comentarios ahí): cada `kf-section-nav-item`
3660
- * proyecta su contenido una única vez y es la grilla la que lo reposiciona
3661
- * visualmente, sin recrearlo ni duplicarlo en el DOM.
3662
- *
3663
- * A diferencia de `kf-tabs` (selector de vista plano, sin contenido propio)
3664
- * y de `kf-sidebar-shell` (rail persistente ENTRE rutas, colapsable, fondo
3665
- * oscuro): esto es navegación DENTRO de una sola pantalla, con paneles de
3666
- * contenido rico por sección.
3667
- *
3668
- * La exclusividad entre ítems (y entre sub-ítems, dentro de cada uno) la
3669
- * decide quien consume el componente, igual que cada `kf-section-nav-item`
3670
- * por separado — `kf-section-nav` no guarda ningún estado de selección.
3671
- *
3672
- * <kf-section-nav>
3673
- * <kf-section-nav-item label="Mis datos personales" icon="user"
3674
- * [active]="openSection() === 'datos'" (toggled)="openSection.set('datos')">
3675
- * <app-personal-data />
3676
- * </kf-section-nav-item>
3677
- * <kf-section-nav-item label="Mis empresas" icon="layers"
3678
- * [active]="openSection() === 'empresas'" (toggled)="openSection.set('empresas')">
3679
- * <app-my-companies />
3680
- * </kf-section-nav-item>
3681
- * </kf-section-nav>
3682
- */
3683
- declare class SectionNav {
3684
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<SectionNav, never>;
3685
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<SectionNav, "kf-section-nav", never, {}, {}, never, ["*"], true, never>;
3686
- }
3687
-
3688
- interface SectionNavSubsectionData {
3689
- key: string;
3690
- label: string;
3691
- icon: IconName;
3692
- disabled?: boolean;
3693
- }
3694
- /**
3695
- * Ítem de nivel superior de `kf-section-nav`: header clickeable (ícono
3696
- * identificador opcional + label + chevron) que expande/colapsa contenido
3697
- * proyectado. La exclusividad entre ítems la decide quien consume el
3698
- * componente — cada click solo emite `toggled` y el padre resuelve qué
3699
- * ítem queda `active`, igual que `kf-folder-tabs` con `value`.
3700
- *
3701
- * `subsections` agrega un segundo nivel de navegación DENTRO del ítem
3702
- * (solo visible cuando el ítem está `active`), cada uno con su propio
3703
- * estado activo (`activeSubsection`) independiente del padre. El contenido
3704
- * de cada sub-ítem se proyecta envolviéndolo en `<kf-section-nav-subsection
3705
- * key="...">` dentro del mismo slot por defecto — ver esa clase.
3706
- *
3707
- * El contenido proyectado usa `[hidden]`, no `@if`: un ítem puede envolver
3708
- * un formulario con estado que el usuario ya completó, y colapsar/expandir
3709
- * (o cruzar el breakpoint propio de 1024px, ver `SectionNav.scss`) no debe
3710
- * destruir ese subárbol.
3711
- *
3712
- * <kf-section-nav-item
3713
- * label="Mis datos personales"
3714
- * icon="user"
3715
- * [active]="openSection() === 'datos'"
3716
- * (toggled)="openSection.set('datos')"
3717
- * >
3718
- * ...
3719
- * </kf-section-nav-item>
3720
- */
3721
- declare class SectionNavItem {
3722
- readonly label: _angular_core.InputSignal<string>;
3723
- readonly icon: _angular_core.InputSignal<IconName | undefined>;
3724
- readonly active: _angular_core.InputSignalWithTransform<boolean, unknown>;
3725
- readonly toggled: _angular_core.OutputEmitterRef<void>;
3726
- /** Sub-ítems navegables de este ítem, visibles solo mientras `active()`. */
3727
- readonly subsections: _angular_core.InputSignal<SectionNavSubsectionData[]>;
3728
- /** `key` del sub-ítem actualmente activo — ver `SectionNavSubsectionData`. */
3729
- readonly activeSubsection: _angular_core.InputSignal<string | undefined>;
3730
- readonly subsectionSelected: _angular_core.OutputEmitterRef<string>;
3731
- protected readonly showSubsections: _angular_core.Signal<boolean>;
3732
- protected onSubsectionClick(key: string, disabled: boolean | undefined, event: MouseEvent): void;
3733
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<SectionNavItem, never>;
3734
- 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>;
3735
- }
3736
-
3737
- /**
3738
- * Envoltorio de contenido para UN sub-ítem de `kf-section-nav-item`. Varios
3739
- * pueden proyectarse juntos, uno por `key` de `subsections()` del padre —
3740
- * cada uno queda montado siempre (mismo criterio `[hidden]` que el propio
3741
- * `kf-section-nav-item`), y solo se muestra el que coincide con
3742
- * `activeSubsection()` del padre.
3743
- *
3744
- * <kf-section-nav-item [subsections]="subs" [activeSubsection]="active()" ...>
3745
- * <kf-section-nav-subsection key="general"><app-general /></kf-section-nav-subsection>
3746
- * <kf-section-nav-subsection key="seguridad"><app-seguridad /></kf-section-nav-subsection>
3747
- * </kf-section-nav-item>
3748
- */
3749
- declare class SectionNavSubsection {
3750
- readonly key: _angular_core.InputSignal<string>;
3751
- private readonly parent;
3752
- protected readonly isActive: _angular_core.Signal<boolean>;
3753
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<SectionNavSubsection, never>;
3754
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<SectionNavSubsection, "kf-section-nav-subsection", never, { "key": { "alias": "key"; "required": true; "isSignal": true; }; }, {}, never, ["*"], true, never>;
3755
- }
3756
-
3757
- interface SegmentedOption {
3758
- value: string;
3759
- label: string;
3760
- }
3761
- /**
3762
- * Control de segmentos relleno, tipo "iOS segmented control": 2-3 opciones
3763
- * con un thumb que se desliza detrás de la opción activa.
3764
- *
3765
- * La posición del thumb se resuelve con una variable CSS (--kf-segmented-index)
3766
- * y `transform: translateX()`, así la transición queda a cargo de CSS puro
3767
- * (mismo criterio que Tabs), sin medir el DOM desde TypeScript.
3768
- *
3769
- * <kf-segmented [options]="options" [value]="value" (valueChange)="onChange($event)" />
3770
- */
3771
- declare class Segmented {
3772
- readonly options: _angular_core.InputSignal<SegmentedOption[]>;
3773
- readonly value: _angular_core.InputSignal<string>;
3774
- readonly valueChange: _angular_core.OutputEmitterRef<string>;
3775
- /** Índice de la opción activa; -1 si `value` no matchea ninguna opción. */
3776
- readonly selectedIndex: _angular_core.Signal<number>;
3777
- select(nextValue: string): void;
3778
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<Segmented, never>;
3779
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<Segmented, "kf-segmented", never, { "options": { "alias": "options"; "required": true; "isSignal": true; }; "value": { "alias": "value"; "required": true; "isSignal": true; }; }, { "valueChange": "valueChange"; }, never, never, true, never>;
3780
- }
3781
-
3782
- /** Normalized shape used internally once `options` (string[] or object[]) is resolved. */
3783
- interface SelectOption {
3784
- value: string;
3785
- label: string;
3786
- }
3787
- /**
3788
- * Dropdown de selección simple con panel propio — NUNCA usa el `<select>`
3789
- * nativo del navegador. Un `<select>` real siempre delega el popup de
3790
- * opciones al sistema operativo, y ningún CSS puede controlar cómo se ve
3791
- * ese popup.
3792
- *
3793
- * El panel flotante se conecta al trigger vía `@angular/cdk/overlay` — el
3794
- * mismo mecanismo que `Dropdown` (ver `../dropdown/Dropdown.ts`).
3795
- *
3796
- * <kf-select label="Sede" [options]="['Lima', 'Arequipa', 'Trujillo']" placeholder="Selecciona…" />
3797
- * <kf-select [options]="[{ value: 'pe', label: 'Perú' }]" (valueChange)="onCountry($event)" />
3798
- *
3799
- * `variant="compact"` cambia el ancho de `240px` fijo a intrínseco con un
3800
- * techo de `160px` (se ajusta al contenido de la opción seleccionada hasta
3801
- * ese máximo, truncando con ellipsis si no entra) y reduce padding/tamaño de
3802
- * fuente — pensado para selectores cortos embebidos en una barra (ej. "Filas
3803
- * por página" en el footer de `kf-data-table`), nunca para un campo de
3804
- * formulario largo, que sigue usando el `variant="field"` por defecto. El
3805
- * panel flotante siempre mide el ancho real del trigger en cada apertura
3806
- * (`SelectOverlay.attach`), así que se angosta solo sin cambios adicionales.
3807
- *
3808
- * <kf-select variant="compact" [options]="['5', '10', '25']" [value]="pageSize()" (valueChange)="..." />
3809
- *
3810
- * `width="full"` ocupa el 100% del contenedor — para selects dentro de
3811
- * layouts flex/grid donde el ancho fijo de `md` no encaja.
3812
- *
3813
- * <kf-select width="full" [options]="branches" [value]="branch()" (valueChange)="..." />
3814
- */
3815
- declare class Select {
3816
- readonly label: _angular_core.InputSignal<string | undefined>;
3817
- readonly options: _angular_core.InputSignal<string[] | SelectOption[]>;
3818
- readonly value: _angular_core.InputSignal<string | undefined>;
3819
- readonly placeholder: _angular_core.InputSignal<string>;
3820
- readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
3821
- /** Borde en rojo y `aria-invalid` en el trigger. Ver `errorMessage` para el mensaje asociado — mismo contrato que `kf-input`. */
3822
- readonly invalid: _angular_core.InputSignalWithTransform<boolean, unknown>;
3823
- /** Mensaje de error con `role="alert"`, debajo del trigger. */
3824
- readonly errorMessage: _angular_core.InputSignal<string | undefined>;
3825
- readonly variant: _angular_core.InputSignal<"field" | "compact">;
3826
- /**
3827
- * Ancho del control. 'md' (240px) es el default histórico y preserva el
3828
- * comportamiento actual sin cambios para consumidores existentes.
3829
- * 'full' ocupa el 100% del contenedor — para selects dentro de layouts
3830
- * flex/grid donde el ancho fijo no encaja (ej. un footer de sidebar).
3831
- */
3832
- readonly width: _angular_core.InputSignal<"sm" | "md" | "lg" | "full">;
3833
- /**
3834
- * Tri-state: si el consumidor no lo especifica (`undefined`), el buscador
3835
- * se activa solo cuando `options().length` supera `searchThreshold()`. Si
3836
- * el consumidor define `true`/`false` explícitamente, ese valor manda por
3837
- * sobre el umbral automático.
3838
- */
3839
- readonly searchable: _angular_core.InputSignal<boolean | undefined>;
3840
- readonly searchThreshold: _angular_core.InputSignal<number>;
3841
- readonly valueChange: _angular_core.OutputEmitterRef<string>;
3842
- private readonly instanceId;
3843
- readonly triggerId: string;
3844
- readonly listboxId: string;
3845
- readonly errorId: string;
3846
- readonly isOpen: _angular_core.WritableSignal<boolean>;
3847
- readonly searchQuery: _angular_core.WritableSignal<string>;
3848
- readonly activeIndex: _angular_core.WritableSignal<number>;
3849
- /** Resolves the union (`string[] | { value; label }[]`) into a single shape for the template. */
3850
- readonly normalizedOptions: _angular_core.Signal<SelectOption[]>;
3851
- readonly selectedOption: _angular_core.Signal<SelectOption | undefined>;
3852
- readonly showSearch: _angular_core.Signal<boolean>;
3853
- /** Opciones visibles tras aplicar el filtro de búsqueda (case/acento-insensitive). */
3854
- readonly filteredOptions: _angular_core.Signal<SelectOption[]>;
3855
- private readonly triggerRef;
3856
- private readonly panelTemplate;
3857
- private readonly searchInputRef;
3858
- private readonly overlay;
3859
- constructor();
3860
- /**
3861
- * Respaldo de `overlayRef.keydownEvents()`: ese observable del CDK solo ve
3862
- * teclas presionadas con el foco DENTRO del pane del overlay. Si el foco
3863
- * sigue en el trigger (o en cualquier otro lado de la página) mientras el
3864
- * panel está abierto, `keydownEvents()` nunca dispara.
3865
- */
3866
- onDocumentEscape(): void;
3867
- toggle(): void;
3868
- select(option: SelectOption): void;
3869
- onSearchInput(event: Event): void;
3870
- /**
3871
- * Mueve `activeIndex` sobre las opciones YA FILTRADAS visibles. Clamp
3872
- * simple (sin wraparound): llegar al final o al principio de la lista se
3873
- * queda ahí en vez de dar la vuelta — es el comportamiento más predecible
3874
- * cuando se combina con un buscador que puede reducir la lista a 1 opción.
3875
- */
3876
- moveActive(delta: number): void;
3877
- selectActive(): void;
3878
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<Select, never>;
3879
- 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>;
3880
- }
3881
-
3882
- interface SelectableCardOption {
3883
- value: string;
3884
- label: string;
3885
- icon?: IconName;
3886
- }
3887
- /**
3888
- * Radio group real (`role="radiogroup"` + `role="radio"` + `aria-checked`),
3889
- * con apariencia de cards apiladas verticalmente — para una decisión ÚNICA Y
3890
- * EXCLUYENTE dentro de un flujo (ej. método de pago, tipo de documento a
3891
- * subir), no para filtrar una lista.
3892
- *
3893
- * Distinto de `kf-filter-chips` a propósito: `FilterChips` es semántica y
3894
- * visualmente un FILTRO (chips horizontales, se resaltan con `--kf-color-ink`
3895
- * — pensado para "Todos/Activos/Archivados" sobre una lista, donde elegir
3896
- * una opción cambia qué se MUESTRA). Acá la elección es la respuesta misma
3897
- * a una pregunta del flujo ("¿con qué método pagás?"), no un filtro — por
3898
- * eso necesita affordance de radio (círculo con punto, más peso visual por
3899
- * opción) en vez de un pill de filtro reciclado.
3900
- *
3901
- * Réplica fiel del patrón ya resuelto en el Design System de referencia
3902
- * (`otd-pagos.jsx` / `otd.css`: `.otd-pay-method` + `.radio`) — apilado
3903
- * vertical con gap 10px, radio de 18px con punto interno vía `::after`,
3904
- * borde+fondo+ícono cambian de color al seleccionar. Se agrega manejo de
3905
- * teclado (flechas arriba/abajo, Home/End) y `:focus-visible` real, ninguno
3906
- * de los dos presentes en el mockup original — mínimos de accesibilidad que
3907
- * no dependen de fidelidad visual al mockup.
3908
- *
3909
- * <kf-selectable-card-group
3910
- * [options]="[{ value: 'card', label: 'Tarjeta', icon: 'credit-card' }]"
3911
- * [value]="method()"
3912
- * (valueChange)="method.set($event)"
3913
- * />
3914
- */
3915
- declare class SelectableCardGroup {
3916
- readonly options: _angular_core.InputSignal<SelectableCardOption[]>;
3917
- readonly value: _angular_core.InputSignal<string>;
3918
- /** Accesible: describe qué está eligiendo el usuario (ej. "Método de pago"). */
3919
- readonly ariaLabel: _angular_core.InputSignal<string>;
3920
- readonly valueChange: _angular_core.OutputEmitterRef<string>;
3921
- select(value: string): void;
3922
- protected onKeydown(event: KeyboardEvent, currentValue: string): void;
3923
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<SelectableCardGroup, never>;
3924
- 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>;
3925
- }
3926
-
3927
- /** Contrato de datos de `kf-sidebar-shell.navItems`. `icon` es requerido:
3928
- * en el sidebar persistente el ícono es parte permanente del diseño. */
3929
- interface SidebarNavItemData {
3930
- icon: IconName;
3931
- label: string;
3932
- href: string;
3933
- active?: boolean;
3934
- }
3935
-
3936
- /**
3937
- * Shell de layout con navegación lateral persistente — el complemento
3938
- * vertical de `kf-portal-header` (navbar horizontal). Pensado para
3939
- * portales tipo "aplicación interna" (Portal OTD, Servicio Star).
3940
- *
3941
- * `navItems` se recibe como array de datos (no como contenido proyectado):
3942
- * el componente no sabe nada de routing, emite `navItemClick` con el ítem
3943
- * clickeado y deja que el consumidor decida cómo navegar.
3944
- *
3945
- * Slots: `[kf-sidebar-brand]` (logo del rail expandido, visible en desktop
3946
- * y en el panel mobile — es el MISMO rail), `[kf-sidebar-brand-collapsed]`
3947
- * (reemplazo opcional cuando el rail está colapsado en desktop),
3948
- * `[kf-sidebar-brand-mobile]` (logo de la topbar angosta de mobile, fondo
3949
- * claro) y `[kf-sidebar-footer]` (tarjeta de usuario / acciones al pie).
3950
- *
3951
- * Botón de colapso: vive como último ítem de la propia lista de
3952
- * navegación, no flotando en el borde del rail — mismo criterio de
3953
- * tap-target grande y predecible que el resto de los ítems.
3954
- *
3955
- * Responsive: bajo los 900px el sidebar NO se reemplaza por un panel
3956
- * distinto — es el MISMO rail el que pasa a `position: fixed` fuera de la
3957
- * pantalla (`translateX(-100%)`) y se desliza a la vista (`translateX(0)`)
3958
- * sobre un backdrop cuando `mobileOpen` es `true`. Una topbar angosta con
3959
- * botón de hamburguesa reemplaza al rail en el flujo del documento
3960
- * mientras el panel está cerrado.
3961
- *
3962
- * A propósito NO reutiliza `kf-drawer` (el panel lateral genérico de la
3963
- * librería): `kf-drawer` tiene su propio fondo claro fijo, incompatible
3964
- * con el texto claro de `kf-sidebar-nav-item` (pensado para el gradiente
3965
- * oscuro), y duplicaría el árbol de navegación en un segundo lugar del
3966
- * DOM. Acá hay un solo rail, un solo árbol de nav, que cambia de posición
3967
- * según el viewport.
3968
- */
3969
- declare class SidebarShell {
3970
- /** Ítems del menú del rail. */
3971
- readonly navItems: _angular_core.InputSignal<SidebarNavItemData[]>;
3972
- /** Estado de colapso del rail en desktop, two-way. Sin efecto bajo los 900px (ver estilos). */
3973
- readonly collapsed: _angular_core.ModelSignal<boolean>;
3974
- /** Visibilidad del rail deslizado como panel en mobile, two-way. */
3975
- readonly mobileOpen: _angular_core.ModelSignal<boolean>;
3976
- /** Se emite al clickear un ítem — el consumidor decide cómo navegar (ej. `router.navigateByUrl`). */
3977
- readonly navItemClick: _angular_core.OutputEmitterRef<SidebarNavItemData>;
3978
- /** `true` bajo los 900px. */
3979
- private readonly isMobileViewport;
3980
- /**
3981
- * Estado de colapso EFECTIVO — el que ya considera el viewport, no el
3982
- * `collapsed` crudo. Es lo que usa el propio rail para sus nav-items y
3983
- * el botón de colapso; se expone público para que el consumidor le
3984
- * pase el mismo valor a lo que proyecte en `[kf-sidebar-footer]` (ej.
3985
- * `kf-drawer-session`) — así un footer proyectado no queda colapsado
3986
- * dentro del panel deslizado de mobile mientras el resto del rail ya
3987
- * muestra todo expandido.
3988
- */
3989
- readonly itemsCollapsed: _angular_core.Signal<boolean>;
3990
- /** Cierra el panel mobile con Escape — mismo criterio que `Dropdown`/`Modal`/`Drawer`. */
3991
- protected onEscape(): void;
3992
- protected onNavItemClick(event: Event, item: SidebarNavItemData): void;
3993
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<SidebarShell, never>;
3994
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<SidebarShell, "kf-sidebar-shell", never, { "navItems": { "alias": "navItems"; "required": false; "isSignal": true; }; "collapsed": { "alias": "collapsed"; "required": false; "isSignal": true; }; "mobileOpen": { "alias": "mobileOpen"; "required": false; "isSignal": true; }; }, { "collapsed": "collapsedChange"; "mobileOpen": "mobileOpenChange"; "navItemClick": "navItemClick"; }, never, ["[kf-sidebar-brand]", "[kf-sidebar-brand-collapsed]", "[kf-sidebar-footer]", "[kf-sidebar-brand-mobile]"], true, never>;
3995
- }
3996
-
3997
- /** Forma del placeholder. */
3998
- type SkeletonVariant = 'text' | 'circle' | 'rect';
3999
- /**
4000
- * Bloque placeholder con shimmer para contenido que todavía carga: una línea
4001
- * de texto, un avatar o un área de card/imagen, según `variant`.
4002
- *
4003
- * Es decorativo (`aria-hidden`): quien lo usa debería marcar el contenedor con
4004
- * `aria-busy="true"` mientras carga, así el lector de pantalla anuncia el
4005
- * estado una vez en vez de leer N placeholders vacíos.
4006
- *
4007
- * `circle` usa `aspect-ratio`, así que alcanza con darle `width`.
4008
- *
4009
- * <kf-skeleton />
4010
- * <kf-skeleton variant="circle" width="40px" />
4011
- * <kf-skeleton variant="rect" width="100%" height="120px" />
4012
- */
4013
- declare class Skeleton {
4014
- readonly variant: _angular_core.InputSignal<SkeletonVariant>;
4015
- /** Ancho CSS. Default `'100%'`. */
4016
- readonly width: _angular_core.InputSignal<string>;
4017
- /** Alto CSS. Vacío deja el alto/aspect-ratio propio de la variante. */
4018
- readonly height: _angular_core.InputSignal<string>;
4019
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<Skeleton, never>;
4020
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<Skeleton, "kf-skeleton", never, { "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "width": { "alias": "width"; "required": false; "isSignal": true; }; "height": { "alias": "height"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
4021
- }
4022
-
4023
- type SpinnerSize = 'sm' | 'md' | 'lg';
4024
- /**
4025
- * Indicador de carga indeterminado: inline, dentro de un botón, o como único
4026
- * contenido de una sección mientras llega la data.
4027
- *
4028
- * Cuando se conoce la forma del contenido que viene, preferir `kf-skeleton`
4029
- * (dibuja la silueta). El spinner es para "algo está pasando" sin forma
4030
- * conocida — guardar, enviar, reintentar.
4031
- *
4032
- * Lleva `role="status"`, así que el lector de pantalla anuncia el `label`
4033
- * cuando aparece. Sin `label` cae en un texto accesible por defecto.
4034
- *
4035
- * <kf-spinner />
4036
- * <kf-spinner size="sm" label="Cargando…" />
4037
- * <kf-spinner onDark />
4038
- */
4039
- declare class Spinner {
4040
- readonly size: _angular_core.InputSignal<SpinnerSize>;
4041
- /** Texto al lado del anillo. Vacío deja solo el anillo (y el texto para lectores). */
4042
- readonly label: _angular_core.InputSignal<string>;
4043
- /** Invierte los colores para fondos oscuros. */
4044
- readonly onDark: _angular_core.InputSignalWithTransform<boolean, unknown>;
4045
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<Spinner, never>;
4046
- 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>;
4047
- }
4048
-
4049
- type SsoProvider = 'microsoft';
4050
- /** Copy y glyph por proveedor — agregar una entrada acá es todo lo que hace
4051
- * falta para soportar un proveedor nuevo (ej. `google`), sin tocar el
4052
- * template ni los estilos del componente. */
4053
- interface SsoProviderConfig {
4054
- readonly label: string;
4055
- }
4056
- /**
4057
- * Botón de inicio de sesión vía SSO corporativo — pensado como pieza suelta,
4058
- * no acoplada a `kf-auth-layout`: cualquier consumidor puede usarlo solo,
4059
- * dentro o fuera del slot `[sso]` de `AuthLayout`.
4060
- *
4061
- * `provider` resuelve copy y glyph — hoy solo `'microsoft'` (el método
4062
- * principal de acceso del ecosistema, cuenta corporativa de Grupo Kaufmann),
4063
- * con la puerta abierta a sumar otros a futuro sin romper la API existente.
4064
- *
4065
- * El glyph es el ícono GENÉRICO de 4 cuadrados de colores que Microsoft
4066
- * publica en sus propias guías de marca para botones de terceros ("Sign in
4067
- * with Microsoft") — nunca el logotipo/wordmark oficial completo. No vive en
4068
- * el sprite de `kf-icon` (`icon-sprite.ts`) a propósito: ese catálogo es
4069
- * monocromático (`currentColor`, ver `Icon.ts`), pensado para trazos de un
4070
- * solo color — este glyph necesita sus 4 colores fijos tal como los define
4071
- * la guía de marca, así que un SVG inline propio es la opción correcta, no
4072
- * un glyph nuevo forzado al sprite compartido.
4073
- *
4074
- * El label por defecto viene de `provider` — pasar contenido propio via
4075
- * `<ng-content>` lo reemplaza (ej. un texto más corto en un lugar angosto).
4076
- *
4077
- * <kf-sso-button provider="microsoft" (ssoClick)="loginWithMicrosoft()" />
4078
- * <kf-sso-button provider="microsoft" [loading]="loggingIn()">
4079
- * Ingresar con cuenta corporativa
4080
- * </kf-sso-button>
4081
- */
4082
- declare class SsoButton {
4083
- readonly provider: _angular_core.InputSignal<"microsoft">;
4084
- readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
4085
- /** Reemplaza el glyph + label por `kf-spinner` y fuerza `disabled` — mismo
4086
- * patrón que `kf-button[loading]`. */
4087
- readonly loading: _angular_core.InputSignalWithTransform<boolean, unknown>;
4088
- /** Igual que `kf-button`: por defecto `submit` dentro de un `<form>`, pero
4089
- * este botón normalmente vive fuera de cualquier form (dispara un
4090
- * redirect a Microsoft Entra ID), así que casi siempre conviene `button`. */
4091
- readonly type: _angular_core.InputSignal<"button" | "submit">;
4092
- protected readonly config: _angular_core.Signal<SsoProviderConfig>;
4093
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<SsoButton, never>;
4094
- 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>;
4095
- }
4096
-
4097
- type StatCardTone = 'primary' | 'success' | 'error' | 'purple';
4098
- /**
4099
- * Card de estadística horizontal: ícono circular grande a la izquierda,
4100
- * valor + label + nota apilados al centro, chevron opcional al final si es
4101
- * clickeable. Distinta de `kf-metric-card` (layout vertical, label arriba +
4102
- * ícono a la derecha) — este es el patrón real del dashboard de OTD ("Bonos
4103
- * otorgados", "Cortesías otorgadas"), no una variante de la misma card.
4104
- *
4105
- * <kf-stat-card icon="gift" tone="purple" value="4" label="Bonos otorgados" note="$15,000.00 en beneficios" (cardClick)="onNavigate('beneficios')" />
4106
- */
4107
- declare class StatCard {
4108
- readonly icon: _angular_core.InputSignal<IconName>;
4109
- readonly tone: _angular_core.InputSignal<StatCardTone>;
4110
- readonly value: _angular_core.InputSignal<string | number>;
4111
- readonly label: _angular_core.InputSignal<string>;
4112
- readonly note: _angular_core.InputSignal<string | undefined>;
4113
- /** Si se escucha `cardClick`, la card se renderiza como botón clickeable con chevron. */
4114
- readonly clickable: _angular_core.InputSignalWithTransform<boolean, unknown>;
4115
- readonly cardClick: _angular_core.OutputEmitterRef<void>;
4116
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<StatCard, never>;
4117
- 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>;
4118
- }
4119
-
4120
- /** Vocabulario semántico único de tono, compartido con `TextTone`. */
4121
- type StatTileTone = 'neutral' | 'ok' | 'warn' | 'error' | 'info';
4122
- type StatTileVariant = 'inline' | 'card';
4123
- /**
4124
- * Indicador numérico compacto: label + valor grande con ícono, para grillas
4125
- * densas de KPIs. Con `cta` en vez de `value` se convierte en un bloque de
4126
- * llamada a la acción (título, descripción y link, sin tono ni número).
4127
- * `variant="inline"` quita fondo/borde para una fila horizontal donde el
4128
- * consumidor pone sus propios separadores.
4129
- *
4130
- * `hasProjectedValue` reemplaza el valor de texto por contenido proyectado
4131
- * (`[valueContent]`) cuando el dato no es un número/string medido — el
4132
- * caso típico es un estado con su propio `kf-badge`, que necesita su
4133
- * tono/dot propios en vez del tono del tile:
4134
- *
4135
- * <kf-stat-tile label="Estado" hasProjectedValue>
4136
- * <kf-badge valueContent [tone]="stateTone()">{{ stateLabel() }}</kf-badge>
4137
- * </kf-stat-tile>
4138
- */
4139
- declare class StatTile {
4140
- readonly cta: _angular_core.InputSignalWithTransform<boolean, unknown>;
4141
- readonly label: _angular_core.InputSignal<string | undefined>;
4142
- readonly value: _angular_core.InputSignal<string | number | undefined>;
4143
- readonly icon: _angular_core.InputSignal<IconName | undefined>;
4144
- readonly variant: _angular_core.InputSignal<StatTileVariant>;
4145
- readonly tone: _angular_core.InputSignal<StatTileTone>;
4146
- /** Tercera línea bajo el valor. Vacía cae en `notePlaceholder`. */
4147
- readonly note: _angular_core.InputSignal<string | null | undefined>;
4148
- /** Qué dice la tercera línea cuando no hay nota. Cada consumidor debería
4149
- * pasar el motivo real de SU dato ausente, no un texto genérico. */
4150
- readonly notePlaceholder: _angular_core.InputSignal<string | undefined>;
4151
- /** Rinde el valor como ausencia, no como dato medido. */
4152
- readonly valueMissing: _angular_core.InputSignalWithTransform<boolean, unknown>;
4153
- /** El valor es contenido proyectado (`<ng-content select="[valueContent]">`,
4154
- * ej. un `kf-badge` de estado) en vez del texto de `value()` — para un
4155
- * dato cuya representación natural no es un número/string medido. */
4156
- readonly hasProjectedValue: _angular_core.InputSignalWithTransform<boolean, unknown>;
4157
- readonly title: _angular_core.InputSignal<string | undefined>;
4158
- readonly description: _angular_core.InputSignal<string | undefined>;
4159
- readonly actionLabel: _angular_core.InputSignal<string | undefined>;
4160
- readonly actionHref: _angular_core.InputSignal<string | undefined>;
4161
- /** Texto real de la nota. `null` significa "no reservar la línea": pasa
4162
- * solo cuando el consumidor no declaró ni nota ni placeholder. */
4163
- protected readonly noteText: _angular_core.Signal<string | null>;
4164
- protected readonly noteIsPlaceholder: _angular_core.Signal<boolean>;
4165
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<StatTile, never>;
4166
- 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>;
4167
- }
4168
-
4169
- /**
4170
- * Pill con borde, en mayúsculas y con letter-spacing, pensada para indicadores
4171
- * de estado únicos y prominentes (ej. "ESTADO TÉCNICO"). El fondo es siempre
4172
- * transparente; el color de borde/texto/punto depende de `tone`.
4173
- *
4174
- * <kf-status-pill tone="ok">Operativo</kf-status-pill>
4175
- * <kf-status-pill tone="error" dot>Fuera de servicio</kf-status-pill>
4176
- */
4177
- declare class StatusPill {
4178
- readonly tone: _angular_core.InputSignal<"ok" | "warn" | "error" | "blue">;
4179
- readonly dot: _angular_core.InputSignalWithTransform<boolean, unknown>;
4180
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<StatusPill, never>;
4181
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<StatusPill, "kf-status-pill", never, { "tone": { "alias": "tone"; "required": false; "isSignal": true; }; "dot": { "alias": "dot"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
4182
- }
4183
-
4184
- interface TabOption {
4185
- value: string;
4186
- label: string;
4187
- icon?: IconName;
4188
- }
4189
- /**
4190
- * Barra de tabs subrayada: un indicador se desliza hasta la pestaña activa.
4191
- * La posición se mide (`offsetLeft`/`offsetWidth`) después de cada render,
4192
- * ya que las pestañas tienen ancho de contenido variable.
4193
- *
4194
- * <kf-tabs [options]="tabs" [value]="active" (valueChange)="active = $event" />
4195
- */
4196
- declare class Tabs {
4197
- readonly options: _angular_core.InputSignal<TabOption[]>;
4198
- readonly value: _angular_core.InputSignal<string>;
4199
- readonly valueChange: _angular_core.OutputEmitterRef<string>;
4200
- private readonly track;
4201
- private readonly destroyRef;
4202
- private readonly activeRect;
4203
- protected readonly indicatorTransform: _angular_core.Signal<string>;
4204
- protected readonly indicatorWidth: _angular_core.Signal<number>;
4205
- constructor();
4206
- private measure;
4207
- select(value: string): void;
4208
- protected onKeydown(event: KeyboardEvent, currentValue: string): void;
4209
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<Tabs, never>;
4210
- 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>;
4211
- }
4212
-
4213
- interface FolderTabOption {
4214
- value: string;
4215
- label: string;
4216
- icon?: IconName;
4217
- }
4218
- /**
4219
- * Marca el contenido que `kf-folder-tabs` debe proyectar dentro de su panel.
4220
- *
4221
- * <div kfFolderTabsPanel>...</div>
4222
- */
4223
- declare class FolderTabsPanel {
4224
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<FolderTabsPanel, never>;
4225
- static ɵdir: _angular_core.ɵɵDirectiveDeclaration<FolderTabsPanel, "[kfFolderTabsPanel]", never, {}, {}, never, never, true, never>;
4226
- }
4227
- /**
4228
- * Tabs con forma de "carpeta": borde superior redondeado, la pestaña activa
4229
- * se funde visualmente con el panel de contenido de abajo. El panel
4230
- * (`kfFolderTabsPanel`) es opcional — sin él, se renderiza solo la fila de
4231
- * pestañas.
4232
- *
4233
- * <kf-folder-tabs [options]="tabs" [value]="active" (valueChange)="active = $event">
4234
- * <div kfFolderTabsPanel>contenido de la pestaña activa</div>
4235
- * </kf-folder-tabs>
4236
- *
4237
- * <!-- o, sin contenido: -->
4238
- * <kf-folder-tabs [options]="tabs" [value]="active" (valueChange)="active = $event" />
4239
- */
4240
- declare class FolderTabs {
4241
- readonly options: _angular_core.InputSignal<FolderTabOption[]>;
4242
- readonly value: _angular_core.InputSignal<string>;
4243
- readonly valueChange: _angular_core.OutputEmitterRef<string>;
4244
- /** Id del panel único — todos los tabs lo referencian vía `aria-controls`. */
4245
- protected readonly panelId: string;
4246
- private readonly projectedPanel;
4247
- protected readonly hasPanel: _angular_core.Signal<boolean>;
4248
- select(value: string): void;
4249
- protected onKeydown(event: KeyboardEvent, currentValue: string): void;
4250
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<FolderTabs, never>;
4251
- 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>;
4252
- }
4253
-
4254
- /**
4255
- * Textarea de formulario apilado: mismo patrón visual y de integración que
4256
- * `kf-input` (label arriba, `ControlValueAccessor`, funciona con
4257
- * `[(ngModel)]` y con Reactive Forms) para el caso de texto libre
4258
- * multilínea (notas, comentarios, descripciones).
4259
- *
4260
- * <kf-textarea label="Notas" formControlName="notes" />
4261
- * <kf-textarea label="Comentario" [(ngModel)]="comment" [minRows]="2" [maxRows]="10" />
4262
- *
4263
- * `minRows`/`maxRows` (3 y 6 por defecto) acotan el crecimiento: el campo
4264
- * crece con el contenido pero nunca pasa de `maxRows` — superado ese tope el
4265
- * contenido hace scroll interno (`overflow-y: auto`) en vez de seguir
4266
- * empujando el layout. Reemplaza el `resize: vertical` nativo (deshabilitado
4267
- * a propósito) porque un resize manual sin tope es exactamente el problema
4268
- * que motivó este componente: un `<textarea>` a mano podía crecer hasta
4269
- * tapar los botones del footer de un modal.
4270
- *
4271
- * <kf-textarea label="Detalle" formControlName="detail" [maxRows]="12" />
4272
- *
4273
- * `invalid` + `errorMessage` y `readonly`/`disabled` siguen el mismo
4274
- * contrato que `kf-input` — ver su JSDoc para la diferencia entre ambos
4275
- * estados.
4276
- */
4277
- declare class Textarea implements ControlValueAccessor, AfterViewInit {
4278
- private static nextId;
4279
- private readonly nativeTextarea;
4280
- readonly label: _angular_core.InputSignal<string | undefined>;
4281
- /** Marca el label con un asterisco visual y aplica `required` al
4282
- * `<textarea>` nativo — el atributo HTML ya expone el estado obligatorio a
4283
- * lectores de pantalla (mapeo implícito a `aria-required`), sin necesidad
4284
- * de un `aria-required` explícito redundante. */
4285
- readonly required: _angular_core.InputSignalWithTransform<boolean, unknown>;
4286
- readonly hint: _angular_core.InputSignal<string | undefined>;
4287
- /** Borde y aria-invalid en rojo. Ver `errorMessage` para el mensaje asociado. */
4288
- readonly invalid: _angular_core.InputSignalWithTransform<boolean, unknown>;
4289
- /** Mensaje de error con `role="alert"` — reemplaza a `hint` mientras esté presente. */
4290
- readonly errorMessage: _angular_core.InputSignal<string | undefined>;
4291
- readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
4292
- /** Ver JSDoc de `kf-input` para la diferencia con `disabled`. */
4293
- readonly readonly: _angular_core.InputSignalWithTransform<boolean, unknown>;
4294
- readonly placeholder: _angular_core.InputSignal<string>;
4295
- /** Filas mínimas visibles en reposo — define la altura de arranque del campo. */
4296
- readonly minRows: _angular_core.InputSignal<number>;
4297
- /** Filas máximas antes de que el contenido pase a scroll interno en vez de seguir creciendo. */
4298
- readonly maxRows: _angular_core.InputSignal<number>;
4299
- protected readonly textareaId: string;
4300
- protected readonly hintId: string;
4301
- protected readonly errorId: string;
4302
- protected readonly value: _angular_core.WritableSignal<string>;
4303
- protected readonly focused: _angular_core.WritableSignal<boolean>;
4304
- /** Deshabilitado por Reactive Forms (formControl.disable()). */
4305
- private readonly disabledByForm;
4306
- /** Disabled real: por input() directo o por el FormControl asociado. */
4307
- protected readonly isDisabled: _angular_core.Signal<boolean>;
4308
- private onChange;
4309
- private onTouched;
4310
- ngAfterViewInit(): void;
4311
- writeValue(value: string): void;
4312
- registerOnChange(fn: (value: string) => void): void;
4313
- registerOnTouched(fn: () => void): void;
4314
- setDisabledState(isDisabled: boolean): void;
4315
- protected onInput(rawValue: string): void;
4316
- protected onBlur(): void;
4317
- /**
4318
- * Recalcula la altura según el contenido real (`scrollHeight`), acotada
4319
- * entre `minRows` y `maxRows` medidos con el line-height ya calculado del
4320
- * propio elemento — así el tope se mantiene correcto sin importar qué
4321
- * font-size/line-height termine aplicando cada tema. Superado `maxRows`
4322
- * el resto del contenido hace scroll interno en vez de seguir creciendo.
4323
- */
4324
- private adjustHeight;
4325
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<Textarea, never>;
4326
- 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>;
4327
- }
4328
-
4329
- /**
4330
- * Da forma al valor de un campo MIENTRAS el usuario escribe — ej.
4331
- * inserta el guion de un RUT chileno automáticamente, sin que el
4332
- * usuario tenga que tipearlo — reposicionando el cursor para que
4333
- * seguir escribiendo se sienta natural (sin esto, cualquier inserción
4334
- * de caracteres empuja el cursor al final del campo en cada tecla).
4335
- *
4336
- * Se aplica junto a `formControlName`/`[formControl]`/`[(ngModel)]`
4337
- * sobre cualquier input que implemente `ControlValueAccessor` (incluye
4338
- * `kf-input`, pero no depende de él — funciona igual sobre un
4339
- * `<input>` nativo):
4340
- *
4341
- * <kf-input formControlName="documentId" [kfFormatInput]="formatChileanRut" />
4342
- *
4343
- * La función de formateo recibe el valor crudo tecleado y devuelve el
4344
- * valor con forma — debe ser determinística e idempotente (aplicarla
4345
- * dos veces seguidas da el mismo resultado), como `formatChileanRut`
4346
- * en este mismo archivo (`./format.ts`).
4347
- */
4348
- declare class FormatInputDirective {
4349
- private readonly ngControl;
4350
- /** La función de formateo a aplicar — sin valor, el directive no
4351
- * hace nada (permite pasar `undefined` condicionalmente sin tener
4352
- * que quitar el directive del template entero). */
4353
- readonly kfFormatInput: _angular_core.InputSignal<((value: string) => string) | undefined>;
4354
- protected onInput(event: Event): void;
4355
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<FormatInputDirective, never>;
4356
- static ɵdir: _angular_core.ɵɵDirectiveDeclaration<FormatInputDirective, "[kfFormatInput]", never, { "kfFormatInput": { "alias": "kfFormatInput"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
4357
- }
4358
-
4359
- /**
4360
- * Grilla de tiles que escalona columnas solo entre DIVISORES de `columns`
4361
- * (4 → 2 → 1, nunca 3), para que ninguna fila quede huérfana. Mide el
4362
- * contenedor (`container-type: inline-size`), no el viewport, porque el
4363
- * mismo bloque se rinde tanto en un drawer angosto como en una página
4364
- * completa bajo el mismo viewport.
4365
- */
4366
- declare class TileGrid {
4367
- /** Columnas en el ancho máximo. Se acota a 1-6: por encima de 6 un tile
4368
- * deja de tener ancho para su propio label. */
4369
- readonly columns: _angular_core.InputSignalWithTransform<number, unknown>;
4370
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<TileGrid, never>;
4371
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<TileGrid, "kf-tile-grid", never, { "columns": { "alias": "columns"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
4372
- }
4373
-
4374
- /**
4375
- * Visual tone of a toast. Maps to the state tokens in
4376
- * projects/ui/src/tokens/base.css. `loading` has no auto-dismiss and shows an
4377
- * indeterminate bar instead of a countdown — pair it with
4378
- * {@link ToastService.promise}. It reuses the `info` tokens, the closest
4379
- * semantic role to "work in progress".
4380
- */
4381
- type ToastTone = 'success' | 'error' | 'warning' | 'info' | 'loading';
4382
- /** Acción opcional (call-to-action) renderizada dentro del toast, junto al botón de cerrar. */
4383
- interface ToastAction {
4384
- /** Label corto, ej. "Ver ticket" — 1-3 palabras, el toast mide solo 360px de ancho. */
4385
- label: string;
4386
- /** Se invoca al hacer click; el toast se cierra solo después, sin que el llamador tenga que llamar `dismiss()`. */
4387
- onClick: () => void;
4388
- }
4389
- /** Options for {@link ToastService.show}. */
4390
- interface ToastOptions {
4391
- /** Color tone. Defaults to `'info'`. */
4392
- tone?: ToastTone;
4393
- /** Milliseconds before auto-dismiss. Ignored when `tone` is `'loading'`. Defaults to `2800`. */
4394
- duration?: number;
4395
- /** Botón de acción opcional, ej. ir al registro sobre el que avisa el toast. */
4396
- action?: ToastAction;
4397
- }
4398
- /** Messages for each leg of {@link ToastService.promise}. */
4399
- interface ToastPromiseMessages<T = unknown> {
4400
- loading?: string;
4401
- success?: string | ((value: T) => string);
4402
- error?: string | ((error: unknown) => string);
4403
- }
4404
- /** The single active toast, as rendered by `kf-toast-host`. */
4405
- interface ToastState {
4406
- id: number;
4407
- message: string;
4408
- tone: ToastTone;
4409
- duration: number;
4410
- leaving: boolean;
4411
- action?: ToastAction;
4412
- }
4413
- /** What's currently holding the auto-dismiss timer paused. Tracked as a set so hover and
4414
- * focus can overlap (e.g. mouse leaves while focus is still on the action button) without
4415
- * resuming the timer early. */
4416
- type ToastPauseSource = 'hover' | 'focus';
4417
- /**
4418
- * Shows one toast at a time — a new `show()` call replaces whatever is
4419
- * currently up (this mirrors a global status line, not a notification stack).
4420
- *
4421
- * Angular has no global function that can mount UI on its own, so the
4422
- * imperative `showToast(...)` of the reference design system becomes a
4423
- * root-provided service holding signal state: `ToastService.show(...)` sets
4424
- * the active toast and `ToastHost` (standalone, mounted once in the app
4425
- * shell) renders it.
4426
- *
4427
- * @example
4428
- * ```ts
4429
- * // app.ts
4430
- * imports: [ToastHost]
4431
- * template: `<kf-toast-host />`
4432
- *
4433
- * // anywhere else
4434
- * private readonly toast = inject(ToastService);
4435
- * this.toast.show('Vehículo guardado', { tone: 'success' });
4436
- *
4437
- * // chains a `loading` toast into the promise's outcome:
4438
- * this.toast.promise(this.api.save(vehicle), {
4439
- * loading: 'Guardando…',
4440
- * success: 'Vehículo guardado',
4441
- * error: 'No se pudo guardar',
4442
- * });
4443
- * ```
4444
- */
4445
- declare class ToastService {
4446
- private readonly _current;
4447
- private nextId;
4448
- private dismissTimer?;
4449
- private removeTimer?;
4450
- /** Wall-clock time the running `dismissTimer` is due to fire; used to compute how much
4451
- * time is left when {@link pause} interrupts it. */
4452
- private dismissDueAt?;
4453
- /** Time left on the auto-dismiss countdown while paused. `undefined` when no countdown
4454
- * is currently paused. */
4455
- private remainingMs?;
4456
- private readonly pauseSources;
4457
- /** The single active toast, or `null`. */
4458
- readonly current: _angular_core.Signal<ToastState | null>;
4459
- /** Shows a toast, replacing whatever is currently shown. Returns its id. */
4460
- show(message: string, options?: ToastOptions): number;
4461
- private scheduleDismiss;
4462
- /**
4463
- * Pauses the auto-dismiss countdown for `id` while `source` (pointer hover or focus) is
4464
- * active on it, so a user reaching for the action/dismiss button never loses the toast
4465
- * mid-interaction (WCAG 2.2.1). No-op when `id` isn't the active toast, it's leaving, or
4466
- * it has no running countdown (persistent or `loading` toasts, or already paused by
4467
- * another source).
4468
- */
4469
- pause(id: number, source: ToastPauseSource): void;
4470
- /**
4471
- * Resumes a countdown paused by {@link pause}, honoring the time already elapsed before
4472
- * the pause. No-op while another pause `source` is still active, or nothing is paused.
4473
- */
4474
- resume(id: number, source: ToastPauseSource): void;
4475
- /**
4476
- * Starts the exit animation, then removes the toast. No-op when `id` is no
4477
- * longer the active toast, so a stale timer never kills a newer toast.
4478
- */
4479
- dismiss(id?: number): void;
4480
- /**
4481
- * Shows a `loading` toast, then swaps it for a `success`/`error` toast once
4482
- * `work` settles. Re-throws `work`'s rejection so callers can still react to
4483
- * the failure.
4484
- */
4485
- promise<T>(work: Promise<T>, messages?: ToastPromiseMessages<T>): Promise<T>;
4486
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<ToastService, never>;
4487
- static ɵprov: _angular_core.ɵɵInjectableDeclaration<ToastService>;
4488
- }
4489
- /**
4490
- * Renders whatever `ToastService` currently holds, fixed bottom-right. Mount
4491
- * one instance in the app shell (e.g. next to the router outlet); it takes no
4492
- * inputs and re-renders itself from the service state.
4493
- *
4494
- * <kf-toast-host />
4495
- */
4496
- declare class ToastHost {
4497
- private readonly toastService;
4498
- /** 0-or-1 wrapper so `@for track t.id` remounts the node on every new toast, restarting its animations. */
4499
- protected readonly toastList: _angular_core.Signal<ToastState[]>;
4500
- protected toastIcon(tone: ToastTone): IconName;
4501
- protected dismiss(id: number): void;
4502
- /** Pauses the toast's auto-dismiss timer while the pointer or keyboard focus is on it. */
4503
- protected pause(t: ToastState, source: ToastPauseSource): void;
4504
- /** Resumes the toast's auto-dismiss timer once neither the pointer nor focus is on it. */
4505
- protected resume(t: ToastState, source: ToastPauseSource): void;
4506
- /** Ejecuta la acción del toast (ej. "Ver ticket") y lo cierra — el
4507
- * llamador no tiene que acordarse de descartarlo desde su propio handler. */
4508
- protected runAction(t: ToastState): void;
4509
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<ToastHost, never>;
4510
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<ToastHost, "kf-toast-host", never, {}, {}, never, never, true, never>;
4511
- }
4512
-
4513
- type TooltipPosition = 'top' | 'bottom' | 'left' | 'right';
4514
- /**
4515
- * Tooltip de hover/focus — reemplaza el atributo `title` nativo del
4516
- * navegador (lento en aparecer, sin estilo, inconsistente entre
4517
- * navegadores/SO) por un panel propio, mismo mecanismo de
4518
- * `@angular/cdk/overlay` que ya usa `Dropdown`.
4519
- *
4520
- * Pensado para el caso "ícono sin label visible necesita comunicar qué
4521
- * es" (ej. `kf-sidebar-nav-item` colapsado). No es two-way: a diferencia
4522
- * de `Dropdown`, esto se abre/cierra solo con hover/focus, nunca por click.
4523
- *
4524
- * <a [kfTooltip]="label()" kfTooltipPosition="right">…</a>
4525
- */
4526
- declare class TooltipDirective {
4527
- text: string;
4528
- kfTooltipPosition: TooltipPosition;
4529
- private readonly host;
4530
- private readonly overlayService;
4531
- private readonly viewContainerRef;
4532
- private readonly injector;
4533
- private readonly contentId;
4534
- private overlayRef;
4535
- /**
4536
- * `focusin` (no `focus`) a propósito: `focus` no burbujea, así que un
4537
- * disparador que ENVUELVE al elemento realmente enfocable (ej.
4538
- * `<kf-button>`, cuyo `<button>` nativo vive en el template del hijo, fuera
4539
- * de alcance de este host) nunca vería el evento — el tooltip quedaría
4540
- * mudo por teclado justo en el caso de uso que más lo necesita (un botón
4541
- * solo-ícono). `focusin` SÍ burbujea y sigue disparando igual para el caso
4542
- * ya cubierto (un `<a>`/`<button>` nativo que es él mismo el host, ver
4543
- * `Tooltip.spec.ts`) — sin regresión ahí.
4544
- */
4545
- protected show(): void;
4546
- protected hide(): void;
4547
- /**
4548
- * `focusout` (no `blur`, mismo motivo que `show()` usa `focusin`): además,
4549
- * a diferencia de `mouseleave`/Escape, acá SÍ hace falta revisar
4550
- * `relatedTarget` — si el host envuelve más de un elemento enfocable, un
4551
- * foco que se mueve de uno a otro DENTRO del mismo host no debería cerrar
4552
- * el tooltip (no "salió" del disparador). `Node` en vez de `EventTarget`:
4553
- * `Node.contains()` es lo que existe para chequear pertenencia al árbol.
4554
- */
4555
- protected onFocusOut(event: FocusEvent): void;
4556
- private positionsFor;
4557
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<TooltipDirective, never>;
4558
- static ɵdir: _angular_core.ɵɵDirectiveDeclaration<TooltipDirective, "[kfTooltip]", never, { "text": { "alias": "kfTooltip"; "required": false; }; "kfTooltipPosition": { "alias": "kfTooltipPosition"; "required": false; }; }, {}, never, never, true, never>;
4559
- }
4560
-
4561
- /**
4562
- * Estado puntual mostrado junto a la identidad del vehículo (ej. "En ruta",
4563
- * "Mantenimiento", "Fuera de servicio"). `tone` define el color semántico:
4564
- * "crit" no tiene token propio en base.css, se mapea a var(--kf-color-error).
4565
- */
4566
- interface VehicleHeaderStatus {
4567
- label: string;
4568
- tone?: 'ok' | 'warn' | 'crit';
4569
- icon?: IconName;
4570
- }
4571
- /**
4572
- * Header de identidad de un vehículo de flota: unidad (patente/id), modelo
4573
- * (como badge, no texto plano — página 24: "ABC-123" junto a un badge
4574
- * "TITAN 4X2"), VIN en fuente mono y un estado opcional. Un área de acciones
4575
- * opcional se proyecta vía `<ng-content select="[actions]">` para no acoplar
4576
- * el componente a un set fijo de botones — el patrón real de la spec es
4577
- * `kf-button variant="outline"` (ej. "Llamar", con ícono) + un `primary`
4578
- * (ej. "Crear Ticket", con ícono "+"):
4579
- *
4580
- * <kf-vehicle-header unit="AB123CD" model="Sprinter 415" vin="9BW..."
4581
- * [status]="{ label: 'En ruta', tone: 'ok' }">
4582
- * <kf-button actions variant="outline" icon="phone">Llamar</kf-button>
4583
- * <kf-button actions icon="plus">Crear Ticket</kf-button>
4584
- * </kf-vehicle-header>
4585
- */
4586
- declare class VehicleHeader {
4587
- readonly unit: _angular_core.InputSignal<string>;
4588
- readonly model: _angular_core.InputSignal<string | undefined>;
4589
- readonly vin: _angular_core.InputSignal<string | undefined>;
4590
- readonly status: _angular_core.InputSignal<VehicleHeaderStatus | undefined>;
4591
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<VehicleHeader, never>;
4592
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<VehicleHeader, "kf-vehicle-header", never, { "unit": { "alias": "unit"; "required": true; "isSignal": true; }; "model": { "alias": "model"; "required": false; "isSignal": true; }; "vin": { "alias": "vin"; "required": false; "isSignal": true; }; "status": { "alias": "status"; "required": false; "isSignal": true; }; }, {}, never, ["[actions]"], true, never>;
4593
- }
4594
-
4595
- type ViewToggleOption = 'table' | 'cards';
4596
- /**
4597
- * Switch de dos posiciones para alternar entre vista de tabla y vista de
4598
- * tarjetas. Pensado para usarse dentro de DataTable, pero es standalone e
4599
- * independiente: no asume nada sobre el componente que lo contiene.
4600
- *
4601
- * Se implementa como un grupo de radio (role="radiogroup") en vez de dos
4602
- * botones sueltos: semánticamente es una única selección excluyente entre
4603
- * dos estados, no dos acciones independientes. Esto también habilita
4604
- * navegación con flechas izquierda/derecha, igual que un <input type="radio">
4605
- * nativo agrupado.
4606
- *
4607
- * <kf-view-toggle [view]="view" (viewChange)="view = $event" />
4608
- */
4609
- declare class ViewToggle {
4610
- readonly view: _angular_core.InputSignal<ViewToggleOption>;
4611
- readonly viewChange: _angular_core.OutputEmitterRef<ViewToggleOption>;
4612
- select(next: ViewToggleOption): void;
4613
- /** Navegación estándar de ARIA radiogroup: flechas mueven foco y seleccionan. */
4614
- onKeydown(event: KeyboardEvent): void;
4615
- static ɵfac: _angular_core.ɵɵFactoryDeclaration<ViewToggle, never>;
4616
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<ViewToggle, "kf-view-toggle", never, { "view": { "alias": "view"; "required": true; "isSignal": true; }; }, { "viewChange": "viewChange"; }, never, never, true, never>;
4617
- }
4618
-
4619
- export { ActionTile, AdminBadge, Alert, AlertRow, AuthLayout, AuthLayoutSso, Avatar, Badge, Breadcrumb, BucketProgress, Button, Card, Carousel, CarouselSlide, Checkbox, Chip, Collapsible, CompanySelector, ContactPopover, 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, RowActions, 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 };
4620
- 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, RowAction, RowActionKind, RowActionsMode, SectionNavSubsectionData, SegmentedOption, SelectableCardOption, SidebarNavItemData, SkeletonVariant, SpinnerSize, SsoProvider, StatCardTone, StatTileTone, StatTileVariant, TabOption, TextAs, TextTone, TextVariant, ToastAction, ToastOptions, ToastPromiseMessages, ToastState, ToastTone, TooltipPosition, VehicleHeaderStatus, ViewToggleOption };
1
+ export * from '@kaufmann-lab/ui/auth';
2
+ export * from '@kaufmann-lab/ui/business';
3
+ export * from '@kaufmann-lab/ui/core';
4
+ export * from '@kaufmann-lab/ui/mobile';
5
+ export * from '@kaufmann-lab/ui/shared';