@ng-forge/dynamic-forms-material 0.3.0 → 0.4.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
+ *
46
+ * @param errorsToDisplay Signal containing the array of errors currently being displayed
47
+ * @param errorId Signal containing the base ID for error elements
48
+ * @param hintId Signal containing the ID for the hint element
49
+ * @param hasHint Function that returns true if a hint is configured
50
+ * @returns Signal containing the aria-describedby value (space-separated IDs) or null
51
+ */
52
+ function createAriaDescribedBySignal(errorsToDisplay, errorId, hintId, hasHint) {
53
+ return computed(() => {
54
+ const errors = errorsToDisplay();
55
+ if (errors.length > 0) {
56
+ return errors.map((_, i) => `${errorId()}-${i}`).join(' ');
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,58 +126,80 @@ 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
- @let ariaDescribedBy = this.ariaDescribedBy();
150
+ @let checkboxId = key() + '-checkbox';
117
151
 
118
152
  <mat-checkbox
153
+ [id]="checkboxId"
119
154
  [formField]="f"
120
155
  [labelPosition]="props()?.labelPosition || 'after'"
121
156
  [indeterminate]="props()?.indeterminate || false"
122
157
  [color]="props()?.color || 'primary'"
123
158
  [disableRipple]="effectiveDisableRipple()"
124
159
  [required]="!!f().required()"
125
- [aria-describedby]="ariaDescribedBy"
160
+ [attr.aria-describedby]="ariaDescribedBy()"
126
161
  [attr.tabindex]="tabIndex()"
127
162
  [attr.hidden]="f().hidden() || null"
128
163
  >
129
164
  {{ label() | dynamicText | async }}
130
165
  </mat-checkbox>
131
166
 
132
- @if (props()?.hint; as hint) {
133
- <div class="mat-hint" [id]="hintId()" [attr.hidden]="f().hidden() || null">{{ hint | dynamicText | async }}</div>
134
- }
135
167
  @for (error of errorsToDisplay(); track error.kind; let i = $index) {
136
168
  <mat-error [id]="errorId() + '-' + i" [attr.hidden]="f().hidden() || null">{{ error.message }}</mat-error>
169
+ } @empty {
170
+ @if (props()?.hint; as hint) {
171
+ <div class="mat-hint" [id]="hintId()" [attr.hidden]="f().hidden() || null">{{ hint | dynamicText | async }}</div>
172
+ }
137
173
  }
138
174
  `, 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 });
139
175
  }
140
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MatCheckboxFieldComponent, decorators: [{
176
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MatCheckboxFieldComponent, decorators: [{
141
177
  type: Component,
142
178
  args: [{ selector: 'df-mat-checkbox', imports: [MatCheckbox, FormField, MatError, DynamicTextPipe, AsyncPipe], template: `
143
179
  @let f = field();
144
- @let ariaDescribedBy = this.ariaDescribedBy();
180
+ @let checkboxId = key() + '-checkbox';
145
181
 
146
182
  <mat-checkbox
183
+ [id]="checkboxId"
147
184
  [formField]="f"
148
185
  [labelPosition]="props()?.labelPosition || 'after'"
149
186
  [indeterminate]="props()?.indeterminate || false"
150
187
  [color]="props()?.color || 'primary'"
151
188
  [disableRipple]="effectiveDisableRipple()"
152
189
  [required]="!!f().required()"
153
- [aria-describedby]="ariaDescribedBy"
190
+ [attr.aria-describedby]="ariaDescribedBy()"
154
191
  [attr.tabindex]="tabIndex()"
155
192
  [attr.hidden]="f().hidden() || null"
156
193
  >
157
194
  {{ label() | dynamicText | async }}
158
195
  </mat-checkbox>
159
196
 
160
- @if (props()?.hint; as hint) {
161
- <div class="mat-hint" [id]="hintId()" [attr.hidden]="f().hidden() || null">{{ hint | dynamicText | async }}</div>
162
- }
163
197
  @for (error of errorsToDisplay(); track error.kind; let i = $index) {
164
198
  <mat-error [id]="errorId() + '-' + i" [attr.hidden]="f().hidden() || null">{{ error.message }}</mat-error>
199
+ } @empty {
200
+ @if (props()?.hint; as hint) {
201
+ <div class="mat-hint" [id]="hintId()" [attr.hidden]="f().hidden() || null">{{ hint | dynamicText | async }}</div>
202
+ }
165
203
  }
166
204
  `, host: {
167
205
  '[class]': 'className()',
@@ -172,8 +210,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
172
210
  }], 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 }] }] } });
173
211
 
174
212
  var matCheckbox_component = /*#__PURE__*/Object.freeze({
175
- __proto__: null,
176
- default: MatCheckboxFieldComponent
213
+ __proto__: null,
214
+ default: MatCheckboxFieldComponent
177
215
  });
178
216
 
179
217
  class MatDatepickerFieldComponent {
@@ -217,54 +255,45 @@ class MatDatepickerFieldComponent {
217
255
  return this.field()().required?.() === true ? true : null;
218
256
  }, ...(ngDevMode ? [{ debugName: "ariaRequired" }] : []));
219
257
  /** aria-describedby: links to hint and error messages for screen readers */
220
- ariaDescribedBy = computed(() => {
221
- const ids = [];
222
- if (this.props()?.hint) {
223
- ids.push(this.hintId());
224
- }
225
- const errors = this.errorsToDisplay();
226
- errors.forEach((_, i) => {
227
- ids.push(`${this.errorId()}-${i}`);
228
- });
229
- return ids.length > 0 ? ids.join(' ') : null;
230
- }, ...(ngDevMode ? [{ debugName: "ariaDescribedBy" }] : []));
231
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MatDatepickerFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
232
- 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()}`", "attr.data-testid": "key()", "attr.hidden": "field()().hidden() || null" } }, providers: [provideNativeDateAdapter()], ngImport: i0, template: `
258
+ ariaDescribedBy = createAriaDescribedBySignal(this.errorsToDisplay, this.errorId, this.hintId, () => !!this.props()?.hint);
259
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MatDatepickerFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
260
+ 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: `
233
261
  @let f = field();
234
- @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
235
- @let ariaDescribedBy = this.ariaDescribedBy();
262
+ @let inputId = key() + '-input';
236
263
 
237
- <mat-form-field [appearance]="effectiveAppearance()" [subscriptSizing]="effectiveSubscriptSizing()" [class]="className() || ''">
264
+ <mat-form-field [appearance]="effectiveAppearance()" [subscriptSizing]="effectiveSubscriptSizing()">
238
265
  @if (label(); as label) {
239
266
  <mat-label>{{ label | dynamicText | async }}</mat-label>
240
267
  }
241
268
 
242
269
  <input
243
270
  matInput
271
+ [id]="inputId"
244
272
  [matDatepicker]="picker"
245
273
  [formField]="f"
246
274
  [placeholder]="(placeholder() | dynamicText | async) ?? ''"
247
275
  [attr.tabindex]="tabIndex()"
248
276
  [min]="minDate()"
249
277
  [max]="maxDate()"
250
- [attr.aria-invalid]="ariaInvalid"
251
- [attr.aria-required]="ariaRequired"
252
- [attr.aria-describedby]="ariaDescribedBy"
278
+ [attr.aria-invalid]="ariaInvalid()"
279
+ [attr.aria-required]="ariaRequired()"
280
+ [attr.aria-describedby]="ariaDescribedBy()"
253
281
  />
254
282
 
255
- <mat-datepicker-toggle matIconSuffix [for]="$any(picker)" />
283
+ <mat-datepicker-toggle matIconSuffix [for]="picker" />
256
284
  <mat-datepicker #picker [startAt]="startAt()" [startView]="props()?.startView || 'month'" [touchUi]="props()?.touchUi ?? false" />
257
285
 
258
- @if (props()?.hint; as hint) {
259
- <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
260
- }
261
286
  @for (error of errorsToDisplay(); track error.kind; let i = $index) {
262
287
  <mat-error [id]="errorId() + '-' + i">{{ error.message }}</mat-error>
288
+ } @empty {
289
+ @if (props()?.hint; as hint) {
290
+ <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
291
+ }
263
292
  }
264
293
  </mat-form-field>
265
294
  `, 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 });
266
295
  }
267
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MatDatepickerFieldComponent, decorators: [{
296
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MatDatepickerFieldComponent, decorators: [{
268
297
  type: Component,
269
298
  args: [{ selector: 'df-mat-datepicker', imports: [
270
299
  MatFormField,
@@ -281,47 +310,49 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
281
310
  AsyncPipe,
282
311
  ], host: {
283
312
  '[id]': '`${key()}`',
313
+ '[class]': 'className()',
284
314
  '[attr.data-testid]': 'key()',
285
315
  '[attr.hidden]': 'field()().hidden() || null',
286
316
  }, template: `
287
317
  @let f = field();
288
- @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
289
- @let ariaDescribedBy = this.ariaDescribedBy();
318
+ @let inputId = key() + '-input';
290
319
 
291
- <mat-form-field [appearance]="effectiveAppearance()" [subscriptSizing]="effectiveSubscriptSizing()" [class]="className() || ''">
320
+ <mat-form-field [appearance]="effectiveAppearance()" [subscriptSizing]="effectiveSubscriptSizing()">
292
321
  @if (label(); as label) {
293
322
  <mat-label>{{ label | dynamicText | async }}</mat-label>
294
323
  }
295
324
 
296
325
  <input
297
326
  matInput
327
+ [id]="inputId"
298
328
  [matDatepicker]="picker"
299
329
  [formField]="f"
300
330
  [placeholder]="(placeholder() | dynamicText | async) ?? ''"
301
331
  [attr.tabindex]="tabIndex()"
302
332
  [min]="minDate()"
303
333
  [max]="maxDate()"
304
- [attr.aria-invalid]="ariaInvalid"
305
- [attr.aria-required]="ariaRequired"
306
- [attr.aria-describedby]="ariaDescribedBy"
334
+ [attr.aria-invalid]="ariaInvalid()"
335
+ [attr.aria-required]="ariaRequired()"
336
+ [attr.aria-describedby]="ariaDescribedBy()"
307
337
  />
308
338
 
309
- <mat-datepicker-toggle matIconSuffix [for]="$any(picker)" />
339
+ <mat-datepicker-toggle matIconSuffix [for]="picker" />
310
340
  <mat-datepicker #picker [startAt]="startAt()" [startView]="props()?.startView || 'month'" [touchUi]="props()?.touchUi ?? false" />
311
341
 
312
- @if (props()?.hint; as hint) {
313
- <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
314
- }
315
342
  @for (error of errorsToDisplay(); track error.kind; let i = $index) {
316
343
  <mat-error [id]="errorId() + '-' + i">{{ error.message }}</mat-error>
344
+ } @empty {
345
+ @if (props()?.hint; as hint) {
346
+ <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
347
+ }
317
348
  }
318
349
  </mat-form-field>
319
350
  `, 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"] }]
320
351
  }], 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 }] }] } });
