@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,324 @@
1
- # MultiselectAutocomplete
1
+ # Searchable Multiselect (`@egose/shadcn-theme-ng/searchable-multiselect`)
2
2
 
3
- This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 20.3.0.
3
+ A shadcn/ui-style **multi-select with chips + popover picker** — selected values render as removable chips, and a popover holds the checkbox option list. There is no exact single shadcn/ui counterpart; it composes the `Popover`, `Button`, and `Checkbox` patterns into one opinionated control.
4
4
 
5
- ## Code scaffolding
5
+ It is a **standalone `ControlValueAccessor` component** (`EgSearchableMultiselect`), so it binds directly to Angular reactive forms (`formControlName`) and template-driven forms (`ngModel`) with a `string[]` value. Internally it reuses `HlmPopover`/`HlmCheckbox`/`HlmButton` — you do not import those yourself.
6
6
 
7
- Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
7
+ > **Ships as:** `@egose/shadcn-theme-ng/searchable-multiselect` and `@egose/shadcn-theme-ng-tw/searchable-multiselect` (the `tw:`-prefixed Tailwind variant — same API, class strings prefixed with `tw:`).
8
+ > See the [package README](../../README.md) for installation, peer dependencies, and Tailwind setup. Do not publish this project directory independently.
9
+
10
+ ## Installation
8
11
 
9
12
  ```bash
10
- ng generate component component-name
13
+ # Plain Tailwind (no prefix)
14
+ npm install @egose/shadcn-theme-ng
15
+
16
+ # Or the tw:-prefixed variant
17
+ npm install @egose/shadcn-theme-ng-tw
18
+ ```
19
+
20
+ ```ts
21
+ import { EgSearchableMultiselect } from '@egose/shadcn-theme-ng/searchable-multiselect';
22
+ // tw variant:
23
+ // import { EgSearchableMultiselect } from '@egose/shadcn-theme-ng-tw/searchable-multiselect';
11
24
  ```
12
25
 
13
- For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
26
+ Peer dependencies (see [package README](../../README.md) for versions): `@angular/core`, `@angular/common`. (No `@spartan-ng/brain` peer — popover/checkbox/button come along as regular library dependencies.)
14
27
 
15
- ```bash
16
- ng generate --help
28
+ ## Imports
29
+
30
+ Real exported symbols (from `src/public-api.ts`):
31
+
32
+ | Symbol | Kind | Description |
33
+ | ------------------------- | -------------------------------------------------- | ---------------------------------------------------------- |
34
+ | `EgSearchableMultiselect` | Standalone component (`eg-searchable-multiselect`) | The whole control; provides `NG_VALUE_ACCESSOR` for itself |
35
+ | `SelectOption` | Interface | `{ label: string; value: string }` |
36
+
37
+ > Note: unlike most subpaths, this one exposes **no `*Imports` array and no `*Module`**. Import `EgSearchableMultiselect` directly — it is standalone.
38
+
39
+ ```ts
40
+ import { Component } from '@angular/core';
41
+ import { EgSearchableMultiselect } from '@egose/shadcn-theme-ng/searchable-multiselect';
42
+
43
+ @Component({
44
+ selector: 'app-demo',
45
+ standalone: true,
46
+ imports: [EgSearchableMultiselect],
47
+ template: ` <eg-searchable-multiselect [options]="options" [(value)]="selected" /> `,
48
+ })
49
+ export class DemoComponent {}
50
+ ```
51
+
52
+ ## Anatomy / Structure
53
+
54
+ ```html
55
+ <eg-searchable-multiselect [options]="options" placeholder="Pick frameworks…" [(value)]="selected" />
17
56
  ```
18
57
 
19
- ## Building
58
+ Renders (internally — you do not write this yourself):
20
59
 
21
- To build the library, run:
60
+ ```html
61
+ <eg-searchable-multiselect>
62
+ <!-- chip row: placeholder pill when empty, removable chips otherwise -->
63
+ <div>
64
+ <span><!-- {{ placeholder }} or chip {{ item.label }} + ✕ button --></span>
65
+ </div>
22
66
 
