@egose/shadcn-theme-ng-tw 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.
Files changed (63) hide show
  1. package/form-autocomplete/README.md +20 -19
  2. package/form-autocomplete/fesm2022/form-autocomplete.mjs +41 -9
  3. package/form-autocomplete/types/form-autocomplete.d.ts +21 -1
  4. package/form-checkbox/README.md +18 -17
  5. package/form-checkbox/fesm2022/form-checkbox.mjs +42 -15
  6. package/form-checkbox/types/form-checkbox.d.ts +21 -1
  7. package/form-combobox/README.md +21 -20
  8. package/form-combobox/fesm2022/form-combobox.mjs +41 -9
  9. package/form-combobox/types/form-combobox.d.ts +21 -1
  10. package/form-date-picker/README.md +28 -27
  11. package/form-date-picker/fesm2022/form-date-picker.mjs +42 -15
  12. package/form-date-picker/types/form-date-picker.d.ts +21 -1
  13. package/form-date-picker-multi/README.md +28 -27
  14. package/form-date-picker-multi/fesm2022/form-date-picker-multi.mjs +42 -15
  15. package/form-date-picker-multi/types/form-date-picker-multi.d.ts +21 -1
  16. package/form-date-range-picker/README.md +28 -27
  17. package/form-date-range-picker/fesm2022/form-date-range-picker.mjs +42 -15
  18. package/form-date-range-picker/types/form-date-range-picker.d.ts +21 -1
  19. package/form-field/README.md +9 -0
  20. package/form-field/fesm2022/form-field.mjs +80 -5
  21. package/form-field/types/form-field.d.ts +50 -2
  22. package/form-field-simple/fesm2022/form-field-simple.mjs +15 -1
  23. package/form-field-simple/types/form-field-simple.d.ts +9 -2
  24. package/form-input-otp/README.md +17 -16
  25. package/form-input-otp/fesm2022/form-input-otp.mjs +41 -9
  26. package/form-input-otp/types/form-input-otp.d.ts +21 -1
  27. package/form-month-year-picker/README.md +26 -25
  28. package/form-month-year-picker/fesm2022/form-month-year-picker.mjs +42 -15
  29. package/form-month-year-picker/types/form-month-year-picker.d.ts +21 -1
  30. package/form-native-select/README.md +18 -17
  31. package/form-native-select/fesm2022/form-native-select.mjs +56 -17
  32. package/form-native-select/types/form-native-select.d.ts +21 -1
  33. package/form-phone-input/README.md +23 -22
  34. package/form-phone-input/fesm2022/form-phone-input.mjs +42 -15
  35. package/form-phone-input/types/form-phone-input.d.ts +21 -1
  36. package/form-radio-group/README.md +18 -17
  37. package/form-radio-group/fesm2022/form-radio-group.mjs +42 -15
  38. package/form-radio-group/types/form-radio-group.d.ts +21 -1
  39. package/form-searchable-multiselect/README.md +18 -17
  40. package/form-searchable-multiselect/fesm2022/form-searchable-multiselect.mjs +42 -15
  41. package/form-searchable-multiselect/types/form-searchable-multiselect.d.ts +21 -1
  42. package/form-select/README.md +20 -19
  43. package/form-select/fesm2022/form-select.mjs +42 -15
  44. package/form-select/types/form-select.d.ts +21 -1
  45. package/form-slider/README.md +19 -18
  46. package/form-slider/fesm2022/form-slider.mjs +41 -9
  47. package/form-slider/types/form-slider.d.ts +21 -1
  48. package/form-switch/README.md +16 -15
  49. package/form-switch/fesm2022/form-switch.mjs +42 -15
  50. package/form-switch/types/form-switch.d.ts +21 -1
  51. package/form-text-input/README.md +27 -26
  52. package/form-text-input/fesm2022/form-text-input.mjs +42 -15
  53. package/form-text-input/types/form-text-input.d.ts +21 -1
  54. package/form-textarea/README.md +23 -22
  55. package/form-textarea/fesm2022/form-textarea.mjs +42 -15
  56. package/form-textarea/types/form-textarea.d.ts +21 -1
  57. package/form-toggle/README.md +17 -16
  58. package/form-toggle/fesm2022/form-toggle.mjs +41 -7
  59. package/form-toggle/types/form-toggle.d.ts +21 -3
  60. package/form-toggle-group/README.md +20 -19
  61. package/form-toggle-group/fesm2022/form-toggle-group.mjs +42 -15
  62. package/form-toggle-group/types/form-toggle-group.d.ts +21 -1
  63. package/package.json +1 -1
@@ -2,13 +2,14 @@ import * as i0 from '@angular/core';
2
2
  import { inject, input, numberAttribute, computed, Component } from '@angular/core';
3
3
  import * as i1 from '@angular/forms';
4
4
  import { FormGroupDirective, ReactiveFormsModule, ControlContainer } from '@angular/forms';
5
- import { HlmFormIdGenerator, HlmFormField, HlmError, HlmHint } from '@egose/shadcn-theme-ng-tw/form-field';
5
+ import { injectEgFormErrorMessages, HlmFormIdGenerator, resolveEgFormError, HlmFormField, HlmError, HlmHint } from '@egose/shadcn-theme-ng-tw/form-field';
6
6
  import { HlmLabel } from '@egose/shadcn-theme-ng-tw/label';
7
7
  import { injectHlmDatePickerMultiConfig, HlmDatePickerMulti, HlmDateMultiInput } from '@egose/shadcn-theme-ng-tw/date-picker';
8
8
  import { hlm } from '@egose/shadcn-theme-ng-tw/utils';
9
9
 
