@egose/shadcn-theme-ng-tw 0.0.28 → 0.2.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 (244) hide show
  1. package/README.md +130 -13
  2. package/accordion/README.md +376 -34
  3. package/accordion/fesm2022/accordion.mjs +20 -20
  4. package/alert/README.md +343 -34
  5. package/alert/fesm2022/alert.mjs +18 -18
  6. package/alert-dialog/README.md +471 -5
  7. package/alert-dialog/fesm2022/alert-dialog.mjs +48 -48
  8. package/alert-dialog/types/alert-dialog.d.ts +1 -1
  9. package/aspect-ratio/README.md +272 -5
  10. package/aspect-ratio/fesm2022/aspect-ratio.mjs +9 -9
  11. package/autocomplete/README.md +473 -34
  12. package/autocomplete/fesm2022/autocomplete.mjs +54 -54
  13. package/autocomplete/types/autocomplete.d.ts +1 -1
  14. package/avatar/README.md +357 -5
  15. package/avatar/fesm2022/avatar.mjs +32 -32
  16. package/badge/README.md +289 -34
  17. package/badge/fesm2022/badge.mjs +4 -4
  18. package/basic-alert/README.md +324 -34
  19. package/basic-alert/fesm2022/basic-alert.mjs +4 -4
  20. package/basic-alert/types/basic-alert.d.ts +1 -1
  21. package/breadcrumb/README.md +406 -5
  22. package/breadcrumb/fesm2022/breadcrumb.mjs +34 -34
  23. package/button/README.md +453 -34
  24. package/button/fesm2022/button.mjs +96 -118
  25. package/button/types/button.d.ts +7 -10
  26. package/button-group/README.md +318 -5
  27. package/button-group/fesm2022/button-group.mjs +20 -20
  28. package/calendar/README.md +328 -34
  29. package/calendar/fesm2022/calendar.mjs +154 -154
  30. package/card/README.md +331 -5
  31. package/card/fesm2022/card.mjs +33 -33
  32. package/carousel/README.md +333 -5
  33. package/carousel/fesm2022/carousel.mjs +47 -44
  34. package/carousel/types/carousel.d.ts +1 -1
  35. package/checkbox/README.md +291 -34
  36. package/checkbox/fesm2022/checkbox.mjs +22 -20
  37. package/checkbox/types/checkbox.d.ts +5 -4
  38. package/collapsible/README.md +332 -5
  39. package/collapsible/fesm2022/collapsible.mjs +15 -15
  40. package/combobox/README.md +507 -5
  41. package/combobox/fesm2022/combobox.mjs +95 -92
  42. package/combobox/types/combobox.d.ts +1 -1
  43. package/command/README.md +435 -5
  44. package/command/fesm2022/command.mjs +56 -56
  45. package/confirmation-dialog/README.md +272 -34
  46. package/confirmation-dialog/fesm2022/confirmation-dialog.mjs +13 -12
  47. package/confirmation-dialog/types/confirmation-dialog.d.ts +9 -5
  48. package/context-menu/README.md +366 -5
  49. package/context-menu/fesm2022/context-menu.mjs +9 -9
  50. package/date-picker/README.md +436 -34
  51. package/date-picker/fesm2022/date-picker.mjs +79 -68
  52. package/date-picker/types/date-picker.d.ts +8 -4
  53. package/dialog/README.md +419 -34
  54. package/dialog/fesm2022/dialog.mjs +48 -48
  55. package/drawer/README.md +395 -5
  56. package/drawer/fesm2022/drawer.mjs +45 -45
  57. package/dropdown-menu/README.md +417 -5
  58. package/dropdown-menu/fesm2022/dropdown-menu.mjs +85 -73
  59. package/dropdown-menu/types/dropdown-menu.d.ts +4 -0
  60. package/empty/README.md +329 -5
  61. package/empty/fesm2022/empty.mjs +31 -31
  62. package/field/README.md +385 -5
  63. package/field/fesm2022/field.mjs +59 -59
  64. package/field/types/field.d.ts +1 -1
  65. package/form-checkbox/README.md +283 -34
  66. package/form-checkbox/fesm2022/form-checkbox.mjs +37 -19
  67. package/form-checkbox/types/form-checkbox.d.ts +7 -1
  68. package/form-date-picker/README.md +293 -34
  69. package/form-date-picker/fesm2022/form-date-picker.mjs +59 -24
  70. package/form-date-picker/types/form-date-picker.d.ts +7 -1
  71. package/form-field/README.md +327 -34
  72. package/form-field/fesm2022/form-field.mjs +30 -16
  73. package/form-field/types/form-field.d.ts +28 -20
  74. package/form-field-simple/README.md +311 -34
  75. package/form-field-simple/fesm2022/form-field-simple.mjs +4 -4
  76. package/form-searchable-multiselect/README.md +332 -34
  77. package/form-searchable-multiselect/fesm2022/form-searchable-multiselect.mjs +41 -21
  78. package/form-searchable-multiselect/types/form-searchable-multiselect.d.ts +8 -2
  79. package/form-select/README.md +321 -34
  80. package/form-select/fesm2022/form-select.mjs +62 -29
  81. package/form-select/types/form-select.d.ts +7 -1
  82. package/form-text-input/README.md +342 -34
  83. package/form-text-input/fesm2022/form-text-input.mjs +43 -55
  84. package/form-text-input/types/form-text-input.d.ts +9 -12
  85. package/form-textarea/README.md +318 -34
  86. package/form-textarea/fesm2022/form-textarea.mjs +42 -23
  87. package/form-textarea/types/form-textarea.d.ts +8 -1
  88. package/hover-card/README.md +256 -5
  89. package/hover-card/fesm2022/hover-card.mjs +19 -19
  90. package/icon/README.md +210 -34
  91. package/icon/fesm2022/icon.mjs +8 -8
  92. package/input/README.md +240 -34
  93. package/input/fesm2022/input.mjs +17 -12
  94. package/input/types/input.d.ts +2 -1
  95. package/input-group/README.md +335 -5
  96. package/input-group/fesm2022/input-group.mjs +37 -37
  97. package/input-group/types/input-group.d.ts +3 -3
  98. package/input-otp/README.md +375 -5
  99. package/input-otp/fesm2022/input-otp.mjs +27 -27
  100. package/item/README.md +385 -5
  101. package/item/fesm2022/item.mjs +54 -54
  102. package/kbd/README.md +291 -5
  103. package/kbd/fesm2022/kbd.mjs +13 -13
  104. package/label/README.md +243 -34
  105. package/label/fesm2022/label.mjs +9 -9
  106. package/label/types/label.d.ts +1 -1
  107. package/layout-simple/README.md +174 -44
  108. package/layout-simple/fesm2022/layout-simple.mjs +917 -398
  109. package/layout-simple/types/layout-simple.d.ts +180 -129
  110. package/menu/README.md +418 -0
  111. package/menu/fesm2022/menu.mjs +461 -0
  112. package/menu/package.json +24 -0
  113. package/menu/types/menu.d.ts +137 -0
  114. package/menubar/README.md +343 -5
  115. package/menubar/fesm2022/menubar.mjs +13 -13
  116. package/native-select/README.md +323 -5
  117. package/native-select/fesm2022/native-select.mjs +19 -19
  118. package/navigation-menu/README.md +369 -5
  119. package/navigation-menu/fesm2022/navigation-menu.mjs +37 -37
  120. package/package.json +183 -154
  121. package/pagination/README.md +388 -5
  122. package/pagination/fesm2022/pagination.mjs +63 -63
  123. package/popover/README.md +302 -34
  124. package/popover/fesm2022/popover.mjs +30 -30
  125. package/progress/README.md +311 -5
  126. package/progress/fesm2022/progress.mjs +13 -13
  127. package/radio-group/README.md +335 -34
  128. package/radio-group/fesm2022/radio-group.mjs +26 -19
  129. package/resizable/README.md +269 -5
  130. package/resizable/fesm2022/resizable.mjs +19 -19
  131. package/scroll-area/README.md +233 -5
  132. package/scroll-area/fesm2022/scroll-area.mjs +9 -9
  133. package/searchable-multiselect/README.md +294 -34
  134. package/searchable-multiselect/fesm2022/searchable-multiselect.mjs +63 -16
  135. package/searchable-multiselect/types/searchable-multiselect.d.ts +8 -2
  136. package/select/README.md +408 -34
  137. package/select/fesm2022/select.mjs +80 -72
  138. package/select/types/select.d.ts +3 -1
  139. package/separator/README.md +193 -34
  140. package/separator/fesm2022/separator.mjs +9 -9
  141. package/separator/types/separator.d.ts +1 -1
  142. package/sheet/README.md +282 -34
  143. package/sheet/fesm2022/sheet.mjs +30 -30
  144. package/sidebar/README.md +457 -5
  145. package/sidebar/fesm2022/sidebar.mjs +136 -134
  146. package/sidebar/types/sidebar.d.ts +3 -3
  147. package/skeleton/README.md +217 -5
  148. package/skeleton/fesm2022/skeleton.mjs +9 -9
  149. package/slider/README.md +273 -5
  150. package/slider/fesm2022/slider.mjs +29 -25
  151. package/sonner/README.md +317 -34
  152. package/sonner/fesm2022/sonner.mjs +9 -9
  153. package/spinner/README.md +255 -34
  154. package/spinner/fesm2022/spinner.mjs +8 -8
  155. package/switch/README.md +281 -34
  156. package/switch/fesm2022/switch.mjs +12 -14
  157. package/table/README.md +423 -5
  158. package/table/fesm2022/table.mjs +41 -41
  159. package/tabs/README.md +382 -34
  160. package/tabs/fesm2022/tabs.mjs +48 -38
  161. package/textarea/README.md +282 -5
  162. package/textarea/fesm2022/textarea.mjs +9 -9
  163. package/toggle/README.md +270 -5
  164. package/toggle/fesm2022/toggle.mjs +14 -14
  165. package/toggle-group/README.md +340 -5
  166. package/toggle-group/fesm2022/toggle-group.mjs +13 -13
  167. package/toggle-group/types/toggle-group.d.ts +13 -7
  168. package/tooltip/README.md +240 -34
  169. package/tooltip/fesm2022/tooltip.mjs +14 -14
  170. package/typography/README.md +271 -5
  171. package/typography/fesm2022/typography.mjs +53 -53
  172. package/typography/types/typography.d.ts +12 -12
  173. package/utils/README.md +274 -34
  174. package/utils/fesm2022/utils.mjs +68 -44
  175. package/accordion/fesm2022/accordion.mjs.map +0 -1
  176. package/alert/fesm2022/alert.mjs.map +0 -1
  177. package/alert-dialog/fesm2022/alert-dialog.mjs.map +0 -1
  178. package/aspect-ratio/fesm2022/aspect-ratio.mjs.map +0 -1
  179. package/autocomplete/fesm2022/autocomplete.mjs.map +0 -1
  180. package/avatar/fesm2022/avatar.mjs.map +0 -1
  181. package/badge/fesm2022/badge.mjs.map +0 -1
  182. package/basic-alert/fesm2022/basic-alert.mjs.map +0 -1
  183. package/breadcrumb/fesm2022/breadcrumb.mjs.map +0 -1
  184. package/button/fesm2022/button.mjs.map +0 -1
  185. package/button-group/fesm2022/button-group.mjs.map +0 -1
  186. package/calendar/fesm2022/calendar.mjs.map +0 -1
  187. package/card/fesm2022/card.mjs.map +0 -1
  188. package/carousel/fesm2022/carousel.mjs.map +0 -1
  189. package/checkbox/fesm2022/checkbox.mjs.map +0 -1
  190. package/collapsible/fesm2022/collapsible.mjs.map +0 -1
  191. package/combobox/fesm2022/combobox.mjs.map +0 -1
  192. package/command/fesm2022/command.mjs.map +0 -1
  193. package/confirmation-dialog/fesm2022/confirmation-dialog.mjs.map +0 -1
  194. package/context-menu/fesm2022/context-menu.mjs.map +0 -1
  195. package/date-picker/fesm2022/date-picker.mjs.map +0 -1
  196. package/dialog/fesm2022/dialog.mjs.map +0 -1
  197. package/drawer/fesm2022/drawer.mjs.map +0 -1
  198. package/dropdown-menu/fesm2022/dropdown-menu.mjs.map +0 -1
  199. package/empty/fesm2022/empty.mjs.map +0 -1
  200. package/field/fesm2022/field.mjs.map +0 -1
  201. package/form-checkbox/fesm2022/form-checkbox.mjs.map +0 -1
  202. package/form-date-picker/fesm2022/form-date-picker.mjs.map +0 -1
  203. package/form-field/fesm2022/form-field.mjs.map +0 -1
  204. package/form-field-simple/fesm2022/form-field-simple.mjs.map +0 -1
  205. package/form-searchable-multiselect/fesm2022/form-searchable-multiselect.mjs.map +0 -1
  206. package/form-select/fesm2022/form-select.mjs.map +0 -1
  207. package/form-text-input/fesm2022/form-text-input.mjs.map +0 -1
  208. package/form-textarea/fesm2022/form-textarea.mjs.map +0 -1
  209. package/hover-card/fesm2022/hover-card.mjs.map +0 -1
  210. package/icon/fesm2022/icon.mjs.map +0 -1
  211. package/input/fesm2022/input.mjs.map +0 -1
  212. package/input-group/fesm2022/input-group.mjs.map +0 -1
  213. package/input-otp/fesm2022/input-otp.mjs.map +0 -1
  214. package/item/fesm2022/item.mjs.map +0 -1
  215. package/kbd/fesm2022/kbd.mjs.map +0 -1
  216. package/label/fesm2022/label.mjs.map +0 -1
  217. package/layout-simple/fesm2022/layout-simple.mjs.map +0 -1
  218. package/menubar/fesm2022/menubar.mjs.map +0 -1
  219. package/native-select/fesm2022/native-select.mjs.map +0 -1
  220. package/navigation-menu/fesm2022/navigation-menu.mjs.map +0 -1
  221. package/pagination/fesm2022/pagination.mjs.map +0 -1
  222. package/popover/fesm2022/popover.mjs.map +0 -1
  223. package/progress/fesm2022/progress.mjs.map +0 -1
  224. package/radio-group/fesm2022/radio-group.mjs.map +0 -1
  225. package/resizable/fesm2022/resizable.mjs.map +0 -1
  226. package/scroll-area/fesm2022/scroll-area.mjs.map +0 -1
  227. package/searchable-multiselect/fesm2022/searchable-multiselect.mjs.map +0 -1
  228. package/select/fesm2022/select.mjs.map +0 -1
  229. package/separator/fesm2022/separator.mjs.map +0 -1
  230. package/sheet/fesm2022/sheet.mjs.map +0 -1
  231. package/sidebar/fesm2022/sidebar.mjs.map +0 -1
  232. package/skeleton/fesm2022/skeleton.mjs.map +0 -1
  233. package/slider/fesm2022/slider.mjs.map +0 -1
  234. package/sonner/fesm2022/sonner.mjs.map +0 -1
  235. package/spinner/fesm2022/spinner.mjs.map +0 -1
  236. package/switch/fesm2022/switch.mjs.map +0 -1
  237. package/table/fesm2022/table.mjs.map +0 -1
  238. package/tabs/fesm2022/tabs.mjs.map +0 -1
  239. package/textarea/fesm2022/textarea.mjs.map +0 -1
  240. package/toggle/fesm2022/toggle.mjs.map +0 -1
  241. package/toggle-group/fesm2022/toggle-group.mjs.map +0 -1
  242. package/tooltip/fesm2022/tooltip.mjs.map +0 -1
  243. package/typography/fesm2022/typography.mjs.map +0 -1
  244. package/utils/fesm2022/utils.mjs.map +0 -1
