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

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,20 +1,23 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, computed, inject, ElementRef, input, afterRenderEffect, ChangeDetectionStrategy, Component, linkedSignal, viewChild, isSignal } from '@angular/core';
2
+ import { InjectionToken, inject, input, computed, ChangeDetectionStrategy, Component, signal, forwardRef, linkedSignal, 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, runPresetAction, DfAddonSlot, FIELD_SIGNAL_CONTEXT, DynamicFormLogger, DEFAULT_PROPS, ARRAY_CONTEXT, buildBaseInputs, DynamicFormError, ADDON_KIND_DEFINITIONS } from '@ng-forge/dynamic-forms';
6
+ import * as i1 from '@ng-forge/dynamic-forms/integration';
7
+ import { injectNgForgeField, NgForgeControl, NgForgeFieldHost, injectNgForgeAddons, ADDON_PRESET_HANDLER, NgForgeAddons, isEqual, injectNgForgeAction, NgForgeActionHost, valueFieldMapper, optionsFieldMapper, checkboxFieldMapper, submitButtonFieldMapper, nextButtonFieldMapper, previousButtonFieldMapper, addArrayItemButtonMapper, prependArrayItemButtonMapper, insertArrayItemButtonMapper, removeArrayItemButtonMapper, popArrayItemButtonMapper, shiftArrayItemButtonMapper, datepickerFieldMapper, injectNgForgeAddonAction, NgForgeAddonAction } from '@ng-forge/dynamic-forms/integration';
7
8
  import { MatError, MatInput, MatHint } from '@angular/material/input';
8
9
  import { AsyncPipe } from '@angular/common';
9
- import { MatFormField, MatLabel, MatSuffix, MatError as MatError$1 } from '@angular/material/form-field';
10
+ import { MatFormField, MatLabel, MatSuffix, MatError as MatError$1, MatPrefix } from '@angular/material/form-field';
10
11
  import { MatDatepicker, MatDatepickerInput, MatDatepickerToggle } from '@angular/material/datepicker';
11
12
  import { provideNativeDateAdapter } from '@angular/material/core';
12
13
  import { explicitEffect } from 'ngxtension/explicit-effect';
13
14
  import { MatRadioGroup, MatRadioButton } from '@angular/material/radio';
14
15
  import { MatSelect, MatOption } from '@angular/material/select';
15
16
  import { MatSlider, MatSliderThumb } from '@angular/material/slider';
16
- import { MatButton } from '@angular/material/button';
17
+ import { MatButton, MatIconButton } from '@angular/material/button';
17
18
  import { MatSlideToggle } from '@angular/material/slide-toggle';
19
+ import { MatIcon } from '@angular/material/icon';
20
+ import { MatProgressSpinner } from '@angular/material/progress-spinner';
18
21
 
19
22
  /**
20
23
  * Injection token for Material Design form configuration.
@@ -39,262 +42,121 @@ import { MatSlideToggle } from '@angular/material/slide-toggle';
39
42
  */
40
43
  const MATERIAL_CONFIG = new InjectionToken('MATERIAL_CONFIG');
41
44
 
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
45
  class MatCheckboxFieldComponent {
66
46
  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 */ []));
47
+ ngf = injectNgForgeField();
75
48
  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
- });
147
- 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
+ disableRipple = computed(() => this.props()?.disableRipple ?? this.materialConfig?.disableRipple ?? false, ...(ngDevMode ? [{ debugName: "disableRipple" }] : /* istanbul ignore next */ []));
50
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: MatCheckboxFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
51
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", 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: `
52
+ @let f = ngf.field();
53
+ @let checkboxId = ngf.key() + '-checkbox';
151
54
 
152
55
  <mat-checkbox
56
+ ngForgeControl="input[type='checkbox']"
153
57
  [id]="checkboxId"
154
58
  [formField]="f"
155
59
  [labelPosition]="props()?.labelPosition || 'after'"
156
60
  [indeterminate]="props()?.indeterminate || false"
157
61
  [color]="props()?.color || 'primary'"
158
- [disableRipple]="effectiveDisableRipple()"
62
+ [disableRipple]="disableRipple()"
159
63
  [required]="!!f().required()"
160
- [attr.aria-describedby]="ariaDescribedBy()"
161
- [attr.tabindex]="tabIndex()"
64
+ [tabIndex]="ngf.tabIndex() ?? 0"
162
65
  [attr.hidden]="f().hidden() || null"
163
66
  >
164
- {{ label() | dynamicText | async }}
67
+ {{ ngf.label() | dynamicText | async }}
165
68
  </mat-checkbox>
166
69
 