10
10
  class EgFormDatePickerMulti {
11
11
  formGroupDirective = inject(FormGroupDirective);
12
+ _errorMessages = injectEgFormErrorMessages();
12
13
  generatedId = inject(HlmFormIdGenerator).generate('eg-form-date-picker-multi');
13
14
  _config = injectHlmDatePickerMultiConfig();
14
15
  label = input(undefined, /* @ts-ignore */
@@ -19,6 +20,13 @@ class EgFormDatePickerMulti {
19
20
  ...(ngDevMode ? [{ debugName: "controlName" }] : /* istanbul ignore next */ []));
20
21
  error = input(undefined, /* @ts-ignore */
21
22
  ...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
23
+ /**
24
+ * Auto-resolve the displayed message from the control's `ValidationErrors`
25
+ * when `error()` is unset. Explicit `error()` always wins. Set to `false`
26
+ * for manual-only messages.
27
+ */
28
+ autoError = input(true, /* @ts-ignore */
29
+ ...(ngDevMode ? [{ debugName: "autoError" }] : /* istanbul ignore next */ []));
22
30
  hint = input(undefined, /* @ts-ignore */
23
31
  ...(ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []));
24
32
  id = input(undefined, /* @ts-ignore */
@@ -58,6 +66,32 @@ class EgFormDatePickerMulti {
58
66
  ...(ngDevMode ? [{ debugName: "errorId" }] : /* istanbul ignore next */ []));
59
67
  hintId = computed(() => `${this.effectiveId()}-hint`, /* @ts-ignore */
60
68
  ...(ngDevMode ? [{ debugName: "hintId" }] : /* istanbul ignore next */ []));
69
+ /**
70
+ * Displayed message: explicit `error()` wins; otherwise (when `autoError()`)
71
+ * auto-resolved from the control's `ValidationErrors`. Plain method (not a
72
+ * computed): `ValidationErrors` is not a signal, so the control must be read
73
+ * fresh on every change-detection pass.
74
+ */
75
+ resolvedError() {
76
+ const explicit = this.error();
77
+ if (explicit)
78
+ return explicit;
79
+ if (!this.autoError())
80
+ return undefined;
81
+ const control = this.formGroupDirective.form.get(this.controlName());
82
+ return resolveEgFormError(control?.errors ?? null, this.label(), this._errorMessages);
83
+ }
84
+ /**
85
+ * Whether the error is surfaced: message present + control invalid +
86
+ * touched/dirty/submitted. Gates the visual `<hlm-error>` (and
87
+ * `describedBy()`, where present) so sighted and screen-reader output agree.
88
+ */
89
+ showError() {
90
+ const control = this.formGroupDirective.form.get(this.controlName());
91
+ return (!!this.resolvedError() &&
92
+ !!control?.invalid &&
93
+ (control.dirty || control.touched || this.formGroupDirective.submitted));
94
+ }
61
95
  /**
62
96
  * Bounds projected onto the picker's `Date` model. The cast keeps the
63
97
  * template's generic inference on `Date` while passing runtime values
@@ -68,12 +102,7 @@ class EgFormDatePickerMulti {
68
102
  $max = computed(() => this.max(), /* @ts-ignore */
69
103
  ...(ngDevMode ? [{ debugName: "$max" }] : /* istanbul ignore next */ []));
70
104
  describedBy() {
71
- const control = this.formGroupDirective.form.get(this.controlName());
72
- return this.error() && control?.invalid && (control.dirty || control.touched)
73
- ? this.errorId()
74
- : this.hint()
75
- ? this.hintId()
76
- : null;
105
+ return this.showError() ? this.errorId() : this.hint() ? this.hintId() : null;
77
106
  }
78
107
  // Styling
79
108
  userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
@@ -101,10 +130,9 @@ class EgFormDatePickerMulti {
101
130
  $hintClass = computed(() => hlm('tw:mt-0', this._config.hintClass, this.hintClass()), /* @ts-ignore */
102
131
  ...(ngDevMode ? [{ debugName: "$hintClass" }] : /* istanbul ignore next */ []));
103
132
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgFormDatePickerMulti, deps: [], target: i0.ɵɵFactoryTarget.Component });
104
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgFormDatePickerMulti, isStandalone: true, selector: "eg-form-date-picker-multi", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, controlId: { classPropertyName: "controlId", publicName: "controlId", isSignal: true, isRequired: false, transformFunction: null }, controlName: { classPropertyName: "controlName", publicName: "controlName", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, minSelection: { classPropertyName: "minSelection", publicName: "minSelection", isSignal: true, isRequired: false, transformFunction: null }, maxSelection: { classPropertyName: "maxSelection", publicName: "maxSelection", isSignal: true, isRequired: false, transformFunction: null }, autoCloseOnMaxSelection: { classPropertyName: "autoCloseOnMaxSelection", publicName: "autoCloseOnMaxSelection", isSignal: true, isRequired: false, transformFunction: null }, captionLayout: { classPropertyName: "captionLayout", publicName: "captionLayout", isSignal: true, isRequired: false, transformFunction: null }, formatDates: { classPropertyName: "formatDates", publicName: "formatDates", isSignal: true, isRequired: false, transformFunction: null }, transformDates: { classPropertyName: "transformDates", publicName: "transformDates", isSignal: true, isRequired: false, transformFunction: null }, parseDate: { classPropertyName: "parseDate", publicName: "parseDate", isSignal: true, isRequired: false, transformFunction: null }, formatInputDates: { classPropertyName: "formatInputDates", publicName: "formatInputDates", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, labelClass: { classPropertyName: "labelClass", publicName: "labelClass", isSignal: true, isRequired: false, transformFunction: null }, pickerClass: { classPropertyName: "pickerClass", publicName: "pickerClass", isSignal: true, isRequired: false, transformFunction: null }, inputClass: { classPropertyName: "inputClass", publicName: "inputClass", isSignal: true, isRequired: false, transformFunction: null }, errorClass: { classPropertyName: "errorClass", publicName: "errorClass", isSignal: true, isRequired: false, transformFunction: null }, hintClass: { classPropertyName: "hintClass", publicName: "hintClass", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "$userClass()" } }, providers: [{ provide: ControlContainer, useExisting: FormGroupDirective }], ngImport: i0, template: `
133
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgFormDatePickerMulti, isStandalone: true, selector: "eg-form-date-picker-multi", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, controlId: { classPropertyName: "controlId", publicName: "controlId", isSignal: true, isRequired: false, transformFunction: null }, controlName: { classPropertyName: "controlName", publicName: "controlName", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, autoError: { classPropertyName: "autoError", publicName: "autoError", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, minSelection: { classPropertyName: "minSelection", publicName: "minSelection", isSignal: true, isRequired: false, transformFunction: null }, maxSelection: { classPropertyName: "maxSelection", publicName: "maxSelection", isSignal: true, isRequired: false, transformFunction: null }, autoCloseOnMaxSelection: { classPropertyName: "autoCloseOnMaxSelection", publicName: "autoCloseOnMaxSelection", isSignal: true, isRequired: false, transformFunction: null }, captionLayout: { classPropertyName: "captionLayout", publicName: "captionLayout", isSignal: true, isRequired: false, transformFunction: null }, formatDates: { classPropertyName: "formatDates", publicName: "formatDates", isSignal: true, isRequired: false, transformFunction: null }, transformDates: { classPropertyName: "transformDates", publicName: "transformDates", isSignal: true, isRequired: false, transformFunction: null }, parseDate: { classPropertyName: "parseDate", publicName: "parseDate", isSignal: true, isRequired: false, transformFunction: null }, formatInputDates: { classPropertyName: "formatInputDates", publicName: "formatInputDates", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, labelClass: { classPropertyName: "labelClass", publicName: "labelClass", isSignal: true, isRequired: false, transformFunction: null }, pickerClass: { classPropertyName: "pickerClass", publicName: "pickerClass", isSignal: true, isRequired: false, transformFunction: null }, inputClass: { classPropertyName: "inputClass", publicName: "inputClass", isSignal: true, isRequired: false, transformFunction: null }, errorClass: { classPropertyName: "errorClass", publicName: "errorClass", isSignal: true, isRequired: false, transformFunction: null }, hintClass: { classPropertyName: "hintClass", publicName: "hintClass", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "$userClass()" } }, providers: [{ provide: ControlContainer, useExisting: FormGroupDirective }], ngImport: i0, template: `
105
134
  @let lbl = label();
106
135
  @let cnm = controlName();
107
- @let err = error();
108
136
  @let hnt = hint();
109
137
  @let rqrd = required();
110
138
 
@@ -144,9 +172,9 @@ class EgFormDatePickerMulti {
144
172
  />
145
173
  </hlm-date-picker-multi>
146
174
 
147
- @if (err) {
175
+ @if (showError()) {
148
176
  <hlm-error [id]="errorId()" [class]="$errorClass()">
149
- {{ err }}
177
+ {{ resolvedError() }}
150
178
  </hlm-error>
151
179
  }
152
180
 
@@ -171,7 +199,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
171
199
  template: `
172
200
  @let lbl = label();
173
201
  @let cnm = controlName();
174
- @let err = error();
175
202
  @let hnt = hint();
176
203
  @let rqrd = required();
177
204
 
@@ -211,9 +238,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
211
238
  />
212
239
  </hlm-date-picker-multi>
213
240
 
214
- @if (err) {
241
+ @if (showError()) {
215
242
  <hlm-error [id]="errorId()" [class]="$errorClass()">
216
- {{ err }}
243
+ {{ resolvedError() }}
217
244
  </hlm-error>
218
245
  }
219
246
 
@@ -225,7 +252,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
225
252
  </hlm-form-field>
226
253
  `,
227
254
  }]
228
- }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], controlId: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlId", required: false }] }], controlName: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlName", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], minSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "minSelection", required: false }] }], maxSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxSelection", required: false }] }], autoCloseOnMaxSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoCloseOnMaxSelection", required: false }] }], captionLayout: [{ type: i0.Input, args: [{ isSignal: true, alias: "captionLayout", required: false }] }], formatDates: [{ type: i0.Input, args: [{ isSignal: true, alias: "formatDates", required: false }] }], transformDates: [{ type: i0.Input, args: [{ isSignal: true, alias: "transformDates", required: false }] }], parseDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "parseDate", required: false }] }], formatInputDates: [{ type: i0.Input, args: [{ isSignal: true, alias: "formatInputDates", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], labelClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelClass", required: false }] }], pickerClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "pickerClass", required: false }] }], inputClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputClass", required: false }] }], errorClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorClass", required: false }] }], hintClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "hintClass", required: false }] }] } });
255
+ }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], controlId: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlId", required: false }] }], controlName: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlName", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], autoError: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoError", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], minSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "minSelection", required: false }] }], maxSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxSelection", required: false }] }], autoCloseOnMaxSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoCloseOnMaxSelection", required: false }] }], captionLayout: [{ type: i0.Input, args: [{ isSignal: true, alias: "captionLayout", required: false }] }], formatDates: [{ type: i0.Input, args: [{ isSignal: true, alias: "formatDates", required: false }] }], transformDates: [{ type: i0.Input, args: [{ isSignal: true, alias: "transformDates", required: false }] }], parseDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "parseDate", required: false }] }], formatInputDates: [{ type: i0.Input, args: [{ isSignal: true, alias: "formatInputDates", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], labelClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelClass", required: false }] }], pickerClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "pickerClass", required: false }] }], inputClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputClass", required: false }] }], errorClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorClass", required: false }] }], hintClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "hintClass", required: false }] }] } });
229
256
 