321
352
 
322
353
  var matDatepicker_component = /*#__PURE__*/Object.freeze({
323
- __proto__: null,
324
- default: MatDatepickerFieldComponent
354
+ __proto__: null,
355
+ default: MatDatepickerFieldComponent
325
356
  });
326
357
 
327
358
  class MatInputFieldComponent {
@@ -400,22 +431,11 @@ class MatInputFieldComponent {
400
431
  return this.field()().required?.() === true ? true : null;
401
432
  }, ...(ngDevMode ? [{ debugName: "ariaRequired" }] : []));
402
433
  /** aria-describedby: links to hint and error messages for screen readers */
403
- ariaDescribedBy = computed(() => {
404
- const ids = [];
405
- if (this.props()?.hint) {
406
- ids.push(this.hintId());
407
- }
408
- const errors = this.errorsToDisplay();
409
- errors.forEach((_, i) => {
410
- ids.push(`${this.errorId()}-${i}`);
411
- });
412
- return ids.length > 0 ? ids.join(' ') : null;
413
- }, ...(ngDevMode ? [{ debugName: "ariaDescribedBy" }] : []));
414
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MatInputFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
415
- 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: `
434
+ ariaDescribedBy = createAriaDescribedBySignal(this.errorsToDisplay, this.errorId, this.hintId, () => !!this.props()?.hint);
435
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MatInputFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
436
+ 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: `
416
437
  @let f = field();
417
- @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
418
- @let ariaDescribedBy = this.ariaDescribedBy();
438
+ @let inputId = key() + '-input';
419
439
 
420
440
  <mat-form-field [appearance]="effectiveAppearance()" [subscriptSizing]="effectiveSubscriptSizing()">