@@ -1,64 +1,362 @@
1
- # FormSearchableMultiselect
1
+ # Form Searchable Multiselect (`@egose/shadcn-theme-ng/form-searchable-multiselect`)
2
2
 
3
- This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 20.3.0.
3
+ `EgFormSearchableMultiselect` is a reactive-forms wrapper that pairs a label, a searchable multi-select popover, and error/hint text into a single form-ready row. It is the shadcn/ui equivalent of a _Form + Combobox (multiple)_ composition: the selection UI itself comes from `EgSearchableMultiselect` (`@egose/shadcn-theme-ng/searchable-multiselect`, a `ControlValueAccessor` built on the popover + checkbox primitives), while this wrapper supplies the surrounding `eg-form-field` layout, generated ids, and `aria-describedby` wiring.
4
4
 
5
- ## Code scaffolding
5
+ > **Ships as:** `@egose/shadcn-theme-ng/form-searchable-multiselect` (plain Tailwind) and `@egose/shadcn-theme-ng-tw/form-searchable-multiselect` (`tw:`-prefixed variant). See the [package README](../../README.md) for installation, peer dependencies, and the Tailwind-variant contract. Do not publish this project directory independently.
6
6
 
7
- Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
7
+ ## Installation
8
8
 
9
9
  ```bash
10
- ng generate component component-name
10
+ # Plain Tailwind (no prefix)
11
+ npm install @egose/shadcn-theme-ng
12
+
13
+ # Or the tw:-prefixed variant
14
+ npm install @egose/shadcn-theme-ng-tw
11
15
  ```