230
257
  /*
231
258
  * Public API Surface of form-date-picker-multi
@@ -5,12 +5,19 @@ import { NumberInput } from '@angular/cdk/coercion';
5
5
  type DatePickerCaptionLayout = 'dropdown' | 'label' | 'dropdown-months' | 'dropdown-years';
6
6
  declare class EgFormDatePickerMulti {
7
7
  private readonly formGroupDirective;
8
+ private readonly _errorMessages;
8
9
  private readonly generatedId;
9
10
  private readonly _config;
10
11
  label: _angular_core.InputSignal<string | undefined>;
11
12
  controlId: _angular_core.InputSignal<string | undefined>;
12
13
  controlName: _angular_core.InputSignal<string>;
13
14
  error: _angular_core.InputSignal<string | undefined>;
15
+ /**
16
+ * Auto-resolve the displayed message from the control's `ValidationErrors`
17
+ * when `error()` is unset. Explicit `error()` always wins. Set to `false`
18
+ * for manual-only messages.
19
+ */
20
+ autoError: _angular_core.InputSignal<boolean>;
14
21
  hint: _angular_core.InputSignal<string | undefined>;
15
22
  id: _angular_core.InputSignal<string | undefined>;
16
23
  name: _angular_core.InputSignal<string | undefined>;
