@letsprogram/ng-oat 0.1.4 → 0.1.5

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.
@@ -1,6 +1,7 @@
1
1
  import * as i0 from '@angular/core';
2
- import { makeEnvironmentProviders, provideAppInitializer, inject, PLATFORM_ID, Injectable, ElementRef, DestroyRef, signal, output, afterNextRender, Directive, InjectionToken, Renderer2, ViewContainerRef, input, TemplateRef, effect, computed, Component, model, CUSTOM_ELEMENTS_SCHEMA, viewChild, viewChildren, contentChildren, contentChild } from '@angular/core';
2
+ import { makeEnvironmentProviders, provideAppInitializer, inject, PLATFORM_ID, Injectable, ElementRef, DestroyRef, signal, output, afterNextRender, Directive, InjectionToken, Renderer2, ViewContainerRef, input, TemplateRef, effect, computed, Component, model, forwardRef, CUSTOM_ELEMENTS_SCHEMA, viewChild, viewChildren, contentChildren, contentChild, HostListener } from '@angular/core';
3
3
  import { DOCUMENT, isPlatformBrowser, NgTemplateOutlet, NgSwitch, NgSwitchCase, NgSwitchDefault } from '@angular/common';
4
+ import { NG_VALUE_ACCESSOR } from '@angular/forms';
4
5
 
5
6
  const DEFAULT_OPTIONS = {
6
7
  assets: { css: false },
@@ -1772,6 +1773,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
1772
1773
  }]
1773
1774
  }], propDecorators: { size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], overlay: [{ type: i0.Input, args: [{ isSignal: true, alias: "overlay", required: false }] }] } });
1774
1775
 
1776
+ /** Injection token provided by each value-type ng-oat form component. */
1777
+ const NG_OAT_VALUE_HOST = new InjectionToken('NG_OAT_VALUE_HOST');
1778
+ /** Injection token provided by each checkbox-type ng-oat form component. */
1779
+ const NG_OAT_CHECKBOX_HOST = new InjectionToken('NG_OAT_CHECKBOX_HOST');
1780
+
1775
1781
  /**
1776
1782
  * Angular wrapper for Oat CSS toggle switch.
1777
1783
  * Implements `FormCheckboxControl` for seamless Signal Forms integration — no CVA needed.
@@ -1789,16 +1795,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
1789
1795
  class NgOatSwitch {
1790
1796
  label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
1791
1797
  checked = model(false, ...(ngDevMode ? [{ debugName: "checked" }] : []));
1792
- disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
1798
+ disabled = model(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
1799
+ touched = model(false, ...(ngDevMode ? [{ debugName: "touched" }] : []));
1793
1800
  value = undefined;
1794
1801
  changed = output();
1795
1802
  onInputChange(event) {
1796
1803
  const val = event.target.checked;
1797
1804
  this.checked.set(val);
1805
+ this.touched.set(true);
1798
1806
  this.changed.emit(val);
1799
1807
  }
1800
1808
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: NgOatSwitch, deps: [], target: i0.ɵɵFactoryTarget.Component });
1801
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.0", type: NgOatSwitch, isStandalone: true, selector: "ng-oat-switch", 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 } }, outputs: { checked: "checkedChange", changed: "changed" }, ngImport: i0, template: `
1809
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.0", type: NgOatSwitch, isStandalone: true, selector: "ng-oat-switch", 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 }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checked: "checkedChange", disabled: "disabledChange", touched: "touchedChange", changed: "changed" }, providers: [{ provide: NG_OAT_CHECKBOX_HOST, useExisting: forwardRef(() => NgOatSwitch) }], ngImport: i0, template: `
1802
1810
  <label>
1803
1811
  <input
1804
1812
  type="checkbox"
@@ -1815,6 +1823,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
1815
1823
  type: Component,
1816
1824
  args: [{
1817
1825
  selector: 'ng-oat-switch',
1826
+ providers: [{ provide: NG_OAT_CHECKBOX_HOST, useExisting: forwardRef(() => NgOatSwitch) }],
1818
1827
  template: `
1819
1828
  <label>
1820
1829
  <input
@@ -1828,7 +1837,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
1828
1837
  </label>
1829
1838
  `,
1830
1839
  }]
1831
- }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], changed: [{ type: i0.Output, args: ["changed"] }] } });
1840
+ }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }, { type: i0.Output, args: ["touchedChange"] }], changed: [{ type: i0.Output, args: ["changed"] }] } });
1832
1841
 
1833
1842
  /**
1834
1843
  * Angular wrapper for Oat CSS skeleton loader.
@@ -3055,9 +3064,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
3055
3064
  class NgOatInputOtp {
3056
3065
  length = input(6, ...(ngDevMode ? [{ debugName: "length" }] : []));
3057
3066
  separator = input(0, ...(ngDevMode ? [{ debugName: "separator" }] : []));
3058
- disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
3067
+ disabled = model(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
3059
3068
  mask = input(false, ...(ngDevMode ? [{ debugName: "mask" }] : []));
3060
3069
  value = model('', ...(ngDevMode ? [{ debugName: "value" }] : []));
3070
+ touched = model(false, ...(ngDevMode ? [{ debugName: "touched" }] : []));
3061
3071
  checked = undefined;
3062
3072
  slotInputRefs = viewChildren('slotInput', ...(ngDevMode ? [{ debugName: "slotInputRefs" }] : []));
3063
3073
  slots = computed(() => {
@@ -3069,6 +3079,7 @@ class NgOatInputOtp {
3069
3079
  const input = event.target;
3070
3080
  const char = input.value.replace(/\D/g, '').slice(-1);
3071
3081
  input.value = char;
3082
+ this.touched.set(true);
3072
3083
  this.updateValueAt(index, char);
3073
3084
  // Auto-advance to next slot
3074
3085
  if (char && index < this.length() - 1) {
@@ -3134,7 +3145,7 @@ class NgOatInputOtp {
3134
3145
  this.focusSlot(0);
3135
3146
  }
3136
3147
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: NgOatInputOtp, deps: [], target: i0.ɵɵFactoryTarget.Component });
3137
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: NgOatInputOtp, isStandalone: true, selector: "ng-oat-input-otp", inputs: { length: { classPropertyName: "length", publicName: "length", isSignal: true, isRequired: false, transformFunction: null }, separator: { classPropertyName: "separator", publicName: "separator", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, mask: { classPropertyName: "mask", publicName: "mask", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange" }, viewQueries: [{ propertyName: "slotInputRefs", predicate: ["slotInput"], descendants: true, isSignal: true }], ngImport: i0, template: `
3148
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: NgOatInputOtp, isStandalone: true, selector: "ng-oat-input-otp", inputs: { length: { classPropertyName: "length", publicName: "length", isSignal: true, isRequired: false, transformFunction: null }, separator: { classPropertyName: "separator", publicName: "separator", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, mask: { classPropertyName: "mask", publicName: "mask", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { disabled: "disabledChange", value: "valueChange", touched: "touchedChange" }, providers: [{ provide: NG_OAT_VALUE_HOST, useExisting: forwardRef(() => NgOatInputOtp) }], viewQueries: [{ propertyName: "slotInputRefs", predicate: ["slotInput"], descendants: true, isSignal: true }], ngImport: i0, template: `
3138
3149
  <div class="otp-group" role="group" [attr.aria-label]="'OTP input with ' + length() + ' digits'">
3139
3150
  @for (slot of slots(); track $index) {
3140
3151
  @if (separator() > 0 && $index === separator()) {
@@ -3162,7 +3173,7 @@ class NgOatInputOtp {
3162
3173
  }
3163
3174
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: NgOatInputOtp, decorators: [{
3164
3175
  type: Component,
3165
- args: [{ selector: 'ng-oat-input-otp', template: `
3176
+ args: [{ selector: 'ng-oat-input-otp', providers: [{ provide: NG_OAT_VALUE_HOST, useExisting: forwardRef(() => NgOatInputOtp) }], template: `
3166
3177
  <div class="otp-group" role="group" [attr.aria-label]="'OTP input with ' + length() + ' digits'">
3167
3178
  @for (slot of slots(); track $index) {
3168
3179
  @if (separator() > 0 && $index === separator()) {
@@ -3187,7 +3198,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
3187
3198
  }
3188
3199
  </div>
3189
3200
  `, styles: [":host{display:inline-block}\n"] }]
3190
- }], propDecorators: { length: [{ type: i0.Input, args: [{ isSignal: true, alias: "length", required: false }] }], separator: [{ type: i0.Input, args: [{ isSignal: true, alias: "separator", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], mask: [{ type: i0.Input, args: [{ isSignal: true, alias: "mask", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], slotInputRefs: [{ type: i0.ViewChildren, args: ['slotInput', { isSignal: true }] }] } });
3201
+ }], propDecorators: { length: [{ type: i0.Input, args: [{ isSignal: true, alias: "length", required: false }] }], separator: [{ type: i0.Input, args: [{ isSignal: true, alias: "separator", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], mask: [{ type: i0.Input, args: [{ isSignal: true, alias: "mask", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }, { type: i0.Output, args: ["touchedChange"] }], slotInputRefs: [{ type: i0.ViewChildren, args: ['slotInput', { isSignal: true }] }] } });
3191
3202
 
3192
3203
  /**
3193
3204
  * Search input with icon, clear button, optional keyboard shortcut badge, and built-in debounce.
@@ -3201,7 +3212,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
3201
3212
  */
