@kaufmann-lab/ui 0.1.0 → 0.1.2

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.0",
3
+ "version": "0.1.2",
4
4
  "description": "Shared Angular component library for the Kaufmann frontends",
5
5
  "license": "MIT",
6
6
  "author": "Kaufmann Lab",
@@ -31,8 +31,15 @@
31
31
  */
32
32
  --kf-color-tertiary-deep: #003252;
33
33
 
34
- --kf-color-secondary: #e6750c;
35
- --kf-color-secondary-hover: #c4650a;
34
+ /*
35
+ * Oscurecido desde el naranja de marca original (#e6750c) — con texto
36
+ * blanco (kf-button variant="secondary") el tono original daba 3.04:1,
37
+ * por debajo del mínimo WCAG AA de 4.5:1 para texto normal. Este valor
38
+ * da 4.56:1; `-hover` sube a 5.42:1. `-strong` (pensado para texto sobre
39
+ * fondo claro, no fondo de botón) no se tocó: ya cumplía de sobra.
40
+ */
41
+ --kf-color-secondary: #b85d09;
42
+ --kf-color-secondary-hover: #a55408;
36
43
  --kf-color-secondary-soft: #fff0e3;
37
44
  --kf-color-secondary-strong: #954e08;
38
45
 
@@ -45,10 +52,16 @@
45
52
  --kf-color-warning-soft: #fff8db;
46
53
  --kf-color-warning-strong: #8a6a00;
47
54
 
48
- --kf-color-error: #eb5757;
55
+ /*
56
+ * Oscurecido desde el rojo original (#eb5757) — con texto blanco
57
+ * (kf-button variant="danger") el tono original daba 3.48:1, por debajo
58
+ * del mínimo WCAG AA de 4.5:1. Este valor da 5.15:1; `-hover` sube a
59
+ * 6.09:1. `-strong` no se tocó: ya cumplía de sobra (7.02:1).
60
+ */
61
+ --kf-color-error: #bc4545;
49
62
  /* Usado también como estado hover/active del botón `danger` — mismo rojo
50
63
  que "error", ver Button.ts (el PDF define un único rojo semántico). */
51
- --kf-color-error-hover: #d64545;
64
+ --kf-color-error-hover: #a93e3e;
52
65
  --kf-color-error-soft: #fdecec;
53
66
  --kf-color-error-strong: #a42d2d;
54
67
 
@@ -19,7 +19,10 @@
19
19
  --kf-color-primary-active: #000000;
20
20
  --kf-color-primary-soft: #f1f4f9;
21
21
 
22
- --kf-color-secondary: #c47f00;
23
- --kf-color-secondary-hover: #a66900;
22
+ /* Oscurecido desde #c47f00 (3.29:1 con texto blanco) para cumplir WCAG AA
23
+ (4.5:1) en kf-button variant="secondary". Mismo criterio que divemotor,
24
+ ver base.css. */
25
+ --kf-color-secondary: #9c6500;
26
+ --kf-color-secondary-hover: #8d5900;
24
27
  --kf-color-secondary-soft: #fdf3e0;
25
28
  }
@@ -19,7 +19,9 @@
19
19
  --kf-color-primary-active: #004f9e;
20
20
  --kf-color-primary-soft: #e8f2ff;
21
21
 
22
- --kf-color-secondary: #e6750c;
23
- --kf-color-secondary-hover: #c4650a;
22
+ /* Ver el comentario del mismo cambio en base.css: oscurecido para cumplir
23
+ WCAG AA (4.5:1) con texto blanco en kf-button variant="secondary". */
24
+ --kf-color-secondary: #b85d09;
25
+ --kf-color-secondary-hover: #a55408;
24
26
  --kf-color-secondary-soft: #fff0e3;
25
27
  }
@@ -2,8 +2,14 @@ import * as _angular_core from '@angular/core';
2
2
  import { AfterContentInit, TemplateRef, AfterViewInit } from '@angular/core';
3
3
  import { ControlValueAccessor } from '@angular/forms';
4
4
 