167
- @if (errorsToDisplay()[0]; as error) {
168
- <mat-error [id]="errorId()" [attr.hidden]="f().hidden() || null">{{ error.message }}</mat-error>
70
+ @if (ngf.errorsToDisplay()[0]; as error) {
71
+ <mat-error [id]="ngf.errorId()" [attr.hidden]="f().hidden() || null">{{ error.message }}</mat-error>
169
72
  } @else if (props()?.hint; as hint) {
170
- <div class="mat-hint" [id]="hintId()" [attr.hidden]="f().hidden() || null">{{ hint | dynamicText | async }}</div>
73
+ <div class="mat-hint" [id]="ngf.hintId()" [attr.hidden]="f().hidden() || null">{{ hint | dynamicText | async }}</div>
171
74
  }
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 });
75
+ `, isInline: true, styles: [":host{display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit)}.df-mat-hint{color:var(--df-hint-color, rgba(0, 0, 0, .6));font-size:var(--df-hint-font-size, .875rem)}.df-mat-error{color:var(--df-error-color, #f44336);font-size:var(--df-error-font-size, .875rem)}\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
76
  }
174
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: MatCheckboxFieldComponent, decorators: [{
77
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: MatCheckboxFieldComponent, decorators: [{
175
78
  type: Component,
176
- args: [{ selector: 'df-mat-checkbox', imports: [MatCheckbox, FormField, MatError, DynamicTextPipe, AsyncPipe], template: `
177
- @let f = field();
178
- @let checkboxId = key() + '-checkbox';
79
+ args: [{ selector: 'df-mat-checkbox', imports: [MatCheckbox, FormField, MatError, DynamicTextPipe, AsyncPipe, NgForgeControl], hostDirectives: [NgForgeFieldHost], template: `
80
+ @let f = ngf.field();
81
+ @let checkboxId = ngf.key() + '-checkbox';
179
82
 
180
83
  <mat-checkbox
84
+ ngForgeControl="input[type='checkbox']"
181
85
  [id]="checkboxId"
182
86
  [formField]="f"
183
87
  [labelPosition]="props()?.labelPosition || 'after'"
184
88
  [indeterminate]="props()?.indeterminate || false"
185
89
  [color]="props()?.color || 'primary'"
186
- [disableRipple]="effectiveDisableRipple()"
90
+ [disableRipple]="disableRipple()"
187
91
  [required]="!!f().required()"
188
- [attr.aria-describedby]="ariaDescribedBy()"
189
- [attr.tabindex]="tabIndex()"
92
+ [tabIndex]="ngf.tabIndex() ?? 0"
190
93
  [attr.hidden]="f().hidden() || null"
191
94
  >
192
- {{ label() | dynamicText | async }}
95
+ {{ ngf.label() | dynamicText | async }}
193
96
  </mat-checkbox>
194
97
 
195
- @if (errorsToDisplay()[0]; as error) {
196
- <mat-error [id]="errorId()" [attr.hidden]="f().hidden() || null">{{ error.message }}</mat-error>
98
+ @if (ngf.errorsToDisplay()[0]; as error) {
99
+ <mat-error [id]="ngf.errorId()" [attr.hidden]="f().hidden() || null">{{ error.message }}</mat-error>
197
100
  } @else if (props()?.hint; as hint) {
198
- <div class="mat-hint" [id]="hintId()" [attr.hidden]="f().hidden() || null">{{ hint | dynamicText | async }}</div>
101
+ <div class="mat-hint" [id]="ngf.hintId()" [attr.hidden]="f().hidden() || null">{{ hint | dynamicText | async }}</div>
199
102
  }
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 }] }] } });
103
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit)}.df-mat-hint{color:var(--df-hint-color, rgba(0, 0, 0, .6));font-size:var(--df-hint-font-size, .875rem)}.df-mat-error{color:var(--df-error-color, #f44336);font-size:var(--df-error-font-size, .875rem)}\n"] }]
104
+ }], propDecorators: { props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }] } });
207
105
 
208
106
  var matCheckbox_component = /*#__PURE__*/Object.freeze({
209
- __proto__: null,
210
- default: MatCheckboxFieldComponent
107
+ __proto__: null,
108
+ default: MatCheckboxFieldComponent
211
109
  });
212
110
 
213
111
  class MatDatepickerFieldComponent {
214
112
  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 */ []));
113
+ ngf = injectNgForgeField();
225
114
  minDate = input(null, ...(ngDevMode ? [{ debugName: "minDate" }] : /* istanbul ignore next */ []));
226
115
  maxDate = input(null, ...(ngDevMode ? [{ debugName: "maxDate" }] : /* istanbul ignore next */ []));
227
116
  startAt = input(null, ...(ngDevMode ? [{ debugName: "startAt" }] : /* istanbul ignore next */ []));
228
117
  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);
257
- 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';
118
+ appearance = computed(() => this.props()?.appearance ?? this.materialConfig?.appearance ?? 'outline', ...(ngDevMode ? [{ debugName: "appearance" }] : /* istanbul ignore next */ []));
119
+ subscriptSizing = computed(() => this.props()?.subscriptSizing ?? this.materialConfig?.subscriptSizing ?? 'dynamic', ...(ngDevMode ? [{ debugName: "subscriptSizing" }] : /* istanbul ignore next */ []));
120
+ floatLabel = computed(() => this.props()?.floatLabel ?? this.materialConfig?.floatLabel ?? 'auto', ...(ngDevMode ? [{ debugName: "floatLabel" }] : /* istanbul ignore next */ []));
121
+ hideRequiredMarker = computed(() => this.props()?.hideRequiredMarker ?? this.materialConfig?.hideRequiredMarker ?? false, ...(ngDevMode ? [{ debugName: "hideRequiredMarker" }] : /* istanbul ignore next */ []));
122
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: MatDatepickerFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
123
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", 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: `
124
+ @let inputId = ngf.key() + '-input';
261
125
 
262
126
  <mat-form-field
263
- [appearance]="effectiveAppearance()"
264
- [subscriptSizing]="effectiveSubscriptSizing()"
265
- [floatLabel]="effectiveFloatLabel()"
266
- [hideRequiredMarker]="effectiveHideRequiredMarker()"
127
+ [appearance]="appearance()"
128
+ [subscriptSizing]="subscriptSizing()"
129
+ [floatLabel]="floatLabel()"
130
+ [hideRequiredMarker]="hideRequiredMarker()"
267
131
  >
268
- @if (label(); as label) {
132
+ @if (ngf.label(); as label) {
269
133
  <mat-label>{{ label | dynamicText | async }}</mat-label>
270
134
  }
271
135
 
272
136
  <input
273
137
  matInput
138
+ ngForgeControl
274
139
  [id]="inputId"
275
140
  [matDatepicker]="picker"
276
- [formField]="f"
277
- [placeholder]="(placeholder() | dynamicText | async) ?? ''"
278
- [attr.tabindex]="tabIndex()"
141
+ [formField]="ngf.field()"
142
+ [placeholder]="(ngf.placeholder() | dynamicText | async) ?? ''"
143
+ [attr.tabindex]="ngf.tabIndex()"
279
144
  [min]="minDate()"
280
145
  [max]="maxDate()"
281
- [attr.aria-invalid]="ariaInvalid()"
282
- [attr.aria-required]="ariaRequired()"
283
- [attr.aria-describedby]="ariaDescribedBy()"
284
146
  />
285
147
 
286
148
  <mat-datepicker-toggle matIconSuffix [for]="picker" />
287
149
  <mat-datepicker #picker [startAt]="startAt()" [startView]="props()?.startView || 'month'" [touchUi]="props()?.touchUi ?? false" />
288
150
 
289
- @if (errorsToDisplay()[0]; as error) {
290
- <mat-error [id]="errorId()">{{ error.message }}</mat-error>
151
+ @if (ngf.errorsToDisplay()[0]; as error) {
152
+ <mat-error [id]="ngf.errorId()">{{ error.message }}</mat-error>
291
153
  } @else if (props()?.hint; as hint) {
292
- <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
154
+ <mat-hint [id]="ngf.hintId()">{{ hint | dynamicText | async }}</mat-hint>
293
155
  }
294
156
  </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 });
157
+ `, isInline: true, styles: [":host{display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit)}.df-mat-hint{color:var(--df-hint-color, rgba(0, 0, 0, .6));font-size:var(--df-hint-font-size, .875rem)}.df-mat-error{color:var(--df-error-color, #f44336);font-size:var(--df-error-font-size, .875rem)}\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
158
  }
297
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: MatDatepickerFieldComponent, decorators: [{
159
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: MatDatepickerFieldComponent, decorators: [{
298
160
  type: Component,
299
161
  args: [{ selector: 'df-mat-datepicker', imports: [
300
162
  MatFormField,
@@ -309,188 +171,280 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
309
171
  MatError$1,
310
172
  DynamicTextPipe,
311
173
  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';
174
+ NgForgeControl,
175
+ ], hostDirectives: [NgForgeFieldHost], template: `
176
+ @let inputId = ngf.key() + '-input';
320
177
 
321
178
  <mat-form-field
322
- [appearance]="effectiveAppearance()"
323
- [subscriptSizing]="effectiveSubscriptSizing()"
324
- [floatLabel]="effectiveFloatLabel()"
325
- [hideRequiredMarker]="effectiveHideRequiredMarker()"
179
+ [appearance]="appearance()"
180
+ [subscriptSizing]="subscriptSizing()"
181
+ [floatLabel]="floatLabel()"
182
+ [hideRequiredMarker]="hideRequiredMarker()"
326
183
  >
327
- @if (label(); as label) {
184
+ @if (ngf.label(); as label) {
328
185
  <mat-label>{{ label | dynamicText | async }}</mat-label>
329
186
  }
330
187
 
331
188
  <input
332
189
  matInput
190
+ ngForgeControl
333
191
  [id]="inputId"
334
192
  [matDatepicker]="picker"
335
- [formField]="f"
336
- [placeholder]="(placeholder() | dynamicText | async) ?? ''"
337
- [attr.tabindex]="tabIndex()"
193
+ [formField]="ngf.field()"
194
+ [placeholder]="(ngf.placeholder() | dynamicText | async) ?? ''"
195
+ [attr.tabindex]="ngf.tabIndex()"
338
196
  [min]="minDate()"
339
197
  [max]="maxDate()"
340
- [attr.aria-invalid]="ariaInvalid()"
341
- [attr.aria-required]="ariaRequired()"
342
- [attr.aria-describedby]="ariaDescribedBy()"
343
198
  />
344
199
 
345
200
  <mat-datepicker-toggle matIconSuffix [for]="picker" />
346
201
  <mat-datepicker #picker [startAt]="startAt()" [startView]="props()?.startView || 'month'" [touchUi]="props()?.touchUi ?? false" />
347
202
 
348
- @if (errorsToDisplay()[0]; as error) {
349
- <mat-error [id]="errorId()">{{ error.message }}</mat-error>
203
+ @if (ngf.errorsToDisplay()[0]; as error) {
204
+ <mat-error [id]="ngf.errorId()">{{ error.message }}</mat-error>
350
205
  } @else if (props()?.hint; as hint) {
351
- <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
206
+ <mat-hint [id]="ngf.hintId()">{{ hint | dynamicText | async }}</mat-hint>
352
207
  }
353
208
  </mat-form-field>
354
- `, 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 }] }] } });
209
+ `, providers: [provideNativeDateAdapter()], changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit)}.df-mat-hint{color:var(--df-hint-color, rgba(0, 0, 0, .6));font-size:var(--df-hint-font-size, .875rem)}.df-mat-error{color:var(--df-error-color, #f44336);font-size:var(--df-error-font-size, .875rem)}\n", "mat-form-field{width:100%}\n"] }]
210
+ }], 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
211
 
357
212
  var matDatepicker_component = /*#__PURE__*/Object.freeze({
358
- __proto__: null,
359
- default: MatDatepickerFieldComponent
213
+ __proto__: null,
214
+ default: MatDatepickerFieldComponent
360
215
  });
361
216
 
217
+ /** Material adapter binding for the shared preset runner. */
218
+ function runMatPresetAction(preset, ctx, collaborators) {
219
+ return runPresetAction(preset, ctx, collaborators, 'Material', 'mat-input');
220
+ }
221
+
222
+ /**
223
+ * Per-field writable signal that overrides the input's `type` attribute.
224
+ *
225
+ * Provided at the `mat-input` field component level. The button addon's
226
+ * `'toggle-password-visibility'` preset writes to it; the field component
227
+ * reads it to compute its effective `type`.
228
+ *
229
+ * Optional from a button's perspective — when the button is hosted inside a
230
+ * field that doesn't provide this token (e.g., textarea or a future
231
+ * non-input field), the toggle preset is a no-op.
232
+ *
233
+ * @example provided per-field:
234
+ * ```typescript
235
+ * @Component({
236
+ * providers: [{ provide: MAT_INPUT_TYPE_OVERRIDE, useValue: signal(undefined) }],
237
+ * })
238
+ * class MatInputFieldComponent { ... }
239
+ * ```
240
+ */
241
+ const MAT_INPUT_TYPE_OVERRIDE = new InjectionToken('MAT_INPUT_TYPE_OVERRIDE');
242
+
362
243
  class MatInputFieldComponent {
363
244
  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 */ []));
245
+ ngf = injectNgForgeField();
246
+ ngfa = injectNgForgeAddons();
371
247
  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
- effectiveAppearance = computed(() => this.props()?.appearance ?? this.materialConfig?.appearance ?? 'outline', ...(ngDevMode ? [{ debugName: "effectiveAppearance" }] : /* istanbul ignore next */ []));
379
- effectiveSubscriptSizing = computed(() => this.props()?.subscriptSizing ?? this.materialConfig?.subscriptSizing ?? 'dynamic', ...(ngDevMode ? [{ debugName: "effectiveSubscriptSizing" }] : /* istanbul ignore next */ []));
380
- effectiveFloatLabel = computed(() => this.props()?.floatLabel ?? this.materialConfig?.floatLabel ?? 'auto', ...(ngDevMode ? [{ debugName: "effectiveFloatLabel" }] : /* istanbul ignore next */ []));
381
- effectiveHideRequiredMarker = computed(() => this.props()?.hideRequiredMarker ?? this.materialConfig?.hideRequiredMarker ?? false, ...(ngDevMode ? [{ debugName: "effectiveHideRequiredMarker" }] : /* istanbul ignore next */ []));
382
- resolvedErrors = createResolvedErrorsSignal(this.field, this.validationMessages, this.defaultValidationMessages);
383
- showErrors = shouldShowErrors(this.field);
384
- errorsToDisplay = computed(() => (this.showErrors() ? this.resolvedErrors() : []), ...(ngDevMode ? [{ debugName: "errorsToDisplay" }] : /* istanbul ignore next */ []));
385
- // ─────────────────────────────────────────────────────────────────────────────
386
- // Accessibility
387
- // ─────────────────────────────────────────────────────────────────────────────
388
- /** Unique ID for the hint element, used for aria-describedby */
389
- hintId = computed(() => `${this.key()}-hint`, ...(ngDevMode ? [{ debugName: "hintId" }] : /* istanbul ignore next */ []));
390
- /** Base ID for error elements, used for aria-describedby */
391
- errorId = computed(() => `${this.key()}-error`, ...(ngDevMode ? [{ debugName: "errorId" }] : /* istanbul ignore next */ []));
392
- /** aria-invalid: true when field is invalid AND touched, false otherwise */
393
- ariaInvalid = computed(() => {
394
- const fieldState = this.field()();
395
- return fieldState.invalid() && fieldState.touched();
396
- }, ...(ngDevMode ? [{ debugName: "ariaInvalid" }] : /* istanbul ignore next */ []));
397
- /** aria-required: true if field is required, null otherwise (to remove attribute) */
398
- ariaRequired = computed(() => {
399
- return this.field()().required?.() === true ? true : null;
400
- }, ...(ngDevMode ? [{ debugName: "ariaRequired" }] : /* istanbul ignore next */ []));
401
- /** aria-describedby: links to hint and error messages for screen readers */
402
- ariaDescribedBy = createAriaDescribedBySignal(this.errorsToDisplay, this.errorId, this.hintId, () => !!this.props()?.hint);
403
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: MatInputFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
404
- 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" } }, ngImport: i0, template: `
405
- @let f = field();
406
- @let inputId = key() + '-input';
248
+ /**
249
+ * Wrapper-style host bag pushed by `DfFieldOutlet`. Declared at the
250
+ * component level so `setInputIfDeclared` (which uses
251
+ * `reflectComponentType`) can write it.
252
+ */
253
+ fieldInputs = input(...(ngDevMode ? [undefined, { debugName: "fieldInputs" }] : /* istanbul ignore next */ []));
254
+ /** Per-instance type override populated by `toggle-password-visibility` preset. */
255
+ typeOverride = inject(MAT_INPUT_TYPE_OVERRIDE);
256
+ appearance = computed(() => this.props()?.appearance ?? this.materialConfig?.appearance ?? 'outline', ...(ngDevMode ? [{ debugName: "appearance" }] : /* istanbul ignore next */ []));
257
+ subscriptSizing = computed(() => this.props()?.subscriptSizing ?? this.materialConfig?.subscriptSizing ?? 'dynamic', ...(ngDevMode ? [{ debugName: "subscriptSizing" }] : /* istanbul ignore next */ []));
258
+ floatLabel = computed(() => this.props()?.floatLabel ?? this.materialConfig?.floatLabel ?? 'auto', ...(ngDevMode ? [{ debugName: "floatLabel" }] : /* istanbul ignore next */ []));
259
+ hideRequiredMarker = computed(() => this.props()?.hideRequiredMarker ?? this.materialConfig?.hideRequiredMarker ?? false, ...(ngDevMode ? [{ debugName: "hideRequiredMarker" }] : /* istanbul ignore next */ []));
260
+ /** Override (set by `toggle-password-visibility` preset) wins over `props().type`. */
261
+ type = computed(() => this.typeOverride() ?? this.props()?.type ?? 'text', ...(ngDevMode ? [{ debugName: "type" }] : /* istanbul ignore next */ []));
262
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: MatInputFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
263
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: MatInputFieldComponent, isStandalone: true, selector: "df-mat-input", inputs: { props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null }, fieldInputs: { classPropertyName: "fieldInputs", publicName: "fieldInputs", isSignal: true, isRequired: false, transformFunction: null } }, providers: [
264
+ {
265
+ provide: MAT_INPUT_TYPE_OVERRIDE,
266
+ useFactory: () => signal(undefined),
267
+ },
268
+ {
269
+ // Adapter-specific preset semantics for `mat-button` addons (clear /
270
+ // reset / paste / copy / toggle-password-visibility). The directive
271
+ // (`NgForgeAddonAction`) delegates here when an addon configures a
272
+ // `preset`. Per-mat-input-instance so the `typeOverride` signal is
273
+ // scoped to one field.
274
+ provide: ADDON_PRESET_HANDLER,
275
+ useFactory: () => {
276
+ const typeOverride = inject(MAT_INPUT_TYPE_OVERRIDE);
277
+ const fsc = inject(FIELD_SIGNAL_CONTEXT, { optional: true });
278
+ const logger = inject(DynamicFormLogger);
279
+ // forwardRef for baselineType only — host.props()?.type gates toggle-password-visibility.
280
+ const host = inject(forwardRef(() => MatInputFieldComponent));
281
+ return {
282
+ run: (preset, ctx) => {
283
+ const fieldKey = ctx.field.key;
284
+ return runMatPresetAction(preset, ctx, {
285
+ typeOverride,
286
+ fieldValueSetter: ctx.setValue,
287
+ fieldDefaultValueGetter: fsc && fieldKey ? () => fsc.defaultValues()?.[fieldKey] : undefined,
288
+ baselineType: () => host.props()?.type,
289
+ logger,
290
+ });
291
+ },
292
+ };
293
+ },
294
+ },
295
+ ], hostDirectives: [{ directive: i1.NgForgeFieldHost }, { directive: i1.NgForgeAddons }], ngImport: i0, template: `
296
+ @let inputId = ngf.key() + '-input';
407
297
 
408
298
  <mat-form-field
409
- [appearance]="effectiveAppearance()"
410
- [subscriptSizing]="effectiveSubscriptSizing()"
411
- [floatLabel]="effectiveFloatLabel()"
412
- [hideRequiredMarker]="effectiveHideRequiredMarker()"
299
+ [appearance]="appearance()"
300
+ [subscriptSizing]="subscriptSizing()"
301
+ [floatLabel]="floatLabel()"
302
+ [hideRequiredMarker]="hideRequiredMarker()"
413
303
  >
414
- @if (label()) {
415
- <mat-label>{{ label() | dynamicText | async }}</mat-label>
304
+ @if (ngf.label()) {
305
+ <mat-label>{{ ngf.label() | dynamicText | async }}</mat-label>
306
+ }
307
+ @for (a of ngfa.prefixAddons(); track $index) {
308
+ <df-addon-slot
309
+ matPrefix
310
+ [class.df-mat-addon-text]="a.kind === 'text'"
311
+ [addon]="a"
312
+ [fieldInputs]="fieldInputs()"
313
+ [hidden]="ngfa.hiddenSignalCache().get(a)"
314
+ />
416
315
  }
417
316
  <input
418
317
  matInput
318
+ ngForgeControl
419
319
  [id]="inputId"
420
- [formField]="f"
421
- [type]="props()?.type ?? 'text'"
422
- [placeholder]="(placeholder() | dynamicText | async) ?? ''"
423
- [attr.tabindex]="tabIndex()"
424
- [attr.aria-invalid]="ariaInvalid()"
425
- [attr.aria-required]="ariaRequired()"
426
- [attr.aria-describedby]="ariaDescribedBy()"
320
+ [formField]="ngf.field()"
321
+ [type]="type()"
322
+ [placeholder]="(ngf.placeholder() | dynamicText | async) ?? ''"
323
+ [attr.tabindex]="ngf.tabIndex()"
427
324
  />
428
- @if (errorsToDisplay()[0]; as error) {
429
- <mat-error [id]="errorId()">{{ error.message }}</mat-error>
325
+ @for (a of ngfa.suffixAddons(); track $index) {
326
+ <df-addon-slot
327
+ matSuffix
328
+ [class.df-mat-addon-text]="a.kind === 'text'"
329
+ [addon]="a"
330
+ [fieldInputs]="fieldInputs()"
331
+ [hidden]="ngfa.hiddenSignalCache().get(a)"
332
+ />
333
+ }
334
+ @if (ngf.errorsToDisplay()[0]; as error) {
335
+ <mat-error [id]="ngf.errorId()">{{ error.message }}</mat-error>
430
336
  } @else if (props()?.hint; as hint) {
431
- <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
337
+ <mat-hint [id]="ngf.hintId()">{{ hint | dynamicText | async }}</mat-hint>
432
338
  }
433
339
  </mat-form-field>
434
- `, 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 });
340
+ `, isInline: true, styles: [":host{display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit)}.df-mat-hint{color:var(--df-hint-color, rgba(0, 0, 0, .6));font-size:var(--df-hint-font-size, .875rem)}.df-mat-error{color:var(--df-error-color, #f44336);font-size:var(--df-error-font-size, .875rem)}\n", "mat-form-field{width:100%}:host([hidden]){display:none!important}:host{--df-mat-addon-prefix-outer-padding: .75em;--df-mat-addon-prefix-inner-padding: .5em;--df-mat-addon-suffix-inner-padding: .5em;--df-mat-addon-suffix-outer-padding: .75em;--df-mat-addon-prefix-text-outer-padding: 1em;--df-mat-addon-prefix-text-inner-padding: 1em;--df-mat-addon-suffix-text-inner-padding: 1em;--df-mat-addon-suffix-text-outer-padding: 1em}df-addon-slot[matprefix]{padding-left:var(--df-mat-addon-prefix-outer-padding);padding-right:var(--df-mat-addon-prefix-inner-padding)}df-addon-slot[matsuffix]{padding-left:var(--df-mat-addon-suffix-inner-padding);padding-right:var(--df-mat-addon-suffix-outer-padding)}df-addon-slot[matprefix].df-mat-addon-text{padding-left:var(--df-mat-addon-prefix-text-outer-padding);padding-right:var(--df-mat-addon-prefix-text-inner-padding)}df-addon-slot[matsuffix].df-mat-addon-text{padding-left:var(--df-mat-addon-suffix-text-inner-padding);padding-right:var(--df-mat-addon-suffix-text-outer-padding)}\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: MatError$1, selector: "mat-error, [matError]", inputs: ["id"] }, { kind: "directive", type: MatPrefix, selector: "[matPrefix], [matIconPrefix], [matTextPrefix]", inputs: ["matTextPrefix"] }, { kind: "directive", type: MatSuffix, selector: "[matSuffix], [matIconSuffix], [matTextSuffix]", inputs: ["matTextSuffix"] }, { kind: "directive", type: FormField, selector: "[formField]", inputs: ["formField"], exportAs: ["formField"] }, { kind: "directive", type: NgForgeControl, selector: "[ngForgeControl]", inputs: ["ngForgeControl"] }, { kind: "component", type: DfAddonSlot, selector: "df-addon-slot", inputs: ["addon", "fieldInputs", "hidden"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
435
341
  }
436
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: MatInputFieldComponent, decorators: [{
342
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: MatInputFieldComponent, decorators: [{
437
343
  type: Component,
438
- args: [{ selector: 'df-mat-input', imports: [MatFormField, MatLabel, MatInput, MatHint, FormField, MatError$1, DynamicTextPipe, AsyncPipe], template: `
439
- @let f = field();
440
- @let inputId = key() + '-input';
344
+ args: [{ selector: 'df-mat-input', imports: [
345
+ MatFormField,
346
+ MatLabel,
347
+ MatInput,
348
+ MatHint,
349
+ MatError$1,
350
+ MatPrefix,
351
+ MatSuffix,
352
+ FormField,
353
+ DynamicTextPipe,
354
+ AsyncPipe,
355
+ NgForgeControl,
356
+ DfAddonSlot,
357
+ ], hostDirectives: [NgForgeFieldHost, NgForgeAddons], template: `
358
+ @let inputId = ngf.key() + '-input';
441
359
 
442
360
  <mat-form-field
443
- [appearance]="effectiveAppearance()"
444
- [subscriptSizing]="effectiveSubscriptSizing()"
445
- [floatLabel]="effectiveFloatLabel()"
446
- [hideRequiredMarker]="effectiveHideRequiredMarker()"
361
+ [appearance]="appearance()"
362
+ [subscriptSizing]="subscriptSizing()"
363
+ [floatLabel]="floatLabel()"
364
+ [hideRequiredMarker]="hideRequiredMarker()"
447
365
  >
448
- @if (label()) {
449
- <mat-label>{{ label() | dynamicText | async }}</mat-label>
366
+ @if (ngf.label()) {
367
+ <mat-label>{{ ngf.label() | dynamicText | async }}</mat-label>
368
+ }
369
+ @for (a of ngfa.prefixAddons(); track $index) {
370
+ <df-addon-slot
371
+ matPrefix
372
+ [class.df-mat-addon-text]="a.kind === 'text'"
373
+ [addon]="a"
374
+ [fieldInputs]="fieldInputs()"
375
+ [hidden]="ngfa.hiddenSignalCache().get(a)"
376
+ />
450
377
  }
451
378
  <input
452
379
  matInput
380
+ ngForgeControl
453
381
  [id]="inputId"
454
- [formField]="f"
455
- [type]="props()?.type ?? 'text'"
456
- [placeholder]="(placeholder() | dynamicText | async) ?? ''"
457
- [attr.tabindex]="tabIndex()"
458
- [attr.aria-invalid]="ariaInvalid()"
459
- [attr.aria-required]="ariaRequired()"
460
- [attr.aria-describedby]="ariaDescribedBy()"
382
+ [formField]="ngf.field()"
383
+ [type]="type()"
384
+ [placeholder]="(ngf.placeholder() | dynamicText | async) ?? ''"
385
+ [attr.tabindex]="ngf.tabIndex()"
461
386
  />
462
- @if (errorsToDisplay()[0]; as error) {
463
- <mat-error [id]="errorId()">{{ error.message }}</mat-error>
387
+ @for (a of ngfa.suffixAddons(); track $index) {
388
+ <df-addon-slot
389
+ matSuffix
390
+ [class.df-mat-addon-text]="a.kind === 'text'"
391
+ [addon]="a"
392
+ [fieldInputs]="fieldInputs()"
393
+ [hidden]="ngfa.hiddenSignalCache().get(a)"
394
+ />
395
+ }
396
+ @if (ngf.errorsToDisplay()[0]; as error) {
397
+ <mat-error [id]="ngf.errorId()">{{ error.message }}</mat-error>
464
398
  } @else if (props()?.hint; as hint) {
465
- <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
399
+ <mat-hint [id]="ngf.hintId()">{{ hint | dynamicText | async }}</mat-hint>
466
400
  }
467
401
  </mat-form-field>
468
- `, changeDetection: ChangeDetectionStrategy.OnPush, host: {
469
- '[id]': '`${key()}`',
470
- '[attr.data-testid]': 'key()',
471
- '[class]': 'className()',
472
- '[attr.hidden]': 'field()().hidden() || null',
473
- }, 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"] }]
474
- }], 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 }] }] } });
402
+ `, changeDetection: ChangeDetectionStrategy.OnPush, providers: [
403
+ {
404
+ provide: MAT_INPUT_TYPE_OVERRIDE,
405
+ useFactory: () => signal(undefined),
406
+ },
407
+ {
408
+ // Adapter-specific preset semantics for `mat-button` addons (clear /
409
+ // reset / paste / copy / toggle-password-visibility). The directive
410
+ // (`NgForgeAddonAction`) delegates here when an addon configures a
411
+ // `preset`. Per-mat-input-instance so the `typeOverride` signal is
412
+ // scoped to one field.
413
+ provide: ADDON_PRESET_HANDLER,
414
+ useFactory: () => {
415
+ const typeOverride = inject(MAT_INPUT_TYPE_OVERRIDE);
416
+ const fsc = inject(FIELD_SIGNAL_CONTEXT, { optional: true });
417
+ const logger = inject(DynamicFormLogger);
418
+ // forwardRef for baselineType only — host.props()?.type gates toggle-password-visibility.
419
+ const host = inject(forwardRef(() => MatInputFieldComponent));
420
+ return {
421
+ run: (preset, ctx) => {
422
+ const fieldKey = ctx.field.key;
423
+ return runMatPresetAction(preset, ctx, {
424
+ typeOverride,
425
+ fieldValueSetter: ctx.setValue,
426
+ fieldDefaultValueGetter: fsc && fieldKey ? () => fsc.defaultValues()?.[fieldKey] : undefined,
427
+ baselineType: () => host.props()?.type,
428
+ logger,
429
+ });
430
+ },
431
+ };
432
+ },
433
+ },
434
+ ], styles: [":host{display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit)}.df-mat-hint{color:var(--df-hint-color, rgba(0, 0, 0, .6));font-size:var(--df-hint-font-size, .875rem)}.df-mat-error{color:var(--df-error-color, #f44336);font-size:var(--df-error-font-size, .875rem)}\n", "mat-form-field{width:100%}:host([hidden]){display:none!important}:host{--df-mat-addon-prefix-outer-padding: .75em;--df-mat-addon-prefix-inner-padding: .5em;--df-mat-addon-suffix-inner-padding: .5em;--df-mat-addon-suffix-outer-padding: .75em;--df-mat-addon-prefix-text-outer-padding: 1em;--df-mat-addon-prefix-text-inner-padding: 1em;--df-mat-addon-suffix-text-inner-padding: 1em;--df-mat-addon-suffix-text-outer-padding: 1em}df-addon-slot[matprefix]{padding-left:var(--df-mat-addon-prefix-outer-padding);padding-right:var(--df-mat-addon-prefix-inner-padding)}df-addon-slot[matsuffix]{padding-left:var(--df-mat-addon-suffix-inner-padding);padding-right:var(--df-mat-addon-suffix-outer-padding)}df-addon-slot[matprefix].df-mat-addon-text{padding-left:var(--df-mat-addon-prefix-text-outer-padding);padding-right:var(--df-mat-addon-prefix-text-inner-padding)}df-addon-slot[matsuffix].df-mat-addon-text{padding-left:var(--df-mat-addon-suffix-text-inner-padding);padding-right:var(--df-mat-addon-suffix-text-outer-padding)}\n"] }]
435
+ }], propDecorators: { props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }], fieldInputs: [{ type: i0.Input, args: [{ isSignal: true, alias: "fieldInputs", required: false }] }] } });
475
436
 
476
437
  var matInput_component = /*#__PURE__*/Object.freeze({
477
- __proto__: null,
478
- default: MatInputFieldComponent
438
+ __proto__: null,
439
+ default: MatInputFieldComponent
479
440
  });
480
441
 
481
442
  class MatMultiCheckboxFieldComponent {
482
- elementRef = inject((ElementRef));
483
- field = input.required(...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
484
- key = input.required(...(ngDevMode ? [{ debugName: "key" }] : /* istanbul ignore next */ []));
485
- label = input(...(ngDevMode ? [undefined, { debugName: "label" }] : /* istanbul ignore next */ []));
486
- placeholder = input(...(ngDevMode ? [undefined, { debugName: "placeholder" }] : /* istanbul ignore next */ []));
487
- className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
488
- tabIndex = input(...(ngDevMode ? [undefined, { debugName: "tabIndex" }] : /* istanbul ignore next */ []));
443
+ ngf = injectNgForgeField();
489
444
  options = input([], ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
490
445
  props = input(...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
491
- meta = input(...(ngDevMode ? [undefined, { debugName: "meta" }] : /* istanbul ignore next */ []));
492
446
  valueViewModel = linkedSignal(() => {
493
- const currentValues = this.field()().value();
447
+ const currentValues = this.ngf.field()().value();
494
448
  return this.options().filter((option) => currentValues.includes(option.value));
495
449
  }, { ...(ngDevMode ? { debugName: "valueViewModel" } : /* istanbul ignore next */ {}), equal: isEqual });
496
450
  /** Computed map of checked option values for O(1) lookup in template */
@@ -502,15 +456,10 @@ class MatMultiCheckboxFieldComponent {
502
456
  return map;
503
457
  }, ...(ngDevMode ? [{ debugName: "checkedValuesMap" }] : /* istanbul ignore next */ []));
504
458
  constructor() {
505
- // Apply meta attributes to all checkbox inputs, re-apply when options change
506
- setupMetaTracking(this.elementRef, this.meta, {
507
- selector: 'input[type="checkbox"]',
508
- dependents: [this.options],
509
- });
510
459
  explicitEffect([this.valueViewModel], ([selectedOptions]) => {
511
460
  const selectedValues = selectedOptions.map((option) => option.value);
512
- if (!isEqual(selectedValues, this.field()().value())) {
513
- this.field()().value.set(selectedValues);
461
+ if (!isEqual(selectedValues, this.ngf.field()().value())) {
462
+ this.ngf.field()().value.set(selectedValues);
514
463
  }
515
464
  });
516
465
  explicitEffect([this.options], ([options]) => {
@@ -532,36 +481,13 @@ class MatMultiCheckboxFieldComponent {
532
481
  }
533
482
  });
534
483
  }
535
- validationMessages = input(...(ngDevMode ? [undefined, { debugName: "validationMessages" }] : /* istanbul ignore next */ []));
536
- defaultValidationMessages = input(...(ngDevMode ? [undefined, { debugName: "defaultValidationMessages" }] : /* istanbul ignore next */ []));
537
- resolvedErrors = createResolvedErrorsSignal(this.field, this.validationMessages, this.defaultValidationMessages);
538
- showErrors = shouldShowErrors(this.field);
539
- errorsToDisplay = computed(() => (this.showErrors() ? this.resolvedErrors() : []), ...(ngDevMode ? [{ debugName: "errorsToDisplay" }] : /* istanbul ignore next */ []));
540
- // ─────────────────────────────────────────────────────────────────────────────
541
- // Accessibility
542
- // ─────────────────────────────────────────────────────────────────────────────
543
- /** Unique ID for the hint element, used for aria-describedby */
544
- hintId = computed(() => `${this.key()}-hint`, ...(ngDevMode ? [{ debugName: "hintId" }] : /* istanbul ignore next */ []));
545
- /** Base ID for error elements, used for aria-describedby */
546
- errorId = computed(() => `${this.key()}-error`, ...(ngDevMode ? [{ debugName: "errorId" }] : /* istanbul ignore next */ []));
547
- /** aria-invalid: true when field is invalid AND touched, false otherwise */
548
- ariaInvalid = computed(() => {
549
- const fieldState = this.field()();
550
- return fieldState.invalid() && fieldState.touched();
551
- }, ...(ngDevMode ? [{ debugName: "ariaInvalid" }] : /* istanbul ignore next */ []));
552
- /** aria-required: true if field is required, null otherwise (to remove attribute) */
553
- ariaRequired = computed(() => {
554
- return this.field()().required?.() === true ? true : null;
555
- }, ...(ngDevMode ? [{ debugName: "ariaRequired" }] : /* istanbul ignore next */ []));
556
- /** aria-describedby: links to hint and error messages for screen readers */
557
- ariaDescribedBy = createAriaDescribedBySignal(this.errorsToDisplay, this.errorId, this.hintId, () => !!this.props()?.hint);
558
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: MatMultiCheckboxFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
559
- 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: `
560
- @let f = field();
561
- @let checkboxGroupId = key() + '-checkbox-group';
484
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: MatMultiCheckboxFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
485
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", 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: `
486
+ @let f = ngf.field();
487
+ @let checkboxGroupId = ngf.key() + '-checkbox-group';
562
488
  @let checked = checkedValuesMap();