12
16
 
13
- For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
17
+ Peer dependencies (Angular, CDK, `@spartan-ng/brain`, `rxjs`, `@ng-icons/core`) are resolved from the versions documented in the [package README](../../README.md#peer-dependencies). Import the subpath — no extra install step per component:
14
18
 
15
- ```bash
16
- ng generate --help
19
+ ```ts
20
+ import { EgFormSearchableMultiselect } from '@egose/shadcn-theme-ng/form-searchable-multiselect';
21
+ // tw variant:
22
+ // import { EgFormSearchableMultiselect } from '@egose/shadcn-theme-ng-tw/form-searchable-multiselect';
17
23
  ```
18
24
 
19
- ## Building
25
+ ## Imports
20
26
 
21
- To build the library, run:
27
+ The public API (`src/public-api.ts`) exports exactly one symbol — the standalone component. There is no `*Imports` array and no `*Module` for this subpath; import the component class directly.
22
28
 
23
- ```bash
24
- ng build form-searchable-multiselect
29
+ ```ts
30
+ import { EgFormSearchableMultiselect } from '@egose/shadcn-theme-ng/form-searchable-multiselect';
31
+
32
+ @Component({
33
+ standalone: true,
34
+ imports: [ReactiveFormsModule, EgFormSearchableMultiselect],
35
+ template: `
36
+ <form [formGroup]="form">
37
+ <eg-form-searchable-multiselect controlName="tags" label="Tags" [options]="tagOptions" />
38
+ </form>
39
+ `,
40
+ })
41
+ export class MyForm {}
42
+ ```
43
+
44
+ Requirements:
45
+
46
+ - The component **must** sit inside a `<form [formGroup]>` (or any `FormGroupDirective` ancestor). It injects `FormGroupDirective` and provides `ControlContainer`, so `controlName` resolves against the parent group.
47
+ - `controlName` is required — it is forwarded as `[formControlName]` to the inner `eg-searchable-multiselect`.
48
+ - The bound `FormControl` should hold a `string[]` (e.g. `new FormControl<string[]>([], { nonNullable: true })`).
49
+
50
+ ## Anatomy / Structure
51
+
52
+ ```html
53
+ <eg-form-searchable-multiselect
54
+ controlName="tags"
55
+ label="Tags"
56
+ error="Pick at least one tag"
57
+ hint="Choose all that apply"
58
+ >
59
+ <!-- rendered internally -->
60
+ <eg-form-field>
61
+ <label hlmLabel for="<effectiveId>">Tags <span>*</span></label>
62
+ <eg-searchable-multiselect
63
+ id="<effectiveId>"
64
+ [options]="[{ label, value }]"
65
+ placeholder="Start typing to add…"
66
+ formControlName="tags"
67
+ ></eg-searchable-multiselect>
68
+ <hlm-error id="<effectiveId>-error">…</hlm-error>
69
+ <hlm-hint id="<effectiveId>-hint">…</hlm-hint>
70
+ </eg-form-field>
71
+ </eg-form-searchable-multiselect>
25
72
  ```
26
73
 
27
- This command will compile your project, and the build artifacts will be placed in the `dist/` directory.
74
+ Real selectors involved: `eg-form-searchable-multiselect` (this wrapper), `eg-form-field` (`@egose/shadcn-theme-ng/form-field-simple`), `label[hlmLabel]`, `eg-searchable-multiselect`, `hlm-error` / `hlm-hint` (`@egose/shadcn-theme-ng/form-field`).
28
75
 
29
- ### Publishing the Library
76
+ ## API reference
30
77
 
31
- Once the project is built, you can publish your library by following these steps:
78
+ ### `eg-form-searchable-multiselect` — `EgFormSearchableMultiselect`
32
79
 
33
- 1. Navigate to the `dist` directory:
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:flex tw:flex-col`). |
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`). |
34
97
 
