@egose/shadcn-theme-ng-tw 0.6.0 → 0.6.2

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 (50) hide show
  1. package/README.md +119 -1
  2. package/autocomplete/fesm2022/autocomplete.mjs +14 -4
  3. package/autocomplete/types/autocomplete.d.ts +4 -1
  4. package/checkbox/README.md +6 -2
  5. package/checkbox/fesm2022/checkbox.mjs +15 -9
  6. package/checkbox/types/checkbox.d.ts +6 -4
  7. package/combobox/fesm2022/combobox.mjs +22 -6
  8. package/combobox/types/combobox.d.ts +8 -2
  9. package/data-table/README.md +11 -1
  10. package/data-table/fesm2022/data-table.mjs +76 -26
  11. package/data-table/types/data-table.d.ts +25 -6
  12. package/date-picker/README.md +41 -9
  13. package/date-picker/fesm2022/date-picker.mjs +265 -59
  14. package/date-picker/types/date-picker.d.ts +58 -27
  15. package/form-autocomplete/README.md +4 -2
  16. package/form-autocomplete/fesm2022/form-autocomplete.mjs +33 -8
  17. package/form-autocomplete/types/form-autocomplete.d.ts +9 -3
  18. package/form-combobox/README.md +4 -2
  19. package/form-combobox/fesm2022/form-combobox.mjs +65 -14
  20. package/form-combobox/types/form-combobox.d.ts +10 -3
  21. package/form-input-otp/README.md +7 -5
  22. package/form-input-otp/fesm2022/form-input-otp.mjs +28 -14
  23. package/form-input-otp/types/form-input-otp.d.ts +9 -3
  24. package/form-searchable-multiselect/README.md +15 -1
  25. package/form-searchable-multiselect/fesm2022/form-searchable-multiselect.mjs +23 -3
  26. package/form-searchable-multiselect/types/form-searchable-multiselect.d.ts +9 -1
  27. package/form-slider/README.md +3 -1
  28. package/form-slider/fesm2022/form-slider.mjs +21 -6
  29. package/form-slider/types/form-slider.d.ts +9 -3
  30. package/form-toggle/README.md +22 -18
  31. package/form-toggle/fesm2022/form-toggle.mjs +25 -8
  32. package/form-toggle/types/form-toggle.d.ts +6 -2
  33. package/input-otp/README.md +33 -9
  34. package/input-otp/fesm2022/input-otp.mjs +127 -11
  35. package/input-otp/types/input-otp.d.ts +24 -4
  36. package/package.json +1 -1
  37. package/pagination/README.md +21 -12
  38. package/pagination/fesm2022/pagination.mjs +83 -75
  39. package/pagination/types/pagination.d.ts +21 -8
  40. package/phone-input/README.md +16 -0
  41. package/phone-input/fesm2022/phone-input.mjs +41 -3
  42. package/phone-input/types/phone-input.d.ts +5 -2
  43. package/searchable-multiselect/README.md +62 -40
  44. package/searchable-multiselect/fesm2022/searchable-multiselect.mjs +97 -36
  45. package/searchable-multiselect/types/searchable-multiselect.d.ts +23 -6
  46. package/slider/fesm2022/slider.mjs +15 -5
  47. package/slider/types/slider.d.ts +6 -2
  48. package/stepper/README.md +39 -19
  49. package/stepper/fesm2022/stepper.mjs +71 -17
  50. package/stepper/types/stepper.d.ts +12 -1
@@ -87,6 +87,18 @@ class EgFormSearchableMultiselect {
87
87
  // Multiselect-specific
88
88
  options = input([], /* @ts-ignore */
89
89
  ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ [])); // [{ label, value }]
90
+ /** Visible native search label; local label filtering preserves every selected form value. */
91
+ searchLabel = input('Search options', /* @ts-ignore */
92
+ ...(ngDevMode ? [{ debugName: "searchLabel" }] : /* istanbul ignore next */ []));
93
+ /** Search hint forwarded independently of the empty-selection placeholder. */
94
+ searchPlaceholder = input('Type to filter…', /* @ts-ignore */
95
+ ...(ngDevMode ? [{ debugName: "searchPlaceholder" }] : /* istanbul ignore next */ []));
96
+ /** Polite live-region copy for an empty filtered option list. */
97
+ emptyMessage = input('No matching options', /* @ts-ignore */
98
+ ...(ngDevMode ? [{ debugName: "emptyMessage" }] : /* istanbul ignore next */ []));
99
+ /** Pure accessible chip-remove name formatter; receives the resolved label or raw unresolved ID. */
100
+ removeLabel = input((option) => `Remove ${option.label}`, /* @ts-ignore */
101
+ ...(ngDevMode ? [{ debugName: "removeLabel" }] : /* istanbul ignore next */ []));
90
102
  // Styling classes