421
441
  @if (label()) {
@@ -424,29 +444,30 @@ class MatInputFieldComponent {
424
444
  <input
425
445
  #inputRef
426
446
  matInput
447
+ [id]="inputId"
427
448
  [formField]="f"
428
449
  [type]="props()?.type ?? 'text'"
429
450
  [placeholder]="(placeholder() | dynamicText | async) ?? ''"
430
451
  [attr.tabindex]="tabIndex()"
431
- [attr.aria-invalid]="ariaInvalid"
432
- [attr.aria-required]="ariaRequired"
433
- [attr.aria-describedby]="ariaDescribedBy"
452
+ [attr.aria-invalid]="ariaInvalid()"
453
+ [attr.aria-required]="ariaRequired()"
454
+ [attr.aria-describedby]="ariaDescribedBy()"
434
455
  />
435
- @if (props()?.hint; as hint) {
436
- <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
437
- }
438
456
  @for (error of errorsToDisplay(); track error.kind; let i = $index) {
439
457
  <mat-error [id]="errorId() + '-' + i">{{ error.message }}</mat-error>
458
+ } @empty {
459
+ @if (props()?.hint; as hint) {
460
+ <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
461
+ }
440
462
  }
441
463
  </mat-form-field>
442
464
  `, 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 });
443
465
  }
444
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MatInputFieldComponent, decorators: [{
466
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MatInputFieldComponent, decorators: [{
445
467
  type: Component,
446
468
  args: [{ selector: 'df-mat-input', imports: [MatFormField, MatLabel, MatInput, MatHint, FormField, MatError$1, DynamicTextPipe, AsyncPipe], template: `
447
469
  @let f = field();
448
- @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
449
- @let ariaDescribedBy = this.ariaDescribedBy();
470
+ @let inputId = key() + '-input';
450
471
 
451
472
  <mat-form-field [appearance]="effectiveAppearance()" [subscriptSizing]="effectiveSubscriptSizing()">
452
473
  @if (label()) {
@@ -455,19 +476,21 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
455
476
  <input
456
477
  #inputRef
457
478
  matInput
479
+ [id]="inputId"
458
480
  [formField]="f"
459
481
  [type]="props()?.type ?? 'text'"
460
482
  [placeholder]="(placeholder() | dynamicText | async) ?? ''"
461
483
  [attr.tabindex]="tabIndex()"
462
- [attr.aria-invalid]="ariaInvalid"
463
- [attr.aria-required]="ariaRequired"
464
- [attr.aria-describedby]="ariaDescribedBy"
484
+ [attr.aria-invalid]="ariaInvalid()"
485
+ [attr.aria-required]="ariaRequired()"
486
+ [attr.aria-describedby]="ariaDescribedBy()"
465
487
  />
466
- @if (props()?.hint; as hint) {
467
- <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
468
- }
469
488
  @for (error of errorsToDisplay(); track error.kind; let i = $index) {
470
489
  <mat-error [id]="errorId() + '-' + i">{{ error.message }}</mat-error>
490
+ } @empty {
491
+ @if (props()?.hint; as hint) {
492
+ <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
493
+ }
471
494
  }
472
495
  </mat-form-field>
473
496
  `, changeDetection: ChangeDetectionStrategy.OnPush, host: {
@@ -479,8 +502,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
479
502
  }], 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 }] }] } });
480
503
 
481
504
  var matInput_component = /*#__PURE__*/Object.freeze({
482
- __proto__: null,
483
- default: MatInputFieldComponent
505
+ __proto__: null,
506
+ default: MatInputFieldComponent
484
507
  });
485
508
 
486
509
  class MatMultiCheckboxFieldComponent {
@@ -498,6 +521,14 @@ class MatMultiCheckboxFieldComponent {
498
521
  const currentValues = this.field()().value();
499
522
  return this.options().filter((option) => currentValues.includes(option.value));
500
523
  }, { ...(ngDevMode ? { debugName: "valueViewModel" } : {}), equal: isEqual });
524
+ /** Computed map of checked option values for O(1) lookup in template */
525
+ checkedValuesMap = computed(() => {
526
+ const map = {};
527
+ for (const opt of this.valueViewModel()) {
528
+ map[String(opt.value)] = true;
529
+ }
530
+ return map;
531
+ }, ...(ngDevMode ? [{ debugName: "checkedValuesMap" }] : []));
501
532
  constructor() {
502
533
  // Apply meta attributes to all checkbox inputs, re-apply when options change
503
534
  setupMetaTracking(this.elementRef, this.meta, {
@@ -551,37 +582,28 @@ class MatMultiCheckboxFieldComponent {
551
582
  return this.field()().required?.() === true ? true : null;
552
583
  }, ...(ngDevMode ? [{ debugName: "ariaRequired" }] : []));
553
584
  /** aria-describedby: links to hint and error messages for screen readers */
554
- ariaDescribedBy = computed(() => {
555
- const ids = [];
556
- if (this.props()?.hint) {
557
- ids.push(this.hintId());
558
- }
559
- const errors = this.errorsToDisplay();
560
- errors.forEach((_, i) => {
561
- ids.push(`${this.errorId()}-${i}`);
562
- });
563
- return ids.length > 0 ? ids.join(' ') : null;
564
- }, ...(ngDevMode ? [{ debugName: "ariaDescribedBy" }] : []));
565
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MatMultiCheckboxFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
566
- 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: `
585
+ ariaDescribedBy = createAriaDescribedBySignal(this.errorsToDisplay, this.errorId, this.hintId, () => !!this.props()?.hint);
586
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MatMultiCheckboxFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
587
+ 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: `
567
588
  @let f = field();
568
- @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
569
- @let ariaDescribedBy = this.ariaDescribedBy();
589
+ @let checkboxGroupId = key() + '-checkbox-group';
590
+ @let checked = checkedValuesMap();
570
591
 
571
592
  @if (label(); as label) {
572
593
  <div class="checkbox-group-label">{{ label | dynamicText | async }}</div>
573
594
  }
574
595
 
575
596
  <div
597
+ [id]="checkboxGroupId"
576
598
  class="checkbox-group"
577
599
  role="group"
578
- [attr.aria-invalid]="ariaInvalid"
579
- [attr.aria-required]="ariaRequired"
580
- [attr.aria-describedby]="ariaDescribedBy"
600
+ [attr.aria-invalid]="ariaInvalid()"
601
+ [attr.aria-required]="ariaRequired()"
602
+ [attr.aria-describedby]="ariaDescribedBy()"
581
603
  >
582
604
  @for (option of options(); track option.value) {
583
605
  <mat-checkbox
584
- [checked]="option | inArray: valueViewModel()"
606
+ [checked]="checked['' + option.value]"
585
607
  [disabled]="f().disabled() || option.disabled"
586
608
  [color]="props()?.color || 'primary'"
587
609
  [labelPosition]="props()?.labelPosition || 'after'"
@@ -592,35 +614,37 @@ class MatMultiCheckboxFieldComponent {
592
614
  }
593
615
  </div>
594
616
 
595
- @if (props()?.hint; as hint) {
596
- <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
597
- }
598
617
  @for (error of errorsToDisplay(); track error.kind; let i = $index) {
599
618
  <mat-error [id]="errorId() + '-' + i">{{ error.message }}</mat-error>
619
+ } @empty {
620
+ @if (props()?.hint; as hint) {
621
+ <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
622
+ }
600
623
  }
601
- `, 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 });
624
+ `, 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 });
602
625
  }
603
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MatMultiCheckboxFieldComponent, decorators: [{
626
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MatMultiCheckboxFieldComponent, decorators: [{
604
627
  type: Component,
605
- args: [{ selector: 'df-mat-multi-checkbox', imports: [MatCheckbox, ValueInArrayPipe, MatError, DynamicTextPipe, AsyncPipe], template: `
628
+ args: [{ selector: 'df-mat-multi-checkbox', imports: [MatCheckbox, MatError, DynamicTextPipe, AsyncPipe], template: `
606
629
  @let f = field();
607
- @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
608
- @let ariaDescribedBy = this.ariaDescribedBy();
630
+ @let checkboxGroupId = key() + '-checkbox-group';
631
+ @let checked = checkedValuesMap();
609
632
 
610
633
  @if (label(); as label) {
611
634
  <div class="checkbox-group-label">{{ label | dynamicText | async }}</div>
612
635
  }
613
636
 
614
637
  <div
638
+ [id]="checkboxGroupId"
615
639
  class="checkbox-group"
616
640
  role="group"
617
- [attr.aria-invalid]="ariaInvalid"
618
- [attr.aria-required]="ariaRequired"
619
- [attr.aria-describedby]="ariaDescribedBy"
641
+ [attr.aria-invalid]="ariaInvalid()"
642
+ [attr.aria-required]="ariaRequired()"
643
+ [attr.aria-describedby]="ariaDescribedBy()"
620
644
  >
621
645
  @for (option of options(); track option.value) {
622
646
  <mat-checkbox
623
- [checked]="option | inArray: valueViewModel()"
647
+ [checked]="checked['' + option.value]"
624
648
  [disabled]="f().disabled() || option.disabled"
625
649
  [color]="props()?.color || 'primary'"
626
650
  [labelPosition]="props()?.labelPosition || 'after'"
@@ -631,23 +655,24 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
631
655
  }
632
656
  </div>
633
657
 
634
- @if (props()?.hint; as hint) {
635
- <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
636
- }
637
658
  @for (error of errorsToDisplay(); track error.kind; let i = $index) {
638
659
  <mat-error [id]="errorId() + '-' + i">{{ error.message }}</mat-error>
660
+ } @empty {
661
+ @if (props()?.hint; as hint) {
662
+ <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
663
+ }
639
664
  }
640
665
  `, host: {
641
666
  '[class]': 'className() || ""',
642
667
  '[id]': '`${key()}`',
643
668
  '[attr.data-testid]': 'key()',
644
669
  '[attr.hidden]': 'field()().hidden() || null',
645
- }, 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"] }]
670
+ }, 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"] }]
646
671
  }], 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 }] }] } });
647
672
 
648
673
  var matMultiCheckbox_component = /*#__PURE__*/Object.freeze({
649
- __proto__: null,
650
- default: MatMultiCheckboxFieldComponent
674
+ __proto__: null,
675
+ default: MatMultiCheckboxFieldComponent
651
676
  });
652
677
 
653
678
  class MatRadioFieldComponent {
@@ -690,32 +715,22 @@ class MatRadioFieldComponent {
690
715
  return this.field()().required?.() === true ? true : null;
691
716
  }, ...(ngDevMode ? [{ debugName: "ariaRequired" }] : []));
692
717
  /** aria-describedby: links to hint and error messages for screen readers */
693
- ariaDescribedBy = computed(() => {
694
- const ids = [];
695
- if (this.props()?.hint) {
696
- ids.push(this.hintId());
697
- }
698
- const errors = this.errorsToDisplay();
699
- errors.forEach((_, i) => {
700
- ids.push(`${this.errorId()}-${i}`);
701
- });
702
- return ids.length > 0 ? ids.join(' ') : null;
703
- }, ...(ngDevMode ? [{ debugName: "ariaDescribedBy" }] : []));
704
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MatRadioFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
705
- 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: `
718
+ ariaDescribedBy = createAriaDescribedBySignal(this.errorsToDisplay, this.errorId, this.hintId, () => !!this.props()?.hint);
719
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MatRadioFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
720
+ 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: `
706
721
  @let f = field();
