@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,503 @@
1
- # Autocomplete
1
+ # Autocomplete (`@egose/shadcn-theme-ng/autocomplete`)
2
2
 
3
- This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 20.3.0.
3
+ A filter-as-you-type popup input in the shadcn/ui Command/Combobox style: a text field anchored to
4
+ a floating listbox with grouped options, an empty state, a clear button, and async status rows.
5
+ Use it for country pickers, command palettes backing a text field, tag inputs, and searchable
6
+ option lists.
4
7
 
5
- ## Code scaffolding
8
+ The implementation styles the headless autocomplete primitives from
9
+ `@spartan-ng/brain/autocomplete` (state, filtering, active-item tracking) combined with
10
+ `BrnPopover` / `BrnPopoverContent` from `@spartan-ng/brain/popover` for floating placement.
11
+ Everything is a thin directive (shadcn classes via `classes()`) except `HlmAutocompleteInput`
12
+ and `HlmAutocompleteItem`, which are small components. `HlmAutocomplete` is the form-field style
13
+ root; `HlmAutocompleteSearch` is the same shell wired for live search callbacks.
6
14
 
7
- Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
15
+ > **Ships as:** `@egose/shadcn-theme-ng/autocomplete` and `@egose/shadcn-theme-ng-tw/autocomplete`
16
+ > (the `tw:`-prefixed Tailwind variant). Both expose the identical TypeScript surface; only the
17
+ > emitted Tailwind class strings differ. See the [package README](../../README.md) for install
18
+ > steps, peer dependencies, and Tailwind setup. Do not publish this project directory independently.
19
+
20
+ ## Installation
8
21
 
9
22
  ```bash
10
- ng generate component component-name
23
+ # Plain Tailwind (no prefix)
24
+ npm install @egose/shadcn-theme-ng
25
+
26
+ # tw:-prefixed Tailwind variant
27
+ npm install @egose/shadcn-theme-ng-tw
11
28
  ```
12
29
 
