@egose/shadcn-theme-ng 0.3.0 → 0.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (68) hide show
  1. package/README.md +1 -1
  2. package/form-autocomplete/README.md +20 -19
  3. package/form-autocomplete/fesm2022/form-autocomplete.mjs +44 -9
  4. package/form-autocomplete/types/form-autocomplete.d.ts +22 -1
  5. package/form-checkbox/README.md +20 -19
  6. package/form-checkbox/fesm2022/form-checkbox.mjs +99 -15
  7. package/form-checkbox/types/form-checkbox.d.ts +43 -3
  8. package/form-combobox/README.md +21 -20
  9. package/form-combobox/fesm2022/form-combobox.mjs +44 -9
  10. package/form-combobox/types/form-combobox.d.ts +22 -1
  11. package/form-date-picker/README.md +28 -27
  12. package/form-date-picker/fesm2022/form-date-picker.mjs +45 -15
  13. package/form-date-picker/types/form-date-picker.d.ts +22 -1
  14. package/form-date-picker-multi/README.md +28 -27
  15. package/form-date-picker-multi/fesm2022/form-date-picker-multi.mjs +45 -15
  16. package/form-date-picker-multi/types/form-date-picker-multi.d.ts +22 -1
  17. package/form-date-range-picker/README.md +28 -27
  18. package/form-date-range-picker/fesm2022/form-date-range-picker.mjs +45 -15
  19. package/form-date-range-picker/types/form-date-range-picker.d.ts +22 -1
  20. package/form-field/README.md +9 -0
  21. package/form-field/fesm2022/form-field.mjs +97 -5
  22. package/form-field/types/form-field.d.ts +96 -2
  23. package/form-field-simple/fesm2022/form-field-simple.mjs +15 -1
  24. package/form-field-simple/types/form-field-simple.d.ts +9 -2
  25. package/form-input-otp/README.md +17 -16
  26. package/form-input-otp/fesm2022/form-input-otp.mjs +43 -8
  27. package/form-input-otp/types/form-input-otp.d.ts +22 -1
  28. package/form-month-year-picker/README.md +26 -25
  29. package/form-month-year-picker/fesm2022/form-month-year-picker.mjs +45 -15
  30. package/form-month-year-picker/types/form-month-year-picker.d.ts +22 -1
  31. package/form-native-select/README.md +18 -17
  32. package/form-native-select/fesm2022/form-native-select.mjs +44 -14
  33. package/form-native-select/types/form-native-select.d.ts +22 -1
  34. package/form-phone-input/README.md +23 -22
  35. package/form-phone-input/fesm2022/form-phone-input.mjs +44 -14
  36. package/form-phone-input/types/form-phone-input.d.ts +22 -1
  37. package/form-radio-group/README.md +18 -17
  38. package/form-radio-group/fesm2022/form-radio-group.mjs +44 -14
  39. package/form-radio-group/types/form-radio-group.d.ts +22 -1
  40. package/form-searchable-multiselect/README.md +18 -17
  41. package/form-searchable-multiselect/fesm2022/form-searchable-multiselect.mjs +44 -14
  42. package/form-searchable-multiselect/types/form-searchable-multiselect.d.ts +22 -1
  43. package/form-select/README.md +20 -19
  44. package/form-select/fesm2022/form-select.mjs +44 -14
  45. package/form-select/types/form-select.d.ts +22 -1
  46. package/form-slider/README.md +19 -18
  47. package/form-slider/fesm2022/form-slider.mjs +43 -8
  48. package/form-slider/types/form-slider.d.ts +22 -1
  49. package/form-switch/README.md +16 -15
  50. package/form-switch/fesm2022/form-switch.mjs +44 -14
  51. package/form-switch/types/form-switch.d.ts +22 -1
  52. package/form-text-input/README.md +27 -26
  53. package/form-text-input/fesm2022/form-text-input.mjs +44 -14
  54. package/form-text-input/types/form-text-input.d.ts +22 -1
  55. package/form-textarea/README.md +23 -22
  56. package/form-textarea/fesm2022/form-textarea.mjs +44 -14
  57. package/form-textarea/types/form-textarea.d.ts +22 -1
  58. package/form-toggle/README.md +17 -16
  59. package/form-toggle/fesm2022/form-toggle.mjs +47 -10
  60. package/form-toggle/types/form-toggle.d.ts +22 -3
  61. package/form-toggle-group/README.md +20 -19
  62. package/form-toggle-group/fesm2022/form-toggle-group.mjs +44 -14
  63. package/form-toggle-group/types/form-toggle-group.d.ts +22 -1
  64. package/package.json +5 -1
  65. package/stepper/README.md +353 -0
  66. package/stepper/fesm2022/stepper.mjs +396 -0
  67. package/stepper/package.json +24 -0
  68. package/stepper/types/stepper.d.ts +116 -0
