@ng-forge/dynamic-forms-material 0.9.0-next.1 → 0.9.0-next.11

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,9 +1,10 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, computed, inject, ElementRef, input, afterRenderEffect, ChangeDetectionStrategy, Component, viewChild, linkedSignal, isSignal } from '@angular/core';
2
+ import { InjectionToken, inject, input, computed, ChangeDetectionStrategy, Component, linkedSignal, viewChild, afterRenderEffect, isSignal } from '@angular/core';
3
3
  import { FormField } from '@angular/forms/signals';
4
4
  import { MatCheckbox } from '@angular/material/checkbox';
5
- import { DynamicTextPipe, EventBus, ARRAY_CONTEXT, resolveTokens, DEFAULT_PROPS, buildBaseInputs } from '@ng-forge/dynamic-forms';
6
- import { createResolvedErrorsSignal, shouldShowErrors, setupMetaTracking, isEqual, valueFieldMapper, optionsFieldMapper, checkboxFieldMapper, submitButtonFieldMapper, nextButtonFieldMapper, previousButtonFieldMapper, addArrayItemButtonMapper, prependArrayItemButtonMapper, insertArrayItemButtonMapper, removeArrayItemButtonMapper, popArrayItemButtonMapper, shiftArrayItemButtonMapper, datepickerFieldMapper } from '@ng-forge/dynamic-forms/integration';
5
+ import { DynamicTextPipe, DEFAULT_PROPS, ARRAY_CONTEXT, buildBaseInputs } from '@ng-forge/dynamic-forms';
6
+ import * as i1 from '@ng-forge/dynamic-forms/integration';
7
+ import { injectNgForgeField, NgForgeControl, NgForgeFieldHost, isEqual, injectNgForgeAction, NgForgeActionHost, valueFieldMapper, optionsFieldMapper, checkboxFieldMapper, submitButtonFieldMapper, nextButtonFieldMapper, previousButtonFieldMapper, addArrayItemButtonMapper, prependArrayItemButtonMapper, insertArrayItemButtonMapper, removeArrayItemButtonMapper, popArrayItemButtonMapper, shiftArrayItemButtonMapper, datepickerFieldMapper } from '@ng-forge/dynamic-forms/integration';
7
8
  import { MatError, MatInput, MatHint } from '@angular/material/input';
8
9
  import { AsyncPipe } from '@angular/common';
9
10
  import { MatFormField, MatLabel, MatSuffix, MatError as MatError$1 } from '@angular/material/form-field';
@@ -39,260 +40,119 @@ import { MatSlideToggle } from '@angular/material/slide-toggle';
39
40
  */
40
41
  const MATERIAL_CONFIG = new InjectionToken('MATERIAL_CONFIG');
41
42
 
