@egose/shadcn-theme-ng 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 (237) hide show
  1. package/README.md +130 -13
  2. package/accordion/README.md +376 -34
  3. package/accordion/fesm2022/accordion.mjs +27 -39
  4. package/alert/README.md +343 -34
  5. package/alert/fesm2022/alert.mjs +47 -47
  6. package/alert-dialog/README.md +471 -5
  7. package/alert-dialog/fesm2022/alert-dialog.mjs +47 -47
  8. package/aspect-ratio/README.md +272 -5
  9. package/aspect-ratio/fesm2022/aspect-ratio.mjs +9 -9
  10. package/autocomplete/README.md +473 -34
  11. package/autocomplete/fesm2022/autocomplete.mjs +53 -71
  12. package/avatar/README.md +357 -5
  13. package/avatar/fesm2022/avatar.mjs +32 -32
  14. package/badge/README.md +289 -34
  15. package/badge/fesm2022/badge.mjs +57 -57
  16. package/basic-alert/README.md +324 -34
  17. package/basic-alert/fesm2022/basic-alert.mjs +6 -18
  18. package/breadcrumb/README.md +406 -5
  19. package/breadcrumb/fesm2022/breadcrumb.mjs +33 -39
  20. package/button/README.md +453 -34
  21. package/button/fesm2022/button.mjs +147 -219
  22. package/button/types/button.d.ts +7 -10
  23. package/button-group/README.md +318 -5
  24. package/button-group/fesm2022/button-group.mjs +20 -20
  25. package/calendar/README.md +328 -34
  26. package/calendar/fesm2022/calendar.mjs +29 -743
  27. package/card/README.md +331 -5
  28. package/card/fesm2022/card.mjs +33 -33
  29. package/carousel/README.md +333 -5
  30. package/carousel/fesm2022/carousel.mjs +39 -78
  31. package/checkbox/README.md +291 -34
  32. package/checkbox/fesm2022/checkbox.mjs +20 -58
  33. package/checkbox/types/checkbox.d.ts +5 -4
  34. package/collapsible/README.md +332 -5
  35. package/collapsible/fesm2022/collapsible.mjs +15 -15
  36. package/combobox/README.md +507 -5
  37. package/combobox/fesm2022/combobox.mjs +91 -224
  38. package/command/README.md +435 -5
  39. package/command/fesm2022/command.mjs +49 -109
  40. package/confirmation-dialog/README.md +272 -34
  41. package/confirmation-dialog/fesm2022/confirmation-dialog.mjs +15 -40
  42. package/confirmation-dialog/types/confirmation-dialog.d.ts +8 -4
  43. package/context-menu/README.md +366 -5
  44. package/context-menu/fesm2022/context-menu.mjs +9 -9
  45. package/date-picker/README.md +436 -34
  46. package/date-picker/fesm2022/date-picker.mjs +64 -267
  47. package/date-picker/types/date-picker.d.ts +7 -3
  48. package/dialog/README.md +419 -34
  49. package/dialog/fesm2022/dialog.mjs +45 -71
  50. package/drawer/README.md +395 -5
  51. package/drawer/fesm2022/drawer.mjs +44 -54
  52. package/dropdown-menu/README.md +417 -5
  53. package/dropdown-menu/fesm2022/dropdown-menu.mjs +82 -70
  54. package/dropdown-menu/types/dropdown-menu.d.ts +4 -0
  55. package/empty/README.md +329 -5
  56. package/empty/fesm2022/empty.mjs +30 -30
  57. package/field/README.md +385 -5
  58. package/field/fesm2022/field.mjs +56 -72
  59. package/form-checkbox/README.md +283 -34
  60. package/form-checkbox/fesm2022/form-checkbox.mjs +25 -85
  61. package/form-checkbox/types/form-checkbox.d.ts +7 -1
  62. package/form-date-picker/README.md +293 -34
  63. package/form-date-picker/fesm2022/form-date-picker.mjs +31 -84
  64. package/form-date-picker/types/form-date-picker.d.ts +7 -1
  65. package/form-field/README.md +327 -34
  66. package/form-field/fesm2022/form-field.mjs +33 -19
  67. package/form-field/types/form-field.d.ts +28 -20
  68. package/form-field-simple/README.md +311 -34
  69. package/form-field-simple/fesm2022/form-field-simple.mjs +4 -4
  70. package/form-searchable-multiselect/README.md +332 -34
  71. package/form-searchable-multiselect/fesm2022/form-searchable-multiselect.mjs +31 -91
  72. package/form-searchable-multiselect/types/form-searchable-multiselect.d.ts +7 -1
  73. package/form-select/README.md +321 -34
  74. package/form-select/fesm2022/form-select.mjs +32 -149
  75. package/form-select/types/form-select.d.ts +7 -1
  76. package/form-text-input/README.md +342 -34
  77. package/form-text-input/fesm2022/form-text-input.mjs +35 -149
  78. package/form-text-input/types/form-text-input.d.ts +9 -12
  79. package/form-textarea/README.md +318 -34
  80. package/form-textarea/fesm2022/form-textarea.mjs +34 -109
  81. package/form-textarea/types/form-textarea.d.ts +8 -1
  82. package/hover-card/README.md +256 -5
  83. package/hover-card/fesm2022/hover-card.mjs +19 -19
  84. package/icon/README.md +210 -34
  85. package/icon/fesm2022/icon.mjs +8 -8
  86. package/input/README.md +240 -34
  87. package/input/fesm2022/input.mjs +17 -12
  88. package/input/types/input.d.ts +2 -1
  89. package/input-group/README.md +335 -5
  90. package/input-group/fesm2022/input-group.mjs +34 -34
  91. package/input-otp/README.md +375 -5
  92. package/input-otp/fesm2022/input-otp.mjs +26 -26
  93. package/item/README.md +385 -5
  94. package/item/fesm2022/item.mjs +53 -53
  95. package/kbd/README.md +291 -5
  96. package/kbd/fesm2022/kbd.mjs +12 -12
  97. package/label/README.md +243 -34
  98. package/label/fesm2022/label.mjs +10 -10
  99. package/label/types/label.d.ts +1 -1
  100. package/layout-simple/README.md +174 -44
  101. package/layout-simple/fesm2022/layout-simple.mjs +508 -603
  102. package/layout-simple/types/layout-simple.d.ts +179 -128
  103. package/menu/README.md +418 -0
  104. package/menu/fesm2022/menu.mjs +455 -0
  105. package/menu/package.json +24 -0
  106. package/menu/types/menu.d.ts +137 -0
  107. package/menubar/README.md +343 -5
  108. package/menubar/fesm2022/menubar.mjs +13 -13
  109. package/native-select/README.md +323 -5
  110. package/native-select/fesm2022/native-select.mjs +19 -19
  111. package/navigation-menu/README.md +369 -5
  112. package/navigation-menu/fesm2022/navigation-menu.mjs +35 -47
  113. package/package.json +183 -154
  114. package/pagination/README.md +388 -5
  115. package/pagination/fesm2022/pagination.mjs +45 -331
  116. package/popover/README.md +302 -34
  117. package/popover/fesm2022/popover.mjs +30 -30
  118. package/progress/README.md +311 -5
  119. package/progress/fesm2022/progress.mjs +13 -13
  120. package/radio-group/README.md +335 -34
  121. package/radio-group/fesm2022/radio-group.mjs +26 -19
  122. package/radio-group/types/radio-group.d.ts +1 -1
  123. package/resizable/README.md +269 -5
  124. package/resizable/fesm2022/resizable.mjs +18 -26
  125. package/scroll-area/README.md +233 -5
  126. package/scroll-area/fesm2022/scroll-area.mjs +9 -9
  127. package/searchable-multiselect/README.md +294 -34
  128. package/searchable-multiselect/fesm2022/searchable-multiselect.mjs +28 -117
  129. package/searchable-multiselect/types/searchable-multiselect.d.ts +8 -2
  130. package/select/README.md +408 -34
  131. package/select/fesm2022/select.mjs +71 -111
  132. package/select/types/select.d.ts +3 -1
  133. package/separator/README.md +193 -34
  134. package/separator/fesm2022/separator.mjs +9 -9
  135. package/separator/types/separator.d.ts +1 -1
  136. package/sheet/README.md +282 -34
  137. package/sheet/fesm2022/sheet.mjs +41 -53
  138. package/sidebar/README.md +457 -5
  139. package/sidebar/fesm2022/sidebar.mjs +125 -227
  140. package/skeleton/README.md +217 -5
  141. package/skeleton/fesm2022/skeleton.mjs +9 -9
  142. package/slider/README.md +273 -5
  143. package/slider/fesm2022/slider.mjs +10 -80
  144. package/sonner/README.md +317 -34
  145. package/sonner/fesm2022/sonner.mjs +13 -13
  146. package/spinner/README.md +255 -34
  147. package/spinner/fesm2022/spinner.mjs +11 -11
  148. package/switch/README.md +281 -34
  149. package/switch/fesm2022/switch.mjs +13 -15
  150. package/table/README.md +423 -5
  151. package/table/fesm2022/table.mjs +41 -41
  152. package/tabs/README.md +382 -34
  153. package/tabs/fesm2022/tabs.mjs +35 -111
  154. package/textarea/README.md +282 -5
  155. package/textarea/fesm2022/textarea.mjs +9 -9
  156. package/toggle/README.md +270 -5
  157. package/toggle/fesm2022/toggle.mjs +13 -13
  158. package/toggle-group/README.md +340 -5
  159. package/toggle-group/fesm2022/toggle-group.mjs +13 -13
  160. package/toggle-group/types/toggle-group.d.ts +13 -7
  161. package/tooltip/README.md +240 -34
  162. package/tooltip/fesm2022/tooltip.mjs +14 -14
  163. package/typography/README.md +271 -5
  164. package/typography/fesm2022/typography.mjs +53 -53
  165. package/typography/types/typography.d.ts +19 -19
  166. package/utils/README.md +274 -34
  167. package/utils/fesm2022/utils.mjs +68 -44
  168. package/accordion/fesm2022/accordion.mjs.map +0 -1
  169. package/alert/fesm2022/alert.mjs.map +0 -1
  170. package/alert-dialog/fesm2022/alert-dialog.mjs.map +0 -1
  171. package/aspect-ratio/fesm2022/aspect-ratio.mjs.map +0 -1
  172. package/autocomplete/fesm2022/autocomplete.mjs.map +0 -1
  173. package/avatar/fesm2022/avatar.mjs.map +0 -1
  174. package/badge/fesm2022/badge.mjs.map +0 -1
  175. package/basic-alert/fesm2022/basic-alert.mjs.map +0 -1
  176. package/breadcrumb/fesm2022/breadcrumb.mjs.map +0 -1
  177. package/button/fesm2022/button.mjs.map +0 -1
  178. package/button-group/fesm2022/button-group.mjs.map +0 -1
  179. package/calendar/fesm2022/calendar.mjs.map +0 -1
  180. package/card/fesm2022/card.mjs.map +0 -1
  181. package/carousel/fesm2022/carousel.mjs.map +0 -1
  182. package/checkbox/fesm2022/checkbox.mjs.map +0 -1
  183. package/collapsible/fesm2022/collapsible.mjs.map +0 -1
  184. package/combobox/fesm2022/combobox.mjs.map +0 -1
  185. package/command/fesm2022/command.mjs.map +0 -1
  186. package/confirmation-dialog/fesm2022/confirmation-dialog.mjs.map +0 -1
  187. package/context-menu/fesm2022/context-menu.mjs.map +0 -1
  188. package/date-picker/fesm2022/date-picker.mjs.map +0 -1
  189. package/dialog/fesm2022/dialog.mjs.map +0 -1
  190. package/drawer/fesm2022/drawer.mjs.map +0 -1
  191. package/dropdown-menu/fesm2022/dropdown-menu.mjs.map +0 -1
  192. package/empty/fesm2022/empty.mjs.map +0 -1
  193. package/field/fesm2022/field.mjs.map +0 -1
  194. package/form-checkbox/fesm2022/form-checkbox.mjs.map +0 -1
  195. package/form-date-picker/fesm2022/form-date-picker.mjs.map +0 -1
  196. package/form-field/fesm2022/form-field.mjs.map +0 -1
  197. package/form-field-simple/fesm2022/form-field-simple.mjs.map +0 -1
  198. package/form-searchable-multiselect/fesm2022/form-searchable-multiselect.mjs.map +0 -1
  199. package/form-select/fesm2022/form-select.mjs.map +0 -1
  200. package/form-text-input/fesm2022/form-text-input.mjs.map +0 -1
  201. package/form-textarea/fesm2022/form-textarea.mjs.map +0 -1
  202. package/hover-card/fesm2022/hover-card.mjs.map +0 -1
  203. package/icon/fesm2022/icon.mjs.map +0 -1
  204. package/input/fesm2022/input.mjs.map +0 -1
  205. package/input-group/fesm2022/input-group.mjs.map +0 -1
  206. package/input-otp/fesm2022/input-otp.mjs.map +0 -1
  207. package/item/fesm2022/item.mjs.map +0 -1
  208. package/kbd/fesm2022/kbd.mjs.map +0 -1
  209. package/label/fesm2022/label.mjs.map +0 -1
  210. package/layout-simple/fesm2022/layout-simple.mjs.map +0 -1
  211. package/menubar/fesm2022/menubar.mjs.map +0 -1
  212. package/native-select/fesm2022/native-select.mjs.map +0 -1
  213. package/navigation-menu/fesm2022/navigation-menu.mjs.map +0 -1
  214. package/pagination/fesm2022/pagination.mjs.map +0 -1
  215. package/popover/fesm2022/popover.mjs.map +0 -1
  216. package/progress/fesm2022/progress.mjs.map +0 -1
  217. package/radio-group/fesm2022/radio-group.mjs.map +0 -1
  218. package/resizable/fesm2022/resizable.mjs.map +0 -1
  219. package/scroll-area/fesm2022/scroll-area.mjs.map +0 -1
  220. package/searchable-multiselect/fesm2022/searchable-multiselect.mjs.map +0 -1
  221. package/select/fesm2022/select.mjs.map +0 -1
  222. package/separator/fesm2022/separator.mjs.map +0 -1
  223. package/sheet/fesm2022/sheet.mjs.map +0 -1
  224. package/sidebar/fesm2022/sidebar.mjs.map +0 -1
  225. package/skeleton/fesm2022/skeleton.mjs.map +0 -1
  226. package/slider/fesm2022/slider.mjs.map +0 -1
  227. package/sonner/fesm2022/sonner.mjs.map +0 -1
  228. package/spinner/fesm2022/spinner.mjs.map +0 -1
  229. package/switch/fesm2022/switch.mjs.map +0 -1
  230. package/table/fesm2022/table.mjs.map +0 -1
  231. package/tabs/fesm2022/tabs.mjs.map +0 -1
  232. package/textarea/fesm2022/textarea.mjs.map +0 -1
  233. package/toggle/fesm2022/toggle.mjs.map +0 -1
  234. package/toggle-group/fesm2022/toggle-group.mjs.map +0 -1
  235. package/tooltip/fesm2022/tooltip.mjs.map +0 -1
  236. package/typography/fesm2022/typography.mjs.map +0 -1
  237. 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/label';