707
- @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
708
- @let ariaDescribedBy = this.ariaDescribedBy();
722
+ @let radioGroupId = key() + '-radio-group';
709
723
 
710
724
  @if (label()) {
711
725
  <div class="radio-label">{{ label() | dynamicText | async }}</div>
712
726
  }
713
727
 
714
728
  <mat-radio-group
729
+ [id]="radioGroupId"
715
730
  [formField]="f"
716
- [attr.aria-invalid]="ariaInvalid"
717
- [attr.aria-required]="ariaRequired"
718
- [attr.aria-describedby]="ariaDescribedBy"
731
+ [attr.aria-invalid]="ariaInvalid()"
732
+ [attr.aria-required]="ariaRequired()"
733
+ [attr.aria-describedby]="ariaDescribedBy()"
719
734
  >
720
735
  @for (option of options(); track option.value) {
721
736
  <mat-radio-button
@@ -729,30 +744,31 @@ class MatRadioFieldComponent {
729
744
  }
730
745
  </mat-radio-group>
731
746
 
732
- @if (props()?.hint; as hint) {
733
- <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
734
- }
735
747
  @for (error of errorsToDisplay(); track error.kind; let i = $index) {
736
748
  <mat-error [id]="errorId() + '-' + i">{{ error.message }}</mat-error>
749
+ } @empty {
750
+ @if (props()?.hint; as hint) {
751
+ <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
752
+ }
737
753
  }
738
754
  `, 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 });
739
755
  }
740
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MatRadioFieldComponent, decorators: [{
756
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MatRadioFieldComponent, decorators: [{
741
757
  type: Component,
742
758
  args: [{ selector: 'df-mat-radio', imports: [MatRadioGroup, MatRadioButton, FormField, MatError, DynamicTextPipe, AsyncPipe], template: `
743
759
  @let f = field();
744
- @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
745
- @let ariaDescribedBy = this.ariaDescribedBy();
760
+ @let radioGroupId = key() + '-radio-group';
746
761
 
747
762
  @if (label()) {
748
763
  <div class="radio-label">{{ label() | dynamicText | async }}</div>
749
764
  }
750
765
 
751
766
  <mat-radio-group
767
+ [id]="radioGroupId"
752
768
  [formField]="f"
753
- [attr.aria-invalid]="ariaInvalid"
754
- [attr.aria-required]="ariaRequired"
755
- [attr.aria-describedby]="ariaDescribedBy"
769
+ [attr.aria-invalid]="ariaInvalid()"
770
+ [attr.aria-required]="ariaRequired()"
771
+ [attr.aria-describedby]="ariaDescribedBy()"
756
772
  >
757
773
  @for (option of options(); track option.value) {
758
774
  <mat-radio-button
@@ -766,11 +782,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
766
782
  }
767
783
  </mat-radio-group>
768
784
 
769
- @if (props()?.hint; as hint) {
770
- <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
771
- }
772
785
  @for (error of errorsToDisplay(); track error.kind; let i = $index) {
773
786
  <mat-error [id]="errorId() + '-' + i">{{ error.message }}</mat-error>
787
+ } @empty {
788
+ @if (props()?.hint; as hint) {
789
+ <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
790
+ }
774
791
  }
775
792
  `, host: {
776
793
  '[class]': 'className() || ""',
@@ -781,8 +798,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
781
798
  }], 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 }] }] } });
782
799
 
783
800
  var matRadio_component = /*#__PURE__*/Object.freeze({
784
- __proto__: null,
785
- default: MatRadioFieldComponent
801
+ __proto__: null,
802
+ default: MatRadioFieldComponent
786
803
  });
787
804
 
788
805
  class MatSelectFieldComponent {
@@ -828,22 +845,11 @@ class MatSelectFieldComponent {
828
845
  return this.field()().required?.() === true ? true : null;
829
846
  }, ...(ngDevMode ? [{ debugName: "ariaRequired" }] : []));
830
847
  /** aria-describedby: links to hint and error messages for screen readers */
831
- ariaDescribedBy = computed(() => {
832
- const ids = [];
833
- if (this.props()?.hint) {
834
- ids.push(this.hintId());
835
- }
836
- const errors = this.errorsToDisplay();
837
- errors.forEach((_, i) => {
838
- ids.push(`${this.errorId()}-${i}`);
839
- });
840
- return ids.length > 0 ? ids.join(' ') : null;
841
- }, ...(ngDevMode ? [{ debugName: "ariaDescribedBy" }] : []));
842
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MatSelectFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
843
- 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: `
848
+ ariaDescribedBy = createAriaDescribedBySignal(this.errorsToDisplay, this.errorId, this.hintId, () => !!this.props()?.hint);
849
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MatSelectFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
850
+ 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: `
844
851
  @let f = field();
845
- @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
846
- @let ariaDescribedBy = this.ariaDescribedBy();
852
+ @let selectId = key() + '-select';
847
853
 
848
854
  <mat-form-field [appearance]="effectiveAppearance()" [subscriptSizing]="effectiveSubscriptSizing()">
849
855
  @if (label(); as label) {
@@ -851,13 +857,14 @@ class MatSelectFieldComponent {
851
857
  }
852
858
 
853
859
  <mat-select
860
+ [id]="selectId"
854
861
  [formField]="f"
855
862
  [placeholder]="(placeholder() | dynamicText | async) ?? ''"
856
863
  [multiple]="props()?.multiple || false"
857
864
  [compareWith]="props()?.compareWith || defaultCompare"
858
- [attr.aria-invalid]="ariaInvalid"
859
- [attr.aria-required]="ariaRequired"
860
- [attr.aria-describedby]="ariaDescribedBy"
865
+ [attr.aria-invalid]="ariaInvalid()"
866
+ [attr.aria-required]="ariaRequired()"
867
+ [attr.aria-describedby]="ariaDescribedBy()"
861
868
  >
862
869
  @for (option of options(); track option.value) {
863
870
  <mat-option [value]="option.value" [disabled]="option.disabled || false">
@@ -866,21 +873,21 @@ class MatSelectFieldComponent {
866
873
  }
867
874
  </mat-select>
868
875
 
869
- @if (props()?.hint; as hint) {
870
- <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
871
- }
872
876
  @for (error of errorsToDisplay(); track error.kind; let i = $index) {
873
877
  <mat-error [id]="errorId() + '-' + i">{{ error.message }}</mat-error>
878
+ } @empty {
879
+ @if (props()?.hint; as hint) {
880
+ <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
881
+ }
874
882
  }
875
883
  </mat-form-field>
876
884
  `, 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 });
877
885
  }