42
- /**
43
- * Creates a signal that computes the aria-describedby value based on errors and hint state.
44
- * Errors take precedence over hints - when errors are displayed, the hint is hidden.
45
- * Only the first error is displayed (single error ID, not indexed).
46
- *
47
- * @param errorsToDisplay Signal containing the array of errors currently being displayed
48
- * @param errorId Signal containing the ID for the error element (single error only)
49
- * @param hintId Signal containing the ID for the hint element
50
- * @param hasHint Function that returns true if a hint is configured
51
- * @returns Signal containing the aria-describedby value or null
52
- */
53
- function createAriaDescribedBySignal(errorsToDisplay, errorId, hintId, hasHint) {
54
- return computed(() => {
55
- if (errorsToDisplay().length > 0) {
56
- return errorId();
57
- }
58
- if (hasHint()) {
59
- return hintId();
60
- }
61
- return null;
62
- });
63
- }
64
-
65
43
  class MatCheckboxFieldComponent {
66
44
  materialConfig = inject(MATERIAL_CONFIG, { optional: true });
67
- elementRef = inject((ElementRef));
68
- field = input.required(...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
69
- key = input.required(...(ngDevMode ? [{ debugName: "key" }] : /* istanbul ignore next */ []));
70
- // Properties
71
- label = input(...(ngDevMode ? [undefined, { debugName: "label" }] : /* istanbul ignore next */ []));
72
- placeholder = input(...(ngDevMode ? [undefined, { debugName: "placeholder" }] : /* istanbul ignore next */ []));
73
- className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
74
- tabIndex = input(...(ngDevMode ? [undefined, { debugName: "tabIndex" }] : /* istanbul ignore next */ []));
45
+ ngf = injectNgForgeField();
75
46
  props = input(...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
76
- meta = input(...(ngDevMode ? [undefined, { debugName: "meta" }] : /* istanbul ignore next */ []));
77
- validationMessages = input(...(ngDevMode ? [undefined, { debugName: "validationMessages" }] : /* istanbul ignore next */ []));
78
- defaultValidationMessages = input(...(ngDevMode ? [undefined, { debugName: "defaultValidationMessages" }] : /* istanbul ignore next */ []));
79
- effectiveDisableRipple = computed(() => this.props()?.disableRipple ?? this.materialConfig?.disableRipple ?? false, ...(ngDevMode ? [{ debugName: "effectiveDisableRipple" }] : /* istanbul ignore next */ []));
80
- resolvedErrors = createResolvedErrorsSignal(this.field, this.validationMessages, this.defaultValidationMessages);
81
- showErrors = shouldShowErrors(this.field);
82
- errorsToDisplay = computed(() => (this.showErrors() ? this.resolvedErrors() : []), ...(ngDevMode ? [{ debugName: "errorsToDisplay" }] : /* istanbul ignore next */ []));
83
- constructor() {
84
- // Apply meta attributes to the internal checkbox input
85
- setupMetaTracking(this.elementRef, this.meta, {
86
- selector: 'input[type="checkbox"]',
87
- });
88
- }
89
- // ─────────────────────────────────────────────────────────────────────────────
90
- // Accessibility
91
- // ─────────────────────────────────────────────────────────────────────────────
92
- /** Unique ID for the hint element, used for aria-describedby */
93
- hintId = computed(() => `${this.key()}-hint`, ...(ngDevMode ? [{ debugName: "hintId" }] : /* istanbul ignore next */ []));
94
- /** Base ID for error elements, used for aria-describedby */
95
- errorId = computed(() => `${this.key()}-error`, ...(ngDevMode ? [{ debugName: "errorId" }] : /* istanbul ignore next */ []));
96
- /** aria-invalid: true when field is invalid AND touched, false otherwise */
97
- ariaInvalid = computed(() => {
98
- const fieldState = this.field()();
99
- return fieldState.invalid() && fieldState.touched();
100
- }, ...(ngDevMode ? [{ debugName: "ariaInvalid" }] : /* istanbul ignore next */ []));
101
- /** aria-required: true if field is required, null otherwise (to remove attribute) */
102
- ariaRequired = computed(() => {
103
- return this.field()().required?.() === true ? true : null;
104
- }, ...(ngDevMode ? [{ debugName: "ariaRequired" }] : /* istanbul ignore next */ []));
105
- /** aria-describedby: links to hint and error messages for screen readers */
106
- ariaDescribedBy = createAriaDescribedBySignal(this.errorsToDisplay, this.errorId, this.hintId, () => !!this.props()?.hint);
107
- /**
108
- * Workaround: Angular Material's MatCheckbox does NOT set aria-required on its internal
109
- * input element when [required] is passed, even though it sets the native required attribute.
110
- * This effect imperatively sets/removes aria-required on the internal input.
111
- *
112
- * Bug: MatCheckbox sets `required` attribute but not `aria-required` for screen readers.
113
- * @see https://github.com/angular/components/issues/XXXXX (TODO: file issue)
114
- *
115
- * Uses afterRenderEffect to ensure DOM is ready before manipulating attributes.
116
- */
117
- syncAriaRequiredToDom = afterRenderEffect(() => {
118
- const isRequired = this.ariaRequired();
119
- const inputEl = this.elementRef.nativeElement.querySelector('input[type="checkbox"]');
120
- if (inputEl) {
121
- if (isRequired) {
122
- inputEl.setAttribute('aria-required', 'true');
123
- }
124
- else {
125
- inputEl.removeAttribute('aria-required');
126
- }
127
- }
128
- });
129
- /**
130
- * Workaround: Angular Material's MatCheckbox does NOT propagate aria-describedby to its internal
131
- * input element. This effect imperatively sets/removes aria-describedby on the internal input.
132
- *
133
- * Uses afterRenderEffect to ensure DOM is ready before querying internal elements.
134
- */
135
- syncAriaDescribedByToDom = afterRenderEffect(() => {
136
- const describedBy = this.ariaDescribedBy();
137
- const inputEl = this.elementRef.nativeElement.querySelector('input[type="checkbox"]');
138
- if (inputEl) {
139
- if (describedBy) {
140
- inputEl.setAttribute('aria-describedby', describedBy);
141
- }
142
- else {
143
- inputEl.removeAttribute('aria-describedby');
144
- }
145
- }
146
- });
47
+ disableRipple = computed(() => this.props()?.disableRipple ?? this.materialConfig?.disableRipple ?? false, ...(ngDevMode ? [{ debugName: "disableRipple" }] : /* istanbul ignore next */ []));
147
48
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: MatCheckboxFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
148
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: MatCheckboxFieldComponent, isStandalone: true, selector: "df-mat-checkbox", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, tabIndex: { classPropertyName: "tabIndex", publicName: "tabIndex", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null }, meta: { classPropertyName: "meta", publicName: "meta", isSignal: true, isRequired: false, transformFunction: null }, validationMessages: { classPropertyName: "validationMessages", publicName: "validationMessages", isSignal: true, isRequired: false, transformFunction: null }, defaultValidationMessages: { classPropertyName: "defaultValidationMessages", publicName: "defaultValidationMessages", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "className()", "id": "`${key()}`", "attr.data-testid": "key()", "attr.hidden": "field()().hidden() || null" } }, ngImport: i0, template: `
149
- @let f = field();
150
- @let checkboxId = key() + '-checkbox';
49
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: MatCheckboxFieldComponent, isStandalone: true, selector: "df-mat-checkbox", inputs: { props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, hostDirectives: [{ directive: i1.NgForgeFieldHost }], ngImport: i0, template: `
50
+ @let f = ngf.field();
51
+ @let checkboxId = ngf.key() + '-checkbox';
151
52
 
152
53
  <mat-checkbox
54
+ ngForgeControl="input[type='checkbox']"
153
55
  [id]="checkboxId"
154
56
  [formField]="f"
155
57
  [labelPosition]="props()?.labelPosition || 'after'"
156
58
  [indeterminate]="props()?.indeterminate || false"
157
59
  [color]="props()?.color || 'primary'"
158
- [disableRipple]="effectiveDisableRipple()"
60
+ [disableRipple]="disableRipple()"
159
61
  [required]="!!f().required()"
160
- [attr.aria-describedby]="ariaDescribedBy()"
161
- [attr.tabindex]="tabIndex()"
62
+ [tabIndex]="ngf.tabIndex() ?? 0"
162
63
  [attr.hidden]="f().hidden() || null"
163
64
  >
164
- {{ label() | dynamicText | async }}
65
+ {{ ngf.label() | dynamicText | async }}
165
66
  </mat-checkbox>
166
67
 
167
- @if (errorsToDisplay()[0]; as error) {
168
- <mat-error [id]="errorId()" [attr.hidden]="f().hidden() || null">{{ error.message }}</mat-error>
68
+ @if (ngf.errorsToDisplay()[0]; as error) {
69
+ <mat-error [id]="ngf.errorId()" [attr.hidden]="f().hidden() || null">{{ error.message }}</mat-error>
169
70
  } @else if (props()?.hint; as hint) {
170
- <div class="mat-hint" [id]="hintId()" [attr.hidden]="f().hidden() || null">{{ hint | dynamicText | async }}</div>
71
+ <div class="mat-hint" [id]="ngf.hintId()" [attr.hidden]="f().hidden() || null">{{ hint | dynamicText | async }}</div>
171
72
  }
172
- `, isInline: true, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: rgba(0, 0, 0, .6);--df-hint-font-size: .875rem;--df-error-color: #f44336;--df-error-font-size: .875rem;display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color)}.df-mat-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size)}.df-mat-error{color:var(--df-error-color);font-size:var(--df-error-font-size)}\n"], dependencies: [{ kind: "component", type: MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "directive", type: MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
73
+ `, isInline: true, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: rgba(0, 0, 0, .6);--df-hint-font-size: .875rem;--df-error-color: #f44336;--df-error-font-size: .875rem;display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color)}.df-mat-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size)}.df-mat-error{color:var(--df-error-color);font-size:var(--df-error-font-size)}\n"], dependencies: [{ kind: "component", type: MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "directive", type: MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: NgForgeControl, selector: "[ngForgeControl]", inputs: ["ngForgeControl"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
173
74
  }
174
75
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: MatCheckboxFieldComponent, decorators: [{
175
76
  type: Component,
176
- args: [{ selector: 'df-mat-checkbox', imports: [MatCheckbox, FormField, MatError, DynamicTextPipe, AsyncPipe], template: `
177
- @let f = field();
178
- @let checkboxId = key() + '-checkbox';
77
+ args: [{ selector: 'df-mat-checkbox', imports: [MatCheckbox, FormField, MatError, DynamicTextPipe, AsyncPipe, NgForgeControl], hostDirectives: [NgForgeFieldHost], template: `
78
+ @let f = ngf.field();
79
+ @let checkboxId = ngf.key() + '-checkbox';
179
80
 
180
81
  <mat-checkbox
82
+ ngForgeControl="input[type='checkbox']"
181
83
  [id]="checkboxId"
182
84
  [formField]="f"
183
85
  [labelPosition]="props()?.labelPosition || 'after'"
184
86
  [indeterminate]="props()?.indeterminate || false"
185
87
  [color]="props()?.color || 'primary'"
186
- [disableRipple]="effectiveDisableRipple()"
88
+ [disableRipple]="disableRipple()"
187
89
  [required]="!!f().required()"
188
- [attr.aria-describedby]="ariaDescribedBy()"
189
- [attr.tabindex]="tabIndex()"
90
+ [tabIndex]="ngf.tabIndex() ?? 0"
190
91
  [attr.hidden]="f().hidden() || null"
191
92
  >
192
- {{ label() | dynamicText | async }}
93
+ {{ ngf.label() | dynamicText | async }}
193
94
  </mat-checkbox>
194
95
 
195
- @if (errorsToDisplay()[0]; as error) {
196
- <mat-error [id]="errorId()" [attr.hidden]="f().hidden() || null">{{ error.message }}</mat-error>
96
+ @if (ngf.errorsToDisplay()[0]; as error) {
97
+ <mat-error [id]="ngf.errorId()" [attr.hidden]="f().hidden() || null">{{ error.message }}</mat-error>
197
98
  } @else if (props()?.hint; as hint) {
198
- <div class="mat-hint" [id]="hintId()" [attr.hidden]="f().hidden() || null">{{ hint | dynamicText | async }}</div>
99
+ <div class="mat-hint" [id]="ngf.hintId()" [attr.hidden]="f().hidden() || null">{{ hint | dynamicText | async }}</div>
199
100
  }
200
- `, host: {
201
- '[class]': 'className()',
202
- '[id]': '`${key()}`',
203
- '[attr.data-testid]': 'key()',
204
- '[attr.hidden]': 'field()().hidden() || null',
205
- }, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: rgba(0, 0, 0, .6);--df-hint-font-size: .875rem;--df-error-color: #f44336;--df-error-font-size: .875rem;display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color)}.df-mat-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size)}.df-mat-error{color:var(--df-error-color);font-size:var(--df-error-font-size)}\n"] }]
206
- }], ctorParameters: () => [], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], key: [{ type: i0.Input, args: [{ isSignal: true, alias: "key", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], tabIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabIndex", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }], meta: [{ type: i0.Input, args: [{ isSignal: true, alias: "meta", required: false }] }], validationMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "validationMessages", required: false }] }], defaultValidationMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultValidationMessages", required: false }] }] } });
101
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: rgba(0, 0, 0, .6);--df-hint-font-size: .875rem;--df-error-color: #f44336;--df-error-font-size: .875rem;display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color)}.df-mat-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size)}.df-mat-error{color:var(--df-error-color);font-size:var(--df-error-font-size)}\n"] }]
102
+ }], propDecorators: { props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }] } });
207
103
 
208
104
  var matCheckbox_component = /*#__PURE__*/Object.freeze({
209
- __proto__: null,
210
- default: MatCheckboxFieldComponent
105
+ __proto__: null,
106
+ default: MatCheckboxFieldComponent
211
107
  });
212
108
 
213
109
  class MatDatepickerFieldComponent {
214
110
  materialConfig = inject(MATERIAL_CONFIG, { optional: true });
215
- elementRef = inject((ElementRef));
216
- field = input.required(...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
217
- key = input.required(...(ngDevMode ? [{ debugName: "key" }] : /* istanbul ignore next */ []));
218
- constructor() {
219
- setupMetaTracking(this.elementRef, this.meta, { selector: 'input' });
220
- }
221
- label = input(...(ngDevMode ? [undefined, { debugName: "label" }] : /* istanbul ignore next */ []));
222
- placeholder = input(...(ngDevMode ? [undefined, { debugName: "placeholder" }] : /* istanbul ignore next */ []));
223
- className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
224
- tabIndex = input(...(ngDevMode ? [undefined, { debugName: "tabIndex" }] : /* istanbul ignore next */ []));
111
+ ngf = injectNgForgeField();
225
112
  minDate = input(null, ...(ngDevMode ? [{ debugName: "minDate" }] : /* istanbul ignore next */ []));
226
113
  maxDate = input(null, ...(ngDevMode ? [{ debugName: "maxDate" }] : /* istanbul ignore next */ []));
227
114
  startAt = input(null, ...(ngDevMode ? [{ debugName: "startAt" }] : /* istanbul ignore next */ []));
228
115
  props = input(...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
229
- meta = input(...(ngDevMode ? [undefined, { debugName: "meta" }] : /* istanbul ignore next */ []));
230
- validationMessages = input(...(ngDevMode ? [undefined, { debugName: "validationMessages" }] : /* istanbul ignore next */ []));
231
- defaultValidationMessages = input(...(ngDevMode ? [undefined, { debugName: "defaultValidationMessages" }] : /* istanbul ignore next */ []));
232
- effectiveAppearance = computed(() => this.props()?.appearance ?? this.materialConfig?.appearance ?? 'outline', ...(ngDevMode ? [{ debugName: "effectiveAppearance" }] : /* istanbul ignore next */ []));
233
- effectiveSubscriptSizing = computed(() => this.props()?.subscriptSizing ?? this.materialConfig?.subscriptSizing ?? 'dynamic', ...(ngDevMode ? [{ debugName: "effectiveSubscriptSizing" }] : /* istanbul ignore next */ []));
234
- effectiveFloatLabel = computed(() => this.props()?.floatLabel ?? this.materialConfig?.floatLabel ?? 'auto', ...(ngDevMode ? [{ debugName: "effectiveFloatLabel" }] : /* istanbul ignore next */ []));
235
- effectiveHideRequiredMarker = computed(() => this.props()?.hideRequiredMarker ?? this.materialConfig?.hideRequiredMarker ?? false, ...(ngDevMode ? [{ debugName: "effectiveHideRequiredMarker" }] : /* istanbul ignore next */ []));
236
- resolvedErrors = createResolvedErrorsSignal(this.field, this.validationMessages, this.defaultValidationMessages);
237
- showErrors = shouldShowErrors(this.field);
238
- errorsToDisplay = computed(() => (this.showErrors() ? this.resolvedErrors() : []), ...(ngDevMode ? [{ debugName: "errorsToDisplay" }] : /* istanbul ignore next */ []));
239
- // ─────────────────────────────────────────────────────────────────────────────
240
- // Accessibility
241
- // ─────────────────────────────────────────────────────────────────────────────
242
- /** Unique ID for the hint element, used for aria-describedby */
243
- hintId = computed(() => `${this.key()}-hint`, ...(ngDevMode ? [{ debugName: "hintId" }] : /* istanbul ignore next */ []));
244
- /** Base ID for error elements, used for aria-describedby */
245
- errorId = computed(() => `${this.key()}-error`, ...(ngDevMode ? [{ debugName: "errorId" }] : /* istanbul ignore next */ []));
246
- /** aria-invalid: true when field is invalid AND touched, false otherwise */
247
- ariaInvalid = computed(() => {
248
- const fieldState = this.field()();
249
- return fieldState.invalid() && fieldState.touched();
250
- }, ...(ngDevMode ? [{ debugName: "ariaInvalid" }] : /* istanbul ignore next */ []));
251
- /** aria-required: true if field is required, null otherwise (to remove attribute) */
252
- ariaRequired = computed(() => {
253
- return this.field()().required?.() === true ? true : null;
254
- }, ...(ngDevMode ? [{ debugName: "ariaRequired" }] : /* istanbul ignore next */ []));
255
- /** aria-describedby: links to hint and error messages for screen readers */
256
- ariaDescribedBy = createAriaDescribedBySignal(this.errorsToDisplay, this.errorId, this.hintId, () => !!this.props()?.hint);
116
+ appearance = computed(() => this.props()?.appearance ?? this.materialConfig?.appearance ?? 'outline', ...(ngDevMode ? [{ debugName: "appearance" }] : /* istanbul ignore next */ []));
117
+ subscriptSizing = computed(() => this.props()?.subscriptSizing ?? this.materialConfig?.subscriptSizing ?? 'dynamic', ...(ngDevMode ? [{ debugName: "subscriptSizing" }] : /* istanbul ignore next */ []));
118
+ floatLabel = computed(() => this.props()?.floatLabel ?? this.materialConfig?.floatLabel ?? 'auto', ...(ngDevMode ? [{ debugName: "floatLabel" }] : /* istanbul ignore next */ []));
119
+ hideRequiredMarker = computed(() => this.props()?.hideRequiredMarker ?? this.materialConfig?.hideRequiredMarker ?? false, ...(ngDevMode ? [{ debugName: "hideRequiredMarker" }] : /* istanbul ignore next */ []));
257
120
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: MatDatepickerFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
258
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: MatDatepickerFieldComponent, isStandalone: true, selector: "df-mat-datepicker", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, tabIndex: { classPropertyName: "tabIndex", publicName: "tabIndex", isSignal: true, isRequired: false, transformFunction: null }, minDate: { classPropertyName: "minDate", publicName: "minDate", isSignal: true, isRequired: false, transformFunction: null }, maxDate: { classPropertyName: "maxDate", publicName: "maxDate", isSignal: true, isRequired: false, transformFunction: null }, startAt: { classPropertyName: "startAt", publicName: "startAt", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null }, meta: { classPropertyName: "meta", publicName: "meta", isSignal: true, isRequired: false, transformFunction: null }, validationMessages: { classPropertyName: "validationMessages", publicName: "validationMessages", isSignal: true, isRequired: false, transformFunction: null }, defaultValidationMessages: { classPropertyName: "defaultValidationMessages", publicName: "defaultValidationMessages", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "id": "`${key()}`", "class": "className()", "attr.data-testid": "key()", "attr.hidden": "field()().hidden() || null" } }, providers: [provideNativeDateAdapter()], ngImport: i0, template: `
259
- @let f = field();
260
- @let inputId = key() + '-input';
121
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: MatDatepickerFieldComponent, isStandalone: true, selector: "df-mat-datepicker", inputs: { minDate: { classPropertyName: "minDate", publicName: "minDate", isSignal: true, isRequired: false, transformFunction: null }, maxDate: { classPropertyName: "maxDate", publicName: "maxDate", isSignal: true, isRequired: false, transformFunction: null }, startAt: { classPropertyName: "startAt", publicName: "startAt", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, providers: [provideNativeDateAdapter()], hostDirectives: [{ directive: i1.NgForgeFieldHost }], ngImport: i0, template: `
122
+ @let inputId = ngf.key() + '-input';
261
123
 
262
124
  <mat-form-field
263
- [appearance]="effectiveAppearance()"
264
- [subscriptSizing]="effectiveSubscriptSizing()"
265
- [floatLabel]="effectiveFloatLabel()"
266
- [hideRequiredMarker]="effectiveHideRequiredMarker()"
125
+ [appearance]="appearance()"
126
+ [subscriptSizing]="subscriptSizing()"
127
+ [floatLabel]="floatLabel()"
128
+ [hideRequiredMarker]="hideRequiredMarker()"
267
129
  >
268
- @if (label(); as label) {
130
+ @if (ngf.label(); as label) {
269
131
  <mat-label>{{ label | dynamicText | async }}</mat-label>
270
132
  }
271
133
 
272
134
  <input
273
135
  matInput
136
+ ngForgeControl
274
137
  [id]="inputId"
275
138
  [matDatepicker]="picker"
276
- [formField]="f"
277
- [placeholder]="(placeholder() | dynamicText | async) ?? ''"
278
- [attr.tabindex]="tabIndex()"
139
+ [formField]="ngf.field()"
140
+ [placeholder]="(ngf.placeholder() | dynamicText | async) ?? ''"
141
+ [attr.tabindex]="ngf.tabIndex()"
279
142
  [min]="minDate()"
280
143
  [max]="maxDate()"
281
- [attr.aria-invalid]="ariaInvalid()"
282
- [attr.aria-required]="ariaRequired()"
283
- [attr.aria-describedby]="ariaDescribedBy()"
284
144
  />
285
145
 
286
146
  <mat-datepicker-toggle matIconSuffix [for]="picker" />
287
147
  <mat-datepicker #picker [startAt]="startAt()" [startView]="props()?.startView || 'month'" [touchUi]="props()?.touchUi ?? false" />
288
148
 
289
- @if (errorsToDisplay()[0]; as error) {
290
- <mat-error [id]="errorId()">{{ error.message }}</mat-error>
149
+ @if (ngf.errorsToDisplay()[0]; as error) {
150
+ <mat-error [id]="ngf.errorId()">{{ error.message }}</mat-error>
291
151
  } @else if (props()?.hint; as hint) {
292
- <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
152
+ <mat-hint [id]="ngf.hintId()">{{ hint | dynamicText | async }}</mat-hint>
293
153
  }
294
154
  </mat-form-field>
295
- `, isInline: true, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: rgba(0, 0, 0, .6);--df-hint-font-size: .875rem;--df-error-color: #f44336;--df-error-font-size: .875rem;display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color)}.df-mat-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size)}.df-mat-error{color:var(--df-error-color);font-size:var(--df-error-font-size)}\n", "mat-form-field{width:100%}\n"], dependencies: [{ kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: MatLabel, selector: "mat-label" }, { kind: "directive", type: MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "directive", type: MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "component", type: MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "directive", type: MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "directive", type: MatError$1, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
155
+ `, isInline: true, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: rgba(0, 0, 0, .6);--df-hint-font-size: .875rem;--df-error-color: #f44336;--df-error-font-size: .875rem;display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color)}.df-mat-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size)}.df-mat-error{color:var(--df-error-color);font-size:var(--df-error-font-size)}\n", "mat-form-field{width:100%}\n"], dependencies: [{ kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: MatLabel, selector: "mat-label" }, { kind: "directive", type: MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "directive", type: MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "component", type: MatDatepicker, selector: "mat-datepicker", exportAs: ["matDatepicker"] }, { kind: "directive", type: MatDatepickerInput, selector: "input[matDatepicker]", inputs: ["matDatepicker", "min", "max", "matDatepickerFilter"], exportAs: ["matDatepickerInput"] }, { kind: "component", type: MatDatepickerToggle, selector: "mat-datepicker-toggle", inputs: ["for", "tabIndex", "aria-label", "disabled", "disableRipple"], exportAs: ["matDatepickerToggle"] }, { kind: "directive", type: MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "directive", type: MatError$1, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: NgForgeControl, selector: "[ngForgeControl]", inputs: ["ngForgeControl"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
296
156
  }
297
157
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: MatDatepickerFieldComponent, decorators: [{
298
158
  type: Component,
@@ -309,228 +169,130 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
309
169
  MatError$1,
310
170
  DynamicTextPipe,
311
171
  AsyncPipe,
312
- ], host: {
313
- '[id]': '`${key()}`',
314
- '[class]': 'className()',
315
- '[attr.data-testid]': 'key()',
316
- '[attr.hidden]': 'field()().hidden() || null',
317
- }, template: `
318
- @let f = field();
319
- @let inputId = key() + '-input';
172
+ NgForgeControl,
173
+ ], hostDirectives: [NgForgeFieldHost], template: `
174
+ @let inputId = ngf.key() + '-input';
320
175
 
321
176
  <mat-form-field
322
- [appearance]="effectiveAppearance()"
323
- [subscriptSizing]="effectiveSubscriptSizing()"
324
- [floatLabel]="effectiveFloatLabel()"
325
- [hideRequiredMarker]="effectiveHideRequiredMarker()"
177
+ [appearance]="appearance()"
178
+ [subscriptSizing]="subscriptSizing()"
179
+ [floatLabel]="floatLabel()"
180
+ [hideRequiredMarker]="hideRequiredMarker()"
326
181
  >
327
- @if (label(); as label) {
182
+ @if (ngf.label(); as label) {
328
183
  <mat-label>{{ label | dynamicText | async }}</mat-label>
329
184
  }
330
185
 
331
186
  <input
332
187
  matInput
188
+ ngForgeControl
333
189
  [id]="inputId"
334
190
  [matDatepicker]="picker"
335
- [formField]="f"
336
- [placeholder]="(placeholder() | dynamicText | async) ?? ''"
337
- [attr.tabindex]="tabIndex()"
191
+ [formField]="ngf.field()"
192
+ [placeholder]="(ngf.placeholder() | dynamicText | async) ?? ''"
193
+ [attr.tabindex]="ngf.tabIndex()"
338
194
  [min]="minDate()"
339
195
  [max]="maxDate()"
340
- [attr.aria-invalid]="ariaInvalid()"
341
- [attr.aria-required]="ariaRequired()"
342
- [attr.aria-describedby]="ariaDescribedBy()"
343
196
  />
344
197
 
345
198
  <mat-datepicker-toggle matIconSuffix [for]="picker" />
346
199
  <mat-datepicker #picker [startAt]="startAt()" [startView]="props()?.startView || 'month'" [touchUi]="props()?.touchUi ?? false" />
347
200
 
348
- @if (errorsToDisplay()[0]; as error) {
349
- <mat-error [id]="errorId()">{{ error.message }}</mat-error>
201
+ @if (ngf.errorsToDisplay()[0]; as error) {
202
+ <mat-error [id]="ngf.errorId()">{{ error.message }}</mat-error>
350
203
  } @else if (props()?.hint; as hint) {
351
- <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
204
+ <mat-hint [id]="ngf.hintId()">{{ hint | dynamicText | async }}</mat-hint>
352
205
  }
353
206
  </mat-form-field>
354
207
  `, providers: [provideNativeDateAdapter()], changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: rgba(0, 0, 0, .6);--df-hint-font-size: .875rem;--df-error-color: #f44336;--df-error-font-size: .875rem;display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color)}.df-mat-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size)}.df-mat-error{color:var(--df-error-color);font-size:var(--df-error-font-size)}\n", "mat-form-field{width:100%}\n"] }]
355
- }], ctorParameters: () => [], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], key: [{ type: i0.Input, args: [{ isSignal: true, alias: "key", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], tabIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabIndex", required: false }] }], minDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "minDate", required: false }] }], maxDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxDate", required: false }] }], startAt: [{ type: i0.Input, args: [{ isSignal: true, alias: "startAt", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }], meta: [{ type: i0.Input, args: [{ isSignal: true, alias: "meta", required: false }] }], validationMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "validationMessages", required: false }] }], defaultValidationMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultValidationMessages", required: false }] }] } });
208
+ }], propDecorators: { minDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "minDate", required: false }] }], maxDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxDate", required: false }] }], startAt: [{ type: i0.Input, args: [{ isSignal: true, alias: "startAt", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }] } });
356
209
 
357
210
  var matDatepicker_component = /*#__PURE__*/Object.freeze({
358
- __proto__: null,
359
- default: MatDatepickerFieldComponent
211
+ __proto__: null,
212
+ default: MatDatepickerFieldComponent
360
213
  });
361
214
 