6
6
  import { EgFormField } from '@egose/shadcn-theme-ng/form-field-simple';
7
- import { HlmError, HlmHint } from '@egose/shadcn-theme-ng/form-field';
7
+ import { HlmFormIdGenerator, HlmError, HlmHint } from '@egose/shadcn-theme-ng/form-field';
8
8
  import { hlm } from '@egose/shadcn-theme-ng/utils';
9
9
  import { EgSearchableMultiselect } from '@egose/shadcn-theme-ng/searchable-multiselect';
10
10
 
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 }]
@@ -43,106 +59,30 @@ class EgFormSearchableMultiselect {
43
59
  hintClass = input('', /* @ts-ignore */
44
60
  ...(ngDevMode ? [{ debugName: "hintClass" }] : /* istanbul ignore next */ []));
45
61
  // Computed class names
46
- $userClass = computed(() => hlm('flex flex-col', this.userClass()), /* @ts-ignore */
62
+ $userClass = computed(() => hlm("flex flex-col", this.userClass()), /* @ts-ignore */
47
63
  ...(ngDevMode ? [{ debugName: "$userClass" }] : /* istanbul ignore next */ []));
48
- $labelClass = computed(() => hlm('mb-1 gap-0', this.labelClass()), /* @ts-ignore */
64
+ $labelClass = computed(() => hlm("mb-1 gap-0", this.labelClass()), /* @ts-ignore */
49
65
  ...(ngDevMode ? [{ debugName: "$labelClass" }] : /* istanbul ignore next */ []));
50
- $controlClass = computed(() => hlm('w-full', this.controlClass()), /* @ts-ignore */
66
+ $controlClass = computed(() => hlm("w-full", this.controlClass()), /* @ts-ignore */
51
67
  ...(ngDevMode ? [{ debugName: "$controlClass" }] : /* istanbul ignore next */ []));
52
- $errorClass = computed(() => hlm('mt-0', this.errorClass()), /* @ts-ignore */
68
+ $errorClass = computed(() => hlm("mt-0", this.errorClass()), /* @ts-ignore */
53
69
  ...(ngDevMode ? [{ debugName: "$errorClass" }] : /* istanbul ignore next */ []));
54
- $hintClass = computed(() => hlm('mt-0', this.hintClass()), /* @ts-ignore */
70
+ $hintClass = computed(() => hlm("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: "w-full" }, providers: [{ provide: ControlContainer, useExisting: FormGroupDirective }], ngImport: i0, template: `
58
- @let lbl = label();
59
- @let cid = controlId();
60
- @let cnm = controlName();
61
- @let err = error();
62
- @let hnt = hint();
63
- @let rqrd = required();
64
-
65
- <eg-form-field>
66
- @if (lbl) {
67
- <span hlmLabel [class]="$labelClass()">
68
- {{ lbl }}
69
- @if (rqrd) {
70
- <span class="text-red-500">*</span>
71
- }
72
- </span>
73
- }
74
-
75
- <eg-searchable-multiselect
76
- [id]="cid || id()"
77
- [options]="options()"
78
- [placeholder]="placeholder()"
79
- [formControlName]="cnm"
80
- [required]="rqrd"
81
- [class]="$controlClass()"
82
- ></eg-searchable-multiselect>
83
-
84
- @if (err) {
85
- <hlm-error [class]="$errorClass()">
86
- {{ err }}
87
- </hlm-error>
88
- }
89
- @if (hnt) {
90
- <hlm-hint [class]="$hintClass()">
91
- {{ hnt }}
92
- </hlm-hint>
93
- }
94
- </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"] }] });
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: "w-full" }, providers: [{ provide: ControlContainer, useExisting: FormGroupDirective }], ngImport: i0, template: "\n @let lbl = label();\n @let cnm = controlName();\n @let err = error();\n @let hnt = hint();\n @let rqrd = required();\n\n <eg-form-field>\n @if (lbl) {\n <label hlmLabel [for]=\"effectiveId()\" [class]=\"$labelClass()\">\n {{ lbl }}\n @if (rqrd) {\n <span class=\"text-red-500\">*</span>\n }\n </label>\n }\n\n <eg-searchable-multiselect\n [id]=\"effectiveId()\"\n [options]=\"options()\"\n [placeholder]=\"placeholder()\"\n [formControlName]=\"cnm\"\n [required]=\"rqrd\"\n [wrapperDisabled]=\"disabled()\"\n [ariaLabel]=\"lbl\"\n [ariaDescribedby]=\"describedBy()\"\n [class]=\"$controlClass()\"\n ></eg-searchable-multiselect>\n\n @if (err) {\n <hlm-error [id]=\"errorId()\" [class]=\"$errorClass()\">\n {{ err }}\n </hlm-error>\n }\n @if (hnt) {\n <hlm-hint [id]=\"hintId()\" [class]=\"$hintClass()\">\n {{ hnt }}\n </hlm-hint>\n }\n </eg-form-field>\n ", isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: EgFormField, selector: "eg-form-field", inputs: ["class"] }, { kind: "directive", type: HlmError, selector: "hlm-error", inputs: ["class"] }, { kind: "directive", type: HlmHint, selector: "hlm-hint", inputs: ["class"] }, { kind: "directive", type: HlmLabel, selector: "[hlmLabel]", inputs: ["class"] }, { kind: "component", type: EgSearchableMultiselect, selector: "eg-searchable-multiselect", inputs: ["options", "placeholder", "id", "disabled", "wrapperDisabled", "ariaLabel", "ariaDescribedby", "class", "value"], outputs: ["valueChange"] }] });
96
74
  }
97
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: EgFormSearchableMultiselect, decorators: [{
75
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgFormSearchableMultiselect, decorators: [{
98
76
  type: Component,
99
77
  args: [{
100
78
  selector: 'eg-form-searchable-multiselect',
101
79
  standalone: true,
102
80
  host: {
103
- class: 'w-full',
81
+ class: "w-full",
104
82
  },
105
83
  imports: [ReactiveFormsModule, EgFormField, HlmError, HlmHint, HlmLabel, EgSearchableMultiselect],
106
84
  providers: [{ provide: ControlContainer, useExisting: FormGroupDirective }],
107
- template: `
108
- @let lbl = label();
109
- @let cid = controlId();
110
- @let cnm = controlName();
111
- @let err = error();
112
- @let hnt = hint();
113
- @let rqrd = required();
114
-
115
- <eg-form-field>
116
- @if (lbl) {
117
- <span hlmLabel [class]="$labelClass()">
118
- {{ lbl }}
119
- @if (rqrd) {
120
- <span class="text-red-500">*</span>
121
- }
122
- </span>
123
- }
124
-
125
- <eg-searchable-multiselect
126
- [id]="cid || id()"
127
- [options]="options()"
128
- [placeholder]="placeholder()"
129
- [formControlName]="cnm"
130
- [required]="rqrd"
131
- [class]="$controlClass()"
132
- ></eg-searchable-multiselect>
133
-
134
- @if (err) {
135
- <hlm-error [class]="$errorClass()">
136
- {{ err }}
137
- </hlm-error>
138
- }
139
- @if (hnt) {
140
- <hlm-hint [class]="$hintClass()">
141
- {{ hnt }}
142
- </hlm-hint>
143
- }
144
- </eg-form-field>
145
- `,
85
+ template: "\n @let lbl = label();\n @let cnm = controlName();\n @let err = error();\n @let hnt = hint();\n @let rqrd = required();\n\n <eg-form-field>\n @if (lbl) {\n <label hlmLabel [for]=\"effectiveId()\" [class]=\"$labelClass()\">\n {{ lbl }}\n @if (rqrd) {\n <span class=\"text-red-500\">*</span>\n }\n </label>\n }\n\n <eg-searchable-multiselect\n [id]=\"effectiveId()\"\n [options]=\"options()\"\n [placeholder]=\"placeholder()\"\n [formControlName]=\"cnm\"\n [required]=\"rqrd\"\n [wrapperDisabled]=\"disabled()\"\n [ariaLabel]=\"lbl\"\n [ariaDescribedby]=\"describedBy()\"\n [class]=\"$controlClass()\"\n ></eg-searchable-multiselect>\n\n @if (err) {\n <hlm-error [id]=\"errorId()\" [class]=\"$errorClass()\">\n {{ err }}\n </hlm-error>\n }\n @if (hnt) {\n <hlm-hint [id]=\"hintId()\" [class]=\"$hintClass()\">\n {{ hnt }}\n </hlm-hint>\n }\n </eg-form-field>\n ",
146
86
  }]
147
87
  }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], controlId: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlId", required: false }] }], controlName: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlName", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], labelClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelClass", required: false }] }], controlClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlClass", required: false }] }], errorClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorClass", required: false }] }], hintClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "hintClass", required: false }] }] } });
148
88
 
@@ -155,4 +95,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
155
95
  */
156
96
 
157
97
  export { EgFormSearchableMultiselect };
158
- //# sourceMappingURL=form-searchable-multiselect.mjs.map
98
+
@@ -3,15 +3,21 @@ import { ClassValue } from 'clsx';
3
3
  import { SelectOption } from '@egose/shadcn-theme-ng/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>;