@@ -70,23 +70,24 @@ Selector (from source): `eg-form-radio-group` (standalone component, host `tw:w-
70
70
 
71
71
  ### `EgFormRadioGroup` — `eg-form-radio-group`
72
72
 
73
- | Input | Type | Default | Description |
74
- | ------------- | ------------------------------- | ------- | ------------------------------------------------------------------ |
75
- | `controlName` | `string` | `''` | `formControlName` key inside the parent `FormGroup`. **Required.** |
76
- | `label` | `string \| undefined` | — | Group label text (hidden when omitted). |
77
- | `error` | `string \| undefined` | — | Error text shown when invalid. |
78
- | `hint` | `string \| undefined` | — | Hint text shown otherwise. |
79
- | `controlId` | `string \| undefined` | — | Explicit id prefix (first priority for `effectiveId`). |
80
- | `id` | `string \| undefined` | — | Fallback id prefix (second priority). |
81
- | `name` | `string \| undefined` | — | Native group `name` (`controlName` wins when set). |
82
- | `disabled` | `boolean` | `false` | Locks interaction (form control stays enabled). |
83
- | `required` | `boolean` | `false` | Shows a red `*` next to the label; forwarded to the group. |
84
- | `options` | `{ value, label, disabled? }[]` | `[]` | Rendered as labelled `hlm-radio` options. |
85
- | `class` | `ClassValue` | `''` | Extra host classes (base `tw:w-full`). |
86
- | `labelClass` | `string` | `''` | Extra group-label classes (base `tw:mb-1`). |
87
- | `groupClass` | `string` | `''` | Extra group classes (base `tw:flex tw:flex-col tw:gap-2`). |
88
- | `errorClass` | `string` | `''` | Extra error classes (base `tw:mt-0`). |
89
- | `hintClass` | `string` | `''` | Extra hint classes (base `tw:mt-0`). |
73
+ | Input | Type | Default | Description |
74
+ | ------------- | ------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
75
+ | `controlName` | `string` | `''` | `formControlName` key inside the parent `FormGroup`. **Required.** |
76
+ | `label` | `string \| undefined` | — | Group label text (hidden when omitted). |
77
+ | `error` | `string \| undefined` | — | Error text shown when invalid. |
78
+ | `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`. |
79
+ | `hint` | `string \| undefined` | — | Hint text shown otherwise. |
80
+ | `controlId` | `string \| undefined` | — | Explicit id prefix (first priority for `effectiveId`). |
81
+ | `id` | `string \| undefined` | — | Fallback id prefix (second priority). |
82
+ | `name` | `string \| undefined` | — | Native group `name` (`controlName` wins when set). |
83
+ | `disabled` | `boolean` | `false` | Locks interaction (form control stays enabled). |
84
+ | `required` | `boolean` | `false` | Shows a red `*` next to the label; forwarded to the group. |
85
+ | `options` | `{ value, label, disabled? }[]` | `[]` | Rendered as labelled `hlm-radio` options. |
86
+ | `class` | `ClassValue` | `''` | Extra host classes (base `tw:w-full`). |
87
+ | `labelClass` | `string` | `''` | Extra group-label classes (base `tw:mb-1`). |
88
+ | `groupClass` | `string` | `''` | Extra group classes (base `tw:flex tw:flex-col tw:gap-2`). |
89
+ | `errorClass` | `string` | `''` | Extra error classes (base `tw:mt-0`). |
90
+ | `hintClass` | `string` | `''` | Extra hint classes (base `tw:mt-0`). |
90
91
 
91
92
  | Member | Description |
92
93
  | -------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
@@ -2,7 +2,7 @@ import * as i0 from '@angular/core';
2
2
  import { InjectionToken, 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/form-field';
5
+ import { injectEgFormErrorMessages, HlmFormIdGenerator, injectEgFormSharedConfig, resolveEgFormError, HlmFormField, HlmError, HlmHint } from '@egose/shadcn-theme-ng/form-field';
6
6
  import { HlmLabel } from '@egose/shadcn-theme-ng/label';
7
7
  import { HlmRadioGroup, HlmRadio, HlmRadioIndicator } from '@egose/shadcn-theme-ng/radio-group';
8
8
  import { hlm } from '@egose/shadcn-theme-ng/utils';
@@ -17,8 +17,10 @@ function injectEgFormRadioGroupConfig() {
17
17
 
18
18
  class EgFormRadioGroup {
19
19
  formGroupDirective = inject(FormGroupDirective);
20
+ _errorMessages = injectEgFormErrorMessages();
20
21
  generatedId = inject(HlmFormIdGenerator).generate('eg-form-radio-group');
21
22
  _config = injectEgFormRadioGroupConfig();
23
+ _shared = injectEgFormSharedConfig();
22
24
  label = input(undefined, /* @ts-ignore */
23
25
  ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
24
26
  controlId = input(undefined, /* @ts-ignore */
@@ -27,6 +29,13 @@ class EgFormRadioGroup {
27
29
  ...(ngDevMode ? [{ debugName: "controlName" }] : /* istanbul ignore next */ []));
28
30
  error = input(undefined, /* @ts-ignore */
29
31
  ...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
32
+ /**
33
+ * Auto-resolve the displayed message from the control's `ValidationErrors`
34
+ * when `error()` is unset. Explicit `error()` always wins. Set to `false`
35
+ * for manual-only messages.
36
+ */
37
+ autoError = input(true, /* @ts-ignore */
38
+ ...(ngDevMode ? [{ debugName: "autoError" }] : /* istanbul ignore next */ []));
30
39
  hint = input(undefined, /* @ts-ignore */
31
40
  ...(ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []));
32
41
  id = input(undefined, /* @ts-ignore */
@@ -45,6 +54,32 @@ class EgFormRadioGroup {
45
54
  ...(ngDevMode ? [{ debugName: "errorId" }] : /* istanbul ignore next */ []));
46
55
  hintId = computed(() => `${this.effectiveId()}-hint`, /* @ts-ignore */
47
56
  ...(ngDevMode ? [{ debugName: "hintId" }] : /* istanbul ignore next */ []));
57
+ /**
58
+ * Displayed message: explicit `error()` wins; otherwise (when `autoError()`)
59
+ * auto-resolved from the control's `ValidationErrors`. Plain method (not a
60
+ * computed): `ValidationErrors` is not a signal, so the control must be read
61
+ * fresh on every change-detection pass.
62
+ */
63
+ resolvedError() {
64
+ const explicit = this.error();
65
+ if (explicit)
66
+ return explicit;
67
+ if (!this.autoError())
68
+ return undefined;
69
+ const control = this.formGroupDirective.form.get(this.controlName());
70
+ return resolveEgFormError(control?.errors ?? null, this.label(), this._errorMessages);
71
+ }
72
+ /**
73
+ * Whether the error is surfaced: message present + control invalid +
74
+ * touched/dirty/submitted. Gates the visual `<hlm-error>` (and
75
+ * `describedBy()`, where present) so sighted and screen-reader output agree.
76
+ */
77
+ showError() {
78
+ const control = this.formGroupDirective.form.get(this.controlName());
79
+ return (!!this.resolvedError() &&
80
+ !!control?.invalid &&
81
+ (control.dirty || control.touched || this.formGroupDirective.submitted));
82
+ }
48
83
  /**
49
84
  * Combined lock for the brain `linkedSignal`-based disabled state: the raw
50
85
  * input write always carries the resolved value, so a same-pass
@@ -54,12 +89,7 @@ class EgFormRadioGroup {
54
89
  return this.disabled() || !!this.formGroupDirective.form.get(this.controlName())?.disabled;
55
90
  }
56
91
  describedBy() {
57
- const control = this.formGroupDirective.form.get(this.controlName());
58
- return this.error() && control?.invalid && (control.dirty || control.touched)
59
- ? this.errorId()
60
- : this.hint()
61
- ? this.hintId()
62
- : null;
92
+ return this.showError() ? this.errorId() : this.hint() ? this.hintId() : null;
63
93
  }
64
94
  // Styling
65
95
  userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
@@ -74,16 +104,16 @@ class EgFormRadioGroup {
74
104
  // Computed classes (library base < global config < per-instance)
75
105
  $userClass = computed(() => hlm("w-full", this.userClass()), /* @ts-ignore */
76
106
  ...(ngDevMode ? [{ debugName: "$userClass" }] : /* istanbul ignore next */ []));
77
- $labelClass = computed(() => hlm("mb-1", this._config.labelClass, this.labelClass()), /* @ts-ignore */
107
+ $labelClass = computed(() => hlm("mb-1", this._shared.labelClass, this._config.labelClass, this.labelClass()), /* @ts-ignore */
78
108
  ...(ngDevMode ? [{ debugName: "$labelClass" }] : /* istanbul ignore next */ []));
79
- $groupClass = computed(() => hlm("flex flex-col gap-2", this._config.groupClass, this.groupClass()), /* @ts-ignore */
109
+ $groupClass = computed(() => hlm("flex flex-col gap-2", this._shared.groupClass, this._config.groupClass, this.groupClass()), /* @ts-ignore */
80
110
  ...(ngDevMode ? [{ debugName: "$groupClass" }] : /* istanbul ignore next */ []));
81
- $errorClass = computed(() => hlm("mt-0", this._config.errorClass, this.errorClass()), /* @ts-ignore */
111
+ $errorClass = computed(() => hlm("mt-0", this._shared.errorClass, this._config.errorClass, this.errorClass()), /* @ts-ignore */
82
112
  ...(ngDevMode ? [{ debugName: "$errorClass" }] : /* istanbul ignore next */ []));
83
- $hintClass = computed(() => hlm("mt-0", this._config.hintClass, this.hintClass()), /* @ts-ignore */
113
+ $hintClass = computed(() => hlm("mt-0", this._shared.hintClass, this._config.hintClass, this.hintClass()), /* @ts-ignore */
84
114
  ...(ngDevMode ? [{ debugName: "$hintClass" }] : /* istanbul ignore next */ []));
85
115
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgFormRadioGroup, deps: [], target: i0.ɵɵFactoryTarget.Component });
86
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgFormRadioGroup, isStandalone: true, selector: "eg-form-radio-group", 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 }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", 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 }, groupClass: { classPropertyName: "groupClass", publicName: "groupClass", 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: "\n @let lbl = label();\n @let cnm = controlName();\n @let err = error();\n @let hnt = hint();\n @let rqrd = required();\n\n <hlm-form-field>\n @if (lbl) {\n <span hlmLabel [class]=\"$labelClass()\"\n >{{ lbl }}\n @if (rqrd) {\n <span class=\"text-red-500\">*</span>\n }\n </span>\n }\n\n <hlm-radio-group\n [formControlName]=\"cnm\"\n [name]=\"cnm || name() || ''\"\n [disabled]=\"effectiveDisabled()\"\n [required]=\"rqrd\"\n [class]=\"$groupClass()\"\n >\n @for (option of options(); track option.value) {\n <label class=\"flex items-center gap-2\">\n <hlm-radio\n [value]=\"option.value\"\n [disabled]=\"option.disabled ?? false\"\n [aria-describedby]=\"describedBy() ?? undefined\"\n >\n <hlm-radio-indicator />\n </hlm-radio>\n <span>{{ option.label }}</span>\n </label>\n }\n </hlm-radio-group>\n\n @if (err) {\n <hlm-error [id]=\"errorId()\" [class]=\"$errorClass()\">\n {{ err }}\n </hlm-error>\n }\n\n @if (hnt) {\n <hlm-hint [id]=\"hintId()\" [class]=\"$hintClass()\">\n {{ hnt }}\n </hlm-hint>\n }\n </hlm-form-field>\n ", isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: HlmFormField, selector: "hlm-form-field", inputs: ["class"] }, { kind: "directive", type: HlmError, selector: "hlm-error", inputs: ["class"] }, { kind: "directive", type: HlmHint, selector: "hlm-hint", inputs: ["class"] }, { kind: "directive", type: HlmLabel, selector: "[hlmLabel]", inputs: ["class"] }, { kind: "directive", type: HlmRadioGroup, selector: "[hlmRadioGroup],hlm-radio-group", inputs: ["class"] }, { kind: "component", type: HlmRadio, selector: "hlm-radio", inputs: ["class", "inputId", "aria-label", "aria-labelledby", "aria-describedby", "value", "required", "disabled"], outputs: ["change"] }, { kind: "component", type: HlmRadioIndicator, selector: "hlm-radio-indicator" }] });
116
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgFormRadioGroup, isStandalone: true, selector: "eg-form-radio-group", 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 }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", 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 }, groupClass: { classPropertyName: "groupClass", publicName: "groupClass", 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: "\n @let lbl = label();\n @let cnm = controlName();\n @let hnt = hint();\n @let rqrd = required();\n\n <hlm-form-field>\n @if (lbl) {\n <span hlmLabel [class]=\"$labelClass()\"\n >{{ lbl }}\n @if (rqrd) {\n <span class=\"text-red-500\">*</span>\n }\n </span>\n }\n\n <hlm-radio-group\n [formControlName]=\"cnm\"\n [name]=\"cnm || name() || ''\"\n [disabled]=\"effectiveDisabled()\"\n [required]=\"rqrd\"\n [class]=\"$groupClass()\"\n >\n @for (option of options(); track option.value) {\n <label class=\"flex items-center gap-2\">\n <hlm-radio\n [value]=\"option.value\"\n [disabled]=\"option.disabled ?? false\"\n [aria-describedby]=\"describedBy() ?? undefined\"\n >\n <hlm-radio-indicator />\n </hlm-radio>\n <span>{{ option.label }}</span>\n </label>\n }\n </hlm-radio-group>\n\n @if (showError()) {\n <hlm-error [id]=\"errorId()\" [class]=\"$errorClass()\">\n {{ resolvedError() }}\n </hlm-error>\n }\n\n @if (hnt) {\n <hlm-hint [id]=\"hintId()\" [class]=\"$hintClass()\">\n {{ hnt }}\n </hlm-hint>\n }\n </hlm-form-field>\n ", isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: HlmFormField, selector: "hlm-form-field", inputs: ["class"] }, { kind: "directive", type: HlmError, selector: "hlm-error", inputs: ["class"] }, { kind: "directive", type: HlmHint, selector: "hlm-hint", inputs: ["class"] }, { kind: "directive", type: HlmLabel, selector: "[hlmLabel]", inputs: ["class"] }, { kind: "directive", type: HlmRadioGroup, selector: "[hlmRadioGroup],hlm-radio-group", inputs: ["class"] }, { kind: "component", type: HlmRadio, selector: "hlm-radio", inputs: ["class", "inputId", "aria-label", "aria-labelledby", "aria-describedby", "value", "required", "disabled"], outputs: ["change"] }, { kind: "component", type: HlmRadioIndicator, selector: "hlm-radio-indicator" }] });
87
117
  }
88
118
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgFormRadioGroup, decorators: [{
89
119
  type: Component,
@@ -95,9 +125,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
95
125
  },
96
126
  imports: [ReactiveFormsModule, HlmFormField, HlmError, HlmHint, HlmLabel, HlmRadioGroup, HlmRadio, HlmRadioIndicator],
97
127
  providers: [{ provide: ControlContainer, useExisting: FormGroupDirective }],
98
- template: "\n @let lbl = label();\n @let cnm = controlName();\n @let err = error();\n @let hnt = hint();\n @let rqrd = required();\n\n <hlm-form-field>\n @if (lbl) {\n <span hlmLabel [class]=\"$labelClass()\"\n >{{ lbl }}\n @if (rqrd) {\n <span class=\"text-red-500\">*</span>\n }\n </span>\n }\n\n <hlm-radio-group\n [formControlName]=\"cnm\"\n [name]=\"cnm || name() || ''\"\n [disabled]=\"effectiveDisabled()\"\n [required]=\"rqrd\"\n [class]=\"$groupClass()\"\n >\n @for (option of options(); track option.value) {\n <label class=\"flex items-center gap-2\">\n <hlm-radio\n [value]=\"option.value\"\n [disabled]=\"option.disabled ?? false\"\n [aria-describedby]=\"describedBy() ?? undefined\"\n >\n <hlm-radio-indicator />\n </hlm-radio>\n <span>{{ option.label }}</span>\n </label>\n }\n </hlm-radio-group>\n\n @if (err) {\n <hlm-error [id]=\"errorId()\" [class]=\"$errorClass()\">\n {{ err }}\n </hlm-error>\n }\n\n @if (hnt) {\n <hlm-hint [id]=\"hintId()\" [class]=\"$hintClass()\">\n {{ hnt }}\n </hlm-hint>\n }\n </hlm-form-field>\n ",
128
+ template: "\n @let lbl = label();\n @let cnm = controlName();\n @let hnt = hint();\n @let rqrd = required();\n\n <hlm-form-field>\n @if (lbl) {\n <span hlmLabel [class]=\"$labelClass()\"\n >{{ lbl }}\n @if (rqrd) {\n <span class=\"text-red-500\">*</span>\n }\n </span>\n }\n\n <hlm-radio-group\n [formControlName]=\"cnm\"\n [name]=\"cnm || name() || ''\"\n [disabled]=\"effectiveDisabled()\"\n [required]=\"rqrd\"\n [class]=\"$groupClass()\"\n >\n @for (option of options(); track option.value) {\n <label class=\"flex items-center gap-2\">\n <hlm-radio\n [value]=\"option.value\"\n [disabled]=\"option.disabled ?? false\"\n [aria-describedby]=\"describedBy() ?? undefined\"\n >\n <hlm-radio-indicator />\n </hlm-radio>\n <span>{{ option.label }}</span>\n </label>\n }\n </hlm-radio-group>\n\n @if (showError()) {\n <hlm-error [id]=\"errorId()\" [class]=\"$errorClass()\">\n {{ resolvedError() }}\n </hlm-error>\n }\n\n @if (hnt) {\n <hlm-hint [id]=\"hintId()\" [class]=\"$hintClass()\">\n {{ hnt }}\n </hlm-hint>\n }\n </hlm-form-field>\n ",
99
129
  }]
100
- }], 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 }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], labelClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelClass", required: false }] }], groupClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupClass", required: false }] }], errorClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorClass", required: false }] }], hintClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "hintClass", required: false }] }] } });
130
+ }], 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 }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], labelClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelClass", required: false }] }], groupClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "groupClass", required: false }] }], errorClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorClass", required: false }] }], hintClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "hintClass", required: false }] }] } });
101
131
 
102
132
  /*
103
133
  * Public API Surface of form-radio-group
@@ -9,12 +9,20 @@ interface RadioOption {
9
9
  }
10
10
  declare class EgFormRadioGroup {
11
11
  private readonly formGroupDirective;
12
+ private readonly _errorMessages;
12
13
  private readonly generatedId;
13
14
  private readonly _config;
15
+ private readonly _shared;
14
16
  label: _angular_core.InputSignal<string | undefined>;
15
17
  controlId: _angular_core.InputSignal<string | undefined>;
16
18
  controlName: _angular_core.InputSignal<string>;
17
19
  error: _angular_core.InputSignal<string | undefined>;
20
+ /**
21
+ * Auto-resolve the displayed message from the control's `ValidationErrors`
22
+ * when `error()` is unset. Explicit `error()` always wins. Set to `false`
23
+ * for manual-only messages.
24
+ */
25
+ autoError: _angular_core.InputSignal<boolean>;
18
26
  hint: _angular_core.InputSignal<string | undefined>;
19
27
  id: _angular_core.InputSignal<string | undefined>;
20
28
  name: _angular_core.InputSignal<string | undefined>;
@@ -24,6 +32,19 @@ declare class EgFormRadioGroup {
24
32
  readonly effectiveId: _angular_core.Signal<string>;
25
33
  readonly errorId: _angular_core.Signal<string>;
26
34
  readonly hintId: _angular_core.Signal<string>;
35
+ /**
36
+ * Displayed message: explicit `error()` wins; otherwise (when `autoError()`)
37
+ * auto-resolved from the control's `ValidationErrors`. Plain method (not a
38
+ * computed): `ValidationErrors` is not a signal, so the control must be read
39
+ * fresh on every change-detection pass.
40
+ */
41
+ protected resolvedError(): string | undefined;
42
+ /**
43
+ * Whether the error is surfaced: message present + control invalid +
44
+ * touched/dirty/submitted. Gates the visual `<hlm-error>` (and
45
+ * `describedBy()`, where present) so sighted and screen-reader output agree.
46
+ */
47
+ protected showError(): boolean;
27
48
  /**
28
49
  * Combined lock for the brain `linkedSignal`-based disabled state: the raw
29
50
  * input write always carries the resolved value, so a same-pass
@@ -42,7 +63,7 @@ declare class EgFormRadioGroup {
42
63
  $errorClass: _angular_core.Signal<string>;
43
64
  $hintClass: _angular_core.Signal<string>;
44
65
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EgFormRadioGroup, never>;
45
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<EgFormRadioGroup, "eg-form-radio-group", 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; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": false; "isSignal": true; }; "userClass": { "alias": "class"; "required": false; "isSignal": true; }; "labelClass": { "alias": "labelClass"; "required": false; "isSignal": true; }; "groupClass": { "alias": "groupClass"; "required": false; "isSignal": true; }; "errorClass": { "alias": "errorClass"; "required": false; "isSignal": true; }; "hintClass": { "alias": "hintClass"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
66
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EgFormRadioGroup, "eg-form-radio-group", 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; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": false; "isSignal": true; }; "userClass": { "alias": "class"; "required": false; "isSignal": true; }; "labelClass": { "alias": "labelClass"; "required": false; "isSignal": true; }; "groupClass": { "alias": "groupClass"; "required": false; "isSignal": true; }; "errorClass": { "alias": "errorClass"; "required": false; "isSignal": true; }; "hintClass": { "alias": "hintClass"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
46
67
  }
47
68
 
48
69
  interface EgFormRadioGroupConfig {
@@ -77,23 +77,24 @@ Real selectors involved: `eg-form-searchable-multiselect` (this wrapper), `eg-fo
77
77
 
78
78
  ### `eg-form-searchable-multiselect` — `EgFormSearchableMultiselect`
79
79
 
80
- | Input | Type | Default | Description |
81
- | --------------------- | --------------------- | ------------------------ | --------------------------------------------------------------------------------------------------- |
82
- | `label` | `string \| undefined` | `undefined` | Field label. When set, a `<label hlmLabel>` bound to the control id is rendered. |
83
- | `controlName` | `string` | `''` | **Required.** Name of the control in the parent `FormGroup`; forwarded as `formControlName`. |
84
- | `controlId` | `string \| undefined` | `undefined` | Explicit control id. Falls back to `id`, then to a generated `eg-form-searchable-multiselect-…` id. |
85
- | `id` | `string \| undefined` | `undefined` | Alias for an explicit control id (same fallback chain as `controlId`). |
86
- | `error` | `string \| undefined` | `undefined` | Error text rendered in `<hlm-error>`. |
87
- | `hint` | `string \| undefined` | `undefined` | Hint text rendered in `<hlm-hint>`. |
88
- | `placeholder` | `string` | `'Start typing to add…'` | Placeholder forwarded to the inner multiselect. |
89
- | `disabled` | `boolean` | `false` | Wrapper-level disable, forwarded as `wrapperDisabled` to the inner multiselect. |
90
- | `required` | `boolean` | `false` | Adds `required` to the inner control and renders a red `*` next to the label. |
91
- | `options` | `SelectOption[]` | `[]` | `{ label: string; value: string }[]` forwarded to the inner multiselect. |
92
- | `class` (`userClass`) | `ClassValue` | `''` | Extra classes for the host (merged over `tw:w-full`). |
93
- | `labelClass` | `string` | `''` | Extra classes for the `<label>` (merged over `tw:mb-1 tw:gap-0`). |
94
- | `controlClass` | `string` | `''` | Extra classes for the inner multiselect (merged over `tw:w-full`). |
95
- | `errorClass` | `string` | `''` | Extra classes for `<hlm-error>` (merged over `tw:mt-0`). |
96
- | `hintClass` | `string` | `''` | Extra classes for `<hlm-hint>` (merged over `tw:mt-0`). |
80
+ | Input | Type | Default | Description |
81
+ | --------------------- | --------------------- | ------------------------ | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
82
+ | `label` | `string \| undefined` | `undefined` | Field label. When set, a `<label hlmLabel>` bound to the control id is rendered. |
83
+ | `controlName` | `string` | `''` | **Required.** Name of the control in the parent `FormGroup`; forwarded as `formControlName`. |
84
+ | `controlId` | `string \| undefined` | `undefined` | Explicit control id. Falls back to `id`, then to a generated `eg-form-searchable-multiselect-…` id. |
85
+ | `id` | `string \| undefined` | `undefined` | Alias for an explicit control id (same fallback chain as `controlId`). |
86
+ | `error` | `string \| undefined` | `undefined` | Error text rendered in `<hlm-error>`. |
87
+ | `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`. |
88
+ | `hint` | `string \| undefined` | `undefined` | Hint text rendered in `<hlm-hint>`. |
89
+ | `placeholder` | `string` | `'Start typing to add…'` | Placeholder forwarded to the inner multiselect. |
90
+ | `disabled` | `boolean` | `false` | Wrapper-level disable, forwarded as `wrapperDisabled` to the inner multiselect. |
91
+ | `required` | `boolean` | `false` | Adds `required` to the inner control and renders a red `*` next to the label. |
92
+ | `options` | `SelectOption[]` | `[]` | `{ label: string; value: string }[]` forwarded to the inner multiselect. |
93
+ | `class` (`userClass`) | `ClassValue` | `''` | Extra classes for the host (merged over `tw:w-full`). |
94
+ | `labelClass` | `string` | `''` | Extra classes for the `<label>` (merged over `tw:mb-1 tw:gap-0`). |
95
+ | `controlClass` | `string` | `''` | Extra classes for the inner multiselect (merged over `tw:w-full`). |
96
+ | `errorClass` | `string` | `''` | Extra classes for `<hlm-error>` (merged over `tw:mt-0`). |
97
+ | `hintClass` | `string` | `''` | Extra classes for `<hlm-hint>` (merged over `tw:mt-0`). |
97
98
 
98
99
  No outputs. Readonly computeds/methods (usable via template ref): `effectiveId()`, `errorId()` (`<id>-error`), `hintId()` (`<id>-hint`), `describedBy(): string | null` (returns the error id when the control is invalid + dirty/touched, else the hint id, else `null`).
99
100
 
@@ -4,7 +4,7 @@ import * as i1 from '@angular/forms';
4
4
  import { FormGroupDirective, ReactiveFormsModule, ControlContainer } from '@angular/forms';
5
5
  import { HlmLabel } from '@egose/shadcn-theme-ng/label';
6
6
  import { EgFormField } from '@egose/shadcn-theme-ng/form-field-simple';
7
- import { HlmFormIdGenerator, HlmError, HlmHint } from '@egose/shadcn-theme-ng/form-field';
7
+ import { injectEgFormErrorMessages, HlmFormIdGenerator, injectEgFormSharedConfig, resolveEgFormError, HlmError, HlmHint } from '@egose/shadcn-theme-ng/form-field';
8
8
  import { hlm } from '@egose/shadcn-theme-ng/utils';
9
9
  import { EgSearchableMultiselect } from '@egose/shadcn-theme-ng/searchable-multiselect';
10
10
 
@@ -18,8 +18,10 @@ function injectEgFormSearchableMultiselectConfig() {
18
18
 
19
19
  class EgFormSearchableMultiselect {
20
20
  formGroupDirective = inject(FormGroupDirective);
21
+ _errorMessages = injectEgFormErrorMessages();
21
22
  generatedId = inject(HlmFormIdGenerator).generate('eg-form-searchable-multiselect');
22
23
  _config = injectEgFormSearchableMultiselectConfig();
24
+ _shared = injectEgFormSharedConfig();
23
25
  // Form field inputs
24
26
  label = input(undefined, /* @ts-ignore */
25
27
  ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
@@ -29,6 +31,13 @@ class EgFormSearchableMultiselect {
29
31
  ...(ngDevMode ? [{ debugName: "controlName" }] : /* istanbul ignore next */ [])); // required for form binding
30
32
  error = input(undefined, /* @ts-ignore */
31
33
  ...(ngDevMode ? [{ debugName: "error" }] : /* istanbul ignore next */ []));
34
+ /**
35
+ * Auto-resolve the displayed message from the control's `ValidationErrors`
36
+ * when `error()` is unset. Explicit `error()` always wins. Set to `false`
37
+ * for manual-only messages.
38
+ */
39
+ autoError = input(true, /* @ts-ignore */
40
+ ...(ngDevMode ? [{ debugName: "autoError" }] : /* istanbul ignore next */ []));
32
41
  hint = input(undefined, /* @ts-ignore */
33
42
  ...(ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []));
34
43
  // HTML/control attributes
@@ -46,13 +55,34 @@ class EgFormSearchableMultiselect {
46
55
  ...(ngDevMode ? [{ debugName: "errorId" }] : /* istanbul ignore next */ []));
47
56
  hintId = computed(() => `${this.effectiveId()}-hint`, /* @ts-ignore */
48
57
  ...(ngDevMode ? [{ debugName: "hintId" }] : /* istanbul ignore next */ []));
49
- describedBy() {
58
+ /**
59
+ * Displayed message: explicit `error()` wins; otherwise (when `autoError()`)
60
+ * auto-resolved from the control's `ValidationErrors`. Plain method (not a
61
+ * computed): `ValidationErrors` is not a signal, so the control must be read
62
+ * fresh on every change-detection pass.
63
+ */
64
+ resolvedError() {
65
+ const explicit = this.error();
66
+ if (explicit)
67
+ return explicit;
68
+ if (!this.autoError())
69
+ return undefined;
70
+ const control = this.formGroupDirective.form.get(this.controlName());
71
+ return resolveEgFormError(control?.errors ?? null, this.label(), this._errorMessages);
72
+ }
73
+ /**
74
+ * Whether the error is surfaced: message present + control invalid +
75
+ * touched/dirty/submitted. Gates the visual `<hlm-error>` (and
76
+ * `describedBy()`, where present) so sighted and screen-reader output agree.
77
+ */
78
+ showError() {
50
79
  const control = this.formGroupDirective.form.get(this.controlName());
51
- return this.error() && control?.invalid && (control.dirty || control.touched)
52
- ? this.errorId()
53
- : this.hint()
54
- ? this.hintId()
55
- : null;
80
+ return (!!this.resolvedError() &&
81
+ !!control?.invalid &&
82
+ (control.dirty || control.touched || this.formGroupDirective.submitted));
83
+ }
84
+ describedBy() {
85
+ return this.showError() ? this.errorId() : this.hint() ? this.hintId() : null;
56
86
  }
57
87
  // Multiselect-specific
58
88
  options = input([], /* @ts-ignore */
@@ -70,16 +100,16 @@ class EgFormSearchableMultiselect {
70
100
  // Computed class names (library base < global config < per-instance)
71
101
  $userClass = computed(() => hlm("w-full", this.userClass()), /* @ts-ignore */
72
102
  ...(ngDevMode ? [{ debugName: "$userClass" }] : /* istanbul ignore next */ []));
73
- $labelClass = computed(() => hlm("mb-1 gap-0", this._config.labelClass, this.labelClass()), /* @ts-ignore */
103
+ $labelClass = computed(() => hlm("mb-1 gap-0", this._shared.labelClass, this._config.labelClass, this.labelClass()), /* @ts-ignore */
74
104
  ...(ngDevMode ? [{ debugName: "$labelClass" }] : /* istanbul ignore next */ []));
75
- $controlClass = computed(() => hlm("w-full", this._config.controlClass, this.controlClass()), /* @ts-ignore */
105
+ $controlClass = computed(() => hlm("w-full", this._shared.controlClass, this._config.controlClass, this.controlClass()), /* @ts-ignore */
76
106
  ...(ngDevMode ? [{ debugName: "$controlClass" }] : /* istanbul ignore next */ []));
77
- $errorClass = computed(() => hlm("mt-0", this._config.errorClass, this.errorClass()), /* @ts-ignore */
107
+ $errorClass = computed(() => hlm("mt-0", this._shared.errorClass, this._config.errorClass, this.errorClass()), /* @ts-ignore */
78
108
  ...(ngDevMode ? [{ debugName: "$errorClass" }] : /* istanbul ignore next */ []));
79
- $hintClass = computed(() => hlm("mt-0", this._config.hintClass, this.hintClass()), /* @ts-ignore */
109
+ $hintClass = computed(() => hlm("mt-0", this._shared.hintClass, this._config.hintClass, this.hintClass()), /* @ts-ignore */
80
110
  ...(ngDevMode ? [{ debugName: "$hintClass" }] : /* istanbul ignore next */ []));
81
111
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgFormSearchableMultiselect, deps: [], target: i0.ɵɵFactoryTarget.Component });
82
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgFormSearchableMultiselect, isStandalone: true, selector: "eg-form-searchable-multiselect", 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 }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", 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 }, options: { classPropertyName: "options", publicName: "options", 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 }, controlClass: { classPropertyName: "controlClass", publicName: "controlClass", 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: "\n @let lbl = label();\n @let cnm = controlName();\n @let err = error();\n @let hnt = hint();\n @let rqrd = required();\n\n <eg-form-field>\n @if (lbl) {\n <label hlmLabel [for]=\"effectiveId()\" [class]=\"$labelClass()\">\n {{ lbl }}\n @if (rqrd) {\n <span class=\"text-red-500\">*</span>\n }\n </label>\n }\n\n <eg-searchable-multiselect\n [id]=\"effectiveId()\"\n [options]=\"options()\"\n [placeholder]=\"placeholder()\"\n [formControlName]=\"cnm\"\n [required]=\"rqrd\"\n [wrapperDisabled]=\"disabled()\"\n [ariaLabel]=\"lbl\"\n [ariaDescribedby]=\"describedBy()\"\n [class]=\"$controlClass()\"\n ></eg-searchable-multiselect>\n\n @if (err) {\n <hlm-error [id]=\"errorId()\" [class]=\"$errorClass()\">\n {{ err }}\n </hlm-error>\n }\n @if (hnt) {\n <hlm-hint [id]=\"hintId()\" [class]=\"$hintClass()\">\n {{ hnt }}\n </hlm-hint>\n }\n </eg-form-field>\n ", isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: EgFormField, selector: "eg-form-field", inputs: ["class"] }, { kind: "directive", type: HlmError, selector: "hlm-error", inputs: ["class"] }, { kind: "directive", type: HlmHint, selector: "hlm-hint", inputs: ["class"] }, { kind: "directive", type: HlmLabel, selector: "[hlmLabel]", inputs: ["class"] }, { kind: "component", type: EgSearchableMultiselect, selector: "eg-searchable-multiselect", inputs: ["options", "placeholder", "id", "disabled", "wrapperDisabled", "ariaLabel", "ariaDescribedby", "class", "value"], outputs: ["valueChange"] }] });
112
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgFormSearchableMultiselect, isStandalone: true, selector: "eg-form-searchable-multiselect", 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 }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", 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 }, options: { classPropertyName: "options", publicName: "options", 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 }, controlClass: { classPropertyName: "controlClass", publicName: "controlClass", 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: "\n @let lbl = label();\n @let cnm = controlName();\n @let hnt = hint();\n @let rqrd = required();\n\n <eg-form-field>\n @if (lbl) {\n <label hlmLabel [for]=\"effectiveId()\" [class]=\"$labelClass()\">\n {{ lbl }}\n @if (rqrd) {\n <span class=\"text-red-500\">*</span>\n }\n </label>\n }\n\n <eg-searchable-multiselect\n [id]=\"effectiveId()\"\n [options]=\"options()\"\n [placeholder]=\"placeholder()\"\n [formControlName]=\"cnm\"\n [required]=\"rqrd\"\n [wrapperDisabled]=\"disabled()\"\n [ariaLabel]=\"lbl\"\n [ariaDescribedby]=\"describedBy()\"\n [class]=\"$controlClass()\"\n ></eg-searchable-multiselect>\n\n @if (showError()) {\n <hlm-error [id]=\"errorId()\" [class]=\"$errorClass()\">\n {{ resolvedError() }}\n </hlm-error>\n }\n @if (hnt) {\n <hlm-hint [id]=\"hintId()\" [class]=\"$hintClass()\">\n {{ hnt }}\n </hlm-hint>\n }\n </eg-form-field>\n ", isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: EgFormField, selector: "eg-form-field", inputs: ["class"] }, { kind: "directive", type: HlmError, selector: "hlm-error", inputs: ["class"] }, { kind: "directive", type: HlmHint, selector: "hlm-hint", inputs: ["class"] }, { kind: "directive", type: HlmLabel, selector: "[hlmLabel]", inputs: ["class"] }, { kind: "component", type: EgSearchableMultiselect, selector: "eg-searchable-multiselect", inputs: ["options", "placeholder", "id", "disabled", "wrapperDisabled", "ariaLabel", "ariaDescribedby", "class", "value"], outputs: ["valueChange"] }] });
83
113
  }
