@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/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" />
@@ -1152,9 +1158,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
1152
1158
  * forms) queda en su valor por defecto — igual que `disabled` combina el
1153
1159
  * input signal con `setDisabledState` en el resto de la librería.
1154
1160
  *
1161
+ * `invalid` pinta el recuadro en rojo y setea `aria-invalid` en el `<input>`
1162
+ * nativo — mismo mecanismo que `Input.invalid`, para el caso típico de un
1163
+ * checkbox obligatorio (ej. "Acepto términos y condiciones") sin marcar.
1164
+ *
1155
1165
  * <kf-checkbox label="Aceptar términos" [(checked)]="accepted" />
1156
1166
  * <kf-checkbox label="Seleccionar todo" [indeterminate]="isPartial()" />
1157
1167
  * <kf-checkbox label="Notificaciones" formControlName="notifications" />
1168
+ * <kf-checkbox label="Acepto los términos y condiciones" [invalid]="submitted() && !accepted()" />
1158
1169
  */
1159
1170
  class Checkbox {
1160
1171
  label = input(/* @ts-ignore */
@@ -1162,6 +1173,8 @@ class Checkbox {
1162
1173
  checked = input(false, { ...(ngDevMode ? { debugName: "checked" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1163
1174
  disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1164
1175
  indeterminate = input(false, { ...(ngDevMode ? { debugName: "indeterminate" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1176
+ /** Recuadro en rojo + aria-invalid — ej. checkbox obligatorio sin marcar. */
1177
+ invalid = input(false, { ...(ngDevMode ? { debugName: "invalid" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1165
1178
  checkedChange = output();
1166
1179
  inputRef = viewChild.required('inputRef', /* @ts-ignore */
1167
1180
  ...(ngDevMode ? [{ debugName: "inputRef" }] : /* istanbul ignore next */ []));
@@ -1197,7 +1210,7 @@ class Checkbox {
1197
1210
  this.inputRef().nativeElement.disabled = isDisabled;
1198
1211
  }
1199
1212
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Checkbox, deps: [], target: i0.ɵɵFactoryTarget.Component });
1200
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Checkbox, isStandalone: true, selector: "kf-checkbox", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, indeterminate: { classPropertyName: "indeterminate", publicName: "indeterminate", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checkedChange: "checkedChange" }, providers: [
1213
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: Checkbox, isStandalone: true, selector: "kf-checkbox", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, indeterminate: { classPropertyName: "indeterminate", publicName: "indeterminate", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checkedChange: "checkedChange" }, providers: [
1201
1214
  {
1202
1215
  provide: NG_VALUE_ACCESSOR,
1203
1216
  useExisting: forwardRef(() => Checkbox),
@@ -1210,15 +1223,16 @@ class Checkbox {
1210
1223
  type="checkbox"
1211
1224
  [checked]="checked()"
1212
1225
  [disabled]="disabled()"
1226
+ [attr.aria-invalid]="invalid() ? true : null"
1213
1227
  (change)="onChange($event)"
1214
1228
  (blur)="onTouched()"
1215
1229
  />
1216
- <span class="kf-checkbox__box"></span>
1230
+ <span class="kf-checkbox__box" [class.kf-checkbox__box--invalid]="invalid()"></span>
1217
1231
  @if (label()) {
1218
1232
  <span class="kf-checkbox__label">{{ label() }}</span>
1219
1233
  }
1220
1234
  </label>
1221
- `, isInline: true, styles: [":host{display:inline-block}.kf-checkbox{display:inline-flex;align-items:center;gap:var(--kf-space-sm);cursor:pointer;font:inherit;font-family:var(--kf-font-body);color:var(--kf-color-text)}.kf-checkbox--disabled{cursor:not-allowed;color:var(--kf-color-text-muted)}.kf-checkbox input{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.kf-checkbox__box{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--kf-space-md);height:var(--kf-space-md);border:1px solid var(--kf-color-border-strong);border-radius:var(--kf-radius-xs);background:var(--kf-color-white);transition:background .14s,border-color .14s}.kf-checkbox input:checked+.kf-checkbox__box,.kf-checkbox input:indeterminate+.kf-checkbox__box{background:var(--kf-color-primary);border-color:var(--kf-color-primary)}.kf-checkbox input:checked+.kf-checkbox__box:after{content:\"\";width:4px;height:8px;border:solid var(--kf-color-white);border-width:0 2px 2px 0;transform:rotate(45deg) translate(-1px,-1px)}.kf-checkbox input:indeterminate+.kf-checkbox__box:after{content:\"\";width:8px;height:2px;background:var(--kf-color-white);border-radius:var(--kf-radius-xs)}.kf-checkbox input:focus-visible+.kf-checkbox__box{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-checkbox--disabled .kf-checkbox__box{opacity:.5}.kf-checkbox__label{-webkit-user-select:none;user-select:none}\n"] });
1235
+ `, isInline: true, styles: [":host{display:inline-block}.kf-checkbox{display:inline-flex;align-items:center;gap:var(--kf-space-sm);cursor:pointer;font:inherit;font-family:var(--kf-font-body);color:var(--kf-color-text)}.kf-checkbox--disabled{cursor:not-allowed;color:var(--kf-color-text-muted)}.kf-checkbox input{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.kf-checkbox__box{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--kf-space-md);height:var(--kf-space-md);border:1px solid var(--kf-color-border-strong);border-radius:var(--kf-radius-xs);background:var(--kf-color-white);transition:background .14s,border-color .14s}.kf-checkbox input:checked+.kf-checkbox__box,.kf-checkbox input:indeterminate+.kf-checkbox__box{background:var(--kf-color-primary);border-color:var(--kf-color-primary)}.kf-checkbox input:checked+.kf-checkbox__box:after{content:\"\";width:4px;height:8px;border:solid var(--kf-color-white);border-width:0 2px 2px 0;transform:rotate(45deg) translate(-1px,-1px)}.kf-checkbox input:indeterminate+.kf-checkbox__box:after{content:\"\";width:8px;height:2px;background:var(--kf-color-white);border-radius:var(--kf-radius-xs)}.kf-checkbox input:focus-visible+.kf-checkbox__box{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-checkbox__box--invalid{border-color:var(--kf-color-error)}.kf-checkbox--disabled .kf-checkbox__box{opacity:.5}.kf-checkbox__label{-webkit-user-select:none;user-select:none}\n"] });
1222
1236
  }
1223
1237
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Checkbox, decorators: [{
1224
1238
  type: Component,
@@ -1229,10 +1243,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
1229
1243
  type="checkbox"
1230
1244
  [checked]="checked()"
1231
1245
  [disabled]="disabled()"
1246
+ [attr.aria-invalid]="invalid() ? true : null"
1232
1247
  (change)="onChange($event)"
1233
1248
  (blur)="onTouched()"
1234
1249
  />
1235
- <span class="kf-checkbox__box"></span>
1250
+ <span class="kf-checkbox__box" [class.kf-checkbox__box--invalid]="invalid()"></span>
1236
1251
  @if (label()) {
1237
1252
  <span class="kf-checkbox__label">{{ label() }}</span>
1238
1253
  }
@@ -1243,8 +1258,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
1243
1258
  useExisting: forwardRef(() => Checkbox),
1244
1259
  multi: true,
1245
1260
  },
1246
- ], styles: [":host{display:inline-block}.kf-checkbox{display:inline-flex;align-items:center;gap:var(--kf-space-sm);cursor:pointer;font:inherit;font-family:var(--kf-font-body);color:var(--kf-color-text)}.kf-checkbox--disabled{cursor:not-allowed;color:var(--kf-color-text-muted)}.kf-checkbox input{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.kf-checkbox__box{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--kf-space-md);height:var(--kf-space-md);border:1px solid var(--kf-color-border-strong);border-radius:var(--kf-radius-xs);background:var(--kf-color-white);transition:background .14s,border-color .14s}.kf-checkbox input:checked+.kf-checkbox__box,.kf-checkbox input:indeterminate+.kf-checkbox__box{background:var(--kf-color-primary);border-color:var(--kf-color-primary)}.kf-checkbox input:checked+.kf-checkbox__box:after{content:\"\";width:4px;height:8px;border:solid var(--kf-color-white);border-width:0 2px 2px 0;transform:rotate(45deg) translate(-1px,-1px)}.kf-checkbox input:indeterminate+.kf-checkbox__box:after{content:\"\";width:8px;height:2px;background:var(--kf-color-white);border-radius:var(--kf-radius-xs)}.kf-checkbox input:focus-visible+.kf-checkbox__box{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-checkbox--disabled .kf-checkbox__box{opacity:.5}.kf-checkbox__label{-webkit-user-select:none;user-select:none}\n"] }]
1247
- }], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], indeterminate: [{ type: i0.Input, args: [{ isSignal: true, alias: "indeterminate", required: false }] }], checkedChange: [{ type: i0.Output, args: ["checkedChange"] }], inputRef: [{ type: i0.ViewChild, args: ['inputRef', { isSignal: true }] }] } });
1261
+ ], styles: [":host{display:inline-block}.kf-checkbox{display:inline-flex;align-items:center;gap:var(--kf-space-sm);cursor:pointer;font:inherit;font-family:var(--kf-font-body);color:var(--kf-color-text)}.kf-checkbox--disabled{cursor:not-allowed;color:var(--kf-color-text-muted)}.kf-checkbox input{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}.kf-checkbox__box{display:inline-flex;align-items:center;justify-content:center;flex-shrink:0;width:var(--kf-space-md);height:var(--kf-space-md);border:1px solid var(--kf-color-border-strong);border-radius:var(--kf-radius-xs);background:var(--kf-color-white);transition:background .14s,border-color .14s}.kf-checkbox input:checked+.kf-checkbox__box,.kf-checkbox input:indeterminate+.kf-checkbox__box{background:var(--kf-color-primary);border-color:var(--kf-color-primary)}.kf-checkbox input:checked+.kf-checkbox__box:after{content:\"\";width:4px;height:8px;border:solid var(--kf-color-white);border-width:0 2px 2px 0;transform:rotate(45deg) translate(-1px,-1px)}.kf-checkbox input:indeterminate+.kf-checkbox__box:after{content:\"\";width:8px;height:2px;background:var(--kf-color-white);border-radius:var(--kf-radius-xs)}.kf-checkbox input:focus-visible+.kf-checkbox__box{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-checkbox__box--invalid{border-color:var(--kf-color-error)}.kf-checkbox--disabled .kf-checkbox__box{opacity:.5}.kf-checkbox__label{-webkit-user-select:none;user-select:none}\n"] }]
1262
+ }], ctorParameters: () => [], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], indeterminate: [{ type: i0.Input, args: [{ isSignal: true, alias: "indeterminate", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], checkedChange: [{ type: i0.Output, args: ["checkedChange"] }], inputRef: [{ type: i0.ViewChild, args: ['inputRef', { isSignal: true }] }] } });
1248
1263
 