@@ -31,6 +38,19 @@ declare class EgFormDatePickerMulti {
31
38
  readonly effectiveId: _angular_core.Signal<string>;
32
39
  readonly errorId: _angular_core.Signal<string>;
33
40
  readonly hintId: _angular_core.Signal<string>;
41
+ /**
42
+ * Displayed message: explicit `error()` wins; otherwise (when `autoError()`)
43
+ * auto-resolved from the control's `ValidationErrors`. Plain method (not a
44
+ * computed): `ValidationErrors` is not a signal, so the control must be read
45
+ * fresh on every change-detection pass.
46
+ */
47
+ protected resolvedError(): string | undefined;
48
+ /**
49
+ * Whether the error is surfaced: message present + control invalid +
50
+ * touched/dirty/submitted. Gates the visual `<hlm-error>` (and
51
+ * `describedBy()`, where present) so sighted and screen-reader output agree.
52
+ */
53
+ protected showError(): boolean;
34
54
  /**
35
55
  * Bounds projected onto the picker's `Date` model. The cast keeps the
36
56
  * template's generic inference on `Date` while passing runtime values
@@ -52,7 +72,7 @@ declare class EgFormDatePickerMulti {
52
72
  $errorClass: _angular_core.Signal<string>;
53
73
  $hintClass: _angular_core.Signal<string>;
54
74
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EgFormDatePickerMulti, never>;
55
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<EgFormDatePickerMulti, "eg-form-date-picker-multi", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "controlId": { "alias": "controlId"; "required": false; "isSignal": true; }; "controlName": { "alias": "controlName"; "required": false; "isSignal": true; }; "error": { "alias": "error"; "required": false; "isSignal": true; }; "hint": { "alias": "hint"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; "name": { "alias": "name"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "readonly": { "alias": "readonly"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "min": { "alias": "min"; "required": false; "isSignal": true; }; "max": { "alias": "max"; "required": false; "isSignal": true; }; "minSelection": { "alias": "minSelection"; "required": false; "isSignal": true; }; "maxSelection": { "alias": "maxSelection"; "required": false; "isSignal": true; }; "autoCloseOnMaxSelection": { "alias": "autoCloseOnMaxSelection"; "required": false; "isSignal": true; }; "captionLayout": { "alias": "captionLayout"; "required": false; "isSignal": true; }; "formatDates": { "alias": "formatDates"; "required": false; "isSignal": true; }; "transformDates": { "alias": "transformDates"; "required": false; "isSignal": true; }; "parseDate": { "alias": "parseDate"; "required": false; "isSignal": true; }; "formatInputDates": { "alias": "formatInputDates"; "required": false; "isSignal": true; }; "userClass": { "alias": "class"; "required": false; "isSignal": true; }; "labelClass": { "alias": "labelClass"; "required": false; "isSignal": true; }; "pickerClass": { "alias": "pickerClass"; "required": false; "isSignal": true; }; "inputClass": { "alias": "inputClass"; "required": false; "isSignal": true; }; "errorClass": { "alias": "errorClass"; "required": false; "isSignal": true; }; "hintClass": { "alias": "hintClass"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
75
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EgFormDatePickerMulti, "eg-form-date-picker-multi", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "controlId": { "alias": "controlId"; "required": false; "isSignal": true; }; "controlName": { "alias": "controlName"; "required": false; "isSignal": true; }; "error": { "alias": "error"; "required": false; "isSignal": true; }; "autoError": { "alias": "autoError"; "required": false; "isSignal": true; }; "hint": { "alias": "hint"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; "name": { "alias": "name"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "readonly": { "alias": "readonly"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "min": { "alias": "min"; "required": false; "isSignal": true; }; "max": { "alias": "max"; "required": false; "isSignal": true; }; "minSelection": { "alias": "minSelection"; "required": false; "isSignal": true; }; "maxSelection": { "alias": "maxSelection"; "required": false; "isSignal": true; }; "autoCloseOnMaxSelection": { "alias": "autoCloseOnMaxSelection"; "required": false; "isSignal": true; }; "captionLayout": { "alias": "captionLayout"; "required": false; "isSignal": true; }; "formatDates": { "alias": "formatDates"; "required": false; "isSignal": true; }; "transformDates": { "alias": "transformDates"; "required": false; "isSignal": true; }; "parseDate": { "alias": "parseDate"; "required": false; "isSignal": true; }; "formatInputDates": { "alias": "formatInputDates"; "required": false; "isSignal": true; }; "userClass": { "alias": "class"; "required": false; "isSignal": true; }; "labelClass": { "alias": "labelClass"; "required": false; "isSignal": true; }; "pickerClass": { "alias": "pickerClass"; "required": false; "isSignal": true; }; "inputClass": { "alias": "inputClass"; "required": false; "isSignal": true; }; "errorClass": { "alias": "errorClass"; "required": false; "isSignal": true; }; "hintClass": { "alias": "hintClass"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
56
76
  }
57
77
 
58
78
  export { EgFormDatePickerMulti };
@@ -84,33 +84,34 @@ Selector (from source): `eg-form-date-range-picker` (standalone component, host
84
84
 
85
85
  ### `EgFormDateRangePicker` — `eg-form-date-range-picker`
86
86
 
87
- | Input | Type | Default | Description |
88
- | ------------------------- | ------------------------------------------------- | --------------------- | ----------------------------------------------------------------------------------------------------------------- |
89
- | `controlName` | `string` | `''` | `formControlName` key inside the parent `FormGroup`. **Required.** |
90
- | `label` | `string \| undefined` | — | Label text (hidden when omitted). |
91
- | `error` | `string \| undefined` | — | Error text shown when invalid. |
92
- | `hint` | `string \| undefined` | — | Hint text shown otherwise. |
93
- | `controlId` | `string \| undefined` | — | Explicit id (first priority for `effectiveId`). |
94
- | `id` | `string \| undefined` | — | Fallback id (second priority). |
95
- | `name` | `string \| undefined` | — | Native `name` forwarded to the inner input (`controlName` wins when set, like `eg-form-text-input`). |
96
- | `placeholder` | `string` | `'Pick a date range'` | Input placeholder. |
97
- | `readonly` | `boolean` | `false` | Native `readonly` forwarded to the inner input (locks typed text; use `disabled` to lock the calendar too). |
98
- | `disabled` | `boolean` | `false` | Locks interaction via picker `disabled` (form control stays enabled). |
99
- | `required` | `boolean` | `false` | Shows a red `*` next to the label. |
100
- | `min` | `Date \| string \| null` | `null` | Minimum date forwarded to `hlm-date-range-picker`. |
101
- | `max` | `Date \| string \| null` | `null` | Maximum date forwarded to `hlm-date-range-picker`. |
102
- | `autoCloseOnEndSelection` | `boolean` | config default | Forwarded to `hlm-date-range-picker` (falls back to `provideHlmDateRangePickerConfig`, default `false`). |
103
- | `captionLayout` | union | `'label'` | Forwarded to `hlm-date-range-picker` (`'dropdown' \| 'label' \| 'dropdown-months' \| 'dropdown-years'`). |
104
- | `formatDates` | `(dates: [Date \| null, Date \| null]) => string` | config default | Display format, forwarded to `hlm-date-range-picker` (falls back to `provideHlmDateRangePickerConfig`). |
105
- | `transformDates` | `(dates: [Date, Date]) => [Date, Date]` | config default | Model transform, forwarded to `hlm-date-range-picker` (falls back to `provideHlmDateRangePickerConfig`). |
106
- | `parseDate` | `(value: string) => [Date, Date] \| null` | config default | Input parsing, forwarded to `hlm-date-range-input` (falls back to `provideHlmDateRangePickerConfig`). |
107
- | `formatInputDates` | `(dates: [Date \| null, Date \| null]) => string` | config default | Edit format while focused, forwarded to `hlm-date-range-input` (falls back to `provideHlmDateRangePickerConfig`). |
108
- | `class` | `ClassValue` | `''` | Extra host classes (base `tw:w-full`). |
109
- | `labelClass` | `string` | `''` | Extra label classes (base `tw:mb-1`). |
110
- | `pickerClass` | `string` | `''` | Extra picker-container classes (base `tw:mb-1`). |
111
- | `inputClass` | `string` | `''` | Extra inner-input classes (e.g. `tw:text-lg` for text size). |
112
- | `errorClass` | `string` | `''` | Extra error classes (base `tw:mt-0`). |
113
- | `hintClass` | `string` | `''` | Extra hint classes (base `tw:mt-0`). |
87
+ | Input | Type | Default | Description |
88
+ | ------------------------- | ------------------------------------------------- | --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
89
+ | `controlName` | `string` | `''` | `formControlName` key inside the parent `FormGroup`. **Required.** |
90
+ | `label` | `string \| undefined` | — | Label text (hidden when omitted). |
91
+ | `error` | `string \| undefined` | — | Error text shown when invalid. |
92
+ | `autoError` | `boolean` | `true` | Auto-resolve the message from the control's `ValidationErrors` when `error` is unset. Explicit `error` always wins; `error=""` counts as unset. Global wording via `provideEgFormErrorMessages`. |
93
+ | `hint` | `string \| undefined` | — | Hint text shown otherwise. |
94
+ | `controlId` | `string \| undefined` | — | Explicit id (first priority for `effectiveId`). |
95
+ | `id` | `string \| undefined` | — | Fallback id (second priority). |
96
+ | `name` | `string \| undefined` | — | Native `name` forwarded to the inner input (`controlName` wins when set, like `eg-form-text-input`). |
97
+ | `placeholder` | `string` | `'Pick a date range'` | Input placeholder. |
98
+ | `readonly` | `boolean` | `false` | Native `readonly` forwarded to the inner input (locks typed text; use `disabled` to lock the calendar too). |
99
+ | `disabled` | `boolean` | `false` | Locks interaction via picker `disabled` (form control stays enabled). |
100
+ | `required` | `boolean` | `false` | Shows a red `*` next to the label. |
101
+ | `min` | `Date \| string \| null` | `null` | Minimum date forwarded to `hlm-date-range-picker`. |
102
+ | `max` | `Date \| string \| null` | `null` | Maximum date forwarded to `hlm-date-range-picker`. |
103
+ | `autoCloseOnEndSelection` | `boolean` | config default | Forwarded to `hlm-date-range-picker` (falls back to `provideHlmDateRangePickerConfig`, default `false`). |
104
+ | `captionLayout` | union | `'label'` | Forwarded to `hlm-date-range-picker` (`'dropdown' \| 'label' \| 'dropdown-months' \| 'dropdown-years'`). |
105
+ | `formatDates` | `(dates: [Date \| null, Date \| null]) => string` | config default | Display format, forwarded to `hlm-date-range-picker` (falls back to `provideHlmDateRangePickerConfig`). |
106
+ | `transformDates` | `(dates: [Date, Date]) => [Date, Date]` | config default | Model transform, forwarded to `hlm-date-range-picker` (falls back to `provideHlmDateRangePickerConfig`). |
107
+ | `parseDate` | `(value: string) => [Date, Date] \| null` | config default | Input parsing, forwarded to `hlm-date-range-input` (falls back to `provideHlmDateRangePickerConfig`). |
108
+ | `formatInputDates` | `(dates: [Date \| null, Date \| null]) => string` | config default | Edit format while focused, forwarded to `hlm-date-range-input` (falls back to `provideHlmDateRangePickerConfig`). |
109
+ | `class` | `ClassValue` | `''` | Extra host classes (base `tw:w-full`). |
110
+ | `labelClass` | `string` | `''` | Extra label classes (base `tw:mb-1`). |
111
+ | `pickerClass` | `string` | `''` | Extra picker-container classes (base `tw:mb-1`). |
112
+ | `inputClass` | `string` | `''` | Extra inner-input classes (e.g. `tw:text-lg` for text size). |
113
+ | `errorClass` | `string` | `''` | Extra error classes (base `tw:mt-0`). |
114
+ | `hintClass` | `string` | `''` | Extra hint classes (base `tw:mt-0`). |
114
115
 
115
116
  | Member | Description |
116
117
  | -------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -2,13 +2,14 @@ import * as i0 from '@angular/core';
2
2
  import { inject, input, computed, Component } from '@angular/core';
3
3
  import * as i1 from '@angular/forms';
4
4
  import { FormGroupDirective, ReactiveFormsModule, ControlContainer } from '@angular/forms';
5
- import { HlmFormIdGenerator, HlmFormField, HlmError, HlmHint } from '@egose/shadcn-theme-ng-tw/form-field';
5
+ import { injectEgFormErrorMessages, HlmFormIdGenerator, resolveEgFormError, HlmFormField, HlmError, HlmHint } from '@egose/shadcn-theme-ng-tw/form-field';
6
6
  import { HlmLabel } from '@egose/shadcn-theme-ng-tw/label';
7
7
  import { injectHlmDateRangePickerConfig, HlmDateRangePicker, HlmDateRangeInput } from '@egose/shadcn-theme-ng-tw/date-picker';
8
8
  import { hlm } from '@egose/shadcn-theme-ng-tw/utils';
9
9
 
10
10
  class EgFormDateRangePicker {
11
11
  formGroupDirective = inject(FormGroupDirective);
12
+ _errorMessages = injectEgFormErrorMessages();
12
13
  generatedId = inject(HlmFormIdGenerator).generate('eg-form-date-range-picker');
13
14
  _config = injectHlmDateRangePickerConfig();
14
15
  // Inputs
@@ -20,6 +21,13 @@ class EgFormDateRangePicker {
20
21
  ...(ngDevMode ? [{ debugName: "controlName" }] : /* istanbul ignore next */ []));