84
114
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgFormSearchableMultiselect, decorators: [{
85
115
  type: Component,
@@ -91,9 +121,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
91
121
  },
92
122
  imports: [ReactiveFormsModule, EgFormField, HlmError, HlmHint, HlmLabel, EgSearchableMultiselect],
93
123
  providers: [{ provide: ControlContainer, useExisting: FormGroupDirective }],
94
- template: "\n @let lbl = label();\n @let cnm = controlName();\n @let err = error();\n @let hnt = hint();\n @let rqrd = required();\n\n <eg-form-field>\n @if (lbl) {\n <label hlmLabel [for]=\"effectiveId()\" [class]=\"$labelClass()\">\n {{ lbl }}\n @if (rqrd) {\n <span class=\"text-red-500\">*</span>\n }\n </label>\n }\n\n <eg-searchable-multiselect\n [id]=\"effectiveId()\"\n [options]=\"options()\"\n [placeholder]=\"placeholder()\"\n [formControlName]=\"cnm\"\n [required]=\"rqrd\"\n [wrapperDisabled]=\"disabled()\"\n [ariaLabel]=\"lbl\"\n [ariaDescribedby]=\"describedBy()\"\n [class]=\"$controlClass()\"\n ></eg-searchable-multiselect>\n\n @if (err) {\n <hlm-error [id]=\"errorId()\" [class]=\"$errorClass()\">\n {{ err }}\n </hlm-error>\n }\n @if (hnt) {\n <hlm-hint [id]=\"hintId()\" [class]=\"$hintClass()\">\n {{ hnt }}\n </hlm-hint>\n }\n </eg-form-field>\n ",
124
+ template: "\n @let lbl = label();\n @let cnm = controlName();\n @let hnt = hint();\n @let rqrd = required();\n\n <eg-form-field>\n @if (lbl) {\n <label hlmLabel [for]=\"effectiveId()\" [class]=\"$labelClass()\">\n {{ lbl }}\n @if (rqrd) {\n <span class=\"text-red-500\">*</span>\n }\n </label>\n }\n\n <eg-searchable-multiselect\n [id]=\"effectiveId()\"\n [options]=\"options()\"\n [placeholder]=\"placeholder()\"\n [formControlName]=\"cnm\"\n [required]=\"rqrd\"\n [wrapperDisabled]=\"disabled()\"\n [ariaLabel]=\"lbl\"\n [ariaDescribedby]=\"describedBy()\"\n [class]=\"$controlClass()\"\n ></eg-searchable-multiselect>\n\n @if (showError()) {\n <hlm-error [id]=\"errorId()\" [class]=\"$errorClass()\">\n {{ resolvedError() }}\n </hlm-error>\n }\n @if (hnt) {\n <hlm-hint [id]=\"hintId()\" [class]=\"$hintClass()\">\n {{ hnt }}\n </hlm-hint>\n }\n </eg-form-field>\n ",
95
125
  }]