91
103
  userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
92
104
  labelClass = input('', /* @ts-ignore */
@@ -109,7 +121,7 @@ class EgFormSearchableMultiselect {
109
121
  $hintClass = computed(() => hlm('tw:mt-0', this._shared.hintClass, this._config.hintClass, this.hintClass()), /* @ts-ignore */
110
122
  ...(ngDevMode ? [{ debugName: "$hintClass" }] : /* istanbul ignore next */ []));
111
123
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgFormSearchableMultiselect, deps: [], target: i0.ɵɵFactoryTarget.Component });
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: `
124
+ 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 }, searchLabel: { classPropertyName: "searchLabel", publicName: "searchLabel", isSignal: true, isRequired: false, transformFunction: null }, searchPlaceholder: { classPropertyName: "searchPlaceholder", publicName: "searchPlaceholder", isSignal: true, isRequired: false, transformFunction: null }, emptyMessage: { classPropertyName: "emptyMessage", publicName: "emptyMessage", isSignal: true, isRequired: false, transformFunction: null }, removeLabel: { classPropertyName: "removeLabel", publicName: "removeLabel", 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: `
113
125
  @let lbl = label();
114
126
  @let cnm = controlName();
115
127
  @let hnt = hint();
@@ -129,6 +141,10 @@ class EgFormSearchableMultiselect {
129
141
  [id]="effectiveId()"
130
142
  [options]="options()"
131
143
  [placeholder]="placeholder()"
144
+ [searchLabel]="searchLabel()"
145
+ [searchPlaceholder]="searchPlaceholder()"
146
+ [emptyMessage]="emptyMessage()"
147
+ [removeLabel]="removeLabel()"
132
148
  [formControlName]="cnm"
133
149
  [required]="rqrd"
134
150
  [wrapperDisabled]="disabled()"
@@ -148,7 +164,7 @@ class EgFormSearchableMultiselect {
148
164
  </hlm-hint>
149
165
  }
150
166
  </eg-form-field>
151
- `, 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"] }] });
167
+ `, 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", "searchLabel", "searchPlaceholder", "emptyMessage", "removeLabel", "id", "disabled", "wrapperDisabled", "ariaLabel", "ariaDescribedby", "class", "value"], outputs: ["valueChange"] }] });
152
168
  }