1249
1264
  /**
1250
1265
  * Chip de fondo suave con un punto líder que indica estado. Pensado
@@ -1307,19 +1322,41 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
1307
1322
  * No reemplaza al `<label>` nativo — lo envuelve, para no perder la
1308
1323
  * asociación de accesibilidad `for`/`id` con el control real.
1309
1324
  *
1325
+ * `required` agrega un asterisco visual en el tono de error, separado del
1326
+ * texto por un espacio y marcado `aria-hidden` — el estado obligatorio real
1327
+ * para lectores de pantalla lo comunica el propio control (`aria-required`
1328
+ * / `required` en el `<input>`/`<select>` asociado), no este label.
1329
+ *
1310
1330
  * <kf-field-label [for]="inputId">Patente o VIN</kf-field-label>
1331
+ * <kf-field-label [for]="inputId" required>Correo electrónico</kf-field-label>
1311
1332
  */
1312
1333
  class FieldLabel {
1313
1334
  /** `id` del control asociado — se refleja como `for` en el `<label>` interno. */
1314
1335
  for = input(/* @ts-ignore */
1315
1336
  ...(ngDevMode ? [undefined, { debugName: "for" }] : /* istanbul ignore next */ []));
1337
+ /** Muestra un asterisco visual indicando que el campo es obligatorio. */
1338
+ required = input(false, { ...(ngDevMode ? { debugName: "required" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
1316
1339
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: FieldLabel, deps: [], target: i0.ɵɵFactoryTarget.Component });
1317
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.0", type: FieldLabel, isStandalone: true, selector: "kf-field-label", inputs: { for: { classPropertyName: "for", publicName: "for", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: ` <label class="kf-field-label" [for]="for()"><ng-content /></label> `, isInline: true, styles: [":host{display:contents}.kf-field-label{font-family:var(--kf-font-title);font-size:.75rem;font-weight:600;letter-spacing:.04em;text-transform:uppercase;color:var(--kf-color-text-muted)}\n"] });
1340
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.0", type: FieldLabel, isStandalone: true, selector: "kf-field-label", inputs: { for: { classPropertyName: "for", publicName: "for", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
1341
+ <label class="kf-field-label" [for]="for()">
1342
+ <ng-content />
1343
+ @if (required()) {
1344
+ <span class="kf-field-label__required" aria-hidden="true">*</span>
1345
+ }
1346
+ </label>
1347
+ `, isInline: true, styles: [":host{display:contents}.kf-field-label{font-family:var(--kf-font-title);font-size:.75rem;font-weight:600;letter-spacing:.04em;text-transform:uppercase;color:var(--kf-color-text-muted)}.kf-field-label__required{color:var(--kf-color-error);margin-inline-start:2px}\n"] });
1318
1348
  }
1319
1349
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: FieldLabel, decorators: [{
1320
1350
  type: Component,
1321
- args: [{ selector: 'kf-field-label', template: ` <label class="kf-field-label" [for]="for()"><ng-content /></label> `, styles: [":host{display:contents}.kf-field-label{font-family:var(--kf-font-title);font-size:.75rem;font-weight:600;letter-spacing:.04em;text-transform:uppercase;color:var(--kf-color-text-muted)}\n"] }]
1322
- }], propDecorators: { for: [{ type: i0.Input, args: [{ isSignal: true, alias: "for", required: false }] }] } });
1351
+ args: [{ selector: 'kf-field-label', template: `
1352
+ <label class="kf-field-label" [for]="for()">
1353
+ <ng-content />
1354
+ @if (required()) {
1355
+ <span class="kf-field-label__required" aria-hidden="true">*</span>
1356
+ }
1357
+ </label>
1358
+ `, styles: [":host{display:contents}.kf-field-label{font-family:var(--kf-font-title);font-size:.75rem;font-weight:600;letter-spacing:.04em;text-transform:uppercase;color:var(--kf-color-text-muted)}.kf-field-label__required{color:var(--kf-color-error);margin-inline-start:2px}\n"] }]
1359
+ }], propDecorators: { for: [{ type: i0.Input, args: [{ isSignal: true, alias: "for", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }] } });
1323
1360
 
1324
1361
  let nextId$2 = 0;
1325
1362
  /**
@@ -1432,7 +1469,7 @@ class Select {
1432
1469
  }
1433
1470
  </ul>
1434
1471
  }
1435
- `, isInline: true, styles: [":host{display:flex;flex-direction:column;gap:var(--kf-space-xs);position:relative;font-family:var(--kf-font-body)}.kf-select__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);width:100%;font:inherit;color:var(--kf-color-text);padding:var(--kf-space-sm) var(--kf-space-md);background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);cursor:pointer;transition:border-color .14s,box-shadow .14s}.kf-select__trigger:hover:not(:disabled){border-color:var(--kf-color-border-strong)}.kf-select__trigger:focus-visible{outline:none;border-color:var(--kf-color-primary);box-shadow:var(--kf-shadow-focus)}.kf-select__trigger--disabled{opacity:.5;cursor:not-allowed}.kf-select__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kf-select__value--placeholder{color:var(--kf-color-text-muted)}.kf-select__caret{flex-shrink:0;width:8px;height:8px;border-right:2px solid var(--kf-color-text-muted);border-bottom:2px solid var(--kf-color-text-muted);transform:rotate(45deg);transition:transform .14s;margin-top:-4px}.kf-select__caret--open{transform:rotate(-135deg);margin-top:4px}.kf-select__panel{position:absolute;top:100%;left:0;right:0;z-index:10;margin:var(--kf-space-xs) 0 0;padding:var(--kf-space-xs) 0;list-style:none;max-height:240px;overflow-y:auto;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-md)}.kf-select__option{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-select__option:hover{background:var(--kf-color-primary-soft)}.kf-select__option--selected{background:var(--kf-color-primary-soft);color:var(--kf-color-primary);font-weight:600}.kf-select__empty{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text-muted)}\n"], dependencies: [{ kind: "component", type: FieldLabel, selector: "kf-field-label", inputs: ["for"] }] });
1472
+ `, isInline: true, styles: [":host{display:flex;flex-direction:column;gap:var(--kf-space-xs);position:relative;font-family:var(--kf-font-body)}.kf-select__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);width:100%;font:inherit;color:var(--kf-color-text);padding:var(--kf-space-sm) var(--kf-space-md);background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);cursor:pointer;transition:border-color .14s,box-shadow .14s}.kf-select__trigger:hover:not(:disabled){border-color:var(--kf-color-border-strong)}.kf-select__trigger:focus-visible{outline:none;border-color:var(--kf-color-primary);box-shadow:var(--kf-shadow-focus)}.kf-select__trigger--disabled{opacity:.5;cursor:not-allowed}.kf-select__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kf-select__value--placeholder{color:var(--kf-color-text-muted)}.kf-select__caret{flex-shrink:0;width:8px;height:8px;border-right:2px solid var(--kf-color-text-muted);border-bottom:2px solid var(--kf-color-text-muted);transform:rotate(45deg);transition:transform .14s;margin-top:-4px}.kf-select__caret--open{transform:rotate(-135deg);margin-top:4px}.kf-select__panel{position:absolute;top:100%;left:0;right:0;z-index:10;margin:var(--kf-space-xs) 0 0;padding:var(--kf-space-xs) 0;list-style:none;max-height:240px;overflow-y:auto;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-md)}.kf-select__option{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-select__option:hover{background:var(--kf-color-primary-soft)}.kf-select__option--selected{background:var(--kf-color-primary-soft);color:var(--kf-color-primary);font-weight:600}.kf-select__empty{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text-muted)}\n"], dependencies: [{ kind: "component", type: FieldLabel, selector: "kf-field-label", inputs: ["for", "required"] }] });
1436
1473
  }
1437
1474
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Select, decorators: [{
1438
1475
  type: Component,
@@ -2678,10 +2715,27 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
2678
2715
  * <kf-input label="Placa" icon="car" placeholder="ABC-123" hint="Formato: 3 letras + 3 números" />
2679
2716
  * <input kf-input formControlName="email" type="email" />
2680
2717
  *
2681
- * Nota sobre "icon": todavía no existe un componente Icon / sprite SVG en esta
2682
- * librería. El prop se acepta igual para no romper el contrato de la spec,
2683
- * pero por ahora solo reserva el espacio visual (placeholder vacío) — no
2684
- * renderiza ningún glyph real.
2718
+ * `invalid` + `errorMessage` cubren el estado de error: con `invalid` el
2719
+ * borde pasa a rojo (`--kf-color-error`) y se setea `aria-invalid="true"` en
2720
+ * el `<input>` nativo; `errorMessage` reemplaza el `hint` neutro por un
2721
+ * mensaje en rojo con `role="alert"`, para que un lector de pantalla lo
2722
+ * anuncie apenas aparece (a diferencia de `hint`, que es solo informativo y
2723
+ * nunca interrumpe). Si se pasan ambos, `errorMessage` tiene prioridad
2724
+ * visual sobre `hint` — no tiene sentido mostrar los dos a la vez.
2725
+ *
2726
+ * <kf-input label="Correo" formControlName="email"
2727
+ * [invalid]="email.invalid && email.touched"
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" />
2685
2739
  */
2686
2740
  class Input {
2687
2741
  static nextId = 0;
@@ -2689,20 +2743,30 @@ class Input {
2689
2743
  ...(ngDevMode ? [{ debugName: "nativeInput" }] : /* istanbul ignore next */ []));
2690
2744
  label = input(/* @ts-ignore */
2691
2745
  ...(ngDevMode ? [undefined, { debugName: "label" }] : /* istanbul ignore next */ []));
2692
- /** Nombre de ícono líder. Ver nota de clase: sin componente Icon todavía. */
2746
+ /** Marca el label con un asterisco visual — no aplica `required` al `<input>` nativo por sí solo. */
2747
+ required = input(false, { ...(ngDevMode ? { debugName: "required" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
2748
+ /** Ícono líder, renderizado vía `kf-icon`. */
2693
2749
  icon = input(/* @ts-ignore */
2694
2750
  ...(ngDevMode ? [undefined, { debugName: "icon" }] : /* istanbul ignore next */ []));
2695
2751
  suffix = input(/* @ts-ignore */
2696
2752
  ...(ngDevMode ? [undefined, { debugName: "suffix" }] : /* istanbul ignore next */ []));
2697
2753
  hint = input(/* @ts-ignore */
2698
2754
  ...(ngDevMode ? [undefined, { debugName: "hint" }] : /* istanbul ignore next */ []));
2755
+ /** Borde y aria-invalid en rojo. Ver `errorMessage` para el mensaje asociado. */
2756
+ invalid = input(false, { ...(ngDevMode ? { debugName: "invalid" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
2757
+ /** Mensaje de error con `role="alert"` — reemplaza a `hint` mientras esté presente. */
2758
+ errorMessage = input(/* @ts-ignore */
2759
+ ...(ngDevMode ? [undefined, { debugName: "errorMessage" }] : /* istanbul ignore next */ []));
2699
2760
  disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
2700
2761
  type = input('text', /* @ts-ignore */
2701
2762
  ...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
2702
2763
  placeholder = input('', /* @ts-ignore */
2703
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 });
2704
2767
  inputId = `kf-input-${Input.nextId++}`;
2705
2768
  hintId = `${this.inputId}-hint`;
2769
+ errorId = `${this.inputId}-error`;
2706
2770
  value = signal('', /* @ts-ignore */
2707
2771
  ...(ngDevMode ? [{ debugName: "value" }] : /* istanbul ignore next */ []));
2708
2772
  focused = signal(false, /* @ts-ignore */
@@ -2713,6 +2777,20 @@ class Input {
2713
2777
  /** Disabled real: por input() directo o por el FormControl asociado. */
2714
2778
  isDisabled = computed(() => this.disabled() || this.disabledByForm(), /* @ts-ignore */
2715
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 */ []));
2716
2794
  onChange = () => { };
2717
2795
  onTouched = () => { };
2718
2796
  ngAfterViewInit() {
@@ -2752,8 +2830,16 @@ class Input {
2752
2830
  this.focused.set(false);
2753
2831
  this.onTouched();
2754
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
+ }
2755
2841
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Input, deps: [], target: i0.ɵɵFactoryTarget.Component });
2756
- 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 }, 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 }, 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: [
2757
2843
  {
2758
2844
  provide: NG_VALUE_ACCESSOR,
2759
2845
  useExisting: forwardRef(() => Input),
@@ -2761,43 +2847,55 @@ class Input {
2761
2847
  },
2762
2848
  ], viewQueries: [{ propertyName: "nativeInput", first: true, predicate: ["nativeInput"], descendants: true, isSignal: true }], ngImport: i0, template: `
2763
2849
  @if (label()) {
2764
- <kf-field-label [for]="inputId">{{ label() }}</kf-field-label>
2850
+ <kf-field-label [for]="inputId" [required]="required()">{{ label() }}</kf-field-label>
2765
2851
  }
2766
2852
  <div
2767
2853
  class="kf-input__field"
2768
2854
  [class.kf-input__field--disabled]="isDisabled()"
2769
2855
  [class.kf-input__field--focused]="focused()"
2856
+ [class.kf-input__field--invalid]="invalid()"
2770
2857
  >
2771
- @if (icon()) {
2772
- <!-- Pendiente: falta componente Icon / sprite SVG. Se reserva el
2773
- espacio pero no se renderiza ningún glyph todavía. -->
2774
- <span class="kf-input__icon" aria-hidden="true"></span>
2858
+ @if (icon(); as iconName) {
2859
+ <kf-icon class="kf-input__icon" [name]="iconName" size="sm" />
2775
2860
  }
2776
2861
  <input
2777
2862
  #nativeInput
2778
2863
  [id]="inputId"
2779
2864
  class="kf-input__control"
2780
- [type]="type()"
2865
+ [type]="effectiveType()"
2781
2866
  [placeholder]="placeholder()"
2782
2867
  [disabled]="isDisabled()"
2783
- [attr.aria-describedby]="hint() ? hintId : null"
2868
+ [attr.aria-describedby]="errorMessage() ? errorId : hint() ? hintId : null"
2869
+ [attr.aria-invalid]="invalid() ? true : null"
2784
2870
  [value]="value()"
2785
2871
  (input)="onInput(nativeInput.value)"
2786
2872
  (blur)="onBlur()"
2787
2873
  (focus)="focused.set(true)"
2788
2874
  />
2789
- @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()) {
2790
2886
  <span class="kf-input__suffix">{{ suffix() }}</span>
2791
2887
  }
2792
2888
  </div>
2793
- @if (hint()) {
2889
+ @if (errorMessage(); as message) {
2890
+ <span class="kf-input__error" [id]="errorId" role="alert">{{ message }}</span>
2891
+ } @else if (hint()) {
2794
2892
  <span class="kf-input__hint" [id]="hintId">{{ hint() }}</span>
2795
2893
  }
2796
- `, 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--disabled{opacity:.5}.kf-input__icon{width:1rem;height:1rem;flex:none}.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)}\n"], dependencies: [{ kind: "ngmodule", type: FormsModule }, { kind: "component", type: FieldLabel, selector: "kf-field-label", inputs: ["for"] }], 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 });
2797
2895
  }
2798
2896
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: Input, decorators: [{
2799
2897
  type: Component,
2800
- args: [{ selector: 'kf-input', changeDetection: ChangeDetectionStrategy.OnPush, imports: [FormsModule, FieldLabel], providers: [
2898
+ args: [{ selector: 'kf-input', changeDetection: ChangeDetectionStrategy.OnPush, imports: [FormsModule, FieldLabel, Icon], providers: [
2801
2899
  {
2802
2900
  provide: NG_VALUE_ACCESSOR,
2803
2901
  useExisting: forwardRef(() => Input),
@@ -2805,40 +2903,52 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
2805
2903
  },
2806
2904
  ], template: `
2807
2905
  @if (label()) {
2808
- <kf-field-label [for]="inputId">{{ label() }}</kf-field-label>
2906
+ <kf-field-label [for]="inputId" [required]="required()">{{ label() }}</kf-field-label>
2809
2907
  }
2810
2908
  <div
2811
2909
  class="kf-input__field"
2812
2910
  [class.kf-input__field--disabled]="isDisabled()"
2813
2911
  [class.kf-input__field--focused]="focused()"
2912
+ [class.kf-input__field--invalid]="invalid()"
2814
2913
  >
2815
- @if (icon()) {
2816
- <!-- Pendiente: falta componente Icon / sprite SVG. Se reserva el
2817
- espacio pero no se renderiza ningún glyph todavía. -->
2818
- <span class="kf-input__icon" aria-hidden="true"></span>
2914
+ @if (icon(); as iconName) {
2915
+ <kf-icon class="kf-input__icon" [name]="iconName" size="sm" />
2819
2916
  }
2820
2917
  <input
2821
2918
  #nativeInput
2822
2919
  [id]="inputId"
2823
2920
  class="kf-input__control"
2824
- [type]="type()"
2921
+ [type]="effectiveType()"
2825
2922
  [placeholder]="placeholder()"
2826
2923
  [disabled]="isDisabled()"
2827
- [attr.aria-describedby]="hint() ? hintId : null"
2924
+ [attr.aria-describedby]="errorMessage() ? errorId : hint() ? hintId : null"
2925
+ [attr.aria-invalid]="invalid() ? true : null"
2828
2926
  [value]="value()"
2829
2927
  (input)="onInput(nativeInput.value)"
2830
2928
  (blur)="onBlur()"
2831
2929
  (focus)="focused.set(true)"
2832
2930
  />
2833
- @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()) {
2834
2942
  <span class="kf-input__suffix">{{ suffix() }}</span>
2835
2943
  }
2836
2944
  </div>
2837
- @if (hint()) {
2945
+ @if (errorMessage(); as message) {
2946
+ <span class="kf-input__error" [id]="errorId" role="alert">{{ message }}</span>
2947
+ } @else if (hint()) {
2838
2948
  <span class="kf-input__hint" [id]="hintId">{{ hint() }}</span>
2839
2949
  }
2840
- `, 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--disabled{opacity:.5}.kf-input__icon{width:1rem;height:1rem;flex:none}.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)}\n"] }]
2841
- }], propDecorators: { nativeInput: [{ type: i0.ViewChild, args: ['nativeInput', { isSignal: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", 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 }] }], 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 }] }] } });
2842
2952
 
2843
2953
  /**
2844
2954
  * Card de estadística/KPI: label, valor grande, ícono opcional en la esquina
@@ -3264,7 +3374,7 @@ class MultiSelect {
3264
3374
  }
3265
3375
  </ul>
3266
3376
  }
3267
- `, isInline: true, styles: [":host{display:inline-flex;flex-direction:column;gap:var(--kf-space-xs);position:relative;font:inherit;min-width:200px}.kf-multi-select__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);width:100%;font:inherit;font-family:var(--kf-font-body);padding:var(--kf-space-sm) var(--kf-space-md);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);background:var(--kf-color-white);color:var(--kf-color-text);cursor:pointer;transition:border-color .14s,box-shadow .14s}.kf-multi-select__trigger:hover:not(:disabled){border-color:var(--kf-color-border-strong)}.kf-multi-select__trigger:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-multi-select__trigger:disabled{opacity:.5;cursor:not-allowed}.kf-multi-select__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;text-align:left}.kf-multi-select__value--placeholder{color:var(--kf-color-text-muted)}.kf-multi-select__count{flex:none;display:inline-flex;align-items:center;justify-content:center;min-width:1.25rem;height:1.25rem;padding:0 5px;border-radius:var(--kf-radius-pill);background:var(--kf-color-primary);color:var(--kf-color-white);font-size:.72rem;font-weight:700}.kf-multi-select__caret{flex-shrink:0;width:8px;height:8px;border-right:2px solid var(--kf-color-text-muted);border-bottom:2px solid var(--kf-color-text-muted);transform:rotate(45deg);transition:transform .14s;margin-top:-4px}.kf-multi-select__caret--open{transform:rotate(-135deg);margin-top:4px}.kf-multi-select__panel{position:absolute;top:100%;left:0;right:0;z-index:10;margin:var(--kf-space-xs) 0 0;padding:var(--kf-space-xs) 0;list-style:none;max-height:240px;overflow-y:auto;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-md)}.kf-multi-select__option{display:flex;align-items:center;gap:var(--kf-space-sm);padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-multi-select__option:hover{background:var(--kf-color-primary-soft)}.kf-multi-select__option--selected{color:var(--kf-color-primary);font-weight:600}.kf-multi-select__checkbox{display:inline-flex;flex-shrink:0;width:var(--kf-space-md);height:var(--kf-space-md);border:1px solid var(--kf-color-border-strong);border-radius:var(--kf-radius-xs);background:var(--kf-color-white);transition:background .14s,border-color .14s;position:relative}.kf-multi-select__checkbox--checked{background:var(--kf-color-primary);border-color:var(--kf-color-primary)}.kf-multi-select__checkbox--checked:after{content:\"\";position:absolute;left:4px;top:1px;width:4px;height:8px;border:solid var(--kf-color-white);border-width:0 2px 2px 0;transform:rotate(45deg)}.kf-multi-select__checkbox--indeterminate{background:var(--kf-color-primary);border-color:var(--kf-color-primary)}.kf-multi-select__checkbox--indeterminate:after{content:\"\";position:absolute;left:3px;top:6px;width:8px;height:2px;background:var(--kf-color-white);border:none;transform:none}.kf-multi-select__option-label{overflow:hidden;text-overflow:ellipsis}.kf-multi-select__empty{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text-muted)}\n"], dependencies: [{ kind: "component", type: FieldLabel, selector: "kf-field-label", inputs: ["for"] }] });
3377
+ `, isInline: true, styles: [":host{display:inline-flex;flex-direction:column;gap:var(--kf-space-xs);position:relative;font:inherit;min-width:200px}.kf-multi-select__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);width:100%;font:inherit;font-family:var(--kf-font-body);padding:var(--kf-space-sm) var(--kf-space-md);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);background:var(--kf-color-white);color:var(--kf-color-text);cursor:pointer;transition:border-color .14s,box-shadow .14s}.kf-multi-select__trigger:hover:not(:disabled){border-color:var(--kf-color-border-strong)}.kf-multi-select__trigger:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-multi-select__trigger:disabled{opacity:.5;cursor:not-allowed}.kf-multi-select__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap;flex:1;text-align:left}.kf-multi-select__value--placeholder{color:var(--kf-color-text-muted)}.kf-multi-select__count{flex:none;display:inline-flex;align-items:center;justify-content:center;min-width:1.25rem;height:1.25rem;padding:0 5px;border-radius:var(--kf-radius-pill);background:var(--kf-color-primary);color:var(--kf-color-white);font-size:.72rem;font-weight:700}.kf-multi-select__caret{flex-shrink:0;width:8px;height:8px;border-right:2px solid var(--kf-color-text-muted);border-bottom:2px solid var(--kf-color-text-muted);transform:rotate(45deg);transition:transform .14s;margin-top:-4px}.kf-multi-select__caret--open{transform:rotate(-135deg);margin-top:4px}.kf-multi-select__panel{position:absolute;top:100%;left:0;right:0;z-index:10;margin:var(--kf-space-xs) 0 0;padding:var(--kf-space-xs) 0;list-style:none;max-height:240px;overflow-y:auto;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-md)}.kf-multi-select__option{display:flex;align-items:center;gap:var(--kf-space-sm);padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-multi-select__option:hover{background:var(--kf-color-primary-soft)}.kf-multi-select__option--selected{color:var(--kf-color-primary);font-weight:600}.kf-multi-select__checkbox{display:inline-flex;flex-shrink:0;width:var(--kf-space-md);height:var(--kf-space-md);border:1px solid var(--kf-color-border-strong);border-radius:var(--kf-radius-xs);background:var(--kf-color-white);transition:background .14s,border-color .14s;position:relative}.kf-multi-select__checkbox--checked{background:var(--kf-color-primary);border-color:var(--kf-color-primary)}.kf-multi-select__checkbox--checked:after{content:\"\";position:absolute;left:4px;top:1px;width:4px;height:8px;border:solid var(--kf-color-white);border-width:0 2px 2px 0;transform:rotate(45deg)}.kf-multi-select__checkbox--indeterminate{background:var(--kf-color-primary);border-color:var(--kf-color-primary)}.kf-multi-select__checkbox--indeterminate:after{content:\"\";position:absolute;left:3px;top:6px;width:8px;height:2px;background:var(--kf-color-white);border:none;transform:none}.kf-multi-select__option-label{overflow:hidden;text-overflow:ellipsis}.kf-multi-select__empty{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text-muted)}\n"], dependencies: [{ kind: "component", type: FieldLabel, selector: "kf-field-label", inputs: ["for", "required"] }] });
3268
3378
  }
3269
3379
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: MultiSelect, decorators: [{
3270
3380
  type: Component,
@@ -3687,7 +3797,7 @@ class PortalHeader {
3687
3797
  <kf-avatar [initials]="userInitials()" pill />
3688
3798
  </button>
3689
3799
  </div>
3690
- `, isInline: true, styles: [":host{display:block;font-family:var(--kf-font-body)}.kf-portal-header__admin-bar{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-md);padding:var(--kf-space-xs) var(--kf-space-lg);background:var(--kf-color-tertiary-deep)}.kf-portal-header__admin-actions{display:flex;gap:var(--kf-space-md)}.kf-portal-header__admin-action{font-size:.8125rem;font-weight:600;color:#ffffffd9;text-decoration:none}.kf-portal-header__admin-action:hover{color:var(--kf-color-white)}.kf-portal-header__brand-bar{display:flex;align-items:center;gap:var(--kf-space-lg);padding:var(--kf-space-sm) var(--kf-space-lg);background:var(--kf-color-tertiary-deep);border-top:1px solid rgba(255,255,255,.1)}.kf-portal-header__logo{font-family:var(--kf-font-title);font-size:1.25rem;font-weight:700;letter-spacing:.02em;color:var(--kf-color-white);text-transform:uppercase}.kf-portal-header__nav{display:flex;align-items:center;gap:var(--kf-space-xs);flex:1 1 auto}.kf-portal-header__nav-item{padding:var(--kf-space-xs) var(--kf-space-md);border-radius:var(--kf-radius-sm);color:#ffffffbf;font-size:.875rem;font-weight:600;text-decoration:none;transition:background .14s,color .14s}.kf-portal-header__nav-item:hover{color:var(--kf-color-white)}.kf-portal-header__nav-item--active{background:var(--kf-color-white);color:var(--kf-color-ink)}.kf-portal-header__session{flex:none;border:none;background:transparent;padding:0;cursor:pointer}\n"], dependencies: [{ kind: "component", type: AdminBadge, selector: "kf-admin-badge", inputs: ["inline"] }, { kind: "component", type: Avatar, selector: "kf-avatar", inputs: ["initials", "src", "round", "pill"] }] });
3800
+ `, isInline: true, styles: [":host{display:block;font-family:var(--kf-font-body)}.kf-portal-header__admin-bar{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-md);padding:var(--kf-space-xs) var(--kf-space-lg);background:var(--kf-color-tertiary-deep)}.kf-portal-header__admin-actions{display:flex;gap:var(--kf-space-md)}.kf-portal-header__admin-action{font-size:.8125rem;font-weight:600;color:#ffffffd9;text-decoration:none}.kf-portal-header__admin-action:hover{color:var(--kf-color-white)}.kf-portal-header__brand-bar{display:flex;align-items:center;gap:var(--kf-space-lg);padding:var(--kf-space-sm) var(--kf-space-lg);background:var(--kf-color-tertiary-deep);border-top:1px solid rgba(255,255,255,.1)}.kf-portal-header__logo{font-family:var(--kf-font-title);font-size:1.25rem;font-weight:700;letter-spacing:.02em;color:var(--kf-color-white);text-transform:uppercase}.kf-portal-header__nav{display:flex;align-items:center;gap:var(--kf-space-xs);flex:1 1 auto}.kf-portal-header__nav-item{padding:var(--kf-space-xs) var(--kf-space-md);border-radius:var(--kf-radius-sm);color:#ffffffbf;font-size:.875rem;font-weight:600;text-decoration:none;transition:background .14s,color .14s}.kf-portal-header__nav-item:hover:not(.kf-portal-header__nav-item--active){color:var(--kf-color-white)}.kf-portal-header__nav-item--active{background:var(--kf-color-white);color:var(--kf-color-ink)}.kf-portal-header__session{flex:none;border:none;background:transparent;padding:0;cursor:pointer}\n"], dependencies: [{ kind: "component", type: AdminBadge, selector: "kf-admin-badge", inputs: ["inline"] }, { kind: "component", type: Avatar, selector: "kf-avatar", inputs: ["initials", "src", "round", "pill"] }] });
3691
3801
  }
3692
3802
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: PortalHeader, decorators: [{
3693
3803
  type: Component,
@@ -3719,7 +3829,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImpor
3719
3829
  <kf-avatar [initials]="userInitials()" pill />
3720
3830
  </button>
3721
3831
  </div>
3722
- `, styles: [":host{display:block;font-family:var(--kf-font-body)}.kf-portal-header__admin-bar{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-md);padding:var(--kf-space-xs) var(--kf-space-lg);background:var(--kf-color-tertiary-deep)}.kf-portal-header__admin-actions{display:flex;gap:var(--kf-space-md)}.kf-portal-header__admin-action{font-size:.8125rem;font-weight:600;color:#ffffffd9;text-decoration:none}.kf-portal-header__admin-action:hover{color:var(--kf-color-white)}.kf-portal-header__brand-bar{display:flex;align-items:center;gap:var(--kf-space-lg);padding:var(--kf-space-sm) var(--kf-space-lg);background:var(--kf-color-tertiary-deep);border-top:1px solid rgba(255,255,255,.1)}.kf-portal-header__logo{font-family:var(--kf-font-title);font-size:1.25rem;font-weight:700;letter-spacing:.02em;color:var(--kf-color-white);text-transform:uppercase}.kf-portal-header__nav{display:flex;align-items:center;gap:var(--kf-space-xs);flex:1 1 auto}.kf-portal-header__nav-item{padding:var(--kf-space-xs) var(--kf-space-md);border-radius:var(--kf-radius-sm);color:#ffffffbf;font-size:.875rem;font-weight:600;text-decoration:none;transition:background .14s,color .14s}.kf-portal-header__nav-item:hover{color:var(--kf-color-white)}.kf-portal-header__nav-item--active{background:var(--kf-color-white);color:var(--kf-color-ink)}.kf-portal-header__session{flex:none;border:none;background:transparent;padding:0;cursor:pointer}\n"] }]
3832
+ `, styles: [":host{display:block;font-family:var(--kf-font-body)}.kf-portal-header__admin-bar{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-md);padding:var(--kf-space-xs) var(--kf-space-lg);background:var(--kf-color-tertiary-deep)}.kf-portal-header__admin-actions{display:flex;gap:var(--kf-space-md)}.kf-portal-header__admin-action{font-size:.8125rem;font-weight:600;color:#ffffffd9;text-decoration:none}.kf-portal-header__admin-action:hover{color:var(--kf-color-white)}.kf-portal-header__brand-bar{display:flex;align-items:center;gap:var(--kf-space-lg);padding:var(--kf-space-sm) var(--kf-space-lg);background:var(--kf-color-tertiary-deep);border-top:1px solid rgba(255,255,255,.1)}.kf-portal-header__logo{font-family:var(--kf-font-title);font-size:1.25rem;font-weight:700;letter-spacing:.02em;color:var(--kf-color-white);text-transform:uppercase}.kf-portal-header__nav{display:flex;align-items:center;gap:var(--kf-space-xs);flex:1 1 auto}.kf-portal-header__nav-item{padding:var(--kf-space-xs) var(--kf-space-md);border-radius:var(--kf-radius-sm);color:#ffffffbf;font-size:.875rem;font-weight:600;text-decoration:none;transition:background .14s,color .14s}.kf-portal-header__nav-item:hover:not(.kf-portal-header__nav-item--active){color:var(--kf-color-white)}.kf-portal-header__nav-item--active{background:var(--kf-color-white);color:var(--kf-color-ink)}.kf-portal-header__session{flex:none;border:none;background:transparent;padding:0;cursor:pointer}\n"] }]
3723
3833
  }], propDecorators: { brand: [{ type: i0.Input, args: [{ isSignal: true, alias: "brand", required: false }] }], isAdmin: [{ type: i0.Input, args: [{ isSignal: true, alias: "isAdmin", required: false }] }], adminActions: [{ type: i0.Input, args: [{ isSignal: true, alias: "adminActions", required: false }] }], navItems: [{ type: i0.Input, args: [{ isSignal: true, alias: "navItems", required: false }] }], userInitials: [{ type: i0.Input, args: [{ isSignal: true, alias: "userInitials", required: true }] }], sessionClick: [{ type: i0.Output, args: ["sessionClick"] }] } });
3724
3834
 
3725
3835
  /**
@@ -4066,7 +4176,7 @@ class SelectMenu {
4066
4176
  }
4067
4177
  </ul>
4068
4178
  }
4069
- `, isInline: true, styles: [":host{display:inline-flex;flex-direction:column;gap:var(--kf-space-xs);position:relative;font:inherit;min-width:200px}.kf-select-menu__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);width:100%;font:inherit;font-family:var(--kf-font-body);padding:var(--kf-space-sm) var(--kf-space-md);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);background:var(--kf-color-white);color:var(--kf-color-text);cursor:pointer;transition:border-color .14s,box-shadow .14s}.kf-select-menu__trigger:hover:not(:disabled){border-color:var(--kf-color-border-strong)}.kf-select-menu__trigger:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-select-menu__trigger:disabled{opacity:.5;cursor:not-allowed}.kf-select-menu__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kf-select-menu__value--placeholder{color:var(--kf-color-text-muted)}.kf-select-menu__caret{flex-shrink:0;width:8px;height:8px;border-right:2px solid var(--kf-color-text-muted);border-bottom:2px solid var(--kf-color-text-muted);transform:rotate(45deg);transition:transform .14s;margin-top:-4px}.kf-select-menu__caret--open{transform:rotate(-135deg);margin-top:4px}.kf-select-menu__panel{position:absolute;top:100%;left:0;right:0;z-index:10;margin:var(--kf-space-xs) 0 0;padding:var(--kf-space-xs) 0;list-style:none;max-height:240px;overflow-y:auto;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-md)}.kf-select-menu__option{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-select-menu__option:hover{background:var(--kf-color-primary-soft)}.kf-select-menu__option--selected{background:var(--kf-color-primary-soft);color:var(--kf-color-primary);font-weight:600}.kf-select-menu__empty{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text-muted)}\n"], dependencies: [{ kind: "component", type: FieldLabel, selector: "kf-field-label", inputs: ["for"] }] });
4179
+ `, isInline: true, styles: [":host{display:inline-flex;flex-direction:column;gap:var(--kf-space-xs);position:relative;font:inherit;min-width:200px}.kf-select-menu__trigger{display:flex;align-items:center;justify-content:space-between;gap:var(--kf-space-sm);width:100%;font:inherit;font-family:var(--kf-font-body);padding:var(--kf-space-sm) var(--kf-space-md);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);background:var(--kf-color-white);color:var(--kf-color-text);cursor:pointer;transition:border-color .14s,box-shadow .14s}.kf-select-menu__trigger:hover:not(:disabled){border-color:var(--kf-color-border-strong)}.kf-select-menu__trigger:focus-visible{outline:none;box-shadow:var(--kf-shadow-focus)}.kf-select-menu__trigger:disabled{opacity:.5;cursor:not-allowed}.kf-select-menu__value{overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.kf-select-menu__value--placeholder{color:var(--kf-color-text-muted)}.kf-select-menu__caret{flex-shrink:0;width:8px;height:8px;border-right:2px solid var(--kf-color-text-muted);border-bottom:2px solid var(--kf-color-text-muted);transform:rotate(45deg);transition:transform .14s;margin-top:-4px}.kf-select-menu__caret--open{transform:rotate(-135deg);margin-top:4px}.kf-select-menu__panel{position:absolute;top:100%;left:0;right:0;z-index:10;margin:var(--kf-space-xs) 0 0;padding:var(--kf-space-xs) 0;list-style:none;max-height:240px;overflow-y:auto;background:var(--kf-color-white);border:1px solid var(--kf-color-border);border-radius:var(--kf-radius-sm);box-shadow:var(--kf-shadow-md)}.kf-select-menu__option{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text);cursor:pointer;white-space:nowrap;overflow:hidden;text-overflow:ellipsis}.kf-select-menu__option:hover{background:var(--kf-color-primary-soft)}.kf-select-menu__option--selected{background:var(--kf-color-primary-soft);color:var(--kf-color-primary);font-weight:600}.kf-select-menu__empty{padding:var(--kf-space-sm) var(--kf-space-md);color:var(--kf-color-text-muted)}\n"], dependencies: [{ kind: "component", type: FieldLabel, selector: "kf-field-label", inputs: ["for", "required"] }] });
4070
4180
  }
4071
4181
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.0", ngImport: i0, type: SelectMenu, decorators: [{
4072
4182
  type: Component,