23
- ```bash
24
- ng build searchable-multiselect
67
+ <!-- popover trigger + checkbox list -->
68
+ <hlm-popover>
69
+ <button hlmPopoverTrigger hlmButton>N selected</button>
70
+ <hlm-popover-content>
71
+ <label><!-- <hlm-checkbox> per option + label text --></label>
72
+ </hlm-popover-content>
73
+ </hlm-popover>
74
+ </eg-searchable-multiselect>
25
75
  ```
26
76
 
27
- This command will compile your project, and the build artifacts will be placed in the `dist/` directory.
77
+ Real selector: `eg-searchable-multiselect` (element). The `✕` chip buttons and popover trigger honor the disabled state; the popover panel is `tw:w-64` with a `tw:max-h-60` scrolling option list.
28
78
 
29
- ### Publishing the Library
79
+ ## API reference
30
80
 
31
- Once the project is built, you can publish your library by following these steps:
81
+ ### EgSearchableMultiselect (component, `ControlValueAccessor`)
32
82
 
33
- 1. Navigate to the `dist` directory:
83
+ | Input | Type | Default | Description |
84
+ | --------------------- | --------------------- | ------------------------ | -------------------------------------------------------------------------------- |
85
+ | `options` | `SelectOption[]` | `[]` | Full option list (`{ label, value }`) |
86
+ | `value` | `string[]` | `[]` | Selected values (one-way in; pairs with `valueChange` for two-way `[(value)]`) |
87
+ | `placeholder` | `string` | `'Start typing to add…'` | Text of the pill shown when nothing is selected |
88
+ | `id` | `string` | `''` | `id` placed on the trigger button |
89
+ | `disabled` | `boolean` | `false` | Disables chips + trigger + checkboxes |
90
+ | `wrapperDisabled` | `boolean` | `false` | Second disable flag (e.g. set by wrapper form components); OR-ed with `disabled` |
91
+ | `ariaLabel` | `string \| undefined` | `undefined` | `aria-label` for the trigger button |
92
+ | `ariaDescribedby` | `string \| null` | `null` | `aria-describedby` for the trigger button |
93
+ | `class` (`userClass`) | `ClassValue` | `''` | Extra classes on the host |
34
94
 
35
- ```bash
36
- cd dist/searchable-multiselect
37
- ```
95
+ | Output | Type | Description |
96
+ | ------------- | ---------- | ---------------------------------------------------- |
97
+ | `valueChange` | `string[]` | Emitted with the new value array on every add/remove |
38
98
 
39
- 2. Run the `npm publish` command to publish your library to the npm registry:
40
- ```bash
41
- npm publish
42
- ```
99
+ `ControlValueAccessor` contract: `writeValue(values)`, `registerOnChange`, `registerOnTouched`, `setDisabledState` are implemented, so `formControl` / `formControlName` / `ngModel` all work. Effective disabled state = `disabled() \|\| wrapperDisabled() \|\| formDisabled()` (the last set by forms via `setDisabledState`).
43
100
 
44
- ## Running unit tests
101
+ API surprise worth knowing: despite the "searchable" name, the current template ships **no filter text field** — the popover shows the full checkbox list and empty state reads "No options". Treat `options` as the complete visible list (filter it yourself before passing it in if you need search). Also `value` is a plain `input`, not a `model`: form writes flow through `writeValue`, and user edits flow out through `valueChange` + the CVA `onChange` callback.
45
102
 
46
- To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
103
+ ## Examples
47
104
 
48
- ```bash
49
- ng test
105
+ ### 1. Basic two-way binding
106
+
107
+ ```ts
108
+ import { Component, signal } from '@angular/core';
109
+ import { EgSearchableMultiselect, type SelectOption } from '@egose/shadcn-theme-ng/searchable-multiselect';
110
+
111
+ @Component({
112
+ selector: 'app-basic-multi',
113
+ standalone: true,
114
+ imports: [EgSearchableMultiselect],
115
+ template: `
116
+ <eg-searchable-multiselect [options]="frameworks" placeholder="Pick frameworks…" [(value)]="selected" />
117
+ <p>Selected: {{ selected().join(', ') || 'none' }}</p>
118
+ `,
119
+ })
120
+ export class BasicMultiComponent {
121
+ readonly frameworks: SelectOption[] = [
122
+ { label: 'Angular', value: 'angular' },
123
+ { label: 'React', value: 'react' },
124
+ { label: 'Vue', value: 'vue' },
125
+ { label: 'Svelte', value: 'svelte' },
126
+ ];
127
+ readonly selected = signal<string[]>(['angular']);
128
+ }
50
129
  ```
51
130
 
52
- ## Running end-to-end tests
131
+ ### 2. Reactive forms
53
132
 
54
- For end-to-end (e2e) testing, run:
133
+ ```ts
134
+ import { Component } from '@angular/core';
135
+ import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
136
+ import { EgSearchableMultiselect, type SelectOption } from '@egose/shadcn-theme-ng/searchable-multiselect';
55
137
 