5
- /** Nombre de cada uno de los 59 íconos del design system (página 5). */
6
- type IconName = 'activity' | 'alert-circle' | 'alert-octagon' | 'alert-triangle' | 'award' | 'bar-chart' | 'bell' | 'bus' | 'calculator' | 'calendar' | 'car' | 'cart' | 'check' | 'check-circle' | 'check-square' | 'chevron-down' | 'chevron-left' | 'chevron-left-bar' | 'chevron-right' | 'clipboard' | 'clock' | 'credit-card' | 'download' | 'edit' | 'external-link' | 'eye' | 'file' | 'filter' | 'gauge' | 'gift' | 'grid' | 'history' | 'inbox' | 'layers' | 'list' | 'map' | 'map-pin' | 'menu' | 'mic' | 'mic-off' | 'more-vertical' | 'pause' | 'phone' | 'play' | 'plus' | 'search' | 'settings' | 'shield-check' | 'sort' | 'star' | 'ticket' | 'tool' | 'trash' | 'truck' | 'upload-cloud' | 'user' | 'user-search' | 'video' | 'x';
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
+ type IconName = 'activity' | 'alert-circle' | 'alert-octagon' | 'alert-triangle' | 'award' | 'bar-chart' | 'bell' | 'bus' | 'calculator' | 'calendar' | 'car' | 'cart' | 'check' | 'check-circle' | 'check-square' | 'chevron-down' | 'chevron-left' | 'chevron-left-bar' | 'chevron-right' | 'clipboard' | 'clock' | 'credit-card' | 'download' | 'edit' | 'external-link' | 'eye' | 'eye-off' | 'file' | 'filter' | 'gauge' | 'gift' | 'grid' | 'history' | 'inbox' | 'layers' | 'list' | 'map' | 'map-pin' | 'menu' | 'mic' | 'mic-off' | 'more-vertical' | 'pause' | 'phone' | 'play' | 'plus' | 'search' | 'settings' | 'shield-check' | 'sort' | 'star' | 'ticket' | 'tool' | 'trash' | 'truck' | 'upload-cloud' | 'user' | 'user-search' | 'video' | 'x';
7
13
  type IconSize = 'sm' | 'md' | 'lg';
8
14
  /**
9
15
  * Ícono de trazo del design system — 59 glyphs (página 5), grilla 24×24,
@@ -323,15 +329,22 @@ declare class Carousel implements AfterContentInit {
323
329
  * forms) queda en su valor por defecto — igual que `disabled` combina el
324
330
  * input signal con `setDisabledState` en el resto de la librería.
325
331
  *
332
+ * `invalid` pinta el recuadro en rojo y setea `aria-invalid` en el `<input>`
333
+ * nativo — mismo mecanismo que `Input.invalid`, para el caso típico de un
334
+ * checkbox obligatorio (ej. "Acepto términos y condiciones") sin marcar.
335
+ *
326
336
  * <kf-checkbox label="Aceptar términos" [(checked)]="accepted" />
327
337
  * <kf-checkbox label="Seleccionar todo" [indeterminate]="isPartial()" />
328
338
  * <kf-checkbox label="Notificaciones" formControlName="notifications" />
339
+ * <kf-checkbox label="Acepto los términos y condiciones" [invalid]="submitted() && !accepted()" />
329
340
  */
330
341
  declare class Checkbox implements ControlValueAccessor {
331
342
  readonly label: _angular_core.InputSignal<string | undefined>;
332
343
  readonly checked: _angular_core.InputSignalWithTransform<boolean, unknown>;
333
344
  readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
334
345
  readonly indeterminate: _angular_core.InputSignalWithTransform<boolean, unknown>;
346
+ /** Recuadro en rojo + aria-invalid — ej. checkbox obligatorio sin marcar. */
347
+ readonly invalid: _angular_core.InputSignalWithTransform<boolean, unknown>;
335
348
  readonly checkedChange: _angular_core.OutputEmitterRef<boolean>;
336
349
  private readonly inputRef;
337
350
  /** Callbacks registrados por Forms; no-op hasta que registerOnChange/Touched las reemplacen. */
@@ -345,7 +358,7 @@ declare class Checkbox implements ControlValueAccessor {
345
358
  registerOnTouched(fn: () => void): void;
346
359
  setDisabledState?(isDisabled: boolean): void;
347
360
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<Checkbox, never>;
348
- 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; }; }, { "checkedChange": "checkedChange"; }, never, never, true, never>;
361
+ 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>;
349
362
  }