3202
3213
  class NgOatSearchInput {
3203
3214
  placeholder = input('Search...', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
3204
- disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
3215
+ disabled = model(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
3205
3216
  shortcut = input('', ...(ngDevMode ? [{ debugName: "shortcut" }] : []));
3206
3217
  debounceMs = input(300, ...(ngDevMode ? [{ debugName: "debounceMs" }] : []));
3207
3218
  value = model('', ...(ngDevMode ? [{ debugName: "value" }] : []));
@@ -3274,7 +3285,7 @@ class NgOatSearchInput {
3274
3285
  this.searchInputRef()?.nativeElement.focus(options);
3275
3286
  }
3276
3287
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: NgOatSearchInput, deps: [], target: i0.ɵɵFactoryTarget.Component });
3277
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: NgOatSearchInput, isStandalone: true, selector: "ng-oat-search-input", inputs: { placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, shortcut: { classPropertyName: "shortcut", publicName: "shortcut", isSignal: true, isRequired: false, transformFunction: null }, debounceMs: { classPropertyName: "debounceMs", publicName: "debounceMs", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", search: "search", touched: "touchedChange" }, viewQueries: [{ propertyName: "searchInputRef", first: true, predicate: ["searchEl"], descendants: true, isSignal: true }], ngImport: i0, template: `
3288
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: NgOatSearchInput, isStandalone: true, selector: "ng-oat-search-input", inputs: { placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, shortcut: { classPropertyName: "shortcut", publicName: "shortcut", isSignal: true, isRequired: false, transformFunction: null }, debounceMs: { classPropertyName: "debounceMs", publicName: "debounceMs", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { disabled: "disabledChange", value: "valueChange", search: "search", touched: "touchedChange" }, providers: [{ provide: NG_OAT_VALUE_HOST, useExisting: forwardRef(() => NgOatSearchInput) }], viewQueries: [{ propertyName: "searchInputRef", first: true, predicate: ["searchEl"], descendants: true, isSignal: true }], ngImport: i0, template: `
3278
3289
  <div class="search-input" [class.has-value]="hasValue()">
3279
3290
  <span class="search-input-icon" aria-hidden="true">
3280
3291
  <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
@@ -3310,7 +3321,7 @@ class NgOatSearchInput {
3310
3321
  }
3311
3322
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: NgOatSearchInput, decorators: [{
3312
3323
  type: Component,
3313
- args: [{ selector: 'ng-oat-search-input', template: `
3324
+ args: [{ selector: 'ng-oat-search-input', providers: [{ provide: NG_OAT_VALUE_HOST, useExisting: forwardRef(() => NgOatSearchInput) }], template: `
3314
3325
  <div class="search-input" [class.has-value]="hasValue()">
3315
3326
  <span class="search-input-icon" aria-hidden="true">
3316
3327
  <svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
@@ -3343,7 +3354,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
3343
3354
  }
3344
3355
  </div>
3345
3356
  `, styles: [":host{display:block}\n"] }]
3346
- }], ctorParameters: () => [], propDecorators: { placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], shortcut: [{ type: i0.Input, args: [{ isSignal: true, alias: "shortcut", required: false }] }], debounceMs: [{ type: i0.Input, args: [{ isSignal: true, alias: "debounceMs", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], search: [{ type: i0.Output, args: ["search"] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }, { type: i0.Output, args: ["touchedChange"] }], searchInputRef: [{ type: i0.ViewChild, args: ['searchEl', { isSignal: true }] }] } });
3357
+ }], ctorParameters: () => [], propDecorators: { placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], shortcut: [{ type: i0.Input, args: [{ isSignal: true, alias: "shortcut", required: false }] }], debounceMs: [{ type: i0.Input, args: [{ isSignal: true, alias: "debounceMs", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], search: [{ type: i0.Output, args: ["search"] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }, { type: i0.Output, args: ["touchedChange"] }], searchInputRef: [{ type: i0.ViewChild, args: ['searchEl', { isSignal: true }] }] } });
3347
3358
 
3348
3359
  /**
3349
3360
  * Separator component for visual content division. Renders as `<hr>` for
@@ -4992,7 +5003,7 @@ class NgOatInput {
4992
5003
  value = model('', ...(ngDevMode ? [{ debugName: "value" }] : []));
4993
5004
  checked = undefined;
4994
5005
  // --- Signal Forms optional bindings ---
4995
- disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
5006
+ disabled = model(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
4996
5007
  readonly = input(false, ...(ngDevMode ? [{ debugName: "readonly" }] : []));
4997
5008
  required = input(false, ...(ngDevMode ? [{ debugName: "required" }] : []));
4998
5009
  invalid = input(false, ...(ngDevMode ? [{ debugName: "invalid" }] : []));
@@ -5007,7 +5018,7 @@ class NgOatInput {
5007
5018
  type = input('text', ...(ngDevMode ? [{ debugName: "type" }] : []));
5008
5019
  placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
5009
5020
  showErrors = input(true, ...(ngDevMode ? [{ debugName: "showErrors" }] : []));
5010
- inputId = computed(() => `ng-oat-input-${++NgOatInput.nextId}`, ...(ngDevMode ? [{ debugName: "inputId" }] : []));
5021
+ inputId = `ng-oat-input-${++NgOatInput.nextId}`;
5011
5022
  onInput(event) {
5012
5023
  const val = event.target.value;
5013
5024
  this.value.set(val);
@@ -5016,23 +5027,23 @@ class NgOatInput {
5016
5027
  this.touched.set(true);
5017
5028
  }
5018
5029
  focus(options) {
5019
- const input = document.getElementById(this.inputId());
5030
+ const input = document.getElementById(this.inputId);
5020
5031
  input?.focus(options);
5021
5032
  }
5022
5033
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: NgOatInput, deps: [], target: i0.ɵɵFactoryTarget.Component });
5023
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: NgOatInput, isStandalone: true, selector: "ng-oat-input", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, minLength: { classPropertyName: "minLength", publicName: "minLength", isSignal: true, isRequired: false, transformFunction: null }, maxLength: { classPropertyName: "maxLength", publicName: "maxLength", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", 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 }, showErrors: { classPropertyName: "showErrors", publicName: "showErrors", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", touched: "touchedChange" }, ngImport: i0, template: `
5034
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: NgOatInput, isStandalone: true, selector: "ng-oat-input", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, minLength: { classPropertyName: "minLength", publicName: "minLength", isSignal: true, isRequired: false, transformFunction: null }, maxLength: { classPropertyName: "maxLength", publicName: "maxLength", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", 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 }, showErrors: { classPropertyName: "showErrors", publicName: "showErrors", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", disabled: "disabledChange", touched: "touchedChange" }, providers: [{ provide: NG_OAT_VALUE_HOST, useExisting: forwardRef(() => NgOatInput) }], ngImport: i0, template: `
5024
5035
  @if (label()) {
5025
- <label [attr.for]="inputId()">{{ label() }}</label>
5036
+ <label [attr.for]="inputId">{{ label() }}</label>
5026
5037
  }
5027
5038
  <input
5028
- [id]="inputId()"
5039
+ [id]="inputId"
5029
5040
  [type]="type()"
5030
5041
  [value]="value()"
5031
5042
  [placeholder]="placeholder()"
5032
5043
  [disabled]="disabled()"
5033
5044
  [readOnly]="readonly()"
5034
5045
  [required]="required()"
5035
- [attr.aria-invalid]="invalid() || null"
5046
+ [attr.aria-invalid]="(invalid() && touched()) || null"
5036
5047
  [attr.min]="min() ?? null"
5037
5048
  [attr.max]="max() ?? null"
5038
5049
  [attr.minlength]="minLength() ?? null"
@@ -5040,28 +5051,28 @@ class NgOatInput {
5040
5051
  (input)="onInput($event)"
5041
5052
  (blur)="onBlur()"
5042
5053
  />
5043
- @if (showErrors()) {
5054
+ <!-- @if (showErrors() && touched()) {
5044
5055
  @for (err of errors(); track $index) {
5045
5056
  <small class="error">{{ err.message }}</small>
5046
5057
  }
5047
- }
5058
+ } -->
5048
5059
  `, isInline: true, styles: [":host{display:block}.error{color:var(--danger);display:block;font-size:var(--text-8);margin-block-start:var(--space-1)}\n"] });
5049
5060
  }
5050
5061
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: NgOatInput, decorators: [{
5051
5062
  type: Component,
5052
- args: [{ selector: 'ng-oat-input', template: `
5063
+ args: [{ selector: 'ng-oat-input', providers: [{ provide: NG_OAT_VALUE_HOST, useExisting: forwardRef(() => NgOatInput) }], template: `
5053
5064
  @if (label()) {
5054
- <label [attr.for]="inputId()">{{ label() }}</label>
5065
+ <label [attr.for]="inputId">{{ label() }}</label>
5055
5066
  }
5056
5067
  <input
5057
- [id]="inputId()"
5068
+ [id]="inputId"
5058
5069
  [type]="type()"
5059
5070
  [value]="value()"
5060
5071
  [placeholder]="placeholder()"
5061
5072
  [disabled]="disabled()"
5062
5073
  [readOnly]="readonly()"
5063
5074
  [required]="required()"
5064
- [attr.aria-invalid]="invalid() || null"
5075
+ [attr.aria-invalid]="(invalid() && touched()) || null"
5065
5076
  [attr.min]="min() ?? null"
5066
5077
  [attr.max]="max() ?? null"
5067
5078
  [attr.minlength]="minLength() ?? null"
@@ -5069,13 +5080,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
5069
5080
  (input)="onInput($event)"
5070
5081
  (blur)="onBlur()"
5071
5082
  />
5072
- @if (showErrors()) {
5083
+ <!-- @if (showErrors() && touched()) {
5073
5084
  @for (err of errors(); track $index) {
5074
5085
  <small class="error">{{ err.message }}</small>
5075
5086
  }
5076
- }
5087
+ } -->
5077
5088
  `, styles: [":host{display:block}.error{color:var(--danger);display:block;font-size:var(--text-8);margin-block-start:var(--space-1)}\n"] }]
5078
- }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], errors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }, { type: i0.Output, args: ["touchedChange"] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], minLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "minLength", required: false }] }], maxLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxLength", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], showErrors: [{ type: i0.Input, args: [{ isSignal: true, alias: "showErrors", required: false }] }] } });
5089
+ }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], errors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }, { type: i0.Output, args: ["touchedChange"] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], minLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "minLength", required: false }] }], maxLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxLength", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], type: [{ type: i0.Input, args: [{ isSignal: true, alias: "type", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], showErrors: [{ type: i0.Input, args: [{ isSignal: true, alias: "showErrors", required: false }] }] } });
5079
5090
 
5080
5091
  /**
5081
5092
  * Oat-styled textarea implementing `FormValueControl<string>`.
@@ -5095,7 +5106,7 @@ class NgOatTextarea {
5095
5106
  static nextId = 0;
5096
5107
  value = model('', ...(ngDevMode ? [{ debugName: "value" }] : []));
5097
5108
  checked = undefined;
5098
- disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
5109
+ disabled = model(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
5099
5110
  readonly = input(false, ...(ngDevMode ? [{ debugName: "readonly" }] : []));
5100
5111
  required = input(false, ...(ngDevMode ? [{ debugName: "required" }] : []));
5101
5112
  invalid = input(false, ...(ngDevMode ? [{ debugName: "invalid" }] : []));
@@ -5108,7 +5119,7 @@ class NgOatTextarea {
5108
5119
  placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
5109
5120
  rows = input(4, ...(ngDevMode ? [{ debugName: "rows" }] : []));
5110
5121
  showErrors = input(true, ...(ngDevMode ? [{ debugName: "showErrors" }] : []));
5111
- textareaId = computed(() => `ng-oat-ta-${++NgOatTextarea.nextId}`, ...(ngDevMode ? [{ debugName: "textareaId" }] : []));
5122
+ textareaId = `ng-oat-ta-${++NgOatTextarea.nextId}`;
5112
5123
  onInput(event) {
5113
5124
  const val = event.target.value;
5114
5125
  this.value.set(val);
@@ -5117,29 +5128,29 @@ class NgOatTextarea {
5117
5128
  this.touched.set(true);
5118
5129
  }
5119
5130
  focus(options) {
5120
- const el = document.getElementById(this.textareaId());
5131
+ const el = document.getElementById(this.textareaId);
5121
5132
  el?.focus(options);
5122
5133
  }
5123
5134
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: NgOatTextarea, deps: [], target: i0.ɵɵFactoryTarget.Component });
5124
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: NgOatTextarea, isStandalone: true, selector: "ng-oat-textarea", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, minLength: { classPropertyName: "minLength", publicName: "minLength", isSignal: true, isRequired: false, transformFunction: null }, maxLength: { classPropertyName: "maxLength", publicName: "maxLength", isSignal: true, isRequired: false, transformFunction: null }, errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, showErrors: { classPropertyName: "showErrors", publicName: "showErrors", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", touched: "touchedChange" }, ngImport: i0, template: `
5135
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: NgOatTextarea, isStandalone: true, selector: "ng-oat-textarea", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, minLength: { classPropertyName: "minLength", publicName: "minLength", isSignal: true, isRequired: false, transformFunction: null }, maxLength: { classPropertyName: "maxLength", publicName: "maxLength", isSignal: true, isRequired: false, transformFunction: null }, errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, showErrors: { classPropertyName: "showErrors", publicName: "showErrors", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", disabled: "disabledChange", touched: "touchedChange" }, providers: [{ provide: NG_OAT_VALUE_HOST, useExisting: forwardRef(() => NgOatTextarea) }], ngImport: i0, template: `
5125
5136
  @if (label()) {
5126
- <label [attr.for]="textareaId()">{{ label() }}</label>
5137
+ <label [attr.for]="textareaId">{{ label() }}</label>
5127
5138
  }
5128
5139
  <textarea
5129
- [id]="textareaId()"
5140
+ [id]="textareaId"
5130
5141
  [value]="value()"
5131
5142
  [placeholder]="placeholder()"
5132
5143
  [disabled]="disabled()"
5133
5144
  [readOnly]="readonly()"
5134
5145
  [required]="required()"
5135
5146
  [rows]="rows()"
5136
- [attr.aria-invalid]="invalid() || null"
5147
+ [attr.aria-invalid]="(invalid() && touched()) || null"
5137
5148
  [attr.minlength]="minLength() ?? null"
5138
5149
  [attr.maxlength]="maxLength() ?? null"
5139
5150
  (input)="onInput($event)"
5140
5151
  (blur)="onBlur()"
5141
5152
  ></textarea>
5142
- @if (showErrors()) {
5153
+ @if (showErrors() && touched()) {
5143
5154
  @for (err of errors(); track $index) {
5144
5155
  <small class="error">{{ err.message }}</small>
5145
5156
  }
@@ -5148,31 +5159,31 @@ class NgOatTextarea {
5148
5159
  }
5149
5160
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: NgOatTextarea, decorators: [{
5150
5161
  type: Component,
5151
- args: [{ selector: 'ng-oat-textarea', template: `
5162
+ args: [{ selector: 'ng-oat-textarea', providers: [{ provide: NG_OAT_VALUE_HOST, useExisting: forwardRef(() => NgOatTextarea) }], template: `
5152
5163
  @if (label()) {
5153
- <label [attr.for]="textareaId()">{{ label() }}</label>
5164
+ <label [attr.for]="textareaId">{{ label() }}</label>
5154
5165
  }
5155
5166
  <textarea
5156
- [id]="textareaId()"
5167
+ [id]="textareaId"
5157
5168
  [value]="value()"
5158
5169
  [placeholder]="placeholder()"
5159
5170
  [disabled]="disabled()"
5160
5171
  [readOnly]="readonly()"
5161
5172
  [required]="required()"
5162
5173
  [rows]="rows()"
5163
- [attr.aria-invalid]="invalid() || null"
5174
+ [attr.aria-invalid]="(invalid() && touched()) || null"
5164
5175
  [attr.minlength]="minLength() ?? null"
5165
5176
  [attr.maxlength]="maxLength() ?? null"
5166
5177
  (input)="onInput($event)"
5167
5178
  (blur)="onBlur()"
5168
5179
  ></textarea>
5169
- @if (showErrors()) {
5180
+ @if (showErrors() && touched()) {
5170
5181
  @for (err of errors(); track $index) {
5171
5182
  <small class="error">{{ err.message }}</small>
5172
5183
  }
5173
5184
  }
5174
5185
  `, styles: [":host{display:block}.error{color:var(--danger);display:block;font-size:var(--text-8);margin-block-start:var(--space-1)}\n"] }]
5175
- }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], minLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "minLength", required: false }] }], maxLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxLength", required: false }] }], errors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }, { type: i0.Output, args: ["touchedChange"] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }], showErrors: [{ type: i0.Input, args: [{ isSignal: true, alias: "showErrors", required: false }] }] } });
5186
+ }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], minLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "minLength", required: false }] }], maxLength: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxLength", required: false }] }], errors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }, { type: i0.Output, args: ["touchedChange"] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }], showErrors: [{ type: i0.Input, args: [{ isSignal: true, alias: "showErrors", required: false }] }] } });
5176
5187
 
5177
5188
  /**
5178
5189
  * Oat-styled select implementing `FormValueControl<string>`.
@@ -5193,7 +5204,7 @@ class NgOatSelect {
5193
5204
  checked = undefined;
5194
5205
  value = model('', ...(ngDevMode ? [{ debugName: "value" }] : []));
5195
5206
  // --- Signal Forms optional bindings ---
5196
- disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
5207
+ disabled = model(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
5197
5208
  required = input(false, ...(ngDevMode ? [{ debugName: "required" }] : []));
5198
5209
  invalid = input(false, ...(ngDevMode ? [{ debugName: "invalid" }] : []));
5199
5210
  errors = input([], ...(ngDevMode ? [{ debugName: "errors" }] : []));
@@ -5203,7 +5214,7 @@ class NgOatSelect {
5203
5214
  placeholder = input('', ...(ngDevMode ? [{ debugName: "placeholder" }] : []));
5204
5215
  options = input.required(...(ngDevMode ? [{ debugName: "options" }] : []));
5205
5216
  showErrors = input(true, ...(ngDevMode ? [{ debugName: "showErrors" }] : []));
5206
- selectId = computed(() => `ng-oat-sel-${++NgOatSelect.nextId}`, ...(ngDevMode ? [{ debugName: "selectId" }] : []));
5217
+ selectId = `ng-oat-sel-${++NgOatSelect.nextId}`;
5207
5218
  onChange(event) {
5208
5219
  const val = event.target.value;
5209
5220
  this.value.set(val);
@@ -5212,20 +5223,20 @@ class NgOatSelect {
5212
5223
  this.touched.set(true);
5213
5224
  }
5214
5225
  focus(options) {
5215
- const el = document.getElementById(this.selectId());
5226
+ const el = document.getElementById(this.selectId);
5216
5227
  el?.focus(options);
5217
5228
  }
5218
5229
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: NgOatSelect, deps: [], target: i0.ɵɵFactoryTarget.Component });
5219
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: NgOatSelect, isStandalone: true, selector: "ng-oat-select", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, showErrors: { classPropertyName: "showErrors", publicName: "showErrors", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", touched: "touchedChange" }, ngImport: i0, template: `
5230
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: NgOatSelect, isStandalone: true, selector: "ng-oat-select", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, showErrors: { classPropertyName: "showErrors", publicName: "showErrors", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", disabled: "disabledChange", touched: "touchedChange" }, providers: [{ provide: NG_OAT_VALUE_HOST, useExisting: forwardRef(() => NgOatSelect) }], ngImport: i0, template: `
5220
5231
  @if (label()) {
5221
- <label [attr.for]="selectId()">{{ label() }}</label>
5232
+ <label [attr.for]="selectId">{{ label() }}</label>
5222
5233
  }
5223
5234
  <select
5224
- [id]="selectId()"
5235
+ [id]="selectId"
5225
5236
  [value]="value()"
5226
5237
  [disabled]="disabled()"
5227
5238
  [required]="required()"
5228
- [attr.aria-invalid]="invalid() || null"
5239
+ [attr.aria-invalid]="(invalid() && touched()) || null"
5229
5240
  (change)="onChange($event)"
5230
5241
  (blur)="onBlur()"
5231
5242
  >
@@ -5241,7 +5252,7 @@ class NgOatSelect {
5241
5252
  </option>
5242
5253
  }
5243
5254
  </select>
5244
- @if (showErrors()) {
5255
+ @if (showErrors() && touched()) {
5245
5256
  @for (err of errors(); track $index) {
5246
5257
  <small class="error">{{ err.message }}</small>
5247
5258
  }
@@ -5250,16 +5261,16 @@ class NgOatSelect {
5250
5261
  }
5251
5262
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: NgOatSelect, decorators: [{
5252
5263
  type: Component,
5253
- args: [{ selector: 'ng-oat-select', template: `
5264
+ args: [{ selector: 'ng-oat-select', providers: [{ provide: NG_OAT_VALUE_HOST, useExisting: forwardRef(() => NgOatSelect) }], template: `
5254
5265
  @if (label()) {
5255
- <label [attr.for]="selectId()">{{ label() }}</label>
5266
+ <label [attr.for]="selectId">{{ label() }}</label>
5256
5267
  }
5257
5268
  <select
5258
- [id]="selectId()"
5269
+ [id]="selectId"
5259
5270
  [value]="value()"
5260
5271
  [disabled]="disabled()"
5261
5272
  [required]="required()"
5262
- [attr.aria-invalid]="invalid() || null"
5273
+ [attr.aria-invalid]="(invalid() && touched()) || null"
5263
5274
  (change)="onChange($event)"
5264
5275
  (blur)="onBlur()"
5265
5276
  >
@@ -5275,13 +5286,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
5275
5286
  </option>
5276
5287
  }
5277
5288
  </select>
5278
- @if (showErrors()) {
5289
+ @if (showErrors() && touched()) {
5279
5290
  @for (err of errors(); track $index) {
5280
5291
  <small class="error">{{ err.message }}</small>
5281
5292
  }
5282
5293
  }
5283
5294
  `, styles: [":host{display:block}.error{color:var(--danger);display:block;font-size:var(--text-8);margin-block-start:var(--space-1)}\n"] }]
5284
- }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], errors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }, { type: i0.Output, args: ["touchedChange"] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], showErrors: [{ type: i0.Input, args: [{ isSignal: true, alias: "showErrors", required: false }] }] } });
5295
+ }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], errors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }, { type: i0.Output, args: ["touchedChange"] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], showErrors: [{ type: i0.Input, args: [{ isSignal: true, alias: "showErrors", required: false }] }] } });
5285
5296
 
5286
5297
  /**
5287
5298
  * Oat-styled checkbox implementing `FormCheckboxControl`.
@@ -5299,7 +5310,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
5299
5310
  */
5300
5311
  class NgOatCheckbox {
5301
5312
  checked = model(false, ...(ngDevMode ? [{ debugName: "checked" }] : []));
5302
- disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
5313
+ disabled = model(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
5314
+ touched = model(false, ...(ngDevMode ? [{ debugName: "touched" }] : []));
5303
5315
  /** FormCheckboxControl requires `value` to be undefined */
5304
5316
  value = undefined;
5305
5317
  // --- Component-specific inputs ---
@@ -5307,9 +5319,10 @@ class NgOatCheckbox {
5307
5319
  onChange(event) {
5308
5320
  const val = event.target.checked;
5309
5321
  this.checked.set(val);
5322
+ this.touched.set(true);
5310
5323
  }
5311
5324
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: NgOatCheckbox, deps: [], target: i0.ɵɵFactoryTarget.Component });
5312
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.0", type: NgOatCheckbox, isStandalone: true, selector: "ng-oat-checkbox", inputs: { checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checked: "checkedChange" }, ngImport: i0, template: `
5325
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.0", type: NgOatCheckbox, isStandalone: true, selector: "ng-oat-checkbox", inputs: { checked: { classPropertyName: "checked", publicName: "checked", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { checked: "checkedChange", disabled: "disabledChange", touched: "touchedChange" }, providers: [{ provide: NG_OAT_CHECKBOX_HOST, useExisting: forwardRef(() => NgOatCheckbox) }], ngImport: i0, template: `
5313
5326
  <label>
5314
5327
  <input
5315
5328
  type="checkbox"
@@ -5325,6 +5338,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
5325
5338
  type: Component,
5326
5339
  args: [{
5327
5340
  selector: 'ng-oat-checkbox',
5341
+ providers: [{ provide: NG_OAT_CHECKBOX_HOST, useExisting: forwardRef(() => NgOatCheckbox) }],
5328
5342
  template: `
5329
5343
  <label>
5330
5344
  <input
@@ -5337,7 +5351,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
5337
5351
  </label>
5338
5352
  `,
5339
5353
  }]
5340
- }], propDecorators: { checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }] } });
5354
+ }], propDecorators: { checked: [{ type: i0.Input, args: [{ isSignal: true, alias: "checked", required: false }] }, { type: i0.Output, args: ["checkedChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }, { type: i0.Output, args: ["touchedChange"] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }] } });
5341
5355
 
5342
5356
  /**
5343
5357
  * Oat-styled radio group implementing `FormValueControl<string>`.
@@ -5364,7 +5378,7 @@ class NgOatRadioGroup {
5364
5378
  value = model('', ...(ngDevMode ? [{ debugName: "value" }] : []));
5365
5379
  checked = undefined;
5366
5380
  // --- Signal Forms optional bindings ---
5367
- disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
5381
+ disabled = model(false, ...(ngDevMode ? [{ debugName: "disabled" }] : []));
5368
5382
  required = input(false, ...(ngDevMode ? [{ debugName: "required" }] : []));
5369
5383
  invalid = input(false, ...(ngDevMode ? [{ debugName: "invalid" }] : []));
5370
5384
  errors = input([], ...(ngDevMode ? [{ debugName: "errors" }] : []));
@@ -5373,13 +5387,13 @@ class NgOatRadioGroup {
5373
5387
  label = input('', ...(ngDevMode ? [{ debugName: "label" }] : []));
5374
5388
  options = input.required(...(ngDevMode ? [{ debugName: "options" }] : []));
5375
5389
  showErrors = input(true, ...(ngDevMode ? [{ debugName: "showErrors" }] : []));
5376
- groupName = computed(() => `ng-oat-radio-${++NgOatRadioGroup.nextId}`, ...(ngDevMode ? [{ debugName: "groupName" }] : []));
5390
+ groupName = `ng-oat-radio-${++NgOatRadioGroup.nextId}`;
5377
5391
  onSelect(val) {
5378
5392
  this.value.set(val);
5379
5393
  this.touched.set(true);
5380
5394
  }
5381
5395
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: NgOatRadioGroup, deps: [], target: i0.ɵɵFactoryTarget.Component });
5382
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: NgOatRadioGroup, isStandalone: true, selector: "ng-oat-radio-group", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, showErrors: { classPropertyName: "showErrors", publicName: "showErrors", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", touched: "touchedChange" }, ngImport: i0, template: `
5396
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: NgOatRadioGroup, isStandalone: true, selector: "ng-oat-radio-group", inputs: { value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, invalid: { classPropertyName: "invalid", publicName: "invalid", isSignal: true, isRequired: false, transformFunction: null }, errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null }, touched: { classPropertyName: "touched", publicName: "touched", isSignal: true, isRequired: false, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: true, transformFunction: null }, showErrors: { classPropertyName: "showErrors", publicName: "showErrors", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { value: "valueChange", disabled: "disabledChange", touched: "touchedChange" }, providers: [{ provide: NG_OAT_VALUE_HOST, useExisting: forwardRef(() => NgOatRadioGroup) }], ngImport: i0, template: `
5383
5397
  @if (label()) {
5384
5398
  <fieldset>
5385
5399
  <legend>{{ label() }}</legend>
@@ -5387,7 +5401,7 @@ class NgOatRadioGroup {
5387
5401
  <label>
5388
5402
  <input
5389
5403
  type="radio"
5390
- [name]="groupName()"
5404
+ [name]="groupName"
5391
5405
  [value]="opt.value"
5392
5406
  [checked]="opt.value === value()"
5393
5407
  [disabled]="disabled() || (opt.disabled ?? false)"
@@ -5402,7 +5416,7 @@ class NgOatRadioGroup {
5402
5416
  <label>
5403
5417
  <input
5404
5418
  type="radio"
5405
- [name]="groupName()"
5419
+ [name]="groupName"
5406
5420
  [value]="opt.value"
5407
5421
  [checked]="opt.value === value()"
5408
5422
  [disabled]="disabled() || (opt.disabled ?? false)"
@@ -5412,7 +5426,7 @@ class NgOatRadioGroup {
5412
5426
  </label>
5413
5427
  }
5414
5428
  }
5415
- @if (showErrors()) {
5429
+ @if (showErrors() && touched()) {
5416
5430
  @for (err of errors(); track $index) {
5417
5431
  <small class="error">{{ err.message }}</small>
5418
5432
  }
@@ -5421,7 +5435,7 @@ class NgOatRadioGroup {
5421
5435
  }
5422
5436
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: NgOatRadioGroup, decorators: [{
5423
5437
  type: Component,
5424
- args: [{ selector: 'ng-oat-radio-group', template: `
5438
+ args: [{ selector: 'ng-oat-radio-group', providers: [{ provide: NG_OAT_VALUE_HOST, useExisting: forwardRef(() => NgOatRadioGroup) }], template: `
5425
5439
  @if (label()) {
5426
5440
  <fieldset>
5427
5441
  <legend>{{ label() }}</legend>
@@ -5429,7 +5443,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
5429
5443
  <label>
5430
5444
  <input
5431
5445
  type="radio"
5432
- [name]="groupName()"
5446
+ [name]="groupName"
5433
5447
  [value]="opt.value"
5434
5448
  [checked]="opt.value === value()"
5435
5449
  [disabled]="disabled() || (opt.disabled ?? false)"
@@ -5444,7 +5458,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
5444
5458
  <label>
5445
5459
  <input
5446
5460
  type="radio"
5447
- [name]="groupName()"
5461
+ [name]="groupName"
5448
5462
  [value]="opt.value"
5449
5463
  [checked]="opt.value === value()"
5450
5464
  [disabled]="disabled() || (opt.disabled ?? false)"
@@ -5454,35 +5468,54 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
5454
5468
  </label>
5455
5469
  }
5456
5470
  }
5457
- @if (showErrors()) {
5471
+ @if (showErrors() && touched()) {
5458
5472
  @for (err of errors(); track $index) {
5459
5473
  <small class="error">{{ err.message }}</small>
5460
5474
  }
5461
5475
  }
5462
5476
  `, styles: [":host{display:block}fieldset{border:none;padding:0;margin:0}legend{font-size:var(--text-7);font-weight:var(--font-medium);margin-block-end:var(--space-1)}label{display:flex;align-items:center;gap:var(--space-2);margin-block-end:var(--space-1)}.error{color:var(--danger);display:block;font-size:var(--text-8);margin-block-start:var(--space-1)}\n"] }]
5463
- }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], errors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }, { type: i0.Output, args: ["touchedChange"] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], showErrors: [{ type: i0.Input, args: [{ isSignal: true, alias: "showErrors", required: false }] }] } });
5477
+ }], propDecorators: { value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }, { type: i0.Output, args: ["valueChange"] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }, { type: i0.Output, args: ["disabledChange"] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], invalid: [{ type: i0.Input, args: [{ isSignal: true, alias: "invalid", required: false }] }], errors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }], touched: [{ type: i0.Input, args: [{ isSignal: true, alias: "touched", required: false }] }, { type: i0.Output, args: ["touchedChange"] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: true }] }], showErrors: [{ type: i0.Input, args: [{ isSignal: true, alias: "showErrors", required: false }] }] } });
5464
5478
 
5465
5479
  /**
5466
- * Displays validation errors for a Signal Forms field.
5467
- * Shows errors only when the field has been touched and is invalid.
5480
+ * Displays validation errors for form fields.
5468
5481
  *
5469
- * Usage:
5482
+ * **Signal Forms** — pass the `FieldState` directly:
5470
5483
  * ```html
5471
- * <ng-oat-input label="Email" [formField]="myForm.email" />
5472
5484
  * <ng-oat-form-error [control]="myForm.email" />
5473
5485
  * ```
5486
+ *
5487
+ * **Reactive / Template-driven** — pass error strings and a visibility flag:
5488
+ * ```html
5489
+ * <ng-oat-form-error [errors]="['Email is required']" [show]="email.touched && email.invalid" />
5490
+ * ```
5474
5491
  */
5475
5492
  class NgOatFormError {
5476
- control = input.required(...(ngDevMode ? [{ debugName: "control" }] : []));
5493
+ /** Signal Forms FieldState for automatic error display. */
5494
+ control = input(...(ngDevMode ? [undefined, { debugName: "control" }] : []));
5495
+ /** Plain error message strings — for Reactive / Template-driven forms. */
5496
+ errors = input([], ...(ngDevMode ? [{ debugName: "errors" }] : []));
5497
+ /** External visibility flag — for Reactive / Template-driven forms. */
5498
+ show = input(false, ...(ngDevMode ? [{ debugName: "show" }] : []));
5499
+ /** Unified list of error message strings. */
5500
+ errorMessages = computed(() => {
5501
+ const ctrl = this.control();
5502
+ if (ctrl) {
5503
+ return ctrl.errors().map(e => e.message);
5504
+ }
5505
+ return this.errors();
5506
+ }, ...(ngDevMode ? [{ debugName: "errorMessages" }] : []));
5477
5507
  shouldShowError = computed(() => {
5478
- const field = this.control();
5479
- return !field.valid() && field.touched();
5508
+ const ctrl = this.control();
5509
+ if (ctrl) {
5510
+ return !ctrl.valid() && ctrl.touched();
5511
+ }
5512
+ return this.show() && this.errors().length > 0;
5480
5513
  }, ...(ngDevMode ? [{ debugName: "shouldShowError" }] : []));
5481
5514
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: NgOatFormError, deps: [], target: i0.ɵɵFactoryTarget.Component });
5482
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: NgOatFormError, isStandalone: true, selector: "ng-oat-form-error", inputs: { control: { classPropertyName: "control", publicName: "control", isSignal: true, isRequired: true, transformFunction: null } }, ngImport: i0, template: `
5515
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.0", type: NgOatFormError, isStandalone: true, selector: "ng-oat-form-error", inputs: { control: { classPropertyName: "control", publicName: "control", isSignal: true, isRequired: false, transformFunction: null }, errors: { classPropertyName: "errors", publicName: "errors", isSignal: true, isRequired: false, transformFunction: null }, show: { classPropertyName: "show", publicName: "show", isSignal: true, isRequired: false, transformFunction: null } }, ngImport: i0, template: `
5483
5516
  @if (shouldShowError()) {
5484
- @for (error of control().errors(); track error.kind) {
5485
- <small class="error">{{ error.message }}</small>
5517
+ @for (msg of errorMessages(); track $index) {
5518
+ <small class="error">{{ msg }}</small>
5486
5519
  }
5487
5520
  }
5488
5521
  `, isInline: true, styles: [":host{display:block}.error{display:block;font-size:var(--text-8);font-weight:var(--font-normal);color:var(--danger);margin-block-start:var(--space-1)}\n"] });
@@ -5491,12 +5524,175 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImpor
5491
5524
  type: Component,
5492
5525
  args: [{ selector: 'ng-oat-form-error', template: `
5493
5526
  @if (shouldShowError()) {
5494
- @for (error of control().errors(); track error.kind) {
5495
- <small class="error">{{ error.message }}</small>
5527
+ @for (msg of errorMessages(); track $index) {
5528
+ <small class="error">{{ msg }}</small>
5496
5529
  }
5497
5530
  }
5498
5531
  `, styles: [":host{display:block}.error{display:block;font-size:var(--text-8);font-weight:var(--font-normal);color:var(--danger);margin-block-start:var(--space-1)}\n"] }]
5499
- }], propDecorators: { control: [{ type: i0.Input, args: [{ isSignal: true, alias: "control", required: true }] }] } });
5532
+ }], propDecorators: { control: [{ type: i0.Input, args: [{ isSignal: true, alias: "control", required: false }] }], errors: [{ type: i0.Input, args: [{ isSignal: true, alias: "errors", required: false }] }], show: [{ type: i0.Input, args: [{ isSignal: true, alias: "show", required: false }] }] } });
5533
+
5534
+ /**
5535
+ * ControlValueAccessor adapter for ng-oat **value-type** components.
5536
+ *
5537
+ * Auto-activates when a Reactive Forms or Template-driven directive is present
5538
+ * (`formControlName`, `[formControl]`, or `[(ngModel)]`).
5539
+ *
5540
+ * Add this directive to your component's `imports` alongside the form component:
5541
+ * ```typescript
5542
+ * imports: [ReactiveFormsModule, NgOatInput, NgOatValueCva]
5543
+ * ```
5544
+ *
5545
+ * ```html
5546
+ * <ng-oat-input label="Email" formControlName="email" />
5547
+ * <ng-oat-input label="Name" [(ngModel)]="name" />
5548
+ * ```
5549
+ */
5550
+ class NgOatValueCva {
5551
+ host = inject(NG_OAT_VALUE_HOST);
5552
+ onChange = () => { };
5553
+ onTouched = () => { };
5554
+ /**
5555
+ * Listen for native `input` and `change` events that bubble from the inner
5556
+ * `<input>`, `<textarea>`, or `<select>` element up to the host component.
5557
+ */
5558
+ onHostValueChange() {
5559
+ this.onChange(this.host.value());
5560
+ }
5561
+ /** Mark as touched when focus leaves the host element tree. */
5562
+ onHostBlur() {
5563
+ this.host.touched.set(true);
5564
+ this.onTouched();
5565
+ }
5566
+ // --- ControlValueAccessor ---
5567
+ writeValue(val) {
5568
+ this.host.value.set(val ?? '');
5569
+ }
5570
+ registerOnChange(fn) {
5571
+ this.onChange = fn;
5572
+ }
5573
+ registerOnTouched(fn) {
5574
+ this.onTouched = fn;
5575
+ }
5576
+ setDisabledState(isDisabled) {
5577
+ this.host.disabled.set(isDisabled);
5578
+ }
5579
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: NgOatValueCva, deps: [], target: i0.ɵɵFactoryTarget.Directive });
5580
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.0", type: NgOatValueCva, isStandalone: true, selector: "ng-oat-input[formControlName],ng-oat-input[formControl],ng-oat-input[ngModel],\nng-oat-textarea[formControlName],ng-oat-textarea[formControl],ng-oat-textarea[ngModel],\nng-oat-select[formControlName],ng-oat-select[formControl],ng-oat-select[ngModel],\nng-oat-radio-group[formControlName],ng-oat-radio-group[formControl],ng-oat-radio-group[ngModel],\nng-oat-search-input[formControlName],ng-oat-search-input[formControl],ng-oat-search-input[ngModel],\nng-oat-input-otp[formControlName],ng-oat-input-otp[formControl],ng-oat-input-otp[ngModel]", host: { listeners: { "input": "onHostValueChange()", "change": "onHostValueChange()", "focusout": "onHostBlur()" } }, providers: [
5581
+ {
5582
+ provide: NG_VALUE_ACCESSOR,
5583
+ useExisting: forwardRef(() => NgOatValueCva),
5584
+ multi: true,
5585
+ },
5586
+ ], ngImport: i0 });
5587
+ }
5588
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: NgOatValueCva, decorators: [{
5589
+ type: Directive,
5590
+ args: [{
5591
+ selector: `ng-oat-input[formControlName],ng-oat-input[formControl],ng-oat-input[ngModel],
5592
+ ng-oat-textarea[formControlName],ng-oat-textarea[formControl],ng-oat-textarea[ngModel],
5593
+ ng-oat-select[formControlName],ng-oat-select[formControl],ng-oat-select[ngModel],
5594
+ ng-oat-radio-group[formControlName],ng-oat-radio-group[formControl],ng-oat-radio-group[ngModel],
5595
+ ng-oat-search-input[formControlName],ng-oat-search-input[formControl],ng-oat-search-input[ngModel],
5596
+ ng-oat-input-otp[formControlName],ng-oat-input-otp[formControl],ng-oat-input-otp[ngModel]`,
5597
+ providers: [
5598
+ {
5599
+ provide: NG_VALUE_ACCESSOR,
5600
+ useExisting: forwardRef(() => NgOatValueCva),
5601
+ multi: true,
5602
+ },
5603
+ ],
5604
+ }]
5605
+ }], propDecorators: { onHostValueChange: [{
5606
+ type: HostListener,
5607
+ args: ['input']
5608
+ }, {
5609
+ type: HostListener,
5610
+ args: ['change']
5611
+ }], onHostBlur: [{
5612
+ type: HostListener,
5613
+ args: ['focusout']
5614
+ }] } });
5615
+
5616
+ /**
5617
+ * ControlValueAccessor adapter for ng-oat **checkbox-type** components.
5618
+ *
5619
+ * Auto-activates when a Reactive Forms or Template-driven directive is present
5620
+ * (`formControlName`, `[formControl]`, or `[(ngModel)]`).
5621
+ *
5622
+ * Add this directive to your component's `imports` alongside the form component:
5623
+ * ```typescript
5624
+ * imports: [ReactiveFormsModule, NgOatCheckbox, NgOatCheckboxCva]
5625
+ * ```
5626
+ *
5627
+ * ```html
5628
+ * <ng-oat-checkbox label="Accept terms" formControlName="accepted" />
5629
+ * <ng-oat-switch label="Alerts" [(ngModel)]="alertsOn" />
5630
+ * ```
5631
+ */
5632
+ class NgOatCheckboxCva {
5633
+ host = inject(NG_OAT_CHECKBOX_HOST);
5634
+ onChange = () => { };
5635
+ onTouched = () => { };
5636
+ /**
5637
+ * Listen for native `change` events that bubble from the inner
5638
+ * `<input type="checkbox">` up to the host component.
5639
+ */
5640
+ onHostChange() {
5641
+ this.onChange(this.host.checked());
5642
+ this.host.touched.set(true);
5643
+ this.onTouched();
5644
+ }
5645
+ /** Mark as touched when focus leaves the host element tree. */
5646
+ onHostBlur() {
5647
+ this.host.touched.set(true);
5648
+ this.onTouched();
5649
+ }
5650
+ // --- ControlValueAccessor ---
5651
+ writeValue(val) {
5652
+ this.host.checked.set(!!val);
5653
+ }
5654
+ registerOnChange(fn) {
5655
+ this.onChange = fn;
5656
+ }
5657
+ registerOnTouched(fn) {
5658
+ this.onTouched = fn;
5659
+ }
5660
+ setDisabledState(isDisabled) {
5661
+ this.host.disabled.set(isDisabled);
5662
+ }
5663
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: NgOatCheckboxCva, deps: [], target: i0.ɵɵFactoryTarget.Directive });
5664
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "21.2.0", type: NgOatCheckboxCva, isStandalone: true, selector: "ng-oat-checkbox[formControlName],ng-oat-checkbox[formControl],ng-oat-checkbox[ngModel],\nng-oat-switch[formControlName],ng-oat-switch[formControl],ng-oat-switch[ngModel]", host: { listeners: { "change": "onHostChange()", "focusout": "onHostBlur()" } }, providers: [
5665
+ {
5666
+ provide: NG_VALUE_ACCESSOR,
5667
+ useExisting: forwardRef(() => NgOatCheckboxCva),
5668
+ multi: true,
5669
+ },
5670
+ ], ngImport: i0 });
5671
+ }
5672
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.0", ngImport: i0, type: NgOatCheckboxCva, decorators: [{
5673
+ type: Directive,
5674
+ args: [{
5675
+ selector: `ng-oat-checkbox[formControlName],ng-oat-checkbox[formControl],ng-oat-checkbox[ngModel],
5676
+ ng-oat-switch[formControlName],ng-oat-switch[formControl],ng-oat-switch[ngModel]`,
5677
+ providers: [
5678
+ {
5679
+ provide: NG_VALUE_ACCESSOR,
5680
+ useExisting: forwardRef(() => NgOatCheckboxCva),
5681
+ multi: true,
5682
+ },
5683
+ ],
5684
+ }]
5685
+ }], propDecorators: { onHostChange: [{
5686
+ type: HostListener,
5687
+ args: ['change']
5688
+ }], onHostBlur: [{
5689
+ type: HostListener,
5690
+ args: ['focusout']
5691
+ }] } });
5692
+
5693
+ // Forms barrel export
5694
+ /** Convenience array — import both CVA adapters at once. */
5695
+ const NgOatFormsCva = [NgOatValueCva, NgOatCheckboxCva];
5500
5696
 
5501
5697
  // Components barrel export
5502
5698
 
@@ -5517,5 +5713,5 @@ const OAT_VERSION_TOKEN = new InjectionToken('OAT_VERSION', {
5517
5713
  * Generated bundle index. Do not edit.
5518
5714
  */
5519
5715
 
5520
- export { NG_OAT_CHIP_GROUP, NG_OAT_TOGGLE_GROUP, NgOatAccordion, NgOatAlert, NgOatAvatar, NgOatBadge, NgOatBreadcrumb, NgOatButton, NgOatCard, NgOatCardCarousel, NgOatCardFooter, NgOatCardHeader, NgOatCarousel, NgOatCheckbox, NgOatChip, NgOatChipGroup, NgOatChipInput, NgOatDialog, NgOatDialogComponent, NgOatDropdown, NgOatDropdownComponent, NgOatFileUpload, NgOatFormError, NgOatInput, NgOatInputOtp, NgOatMeter, NgOatPagination, NgOatProgress, NgOatRadioGroup, NgOatSearchInput, NgOatSelect, NgOatSeparator, NgOatSidebar, NgOatSidebarComponent, NgOatSkeleton, NgOatSpinner, NgOatSplitButton, NgOatSwitch, NgOatTable, NgOatTabs, NgOatTabsComponent, NgOatTextarea, NgOatThemeRef, NgOatThemeSelector, NgOatThemeSelectorIcon, NgOatToast, NgOatToggle, NgOatToggleGroup, NgOatToolbar, NgOatToolbarRow, NgOatTooltip, NgOatTooltipComponent, OAT_TOKEN_MAP, OAT_VERSION, OAT_VERSION_TOKEN, TOOLTIP_POSITIONER, provideNgOat, provideNgOatTheme };
5716
+ export { NG_OAT_CHECKBOX_HOST, NG_OAT_CHIP_GROUP, NG_OAT_TOGGLE_GROUP, NG_OAT_VALUE_HOST, NgOatAccordion, NgOatAlert, NgOatAvatar, NgOatBadge, NgOatBreadcrumb, NgOatButton, NgOatCard, NgOatCardCarousel, NgOatCardFooter, NgOatCardHeader, NgOatCarousel, NgOatCheckbox, NgOatCheckboxCva, NgOatChip, NgOatChipGroup, NgOatChipInput, NgOatDialog, NgOatDialogComponent, NgOatDropdown, NgOatDropdownComponent, NgOatFileUpload, NgOatFormError, NgOatFormsCva, NgOatInput, NgOatInputOtp, NgOatMeter, NgOatPagination, NgOatProgress, NgOatRadioGroup, NgOatSearchInput, NgOatSelect, NgOatSeparator, NgOatSidebar, NgOatSidebarComponent, NgOatSkeleton, NgOatSpinner, NgOatSplitButton, NgOatSwitch, NgOatTable, NgOatTabs, NgOatTabsComponent, NgOatTextarea, NgOatThemeRef, NgOatThemeSelector, NgOatThemeSelectorIcon, NgOatToast, NgOatToggle, NgOatToggleGroup, NgOatToolbar, NgOatToolbarRow, NgOatTooltip, NgOatTooltipComponent, NgOatValueCva, OAT_TOKEN_MAP, OAT_VERSION, OAT_VERSION_TOKEN, TOOLTIP_POSITIONER, provideNgOat, provideNgOatTheme };
5521
5717
  //# sourceMappingURL=letsprogram-ng-oat.mjs.map