56
- ```bash
57
- ng e2e
138
+ @Component({
139
+ selector: 'app-reactive-multi',
140
+ standalone: true,
141
+ imports: [EgSearchableMultiselect, ReactiveFormsModule],
142
+ template: `
143
+ <form [formGroup]="form" (ngSubmit)="submit()">
144
+ <label for="skills">Skills (pick at least one)</label>
145
+ <eg-searchable-multiselect id="skills" [options]="skills" formControlName="skillIds" />
146
+ @if (form.controls.skillIds.invalid && form.controls.skillIds.touched) {
147
+ <p class="tw:text-destructive tw:text-sm">Choose at least one skill.</p>
148
+ }
149
+ <button type="submit">Save</button>
150
+ </form>
151
+ `,
152
+ })
153
+ export class ReactiveMultiComponent {
154
+ readonly skills: SelectOption[] = [
155
+ { label: 'TypeScript', value: 'ts' },
156
+ { label: 'CSS', value: 'css' },
157
+ { label: 'Testing', value: 'testing' },
158
+ ];
159
+ readonly form = new FormGroup({
160
+ skillIds: new FormControl<string[]>(['ts'], { validators: Validators.required }),
161
+ });
162
+
163
+ submit(): void {
164
+ this.form.markAllAsTouched();
165
+ console.log(this.form.value);
166
+ }
167
+ }
58
168
  ```
59
169
 
