@spartan-ng/brain 0.0.1-alpha.668 → 0.0.1-alpha.669

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,7 +1,7 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { signal, computed, Injectable, input, booleanAttribute, Directive, InjectionToken, inject, Injector, DestroyRef, effect } from '@angular/core';
3
3
  import { NgForm, FormGroupDirective, NgControl } from '@angular/forms';
4
- import { ErrorStateMatcher, ErrorStateTracker } from '@spartan-ng/brain/forms';
4
+ import { ErrorStateMatcher, createStateTracker } from '@spartan-ng/brain/forms';
5
5
 
6
6
  class BrnFieldA11yService {
7
7
  _descriptions = signal([], ...(ngDevMode ? [{ debugName: "_descriptions" }] : []));
@@ -105,26 +105,36 @@ class BrnFieldControl {
105
105
  _field = inject(BrnField, { optional: true });
106
106
  _destroyRef = inject(DestroyRef);
107
107
  _idEffectRef;
108
- _errorStateTracker = new ErrorStateTracker(this._errorStateMatcher, this._parentFormGroup, this._parentForm);
108
+ _stateTracker = signal(null, ...(ngDevMode ? [{ debugName: "_stateTracker" }] : []));
109
+ /** Sentinel value to differentiate "never checked" from "control is null". */
110
+ _lastControl = null;
109
111
  /** Gets the AbstractControlDirective for this control. */
110
112
  ngControl = null;
111
113
  id = signal(undefined, ...(ngDevMode ? [{ debugName: "id" }] : []));
112
- controlState = this._errorStateTracker.controlState;
113
- errors = this._errorStateTracker.errors;
114
- dirty = this._errorStateTracker.dirty;
115
- invalid = this._errorStateTracker.invalid;
116
- spartanInvalid = this._errorStateTracker.spartanInvalid;
117
- touched = this._errorStateTracker.touched;
114
+ controlState = computed(() => this._stateTracker()?.controlState() ?? null, ...(ngDevMode ? [{ debugName: "controlState" }] : []));
115
+ errors = computed(() => this._stateTracker()?.errors() ?? null, ...(ngDevMode ? [{ debugName: "errors" }] : []));
116
+ dirty = computed(() => this._stateTracker()?.dirty() ?? null, ...(ngDevMode ? [{ debugName: "dirty" }] : []));
117
+ invalid = computed(() => this._stateTracker()?.invalid() ?? null, ...(ngDevMode ? [{ debugName: "invalid" }] : []));
118
+ spartanInvalid = computed(() => this._stateTracker()?.spartanInvalid() ?? null, ...(ngDevMode ? [{ debugName: "spartanInvalid" }] : []));
119
+ touched = computed(() => this._stateTracker()?.touched() ?? null, ...(ngDevMode ? [{ debugName: "touched" }] : []));
118
120
  constructor() {
119
121
  this._field?.registerFieldControl(this);
120
122
  this._destroyRef.onDestroy(() => {
121
123
  this._idEffectRef?.destroy();
124
+ this._stateTracker()?.destroy();
122
125
  });
123
126
  }
124
127
  ngOnInit() {
125
128
  this.ngControl = this._injector.get(NgControl, null);
129
+ // Try to sync the tracker eagerly.
130
+ this._syncTracker();
131
+ // For template-driven forms and FormControlName, the control is often resolved
132
+ // asynchronously by Angular's directives after our ngOnInit/ngDoCheck.
133
+ // Schedule a one-time microtask to catch the initial resolution.
126
134
  if (this.ngControl) {
127
- this._errorStateTracker.setControl(this.ngControl);
135
+ Promise.resolve().then(() => {
136
+ this._syncTracker();
137
+ });
128
138
  }
129
139
  const labelable = this._injector.get(BrnLabelable, null);
130
140
  if (labelable) {
@@ -133,6 +143,25 @@ class BrnFieldControl {
133
143
  }, ...(ngDevMode ? [{ debugName: "_idEffectRef", injector: this._injector }] : [{ injector: this._injector }]));
134
144
  }
135
145
  }
146
+ // Re-evaluate the control reference on every change detection cycle because
147
+ // the underlying AbstractControl may change when [formControl] rebinds to a new instance.
148
+ // When the instance changes we tear down the old tracker and create a fresh one.
149
+ ngDoCheck() {
150
+ this._syncTracker();
151
+ }
152
+ /** @returns true if the control reference changed */
153
+ _syncTracker() {
154
+ if (!this.ngControl)
155
+ return;
156
+ const currentControl = this.ngControl.control ?? null;
157
+ if (currentControl === this._lastControl)
158
+ return;
159
+ this._lastControl = currentControl;
160
+ this._stateTracker()?.destroy();
161
+ this._stateTracker.set(currentControl
162
+ ? createStateTracker(this.ngControl, this._errorStateMatcher, this._parentFormGroup, this._parentForm)
163
+ : null);
164
+ }
136
165
  /** @nocollapse */ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.3.17", ngImport: i0, type: BrnFieldControl, deps: [], target: i0.ɵɵFactoryTarget.Directive });
137
166
  /** @nocollapse */ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.3.17", type: BrnFieldControl, isStandalone: true, ngImport: i0 });
138
167
  }