153
169
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgFormSearchableMultiselect, decorators: [{
154
170
  type: Component,
@@ -180,6 +196,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
180
196
  [id]="effectiveId()"
181
197
  [options]="options()"
182
198
  [placeholder]="placeholder()"
199
+ [searchLabel]="searchLabel()"
200
+ [searchPlaceholder]="searchPlaceholder()"
201
+ [emptyMessage]="emptyMessage()"
202
+ [removeLabel]="removeLabel()"
183
203
  [formControlName]="cnm"
184
204
  [required]="rqrd"
185
205
  [wrapperDisabled]="disabled()"
@@ -201,7 +221,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
201
221
  </eg-form-field>
202
222
  `,
203
223
  }]
204
- }], 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 }] }] } });
224
+ }], 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 }] }], searchLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchLabel", required: false }] }], searchPlaceholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "searchPlaceholder", required: false }] }], emptyMessage: [{ type: i0.Input, args: [{ isSignal: true, alias: "emptyMessage", required: false }] }], removeLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "removeLabel", 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 }] }] } });
205
225
 
206
226
  /*
207
227
  * Public API Surface of form-searchable-multiselect
@@ -42,6 +42,14 @@ declare class EgFormSearchableMultiselect {
42
42
  protected showError(): boolean;
43
43
  describedBy(): string | null;
44
44
  options: _angular_core.InputSignal<SelectOption[]>;
45
+ /** Visible native search label; local label filtering preserves every selected form value. */
46
+ searchLabel: _angular_core.InputSignal<string>;
47
+ /** Search hint forwarded independently of the empty-selection placeholder. */
48
+ searchPlaceholder: _angular_core.InputSignal<string>;
49
+ /** Polite live-region copy for an empty filtered option list. */
50
+ emptyMessage: _angular_core.InputSignal<string>;
51
+ /** Pure accessible chip-remove name formatter; receives the resolved label or raw unresolved ID. */
52
+ removeLabel: _angular_core.InputSignal<(option: SelectOption) => string>;
45
53
  userClass: _angular_core.InputSignal<ClassValue>;
46
54
  labelClass: _angular_core.InputSignal<string>;
47
55
  controlClass: _angular_core.InputSignal<string>;
@@ -53,7 +61,7 @@ declare class EgFormSearchableMultiselect {
53
61
  $errorClass: _angular_core.Signal<string>;
54
62
  $hintClass: _angular_core.Signal<string>;
55
63
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EgFormSearchableMultiselect, 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>;
64
+ 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; }; "searchLabel": { "alias": "searchLabel"; "required": false; "isSignal": true; }; "searchPlaceholder": { "alias": "searchPlaceholder"; "required": false; "isSignal": true; }; "emptyMessage": { "alias": "emptyMessage"; "required": false; "isSignal": true; }; "removeLabel": { "alias": "removeLabel"; "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>;
57
65
  }
58
66
 
59
67
  interface EgFormSearchableMultiselectConfig {
@@ -158,7 +158,9 @@ export class PriceComponent {
158
158
  ## Accessibility notes
159
159
 
160
160
  - The label is linked via `aria-labelledby` (sliders have no `for`-compatible control); always pass a `label`.
161
- - `BrnSlider` currently accepts but does not render `aria-labelledby` on its host — the binding documents intent and activates if upstream renders it. Error/hint ids render without an input-level `describedby` link for the same reason.
161
+ - Each focusable thumb carries `aria-labelledby`, form-control `aria-invalid`, and `aria-describedby` for the displayed error (invalid + touched/dirty/submitted), otherwise the hint. Correction/reset restores the hint, including range sliders.
162
+ - Optional `[aria-describedby]` (`string | null`, default `null`) adds consumer-owned IDs before the current message ID, normalized and deduplicated. Keep external elements mounted and reserve `<effectiveId>-error` / `-hint` for the wrapper. Bind the input rather than `[attr.aria-describedby]` on the wrapper host.
163
+ - The `required` asterisk remains visual; configure validators independently. The slider role does not support `aria-required`. `HlmError` is not a live region; automated DOM/focus coverage does not establish screen-reader announcement behavior.
162
164
 
163
165
  ## Theming / CSS variables
164
166
 
@@ -1,11 +1,12 @@
1
1
  import * as i0 from '@angular/core';
2
- import { InjectionToken, inject, input, numberAttribute, computed, Component } from '@angular/core';
2
+ import { InjectionToken, inject, ChangeDetectorRef, DestroyRef, 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
5
  import { injectEgFormErrorMessages, HlmFormIdGenerator, injectEgFormSharedConfig, 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 { HlmSlider } from '@egose/shadcn-theme-ng-tw/slider';
8
8
  import { hlm } from '@egose/shadcn-theme-ng-tw/utils';
9
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
9
10
 
10
11
  const EgFormSliderConfigToken = new InjectionToken('EgFormSliderConfig');
11
12
  function provideEgFormSliderConfig(config) {
@@ -21,6 +22,14 @@ class EgFormSlider {
21
22
  generatedId = inject(HlmFormIdGenerator).generate('eg-form-slider');
22
23
  _config = injectEgFormSliderConfig();
23
24
  _shared = injectEgFormSharedConfig();
25
+ _cdr = inject(ChangeDetectorRef);
26
+ _destroyRef = inject(DestroyRef);
27
+ ngOnInit() {
28
+ // Submit/reset may change message visibility without changing the field's value or validity.
29
+ this.formGroupDirective.form.events
30
+ .pipe(takeUntilDestroyed(this._destroyRef))
31
+ .subscribe(() => this._cdr.markForCheck());
32
+ }
24
33
  label = input(undefined, /* @ts-ignore */
25
34
  ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
26
35
  controlId = input(undefined, /* @ts-ignore */
@@ -38,6 +47,8 @@ class EgFormSlider {
38
47
  ...(ngDevMode ? [{ debugName: "autoError" }] : /* istanbul ignore next */ []));
39
48
  hint = input(undefined, /* @ts-ignore */
40
49
  ...(ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []));
50
+ /** Consumer-owned description IDs, merged with the current error and hint IDs on every thumb. */
51
+ ariaDescribedBy = input(null, { ...(ngDevMode ? { debugName: "ariaDescribedBy" } : /* istanbul ignore next */ {}), alias: 'aria-describedby' });
41
52
  id = input(undefined, /* @ts-ignore */
42
53
  ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
43
54
  disabled = input(false, /* @ts-ignore */
@@ -89,8 +100,10 @@ class EgFormSlider {
89
100
  effectiveDisabled() {
90
101
  return this.disabled() || !!this.formGroupDirective.form.get(this.controlName())?.disabled;
91
102
  }
92
- // NOTE: BrnSlider exposes aria-labelledby but no aria-describedby, so error/hint
93
- // ids render for sighted users without an input-level describedby link.
103
+ describedBy() {
104
+ const ids = [this.ariaDescribedBy(), this.showError() ? this.errorId() : this.hint() ? this.hintId() : null];
105
+ return [...new Set(ids.filter(Boolean).join(' ').split(/\s+/).filter(Boolean))].join(' ') || null;
106
+ }
94
107
  // Styling
95
108
  userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
96
109
  labelClass = input('', /* @ts-ignore */
@@ -113,7 +126,7 @@ class EgFormSlider {
113
126
  $hintClass = computed(() => hlm('tw:mt-0', this._shared.hintClass, this._config.hintClass, this.hintClass()), /* @ts-ignore */
114
127
  ...(ngDevMode ? [{ debugName: "$hintClass" }] : /* istanbul ignore next */ []));
115
128
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgFormSlider, deps: [], target: i0.ɵɵFactoryTarget.Component });
116
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgFormSlider, isStandalone: true, selector: "eg-form-slider", 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 }, 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 }, step: { classPropertyName: "step", publicName: "step", 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 }, sliderClass: { classPropertyName: "sliderClass", publicName: "sliderClass", 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: `
129
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgFormSlider, isStandalone: true, selector: "eg-form-slider", 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 }, ariaDescribedBy: { classPropertyName: "ariaDescribedBy", publicName: "aria-describedby", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", 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 }, step: { classPropertyName: "step", publicName: "step", 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 }, sliderClass: { classPropertyName: "sliderClass", publicName: "sliderClass", 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: `
117
130
  @let lbl = label();
118
131
  @let cnm = controlName();
119
132
  @let hnt = hint();
@@ -137,6 +150,7 @@ class EgFormSlider {
137
150
  [step]="step()"
138
151
  [disabled]="effectiveDisabled()"
139
152
  [aria-labelledby]="lbl ? labelId() : null"
153
+ [aria-describedby]="describedBy()"
140
154
  [class]="$sliderClass()"
141
155
  />
142
156
 
@@ -152,7 +166,7 @@ class EgFormSlider {
152
166
  </hlm-hint>
153
167
  }
154
168
  </hlm-form-field>
155
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { 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: "component", type: HlmSlider, selector: "hlm-slider, brn-slider [hlm]" }] });
169
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { 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: "component", type: HlmSlider, selector: "hlm-slider, brn-slider [hlm]", inputs: ["aria-describedby"] }] });
156
170
  }