96
- }], 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 }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], labelClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelClass", required: false }] }], controlClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlClass", required: false }] }], errorClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorClass", required: false }] }], hintClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "hintClass", required: false }] }] } });
126
+ }], 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 }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], labelClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelClass", required: false }] }], controlClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlClass", required: false }] }], errorClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorClass", required: false }] }], hintClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "hintClass", required: false }] }] } });
97
127
 
98
128
  /*
99
129
  * Public API Surface of form-searchable-multiselect
@@ -5,12 +5,20 @@ import { SelectOption } from '@egose/shadcn-theme-ng/searchable-multiselect';
5
5
 
6
6
  declare class EgFormSearchableMultiselect {
7
7
  private readonly formGroupDirective;
8
+ private readonly _errorMessages;
8
9
  private readonly generatedId;
9
10
  private readonly _config;
11
+ private readonly _shared;
10
12
  label: _angular_core.InputSignal<string | undefined>;
11
13
  controlId: _angular_core.InputSignal<string | undefined>;
12
14
  controlName: _angular_core.InputSignal<string>;
13
15
  error: _angular_core.InputSignal<string | undefined>;
16
+ /**
17
+ * Auto-resolve the displayed message from the control's `ValidationErrors`
18
+ * when `error()` is unset. Explicit `error()` always wins. Set to `false`
19
+ * for manual-only messages.
20
+ */
21
+ autoError: _angular_core.InputSignal<boolean>;
14
22
  hint: _angular_core.InputSignal<string | undefined>;
