@kaufmann-lab/ui 0.1.25 → 0.1.27

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@kaufmann-lab/ui",
3
- "version": "0.1.25",
3
+ "version": "0.1.27",
4
4
  "description": "Shared Angular component library for the Kaufmann frontends",
5
5
  "license": "MIT",
6
6
  "author": "Kaufmann Lab",
@@ -17,6 +17,11 @@
17
17
  "tslib": "^2.3.0"
18
18
  },
19
19
  "sideEffects": false,
20
+ "files": [
21
+ "fesm2022",
22
+ "types",
23
+ "src/tokens"
24
+ ],
20
25
  "module": "fesm2022/kaufmann-lab-ui.mjs",
21
26
  "typings": "types/kaufmann-lab-ui.d.ts",
22
27
  "exports": {
@@ -156,6 +156,12 @@
156
156
  --kf-font-size-title-sm: 1.125rem;
157
157
  --kf-font-size-body: 1rem;
158
158
  --kf-font-size-body-sm: 0.875rem;
159
+ /*
160
+ * Tamaño de etiqueta/valor en filas y paneles densos (13px): queda entre
161
+ * `body-sm` (14px) y `caption` (12px). Es solo el token; varios CSS de la
162
+ * librería todavía escriben `0.8125rem` suelto y se migran por separado.
163
+ */
164
+ --kf-font-size-label: 0.8125rem;
159
165
  --kf-font-size-caption: 0.75rem;
160
166
 
161
167
  /*
@@ -214,6 +220,13 @@
214
220
  */
215
221
  --kf-shadow-focus-error: 0 0 0 2px var(--kf-color-white), 0 0 0 4px var(--kf-color-error);
216
222
 
223
+ /*
224
+ * Anillo de selección de `kf-stat-tile interactive` en estado `pressed`:
225
+ * 1px de marca por fuera del borde (que también pasa a marca). Un tema o
226
+ * una pantalla lo puede redefinir sin tocar el componente.
227
+ */
228
+ --kf-stat-tile-ring: 0 0 0 1px var(--kf-color-primary);
229
+
217
230
  /*
218
231
  * ---------- Anchos de controles de formulario ----------
219
232
  * Usados por `width` de `kf-select`/`kf-multi-select`. `md` (240px) es
@@ -223,6 +236,8 @@
223
236
  --kf-select-width-sm: 160px;
224
237
  --kf-select-width-md: 240px;
225
238
  --kf-select-width-lg: 320px;
239
+ /* Techo de `kf-select width="fit"`: más allá de este ancho trunca con elipsis. */
240
+ --kf-select-fit-max-width: 20rem;
226
241
 
227
242
  /*
228
243
  * ---------- Geometría del checkbox ----------
@@ -241,6 +241,15 @@ type AlertVariant = 'card' | 'bar';
241
241
  * sobra.
242
242
  *
243
243
  * <kf-alert variant="bar" tone="warn" title="Modo suplantación">Viendo como Ana Pérez.</kf-alert>
244
+ *
245
+ * Los elementos con el atributo `kfAlertActions` se proyectan en una fila
246
+ * propia bajo el mensaje (con salto de línea si no caben, también en la
247
+ * variante `bar`). Sin ellos la fila no existe y el aviso queda igual.
248
+ *
249
+ * <kf-alert tone="warn" title="SOAT por vencer">
250
+ * Vence el 15/08.
251
+ * <div kfAlertActions><kf-button size="sm">Renovar</kf-button></div>
252
+ * </kf-alert>
244
253
  */
245
254
  declare class Alert {
246
255
  readonly tone: _angular_core.InputSignal<AlertTone>;
@@ -258,7 +267,7 @@ declare class Alert {
258
267
  protected readonly ariaRole: _angular_core.Signal<"alert" | "status">;
259
268
  protected readonly toneIcon: _angular_core.Signal<IconName>;
260
269
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<Alert, never>;
261
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<Alert, "kf-alert", never, { "tone": { "alias": "tone"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "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>;
270
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<Alert, "kf-alert", never, { "tone": { "alias": "tone"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "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, ["*", "[kfAlertActions]"], true, never>;
262
271
  }
263
272
 
264
273
  type AlertRowTone = 'warn' | 'error' | 'neutral';
@@ -938,6 +947,12 @@ type DataTableDensity = 'comfortable' | 'compact';
938
947
  /** `key:direction` — mismo formato que `sort` de `kf-quote-picker`, para
939
948
  * que un consumidor que ya conoce ese patrón no aprenda uno nuevo acá. */
940
949
  type DataTableSort = `${string}:${'asc' | 'desc'}`;
950
+ /** Nombre de la entidad listada: un `string` (plural fijo) o `{ one, other }`
951
+ * para usar el singular cuando el total es 1. */
952
+ type DataTableItemLabel = string | {
953
+ one: string;
954
+ other: string;
955
+ };
941
956
  /**
942
957
  * Tabla estandarizada con soporte opcional de toggle tabla/cards.
943
958
  *
@@ -1002,9 +1017,11 @@ type DataTableSort = `${string}:${'asc' | 'desc'}`;
1002
1017
  * por página" viene activo por defecto con `DEFAULT_PAGE_SIZE_OPTIONS`
1003
1018
  * ([10, 25, 50], estándar del portal) — pasar `pageSizeOptions` propio lo
1004
1019
  * pisa, y pasar `[]` lo desactiva (paginación sin selector). `itemLabel`
1005
- * completa el texto informativo ("1–10 de 42 beneficios").
1020
+ * completa el texto informativo ("1–10 de 42 beneficios"); con
1021
+ * `{ one, other }` usa el singular cuando el total es 1 ("1–1 de 1 beneficio").
1006
1022
  *
1007
1023
  * <kf-data-table [columns]="columns" [rows]="rows" [pageSize]="25" itemLabel="beneficios" />
1024
+ * <kf-data-table [columns]="columns" [rows]="rows" [pageSize]="25" [itemLabel]="{ one: 'unidad', other: 'unidades' }" />
1008
1025
  * <kf-data-table [columns]="columns" [rows]="rows" [pageSize]="25" [pageSizeOptions]="[]" />
1009
1026
  *
1010
1027
  * La página es `[(page)]` (el consumidor puede llevarla a la URL) y se
@@ -1225,10 +1242,13 @@ declare class DataTable<T = unknown> {
1225
1242
  readonly pageSizeChange: _angular_core.OutputEmitterRef<number>;
1226
1243
  /** Nombre de la entidad listada (ej. "beneficios", "facturas"), en
1227
1244
  * plural — se agrega al final del texto informativo de la paginación
1228
- * interna ("1–5 de 7 beneficios"). Sin esto, el texto queda solo con
1229
- * números ("1–5 de 7"): la librería no impone un idioma ni un nombre
1230
- * fijo, cada consumidor decide el suyo. */
1231
- readonly itemLabel: _angular_core.InputSignal<string>;
1245
+ * interna ("1–5 de 7 beneficios"). Acepta `{ one, other }` para usar el
1246
+ * singular cuando el total es 1 ("1–1 de 1 beneficio"); un `string`
1247
+ * conserva el comportamiento de siempre (mismo texto para cualquier
1248
+ * total). Sin esto, el texto queda solo con números ("1–5 de 7"): la
1249
+ * librería no impone un idioma ni un nombre fijo, cada consumidor decide
1250
+ * el suyo. */
1251
+ readonly itemLabel: _angular_core.InputSignal<DataTableItemLabel>;
1232
1252
  private readonly internalPageSize;
1233
1253
  /** Tamaño de página efectivo: el que el usuario elija en el selector
1234
1254
  * (si `pageSizeOptions` está activo), o el `pageSize` del consumidor. */
@@ -3246,6 +3266,8 @@ declare class MilestoneTimeline {
3246
3266
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<MilestoneTimeline, "kf-milestone-timeline", never, { "groups": { "alias": "groups"; "required": true; "isSignal": true; }; "expandedIds": { "alias": "expandedIds"; "required": false; "isSignal": true; }; "nextItemId": { "alias": "nextItemId"; "required": false; "isSignal": true; }; "labels": { "alias": "labels"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "headingLevel": { "alias": "headingLevel"; "required": false; "isSignal": true; }; }, { "expandedIds": "expandedIdsChange"; }, never, never, true, never>;
3247
3267
  }
3248
3268
 
3269
+ /** Anchos con nombre de `size`: 480 / 640 / 880 / 1120 px. */
3270
+ type ModalSize = 'sm' | 'md' | 'lg' | 'xl';
3249
3271
  /**
3250
3272
  * Diálogo centrado sobre un backdrop: confirmaciones destructivas, formularios
3251
3273
  * cortos, decisiones que bloquean el flujo.
@@ -3284,6 +3306,22 @@ declare class MilestoneTimeline {
3284
3306
  * <kf-link footer class="kf-modal__footer-start" disabled>Ver documento</kf-link>
3285
3307
  * <kf-button footer (click)="open.set(false)">Cerrar</kf-button>
3286
3308
  *
3309
+ * ## Ancho: `size` y `width`
3310
+ * `size` (`'sm'` 480px, `'md'` 640px, `'lg'` 880px, `'xl'` 1120px) elige un
3311
+ * ancho con nombre; sin `size` ni `width` el ancho es el histórico de 480px.
3312
+ * `width` sigue aceptando cualquier valor CSS y, si se pasa, manda sobre
3313
+ * `size`. En pantallas angostas el panel nunca supera el ancho del viewport.
3314
+ *
3315
+ * <kf-modal [(open)]="open" title="Nuevo ticket" size="xl">…</kf-modal>
3316
+ *
3317
+ * ## Separadores: `dividers`
3318
+ * Con `dividers` el encabezado y el footer quedan separados del cuerpo por
3319
+ * una línea (`--kf-color-border`); el del footer solo aparece si hay
3320
+ * acciones. Sirve cuando el cuerpo se desplaza (`maxHeight`) y su contenido
3321
+ * no debe cortarse contra los botones. Sin `dividers` no cambia nada.
3322
+ *
3323
+ * <kf-modal [(open)]="open" title="Nuevo ticket" size="lg" dividers maxHeight="85vh">…</kf-modal>
3324
+ *
3287
3325
  * ## Modal de listado (alto acotado, filtros fijos)
3288
3326
  * Para un diálogo con mucho contenido (ej. elegir un ítem entre cientos):
3289
3327
  * - `maxHeight` (ej. `"85vh"`) acota el panel; solo `.kf-modal__body` se
@@ -3316,6 +3354,10 @@ declare class Modal {
3316
3354
  * más, ej. `width="720px"` — sigue topeado por `100vw` en pantallas
3317
3355
  * angostas, mismo `min()` de siempre. */
3318
3356
  readonly width: _angular_core.InputSignal<string | null>;
3357
+ /** Ancho con nombre sobre `--kf-modal-width`; `width` lo pisa si ambos están. */
3358
+ readonly size: _angular_core.InputSignal<ModalSize | null>;
3359
+ /** Línea bajo el encabezado y sobre el footer (si tiene acciones). */
3360
+ readonly dividers: _angular_core.InputSignalWithTransform<boolean, unknown>;
3319
3361
  /** Alto máximo del panel (ej. `"85vh"`); con valor, solo el cuerpo se
3320
3362
  * desplaza. Sin valor (default) el panel crece con su contenido. */
3321
3363
  readonly maxHeight: _angular_core.InputSignal<string | null>;
@@ -3349,7 +3391,7 @@ declare class Modal {
3349
3391
  */
3350
3392
  protected onHostClick(event: MouseEvent): void;
3351
3393
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<Modal, never>;
3352
- 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; }; "maxHeight": { "alias": "maxHeight"; "required": false; "isSignal": true; }; "fullscreenOnMobile": { "alias": "fullscreenOnMobile"; "required": false; "isSignal": true; }; "initialFocus": { "alias": "initialFocus"; "required": false; "isSignal": true; }; }, { "open": "openChange"; }, never, ["[kf-modal-meta]", "[kf-modal-toolbar]", "*", "[footer]"], true, never>;
3394
+ 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; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "dividers": { "alias": "dividers"; "required": false; "isSignal": true; }; "maxHeight": { "alias": "maxHeight"; "required": false; "isSignal": true; }; "fullscreenOnMobile": { "alias": "fullscreenOnMobile"; "required": false; "isSignal": true; }; "initialFocus": { "alias": "initialFocus"; "required": false; "isSignal": true; }; }, { "open": "openChange"; }, never, ["[kf-modal-meta]", "[kf-modal-toolbar]", "*", "[footer]"], true, never>;
3353
3395
  }
3354
3396
 
3355
3397
  /** Color de la franja izquierda y del fondo. */
@@ -3450,7 +3492,7 @@ declare class OptionList {
3450
3492
  }
3451
3493
 
3452
3494
  type TextAs = 'h1' | 'h2' | 'h3' | 'p' | 'span';
3453
- type TextVariant = 'title-xxl' | 'title-xl' | 'title-lg' | 'title-md' | 'title-sm' | 'body' | 'body-muted' | 'caption' | 'overline' | 'overline-strong';
3495
+ type TextVariant = 'title-xxl' | 'title-xl' | 'title-lg' | 'title-md' | 'title-sm' | 'body' | 'body-muted' | 'body-sm' | 'body-sm-muted' | 'caption' | 'overline' | 'overline-strong';
3454
3496
  type TextTone = 'neutral' | 'ok' | 'warn' | 'error' | 'info';
3455
3497
  /**
3456
3498
  * Tipografía unificada — separa la etiqueta semántica (`as`, qué es el
@@ -3471,6 +3513,12 @@ type TextTone = 'neutral' | 'ok' | 'warn' | 'error' | 'info';
3471
3513
  * títulos protagonistas de pantalla completa (ej. bienvenida del home vía
3472
3514
  * `kf-page-header[size=xl|xxl]`) — ver tokens/base.css.
3473
3515
  *
3516
+ * `body-sm`/`body-sm-muted` son el paso intermedio entre `body` (1rem) y
3517
+ * `caption` (0.75rem): 0.875rem (`--kf-font-size-body-sm`), para texto de
3518
+ * apoyo en filas y paneles densos. Mismo criterio de color que `body`/
3519
+ * `body-muted`: `body-sm` hereda el color de texto y `body-sm-muted` usa el
3520
+ * atenuado.
3521
+ *
3474
3522
  * `overline`/`overline-strong` son para el patrón "texto chico en mayúsculas
3475
3523
  * junto a un dato" (ej. el label "RUC" de `kf-company-selector`, el badge de
3476
3524
  * `kf-admin-badge`) — antes de estas variantes, ese patrón se reimplementaba
@@ -3482,6 +3530,7 @@ type TextTone = 'neutral' | 'ok' | 'warn' | 'error' | 'info';
3482
3530
  *
3483
3531
  * <kf-text as="h1" variant="title-lg">Bienvenido de nuevo</kf-text>
3484
3532
  * <kf-text as="p" variant="body-muted">Ingresá con tu cuenta corporativa.</kf-text>
3533
+ * <kf-text as="span" variant="body-sm">Última actualización hace 5 min</kf-text>
3485
3534
  * <kf-text as="span" variant="caption" tone="error">Campo obligatorio</kf-text>
3486
3535
  * <kf-text as="span" variant="overline">RUC</kf-text>
3487
3536
  * <kf-text as="span" variant="overline-strong" tone="warn">Vista administrador</kf-text>
@@ -4258,6 +4307,28 @@ interface SelectOption {
4258
4307
  * panelTitle="Ordenar por" panelWidth="content"
4259
4308
  * [options]="sortOptions" [value]="sort()" (valueChange)="sort.set($event)" />
4260
4309
  *
4310
+ * `width="fit"` ajusta el ancho al contenido: mide la opción más larga (y el
4311
+ * placeholder) con la tipografía real y no cambia al elegir otra, hasta un
4312
+ * tope de `--kf-select-fit-max-width` (20rem por defecto; lo que no entra se
4313
+ * trunca con elipsis). Pensado para selects en barras de herramientas o
4314
+ * encabezados donde un ancho fijo deja espacio vacío. La medición se hace
4315
+ * con una pasada invisible de todas las opciones, así que conviene para
4316
+ * listas cortas o medianas; con cientos de opciones usar un ancho fijo. Si
4317
+ * alguna opción trae `tag`, también se reserva el hueco del tag más ancho,
4318
+ * por lo que elegir una opción con tag tampoco cambia el ancho. Conviene
4319
+ * combinarlo con `panelWidth="content"`: con el default (`"trigger"`) el panel
4320
+ * mide lo mismo que el trigger y las opciones, que suman su propio padding
4321
+ * (y su tag, si lo traen), pueden quedar truncadas.
4322
+ *
4323
+ * <kf-select width="fit" label="División" hideLabel [options]="divisions" [value]="division()" (valueChange)="..." />
4324
+ *
4325
+ * `size="sm"` fija la altura del trigger en 32px (`--kf-select-min-height`)
4326
+ * para filas de filtros densas, sin tocar el padding horizontal ni el
4327
+ * `variant`. El default (`'md'`) no cambia nada. Para otra altura puntual,
4328
+ * declarar `--kf-select-min-height` en el propio `kf-select`.
4329
+ *
4330
+ * <kf-select size="sm" width="fit" [options]="ranges" [value]="range()" (valueChange)="..." />
4331
+ *
4261
4332
  * Teclado (patrón listbox de WAI-ARIA): ArrowDown/ArrowUp sobre el trigger
4262
4333
  * cerrado lo abren; al abrir, el foco pasa a la opción seleccionada (o al
4263
4334
  * buscador, si está visible). En el panel, flechas y Home/End mueven el
@@ -4290,6 +4361,12 @@ declare class Select {
4290
4361
  readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
4291
4362
  /** Borde en rojo y `aria-invalid` en el trigger. Ver `errorMessage` para el mensaje asociado — mismo contrato que `kf-input`. */
4292
4363
  readonly invalid: _angular_core.InputSignalWithTransform<boolean, unknown>;
4364
+ /**
4365
+ * Pone el asterisco rojo en la etiqueta (igual que `kf-input`) y
4366
+ * `aria-required="true"` en el trigger. Solo señaliza: la validación sigue
4367
+ * en el formulario del consumidor.
4368
+ */
4369
+ readonly required: _angular_core.InputSignalWithTransform<boolean, unknown>;
4293
4370
  /** Mensaje de error con `role="alert"`, debajo del trigger. */
4294
4371
  readonly errorMessage: _angular_core.InputSignal<string | undefined>;
4295
4372
  readonly variant: _angular_core.InputSignal<"field" | "compact">;
@@ -4298,8 +4375,14 @@ declare class Select {
4298
4375
  * comportamiento actual sin cambios para consumidores existentes.
4299
4376
  * 'full' ocupa el 100% del contenedor — para selects dentro de layouts
4300
4377
  * flex/grid donde el ancho fijo no encaja (ej. un footer de sidebar).
4378
+ * 'fit' mide el contenido (opción más larga) con un tope.
4301
4379
  */
4302
- readonly width: _angular_core.InputSignal<"sm" | "md" | "lg" | "full">;
4380
+ readonly width: _angular_core.InputSignal<"sm" | "md" | "lg" | "full" | "fit">;
4381
+ /**
4382
+ * Altura del trigger. 'md' (default) conserva la altura histórica; 'sm'
4383
+ * la fija en 32px para barras de filtros densas.
4384
+ */
4385
+ readonly size: _angular_core.InputSignal<"sm" | "md">;
4303
4386
  /**
4304
4387
  * Tri-state: si el consumidor no lo especifica (`undefined`), el buscador
4305
4388
  * se activa solo cuando `options().length` supera `searchThreshold()`. Si
@@ -4347,6 +4430,14 @@ declare class Select {
4347
4430
  * nombre accesible, así que con label visible no se emite.
4348
4431
  */
4349
4432
  readonly triggerAriaLabel: _angular_core.Signal<string | null>;
4433
+ /**
4434
+ * Textos que `width="fit"` mide para dimensionar el trigger: el de cada
4435
+ * opción tal como se vería en el trigger (`shortLabel` o `label`) y el
4436
+ * placeholder. Sin repetidos.
4437
+ */
4438
+ protected readonly fitTexts: _angular_core.Signal<string[]>;
4439
+ /** Tags distintos de las opciones, para reservar el hueco del más ancho con `width="fit"`. */
4440
+ protected readonly fitTags: _angular_core.Signal<string[]>;
4350
4441
  readonly showSearch: _angular_core.Signal<boolean>;
4351
4442
  /** Opciones visibles tras aplicar el filtro de búsqueda (case/acento-insensitive). */
4352
4443
  readonly filteredOptions: _angular_core.Signal<SelectOption[]>;
@@ -4436,7 +4527,7 @@ declare class Select {
4436
4527
  private scrollActiveIntoView;
4437
4528
  selectActive(): void;
4438
4529
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<Select, never>;
4439
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<Select, "kf-select", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "hideLabel": { "alias": "hideLabel"; "required": false; "isSignal": true; }; "prefix": { "alias": "prefix"; "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; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "panelTitle": { "alias": "panelTitle"; "required": false; "isSignal": true; }; "panelWidth": { "alias": "panelWidth"; "required": false; "isSignal": true; }; }, { "valueChange": "valueChange"; }, never, never, true, never>;
4530
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<Select, "kf-select", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "hideLabel": { "alias": "hideLabel"; "required": false; "isSignal": true; }; "prefix": { "alias": "prefix"; "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; }; "required": { "alias": "required"; "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; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "searchable": { "alias": "searchable"; "required": false; "isSignal": true; }; "searchThreshold": { "alias": "searchThreshold"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "panelTitle": { "alias": "panelTitle"; "required": false; "isSignal": true; }; "panelWidth": { "alias": "panelWidth"; "required": false; "isSignal": true; }; }, { "valueChange": "valueChange"; }, never, never, true, never>;
4440
4531
  }
4441
4532
 
4442
4533
  interface SelectableCardOption {
@@ -4564,8 +4655,13 @@ declare class SidebarShell {
4564
4655
  * muestra todo expandido.
4565
4656
  */
4566
4657
  readonly itemsCollapsed: _angular_core.Signal<boolean>;
4567
- /** Cierra el panel mobile con Escape — mismo criterio que `Dropdown`/`Modal`/`Drawer`. */
4568
- protected onEscape(): void;
4658
+ /**
4659
+ * Cierra el panel mobile con Escape — mismo criterio que `Drawer`: si un
4660
+ * panel flotante de adentro (`kf-dropdown`, `kf-select`) ya consumió la
4661
+ * tecla en captura (`defaultPrevented`), el panel mobile sigue abierto y
4662
+ * solo cierra la capa superior.
4663
+ */
4664
+ protected onEscape(event: Event): void;
4569
4665
  protected onNavItemClick(event: Event, item: SidebarNavItemData): void;
4570
4666
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<SidebarShell, never>;
4571
4667
  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>;
@@ -4718,6 +4814,24 @@ type StatTileVariant = 'inline' | 'card';
4718
4814
  * <kf-stat-tile label="Estado" hasProjectedValue>
4719
4815
  * <kf-badge valueContent [tone]="stateTone()">{{ stateLabel() }}</kf-badge>
4720
4816
  * </kf-stat-tile>
4817
+ *
4818
+ * `interactive` convierte el tile en un botón (`role="button"`, enfocable,
4819
+ * Enter y Espacio) que emite `tileClick`: para un KPI que filtra o despliega
4820
+ * algo. El click sobre un control proyectado dentro del tile (link, botón,
4821
+ * campo) no emite `tileClick`; el de texto común sí. `pressed`
4822
+ * (`aria-pressed`) lo vuelve un botón de alternancia, útil como filtro; `expanded` (`aria-expanded`) indica que controla un panel
4823
+ * desplegable. Ambos son `boolean | null`: `null` (default) no emite el
4824
+ * atributo. El estado `pressed` se marca con un borde y el anillo
4825
+ * `--kf-stat-tile-ring`; el foco de teclado usa `--kf-shadow-focus`. Sin
4826
+ * `interactive` el tile se comporta exactamente como antes (los demás inputs
4827
+ * se ignoran), y con `cta` tampoco aplica: ya contiene su propio link.
4828
+ *
4829
+ * Distinto de `kf-stat-card` (`clickable` + `pressed`): esa card tiene el
4830
+ * ícono circular a la izquierda y un chevron; el tile conserva label arriba,
4831
+ * ícono a la derecha, valor y nota, y suma `expanded`.
4832
+ *
4833
+ * <kf-stat-tile interactive label="En ruta" value="12" icon="truck"
4834
+ * [pressed]="filter() === 'route'" (tileClick)="toggleFilter('route')" />
4721
4835
  */
4722
4836
  declare class StatTile {
4723
4837
  readonly cta: _angular_core.InputSignalWithTransform<boolean, unknown>;
@@ -4728,6 +4842,11 @@ declare class StatTile {
4728
4842
  readonly tone: _angular_core.InputSignal<StatTileTone>;
4729
4843
  /** Tercera línea bajo el valor. Vacía cae en `notePlaceholder`. */
4730
4844
  readonly note: _angular_core.InputSignal<string | null | undefined>;
4845
+ /** Máximo de líneas visibles de la nota antes de la elipsis (`…`); el texto
4846
+ * completo sigue en el DOM y en el `title`. Default 2, como siempre. Un
4847
+ * valor menor a 1 o no numérico no limita la altura: la nota se muestra
4848
+ * entera. */
4849
+ readonly noteLines: _angular_core.InputSignalWithTransform<number, unknown>;
4731
4850
  /** Qué dice la tercera línea cuando no hay nota. Cada consumidor debería
4732
4851
  * pasar el motivo real de SU dato ausente, no un texto genérico. */
4733
4852
  readonly notePlaceholder: _angular_core.InputSignal<string | undefined>;
@@ -4747,12 +4866,39 @@ declare class StatTile {
4747
4866
  * que `actionClick` de `kf-panel`, sin depender de `@angular/router`.
4748
4867
  */
4749
4868
  readonly actionClick: _angular_core.OutputEmitterRef<MouseEvent>;
4869
+ /** Convierte el tile en un botón; ver la descripción de la clase. */
4870
+ readonly interactive: _angular_core.InputSignalWithTransform<boolean, unknown>;
4871
+ /** Estado de alternancia (`aria-pressed`); `null` = no aplica. */
4872
+ readonly pressed: _angular_core.InputSignal<boolean | null>;
4873
+ /** El tile controla un panel (`aria-expanded`); `null` = no aplica. */
4874
+ readonly expanded: _angular_core.InputSignal<boolean | null>;
4875
+ /** Activación del tile interactivo: click, Enter o Espacio. */
4876
+ readonly tileClick: _angular_core.OutputEmitterRef<void>;
4877
+ /** La variante CTA ya tiene su propio link: no puede ser además un botón. */
4878
+ protected readonly isInteractive: _angular_core.Signal<boolean>;
4750
4879
  /** Texto real de la nota. `null` significa "no reservar la línea": pasa
4751
4880
  * solo cuando el consumidor no declaró ni nota ni placeholder. */
4752
4881
  protected readonly noteText: _angular_core.Signal<string | null>;
4882
+ protected onActivate(event: MouseEvent): void;
4883
+ /**
4884
+ * Un control proyectado dentro del tile (link, botón, campo…) tiene su
4885
+ * propia acción: su click no debe contar como activar el tile. El texto o
4886
+ * los spans comunes sí activan, por eso se busca el control con `closest`
4887
+ * y no se compara `target` con `currentTarget`.
4888
+ */
4889
+ private isFromProjectedControl;
4890
+ /**
4891
+ * Teclado de un botón nativo: Enter activa al presionar y Espacio al soltar
4892
+ * (en `keydown` solo se frena el scroll de la página). Solo cuenta cuando el
4893
+ * foco está en el propio tile, no en contenido proyectado dentro de él.
4894
+ */
4895
+ protected onKeydown(event: KeyboardEvent): void;
4896
+ protected onKeyup(event: KeyboardEvent): void;
4897
+ /** Valor de `line-clamp`: entero ≥ 1, o `none` para mostrar la nota entera. */
4898
+ protected readonly noteClamp: _angular_core.Signal<string>;
4753
4899
  protected readonly noteIsPlaceholder: _angular_core.Signal<boolean>;
4754
4900
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<StatTile, never>;
4755
- 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; }; }, { "actionClick": "actionClick"; }, never, ["[valueContent]"], true, never>;
4901
+ 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; }; "noteLines": { "alias": "noteLines"; "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; }; "interactive": { "alias": "interactive"; "required": false; "isSignal": true; }; "pressed": { "alias": "pressed"; "required": false; "isSignal": true; }; "expanded": { "alias": "expanded"; "required": false; "isSignal": true; }; }, { "actionClick": "actionClick"; "tileClick": "tileClick"; }, never, ["[valueContent]"], true, never>;
4756
4902
  }
4757
4903
 
4758
4904
  /**
@@ -4821,6 +4967,70 @@ declare class StatusPill {
4821
4967
  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>;
4822
4968
  }
4823
4969
 
4970
+ type SwitchSize = 'sm' | 'md';
4971
+ /**
4972
+ * Interruptor de activar/desactivar con efecto inmediato (preferencias,
4973
+ * ajustes). Para elegir opciones dentro de un formulario que se envía al
4974
+ * final, usar `kf-checkbox`.
4975
+ *
4976
+ * Es un `<button role="switch" aria-checked>`: el lector de pantalla anuncia
4977
+ * el estado con el propio rol, por eso no hay texto extra de "activado". Enter
4978
+ * y Espacio lo alternan como en cualquier botón.
4979
+ *
4980
+ * `label` es obligatorio: da el nombre accesible aunque no se vea. Con
4981
+ * `hideLabel` se oculta el texto y pasa a `aria-label`, para un interruptor
4982
+ * cuyo contexto ya lo explica la fila que lo contiene.
4983
+ *
4984
+ * `checked` es two-way (`model`). Implementa `ControlValueAccessor`, así que
4985
+ * también funciona con `formControlName`/`ngModel`; en ese modo `writeValue`
4986
+ * escribe en el mismo `checked`. `disabled` del input y el del formulario se
4987
+ * combinan: basta con que uno esté activo.
4988
+ *
4989
+ * `writeValue` (el formulario escribiendo el valor por código, ej.
4990
+ * `setValue`/`reset`) también emite `checkedChange`, porque escribe en el
4991
+ * `model`. Combinar `[(checked)]` con `formControlName`/`ngModel` en el mismo
4992
+ * switch hace que el valor llegue dos veces al consumidor: usar una sola de
4993
+ * las dos vías, o ignorar en el handler el eco que no cambia el valor.
4994
+ *
4995
+ * `describedBy` recibe el `id` de un texto de ayuda externo (ej. el `<p>` de
4996
+ * una fila de preferencias) y lo enlaza con `aria-describedby` del botón, para
4997
+ * que el lector de pantalla lo anuncie junto al nombre y el estado. Sin valor,
4998
+ * el atributo no se renderiza.
4999
+ *
5000
+ * El color sale de roles (`--kf-color-primary` encendido,
5001
+ * `--kf-color-border-control` apagado) y el foco de `--kf-shadow-focus`; no
5002
+ * recibe colores.
5003
+ *
5004
+ * <kf-switch label="Fijar indicadores" [(checked)]="pinned" />
5005
+ * <kf-switch label="Mapa junto a la tabla" hideLabel size="sm" formControlName="splitMap" />
5006
+ * <kf-switch label="Alertas por correo" describedBy="mail-hint" [(checked)]="mail" />
5007
+ * <p id="mail-hint">Se envía un resumen cada hora.</p>
5008
+ */
5009
+ declare class Switch implements ControlValueAccessor {
5010
+ /** Nombre accesible y texto visible (salvo con `hideLabel`). */
5011
+ readonly label: _angular_core.InputSignal<string>;
5012
+ /** Oculta el texto y lo deja como `aria-label`. */
5013
+ readonly hideLabel: _angular_core.InputSignalWithTransform<boolean, unknown>;
5014
+ readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
5015
+ readonly size: _angular_core.InputSignal<SwitchSize>;
5016
+ /** `id` del texto de ayuda que describe al interruptor (`aria-describedby`). */
5017
+ readonly describedBy: _angular_core.InputSignal<string | null>;
5018
+ /** Estado encendido, two-way: `[(checked)]="pinned"`. */
5019
+ readonly checked: _angular_core.ModelSignal<boolean>;
5020
+ private readonly disabledByForm;
5021
+ protected readonly isDisabled: _angular_core.Signal<boolean>;
5022
+ private onChangeFn;
5023
+ private onTouchedFn;
5024
+ protected toggle(): void;
5025
+ protected onBlur(): void;
5026
+ writeValue(value: boolean | null): void;
5027
+ registerOnChange(fn: (value: boolean) => void): void;
5028
+ registerOnTouched(fn: () => void): void;
5029
+ setDisabledState(isDisabled: boolean): void;
5030
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<Switch, never>;
5031
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<Switch, "kf-switch", never, { "label": { "alias": "label"; "required": true; "isSignal": true; }; "hideLabel": { "alias": "hideLabel"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "describedBy": { "alias": "describedBy"; "required": false; "isSignal": true; }; "checked": { "alias": "checked"; "required": false; "isSignal": true; }; }, { "checked": "checkedChange"; }, never, never, true, never>;
5032
+ }
5033
+
4824
5034
  interface TabOption {
4825
5035
  value: string;
4826
5036
  label: string;
@@ -4910,6 +5120,16 @@ declare class FolderTabs {
4910
5120
  *
4911
5121
  * <kf-textarea label="Detalle" formControlName="detail" [maxRows]="12" />
4912
5122
  *
5123
+ * `maxlength` limita el texto (`maxlength` nativo, entero positivo) y
5124
+ * `showCounter` agrega el contador «0 / 500» pegado bajo el campo, en la misma
5125
+ * fila que el `hint`/`errorMessage`. Ambos son opt-in: sin ellos el campo no
5126
+ * cambia. El contador sin `maxlength` no se muestra (no hay tope que contar).
5127
+ * Visualmente pasa a rojo al llegar al tope; para lectores de pantalla solo
5128
+ * anuncia (`aria-live="polite"`) al cruzar el 90 % y al llegar al límite, no
5129
+ * en cada tecla.
5130
+ *
5131
+ * <kf-textarea label="Motivo" formControlName="reason" [maxlength]="500" showCounter />
5132
+ *
4913
5133
  * `invalid` + `errorMessage` y `readonly`/`disabled` siguen el mismo
4914
5134
  * contrato que `kf-input` — ver su JSDoc para la diferencia entre ambos
4915
5135
  * estados.
@@ -4936,15 +5156,29 @@ declare class Textarea implements ControlValueAccessor, AfterViewInit {
4936
5156
  readonly minRows: _angular_core.InputSignal<number>;
4937
5157
  /** Filas máximas antes de que el contenido pase a scroll interno en vez de seguir creciendo. */
4938
5158
  readonly maxRows: _angular_core.InputSignal<number>;
5159
+ /** Largo máximo del texto (`maxlength` nativo), entero positivo. */
5160
+ readonly maxlength: _angular_core.InputSignalWithTransform<number | null, string | number | null | undefined>;
5161
+ /** Muestra el contador «n / maxlength» bajo el campo. Requiere `maxlength`. */
5162
+ readonly showCounter: _angular_core.InputSignalWithTransform<boolean, unknown>;
4939
5163
  protected readonly textareaId: string;
4940
5164
  protected readonly hintId: string;
4941
5165
  protected readonly errorId: string;
5166
+ protected readonly counterId: string;
4942
5167
  protected readonly value: _angular_core.WritableSignal<string>;
4943
5168
  protected readonly focused: _angular_core.WritableSignal<boolean>;
4944
5169
  /** Deshabilitado por Reactive Forms (formControl.disable()). */
4945
5170
  private readonly disabledByForm;
4946
5171
  /** Disabled real: por input() directo o por el FormControl asociado. */
4947
5172
  protected readonly isDisabled: _angular_core.Signal<boolean>;
5173
+ protected readonly counterVisible: _angular_core.Signal<boolean>;
5174
+ protected readonly atLimit: _angular_core.Signal<boolean>;
5175
+ /**
5176
+ * Texto del `aria-live`: cambia solo al cruzar un umbral (90 % y tope), así
5177
+ * el lector no lee cada carácter escrito cerca del límite.
5178
+ */
5179
+ protected readonly counterAnnouncement: _angular_core.Signal<string>;
5180
+ /** Hint o error, más el contador cuando se muestra. */
5181
+ protected readonly describedBy: _angular_core.Signal<string | null>;
4948
5182
  private onChange;
4949
5183
  private onTouched;
4950
5184
  ngAfterViewInit(): void;
@@ -4963,7 +5197,7 @@ declare class Textarea implements ControlValueAccessor, AfterViewInit {
4963
5197
  */
4964
5198
  private adjustHeight;
4965
5199
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<Textarea, never>;
4966
- 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>;
5200
+ 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; }; "maxlength": { "alias": "maxlength"; "required": false; "isSignal": true; }; "showCounter": { "alias": "showCounter"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
4967
5201
  }
4968
5202
 
4969
5203
  /**
@@ -5302,5 +5536,5 @@ declare class ViewToggle {
5302
5536
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<ViewToggle, "kf-view-toggle", never, { "view": { "alias": "view"; "required": true; "isSignal": true; }; "iconOnly": { "alias": "iconOnly"; "required": false; "isSignal": true; }; "tableLabel": { "alias": "tableLabel"; "required": false; "isSignal": true; }; "cardsLabel": { "alias": "cardsLabel"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; }, { "viewChange": "viewChange"; }, never, never, true, never>;
5303
5537
  }
5304
5538
 
5305
- export { ActionTile, AdminBadge, Alert, AlertRow, Avatar, Badge, Breadcrumb, BucketProgress, Button, Calendar, Card, Carousel, CarouselSlide, Checkbox, Chip, Collapsible, ContactPopover, ContentSlot, ContentState, ContentStateLoading, ContentViewer, CursorPager, DATA_TABLE_MAX_SKELETON_ROWS, DEFAULT_MILESTONE_TIMELINE_LABELS, DEFAULT_PAGE_SIZE_OPTIONS, DataTable, Divider, DocumentRow, Drawer, Dropdown, DropdownItem, DropdownPanel, EmptyState, EventTimeline, FeatureCard, Field, FieldLabel, FileInput, Filter, FilterChips, FolderTabs, FolderTabsPanel, Footer, FormatInputDirective, Icon, Input, JourneyStepper, LIST_TOOLBAR_HYSTERESIS, Link, ListToolbar, MetricCard, MilestoneTimeline, Modal, MultiSelect, NoteBlock, OptionList, PROGRESS_STATUS, PROGRESS_STATUS_LABEL, PageHeader, Pagination, Panel, PopoverDirective, PortalHeader, Progress, PulseDot, RowActions, SKELETON_PRESET_MAX_COUNT, SectionHeader, SectionNav, SectionNavItem, SectionNavSubsection, Segmented, Select, SelectableCardGroup, SidebarShell, Skeleton, SkeletonPreset, Spinner, StatCard, StatTile, StatusMark, StatusNode, StatusPill, TOAST_DEFAULT_DURATION_MS, Tabs, Text, Textarea, TileGrid, TimeSlotPicker, ToastHost, ToastService, TooltipDirective, ViewToggle, dataTableViewSignal, formatFileSize, resolveListToolbarMode };
5306
- export type { AlertRowTone, AlertTone, AlertVariant, BreadcrumbItem, BucketProgressItem, CalendarDay, ContentStateKind, ContentStateLoad, ContentViewerType, DataTableCardRole, DataTableCellClick, DataTableColumn, DataTableDensity, DataTableSort, DataTableView, DividerOrientation, DocumentRowTone, DrawerRole, DrawerSide, DropdownItemTone, DropdownPanelRole, DropdownPanelVariant, EmptyStateTone, EventTimelineItem, EventTimelineTone, FileRejection, FileRejectionReason, FilterChipOption, FilterFieldChange, FilterFieldConfig, FilterFieldType, FilterValue, FolderTabOption, FooterLink, IconName, IconRotation, IconSize, InputBound, JourneyStep, JourneyStepStatus, JourneyStepperColorScheme, JourneyStepperOrientation, JourneyStepperSurface, LinkVariant, ListToolbarMode, MilestoneGroup, MilestoneItem, MilestoneTimelineLabels, MultiSelectOption, NoteBlockTone, OptionListOption, PageHeaderTitleSize, PaginationItem, PopoverPlacement, PortalHeaderAdminAction, PortalHeaderNavItem, ProgressSize, ProgressStatus, ProgressTone, RowAction, RowActionKind, RowActionsMode, SectionNavSubsectionData, SegmentedOption, SelectOption, SelectableCardOption, SidebarNavItemData, SkeletonPresetVariant, SkeletonVariant, SpinnerSize, StatCardDensity, StatCardTone, StatTileTone, StatTileVariant, TabOption, TextAs, TextTone, TextVariant, TimeSlot, ToastAction, ToastOptions, ToastPromiseMessages, ToastState, ToastTone, TooltipPosition, ViewToggleOption };
5539
+ export { ActionTile, AdminBadge, Alert, AlertRow, Avatar, Badge, Breadcrumb, BucketProgress, Button, Calendar, Card, Carousel, CarouselSlide, Checkbox, Chip, Collapsible, ContactPopover, ContentSlot, ContentState, ContentStateLoading, ContentViewer, CursorPager, DATA_TABLE_MAX_SKELETON_ROWS, DEFAULT_MILESTONE_TIMELINE_LABELS, DEFAULT_PAGE_SIZE_OPTIONS, DataTable, Divider, DocumentRow, Drawer, Dropdown, DropdownItem, DropdownPanel, EmptyState, EventTimeline, FeatureCard, Field, FieldLabel, FileInput, Filter, FilterChips, FolderTabs, FolderTabsPanel, Footer, FormatInputDirective, Icon, Input, JourneyStepper, LIST_TOOLBAR_HYSTERESIS, Link, ListToolbar, MetricCard, MilestoneTimeline, Modal, MultiSelect, NoteBlock, OptionList, PROGRESS_STATUS, PROGRESS_STATUS_LABEL, PageHeader, Pagination, Panel, PopoverDirective, PortalHeader, Progress, PulseDot, RowActions, SKELETON_PRESET_MAX_COUNT, SectionHeader, SectionNav, SectionNavItem, SectionNavSubsection, Segmented, Select, SelectableCardGroup, SidebarShell, Skeleton, SkeletonPreset, Spinner, StatCard, StatTile, StatusMark, StatusNode, StatusPill, Switch, TOAST_DEFAULT_DURATION_MS, Tabs, Text, Textarea, TileGrid, TimeSlotPicker, ToastHost, ToastService, TooltipDirective, ViewToggle, dataTableViewSignal, formatFileSize, resolveListToolbarMode };
5540
+ export type { AlertRowTone, AlertTone, AlertVariant, BreadcrumbItem, BucketProgressItem, CalendarDay, ContentStateKind, ContentStateLoad, ContentViewerType, DataTableCardRole, DataTableCellClick, DataTableColumn, DataTableDensity, DataTableItemLabel, DataTableSort, DataTableView, DividerOrientation, DocumentRowTone, DrawerRole, DrawerSide, DropdownItemTone, DropdownPanelRole, DropdownPanelVariant, EmptyStateTone, EventTimelineItem, EventTimelineTone, FileRejection, FileRejectionReason, FilterChipOption, FilterFieldChange, FilterFieldConfig, FilterFieldType, FilterValue, FolderTabOption, FooterLink, IconName, IconRotation, IconSize, InputBound, JourneyStep, JourneyStepStatus, JourneyStepperColorScheme, JourneyStepperOrientation, JourneyStepperSurface, LinkVariant, ListToolbarMode, MilestoneGroup, MilestoneItem, MilestoneTimelineLabels, ModalSize, MultiSelectOption, NoteBlockTone, OptionListOption, PageHeaderTitleSize, PaginationItem, PopoverPlacement, PortalHeaderAdminAction, PortalHeaderNavItem, ProgressSize, ProgressStatus, ProgressTone, RowAction, RowActionKind, RowActionsMode, SectionNavSubsectionData, SegmentedOption, SelectOption, SelectableCardOption, SidebarNavItemData, SkeletonPresetVariant, SkeletonVariant, SpinnerSize, StatCardDensity, StatCardTone, StatTileTone, StatTileVariant, SwitchSize, TabOption, TextAs, TextTone, TextVariant, TimeSlot, ToastAction, ToastOptions, ToastPromiseMessages, ToastState, ToastTone, TooltipPosition, ViewToggleOption };
@@ -306,6 +306,7 @@ interface MapMarker {
306
306
  * <kf-map label="Ubicación del vehículo y del destino" [markers]="markers()" />
307
307
  * <kf-map size="sm" variant="preview" label="Vista previa de la ruta" [route]="route()" />
308
308
  * <kf-map label="Flota completa" [markers]="fleet()" [controls]="['zoom', 'recenter', 'cluster', 'follow']" />
309
+ * <kf-map label="Flota completa" [markers]="fleet()" [clusterAbove]="50" />
309
310
  */
310
311
  declare class MapView {
311
312
  readonly size: _angular_core.InputSignal<MapSize>;
@@ -329,6 +330,15 @@ declare class MapView {
329
330
  readonly theme: _angular_core.InputSignal<MapTheme>;
330
331
  /** Controles flotantes a mostrar — ver JSDoc de la clase y `MapControls.ts`. */
331
332
  readonly controls: _angular_core.InputSignal<MapControlKind[]>;
333
+ /**
334
+ * Umbral de agrupado automático: con `markers().length > clusterAbove` el
335
+ * mapa entra en modo cluster (el mismo que activa el control `cluster`),
336
+ * en vez de apilar cientos de pins y de paradas de Tab. `null` (default)
337
+ * conserva el comportamiento de siempre: nunca agrupa solo. Con el control
338
+ * `cluster` visible, el botón sigue mandando: un clic reemplaza lo
339
+ * automático hasta que se destruya el mapa.
340
+ */
341
+ readonly clusterAbove: _angular_core.InputSignal<number | null>;
332
342
  /**
333
343
  * Descripción accesible real del mapa — el `<div>` donde Mapbox renderiza
334
344
  * su canvas queda vacío de semántica para un lector de pantalla. Requerido
@@ -360,7 +370,7 @@ declare class MapView {
360
370
  protected readonly isPreview: _angular_core.Signal<boolean>;
361
371
  protected readonly containerRole: _angular_core.Signal<"img" | "region">;
362
372
  /** Ver estado de carga/error real, más abajo (`status`). */
363
- protected readonly status: _angular_core.WritableSignal<"error" | "loading" | "ready">;
373
+ protected readonly status: _angular_core.WritableSignal<"loading" | "ready" | "error">;
364
374
  /** Elección manual del consumidor final vía el control `mapStyle` — pisa
365
375
  * `theme()` de forma sostenida (no vuelve a `theme()` sola) hasta que se
366
376
  * destruya este `kf-map`; ver `resolvedTheme()`. */
@@ -369,7 +379,10 @@ declare class MapView {
369
379
  /** Igual que `resolvedTheme()` pero público para el template — lo necesita
370
380
  * `MapThemePicker` (vía `MapControls`) para marcar la opción activa. */
371
381
  protected readonly activeTheme: _angular_core.Signal<MapTheme>;
372
- protected readonly clusterActive: _angular_core.WritableSignal<boolean>;
382
+ /** Elección manual del botón `cluster`; `null` = sin elegir, manda `clusterAbove`. */
383
+ private readonly clusterChoice;
384
+ private readonly clusterAutomatic;
385
+ protected readonly clusterActive: _angular_core.Signal<boolean>;
373
386
  protected readonly followActive: _angular_core.WritableSignal<boolean>;
374
387
  protected readonly hasLiveMarker: _angular_core.Signal<boolean>;
375
388
  protected readonly isFullscreen: _angular_core.WritableSignal<boolean>;
@@ -624,7 +637,7 @@ declare class MapView {
624
637
  protected handleThemeSelect(theme: MapTheme): void;
625
638
  protected handleFullscreenToggle(): void;
626
639
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<MapView, never>;
627
- 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>;
640
+ 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; }; "clusterAbove": { "alias": "clusterAbove"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": true; "isSignal": true; }; }, { "markerSelect": "markerSelect"; "markerAction": "markerAction"; }, never, never, true, never>;
628
641
  }
629
642
 
630
643
  export { MAP_CONFIG, MapControls, MapView, STYLE_BY_THEME, THEME_LABEL, THEME_ORDER, provideMapConfig };