@kaufmann-lab/ui 0.1.1 → 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/README.md CHANGED
@@ -1,25 +1,34 @@
1
1
  # @kaufmann-lab/ui
2
2
 
3
- Shared Angular component library for the Kaufmann frontends. Consumed by Portal
4
- LATAM, Portal OTD and Portal Star — including the Ionic mobile app, which is
5
- still Angular underneath.
3
+ Librería de componentes Angular compartida para los frontends de Kaufmann.
4
+ La consumen Portal LATAM, Portal OTD y Portal Star — incluida la app móvil
5
+ Ionic, que por debajo sigue siendo Angular.
6
6
 
7
- ## Requirements
7
+ > **Nota sobre este README**: por ahora solo documenta `<kf-button>` en
8
+ > detalle. La librería ya tiene muchos más componentes (`Input`, `Checkbox`,
9
+ > `Select`, `Modal`, `Card`, `Tabs`, `Toast`, y otros) — para verlos, mirá el
10
+ > código fuente en `projects/ui/src/lib/` en el
11
+ > [repositorio](https://github.com/kaufmann-lab/kaufmann-ui-angular) hasta
12
+ > que esta tabla se complete. Ver también el
13
+ > [CHANGELOG](https://github.com/kaufmann-lab/kaufmann-ui-angular/blob/main/CHANGELOG.md)
14
+ > para el detalle de qué cambió en cada versión publicada.
8
15
 
9
- Angular 22 and TypeScript ~6.0.3. Angular is a peer dependency, so the library
10
- uses whatever Angular version the host application already has installed. All
11
- consumers must be on the same Angular major.
16
+ ## Requisitos
12
17
 
13
- ## Install
18
+ Angular 22 y TypeScript ~6.0.3. Angular es una `peerDependency`, así que la
19
+ librería usa la versión de Angular que ya tenga instalada la aplicación
20
+ anfitriona. Todos los consumidores deben estar en el mismo major de Angular.
21
+
22
+ ## Instalación
14
23
 
15
24
  ```bash
16
25
  pnpm add @kaufmann-lab/ui
17
26
  ```
18
27
 
19
- ## Usage
28
+ ## Uso
20
29
 
21
- Every component is standalone — there is no module to import. Add the component
22
- to the `imports` of whichever component uses it:
30
+ Todos los componentes son standalone — no hay ningún módulo para importar.
31
+ Agregá el componente a los `imports` de quien lo use:
23
32
 
24
33
  ```ts
25
34
  import { Component } from '@angular/core';
@@ -28,34 +37,36 @@ import { Button } from '@kaufmann-lab/ui';
28
37
  @Component({
29
38
  selector: 'app-customers',
30
39
  imports: [Button],
31
- template: `<kf-button (click)="save()">Save</kf-button>`,
40
+ template: `<kf-button (click)="save()">Guardar</kf-button>`,
32
41
  })
33
42
  export class Customers {}
34
43
  ```
35
44
 
36
- Styles ship compiled inside each component with view encapsulation. Nothing to
37
- import in `angular.json`, no global stylesheet to wire up.
45
+ Los estilos viajan compilados dentro de cada componente con view
46
+ encapsulation. No hay nada para importar en `angular.json`, ni ninguna hoja
47
+ de estilos global para conectar.
38
48
 
39
- ## Components
49
+ ## Componentes
40
50
 
41
51
  ### `<kf-button>`
42
52
 
43
- Wraps a native `<button>`, so keyboard handling, focus and form submission work
44
- as expected. `(click)` bubbles from the inner button — bind it on the host.
53
+ Envuelve un `<button>` nativo, así que el manejo de teclado, el foco y el
54
+ envío de formularios funcionan como se espera. `(click)` burbujea desde el
55
+ botón interno — bindealo en el host.
45
56
 
46
- | Input | Type | Default | Notes |
47
- | --- | --- | --- | --- |
48
- | `variant` | `'primary' \| 'ghost'` | `'primary'` | |
49
- | `disabled` | `boolean` | `false` | Bare attribute works: `<kf-button disabled>` |
50
- | `type` | `'button' \| 'submit' \| 'reset'` | `'submit'` | Matches the native default |
57
+ | Input | Tipo | Default | Notas |
58
+ | ---------- | --------------------------------- | ----------- | --------------------------------------------------- |
59
+ | `variant` | `'primary' \| 'ghost'` | `'primary'` | |
60
+ | `disabled` | `boolean` | `false` | El atributo suelto funciona: `<kf-button disabled>` |
61
+ | `type` | `'button' \| 'submit' \| 'reset'` | `'submit'` | Coincide con el default nativo |
51
62
 
52
63
  ```html
53
- <kf-button (click)="save()">Save</kf-button>
54
- <kf-button variant="ghost">Cancel</kf-button>
55
- <kf-button type="button" disabled>Unavailable</kf-button>
64
+ <kf-button (click)="save()">Guardar</kf-button>
65
+ <kf-button variant="ghost">Cancelar</kf-button>
66
+ <kf-button type="button" disabled>No disponible</kf-button>
56
67
  ```
57
68
 
58
- Theming is done through a CSS custom property on the host:
69
+ El theming se hace mediante una custom property de CSS en el host:
59
70
 
60
71
  ```css
61
72
  kf-button {
@@ -152,6 +152,12 @@ const KF_ICON_SPRITE = `<svg xmlns="http://www.w3.org/2000/svg" style="display:
152
152
  <path d="M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0" />
153
153
  <circle cx="12" cy="12" r="3" />
154
154
  </symbol>
155
+ <symbol id="kf-icon-eye-off" viewBox="0 0 24 24">
156
+ <path d="M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49" />
157
+ <path d="M14.084 14.158a3 3 0 0 1-4.242-4.242" />
158
+ <path d="M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143" />
159
+ <path d="m2 2 20 20" />
160
+ </symbol>
155
161
  <symbol id="kf-icon-file" viewBox="0 0 24 24">
156
162
  <path d="M6 22a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h8a2.4 2.4 0 0 1 1.704.706l3.588 3.588A2.4 2.4 0 0 1 20 8v12a2 2 0 0 1-2 2z" />
157
163
  <path d="M14 2v5a1 1 0 0 0 1 1h5" />
@@ -2720,6 +2726,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
2720
2726
  * <kf-input label="Correo" formControlName="email"
2721
2727
  * [invalid]="email.invalid && email.touched"
2722
2728
  * [errorMessage]="email.touched && email.hasError('required') ? 'Ingresa tu correo.' : undefined" />
2729
+ *
2730
+ * `toggleable` solo tiene efecto cuando `type="password"`: agrega un botón
2731
+ * de ojo al final del campo que alterna la visibilidad del valor tipeado
2732
+ * (cambia el `type` real del `<input>` nativo entre `password` y `text`,
2733
+ * nunca expone el valor en el DOM de otra forma). El botón es `type="button"`
2734
+ * — nunca dispara submit del form contenedor — y anuncia su acción con
2735
+ * `aria-label` dinámico y `aria-pressed`, ya que es un toggle, no un botón
2736
+ * de una sola acción.
2737
+ *
2738
+ * <kf-input type="password" label="Contraseña" toggleable formControlName="password" />
2723
2739
  */
2724
2740
  class Input {
2725
2741
  static nextId = 0;
@@ -2746,6 +2762,8 @@ class Input {
2746
2762
  ...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
2747
2763
  placeholder = input('', /* @ts-ignore */
2748
2764
  ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
2765
+ /** Agrega el botón de mostrar/ocultar — sin efecto si `type` no es `"password"`. */
2766
+ toggleable = input(false, { ...(ngDevMode ? { debugName: "toggleable" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
2749
2767
  inputId = `kf-input-${Input.nextId++}`;
2750
2768
  hintId = `${this.inputId}-hint`;
2751
2769
  errorId = `${this.inputId}-error`;
@@ -2759,6 +2777,20 @@ class Input {
2759
2777
  /** Disabled real: por input() directo o por el FormControl asociado. */
2760
2778
  isDisabled = computed(() => this.disabled() || this.disabledByForm(), /* @ts-ignore */
2761
2779
  ...(ngDevMode ? [{ debugName: "isDisabled" }] : /* istanbul ignore next */ []));
2780
+ /** Estado interno del toggle — irrelevante si `showPasswordToggle()` es false. */
2781
+ passwordVisible = signal(false, /* @ts-ignore */
2782
+ ...(ngDevMode ? [{ debugName: "passwordVisible" }] : /* istanbul ignore next */ []));
2783
+ /** El botón solo tiene sentido si el campo realmente es de contraseña. */
2784
+ showPasswordToggle = computed(() => this.toggleable() && this.type() === 'password', /* @ts-ignore */
2785
+ ...(ngDevMode ? [{ debugName: "showPasswordToggle" }] : /* istanbul ignore next */ []));
2786
+ /**
2787
+ * `type` real que recibe el `<input>` nativo: igual a `type()` salvo
2788
+ * cuando el toggle está activo y visible, donde pasa a `text` para
2789
+ * mostrar el valor en claro sin cambiar `type()` en sí (evita que un
2790
+ * consumidor que lee `type()` desde afuera vea un valor inconsistente).
2791
+ */
2792
+ effectiveType = computed(() => this.showPasswordToggle() && this.passwordVisible() ? 'text' : this.type(), /* @ts-ignore */
2793
+ ...(ngDevMode ? [{ debugName: "effectiveType" }] : /* istanbul ignore next */ []));
2762
2794
  onChange = () => { };
2763
2795
  onTouched = () => { };
2764
2796
  ngAfterViewInit() {
@@ -2798,8 +2830,16 @@ class Input {
2798
2830
  this.focused.set(false);
2799
2831
  this.onTouched();
2800
2832
  }
2833
+ /**
2834
+ * Alterna la visibilidad del valor. No dispara `onBlur`/pierde el foco:
2835
+ * el botón vive dentro del mismo `.kf-input__field`, así que un click acá
2836
+ * no debe tratarse como si el usuario hubiese abandonado el campo.
2837
+ */
2838
+ onTogglePasswordVisibility() {
2839
+ this.passwordVisible.update((visible) => !visible);
2840
+ }
2801
2841
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Input, deps: [], target: i0.ɵɵFactoryTarget.Component });
2802
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Input, isStandalone: true, selector: "kf-input", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, suffix: { classPropertyName: "suffix", publicName: "suffix", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, errorMessage: { classPropertyName: "errorMessage", publicName: "errorMessage", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
2842
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Input, isStandalone: true, selector: "kf-input", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, icon: { classPropertyName: "icon", publicName: "icon", isSignal: true, isRequired: false, transformFunction: null }, suffix: { classPropertyName: "suffix", publicName: "suffix", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, errorMessage: { classPropertyName: "errorMessage", publicName: "errorMessage", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, toggleable: { classPropertyName: "toggleable", publicName: "toggleable", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
2803
2843
  {
2804
2844
  provide: NG_VALUE_ACCESSOR,
2805
2845
  useExisting: forwardRef(() => Input),
@@ -2822,7 +2862,7 @@ class Input {
2822
2862
  #nativeInput
2823
2863
  [id]="inputId"
2824
2864
  class="kf-input__control"
2825
- [type]="type()"
2865
+ [type]="effectiveType()"
2826
2866
  [placeholder]="placeholder()"
2827
2867
  [disabled]="isDisabled()"
2828
2868
  [attr.aria-describedby]="errorMessage() ? errorId : hint() ? hintId : null"
@@ -2832,7 +2872,17 @@ class Input {
2832
2872
  (blur)="onBlur()"
2833
2873
  (focus)="focused.set(true)"
2834
2874
  />
2835
- @if (suffix()) {
2875
+ @if (showPasswordToggle()) {
2876
+ <button
2877
+ type="button"
2878
+ class="kf-input__toggle"
2879
+ [attr.aria-label]="passwordVisible() ? 'Ocultar contraseña' : 'Mostrar contraseña'"
2880
+ [attr.aria-pressed]="passwordVisible()"
2881
+ (click)="onTogglePasswordVisibility()"
2882
+ >
2883
+ <kf-icon [name]="passwordVisible() ? 'eye-off' : 'eye'" size="sm" />
2884
+ </button>
2885
+ } @else if (suffix()) {
2836
2886
  <span class="kf-input__suffix">{{ suffix() }}</span>
2837
2887
  }
2838
2888
  </div>
@@ -2841,7 +2891,7 @@ class Input {
2841
2891
  } @else if (hint()) {
2842
2892
  <span class="kf-input__hint" [id]="hintId">{{ hint() }}</span>
2843
2893
  }
2844
- `, isInline: true, styles: [":host{display:flex;flex-direction:column;gap:var(--kf-space-xs);font-family:var(--kf-font-body)}.kf-input__field{display:flex;align-items:center;gap:var(--kf-space-sm);padding:0 var(--kf-space-sm);background:var(--kf-color-surface);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);transition:background .14s,border-color .14s,box-shadow .14s}.kf-input__field--focused{background:var(--kf-color-white);border-color:var(--kf-color-primary);box-shadow:var(--kf-shadow-focus)}.kf-input__field--invalid{border-color:var(--kf-color-error)}.kf-input__field--invalid.kf-input__field--focused{box-shadow:0 0 0 3px #eb575738}.kf-input__field--disabled{opacity:.5}.kf-input__icon{color:var(--kf-color-text-muted)}.kf-input__field--invalid .kf-input__icon{color:var(--kf-color-error)}.kf-input__control{flex:1;min-width:0;border:none;outline:none;background:transparent;font:inherit;color:var(--kf-color-text);padding:var(--kf-space-sm) 0}.kf-input__control::placeholder{color:var(--kf-color-text-muted)}.kf-input__control:disabled{cursor:not-allowed}.kf-input__suffix{flex:none;color:var(--kf-color-text-muted);font-size:.875rem}.kf-input__hint{font-size:.75rem;color:var(--kf-color-text-muted)}.kf-input__error{display:flex;align-items:center;gap:var(--kf-space-xs);font-size:.75rem;font-weight:600;color:var(--kf-color-error-strong)}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "component", type: FieldLabel, selector: "kf-field-label", inputs: ["for", "required"] }, { kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2894
+ `, isInline: true, styles: [":host{display:flex;flex-direction:column;gap:var(--kf-space-xs);font-family:var(--kf-font-body)}.kf-input__field{display:flex;align-items:center;gap:var(--kf-space-sm);padding:0 var(--kf-space-sm);background:var(--kf-color-surface);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);transition:background .14s,border-color .14s,box-shadow .14s}.kf-input__field--focused{background:var(--kf-color-white);border-color:var(--kf-color-primary);box-shadow:var(--kf-shadow-focus)}.kf-input__field--invalid{border-color:var(--kf-color-error)}.kf-input__field--invalid.kf-input__field--focused{box-shadow:0 0 0 3px #eb575738}.kf-input__field--disabled{opacity:.5}.kf-input__icon{color:var(--kf-color-text-muted)}.kf-input__field--invalid .kf-input__icon{color:var(--kf-color-error)}.kf-input__control{flex:1;min-width:0;border:none;outline:none;background:transparent;font:inherit;color:var(--kf-color-text);padding:var(--kf-space-sm) 0}.kf-input__control::placeholder{color:var(--kf-color-text-muted)}.kf-input__control:disabled{cursor:not-allowed}.kf-input__suffix{flex:none;color:var(--kf-color-text-muted);font-size:.875rem}.kf-input__toggle{flex:none;display:inline-flex;align-items:center;justify-content:center;border:none;background:transparent;padding:var(--kf-space-xs);margin:0 calc(var(--kf-space-xs) * -1);color:var(--kf-color-text-muted);cursor:pointer;border-radius:var(--kf-radius-xs);transition:color .14s}.kf-input__toggle:hover{color:var(--kf-color-text)}.kf-input__toggle:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-input__hint{font-size:.75rem;color:var(--kf-color-text-muted)}.kf-input__error{display:flex;align-items:center;gap:var(--kf-space-xs);font-size:.75rem;font-weight:600;color:var(--kf-color-error-strong)}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "component", type: FieldLabel, selector: "kf-field-label", inputs: ["for", "required"] }, { kind: "component", type: Icon, selector: "kf-icon", inputs: ["name", "size"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
2845
2895
  }
2846
2896
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Input, decorators: [{
2847
2897
  type: Component,
@@ -2868,7 +2918,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
2868
2918
  #nativeInput
2869
2919
  [id]="inputId"
2870
2920
  class="kf-input__control"
2871
- [type]="type()"
2921
+ [type]="effectiveType()"
2872
2922
  [placeholder]="placeholder()"
2873
2923
  [disabled]="isDisabled()"
2874
2924
  [attr.aria-describedby]="errorMessage() ? errorId : hint() ? hintId : null"
@@ -2878,7 +2928,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
2878
2928
  (blur)="onBlur()"
2879
2929
  (focus)="focused.set(true)"
2880
2930
  />
2881
- @if (suffix()) {
2931
+ @if (showPasswordToggle()) {
2932
+ <button
2933
+ type="button"
2934
+ class="kf-input__toggle"
2935
+ [attr.aria-label]="passwordVisible() ? 'Ocultar contraseña' : 'Mostrar contraseña'"
2936
+ [attr.aria-pressed]="passwordVisible()"
2937
+ (click)="onTogglePasswordVisibility()"
2938
+ >
2939
+ <kf-icon [name]="passwordVisible() ? 'eye-off' : 'eye'" size="sm" />
2940
+ </button>
2941
+ } @else if (suffix()) {
2882
2942
  <span class="kf-input__suffix">{{ suffix() }}</span>
2883
2943
  }
2884
2944
  </div>
@@ -2887,8 +2947,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
2887
2947
  } @else if (hint()) {
2888
2948
  <span class="kf-input__hint" [id]="hintId">{{ hint() }}</span>
2889
2949
  }
2890
- `, styles: [":host{display:flex;flex-direction:column;gap:var(--kf-space-xs);font-family:var(--kf-font-body)}.kf-input__field{display:flex;align-items:center;gap:var(--kf-space-sm);padding:0 var(--kf-space-sm);background:var(--kf-color-surface);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);transition:background .14s,border-color .14s,box-shadow .14s}.kf-input__field--focused{background:var(--kf-color-white);border-color:var(--kf-color-primary);box-shadow:var(--kf-shadow-focus)}.kf-input__field--invalid{border-color:var(--kf-color-error)}.kf-input__field--invalid.kf-input__field--focused{box-shadow:0 0 0 3px #eb575738}.kf-input__field--disabled{opacity:.5}.kf-input__icon{color:var(--kf-color-text-muted)}.kf-input__field--invalid .kf-input__icon{color:var(--kf-color-error)}.kf-input__control{flex:1;min-width:0;border:none;outline:none;background:transparent;font:inherit;color:var(--kf-color-text);padding:var(--kf-space-sm) 0}.kf-input__control::placeholder{color:var(--kf-color-text-muted)}.kf-input__control:disabled{cursor:not-allowed}.kf-input__suffix{flex:none;color:var(--kf-color-text-muted);font-size:.875rem}.kf-input__hint{font-size:.75rem;color:var(--kf-color-text-muted)}.kf-input__error{display:flex;align-items:center;gap:var(--kf-space-xs);font-size:.75rem;font-weight:600;color:var(--kf-color-error-strong)}\n"] }]
2891
- }], propDecorators: { nativeInput: [{ type: i0.ViewChild, args: ['nativeInput', { isSignal: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], suffix: [{ type: i0.Input, args: [{ isSignal: true, alias: "suffix", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], errorMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMessage", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }] } });
2950
+ `, styles: [":host{display:flex;flex-direction:column;gap:var(--kf-space-xs);font-family:var(--kf-font-body)}.kf-input__field{display:flex;align-items:center;gap:var(--kf-space-sm);padding:0 var(--kf-space-sm);background:var(--kf-color-surface);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);transition:background .14s,border-color .14s,box-shadow .14s}.kf-input__field--focused{background:var(--kf-color-white);border-color:var(--kf-color-primary);box-shadow:var(--kf-shadow-focus)}.kf-input__field--invalid{border-color:var(--kf-color-error)}.kf-input__field--invalid.kf-input__field--focused{box-shadow:0 0 0 3px #eb575738}.kf-input__field--disabled{opacity:.5}.kf-input__icon{color:var(--kf-color-text-muted)}.kf-input__field--invalid .kf-input__icon{color:var(--kf-color-error)}.kf-input__control{flex:1;min-width:0;border:none;outline:none;background:transparent;font:inherit;color:var(--kf-color-text);padding:var(--kf-space-sm) 0}.kf-input__control::placeholder{color:var(--kf-color-text-muted)}.kf-input__control:disabled{cursor:not-allowed}.kf-input__suffix{flex:none;color:var(--kf-color-text-muted);font-size:.875rem}.kf-input__toggle{flex:none;display:inline-flex;align-items:center;justify-content:center;border:none;background:transparent;padding:var(--kf-space-xs);margin:0 calc(var(--kf-space-xs) * -1);color:var(--kf-color-text-muted);cursor:pointer;border-radius:var(--kf-radius-xs);transition:color .14s}.kf-input__toggle:hover{color:var(--kf-color-text)}.kf-input__toggle:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-input__hint{font-size:.75rem;color:var(--kf-color-text-muted)}.kf-input__error{display:flex;align-items:center;gap:var(--kf-space-xs);font-size:.75rem;font-weight:600;color:var(--kf-color-error-strong)}\n"] }]
2951
+ }], propDecorators: { nativeInput: [{ type: i0.ViewChild, args: ['nativeInput', { isSignal: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], icon: [{ type: i0.Input, args: [{ isSignal: true, alias: "icon", required: false }] }], suffix: [{ type: i0.Input, args: [{ isSignal: true, alias: "suffix", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], errorMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorMessage", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], toggleable: [{ type: i0.Input, args: [{ isSignal: true, alias: "toggleable", required: false }] }] } });
2892
2952
 
2893
2953
  /**
2894
2954
  * Card de estadística/KPI: label, valor grande, ícono opcional en la esquina