157
171
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgFormSlider, decorators: [{
158
172
  type: Component,
@@ -188,6 +202,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
188
202
  [step]="step()"
189
203
  [disabled]="effectiveDisabled()"
190
204
  [aria-labelledby]="lbl ? labelId() : null"
205
+ [aria-describedby]="describedBy()"
191
206
  [class]="$sliderClass()"
192
207
  />
193
208
 
@@ -205,7 +220,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
205
220
  </hlm-form-field>
206
221
  `,
207
222
  }]
208
- }], 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 }] }], 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 }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], labelClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelClass", required: false }] }], sliderClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "sliderClass", required: false }] }], errorClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorClass", required: false }] }], hintClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "hintClass", required: false }] }] } });
223
+ }], 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 }] }], ariaDescribedBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-describedby", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", 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 }] }], step: [{ type: i0.Input, args: [{ isSignal: true, alias: "step", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], labelClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelClass", required: false }] }], sliderClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "sliderClass", required: false }] }], errorClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorClass", required: false }] }], hintClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "hintClass", required: false }] }] } });
209
224
 
210
225
  /*
211
226
  * Public API Surface of form-slider
@@ -1,14 +1,17 @@
1
1
  import * as _angular_core from '@angular/core';
2
- import { ValueProvider } from '@angular/core';
2
+ import { OnInit, ValueProvider } from '@angular/core';
3
3
  import { ClassValue } from 'clsx';
4
4
  import { NumberInput } from '@angular/cdk/coercion';
5
5
 
6
- declare class EgFormSlider {
6
+ declare class EgFormSlider implements OnInit {
7
7
  private readonly formGroupDirective;
8
8
  private readonly _errorMessages;
9
9
  private readonly generatedId;
10
10
  private readonly _config;
11
11
  private readonly _shared;
12
+ private readonly _cdr;
13
+ private readonly _destroyRef;
14
+ ngOnInit(): void;
12
15
  label: _angular_core.InputSignal<string | undefined>;
13
16
  controlId: _angular_core.InputSignal<string | undefined>;
14
17
  controlName: _angular_core.InputSignal<string>;
@@ -20,6 +23,8 @@ declare class EgFormSlider {
20
23
  */
21
24
  autoError: _angular_core.InputSignal<boolean>;
22
25
  hint: _angular_core.InputSignal<string | undefined>;
26
+ /** Consumer-owned description IDs, merged with the current error and hint IDs on every thumb. */
27
+ ariaDescribedBy: _angular_core.InputSignal<string | null>;
23
28
  id: _angular_core.InputSignal<string | undefined>;
24
29
  disabled: _angular_core.InputSignal<boolean>;
25
30
  required: _angular_core.InputSignal<boolean>;
@@ -49,6 +54,7 @@ declare class EgFormSlider {
49
54
  * wrapper-toggle + `control.disable()` cannot clobber CVA state.
50
55
  */
51
56
  effectiveDisabled(): boolean;
57
+ protected describedBy(): string | null;
52
58
  userClass: _angular_core.InputSignal<ClassValue>;
53
59
  labelClass: _angular_core.InputSignal<string>;
54
60
  sliderClass: _angular_core.InputSignal<string>;
@@ -60,7 +66,7 @@ declare class EgFormSlider {
60
66
  $errorClass: _angular_core.Signal<string>;
61
67
  $hintClass: _angular_core.Signal<string>;
62
68
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EgFormSlider, never>;
63
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<EgFormSlider, "eg-form-slider", 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; }; "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; }; "step": { "alias": "step"; "required": false; "isSignal": true; }; "userClass": { "alias": "class"; "required": false; "isSignal": true; }; "labelClass": { "alias": "labelClass"; "required": false; "isSignal": true; }; "sliderClass": { "alias": "sliderClass"; "required": false; "isSignal": true; }; "errorClass": { "alias": "errorClass"; "required": false; "isSignal": true; }; "hintClass": { "alias": "hintClass"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
69
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EgFormSlider, "eg-form-slider", 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; }; "ariaDescribedBy": { "alias": "aria-describedby"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "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; }; "step": { "alias": "step"; "required": false; "isSignal": true; }; "userClass": { "alias": "class"; "required": false; "isSignal": true; }; "labelClass": { "alias": "labelClass"; "required": false; "isSignal": true; }; "sliderClass": { "alias": "sliderClass"; "required": false; "isSignal": true; }; "errorClass": { "alias": "errorClass"; "required": false; "isSignal": true; }; "hintClass": { "alias": "hintClass"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
64
70
  }
65
71
 
66
72
  interface EgFormSliderConfig {
@@ -77,23 +77,24 @@ Selector (from source): `eg-form-toggle` (standalone component, host `tw:w-full`
77
77
 
78
78
  Bind `formControlName` on the element; there is no `controlName` input.
79
79
 
80
- | Input | Type | Default | Description |
81
- | ------------- | --------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
82
- | `label` | `string \| undefined` | — | Label text beside the toggle (hidden when omitted). |
83
- | `error` | `string \| undefined` | — | Error text shown when invalid. |
84
- | `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`. |
85
- | `hint` | `string \| undefined` | — | Hint text shown otherwise. |
86
- | `controlId` | `string \| undefined` | — | Explicit id (first priority for `effectiveId`). |
87
- | `id` | `string \| undefined` | — | Fallback id (second priority). |
88
- | `disabled` | `boolean` | `false` | Locks interaction (form control stays enabled). |
89
- | `required` | `boolean` | `false` | Shows a red `*` next to the label. |
90
- | `variant` | `'default' \| 'outline'` | `'default'` | Toggle variant forwarded to `hlmToggle`. |
91
- | `size` | `'default' \| 'sm' \| 'lg'` | `'default'` | Toggle size forwarded to `hlmToggle`. |
92
- | `class` | `ClassValue` | `''` | Extra host classes (base `tw:w-full`). |
93
- | `toggleClass` | `string` | `''` | Extra toggle-button classes. |
94
- | `labelClass` | `string` | `''` | Extra label classes. |
95
- | `errorClass` | `string` | `''` | Extra error classes (base `tw:mt-0`). |
96
- | `hintClass` | `string` | `''` | Extra hint classes (base `tw:mt-0`). |
80
+ | Input | Type | Default | Description |
81
+ | ------------------ | --------------------------- | ----------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
82
+ | `label` | `string \| undefined` | — | Label text beside the toggle (hidden when omitted). |
83
+ | `error` | `string \| undefined` | — | Error text shown when invalid. |
84
+ | `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`. |
85
+ | `hint` | `string \| undefined` | — | Hint text shown otherwise. |
86
+ | `aria-describedby` | `string \| null` | `null` | Consumer-owned description IDs merged with the displayed error or hint on the native button. |
87
+ | `controlId` | `string \| undefined` | — | Explicit id (first priority for `effectiveId`). |
88
+ | `id` | `string \| undefined` | — | Fallback id (second priority). |
89
+ | `disabled` | `boolean` | `false` | Locks interaction (form control stays enabled). |
90
+ | `required` | `boolean` | `false` | Shows a red `*` next to the label. |
91
+ | `variant` | `'default' \| 'outline'` | `'default'` | Toggle variant forwarded to `hlmToggle`. |
92
+ | `size` | `'default' \| 'sm' \| 'lg'` | `'default'` | Toggle size forwarded to `hlmToggle`. |
93
+ | `class` | `ClassValue` | `''` | Extra host classes (base `tw:w-full`). |
94
+ | `toggleClass` | `string` | `''` | Extra toggle-button classes. |
95
+ | `labelClass` | `string` | `''` | Extra label classes. |
96
+ | `errorClass` | `string` | `''` | Extra error classes (base `tw:mt-0`). |
97
+ | `hintClass` | `string` | `''` | Extra hint classes (base `tw:mt-0`). |
97
98
 
98
99
  | Member | Description |
99
100
  | -------------------- | ------------------------------------------------------------------------------ |
@@ -163,7 +164,10 @@ export class ConsentToggleComponent {
163
164
  ## Accessibility notes
164
165
 
165
166
  - Label `for` ↔ button `id` wiring is automatic via `effectiveId` — always pass a `label`.
166
- - The required `*` is visual; pair with `Validators.requiredTrue` so assistive tech and validation agree.
167
+ - The actual toggle button exposes `aria-pressed` and references the **currently displayed error or hint** with `aria-describedby`. Touch/dirty-only changes and submission update the description; correction/reset restore the hint when present. Removing a hint removes its reference.
168
+ - Pass consumer-owned IDs with `[aria-describedby]="'notification-help notification-policy'"`. They precede the active wrapper message ID; whitespace is normalized and duplicate IDs are removed. Keep those external elements mounted. `${effectiveId}-error` and `${effectiveId}-hint` are reserved for wrapper messages; do not supply them as consumer IDs.
169
+ - The button exposes `aria-invalid="true"` while its validation error is displayed and removes the attribute when feedback clears. The native label association and pressed state remain independent of validation.
170
+ - The required `*` is visual; configure `Validators.requiredTrue` independently. Toggle buttons do not support `aria-required`.
167
171
 
168
172
  ## Theming / CSS variables
169
173
 
@@ -53,6 +53,8 @@ class EgFormToggle {
53
53
  ...(ngDevMode ? [{ debugName: "autoError" }] : /* istanbul ignore next */ []));
54
54
  hint = input(undefined, /* @ts-ignore */
55
55
  ...(ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []));
56
+ /** Consumer-owned description IDs, merged with the displayed error or hint on the button. */
57
+ ariaDescribedBy = input(null, { ...(ngDevMode ? { debugName: "ariaDescribedBy" } : /* istanbul ignore next */ {}), alias: 'aria-describedby' });
56
58
  id = input(undefined, /* @ts-ignore */
57
59
  ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
58
60
  disabled = input(false, { ...(ngDevMode ? { debugName: "disabled" } : /* istanbul ignore next */ {}), transform: booleanAttribute });
@@ -78,12 +80,14 @@ class EgFormToggle {
78
80
  ...(ngDevMode ? [{ debugName: "_status" }] : /* istanbul ignore next */ []));
79
81
  _submitted = signal(false, /* @ts-ignore */
80
82
  ...(ngDevMode ? [{ debugName: "_submitted" }] : /* istanbul ignore next */ []));
83
+ _interacted = signal(false, /* @ts-ignore */
84
+ ...(ngDevMode ? [{ debugName: "_interacted" }] : /* istanbul ignore next */ []));
81
85
  /**
82
86
  * Displayed message: explicit `error()` wins; otherwise (when `autoError()`)
83
87
  * auto-resolved from the control's `ValidationErrors`. Plain method (not a
84
88
  * computed): `ValidationErrors` is not a signal, so the control must be read
85
89
  * fresh on every change-detection pass (`showError()` re-evaluates with it
86
- * via the `_status`/`_submitted` signals).
90
+ * via the `_status`/`_submitted`/`_interacted` signals).
87
91
  */
88
92
  resolvedError() {
89
93
  const explicit = this.error();
@@ -96,20 +100,26 @@ class EgFormToggle {
96
100
  showError = computed(() => {
97
101
  this._status();
98
102
  this._submitted();
103
+ this._interacted();
99
104
  const control = this._controlDir?.control;
100
- return (!!this.resolvedError() &&
101
- !!control?.errors &&
102
- (control.touched || control.dirty || !!this._formGroupDirective?.submitted));
105
+ return !!this.resolvedError() && !!control?.errors && (this._interacted() || this._submitted());
103
106
  }, /* @ts-ignore */
104
107
  ...(ngDevMode ? [{ debugName: "showError" }] : /* istanbul ignore next */ []));
105
108
  showHint = computed(() => !this.showError() && !!this.hint(), /* @ts-ignore */
106
109
  ...(ngDevMode ? [{ debugName: "showHint" }] : /* istanbul ignore next */ []));
110
+ describedBy() {
111
+ const ids = [this.ariaDescribedBy(), this.showError() ? this.errorId() : this.showHint() ? this.hintId() : null];
112
+ return [...new Set(ids.filter(Boolean).join(' ').split(/\s+/).filter(Boolean))].join(' ') || null;
113
+ }
107
114
  _onChange;
108
115
  _onTouched;
109
116
  ngDoCheck() {
110
117
  const submitted = !!this._formGroupDirective?.submitted;
111
118
  if (this._submitted() !== submitted)
112
119
  this._submitted.set(submitted);
120
+ // Keep silent interaction updates (emitEvent: false) visible on the next check too.
121
+ const control = this._controlDir?.control;
122
+ this._interacted.set(!!control && (control.touched || control.dirty));
113
123
  }
114
124
  ngAfterViewInit() {
115
125
  // Resolved here (not earlier): this component IS the value accessor for
@@ -119,8 +129,11 @@ class EgFormToggle {
119
129
  this._controlDir = this._injector.get(FormControlName, null, { self: true, optional: true }) ?? null;
120
130
  const control = this._controlDir?.control;
121
131
  this._status.set(control?.status ?? null);
122
- control?.statusChanges.pipe(takeUntilDestroyed(this._destroyRef)).subscribe((status) => {
123
- this._status.set(status);
132
+ this._interacted.set(!!control && (control.touched || control.dirty));
133
+ // Touch/dirty/reset events must invalidate feedback even when status stays INVALID.
134
+ control?.events.pipe(takeUntilDestroyed(this._destroyRef)).subscribe(() => {
135
+ this._status.set(control.status);
136
+ this._interacted.set(control.touched || control.dirty);
124
137
  });
125
138
  }
126
139
  _handleStateChange(state) {
@@ -167,7 +180,7 @@ class EgFormToggle {
167
180
  $hintClass = computed(() => hlm('tw:mt-0', this._shared.hintClass, this._config.hintClass, this.hintClass()), /* @ts-ignore */
168
181
  ...(ngDevMode ? [{ debugName: "$hintClass" }] : /* istanbul ignore next */ []));
169
182
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgFormToggle, deps: [], target: i0.ɵɵFactoryTarget.Component });
170
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgFormToggle, isStandalone: true, selector: "eg-form-toggle", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, controlId: { classPropertyName: "controlId", publicName: "controlId", 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 }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, toggleClass: { classPropertyName: "toggleClass", publicName: "toggleClass", isSignal: true, isRequired: false, transformFunction: null }, labelClass: { classPropertyName: "labelClass", publicName: "labelClass", 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: [EG_FORM_TOGGLE_VALUE_ACCESSOR, provideIcons({ lucideCheck })], ngImport: i0, template: `
183
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgFormToggle, isStandalone: true, selector: "eg-form-toggle", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, controlId: { classPropertyName: "controlId", publicName: "controlId", 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 }, ariaDescribedBy: { classPropertyName: "ariaDescribedBy", publicName: "aria-describedby", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", 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 }, variant: { classPropertyName: "variant", publicName: "variant", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, toggleClass: { classPropertyName: "toggleClass", publicName: "toggleClass", isSignal: true, isRequired: false, transformFunction: null }, labelClass: { classPropertyName: "labelClass", publicName: "labelClass", 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: [EG_FORM_TOGGLE_VALUE_ACCESSOR, provideIcons({ lucideCheck })], ngImport: i0, template: `
171
184
  @let lbl = label();
172
185
  @let rqrd = required();
173
186
 
@@ -177,6 +190,8 @@ class EgFormToggle {
177
190
  hlmToggle
178
191
  type="button"
179
192
  [id]="effectiveId()"
193
+ [attr.aria-describedby]="describedBy()"
194
+ [attr.aria-invalid]="showError() ? 'true' : null"
180
195
  [state]="_state() ? 'on' : 'off'"
181
196
  (stateChange)="_handleStateChange($event)"
182
197
  [disabled]="_disabled()"
@@ -231,6 +246,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
231
246
  hlmToggle
232
247
  type="button"
233
248
  [id]="effectiveId()"
249
+ [attr.aria-describedby]="describedBy()"
250
+ [attr.aria-invalid]="showError() ? 'true' : null"
234
251
  [state]="_state() ? 'on' : 'off'"
235
252
  (stateChange)="_handleStateChange($event)"
236
253
  [disabled]="_disabled()"
@@ -264,7 +281,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
264
281
  </div>
265
282
  `,
266
283
  }]
267
- }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], controlId: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlId", 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 }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], toggleClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "toggleClass", required: false }] }], labelClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelClass", required: false }] }], errorClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorClass", required: false }] }], hintClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "hintClass", required: false }] }] } });
284
+ }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], controlId: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlId", 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 }] }], ariaDescribedBy: [{ type: i0.Input, args: [{ isSignal: true, alias: "aria-describedby", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], variant: [{ type: i0.Input, args: [{ isSignal: true, alias: "variant", required: false }] }], size: [{ type: i0.Input, args: [{ isSignal: true, alias: "size", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], toggleClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "toggleClass", required: false }] }], labelClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelClass", required: false }] }], errorClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorClass", required: false }] }], hintClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "hintClass", required: false }] }] } });
268
285
 
269
286
  /*
270
287
  * Public API Surface of form-toggle
@@ -36,6 +36,8 @@ declare class EgFormToggle implements ControlValueAccessor, AfterViewInit, DoChe
36
36
  */
37
37
  autoError: _angular_core.InputSignal<boolean>;
38
38
  hint: _angular_core.InputSignal<string | undefined>;
39
+ /** Consumer-owned description IDs, merged with the displayed error or hint on the button. */
40
+ ariaDescribedBy: _angular_core.InputSignal<string | null>;
39
41
  id: _angular_core.InputSignal<string | undefined>;
40
42
  disabled: _angular_core.InputSignalWithTransform<boolean, BooleanInput>;
41
43
  required: _angular_core.InputSignal<boolean>;
@@ -49,16 +51,18 @@ declare class EgFormToggle implements ControlValueAccessor, AfterViewInit, DoChe
49
51
  protected readonly _disabled: _angular_core.Signal<boolean>;
50
52
  private readonly _status;
51
53
  private readonly _submitted;
54
+ private readonly _interacted;
52
55
  /**
53
56
  * Displayed message: explicit `error()` wins; otherwise (when `autoError()`)
54
57
  * auto-resolved from the control's `ValidationErrors`. Plain method (not a
55
58
  * computed): `ValidationErrors` is not a signal, so the control must be read
56
59
  * fresh on every change-detection pass (`showError()` re-evaluates with it
57
- * via the `_status`/`_submitted` signals).
60
+ * via the `_status`/`_submitted`/`_interacted` signals).
58
61
  */
59
62
  protected resolvedError(): string | undefined;
60
63
  protected readonly showError: _angular_core.Signal<boolean>;
61
64
  protected readonly showHint: _angular_core.Signal<boolean>;
65
+ protected describedBy(): string | null;
62
66
  protected _onChange?: ChangeFn<boolean>;
63
67
  protected _onTouched?: TouchFn;
64
68
  ngDoCheck(): void;
@@ -80,7 +84,7 @@ declare class EgFormToggle implements ControlValueAccessor, AfterViewInit, DoChe
80
84
  $errorClass: _angular_core.Signal<string>;
81
85
  $hintClass: _angular_core.Signal<string>;
82
86
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EgFormToggle, never>;
83
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<EgFormToggle, "eg-form-toggle", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "controlId": { "alias": "controlId"; "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; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "userClass": { "alias": "class"; "required": false; "isSignal": true; }; "toggleClass": { "alias": "toggleClass"; "required": false; "isSignal": true; }; "labelClass": { "alias": "labelClass"; "required": false; "isSignal": true; }; "errorClass": { "alias": "errorClass"; "required": false; "isSignal": true; }; "hintClass": { "alias": "hintClass"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
87
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EgFormToggle, "eg-form-toggle", never, { "label": { "alias": "label"; "required": false; "isSignal": true; }; "controlId": { "alias": "controlId"; "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; }; "ariaDescribedBy": { "alias": "aria-describedby"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "required": { "alias": "required"; "required": false; "isSignal": true; }; "variant": { "alias": "variant"; "required": false; "isSignal": true; }; "size": { "alias": "size"; "required": false; "isSignal": true; }; "userClass": { "alias": "class"; "required": false; "isSignal": true; }; "toggleClass": { "alias": "toggleClass"; "required": false; "isSignal": true; }; "labelClass": { "alias": "labelClass"; "required": false; "isSignal": true; }; "errorClass": { "alias": "errorClass"; "required": false; "isSignal": true; }; "hintClass": { "alias": "hintClass"; "required": false; "isSignal": true; }; }, {}, never, never, true, never>;
84
88
  }
85
89
 
86
90
  interface EgFormToggleConfig {