350
363
 
351
364
  /**
@@ -666,13 +679,21 @@ declare class Field {
666
679
  * No reemplaza al `<label>` nativo — lo envuelve, para no perder la
667
680
  * asociación de accesibilidad `for`/`id` con el control real.
668
681
  *
682
+ * `required` agrega un asterisco visual en el tono de error, separado del
683
+ * texto por un espacio y marcado `aria-hidden` — el estado obligatorio real
684
+ * para lectores de pantalla lo comunica el propio control (`aria-required`
685
+ * / `required` en el `<input>`/`<select>` asociado), no este label.
686
+ *
669
687
  * <kf-field-label [for]="inputId">Patente o VIN</kf-field-label>
688
+ * <kf-field-label [for]="inputId" required>Correo electrónico</kf-field-label>
670
689
  */
671
690
  declare class FieldLabel {
672
691
  /** `id` del control asociado — se refleja como `for` en el `<label>` interno. */
673
692
  readonly for: _angular_core.InputSignal<string | undefined>;
693
+ /** Muestra un asterisco visual indicando que el campo es obligatorio. */
694
+ readonly required: _angular_core.InputSignalWithTransform<boolean, unknown>;
674
695
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<FieldLabel, never>;
675
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<FieldLabel, "kf-field-label", never, { "for": { "alias": "for"; "required": false; "isSignal": true; }; }, {}, never, ["*"], true, never>;
696
+ 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>;
676
697
  }
677
698
 
678
699
  /** Forma normalizada interna de cada opción, ya resuelta a value/label. */