362
215
  class MatInputFieldComponent {
363
216
  materialConfig = inject(MATERIAL_CONFIG, { optional: true });
364
- elementRef = inject((ElementRef));
365
- field = input.required(...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
366
- key = input.required(...(ngDevMode ? [{ debugName: "key" }] : /* istanbul ignore next */ []));
367
- label = input(...(ngDevMode ? [undefined, { debugName: "label" }] : /* istanbul ignore next */ []));
368
- placeholder = input(...(ngDevMode ? [undefined, { debugName: "placeholder" }] : /* istanbul ignore next */ []));
369
- className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
370
- tabIndex = input(...(ngDevMode ? [undefined, { debugName: "tabIndex" }] : /* istanbul ignore next */ []));
217
+ ngf = injectNgForgeField();
371
218
  props = input(...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
372
- meta = input(...(ngDevMode ? [undefined, { debugName: "meta" }] : /* istanbul ignore next */ []));
373
- validationMessages = input(...(ngDevMode ? [undefined, { debugName: "validationMessages" }] : /* istanbul ignore next */ []));
374
- defaultValidationMessages = input(...(ngDevMode ? [undefined, { debugName: "defaultValidationMessages" }] : /* istanbul ignore next */ []));
375
- constructor() {
376
- setupMetaTracking(this.elementRef, this.meta, { selector: 'input' });
377
- }
378
- /**
379
- * Reference to the native input element.
380
- * Used to imperatively sync the readonly attribute since Angular Signal Forms'
381
- * [field] directive doesn't sync FieldState.readonly() to the DOM.
382
- */
383
- inputRef = viewChild('inputRef', ...(ngDevMode ? [{ debugName: "inputRef" }] : /* istanbul ignore next */ []));
384
- /**
385
- * Computed signal that extracts the readonly state from the field.
386
- * Used by the effect to reactively sync the readonly attribute to the DOM.
387
- */
388
- isReadonly = computed(() => this.field()().readonly(), ...(ngDevMode ? [{ debugName: "isReadonly" }] : /* istanbul ignore next */ []));
389
- /**
390
- * Workaround: Angular Signal Forms' [field] directive does NOT sync the readonly
391
- * attribute to the DOM, even though FieldState.readonly() returns the correct value.
392
- * This effect imperatively sets/removes the readonly attribute on the native input
393
- * element whenever the readonly state changes.
394
- *
395
- * Note: We cannot use [readonly] or [attr.readonly] bindings because Angular throws
396
- * NG8022: "Binding to '[readonly]' is not allowed on nodes using the '[field]' directive"
397
- *
398
- * Uses afterRenderEffect to ensure DOM is ready before manipulating attributes.
399
- *
400
- * @see https://github.com/angular/angular/issues/65897
401
- */
402
- syncReadonlyToDom = afterRenderEffect({
403
- write: () => {
404
- const inputRef = this.inputRef();
405
- const isReadonly = this.isReadonly();
406
- if (inputRef?.nativeElement) {
407
- if (isReadonly) {
408
- inputRef.nativeElement.setAttribute('readonly', '');
409
- }
410
- else {
411
- inputRef.nativeElement.removeAttribute('readonly');
412
- }
413
- }
414
- },
415
- });
416
- effectiveAppearance = computed(() => this.props()?.appearance ?? this.materialConfig?.appearance ?? 'outline', ...(ngDevMode ? [{ debugName: "effectiveAppearance" }] : /* istanbul ignore next */ []));
417
- effectiveSubscriptSizing = computed(() => this.props()?.subscriptSizing ?? this.materialConfig?.subscriptSizing ?? 'dynamic', ...(ngDevMode ? [{ debugName: "effectiveSubscriptSizing" }] : /* istanbul ignore next */ []));
418
- effectiveFloatLabel = computed(() => this.props()?.floatLabel ?? this.materialConfig?.floatLabel ?? 'auto', ...(ngDevMode ? [{ debugName: "effectiveFloatLabel" }] : /* istanbul ignore next */ []));
419
- effectiveHideRequiredMarker = computed(() => this.props()?.hideRequiredMarker ?? this.materialConfig?.hideRequiredMarker ?? false, ...(ngDevMode ? [{ debugName: "effectiveHideRequiredMarker" }] : /* istanbul ignore next */ []));
420
- resolvedErrors = createResolvedErrorsSignal(this.field, this.validationMessages, this.defaultValidationMessages);
421
- showErrors = shouldShowErrors(this.field);
422
- errorsToDisplay = computed(() => (this.showErrors() ? this.resolvedErrors() : []), ...(ngDevMode ? [{ debugName: "errorsToDisplay" }] : /* istanbul ignore next */ []));
423
- // ─────────────────────────────────────────────────────────────────────────────
424
- // Accessibility
425
- // ─────────────────────────────────────────────────────────────────────────────
426
- /** Unique ID for the hint element, used for aria-describedby */
427
- hintId = computed(() => `${this.key()}-hint`, ...(ngDevMode ? [{ debugName: "hintId" }] : /* istanbul ignore next */ []));
428
- /** Base ID for error elements, used for aria-describedby */
429
- errorId = computed(() => `${this.key()}-error`, ...(ngDevMode ? [{ debugName: "errorId" }] : /* istanbul ignore next */ []));
430
- /** aria-invalid: true when field is invalid AND touched, false otherwise */
431
- ariaInvalid = computed(() => {
432
- const fieldState = this.field()();
433
- return fieldState.invalid() && fieldState.touched();
434
- }, ...(ngDevMode ? [{ debugName: "ariaInvalid" }] : /* istanbul ignore next */ []));
435
- /** aria-required: true if field is required, null otherwise (to remove attribute) */
436
- ariaRequired = computed(() => {
437
- return this.field()().required?.() === true ? true : null;
438
- }, ...(ngDevMode ? [{ debugName: "ariaRequired" }] : /* istanbul ignore next */ []));
439
- /** aria-describedby: links to hint and error messages for screen readers */
440
- ariaDescribedBy = createAriaDescribedBySignal(this.errorsToDisplay, this.errorId, this.hintId, () => !!this.props()?.hint);
219
+ appearance = computed(() => this.props()?.appearance ?? this.materialConfig?.appearance ?? 'outline', ...(ngDevMode ? [{ debugName: "appearance" }] : /* istanbul ignore next */ []));
220
+ subscriptSizing = computed(() => this.props()?.subscriptSizing ?? this.materialConfig?.subscriptSizing ?? 'dynamic', ...(ngDevMode ? [{ debugName: "subscriptSizing" }] : /* istanbul ignore next */ []));
221
+ floatLabel = computed(() => this.props()?.floatLabel ?? this.materialConfig?.floatLabel ?? 'auto', ...(ngDevMode ? [{ debugName: "floatLabel" }] : /* istanbul ignore next */ []));
222
+ hideRequiredMarker = computed(() => this.props()?.hideRequiredMarker ?? this.materialConfig?.hideRequiredMarker ?? false, ...(ngDevMode ? [{ debugName: "hideRequiredMarker" }] : /* istanbul ignore next */ []));
441
223
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: MatInputFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
442
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: MatInputFieldComponent, isStandalone: true, selector: "df-mat-input", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, tabIndex: { classPropertyName: "tabIndex", publicName: "tabIndex", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null }, meta: { classPropertyName: "meta", publicName: "meta", isSignal: true, isRequired: false, transformFunction: null }, validationMessages: { classPropertyName: "validationMessages", publicName: "validationMessages", isSignal: true, isRequired: false, transformFunction: null }, defaultValidationMessages: { classPropertyName: "defaultValidationMessages", publicName: "defaultValidationMessages", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "id": "`${key()}`", "attr.data-testid": "key()", "class": "className()", "attr.hidden": "field()().hidden() || null" } }, viewQueries: [{ propertyName: "inputRef", first: true, predicate: ["inputRef"], descendants: true, isSignal: true }], ngImport: i0, template: `
443
- @let f = field();
444
- @let inputId = key() + '-input';
224
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: MatInputFieldComponent, isStandalone: true, selector: "df-mat-input", inputs: { props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, hostDirectives: [{ directive: i1.NgForgeFieldHost }], ngImport: i0, template: `
225
+ @let inputId = ngf.key() + '-input';
445
226
 
446
227
  <mat-form-field
447
- [appearance]="effectiveAppearance()"
448
- [subscriptSizing]="effectiveSubscriptSizing()"
449
- [floatLabel]="effectiveFloatLabel()"
450
- [hideRequiredMarker]="effectiveHideRequiredMarker()"
228
+ [appearance]="appearance()"
229
+ [subscriptSizing]="subscriptSizing()"
230
+ [floatLabel]="floatLabel()"
231
+ [hideRequiredMarker]="hideRequiredMarker()"
451
232
  >
452
- @if (label()) {
453
- <mat-label>{{ label() | dynamicText | async }}</mat-label>
233
+ @if (ngf.label()) {
234
+ <mat-label>{{ ngf.label() | dynamicText | async }}</mat-label>
454
235
  }
455
236
  <input
456
- #inputRef
457
237
  matInput
238
+ ngForgeControl
458
239
  [id]="inputId"
459
- [formField]="f"
240
+ [formField]="ngf.field()"
460
241
  [type]="props()?.type ?? 'text'"
461
- [placeholder]="(placeholder() | dynamicText | async) ?? ''"
462
- [attr.tabindex]="tabIndex()"
463
- [attr.aria-invalid]="ariaInvalid()"
464
- [attr.aria-required]="ariaRequired()"
465
- [attr.aria-describedby]="ariaDescribedBy()"
242
+ [placeholder]="(ngf.placeholder() | dynamicText | async) ?? ''"
243
+ [attr.tabindex]="ngf.tabIndex()"
466
244
  />
467
- @if (errorsToDisplay()[0]; as error) {
468
- <mat-error [id]="errorId()">{{ error.message }}</mat-error>
245
+ @if (ngf.errorsToDisplay()[0]; as error) {
246
+ <mat-error [id]="ngf.errorId()">{{ error.message }}</mat-error>
469
247
  } @else if (props()?.hint; as hint) {
470
- <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
248
+ <mat-hint [id]="ngf.hintId()">{{ hint | dynamicText | async }}</mat-hint>
471
249
  }
472
250
  </mat-form-field>
473
- `, isInline: true, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: rgba(0, 0, 0, .6);--df-hint-font-size: .875rem;--df-error-color: #f44336;--df-error-font-size: .875rem;display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color)}.df-mat-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size)}.df-mat-error{color:var(--df-error-color);font-size:var(--df-error-font-size)}\n", "mat-form-field{width:100%}\n"], dependencies: [{ kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: MatLabel, selector: "mat-label" }, { kind: "directive", type: MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "directive", type: MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "directive", type: MatError$1, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
251
+ `, isInline: true, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: rgba(0, 0, 0, .6);--df-hint-font-size: .875rem;--df-error-color: #f44336;--df-error-font-size: .875rem;display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color)}.df-mat-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size)}.df-mat-error{color:var(--df-error-color);font-size:var(--df-error-font-size)}\n", "mat-form-field{width:100%}\n"], dependencies: [{ kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: MatLabel, selector: "mat-label" }, { kind: "directive", type: MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "directive", type: MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "directive", type: MatError$1, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: NgForgeControl, selector: "[ngForgeControl]", inputs: ["ngForgeControl"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
474
252
  }
475
253
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: MatInputFieldComponent, decorators: [{
476
254
  type: Component,
477
- args: [{ selector: 'df-mat-input', imports: [MatFormField, MatLabel, MatInput, MatHint, FormField, MatError$1, DynamicTextPipe, AsyncPipe], template: `
478
- @let f = field();
479
- @let inputId = key() + '-input';
255
+ args: [{ selector: 'df-mat-input', imports: [MatFormField, MatLabel, MatInput, MatHint, FormField, MatError$1, DynamicTextPipe, AsyncPipe, NgForgeControl], hostDirectives: [NgForgeFieldHost], template: `
256
+ @let inputId = ngf.key() + '-input';
480
257
 
481
258
  <mat-form-field
482
- [appearance]="effectiveAppearance()"
483
- [subscriptSizing]="effectiveSubscriptSizing()"
484
- [floatLabel]="effectiveFloatLabel()"
485
- [hideRequiredMarker]="effectiveHideRequiredMarker()"
259
+ [appearance]="appearance()"
260
+ [subscriptSizing]="subscriptSizing()"
261
+ [floatLabel]="floatLabel()"
262
+ [hideRequiredMarker]="hideRequiredMarker()"
486
263
  >
487
- @if (label()) {
488
- <mat-label>{{ label() | dynamicText | async }}</mat-label>
264
+ @if (ngf.label()) {
265
+ <mat-label>{{ ngf.label() | dynamicText | async }}</mat-label>
489
266
  }
490
267
  <input
491
- #inputRef
492
268
  matInput
269
+ ngForgeControl
493
270
  [id]="inputId"
494
- [formField]="f"
271
+ [formField]="ngf.field()"
495
272
  [type]="props()?.type ?? 'text'"
496
- [placeholder]="(placeholder() | dynamicText | async) ?? ''"
497
- [attr.tabindex]="tabIndex()"
498
- [attr.aria-invalid]="ariaInvalid()"
499
- [attr.aria-required]="ariaRequired()"
500
- [attr.aria-describedby]="ariaDescribedBy()"
273
+ [placeholder]="(ngf.placeholder() | dynamicText | async) ?? ''"
274
+ [attr.tabindex]="ngf.tabIndex()"
501
275
  />
502
- @if (errorsToDisplay()[0]; as error) {
503
- <mat-error [id]="errorId()">{{ error.message }}</mat-error>
276
+ @if (ngf.errorsToDisplay()[0]; as error) {
277
+ <mat-error [id]="ngf.errorId()">{{ error.message }}</mat-error>
504
278
  } @else if (props()?.hint; as hint) {
505
- <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
279
+ <mat-hint [id]="ngf.hintId()">{{ hint | dynamicText | async }}</mat-hint>
506
280
  }
507
281
  </mat-form-field>
508
- `, changeDetection: ChangeDetectionStrategy.OnPush, host: {
509
- '[id]': '`${key()}`',
510
- '[attr.data-testid]': 'key()',
511
- '[class]': 'className()',
512
- '[attr.hidden]': 'field()().hidden() || null',
513
- }, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: rgba(0, 0, 0, .6);--df-hint-font-size: .875rem;--df-error-color: #f44336;--df-error-font-size: .875rem;display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color)}.df-mat-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size)}.df-mat-error{color:var(--df-error-color);font-size:var(--df-error-font-size)}\n", "mat-form-field{width:100%}\n"] }]
514
- }], ctorParameters: () => [], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], key: [{ type: i0.Input, args: [{ isSignal: true, alias: "key", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], tabIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabIndex", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }], meta: [{ type: i0.Input, args: [{ isSignal: true, alias: "meta", required: false }] }], validationMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "validationMessages", required: false }] }], defaultValidationMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultValidationMessages", required: false }] }], inputRef: [{ type: i0.ViewChild, args: ['inputRef', { isSignal: true }] }] } });
282
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: rgba(0, 0, 0, .6);--df-hint-font-size: .875rem;--df-error-color: #f44336;--df-error-font-size: .875rem;display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color)}.df-mat-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size)}.df-mat-error{color:var(--df-error-color);font-size:var(--df-error-font-size)}\n", "mat-form-field{width:100%}\n"] }]
283
+ }], propDecorators: { props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }] } });
515
284
 
516
285
  var matInput_component = /*#__PURE__*/Object.freeze({
517
- __proto__: null,
518
- default: MatInputFieldComponent
286
+ __proto__: null,
287
+ default: MatInputFieldComponent
519
288
  });
520
289
 
521
290
  class MatMultiCheckboxFieldComponent {
522
- elementRef = inject((ElementRef));
523
- field = input.required(...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
524
- key = input.required(...(ngDevMode ? [{ debugName: "key" }] : /* istanbul ignore next */ []));
525
- label = input(...(ngDevMode ? [undefined, { debugName: "label" }] : /* istanbul ignore next */ []));
526
- placeholder = input(...(ngDevMode ? [undefined, { debugName: "placeholder" }] : /* istanbul ignore next */ []));
527
- className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
528
- tabIndex = input(...(ngDevMode ? [undefined, { debugName: "tabIndex" }] : /* istanbul ignore next */ []));
291
+ ngf = injectNgForgeField();
529
292
  options = input([], ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
530
293
  props = input(...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
531
- meta = input(...(ngDevMode ? [undefined, { debugName: "meta" }] : /* istanbul ignore next */ []));
532
294
  valueViewModel = linkedSignal(() => {
533
- const currentValues = this.field()().value();
295
+ const currentValues = this.ngf.field()().value();
534
296
  return this.options().filter((option) => currentValues.includes(option.value));
535
297
  }, { ...(ngDevMode ? { debugName: "valueViewModel" } : /* istanbul ignore next */ {}), equal: isEqual });
536
298
  /** Computed map of checked option values for O(1) lookup in template */
@@ -542,15 +304,10 @@ class MatMultiCheckboxFieldComponent {
542
304
  return map;
543
305
  }, ...(ngDevMode ? [{ debugName: "checkedValuesMap" }] : /* istanbul ignore next */ []));
544
306
  constructor() {
545
- // Apply meta attributes to all checkbox inputs, re-apply when options change
546
- setupMetaTracking(this.elementRef, this.meta, {
547
- selector: 'input[type="checkbox"]',
548
- dependents: [this.options],
549
- });
550
307
  explicitEffect([this.valueViewModel], ([selectedOptions]) => {
551
308
  const selectedValues = selectedOptions.map((option) => option.value);
552
- if (!isEqual(selectedValues, this.field()().value())) {
553
- this.field()().value.set(selectedValues);
309
+ if (!isEqual(selectedValues, this.ngf.field()().value())) {
310
+ this.ngf.field()().value.set(selectedValues);
554
311
  }
555
312
  });
556
313
  explicitEffect([this.options], ([options]) => {
@@ -572,36 +329,13 @@ class MatMultiCheckboxFieldComponent {
572
329
  }
573
330
  });
574
331
  }
575
- validationMessages = input(...(ngDevMode ? [undefined, { debugName: "validationMessages" }] : /* istanbul ignore next */ []));
576
- defaultValidationMessages = input(...(ngDevMode ? [undefined, { debugName: "defaultValidationMessages" }] : /* istanbul ignore next */ []));
577
- resolvedErrors = createResolvedErrorsSignal(this.field, this.validationMessages, this.defaultValidationMessages);
578
- showErrors = shouldShowErrors(this.field);
579
- errorsToDisplay = computed(() => (this.showErrors() ? this.resolvedErrors() : []), ...(ngDevMode ? [{ debugName: "errorsToDisplay" }] : /* istanbul ignore next */ []));
580
- // ─────────────────────────────────────────────────────────────────────────────
581
- // Accessibility
582
- // ─────────────────────────────────────────────────────────────────────────────
583
- /** Unique ID for the hint element, used for aria-describedby */
584
- hintId = computed(() => `${this.key()}-hint`, ...(ngDevMode ? [{ debugName: "hintId" }] : /* istanbul ignore next */ []));
585
- /** Base ID for error elements, used for aria-describedby */
586
- errorId = computed(() => `${this.key()}-error`, ...(ngDevMode ? [{ debugName: "errorId" }] : /* istanbul ignore next */ []));
587
- /** aria-invalid: true when field is invalid AND touched, false otherwise */
588
- ariaInvalid = computed(() => {
589
- const fieldState = this.field()();
590
- return fieldState.invalid() && fieldState.touched();
591
- }, ...(ngDevMode ? [{ debugName: "ariaInvalid" }] : /* istanbul ignore next */ []));
592
- /** aria-required: true if field is required, null otherwise (to remove attribute) */
593
- ariaRequired = computed(() => {
594
- return this.field()().required?.() === true ? true : null;
595
- }, ...(ngDevMode ? [{ debugName: "ariaRequired" }] : /* istanbul ignore next */ []));
596
- /** aria-describedby: links to hint and error messages for screen readers */
597
- ariaDescribedBy = createAriaDescribedBySignal(this.errorsToDisplay, this.errorId, this.hintId, () => !!this.props()?.hint);
598
332
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: MatMultiCheckboxFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
599
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: MatMultiCheckboxFieldComponent, isStandalone: true, selector: "df-mat-multi-checkbox", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, tabIndex: { classPropertyName: "tabIndex", publicName: "tabIndex", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null }, meta: { classPropertyName: "meta", publicName: "meta", isSignal: true, isRequired: false, transformFunction: null }, validationMessages: { classPropertyName: "validationMessages", publicName: "validationMessages", isSignal: true, isRequired: false, transformFunction: null }, defaultValidationMessages: { classPropertyName: "defaultValidationMessages", publicName: "defaultValidationMessages", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "className() || \"\"", "id": "`${key()}`", "attr.data-testid": "key()", "attr.hidden": "field()().hidden() || null" } }, ngImport: i0, template: `
600
- @let f = field();
601
- @let checkboxGroupId = key() + '-checkbox-group';
333
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: MatMultiCheckboxFieldComponent, isStandalone: true, selector: "df-mat-multi-checkbox", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, hostDirectives: [{ directive: i1.NgForgeFieldHost }], ngImport: i0, template: `
334
+ @let f = ngf.field();
335
+ @let checkboxGroupId = ngf.key() + '-checkbox-group';
602
336
  @let checked = checkedValuesMap();
603
337
 
604
- @if (label(); as label) {
338
+ @if (ngf.label(); as label) {
605
339
  <div class="checkbox-group-label">{{ label | dynamicText | async }}</div>
606
340
  }
607
341
 
@@ -609,12 +343,13 @@ class MatMultiCheckboxFieldComponent {
609
343
  [id]="checkboxGroupId"
610
344
  class="checkbox-group"
611
345
  role="group"
612
- [attr.aria-invalid]="ariaInvalid()"
613
- [attr.aria-required]="ariaRequired()"
614
- [attr.aria-describedby]="ariaDescribedBy()"
346
+ [attr.aria-invalid]="ngf.ariaInvalid()"
347
+ [attr.aria-required]="ngf.ariaRequired()"
348
+ [attr.aria-describedby]="ngf.ariaDescribedBy()"
615
349
  >
616
350
  @for (option of options(); track option.value) {
617
351
  <mat-checkbox
352
+ ngForgeControl="input[type='checkbox']"
618
353
  [checked]="checked['' + option.value]"
619
354
  [disabled]="f().disabled() || option.disabled"
620
355
  [color]="props()?.color || 'primary'"
@@ -626,21 +361,21 @@ class MatMultiCheckboxFieldComponent {
626
361
  }
627
362
  </div>
628
363
 
629
- @if (errorsToDisplay()[0]; as error) {
630
- <mat-error [id]="errorId()">{{ error.message }}</mat-error>
364
+ @if (ngf.errorsToDisplay()[0]; as error) {
365
+ <mat-error [id]="ngf.errorId()">{{ error.message }}</mat-error>
631
366
  } @else if (props()?.hint; as hint) {
632
- <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
367
+ <div class="mat-hint" [id]="ngf.hintId()">{{ hint | dynamicText | async }}</div>
633
368
  }
634
- `, isInline: true, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: rgba(0, 0, 0, .6);--df-hint-font-size: .875rem;--df-error-color: #f44336;--df-error-font-size: .875rem;display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color)}.df-mat-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size)}.df-mat-error{color:var(--df-error-color);font-size:var(--df-error-font-size)}\n"], dependencies: [{ kind: "component", type: MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "directive", type: MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
369
+ `, isInline: true, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: rgba(0, 0, 0, .6);--df-hint-font-size: .875rem;--df-error-color: #f44336;--df-error-font-size: .875rem;display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color)}.df-mat-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size)}.df-mat-error{color:var(--df-error-color);font-size:var(--df-error-font-size)}\n"], dependencies: [{ kind: "component", type: MatCheckbox, selector: "mat-checkbox", inputs: ["aria-label", "aria-labelledby", "aria-describedby", "aria-expanded", "aria-controls", "aria-owns", "id", "required", "labelPosition", "name", "value", "disableRipple", "tabIndex", "color", "disabledInteractive", "checked", "disabled", "indeterminate"], outputs: ["change", "indeterminateChange"], exportAs: ["matCheckbox"] }, { kind: "directive", type: MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: NgForgeControl, selector: "[ngForgeControl]", inputs: ["ngForgeControl"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
635
370
  }
636
371
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: MatMultiCheckboxFieldComponent, decorators: [{
637
372
  type: Component,
638
- args: [{ selector: 'df-mat-multi-checkbox', imports: [MatCheckbox, MatError, DynamicTextPipe, AsyncPipe], template: `
639
- @let f = field();
640
- @let checkboxGroupId = key() + '-checkbox-group';
373
+ args: [{ selector: 'df-mat-multi-checkbox', imports: [MatCheckbox, MatError, DynamicTextPipe, AsyncPipe, NgForgeControl], hostDirectives: [NgForgeFieldHost], template: `
374
+ @let f = ngf.field();
375
+ @let checkboxGroupId = ngf.key() + '-checkbox-group';
641
376
  @let checked = checkedValuesMap();
642
377
 
643
- @if (label(); as label) {
378
+ @if (ngf.label(); as label) {
644
379
  <div class="checkbox-group-label">{{ label | dynamicText | async }}</div>
645
380
  }
646
381
 
@@ -648,12 +383,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
648
383
  [id]="checkboxGroupId"
649
384
  class="checkbox-group"
650
385
  role="group"
651
- [attr.aria-invalid]="ariaInvalid()"
652
- [attr.aria-required]="ariaRequired()"
653
- [attr.aria-describedby]="ariaDescribedBy()"
386
+ [attr.aria-invalid]="ngf.ariaInvalid()"
387
+ [attr.aria-required]="ngf.ariaRequired()"
388
+ [attr.aria-describedby]="ngf.ariaDescribedBy()"
654
389
  >
655
390
  @for (option of options(); track option.value) {
656
391
  <mat-checkbox
392
+ ngForgeControl="input[type='checkbox']"
657
393
  [checked]="checked['' + option.value]"
658
394
  [disabled]="f().disabled() || option.disabled"
659
395
  [color]="props()?.color || 'primary'"
@@ -665,83 +401,42 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
665
401
  }
666
402
  </div>
667
403
 
668
- @if (errorsToDisplay()[0]; as error) {
669
- <mat-error [id]="errorId()">{{ error.message }}</mat-error>
404
+ @if (ngf.errorsToDisplay()[0]; as error) {
405
+ <mat-error [id]="ngf.errorId()">{{ error.message }}</mat-error>
670
406
  } @else if (props()?.hint; as hint) {
671
- <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
407
+ <div class="mat-hint" [id]="ngf.hintId()">{{ hint | dynamicText | async }}</div>
672
408
  }
673
- `, host: {
674
- '[class]': 'className() || ""',
675
- '[id]': '`${key()}`',
676
- '[attr.data-testid]': 'key()',
677
- '[attr.hidden]': 'field()().hidden() || null',
678
- }, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: rgba(0, 0, 0, .6);--df-hint-font-size: .875rem;--df-error-color: #f44336;--df-error-font-size: .875rem;display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color)}.df-mat-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size)}.df-mat-error{color:var(--df-error-color);font-size:var(--df-error-font-size)}\n"] }]
679
- }], ctorParameters: () => [], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], key: [{ type: i0.Input, args: [{ isSignal: true, alias: "key", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], tabIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabIndex", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }], meta: [{ type: i0.Input, args: [{ isSignal: true, alias: "meta", required: false }] }], validationMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "validationMessages", required: false }] }], defaultValidationMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultValidationMessages", required: false }] }] } });
409
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: rgba(0, 0, 0, .6);--df-hint-font-size: .875rem;--df-error-color: #f44336;--df-error-font-size: .875rem;display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color)}.df-mat-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size)}.df-mat-error{color:var(--df-error-color);font-size:var(--df-error-font-size)}\n"] }]
410
+ }], ctorParameters: () => [], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }] } });
680
411
 