60
- Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
170
+ ### 3. Template-driven forms (`ngModel`)
171
+
172
+ ```ts
173
+ import { Component } from '@angular/core';
174
+ import { FormsModule } from '@angular/forms';
175
+ import { EgSearchableMultiselect, type SelectOption } from '@egose/shadcn-theme-ng/searchable-multiselect';
176
+
177
+ @Component({
178
+ selector: 'app-ngmodel-multi',
179
+ standalone: true,
180
+ imports: [EgSearchableMultiselect, FormsModule],
181
+ template: `
182
+ <eg-searchable-multiselect name="tags" [options]="tags" [(ngModel)]="selected" #tagsCtrl="ngModel" />
183
+ @if (tagsCtrl.touched && !selected.length) {
184
+ <p class="tw:text-destructive tw:text-sm">Pick at least one tag.</p>
185
+ }
186
+ `,
187
+ })
188
+ export class NgModelMultiComponent {
189
+ readonly tags: SelectOption[] = [
190
+ { label: 'Bug', value: 'bug' },
191
+ { label: 'Feature', value: 'feature' },
192
+ { label: 'Docs', value: 'docs' },
193
+ ];
194
+ selected: string[] = [];
195
+ }
196
+ ```
197
+
198
+ ### 4. Disabled / read-only states
199
+
200
+ ```ts
201
+ import { Component } from '@angular/core';
202
+ import { EgSearchableMultiselect } from '@egose/shadcn-theme-ng/searchable-multiselect';
203
+
204
+ @Component({
205
+ selector: 'app-disabled-multi',
206
+ standalone: true,
207
+ imports: [EgSearchableMultiselect],
208
+ template: `
209
+ <!-- fully disabled: chips, ✕ buttons, trigger, checkboxes -->
210
+ <eg-searchable-multiselect [options]="options" [value]="['a']" disabled />
211
+
212
+ <!-- wrapper-driven disable (e.g. parent form section locked) -->
213
+ <eg-searchable-multiselect [options]="options" [value]="['a']" [wrapperDisabled]="locked" />
214
+ `,
215
+ })
216
+ export class DisabledMultiComponent {
217
+ readonly locked = true;
218
+ readonly options = [
219
+ { label: 'Alpha', value: 'a' },
220
+ { label: 'Beta', value: 'b' },
221
+ ];
222
+ }
223
+ ```
224
+
225
+ Disabling via `formControl.disable()` works too — it flows through `setDisabledState`.
226
+
227
+ ### 5. Client-side search (filter `options` yourself)
228
+
229
+ Since the popover lists exactly what you pass in `options`, implement search by filtering upstream:
230
+
231
+ ```ts
232
+ import { Component, computed, signal } from '@angular/core';
233
+ import { EgSearchableMultiselect } from '@egose/shadcn-theme-ng/searchable-multiselect';
234
+
235
+ @Component({
236
+ selector: 'app-search-multi',
237
+ standalone: true,
238
+ imports: [EgSearchableMultiselect],
239
+ template: `
240
+ <input
241
+ type="search"
242
+ placeholder="Filter options…"
243
+ [value]="query()"
244
+ (input)="query.set($any($event.target).value)"
245
+ aria-label="Filter options"
246
+ />
247
+ <eg-searchable-multiselect [options]="filtered()" [(value)]="selected" />
248
+ `,
249
+ })
250
+ export class SearchMultiComponent {
251
+ readonly query = signal('');
252
+ readonly selected = signal<string[]>([]);
253
+ private readonly all = [
254
+ { label: 'Angular', value: 'angular' },
255
+ { label: 'React', value: 'react' },
256
+ { label: 'Vue', value: 'vue' },
257
+ { label: 'Svelte', value: 'svelte' },
258
+ { label: 'Solid', value: 'solid' },
259
+ ];
260
+ readonly filtered = computed(() => {
261
+ const q = this.query().trim().toLowerCase();
262
+ return q ? this.all.filter((o) => o.label.toLowerCase().includes(q)) : this.all;
263
+ });
264
+ }
265
+ ```
266
+
267
+ Note: selections whose option is currently filtered out stay selected internally (chips still show) but have no checkbox row until the filter matches again. Unknown values passed via `value`/`writeValue` that match no option are dropped from the chip row.
268
+
269
+ ### 6. Async options + reacting to changes
270
+
271
+ ```ts
272
+ import { Component, resource, signal } from '@angular/core';
273
+ import { EgSearchableMultiselect, type SelectOption } from '@egose/shadcn-theme-ng/searchable-multiselect';
274
+
275
+ @Component({
276
+ selector: 'app-async-multi',
277
+ standalone: true,
278
+ imports: [EgSearchableMultiselect],
279
+ template: `
280
+ @if (users.isLoading()) {
281
+ <p>Loading users…</p>
282
+ } @else {
283
+ <eg-searchable-multiselect
284
+ [options]="users.value() ?? []"
285
+ [(value)]="assignees"
286
+ (valueChange)="onChange($event)"
287
+ ariaLabel="Assignees"
288
+ />
289
+ }
290
+ `,
291
+ })
292
+ export class AsyncMultiComponent {
293
+ readonly assignees = signal<string[]>([]);
294
+ readonly users = resource({
295
+ loader: async (): Promise<SelectOption[]> => {
296
+ const res = await fetch('/api/users');
297
+ const list = (await res.json()) as Array<{ id: string; name: string }>;
298
+ return list.map((u) => ({ label: u.name, value: u.id }));
299
+ },
300
+ });
301
+
302
+ onChange(values: string[]): void {
303
+ console.log('assignees now:', values);
304
+ }
305
+ }
306
+ ```
307
+
308
+ ## Accessibility notes
309
+
310
+ - The trigger is a real `<button>` — give it an accessible name via `ariaLabel` (or a visible `<label>` paired with `id`) and descriptions via `ariaDescribedby`.
311
+ - Options render as native checkbox-backed `hlm-checkbox` rows inside `<label>` elements, so they are keyboard-operable and announced per option.
312
+ - Chip `✕` buttons are real buttons and disabled along with the control; keep chip text concise so screen readers announce removals cleanly.
313
+ - The empty state is a plain text pill (not focusable) — the popover trigger remains the single keyboard entry point, which keeps tab order simple.
314
+
315
+ ## Theming / CSS variables
316
+
317
+ Class-driven (chips, popover panel, checkbox rows). Extend via the `class` input on the host; inner popover width (`tw:w-64`) and list height (`tw:max-h-60`) are fixed in the template.
61
318
 