@@ -754,30 +775,67 @@ declare class FolderTabs {
754
775
  * <kf-input label="Placa" icon="car" placeholder="ABC-123" hint="Formato: 3 letras + 3 números" />
755
776
  * <input kf-input formControlName="email" type="email" />
756
777
  *
757
- * Nota sobre "icon": todavía no existe un componente Icon / sprite SVG en esta
758
- * librería. El prop se acepta igual para no romper el contrato de la spec,
759
- * pero por ahora solo reserva el espacio visual (placeholder vacío) — no
760
- * renderiza ningún glyph real.
778
+ * `invalid` + `errorMessage` cubren el estado de error: con `invalid` el
779
+ * borde pasa a rojo (`--kf-color-error`) y se setea `aria-invalid="true"` en
780
+ * el `<input>` nativo; `errorMessage` reemplaza el `hint` neutro por un
781
+ * mensaje en rojo con `role="alert"`, para que un lector de pantalla lo
782
+ * anuncie apenas aparece (a diferencia de `hint`, que es solo informativo y
783
+ * nunca interrumpe). Si se pasan ambos, `errorMessage` tiene prioridad
784
+ * visual sobre `hint` — no tiene sentido mostrar los dos a la vez.
785
+ *
786
+ * <kf-input label="Correo" formControlName="email"
787
+ * [invalid]="email.invalid && email.touched"
788
+ * [errorMessage]="email.touched && email.hasError('required') ? 'Ingresa tu correo.' : undefined" />
789
+ *
790
+ * `toggleable` solo tiene efecto cuando `type="password"`: agrega un botón
791
+ * de ojo al final del campo que alterna la visibilidad del valor tipeado
792
+ * (cambia el `type` real del `<input>` nativo entre `password` y `text`,
793
+ * nunca expone el valor en el DOM de otra forma). El botón es `type="button"`
794
+ * — nunca dispara submit del form contenedor — y anuncia su acción con
795
+ * `aria-label` dinámico y `aria-pressed`, ya que es un toggle, no un botón
796
+ * de una sola acción.
797
+ *
798
+ * <kf-input type="password" label="Contraseña" toggleable formControlName="password" />
761
799
  */
762
800
  declare class Input implements ControlValueAccessor, AfterViewInit {
763
801
  private static nextId;
764
802
  private readonly nativeInput;
765
803
  readonly label: _angular_core.InputSignal<string | undefined>;
766
- /** Nombre de ícono líder. Ver nota de clase: sin componente Icon todavía. */
767
- readonly icon: _angular_core.InputSignal<string | undefined>;
804
+ /** Marca el label con un asterisco visual — no aplica `required` al `<input>` nativo por sí solo. */
805
+ readonly required: _angular_core.InputSignalWithTransform<boolean, unknown>;
806
+ /** Ícono líder, renderizado vía `kf-icon`. */
807
+ readonly icon: _angular_core.InputSignal<IconName | undefined>;
768
808
  readonly suffix: _angular_core.InputSignal<string | undefined>;
769
809
  readonly hint: _angular_core.InputSignal<string | undefined>;
810
+ /** Borde y aria-invalid en rojo. Ver `errorMessage` para el mensaje asociado. */
811
+ readonly invalid: _angular_core.InputSignalWithTransform<boolean, unknown>;
812
+ /** Mensaje de error con `role="alert"` — reemplaza a `hint` mientras esté presente. */
813
+ readonly errorMessage: _angular_core.InputSignal<string | undefined>;
770
814
  readonly disabled: _angular_core.InputSignalWithTransform<boolean, unknown>;
771
815
  readonly type: _angular_core.InputSignal<string>;
772
816
  readonly placeholder: _angular_core.InputSignal<string>;
817
+ /** Agrega el botón de mostrar/ocultar — sin efecto si `type` no es `"password"`. */
818
+ readonly toggleable: _angular_core.InputSignalWithTransform<boolean, unknown>;
773
819
  protected readonly inputId: string;
774
820
  protected readonly hintId: string;
821
+ protected readonly errorId: string;
775
822
  protected readonly value: _angular_core.WritableSignal<string>;
776
823
  protected readonly focused: _angular_core.WritableSignal<boolean>;
777
824
  /** Deshabilitado por Reactive Forms (formControl.disable()). */
778
825
  private readonly disabledByForm;
779
826
  /** Disabled real: por input() directo o por el FormControl asociado. */
780
827
  protected readonly isDisabled: _angular_core.Signal<boolean>;
828
+ /** Estado interno del toggle — irrelevante si `showPasswordToggle()` es false. */
829
+ protected readonly passwordVisible: _angular_core.WritableSignal<boolean>;
830
+ /** El botón solo tiene sentido si el campo realmente es de contraseña. */
831
+ protected readonly showPasswordToggle: _angular_core.Signal<boolean>;
832
+ /**
833
+ * `type` real que recibe el `<input>` nativo: igual a `type()` salvo
834
+ * cuando el toggle está activo y visible, donde pasa a `text` para
835
+ * mostrar el valor en claro sin cambiar `type()` en sí (evita que un
836
+ * consumidor que lee `type()` desde afuera vea un valor inconsistente).
837
+ */
838
+ protected readonly effectiveType: _angular_core.Signal<string>;
781
839
  private onChange;
782
840
  private onTouched;
783
841
  ngAfterViewInit(): void;
@@ -787,8 +845,14 @@ declare class Input implements ControlValueAccessor, AfterViewInit {
787
845
  setDisabledState(isDisabled: boolean): void;
788
846
  protected onInput(rawValue: string): void;
789
847
  protected onBlur(): void;
848
+ /**
849
+ * Alterna la visibilidad del valor. No dispara `onBlur`/pierde el foco:
850
+ * el botón vive dentro del mismo `.kf-input__field`, así que un click acá
851
+ * no debe tratarse como si el usuario hubiese abandonado el campo.
852
+ */
853
+ protected onTogglePasswordVisibility(): void;
790
854
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<Input, never>;
791
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<Input, "kf-input", never, { "label": { "alias": "label"; "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; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "type": { "alias": "type"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
855
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<Input, "kf-input", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "icon": { "alias": "icon"; "required": false; "isSignal": true; }; "suffix": { "alias": "suffix"; "required": false; "isSignal": true; }; "hint": { "alias": "hint"; "required": false; "isSignal": true; }; "invalid": { "alias": "invalid"; "required": false; "isSignal": true; }; "errorMessage": { "alias": "errorMessage"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "type": { "alias": "type"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "toggleable": { "alias": "toggleable"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
792
856
  }
793
857
 
794
858
  /**