35
- ```bash
36
- cd dist/form-searchable-multiselect
37
- ```
98
+ 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`).
38
99
 
39
- 2. Run the `npm publish` command to publish your library to the npm registry:
40
- ```bash
41
- npm publish
42
- ```
100
+ `SelectOption` is `export interface SelectOption { label: string; value: string }` from `@egose/shadcn-theme-ng/searchable-multiselect`.
43
101
 
44
- ## Running unit tests
102
+ ## Examples
45
103
 
46
- To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
104
+ ### 1. Basic reactive form
47
105
 
48
- ```bash
49
- ng test
106
+ ```ts
107
+ import { Component } from '@angular/core';
108
+ import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
109
+ import { EgFormSearchableMultiselect } from '@egose/shadcn-theme-ng/form-searchable-multiselect';
110
+
111
+ @Component({
112
+ standalone: true,
113
+ imports: [ReactiveFormsModule, EgFormSearchableMultiselect],
114
+ template: `
115
+ <form [formGroup]="form" (ngSubmit)="save()">
116
+ <eg-form-searchable-multiselect controlName="tags" label="Tags" [options]="tagOptions" />
117
+ <button type="submit">Save</button>
118
+ </form>
119
+ `,
120
+ })
121
+ export class BasicExample {
122
+ readonly form = new FormGroup({
123
+ tags: new FormControl<string[]>([], { nonNullable: true }),
124
+ });
125
+ readonly tagOptions = [
126
+ { value: 'angular', label: 'Angular' },
127
+ { value: 'react', label: 'React' },
128
+ { value: 'vue', label: 'Vue' },
129
+ ];
130
+
131
+ save() {
132
+ console.log(this.form.value.tags); // e.g. ['angular', 'vue']
133
+ }
134
+ }
50
135
  ```