21
22
  error = input(undefined, /* @ts-ignore */
22
23
  ...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
24
+ /**
25
+ * Auto-resolve the displayed message from the control's `ValidationErrors`
26
+ * when `error()` is unset. Explicit `error()` always wins. Set to `false`
27
+ * for manual-only messages.
28
+ */
29
+ autoError = input(true, /* @ts-ignore */
30
+ ...(ngDevMode ? [{ debugName: "autoError" }] : /* istanbul ignore next */ []));
23
31
  hint = input(undefined, /* @ts-ignore */
24
32
  ...(ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []));
25
33
  // HTML attributes
@@ -59,6 +67,32 @@ class EgFormDateRangePicker {
59
67
  ...(ngDevMode ? [{ debugName: "errorId" }] : /* istanbul ignore next */ []));
60
68
  hintId = computed(() => `${this.effectiveId()}-hint`, /* @ts-ignore */
61
69
  ...(ngDevMode ? [{ debugName: "hintId" }] : /* istanbul ignore next */ []));
70
+ /**
71
+ * Displayed message: explicit `error()` wins; otherwise (when `autoError()`)
72
+ * auto-resolved from the control's `ValidationErrors`. Plain method (not a
73
+ * computed): `ValidationErrors` is not a signal, so the control must be read
74
+ * fresh on every change-detection pass.
75
+ */
76
+ resolvedError() {
77
+ const explicit = this.error();
78
+ if (explicit)
79
+ return explicit;
80
+ if (!this.autoError())
81
+ return undefined;
82
+ const control = this.formGroupDirective.form.get(this.controlName());
83
+ return resolveEgFormError(control?.errors ?? null, this.label(), this._errorMessages);
84
+ }
85
+ /**
86
+ * Whether the error is surfaced: message present + control invalid +
87
+ * touched/dirty/submitted. Gates the visual `<hlm-error>` (and
88
+ * `describedBy()`, where present) so sighted and screen-reader output agree.
89
+ */
90
+ showError() {
91
+ const control = this.formGroupDirective.form.get(this.controlName());
92
+ return (!!this.resolvedError() &&
93
+ !!control?.invalid &&
94
+ (control.dirty || control.touched || this.formGroupDirective.submitted));
95
+ }
62
96
  /**
63
97
  * Bounds projected onto the picker's `Date` model. The cast keeps the
64
98
  * template's generic inference on `Date` while passing runtime values
@@ -69,12 +103,7 @@ class EgFormDateRangePicker {
69
103
  $max = computed(() => this.max(), /* @ts-ignore */
70
104
  ...(ngDevMode ? [{ debugName: "$max" }] : /* istanbul ignore next */ []));