13
- For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
30
+ Peer dependencies (Angular, `@spartan-ng/brain`, `@ng-icons/*`, `rxjs`, …) are documented in the
31
+ [package README](../../README.md#peer-dependencies). This subpath additionally relies at runtime on
32
+ `@egose/shadcn-theme-ng/utils` (`classes()`), `@egose/shadcn-theme-ng/input-group`
33
+ (`HlmInputGroup*`, composed inside `HlmAutocompleteInput`), and `@ng-icons/lucide`
34
+ (`lucideSearch`, `lucideX`, `lucideCheck`).
14
35
 
15
- ```bash
16
- ng generate --help
36
+ ## Imports
37
+
38
+ All symbols are exported from the subpath root (`projects/autocomplete/src/public-api.ts`):
39
+
40
+ ```ts
41
+ import {
42
+ HlmAutocomplete,
43
+ HlmAutocompleteSearch,
44
+ HlmAutocompleteInput,
45
+ HlmAutocompleteContent,
46
+ HlmAutocompletePortal,
47
+ HlmAutocompleteList,
48
+ HlmAutocompleteItem,
49
+ HlmAutocompleteGroup,
50
+ HlmAutocompleteLabel,
51
+ HlmAutocompleteEmpty,
52
+ HlmAutocompleteSeparator,
53
+ HlmAutocompleteStatus,
54
+ HlmAutocompleteImports,
55
+ HlmAutocompleteModule,
56
+ } from '@egose/shadcn-theme-ng/autocomplete';
57
+ // tw variant: replace with '@egose/shadcn-theme-ng-tw/autocomplete'
17
58
  ```
18
59
 
19
- ## Building
60
+ Standalone component — spread the `*Imports` array:
20
61
 
21
- To build the library, run:
62
+ ```ts
63
+ import { Component } from '@angular/core';
64
+ import { HlmAutocompleteImports } from '@egose/shadcn-theme-ng/autocomplete';
22
65
 
23
- ```bash
24
- ng build autocomplete
66
+ @Component({
67
+ selector: 'app-demo',
68
+ standalone: true,
69
+ imports: [...HlmAutocompleteImports],
70
+ template: `...`,
71
+ })
72
+ export class DemoComponent {}
73
+ ```
74
+
75
+ NgModule-based consumer — import the module:
76
+
77
+ ```ts
78
+ import { NgModule } from '@angular/core';
79
+ import { HlmAutocompleteModule } from '@egose/shadcn-theme-ng/autocomplete';
80
+
81
+ @NgModule({ imports: [HlmAutocompleteModule] })
82
+ export class DemoModule {}
25
83
  ```
26
84
 
27
- This command will compile your project, and the build artifacts will be placed in the `dist/` directory.
85
+ ## Anatomy / Structure
28
86
 
29
- ### Publishing the Library
87
+ ```html
88
+ <div hlmAutocomplete [(value)]="selected" [(search)]="query">
89
+ <hlm-autocomplete-input placeholder="Search frameworks..." />
30
90
 
31
- Once the project is built, you can publish your library by following these steps:
91
+ <div hlmAutocompletePortal>
92
+ <div hlmAutocompleteContent>
93
+ <ul hlmAutocompleteList>
94
+ <li hlmAutocompleteGroup>
95
+ <span hlmAutocompleteLabel>Suggestions</span>
96
+ <hlm-autocomplete-item [value]="opt" [id]="opt.id"> {{ opt.label }} </hlm-autocomplete-item>
97
+ </li>
98
+ <div hlmAutocompleteSeparator></div>
99
+ <hlm-autocomplete-empty>No results for "{{ query }}".</hlm-autocomplete-empty>
100
+ <hlm-autocomplete-status>Loading…</hlm-autocomplete-status>
101
+ </ul>
102
+ </div>
103
+ </div>
104
+ </div>
105
+ ```
32
106
 
33
- 1. Navigate to the `dist` directory:
107
+ Real selectors (from source):
34
108
 
35
- ```bash
36
- cd dist/autocomplete
37
- ```
109
+ | Class | Selector(s) | Kind |
110
+ | -------------------------- | ---------------------------------------------------- | --------- |
111
+ | `HlmAutocomplete` | `[hlmAutocomplete], hlm-autocomplete` | Directive |
112
+ | `HlmAutocompleteSearch` | `[hlmAutocompleteSearch], hlm-autocomplete-search` | Directive |
113
+ | `HlmAutocompleteInput` | `hlm-autocomplete-input` | Component |
114
+ | `HlmAutocompleteContent` | `[hlmAutocompleteContent], hlm-autocomplete-content` | Directive |
115
+ | `HlmAutocompletePortal` | `[hlmAutocompletePortal]` | Directive |
116
+ | `HlmAutocompleteList` | `[hlmAutocompleteList]` | Directive |
117
+ | `HlmAutocompleteItem` | `hlm-autocomplete-item` | Component |
118
+ | `HlmAutocompleteGroup` | `[hlmAutocompleteGroup]` | Directive |
119
+ | `HlmAutocompleteLabel` | `[hlmAutocompleteLabel]` | Directive |
120
+ | `HlmAutocompleteEmpty` | `[hlmAutocompleteEmpty], hlm-autocomplete-empty` | Directive |
121
+ | `HlmAutocompleteSeparator` | `[hlmAutocompleteSeparator]` | Directive |
122
+ | `HlmAutocompleteStatus` | `[hlmAutocompleteStatus], hlm-autocomplete-status` | Directive |
38
123
 
39
- 2. Run the `npm publish` command to publish your library to the npm registry:
40
- ```bash
41
- npm publish
42
- ```
124
+ ## API reference
43
125
 
44
- ## Running unit tests
126
+ Inputs/outputs below come from the `hostDirectives` declarations in source. `HlmAutocomplete`
127
+ and `HlmAutocompleteSearch` both also carry `BrnPopover` behavior
128
+ (`align` default `'start'`, `sideOffset` default `6`).
45
129
 
46
- To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
130
+ ### `HlmAutocomplete` — `[hlmAutocomplete], hlm-autocomplete`
47
131
 
48
- ```bash
49
- ng test
132
+ `BrnAutocomplete` + `BrnPopover`.
133
+
134
+ | Input | Description |
135
+ | ------------------------------------------------------------------------ | ----------------------------------------------------------------------- |
136
+ | `autoHighlight` | Auto-highlight the first matching item. |
137
+ | `disabled` | Disable the whole autocomplete. |
138
+ | `value` | Currently selected value (two-way with `valueChange`). |
139
+ | `search` | Current search text (two-way with `searchChange`). |
140
+ | `itemToString` | Maps an item value to its display string. |
141
+ | `isItemEqualToValue` | Equality check for selection (`HlmAutocomplete` only, not on `Search`). |
142
+ | `align`, `closeOnOutsidePointerEvents`, `sideOffset`, `state`, `offsetX` | Popover placement/state (via `BrnPopover`). |
143
+
144
+ | Output | Description |
145
+ | -------------- | ---------------------------------------------- |
146
+ | `valueChange` | Emits the newly selected value. |
147
+ | `searchChange` | Emits the search text on each keystroke. |
148
+ | `stateChanged` | Popover open-state changes (via `BrnPopover`). |
149
+ | `closed` | Popover closed (via `BrnPopover`). |
150
+
151
+ ### `HlmAutocompleteSearch` — `[hlmAutocompleteSearch], hlm-autocomplete-search`
152
+
153
+ Same as `HlmAutocomplete` **minus** `isItemEqualToValue`. Prefer it when the list is driven by a
154
+ server query rather than local equality.
155
+
156
+ ### `HlmAutocompleteInput` — `hlm-autocomplete-input`
157
+
158
+ Component composing `BrnAutocompleteAnchor` + `HlmInputGroup` around a native `<input
159
+ brnAutocompleteInput hlmInputGroupInput>`, with optional search/clear addons.
160
+
161
+ | Input | Type | Default | Description |
162
+ | ----------------------------------------------- | ---------------------- | ------------------------------------------------- | --------------------------------------------------------- |
163
+ | `inputId` | `string` | `'hlm-autocomplete-input-<n>'` (auto-incremented) | `id` forwarded to the inner input. |
164
+ | `placeholder` | `string` | `''` | Placeholder text. |
165
+ | `showSearch` | `boolean` | `true` | Show the leading search icon addon. |
166
+ | `showClear` | `boolean` | `false` | Show the trailing clear (`*brnAutocompleteClear`) button. |
167
+ | `forceInvalid` | `boolean` | `false` | Force the invalid visual state. |
168
+ | `aria-invalid` (alias of `ariaInvalidOverride`) | `boolean \| undefined` | `undefined` (auto-detect from parent error state) | Manual override for `aria-invalid`. |
169
+
170
+ ### `HlmAutocompleteItem` — `hlm-autocomplete-item`
171
+
172
+ Component wrapping `BrnAutocompleteItem`; renders a check icon at the inline-end when the item is
173
+ active (`_active()` signal read from the injected brain item).
174
+
175
+ | Input (via brain) | Description |
176
+ | ----------------- | ---------------------------- |
177
+ | `id` | Item id. |
178
+ | `disabled` | Disable this option. |
179
+ | `value` | The option value (any type). |
180
+
181
+ ### `HlmAutocompleteList` / `HlmAutocompleteLabel` / `HlmAutocompleteSeparator`
182
+
183
+ | Directive | Brain inputs | Notes |
184
+ | --------------------------------------------------------- | ------------- | ----------------------------------------------------------------------- |
185
+ | `HlmAutocompleteList` (`[hlmAutocompleteList]`) | `id` | Scrollable list shell; collapses padding when empty (`data-empty:p-0`). |
186
+ | `HlmAutocompleteLabel` (`[hlmAutocompleteLabel]`) | `id` | Small muted group caption. |
187
+ | `HlmAutocompleteGroup` (`[hlmAutocompleteGroup]`) | — | Groups items; hides via `data-hidden`. |
188
+ | `HlmAutocompleteSeparator` (`[hlmAutocompleteSeparator]`) | `orientation` | 1px divider between groups. |
189
+
190
+ ### `HlmAutocompleteContent` / `HlmAutocompletePortal`
191
+
192
+ | Directive | Brain primitive | Notes |
193
+ | ------------------------ | ----------------------------------------------- | --------------------------------------------------------------------------------------- |
194
+ | `HlmAutocompleteContent` | `BrnAutocompleteContent` | Floating panel (`max-h-72`, popover theme, width synced to `--brn-autocomplete-width`). |
195
+ | `HlmAutocompletePortal` | `BrnPopoverContent` (`context`, `class` inputs) | CDK-portal outlet — wrap the content in it. |
196
+
197
+ ### `HlmAutocompleteEmpty` / `HlmAutocompleteStatus`
198
+
199
+ Layout-only states, no inputs. `Empty` only displays when the content reports
200
+ `group-data-empty` (no matches); `Status` is a centered row for spinners / "loading…" /
201
+ result counts.
202
+
203
+ ## Examples
204
+
205
+ ### 1. Basic local filtering
206
+
207
+ ```ts
208
+ import { Component, computed, signal } from '@angular/core';
209
+ import { HlmAutocompleteImports } from '@egose/shadcn-theme-ng/autocomplete';
210
+
211
+ interface Fruit {
212
+ id: string;
213
+ label: string;
214
+ }
215
+
216
+ @Component({
217
+ selector: 'app-autocomplete-basic',
218
+ standalone: true,
219
+ imports: [...HlmAutocompleteImports],
220
+ template: `
221
+ <div hlmAutocomplete [(search)]="query" [(value)]="selected">
222
+ <hlm-autocomplete-input placeholder="Search fruit..." [showClear]="true" />
223
+ <div hlmAutocompletePortal>
224
+ <div hlmAutocompleteContent>
225
+ <ul hlmAutocompleteList>
226
+ @for (f of filtered(); track f.id) {
227
+ <hlm-autocomplete-item [value]="f" [id]="f.id">{{ f.label }}</hlm-autocomplete-item>
228
+ }
229
+ <hlm-autocomplete-empty>No fruit matches "{{ query() }}".</hlm-autocomplete-empty>
230
+ </ul>
231
+ </div>
232
+ </div>
233
+ </div>
234
+ <p class="tw:mt-2 tw:text-sm">Selected: {{ selected()?.label ?? 'none' }}</p>
235
+ `,
236
+ })
237
+ export class AutocompleteBasicComponent {
238
+ private readonly all: Fruit[] = [
239
+ { id: 'apple', label: 'Apple' },
240
+ { id: 'banana', label: 'Banana' },
241
+ { id: 'cherry', label: 'Cherry' },
242
+ { id: 'date', label: 'Date' },
243
+ ];
244
+ readonly query = signal('');
245
+ readonly selected = signal<Fruit | null>(null);
246
+ readonly filtered = computed(() => {
247
+ const q = this.query().trim().toLowerCase();
248
+ return q ? this.all.filter((f) => f.label.toLowerCase().includes(q)) : this.all;
249
+ });
250
+ }
50
251
  ```
51
252
 
52
- ## Running end-to-end tests
253
+ ### 2. Grouped options with labels and separators
53
254
 
54
- For end-to-end (e2e) testing, run:
255
+ ```html
256
+ <div hlmAutocomplete [(search)]="query" [(value)]="picked">
257
+ <hlm-autocomplete-input placeholder="Pick a city..." />
258
+ <div hlmAutocompletePortal>
259
+ <div hlmAutocompleteContent>
260
+ <ul hlmAutocompleteList>
261
+ <li hlmAutocompleteGroup>
262
+ <span hlmAutocompleteLabel>Europe</span>
263
+ <hlm-autocomplete-item [value]="'berlin'" id="berlin">Berlin</hlm-autocomplete-item>
264
+ <hlm-autocomplete-item [value]="'paris'" id="paris">Paris</hlm-autocomplete-item>
265
+ </li>
266
+ <div hlmAutocompleteSeparator></div>
267
+ <li hlmAutocompleteGroup>
268
+ <span hlmAutocompleteLabel>Asia</span>
269
+ <hlm-autocomplete-item [value]="'tokyo'" id="tokyo">Tokyo</hlm-autocomplete-item>
270
+ <hlm-autocomplete-item [value]="'seoul'" id="seoul" [disabled]="true">
271
+ Seoul (disabled)
272
+ </hlm-autocomplete-item>
273
+ </li>
274
+ <hlm-autocomplete-empty>Nothing found.</hlm-autocomplete-empty>
275
+ </ul>
276
+ </div>
277
+ </div>
278
+ </div>
279
+ ```
55
280
 
56
- ```bash
57
- ng e2e
281
+ ```ts
282
+ import { Component, signal } from '@angular/core';
283
+ import { HlmAutocompleteImports } from '@egose/shadcn-theme-ng/autocomplete';
284
+
285
+ @Component({
286
+ selector: 'app-autocomplete-groups',
287
+ standalone: true,
288
+ imports: [...HlmAutocompleteImports],
289
+ templateUrl: './autocomplete-groups.html',
290
+ })
291
+ export class AutocompleteGroupsComponent {
292
+ readonly query = signal('');
293
+ readonly picked = signal<string | null>(null);
294
+ }
295
+ ```
296
+
297
+ ### 3. Async server search with a status row
298
+
299
+ Use `HlmAutocompleteSearch` and react to `searchChange` with a debounced fetch:
300
+
301
+ ```ts
302
+ import { Component, signal } from '@angular/core';
303
+ import { toObservable } from '@angular/core/rxjs-interop';
304
+ import { debounceTime, distinctUntilChanged, switchMap } from 'rxjs/operators';
305
+ import { HlmAutocompleteImports } from '@egose/shadcn-theme-ng/autocomplete';
306
+
307
+ @Component({
308
+ selector: 'app-autocomplete-async',
309
+ standalone: true,
310
+ imports: [...HlmAutocompleteImports],
311
+ template: `
312
+ <div hlmAutocompleteSearch [(search)]="query" [(value)]="user" (searchChange)="onSearch($event)">
313
+ <hlm-autocomplete-input placeholder="Search users..." />
314
+ <div hlmAutocompletePortal>
315
+ <div hlmAutocompleteContent>
316
+ <ul hlmAutocompleteList>
317
+ @for (u of results(); track u.id) {
318
+ <hlm-autocomplete-item [value]="u" [id]="u.id">{{ u.name }}</hlm-autocomplete-item>
319
+ }
320
+ @if (loading()) {
321
+ <hlm-autocomplete-status>Searching…</hlm-autocomplete-status>
322
+ } @else {
323
+ <hlm-autocomplete-empty>No users for "{{ query() }}".</hlm-autocomplete-empty>
324
+ }
325
+ </ul>
326
+ </div>
327
+ </div>
328
+ </div>
329
+ `,
330
+ })
331
+ export class AutocompleteAsyncComponent {
332
+ readonly query = signal('');
333
+ readonly user = signal<{ id: string; name: string } | null>(null);
334
+ readonly results = signal<{ id: string; name: string }[]>([]);
335
+ readonly loading = signal(false);
336
+
337
+ constructor() {
338
+ toObservable(this.query)
339
+ .pipe(debounceTime(250), distinctUntilChanged())
340
+ .subscribe((q) => void this.fetch(q));
341
+ }
342
+
343
+ onSearch(_: string) {
344
+ this.loading.set(true);
345
+ }
346
+
347
+ private async fetch(q: string) {
348
+ if (!q.trim()) {
349
+ this.results.set([]);
350
+ this.loading.set(false);
351
+ return;
352
+ }
353
+ const res = await fetch(`/api/users?q=${encodeURIComponent(q)}`).then((r) => r.json());
354
+ this.results.set(res);
355
+ this.loading.set(false);
356
+ }
357
+ }
358
+ ```
359
+
360
+ > `switchMap`-based cancellation is preferable for real apps; the manual version above keeps the
361
+ > example dependency-free. `onSearch` only flips the spinner — the debounced fetch does the work.
362
+
363
+ ### 4. Reactive-forms binding with validation visuals
364
+
365
+ ```ts
366
+ import { Component } from '@angular/core';
367
+ import { FormControl, ReactiveFormsModule, Validators } from '@angular/forms';
368
+ import { HlmAutocompleteImports } from '@egose/shadcn-theme-ng/autocomplete';
369
+
370
+ @Component({
371
+ selector: 'app-autocomplete-form',
372
+ standalone: true,
373
+ imports: [...HlmAutocompleteImports, ReactiveFormsModule],
374
+ template: `
375
+ <div hlmAutocomplete [value]="control.value" (valueChange)="control.setValue($event)">
376
+ <hlm-autocomplete-input placeholder="Country (required)..." [forceInvalid]="control.touched && control.invalid" />
377
+ <div hlmAutocompletePortal>
378
+ <div hlmAutocompleteContent>
379
+ <ul hlmAutocompleteList>
380
+ @for (c of countries; track c) {
381
+ <hlm-autocomplete-item [value]="c" [id]="c">{{ c }}</hlm-autocomplete-item>
382
+ }
383
+ <hlm-autocomplete-empty>No match.</hlm-autocomplete-empty>
384
+ </ul>
385
+ </div>
386
+ </div>
387
+ </div>
388
+ @if (control.touched && control.invalid) {
389
+ <p class="tw:mt-1 tw:text-sm tw:text-destructive">Please choose a country.</p>
390
+ }
391
+ `,
392
+ })
393
+ export class AutocompleteFormComponent {
394
+ readonly control = new FormControl<string | null>(null, Validators.required);
395
+ readonly countries = ['Austria', 'France', 'Japan', 'Kenya', 'Peru'];
396
+ }
397
+ ```
398
+
399
+ ### 5. Custom display strings with `itemToString` (object values)
400
+
401
+ ```ts
402
+ import { Component, signal } from '@angular/core';
403
+ import { HlmAutocompleteImports } from '@egose/shadcn-theme-ng/autocomplete';
404
+
405
+ interface Repo {
406
+ id: number;
407
+ fullName: string;
408
+ stars: number;
409
+ }
410
+
411
+ @Component({
412
+ selector: 'app-autocomplete-tostring',
413
+ standalone: true,
414
+ imports: [...HlmAutocompleteImports],
415
+ template: `
416
+ <div hlmAutocomplete [(search)]="query" [(value)]="repo" [itemToString]="toLabel" [isItemEqualToValue]="sameRepo">
417
+ <hlm-autocomplete-input placeholder="Search repos..." [showClear]="true" />
418
+ <div hlmAutocompletePortal>
419
+ <div hlmAutocompleteContent>
420
+ <ul hlmAutocompleteList>
421
+ @for (r of repos; track r.id) {
422
+ <hlm-autocomplete-item [value]="r" [id]="String(r.id)">
423
+ {{ r.fullName }} ★ {{ r.stars }}
424
+ </hlm-autocomplete-item>
425
+ }
426
+ <hlm-autocomplete-empty>No repositories found.</hlm-autocomplete-empty>
427
+ </ul>
428
+ </div>
429
+ </div>
430
+ </div>
431
+ `,
432
+ })
433
+ export class AutocompleteToStringComponent {
434
+ readonly query = signal('');
435
+ readonly repo = signal<Repo | null>(null);
436
+ readonly repos: Repo[] = [
437
+ { id: 1, fullName: 'spartan-ng/spartan', stars: 4200 },
438
+ { id: 2, fullName: 'angular/angular', stars: 96000 },
439
+ ];
440
+ readonly toLabel = (r: Repo | null) => r?.fullName ?? '';
441
+ readonly sameRepo = (a: Repo | null, b: Repo | null) => a?.id === b?.id;
442
+ }
58
443
  ```
59
444
 
60
- Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
445
+ ### 6. Disabled state + custom ids for a11y wiring
446
+
447
+ ```ts
448
+ import { Component, signal } from '@angular/core';
449
+ import { HlmAutocompleteImports } from '@egose/shadcn-theme-ng/autocomplete';
450
+
451
+ @Component({
452
+ selector: 'app-autocomplete-disabled',
453
+ standalone: true,
454
+ imports: [...HlmAutocompleteImports],
455
+ template: `
456
+ <label for="city-input" class="tw:mb-1 tw:block tw:text-sm tw:font-medium">City</label>
457
+ <div hlmAutocomplete [disabled]="locked()" [(value)]="city">
458
+ <hlm-autocomplete-input inputId="city-input" placeholder="Pick a city..." />
459
+ <div hlmAutocompletePortal>
460
+ <div hlmAutocompleteContent>
461
+ <ul hlmAutocompleteList>
462
+ <hlm-autocomplete-item [value]="'oslo'" id="oslo">Oslo</hlm-autocomplete-item>
463
+ <hlm-autocomplete-item [value]="'lima'" id="lima">Lima</hlm-autocomplete-item>
464
+ <hlm-autocomplete-empty>No match.</hlm-autocomplete-empty>
465
+ </ul>
466
+ </div>
467
+ </div>
468
+ </div>
469
+ <button class="tw:mt-2" (click)="locked.update((v) => !v)">
470
+ {{ locked() ? 'Unlock' : 'Lock' }}
471
+ </button>
472
+ `,
473
+ })
474
+ export class AutocompleteDisabledComponent {
475
+ readonly locked = signal(true);
476
+ readonly city = signal<string | null>(null);
477
+ }
478
+ ```
479
+
480
+ ## Accessibility notes
481
+
482
+ - The input is a real text field (`brnAutocompleteInput`) with listbox semantics from the brain:
483
+ arrow keys move the highlight, `Enter` selects, `Escape` dismisses. The clear button is a native
484
+ `<button>` disabled in sync with the input.
485
+ - `HlmAutocompleteEmpty` / `HlmAutocompleteStatus` give screen-reader users feedback for the two
486
+ critical non-visual states (no matches / loading) — always include at least the empty row.
487
+ - Label the field: either set `inputId` and pair it with a `<label for>`, or wrap the group with
488
+ `HlmField`/`hlmLabel` from `@egose/shadcn-theme-ng/field` / `.../label`.
489
+ - `aria-invalid` auto-detects the parent error state; only set the override when you manage
490
+ validity yourself (e.g. cross-field rules), and pair it with visible error text.
491
+
492
+ ## Theming / CSS variables
493
+
494
+ Class-based styling; the floating panel width tracks the anchor via the
495
+ `--brn-autocomplete-width` custom property set by the brain (`w-(--brn-autocomplete-width)`).
496
+ No theme variables of its own — adjust popover placement with the `align` / `sideOffset` inputs.
61
497
 
62
- ## Additional Resources
498
+ ## Related subpaths
63
499
 
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.
500
+ - `@egose/shadcn-theme-ng/input-group` — the input shell composed inside `HlmAutocompleteInput`
501
+ - `@egose/shadcn-theme-ng/combobox` — button-triggered (rather than text-anchored) picker
502
+ - `@egose/shadcn-theme-ng/command` — command-palette list primitives
503
+ - `@egose/shadcn-theme-ng/popover` — lower-level floating panels