563
489
 
564
- @if (label(); as label) {
490
+ @if (ngf.label(); as label) {
565
491
  <div class="checkbox-group-label">{{ label | dynamicText | async }}</div>
566
492
  }
567
493
 
@@ -569,12 +495,13 @@ class MatMultiCheckboxFieldComponent {
569
495
  [id]="checkboxGroupId"
570
496
  class="checkbox-group"
571
497
  role="group"
572
- [attr.aria-invalid]="ariaInvalid()"
573
- [attr.aria-required]="ariaRequired()"
574
- [attr.aria-describedby]="ariaDescribedBy()"
498
+ [attr.aria-invalid]="ngf.ariaInvalid()"
499
+ [attr.aria-required]="ngf.ariaRequired()"
500
+ [attr.aria-describedby]="ngf.ariaDescribedBy()"
575
501
  >
576
502
  @for (option of options(); track option.value) {
577
503
  <mat-checkbox
504
+ ngForgeControl="input[type='checkbox']"
578
505
  [checked]="checked['' + option.value]"
579
506
  [disabled]="f().disabled() || option.disabled"
580
507
  [color]="props()?.color || 'primary'"
@@ -586,21 +513,21 @@ class MatMultiCheckboxFieldComponent {
586
513
  }
587
514
  </div>
588
515
 
589
- @if (errorsToDisplay()[0]; as error) {
590
- <mat-error [id]="errorId()">{{ error.message }}</mat-error>
516
+ @if (ngf.errorsToDisplay()[0]; as error) {
517
+ <mat-error [id]="ngf.errorId()">{{ error.message }}</mat-error>
591
518
  } @else if (props()?.hint; as hint) {
592
- <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
519
+ <div class="mat-hint" [id]="ngf.hintId()">{{ hint | dynamicText | async }}</div>
593
520
  }
594
- `, 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 });
521
+ `, isInline: true, styles: [":host{display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit)}.df-mat-hint{color:var(--df-hint-color, rgba(0, 0, 0, .6));font-size:var(--df-hint-font-size, .875rem)}.df-mat-error{color:var(--df-error-color, #f44336);font-size:var(--df-error-font-size, .875rem)}\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 });
595
522
  }
596
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: MatMultiCheckboxFieldComponent, decorators: [{
523
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: MatMultiCheckboxFieldComponent, decorators: [{
597
524
  type: Component,
598
- args: [{ selector: 'df-mat-multi-checkbox', imports: [MatCheckbox, MatError, DynamicTextPipe, AsyncPipe], template: `
599
- @let f = field();
600
- @let checkboxGroupId = key() + '-checkbox-group';
525
+ args: [{ selector: 'df-mat-multi-checkbox', imports: [MatCheckbox, MatError, DynamicTextPipe, AsyncPipe, NgForgeControl], hostDirectives: [NgForgeFieldHost], template: `
526
+ @let f = ngf.field();
527
+ @let checkboxGroupId = ngf.key() + '-checkbox-group';
601
528
  @let checked = checkedValuesMap();
602
529
 
603
- @if (label(); as label) {
530
+ @if (ngf.label(); as label) {
604
531
  <div class="checkbox-group-label">{{ label | dynamicText | async }}</div>
605
532
  }
606
533
 
@@ -608,12 +535,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
608
535
  [id]="checkboxGroupId"
609
536
  class="checkbox-group"
610
537
  role="group"
611
- [attr.aria-invalid]="ariaInvalid()"
612
- [attr.aria-required]="ariaRequired()"
613
- [attr.aria-describedby]="ariaDescribedBy()"
538
+ [attr.aria-invalid]="ngf.ariaInvalid()"
539
+ [attr.aria-required]="ngf.ariaRequired()"
540
+ [attr.aria-describedby]="ngf.ariaDescribedBy()"
614
541
  >
615
542
  @for (option of options(); track option.value) {
616
543
  <mat-checkbox
544
+ ngForgeControl="input[type='checkbox']"
617
545
  [checked]="checked['' + option.value]"
618
546
  [disabled]="f().disabled() || option.disabled"
619
547
  [color]="props()?.color || 'primary'"
@@ -625,83 +553,42 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
625
553
  }
626
554
  </div>
627
555
 
628
- @if (errorsToDisplay()[0]; as error) {
629
- <mat-error [id]="errorId()">{{ error.message }}</mat-error>
556
+ @if (ngf.errorsToDisplay()[0]; as error) {
557
+ <mat-error [id]="ngf.errorId()">{{ error.message }}</mat-error>
630
558
  } @else if (props()?.hint; as hint) {
631
- <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
559
+ <div class="mat-hint" [id]="ngf.hintId()">{{ hint | dynamicText | async }}</div>
632
560
  }
633
- `, host: {
634
- '[class]': 'className() || ""',
635
- '[id]': '`${key()}`',
636
- '[attr.data-testid]': 'key()',
637
- '[attr.hidden]': 'field()().hidden() || null',
638
- }, 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"] }]
639
- }], 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 }] }] } });
561
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit)}.df-mat-hint{color:var(--df-hint-color, rgba(0, 0, 0, .6));font-size:var(--df-hint-font-size, .875rem)}.df-mat-error{color:var(--df-error-color, #f44336);font-size:var(--df-error-font-size, .875rem)}\n"] }]
562
+ }], ctorParameters: () => [], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }] } });
640
563
 
641
564
  var matMultiCheckbox_component = /*#__PURE__*/Object.freeze({
642
- __proto__: null,
643
- default: MatMultiCheckboxFieldComponent
565
+ __proto__: null,
566
+ default: MatMultiCheckboxFieldComponent
644
567
  });
645
568
 
646
569
  class MatRadioFieldComponent {
647
- elementRef = inject((ElementRef));
648
- field = input.required(...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
649
- key = input.required(...(ngDevMode ? [{ debugName: "key" }] : /* istanbul ignore next */ []));
650
- label = input(...(ngDevMode ? [undefined, { debugName: "label" }] : /* istanbul ignore next */ []));
651
- placeholder = input(...(ngDevMode ? [undefined, { debugName: "placeholder" }] : /* istanbul ignore next */ []));
652
- className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
653
- tabIndex = input(...(ngDevMode ? [undefined, { debugName: "tabIndex" }] : /* istanbul ignore next */ []));
570
+ ngf = injectNgForgeField();
654
571
  options = input([], ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
655
572
  props = input(...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
656
- meta = input(...(ngDevMode ? [undefined, { debugName: "meta" }] : /* istanbul ignore next */ []));
657
- validationMessages = input(...(ngDevMode ? [undefined, { debugName: "validationMessages" }] : /* istanbul ignore next */ []));
658
- defaultValidationMessages = input(...(ngDevMode ? [undefined, { debugName: "defaultValidationMessages" }] : /* istanbul ignore next */ []));
659
- resolvedErrors = createResolvedErrorsSignal(this.field, this.validationMessages, this.defaultValidationMessages);
660
- showErrors = shouldShowErrors(this.field);
661
- errorsToDisplay = computed(() => (this.showErrors() ? this.resolvedErrors() : []), ...(ngDevMode ? [{ debugName: "errorsToDisplay" }] : /* istanbul ignore next */ []));
662
- constructor() {
663
- // Apply meta attributes to all radio inputs, re-apply when options change
664
- setupMetaTracking(this.elementRef, this.meta, {
665
- selector: 'input[type="radio"]',
666
- dependents: [this.options],
667
- });
668
- }
669
- // ─────────────────────────────────────────────────────────────────────────────
670
- // Accessibility
671
- // ─────────────────────────────────────────────────────────────────────────────
672
- /** Unique ID for the hint element, used for aria-describedby */
673
- hintId = computed(() => `${this.key()}-hint`, ...(ngDevMode ? [{ debugName: "hintId" }] : /* istanbul ignore next */ []));
674
- /** Base ID for error elements, used for aria-describedby */
675
- errorId = computed(() => `${this.key()}-error`, ...(ngDevMode ? [{ debugName: "errorId" }] : /* istanbul ignore next */ []));
676
- /** aria-invalid: true when field is invalid AND touched, false otherwise */
677
- ariaInvalid = computed(() => {
678
- const fieldState = this.field()();
679
- return fieldState.invalid() && fieldState.touched();
680
- }, ...(ngDevMode ? [{ debugName: "ariaInvalid" }] : /* istanbul ignore next */ []));
681
- /** aria-required: true if field is required, null otherwise (to remove attribute) */
682
- ariaRequired = computed(() => {
683
- return this.field()().required?.() === true ? true : null;
684
- }, ...(ngDevMode ? [{ debugName: "ariaRequired" }] : /* istanbul ignore next */ []));
685
- /** aria-describedby: links to hint and error messages for screen readers */
686
- ariaDescribedBy = createAriaDescribedBySignal(this.errorsToDisplay, this.errorId, this.hintId, () => !!this.props()?.hint);
687
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: MatRadioFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
688
- 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: `
689
- @let f = field();
690
- @let radioGroupId = key() + '-radio-group';
691
-
692
- @if (label()) {
693
- <div class="radio-label">{{ label() | dynamicText | async }}</div>
573
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: MatRadioFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
574
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", 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: `
575
+ @let f = ngf.field();
576
+ @let radioGroupId = ngf.key() + '-radio-group';
577
+
578
+ @if (ngf.label()) {
579
+ <div class="radio-label">{{ ngf.label() | dynamicText | async }}</div>
694
580
  }
695
581
 
696
582
  <mat-radio-group
697
583
  [id]="radioGroupId"
698
584
  [formField]="f"
699
- [attr.aria-invalid]="ariaInvalid()"
700
- [attr.aria-required]="ariaRequired()"
701
- [attr.aria-describedby]="ariaDescribedBy()"
585
+ [attr.aria-invalid]="ngf.ariaInvalid()"
586
+ [attr.aria-required]="ngf.ariaRequired()"
587
+ [attr.aria-describedby]="ngf.ariaDescribedBy()"
702
588
  >
703
589
  @for (option of options(); track option.value) {
704
590
  <mat-radio-button
591
+ ngForgeControl="input[type='radio']"
705
592
  [value]="option.value"
706
593
  [disabled]="option.disabled || false"
707
594
  [color]="props()?.color || 'primary'"
@@ -712,32 +599,33 @@ class MatRadioFieldComponent {
712
599
  }
713
600
  </mat-radio-group>
714
601
 
715
- @if (errorsToDisplay()[0]; as error) {
716
- <mat-error [id]="errorId()">{{ error.message }}</mat-error>
602
+ @if (ngf.errorsToDisplay()[0]; as error) {
603
+ <mat-error [id]="ngf.errorId()">{{ error.message }}</mat-error>
717
604
  } @else if (props()?.hint; as hint) {
718
- <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
605
+ <div class="mat-hint" [id]="ngf.hintId()">{{ hint | dynamicText | async }}</div>
719
606
  }
720
- `, 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 });
607
+ `, isInline: true, styles: [":host{display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit)}.df-mat-hint{color:var(--df-hint-color, rgba(0, 0, 0, .6));font-size:var(--df-hint-font-size, .875rem)}.df-mat-error{color:var(--df-error-color, #f44336);font-size:var(--df-error-font-size, .875rem)}\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 });
721
608
  }
722
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: MatRadioFieldComponent, decorators: [{
609
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: MatRadioFieldComponent, decorators: [{
723
610
  type: Component,
724
- args: [{ selector: 'df-mat-radio', imports: [MatRadioGroup, MatRadioButton, FormField, MatError, DynamicTextPipe, AsyncPipe], template: `
725
- @let f = field();
726
- @let radioGroupId = key() + '-radio-group';
611
+ args: [{ selector: 'df-mat-radio', imports: [MatRadioGroup, MatRadioButton, FormField, MatError, DynamicTextPipe, AsyncPipe, NgForgeControl], hostDirectives: [NgForgeFieldHost], template: `
612
+ @let f = ngf.field();
613
+ @let radioGroupId = ngf.key() + '-radio-group';
727
614
 
728
- @if (label()) {
729
- <div class="radio-label">{{ label() | dynamicText | async }}</div>
615
+ @if (ngf.label()) {
616
+ <div class="radio-label">{{ ngf.label() | dynamicText | async }}</div>
730
617
  }
731
618
 
732
619
  <mat-radio-group
733
620
  [id]="radioGroupId"
734
621
  [formField]="f"
735
- [attr.aria-invalid]="ariaInvalid()"
736
- [attr.aria-required]="ariaRequired()"
737
- [attr.aria-describedby]="ariaDescribedBy()"
622
+ [attr.aria-invalid]="ngf.ariaInvalid()"
623
+ [attr.aria-required]="ngf.ariaRequired()"
624
+ [attr.aria-describedby]="ngf.ariaDescribedBy()"
738
625
  >
739
626
  @for (option of options(); track option.value) {
740
627
  <mat-radio-button
628
+ ngForgeControl="input[type='radio']"
741
629
  [value]="option.value"
742
630
  [disabled]="option.disabled || false"
743
631
  [color]="props()?.color || 'primary'"
@@ -748,94 +636,50 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
748
636
  }
749
637
  </mat-radio-group>
750
638
 
751
- @if (errorsToDisplay()[0]; as error) {
752
- <mat-error [id]="errorId()">{{ error.message }}</mat-error>
639
+ @if (ngf.errorsToDisplay()[0]; as error) {
640
+ <mat-error [id]="ngf.errorId()">{{ error.message }}</mat-error>
753
641
  } @else if (props()?.hint; as hint) {
754
- <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
642
+ <div class="mat-hint" [id]="ngf.hintId()">{{ hint | dynamicText | async }}</div>
755
643
  }
756
- `, host: {
757
- '[class]': 'className() || ""',
758
- '[id]': '`${key()}`',
759
- '[attr.data-testid]': 'key()',
760
- '[attr.hidden]': 'field()().hidden() || null',
761
- }, 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"] }]
762
- }], 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 }] }] } });
644
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit)}.df-mat-hint{color:var(--df-hint-color, rgba(0, 0, 0, .6));font-size:var(--df-hint-font-size, .875rem)}.df-mat-error{color:var(--df-error-color, #f44336);font-size:var(--df-error-font-size, .875rem)}\n"] }]
645
+ }], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }] } });
763
646
 
764
647
  var matRadio_component = /*#__PURE__*/Object.freeze({
765
- __proto__: null,
766
- default: MatRadioFieldComponent
648
+ __proto__: null,
649
+ default: MatRadioFieldComponent
767
650
  });
768
651
 
769
652
  class MatSelectFieldComponent {
770
653
  materialConfig = inject(MATERIAL_CONFIG, { optional: true });
771
- elementRef = inject((ElementRef));
772
- field = input.required(...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
773
- key = input.required(...(ngDevMode ? [{ debugName: "key" }] : /* istanbul ignore next */ []));
774
- label = input(...(ngDevMode ? [undefined, { debugName: "label" }] : /* istanbul ignore next */ []));
775
- placeholder = input(...(ngDevMode ? [undefined, { debugName: "placeholder" }] : /* istanbul ignore next */ []));
776
- className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
777
- tabIndex = input(...(ngDevMode ? [undefined, { debugName: "tabIndex" }] : /* istanbul ignore next */ []));
654
+ ngf = injectNgForgeField();
778
655
  options = input([], ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
779
656
  props = input(...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
780
- meta = input(...(ngDevMode ? [undefined, { debugName: "meta" }] : /* istanbul ignore next */ []));
781
- validationMessages = input(...(ngDevMode ? [undefined, { debugName: "validationMessages" }] : /* istanbul ignore next */ []));
782
- defaultValidationMessages = input(...(ngDevMode ? [undefined, { debugName: "defaultValidationMessages" }] : /* istanbul ignore next */ []));
783
- effectiveAppearance = computed(() => this.props()?.appearance ?? this.materialConfig?.appearance ?? 'outline', ...(ngDevMode ? [{ debugName: "effectiveAppearance" }] : /* istanbul ignore next */ []));
784
- effectiveSubscriptSizing = computed(() => this.props()?.subscriptSizing ?? this.materialConfig?.subscriptSizing ?? 'dynamic', ...(ngDevMode ? [{ debugName: "effectiveSubscriptSizing" }] : /* istanbul ignore next */ []));
785
- effectiveFloatLabel = computed(() => this.props()?.floatLabel ?? this.materialConfig?.floatLabel ?? 'auto', ...(ngDevMode ? [{ debugName: "effectiveFloatLabel" }] : /* istanbul ignore next */ []));
786
- effectiveHideRequiredMarker = computed(() => this.props()?.hideRequiredMarker ?? this.materialConfig?.hideRequiredMarker ?? false, ...(ngDevMode ? [{ debugName: "effectiveHideRequiredMarker" }] : /* istanbul ignore next */ []));
787
- resolvedErrors = createResolvedErrorsSignal(this.field, this.validationMessages, this.defaultValidationMessages);
788
- showErrors = shouldShowErrors(this.field);
789
- errorsToDisplay = computed(() => (this.showErrors() ? this.resolvedErrors() : []), ...(ngDevMode ? [{ debugName: "errorsToDisplay" }] : /* istanbul ignore next */ []));
657
+ appearance = computed(() => this.props()?.appearance ?? this.materialConfig?.appearance ?? 'outline', ...(ngDevMode ? [{ debugName: "appearance" }] : /* istanbul ignore next */ []));
658
+ subscriptSizing = computed(() => this.props()?.subscriptSizing ?? this.materialConfig?.subscriptSizing ?? 'dynamic', ...(ngDevMode ? [{ debugName: "subscriptSizing" }] : /* istanbul ignore next */ []));
659
+ floatLabel = computed(() => this.props()?.floatLabel ?? this.materialConfig?.floatLabel ?? 'auto', ...(ngDevMode ? [{ debugName: "floatLabel" }] : /* istanbul ignore next */ []));
660
+ hideRequiredMarker = computed(() => this.props()?.hideRequiredMarker ?? this.materialConfig?.hideRequiredMarker ?? false, ...(ngDevMode ? [{ debugName: "hideRequiredMarker" }] : /* istanbul ignore next */ []));
790
661
  defaultCompare = Object.is;
791
- constructor() {
792
- // mat-select has no native input - apply meta to mat-select element
793
- setupMetaTracking(this.elementRef, this.meta, {
794
- selector: 'mat-select',
795
- });
796
- }
797
- // ─────────────────────────────────────────────────────────────────────────────
798
- // Accessibility
799
- // ─────────────────────────────────────────────────────────────────────────────
800
- /** Unique ID for the hint element, used for aria-describedby */
801
- hintId = computed(() => `${this.key()}-hint`, ...(ngDevMode ? [{ debugName: "hintId" }] : /* istanbul ignore next */ []));
802
- /** Base ID for error elements, used for aria-describedby */
803
- errorId = computed(() => `${this.key()}-error`, ...(ngDevMode ? [{ debugName: "errorId" }] : /* istanbul ignore next */ []));
804
- /** aria-invalid: true when field is invalid AND touched, false otherwise */
805
- ariaInvalid = computed(() => {
806
- const fieldState = this.field()();
807
- return fieldState.invalid() && fieldState.touched();
808
- }, ...(ngDevMode ? [{ debugName: "ariaInvalid" }] : /* istanbul ignore next */ []));
809
- /** aria-required: true if field is required, null otherwise (to remove attribute) */
810
- ariaRequired = computed(() => {
811
- return this.field()().required?.() === true ? true : null;
812
- }, ...(ngDevMode ? [{ debugName: "ariaRequired" }] : /* istanbul ignore next */ []));
813
- /** aria-describedby: links to hint and error messages for screen readers */
814
- ariaDescribedBy = createAriaDescribedBySignal(this.errorsToDisplay, this.errorId, this.hintId, () => !!this.props()?.hint);
815
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: MatSelectFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
816
- 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: `
817
- @let f = field();
818
- @let selectId = key() + '-select';
662
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: MatSelectFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
663
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", 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: `
664
+ @let selectId = ngf.key() + '-select';
819
665
 
820
666
  <mat-form-field
821
- [appearance]="effectiveAppearance()"
822
- [subscriptSizing]="effectiveSubscriptSizing()"
823
- [floatLabel]="effectiveFloatLabel()"
824
- [hideRequiredMarker]="effectiveHideRequiredMarker()"
667
+ [appearance]="appearance()"
668
+ [subscriptSizing]="subscriptSizing()"
669
+ [floatLabel]="floatLabel()"
670
+ [hideRequiredMarker]="hideRequiredMarker()"
825
671
  >
826
- @if (label(); as label) {
672
+ @if (ngf.label(); as label) {
827
673
  <mat-label>{{ label | dynamicText | async }}</mat-label>
828
674
  }
829
675
 
830
676
  <mat-select
677
+ ngForgeControl
831
678
  [id]="selectId"
832
- [formField]="f"
833
- [placeholder]="(placeholder() | dynamicText | async) ?? ''"
679
+ [formField]="ngf.field()"
680
+ [placeholder]="(ngf.placeholder() | dynamicText | async) ?? ''"
834
681
  [multiple]="props()?.multiple || false"
835
682
  [compareWith]="props()?.compareWith || defaultCompare"
836
- [attr.aria-invalid]="ariaInvalid()"
837
- [attr.aria-required]="ariaRequired()"
838
- [attr.aria-describedby]="ariaDescribedBy()"
839
683
  >
840
684
  @for (option of options(); track option.value) {
841
685
  <mat-option [value]="option.value" [disabled]="option.disabled || false">
@@ -844,39 +688,36 @@ class MatSelectFieldComponent {
844
688
  }
845
689
  </mat-select>
846
690
 
847
- @if (errorsToDisplay()[0]; as error) {
848
- <mat-error [id]="errorId()">{{ error.message }}</mat-error>
691
+ @if (ngf.errorsToDisplay()[0]; as error) {
692
+ <mat-error [id]="ngf.errorId()">{{ error.message }}</mat-error>
849
693
  } @else if (props()?.hint; as hint) {
850
- <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
694
+ <mat-hint [id]="ngf.hintId()">{{ hint | dynamicText | async }}</mat-hint>
851
695
  }
852
696
  </mat-form-field>
853
- `, 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 });
697
+ `, isInline: true, styles: [":host{display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit)}.df-mat-hint{color:var(--df-hint-color, rgba(0, 0, 0, .6));font-size:var(--df-hint-font-size, .875rem)}.df-mat-error{color:var(--df-error-color, #f44336);font-size:var(--df-error-font-size, .875rem)}\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 });
854
698
  }
855
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: MatSelectFieldComponent, decorators: [{
699
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: MatSelectFieldComponent, decorators: [{
856
700
  type: Component,
857
- args: [{ selector: 'df-mat-select', imports: [MatFormField, MatLabel, MatSelect, MatOption, MatHint, FormField, MatError$1, DynamicTextPipe, AsyncPipe], template: `
858
- @let f = field();
859
- @let selectId = key() + '-select';
701
+ args: [{ selector: 'df-mat-select', imports: [MatFormField, MatLabel, MatSelect, MatOption, MatHint, FormField, MatError$1, DynamicTextPipe, AsyncPipe, NgForgeControl], hostDirectives: [NgForgeFieldHost], template: `
702
+ @let selectId = ngf.key() + '-select';
860
703
 
861
704
  <mat-form-field
862
- [appearance]="effectiveAppearance()"
863
- [subscriptSizing]="effectiveSubscriptSizing()"
864
- [floatLabel]="effectiveFloatLabel()"
865
- [hideRequiredMarker]="effectiveHideRequiredMarker()"
705
+ [appearance]="appearance()"
706
+ [subscriptSizing]="subscriptSizing()"
707
+ [floatLabel]="floatLabel()"
708
+ [hideRequiredMarker]="hideRequiredMarker()"
866
709
  >
867
- @if (label(); as label) {
710
+ @if (ngf.label(); as label) {
868
711
  <mat-label>{{ label | dynamicText | async }}</mat-label>
869
712
  }
870
713
 
871
714
  <mat-select
715
+ ngForgeControl
872
716
  [id]="selectId"
873
- [formField]="f"
874
- [placeholder]="(placeholder() | dynamicText | async) ?? ''"
717
+ [formField]="ngf.field()"
718
+ [placeholder]="(ngf.placeholder() | dynamicText | async) ?? ''"
875
719
  [multiple]="props()?.multiple || false"
876
720
  [compareWith]="props()?.compareWith || defaultCompare"
877
- [attr.aria-invalid]="ariaInvalid()"
878
- [attr.aria-required]="ariaRequired()"
879
- [attr.aria-describedby]="ariaDescribedBy()"
880
721
  >
881
722
  @for (option of options(); track option.value) {
882
723
  <mat-option [value]="option.value" [disabled]="option.disabled || false">
@@ -885,69 +726,31 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
885
726
  }
886
727
  </mat-select>
887
728
 
888
- @if (errorsToDisplay()[0]; as error) {
889
- <mat-error [id]="errorId()">{{ error.message }}</mat-error>
729
+ @if (ngf.errorsToDisplay()[0]; as error) {
730
+ <mat-error [id]="ngf.errorId()">{{ error.message }}</mat-error>
890
731
  } @else if (props()?.hint; as hint) {
891
- <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
732
+ <mat-hint [id]="ngf.hintId()">{{ hint | dynamicText | async }}</mat-hint>
892
733
  }
893
734
  </mat-form-field>
894
- `, changeDetection: ChangeDetectionStrategy.OnPush, host: {
895
- '[id]': '`${key()}`',
896
- '[attr.data-testid]': 'key()',
897
- '[class]': 'className()',
898
- '[attr.hidden]': 'field()().hidden() || null',
899
- }, 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"] }]
900
- }], 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 }] }] } });
735
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit)}.df-mat-hint{color:var(--df-hint-color, rgba(0, 0, 0, .6));font-size:var(--df-hint-font-size, .875rem)}.df-mat-error{color:var(--df-error-color, #f44336);font-size:var(--df-error-font-size, .875rem)}\n", "mat-form-field{width:100%}\n"] }]
736
+ }], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }] } });
901
737
 
902
738
  var matSelect_component = /*#__PURE__*/Object.freeze({
903
- __proto__: null,
904
- default: MatSelectFieldComponent
739
+ __proto__: null,
740
+ default: MatSelectFieldComponent
905
741
  });
906
742
 
907
743
  class MatSliderFieldComponent {
908
- elementRef = inject((ElementRef));
909
- field = input.required(...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
910
- key = input.required(...(ngDevMode ? [{ debugName: "key" }] : /* istanbul ignore next */ []));
911
- constructor() {
912
- setupMetaTracking(this.elementRef, this.meta, { selector: 'input' });
913
- }
914
- label = input(...(ngDevMode ? [undefined, { debugName: "label" }] : /* istanbul ignore next */ []));
915
- placeholder = input(...(ngDevMode ? [undefined, { debugName: "placeholder" }] : /* istanbul ignore next */ []));
916
- className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
917
- tabIndex = input(...(ngDevMode ? [undefined, { debugName: "tabIndex" }] : /* istanbul ignore next */ []));
744
+ ngf = injectNgForgeField();
918
745
  step = input(...(ngDevMode ? [undefined, { debugName: "step" }] : /* istanbul ignore next */ []));
919
746
  props = input(...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
920
- meta = input(...(ngDevMode ? [undefined, { debugName: "meta" }] : /* istanbul ignore next */ []));
921
- validationMessages = input(...(ngDevMode ? [undefined, { debugName: "validationMessages" }] : /* istanbul ignore next */ []));
922
- defaultValidationMessages = input(...(ngDevMode ? [undefined, { debugName: "defaultValidationMessages" }] : /* istanbul ignore next */ []));
923
- resolvedErrors = createResolvedErrorsSignal(this.field, this.validationMessages, this.defaultValidationMessages);
924
- showErrors = shouldShowErrors(this.field);
925
- errorsToDisplay = computed(() => (this.showErrors() ? this.resolvedErrors() : []), ...(ngDevMode ? [{ debugName: "errorsToDisplay" }] : /* istanbul ignore next */ []));
926
- // ─────────────────────────────────────────────────────────────────────────────
927
- // Accessibility
928
- // ─────────────────────────────────────────────────────────────────────────────
929
- /** Unique ID for the hint element, used for aria-describedby */
930
- hintId = computed(() => `${this.key()}-hint`, ...(ngDevMode ? [{ debugName: "hintId" }] : /* istanbul ignore next */ []));
931
- /** Base ID for error elements, used for aria-describedby */
932
- errorId = computed(() => `${this.key()}-error`, ...(ngDevMode ? [{ debugName: "errorId" }] : /* istanbul ignore next */ []));
933
- /** aria-required: true when field has required validation, null otherwise */
934
- ariaRequired = computed(() => {
935
- return this.field()().required?.() === true ? true : null;
936
- }, ...(ngDevMode ? [{ debugName: "ariaRequired" }] : /* istanbul ignore next */ []));
937
- /** aria-invalid: true when field is invalid AND touched, false otherwise */
938
- ariaInvalid = computed(() => {
939
- const fieldState = this.field()();
940
- return fieldState.invalid() && fieldState.touched();
941
- }, ...(ngDevMode ? [{ debugName: "ariaInvalid" }] : /* istanbul ignore next */ []));
942
- /** aria-describedby: links to hint and error messages for screen readers */
943
- ariaDescribedBy = createAriaDescribedBySignal(this.errorsToDisplay, this.errorId, this.hintId, () => !!this.props()?.hint);
944
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: MatSliderFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
945
- 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: `
946
- @let f = field();
947
- @let inputId = key() + '-input';
948
-
949
- @if (label(); as label) {
950
- <div class="slider-label">{{ label | dynamicText | async }}</div>
747
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: MatSliderFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
748
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", 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: `
749
+ @let f = ngf.field();
750
+ @let inputId = ngf.key() + '-input';
751
+
752
+ @if (ngf.label(); as label) {
753
+ <label class="slider-label" [for]="inputId">{{ label | dynamicText | async }}</label>
951
754
  }
952
755
 
953
756
  <mat-slider
@@ -959,32 +762,24 @@ class MatSliderFieldComponent {
959
762
  [color]="props()?.color || 'primary'"
960
763
  class="slider-container"
961
764
  >
962
- <input
963
- matSliderThumb
964
- [id]="inputId"
965
- [formField]="f"
966
- [attr.tabindex]="tabIndex()"
967
- [attr.aria-invalid]="ariaInvalid()"
968
- [attr.aria-required]="ariaRequired()"
969
- [attr.aria-describedby]="ariaDescribedBy()"
970
- />
765
+ <input matSliderThumb ngForgeControl [id]="inputId" [formField]="f" [attr.tabindex]="ngf.tabIndex()" />
971
766
  </mat-slider>
972
767
 
973
- @if (errorsToDisplay()[0]; as error) {
974
- <mat-error [id]="errorId()">{{ error.message }}</mat-error>
768
+ @if (ngf.errorsToDisplay()[0]; as error) {
769
+ <mat-error [id]="ngf.errorId()">{{ error.message }}</mat-error>
975
770
  } @else if (props()?.hint; as hint) {
976
- <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
771
+ <div class="mat-hint" [id]="ngf.hintId()">{{ hint | dynamicText | async }}</div>
977
772
  }
978
- `, 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 });
773
+ `, isInline: true, styles: [":host{display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit)}.df-mat-hint{color:var(--df-hint-color, rgba(0, 0, 0, .6));font-size:var(--df-hint-font-size, .875rem)}.df-mat-error{color:var(--df-error-color, #f44336);font-size:var(--df-error-font-size, .875rem)}\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 });
979
774
  }
980
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: MatSliderFieldComponent, decorators: [{
775
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: MatSliderFieldComponent, decorators: [{
981
776
  type: Component,
982
- args: [{ selector: 'df-mat-slider', imports: [MatSlider, MatSliderThumb, MatError, DynamicTextPipe, AsyncPipe, FormField], template: `
983
- @let f = field();
984
- @let inputId = key() + '-input';
777
+ args: [{ selector: 'df-mat-slider', imports: [MatSlider, MatSliderThumb, MatError, DynamicTextPipe, AsyncPipe, FormField, NgForgeControl], hostDirectives: [NgForgeFieldHost], template: `
778
+ @let f = ngf.field();
779
+ @let inputId = ngf.key() + '-input';
985
780
 
986
- @if (label(); as label) {
987
- <div class="slider-label">{{ label | dynamicText | async }}</div>
781
+ @if (ngf.label(); as label) {
782
+ <label class="slider-label" [for]="inputId">{{ label | dynamicText | async }}</label>
988
783
  }
989
784
 
990
785
  <mat-slider
@@ -996,442 +791,218 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImpor
996
791
  [color]="props()?.color || 'primary'"
997
792
  class="slider-container"
998
793
  >
999
- <input
1000
- matSliderThumb
1001
- [id]="inputId"
1002
- [formField]="f"
1003
- [attr.tabindex]="tabIndex()"
1004
- [attr.aria-invalid]="ariaInvalid()"
1005
- [attr.aria-required]="ariaRequired()"
1006
- [attr.aria-describedby]="ariaDescribedBy()"
1007
- />
794
+ <input matSliderThumb ngForgeControl [id]="inputId" [formField]="f" [attr.tabindex]="ngf.tabIndex()" />
1008
795
  </mat-slider>
1009
796
 
1010
- @if (errorsToDisplay()[0]; as error) {
1011
- <mat-error [id]="errorId()">{{ error.message }}</mat-error>
797
+ @if (ngf.errorsToDisplay()[0]; as error) {
798
+ <mat-error [id]="ngf.errorId()">{{ error.message }}</mat-error>
1012
799
  } @else if (props()?.hint; as hint) {
1013
- <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
800
+ <div class="mat-hint" [id]="ngf.hintId()">{{ hint | dynamicText | async }}</div>
1014
801
  }
1015
- `, host: {
1016
- '[class]': 'className()',
1017
- '[id]': '`${key()}`',
1018
- '[attr.data-testid]': 'key()',
1019
- '[attr.hidden]': 'field()().hidden() || null',
1020
- }, 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"] }]
1021
- }], 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 }] }] } });
802
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit)}.df-mat-hint{color:var(--df-hint-color, rgba(0, 0, 0, .6));font-size:var(--df-hint-font-size, .875rem)}.df-mat-error{color:var(--df-error-color, #f44336);font-size:var(--df-error-font-size, .875rem)}\n", ".slider-container{width:100%}\n"] }]
803
+ }], propDecorators: { step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }] } });
1022
804
 
1023
805
  var matSlider_component = /*#__PURE__*/Object.freeze({
1024
- __proto__: null,
1025
- default: MatSliderFieldComponent
806
+ __proto__: null,
807
+ default: MatSliderFieldComponent
1026
808
  });
1027
809
 
1028
810
  class MatButtonFieldComponent {
1029
- eventBus = inject(EventBus);
1030
- arrayContext = inject(ARRAY_CONTEXT, { optional: true });
1031
- key = input.required(...(ngDevMode ? [{ debugName: "key" }] : /* istanbul ignore next */ []));
1032
- label = input.required(...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
1033
- disabled = input(false, ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
1034
- hidden = input(false, ...(ngDevMode ? [{ debugName: "hidden" }] : /* istanbul ignore next */ []));
1035
- tabIndex = input(...(ngDevMode ? [undefined, { debugName: "tabIndex" }] : /* istanbul ignore next */ []));
1036
- className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
1037
- /** Event to dispatch on click. Optional for submit buttons (native form submit handles it). */
1038
- event = input(...(ngDevMode ? [undefined, { debugName: "event" }] : /* istanbul ignore next */ []));
1039
- eventArgs = input(...(ngDevMode ? [undefined, { debugName: "eventArgs" }] : /* istanbul ignore next */ []));
811
+ action = injectNgForgeAction();
1040
812
  props = input(...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
1041
- eventContext = input(...(ngDevMode ? [undefined, { debugName: "eventContext" }] : /* istanbul ignore next */ []));
1042
- /** Resolved button type - defaults to 'button' if not specified in props */
1043
813
  buttonType = computed(() => this.props()?.type ?? 'button', ...(ngDevMode ? [{ debugName: "buttonType" }] : /* istanbul ignore next */ []));
1044
- buttonTestId = computed(() => `${this.buttonType()}-${this.key()}`, ...(ngDevMode ? [{ debugName: "buttonTestId" }] : /* istanbul ignore next */ []));
1045
- /**
1046
- * Handle button click.
1047
- * - For submit buttons (type="submit"): do nothing, native form submit handles it
1048
- * - For other buttons: dispatch the configured event via EventBus
1049
- */
814
+ buttonTestId = computed(() => `${this.buttonType()}-${this.action.key()}`, ...(ngDevMode ? [{ debugName: "buttonTestId" }] : /* istanbul ignore next */ []));
1050
815
  onClick() {
1051
- // Native submit buttons let the form handle submission
1052
- if (this.buttonType() === 'submit') {
816
+ if (this.buttonType() === 'submit')
1053
817
  return;
1054
- }
1055
- // Other buttons dispatch their event (if configured)
1056
- const event = this.event();
1057
- if (event) {
1058
- this.dispatchEvent(event);
1059
- }
1060
- }
1061
- dispatchEvent(event) {
1062
- const args = this.eventArgs();
1063
- if (args && args.length > 0) {
1064
- // Build context from injected ARRAY_CONTEXT (with linkedSignal index) or fallback to eventContext
1065
- const context = this.arrayContext
1066
- ? {
1067
- key: this.key(),
1068
- // Read signal to get current index (automatically updates via linkedSignal)
1069
- index: this.arrayContext.index(),
1070
- arrayKey: this.arrayContext.arrayKey,
1071
- formValue: this.arrayContext.formValue,
1072
- }
1073
- : this.eventContext() || { key: this.key() };
1074
- // Resolve tokens in event args using the provided context
1075
- const resolvedArgs = resolveTokens(args, context);
1076
- // Dispatch event with resolved args
1077
- this.eventBus.dispatch(event, ...resolvedArgs);
1078
- }
1079
- else {
1080
- // No args, dispatch event without arguments
1081
- this.eventBus.dispatch(event);
1082
- }
818
+ this.action.dispatch();
1083
819
  }
1084
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: MatButtonFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1085
- 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: `
1086
- @let buttonId = key() + '-button';
820
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: MatButtonFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
821
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.14", 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: `
822
+ @let buttonId = action.key() + '-button';
1087
823
  <button
1088
824
  mat-raised-button
1089
825
  [id]="buttonId"
1090
826
  [type]="buttonType()"
1091
827
  [color]="props()?.color || 'primary'"
1092
- [disabled]="disabled()"
828
+ [disabled]="action.disabled()"
829
+ [attr.tabindex]="action.tabIndex()"
1093
830
  [attr.data-testid]="buttonTestId()"
1094
831
  (click)="onClick()"
1095
832
  >
1096
- {{ label() | dynamicText | async }}
833
+ {{ action.label() | dynamicText | async }}
1097
834
  </button>
1098
- `, 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 });
835
+ `, isInline: true, styles: [":host{display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit)}.df-mat-hint{color:var(--df-hint-color, rgba(0, 0, 0, .6));font-size:var(--df-hint-font-size, .875rem)}.df-mat-error{color:var(--df-error-color, #f44336);font-size:var(--df-error-font-size, .875rem)}\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 });
1099
836
  }
1100
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: MatButtonFieldComponent, decorators: [{
837
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: MatButtonFieldComponent, decorators: [{
1101
838
  type: Component,
1102
- args: [{ selector: 'df-mat-button', imports: [MatButton, DynamicTextPipe, AsyncPipe], host: {
1103
- '[id]': '`${key()}`',
1104
- '[attr.data-testid]': 'key()',
1105
- '[class]': 'className()',
1106
- '[attr.hidden]': 'hidden() || null',
1107
- }, template: `
1108
- @let buttonId = key() + '-button';
839
+ args: [{ selector: 'df-mat-button', imports: [MatButton, DynamicTextPipe, AsyncPipe], hostDirectives: [NgForgeActionHost], template: `
840
+ @let buttonId = action.key() + '-button';
1109
841
  <button
1110
842
  mat-raised-button
1111
843
  [id]="buttonId"
1112
844
  [type]="buttonType()"
1113
845
  [color]="props()?.color || 'primary'"
1114
- [disabled]="disabled()"
846
+ [disabled]="action.disabled()"
847
+ [attr.tabindex]="action.tabIndex()"
1115
848
  [attr.data-testid]="buttonTestId()"
1116
849
  (click)="onClick()"
1117
850
  >
1118
- {{ label() | dynamicText | async }}
851
+ {{ action.label() | dynamicText | async }}
1119
852
  </button>
1120
- `, 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"] }]
1121
- }], 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 }] }] } });
853
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit)}.df-mat-hint{color:var(--df-hint-color, rgba(0, 0, 0, .6));font-size:var(--df-hint-font-size, .875rem)}.df-mat-error{color:var(--df-error-color, #f44336);font-size:var(--df-error-font-size, .875rem)}\n", "button{min-width:fit-content}\n"] }]
854
+ }], propDecorators: { props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }] } });
1122
855
 
1123
856
  var matButton_component = /*#__PURE__*/Object.freeze({
1124
- __proto__: null,
1125
- default: MatButtonFieldComponent
857
+ __proto__: null,
858
+ default: MatButtonFieldComponent
1126
859
  });
1127
860
 
1128
861
  // Public API - component
1129
862
 
1130
863
  class MatTextareaFieldComponent {
1131
864
  materialConfig = inject(MATERIAL_CONFIG, { optional: true });
1132
- elementRef = inject((ElementRef));
1133
- field = input.required(...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
1134
- key = input.required(...(ngDevMode ? [{ debugName: "key" }] : /* istanbul ignore next */ []));
1135
- label = input(...(ngDevMode ? [undefined, { debugName: "label" }] : /* istanbul ignore next */ []));
1136
- placeholder = input(...(ngDevMode ? [undefined, { debugName: "placeholder" }] : /* istanbul ignore next */ []));
1137
- className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
1138
- tabIndex = input(...(ngDevMode ? [undefined, { debugName: "tabIndex" }] : /* istanbul ignore next */ []));
865
+ ngf = injectNgForgeField();
1139
866
  props = input(...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
1140
- meta = input(...(ngDevMode ? [undefined, { debugName: "meta" }] : /* istanbul ignore next */ []));
1141
- validationMessages = input(...(ngDevMode ? [undefined, { debugName: "validationMessages" }] : /* istanbul ignore next */ []));
1142
- defaultValidationMessages = input(...(ngDevMode ? [undefined, { debugName: "defaultValidationMessages" }] : /* istanbul ignore next */ []));
1143
- constructor() {
1144
- setupMetaTracking(this.elementRef, this.meta, { selector: 'textarea' });
1145
- }
1146
- /**
1147
- * Reference to the native textarea element.
1148
- * Used to imperatively sync the readonly attribute since Angular Signal Forms'
1149
- * [field] directive doesn't sync FieldState.readonly() to the DOM.
1150
- */
1151
- textareaRef = viewChild('textareaRef', ...(ngDevMode ? [{ debugName: "textareaRef" }] : /* istanbul ignore next */ []));
1152
- /**
1153
- * Computed signal that extracts the readonly state from the field.
1154
- * Used by the effect to reactively sync the readonly attribute to the DOM.
1155
- */
1156
- isReadonly = computed(() => this.field()().readonly(), ...(ngDevMode ? [{ debugName: "isReadonly" }] : /* istanbul ignore next */ []));
1157
- /**
1158
- * Workaround: Angular Signal Forms' [field] directive does NOT sync the readonly
1159
- * attribute to the DOM, even though FieldState.readonly() returns the correct value.
1160
- * This effect imperatively sets/removes the readonly attribute on the native textarea
1161
- * element whenever the readonly state changes.
1162
- *
1163
- * Note: We cannot use [readonly] or [attr.readonly] bindings because Angular throws
1164
- * NG8022: "Binding to '[readonly]' is not allowed on nodes using the '[field]' directive"
1165
- *
1166
- * Uses afterRenderEffect to ensure DOM is ready before manipulating attributes.
1167
- *
1168
- * @see https://github.com/angular/angular/issues/65897
1169
- */
1170
- syncReadonlyToDom = afterRenderEffect({
1171
- write: () => {
1172
- const textareaRef = this.textareaRef();
1173
- const isReadonly = this.isReadonly();
1174
- if (textareaRef?.nativeElement) {
1175
- if (isReadonly) {
1176
- textareaRef.nativeElement.setAttribute('readonly', '');
1177
- }
1178
- else {
1179
- textareaRef.nativeElement.removeAttribute('readonly');
1180
- }
1181
- }
1182
- },
1183
- });
1184
- effectiveAppearance = computed(() => this.props()?.appearance ?? this.materialConfig?.appearance ?? 'outline', ...(ngDevMode ? [{ debugName: "effectiveAppearance" }] : /* istanbul ignore next */ []));
1185
- effectiveSubscriptSizing = computed(() => this.props()?.subscriptSizing ?? this.materialConfig?.subscriptSizing ?? 'dynamic', ...(ngDevMode ? [{ debugName: "effectiveSubscriptSizing" }] : /* istanbul ignore next */ []));
1186
- effectiveFloatLabel = computed(() => this.props()?.floatLabel ?? this.materialConfig?.floatLabel ?? 'auto', ...(ngDevMode ? [{ debugName: "effectiveFloatLabel" }] : /* istanbul ignore next */ []));
1187
- effectiveHideRequiredMarker = computed(() => this.props()?.hideRequiredMarker ?? this.materialConfig?.hideRequiredMarker ?? false, ...(ngDevMode ? [{ debugName: "effectiveHideRequiredMarker" }] : /* istanbul ignore next */ []));
1188
- resolvedErrors = createResolvedErrorsSignal(this.field, this.validationMessages, this.defaultValidationMessages);
1189
- showErrors = shouldShowErrors(this.field);
1190
- errorsToDisplay = computed(() => (this.showErrors() ? this.resolvedErrors() : []), ...(ngDevMode ? [{ debugName: "errorsToDisplay" }] : /* istanbul ignore next */ []));
1191
- // ─────────────────────────────────────────────────────────────────────────────
1192
- // Accessibility
1193
- // ─────────────────────────────────────────────────────────────────────────────
1194
- /** Unique ID for the hint element, used for aria-describedby */
1195
- hintId = computed(() => `${this.key()}-hint`, ...(ngDevMode ? [{ debugName: "hintId" }] : /* istanbul ignore next */ []));
1196
- /** Base ID for error elements, used for aria-describedby */
1197
- errorId = computed(() => `${this.key()}-error`, ...(ngDevMode ? [{ debugName: "errorId" }] : /* istanbul ignore next */ []));
1198
- /** aria-invalid: true when field is invalid AND touched, false otherwise */
1199
- ariaInvalid = computed(() => {
1200
- const fieldState = this.field()();
1201
- return fieldState.invalid() && fieldState.touched();
1202
- }, ...(ngDevMode ? [{ debugName: "ariaInvalid" }] : /* istanbul ignore next */ []));
1203
- /** aria-required: true if field is required, null otherwise (to remove attribute) */
1204
- ariaRequired = computed(() => {
1205
- return this.field()().required?.() === true ? true : null;
1206
- }, ...(ngDevMode ? [{ debugName: "ariaRequired" }] : /* istanbul ignore next */ []));
1207
- /** aria-describedby: links to hint and error messages for screen readers */
1208
- ariaDescribedBy = createAriaDescribedBySignal(this.errorsToDisplay, this.errorId, this.hintId, () => !!this.props()?.hint);
1209
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: MatTextareaFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1210
- 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: `
1211
- @let f = field();
1212
- @let textareaId = key() + '-textarea';
867
+ appearance = computed(() => this.props()?.appearance ?? this.materialConfig?.appearance ?? 'outline', ...(ngDevMode ? [{ debugName: "appearance" }] : /* istanbul ignore next */ []));
868
+ subscriptSizing = computed(() => this.props()?.subscriptSizing ?? this.materialConfig?.subscriptSizing ?? 'dynamic', ...(ngDevMode ? [{ debugName: "subscriptSizing" }] : /* istanbul ignore next */ []));
869
+ floatLabel = computed(() => this.props()?.floatLabel ?? this.materialConfig?.floatLabel ?? 'auto', ...(ngDevMode ? [{ debugName: "floatLabel" }] : /* istanbul ignore next */ []));
870
+ hideRequiredMarker = computed(() => this.props()?.hideRequiredMarker ?? this.materialConfig?.hideRequiredMarker ?? false, ...(ngDevMode ? [{ debugName: "hideRequiredMarker" }] : /* istanbul ignore next */ []));
871
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: MatTextareaFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
872
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: MatTextareaFieldComponent, isStandalone: true, selector: "df-mat-textarea", inputs: { props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null } }, hostDirectives: [{ directive: i1.NgForgeFieldHost }], ngImport: i0, template: `
873
+ @let textareaId = ngf.key() + '-textarea';
1213
874
 
1214
875
  <mat-form-field
1215
- [appearance]="effectiveAppearance()"
1216
- [subscriptSizing]="effectiveSubscriptSizing()"
1217
- [floatLabel]="effectiveFloatLabel()"
1218
- [hideRequiredMarker]="effectiveHideRequiredMarker()"
876
+ [appearance]="appearance()"
877
+ [subscriptSizing]="subscriptSizing()"
878
+ [floatLabel]="floatLabel()"
879
+ [hideRequiredMarker]="hideRequiredMarker()"
1219
880
  >
1220
- @if (label()) {
1221
- <mat-label>{{ label() | dynamicText | async }}</mat-label>
881
+ @if (ngf.label()) {
882
+ <mat-label>{{ ngf.label() | dynamicText | async }}</mat-label>
1222
883
  }
1223
884
 
1224
885
  <textarea
1225
- #textareaRef
886
+ ngForgeControl
1226
887
  matInput
1227
888
  [id]="textareaId"
1228
- [formField]="f"
1229
- [placeholder]="(placeholder() | dynamicText | async) ?? ''"
1230
- [attr.tabindex]="tabIndex()"
1231
- [attr.aria-invalid]="ariaInvalid()"
1232
- [attr.aria-required]="ariaRequired()"
1233
- [attr.aria-describedby]="ariaDescribedBy()"
889
+ [formField]="ngf.field()"
890
+ [placeholder]="(ngf.placeholder() | dynamicText | async) ?? ''"
891
+ [attr.tabindex]="ngf.tabIndex()"
1234
892
  [style.resize]="props()?.resize || 'vertical'"
1235
893
  ></textarea>
1236
894
 
1237
- @if (errorsToDisplay()[0]; as error) {
1238
- <mat-error [id]="errorId()">{{ error.message }}</mat-error>
895
+ @if (ngf.errorsToDisplay()[0]; as error) {
896
+ <mat-error [id]="ngf.errorId()">{{ error.message }}</mat-error>
1239
897
  } @else if (props()?.hint; as hint) {
1240
- <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
898
+ <mat-hint [id]="ngf.hintId()">{{ hint | dynamicText | async }}</mat-hint>
1241
899
  }
1242
900
  </mat-form-field>
1243
- `, 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 });
901
+ `, isInline: true, styles: [":host{display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit)}.df-mat-hint{color:var(--df-hint-color, rgba(0, 0, 0, .6));font-size:var(--df-hint-font-size, .875rem)}.df-mat-error{color:var(--df-error-color, #f44336);font-size:var(--df-error-font-size, .875rem)}\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 });
1244
902
  }
1245
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: MatTextareaFieldComponent, decorators: [{
903
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: MatTextareaFieldComponent, decorators: [{
1246
904
  type: Component,
1247
- args: [{ selector: 'df-mat-textarea', imports: [MatFormField, MatLabel, MatInput, MatHint, FormField, MatError$1, DynamicTextPipe, AsyncPipe], template: `
1248
- @let f = field();
1249
- @let textareaId = key() + '-textarea';
905
+ args: [{ selector: 'df-mat-textarea', imports: [MatFormField, MatLabel, MatInput, MatHint, FormField, MatError$1, DynamicTextPipe, AsyncPipe, NgForgeControl], hostDirectives: [NgForgeFieldHost], template: `
906
+ @let textareaId = ngf.key() + '-textarea';
1250
907
 
1251
908
  <mat-form-field
1252
- [appearance]="effectiveAppearance()"
1253
- [subscriptSizing]="effectiveSubscriptSizing()"
1254
- [floatLabel]="effectiveFloatLabel()"
1255
- [hideRequiredMarker]="effectiveHideRequiredMarker()"
909
+ [appearance]="appearance()"
910
+ [subscriptSizing]="subscriptSizing()"
911
+ [floatLabel]="floatLabel()"
912
+ [hideRequiredMarker]="hideRequiredMarker()"
1256
913
  >
1257
- @if (label()) {
1258
- <mat-label>{{ label() | dynamicText | async }}</mat-label>
914
+ @if (ngf.label()) {
915
+ <mat-label>{{ ngf.label() | dynamicText | async }}</mat-label>
1259
916
  }
1260
917
 
1261
918
  <textarea
1262
- #textareaRef
919
+ ngForgeControl
1263
920
  matInput
1264
921
  [id]="textareaId"
1265
- [formField]="f"
1266
- [placeholder]="(placeholder() | dynamicText | async) ?? ''"
1267
- [attr.tabindex]="tabIndex()"
1268
- [attr.aria-invalid]="ariaInvalid()"
1269
- [attr.aria-required]="ariaRequired()"
1270
- [attr.aria-describedby]="ariaDescribedBy()"
922
+ [formField]="ngf.field()"
923
+ [placeholder]="(ngf.placeholder() | dynamicText | async) ?? ''"
924
+ [attr.tabindex]="ngf.tabIndex()"
1271
925
  [style.resize]="props()?.resize || 'vertical'"
1272
926
  ></textarea>
1273
927
 
1274
- @if (errorsToDisplay()[0]; as error) {
1275
- <mat-error [id]="errorId()">{{ error.message }}</mat-error>
928
+ @if (ngf.errorsToDisplay()[0]; as error) {
929
+ <mat-error [id]="ngf.errorId()">{{ error.message }}</mat-error>
1276
930
  } @else if (props()?.hint; as hint) {
1277
- <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
931
+ <mat-hint [id]="ngf.hintId()">{{ hint | dynamicText | async }}</mat-hint>
1278
932
  }
1279
933
  </mat-form-field>
1280
- `, changeDetection: ChangeDetectionStrategy.OnPush, host: {
1281
- '[id]': '`${key()}`',
1282
- '[attr.data-testid]': 'key()',
1283
- '[class]': 'className()',
1284
- '[attr.hidden]': 'field()().hidden() || null',
1285
- }, 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"] }]
1286
- }], 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 }] }] } });
934
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit)}.df-mat-hint{color:var(--df-hint-color, rgba(0, 0, 0, .6));font-size:var(--df-hint-font-size, .875rem)}.df-mat-error{color:var(--df-error-color, #f44336);font-size:var(--df-error-font-size, .875rem)}\n", "mat-form-field{width:100%}\n"] }]
935
+ }], propDecorators: { props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }] } });
1287
936
 
1288
937
  var matTextarea_component = /*#__PURE__*/Object.freeze({
1289
- __proto__: null,
1290
- default: MatTextareaFieldComponent
938
+ __proto__: null,
939
+ default: MatTextareaFieldComponent
1291
940
  });
1292
941
 
1293
942
  class MatToggleFieldComponent {
1294
943
  materialConfig = inject(MATERIAL_CONFIG, { optional: true });
1295
- elementRef = inject((ElementRef));
1296
- field = input.required(...(ngDevMode ? [{ debugName: "field" }] : /* istanbul ignore next */ []));
1297
- key = input.required(...(ngDevMode ? [{ debugName: "key" }] : /* istanbul ignore next */ []));
1298
- label = input(...(ngDevMode ? [undefined, { debugName: "label" }] : /* istanbul ignore next */ []));
1299
- placeholder = input(...(ngDevMode ? [undefined, { debugName: "placeholder" }] : /* istanbul ignore next */ []));
1300
- className = input('', ...(ngDevMode ? [{ debugName: "className" }] : /* istanbul ignore next */ []));
1301
- tabIndex = input(...(ngDevMode ? [undefined, { debugName: "tabIndex" }] : /* istanbul ignore next */ []));
944
+ ngf = injectNgForgeField();
1302
945
  props = input(...(ngDevMode ? [undefined, { debugName: "props" }] : /* istanbul ignore next */ []));
1303
- meta = input(...(ngDevMode ? [undefined, { debugName: "meta" }] : /* istanbul ignore next */ []));
1304
- validationMessages = input(...(ngDevMode ? [undefined, { debugName: "validationMessages" }] : /* istanbul ignore next */ []));
1305
- defaultValidationMessages = input(...(ngDevMode ? [undefined, { debugName: "defaultValidationMessages" }] : /* istanbul ignore next */ []));
1306
- effectiveDisableRipple = computed(() => this.props()?.disableRipple ?? this.materialConfig?.disableRipple ?? false, ...(ngDevMode ? [{ debugName: "effectiveDisableRipple" }] : /* istanbul ignore next */ []));
1307
- resolvedErrors = createResolvedErrorsSignal(this.field, this.validationMessages, this.defaultValidationMessages);
1308
- showErrors = shouldShowErrors(this.field);
1309
- errorsToDisplay = computed(() => (this.showErrors() ? this.resolvedErrors() : []), ...(ngDevMode ? [{ debugName: "errorsToDisplay" }] : /* istanbul ignore next */ []));
1310
- constructor() {
1311
- // Apply meta attributes to the internal toggle button
1312
- // Note: mat-slide-toggle uses <button role="switch"> instead of <input type="checkbox">
1313
- setupMetaTracking(this.elementRef, this.meta, {
1314
- selector: 'button[role="switch"]',
1315
- });
1316
- }
1317
- // ─────────────────────────────────────────────────────────────────────────────
1318
- // Accessibility
1319
- // ─────────────────────────────────────────────────────────────────────────────
1320
- /** Unique ID for the hint element, used for aria-describedby */
1321
- hintId = computed(() => `${this.key()}-hint`, ...(ngDevMode ? [{ debugName: "hintId" }] : /* istanbul ignore next */ []));
1322
- /** Base ID for error elements, used for aria-describedby */
1323
- errorId = computed(() => `${this.key()}-error`, ...(ngDevMode ? [{ debugName: "errorId" }] : /* istanbul ignore next */ []));
1324
- /** aria-invalid: true when field is invalid AND touched, false otherwise */
1325
- ariaInvalid = computed(() => {
1326
- const fieldState = this.field()();
1327
- return fieldState.invalid() && fieldState.touched();
1328
- }, ...(ngDevMode ? [{ debugName: "ariaInvalid" }] : /* istanbul ignore next */ []));
1329
- /** aria-required: true if field is required, null otherwise (to remove attribute) */
1330
- ariaRequired = computed(() => {
1331
- return this.field()().required?.() === true ? true : null;
1332
- }, ...(ngDevMode ? [{ debugName: "ariaRequired" }] : /* istanbul ignore next */ []));
1333
- /** aria-describedby: links to hint and error messages for screen readers */
1334
- ariaDescribedBy = createAriaDescribedBySignal(this.errorsToDisplay, this.errorId, this.hintId, () => !!this.props()?.hint);
1335
- /**
1336
- * Workaround: Angular Material's MatSlideToggle does NOT propagate aria-required to its internal
1337
- * button element. This effect imperatively sets/removes aria-required on the internal button.
1338
- *
1339
- * Uses afterRenderEffect to ensure DOM is ready before querying internal elements.
1340
- */
1341
- syncAriaRequiredToDom = afterRenderEffect(() => {
1342
- const isRequired = this.ariaRequired();
1343
- const buttonEl = this.elementRef.nativeElement.querySelector('button[role="switch"]');
1344
- if (buttonEl) {
1345
- if (isRequired) {
1346
- buttonEl.setAttribute('aria-required', 'true');
1347
- }
1348
- else {
1349
- buttonEl.removeAttribute('aria-required');
1350
- }
1351
- }
1352
- });
1353
- /**
1354
- * Workaround: Angular Material's MatSlideToggle does NOT propagate aria-describedby to its internal
1355
- * button element. This effect imperatively sets/removes aria-describedby on the internal button.
1356
- *
1357
- * Uses afterRenderEffect to ensure DOM is ready before querying internal elements.
1358
- */
1359
- syncAriaDescribedByToDom = afterRenderEffect(() => {
1360
- const describedBy = this.ariaDescribedBy();
1361
- const buttonEl = this.elementRef.nativeElement.querySelector('button[role="switch"]');
1362
- if (buttonEl) {
1363
- if (describedBy) {
1364
- buttonEl.setAttribute('aria-describedby', describedBy);
1365
- }
1366
- else {
1367
- buttonEl.removeAttribute('aria-describedby');
1368
- }
1369
- }
1370
- });
1371
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: MatToggleFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1372
- 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: `
1373
- @let f = field();
1374
- @let toggleId = key() + '-toggle';
946
+ disableRipple = computed(() => this.props()?.disableRipple ?? this.materialConfig?.disableRipple ?? false, ...(ngDevMode ? [{ debugName: "disableRipple" }] : /* istanbul ignore next */ []));
947
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: MatToggleFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
948
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", 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: `
949
+ @let f = ngf.field();
950
+ @let toggleId = ngf.key() + '-toggle';
1375
951
 
1376
952
  <mat-slide-toggle
953
+ ngForgeControl="button[role='switch']"
1377
954
  [id]="toggleId"
1378
955
  [formField]="f"
1379
956
  [color]="props()?.color || 'primary'"
1380
957
  [labelPosition]="props()?.labelPosition || 'after'"
1381
958
  [hideIcon]="props()?.hideIcon || false"
1382
- [disableRipple]="effectiveDisableRipple()"
959
+ [disableRipple]="disableRipple()"
1383
960
  [required]="!!f().required()"
1384
- [attr.aria-describedby]="ariaDescribedBy()"
1385
- [attr.tabindex]="tabIndex()"
961
+ [tabIndex]="ngf.tabIndex() ?? 0"
1386
962
  class="toggle-container"
1387
963
  >
1388
- {{ label() | dynamicText | async }}
964
+ {{ ngf.label() | dynamicText | async }}
1389
965
  </mat-slide-toggle>
1390
966
 
1391
- @if (errorsToDisplay()[0]; as error) {
1392
- <mat-error [id]="errorId()">{{ error.message }}</mat-error>
967
+ @if (ngf.errorsToDisplay()[0]; as error) {
968
+ <mat-error [id]="ngf.errorId()">{{ error.message }}</mat-error>
1393
969
  } @else if (props()?.hint; as hint) {
1394
- <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
970
+ <div class="mat-hint" [id]="ngf.hintId()">{{ hint | dynamicText | async }}</div>
1395
971
  }
1396
- `, 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 });
972
+ `, isInline: true, styles: [":host{display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit)}.df-mat-hint{color:var(--df-hint-color, rgba(0, 0, 0, .6));font-size:var(--df-hint-font-size, .875rem)}.df-mat-error{color:var(--df-error-color, #f44336);font-size:var(--df-error-font-size, .875rem)}\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 });
1397
973
  }
1398
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.8", ngImport: i0, type: MatToggleFieldComponent, decorators: [{
974
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: MatToggleFieldComponent, decorators: [{
1399
975
  type: Component,
1400
- args: [{ selector: 'df-mat-toggle', imports: [MatSlideToggle, FormField, MatError, DynamicTextPipe, AsyncPipe], template: `
1401
- @let f = field();
1402
- @let toggleId = key() + '-toggle';
976
+ args: [{ selector: 'df-mat-toggle', imports: [MatSlideToggle, FormField, MatError, DynamicTextPipe, AsyncPipe, NgForgeControl], hostDirectives: [NgForgeFieldHost], template: `
977
+ @let f = ngf.field();
978
+ @let toggleId = ngf.key() + '-toggle';
1403
979
 
1404
980
  <mat-slide-toggle
981
+ ngForgeControl="button[role='switch']"
1405
982
  [id]="toggleId"
1406
983
  [formField]="f"
1407
984
  [color]="props()?.color || 'primary'"
1408
985
  [labelPosition]="props()?.labelPosition || 'after'"
1409
986
  [hideIcon]="props()?.hideIcon || false"
1410
- [disableRipple]="effectiveDisableRipple()"
987
+ [disableRipple]="disableRipple()"
1411
988
  [required]="!!f().required()"
1412
- [attr.aria-describedby]="ariaDescribedBy()"
1413
- [attr.tabindex]="tabIndex()"
989
+ [tabIndex]="ngf.tabIndex() ?? 0"
1414
990
  class="toggle-container"
1415
991
  >
1416
- {{ label() | dynamicText | async }}
992
+ {{ ngf.label() | dynamicText | async }}
1417
993
  </mat-slide-toggle>
1418
994
 
1419
- @if (errorsToDisplay()[0]; as error) {
1420
- <mat-error [id]="errorId()">{{ error.message }}</mat-error>
995
+ @if (ngf.errorsToDisplay()[0]; as error) {
996
+ <mat-error [id]="ngf.errorId()">{{ error.message }}</mat-error>
1421
997
  } @else if (props()?.hint; as hint) {
1422
- <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
998
+ <div class="mat-hint" [id]="ngf.hintId()">{{ hint | dynamicText | async }}</div>
1423
999
  }
1424
- `, host: {
1425
- '[class]': 'className()',
1426
- '[id]': '`${key()}`',
1427
- '[attr.data-testid]': 'key()',
1428
- '[attr.hidden]': 'field()().hidden() || null',
1429
- }, 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"] }]
1430
- }], 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 }] }] } });
1000
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [":host{display:block;width:100%}:host([hidden]){display:none!important}.df-mat-field{display:flex;flex-direction:column;gap:var(--df-field-gap, .5rem);width:100%}.df-mat-label{font-weight:var(--df-label-font-weight, 500);color:var(--df-label-color, inherit)}.df-mat-hint{color:var(--df-hint-color, rgba(0, 0, 0, .6));font-size:var(--df-hint-font-size, .875rem)}.df-mat-error{color:var(--df-error-color, #f44336);font-size:var(--df-error-font-size, .875rem)}\n"] }]
1001
+ }], propDecorators: { props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }] } });
1431
1002
 
1432
1003
  var matToggle_component = /*#__PURE__*/Object.freeze({
1433
- __proto__: null,
1434
- default: MatToggleFieldComponent
1004
+ __proto__: null,
1005
+ default: MatToggleFieldComponent
1435
1006
  });
1436
1007
 
1437
1008
  // Public exports for material field components and types
@@ -1524,11 +1095,20 @@ function buttonFieldMapper(fieldDef) {
1524
1095
  */
1525
1096
 
1526
1097
  /**
1527
- * Base definition for button field types that don't use the `field` input.
1528
- * Button fields render immediately without waiting for form value integration.
1098
+ * Base for value-bearing field types — waits for the mapper to emit the `field`
1099
+ * input before instantiating the component (matches NgForgeField's
1100
+ * `input.required<FieldTree<T>>()` contract).
1101
+ */
1102
+ const VALUE_FIELD_TYPES_BASE = {
1103
+ renderReadyWhen: ['field'],
1104
+ };
1105
+ /**
1106
+ * Base for action/button field types that don't bind to a form value.
1107
+ * Renders immediately, opted out of form-value collection.
1529
1108
  */
1530
1109
  const BUTTON_FIELD_TYPES_BASE = {
1531
1110
  renderReadyWhen: [],
1111
+ valueHandling: 'exclude',
1532
1112
  };
1533
1113
  /**
1534
1114
  * Material Design field type definitions
@@ -1541,87 +1121,83 @@ const MATERIAL_FIELD_TYPES = [
1541
1121
  mapper: valueFieldMapper,
1542
1122
  propsToMeta: ['type'],
1543
1123
  scope: ['text-input', 'numeric'],
1124
+ addons: {
1125
+ slots: ['prefix', 'suffix'],
1126
+ },
1127
+ ...VALUE_FIELD_TYPES_BASE,
1544
1128
  },
1545
1129
  {
1546
1130
  name: MatField.Select,
1547
1131
  loadComponent: () => Promise.resolve().then(function () { return matSelect_component; }),
1548
1132
  mapper: optionsFieldMapper,
1549
1133
  scope: 'single-select',
1134
+ ...VALUE_FIELD_TYPES_BASE,
1550
1135
  },
1551
1136
  {
1552
1137
  name: MatField.Checkbox,
1553
1138
  loadComponent: () => Promise.resolve().then(function () { return matCheckbox_component; }),
1554
1139
  mapper: checkboxFieldMapper,
1555
1140
  scope: 'boolean',
1141
+ ...VALUE_FIELD_TYPES_BASE,
1556
1142
  },
1557
1143
  {
1558
1144
  name: MatField.Button,
1559
1145
  loadComponent: () => Promise.resolve().then(function () { return matButton_component; }),
1560
1146
  mapper: buttonFieldMapper,
1561
- valueHandling: 'exclude',
1562
1147
  ...BUTTON_FIELD_TYPES_BASE,
1563
1148
  },
1564
1149
  {
1565
1150
  name: MatField.Submit,
1566
1151
  loadComponent: () => Promise.resolve().then(function () { return matButton_component; }),
1567
1152
  mapper: submitButtonFieldMapper,
1568
- valueHandling: 'exclude',
1569
1153
  ...BUTTON_FIELD_TYPES_BASE,
1570
1154
  },
1571
1155
  {
1572
1156
  name: MatField.Next,
1573
1157
  loadComponent: () => Promise.resolve().then(function () { return matButton_component; }),
1574
1158
  mapper: nextButtonFieldMapper,
1575
- valueHandling: 'exclude',
1576
1159
  ...BUTTON_FIELD_TYPES_BASE,
1577
1160
  },
1578
1161
  {
1579
1162
  name: MatField.Previous,
1580
1163
  loadComponent: () => Promise.resolve().then(function () { return matButton_component; }),
1581
1164
  mapper: previousButtonFieldMapper,
1582
- valueHandling: 'exclude',
1583
1165
  ...BUTTON_FIELD_TYPES_BASE,
1584
1166
  },
1585
1167
  {
1586
1168
  name: MatField.AddArrayItem,
1587
1169
  loadComponent: () => Promise.resolve().then(function () { return matButton_component; }),
1588
1170
  mapper: addArrayItemButtonMapper,
1589
- valueHandling: 'exclude',
1590
1171
  ...BUTTON_FIELD_TYPES_BASE,
1591
1172
  },
1592
1173
  {
1593
1174
  name: MatField.PrependArrayItem,
1594
1175
  loadComponent: () => Promise.resolve().then(function () { return matButton_component; }),
1595
1176
  mapper: prependArrayItemButtonMapper,
1596
- valueHandling: 'exclude',
1597
1177
  ...BUTTON_FIELD_TYPES_BASE,
1598
1178
  },
1599
1179
  {
1600
1180
  name: MatField.InsertArrayItem,
1601
1181
  loadComponent: () => Promise.resolve().then(function () { return matButton_component; }),
1602
1182
  mapper: insertArrayItemButtonMapper,
1603
- valueHandling: 'exclude',
1604
1183
  ...BUTTON_FIELD_TYPES_BASE,
1605
1184
  },
1606
1185
  {
1607
1186
  name: MatField.RemoveArrayItem,
1608
1187
  loadComponent: () => Promise.resolve().then(function () { return matButton_component; }),
1609
1188
  mapper: removeArrayItemButtonMapper,
1610
- valueHandling: 'exclude',
1611
1189
  ...BUTTON_FIELD_TYPES_BASE,
1612
1190
  },
1613
1191
  {
1614
1192
  name: MatField.PopArrayItem,
1615
1193
  loadComponent: () => Promise.resolve().then(function () { return matButton_component; }),
1616
1194
  mapper: popArrayItemButtonMapper,
1617
- valueHandling: 'exclude',
1618
1195
  ...BUTTON_FIELD_TYPES_BASE,
1619
1196
  },
1620
1197
  {
1621
1198
  name: MatField.ShiftArrayItem,
1622
1199
  loadComponent: () => Promise.resolve().then(function () { return matButton_component; }),
1623
1200
  mapper: shiftArrayItemButtonMapper,
1624
- valueHandling: 'exclude',
1625
1201
  ...BUTTON_FIELD_TYPES_BASE,
1626
1202
  },
1627
1203
  {
@@ -1630,36 +1206,42 @@ const MATERIAL_FIELD_TYPES = [
1630
1206
  mapper: valueFieldMapper,
1631
1207
  propsToMeta: ['rows', 'cols'],
1632
1208
  scope: 'text-input',
1209
+ ...VALUE_FIELD_TYPES_BASE,
1633
1210
  },
1634
1211
  {
1635
1212
  name: MatField.Radio,
1636
1213
  loadComponent: () => Promise.resolve().then(function () { return matRadio_component; }),
1637
1214
  mapper: optionsFieldMapper,
1638
1215
  scope: 'single-select',
1216
+ ...VALUE_FIELD_TYPES_BASE,
1639
1217
  },
1640
1218
  {
1641
1219
  name: MatField.MultiCheckbox,
1642
1220
  loadComponent: () => Promise.resolve().then(function () { return matMultiCheckbox_component; }),
1643
1221
  mapper: optionsFieldMapper,
1644
1222
  scope: 'multi-select',
1223
+ ...VALUE_FIELD_TYPES_BASE,
1645
1224
  },
1646
1225
  {
1647
1226
  name: MatField.Datepicker,
1648
1227
  loadComponent: () => Promise.resolve().then(function () { return matDatepicker_component; }),
1649
1228
  mapper: datepickerFieldMapper,
1650
1229
  scope: 'date',
1230
+ ...VALUE_FIELD_TYPES_BASE,
1651
1231
  },
1652
1232
  {
1653
1233
  name: MatField.Slider,
1654
1234
  loadComponent: () => Promise.resolve().then(function () { return matSlider_component; }),
1655
1235
  mapper: valueFieldMapper,
1656
1236
  scope: 'numeric',
1237
+ ...VALUE_FIELD_TYPES_BASE,
1657
1238
  },
1658
1239
  {
1659
1240
  name: MatField.Toggle,
1660
1241
  loadComponent: () => Promise.resolve().then(function () { return matToggle_component; }),
1661
1242
  mapper: checkboxFieldMapper,
1662
1243
  scope: 'boolean',
1244
+ ...VALUE_FIELD_TYPES_BASE,
1663
1245
  },
1664
1246
  ];
1665
1247
 
@@ -1670,19 +1252,213 @@ const MATERIAL_FIELD_TYPES = [
1670
1252
  */
1671
1253
 
1672
1254
  function withMaterialFields(config) {
1673
- if (!config) {
1674
- return MATERIAL_FIELD_TYPES;
1675
- }
1676
- const fieldsWithConfig = [
1677
- ...MATERIAL_FIELD_TYPES,
1678
- {
1255
+ // Always include the addons feature — mat-icon / mat-button are part of
1256
+ // the canonical Material surface.
1257
+ const base = [...MATERIAL_FIELD_TYPES, withMaterialAddons()];
1258
+ if (config) {
1259
+ base.push({
1679
1260
  ɵkind: 'material-config',
1680
1261
  ɵproviders: [{ provide: MATERIAL_CONFIG, useValue: config }],
1681
- },
1682
- ];
1683
- // Safe: this preserves all material field definitions and appends exactly one config feature.
1684
- return fieldsWithConfig;
1262
+ });
1263
+ return base;
1264
+ }
1265
+ return base;
1266
+ }
1267
+ /* -- Material addon kinds ----------------------------------------------- */
1268
+ const MAT_ICON_KIND = {
1269
+ kind: 'mat-icon',
1270
+ loadComponent: () => Promise.resolve().then(function () { return matIconAddon_component; }).then((m) => m.MatIconAddonComponent),
1271
+ validate: (addon, fieldKey) => {
1272
+ if (typeof addon.icon !== 'string' || addon.icon.length === 0) {
1273
+ throw new DynamicFormError(`Addon kind 'mat-icon' requires a non-empty 'icon' string (field: '${fieldKey}').`);
1274
+ }
1275
+ },
1276
+ };
1277
+ const MAT_BUTTON_KIND = {
1278
+ kind: 'mat-button',
1279
+ loadComponent: () => Promise.resolve().then(function () { return matButtonAddon_component; }).then((m) => m.MatButtonAddonComponent),
1280
+ validate: (addon, fieldKey) => {
1281
+ // Exactly one of preset / actionRef / action — validator drops the addon
1282
+ // (with warning) if the rule is violated.
1283
+ const set = [addon.preset, addon.actionRef, addon.action].filter((v) => v !== undefined);
1284
+ if (set.length > 1) {
1285
+ throw new DynamicFormError(`Addon kind 'mat-button' on field '${fieldKey}' has more than one of preset/actionRef/action — exactly one allowed.`);
1286
+ }
1287
+ // Icon-only buttons require ariaLabel for screen readers.
1288
+ if (addon.icon && !addon.label && !addon.ariaLabel) {
1289
+ throw new DynamicFormError(`Addon kind 'mat-button' on field '${fieldKey}' is icon-only — provide 'ariaLabel' for accessibility.`);
1290
+ }
1291
+ },
1292
+ };
1293
+ /**
1294
+ * Register Material-shipped addon kinds (`mat-icon`, `mat-button`) standalone.
1295
+ *
1296
+ * **Most users don't need this** — `withMaterialFields()` auto-includes
1297
+ * these kinds. Call `withMaterialAddons()` directly only when you want
1298
+ * Material addon kinds without the Material field types (e.g., a custom
1299
+ * field set that wants to render `mat-icon` prefixes), or when you're
1300
+ * stitching addons through a different DI scope.
1301
+ *
1302
+ * @example
1303
+ * ```typescript
1304
+ * // Custom field types + Material addon kinds.
1305
+ * provideDynamicForm(
1306
+ * ...myCustomFields(),
1307
+ * withMaterialAddons(),
1308
+ * );
1309
+ * ```
1310
+ *
1311
+ * Adapter authors who need to override a kind with a customised renderer
1312
+ * should call `withCustomAddon(...)` directly instead.
1313
+ */
1314
+ function withMaterialAddons() {
1315
+ return {
1316
+ ɵkind: 'addons',
1317
+ ɵproviders: [
1318
+ { provide: ADDON_KIND_DEFINITIONS, useValue: MAT_ICON_KIND, multi: true },
1319
+ { provide: ADDON_KIND_DEFINITIONS, useValue: MAT_BUTTON_KIND, multi: true },
1320
+ ],
1321
+ };
1322
+ }
1323
+
1324
+ /**
1325
+ * Renderer for the `mat-icon` addon kind.
1326
+ *
1327
+ * Outputs `<mat-icon>{icon}</mat-icon>` using Material Icons ligatures. The
1328
+ * host is set `aria-hidden="true"` by default; if the addon supplies an
1329
+ * `ariaLabel`, it is applied so the icon is announced by screen readers.
1330
+ */
1331
+ class MatIconAddonComponent {
1332
+ addon = input.required(...(ngDevMode ? [{ debugName: "addon" }] : /* istanbul ignore next */ []));
1333
+ /** Accepted for contract uniformity — `NgComponentOutlet` setInput is strict; every kind must declare it. */
1334
+ fieldInputs = input(...(ngDevMode ? [undefined, { debugName: "fieldInputs" }] : /* istanbul ignore next */ []));
1335
+ ariaLabel = computed(() => this.addon().ariaLabel, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
1336
+ hasAriaLabel = computed(() => this.addon().ariaLabel !== undefined, ...(ngDevMode ? [{ debugName: "hasAriaLabel" }] : /* istanbul ignore next */ []));
1337
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: MatIconAddonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1338
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.2.14", type: MatIconAddonComponent, isStandalone: true, selector: "df-mat-icon-addon", inputs: { addon: { classPropertyName: "addon", publicName: "addon", isSignal: true, isRequired: true, transformFunction: null }, fieldInputs: { classPropertyName: "fieldInputs", publicName: "fieldInputs", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "attr.aria-hidden": "hasAriaLabel() ? null : \"true\"" } }, ngImport: i0, template: `<mat-icon [attr.aria-label]="(ariaLabel() | dynamicText | async) ?? null">{{ addon().icon }}</mat-icon>`, isInline: true, dependencies: [{ kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "pipe", type: AsyncPipe, name: "async" }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1685
1339
  }
1340
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: MatIconAddonComponent, decorators: [{
1341
+ type: Component,
1342
+ args: [{
1343
+ selector: 'df-mat-icon-addon',
1344
+ imports: [MatIcon, AsyncPipe, DynamicTextPipe],
1345
+ template: `<mat-icon [attr.aria-label]="(ariaLabel() | dynamicText | async) ?? null">{{ addon().icon }}</mat-icon>`,
1346
+ host: {
1347
+ '[attr.aria-hidden]': 'hasAriaLabel() ? null : "true"',
1348
+ },
1349
+ changeDetection: ChangeDetectionStrategy.OnPush,
1350
+ }]
1351
+ }], propDecorators: { addon: [{ type: i0.Input, args: [{ isSignal: true, alias: "addon", required: true }] }], fieldInputs: [{ type: i0.Input, args: [{ isSignal: true, alias: "fieldInputs", required: false }] }] } });
1352
+
1353
+ var matIconAddon_component = /*#__PURE__*/Object.freeze({
1354
+ __proto__: null,
1355
+ MatIconAddonComponent: MatIconAddonComponent
1356
+ });
1357
+
1358
+ /**
1359
+ * Renderer for the `mat-button` addon kind.
1360
+ *
1361
+ * Renders `<button mat-icon-button>` when icon-only (no label), or
1362
+ * `<button mat-button>` when labeled. Click dispatch (preset / actionRef /
1363
+ * action precedence, multi-set warning, `disabled` / `loading` resolution)
1364
+ * lives on `NgForgeAddonAction`; this component focuses on the visual layer.
1365
+ */
1366
+ class MatButtonAddonComponent {
1367
+ action = injectNgForgeAddonAction();
1368
+ /** Re-exposed for template binding — same signal stored on the directive. */
1369
+ addon = this.action.addon;
1370
+ label = computed(() => this.addon().label, ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
1371
+ ariaLabel = computed(() => this.addon().ariaLabel, ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
1372
+ isIconOnly = computed(() => {
1373
+ const a = this.addon();
1374
+ return !!a.icon && !a.label;
1375
+ }, ...(ngDevMode ? [{ debugName: "isIconOnly" }] : /* istanbul ignore next */ []));
1376
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: MatButtonAddonComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1377
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.2.14", type: MatButtonAddonComponent, isStandalone: true, selector: "df-mat-button-addon", hostDirectives: [{ directive: i1.NgForgeAddonAction }], ngImport: i0, template: `
1378
+ @if (isIconOnly()) {
1379
+ <button
1380
+ mat-icon-button
1381
+ type="button"
1382
+ [color]="addon().color ?? null"
1383
+ [disabled]="action.disabled() || action.loading()"
1384
+ [attr.aria-label]="(ariaLabel() | dynamicText | async) ?? null"
1385
+ [attr.aria-busy]="action.loading() || null"
1386
+ (click)="action.dispatch()"
1387
+ >
1388
+ @if (action.loading()) {
1389
+ <mat-progress-spinner mode="indeterminate" diameter="18" aria-hidden="true" />
1390
+ <span class="df-mat-sr-only" role="status">Loading…</span>
1391
+ } @else {
1392
+ <mat-icon aria-hidden="true">{{ addon().icon }}</mat-icon>
1393
+ }
1394
+ </button>
1395
+ } @else {
1396
+ <button
1397
+ mat-button
1398
+ type="button"
1399
+ [color]="addon().color ?? null"
1400
+ [disabled]="action.disabled() || action.loading()"
1401
+ [attr.aria-label]="(ariaLabel() | dynamicText | async) ?? null"
1402
+ [attr.aria-busy]="action.loading() || null"
1403
+ (click)="action.dispatch()"
1404
+ >
1405
+ @if (action.loading()) {
1406
+ <mat-progress-spinner mode="indeterminate" diameter="18" aria-hidden="true" />
1407
+ <span class="df-mat-sr-only" role="status">Loading…</span>
1408
+ } @else if (addon().icon; as icon) {
1409
+ <mat-icon aria-hidden="true">{{ icon }}</mat-icon>
1410
+ }
1411
+ {{ label() | dynamicText | async }}
1412
+ </button>
1413
+ }
1414
+ `, isInline: true, styles: [".df-mat-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\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: "component", type: MatIconButton, selector: "button[mat-icon-button], a[mat-icon-button], button[matIconButton], a[matIconButton]", exportAs: ["matButton", "matAnchor"] }, { kind: "component", type: MatIcon, selector: "mat-icon", inputs: ["color", "inline", "svgIcon", "fontSet", "fontIcon"], exportAs: ["matIcon"] }, { kind: "component", type: MatProgressSpinner, selector: "mat-progress-spinner, mat-spinner", inputs: ["color", "mode", "value", "diameter", "strokeWidth"], exportAs: ["matProgressSpinner"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1415
+ }
1416
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.2.14", ngImport: i0, type: MatButtonAddonComponent, decorators: [{
1417
+ type: Component,
1418
+ args: [{ selector: 'df-mat-button-addon', imports: [MatButton, MatIconButton, MatIcon, MatProgressSpinner, DynamicTextPipe, AsyncPipe], hostDirectives: [NgForgeAddonAction], template: `
1419
+ @if (isIconOnly()) {
1420
+ <button
1421
+ mat-icon-button
1422
+ type="button"
1423
+ [color]="addon().color ?? null"
1424
+ [disabled]="action.disabled() || action.loading()"
1425
+ [attr.aria-label]="(ariaLabel() | dynamicText | async) ?? null"
1426
+ [attr.aria-busy]="action.loading() || null"
1427
+ (click)="action.dispatch()"
1428
+ >
1429
+ @if (action.loading()) {
1430
+ <mat-progress-spinner mode="indeterminate" diameter="18" aria-hidden="true" />
1431
+ <span class="df-mat-sr-only" role="status">Loading…</span>
1432
+ } @else {
1433
+ <mat-icon aria-hidden="true">{{ addon().icon }}</mat-icon>
1434
+ }
1435
+ </button>
1436
+ } @else {
1437
+ <button
1438
+ mat-button
1439
+ type="button"
1440
+ [color]="addon().color ?? null"
1441
+ [disabled]="action.disabled() || action.loading()"
1442
+ [attr.aria-label]="(ariaLabel() | dynamicText | async) ?? null"
1443
+ [attr.aria-busy]="action.loading() || null"
1444
+ (click)="action.dispatch()"
1445
+ >
1446
+ @if (action.loading()) {
1447
+ <mat-progress-spinner mode="indeterminate" diameter="18" aria-hidden="true" />
1448
+ <span class="df-mat-sr-only" role="status">Loading…</span>
1449
+ } @else if (addon().icon; as icon) {
1450
+ <mat-icon aria-hidden="true">{{ icon }}</mat-icon>
1451
+ }
1452
+ {{ label() | dynamicText | async }}
1453
+ </button>
1454
+ }
1455
+ `, changeDetection: ChangeDetectionStrategy.OnPush, styles: [".df-mat-sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}\n"] }]
1456
+ }] });
1457
+
1458
+ var matButtonAddon_component = /*#__PURE__*/Object.freeze({
1459
+ __proto__: null,
1460
+ MatButtonAddonComponent: MatButtonAddonComponent
1461
+ });
1686
1462
 
1687
1463
  // Field components
1688
1464
 
@@ -1690,5 +1466,5 @@ function withMaterialFields(config) {
1690
1466
  * Generated bundle index. Do not edit.
1691
1467
  */
1692
1468
 
1693
- export { MATERIAL_CONFIG, MATERIAL_FIELD_TYPES, MatButtonFieldComponent, MatCheckboxFieldComponent, MatDatepickerFieldComponent, MatField, MatInputFieldComponent, MatMultiCheckboxFieldComponent, MatRadioFieldComponent, MatSelectFieldComponent, MatSliderFieldComponent, MatTextareaFieldComponent, MatToggleFieldComponent, withMaterialFields };
1469
+ export { MATERIAL_CONFIG, MATERIAL_FIELD_TYPES, MAT_INPUT_TYPE_OVERRIDE, MatButtonAddonComponent, MatButtonFieldComponent, MatCheckboxFieldComponent, MatDatepickerFieldComponent, MatField, MatIconAddonComponent, MatInputFieldComponent, MatMultiCheckboxFieldComponent, MatRadioFieldComponent, MatSelectFieldComponent, MatSliderFieldComponent, MatTextareaFieldComponent, MatToggleFieldComponent, withMaterialAddons, withMaterialFields };
1694
1470
  //# sourceMappingURL=ng-forge-dynamic-forms-material.mjs.map