878
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MatSelectFieldComponent, decorators: [{
886
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MatSelectFieldComponent, decorators: [{
879
887
  type: Component,
880
888
  args: [{ selector: 'df-mat-select', imports: [MatFormField, MatLabel, MatSelect, MatOption, MatHint, FormField, MatError$1, DynamicTextPipe, AsyncPipe], template: `
881
889
  @let f = field();
882
- @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
883
- @let ariaDescribedBy = this.ariaDescribedBy();
890
+ @let selectId = key() + '-select';
884
891
 
885
892
  <mat-form-field [appearance]="effectiveAppearance()" [subscriptSizing]="effectiveSubscriptSizing()">
886
893
  @if (label(); as label) {
@@ -888,13 +895,14 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
888
895
  }
889
896
 
890
897
  <mat-select
898
+ [id]="selectId"
891
899
  [formField]="f"
892
900
  [placeholder]="(placeholder() | dynamicText | async) ?? ''"
893
901
  [multiple]="props()?.multiple || false"
894
902
  [compareWith]="props()?.compareWith || defaultCompare"
895
- [attr.aria-invalid]="ariaInvalid"
896
- [attr.aria-required]="ariaRequired"
897
- [attr.aria-describedby]="ariaDescribedBy"
903
+ [attr.aria-invalid]="ariaInvalid()"
904
+ [attr.aria-required]="ariaRequired()"
905
+ [attr.aria-describedby]="ariaDescribedBy()"
898
906
  >
899
907
  @for (option of options(); track option.value) {
900
908
  <mat-option [value]="option.value" [disabled]="option.disabled || false">
@@ -903,11 +911,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
903
911
  }
904
912
  </mat-select>
905
913
 
906
- @if (props()?.hint; as hint) {
907
- <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
908
- }
909
914
  @for (error of errorsToDisplay(); track error.kind; let i = $index) {
910
915
  <mat-error [id]="errorId() + '-' + i">{{ error.message }}</mat-error>
916
+ } @empty {
917
+ @if (props()?.hint; as hint) {
918
+ <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
919
+ }
911
920
  }
912
921
  </mat-form-field>
913
922
  `, changeDetection: ChangeDetectionStrategy.OnPush, host: {
@@ -919,8 +928,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
919
928
  }], 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 }] }] } });
920
929
 
921
930
  var matSelect_component = /*#__PURE__*/Object.freeze({
922
- __proto__: null,
923
- default: MatSelectFieldComponent
931
+ __proto__: null,
932
+ default: MatSelectFieldComponent
924
933
  });
925
934
 
926
935
  class MatSliderFieldComponent {
@@ -954,22 +963,11 @@ class MatSliderFieldComponent {
954
963
  return fieldState.invalid() && fieldState.touched();
955
964
  }, ...(ngDevMode ? [{ debugName: "ariaInvalid" }] : []));
956
965
  /** aria-describedby: links to hint and error messages for screen readers */
957
- ariaDescribedBy = computed(() => {
958
- const ids = [];
959
- if (this.props()?.hint) {
960
- ids.push(this.hintId());
961
- }
962
- const errors = this.errorsToDisplay();
963
- errors.forEach((_, i) => {
964
- ids.push(`${this.errorId()}-${i}`);
965
- });
966
- return ids.length > 0 ? ids.join(' ') : null;
967
- }, ...(ngDevMode ? [{ debugName: "ariaDescribedBy" }] : []));
968
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MatSliderFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
969
- 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: `
966
+ ariaDescribedBy = createAriaDescribedBySignal(this.errorsToDisplay, this.errorId, this.hintId, () => !!this.props()?.hint);
967
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MatSliderFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
968
+ 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: `
970
969
  @let f = field();
971
- @let ariaInvalid = this.ariaInvalid();
972
- @let ariaDescribedBy = this.ariaDescribedBy();
970
+ @let inputId = key() + '-input';
973
971
 
974
972
  @if (label(); as label) {
975
973
  <div class="slider-label">{{ label | dynamicText | async }}</div>
@@ -986,27 +984,28 @@ class MatSliderFieldComponent {
986
984
  >
987
985
  <input
988
986
  matSliderThumb
987
+ [id]="inputId"
989
988
  [formField]="f"
990
989
  [attr.tabindex]="tabIndex()"
991
- [attr.aria-invalid]="ariaInvalid"
992
- [attr.aria-describedby]="ariaDescribedBy"
990
+ [attr.aria-invalid]="ariaInvalid()"
991
+ [attr.aria-describedby]="ariaDescribedBy()"
993
992
  />
994
993
  </mat-slider>
995
994
 
996
- @if (props()?.hint; as hint) {
997
- <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
998
- }
999
995
  @for (error of errorsToDisplay(); track error.kind; let i = $index) {
1000
996
  <mat-error [id]="errorId() + '-' + i">{{ error.message }}</mat-error>
997
+ } @empty {
998
+ @if (props()?.hint; as hint) {
999
+ <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
1000
+ }
1001
1001
  }
1002
1002
  `, 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 });
1003
1003
  }
1004
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MatSliderFieldComponent, decorators: [{
1004
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MatSliderFieldComponent, decorators: [{
1005
1005
  type: Component,
1006
1006
  args: [{ selector: 'df-mat-slider', imports: [MatSlider, MatSliderThumb, MatError, DynamicTextPipe, AsyncPipe, FormField], template: `
1007
1007
  @let f = field();
1008
- @let ariaInvalid = this.ariaInvalid();
1009
- @let ariaDescribedBy = this.ariaDescribedBy();
1008
+ @let inputId = key() + '-input';
1010
1009
 
1011
1010
  @if (label(); as label) {
1012
1011
  <div class="slider-label">{{ label | dynamicText | async }}</div>
@@ -1023,18 +1022,20 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
1023
1022
  >
1024
1023
  <input
1025
1024
  matSliderThumb
1025
+ [id]="inputId"
1026
1026
  [formField]="f"
1027
1027
  [attr.tabindex]="tabIndex()"
1028
- [attr.aria-invalid]="ariaInvalid"
1029
- [attr.aria-describedby]="ariaDescribedBy"
1028
+ [attr.aria-invalid]="ariaInvalid()"
1029
+ [attr.aria-describedby]="ariaDescribedBy()"
1030
1030
  />
1031
1031
  </mat-slider>
1032
1032
 
1033
- @if (props()?.hint; as hint) {
1034
- <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
1035
- }
1036
1033
  @for (error of errorsToDisplay(); track error.kind; let i = $index) {
1037
1034
  <mat-error [id]="errorId() + '-' + i">{{ error.message }}</mat-error>
1035
+ } @empty {
1036
+ @if (props()?.hint; as hint) {
1037
+ <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
1038
+ }
1038
1039
  }
1039
1040
  `, host: {
1040
1041
  '[class]': 'className()',
@@ -1045,8 +1046,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
1045
1046
  }], 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 }] }] } });
1046
1047
 
1047
1048
  var matSlider_component = /*#__PURE__*/Object.freeze({
1048
- __proto__: null,
1049
- default: MatSliderFieldComponent
1049
+ __proto__: null,
1050
+ default: MatSliderFieldComponent
1050
1051
  });
1051
1052
 
1052
1053
  class MatButtonFieldComponent {
@@ -1105,10 +1106,12 @@ class MatButtonFieldComponent {
1105
1106
  this.eventBus.dispatch(event);
1106
1107
  }
1107
1108
  }
1108
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MatButtonFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1109
- 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: `
1109
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MatButtonFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1110
+ 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: `
1111
+ @let buttonId = key() + '-button';
1110
1112
  <button
1111
1113
  mat-raised-button
1114
+ [id]="buttonId"
1112
1115
  [type]="buttonType()"
1113
1116
  [color]="props()?.color || 'primary'"
1114
1117
  [disabled]="disabled()"
@@ -1119,15 +1122,18 @@ class MatButtonFieldComponent {
1119
1122
  </button>
1120
1123
  `, 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 });
1121
1124
  }