51
136
 
52
- ## Running end-to-end tests
137
+ ### 2. Required field with validation error
53
138
 
54
- For end-to-end (e2e) testing, run:
139
+ The `error` string is always rendered when provided; `aria-describedby` flips from the hint id to the error id once the control is invalid and dirty/touched.
55
140
 
56
- ```bash
57
- ng e2e
141
+ ```ts
142
+ import { Component } from '@angular/core';
143
+ import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
144
+ import { EgFormSearchableMultiselect } from '@egose/shadcn-theme-ng/form-searchable-multiselect';
145
+
146
+ @Component({
147
+ standalone: true,
148
+ imports: [ReactiveFormsModule, EgFormSearchableMultiselect],
149
+ template: `
150
+ <form [formGroup]="form">
151
+ <eg-form-searchable-multiselect
152
+ controlName="skills"
153
+ label="Skills"
154
+ [options]="skillOptions"
155
+ error="Pick at least one skill"
156
+ hint="Choose all that apply"
157
+ required
158
+ />
159
+ </form>
160
+ `,
161
+ })
162
+ export class RequiredExample {
163
+ readonly form = new FormGroup({
164
+ skills: new FormControl<string[]>([], { nonNullable: true, validators: Validators.required }),
165
+ });
166
+ readonly skillOptions = [
167
+ { value: 'ts', label: 'TypeScript' },
168
+ { value: 'a11y', label: 'Accessibility' },
169
+ { value: 'testing', label: 'Testing' },
170
+ ];
171
+ }
58
172
  ```
59
173
 
60
- Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
174
+ ### 3. Disabled states (wrapper input vs reactive disable)
175
+
176
+ ```ts
177
+ import { Component, signal } from '@angular/core';
178
+ import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
179
+ import { EgFormSearchableMultiselect } from '@egose/shadcn-theme-ng/form-searchable-multiselect';
180
+
181
+ @Component({
182
+ standalone: true,
183
+ imports: [ReactiveFormsModule, EgFormSearchableMultiselect],
184
+ template: `
185
+ <form [formGroup]="form">
186
+ <!-- static wrapper disable -->
187
+ <eg-form-searchable-multiselect
188
+ controlName="tags"
189
+ label="Tags (locked)"
190
+ [options]="tagOptions"
191
+ [disabled]="true"
192
+ />
193
+ <!-- reactive disable driven by a signal -->
194
+ <eg-form-searchable-multiselect
195
+ controlName="extras"
196
+ label="Extras"
197
+ [options]="tagOptions"
198
+ [disabled]="locked()"
199
+ />
200
+ <button type="button" (click)="toggle()">Toggle extras</button>
201
+ </form>
202
+ `,
203
+ })
204
+ export class DisabledExample {
205
+ readonly form = new FormGroup({
206
+ tags: new FormControl<string[]>(['angular'], { nonNullable: true }),
207
+ extras: new FormControl<string[]>([], { nonNullable: true }),
208
+ });
209
+ readonly tagOptions = [{ value: 'angular', label: 'Angular' }];
210
+ readonly locked = signal(true);
211
+
212
+ toggle() {
213
+ this.locked.update((v) => !v);
214
+ }
215
+
216
+ disableViaFormApi() {
217
+ this.form.controls.extras.disable(); // also disables the trigger (inner CVA)
218
+ }
219
+ }
220
+ ```
221
+
222
+ ### 4. Custom ids, placeholder, and styling hooks
223
+
224
+ ```ts
225
+ import { Component } from '@angular/core';
226
+ import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
227
+ import { EgFormSearchableMultiselect } from '@egose/shadcn-theme-ng/form-searchable-multiselect';
228
+
229
+ @Component({
230
+ standalone: true,
231
+ imports: [ReactiveFormsModule, EgFormSearchableMultiselect],
232
+ template: `
233
+ <form [formGroup]="form">
234
+ <eg-form-searchable-multiselect
235
+ controlName="frameworks"
236
+ controlId="framework-picker"
237
+ label="Frameworks"
238
+ placeholder="Search frameworks…"
239
+ hint="Type to filter, Enter to add"
240
+ [options]="frameworks"
241
+ class="tw:max-w-md"
242
+ labelClass="tw:font-semibold"
243
+ controlClass="tw:min-h-11"
244
+ />
245
+ </form>
246
+ `,
247
+ })
248
+ export class StyledExample {
249
+ readonly form = new FormGroup({
250
+ frameworks: new FormControl<string[]>([], { nonNullable: true }),
251
+ });
252
+ readonly frameworks = [
253
+ { value: 'angular', label: 'Angular' },
254
+ { value: 'svelte', label: 'Svelte' },
255
+ { value: 'solid', label: 'Solid' },
256
+ ];
257
+ }
258
+ ```
259
+
260
+ ### 5. Async options (server-driven list)
261
+
262
+ ```ts
263
+ import { Component, inject, resource } from '@angular/core';
264
+ import { HttpClient } from '@angular/common/http';
265
+ import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
266
+ import { EgFormSearchableMultiselect } from '@egose/shadcn-theme-ng/form-searchable-multiselect';
267
+
268
+ @Component({
269
+ standalone: true,
270
+ imports: [ReactiveFormsModule, EgFormSearchableMultiselect],
271
+ template: `
272
+ <form [formGroup]="form">
273
+ <eg-form-searchable-multiselect
274
+ controlName="projects"
275
+ label="Projects"
276
+ [options]="projectOptions.value() ?? []"
277
+ [placeholder]="projectOptions.isLoading() ? 'Loading projects…' : 'Start typing to add…'"
278
+ [disabled]="projectOptions.isLoading()"
279
+ hint="Options load from the server"
280
+ />
281
+ </form>
282
+ `,
283
+ })
284
+ export class AsyncExample {
285
+ private readonly http = inject(HttpClient);
286
+ readonly form = new FormGroup({
287
+ projects: new FormControl<string[]>([], { nonNullable: true }),
288
+ });
289
+ readonly projectOptions = resource({
290
+ loader: () => this.http.get<{ label: string; value: string }[]>('/api/projects').toPromise(),
291
+ });
292
+ }
293
+ ```
294
+
295
+ ### 6. Programmatic control (preselect, reset, submit gating)
296
+
297
+ ```ts
298
+ import { Component } from '@angular/core';
299
+ import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
300
+ import { EgFormSearchableMultiselect } from '@egose/shadcn-theme-ng/form-searchable-multiselect';
301
+
302
+ @Component({
303
+ standalone: true,
304
+ imports: [ReactiveFormsModule, EgFormSearchableMultiselect],
305
+ template: `
306
+ <form [formGroup]="form" (ngSubmit)="submit()">
307
+ <eg-form-searchable-multiselect
308
+ #tagsField
309
+ controlName="tags"
310
+ label="Tags"
311
+ [options]="tagOptions"
312
+ error="Pick at least one tag"
313
+ />
314
+ <div class="tw:flex tw:gap-2">
315
+ <button type="button" (click)="preselect()">Select all</button>
316
+ <button type="button" (click)="form.controls.tags.reset()">Clear</button>
317
+ <button type="submit" [disabled]="form.invalid">Submit ({{ form.value.tags?.length ?? 0 }})</button>
318
+ </div>
319
+ <p>Control id: {{ tagsField.effectiveId() }}</p>
320
+ </form>
321
+ `,
322
+ })
323
+ export class ProgrammaticExample {
324
+ readonly form = new FormGroup({
325
+ tags: new FormControl<string[]>([], { nonNullable: true, validators: Validators.required }),
326
+ });
327
+ readonly tagOptions = [
328
+ { value: 'angular', label: 'Angular' },
329
+ { value: 'react', label: 'React' },
330
+ ];
331
+
332
+ preselect() {
333
+ this.form.controls.tags.setValue(this.tagOptions.map((o) => o.value));
334
+ }
335
+
336
+ submit() {
337
+ if (this.form.invalid) {
338
+ this.form.markAllAsTouched();
339
+ return;
340
+ }
341
+ console.log(this.form.getRawValue());
342
+ }
343
+ }
344
+ ```
345
+
346
+ ## Accessibility notes
347
+
348
+ - The `<label hlmLabel [for]="effectiveId()">` is bound to the inner multiselect trigger id, so clicking the label focuses/opens the control.
349
+ - `aria-describedby` points at the error element only when the control is invalid and dirty/touched; otherwise it points at the hint element (or is absent). Keep `error`/`hint` text concise and unique per field.
350
+ - The inner `eg-searchable-multiselect` renders a real `<button>` trigger plus checkbox options; keyboard users open with Enter/Space and pick with the checkbox semantics. The `required` input adds a visual `*` — also add `Validators.required` so assistive tech gets the invalid state.
351
+ - `ariaLabel` on the inner control falls back to the visible label; pass an explicit `controlId` when several multiselects share a page to keep ids stable for tests.
352
+
353
+ ## Theming / CSS variables
354
+
355
+ No component-specific CSS variables; styling flows through the shared theme tokens (`--radius`, `--popover`, `--ring`, …) consumed by the inner popover/checkbox classes. Use `class` / `labelClass` / `controlClass` / `errorClass` / `hintClass` to append utilities.
61
356
 
62
- ## Additional Resources
357
+ ## Related subpaths
63
358
 
64
- For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
359
+ - `@egose/shadcn-theme-ng/searchable-multiselect` — the underlying `EgSearchableMultiselect` + `SelectOption` (use it directly for template-driven or non-form usage).
360
+ - `@egose/shadcn-theme-ng/form-field-simple` — `EgFormField` layout used internally.
361
+ - `@egose/shadcn-theme-ng/form-field` — `HlmError`, `HlmHint`, `HlmFormIdGenerator`.
362
+ - `@egose/shadcn-theme-ng/label` — `HlmLabel`.
@@ -1,14 +1,16 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, computed, Component } from '@angular/core';
2
+ import { inject, input, computed, Component } from '@angular/core';
3
3
  import * as i1 from '@angular/forms';
4
- import { ReactiveFormsModule, FormGroupDirective, ControlContainer } from '@angular/forms';
4
+ import { FormGroupDirective, ReactiveFormsModule, ControlContainer } from '@angular/forms';
5
5
  import { HlmLabel } from '@egose/shadcn-theme-ng-tw/label';
6
6
  import { EgFormField } from '@egose/shadcn-theme-ng-tw/form-field-simple';
7
- import { HlmError, HlmHint } from '@egose/shadcn-theme-ng-tw/form-field';
7
+ import { HlmFormIdGenerator, HlmError, HlmHint } from '@egose/shadcn-theme-ng-tw/form-field';
8
8
  import { hlm } from '@egose/shadcn-theme-ng-tw/utils';
9
9
  import { EgSearchableMultiselect } from '@egose/shadcn-theme-ng-tw/searchable-multiselect';
10
10
 
11
11
  class EgFormSearchableMultiselect {
12
+ formGroupDirective = inject(FormGroupDirective);
13
+ generatedId = inject(HlmFormIdGenerator).generate('eg-form-searchable-multiselect');
12
14
  // Form field inputs
13
15
  label = input(undefined, /* @ts-ignore */
14
16
  ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
@@ -21,7 +23,7 @@ class EgFormSearchableMultiselect {
21
23
  hint = input(undefined, /* @ts-ignore */
22
24
  ...(ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []));
23
25
  // HTML/control attributes
24
- id = input(crypto.randomUUID(), /* @ts-ignore */
26
+ id = input(undefined, /* @ts-ignore */
25
27
  ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
26
28
  placeholder = input('Start typing to add…', /* @ts-ignore */
27
29
  ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
@@ -29,6 +31,20 @@ class EgFormSearchableMultiselect {
29
31
  ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
30
32
  required = input(false, /* @ts-ignore */
31
33
  ...(ngDevMode ? [{ debugName: "required" }] : /* istanbul ignore next */ []));
34
+ effectiveId = computed(() => this.controlId() || this.id() || this.generatedId, /* @ts-ignore */
35
+ ...(ngDevMode ? [{ debugName: "effectiveId" }] : /* istanbul ignore next */ []));
36
+ errorId = computed(() => `${this.effectiveId()}-error`, /* @ts-ignore */
37
+ ...(ngDevMode ? [{ debugName: "errorId" }] : /* istanbul ignore next */ []));
38
+ hintId = computed(() => `${this.effectiveId()}-hint`, /* @ts-ignore */
39
+ ...(ngDevMode ? [{ debugName: "hintId" }] : /* istanbul ignore next */ []));
40
+ describedBy() {
41
+ const control = this.formGroupDirective.form.get(this.controlName());
42
+ return this.error() && control?.invalid && (control.dirty || control.touched)
43
+ ? this.errorId()
44
+ : this.hint()
45
+ ? this.hintId()
46
+ : null;
47
+ }
32
48
  // Multiselect-specific
33
49
  options = input([], /* @ts-ignore */
34
50
  ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ [])); // [{ label, value }]
@@ -53,10 +69,9 @@ class EgFormSearchableMultiselect {
53
69
  ...(ngDevMode ? [{ debugName: "$errorClass" }] : /* istanbul ignore next */ []));
54
70
  $hintClass = computed(() => hlm('tw:mt-0', this.hintClass()), /* @ts-ignore */
55
71
  ...(ngDevMode ? [{ debugName: "$hintClass" }] : /* istanbul ignore next */ []));
56
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: EgFormSearchableMultiselect, deps: [], target: i0.ɵɵFactoryTarget.Component });
57
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", 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: { classAttribute: "tw:w-full" }, providers: [{ provide: ControlContainer, useExisting: FormGroupDirective }], ngImport: i0, template: `
72
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgFormSearchableMultiselect, deps: [], target: i0.ɵɵFactoryTarget.Component });
73
+ 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: { classAttribute: "tw:w-full" }, providers: [{ provide: ControlContainer, useExisting: FormGroupDirective }], ngImport: i0, template: `
58
74
  @let lbl = label();
59
- @let cid = controlId();
60
75
  @let cnm = controlName();
61
76
  @let err = error();
62
77
  @let hnt = hint();
@@ -64,37 +79,40 @@ class EgFormSearchableMultiselect {
64
79
 
65
80
  <eg-form-field>
66
81
  @if (lbl) {
67
- <span hlmLabel [class]="$labelClass()">
82
+ <label hlmLabel [for]="effectiveId()" [class]="$labelClass()">
68
83
  {{ lbl }}
69
84
  @if (rqrd) {
70
85
  <span class="tw:text-red-500">*</span>
71
86
  }
72
- </span>
87
+ </label>
73
88
  }
74
89
 
75
90
  <eg-searchable-multiselect
76
- [id]="cid || id()"
91
+ [id]="effectiveId()"
77
92
  [options]="options()"
78
93
  [placeholder]="placeholder()"
79
94
  [formControlName]="cnm"
80
95
  [required]="rqrd"
96
+ [wrapperDisabled]="disabled()"
97
+ [ariaLabel]="lbl"
98
+ [ariaDescribedby]="describedBy()"
81
99
  [class]="$controlClass()"
82
100
  ></eg-searchable-multiselect>
83
101
 
84
102
  @if (err) {
85
- <hlm-error [class]="$errorClass()">
103
+ <hlm-error [id]="errorId()" [class]="$errorClass()">
86
104
  {{ err }}
87
105
  </hlm-error>
88
106
  }
89
107
  @if (hnt) {
90
- <hlm-hint [class]="$hintClass()">
108
+ <hlm-hint [id]="hintId()" [class]="$hintClass()">
91
109
  {{ hnt }}
92
110
  </hlm-hint>
93
111
  }
94
112
  </eg-form-field>
95
- `, 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", "class", "value"], outputs: ["valueChange"] }] });
113
+ `, 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"] }] });
96
114
  }
97
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: EgFormSearchableMultiselect, decorators: [{
115
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgFormSearchableMultiselect, decorators: [{
98
116
  type: Component,
99
117
  args: [{
100
118
  selector: 'eg-form-searchable-multiselect',
@@ -106,7 +124,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
106
124
  providers: [{ provide: ControlContainer, useExisting: FormGroupDirective }],
107
125
  template: `