681
412
  var matMultiCheckbox_component = /*#__PURE__*/Object.freeze({
682
- __proto__: null,
683
- default: MatMultiCheckboxFieldComponent
413
+ __proto__: null,
414
+ default: MatMultiCheckboxFieldComponent
684
415
  });
685
416
 
686
417
  class MatRadioFieldComponent {
687
- elementRef = inject((ElementRef));
688
- field = input.required(...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
689
- key = input.required(...(ngDevMode ? [{ debugName: "key" }] : /* istanbul ignore next */ []));
690
- label = input(...(ngDevMode ? [undefined, { debugName: "label" }] : /* istanbul ignore next */ []));
691
- placeholder = input(...(ngDevMode ? [undefined, { debugName: "placeholder" }] : /* istanbul ignore next */ []));
692
- className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
693
- tabIndex = input(...(ngDevMode ? [undefined, { debugName: "tabIndex" }] : /* istanbul ignore next */ []));
418
+ ngf = injectNgForgeField();
694
419
  options = input([], ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
695
420
  props = input(...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
696
- meta = input(...(ngDevMode ? [undefined, { debugName: "meta" }] : /* istanbul ignore next */ []));
697
- validationMessages = input(...(ngDevMode ? [undefined, { debugName: "validationMessages" }] : /* istanbul ignore next */ []));
698
- defaultValidationMessages = input(...(ngDevMode ? [undefined, { debugName: "defaultValidationMessages" }] : /* istanbul ignore next */ []));
699
- resolvedErrors = createResolvedErrorsSignal(this.field, this.validationMessages, this.defaultValidationMessages);
700
- showErrors = shouldShowErrors(this.field);
701
- errorsToDisplay = computed(() => (this.showErrors() ? this.resolvedErrors() : []), ...(ngDevMode ? [{ debugName: "errorsToDisplay" }] : /* istanbul ignore next */ []));
702
- constructor() {
703
- // Apply meta attributes to all radio inputs, re-apply when options change
704
- setupMetaTracking(this.elementRef, this.meta, {
705
- selector: 'input[type="radio"]',
706
- dependents: [this.options],
707
- });
708
- }
709
- // ─────────────────────────────────────────────────────────────────────────────
710
- // Accessibility
711
- // ─────────────────────────────────────────────────────────────────────────────
712
- /** Unique ID for the hint element, used for aria-describedby */
713
- hintId = computed(() => `${this.key()}-hint`, ...(ngDevMode ? [{ debugName: "hintId" }] : /* istanbul ignore next */ []));
714
- /** Base ID for error elements, used for aria-describedby */
715
- errorId = computed(() => `${this.key()}-error`, ...(ngDevMode ? [{ debugName: "errorId" }] : /* istanbul ignore next */ []));
716
- /** aria-invalid: true when field is invalid AND touched, false otherwise */
717
- ariaInvalid = computed(() => {
718
- const fieldState = this.field()();
719
- return fieldState.invalid() && fieldState.touched();
720
- }, ...(ngDevMode ? [{ debugName: "ariaInvalid" }] : /* istanbul ignore next */ []));
721
- /** aria-required: true if field is required, null otherwise (to remove attribute) */
722
- ariaRequired = computed(() => {
723
- return this.field()().required?.() === true ? true : null;
724
- }, ...(ngDevMode ? [{ debugName: "ariaRequired" }] : /* istanbul ignore next */ []));
725
- /** aria-describedby: links to hint and error messages for screen readers */
726
- ariaDescribedBy = createAriaDescribedBySignal(this.errorsToDisplay, this.errorId, this.hintId, () => !!this.props()?.hint);
727
421
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: MatRadioFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
728
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: MatRadioFieldComponent, isStandalone: true, selector: "df-mat-radio", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, tabIndex: { classPropertyName: "tabIndex", publicName: "tabIndex", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null }, meta: { classPropertyName: "meta", publicName: "meta", isSignal: true, isRequired: false, transformFunction: null }, validationMessages: { classPropertyName: "validationMessages", publicName: "validationMessages", isSignal: true, isRequired: false, transformFunction: null }, defaultValidationMessages: { classPropertyName: "defaultValidationMessages", publicName: "defaultValidationMessages", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "className() || \"\"", "id": "`${key()}`", "attr.data-testid": "key()", "attr.hidden": "field()().hidden() || null" } }, ngImport: i0, template: `
729
- @let f = field();
730
- @let radioGroupId = key() + '-radio-group';
422
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: MatRadioFieldComponent, isStandalone: true, selector: "df-mat-radio", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, hostDirectives: [{ directive: i1.NgForgeFieldHost }], ngImport: i0, template: `
423
+ @let f = ngf.field();
424
+ @let radioGroupId = ngf.key() + '-radio-group';
731
425
 
732
- @if (label()) {
733
- <div class="radio-label">{{ label() | dynamicText | async }}</div>
426
+ @if (ngf.label()) {
427
+ <div class="radio-label">{{ ngf.label() | dynamicText | async }}</div>
734
428
  }
735
429
 
736
430
  <mat-radio-group
737
431
  [id]="radioGroupId"
738
432
  [formField]="f"
739
- [attr.aria-invalid]="ariaInvalid()"
740
- [attr.aria-required]="ariaRequired()"
741
- [attr.aria-describedby]="ariaDescribedBy()"
433
+ [attr.aria-invalid]="ngf.ariaInvalid()"
434
+ [attr.aria-required]="ngf.ariaRequired()"
435
+ [attr.aria-describedby]="ngf.ariaDescribedBy()"
742
436
  >
743
437
  @for (option of options(); track option.value) {
744
438
  <mat-radio-button
439
+ ngForgeControl="input[type='radio']"
745
440
  [value]="option.value"
746
441
  [disabled]="option.disabled || false"
747
442
  [color]="props()?.color || 'primary'"
@@ -752,32 +447,33 @@ class MatRadioFieldComponent {
752
447
  }
753
448
  </mat-radio-group>
754
449
 
755
- @if (errorsToDisplay()[0]; as error) {
756
- <mat-error [id]="errorId()">{{ error.message }}</mat-error>
450
+ @if (ngf.errorsToDisplay()[0]; as error) {
451
+ <mat-error [id]="ngf.errorId()">{{ error.message }}</mat-error>
757
452
  } @else if (props()?.hint; as hint) {
758
- <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
453
+ <div class="mat-hint" [id]="ngf.hintId()">{{ hint | dynamicText | async }}</div>
759
454
  }
760
- `, isInline: true, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: rgba(0, 0, 0, .6);--df-hint-font-size: .875rem;--df-error-color: #f44336;--df-error-font-size: .875rem;display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color)}.df-mat-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size)}.df-mat-error{color:var(--df-error-color);font-size:var(--df-error-font-size)}\n"], dependencies: [{ kind: "directive", type: MatRadioGroup, selector: "mat-radio-group", inputs: ["color", "name", "labelPosition", "value", "selected", "disabled", "required", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioGroup"] }, { kind: "component", type: MatRadioButton, selector: "mat-radio-button", inputs: ["id", "name", "aria-label", "aria-labelledby", "aria-describedby", "disableRipple", "tabIndex", "checked", "value", "labelPosition", "disabled", "required", "color", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioButton"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "directive", type: MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
455
+ `, isInline: true, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: rgba(0, 0, 0, .6);--df-hint-font-size: .875rem;--df-error-color: #f44336;--df-error-font-size: .875rem;display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color)}.df-mat-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size)}.df-mat-error{color:var(--df-error-color);font-size:var(--df-error-font-size)}\n"], dependencies: [{ kind: "directive", type: MatRadioGroup, selector: "mat-radio-group", inputs: ["color", "name", "labelPosition", "value", "selected", "disabled", "required", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioGroup"] }, { kind: "component", type: MatRadioButton, selector: "mat-radio-button", inputs: ["id", "name", "aria-label", "aria-labelledby", "aria-describedby", "disableRipple", "tabIndex", "checked", "value", "labelPosition", "disabled", "required", "color", "disabledInteractive"], outputs: ["change"], exportAs: ["matRadioButton"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "directive", type: MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: NgForgeControl, selector: "[ngForgeControl]", inputs: ["ngForgeControl"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
761
456
  }
762
457
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: MatRadioFieldComponent, decorators: [{
763
458
  type: Component,
764
- args: [{ selector: 'df-mat-radio', imports: [MatRadioGroup, MatRadioButton, FormField, MatError, DynamicTextPipe, AsyncPipe], template: `
765
- @let f = field();
766
- @let radioGroupId = key() + '-radio-group';
459
+ args: [{ selector: 'df-mat-radio', imports: [MatRadioGroup, MatRadioButton, FormField, MatError, DynamicTextPipe, AsyncPipe, NgForgeControl], hostDirectives: [NgForgeFieldHost], template: `
460
+ @let f = ngf.field();
461
+ @let radioGroupId = ngf.key() + '-radio-group';
767
462
 
768
- @if (label()) {
769
- <div class="radio-label">{{ label() | dynamicText | async }}</div>
463
+ @if (ngf.label()) {
464
+ <div class="radio-label">{{ ngf.label() | dynamicText | async }}</div>
770
465
  }
771
466
 
772
467
  <mat-radio-group
773
468
  [id]="radioGroupId"
774
469
  [formField]="f"
775
- [attr.aria-invalid]="ariaInvalid()"
776
- [attr.aria-required]="ariaRequired()"
777
- [attr.aria-describedby]="ariaDescribedBy()"
470
+ [attr.aria-invalid]="ngf.ariaInvalid()"
471
+ [attr.aria-required]="ngf.ariaRequired()"
472
+ [attr.aria-describedby]="ngf.ariaDescribedBy()"
778
473
  >
779
474
  @for (option of options(); track option.value) {
780
475
  <mat-radio-button
476
+ ngForgeControl="input[type='radio']"
781
477
  [value]="option.value"
782
478
  [disabled]="option.disabled || false"
783
479
  [color]="props()?.color || 'primary'"
@@ -788,94 +484,50 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
788
484
  }
789
485
  </mat-radio-group>
790
486
 
791
- @if (errorsToDisplay()[0]; as error) {
792
- <mat-error [id]="errorId()">{{ error.message }}</mat-error>
487
+ @if (ngf.errorsToDisplay()[0]; as error) {
488
+ <mat-error [id]="ngf.errorId()">{{ error.message }}</mat-error>
793
489
  } @else if (props()?.hint; as hint) {
794
- <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
490
+ <div class="mat-hint" [id]="ngf.hintId()">{{ hint | dynamicText | async }}</div>
795
491
  }
796
- `, host: {
797
- '[class]': 'className() || ""',
798
- '[id]': '`${key()}`',
799
- '[attr.data-testid]': 'key()',
800
- '[attr.hidden]': 'field()().hidden() || null',
801
- }, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: rgba(0, 0, 0, .6);--df-hint-font-size: .875rem;--df-error-color: #f44336;--df-error-font-size: .875rem;display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color)}.df-mat-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size)}.df-mat-error{color:var(--df-error-color);font-size:var(--df-error-font-size)}\n"] }]
802
- }], ctorParameters: () => [], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], key: [{ type: i0.Input, args: [{ isSignal: true, alias: "key", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], tabIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabIndex", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }], meta: [{ type: i0.Input, args: [{ isSignal: true, alias: "meta", required: false }] }], validationMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "validationMessages", required: false }] }], defaultValidationMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultValidationMessages", required: false }] }] } });
492
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: rgba(0, 0, 0, .6);--df-hint-font-size: .875rem;--df-error-color: #f44336;--df-error-font-size: .875rem;display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color)}.df-mat-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size)}.df-mat-error{color:var(--df-error-color);font-size:var(--df-error-font-size)}\n"] }]
493
+ }], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }] } });
803
494
 
804
495
  var matRadio_component = /*#__PURE__*/Object.freeze({
805
- __proto__: null,
806
- default: MatRadioFieldComponent
496
+ __proto__: null,
497
+ default: MatRadioFieldComponent
807
498
  });
808
499
 
809
500
  class MatSelectFieldComponent {
810
501
  materialConfig = inject(MATERIAL_CONFIG, { optional: true });
811
- elementRef = inject((ElementRef));
812
- field = input.required(...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
813
- key = input.required(...(ngDevMode ? [{ debugName: "key" }] : /* istanbul ignore next */ []));
814
- label = input(...(ngDevMode ? [undefined, { debugName: "label" }] : /* istanbul ignore next */ []));
815
- placeholder = input(...(ngDevMode ? [undefined, { debugName: "placeholder" }] : /* istanbul ignore next */ []));
816
- className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
817
- tabIndex = input(...(ngDevMode ? [undefined, { debugName: "tabIndex" }] : /* istanbul ignore next */ []));
502
+ ngf = injectNgForgeField();
818
503
  options = input([], ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
819
504
  props = input(...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
820
- meta = input(...(ngDevMode ? [undefined, { debugName: "meta" }] : /* istanbul ignore next */ []));
821
- validationMessages = input(...(ngDevMode ? [undefined, { debugName: "validationMessages" }] : /* istanbul ignore next */ []));
822
- defaultValidationMessages = input(...(ngDevMode ? [undefined, { debugName: "defaultValidationMessages" }] : /* istanbul ignore next */ []));
823
- effectiveAppearance = computed(() => this.props()?.appearance ?? this.materialConfig?.appearance ?? 'outline', ...(ngDevMode ? [{ debugName: "effectiveAppearance" }] : /* istanbul ignore next */ []));
824
- effectiveSubscriptSizing = computed(() => this.props()?.subscriptSizing ?? this.materialConfig?.subscriptSizing ?? 'dynamic', ...(ngDevMode ? [{ debugName: "effectiveSubscriptSizing" }] : /* istanbul ignore next */ []));
825
- effectiveFloatLabel = computed(() => this.props()?.floatLabel ?? this.materialConfig?.floatLabel ?? 'auto', ...(ngDevMode ? [{ debugName: "effectiveFloatLabel" }] : /* istanbul ignore next */ []));
826
- effectiveHideRequiredMarker = computed(() => this.props()?.hideRequiredMarker ?? this.materialConfig?.hideRequiredMarker ?? false, ...(ngDevMode ? [{ debugName: "effectiveHideRequiredMarker" }] : /* istanbul ignore next */ []));
827
- resolvedErrors = createResolvedErrorsSignal(this.field, this.validationMessages, this.defaultValidationMessages);
828
- showErrors = shouldShowErrors(this.field);
829
- errorsToDisplay = computed(() => (this.showErrors() ? this.resolvedErrors() : []), ...(ngDevMode ? [{ debugName: "errorsToDisplay" }] : /* istanbul ignore next */ []));
505
+ appearance = computed(() => this.props()?.appearance ?? this.materialConfig?.appearance ?? 'outline', ...(ngDevMode ? [{ debugName: "appearance" }] : /* istanbul ignore next */ []));
506
+ subscriptSizing = computed(() => this.props()?.subscriptSizing ?? this.materialConfig?.subscriptSizing ?? 'dynamic', ...(ngDevMode ? [{ debugName: "subscriptSizing" }] : /* istanbul ignore next */ []));
507
+ floatLabel = computed(() => this.props()?.floatLabel ?? this.materialConfig?.floatLabel ?? 'auto', ...(ngDevMode ? [{ debugName: "floatLabel" }] : /* istanbul ignore next */ []));
508
+ hideRequiredMarker = computed(() => this.props()?.hideRequiredMarker ?? this.materialConfig?.hideRequiredMarker ?? false, ...(ngDevMode ? [{ debugName: "hideRequiredMarker" }] : /* istanbul ignore next */ []));
830
509
  defaultCompare = Object.is;
831
- constructor() {
832
- // mat-select has no native input - apply meta to mat-select element
833
- setupMetaTracking(this.elementRef, this.meta, {
834
- selector: 'mat-select',
835
- });
836
- }
837
- // ─────────────────────────────────────────────────────────────────────────────
838
- // Accessibility
839
- // ─────────────────────────────────────────────────────────────────────────────
840
- /** Unique ID for the hint element, used for aria-describedby */
841
- hintId = computed(() => `${this.key()}-hint`, ...(ngDevMode ? [{ debugName: "hintId" }] : /* istanbul ignore next */ []));
842
- /** Base ID for error elements, used for aria-describedby */
843
- errorId = computed(() => `${this.key()}-error`, ...(ngDevMode ? [{ debugName: "errorId" }] : /* istanbul ignore next */ []));
844
- /** aria-invalid: true when field is invalid AND touched, false otherwise */
845
- ariaInvalid = computed(() => {
846
- const fieldState = this.field()();
847
- return fieldState.invalid() && fieldState.touched();
848
- }, ...(ngDevMode ? [{ debugName: "ariaInvalid" }] : /* istanbul ignore next */ []));
849
- /** aria-required: true if field is required, null otherwise (to remove attribute) */
850
- ariaRequired = computed(() => {
851
- return this.field()().required?.() === true ? true : null;
852
- }, ...(ngDevMode ? [{ debugName: "ariaRequired" }] : /* istanbul ignore next */ []));
853
- /** aria-describedby: links to hint and error messages for screen readers */
854
- ariaDescribedBy = createAriaDescribedBySignal(this.errorsToDisplay, this.errorId, this.hintId, () => !!this.props()?.hint);
855
510
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: MatSelectFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
856
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: MatSelectFieldComponent, isStandalone: true, selector: "df-mat-select", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, tabIndex: { classPropertyName: "tabIndex", publicName: "tabIndex", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null }, meta: { classPropertyName: "meta", publicName: "meta", isSignal: true, isRequired: false, transformFunction: null }, validationMessages: { classPropertyName: "validationMessages", publicName: "validationMessages", isSignal: true, isRequired: false, transformFunction: null }, defaultValidationMessages: { classPropertyName: "defaultValidationMessages", publicName: "defaultValidationMessages", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "id": "`${key()}`", "attr.data-testid": "key()", "class": "className()", "attr.hidden": "field()().hidden() || null" } }, ngImport: i0, template: `
857
- @let f = field();
858
- @let selectId = key() + '-select';
511
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: MatSelectFieldComponent, isStandalone: true, selector: "df-mat-select", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, hostDirectives: [{ directive: i1.NgForgeFieldHost }], ngImport: i0, template: `
512
+ @let selectId = ngf.key() + '-select';
859
513
 
860
514
  <mat-form-field
861
- [appearance]="effectiveAppearance()"
862
- [subscriptSizing]="effectiveSubscriptSizing()"
863
- [floatLabel]="effectiveFloatLabel()"
864
- [hideRequiredMarker]="effectiveHideRequiredMarker()"
515
+ [appearance]="appearance()"
516
+ [subscriptSizing]="subscriptSizing()"
517
+ [floatLabel]="floatLabel()"
518
+ [hideRequiredMarker]="hideRequiredMarker()"
865
519
  >
866
- @if (label(); as label) {
520
+ @if (ngf.label(); as label) {
867
521
  <mat-label>{{ label | dynamicText | async }}</mat-label>
868
522
  }
869
523
 
870
524
  <mat-select
525
+ ngForgeControl
871
526
  [id]="selectId"
872
- [formField]="f"
873
- [placeholder]="(placeholder() | dynamicText | async) ?? ''"
527
+ [formField]="ngf.field()"
528
+ [placeholder]="(ngf.placeholder() | dynamicText | async) ?? ''"
874
529
  [multiple]="props()?.multiple || false"
875
530
  [compareWith]="props()?.compareWith || defaultCompare"
876
- [attr.aria-invalid]="ariaInvalid()"
877
- [attr.aria-required]="ariaRequired()"
878
- [attr.aria-describedby]="ariaDescribedBy()"
879
531
  >
880
532
  @for (option of options(); track option.value) {
881
533
  <mat-option [value]="option.value" [disabled]="option.disabled || false">
@@ -884,39 +536,36 @@ class MatSelectFieldComponent {
884
536
  }
885
537
  </mat-select>
886
538
 
887
- @if (errorsToDisplay()[0]; as error) {
888
- <mat-error [id]="errorId()">{{ error.message }}</mat-error>
539
+ @if (ngf.errorsToDisplay()[0]; as error) {
540
+ <mat-error [id]="ngf.errorId()">{{ error.message }}</mat-error>
889
541
  } @else if (props()?.hint; as hint) {
890
- <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
542
+ <mat-hint [id]="ngf.hintId()">{{ hint | dynamicText | async }}</mat-hint>
891
543
  }
892
544
  </mat-form-field>
893
- `, isInline: true, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: rgba(0, 0, 0, .6);--df-hint-font-size: .875rem;--df-error-color: #f44336;--df-error-font-size: .875rem;display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color)}.df-mat-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size)}.df-mat-error{color:var(--df-error-color);font-size:var(--df-error-font-size)}\n", "mat-form-field{width:100%}\n"], dependencies: [{ kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: MatLabel, selector: "mat-label" }, { kind: "component", type: MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "directive", type: MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "directive", type: MatError$1, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
545
+ `, isInline: true, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: rgba(0, 0, 0, .6);--df-hint-font-size: .875rem;--df-error-color: #f44336;--df-error-font-size: .875rem;display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color)}.df-mat-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size)}.df-mat-error{color:var(--df-error-color);font-size:var(--df-error-font-size)}\n", "mat-form-field{width:100%}\n"], dependencies: [{ kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: MatLabel, selector: "mat-label" }, { kind: "component", type: MatSelect, selector: "mat-select", inputs: ["aria-describedby", "panelClass", "disabled", "disableRipple", "tabIndex", "hideSingleSelectionIndicator", "placeholder", "required", "multiple", "disableOptionCentering", "compareWith", "value", "aria-label", "aria-labelledby", "errorStateMatcher", "typeaheadDebounceInterval", "sortComparator", "id", "panelWidth", "canSelectNullableOptions"], outputs: ["openedChange", "opened", "closed", "selectionChange", "valueChange"], exportAs: ["matSelect"] }, { kind: "component", type: MatOption, selector: "mat-option", inputs: ["value", "id", "disabled"], outputs: ["onSelectionChange"], exportAs: ["matOption"] }, { kind: "directive", type: MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "directive", type: MatError$1, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: NgForgeControl, selector: "[ngForgeControl]", inputs: ["ngForgeControl"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
894
546
  }
895
547
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: MatSelectFieldComponent, decorators: [{
896
548
  type: Component,
897
- args: [{ selector: 'df-mat-select', imports: [MatFormField, MatLabel, MatSelect, MatOption, MatHint, FormField, MatError$1, DynamicTextPipe, AsyncPipe], template: `
898
- @let f = field();
899
- @let selectId = key() + '-select';
549
+ args: [{ selector: 'df-mat-select', imports: [MatFormField, MatLabel, MatSelect, MatOption, MatHint, FormField, MatError$1, DynamicTextPipe, AsyncPipe, NgForgeControl], hostDirectives: [NgForgeFieldHost], template: `
550
+ @let selectId = ngf.key() + '-select';
900
551
 
901
552
  <mat-form-field
902
- [appearance]="effectiveAppearance()"
903
- [subscriptSizing]="effectiveSubscriptSizing()"
904
- [floatLabel]="effectiveFloatLabel()"
905
- [hideRequiredMarker]="effectiveHideRequiredMarker()"
553
+ [appearance]="appearance()"
554
+ [subscriptSizing]="subscriptSizing()"
555
+ [floatLabel]="floatLabel()"
556
+ [hideRequiredMarker]="hideRequiredMarker()"
906
557
  >
907
- @if (label(); as label) {
558
+ @if (ngf.label(); as label) {
908
559
  <mat-label>{{ label | dynamicText | async }}</mat-label>
909
560
  }
910
561
 
911
562
  <mat-select
563
+ ngForgeControl
912
564
  [id]="selectId"
913
- [formField]="f"
914
- [placeholder]="(placeholder() | dynamicText | async) ?? ''"
565
+ [formField]="ngf.field()"
566
+ [placeholder]="(ngf.placeholder() | dynamicText | async) ?? ''"
915
567
  [multiple]="props()?.multiple || false"
916
568
  [compareWith]="props()?.compareWith || defaultCompare"
917
- [attr.aria-invalid]="ariaInvalid()"
918
- [attr.aria-required]="ariaRequired()"
919
- [attr.aria-describedby]="ariaDescribedBy()"
920
569
  >
921
570
  @for (option of options(); track option.value) {
922
571
  <mat-option [value]="option.value" [disabled]="option.disabled || false">
@@ -925,69 +574,31 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
925
574
  }
926
575
  </mat-select>
927
576
 
928
- @if (errorsToDisplay()[0]; as error) {
929
- <mat-error [id]="errorId()">{{ error.message }}</mat-error>
577
+ @if (ngf.errorsToDisplay()[0]; as error) {
578
+ <mat-error [id]="ngf.errorId()">{{ error.message }}</mat-error>
930
579
  } @else if (props()?.hint; as hint) {
931
- <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
580
+ <mat-hint [id]="ngf.hintId()">{{ hint | dynamicText | async }}</mat-hint>
932
581
  }
933
582
  </mat-form-field>
934
- `, changeDetection: ChangeDetectionStrategy.OnPush, host: {
935
- '[id]': '`${key()}`',
936
- '[attr.data-testid]': 'key()',
937
- '[class]': 'className()',
938
- '[attr.hidden]': 'field()().hidden() || null',
939
- }, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: rgba(0, 0, 0, .6);--df-hint-font-size: .875rem;--df-error-color: #f44336;--df-error-font-size: .875rem;display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color)}.df-mat-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size)}.df-mat-error{color:var(--df-error-color);font-size:var(--df-error-font-size)}\n", "mat-form-field{width:100%}\n"] }]
940
- }], ctorParameters: () => [], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], key: [{ type: i0.Input, args: [{ isSignal: true, alias: "key", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], tabIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabIndex", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }], meta: [{ type: i0.Input, args: [{ isSignal: true, alias: "meta", required: false }] }], validationMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "validationMessages", required: false }] }], defaultValidationMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultValidationMessages", required: false }] }] } });
583
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: rgba(0, 0, 0, .6);--df-hint-font-size: .875rem;--df-error-color: #f44336;--df-error-font-size: .875rem;display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color)}.df-mat-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size)}.df-mat-error{color:var(--df-error-color);font-size:var(--df-error-font-size)}\n", "mat-form-field{width:100%}\n"] }]
584
+ }], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }] } });
941
585
 
