@ng-forge/dynamic-forms-material 0.3.1 → 0.5.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,15 +1,15 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, inject, ElementRef, input, computed, ChangeDetectionStrategy, Component, viewChild, afterRenderEffect, linkedSignal, isSignal } from '@angular/core';
2
+ import { InjectionToken, computed, inject, ElementRef, input, afterRenderEffect, ChangeDetectionStrategy, Component, viewChild, 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, buildBaseInputs, FIELD_SIGNAL_CONTEXT, resolveSubmitButtonDisabled, resolveNextButtonDisabled, NextPageEvent, PreviousPageEvent, DynamicFormLogger, AddArrayItemEvent, RemoveArrayItemEvent } from '@ng-forge/dynamic-forms';
6
- import { createResolvedErrorsSignal, shouldShowErrors, setupMetaTracking, isEqual, ValueInArrayPipe, valueFieldMapper, optionsFieldMapper, checkboxFieldMapper, datepickerFieldMapper } from '@ng-forge/dynamic-forms/integration';
5
+ import { DynamicTextPipe, EventBus, ARRAY_CONTEXT, resolveTokens, DEFAULT_PROPS, buildBaseInputs, RootFormRegistryService, FORM_OPTIONS, resolveSubmitButtonDisabled, FIELD_SIGNAL_CONTEXT, resolveNextButtonDisabled, NextPageEvent, PreviousPageEvent, DynamicFormLogger, AddArrayItemEvent, RemoveArrayItemEvent } from '@ng-forge/dynamic-forms';
6
+ import { createResolvedErrorsSignal, shouldShowErrors, setupMetaTracking, isEqual, valueFieldMapper, optionsFieldMapper, checkboxFieldMapper, datepickerFieldMapper } from '@ng-forge/dynamic-forms/integration';
7
7
  import { MatError, MatInput, MatHint } from '@angular/material/input';
8
8
  import { AsyncPipe } from '@angular/common';
9
- import { explicitEffect } from 'ngxtension/explicit-effect';
10
9
  import { MatFormField, MatLabel, MatSuffix, MatError as MatError$1 } from '@angular/material/form-field';
11
10
  import { MatDatepicker, MatDatepickerInput, MatDatepickerToggle } from '@angular/material/datepicker';
12
11
  import { provideNativeDateAdapter } from '@angular/material/core';
12
+ import { explicitEffect } from 'ngxtension/explicit-effect';
13
13
  import { MatRadioGroup, MatRadioButton } from '@angular/material/radio';
14
14
  import { MatSelect, MatOption } from '@angular/material/select';
15
15
  import { MatSlider, MatSliderThumb } from '@angular/material/slider';
@@ -39,6 +39,29 @@ import { MatSlideToggle } from '@angular/material/slide-toggle';
39
39
  */
40
40
  const MATERIAL_CONFIG = new InjectionToken('MATERIAL_CONFIG');
41
41
 
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
+
42
65
  class MatCheckboxFieldComponent {
43
66
  materialConfig = inject(MATERIAL_CONFIG, { optional: true });
44
67
  elementRef = inject((ElementRef));
@@ -80,17 +103,7 @@ class MatCheckboxFieldComponent {
80
103
  return this.field()().required?.() === true ? true : null;
81
104
  }, ...(ngDevMode ? [{ debugName: "ariaRequired" }] : []));
82
105
  /** aria-describedby: links to hint and error messages for screen readers */
83
- ariaDescribedBy = computed(() => {
84
- const ids = [];
85
- if (this.props()?.hint) {
86
- ids.push(this.hintId());
87
- }
88
- const errors = this.errorsToDisplay();
89
- errors.forEach((_, i) => {
90
- ids.push(`${this.errorId()}-${i}`);
91
- });
92
- return ids.join(' ');
93
- }, ...(ngDevMode ? [{ debugName: "ariaDescribedBy" }] : []));
106
+ ariaDescribedBy = createAriaDescribedBySignal(this.errorsToDisplay, this.errorId, this.hintId, () => !!this.props()?.hint);
94
107
  /**
95
108
  * Workaround: Angular Material's MatCheckbox does NOT set aria-required on its internal
96
109
  * input element when [required] is passed, even though it sets the native required attribute.
@@ -98,8 +111,11 @@ class MatCheckboxFieldComponent {
98
111
  *
99
112
  * Bug: MatCheckbox sets `required` attribute but not `aria-required` for screen readers.
100
113
  * @see https://github.com/angular/components/issues/XXXXX (TODO: file issue)
114
+ *
115
+ * Uses afterRenderEffect to ensure DOM is ready before manipulating attributes.
101
116
  */