15
23
  id: _angular_core.InputSignal<string | undefined>;
16
24
  placeholder: _angular_core.InputSignal<string>;
@@ -19,6 +27,19 @@ declare class EgFormSearchableMultiselect {
19
27
  readonly effectiveId: _angular_core.Signal<string>;
20
28
  readonly errorId: _angular_core.Signal<string>;
21
29
  readonly hintId: _angular_core.Signal<string>;
30
+ /**
31
+ * Displayed message: explicit `error()` wins; otherwise (when `autoError()`)
32
+ * auto-resolved from the control's `ValidationErrors`. Plain method (not a
33
+ * computed): `ValidationErrors` is not a signal, so the control must be read
34
+ * fresh on every change-detection pass.
35
+ */
36
+ protected resolvedError(): string | undefined;
37
+ /**
38
+ * Whether the error is surfaced: message present + control invalid +
39
+ * touched/dirty/submitted. Gates the visual `<hlm-error>` (and
40
+ * `describedBy()`, where present) so sighted and screen-reader output agree.
41
+ */
42
+ protected showError(): boolean;
22
43
  describedBy(): string | null;
23
44
  options: _angular_core.InputSignal<SelectOption[]>;
24
45
  userClass: _angular_core.InputSignal<ClassValue>;
@@ -32,7 +53,7 @@ declare class EgFormSearchableMultiselect {
32
53
  $errorClass: _angular_core.Signal<string>;
33
54
  $hintClass: _angular_core.Signal<string>;
34
55
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EgFormSearchableMultiselect, never>;
35
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<EgFormSearchableMultiselect, "eg-form-searchable-multiselect", 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; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": false; "isSignal": true; }; "userClass": { "alias": "class"; "required": false; "isSignal": true; }; "labelClass": { "alias": "labelClass"; "required": false; "isSignal": true; }; "controlClass": { "alias": "controlClass"; "required": false; "isSignal": true; }; "errorClass": { "alias": "errorClass"; "required": false; "isSignal": true; }; "hintClass": { "alias": "hintClass"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
56
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EgFormSearchableMultiselect, "eg-form-searchable-multiselect", 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; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "options": { "alias": "options"; "required": false; "isSignal": true; }; "userClass": { "alias": "class"; "required": false; "isSignal": true; }; "labelClass": { "alias": "labelClass"; "required": false; "isSignal": true; }; "controlClass": { "alias": "controlClass"; "required": false; "isSignal": true; }; "errorClass": { "alias": "errorClass"; "required": false; "isSignal": true; }; "hintClass": { "alias": "hintClass"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
36
57
  }
37
58
 
38
59
  interface EgFormSearchableMultiselectConfig {
@@ -75,25 +75,26 @@ In multi mode the inner `brn-select` becomes `brn-select-multiple`; everything e
75
75
 
76
76
  ### `eg-form-select` — `EgFormSelect`
77
77
 
78
- | Input | Type | Default | Description |
79
- | --------------------- | --------------------- | ----------- | --------------------------------------------------------------------------------------------------------- |
80
- | `label` | `string \| undefined` | `undefined` | Field label rendered as `<label hlmLabel>` bound to the trigger button id. |
81
- | `controlName` | `string` | `''` | **Required.** Control name in the parent `FormGroup`; forwarded as `formControlName`. |
82
- | `controlId` | `string \| undefined` | `undefined` | Explicit id; falls back to `id`, then generated `eg-form-select-…`. |
83
- | `id` | `string \| undefined` | `undefined` | Alias for an explicit id (same fallback chain). Forwarded as `buttonId` to the select trigger. |
84
- | `error` | `string \| undefined` | `undefined` | Error text rendered in `<hlm-error>`. |
85
- | `hint` | `string \| undefined` | `undefined` | Hint text rendered in `<hlm-hint>`. |
86
- | `placeholder` | `string` | `''` | Placeholder forwarded to `<hlm-select-value>`. |
87
- | `disabled` | `boolean` | `false` | Forwarded as `wrapperDisabled` to `<hlm-select-trigger>`. |
88
- | `required` | `boolean` | `false` | Renders a red `*` next to the label (pair with `Validators.required`). |
89
- | `multiple` | `boolean` | `false` | When `true`, renders `brn-select-multiple` (value is `string[]`). |
90
- | `options` | `SelectOption[]` | `[]` | `{ value: string; label: string }[]` rendered as `<hlm-select-item>` rows. Local interface, not exported. |
91
- | `optionsLabel` | `string \| undefined` | `undefined` | Optional group heading rendered once as `<hlm-select-label>`. |
92
- | `class` (`userClass`) | `ClassValue` | `''` | Extra host classes (merged over `tw:flex tw:flex-col`). |
93
- | `labelClass` | `string` | `''` | Extra label classes (merged over `tw:mb-1 tw:gap-0`). |
94
- | `selectClass` | `string` | `''` | Extra trigger classes (merged over `tw:w-full`). |
95
- | `errorClass` | `string` | `''` | Extra error classes (merged over `tw:mt-0`). |
96
- | `hintClass` | `string` | `''` | Extra hint classes (merged over `tw:mt-0`). |
78
+ | Input | Type | Default | Description |
79
+ | --------------------- | --------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
80
+ | `label` | `string \| undefined` | `undefined` | Field label rendered as `<label hlmLabel>` bound to the trigger button id. |
81
+ | `controlName` | `string` | `''` | **Required.** Control name in the parent `FormGroup`; forwarded as `formControlName`. |
82
+ | `controlId` | `string \| undefined` | `undefined` | Explicit id; falls back to `id`, then generated `eg-form-select-…`. |
83
+ | `id` | `string \| undefined` | `undefined` | Alias for an explicit id (same fallback chain). Forwarded as `buttonId` to the select trigger. |
84
+ | `error` | `string \| undefined` | `undefined` | Error text rendered in `<hlm-error>`. |
85
+ | `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`. |
86
+ | `hint` | `string \| undefined` | `undefined` | Hint text rendered in `<hlm-hint>`. |
87
+ | `placeholder` | `string` | `''` | Placeholder forwarded to `<hlm-select-value>`. |
88
+ | `disabled` | `boolean` | `false` | Forwarded as `wrapperDisabled` to `<hlm-select-trigger>`. |
89
+ | `required` | `boolean` | `false` | Renders a red `*` next to the label (pair with `Validators.required`). |
90
+ | `multiple` | `boolean` | `false` | When `true`, renders `brn-select-multiple` (value is `string[]`). |
91
+ | `options` | `SelectOption[]` | `[]` | `{ value: string; label: string }[]` rendered as `<hlm-select-item>` rows. Local interface, not exported. |
92
+ | `optionsLabel` | `string \| undefined` | `undefined` | Optional group heading rendered once as `<hlm-select-label>`. |
93
+ | `class` (`userClass`) | `ClassValue` | `''` | Extra host classes (merged over `tw:flex tw:flex-col`). |
94
+ | `labelClass` | `string` | `''` | Extra label classes (merged over `tw:mb-1 tw:gap-0`). |
95
+ | `selectClass` | `string` | `''` | Extra trigger classes (merged over `tw:w-full`). |
96
+ | `errorClass` | `string` | `''` | Extra error classes (merged over `tw:mt-0`). |
97
+ | `hintClass` | `string` | `''` | Extra hint classes (merged over `tw:mt-0`). |
97
98
 
98
99
  No outputs. Readonly computeds/methods: `effectiveId()`, `errorId()`, `hintId()`, `describedBy(): string | null` (error id when invalid + dirty/touched, else hint id, else `null`).
99
100