942
586
  var matSelect_component = /*#__PURE__*/Object.freeze({
943
- __proto__: null,
944
- default: MatSelectFieldComponent
587
+ __proto__: null,
588
+ default: MatSelectFieldComponent
945
589
  });
946
590
 
947
591
  class MatSliderFieldComponent {
948
- elementRef = inject((ElementRef));
949
- field = input.required(...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
950
- key = input.required(...(ngDevMode ? [{ debugName: "key" }] : /* istanbul ignore next */ []));
951
- constructor() {
952
- setupMetaTracking(this.elementRef, this.meta, { selector: 'input' });
953
- }
954
- label = input(...(ngDevMode ? [undefined, { debugName: "label" }] : /* istanbul ignore next */ []));
955
- placeholder = input(...(ngDevMode ? [undefined, { debugName: "placeholder" }] : /* istanbul ignore next */ []));
956
- className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
957
- tabIndex = input(...(ngDevMode ? [undefined, { debugName: "tabIndex" }] : /* istanbul ignore next */ []));
592
+ ngf = injectNgForgeField();
958
593
  step = input(...(ngDevMode ? [undefined, { debugName: "step" }] : /* istanbul ignore next */ []));
959
594
  props = input(...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
960
- meta = input(...(ngDevMode ? [undefined, { debugName: "meta" }] : /* istanbul ignore next */ []));
961
- validationMessages = input(...(ngDevMode ? [undefined, { debugName: "validationMessages" }] : /* istanbul ignore next */ []));
962
- defaultValidationMessages = input(...(ngDevMode ? [undefined, { debugName: "defaultValidationMessages" }] : /* istanbul ignore next */ []));
963
- resolvedErrors = createResolvedErrorsSignal(this.field, this.validationMessages, this.defaultValidationMessages);
964
- showErrors = shouldShowErrors(this.field);
965
- errorsToDisplay = computed(() => (this.showErrors() ? this.resolvedErrors() : []), ...(ngDevMode ? [{ debugName: "errorsToDisplay" }] : /* istanbul ignore next */ []));
966
- // ─────────────────────────────────────────────────────────────────────────────
967
- // Accessibility
968
- // ─────────────────────────────────────────────────────────────────────────────
969
- /** Unique ID for the hint element, used for aria-describedby */
970
- hintId = computed(() => `${this.key()}-hint`, ...(ngDevMode ? [{ debugName: "hintId" }] : /* istanbul ignore next */ []));
971
- /** Base ID for error elements, used for aria-describedby */
972
- errorId = computed(() => `${this.key()}-error`, ...(ngDevMode ? [{ debugName: "errorId" }] : /* istanbul ignore next */ []));
973
- /** aria-required: true when field has required validation, null otherwise */
974
- ariaRequired = computed(() => {
975
- return this.field()().required?.() === true ? true : null;
976
- }, ...(ngDevMode ? [{ debugName: "ariaRequired" }] : /* istanbul ignore next */ []));
977
- /** aria-invalid: true when field is invalid AND touched, false otherwise */
978
- ariaInvalid = computed(() => {
979
- const fieldState = this.field()();
980
- return fieldState.invalid() && fieldState.touched();
981
- }, ...(ngDevMode ? [{ debugName: "ariaInvalid" }] : /* istanbul ignore next */ []));
982
- /** aria-describedby: links to hint and error messages for screen readers */
983
- ariaDescribedBy = createAriaDescribedBySignal(this.errorsToDisplay, this.errorId, this.hintId, () => !!this.props()?.hint);
984
595
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: MatSliderFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
985
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: MatSliderFieldComponent, isStandalone: true, selector: "df-mat-slider", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, tabIndex: { classPropertyName: "tabIndex", publicName: "tabIndex", isSignal: true, isRequired: false, transformFunction: null }, step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null }, meta: { classPropertyName: "meta", publicName: "meta", isSignal: true, isRequired: false, transformFunction: null }, validationMessages: { classPropertyName: "validationMessages", publicName: "validationMessages", isSignal: true, isRequired: false, transformFunction: null }, defaultValidationMessages: { classPropertyName: "defaultValidationMessages", publicName: "defaultValidationMessages", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "className()", "id": "`${key()}`", "attr.data-testid": "key()", "attr.hidden": "field()().hidden() || null" } }, ngImport: i0, template: `
986
- @let f = field();
987
- @let inputId = key() + '-input';
596
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: MatSliderFieldComponent, isStandalone: true, selector: "df-mat-slider", inputs: { step: { classPropertyName: "step", publicName: "step", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, hostDirectives: [{ directive: i1.NgForgeFieldHost }], ngImport: i0, template: `
597
+ @let f = ngf.field();
598
+ @let inputId = ngf.key() + '-input';
988
599
 
989
- @if (label(); as label) {
990
- <div class="slider-label">{{ label | dynamicText | async }}</div>
600
+ @if (ngf.label(); as label) {
601
+ <label class="slider-label" [for]="inputId">{{ label | dynamicText | async }}</label>
991
602
  }
992
603
 
993
604
  <mat-slider
@@ -999,32 +610,24 @@ class MatSliderFieldComponent {
999
610
  [color]="props()?.color || 'primary'"
1000
611
  class="slider-container"
1001
612
  >
1002
- <input
1003
- matSliderThumb
1004
- [id]="inputId"
1005
- [formField]="f"
1006
- [attr.tabindex]="tabIndex()"
1007
- [attr.aria-invalid]="ariaInvalid()"
1008
- [attr.aria-required]="ariaRequired()"
1009
- [attr.aria-describedby]="ariaDescribedBy()"
1010
- />
613
+ <input matSliderThumb ngForgeControl [id]="inputId" [formField]="f" [attr.tabindex]="ngf.tabIndex()" />
1011
614
  </mat-slider>
1012
615
 
1013
- @if (errorsToDisplay()[0]; as error) {
1014
- <mat-error [id]="errorId()">{{ error.message }}</mat-error>
616
+ @if (ngf.errorsToDisplay()[0]; as error) {
617
+ <mat-error [id]="ngf.errorId()">{{ error.message }}</mat-error>
1015
618
  } @else if (props()?.hint; as hint) {
1016
- <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
619
+ <div class="mat-hint" [id]="ngf.hintId()">{{ hint | dynamicText | async }}</div>
1017
620
  }
1018
- `, isInline: true, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: rgba(0, 0, 0, .6);--df-hint-font-size: .875rem;--df-error-color: #f44336;--df-error-font-size: .875rem;display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color)}.df-mat-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size)}.df-mat-error{color:var(--df-error-color);font-size:var(--df-error-font-size)}\n", ".slider-container{width:100%}\n"], dependencies: [{ kind: "component", type: MatSlider, selector: "mat-slider", inputs: ["disabled", "discrete", "showTickMarks", "min", "color", "disableRipple", "max", "step", "displayWith"], exportAs: ["matSlider"] }, { kind: "directive", type: MatSliderThumb, selector: "input[matSliderThumb]", inputs: ["value"], outputs: ["valueChange", "dragStart", "dragEnd"], exportAs: ["matSliderThumb"] }, { kind: "directive", type: MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
621
+ `, isInline: true, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: rgba(0, 0, 0, .6);--df-hint-font-size: .875rem;--df-error-color: #f44336;--df-error-font-size: .875rem;display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color)}.df-mat-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size)}.df-mat-error{color:var(--df-error-color);font-size:var(--df-error-font-size)}\n", ".slider-container{width:100%}\n"], dependencies: [{ kind: "component", type: MatSlider, selector: "mat-slider", inputs: ["disabled", "discrete", "showTickMarks", "min", "color", "disableRipple", "max", "step", "displayWith"], exportAs: ["matSlider"] }, { kind: "directive", type: MatSliderThumb, selector: "input[matSliderThumb]", inputs: ["value"], outputs: ["valueChange", "dragStart", "dragEnd"], exportAs: ["matSliderThumb"] }, { kind: "directive", type: MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "directive", type: NgForgeControl, selector: "[ngForgeControl]", inputs: ["ngForgeControl"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1019
622
  }
1020
623
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: MatSliderFieldComponent, decorators: [{
1021
624
  type: Component,
1022
- args: [{ selector: 'df-mat-slider', imports: [MatSlider, MatSliderThumb, MatError, DynamicTextPipe, AsyncPipe, FormField], template: `
1023
- @let f = field();
1024
- @let inputId = key() + '-input';
625
+ args: [{ selector: 'df-mat-slider', imports: [MatSlider, MatSliderThumb, MatError, DynamicTextPipe, AsyncPipe, FormField, NgForgeControl], hostDirectives: [NgForgeFieldHost], template: `
626
+ @let f = ngf.field();
627
+ @let inputId = ngf.key() + '-input';
1025
628
 
1026
- @if (label(); as label) {
1027
- <div class="slider-label">{{ label | dynamicText | async }}</div>
629
+ @if (ngf.label(); as label) {
630
+ <label class="slider-label" [for]="inputId">{{ label | dynamicText | async }}</label>
1028
631
  }
1029
632
 
1030
633
  <mat-slider
@@ -1036,153 +639,79 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
1036
639
  [color]="props()?.color || 'primary'"
1037
640
  class="slider-container"
1038
641
  >
1039
- <input
1040
- matSliderThumb
1041
- [id]="inputId"
1042
- [formField]="f"
1043
- [attr.tabindex]="tabIndex()"
1044
- [attr.aria-invalid]="ariaInvalid()"
1045
- [attr.aria-required]="ariaRequired()"
1046
- [attr.aria-describedby]="ariaDescribedBy()"
1047
- />
642
+ <input matSliderThumb ngForgeControl [id]="inputId" [formField]="f" [attr.tabindex]="ngf.tabIndex()" />
1048
643
  </mat-slider>
1049
644
 
1050
- @if (errorsToDisplay()[0]; as error) {
1051
- <mat-error [id]="errorId()">{{ error.message }}</mat-error>
645
+ @if (ngf.errorsToDisplay()[0]; as error) {
646
+ <mat-error [id]="ngf.errorId()">{{ error.message }}</mat-error>
1052
647
  } @else if (props()?.hint; as hint) {
1053
- <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
648
+ <div class="mat-hint" [id]="ngf.hintId()">{{ hint | dynamicText | async }}</div>
1054
649
  }
1055
- `, host: {
1056
- '[class]': 'className()',
1057
- '[id]': '`${key()}`',
1058
- '[attr.data-testid]': 'key()',
1059
- '[attr.hidden]': 'field()().hidden() || null',
1060
- }, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: rgba(0, 0, 0, .6);--df-hint-font-size: .875rem;--df-error-color: #f44336;--df-error-font-size: .875rem;display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color)}.df-mat-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size)}.df-mat-error{color:var(--df-error-color);font-size:var(--df-error-font-size)}\n", ".slider-container{width:100%}\n"] }]
1061
- }], ctorParameters: () => [], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], key: [{ type: i0.Input, args: [{ isSignal: true, alias: "key", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], tabIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabIndex", required: false }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }], meta: [{ type: i0.Input, args: [{ isSignal: true, alias: "meta", required: false }] }], validationMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "validationMessages", required: false }] }], defaultValidationMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultValidationMessages", required: false }] }] } });
650
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: rgba(0, 0, 0, .6);--df-hint-font-size: .875rem;--df-error-color: #f44336;--df-error-font-size: .875rem;display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color)}.df-mat-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size)}.df-mat-error{color:var(--df-error-color);font-size:var(--df-error-font-size)}\n", ".slider-container{width:100%}\n"] }]
651
+ }], propDecorators: { step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }] } });
1062
652
 
1063
653
  var matSlider_component = /*#__PURE__*/Object.freeze({
1064
- __proto__: null,
1065
- default: MatSliderFieldComponent
654
+ __proto__: null,
655
+ default: MatSliderFieldComponent
1066
656
  });
1067
657
 
1068
658
  class MatButtonFieldComponent {
1069
- eventBus = inject(EventBus);
1070
- arrayContext = inject(ARRAY_CONTEXT, { optional: true });
1071
- key = input.required(...(ngDevMode ? [{ debugName: "key" }] : /* istanbul ignore next */ []));
1072
- label = input.required(...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
1073
- disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
1074
- hidden = input(false, ...(ngDevMode ? [{ debugName: "hidden" }] : /* istanbul ignore next */ []));
1075
- tabIndex = input(...(ngDevMode ? [undefined, { debugName: "tabIndex" }] : /* istanbul ignore next */ []));
1076
- className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
1077
- /** Event to dispatch on click. Optional for submit buttons (native form submit handles it). */
1078
- event = input(...(ngDevMode ? [undefined, { debugName: "event" }] : /* istanbul ignore next */ []));
1079
- eventArgs = input(...(ngDevMode ? [undefined, { debugName: "eventArgs" }] : /* istanbul ignore next */ []));
659
+ action = injectNgForgeAction();
1080
660
  props = input(...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
1081
- eventContext = input(...(ngDevMode ? [undefined, { debugName: "eventContext" }] : /* istanbul ignore next */ []));
1082
- /** Resolved button type - defaults to 'button' if not specified in props */
1083
661
  buttonType = computed(() => this.props()?.type ?? 'button', ...(ngDevMode ? [{ debugName: "buttonType" }] : /* istanbul ignore next */ []));
1084
- buttonTestId = computed(() => `${this.buttonType()}-${this.key()}`, ...(ngDevMode ? [{ debugName: "buttonTestId" }] : /* istanbul ignore next */ []));
1085
- /**
1086
- * Handle button click.
1087
- * - For submit buttons (type="submit"): do nothing, native form submit handles it
1088
- * - For other buttons: dispatch the configured event via EventBus
1089
- */
662
+ buttonTestId = computed(() => `${this.buttonType()}-${this.action.key()}`, ...(ngDevMode ? [{ debugName: "buttonTestId" }] : /* istanbul ignore next */ []));
1090
663
  onClick() {
1091
- // Native submit buttons let the form handle submission
1092
- if (this.buttonType() === 'submit') {
664
+ if (this.buttonType() === 'submit')
1093
665
  return;
1094
- }
1095
- // Other buttons dispatch their event (if configured)
1096
- const event = this.event();
1097
- if (event) {
1098
- this.dispatchEvent(event);
1099
- }
1100
- }
1101
- dispatchEvent(event) {
1102
- const args = this.eventArgs();
1103
- if (args && args.length > 0) {
1104
- // Build context from injected ARRAY_CONTEXT (with linkedSignal index) or fallback to eventContext
1105
- const context = this.arrayContext
1106
- ? {
1107
- key: this.key(),
1108
- // Read signal to get current index (automatically updates via linkedSignal)
1109
- index: this.arrayContext.index(),
1110
- arrayKey: this.arrayContext.arrayKey,
1111
- formValue: this.arrayContext.formValue,
1112
- }
1113
- : this.eventContext() || { key: this.key() };
1114
- // Resolve tokens in event args using the provided context
1115
- const resolvedArgs = resolveTokens(args, context);
1116
- // Dispatch event with resolved args
1117
- this.eventBus.dispatch(event, ...resolvedArgs);
1118
- }
1119
- else {
1120
- // No args, dispatch event without arguments
1121
- this.eventBus.dispatch(event);
1122
- }
666
+ this.action.dispatch();
1123
667
  }
1124
668
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: MatButtonFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1125
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.8", type: MatButtonFieldComponent, isStandalone: true, selector: "df-mat-button", inputs: { key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: true, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, hidden: { classPropertyName: "hidden", publicName: "hidden", isSignal: true, isRequired: false, transformFunction: null }, tabIndex: { classPropertyName: "tabIndex", publicName: "tabIndex", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, event: { classPropertyName: "event", publicName: "event", isSignal: true, isRequired: false, transformFunction: null }, eventArgs: { classPropertyName: "eventArgs", publicName: "eventArgs", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null }, eventContext: { classPropertyName: "eventContext", publicName: "eventContext", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "id": "`${key()}`", "attr.data-testid": "key()", "class": "className()", "attr.hidden": "hidden() || null" } }, ngImport: i0, template: `
1126
- @let buttonId = key() + '-button';
669
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.8", type: MatButtonFieldComponent, isStandalone: true, selector: "df-mat-button", inputs: { props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, hostDirectives: [{ directive: i1.NgForgeActionHost }], ngImport: i0, template: `
670
+ @let buttonId = action.key() + '-button';
1127
671
  <button
1128
672
  mat-raised-button
1129
673
  [id]="buttonId"
1130
674
  [type]="buttonType()"
1131
675
  [color]="props()?.color || 'primary'"
1132
- [disabled]="disabled()"
676
+ [disabled]="action.disabled()"
677
+ [attr.tabindex]="action.tabIndex()"
1133
678
  [attr.data-testid]="buttonTestId()"
1134
679
  (click)="onClick()"
1135
680
  >
1136
- {{ label() | dynamicText | async }}
681
+ {{ action.label() | dynamicText | async }}
1137
682
  </button>
1138
683
  `, isInline: true, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: rgba(0, 0, 0, .6);--df-hint-font-size: .875rem;--df-error-color: #f44336;--df-error-font-size: .875rem;display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color)}.df-mat-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size)}.df-mat-error{color:var(--df-error-color);font-size:var(--df-error-font-size)}\n", "button{min-width:fit-content}\n"], dependencies: [{ kind: "component", type: MatButton, selector: " button[matButton], a[matButton], button[mat-button], button[mat-raised-button], button[mat-flat-button], button[mat-stroked-button], a[mat-button], a[mat-raised-button], a[mat-flat-button], a[mat-stroked-button] ", inputs: ["matButton"], exportAs: ["matButton", "matAnchor"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1139
684
  }
1140
685
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: MatButtonFieldComponent, decorators: [{
1141
686
  type: Component,
1142
- args: [{ selector: 'df-mat-button', imports: [MatButton, DynamicTextPipe, AsyncPipe], host: {
1143
- '[id]': '`${key()}`',
1144
- '[attr.data-testid]': 'key()',
1145
- '[class]': 'className()',
1146
- '[attr.hidden]': 'hidden() || null',
1147
- }, template: `
1148
- @let buttonId = key() + '-button';
687
+ args: [{ selector: 'df-mat-button', imports: [MatButton, DynamicTextPipe, AsyncPipe], hostDirectives: [NgForgeActionHost], template: `
688
+ @let buttonId = action.key() + '-button';
1149
689
  <button
1150
690
  mat-raised-button
1151
691
  [id]="buttonId"
1152
692
  [type]="buttonType()"
1153
693
  [color]="props()?.color || 'primary'"
1154
- [disabled]="disabled()"
694
+ [disabled]="action.disabled()"
695
+ [attr.tabindex]="action.tabIndex()"
1155
696
  [attr.data-testid]="buttonTestId()"
1156
697
  (click)="onClick()"
1157
698
  >
1158
- {{ label() | dynamicText | async }}
699
+ {{ action.label() | dynamicText | async }}
1159
700
  </button>
1160
701
  `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: rgba(0, 0, 0, .6);--df-hint-font-size: .875rem;--df-error-color: #f44336;--df-error-font-size: .875rem;display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color)}.df-mat-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size)}.df-mat-error{color:var(--df-error-color);font-size:var(--df-error-font-size)}\n", "button{min-width:fit-content}\n"] }]
1161
- }], propDecorators: { key: [{ type: i0.Input, args: [{ isSignal: true, alias: "key", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: true }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], hidden: [{ type: i0.Input, args: [{ isSignal: true, alias: "hidden", required: false }] }], tabIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabIndex", required: false }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], event: [{ type: i0.Input, args: [{ isSignal: true, alias: "event", required: false }] }], eventArgs: [{ type: i0.Input, args: [{ isSignal: true, alias: "eventArgs", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }], eventContext: [{ type: i0.Input, args: [{ isSignal: true, alias: "eventContext", required: false }] }] } });
702
+ }], propDecorators: { props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }] } });
1162
703
 
1163
704
  var matButton_component = /*#__PURE__*/Object.freeze({
1164
- __proto__: null,
1165
- default: MatButtonFieldComponent
705
+ __proto__: null,
706
+ default: MatButtonFieldComponent
1166
707
  });
1167
708
 
1168
709
  // Public API - component
1169
710
 
1170
711
  class MatTextareaFieldComponent {
1171
712
  materialConfig = inject(MATERIAL_CONFIG, { optional: true });
1172
- elementRef = inject((ElementRef));
1173
- field = input.required(...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
1174
- key = input.required(...(ngDevMode ? [{ debugName: "key" }] : /* istanbul ignore next */ []));
1175
- label = input(...(ngDevMode ? [undefined, { debugName: "label" }] : /* istanbul ignore next */ []));
1176
- placeholder = input(...(ngDevMode ? [undefined, { debugName: "placeholder" }] : /* istanbul ignore next */ []));
1177
- className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
1178
- tabIndex = input(...(ngDevMode ? [undefined, { debugName: "tabIndex" }] : /* istanbul ignore next */ []));
713
+ ngf = injectNgForgeField();
1179
714
  props = input(...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
1180
- meta = input(...(ngDevMode ? [undefined, { debugName: "meta" }] : /* istanbul ignore next */ []));
1181
- validationMessages = input(...(ngDevMode ? [undefined, { debugName: "validationMessages" }] : /* istanbul ignore next */ []));
1182
- defaultValidationMessages = input(...(ngDevMode ? [undefined, { debugName: "defaultValidationMessages" }] : /* istanbul ignore next */ []));
1183
- constructor() {
1184
- setupMetaTracking(this.elementRef, this.meta, { selector: 'textarea' });
1185
- }
1186
715
  /**
1187
716
  * Reference to the native textarea element.
1188
717
  * Used to imperatively sync the readonly attribute since Angular Signal Forms'
@@ -1193,7 +722,7 @@ class MatTextareaFieldComponent {
1193
722
  * Computed signal that extracts the readonly state from the field.
1194
723
  * Used by the effect to reactively sync the readonly attribute to the DOM.
1195
724
  */
1196
- isReadonly = computed(() => this.field()().readonly(), ...(ngDevMode ? [{ debugName: "isReadonly" }] : /* istanbul ignore next */ []));
725
+ isReadonly = computed(() => this.ngf.field()().readonly(), ...(ngDevMode ? [{ debugName: "isReadonly" }] : /* istanbul ignore next */ []));
1197
726
  /**
1198
727
  * Workaround: Angular Signal Forms' [field] directive does NOT sync the readonly
1199
728
  * attribute to the DOM, even though FieldState.readonly() returns the correct value.
@@ -1221,257 +750,147 @@ class MatTextareaFieldComponent {
1221
750
  }
1222
751
  },
1223
752
  });
1224
- effectiveAppearance = computed(() => this.props()?.appearance ?? this.materialConfig?.appearance ?? 'outline', ...(ngDevMode ? [{ debugName: "effectiveAppearance" }] : /* istanbul ignore next */ []));
1225
- effectiveSubscriptSizing = computed(() => this.props()?.subscriptSizing ?? this.materialConfig?.subscriptSizing ?? 'dynamic', ...(ngDevMode ? [{ debugName: "effectiveSubscriptSizing" }] : /* istanbul ignore next */ []));
1226
- effectiveFloatLabel = computed(() => this.props()?.floatLabel ?? this.materialConfig?.floatLabel ?? 'auto', ...(ngDevMode ? [{ debugName: "effectiveFloatLabel" }] : /* istanbul ignore next */ []));
1227
- effectiveHideRequiredMarker = computed(() => this.props()?.hideRequiredMarker ?? this.materialConfig?.hideRequiredMarker ?? false, ...(ngDevMode ? [{ debugName: "effectiveHideRequiredMarker" }] : /* istanbul ignore next */ []));
1228
- resolvedErrors = createResolvedErrorsSignal(this.field, this.validationMessages, this.defaultValidationMessages);
1229
- showErrors = shouldShowErrors(this.field);
1230
- errorsToDisplay = computed(() => (this.showErrors() ? this.resolvedErrors() : []), ...(ngDevMode ? [{ debugName: "errorsToDisplay" }] : /* istanbul ignore next */ []));
1231
- // ─────────────────────────────────────────────────────────────────────────────
1232
- // Accessibility
1233
- // ─────────────────────────────────────────────────────────────────────────────
1234
- /** Unique ID for the hint element, used for aria-describedby */
1235
- hintId = computed(() => `${this.key()}-hint`, ...(ngDevMode ? [{ debugName: "hintId" }] : /* istanbul ignore next */ []));
1236
- /** Base ID for error elements, used for aria-describedby */
1237
- errorId = computed(() => `${this.key()}-error`, ...(ngDevMode ? [{ debugName: "errorId" }] : /* istanbul ignore next */ []));
1238
- /** aria-invalid: true when field is invalid AND touched, false otherwise */
1239
- ariaInvalid = computed(() => {
1240
- const fieldState = this.field()();
1241
- return fieldState.invalid() && fieldState.touched();
1242
- }, ...(ngDevMode ? [{ debugName: "ariaInvalid" }] : /* istanbul ignore next */ []));
1243
- /** aria-required: true if field is required, null otherwise (to remove attribute) */
1244
- ariaRequired = computed(() => {
1245
- return this.field()().required?.() === true ? true : null;
1246
- }, ...(ngDevMode ? [{ debugName: "ariaRequired" }] : /* istanbul ignore next */ []));
1247
- /** aria-describedby: links to hint and error messages for screen readers */
1248
- ariaDescribedBy = createAriaDescribedBySignal(this.errorsToDisplay, this.errorId, this.hintId, () => !!this.props()?.hint);
753
+ appearance = computed(() => this.props()?.appearance ?? this.materialConfig?.appearance ?? 'outline', ...(ngDevMode ? [{ debugName: "appearance" }] : /* istanbul ignore next */ []));
754
+ subscriptSizing = computed(() => this.props()?.subscriptSizing ?? this.materialConfig?.subscriptSizing ?? 'dynamic', ...(ngDevMode ? [{ debugName: "subscriptSizing" }] : /* istanbul ignore next */ []));
755
+ floatLabel = computed(() => this.props()?.floatLabel ?? this.materialConfig?.floatLabel ?? 'auto', ...(ngDevMode ? [{ debugName: "floatLabel" }] : /* istanbul ignore next */ []));
756
+ hideRequiredMarker = computed(() => this.props()?.hideRequiredMarker ?? this.materialConfig?.hideRequiredMarker ?? false, ...(ngDevMode ? [{ debugName: "hideRequiredMarker" }] : /* istanbul ignore next */ []));
1249
757
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: MatTextareaFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1250
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: MatTextareaFieldComponent, isStandalone: true, selector: "df-mat-textarea", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, tabIndex: { classPropertyName: "tabIndex", publicName: "tabIndex", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null }, meta: { classPropertyName: "meta", publicName: "meta", isSignal: true, isRequired: false, transformFunction: null }, validationMessages: { classPropertyName: "validationMessages", publicName: "validationMessages", isSignal: true, isRequired: false, transformFunction: null }, defaultValidationMessages: { classPropertyName: "defaultValidationMessages", publicName: "defaultValidationMessages", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "id": "`${key()}`", "attr.data-testid": "key()", "class": "className()", "attr.hidden": "field()().hidden() || null" } }, viewQueries: [{ propertyName: "textareaRef", first: true, predicate: ["textareaRef"], descendants: true, isSignal: true }], ngImport: i0, template: `
1251
- @let f = field();
1252
- @let textareaId = key() + '-textarea';
758
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: MatTextareaFieldComponent, isStandalone: true, selector: "df-mat-textarea", inputs: { props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, viewQueries: [{ propertyName: "textareaRef", first: true, predicate: ["textareaRef"], descendants: true, isSignal: true }], hostDirectives: [{ directive: i1.NgForgeFieldHost }], ngImport: i0, template: `
759
+ @let textareaId = ngf.key() + '-textarea';
1253
760
 
1254
761
  <mat-form-field
1255
- [appearance]="effectiveAppearance()"
1256
- [subscriptSizing]="effectiveSubscriptSizing()"
1257
- [floatLabel]="effectiveFloatLabel()"
1258
- [hideRequiredMarker]="effectiveHideRequiredMarker()"
762
+ [appearance]="appearance()"
763
+ [subscriptSizing]="subscriptSizing()"
764
+ [floatLabel]="floatLabel()"
765
+ [hideRequiredMarker]="hideRequiredMarker()"
1259
766
  >
1260
- @if (label()) {
1261
- <mat-label>{{ label() | dynamicText | async }}</mat-label>
767
+ @if (ngf.label()) {
768
+ <mat-label>{{ ngf.label() | dynamicText | async }}</mat-label>
1262
769
  }
1263
770
 
1264
771
  <textarea
772
+ ngForgeControl
1265
773
  #textareaRef
1266
774
  matInput
1267
775
  [id]="textareaId"
1268
- [formField]="f"
1269
- [placeholder]="(placeholder() | dynamicText | async) ?? ''"
1270
- [attr.tabindex]="tabIndex()"
1271
- [attr.aria-invalid]="ariaInvalid()"
1272
- [attr.aria-required]="ariaRequired()"
1273
- [attr.aria-describedby]="ariaDescribedBy()"
776
+ [formField]="ngf.field()"
777
+ [placeholder]="(ngf.placeholder() | dynamicText | async) ?? ''"
778
+ [attr.tabindex]="ngf.tabIndex()"
1274
779
  [style.resize]="props()?.resize || 'vertical'"
1275
780
  ></textarea>
1276
781
 
1277
- @if (errorsToDisplay()[0]; as error) {
1278
- <mat-error [id]="errorId()">{{ error.message }}</mat-error>
782
+ @if (ngf.errorsToDisplay()[0]; as error) {
783
+ <mat-error [id]="ngf.errorId()">{{ error.message }}</mat-error>
1279
784
  } @else if (props()?.hint; as hint) {
1280
- <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
785
+ <mat-hint [id]="ngf.hintId()">{{ hint | dynamicText | async }}</mat-hint>
1281
786
  }
1282
787
  </mat-form-field>
1283
- `, isInline: true, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: rgba(0, 0, 0, .6);--df-hint-font-size: .875rem;--df-error-color: #f44336;--df-error-font-size: .875rem;display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color)}.df-mat-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size)}.df-mat-error{color:var(--df-error-color);font-size:var(--df-error-font-size)}\n", "mat-form-field{width:100%}\n"], dependencies: [{ kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: MatLabel, selector: "mat-label" }, { kind: "directive", type: MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "directive", type: MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "directive", type: MatError$1, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
788
+ `, isInline: true, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: rgba(0, 0, 0, .6);--df-hint-font-size: .875rem;--df-error-color: #f44336;--df-error-font-size: .875rem;display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color)}.df-mat-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size)}.df-mat-error{color:var(--df-error-color);font-size:var(--df-error-font-size)}\n", "mat-form-field{width:100%}\n"], dependencies: [{ kind: "component", type: MatFormField, selector: "mat-form-field", inputs: ["hideRequiredMarker", "color", "floatLabel", "appearance", "subscriptSizing", "hintLabel"], exportAs: ["matFormField"] }, { kind: "directive", type: MatLabel, selector: "mat-label" }, { kind: "directive", type: MatInput, selector: "input[matInput], textarea[matInput], select[matNativeControl], input[matNativeControl], textarea[matNativeControl]", inputs: ["disabled", "id", "placeholder", "name", "required", "type", "errorStateMatcher", "aria-describedby", "value", "readonly", "disabledInteractive"], exportAs: ["matInput"] }, { kind: "directive", type: MatHint, selector: "mat-hint", inputs: ["align", "id"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "directive", type: MatError$1, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: NgForgeControl, selector: "[ngForgeControl]", inputs: ["ngForgeControl"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1284
789
  }
1285
790
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: MatTextareaFieldComponent, decorators: [{
1286
791
  type: Component,
1287
- args: [{ selector: 'df-mat-textarea', imports: [MatFormField, MatLabel, MatInput, MatHint, FormField, MatError$1, DynamicTextPipe, AsyncPipe], template: `
1288
- @let f = field();
1289
- @let textareaId = key() + '-textarea';
792
+ args: [{ selector: 'df-mat-textarea', imports: [MatFormField, MatLabel, MatInput, MatHint, FormField, MatError$1, DynamicTextPipe, AsyncPipe, NgForgeControl], hostDirectives: [NgForgeFieldHost], template: `
793
+ @let textareaId = ngf.key() + '-textarea';
1290
794
 
1291
795
  <mat-form-field
1292
- [appearance]="effectiveAppearance()"
1293
- [subscriptSizing]="effectiveSubscriptSizing()"
1294
- [floatLabel]="effectiveFloatLabel()"
1295
- [hideRequiredMarker]="effectiveHideRequiredMarker()"
796
+ [appearance]="appearance()"
797
+ [subscriptSizing]="subscriptSizing()"
798
+ [floatLabel]="floatLabel()"
799
+ [hideRequiredMarker]="hideRequiredMarker()"
1296
800
  >
1297
- @if (label()) {
1298
- <mat-label>{{ label() | dynamicText | async }}</mat-label>
801
+ @if (ngf.label()) {
802
+ <mat-label>{{ ngf.label() | dynamicText | async }}</mat-label>
1299
803
  }
1300
804
 
1301
805
  <textarea
806
+ ngForgeControl
1302
807
  #textareaRef
1303
808
  matInput
1304
809
  [id]="textareaId"
1305
- [formField]="f"
1306
- [placeholder]="(placeholder() | dynamicText | async) ?? ''"
1307
- [attr.tabindex]="tabIndex()"
1308
- [attr.aria-invalid]="ariaInvalid()"
1309
- [attr.aria-required]="ariaRequired()"
1310
- [attr.aria-describedby]="ariaDescribedBy()"
810
+ [formField]="ngf.field()"
811
+ [placeholder]="(ngf.placeholder() | dynamicText | async) ?? ''"
812
+ [attr.tabindex]="ngf.tabIndex()"
1311
813
  [style.resize]="props()?.resize || 'vertical'"
1312
814
  ></textarea>
1313
815
 
1314
- @if (errorsToDisplay()[0]; as error) {
1315
- <mat-error [id]="errorId()">{{ error.message }}</mat-error>
816
+ @if (ngf.errorsToDisplay()[0]; as error) {
817
+ <mat-error [id]="ngf.errorId()">{{ error.message }}</mat-error>
1316
818
  } @else if (props()?.hint; as hint) {
1317
- <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
819
+ <mat-hint [id]="ngf.hintId()">{{ hint | dynamicText | async }}</mat-hint>
1318
820
  }
1319
821
  </mat-form-field>
1320
- `, changeDetection: ChangeDetectionStrategy.OnPush, host: {
1321
- '[id]': '`${key()}`',
1322
- '[attr.data-testid]': 'key()',
1323
- '[class]': 'className()',
1324
- '[attr.hidden]': 'field()().hidden() || null',
1325
- }, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: rgba(0, 0, 0, .6);--df-hint-font-size: .875rem;--df-error-color: #f44336;--df-error-font-size: .875rem;display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color)}.df-mat-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size)}.df-mat-error{color:var(--df-error-color);font-size:var(--df-error-font-size)}\n", "mat-form-field{width:100%}\n"] }]
1326
- }], ctorParameters: () => [], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], key: [{ type: i0.Input, args: [{ isSignal: true, alias: "key", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], tabIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabIndex", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }], meta: [{ type: i0.Input, args: [{ isSignal: true, alias: "meta", required: false }] }], validationMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "validationMessages", required: false }] }], defaultValidationMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultValidationMessages", required: false }] }], textareaRef: [{ type: i0.ViewChild, args: ['textareaRef', { isSignal: true }] }] } });
822
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: rgba(0, 0, 0, .6);--df-hint-font-size: .875rem;--df-error-color: #f44336;--df-error-font-size: .875rem;display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color)}.df-mat-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size)}.df-mat-error{color:var(--df-error-color);font-size:var(--df-error-font-size)}\n", "mat-form-field{width:100%}\n"] }]
823
+ }], propDecorators: { props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }], textareaRef: [{ type: i0.ViewChild, args: ['textareaRef', { isSignal: true }] }] } });
1327
824
 
1328
825
  var matTextarea_component = /*#__PURE__*/Object.freeze({
1329
- __proto__: null,
1330
- default: MatTextareaFieldComponent
826
+ __proto__: null,
827
+ default: MatTextareaFieldComponent
1331
828
  });
1332
829
 
1333
830
  class MatToggleFieldComponent {
1334
831
  materialConfig = inject(MATERIAL_CONFIG, { optional: true });
1335
- elementRef = inject((ElementRef));
1336
- field = input.required(...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
1337
- key = input.required(...(ngDevMode ? [{ debugName: "key" }] : /* istanbul ignore next */ []));
1338
- label = input(...(ngDevMode ? [undefined, { debugName: "label" }] : /* istanbul ignore next */ []));
1339
- placeholder = input(...(ngDevMode ? [undefined, { debugName: "placeholder" }] : /* istanbul ignore next */ []));
1340
- className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
1341
- tabIndex = input(...(ngDevMode ? [undefined, { debugName: "tabIndex" }] : /* istanbul ignore next */ []));
832
+ ngf = injectNgForgeField();
1342
833
  props = input(...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
1343
- meta = input(...(ngDevMode ? [undefined, { debugName: "meta" }] : /* istanbul ignore next */ []));
1344
- validationMessages = input(...(ngDevMode ? [undefined, { debugName: "validationMessages" }] : /* istanbul ignore next */ []));
1345
- defaultValidationMessages = input(...(ngDevMode ? [undefined, { debugName: "defaultValidationMessages" }] : /* istanbul ignore next */ []));
1346
- effectiveDisableRipple = computed(() => this.props()?.disableRipple ?? this.materialConfig?.disableRipple ?? false, ...(ngDevMode ? [{ debugName: "effectiveDisableRipple" }] : /* istanbul ignore next */ []));
1347
- resolvedErrors = createResolvedErrorsSignal(this.field, this.validationMessages, this.defaultValidationMessages);
1348
- showErrors = shouldShowErrors(this.field);
1349
- errorsToDisplay = computed(() => (this.showErrors() ? this.resolvedErrors() : []), ...(ngDevMode ? [{ debugName: "errorsToDisplay" }] : /* istanbul ignore next */ []));
1350
- constructor() {
1351
- // Apply meta attributes to the internal toggle button
1352
- // Note: mat-slide-toggle uses <button role="switch"> instead of <input type="checkbox">
1353
- setupMetaTracking(this.elementRef, this.meta, {
1354
- selector: 'button[role="switch"]',
1355
- });
1356
- }
1357
- // ─────────────────────────────────────────────────────────────────────────────
1358
- // Accessibility
1359
- // ─────────────────────────────────────────────────────────────────────────────
1360
- /** Unique ID for the hint element, used for aria-describedby */
1361
- hintId = computed(() => `${this.key()}-hint`, ...(ngDevMode ? [{ debugName: "hintId" }] : /* istanbul ignore next */ []));
1362
- /** Base ID for error elements, used for aria-describedby */
1363
- errorId = computed(() => `${this.key()}-error`, ...(ngDevMode ? [{ debugName: "errorId" }] : /* istanbul ignore next */ []));
1364
- /** aria-invalid: true when field is invalid AND touched, false otherwise */
1365
- ariaInvalid = computed(() => {
1366
- const fieldState = this.field()();
1367
- return fieldState.invalid() && fieldState.touched();
1368
- }, ...(ngDevMode ? [{ debugName: "ariaInvalid" }] : /* istanbul ignore next */ []));
1369
- /** aria-required: true if field is required, null otherwise (to remove attribute) */
1370
- ariaRequired = computed(() => {
1371
- return this.field()().required?.() === true ? true : null;
1372
- }, ...(ngDevMode ? [{ debugName: "ariaRequired" }] : /* istanbul ignore next */ []));
1373
- /** aria-describedby: links to hint and error messages for screen readers */
1374
- ariaDescribedBy = createAriaDescribedBySignal(this.errorsToDisplay, this.errorId, this.hintId, () => !!this.props()?.hint);
1375
- /**
1376
- * Workaround: Angular Material's MatSlideToggle does NOT propagate aria-required to its internal
1377
- * button element. This effect imperatively sets/removes aria-required on the internal button.
1378
- *
1379
- * Uses afterRenderEffect to ensure DOM is ready before querying internal elements.
1380
- */
1381
- syncAriaRequiredToDom = afterRenderEffect(() => {
1382
- const isRequired = this.ariaRequired();
1383
- const buttonEl = this.elementRef.nativeElement.querySelector('button[role="switch"]');
1384
- if (buttonEl) {
1385
- if (isRequired) {
1386
- buttonEl.setAttribute('aria-required', 'true');
1387
- }
1388
- else {
1389
- buttonEl.removeAttribute('aria-required');
1390
- }
1391
- }
1392
- });
1393
- /**
1394
- * Workaround: Angular Material's MatSlideToggle does NOT propagate aria-describedby to its internal
1395
- * button element. This effect imperatively sets/removes aria-describedby on the internal button.
1396
- *
1397
- * Uses afterRenderEffect to ensure DOM is ready before querying internal elements.
1398
- */
1399
- syncAriaDescribedByToDom = afterRenderEffect(() => {
1400
- const describedBy = this.ariaDescribedBy();
1401
- const buttonEl = this.elementRef.nativeElement.querySelector('button[role="switch"]');
1402
- if (buttonEl) {
1403
- if (describedBy) {
1404
- buttonEl.setAttribute('aria-describedby', describedBy);
1405
- }
1406
- else {
1407
- buttonEl.removeAttribute('aria-describedby');
1408
- }
1409
- }
1410
- });
834
+ disableRipple = computed(() => this.props()?.disableRipple ?? this.materialConfig?.disableRipple ?? false, ...(ngDevMode ? [{ debugName: "disableRipple" }] : /* istanbul ignore next */ []));
1411
835
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: MatToggleFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1412
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: MatToggleFieldComponent, isStandalone: true, selector: "df-mat-toggle", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, tabIndex: { classPropertyName: "tabIndex", publicName: "tabIndex", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null }, meta: { classPropertyName: "meta", publicName: "meta", isSignal: true, isRequired: false, transformFunction: null }, validationMessages: { classPropertyName: "validationMessages", publicName: "validationMessages", isSignal: true, isRequired: false, transformFunction: null }, defaultValidationMessages: { classPropertyName: "defaultValidationMessages", publicName: "defaultValidationMessages", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "className()", "id": "`${key()}`", "attr.data-testid": "key()", "attr.hidden": "field()().hidden() || null" } }, ngImport: i0, template: `
1413
- @let f = field();
1414
- @let toggleId = key() + '-toggle';
836
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.8", type: MatToggleFieldComponent, isStandalone: true, selector: "df-mat-toggle", inputs: { props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, hostDirectives: [{ directive: i1.NgForgeFieldHost }], ngImport: i0, template: `
837
+ @let f = ngf.field();
838
+ @let toggleId = ngf.key() + '-toggle';
1415
839
 
1416
840
  <mat-slide-toggle
841
+ ngForgeControl="button[role='switch']"
1417
842
  [id]="toggleId"
1418
843
  [formField]="f"
1419
844
  [color]="props()?.color || 'primary'"
1420
845
  [labelPosition]="props()?.labelPosition || 'after'"
1421
846
  [hideIcon]="props()?.hideIcon || false"
1422
- [disableRipple]="effectiveDisableRipple()"
847
+ [disableRipple]="disableRipple()"
1423
848
  [required]="!!f().required()"
1424
- [attr.aria-describedby]="ariaDescribedBy()"
1425
- [attr.tabindex]="tabIndex()"
849
+ [tabIndex]="ngf.tabIndex() ?? 0"
1426
850
  class="toggle-container"
1427
851
  >
1428
- {{ label() | dynamicText | async }}
852
+ {{ ngf.label() | dynamicText | async }}
1429
853
  </mat-slide-toggle>
1430
854
 
1431
- @if (errorsToDisplay()[0]; as error) {
1432
- <mat-error [id]="errorId()">{{ error.message }}</mat-error>
855
+ @if (ngf.errorsToDisplay()[0]; as error) {
856
+ <mat-error [id]="ngf.errorId()">{{ error.message }}</mat-error>
1433
857
  } @else if (props()?.hint; as hint) {
1434
- <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
858
+ <div class="mat-hint" [id]="ngf.hintId()">{{ hint | dynamicText | async }}</div>
1435
859
  }
1436
- `, isInline: true, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: rgba(0, 0, 0, .6);--df-hint-font-size: .875rem;--df-error-color: #f44336;--df-error-font-size: .875rem;display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color)}.df-mat-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size)}.df-mat-error{color:var(--df-error-color);font-size:var(--df-error-font-size)}\n"], dependencies: [{ kind: "component", type: MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "directive", type: MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
860
+ `, isInline: true, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: rgba(0, 0, 0, .6);--df-hint-font-size: .875rem;--df-error-color: #f44336;--df-error-font-size: .875rem;display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color)}.df-mat-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size)}.df-mat-error{color:var(--df-error-color);font-size:var(--df-error-font-size)}\n"], dependencies: [{ kind: "component", type: MatSlideToggle, selector: "mat-slide-toggle", inputs: ["name", "id", "labelPosition", "aria-label", "aria-labelledby", "aria-describedby", "required", "color", "disabled", "disableRipple", "tabIndex", "checked", "hideIcon", "disabledInteractive"], outputs: ["change", "toggleChange"], exportAs: ["matSlideToggle"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "directive", type: MatError, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: NgForgeControl, selector: "[ngForgeControl]", inputs: ["ngForgeControl"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1437
861
  }
1438
862
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: MatToggleFieldComponent, decorators: [{
1439
863
  type: Component,
1440
- args: [{ selector: 'df-mat-toggle', imports: [MatSlideToggle, FormField, MatError, DynamicTextPipe, AsyncPipe], template: `
1441
- @let f = field();
1442
- @let toggleId = key() + '-toggle';
864
+ args: [{ selector: 'df-mat-toggle', imports: [MatSlideToggle, FormField, MatError, DynamicTextPipe, AsyncPipe, NgForgeControl], hostDirectives: [NgForgeFieldHost], template: `
865
+ @let f = ngf.field();
866
+ @let toggleId = ngf.key() + '-toggle';
1443
867
 
1444
868
  <mat-slide-toggle
869
+ ngForgeControl="button[role='switch']"
1445
870
  [id]="toggleId"
1446
871
  [formField]="f"
1447
872
  [color]="props()?.color || 'primary'"
1448
873
  [labelPosition]="props()?.labelPosition || 'after'"
1449
874
  [hideIcon]="props()?.hideIcon || false"
1450
- [disableRipple]="effectiveDisableRipple()"
875
+ [disableRipple]="disableRipple()"
1451
876
  [required]="!!f().required()"
1452
- [attr.aria-describedby]="ariaDescribedBy()"
1453
- [attr.tabindex]="tabIndex()"
877
+ [tabIndex]="ngf.tabIndex() ?? 0"
1454
878
  class="toggle-container"
1455
879
  >
1456
- {{ label() | dynamicText | async }}
880
+ {{ ngf.label() | dynamicText | async }}
1457
881
  </mat-slide-toggle>
1458
882
 
1459
- @if (errorsToDisplay()[0]; as error) {
1460
- <mat-error [id]="errorId()">{{ error.message }}</mat-error>
883
+ @if (ngf.errorsToDisplay()[0]; as error) {
884
+ <mat-error [id]="ngf.errorId()">{{ error.message }}</mat-error>
1461
885
  } @else if (props()?.hint; as hint) {
1462
- <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
886
+ <div class="mat-hint" [id]="ngf.hintId()">{{ hint | dynamicText | async }}</div>
1463
887
  }
1464
- `, host: {
1465
- '[class]': 'className()',
1466
- '[id]': '`${key()}`',
1467
- '[attr.data-testid]': 'key()',
1468
- '[attr.hidden]': 'field()().hidden() || null',
1469
- }, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: rgba(0, 0, 0, .6);--df-hint-font-size: .875rem;--df-error-color: #f44336;--df-error-font-size: .875rem;display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color)}.df-mat-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size)}.df-mat-error{color:var(--df-error-color);font-size:var(--df-error-font-size)}\n"] }]
1470
- }], ctorParameters: () => [], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], key: [{ type: i0.Input, args: [{ isSignal: true, alias: "key", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], tabIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabIndex", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }], meta: [{ type: i0.Input, args: [{ isSignal: true, alias: "meta", required: false }] }], validationMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "validationMessages", required: false }] }], defaultValidationMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultValidationMessages", required: false }] }] } });
888
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{--df-field-gap: .5rem;--df-label-font-weight: 500;--df-label-color: inherit;--df-hint-color: rgba(0, 0, 0, .6);--df-hint-font-size: .875rem;--df-error-color: #f44336;--df-error-font-size: .875rem;display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight);color:var(--df-label-color)}.df-mat-hint{color:var(--df-hint-color);font-size:var(--df-hint-font-size)}.df-mat-error{color:var(--df-error-color);font-size:var(--df-error-font-size)}\n"] }]
889
+ }], propDecorators: { props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }] } });
1471
890
 
1472
891
  var matToggle_component = /*#__PURE__*/Object.freeze({
1473
- __proto__: null,
1474
- default: MatToggleFieldComponent
892
+ __proto__: null,
893
+ default: MatToggleFieldComponent
1475
894
  });
1476
895
 
1477
896
  // Public exports for material field components and types