102
- syncAriaRequiredToDom = explicitEffect([this.ariaRequired], ([isRequired]) => {
117
+ syncAriaRequiredToDom = afterRenderEffect(() => {
118
+ const isRequired = this.ariaRequired();
103
119
  const inputEl = this.elementRef.nativeElement.querySelector('input[type="checkbox"]');
104
120
  if (inputEl) {
105
121
  if (isRequired) {
@@ -110,11 +126,28 @@ class MatCheckboxFieldComponent {
110
126
  }
111
127
  }
112
128
  });
113
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MatCheckboxFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
114
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.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: `
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.1.0", ngImport: i0, type: MatCheckboxFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
148
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", 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: `
115
149
  @let f = field();
116
150
  @let checkboxId = key() + '-checkbox';
117
- @let ariaDescribedBy = this.ariaDescribedBy();
118
151
 
119
152
  <mat-checkbox
120
153
  [id]="checkboxId"
@@ -124,27 +157,25 @@ class MatCheckboxFieldComponent {
124
157
  [color]="props()?.color || 'primary'"
125
158
  [disableRipple]="effectiveDisableRipple()"
126
159
  [required]="!!f().required()"
127
- [aria-describedby]="ariaDescribedBy"
160
+ [attr.aria-describedby]="ariaDescribedBy()"
128
161
  [attr.tabindex]="tabIndex()"
129
162
  [attr.hidden]="f().hidden() || null"
130
163
  >
131
164
  {{ label() | dynamicText | async }}
132
165
  </mat-checkbox>
133
166
 
134
- @if (props()?.hint; as hint) {
167
+ @if (errorsToDisplay()[0]; as error) {
168
+ <mat-error [id]="errorId()" [attr.hidden]="f().hidden() || null">{{ error.message }}</mat-error>
169
+ } @else if (props()?.hint; as hint) {
135
170
  <div class="mat-hint" [id]="hintId()" [attr.hidden]="f().hidden() || null">{{ hint | dynamicText | async }}</div>
136
171
  }
137
- @for (error of errorsToDisplay(); track error.kind; let i = $index) {
138
- <mat-error [id]="errorId() + '-' + i" [attr.hidden]="f().hidden() || null">{{ error.message }}</mat-error>
139
- }
140
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"] }, { 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 });
141
173
  }
142
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MatCheckboxFieldComponent, decorators: [{
174
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MatCheckboxFieldComponent, decorators: [{
143
175
  type: Component,
144
176
  args: [{ selector: 'df-mat-checkbox', imports: [MatCheckbox, FormField, MatError, DynamicTextPipe, AsyncPipe], template: `
145
177
  @let f = field();
146
178
  @let checkboxId = key() + '-checkbox';
147
- @let ariaDescribedBy = this.ariaDescribedBy();
148
179
 
149
180
  <mat-checkbox
150
181
  [id]="checkboxId"
@@ -154,19 +185,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
154
185
  [color]="props()?.color || 'primary'"
155
186
  [disableRipple]="effectiveDisableRipple()"
156
187
  [required]="!!f().required()"
157
- [aria-describedby]="ariaDescribedBy"
188
+ [attr.aria-describedby]="ariaDescribedBy()"
158
189
  [attr.tabindex]="tabIndex()"
159
190
  [attr.hidden]="f().hidden() || null"
160
191
  >
161
192
  {{ label() | dynamicText | async }}
162
193
  </mat-checkbox>
163
194
 
164
- @if (props()?.hint; as hint) {
195
+ @if (errorsToDisplay()[0]; as error) {
196
+ <mat-error [id]="errorId()" [attr.hidden]="f().hidden() || null">{{ error.message }}</mat-error>
197
+ } @else if (props()?.hint; as hint) {
165
198
  <div class="mat-hint" [id]="hintId()" [attr.hidden]="f().hidden() || null">{{ hint | dynamicText | async }}</div>
166
199
  }
167
- @for (error of errorsToDisplay(); track error.kind; let i = $index) {
168
- <mat-error [id]="errorId() + '-' + i" [attr.hidden]="f().hidden() || null">{{ error.message }}</mat-error>
169
- }
170
200
  `, host: {
171
201
  '[class]': 'className()',
172
202
  '[id]': '`${key()}`',
@@ -176,8 +206,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
176
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 }] }] } });
177
207
 
178
208
  var matCheckbox_component = /*#__PURE__*/Object.freeze({
179
- __proto__: null,
180
- default: MatCheckboxFieldComponent
209
+ __proto__: null,
210
+ default: MatCheckboxFieldComponent
181
211
  });
182
212
 
183
213
  class MatDatepickerFieldComponent {
@@ -221,23 +251,11 @@ class MatDatepickerFieldComponent {
221
251
  return this.field()().required?.() === true ? true : null;
222
252
  }, ...(ngDevMode ? [{ debugName: "ariaRequired" }] : []));
223
253
  /** aria-describedby: links to hint and error messages for screen readers */
224
- ariaDescribedBy = computed(() => {
225
- const ids = [];
226
- if (this.props()?.hint) {
227
- ids.push(this.hintId());
228
- }
229
- const errors = this.errorsToDisplay();
230
- errors.forEach((_, i) => {
231
- ids.push(`${this.errorId()}-${i}`);
232
- });
233
- return ids.length > 0 ? ids.join(' ') : null;
234
- }, ...(ngDevMode ? [{ debugName: "ariaDescribedBy" }] : []));
235
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MatDatepickerFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
236
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.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: `
254
+ ariaDescribedBy = createAriaDescribedBySignal(this.errorsToDisplay, this.errorId, this.hintId, () => !!this.props()?.hint);
255
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MatDatepickerFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
256
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", 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: `
237
257
  @let f = field();
238
258
  @let inputId = key() + '-input';
239
- @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
240
- @let ariaDescribedBy = this.ariaDescribedBy();
241
259
 
242
260
  <mat-form-field [appearance]="effectiveAppearance()" [subscriptSizing]="effectiveSubscriptSizing()">
243
261
  @if (label(); as label) {
@@ -253,24 +271,23 @@ class MatDatepickerFieldComponent {
253
271
  [attr.tabindex]="tabIndex()"
254
272
  [min]="minDate()"
255
273
  [max]="maxDate()"
256
- [attr.aria-invalid]="ariaInvalid"
257
- [attr.aria-required]="ariaRequired"
258
- [attr.aria-describedby]="ariaDescribedBy"
274
+ [attr.aria-invalid]="ariaInvalid()"
275
+ [attr.aria-required]="ariaRequired()"
276
+ [attr.aria-describedby]="ariaDescribedBy()"
259
277
  />
260
278
 
261
- <mat-datepicker-toggle matIconSuffix [for]="$any(picker)" />
279
+ <mat-datepicker-toggle matIconSuffix [for]="picker" />
262
280
  <mat-datepicker #picker [startAt]="startAt()" [startView]="props()?.startView || 'month'" [touchUi]="props()?.touchUi ?? false" />
263
281
 
264
- @if (props()?.hint; as hint) {
282
+ @if (errorsToDisplay()[0]; as error) {
283
+ <mat-error [id]="errorId()">{{ error.message }}</mat-error>
284
+ } @else if (props()?.hint; as hint) {
265
285
  <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
266
286
  }
267
- @for (error of errorsToDisplay(); track error.kind; let i = $index) {
268
- <mat-error [id]="errorId() + '-' + i">{{ error.message }}</mat-error>
269
- }
270
287
  </mat-form-field>
271
288
  `, 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"] }, { 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 });
272
289
  }
273
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MatDatepickerFieldComponent, decorators: [{
290
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MatDatepickerFieldComponent, decorators: [{
274
291
  type: Component,
275
292
  args: [{ selector: 'df-mat-datepicker', imports: [
276
293
  MatFormField,
@@ -293,8 +310,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
293
310
  }, template: `
294
311
  @let f = field();
295
312
  @let inputId = key() + '-input';
296
- @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
297
- @let ariaDescribedBy = this.ariaDescribedBy();
298
313
 
299
314
  <mat-form-field [appearance]="effectiveAppearance()" [subscriptSizing]="effectiveSubscriptSizing()">
300
315
  @if (label(); as label) {
@@ -310,27 +325,26 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
310
325
  [attr.tabindex]="tabIndex()"
311
326
  [min]="minDate()"
312
327
  [max]="maxDate()"
313
- [attr.aria-invalid]="ariaInvalid"
314
- [attr.aria-required]="ariaRequired"
315
- [attr.aria-describedby]="ariaDescribedBy"
328
+ [attr.aria-invalid]="ariaInvalid()"
329
+ [attr.aria-required]="ariaRequired()"
330
+ [attr.aria-describedby]="ariaDescribedBy()"
316
331
  />
317
332
 
318
- <mat-datepicker-toggle matIconSuffix [for]="$any(picker)" />
333
+ <mat-datepicker-toggle matIconSuffix [for]="picker" />
319
334
  <mat-datepicker #picker [startAt]="startAt()" [startView]="props()?.startView || 'month'" [touchUi]="props()?.touchUi ?? false" />
320
335
 
321
- @if (props()?.hint; as hint) {
336
+ @if (errorsToDisplay()[0]; as error) {
337
+ <mat-error [id]="errorId()">{{ error.message }}</mat-error>
338
+ } @else if (props()?.hint; as hint) {
322
339
  <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
323
340
  }
324
- @for (error of errorsToDisplay(); track error.kind; let i = $index) {
325
- <mat-error [id]="errorId() + '-' + i">{{ error.message }}</mat-error>
326
- }
327
341
  </mat-form-field>
328
342
  `, 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"] }]
329
343
  }], 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 }] }] } });
330
344
 
331
345
  var matDatepicker_component = /*#__PURE__*/Object.freeze({
332
- __proto__: null,
333
- default: MatDatepickerFieldComponent
346
+ __proto__: null,
347
+ default: MatDatepickerFieldComponent
334
348
  });
335
349
 
336
350
  class MatInputFieldComponent {
@@ -409,23 +423,11 @@ class MatInputFieldComponent {
409
423
  return this.field()().required?.() === true ? true : null;
410
424
  }, ...(ngDevMode ? [{ debugName: "ariaRequired" }] : []));
411
425
  /** aria-describedby: links to hint and error messages for screen readers */
412
- ariaDescribedBy = computed(() => {
413
- const ids = [];
414
- if (this.props()?.hint) {
415
- ids.push(this.hintId());
416
- }
417
- const errors = this.errorsToDisplay();
418
- errors.forEach((_, i) => {
419
- ids.push(`${this.errorId()}-${i}`);
420
- });
421
- return ids.length > 0 ? ids.join(' ') : null;
422
- }, ...(ngDevMode ? [{ debugName: "ariaDescribedBy" }] : []));
423
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MatInputFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
424
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.8", type: MatInputFieldComponent, isStandalone: true, selector: "df-mat-input", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, tabIndex: { classPropertyName: "tabIndex", publicName: "tabIndex", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null }, meta: { classPropertyName: "meta", publicName: "meta", isSignal: true, isRequired: false, transformFunction: null }, validationMessages: { classPropertyName: "validationMessages", publicName: "validationMessages", isSignal: true, isRequired: false, transformFunction: null }, defaultValidationMessages: { classPropertyName: "defaultValidationMessages", publicName: "defaultValidationMessages", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "id": "`${key()}`", "attr.data-testid": "key()", "class": "className()", "attr.hidden": "field()().hidden() || null" } }, viewQueries: [{ propertyName: "inputRef", first: true, predicate: ["inputRef"], descendants: true, isSignal: true }], ngImport: i0, template: `
426
+ ariaDescribedBy = createAriaDescribedBySignal(this.errorsToDisplay, this.errorId, this.hintId, () => !!this.props()?.hint);
427
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MatInputFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
428
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", type: MatInputFieldComponent, isStandalone: true, selector: "df-mat-input", inputs: { field: { classPropertyName: "field", publicName: "field", isSignal: true, isRequired: true, transformFunction: null }, key: { classPropertyName: "key", publicName: "key", isSignal: true, isRequired: true, transformFunction: null }, label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, className: { classPropertyName: "className", publicName: "className", isSignal: true, isRequired: false, transformFunction: null }, tabIndex: { classPropertyName: "tabIndex", publicName: "tabIndex", isSignal: true, isRequired: false, transformFunction: null }, props: { classPropertyName: "props", publicName: "props", isSignal: true, isRequired: false, transformFunction: null }, meta: { classPropertyName: "meta", publicName: "meta", isSignal: true, isRequired: false, transformFunction: null }, validationMessages: { classPropertyName: "validationMessages", publicName: "validationMessages", isSignal: true, isRequired: false, transformFunction: null }, defaultValidationMessages: { classPropertyName: "defaultValidationMessages", publicName: "defaultValidationMessages", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "id": "`${key()}`", "attr.data-testid": "key()", "class": "className()", "attr.hidden": "field()().hidden() || null" } }, viewQueries: [{ propertyName: "inputRef", first: true, predicate: ["inputRef"], descendants: true, isSignal: true }], ngImport: i0, template: `
425
429
  @let f = field();
426
430
  @let inputId = key() + '-input';
427
- @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
428
- @let ariaDescribedBy = this.ariaDescribedBy();
429
431
 
430
432
  <mat-form-field [appearance]="effectiveAppearance()" [subscriptSizing]="effectiveSubscriptSizing()">
431
433
  @if (label()) {
@@ -439,26 +441,23 @@ class MatInputFieldComponent {
439
441
  [type]="props()?.type ?? 'text'"
440
442
  [placeholder]="(placeholder() | dynamicText | async) ?? ''"
441
443
  [attr.tabindex]="tabIndex()"
442
- [attr.aria-invalid]="ariaInvalid"
443
- [attr.aria-required]="ariaRequired"
444
- [attr.aria-describedby]="ariaDescribedBy"
444
+ [attr.aria-invalid]="ariaInvalid()"
445
+ [attr.aria-required]="ariaRequired()"
446
+ [attr.aria-describedby]="ariaDescribedBy()"
445
447
  />
446
- @if (props()?.hint; as hint) {
448
+ @if (errorsToDisplay()[0]; as error) {
449
+ <mat-error [id]="errorId()">{{ error.message }}</mat-error>
450
+ } @else if (props()?.hint; as hint) {
447
451
  <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
448
452
  }
449
- @for (error of errorsToDisplay(); track error.kind; let i = $index) {
450
- <mat-error [id]="errorId() + '-' + i">{{ error.message }}</mat-error>
451
- }
452
453
  </mat-form-field>
453
454
  `, 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"] }, { 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 });
454
455
  }
455
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MatInputFieldComponent, decorators: [{
456
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MatInputFieldComponent, decorators: [{
456
457
  type: Component,
457
458
  args: [{ selector: 'df-mat-input', imports: [MatFormField, MatLabel, MatInput, MatHint, FormField, MatError$1, DynamicTextPipe, AsyncPipe], template: `
458
459
  @let f = field();
459
460
  @let inputId = key() + '-input';
460
- @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
461
- @let ariaDescribedBy = this.ariaDescribedBy();
462
461
 
463
462
  <mat-form-field [appearance]="effectiveAppearance()" [subscriptSizing]="effectiveSubscriptSizing()">
464
463
  @if (label()) {
@@ -472,16 +471,15 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
472
471
  [type]="props()?.type ?? 'text'"
473
472
  [placeholder]="(placeholder() | dynamicText | async) ?? ''"
474
473
  [attr.tabindex]="tabIndex()"
475
- [attr.aria-invalid]="ariaInvalid"
476
- [attr.aria-required]="ariaRequired"
477
- [attr.aria-describedby]="ariaDescribedBy"
474
+ [attr.aria-invalid]="ariaInvalid()"
475
+ [attr.aria-required]="ariaRequired()"
476
+ [attr.aria-describedby]="ariaDescribedBy()"
478
477
  />
479
- @if (props()?.hint; as hint) {
478
+ @if (errorsToDisplay()[0]; as error) {
479
+ <mat-error [id]="errorId()">{{ error.message }}</mat-error>
480
+ } @else if (props()?.hint; as hint) {
480
481
  <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
481
482
  }
482
- @for (error of errorsToDisplay(); track error.kind; let i = $index) {
483
- <mat-error [id]="errorId() + '-' + i">{{ error.message }}</mat-error>
484
- }
485
483
  </mat-form-field>
486
484
  `, changeDetection: ChangeDetectionStrategy.OnPush, host: {
487
485
  '[id]': '`${key()}`',
@@ -492,8 +490,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
492
490
  }], ctorParameters: () => [], propDecorators: { field: [{ type: i0.Input, args: [{ isSignal: true, alias: "field", required: true }] }], key: [{ type: i0.Input, args: [{ isSignal: true, alias: "key", required: true }] }], label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], className: [{ type: i0.Input, args: [{ isSignal: true, alias: "className", required: false }] }], tabIndex: [{ type: i0.Input, args: [{ isSignal: true, alias: "tabIndex", required: false }] }], props: [{ type: i0.Input, args: [{ isSignal: true, alias: "props", required: false }] }], meta: [{ type: i0.Input, args: [{ isSignal: true, alias: "meta", required: false }] }], validationMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "validationMessages", required: false }] }], defaultValidationMessages: [{ type: i0.Input, args: [{ isSignal: true, alias: "defaultValidationMessages", required: false }] }], inputRef: [{ type: i0.ViewChild, args: ['inputRef', { isSignal: true }] }] } });
493
491
 
494
492
  var matInput_component = /*#__PURE__*/Object.freeze({
495
- __proto__: null,
496
- default: MatInputFieldComponent
493
+ __proto__: null,
494
+ default: MatInputFieldComponent
497
495
  });
498
496
 
499
497
  class MatMultiCheckboxFieldComponent {
@@ -511,6 +509,14 @@ class MatMultiCheckboxFieldComponent {
511
509
  const currentValues = this.field()().value();
512
510
  return this.options().filter((option) => currentValues.includes(option.value));
513
511
  }, { ...(ngDevMode ? { debugName: "valueViewModel" } : {}), equal: isEqual });
512
+ /** Computed map of checked option values for O(1) lookup in template */
513
+ checkedValuesMap = computed(() => {
514
+ const map = {};
515
+ for (const opt of this.valueViewModel()) {
516
+ map[String(opt.value)] = true;
517
+ }
518
+ return map;
519
+ }, ...(ngDevMode ? [{ debugName: "checkedValuesMap" }] : []));
514
520
  constructor() {
515
521
  // Apply meta attributes to all checkbox inputs, re-apply when options change
516
522
  setupMetaTracking(this.elementRef, this.meta, {
@@ -564,23 +570,12 @@ class MatMultiCheckboxFieldComponent {
564
570
  return this.field()().required?.() === true ? true : null;
565
571
  }, ...(ngDevMode ? [{ debugName: "ariaRequired" }] : []));
566
572
  /** aria-describedby: links to hint and error messages for screen readers */
567
- ariaDescribedBy = computed(() => {
568
- const ids = [];
569
- if (this.props()?.hint) {
570
- ids.push(this.hintId());
571
- }
572
- const errors = this.errorsToDisplay();
573
- errors.forEach((_, i) => {
574
- ids.push(`${this.errorId()}-${i}`);
575
- });
576
- return ids.length > 0 ? ids.join(' ') : null;
577
- }, ...(ngDevMode ? [{ debugName: "ariaDescribedBy" }] : []));
578
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MatMultiCheckboxFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
579
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.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" } }, providers: [ValueInArrayPipe], ngImport: i0, template: `
573
+ ariaDescribedBy = createAriaDescribedBySignal(this.errorsToDisplay, this.errorId, this.hintId, () => !!this.props()?.hint);
574
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MatMultiCheckboxFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
575
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", 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: `
580
576
  @let f = field();
581
577
  @let checkboxGroupId = key() + '-checkbox-group';
582
- @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
583
- @let ariaDescribedBy = this.ariaDescribedBy();
578
+ @let checked = checkedValuesMap();
584
579
 
585
580
  @if (label(); as label) {
586
581
  <div class="checkbox-group-label">{{ label | dynamicText | async }}</div>
@@ -590,13 +585,13 @@ class MatMultiCheckboxFieldComponent {
590
585
  [id]="checkboxGroupId"
591
586
  class="checkbox-group"
592
587
  role="group"
593
- [attr.aria-invalid]="ariaInvalid"
594
- [attr.aria-required]="ariaRequired"
595
- [attr.aria-describedby]="ariaDescribedBy"
588
+ [attr.aria-invalid]="ariaInvalid()"
589
+ [attr.aria-required]="ariaRequired()"
590
+ [attr.aria-describedby]="ariaDescribedBy()"
596
591
  >
597
592
  @for (option of options(); track option.value) {
598
593
  <mat-checkbox
599
- [checked]="option | inArray: valueViewModel()"
594
+ [checked]="checked['' + option.value]"
600
595
  [disabled]="f().disabled() || option.disabled"
601
596
  [color]="props()?.color || 'primary'"
602
597
  [labelPosition]="props()?.labelPosition || 'after'"
@@ -607,21 +602,19 @@ class MatMultiCheckboxFieldComponent {
607
602
  }
608
603
  </div>
609
604
 
610
- @if (props()?.hint; as hint) {
605
+ @if (errorsToDisplay()[0]; as error) {
606
+ <mat-error [id]="errorId()">{{ error.message }}</mat-error>
607
+ } @else if (props()?.hint; as hint) {
611
608
  <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
612
609
  }
613
- @for (error of errorsToDisplay(); track error.kind; let i = $index) {
614
- <mat-error [id]="errorId() + '-' + i">{{ error.message }}</mat-error>
615
- }
616
- `, 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: ValueInArrayPipe, name: "inArray" }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
610
+ `, 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 });
617
611
  }
618
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MatMultiCheckboxFieldComponent, decorators: [{
612
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MatMultiCheckboxFieldComponent, decorators: [{
619
613
  type: Component,
620
- args: [{ selector: 'df-mat-multi-checkbox', imports: [MatCheckbox, ValueInArrayPipe, MatError, DynamicTextPipe, AsyncPipe], template: `
614
+ args: [{ selector: 'df-mat-multi-checkbox', imports: [MatCheckbox, MatError, DynamicTextPipe, AsyncPipe], template: `
621
615
  @let f = field();
622
616
  @let checkboxGroupId = key() + '-checkbox-group';
623
- @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
624
- @let ariaDescribedBy = this.ariaDescribedBy();
617
+ @let checked = checkedValuesMap();
625
618
 
626
619
  @if (label(); as label) {
627
620
  <div class="checkbox-group-label">{{ label | dynamicText | async }}</div>
@@ -631,13 +624,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
631
624
  [id]="checkboxGroupId"
632
625
  class="checkbox-group"
633
626
  role="group"
634
- [attr.aria-invalid]="ariaInvalid"
635
- [attr.aria-required]="ariaRequired"
636
- [attr.aria-describedby]="ariaDescribedBy"
627
+ [attr.aria-invalid]="ariaInvalid()"
628
+ [attr.aria-required]="ariaRequired()"
629
+ [attr.aria-describedby]="ariaDescribedBy()"
637
630
  >
638
631
  @for (option of options(); track option.value) {
639
632
  <mat-checkbox
640
- [checked]="option | inArray: valueViewModel()"
633
+ [checked]="checked['' + option.value]"
641
634
  [disabled]="f().disabled() || option.disabled"
642
635
  [color]="props()?.color || 'primary'"
643
636
  [labelPosition]="props()?.labelPosition || 'after'"
@@ -648,23 +641,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
648
641
  }
649
642
  </div>
650
643
 
651
- @if (props()?.hint; as hint) {
644
+ @if (errorsToDisplay()[0]; as error) {
645
+ <mat-error [id]="errorId()">{{ error.message }}</mat-error>
646
+ } @else if (props()?.hint; as hint) {
652
647
  <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
653
648
  }
654
- @for (error of errorsToDisplay(); track error.kind; let i = $index) {
655
- <mat-error [id]="errorId() + '-' + i">{{ error.message }}</mat-error>
656
- }
657
649
  `, host: {
658
650
  '[class]': 'className() || ""',
659
651
  '[id]': '`${key()}`',
660
652
  '[attr.data-testid]': 'key()',
661
653
  '[attr.hidden]': 'field()().hidden() || null',
662
- }, providers: [ValueInArrayPipe], 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"] }]
654
+ }, 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"] }]
663
655
  }], 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 }] }] } });
664
656
 
665
657
  var matMultiCheckbox_component = /*#__PURE__*/Object.freeze({
666
- __proto__: null,
667
- default: MatMultiCheckboxFieldComponent
658
+ __proto__: null,
659
+ default: MatMultiCheckboxFieldComponent
668
660
  });
669
661
 
670
662
  class MatRadioFieldComponent {
@@ -707,23 +699,11 @@ class MatRadioFieldComponent {
707
699
  return this.field()().required?.() === true ? true : null;
708
700
  }, ...(ngDevMode ? [{ debugName: "ariaRequired" }] : []));
709
701
  /** aria-describedby: links to hint and error messages for screen readers */
710
- ariaDescribedBy = computed(() => {
711
- const ids = [];
712
- if (this.props()?.hint) {
713
- ids.push(this.hintId());
714
- }
715
- const errors = this.errorsToDisplay();
716
- errors.forEach((_, i) => {
717
- ids.push(`${this.errorId()}-${i}`);
718
- });
719
- return ids.length > 0 ? ids.join(' ') : null;
720
- }, ...(ngDevMode ? [{ debugName: "ariaDescribedBy" }] : []));
721
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MatRadioFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
722
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.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: `
702
+ ariaDescribedBy = createAriaDescribedBySignal(this.errorsToDisplay, this.errorId, this.hintId, () => !!this.props()?.hint);
703
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MatRadioFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
704
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", 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: `
723
705
  @let f = field();
724
706
  @let radioGroupId = key() + '-radio-group';
725
- @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
726
- @let ariaDescribedBy = this.ariaDescribedBy();
727
707
 
728
708
  @if (label()) {
729
709
  <div class="radio-label">{{ label() | dynamicText | async }}</div>
@@ -732,9 +712,9 @@ class MatRadioFieldComponent {
732
712
  <mat-radio-group
733
713
  [id]="radioGroupId"
734
714
  [formField]="f"
735
- [attr.aria-invalid]="ariaInvalid"
736
- [attr.aria-required]="ariaRequired"
737
- [attr.aria-describedby]="ariaDescribedBy"
715
+ [attr.aria-invalid]="ariaInvalid()"
716
+ [attr.aria-required]="ariaRequired()"
717
+ [attr.aria-describedby]="ariaDescribedBy()"
738
718
  >
739
719
  @for (option of options(); track option.value) {
740
720
  <mat-radio-button
@@ -748,21 +728,18 @@ class MatRadioFieldComponent {
748
728
  }
749
729
  </mat-radio-group>
750
730
 
751
- @if (props()?.hint; as hint) {
731
+ @if (errorsToDisplay()[0]; as error) {
732
+ <mat-error [id]="errorId()">{{ error.message }}</mat-error>
733
+ } @else if (props()?.hint; as hint) {
752
734
  <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
753
735
  }
754
- @for (error of errorsToDisplay(); track error.kind; let i = $index) {
755
- <mat-error [id]="errorId() + '-' + i">{{ error.message }}</mat-error>
756
- }
757
736
  `, 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"] }, { 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 });
758
737
  }
759
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MatRadioFieldComponent, decorators: [{
738
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MatRadioFieldComponent, decorators: [{
760
739
  type: Component,
761
740
  args: [{ selector: 'df-mat-radio', imports: [MatRadioGroup, MatRadioButton, FormField, MatError, DynamicTextPipe, AsyncPipe], template: `
762
741
  @let f = field();
763
742
  @let radioGroupId = key() + '-radio-group';
764
- @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
765
- @let ariaDescribedBy = this.ariaDescribedBy();
766
743
 
767
744
  @if (label()) {
768
745
  <div class="radio-label">{{ label() | dynamicText | async }}</div>
@@ -771,9 +748,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
771
748
  <mat-radio-group
772
749
  [id]="radioGroupId"
773
750
  [formField]="f"
774
- [attr.aria-invalid]="ariaInvalid"
775
- [attr.aria-required]="ariaRequired"
776
- [attr.aria-describedby]="ariaDescribedBy"
751
+ [attr.aria-invalid]="ariaInvalid()"
752
+ [attr.aria-required]="ariaRequired()"
753
+ [attr.aria-describedby]="ariaDescribedBy()"
777
754
  >
778
755
  @for (option of options(); track option.value) {
779
756
  <mat-radio-button
@@ -787,12 +764,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
787
764
  }
788
765
  </mat-radio-group>
789
766
 
790
- @if (props()?.hint; as hint) {
767
+ @if (errorsToDisplay()[0]; as error) {
768
+ <mat-error [id]="errorId()">{{ error.message }}</mat-error>
769
+ } @else if (props()?.hint; as hint) {
791
770
  <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
792
771
  }
793
- @for (error of errorsToDisplay(); track error.kind; let i = $index) {
794
- <mat-error [id]="errorId() + '-' + i">{{ error.message }}</mat-error>
795
- }
796
772
  `, host: {
797
773
  '[class]': 'className() || ""',
798
774
  '[id]': '`${key()}`',
@@ -802,8 +778,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
802
778
  }], 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 }] }] } });
803
779
 
804
780
  var matRadio_component = /*#__PURE__*/Object.freeze({
805
- __proto__: null,
806
- default: MatRadioFieldComponent
781
+ __proto__: null,
782
+ default: MatRadioFieldComponent
807
783
  });
808
784
 
809
785
  class MatSelectFieldComponent {
@@ -849,23 +825,11 @@ class MatSelectFieldComponent {
849
825
  return this.field()().required?.() === true ? true : null;
850
826
  }, ...(ngDevMode ? [{ debugName: "ariaRequired" }] : []));
851
827
  /** aria-describedby: links to hint and error messages for screen readers */
852
- ariaDescribedBy = computed(() => {
853
- const ids = [];
854
- if (this.props()?.hint) {
855
- ids.push(this.hintId());
856
- }
857
- const errors = this.errorsToDisplay();
858
- errors.forEach((_, i) => {
859
- ids.push(`${this.errorId()}-${i}`);
860
- });
861
- return ids.length > 0 ? ids.join(' ') : null;
862
- }, ...(ngDevMode ? [{ debugName: "ariaDescribedBy" }] : []));
863
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MatSelectFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
864
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.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: `
828
+ ariaDescribedBy = createAriaDescribedBySignal(this.errorsToDisplay, this.errorId, this.hintId, () => !!this.props()?.hint);
829
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MatSelectFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
830
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", 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: `
865
831
  @let f = field();
866
832
  @let selectId = key() + '-select';
867
- @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
868
- @let ariaDescribedBy = this.ariaDescribedBy();
869
833
 
870
834
  <mat-form-field [appearance]="effectiveAppearance()" [subscriptSizing]="effectiveSubscriptSizing()">
871
835
  @if (label(); as label) {
@@ -878,9 +842,9 @@ class MatSelectFieldComponent {
878
842
  [placeholder]="(placeholder() | dynamicText | async) ?? ''"
879
843
  [multiple]="props()?.multiple || false"
880
844
  [compareWith]="props()?.compareWith || defaultCompare"
881
- [attr.aria-invalid]="ariaInvalid"
882
- [attr.aria-required]="ariaRequired"
883
- [attr.aria-describedby]="ariaDescribedBy"
845
+ [attr.aria-invalid]="ariaInvalid()"
846
+ [attr.aria-required]="ariaRequired()"
847
+ [attr.aria-describedby]="ariaDescribedBy()"
884
848
  >
885
849
  @for (option of options(); track option.value) {
886
850
  <mat-option [value]="option.value" [disabled]="option.disabled || false">
@@ -889,22 +853,19 @@ class MatSelectFieldComponent {
889
853
  }
890
854
  </mat-select>
891
855
 
892
- @if (props()?.hint; as hint) {
856
+ @if (errorsToDisplay()[0]; as error) {
857
+ <mat-error [id]="errorId()">{{ error.message }}</mat-error>
858
+ } @else if (props()?.hint; as hint) {
893
859
  <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
894
860
  }
895
- @for (error of errorsToDisplay(); track error.kind; let i = $index) {
896
- <mat-error [id]="errorId() + '-' + i">{{ error.message }}</mat-error>
897
- }
898
861
  </mat-form-field>
899
862
  `, 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"] }, { 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 });
900
863
  }
901
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MatSelectFieldComponent, decorators: [{
864
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MatSelectFieldComponent, decorators: [{
902
865
  type: Component,
903
866
  args: [{ selector: 'df-mat-select', imports: [MatFormField, MatLabel, MatSelect, MatOption, MatHint, FormField, MatError$1, DynamicTextPipe, AsyncPipe], template: `
904
867
  @let f = field();
905
868
  @let selectId = key() + '-select';
906
- @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
907
- @let ariaDescribedBy = this.ariaDescribedBy();
908
869
 
909
870
  <mat-form-field [appearance]="effectiveAppearance()" [subscriptSizing]="effectiveSubscriptSizing()">
910
871
  @if (label(); as label) {
@@ -917,9 +878,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
917
878
  [placeholder]="(placeholder() | dynamicText | async) ?? ''"
918
879
  [multiple]="props()?.multiple || false"
919
880
  [compareWith]="props()?.compareWith || defaultCompare"
920
- [attr.aria-invalid]="ariaInvalid"
921
- [attr.aria-required]="ariaRequired"
922
- [attr.aria-describedby]="ariaDescribedBy"
881
+ [attr.aria-invalid]="ariaInvalid()"
882
+ [attr.aria-required]="ariaRequired()"
883
+ [attr.aria-describedby]="ariaDescribedBy()"
923
884
  >
924
885
  @for (option of options(); track option.value) {
925
886
  <mat-option [value]="option.value" [disabled]="option.disabled || false">
@@ -928,12 +889,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
928
889
  }
929
890
  </mat-select>
930
891
 
931
- @if (props()?.hint; as hint) {
892
+ @if (errorsToDisplay()[0]; as error) {
893
+ <mat-error [id]="errorId()">{{ error.message }}</mat-error>
894
+ } @else if (props()?.hint; as hint) {
932
895
  <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
933
896
  }
934
- @for (error of errorsToDisplay(); track error.kind; let i = $index) {
935
- <mat-error [id]="errorId() + '-' + i">{{ error.message }}</mat-error>
936
- }
937
897
  </mat-form-field>
938
898
  `, changeDetection: ChangeDetectionStrategy.OnPush, host: {
939
899
  '[id]': '`${key()}`',
@@ -944,8 +904,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
944
904
  }], 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 }] }] } });
945
905
 
946
906
  var matSelect_component = /*#__PURE__*/Object.freeze({
947
- __proto__: null,
948
- default: MatSelectFieldComponent
907
+ __proto__: null,
908
+ default: MatSelectFieldComponent
949
909
  });
950
910
 
951
911
  class MatSliderFieldComponent {
@@ -979,23 +939,11 @@ class MatSliderFieldComponent {
979
939
  return fieldState.invalid() && fieldState.touched();
980
940
  }, ...(ngDevMode ? [{ debugName: "ariaInvalid" }] : []));
981
941
  /** aria-describedby: links to hint and error messages for screen readers */
982
- ariaDescribedBy = computed(() => {
983
- const ids = [];
984
- if (this.props()?.hint) {
985
- ids.push(this.hintId());
986
- }
987
- const errors = this.errorsToDisplay();
988
- errors.forEach((_, i) => {
989
- ids.push(`${this.errorId()}-${i}`);
990
- });
991
- return ids.length > 0 ? ids.join(' ') : null;
992
- }, ...(ngDevMode ? [{ debugName: "ariaDescribedBy" }] : []));
993
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MatSliderFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
994
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.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 }, 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: `
942
+ ariaDescribedBy = createAriaDescribedBySignal(this.errorsToDisplay, this.errorId, this.hintId, () => !!this.props()?.hint);
943
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MatSliderFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
944
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", 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 }, 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: `
995
945
  @let f = field();
996
946
  @let inputId = key() + '-input';
997
- @let ariaInvalid = this.ariaInvalid();
998
- @let ariaDescribedBy = this.ariaDescribedBy();
999
947
 
1000
948
  @if (label(); as label) {
1001
949
  <div class="slider-label">{{ label | dynamicText | async }}</div>
@@ -1015,26 +963,23 @@ class MatSliderFieldComponent {
1015
963
  [id]="inputId"
1016
964
  [formField]="f"
1017
965
  [attr.tabindex]="tabIndex()"
1018
- [attr.aria-invalid]="ariaInvalid"
1019
- [attr.aria-describedby]="ariaDescribedBy"
966
+ [attr.aria-invalid]="ariaInvalid()"
967
+ [attr.aria-describedby]="ariaDescribedBy()"
1020
968
  />
1021
969
  </mat-slider>
1022
970
 
1023
- @if (props()?.hint; as hint) {
971
+ @if (errorsToDisplay()[0]; as error) {
972
+ <mat-error [id]="errorId()">{{ error.message }}</mat-error>
973
+ } @else if (props()?.hint; as hint) {
1024
974
  <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
1025
975
  }
1026
- @for (error of errorsToDisplay(); track error.kind; let i = $index) {
1027
- <mat-error [id]="errorId() + '-' + i">{{ error.message }}</mat-error>
1028
- }
1029
976
  `, 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"] }, { kind: "pipe", type: DynamicTextPipe, name: "dynamicText" }, { kind: "pipe", type: AsyncPipe, name: "async" }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
1030
977
  }
1031
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MatSliderFieldComponent, decorators: [{
978
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MatSliderFieldComponent, decorators: [{
1032
979
  type: Component,
1033
980
  args: [{ selector: 'df-mat-slider', imports: [MatSlider, MatSliderThumb, MatError, DynamicTextPipe, AsyncPipe, FormField], template: `
1034
981
  @let f = field();
1035
982
  @let inputId = key() + '-input';
1036
- @let ariaInvalid = this.ariaInvalid();
1037
- @let ariaDescribedBy = this.ariaDescribedBy();
1038
983
 
1039
984
  @if (label(); as label) {
1040
985
  <div class="slider-label">{{ label | dynamicText | async }}</div>
@@ -1054,17 +999,16 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
1054
999
  [id]="inputId"
1055
1000
  [formField]="f"
1056
1001
  [attr.tabindex]="tabIndex()"
1057
- [attr.aria-invalid]="ariaInvalid"
1058
- [attr.aria-describedby]="ariaDescribedBy"
1002
+ [attr.aria-invalid]="ariaInvalid()"
1003
+ [attr.aria-describedby]="ariaDescribedBy()"
1059
1004
  />
1060
1005
  </mat-slider>
1061
1006
 
1062
- @if (props()?.hint; as hint) {
1007
+ @if (errorsToDisplay()[0]; as error) {
1008
+ <mat-error [id]="errorId()">{{ error.message }}</mat-error>
1009
+ } @else if (props()?.hint; as hint) {
1063
1010
  <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
1064
1011
  }
1065
- @for (error of errorsToDisplay(); track error.kind; let i = $index) {
1066
- <mat-error [id]="errorId() + '-' + i">{{ error.message }}</mat-error>
1067
- }
1068
1012
  `, host: {
1069
1013
  '[class]': 'className()',
1070
1014
  '[id]': '`${key()}`',
@@ -1074,8 +1018,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
1074
1018
  }], 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 }] }] } });
1075
1019
 
1076
1020
  var matSlider_component = /*#__PURE__*/Object.freeze({
1077
- __proto__: null,
1078
- default: MatSliderFieldComponent
1021
+ __proto__: null,
1022
+ default: MatSliderFieldComponent
1079
1023
  });
1080
1024
 
1081
1025
  class MatButtonFieldComponent {
@@ -1134,8 +1078,8 @@ class MatButtonFieldComponent {
1134
1078
  this.eventBus.dispatch(event);
1135
1079
  }
1136
1080
  }
1137
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MatButtonFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1138
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.0.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()" } }, ngImport: i0, template: `
1081
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MatButtonFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1082
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "21.1.0", 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: `
1139
1083
  @let buttonId = key() + '-button';
1140
1084
  <button
1141
1085
  mat-raised-button
@@ -1150,12 +1094,13 @@ class MatButtonFieldComponent {
1150
1094
  </button>
1151
1095
  `, 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 });
1152
1096
  }
1153
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MatButtonFieldComponent, decorators: [{
1097
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MatButtonFieldComponent, decorators: [{
1154
1098
  type: Component,
1155
1099
  args: [{ selector: 'df-mat-button', imports: [MatButton, DynamicTextPipe, AsyncPipe], host: {
1156
1100
  '[id]': '`${key()}`',
1157
1101
  '[attr.data-testid]': 'key()',
1158
1102
  '[class]': 'className()',
1103
+ '[attr.hidden]': 'hidden() || null',
1159
1104
  }, template: `
1160
1105
  @let buttonId = key() + '-button';
1161
1106
  <button
@@ -1173,8 +1118,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
1173
1118
  }], 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 }] }] } });
1174
1119
 
1175
1120
  var matButton_component = /*#__PURE__*/Object.freeze({
1176
- __proto__: null,
1177
- default: MatButtonFieldComponent
1121
+ __proto__: null,
1122
+ default: MatButtonFieldComponent
1178
1123
  });
1179
1124
 
1180
1125
  // Public API - component
@@ -1215,17 +1160,23 @@ class MatTextareaFieldComponent {
1215
1160
  * Note: We cannot use [readonly] or [attr.readonly] bindings because Angular throws
1216
1161
  * NG8022: "Binding to '[readonly]' is not allowed on nodes using the '[field]' directive"
1217
1162
  *
1163
+ * Uses afterRenderEffect to ensure DOM is ready before manipulating attributes.
1164
+ *
1218
1165
  * @see https://github.com/angular/angular/issues/65897
1219
1166
  */
1220
- syncReadonlyToDom = explicitEffect([this.textareaRef, this.isReadonly], ([textareaRef, isReadonly]) => {
1221
- if (textareaRef?.nativeElement) {
1222
- if (isReadonly) {
1223
- textareaRef.nativeElement.setAttribute('readonly', '');
1224
- }
1225
- else {
1226
- textareaRef.nativeElement.removeAttribute('readonly');
1167
+ syncReadonlyToDom = afterRenderEffect({
1168
+ write: () => {
1169
+ const textareaRef = this.textareaRef();
1170
+ const isReadonly = this.isReadonly();
1171
+ if (textareaRef?.nativeElement) {
1172
+ if (isReadonly) {
1173
+ textareaRef.nativeElement.setAttribute('readonly', '');
1174
+ }
1175
+ else {
1176
+ textareaRef.nativeElement.removeAttribute('readonly');
1177
+ }
1227
1178
  }
1228
- }
1179
+ },
1229
1180
  });
1230
1181
  effectiveAppearance = computed(() => this.props()?.appearance ?? this.materialConfig?.appearance ?? 'outline', ...(ngDevMode ? [{ debugName: "effectiveAppearance" }] : []));
1231
1182
  effectiveSubscriptSizing = computed(() => this.props()?.subscriptSizing ?? this.materialConfig?.subscriptSizing ?? 'dynamic', ...(ngDevMode ? [{ debugName: "effectiveSubscriptSizing" }] : []));
@@ -1249,23 +1200,11 @@ class MatTextareaFieldComponent {
1249
1200
  return this.field()().required?.() === true ? true : null;
1250
1201
  }, ...(ngDevMode ? [{ debugName: "ariaRequired" }] : []));
1251
1202
  /** aria-describedby: links to hint and error messages for screen readers */
1252
- ariaDescribedBy = computed(() => {
1253
- const ids = [];
1254
- if (this.props()?.hint) {
1255
- ids.push(this.hintId());
1256
- }
1257
- const errors = this.errorsToDisplay();
1258
- errors.forEach((_, i) => {
1259
- ids.push(`${this.errorId()}-${i}`);
1260
- });
1261
- return ids.length > 0 ? ids.join(' ') : null;
1262
- }, ...(ngDevMode ? [{ debugName: "ariaDescribedBy" }] : []));
1263
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MatTextareaFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1264
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.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: `
1203
+ ariaDescribedBy = createAriaDescribedBySignal(this.errorsToDisplay, this.errorId, this.hintId, () => !!this.props()?.hint);
1204
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MatTextareaFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1205
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", 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: `
1265
1206
  @let f = field();
1266
1207
  @let textareaId = key() + '-textarea';
1267
- @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
1268
- @let ariaDescribedBy = this.ariaDescribedBy();
1269
1208
 
1270
1209
  <mat-form-field [appearance]="effectiveAppearance()" [subscriptSizing]="effectiveSubscriptSizing()">
1271
1210
  @if (label()) {
@@ -1279,28 +1218,25 @@ class MatTextareaFieldComponent {
1279
1218
  [formField]="f"
1280
1219
  [placeholder]="(placeholder() | dynamicText | async) ?? ''"
1281
1220
  [attr.tabindex]="tabIndex()"
1282
- [attr.aria-invalid]="ariaInvalid"
1283
- [attr.aria-required]="ariaRequired"
1284
- [attr.aria-describedby]="ariaDescribedBy"
1221
+ [attr.aria-invalid]="ariaInvalid()"
1222
+ [attr.aria-required]="ariaRequired()"
1223
+ [attr.aria-describedby]="ariaDescribedBy()"
1285
1224
  [style.resize]="props()?.resize || 'vertical'"
1286
1225
  ></textarea>
1287
1226
 
1288
- @if (props()?.hint; as hint) {
1227
+ @if (errorsToDisplay()[0]; as error) {
1228
+ <mat-error [id]="errorId()">{{ error.message }}</mat-error>
1229
+ } @else if (props()?.hint; as hint) {
1289
1230
  <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
1290
1231
  }
1291
- @for (error of errorsToDisplay(); track error.kind; let i = $index) {
1292
- <mat-error [id]="errorId() + '-' + i">{{ error.message }}</mat-error>
1293
- }
1294
1232
  </mat-form-field>
1295
1233
  `, 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"] }, { 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 });
1296
1234
  }
1297
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MatTextareaFieldComponent, decorators: [{
1235
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MatTextareaFieldComponent, decorators: [{
1298
1236
  type: Component,
1299
1237
  args: [{ selector: 'df-mat-textarea', imports: [MatFormField, MatLabel, MatInput, MatHint, FormField, MatError$1, DynamicTextPipe, AsyncPipe], template: `
1300
1238
  @let f = field();
1301
1239
  @let textareaId = key() + '-textarea';
1302
- @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
1303
- @let ariaDescribedBy = this.ariaDescribedBy();
1304
1240
 
1305
1241
  <mat-form-field [appearance]="effectiveAppearance()" [subscriptSizing]="effectiveSubscriptSizing()">
1306
1242
  @if (label()) {
@@ -1314,18 +1250,17 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
1314
1250
  [formField]="f"
1315
1251
  [placeholder]="(placeholder() | dynamicText | async) ?? ''"
1316
1252
  [attr.tabindex]="tabIndex()"
1317
- [attr.aria-invalid]="ariaInvalid"
1318
- [attr.aria-required]="ariaRequired"
1319
- [attr.aria-describedby]="ariaDescribedBy"
1253
+ [attr.aria-invalid]="ariaInvalid()"
1254
+ [attr.aria-required]="ariaRequired()"
1255
+ [attr.aria-describedby]="ariaDescribedBy()"
1320
1256
  [style.resize]="props()?.resize || 'vertical'"
1321
1257
  ></textarea>
1322
1258
 
1323
- @if (props()?.hint; as hint) {
1259
+ @if (errorsToDisplay()[0]; as error) {
1260
+ <mat-error [id]="errorId()">{{ error.message }}</mat-error>
1261
+ } @else if (props()?.hint; as hint) {
1324
1262
  <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
1325
1263
  }
1326
- @for (error of errorsToDisplay(); track error.kind; let i = $index) {
1327
- <mat-error [id]="errorId() + '-' + i">{{ error.message }}</mat-error>
1328
- }
1329
1264
  </mat-form-field>
1330
1265
  `, changeDetection: ChangeDetectionStrategy.OnPush, host: {
1331
1266
  '[id]': '`${key()}`',
@@ -1336,8 +1271,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
1336
1271
  }], 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 }] }] } });
1337
1272
 
1338
1273
  var matTextarea_component = /*#__PURE__*/Object.freeze({
1339
- __proto__: null,
1340
- default: MatTextareaFieldComponent
1274
+ __proto__: null,
1275
+ default: MatTextareaFieldComponent
1341
1276
  });
1342
1277
 
1343
1278
  class MatToggleFieldComponent {
@@ -1381,22 +1316,47 @@ class MatToggleFieldComponent {
1381
1316
  return this.field()().required?.() === true ? true : null;
1382
1317
  }, ...(ngDevMode ? [{ debugName: "ariaRequired" }] : []));
1383
1318
  /** aria-describedby: links to hint and error messages for screen readers */
1384
- ariaDescribedBy = computed(() => {
1385
- const ids = [];
1386
- if (this.props()?.hint) {
1387
- ids.push(this.hintId());
1319
+ ariaDescribedBy = createAriaDescribedBySignal(this.errorsToDisplay, this.errorId, this.hintId, () => !!this.props()?.hint);
1320
+ /**
1321
+ * Workaround: Angular Material's MatSlideToggle does NOT propagate aria-required to its internal
1322
+ * button element. This effect imperatively sets/removes aria-required on the internal button.
1323
+ *
1324
+ * Uses afterRenderEffect to ensure DOM is ready before querying internal elements.
1325
+ */
1326
+ syncAriaRequiredToDom = afterRenderEffect(() => {
1327
+ const isRequired = this.ariaRequired();
1328
+ const buttonEl = this.elementRef.nativeElement.querySelector('button[role="switch"]');
1329
+ if (buttonEl) {
1330
+ if (isRequired) {
1331
+ buttonEl.setAttribute('aria-required', 'true');
1332
+ }
1333
+ else {
1334
+ buttonEl.removeAttribute('aria-required');
1335
+ }
1388
1336
  }
1389
- const errors = this.errorsToDisplay();
1390
- errors.forEach((_, i) => {
1391
- ids.push(`${this.errorId()}-${i}`);
1392
- });
1393
- return ids.join(' ');
1394
- }, ...(ngDevMode ? [{ debugName: "ariaDescribedBy" }] : []));
1395
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MatToggleFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1396
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.0.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: `
1337
+ });
1338
+ /**
1339
+ * Workaround: Angular Material's MatSlideToggle does NOT propagate aria-describedby to its internal
1340
+ * button element. This effect imperatively sets/removes aria-describedby on the internal button.
1341
+ *
1342
+ * Uses afterRenderEffect to ensure DOM is ready before querying internal elements.
1343
+ */
1344
+ syncAriaDescribedByToDom = afterRenderEffect(() => {
1345
+ const describedBy = this.ariaDescribedBy();
1346
+ const buttonEl = this.elementRef.nativeElement.querySelector('button[role="switch"]');
1347
+ if (buttonEl) {
1348
+ if (describedBy) {
1349
+ buttonEl.setAttribute('aria-describedby', describedBy);
1350
+ }
1351
+ else {
1352
+ buttonEl.removeAttribute('aria-describedby');
1353
+ }
1354
+ }
1355
+ });
1356
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MatToggleFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1357
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "21.1.0", 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: `
1397
1358
  @let f = field();
1398
1359
  @let toggleId = key() + '-toggle';
1399
- @let ariaDescribedBy = this.ariaDescribedBy();
1400
1360
 
1401
1361
  <mat-slide-toggle
1402
1362
  [id]="toggleId"
@@ -1406,27 +1366,25 @@ class MatToggleFieldComponent {
1406
1366
  [hideIcon]="props()?.hideIcon || false"
1407
1367
  [disableRipple]="effectiveDisableRipple()"
1408
1368
  [required]="!!f().required()"
1409
- [aria-describedby]="ariaDescribedBy"
1369
+ [attr.aria-describedby]="ariaDescribedBy()"
1410
1370
  [attr.tabindex]="tabIndex()"
1411
1371
  class="toggle-container"
1412
1372
  >
1413
1373
  {{ label() | dynamicText | async }}
1414
1374
  </mat-slide-toggle>
1415
1375
 
1416
- @if (props()?.hint; as hint) {
1376
+ @if (errorsToDisplay()[0]; as error) {
1377
+ <mat-error [id]="errorId()">{{ error.message }}</mat-error>
1378
+ } @else if (props()?.hint; as hint) {
1417
1379
  <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
1418
1380
  }
1419
- @for (error of errorsToDisplay(); track error.kind; let i = $index) {
1420
- <mat-error [id]="errorId() + '-' + i">{{ error.message }}</mat-error>
1421
- }
1422
1381
  `, 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"] }, { 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 });
1423
1382
  }
1424
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MatToggleFieldComponent, decorators: [{
1383
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MatToggleFieldComponent, decorators: [{
1425
1384
  type: Component,
1426
1385
  args: [{ selector: 'df-mat-toggle', imports: [MatSlideToggle, FormField, MatError, DynamicTextPipe, AsyncPipe], template: `
1427
1386
  @let f = field();
1428
1387
  @let toggleId = key() + '-toggle';
1429
- @let ariaDescribedBy = this.ariaDescribedBy();
1430
1388
 
1431
1389
  <mat-slide-toggle
1432
1390
  [id]="toggleId"
@@ -1436,19 +1394,18 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
1436
1394
  [hideIcon]="props()?.hideIcon || false"
1437
1395
  [disableRipple]="effectiveDisableRipple()"
1438
1396
  [required]="!!f().required()"
1439
- [aria-describedby]="ariaDescribedBy"
1397
+ [attr.aria-describedby]="ariaDescribedBy()"
1440
1398
  [attr.tabindex]="tabIndex()"
1441
1399
  class="toggle-container"
1442
1400
  >
1443
1401
  {{ label() | dynamicText | async }}
1444
1402
  </mat-slide-toggle>
1445
1403
 
1446
- @if (props()?.hint; as hint) {
1404
+ @if (errorsToDisplay()[0]; as error) {
1405
+ <mat-error [id]="errorId()">{{ error.message }}</mat-error>
1406
+ } @else if (props()?.hint; as hint) {
1447
1407
  <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
1448
1408
  }
1449
- @for (error of errorsToDisplay(); track error.kind; let i = $index) {
1450
- <mat-error [id]="errorId() + '-' + i">{{ error.message }}</mat-error>
1451
- }
1452
1409
  `, host: {
1453
1410
  '[class]': 'className()',
1454
1411
  '[id]': '`${key()}`',
@@ -1458,8 +1415,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
1458
1415
  }], 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 }] }] } });
1459
1416
 
1460
1417
  var matToggle_component = /*#__PURE__*/Object.freeze({
1461
- __proto__: null,
1462
- default: MatToggleFieldComponent
1418
+ __proto__: null,
1419
+ default: MatToggleFieldComponent
1463
1420
  });
1464
1421
 
1465
1422
  // Public exports for material field components and types
@@ -1495,9 +1452,9 @@ const MatField = {
1495
1452
  * @returns Signal containing Record of input names to values for ngComponentOutlet
1496
1453
  */
1497
1454
  function buttonFieldMapper(fieldDef) {
1498
- // Build base inputs (static, from field definition)
1499
- const baseInputs = buildBaseInputs(fieldDef);
1455
+ const defaultProps = inject(DEFAULT_PROPS);
1500
1456
  return computed(() => {
1457
+ const baseInputs = buildBaseInputs(fieldDef, defaultProps());
1501
1458
  const inputs = {
1502
1459
  ...baseInputs,
1503
1460
  };
@@ -1535,20 +1492,21 @@ function buttonFieldMapper(fieldDef) {
1535
1492
  * @returns Signal containing Record of input names to values for ngComponentOutlet
1536
1493
  */
1537
1494
  function submitButtonFieldMapper(fieldDef) {
1538
- // Inject field signal context to access form state and options
1539
- const fieldSignalContext = inject(FIELD_SIGNAL_CONTEXT);
1540
- // Build base inputs (static, from field definition)
1541
- const baseInputs = buildBaseInputs(fieldDef);
1542
- // Use button-logic-resolver to compute disabled state
1495
+ const rootFormRegistry = inject(RootFormRegistryService);
1496
+ const defaultProps = inject(DEFAULT_PROPS);
1497
+ const formOptions = inject(FORM_OPTIONS);
1543
1498
  const fieldWithLogic = fieldDef;
1544
- const disabledSignal = resolveSubmitButtonDisabled({
1545
- form: fieldSignalContext.form,
1546
- formOptions: fieldSignalContext.formOptions,
1547
- fieldLogic: fieldWithLogic.logic,
1548
- explicitlyDisabled: fieldDef.disabled,
1549
- });
1550
- // Return computed signal - evaluates disabledSignal inside for reactivity
1551
1499
  return computed(() => {
1500
+ const baseInputs = buildBaseInputs(fieldDef, defaultProps());
1501
+ // Use rootFormRegistry instead of fieldSignalContext.form because when the submit button
1502
+ // is inside a group/array, fieldSignalContext.form points to the nested form tree,
1503
+ // not the root form. We need root form validity for submit button disabled state (#157).
1504
+ const disabledSignal = resolveSubmitButtonDisabled({
1505
+ form: rootFormRegistry.getRootForm(),
1506
+ formOptions: formOptions(),
1507
+ fieldLogic: fieldWithLogic.logic,
1508
+ explicitlyDisabled: fieldDef.disabled,
1509
+ });
1552
1510
  const inputs = {
1553
1511
  ...baseInputs,
1554
1512
  // No event - native form submit handles it via form's onNativeSubmit
@@ -1575,21 +1533,19 @@ function submitButtonFieldMapper(fieldDef) {
1575
1533
  * @returns Signal containing Record of input names to values for ngComponentOutlet
1576
1534
  */
1577
1535
  function nextButtonFieldMapper(fieldDef) {
1578
- // Inject field signal context to access form state and options
1579
1536
  const fieldSignalContext = inject(FIELD_SIGNAL_CONTEXT);
1580
- // Build base inputs (static, from field definition)
1581
- const baseInputs = buildBaseInputs(fieldDef);
1582
- // Use button-logic-resolver to compute disabled state
1537
+ const defaultProps = inject(DEFAULT_PROPS);
1538
+ const formOptions = inject(FORM_OPTIONS);
1583
1539
  const fieldWithLogic = fieldDef;
1584
- const disabledSignal = resolveNextButtonDisabled({
1585
- form: fieldSignalContext.form,
1586
- formOptions: fieldSignalContext.formOptions,
1587
- fieldLogic: fieldWithLogic.logic,
1588
- explicitlyDisabled: fieldDef.disabled,
1589
- currentPageValid: fieldSignalContext.currentPageValid,
1590
- });
1591
- // Return computed signal - evaluates disabledSignal inside for reactivity
1592
1540
  return computed(() => {
1541
+ const baseInputs = buildBaseInputs(fieldDef, defaultProps());
1542
+ const disabledSignal = resolveNextButtonDisabled({
1543
+ form: fieldSignalContext.form,
1544
+ formOptions: formOptions(),
1545
+ fieldLogic: fieldWithLogic.logic,
1546
+ explicitlyDisabled: fieldDef.disabled,
1547
+ currentPageValid: fieldSignalContext.currentPageValid,
1548
+ });
1593
1549
  const inputs = {
1594
1550
  ...baseInputs,
1595
1551
  event: NextPageEvent,
@@ -1610,9 +1566,9 @@ function nextButtonFieldMapper(fieldDef) {
1610
1566
  * @returns Signal containing Record of input names to values for ngComponentOutlet
1611
1567
  */
1612
1568
  function previousButtonFieldMapper(fieldDef) {
1613
- // Build base inputs (static, from field definition)
1614
- const baseInputs = buildBaseInputs(fieldDef);
1569
+ const defaultProps = inject(DEFAULT_PROPS);
1615
1570
  return computed(() => {
1571
+ const baseInputs = buildBaseInputs(fieldDef, defaultProps());
1616
1572
  const inputs = {
1617
1573
  ...baseInputs,
1618
1574
  event: PreviousPageEvent,
@@ -1638,10 +1594,9 @@ function previousButtonFieldMapper(fieldDef) {
1638
1594
  * @returns Signal containing Record of input names to values for ngComponentOutlet
1639
1595
  */
1640
1596
  function addArrayItemButtonFieldMapper(fieldDef) {
1641
- // Try to get array context (available when inside an array)
1642
- // Use optional injection so it doesn't fail when outside an array
1643
1597
  const arrayContext = inject(ARRAY_CONTEXT, { optional: true });
1644
1598
  const logger = inject(DynamicFormLogger);
1599
+ const defaultProps = inject(DEFAULT_PROPS);
1645
1600
  // Determine the target array key
1646
1601
  // Priority: explicit arrayKey from fieldDef > arrayKey from context
1647
1602
  const targetArrayKey = fieldDef.arrayKey ?? arrayContext?.arrayKey;
@@ -1649,13 +1604,12 @@ function addArrayItemButtonFieldMapper(fieldDef) {
1649
1604
  logger.warn(`addArrayItem button "${fieldDef.key}" has no array context. ` +
1650
1605
  'Either place it inside an array field, or provide an explicit arrayKey property.');
1651
1606
  }
1652
- // Build base inputs (static, from field definition)
1653
- const baseInputs = buildBaseInputs(fieldDef);
1654
1607
  // Set default eventArgs for AddArrayItemEvent (arrayKey)
1655
1608
  // User can override by providing eventArgs in field definition
1656
1609
  const defaultEventArgs = ['$arrayKey'];
1657
1610
  const eventArgs = 'eventArgs' in fieldDef && fieldDef.eventArgs !== undefined ? fieldDef.eventArgs : defaultEventArgs;
1658
1611
  return computed(() => {
1612
+ const baseInputs = buildBaseInputs(fieldDef, defaultProps());
1659
1613
  // Read signal value if index is a signal (supports differential updates)
1660
1614
  const getIndex = () => {
1661
1615
  if (!arrayContext)
@@ -1694,10 +1648,9 @@ function addArrayItemButtonFieldMapper(fieldDef) {
1694
1648
  * @returns Signal containing Record of input names to values for ngComponentOutlet
1695
1649
  */
1696
1650
  function removeArrayItemButtonFieldMapper(fieldDef) {
1697
- // Try to get array context (available when inside an array)
1698
- // Use optional injection so it doesn't fail when outside an array
1699
1651
  const arrayContext = inject(ARRAY_CONTEXT, { optional: true });
1700
1652
  const logger = inject(DynamicFormLogger);
1653
+ const defaultProps = inject(DEFAULT_PROPS);
1701
1654
  // Determine the target array key
1702
1655
  // Priority: explicit arrayKey from fieldDef > arrayKey from context
1703
1656
  const targetArrayKey = fieldDef.arrayKey ?? arrayContext?.arrayKey;
@@ -1705,14 +1658,13 @@ function removeArrayItemButtonFieldMapper(fieldDef) {
1705
1658
  logger.warn(`removeArrayItem button "${fieldDef.key}" has no array context. ` +
1706
1659
  'Either place it inside an array field, or provide an explicit arrayKey property.');
1707
1660
  }
1708
- // Build base inputs (static, from field definition)
1709
- const baseInputs = buildBaseInputs(fieldDef);
1710
1661
  // Set default eventArgs for RemoveArrayItemEvent (arrayKey, index if inside array)
1711
1662
  // When outside array, only pass arrayKey (removes last by default)
1712
1663
  // User can override by providing eventArgs in field definition
1713
1664
  const defaultEventArgs = arrayContext ? ['$arrayKey', '$index'] : ['$arrayKey'];
1714
1665
  const eventArgs = 'eventArgs' in fieldDef && fieldDef.eventArgs !== undefined ? fieldDef.eventArgs : defaultEventArgs;
1715
1666
  return computed(() => {
1667
+ const baseInputs = buildBaseInputs(fieldDef, defaultProps());
1716
1668
  // Read signal value if index is a signal (supports differential updates)
1717
1669
  const getIndex = () => {
1718
1670
  if (!arrayContext)