@@ -1 +1 @@
1
- {"version":3,"file":"spartan-ng-brain-field.mjs","sources":["../../../../libs/brain/field/src/lib/brn-field-aria.service.ts","../../../../libs/brain/field/src/lib/brn-field.ts","../../../../libs/brain/field/src/lib/brn-labelable.ts","../../../../libs/brain/field/src/lib/brn-field-control.ts","../../../../libs/brain/field/src/lib/brn-field-control-described-by.ts","../../../../libs/brain/field/src/index.ts","../../../../libs/brain/field/src/spartan-ng-brain-field.ts"],"sourcesContent":["import { computed, Injectable, signal } from '@angular/core';\n\n@Injectable()\nexport class BrnFieldA11yService {\n\tprivate readonly _descriptions = signal<string[]>([]);\n\tprivate readonly _errors = signal<string[]>([]);\n\n\tpublic readonly describedBy = computed(() => {\n\t\tconst ids = [...this._descriptions(), ...this._errors()].filter(Boolean);\n\t\tconst uniqueIds = [...new Set(ids)];\n\t\treturn uniqueIds.length ? uniqueIds.join(' ') : null;\n\t});\n\n\tpublic registerDescription(id: string) {\n\t\tthis._descriptions.update((ids) => (ids.includes(id) ? ids : [...ids, id]));\n\t}\n\n\tpublic unregisterDescription(id: string) {\n\t\tthis._descriptions.update((ids) => ids.filter((value) => value !== id));\n\t}\n\n\tpublic registerError(id: string) {\n\t\tthis._errors.update((ids) => (ids.includes(id) ? ids : [...ids, id]));\n\t}\n\n\tpublic unregisterError(id: string) {\n\t\tthis._errors.update((ids) => ids.filter((value) => value !== id));\n\t}\n}\n","import { BooleanInput } from '@angular/cdk/coercion';\nimport { booleanAttribute, computed, Directive, input, signal } from '@angular/core';\nimport { BrnFieldA11yService } from './brn-field-aria.service';\nimport { BrnFieldControl } from './brn-field-control';\nimport { BrnLabelable } from './brn-labelable';\n\n@Directive({\n\tselector: '[brnField],brn-field',\n\tproviders: [BrnFieldA11yService],\n\thost: {\n\t\t'[attr.data-invalid]': '_invalid() ? \"true\" : null',\n\t\t'[attr.data-matches-spartan-invalid]': '_spartanInvalid() ? \"true\" : null',\n\t\t'[attr.data-touched]': '_touched() ? \"true\" : null',\n\t\t'[attr.data-dirty]': '_dirty() ? \"true\" : null',\n\t},\n})\nexport class BrnField {\n\tprivate readonly _brnFieldControl = signal<BrnFieldControl | null>(null);\n\tprivate readonly _labelable = signal<BrnLabelable | null>(null);\n\n\t/** Whether the field is invalid. Overrides the `data-invalid` attribute. */\n\tpublic readonly dataInvalid = input<boolean, BooleanInput>(false, {\n\t\ttransform: booleanAttribute,\n\t\talias: 'data-invalid',\n\t});\n\n\t/**\n\t * Whether to force the field into an invalid state, regardless of the form control's state.\n\t * Overrides both the `data-invalid` and `data-matches-spartan-invalid` attributes.\n\t */\n\tpublic readonly forceInvalid = input<boolean, BooleanInput>(false, {\n\t\ttransform: booleanAttribute,\n\t});\n\n\tprotected readonly _invalid = computed(() => {\n\t\tif (this.forceInvalid() || this.dataInvalid()) return true;\n\n\t\tconst control = this._brnFieldControl();\n\t\tif (!control || !control.ngControl) return false;\n\n\t\treturn control.controlState()?.invalid;\n\t});\n\n\tprotected readonly _spartanInvalid = computed(() => {\n\t\treturn this.forceInvalid() || (this._brnFieldControl()?.controlState()?.spartanInvalid ?? null);\n\t});\n\n\tprotected readonly _dirty = computed(() => {\n\t\treturn this._brnFieldControl()?.controlState()?.dirty ?? null;\n\t});\n\n\tprotected readonly _touched = computed(() => {\n\t\treturn this._brnFieldControl()?.controlState()?.touched ?? null;\n\t});\n\n\tpublic readonly labelableId = computed(() => this._brnFieldControl()?.id?.() ?? this._labelable()?.labelableId());\n\n\tpublic readonly errors = computed(() => this._brnFieldControl()?.errors() ?? null);\n\tpublic readonly controlState = computed(() => this._brnFieldControl()?.controlState() ?? null);\n\n\tpublic registerFieldControl(fieldControl: BrnFieldControl) {\n\t\tthis._brnFieldControl.set(fieldControl);\n\t}\n\n\tpublic registerLabelable(labelable: BrnLabelable) {\n\t\tthis._labelable.set(labelable);\n\t}\n}\n","import { type ExistingProvider, inject, InjectionToken, type Signal, type Type } from '@angular/core';\n\nexport interface BrnLabelable {\n\tlabelableId: Signal<string | null | undefined>;\n}\n\nexport const BrnLabelable = new InjectionToken<BrnLabelable>('BrnLabelable');\n\nexport function provideBrnLabelable(labelable: Type<BrnLabelable>): ExistingProvider {\n\treturn { provide: BrnLabelable, useExisting: labelable };\n}\n\nexport function injectBrnLabelable(): BrnLabelable | null {\n\treturn inject(BrnLabelable, { optional: true });\n}\n","import { DestroyRef, Directive, effect, inject, Injector, OnInit, signal } from '@angular/core';\nimport { FormGroupDirective, NgControl, NgForm } from '@angular/forms';\nimport { ErrorStateMatcher, ErrorStateTracker } from '@spartan-ng/brain/forms';\nimport { BrnField } from './brn-field';\nimport { BrnLabelable } from './brn-labelable';\n\n@Directive()\nexport class BrnFieldControl implements OnInit {\n\tprivate readonly _injector = inject(Injector);\n\tprivate readonly _errorStateMatcher = inject(ErrorStateMatcher);\n\tprivate readonly _parentForm = inject(NgForm, { optional: true });\n\tprivate readonly _parentFormGroup = inject(FormGroupDirective, { optional: true });\n\tprivate readonly _field = inject(BrnField, { optional: true });\n\tprivate readonly _destroyRef = inject(DestroyRef);\n\n\tprivate _idEffectRef?: ReturnType<typeof effect>;\n\n\tprivate readonly _errorStateTracker = new ErrorStateTracker(\n\t\tthis._errorStateMatcher,\n\t\tthis._parentFormGroup,\n\t\tthis._parentForm,\n\t);\n\n\t/** Gets the AbstractControlDirective for this control. */\n\tpublic ngControl: NgControl | null = null;\n\n\tpublic readonly id = signal<string | null | undefined>(undefined);\n\n\tpublic readonly controlState = this._errorStateTracker.controlState;\n\tpublic readonly errors = this._errorStateTracker.errors;\n\tpublic readonly dirty = this._errorStateTracker.dirty;\n\tpublic readonly invalid = this._errorStateTracker.invalid;\n\tpublic readonly spartanInvalid = this._errorStateTracker.spartanInvalid;\n\tpublic readonly touched = this._errorStateTracker.touched;\n\n\tconstructor() {\n\t\tthis._field?.registerFieldControl(this);\n\t\tthis._destroyRef.onDestroy(() => {\n\t\t\tthis._idEffectRef?.destroy();\n\t\t});\n\t}\n\n\tngOnInit(): void {\n\t\tthis.ngControl = this._injector.get(NgControl, null);\n\t\tif (this.ngControl) {\n\t\t\tthis._errorStateTracker.setControl(this.ngControl);\n\t\t}\n\n\t\tconst labelable = this._injector.get(BrnLabelable, null);\n\t\tif (labelable) {\n\t\t\tthis._idEffectRef = effect(\n\t\t\t\t() => {\n\t\t\t\t\tthis.id.set(labelable.labelableId());\n\t\t\t\t},\n\t\t\t\t{ injector: this._injector },\n\t\t\t);\n\t\t}\n\t}\n}\n","import { computed, Directive, inject, input } from '@angular/core';\nimport { BrnFieldA11yService } from './brn-field-aria.service';\n\n@Directive({\n\tselector: '[brnFieldControlDescribedBy]',\n\thost: {\n\t\t'[attr.aria-describedby]': '_computedDescribedBy()',\n\t},\n})\nexport class BrnFieldControlDescribedBy {\n\tpublic readonly describedBy = input<string | null>(null, { alias: 'aria-describedby' });\n\tprivate readonly _a11y = inject(BrnFieldA11yService, { optional: true });\n\n\tprotected readonly _computedDescribedBy = computed(() => {\n\t\tconst manual = this.describedBy();\n\t\tconst manualList = manual ? manual.split(/\\s+/).filter(Boolean) : [];\n\t\tconst fieldIds = this._a11y?.describedBy() ?? null;\n\t\tconst fieldList = fieldIds ? fieldIds.split(/\\s+/).filter(Boolean) : [];\n\n\t\tconst combined = [...new Set([...manualList, ...fieldList])];\n\t\treturn combined.length ? combined.join(' ') : null;\n\t});\n}\n","import { BrnField } from './lib/brn-field';\nimport { BrnFieldControl } from './lib/brn-field-control';\nimport { BrnFieldControlDescribedBy } from './lib/brn-field-control-described-by';\n\nexport * from './lib/brn-field';\nexport * from './lib/brn-field-aria.service';\nexport * from './lib/brn-field-control';\nexport * from './lib/brn-field-control-described-by';\nexport * from './lib/brn-labelable';\n\nexport const BrnFieldImports = [BrnField, BrnFieldControl, BrnFieldControlDescribedBy] as const;\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"spartan-ng-brain-field.mjs","sources":["../../../../libs/brain/field/src/lib/brn-field-aria.service.ts","../../../../libs/brain/field/src/lib/brn-field.ts","../../../../libs/brain/field/src/lib/brn-labelable.ts","../../../../libs/brain/field/src/lib/brn-field-control.ts","../../../../libs/brain/field/src/lib/brn-field-control-described-by.ts","../../../../libs/brain/field/src/index.ts","../../../../libs/brain/field/src/spartan-ng-brain-field.ts"],"sourcesContent":["import { computed, Injectable, signal } from '@angular/core';\n\n@Injectable()\nexport class BrnFieldA11yService {\n\tprivate readonly _descriptions = signal<string[]>([]);\n\tprivate readonly _errors = signal<string[]>([]);\n\n\tpublic readonly describedBy = computed(() => {\n\t\tconst ids = [...this._descriptions(), ...this._errors()].filter(Boolean);\n\t\tconst uniqueIds = [...new Set(ids)];\n\t\treturn uniqueIds.length ? uniqueIds.join(' ') : null;\n\t});\n\n\tpublic registerDescription(id: string) {\n\t\tthis._descriptions.update((ids) => (ids.includes(id) ? ids : [...ids, id]));\n\t}\n\n\tpublic unregisterDescription(id: string) {\n\t\tthis._descriptions.update((ids) => ids.filter((value) => value !== id));\n\t}\n\n\tpublic registerError(id: string) {\n\t\tthis._errors.update((ids) => (ids.includes(id) ? ids : [...ids, id]));\n\t}\n\n\tpublic unregisterError(id: string) {\n\t\tthis._errors.update((ids) => ids.filter((value) => value !== id));\n\t}\n}\n","import { BooleanInput } from '@angular/cdk/coercion';\nimport { booleanAttribute, computed, Directive, input, signal } from '@angular/core';\nimport { BrnFieldA11yService } from './brn-field-aria.service';\nimport { BrnFieldControl } from './brn-field-control';\nimport { BrnLabelable } from './brn-labelable';\n\n@Directive({\n\tselector: '[brnField],brn-field',\n\tproviders: [BrnFieldA11yService],\n\thost: {\n\t\t'[attr.data-invalid]': '_invalid() ? \"true\" : null',\n\t\t'[attr.data-matches-spartan-invalid]': '_spartanInvalid() ? \"true\" : null',\n\t\t'[attr.data-touched]': '_touched() ? \"true\" : null',\n\t\t'[attr.data-dirty]': '_dirty() ? \"true\" : null',\n\t},\n})\nexport class BrnField {\n\tprivate readonly _brnFieldControl = signal<BrnFieldControl | null>(null);\n\tprivate readonly _labelable = signal<BrnLabelable | null>(null);\n\n\t/** Whether the field is invalid. Overrides the `data-invalid` attribute. */\n\tpublic readonly dataInvalid = input<boolean, BooleanInput>(false, {\n\t\ttransform: booleanAttribute,\n\t\talias: 'data-invalid',\n\t});\n\n\t/**\n\t * Whether to force the field into an invalid state, regardless of the form control's state.\n\t * Overrides both the `data-invalid` and `data-matches-spartan-invalid` attributes.\n\t */\n\tpublic readonly forceInvalid = input<boolean, BooleanInput>(false, {\n\t\ttransform: booleanAttribute,\n\t});\n\n\tprotected readonly _invalid = computed(() => {\n\t\tif (this.forceInvalid() || this.dataInvalid()) return true;\n\n\t\tconst control = this._brnFieldControl();\n\t\tif (!control || !control.ngControl) return false;\n\n\t\treturn control.controlState()?.invalid;\n\t});\n\n\tprotected readonly _spartanInvalid = computed(() => {\n\t\treturn this.forceInvalid() || (this._brnFieldControl()?.controlState()?.spartanInvalid ?? null);\n\t});\n\n\tprotected readonly _dirty = computed(() => {\n\t\treturn this._brnFieldControl()?.controlState()?.dirty ?? null;\n\t});\n\n\tprotected readonly _touched = computed(() => {\n\t\treturn this._brnFieldControl()?.controlState()?.touched ?? null;\n\t});\n\n\tpublic readonly labelableId = computed(() => this._brnFieldControl()?.id?.() ?? this._labelable()?.labelableId());\n\n\tpublic readonly errors = computed(() => this._brnFieldControl()?.errors() ?? null);\n\tpublic readonly controlState = computed(() => this._brnFieldControl()?.controlState() ?? null);\n\n\tpublic registerFieldControl(fieldControl: BrnFieldControl) {\n\t\tthis._brnFieldControl.set(fieldControl);\n\t}\n\n\tpublic registerLabelable(labelable: BrnLabelable) {\n\t\tthis._labelable.set(labelable);\n\t}\n}\n","import { type ExistingProvider, inject, InjectionToken, type Signal, type Type } from '@angular/core';\n\nexport interface BrnLabelable {\n\tlabelableId: Signal<string | null | undefined>;\n}\n\nexport const BrnLabelable = new InjectionToken<BrnLabelable>('BrnLabelable');\n\nexport function provideBrnLabelable(labelable: Type<BrnLabelable>): ExistingProvider {\n\treturn { provide: BrnLabelable, useExisting: labelable };\n}\n\nexport function injectBrnLabelable(): BrnLabelable | null {\n\treturn inject(BrnLabelable, { optional: true });\n}\n","import { computed, DestroyRef, Directive, DoCheck, effect, inject, Injector, OnInit, signal } from '@angular/core';\nimport { type AbstractControl, FormGroupDirective, NgControl, NgForm } from '@angular/forms';\nimport { createStateTracker, ErrorStateMatcher, type StateTracker } from '@spartan-ng/brain/forms';\nimport { BrnField } from './brn-field';\nimport { BrnLabelable } from './brn-labelable';\n\n@Directive()\nexport class BrnFieldControl implements OnInit, DoCheck {\n\tprivate readonly _injector = inject(Injector);\n\tprivate readonly _errorStateMatcher = inject(ErrorStateMatcher);\n\tprivate readonly _parentForm = inject(NgForm, { optional: true });\n\tprivate readonly _parentFormGroup = inject(FormGroupDirective, { optional: true });\n\tprivate readonly _field = inject(BrnField, { optional: true });\n\tprivate readonly _destroyRef = inject(DestroyRef);\n\n\tprivate _idEffectRef?: ReturnType<typeof effect>;\n\tprivate readonly _stateTracker = signal<StateTracker | null>(null);\n\t/** Sentinel value to differentiate \"never checked\" from \"control is null\". */\n\tprivate _lastControl: AbstractControl | null = null;\n\n\t/** Gets the AbstractControlDirective for this control. */\n\tpublic ngControl: NgControl | null = null;\n\n\tpublic readonly id = signal<string | null | undefined>(undefined);\n\n\tpublic readonly controlState = computed(() => this._stateTracker()?.controlState() ?? null);\n\tpublic readonly errors = computed(() => this._stateTracker()?.errors() ?? null);\n\tpublic readonly dirty = computed(() => this._stateTracker()?.dirty() ?? null);\n\tpublic readonly invalid = computed(() => this._stateTracker()?.invalid() ?? null);\n\tpublic readonly spartanInvalid = computed(() => this._stateTracker()?.spartanInvalid() ?? null);\n\tpublic readonly touched = computed(() => this._stateTracker()?.touched() ?? null);\n\n\tconstructor() {\n\t\tthis._field?.registerFieldControl(this);\n\t\tthis._destroyRef.onDestroy(() => {\n\t\t\tthis._idEffectRef?.destroy();\n\t\t\tthis._stateTracker()?.destroy();\n\t\t});\n\t}\n\n\tngOnInit(): void {\n\t\tthis.ngControl = this._injector.get(NgControl, null);\n\n\t\t// Try to sync the tracker eagerly.\n\t\tthis._syncTracker();\n\n\t\t// For template-driven forms and FormControlName, the control is often resolved\n\t\t// asynchronously by Angular's directives after our ngOnInit/ngDoCheck.\n\t\t// Schedule a one-time microtask to catch the initial resolution.\n\t\tif (this.ngControl) {\n\t\t\tPromise.resolve().then(() => {\n\t\t\t\tthis._syncTracker();\n\t\t\t});\n\t\t}\n\n\t\tconst labelable = this._injector.get(BrnLabelable, null);\n\t\tif (labelable) {\n\t\t\tthis._idEffectRef = effect(\n\t\t\t\t() => {\n\t\t\t\t\tthis.id.set(labelable.labelableId());\n\t\t\t\t},\n\t\t\t\t{ injector: this._injector },\n\t\t\t);\n\t\t}\n\t}\n\n\t// Re-evaluate the control reference on every change detection cycle because\n\t// the underlying AbstractControl may change when [formControl] rebinds to a new instance.\n\t// When the instance changes we tear down the old tracker and create a fresh one.\n\tngDoCheck(): void {\n\t\tthis._syncTracker();\n\t}\n\n\t/** @returns true if the control reference changed */\n\tprivate _syncTracker(): void {\n\t\tif (!this.ngControl) return;\n\t\tconst currentControl = this.ngControl.control ?? null;\n\t\tif (currentControl === this._lastControl) return;\n\t\tthis._lastControl = currentControl;\n\t\tthis._stateTracker()?.destroy();\n\t\tthis._stateTracker.set(\n\t\t\tcurrentControl\n\t\t\t\t? createStateTracker(this.ngControl, this._errorStateMatcher, this._parentFormGroup, this._parentForm)\n\t\t\t\t: null,\n\t\t);\n\t}\n}\n","import { computed, Directive, inject, input } from '@angular/core';\nimport { BrnFieldA11yService } from './brn-field-aria.service';\n\n@Directive({\n\tselector: '[brnFieldControlDescribedBy]',\n\thost: {\n\t\t'[attr.aria-describedby]': '_computedDescribedBy()',\n\t},\n})\nexport class BrnFieldControlDescribedBy {\n\tpublic readonly describedBy = input<string | null>(null, { alias: 'aria-describedby' });\n\tprivate readonly _a11y = inject(BrnFieldA11yService, { optional: true });\n\n\tprotected readonly _computedDescribedBy = computed(() => {\n\t\tconst manual = this.describedBy();\n\t\tconst manualList = manual ? manual.split(/\\s+/).filter(Boolean) : [];\n\t\tconst fieldIds = this._a11y?.describedBy() ?? null;\n\t\tconst fieldList = fieldIds ? fieldIds.split(/\\s+/).filter(Boolean) : [];\n\n\t\tconst combined = [...new Set([...manualList, ...fieldList])];\n\t\treturn combined.length ? combined.join(' ') : null;\n\t});\n}\n","import { BrnField } from './lib/brn-field';\nimport { BrnFieldControl } from './lib/brn-field-control';\nimport { BrnFieldControlDescribedBy } from './lib/brn-field-control-described-by';\n\nexport * from './lib/brn-field';\nexport * from './lib/brn-field-aria.service';\nexport * from './lib/brn-field-control';\nexport * from './lib/brn-field-control-described-by';\nexport * from './lib/brn-labelable';\n\nexport const BrnFieldImports = [BrnField, BrnFieldControl, BrnFieldControlDescribedBy] as const;\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
@@ -1,9 +1,120 @@
1
1
  import * as i0 from '@angular/core';
2
- import { Injectable, computed } from '@angular/core';
2
+ import { signal, computed, isSignal, Injectable } from '@angular/core';
3
3
  import { NgForm } from '@angular/forms';
4
- import { toSignal } from '@angular/core/rxjs-interop';
5
- import { BehaviorSubject, of } from 'rxjs';
6
- import { switchMap, startWith, map, catchError } from 'rxjs/operators';
4
+
5
+ function controlStateEqual(a, b) {
6
+ return (a === b ||
7
+ (a != null &&
8
+ b != null &&
9
+ a.dirty === b.dirty &&
10
+ a.invalid === b.invalid &&
11
+ a.touched === b.touched &&
12
+ a.spartanInvalid === b.spartanInvalid &&
13
+ a.errors === b.errors));
14
+ }
15
+
16
+ class ReactiveStateTracker {
17
+ _ngControl;
18
+ _matcher;
19
+ _parentFormGroup;
20
+ _parentForm;
21
+ _stateVersion = signal(0, ...(ngDevMode ? [{ debugName: "_stateVersion" }] : []));
22
+ _eventsSubscription;
23
+ controlState = computed(() => {
24
+ const control = this._ngControl.control;
25
+ if (!control)
26
+ return null;
27
+ this._stateVersion();
28
+ const spartanInvalid = this._matcher?.isInvalid(control, this._controlParent) ?? false;
29
+ return {
30
+ dirty: control.dirty,
31
+ errors: control.errors,
32
+ invalid: control.invalid,
33
+ spartanInvalid,
34
+ touched: control.touched,
35
+ };
36
+ }, ...(ngDevMode ? [{ debugName: "controlState", equal: controlStateEqual }] : [{ equal: controlStateEqual }]));
37
+ errors = computed(() => this.controlState()?.errors ?? null, ...(ngDevMode ? [{ debugName: "errors" }] : []));
38
+ dirty = computed(() => this.controlState()?.dirty ?? null, ...(ngDevMode ? [{ debugName: "dirty" }] : []));
39
+ invalid = computed(() => this.controlState()?.invalid ?? null, ...(ngDevMode ? [{ debugName: "invalid" }] : []));
40
+ spartanInvalid = computed(() => this.controlState()?.spartanInvalid ?? null, ...(ngDevMode ? [{ debugName: "spartanInvalid" }] : []));
41
+ touched = computed(() => this.controlState()?.touched ?? null, ...(ngDevMode ? [{ debugName: "touched" }] : []));
42
+ get _controlParent() {
43
+ return this._parentFormGroup || this._parentForm;
44
+ }
45
+ constructor(_ngControl, _matcher, _parentFormGroup, _parentForm) {
46
+ this._ngControl = _ngControl;
47
+ this._matcher = _matcher;
48
+ this._parentFormGroup = _parentFormGroup;
49
+ this._parentForm = _parentForm;
50
+ const control = _ngControl.control;
51
+ if (control) {
52
+ this._eventsSubscription = control.events.subscribe(() => {
53
+ this._stateVersion.update((v) => v + 1);
54
+ });
55
+ }
56
+ }
57
+ destroy() {
58
+ this._eventsSubscription?.unsubscribe();
59
+ }
60
+ }
61
+
62
+ class SignalStateTracker {
63
+ _ngControl;
64
+ _matcher;
65
+ _parentFormGroup;
66
+ _parentForm;
67
+ _control = computed(() => {
68
+ const control = this._ngControl.control;
69
+ if (!control)
70
+ return null;
71
+ return control;
72
+ }, ...(ngDevMode ? [{ debugName: "_control" }] : []));
73
+ // With signal forms, AbstractControl is implemented by InteropNgControl, whose control state
74
+ // properties (e.g. dirty, touched, invalid) are getter functions that internally read FormField state signals.
75
+ // Accessing them inside a computed() therefore creates reactive signal dependencies automatically.
76
+ // See: https://github.com/angular/angular/blob/39e382a756b552d2b7bd3ce2c364daee9d7a0056/packages/forms/signals/src/controls/interop_ng_control.ts#L68-L129
77
+ dirty = computed(() => this._control()?.dirty ?? null, ...(ngDevMode ? [{ debugName: "dirty" }] : []));
78
+ touched = computed(() => this._control()?.touched ?? null, ...(ngDevMode ? [{ debugName: "touched" }] : []));
79
+ invalid = computed(() => this._control()?.invalid ?? null, ...(ngDevMode ? [{ debugName: "invalid" }] : []));
80
+ errors = computed(() => this._control()?.errors ?? null, ...(ngDevMode ? [{ debugName: "errors" }] : []));
81
+ spartanInvalid = computed(() => {
82
+ const control = this._control();
83
+ if (!control) {
84
+ return null;
85
+ }
86
+ return this._matcher?.isInvalid(control, this._controlParent) ?? false;
87
+ }, ...(ngDevMode ? [{ debugName: "spartanInvalid" }] : []));
88
+ controlState = computed(() => {
89
+ const dirty = this.dirty();
90
+ const invalid = this.invalid();
91
+ const touched = this.touched();
92
+ const spartanInvalid = this.spartanInvalid();
93
+ const errors = this.errors();
94
+ if (dirty === null || invalid === null || touched === null || spartanInvalid === null)
95
+ return null;
96
+ return { dirty, errors, invalid, spartanInvalid, touched };
97
+ }, ...(ngDevMode ? [{ debugName: "controlState", equal: controlStateEqual }] : [{ equal: controlStateEqual }]));
98
+ get _controlParent() {
99
+ return this._parentFormGroup || this._parentForm;
100
+ }
101
+ constructor(_ngControl, _matcher, _parentFormGroup, _parentForm) {
102
+ this._ngControl = _ngControl;
103
+ this._matcher = _matcher;
104
+ this._parentFormGroup = _parentFormGroup;
105
+ this._parentForm = _parentForm;
106
+ }
107
+ destroy() {
108
+ // No subscriptions to clean up for signal-based controls
109
+ }
110
+ }
111
+
112
+ function createStateTracker(ngControl, matcher, parentFormGroup, parentForm) {
113
+ if (ngControl.control && 'field' in ngControl.control && isSignal(ngControl.control.field)) {
114
+ return new SignalStateTracker(ngControl, matcher, parentFormGroup, parentForm);
115
+ }
116
+ return new ReactiveStateTracker(ngControl, matcher, parentFormGroup, parentForm);
117
+ }
7
118
 
8
119
  /**
9
120
  * Provides a custom {@link ErrorStateMatcher} at the component or application level.
@@ -57,63 +168,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.3.17", ngImpo
57
168
  args: [{ providedIn: 'root' }]
58
169
  }] });
59
170
 
60
- class ErrorStateTracker {
61
- _matcher;
62
- _parentFormGroup;
63
- _parentForm;
64
- _ngControl = new BehaviorSubject(null);
65
- controlState = toSignal(this._ngControl.pipe(switchMap((ngControl) => {
66
- if (!ngControl)
67
- return of(null);
68
- const control = ngControl.control;
69
- if (!control) {
70
- throw new Error(`ErrorStateTracker: ngControl.control is null or undefined. This usually means the form control directive (e.g. formControlName or ngModel) ` +
71
- `found an NgControl in the DI tree but no ControlValueAccessor was registered to back it. ` +
72
- `Ensure the host component or directive provides NG_VALUE_ACCESSOR (e.g. { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => YourComponent), multi: true }) ` +
73
- `and implements the ControlValueAccessor interface.`);
74
- }
75
- return control.events.pipe(startWith(() => this._getState(control)), map(() => this._getState(control)));
76
- }), catchError((error) => {
77
- console.error(error);
78
- return of(null);
79
- })), {
80
- requireSync: true,
81
- equal: (a, b) => JSON.stringify(a) === JSON.stringify(b),
82
- });
83
- errors = computed(() => this.controlState()?.errors ?? null, ...(ngDevMode ? [{ debugName: "errors" }] : []));
84
- dirty = computed(() => this.controlState()?.dirty ?? null, ...(ngDevMode ? [{ debugName: "dirty" }] : []));
85
- invalid = computed(() => this.controlState()?.invalid ?? null, ...(ngDevMode ? [{ debugName: "invalid" }] : []));
86
- spartanInvalid = computed(() => this.controlState()?.spartanInvalid ?? null, ...(ngDevMode ? [{ debugName: "spartanInvalid" }] : []));
87
- touched = computed(() => this.controlState()?.touched ?? null, ...(ngDevMode ? [{ debugName: "touched" }] : []));
88
- get _controlParent() {
89
- return this._parentFormGroup || this._parentForm;
90
- }
91
- constructor(_matcher, _parentFormGroup, _parentForm) {
92
- this._matcher = _matcher;
93
- this._parentFormGroup = _parentFormGroup;
94
- this._parentForm = _parentForm;
95
- }
96
- setControl(ngControl) {
97
- // Wait for next tick so the ngControl.control property gets initialized
98
- Promise.resolve().then(() => {
99
- this._ngControl.next(ngControl);
100
- });
101
- }
102
- _getState(control) {
103
- const spartanInvalid = this._matcher?.isInvalid(control, this._controlParent) ?? false;
104
- return {
105
- dirty: control.dirty,
106
- errors: control.errors,
107
- invalid: control.invalid,
108
- spartanInvalid: spartanInvalid,
109
- touched: control.touched,
110
- };
111
- }
112
- }
113
-
114
171
  /**
115
172
  * Generated bundle index. Do not edit.
116
173
  */
117
174
 
118
- export { ErrorStateMatcher, ErrorStateTracker, ShowOnDirtyErrorStateMatcher, provideErrorStateMatcher };
175
+ export { ErrorStateMatcher, ReactiveStateTracker, ShowOnDirtyErrorStateMatcher, SignalStateTracker, controlStateEqual, createStateTracker, provideErrorStateMatcher };
119
176
  //# sourceMappingURL=spartan-ng-brain-forms.mjs.map
@@ -1 +1 @@
1
- {"version":3,"file":"spartan-ng-brain-forms.mjs","sources":["../../../../libs/brain/forms/src/lib/error-options.ts","../../../../libs/brain/forms/src/lib/error-state-tracker.ts","../../../../libs/brain/forms/src/spartan-ng-brain-forms.ts"],"sourcesContent":["import { Injectable, type Provider, type Type } from '@angular/core';\nimport { type AbstractControl, type FormGroupDirective, NgForm } from '@angular/forms';\n\n/**\n * Provides a custom {@link ErrorStateMatcher} at the component or application level.\n *\n * @example <caption>Application-level (appConfig)</caption>\n * ```ts\n * provideErrorStateMatcher(ShowOnDirtyErrorStateMatcher)\n * ```\n *\n * @example <caption>Component-level</caption>\n * ```ts\n * providers: [provideErrorStateMatcher(ShowOnDirtyErrorStateMatcher)]\n * ```\n */\nexport function provideErrorStateMatcher(matcher: Type<ErrorStateMatcher>): Provider {\n\treturn { provide: ErrorStateMatcher, useClass: matcher };\n}\n\n/** Error state matcher that matches when a control is invalid and dirty. */\n@Injectable()\nexport class ShowOnDirtyErrorStateMatcher implements ErrorStateMatcher {\n\tisInvalid(control: AbstractControl | null, form: FormGroupDirective | NgForm | null): boolean {\n\t\treturn !!(control && control.invalid && (control.dirty || (form instanceof NgForm && form.submitted)));\n\t}\n}\n\n/**\n * Determines when a form control should be considered in an invalid (error) state.\n *\n * The return value of `isInvalid` is reflected as the `data-matches-spartan-invalid` attribute\n * on field components, which drives both error styling and the visibility of `HlmFieldError`\n * messages.\n *\n * The default implementation matches when the control is invalid and either touched or, for\n * template-driven forms, the parent `NgForm` has been submitted.\n *\n * Provide a custom implementation (e.g. `ShowOnDirtyErrorStateMatcher`) at the component or\n * application level to change when errors are shown.\n */\n@Injectable({ providedIn: 'root' })\nexport class ErrorStateMatcher {\n\tisInvalid(control: AbstractControl | null, form: FormGroupDirective | NgForm | null): boolean {\n\t\treturn !!(control && control.invalid && (control.touched || (form instanceof NgForm && form.submitted)));\n\t}\n}\n","import { computed } from '@angular/core';\nimport { toSignal } from '@angular/core/rxjs-interop';\nimport type { AbstractControl, FormGroupDirective, NgControl, NgForm } from '@angular/forms';\nimport { BehaviorSubject, of } from 'rxjs';\nimport { catchError, map, startWith, switchMap } from 'rxjs/operators';\nimport type { ControlState } from './control-state';\nimport type { ErrorStateMatcher } from './error-options';\n\nexport class ErrorStateTracker {\n\tprivate readonly _ngControl = new BehaviorSubject<NgControl | null>(null);\n\n\tpublic readonly controlState = toSignal<ControlState | null>(\n\t\tthis._ngControl.pipe(\n\t\t\tswitchMap((ngControl) => {\n\t\t\t\tif (!ngControl) return of(null);\n\n\t\t\t\tconst control = ngControl.control as AbstractControl | null | undefined;\n\t\t\t\tif (!control) {\n\t\t\t\t\tthrow new Error(\n\t\t\t\t\t\t`ErrorStateTracker: ngControl.control is null or undefined. This usually means the form control directive (e.g. formControlName or ngModel) ` +\n\t\t\t\t\t\t\t`found an NgControl in the DI tree but no ControlValueAccessor was registered to back it. ` +\n\t\t\t\t\t\t\t`Ensure the host component or directive provides NG_VALUE_ACCESSOR (e.g. { provide: NG_VALUE_ACCESSOR, useExisting: forwardRef(() => YourComponent), multi: true }) ` +\n\t\t\t\t\t\t\t`and implements the ControlValueAccessor interface.`,\n\t\t\t\t\t);\n\t\t\t\t}\n\n\t\t\t\treturn control.events.pipe(\n\t\t\t\t\tstartWith(() => this._getState(control)),\n\t\t\t\t\tmap(() => this._getState(control)),\n\t\t\t\t);\n\t\t\t}),\n\t\t\tcatchError((error) => {\n\t\t\t\tconsole.error(error);\n\t\t\t\treturn of(null);\n\t\t\t}),\n\t\t),\n\t\t{\n\t\t\trequireSync: true,\n\t\t\tequal: (a, b) => JSON.stringify(a) === JSON.stringify(b),\n\t\t},\n\t);\n\n\tpublic readonly errors = computed(() => this.controlState()?.errors ?? null);\n\tpublic readonly dirty = computed(() => this.controlState()?.dirty ?? null);\n\tpublic readonly invalid = computed(() => this.controlState()?.invalid ?? null);\n\tpublic readonly spartanInvalid = computed(() => this.controlState()?.spartanInvalid ?? null);\n\tpublic readonly touched = computed(() => this.controlState()?.touched ?? null);\n\n\tprivate get _controlParent() {\n\t\treturn this._parentFormGroup || this._parentForm;\n\t}\n\n\tconstructor(\n\t\tprivate readonly _matcher: ErrorStateMatcher | null,\n\t\tprivate readonly _parentFormGroup: FormGroupDirective | null,\n\t\tprivate readonly _parentForm: NgForm | null,\n\t) {}\n\n\tpublic setControl(ngControl: NgControl): void {\n\t\t// Wait for next tick so the ngControl.control property gets initialized\n\t\tPromise.resolve().then(() => {\n\t\t\tthis._ngControl.next(ngControl);\n\t\t});\n\t}\n\n\tprivate _getState(control: AbstractControl): ControlState {\n\t\tconst spartanInvalid = this._matcher?.isInvalid(control, this._controlParent) ?? false;\n\n\t\treturn {\n\t\t\tdirty: control.dirty,\n\t\t\terrors: control.errors,\n\t\t\tinvalid: control.invalid,\n\t\t\tspartanInvalid: spartanInvalid,\n\t\t\ttouched: control.touched,\n\t\t};\n\t}\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"spartan-ng-brain-forms.mjs","sources":["../../../../libs/brain/forms/src/lib/state-tracker.ts","../../../../libs/brain/forms/src/lib/reactive-state-tracker.ts","../../../../libs/brain/forms/src/lib/signal-state-tracker.ts","../../../../libs/brain/forms/src/lib/create-state-tracker.ts","../../../../libs/brain/forms/src/lib/error-options.ts","../../../../libs/brain/forms/src/spartan-ng-brain-forms.ts"],"sourcesContent":["import type { Signal } from '@angular/core';\nimport type { ValidationErrors } from '@angular/forms';\nimport type { ControlState } from './control-state';\n\nexport interface StateTracker {\n\treadonly controlState: Signal<ControlState | null>;\n\treadonly errors: Signal<ValidationErrors | null>;\n\treadonly dirty: Signal<boolean | null>;\n\treadonly invalid: Signal<boolean | null>;\n\treadonly spartanInvalid: Signal<boolean | null>;\n\treadonly touched: Signal<boolean | null>;\n\n\tdestroy(): void;\n}\n\nexport function controlStateEqual(a: ControlState | null, b: ControlState | null): boolean {\n\treturn (\n\t\ta === b ||\n\t\t(a != null &&\n\t\t\tb != null &&\n\t\t\ta.dirty === b.dirty &&\n\t\t\ta.invalid === b.invalid &&\n\t\t\ta.touched === b.touched &&\n\t\t\ta.spartanInvalid === b.spartanInvalid &&\n\t\t\ta.errors === b.errors)\n\t);\n}\n","import { computed, signal } from '@angular/core';\nimport type { AbstractControl, FormGroupDirective, NgControl, NgForm } from '@angular/forms';\nimport type { Subscription } from 'rxjs';\nimport type { ControlState } from './control-state';\nimport type { ErrorStateMatcher } from './error-options';\nimport { controlStateEqual, type StateTracker } from './state-tracker';\n\nexport class ReactiveStateTracker implements StateTracker {\n\tprivate readonly _stateVersion = signal(0);\n\tprivate readonly _eventsSubscription?: Subscription;\n\n\tpublic readonly controlState = computed<ControlState | null>(\n\t\t() => {\n\t\t\tconst control = this._ngControl.control as AbstractControl | null | undefined;\n\t\t\tif (!control) return null;\n\n\t\t\tthis._stateVersion();\n\n\t\t\tconst spartanInvalid = this._matcher?.isInvalid(control, this._controlParent) ?? false;\n\n\t\t\treturn {\n\t\t\t\tdirty: control.dirty,\n\t\t\t\terrors: control.errors,\n\t\t\t\tinvalid: control.invalid,\n\t\t\t\tspartanInvalid,\n\t\t\t\ttouched: control.touched,\n\t\t\t};\n\t\t},\n\t\t{ equal: controlStateEqual },\n\t);\n\n\tpublic readonly errors = computed(() => this.controlState()?.errors ?? null);\n\tpublic readonly dirty = computed(() => this.controlState()?.dirty ?? null);\n\tpublic readonly invalid = computed(() => this.controlState()?.invalid ?? null);\n\tpublic readonly spartanInvalid = computed(() => this.controlState()?.spartanInvalid ?? null);\n\tpublic readonly touched = computed(() => this.controlState()?.touched ?? null);\n\n\tprivate get _controlParent() {\n\t\treturn this._parentFormGroup || this._parentForm;\n\t}\n\n\tconstructor(\n\t\tprivate readonly _ngControl: NgControl,\n\t\tprivate readonly _matcher: ErrorStateMatcher | null,\n\t\tprivate readonly _parentFormGroup: FormGroupDirective | null,\n\t\tprivate readonly _parentForm: NgForm | null,\n\t) {\n\t\tconst control = _ngControl.control;\n\t\tif (control) {\n\t\t\tthis._eventsSubscription = control.events.subscribe(() => {\n\t\t\t\tthis._stateVersion.update((v) => v + 1);\n\t\t\t});\n\t\t}\n\t}\n\n\tpublic destroy(): void {\n\t\tthis._eventsSubscription?.unsubscribe();\n\t}\n}\n","import { computed } from '@angular/core';\nimport type { AbstractControl, FormGroupDirective, NgControl, NgForm } from '@angular/forms';\nimport type { ControlState } from './control-state';\nimport type { ErrorStateMatcher } from './error-options';\nimport { controlStateEqual, type StateTracker } from './state-tracker';\n\nexport class SignalStateTracker implements StateTracker {\n\tprivate readonly _control = computed(() => {\n\t\tconst control = this._ngControl.control as AbstractControl | null | undefined;\n\t\tif (!control) return null;\n\t\treturn control;\n\t});\n\t// With signal forms, AbstractControl is implemented by InteropNgControl, whose control state\n\t// properties (e.g. dirty, touched, invalid) are getter functions that internally read FormField state signals.\n\t// Accessing them inside a computed() therefore creates reactive signal dependencies automatically.\n\t// See: https://github.com/angular/angular/blob/39e382a756b552d2b7bd3ce2c364daee9d7a0056/packages/forms/signals/src/controls/interop_ng_control.ts#L68-L129\n\tpublic readonly dirty = computed<boolean | null>(() => this._control()?.dirty ?? null);\n\tpublic readonly touched = computed<boolean | null>(() => this._control()?.touched ?? null);\n\tpublic readonly invalid = computed<boolean | null>(() => this._control()?.invalid ?? null);\n\tpublic readonly errors = computed(() => this._control()?.errors ?? null);\n\tpublic readonly spartanInvalid = computed<boolean | null>(() => {\n\t\tconst control = this._control();\n\t\tif (!control) {\n\t\t\treturn null;\n\t\t}\n\t\treturn this._matcher?.isInvalid(control, this._controlParent) ?? false;\n\t});\n\n\tpublic readonly controlState = computed<ControlState | null>(\n\t\t() => {\n\t\t\tconst dirty = this.dirty();\n\t\t\tconst invalid = this.invalid();\n\t\t\tconst touched = this.touched();\n\t\t\tconst spartanInvalid = this.spartanInvalid();\n\t\t\tconst errors = this.errors();\n\t\t\tif (dirty === null || invalid === null || touched === null || spartanInvalid === null) return null;\n\t\t\treturn { dirty, errors, invalid, spartanInvalid, touched };\n\t\t},\n\t\t{ equal: controlStateEqual },\n\t);\n\n\tprivate get _controlParent() {\n\t\treturn this._parentFormGroup || this._parentForm;\n\t}\n\n\tconstructor(\n\t\tprivate readonly _ngControl: NgControl,\n\t\tprivate readonly _matcher: ErrorStateMatcher | null,\n\t\tprivate readonly _parentFormGroup: FormGroupDirective | null,\n\t\tprivate readonly _parentForm: NgForm | null,\n\t) {}\n\n\tpublic destroy(): void {\n\t\t// No subscriptions to clean up for signal-based controls\n\t}\n}\n","import { isSignal } from '@angular/core';\nimport type { FormGroupDirective, NgControl, NgForm } from '@angular/forms';\nimport type { ErrorStateMatcher } from './error-options';\nimport { ReactiveStateTracker } from './reactive-state-tracker';\nimport { SignalStateTracker } from './signal-state-tracker';\nimport type { StateTracker } from './state-tracker';\n\nexport function createStateTracker(\n\tngControl: NgControl,\n\tmatcher: ErrorStateMatcher | null,\n\tparentFormGroup: FormGroupDirective | null,\n\tparentForm: NgForm | null,\n): StateTracker {\n\tif (ngControl.control && 'field' in ngControl.control && isSignal(ngControl.control.field)) {\n\t\treturn new SignalStateTracker(ngControl, matcher, parentFormGroup, parentForm);\n\t}\n\treturn new ReactiveStateTracker(ngControl, matcher, parentFormGroup, parentForm);\n}\n","import { Injectable, type Provider, type Type } from '@angular/core';\nimport { type AbstractControl, type FormGroupDirective, NgForm } from '@angular/forms';\n\n/**\n * Provides a custom {@link ErrorStateMatcher} at the component or application level.\n *\n * @example <caption>Application-level (appConfig)</caption>\n * ```ts\n * provideErrorStateMatcher(ShowOnDirtyErrorStateMatcher)\n * ```\n *\n * @example <caption>Component-level</caption>\n * ```ts\n * providers: [provideErrorStateMatcher(ShowOnDirtyErrorStateMatcher)]\n * ```\n */\nexport function provideErrorStateMatcher(matcher: Type<ErrorStateMatcher>): Provider {\n\treturn { provide: ErrorStateMatcher, useClass: matcher };\n}\n\n/** Error state matcher that matches when a control is invalid and dirty. */\n@Injectable()\nexport class ShowOnDirtyErrorStateMatcher implements ErrorStateMatcher {\n\tisInvalid(control: AbstractControl | null, form: FormGroupDirective | NgForm | null): boolean {\n\t\treturn !!(control && control.invalid && (control.dirty || (form instanceof NgForm && form.submitted)));\n\t}\n}\n\n/**\n * Determines when a form control should be considered in an invalid (error) state.\n *\n * The return value of `isInvalid` is reflected as the `data-matches-spartan-invalid` attribute\n * on field components, which drives both error styling and the visibility of `HlmFieldError`\n * messages.\n *\n * The default implementation matches when the control is invalid and either touched or, for\n * template-driven forms, the parent `NgForm` has been submitted.\n *\n * Provide a custom implementation (e.g. `ShowOnDirtyErrorStateMatcher`) at the component or\n * application level to change when errors are shown.\n */\n@Injectable({ providedIn: 'root' })\nexport class ErrorStateMatcher {\n\tisInvalid(control: AbstractControl | null, form: FormGroupDirective | NgForm | null): boolean {\n\t\treturn !!(control && control.invalid && (control.touched || (form instanceof NgForm && form.submitted)));\n\t}\n}\n","/**\n * Generated bundle index. Do not edit.\n */\n\nexport * from './index';\n"],"names":[],"mappings":"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"}
package/field/index.d.ts CHANGED
@@ -2,10 +2,10 @@ import * as _spartan_ng_brain_forms from '@spartan-ng/brain/forms';
2
2
  import * as _angular_forms from '@angular/forms';
3
3
  import { NgControl } from '@angular/forms';
4
4
  import * as _angular_core from '@angular/core';
5
- import { OnInit, Signal, InjectionToken, Type, ExistingProvider } from '@angular/core';
5
+ import { OnInit, DoCheck, Signal, InjectionToken, Type, ExistingProvider } from '@angular/core';
6
6
  import { BooleanInput } from '@angular/cdk/coercion';
7
7
 
8
- declare class BrnFieldControl implements OnInit {
8
+ declare class BrnFieldControl implements OnInit, DoCheck {
9
9
  private readonly _injector;
10
10
  private readonly _errorStateMatcher;
11
11
  private readonly _parentForm;
@@ -13,7 +13,9 @@ declare class BrnFieldControl implements OnInit {
13
13
  private readonly _field;
14
14
  private readonly _destroyRef;
15
15
  private _idEffectRef?;
16
- private readonly _errorStateTracker;
16
+ private readonly _stateTracker;
17
+ /** Sentinel value to differentiate "never checked" from "control is null". */
18
+ private _lastControl;
17
19
  /** Gets the AbstractControlDirective for this control. */
18
20
  ngControl: NgControl | null;
19
21
  readonly id: _angular_core.WritableSignal<string | null | undefined>;
@@ -25,6 +27,9 @@ declare class BrnFieldControl implements OnInit {
25
27
  readonly touched: _angular_core.Signal<boolean | null>;
26
28
  constructor();
27
29
  ngOnInit(): void;
30
+ ngDoCheck(): void;
31
+ /** @returns true if the control reference changed */
32
+ private _syncTracker;
28
33
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<BrnFieldControl, never>;
29
34
  static ɵdir: _angular_core.ɵɵDirectiveDeclaration<BrnFieldControl, never, never, {}, {}, never, never, true, never>;
30
35
  }
package/forms/index.d.ts CHANGED
@@ -1,7 +1,7 @@
1
1
  import * as _angular_forms from '@angular/forms';
2
2
  import { ValidationErrors, AbstractControl, FormGroupDirective, NgForm, NgControl } from '@angular/forms';
3
3
  import * as _angular_core from '@angular/core';
4
- import { Type, Provider } from '@angular/core';
4
+ import { Type, Provider, Signal } from '@angular/core';
5
5
 
6
6
  interface ControlState {
7
7
  dirty: boolean;
@@ -53,11 +53,26 @@ declare class ErrorStateMatcher {
53
53
  static ɵprov: _angular_core.ɵɵInjectableDeclaration<ErrorStateMatcher>;
54
54
  }
55
55
 
56
- declare class ErrorStateTracker {
56
+ interface StateTracker {
57
+ readonly controlState: Signal<ControlState | null>;
58
+ readonly errors: Signal<ValidationErrors | null>;
59
+ readonly dirty: Signal<boolean | null>;
60
+ readonly invalid: Signal<boolean | null>;
61
+ readonly spartanInvalid: Signal<boolean | null>;
62
+ readonly touched: Signal<boolean | null>;
63
+ destroy(): void;
64
+ }
65
+ declare function controlStateEqual(a: ControlState | null, b: ControlState | null): boolean;
66
+
67
+ declare function createStateTracker(ngControl: NgControl, matcher: ErrorStateMatcher | null, parentFormGroup: FormGroupDirective | null, parentForm: NgForm | null): StateTracker;
68
+
69
+ declare class ReactiveStateTracker implements StateTracker {
70
+ private readonly _ngControl;
57
71
  private readonly _matcher;
58
72
  private readonly _parentFormGroup;
59
73
  private readonly _parentForm;
60
- private readonly _ngControl;
74
+ private readonly _stateVersion;
75
+ private readonly _eventsSubscription?;
61
76
  readonly controlState: _angular_core.Signal<ControlState | null>;
62
77
  readonly errors: _angular_core.Signal<_angular_forms.ValidationErrors | null>;
63
78
  readonly dirty: _angular_core.Signal<boolean | null>;
@@ -65,10 +80,26 @@ declare class ErrorStateTracker {
65
80
  readonly spartanInvalid: _angular_core.Signal<boolean | null>;
66
81
  readonly touched: _angular_core.Signal<boolean | null>;
67
82
  private get _controlParent();
68
- constructor(_matcher: ErrorStateMatcher | null, _parentFormGroup: FormGroupDirective | null, _parentForm: NgForm | null);
69
- setControl(ngControl: NgControl): void;
70
- private _getState;
83
+ constructor(_ngControl: NgControl, _matcher: ErrorStateMatcher | null, _parentFormGroup: FormGroupDirective | null, _parentForm: NgForm | null);
84
+ destroy(): void;
85
+ }
86
+
87
+ declare class SignalStateTracker implements StateTracker {
88
+ private readonly _ngControl;
89
+ private readonly _matcher;
90
+ private readonly _parentFormGroup;
91
+ private readonly _parentForm;
92
+ private readonly _control;
93
+ readonly dirty: _angular_core.Signal<boolean | null>;
94
+ readonly touched: _angular_core.Signal<boolean | null>;
95
+ readonly invalid: _angular_core.Signal<boolean | null>;
96
+ readonly errors: _angular_core.Signal<_angular_forms.ValidationErrors | null>;
97
+ readonly spartanInvalid: _angular_core.Signal<boolean | null>;
98
+ readonly controlState: _angular_core.Signal<ControlState | null>;
99
+ private get _controlParent();
100
+ constructor(_ngControl: NgControl, _matcher: ErrorStateMatcher | null, _parentFormGroup: FormGroupDirective | null, _parentForm: NgForm | null);
101
+ destroy(): void;
71
102
  }
72
103
 
73
- export { ErrorStateMatcher, ErrorStateTracker, ShowOnDirtyErrorStateMatcher, provideErrorStateMatcher };
74
- export type { ChangeFn, ControlState, TouchFn };
104
+ export { ErrorStateMatcher, ReactiveStateTracker, ShowOnDirtyErrorStateMatcher, SignalStateTracker, controlStateEqual, createStateTracker, provideErrorStateMatcher };
105
+ export type { ChangeFn, ControlState, StateTracker, TouchFn };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@spartan-ng/brain",
3
- "version": "0.0.1-alpha.668",
3
+ "version": "0.0.1-alpha.669",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/spartan-ng/spartan"