62
- ## Additional Resources
319
+ ## Related subpaths
63
320
 
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.
321
+ - `@egose/shadcn-theme-ng/form-searchable-multiselect` — form-field wrapper (label/description/error) around this control
322
+ - `@egose/shadcn-theme-ng/select` — single-select dropdown counterpart
323
+ - `@egose/shadcn-theme-ng/popover` — the underlying popover primitive
324
+ - `@egose/shadcn-theme-ng/checkbox` — the underlying option-row primitive
@@ -1,7 +1,7 @@
1
1
  import * as i0 from '@angular/core';
2
2
  import { input, output, signal, computed, forwardRef, ChangeDetectionStrategy, Component } from '@angular/core';
3
3
  import { NG_VALUE_ACCESSOR } from '@angular/forms';
4
- import { HlmPopover, HlmPopoverTrigger, HlmPopoverContent } from '@egose/shadcn-theme-ng-tw/popover';
4
+ import { HlmPopover, HlmPopoverTrigger, HlmPopoverContent, HlmPopoverPortal } from '@egose/shadcn-theme-ng-tw/popover';
5
5
  import { HlmButton } from '@egose/shadcn-theme-ng-tw/button';
6
6
  import { HlmCheckbox } from '@egose/shadcn-theme-ng-tw/checkbox';
7
7
  import { hlm } from '@egose/shadcn-theme-ng-tw/utils';
@@ -12,6 +12,16 @@ class EgSearchableMultiselect {
12
12
  ...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ []));