108
126
  @let lbl = label();
109
- @let cid = controlId();
110
127
  @let cnm = controlName();
111
128
  @let err = error();
112
129
  @let hnt = hint();
@@ -114,30 +131,33 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
114
131
 
115
132
  <eg-form-field>
116
133
  @if (lbl) {
117
- <span hlmLabel [class]="$labelClass()">
134
+ <label hlmLabel [for]="effectiveId()" [class]="$labelClass()">
118
135
  {{ lbl }}
119
136
  @if (rqrd) {
120
137
  <span class="tw:text-red-500">*</span>
121
138
  }
122
- </span>
139
+ </label>
123
140
  }
124
141
 
125
142
  <eg-searchable-multiselect
126
- [id]="cid || id()"
143
+ [id]="effectiveId()"
127
144
  [options]="options()"
128
145
  [placeholder]="placeholder()"
129
146
  [formControlName]="cnm"
130
147
  [required]="rqrd"
148
+ [wrapperDisabled]="disabled()"
149
+ [ariaLabel]="lbl"
150
+ [ariaDescribedby]="describedBy()"
131
151
  [class]="$controlClass()"
132
152
  ></eg-searchable-multiselect>
133
153
 
134
154
  @if (err) {
135
- <hlm-error [class]="$errorClass()">
155
+ <hlm-error [id]="errorId()" [class]="$errorClass()">
136
156
  {{ err }}
137
157
  </hlm-error>
138
158
  }