71
105
  describedBy() {
72
- const control = this.formGroupDirective.form.get(this.controlName());
73
- return this.error() && control?.invalid && (control.dirty || control.touched)
74
- ? this.errorId()
75
- : this.hint()
76
- ? this.hintId()
77
- : null;
106
+ return this.showError() ? this.errorId() : this.hint() ? this.hintId() : null;
78
107
  }
79
108
  // Styling
80
109
  userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
@@ -102,10 +131,9 @@ class EgFormDateRangePicker {
102
131
  $hintClass = computed(() => hlm('tw:mt-0', this._config.hintClass, this.hintClass()), /* @ts-ignore */
103
132
  ...(ngDevMode ? [{ debugName: "$hintClass" }] : /* istanbul ignore next */ []));
104
133
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgFormDateRangePicker, deps: [], target: i0.ɵɵFactoryTarget.Component });
105
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgFormDateRangePicker, isStandalone: true, selector: "eg-form-date-range-picker", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, controlId: { classPropertyName: "controlId", publicName: "controlId", isSignal: true, isRequired: false, transformFunction: null }, controlName: { classPropertyName: "controlName", publicName: "controlName", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, autoCloseOnEndSelection: { classPropertyName: "autoCloseOnEndSelection", publicName: "autoCloseOnEndSelection", isSignal: true, isRequired: false, transformFunction: null }, captionLayout: { classPropertyName: "captionLayout", publicName: "captionLayout", isSignal: true, isRequired: false, transformFunction: null }, formatDates: { classPropertyName: "formatDates", publicName: "formatDates", isSignal: true, isRequired: false, transformFunction: null }, transformDates: { classPropertyName: "transformDates", publicName: "transformDates", isSignal: true, isRequired: false, transformFunction: null }, parseDate: { classPropertyName: "parseDate", publicName: "parseDate", isSignal: true, isRequired: false, transformFunction: null }, formatInputDates: { classPropertyName: "formatInputDates", publicName: "formatInputDates", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, labelClass: { classPropertyName: "labelClass", publicName: "labelClass", isSignal: true, isRequired: false, transformFunction: null }, pickerClass: { classPropertyName: "pickerClass", publicName: "pickerClass", isSignal: true, isRequired: false, transformFunction: null }, inputClass: { classPropertyName: "inputClass", publicName: "inputClass", isSignal: true, isRequired: false, transformFunction: null }, errorClass: { classPropertyName: "errorClass", publicName: "errorClass", isSignal: true, isRequired: false, transformFunction: null }, hintClass: { classPropertyName: "hintClass", publicName: "hintClass", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "$userClass()" } }, providers: [{ provide: ControlContainer, useExisting: FormGroupDirective }], ngImport: i0, template: `
134
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgFormDateRangePicker, isStandalone: true, selector: "eg-form-date-range-picker", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, controlId: { classPropertyName: "controlId", publicName: "controlId", isSignal: true, isRequired: false, transformFunction: null }, controlName: { classPropertyName: "controlName", publicName: "controlName", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, autoError: { classPropertyName: "autoError", publicName: "autoError", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, autoCloseOnEndSelection: { classPropertyName: "autoCloseOnEndSelection", publicName: "autoCloseOnEndSelection", isSignal: true, isRequired: false, transformFunction: null }, captionLayout: { classPropertyName: "captionLayout", publicName: "captionLayout", isSignal: true, isRequired: false, transformFunction: null }, formatDates: { classPropertyName: "formatDates", publicName: "formatDates", isSignal: true, isRequired: false, transformFunction: null }, transformDates: { classPropertyName: "transformDates", publicName: "transformDates", isSignal: true, isRequired: false, transformFunction: null }, parseDate: { classPropertyName: "parseDate", publicName: "parseDate", isSignal: true, isRequired: false, transformFunction: null }, formatInputDates: { classPropertyName: "formatInputDates", publicName: "formatInputDates", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, labelClass: { classPropertyName: "labelClass", publicName: "labelClass", isSignal: true, isRequired: false, transformFunction: null }, pickerClass: { classPropertyName: "pickerClass", publicName: "pickerClass", isSignal: true, isRequired: false, transformFunction: null }, inputClass: { classPropertyName: "inputClass", publicName: "inputClass", isSignal: true, isRequired: false, transformFunction: null }, errorClass: { classPropertyName: "errorClass", publicName: "errorClass", isSignal: true, isRequired: false, transformFunction: null }, hintClass: { classPropertyName: "hintClass", publicName: "hintClass", isSignal: true, isRequired: false, transformFunction: null } }, host: { properties: { "class": "$userClass()" } }, providers: [{ provide: ControlContainer, useExisting: FormGroupDirective }], ngImport: i0, template: `
106
135
  @let lbl = label();
107
136
  @let cnm = controlName();
108
- @let err = error();
109
137
  @let hnt = hint();
110
138
  @let rqrd = required();
111
139
 
@@ -143,9 +171,9 @@ class EgFormDateRangePicker {
143
171
  />
144
172
  </hlm-date-range-picker>
145
173
 
146
- @if (err) {
174
+ @if (showError()) {
147
175
  <hlm-error [id]="errorId()" [class]="$errorClass()">
148
- {{ err }}
176
+ {{ resolvedError() }}
149
177
  </hlm-error>
150
178
  }
151
179
 
@@ -170,7 +198,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
170
198
  template: `
171
199
  @let lbl = label();
172
200
  @let cnm = controlName();
173
- @let err = error();
174
201
  @let hnt = hint();
175
202
  @let rqrd = required();
176
203
 
@@ -208,9 +235,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
208
235
  />
209
236
  </hlm-date-range-picker>
210
237
 
211
- @if (err) {
238
+ @if (showError()) {
212
239
  <hlm-error [id]="errorId()" [class]="$errorClass()">
213
- {{ err }}
240
+ {{ resolvedError() }}
214
241
  </hlm-error>
215
242
  }
216
243
 
@@ -222,7 +249,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
222
249
  </hlm-form-field>
223
250
  `,
224
251
  }]
225
- }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], controlId: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlId", required: false }] }], controlName: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlName", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], autoCloseOnEndSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoCloseOnEndSelection", required: false }] }], captionLayout: [{ type: i0.Input, args: [{ isSignal: true, alias: "captionLayout", required: false }] }], formatDates: [{ type: i0.Input, args: [{ isSignal: true, alias: "formatDates", required: false }] }], transformDates: [{ type: i0.Input, args: [{ isSignal: true, alias: "transformDates", required: false }] }], parseDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "parseDate", required: false }] }], formatInputDates: [{ type: i0.Input, args: [{ isSignal: true, alias: "formatInputDates", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], labelClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelClass", required: false }] }], pickerClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "pickerClass", required: false }] }], inputClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputClass", required: false }] }], errorClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorClass", required: false }] }], hintClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "hintClass", required: false }] }] } });
252
+ }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], controlId: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlId", required: false }] }], controlName: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlName", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], autoError: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoError", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], min: [{ type: i0.Input, args: [{ isSignal: true, alias: "min", required: false }] }], max: [{ type: i0.Input, args: [{ isSignal: true, alias: "max", required: false }] }], autoCloseOnEndSelection: [{ type: i0.Input, args: [{ isSignal: true, alias: "autoCloseOnEndSelection", required: false }] }], captionLayout: [{ type: i0.Input, args: [{ isSignal: true, alias: "captionLayout", required: false }] }], formatDates: [{ type: i0.Input, args: [{ isSignal: true, alias: "formatDates", required: false }] }], transformDates: [{ type: i0.Input, args: [{ isSignal: true, alias: "transformDates", required: false }] }], parseDate: [{ type: i0.Input, args: [{ isSignal: true, alias: "parseDate", required: false }] }], formatInputDates: [{ type: i0.Input, args: [{ isSignal: true, alias: "formatInputDates", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], labelClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelClass", required: false }] }], pickerClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "pickerClass", required: false }] }], inputClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "inputClass", required: false }] }], errorClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorClass", required: false }] }], hintClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "hintClass", required: false }] }] } });
226
253
 