1122
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MatButtonFieldComponent, decorators: [{
1125
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MatButtonFieldComponent, decorators: [{
1123
1126
  type: Component,
1124
1127
  args: [{ selector: 'df-mat-button', imports: [MatButton, DynamicTextPipe, AsyncPipe], host: {
1125
1128
  '[id]': '`${key()}`',
1126
1129
  '[attr.data-testid]': 'key()',
1127
1130
  '[class]': 'className()',
1131
+ '[attr.hidden]': 'hidden() || null',
1128
1132
  }, template: `
1133
+ @let buttonId = key() + '-button';
1129
1134
  <button
1130
1135
  mat-raised-button
1136
+ [id]="buttonId"
1131
1137
  [type]="buttonType()"
1132
1138
  [color]="props()?.color || 'primary'"
1133
1139
  [disabled]="disabled()"
@@ -1140,8 +1146,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
1140
1146
  }], 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 }] }] } });
1141
1147
 
1142
1148
  var matButton_component = /*#__PURE__*/Object.freeze({
1143
- __proto__: null,
1144
- default: MatButtonFieldComponent
1149
+ __proto__: null,
1150
+ default: MatButtonFieldComponent
1145
1151
  });
1146
1152
 
1147
1153
  // Public API - component
@@ -1182,17 +1188,23 @@ class MatTextareaFieldComponent {
1182
1188
  * Note: We cannot use [readonly] or [attr.readonly] bindings because Angular throws
1183
1189
  * NG8022: "Binding to '[readonly]' is not allowed on nodes using the '[field]' directive"
1184
1190
  *
1191
+ * Uses afterRenderEffect to ensure DOM is ready before manipulating attributes.
1192
+ *
1185
1193
  * @see https://github.com/angular/angular/issues/65897
1186
1194
  */
1187
- syncReadonlyToDom = explicitEffect([this.textareaRef, this.isReadonly], ([textareaRef, isReadonly]) => {
1188
- if (textareaRef?.nativeElement) {
1189
- if (isReadonly) {
1190
- textareaRef.nativeElement.setAttribute('readonly', '');
1191
- }
1192
- else {
1193
- textareaRef.nativeElement.removeAttribute('readonly');
1195
+ syncReadonlyToDom = afterRenderEffect({
1196
+ write: () => {
1197
+ const textareaRef = this.textareaRef();
1198
+ const isReadonly = this.isReadonly();
1199
+ if (textareaRef?.nativeElement) {
1200
+ if (isReadonly) {
1201
+ textareaRef.nativeElement.setAttribute('readonly', '');
1202
+ }
1203
+ else {
1204
+ textareaRef.nativeElement.removeAttribute('readonly');
1205
+ }
1194
1206
  }
1195
- }
1207
+ },
1196
1208
  });
1197
1209
  effectiveAppearance = computed(() => this.props()?.appearance ?? this.materialConfig?.appearance ?? 'outline', ...(ngDevMode ? [{ debugName: "effectiveAppearance" }] : []));
1198
1210
  effectiveSubscriptSizing = computed(() => this.props()?.subscriptSizing ?? this.materialConfig?.subscriptSizing ?? 'dynamic', ...(ngDevMode ? [{ debugName: "effectiveSubscriptSizing" }] : []));
@@ -1216,22 +1228,11 @@ class MatTextareaFieldComponent {
1216
1228
  return this.field()().required?.() === true ? true : null;
1217
1229
  }, ...(ngDevMode ? [{ debugName: "ariaRequired" }] : []));
1218
1230
  /** aria-describedby: links to hint and error messages for screen readers */
1219
- ariaDescribedBy = computed(() => {
1220
- const ids = [];
1221
- if (this.props()?.hint) {
1222
- ids.push(this.hintId());
1223
- }
1224
- const errors = this.errorsToDisplay();
1225
- errors.forEach((_, i) => {
1226
- ids.push(`${this.errorId()}-${i}`);
1227
- });
1228
- return ids.length > 0 ? ids.join(' ') : null;
1229
- }, ...(ngDevMode ? [{ debugName: "ariaDescribedBy" }] : []));
1230
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MatTextareaFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1231
- 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: `
1231
+ ariaDescribedBy = createAriaDescribedBySignal(this.errorsToDisplay, this.errorId, this.hintId, () => !!this.props()?.hint);
1232
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MatTextareaFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1233
+ 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: `
1232
1234
  @let f = field();
1233
- @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
1234
- @let ariaDescribedBy = this.ariaDescribedBy();
1235
+ @let textareaId = key() + '-textarea';
1235
1236
 
1236
1237
  <mat-form-field [appearance]="effectiveAppearance()" [subscriptSizing]="effectiveSubscriptSizing()">
1237
1238
  @if (label()) {
@@ -1241,30 +1242,31 @@ class MatTextareaFieldComponent {
1241
1242
  <textarea
1242
1243
  #textareaRef
1243
1244
  matInput
1245
+ [id]="textareaId"
1244
1246
  [formField]="f"
1245
1247
  [placeholder]="(placeholder() | dynamicText | async) ?? ''"
1246
1248
  [attr.tabindex]="tabIndex()"
1247
- [attr.aria-invalid]="ariaInvalid"
1248
- [attr.aria-required]="ariaRequired"
1249
- [attr.aria-describedby]="ariaDescribedBy"
1249
+ [attr.aria-invalid]="ariaInvalid()"
1250
+ [attr.aria-required]="ariaRequired()"
1251
+ [attr.aria-describedby]="ariaDescribedBy()"
1250
1252
  [style.resize]="props()?.resize || 'vertical'"
1251
1253
  ></textarea>
1252
1254
 
1253
- @if (props()?.hint; as hint) {
1254
- <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
1255
- }
1256
1255
  @for (error of errorsToDisplay(); track error.kind; let i = $index) {
1257
1256
  <mat-error [id]="errorId() + '-' + i">{{ error.message }}</mat-error>
1257
+ } @empty {
1258
+ @if (props()?.hint; as hint) {
1259
+ <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
1260
+ }
1258
1261
  }
1259
1262
  </mat-form-field>
1260
1263
  `, 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 });
1261
1264
  }
1262
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MatTextareaFieldComponent, decorators: [{
1265
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MatTextareaFieldComponent, decorators: [{
1263
1266
  type: Component,
1264
1267
  args: [{ selector: 'df-mat-textarea', imports: [MatFormField, MatLabel, MatInput, MatHint, FormField, MatError$1, DynamicTextPipe, AsyncPipe], template: `
1265
1268
  @let f = field();
1266
- @let ariaInvalid = this.ariaInvalid(); @let ariaRequired = this.ariaRequired();
1267
- @let ariaDescribedBy = this.ariaDescribedBy();
1269
+ @let textareaId = key() + '-textarea';
1268
1270
 
1269
1271
  <mat-form-field [appearance]="effectiveAppearance()" [subscriptSizing]="effectiveSubscriptSizing()">
1270
1272
  @if (label()) {
@@ -1274,20 +1276,22 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
1274
1276
  <textarea
1275
1277
  #textareaRef
1276
1278
  matInput
1279
+ [id]="textareaId"
1277
1280
  [formField]="f"
1278
1281
  [placeholder]="(placeholder() | dynamicText | async) ?? ''"
1279
1282
  [attr.tabindex]="tabIndex()"
1280
- [attr.aria-invalid]="ariaInvalid"
1281
- [attr.aria-required]="ariaRequired"
1282
- [attr.aria-describedby]="ariaDescribedBy"
1283
+ [attr.aria-invalid]="ariaInvalid()"
1284
+ [attr.aria-required]="ariaRequired()"
1285
+ [attr.aria-describedby]="ariaDescribedBy()"
1283
1286
  [style.resize]="props()?.resize || 'vertical'"
1284
1287
  ></textarea>
1285
1288
 
1286
- @if (props()?.hint; as hint) {
1287
- <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
1288
- }
1289
1289
  @for (error of errorsToDisplay(); track error.kind; let i = $index) {
1290
1290
  <mat-error [id]="errorId() + '-' + i">{{ error.message }}</mat-error>
1291
+ } @empty {
1292
+ @if (props()?.hint; as hint) {
1293
+ <mat-hint [id]="hintId()">{{ hint | dynamicText | async }}</mat-hint>
1294
+ }
1291
1295
  }
1292
1296
  </mat-form-field>
1293
1297
  `, changeDetection: ChangeDetectionStrategy.OnPush, host: {
@@ -1299,8 +1303,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
1299
1303
  }], 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 }] }] } });
1300
1304
 
1301
1305
  var matTextarea_component = /*#__PURE__*/Object.freeze({
1302
- __proto__: null,
1303
- default: MatTextareaFieldComponent
1306
+ __proto__: null,
1307
+ default: MatTextareaFieldComponent
1304
1308
  });
1305
1309
 
1306
1310
  class MatToggleFieldComponent {
@@ -1344,69 +1348,99 @@ class MatToggleFieldComponent {
1344
1348
  return this.field()().required?.() === true ? true : null;
1345
1349
  }, ...(ngDevMode ? [{ debugName: "ariaRequired" }] : []));
1346
1350
  /** aria-describedby: links to hint and error messages for screen readers */
1347
- ariaDescribedBy = computed(() => {
1348
- const ids = [];
1349
- if (this.props()?.hint) {
1350
- ids.push(this.hintId());
1351
+ ariaDescribedBy = createAriaDescribedBySignal(this.errorsToDisplay, this.errorId, this.hintId, () => !!this.props()?.hint);
1352
+ /**
1353
+ * Workaround: Angular Material's MatSlideToggle does NOT propagate aria-required to its internal
1354
+ * button element. This effect imperatively sets/removes aria-required on the internal button.
1355
+ *
1356
+ * Uses afterRenderEffect to ensure DOM is ready before querying internal elements.
1357
+ */
1358
+ syncAriaRequiredToDom = afterRenderEffect(() => {
1359
+ const isRequired = this.ariaRequired();
1360
+ const buttonEl = this.elementRef.nativeElement.querySelector('button[role="switch"]');
1361
+ if (buttonEl) {
1362
+ if (isRequired) {
1363
+ buttonEl.setAttribute('aria-required', 'true');
1364
+ }
1365
+ else {
1366
+ buttonEl.removeAttribute('aria-required');
1367
+ }
1351
1368
  }
1352
- const errors = this.errorsToDisplay();
1353
- errors.forEach((_, i) => {
1354
- ids.push(`${this.errorId()}-${i}`);
1355
- });
1356
- return ids.join(' ');
1357
- }, ...(ngDevMode ? [{ debugName: "ariaDescribedBy" }] : []));
1358
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MatToggleFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1359
- 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: `
1369
+ });
1370
+ /**
1371
+ * Workaround: Angular Material's MatSlideToggle does NOT propagate aria-describedby to its internal
1372
+ * button element. This effect imperatively sets/removes aria-describedby on the internal button.
1373
+ *
1374
+ * Uses afterRenderEffect to ensure DOM is ready before querying internal elements.
1375
+ */
1376
+ syncAriaDescribedByToDom = afterRenderEffect(() => {
1377
+ const describedBy = this.ariaDescribedBy();
1378
+ const buttonEl = this.elementRef.nativeElement.querySelector('button[role="switch"]');
1379
+ if (buttonEl) {
1380
+ if (describedBy) {
1381
+ buttonEl.setAttribute('aria-describedby', describedBy);
1382
+ }
1383
+ else {
1384
+ buttonEl.removeAttribute('aria-describedby');
1385
+ }
1386
+ }
1387
+ });
1388
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MatToggleFieldComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
1389
+ 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: `
1360
1390
  @let f = field();
1361
- @let ariaDescribedBy = this.ariaDescribedBy();
1391
+ @let toggleId = key() + '-toggle';
1362
1392
 
1363
1393
  <mat-slide-toggle
1394
+ [id]="toggleId"
1364
1395
  [formField]="f"
1365
1396
  [color]="props()?.color || 'primary'"
1366
1397
  [labelPosition]="props()?.labelPosition || 'after'"
1367
1398
  [hideIcon]="props()?.hideIcon || false"
1368
1399
  [disableRipple]="effectiveDisableRipple()"
1369
1400
  [required]="!!f().required()"
1370
- [aria-describedby]="ariaDescribedBy"
1401
+ [attr.aria-describedby]="ariaDescribedBy()"
1371
1402
  [attr.tabindex]="tabIndex()"
1372
1403
  class="toggle-container"
1373
1404
  >
1374
1405
  {{ label() | dynamicText | async }}
1375
1406
  </mat-slide-toggle>
1376
1407
 
1377
- @if (props()?.hint; as hint) {
1378
- <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
1379
- }
1380
1408
  @for (error of errorsToDisplay(); track error.kind; let i = $index) {
1381
1409
  <mat-error [id]="errorId() + '-' + i">{{ error.message }}</mat-error>
1410
+ } @empty {
1411
+ @if (props()?.hint; as hint) {
1412
+ <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
1413
+ }
1382
1414
  }
1383
1415
  `, 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 });
1384
1416
  }
1385
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImport: i0, type: MatToggleFieldComponent, decorators: [{
1417
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.1.0", ngImport: i0, type: MatToggleFieldComponent, decorators: [{
1386
1418
  type: Component,
1387
1419
  args: [{ selector: 'df-mat-toggle', imports: [MatSlideToggle, FormField, MatError, DynamicTextPipe, AsyncPipe], template: `
1388
1420
  @let f = field();
1389
- @let ariaDescribedBy = this.ariaDescribedBy();
1421
+ @let toggleId = key() + '-toggle';
1390
1422
 
1391
1423
  <mat-slide-toggle
1424
+ [id]="toggleId"
1392
1425
  [formField]="f"
1393
1426
  [color]="props()?.color || 'primary'"
1394
1427
  [labelPosition]="props()?.labelPosition || 'after'"
1395
1428
  [hideIcon]="props()?.hideIcon || false"
1396
1429
  [disableRipple]="effectiveDisableRipple()"
1397
1430
  [required]="!!f().required()"
1398
- [aria-describedby]="ariaDescribedBy"
1431
+ [attr.aria-describedby]="ariaDescribedBy()"
1399
1432
  [attr.tabindex]="tabIndex()"
1400
1433
  class="toggle-container"
1401
1434
  >
1402
1435
  {{ label() | dynamicText | async }}
1403
1436
  </mat-slide-toggle>
1404
1437
 
1405
- @if (props()?.hint; as hint) {
1406
- <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
1407
- }
1408
1438
  @for (error of errorsToDisplay(); track error.kind; let i = $index) {
1409
1439
  <mat-error [id]="errorId() + '-' + i">{{ error.message }}</mat-error>
1440
+ } @empty {
1441
+ @if (props()?.hint; as hint) {
1442
+ <div class="mat-hint" [id]="hintId()">{{ hint | dynamicText | async }}</div>
1443
+ }
1410
1444
  }
1411
1445
  `, host: {
1412
1446
  '[class]': 'className()',
@@ -1417,8 +1451,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "21.0.8", ngImpor
1417
1451
  }], 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 }] }] } });
1418
1452
 
1419
1453
  var matToggle_component = /*#__PURE__*/Object.freeze({
1420
- __proto__: null,
1421
- default: MatToggleFieldComponent
1454
+ __proto__: null,
1455
+ default: MatToggleFieldComponent
1422
1456
  });
1423
1457
 
1424
1458
  // Public exports for material field components and types
@@ -1454,9 +1488,9 @@ const MatField = {
1454
1488
  * @returns Signal containing Record of input names to values for ngComponentOutlet
1455
1489
  */
1456
1490
  function buttonFieldMapper(fieldDef) {
1457
- // Build base inputs (static, from field definition)
1458
- const baseInputs = buildBaseInputs(fieldDef);
1491
+ const defaultProps = inject(DEFAULT_PROPS);
1459
1492
  return computed(() => {
1493
+ const baseInputs = buildBaseInputs(fieldDef, defaultProps());
1460
1494
  const inputs = {
1461
1495
  ...baseInputs,
1462
1496
  };
@@ -1494,20 +1528,21 @@ function buttonFieldMapper(fieldDef) {
1494
1528
  * @returns Signal containing Record of input names to values for ngComponentOutlet
1495
1529
  */
1496
1530
  function submitButtonFieldMapper(fieldDef) {
1497
- // Inject field signal context to access form state and options
1498
- const fieldSignalContext = inject(FIELD_SIGNAL_CONTEXT);
1499
- // Build base inputs (static, from field definition)
1500
- const baseInputs = buildBaseInputs(fieldDef);
1501
- // Use button-logic-resolver to compute disabled state
1531
+ const rootFormRegistry = inject(RootFormRegistryService);
1532
+ const defaultProps = inject(DEFAULT_PROPS);
1533
+ const formOptions = inject(FORM_OPTIONS);
1502
1534
  const fieldWithLogic = fieldDef;
1503
- const disabledSignal = resolveSubmitButtonDisabled({
1504
- form: fieldSignalContext.form,
1505
- formOptions: fieldSignalContext.formOptions,
1506
- fieldLogic: fieldWithLogic.logic,
1507
- explicitlyDisabled: fieldDef.disabled,
1508
- });
1509
- // Return computed signal - evaluates disabledSignal inside for reactivity
1510
1535
  return computed(() => {
1536
+ const baseInputs = buildBaseInputs(fieldDef, defaultProps());
1537
+ // Use rootFormRegistry instead of fieldSignalContext.form because when the submit button
1538
+ // is inside a group/array, fieldSignalContext.form points to the nested form tree,
1539
+ // not the root form. We need root form validity for submit button disabled state (#157).
1540
+ const disabledSignal = resolveSubmitButtonDisabled({
1541
+ form: rootFormRegistry.getRootForm(),
1542
+ formOptions: formOptions(),
1543
+ fieldLogic: fieldWithLogic.logic,
1544
+ explicitlyDisabled: fieldDef.disabled,
1545
+ });
1511
1546
  const inputs = {
1512
1547
  ...baseInputs,
1513
1548
  // No event - native form submit handles it via form's onNativeSubmit
@@ -1534,21 +1569,19 @@ function submitButtonFieldMapper(fieldDef) {
1534
1569
  * @returns Signal containing Record of input names to values for ngComponentOutlet
1535
1570
  */
1536
1571
  function nextButtonFieldMapper(fieldDef) {
1537
- // Inject field signal context to access form state and options
1538
1572
  const fieldSignalContext = inject(FIELD_SIGNAL_CONTEXT);
1539
- // Build base inputs (static, from field definition)
1540
- const baseInputs = buildBaseInputs(fieldDef);
1541
- // Use button-logic-resolver to compute disabled state
1573
+ const defaultProps = inject(DEFAULT_PROPS);
1574
+ const formOptions = inject(FORM_OPTIONS);
1542
1575
  const fieldWithLogic = fieldDef;
1543
- const disabledSignal = resolveNextButtonDisabled({
1544
- form: fieldSignalContext.form,
1545
- formOptions: fieldSignalContext.formOptions,
1546
- fieldLogic: fieldWithLogic.logic,
1547
- explicitlyDisabled: fieldDef.disabled,
1548
- currentPageValid: fieldSignalContext.currentPageValid,
1549
- });
1550
- // Return computed signal - evaluates disabledSignal inside for reactivity
1551
1576
  return computed(() => {
1577
+ const baseInputs = buildBaseInputs(fieldDef, defaultProps());
1578
+ const disabledSignal = resolveNextButtonDisabled({
1579
+ form: fieldSignalContext.form,
1580
+ formOptions: formOptions(),
1581
+ fieldLogic: fieldWithLogic.logic,
1582
+ explicitlyDisabled: fieldDef.disabled,
1583
+ currentPageValid: fieldSignalContext.currentPageValid,
1584
+ });
1552
1585
  const inputs = {
1553
1586
  ...baseInputs,
1554
1587
  event: NextPageEvent,
@@ -1569,9 +1602,9 @@ function nextButtonFieldMapper(fieldDef) {
1569
1602
  * @returns Signal containing Record of input names to values for ngComponentOutlet
1570
1603
  */
1571
1604
  function previousButtonFieldMapper(fieldDef) {
1572
- // Build base inputs (static, from field definition)
1573
- const baseInputs = buildBaseInputs(fieldDef);
1605
+ const defaultProps = inject(DEFAULT_PROPS);
1574
1606
  return computed(() => {
1607
+ const baseInputs = buildBaseInputs(fieldDef, defaultProps());
1575
1608
  const inputs = {
1576
1609
  ...baseInputs,
1577
1610
  event: PreviousPageEvent,
@@ -1597,10 +1630,9 @@ function previousButtonFieldMapper(fieldDef) {
1597
1630
  * @returns Signal containing Record of input names to values for ngComponentOutlet
1598
1631
  */
1599
1632
  function addArrayItemButtonFieldMapper(fieldDef) {
1600
- // Try to get array context (available when inside an array)
1601
- // Use optional injection so it doesn't fail when outside an array
1602
1633
  const arrayContext = inject(ARRAY_CONTEXT, { optional: true });
1603
1634
  const logger = inject(DynamicFormLogger);
1635
+ const defaultProps = inject(DEFAULT_PROPS);
1604
1636
  // Determine the target array key
1605
1637
  // Priority: explicit arrayKey from fieldDef > arrayKey from context
1606
1638
  const targetArrayKey = fieldDef.arrayKey ?? arrayContext?.arrayKey;
@@ -1608,13 +1640,12 @@ function addArrayItemButtonFieldMapper(fieldDef) {
1608
1640
  logger.warn(`addArrayItem button "${fieldDef.key}" has no array context. ` +
1609
1641
  'Either place it inside an array field, or provide an explicit arrayKey property.');
1610
1642
  }
1611
- // Build base inputs (static, from field definition)
1612
- const baseInputs = buildBaseInputs(fieldDef);
1613
1643
  // Set default eventArgs for AddArrayItemEvent (arrayKey)
1614
1644
  // User can override by providing eventArgs in field definition
1615
1645
  const defaultEventArgs = ['$arrayKey'];
1616
1646
  const eventArgs = 'eventArgs' in fieldDef && fieldDef.eventArgs !== undefined ? fieldDef.eventArgs : defaultEventArgs;
1617
1647
  return computed(() => {
1648
+ const baseInputs = buildBaseInputs(fieldDef, defaultProps());
1618
1649
  // Read signal value if index is a signal (supports differential updates)
1619
1650
  const getIndex = () => {
1620
1651
  if (!arrayContext)
@@ -1653,10 +1684,9 @@ function addArrayItemButtonFieldMapper(fieldDef) {
1653
1684
  * @returns Signal containing Record of input names to values for ngComponentOutlet
1654
1685
  */
1655
1686
  function removeArrayItemButtonFieldMapper(fieldDef) {
1656
- // Try to get array context (available when inside an array)
1657
- // Use optional injection so it doesn't fail when outside an array
1658
1687
  const arrayContext = inject(ARRAY_CONTEXT, { optional: true });
1659
1688
  const logger = inject(DynamicFormLogger);
1689
+ const defaultProps = inject(DEFAULT_PROPS);
1660
1690
  // Determine the target array key
1661
1691
  // Priority: explicit arrayKey from fieldDef > arrayKey from context
1662
1692
  const targetArrayKey = fieldDef.arrayKey ?? arrayContext?.arrayKey;
@@ -1664,14 +1694,13 @@ function removeArrayItemButtonFieldMapper(fieldDef) {
1664
1694
  logger.warn(`removeArrayItem button "${fieldDef.key}" has no array context. ` +
1665
1695
  'Either place it inside an array field, or provide an explicit arrayKey property.');
1666
1696
  }
1667
- // Build base inputs (static, from field definition)
1668
- const baseInputs = buildBaseInputs(fieldDef);
1669
1697
  // Set default eventArgs for RemoveArrayItemEvent (arrayKey, index if inside array)
1670
1698
  // When outside array, only pass arrayKey (removes last by default)
1671
1699
  // User can override by providing eventArgs in field definition
1672
1700
  const defaultEventArgs = arrayContext ? ['$arrayKey', '$index'] : ['$arrayKey'];
1673
1701
  const eventArgs = 'eventArgs' in fieldDef && fieldDef.eventArgs !== undefined ? fieldDef.eventArgs : defaultEventArgs;
1674
1702
  return computed(() => {
1703
+ const baseInputs = buildBaseInputs(fieldDef, defaultProps());
1675
1704
  // Read signal value if index is a signal (supports differential updates)
1676
1705
  const getIndex = () => {
1677
1706
  if (!arrayContext)