13
13
  placeholder = input('Start typing to add…', /* @ts-ignore */
14
14
  ...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
15
+ id = input('', /* @ts-ignore */
16
+ ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
17
+ disabled = input(false, /* @ts-ignore */
18
+ ...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
19
+ wrapperDisabled = input(false, /* @ts-ignore */
20
+ ...(ngDevMode ? [{ debugName: "wrapperDisabled" }] : /* istanbul ignore next */ []));
21
+ ariaLabel = input(undefined, /* @ts-ignore */
22
+ ...(ngDevMode ? [{ debugName: "ariaLabel" }] : /* istanbul ignore next */ []));
23
+ ariaDescribedby = input(null, /* @ts-ignore */
24
+ ...(ngDevMode ? [{ debugName: "ariaDescribedby" }] : /* istanbul ignore next */ []));
15
25
  userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
16
26
  /** Value: array of selected values (string[]) */
17
27
  value = input([], /* @ts-ignore */
@@ -19,7 +29,10 @@ class EgSearchableMultiselect {
19
29
  valueChange = output();
20
30
  selectedItems = signal([], /* @ts-ignore */
21
31
  ...(ngDevMode ? [{ debugName: "selectedItems" }] : /* istanbul ignore next */ []));
22
- isDisabled = false;
32
+ formDisabled = signal(false, /* @ts-ignore */
33
+ ...(ngDevMode ? [{ debugName: "formDisabled" }] : /* istanbul ignore next */ []));
34
+ disabledState = computed(() => this.disabled() || this.wrapperDisabled() || this.formDisabled(), /* @ts-ignore */
35
+ ...(ngDevMode ? [{ debugName: "disabledState" }] : /* istanbul ignore next */ []));
23
36
  isFormBound = false;
24
37
  _hostClass = computed(() => hlm(this.userClass()), /* @ts-ignore */
25
38
  ...(ngDevMode ? [{ debugName: "_hostClass" }] : /* istanbul ignore next */ []));
@@ -27,6 +40,8 @@ class EgSearchableMultiselect {
27
40
  return this.selectedItems().some((s) => s.value === value);
28
41
  }
29
42
  toggle(value, checked) {
43
+ if (this.disabledState())
44
+ return;
30
45
  if (checked) {
31
46
  this.addItem(value);
32
47
  }
@@ -44,6 +59,8 @@ class EgSearchableMultiselect {
44
59
  }
45
60
  }
46
61
  removeItem(value) {
62
+ if (this.disabledState())
63
+ return;
47
64
  const updated = this.selectedItems().filter((i) => i.value !== value);
48
65
  this.updateValueFromSelected(updated);
49
66
  }
@@ -75,10 +92,10 @@ class EgSearchableMultiselect {
75
92
  this.onTouched = fn;
76
93
  }
77
94
  setDisabledState(isDisabled) {
78
- this.isDisabled = isDisabled;
95
+ this.formDisabled.set(isDisabled);
79
96
  }
80
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: EgSearchableMultiselect, deps: [], target: i0.ɵɵFactoryTarget.Component });
81
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: EgSearchableMultiselect, isStandalone: true, selector: "eg-searchable-multiselect", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, host: { properties: { "class": "_hostClass()" } }, providers: [
97
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgSearchableMultiselect, deps: [], target: i0.ɵɵFactoryTarget.Component });
98
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgSearchableMultiselect, isStandalone: true, selector: "eg-searchable-multiselect", inputs: { options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, wrapperDisabled: { classPropertyName: "wrapperDisabled", publicName: "wrapperDisabled", isSignal: true, isRequired: false, transformFunction: null }, ariaLabel: { classPropertyName: "ariaLabel", publicName: "ariaLabel", isSignal: true, isRequired: false, transformFunction: null }, ariaDescribedby: { classPropertyName: "ariaDescribedby", publicName: "ariaDescribedby", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, value: { classPropertyName: "value", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, host: { properties: { "class": "_hostClass()" } }, providers: [
82
99
  {
83
100
  provide: NG_VALUE_ACCESSOR,
84
101
  useExisting: forwardRef(() => EgSearchableMultiselect),
@@ -104,6 +121,7 @@ class EgSearchableMultiselect {
104
121
  {{ item.label }}
105
122
  <button
106
123
  type="button"
124
+ [disabled]="disabledState()"
107
125
  (click)="removeItem(item.value)"
108
126
  class="tw:bg-transparent tw:border-0 tw:text-gray-600
109
127
  tw:hover:text-red-500 tw:cursor-pointer"
@@ -117,17 +135,31 @@ class EgSearchableMultiselect {
117
135
 
118
136
  <!-- Trigger -->
119
137
  <hlm-popover>
120
- <button hlmPopoverTrigger hlmButton variant="secondary" appearance="outline" type="button">
138
+ <button
139
+ hlmPopoverTrigger
140
+ hlmButton
141
+ variant="secondary"
142
+ appearance="outline"
143
+ type="button"
144
+ [id]="id()"
145
+ [disabled]="disabledState()"
146
+ [attr.aria-label]="ariaLabel()"
147
+ [attr.aria-describedby]="ariaDescribedby()"
148
+ >
121
149
  {{ selectedItems().length }} selected
122
150
  </button>
123
151
 
124
- <hlm-popover-content class="tw:w-64 tw:p-2">
152
+ <hlm-popover-content class="tw:w-64 tw:p-2" *hlmPopoverPortal="let ctx">
125
153
  <div class="tw:max-h-60 tw:overflow-auto tw:flex tw:flex-col tw:gap-1">
126
154
  @for (option of options(); track option.value) {
127
155
  <label
128
156
  class="tw:flex tw:items-center tw:gap-2 tw:cursor-pointer tw:px-2 tw:py-1 tw:rounded-sm tw:hover:bg-secondary"
129
157
  >
130
- <hlm-checkbox [checked]="isSelected(option.value)" (changed)="toggle(option.value, $event)" />
158
+ <hlm-checkbox
159
+ [checked]="isSelected(option.value)"
160
+ [disabled]="disabledState()"
161
+ (changed)="toggle(option.value, $event)"
162
+ />
131
163
  <span class="tw:text-sm">{{ option.label }}</span>
132
164
  </label>
133
165
  } @empty {
@@ -137,14 +169,14 @@ class EgSearchableMultiselect {
137
169
  </hlm-popover-content>
138
170
  </hlm-popover>
139
171
  </div>
140
- `, isInline: true, dependencies: [{ kind: "directive", type: HlmPopover, selector: "[hlmPopover],hlm-popover" }, { kind: "directive", type: HlmPopoverTrigger, selector: "button[hlmPopoverTrigger],button[hlmPopoverTriggerFor]" }, { kind: "directive", type: HlmPopoverContent, selector: "[hlmPopoverContent],hlm-popover-content" }, { kind: "component", type: HlmButton, selector: "button[hlmButton], a[hlmButton]", inputs: ["variant", "size", "appearance", "loading", "icon", "iconPosition", "class", "spinnerUserClass", "disabled", "type"] }, { kind: "component", type: HlmCheckbox, selector: "hlm-checkbox", inputs: ["class", "id", "aria-label", "aria-labelledby", "aria-describedby", "checked", "name", "required", "disabled"], outputs: ["checkedChange", "changed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
172
+ `, isInline: true, dependencies: [{ kind: "directive", type: HlmPopover, selector: "[hlmPopover],hlm-popover" }, { kind: "directive", type: HlmPopoverTrigger, selector: "button[hlmPopoverTrigger],button[hlmPopoverTriggerFor]" }, { kind: "directive", type: HlmPopoverContent, selector: "[hlmPopoverContent],hlm-popover-content" }, { kind: "directive", type: HlmPopoverPortal, selector: "[hlmPopoverPortal]" }, { kind: "component", type: HlmButton, selector: "button[hlmButton], a[hlmButton]", inputs: ["variant", "size", "appearance", "loading", "icon", "iconPosition", "class", "spinnerUserClass", "disabled", "type"] }, { kind: "component", type: HlmCheckbox, selector: "hlm-checkbox", inputs: ["class", "id", "aria-label", "aria-labelledby", "aria-describedby", "checked", "name", "required", "disabled", "wrapperDisabled"], outputs: ["checkedChange", "changed"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
141
173
  }
142
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: EgSearchableMultiselect, decorators: [{
174
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgSearchableMultiselect, decorators: [{
143
175
  type: Component,
144
176
  args: [{
145
177
  selector: 'eg-searchable-multiselect',
146
178
  standalone: true,
147
- imports: [HlmPopover, HlmPopoverTrigger, HlmPopoverContent, HlmButton, HlmCheckbox],
179
+ imports: [HlmPopover, HlmPopoverTrigger, HlmPopoverContent, HlmPopoverPortal, HlmButton, HlmCheckbox],
148
180
  changeDetection: ChangeDetectionStrategy.OnPush,
149
181
  providers: [
150
182
  {
@@ -176,6 +208,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
176
208
  {{ item.label }}
177
209
  <button
178
210
  type="button"
211
+ [disabled]="disabledState()"
179
212
  (click)="removeItem(item.value)"
180
213
  class="tw:bg-transparent tw:border-0 tw:text-gray-600
181
214
  tw:hover:text-red-500 tw:cursor-pointer"
@@ -189,17 +222,31 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
189
222
 
190
223
  <!-- Trigger -->
191
224
  <hlm-popover>
192
- <button hlmPopoverTrigger hlmButton variant="secondary" appearance="outline" type="button">
225
+ <button
226
+ hlmPopoverTrigger
227
+ hlmButton
228
+ variant="secondary"
229
+ appearance="outline"
230
+ type="button"
231
+ [id]="id()"
232
+ [disabled]="disabledState()"
233
+ [attr.aria-label]="ariaLabel()"
234
+ [attr.aria-describedby]="ariaDescribedby()"
235
+ >
193
236
  {{ selectedItems().length }} selected
194
237
  </button>
195
238
 
196
- <hlm-popover-content class="tw:w-64 tw:p-2">
239
+ <hlm-popover-content class="tw:w-64 tw:p-2" *hlmPopoverPortal="let ctx">
197
240
  <div class="tw:max-h-60 tw:overflow-auto tw:flex tw:flex-col tw:gap-1">
198
241
  @for (option of options(); track option.value) {
199
242
  <label
200
243
  class="tw:flex tw:items-center tw:gap-2 tw:cursor-pointer tw:px-2 tw:py-1 tw:rounded-sm tw:hover:bg-secondary"
201
244
  >
202
- <hlm-checkbox [checked]="isSelected(option.value)" (changed)="toggle(option.value, $event)" />
245
+ <hlm-checkbox
246
+ [checked]="isSelected(option.value)"
247
+ [disabled]="disabledState()"
248
+ (changed)="toggle(option.value, $event)"
249
+ />
203
250
  <span class="tw:text-sm">{{ option.label }}</span>
204
251
  </label>
205
252
  } @empty {
@@ -211,7 +258,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
211
258
  </div>
212
259
  `,
213
260
  }]
214
- }], ctorParameters: () => [], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }] } });
261
+ }], ctorParameters: () => [], propDecorators: { options: [{ type: i0.Input, args: [{ isSignal: true, alias: "options", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], wrapperDisabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "wrapperDisabled", required: false }] }], ariaLabel: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaLabel", required: false }] }], ariaDescribedby: [{ type: i0.Input, args: [{ isSignal: true, alias: "ariaDescribedby", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], value: [{ type: i0.Input, args: [{ isSignal: true, alias: "value", required: false }] }], valueChange: [{ type: i0.Output, args: ["valueChange"] }] } });
215
262
 
216
263
  /*
217
264
  * Public API Surface of searchable-multiselect
@@ -222,4 +269,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
222
269
  */
223
270
 
224
271
  export { EgSearchableMultiselect };
225
- //# sourceMappingURL=searchable-multiselect.mjs.map
272
+
@@ -10,12 +10,18 @@ declare class EgSearchableMultiselect implements ControlValueAccessor {
10
10
  /** Full option list with labels/values */
11
11
  options: _angular_core.InputSignal<SelectOption[]>;
12
12
  placeholder: _angular_core.InputSignal<string>;
13
+ id: _angular_core.InputSignal<string>;
14
+ disabled: _angular_core.InputSignal<boolean>;
15
+ wrapperDisabled: _angular_core.InputSignal<boolean>;
16
+ ariaLabel: _angular_core.InputSignal<string | undefined>;
17
+ ariaDescribedby: _angular_core.InputSignal<string | null>;
13
18
  userClass: _angular_core.InputSignal<ClassValue>;
14
19
  /** Value: array of selected values (string[]) */
15
20
  value: _angular_core.InputSignal<string[]>;
16
21
  valueChange: _angular_core.OutputEmitterRef<string[]>;
17
22
  protected readonly selectedItems: _angular_core.WritableSignal<SelectOption[]>;
18
- protected isDisabled: boolean;
23
+ protected readonly formDisabled: _angular_core.WritableSignal<boolean>;
24
+ protected readonly disabledState: _angular_core.Signal<boolean>;
19
25
  private isFormBound;
20
26
  protected readonly _hostClass: _angular_core.Signal<string>;
21
27
  protected isSelected(value: string): boolean;
@@ -32,7 +38,7 @@ declare class EgSearchableMultiselect implements ControlValueAccessor {
32
38
  registerOnTouched(fn: () => void): void;
33
39
  setDisabledState(isDisabled: boolean): void;
34
40
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EgSearchableMultiselect, never>;
35
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<EgSearchableMultiselect, "eg-searchable-multiselect", never, { "options": { "alias": "options"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "userClass": { "alias": "class"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; }, { "valueChange": "valueChange"; }, never, never, true, never>;
41
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<EgSearchableMultiselect, "eg-searchable-multiselect", never, { "options": { "alias": "options"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "id": { "alias": "id"; "required": false; "isSignal": true; }; "disabled": { "alias": "disabled"; "required": false; "isSignal": true; }; "wrapperDisabled": { "alias": "wrapperDisabled"; "required": false; "isSignal": true; }; "ariaLabel": { "alias": "ariaLabel"; "required": false; "isSignal": true; }; "ariaDescribedby": { "alias": "ariaDescribedby"; "required": false; "isSignal": true; }; "userClass": { "alias": "class"; "required": false; "isSignal": true; }; "value": { "alias": "value"; "required": false; "isSignal": true; }; }, { "valueChange": "valueChange"; }, never, never, true, never>;
36
42
  }
37
43
 
38
44
  export { EgSearchableMultiselect };