139
159
  @if (hnt) {
140
- <hlm-hint [class]="$hintClass()">
160
+ <hlm-hint [id]="hintId()" [class]="$hintClass()">
141
161
  {{ hnt }}
142
162
  </hlm-hint>
143
163
  }
@@ -155,4 +175,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
155
175
  */
156
176
 
157
177
  export { EgFormSearchableMultiselect };
158
- //# sourceMappingURL=form-searchable-multiselect.mjs.map
178
+
@@ -1,17 +1,23 @@
1
1
  import * as _angular_core from '@angular/core';
2
2
  import { ClassValue } from 'clsx';
3
- import { SelectOption } from '@egose/shadcn-theme-ng/searchable-multiselect';
3
+ import { SelectOption } from '@egose/shadcn-theme-ng-tw/searchable-multiselect';
4
4
 
5
5
  declare class EgFormSearchableMultiselect {
6
+ private readonly formGroupDirective;
7
+ private readonly generatedId;
6
8
  label: _angular_core.InputSignal<string | undefined>;
7
9
  controlId: _angular_core.InputSignal<string | undefined>;
8
10
  controlName: _angular_core.InputSignal<string>;
9
11
  error: _angular_core.InputSignal<string | undefined>;
10
12
  hint: _angular_core.InputSignal<string | undefined>;
11
- id: _angular_core.InputSignal<string>;
13
+ id: _angular_core.InputSignal<string | undefined>;
12
14
  placeholder: _angular_core.InputSignal<string>;
13
15
  disabled: _angular_core.InputSignal<boolean>;
14
16
  required: _angular_core.InputSignal<boolean>;
17
+ readonly effectiveId: _angular_core.Signal<string>;
18
+ readonly errorId: _angular_core.Signal<string>;
19
+ readonly hintId: _angular_core.Signal<string>;
20
+ describedBy(): string | null;
15
21
  options: _angular_core.InputSignal<SelectOption[]>;
16
22
  userClass: _angular_core.InputSignal<ClassValue>;
17
23
  labelClass: _angular_core.InputSignal<string>;