227
254
  /*
228
255
  * Public API Surface of form-date-range-picker
@@ -4,12 +4,19 @@ import { ClassValue } from 'clsx';
4
4
  type DateRangeCaptionLayout = 'dropdown' | 'label' | 'dropdown-months' | 'dropdown-years';
5
5
  declare class EgFormDateRangePicker {
6
6
  private readonly formGroupDirective;
7
+ private readonly _errorMessages;
7
8
  private readonly generatedId;
8
9
  private readonly _config;
9
10
  label: _angular_core.InputSignal<string | undefined>;
10
11
  controlId: _angular_core.InputSignal<string | undefined>;
11
12
  controlName: _angular_core.InputSignal<string>;
12
13
  error: _angular_core.InputSignal<string | undefined>;
14
+ /**
15
+ * Auto-resolve the displayed message from the control's `ValidationErrors`
16
+ * when `error()` is unset. Explicit `error()` always wins. Set to `false`
17
+ * for manual-only messages.
18
+ */
19
+ autoError: _angular_core.InputSignal<boolean>;
13
20
  hint: _angular_core.InputSignal<string | undefined>;
14
21
  id: _angular_core.InputSignal<string | undefined>;
15
22
  name: _angular_core.InputSignal<string | undefined>;
@@ -28,6 +35,19 @@ declare class EgFormDateRangePicker {
28
35
  readonly effectiveId: _angular_core.Signal<string>;
29
36
  readonly errorId: _angular_core.Signal<string>;
30
37
  readonly hintId: _angular_core.Signal<string>;
38
+ /**
39
+ * Displayed message: explicit `error()` wins; otherwise (when `autoError()`)
40
+ * auto-resolved from the control's `ValidationErrors`. Plain method (not a
41
+ * computed): `ValidationErrors` is not a signal, so the control must be read
42
+ * fresh on every change-detection pass.
43
+ */
44
+ protected resolvedError(): string | undefined;
45
+ /**
46
+ * Whether the error is surfaced: message present + control invalid +
47
+ * touched/dirty/submitted. Gates the visual `<hlm-error>` (and
48
+ * `describedBy()`, where present) so sighted and screen-reader output agree.
49
+ */
50
+ protected showError(): boolean;
31
51
  /**
32
52
  * Bounds projected onto the picker's `Date` model. The cast keeps the
33
53
  * template's generic inference on `Date` while passing runtime values
@@ -49,7 +69,7 @@ declare class EgFormDateRangePicker {
49
69
  $errorClass: _angular_core.Signal<string>;
50
70
  $hintClass: _angular_core.Signal<string>;
51
71
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EgFormDateRangePicker, never>;
52
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<EgFormDateRangePicker, "eg-form-date-range-picker", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "controlId": { "alias": "controlId"; "required": false; "isSignal": true; }; "controlName": { "alias": "controlName"; "required": false; "isSignal": true; }; "error": { "alias": "error"; "required": false; "isSignal": true; }; "hint": { "alias": "hint"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; "name": { "alias": "name"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "readonly": { "alias": "readonly"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "min": { "alias": "min"; "required": false; "isSignal": true; }; "max": { "alias": "max"; "required": false; "isSignal": true; }; "autoCloseOnEndSelection": { "alias": "autoCloseOnEndSelection"; "required": false; "isSignal": true; }; "captionLayout": { "alias": "captionLayout"; "required": false; "isSignal": true; }; "formatDates": { "alias": "formatDates"; "required": false; "isSignal": true; }; "transformDates": { "alias": "transformDates"; "required": false; "isSignal": true; }; "parseDate": { "alias": "parseDate"; "required": false; "isSignal": true; }; "formatInputDates": { "alias": "formatInputDates"; "required": false; "isSignal": true; }; "userClass": { "alias": "class"; "required": false; "isSignal": true; }; "labelClass": { "alias": "labelClass"; "required": false; "isSignal": true; }; "pickerClass": { "alias": "pickerClass"; "required": false; "isSignal": true; }; "inputClass": { "alias": "inputClass"; "required": false; "isSignal": true; }; "errorClass": { "alias": "errorClass"; "required": false; "isSignal": true; }; "hintClass": { "alias": "hintClass"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
72
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EgFormDateRangePicker, "eg-form-date-range-picker", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "controlId": { "alias": "controlId"; "required": false; "isSignal": true; }; "controlName": { "alias": "controlName"; "required": false; "isSignal": true; }; "error": { "alias": "error"; "required": false; "isSignal": true; }; "autoError": { "alias": "autoError"; "required": false; "isSignal": true; }; "hint": { "alias": "hint"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; "name": { "alias": "name"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "readonly": { "alias": "readonly"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "min": { "alias": "min"; "required": false; "isSignal": true; }; "max": { "alias": "max"; "required": false; "isSignal": true; }; "autoCloseOnEndSelection": { "alias": "autoCloseOnEndSelection"; "required": false; "isSignal": true; }; "captionLayout": { "alias": "captionLayout"; "required": false; "isSignal": true; }; "formatDates": { "alias": "formatDates"; "required": false; "isSignal": true; }; "transformDates": { "alias": "transformDates"; "required": false; "isSignal": true; }; "parseDate": { "alias": "parseDate"; "required": false; "isSignal": true; }; "formatInputDates": { "alias": "formatInputDates"; "required": false; "isSignal": true; }; "userClass": { "alias": "class"; "required": false; "isSignal": true; }; "labelClass": { "alias": "labelClass"; "required": false; "isSignal": true; }; "pickerClass": { "alias": "pickerClass"; "required": false; "isSignal": true; }; "inputClass": { "alias": "inputClass"; "required": false; "isSignal": true; }; "errorClass": { "alias": "errorClass"; "required": false; "isSignal": true; }; "hintClass": { "alias": "hintClass"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
53
73
  }
54
74
 
55
75
  export { EgFormDateRangePicker };
@@ -338,6 +338,15 @@ export class CheckFieldComponent {
338
338
  }
339
339
  ```
340
340
 
341
+ ## Automatic error messages (`eg-form-*` wrappers)
342
+
343
+ The `eg-form-*` wrappers auto-resolve their displayed message from the control's `ValidationErrors` when no explicit `error` is set:
344
+
345
+ - Resolution order: explicit `error()` wins; otherwise (when `autoError()` is `true`, the default) the message is resolved from a custom dictionary entry, then the built-in defaults (`required`, `requiredTrue`, `email`, `minlength`, `maxlength`, `min`, `max`, `pattern`), then a generic "`label` is invalid" fallback. `error=""` is treated as unset.
346
+ - Set `autoError="false"` on a wrapper for manual-only messages.
347
+ - Global wording / custom validator keys (e.g. `usernameTaken`) and i18n are configured app-wide via `provideEgFormErrorMessages({...})` (merged over `DEFAULT_EG_FORM_ERROR_MESSAGES`, read with `injectEgFormErrorMessages()`); per-call overrides go through `resolveEgFormError(errors, label, messages)`.
348
+ - _When_ a message surfaces: wrappers render `<hlm-error>` (and point `aria-describedby` at it) only when the control is invalid + touched/dirty/submitted — otherwise the hint shows.
349
+
341
350
  ## Accessibility notes
342
351
 
343
352
  - The wrapper itself adds no label — always include a `<label hlmLabel>` (or `aria-label` on the control) so the field has an accessible name.