@kaufmann-lab/ui 0.1.26 → 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.26",
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",
@@ -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. */
@@ -1438,7 +1458,7 @@ declare class SkeletonPreset {
1438
1458
  protected readonly items: _angular_core.Signal<number[]>;
1439
1459
  /** Columnas de la tabla sintética (encabezados en blanco: lo real aún no llegó). */
1440
1460
  protected readonly tableColumns: _angular_core.Signal<DataTableColumn[]>;
1441
- protected readonly formColumns: _angular_core.Signal<1 | 2>;
1461
+ protected readonly formColumns: _angular_core.Signal<2 | 1>;
1442
1462
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<SkeletonPreset, never>;
1443
1463
  static ɵcmp: _angular_core.ɵɵComponentDeclaration<SkeletonPreset, "kf-skeleton-preset", never, { "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "count": { "alias": "count"; "required": false; "isSignal": true; }; "columns": { "alias": "columns"; "required": false; "isSignal": true; }; "label": { "alias": "label"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
1444
1464
  }
@@ -4341,6 +4361,12 @@ declare class Select {
4341
4361
  readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
4342
4362
  /** Borde en rojo y `aria-invalid` en el trigger. Ver `errorMessage` para el mensaje asociado — mismo contrato que `kf-input`. */
4343
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>;
4344
4370
  /** Mensaje de error con `role="alert"`, debajo del trigger. */
4345
4371
  readonly errorMessage: _angular_core.InputSignal<string | undefined>;
4346
4372
  readonly variant: _angular_core.InputSignal<"field" | "compact">;
@@ -4501,7 +4527,7 @@ declare class Select {
4501
4527
  private scrollActiveIntoView;
4502
4528
  selectActive(): void;
4503
4529
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<Select, never>;
4504
- 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; }; "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>;
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>;
4505
4531
  }
4506
4532
 
4507
4533
  interface SelectableCardOption {
@@ -4629,8 +4655,13 @@ declare class SidebarShell {
4629
4655
  * muestra todo expandido.
4630
4656
  */
4631
4657
  readonly itemsCollapsed: _angular_core.Signal<boolean>;
4632
- /** Cierra el panel mobile con Escape — mismo criterio que `Dropdown`/`Modal`/`Drawer`. */
4633
- 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;
4634
4665
  protected onNavItemClick(event: Event, item: SidebarNavItemData): void;
4635
4666
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<SidebarShell, never>;
4636
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>;
@@ -4811,6 +4842,11 @@ declare class StatTile {
4811
4842
  readonly tone: _angular_core.InputSignal<StatTileTone>;
4812
4843
  /** Tercera línea bajo el valor. Vacía cae en `notePlaceholder`. */
4813
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>;
4814
4850
  /** Qué dice la tercera línea cuando no hay nota. Cada consumidor debería
4815
4851
  * pasar el motivo real de SU dato ausente, no un texto genérico. */
4816
4852
  readonly notePlaceholder: _angular_core.InputSignal<string | undefined>;
@@ -4858,9 +4894,11 @@ declare class StatTile {
4858
4894
  */
4859
4895
  protected onKeydown(event: KeyboardEvent): void;
4860
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>;
4861
4899
  protected readonly noteIsPlaceholder: _angular_core.Signal<boolean>;
4862
4900
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<StatTile, never>;
4863
- 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; }; "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>;
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>;
4864
4902
  }
4865
4903
 
4866
4904
  /**
@@ -4954,12 +4992,19 @@ type SwitchSize = 'sm' | 'md';
4954
4992
  * switch hace que el valor llegue dos veces al consumidor: usar una sola de
4955
4993
  * las dos vías, o ignorar en el handler el eco que no cambia el valor.
4956
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
+ *
4957
5000
  * El color sale de roles (`--kf-color-primary` encendido,
4958
5001
  * `--kf-color-border-control` apagado) y el foco de `--kf-shadow-focus`; no
4959
5002
  * recibe colores.
4960
5003
  *
4961
5004
  * <kf-switch label="Fijar indicadores" [(checked)]="pinned" />
4962
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>
4963
5008
  */
4964
5009
  declare class Switch implements ControlValueAccessor {
4965
5010
  /** Nombre accesible y texto visible (salvo con `hideLabel`). */
@@ -4968,6 +5013,8 @@ declare class Switch implements ControlValueAccessor {
4968
5013
  readonly hideLabel: _angular_core.InputSignalWithTransform<boolean, unknown>;
4969
5014
  readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
4970
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>;
4971
5018
  /** Estado encendido, two-way: `[(checked)]="pinned"`. */
4972
5019
  readonly checked: _angular_core.ModelSignal<boolean>;
4973
5020
  private readonly disabledByForm;
@@ -4981,7 +5028,7 @@ declare class Switch implements ControlValueAccessor {
4981
5028
  registerOnTouched(fn: () => void): void;
4982
5029
  setDisabledState(isDisabled: boolean): void;
4983
5030
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<Switch, never>;
4984
- 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; }; "checked": { "alias": "checked"; "required": false; "isSignal": true; }; }, { "checked": "checkedChange"; }, never, never, true, 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>;
4985
5032
  }
4986
5033
 
4987
5034
  interface TabOption {
@@ -5073,6 +5120,16 @@ declare class FolderTabs {
5073
5120
  *
5074
5121
  * <kf-textarea label="Detalle" formControlName="detail" [maxRows]="12" />
5075
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
+ *
5076
5133
  * `invalid` + `errorMessage` y `readonly`/`disabled` siguen el mismo
5077
5134
  * contrato que `kf-input` — ver su JSDoc para la diferencia entre ambos
5078
5135
  * estados.
@@ -5099,15 +5156,29 @@ declare class Textarea implements ControlValueAccessor, AfterViewInit {
5099
5156
  readonly minRows: _angular_core.InputSignal<number>;
5100
5157
  /** Filas máximas antes de que el contenido pase a scroll interno en vez de seguir creciendo. */
5101
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>;
5102
5163
  protected readonly textareaId: string;
5103
5164
  protected readonly hintId: string;
5104
5165
  protected readonly errorId: string;
5166
+ protected readonly counterId: string;
5105
5167
  protected readonly value: _angular_core.WritableSignal<string>;
5106
5168
  protected readonly focused: _angular_core.WritableSignal<boolean>;
5107
5169
  /** Deshabilitado por Reactive Forms (formControl.disable()). */
5108
5170
  private readonly disabledByForm;
5109
5171
  /** Disabled real: por input() directo o por el FormControl asociado. */
5110
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>;
5111
5182
  private onChange;
5112
5183
  private onTouched;
5113
5184
  ngAfterViewInit(): void;
@@ -5126,7 +5197,7 @@ declare class Textarea implements ControlValueAccessor, AfterViewInit {
5126
5197
  */
5127
5198
  private adjustHeight;
5128
5199
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<Textarea, never>;
5129
- 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>;
5130
5201
  }
5131
5202
 
5132
5203
  /**
@@ -5466,4 +5537,4 @@ declare class ViewToggle {
5466
5537
  }
5467
5538
 
5468
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 };
5469
- 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, 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 };
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 };