@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.
- package/README.md +130 -13
- package/accordion/README.md +376 -34
- package/accordion/fesm2022/accordion.mjs +20 -20
- package/alert/README.md +343 -34
- package/alert/fesm2022/alert.mjs +18 -18
- package/alert-dialog/README.md +471 -5
- package/alert-dialog/fesm2022/alert-dialog.mjs +48 -48
- package/alert-dialog/types/alert-dialog.d.ts +1 -1
- package/aspect-ratio/README.md +272 -5
- package/aspect-ratio/fesm2022/aspect-ratio.mjs +9 -9
- package/autocomplete/README.md +473 -34
- package/autocomplete/fesm2022/autocomplete.mjs +54 -54
- package/autocomplete/types/autocomplete.d.ts +1 -1
- package/avatar/README.md +357 -5
- package/avatar/fesm2022/avatar.mjs +32 -32
- package/badge/README.md +289 -34
- package/badge/fesm2022/badge.mjs +4 -4
- package/basic-alert/README.md +324 -34
- package/basic-alert/fesm2022/basic-alert.mjs +4 -4
- package/basic-alert/types/basic-alert.d.ts +1 -1
- package/breadcrumb/README.md +406 -5
- package/breadcrumb/fesm2022/breadcrumb.mjs +34 -34
- package/button/README.md +453 -34
- package/button/fesm2022/button.mjs +96 -118
- package/button/types/button.d.ts +7 -10
- package/button-group/README.md +318 -5
- package/button-group/fesm2022/button-group.mjs +20 -20
- package/calendar/README.md +328 -34
- package/calendar/fesm2022/calendar.mjs +154 -154
- package/card/README.md +331 -5
- package/card/fesm2022/card.mjs +33 -33
- package/carousel/README.md +333 -5
- package/carousel/fesm2022/carousel.mjs +47 -44
- package/carousel/types/carousel.d.ts +1 -1
- package/checkbox/README.md +291 -34
- package/checkbox/fesm2022/checkbox.mjs +22 -20
- package/checkbox/types/checkbox.d.ts +5 -4
- package/collapsible/README.md +332 -5
- package/collapsible/fesm2022/collapsible.mjs +15 -15
- package/combobox/README.md +507 -5
- package/combobox/fesm2022/combobox.mjs +95 -92
- package/combobox/types/combobox.d.ts +1 -1
- package/command/README.md +435 -5
- package/command/fesm2022/command.mjs +56 -56
- package/confirmation-dialog/README.md +272 -34
- package/confirmation-dialog/fesm2022/confirmation-dialog.mjs +13 -12
- package/confirmation-dialog/types/confirmation-dialog.d.ts +9 -5
- package/context-menu/README.md +366 -5
- package/context-menu/fesm2022/context-menu.mjs +9 -9
- package/date-picker/README.md +436 -34
- package/date-picker/fesm2022/date-picker.mjs +79 -68
- package/date-picker/types/date-picker.d.ts +8 -4
- package/dialog/README.md +419 -34
- package/dialog/fesm2022/dialog.mjs +48 -48
- package/drawer/README.md +395 -5
- package/drawer/fesm2022/drawer.mjs +45 -45
- package/dropdown-menu/README.md +417 -5
- package/dropdown-menu/fesm2022/dropdown-menu.mjs +85 -73
- package/dropdown-menu/types/dropdown-menu.d.ts +4 -0
- package/empty/README.md +329 -5
- package/empty/fesm2022/empty.mjs +31 -31
- package/field/README.md +385 -5
- package/field/fesm2022/field.mjs +59 -59
- package/field/types/field.d.ts +1 -1
- package/form-checkbox/README.md +283 -34
- package/form-checkbox/fesm2022/form-checkbox.mjs +37 -19
- package/form-checkbox/types/form-checkbox.d.ts +7 -1
- package/form-date-picker/README.md +293 -34
- package/form-date-picker/fesm2022/form-date-picker.mjs +59 -24
- package/form-date-picker/types/form-date-picker.d.ts +7 -1
- package/form-field/README.md +327 -34
- package/form-field/fesm2022/form-field.mjs +30 -16
- package/form-field/types/form-field.d.ts +28 -20
- package/form-field-simple/README.md +311 -34
- package/form-field-simple/fesm2022/form-field-simple.mjs +4 -4
- package/form-searchable-multiselect/README.md +332 -34
- package/form-searchable-multiselect/fesm2022/form-searchable-multiselect.mjs +41 -21
- package/form-searchable-multiselect/types/form-searchable-multiselect.d.ts +8 -2
- package/form-select/README.md +321 -34
- package/form-select/fesm2022/form-select.mjs +62 -29
- package/form-select/types/form-select.d.ts +7 -1
- package/form-text-input/README.md +342 -34
- package/form-text-input/fesm2022/form-text-input.mjs +43 -55
- package/form-text-input/types/form-text-input.d.ts +9 -12
- package/form-textarea/README.md +318 -34
- package/form-textarea/fesm2022/form-textarea.mjs +42 -23
- package/form-textarea/types/form-textarea.d.ts +8 -1
- package/hover-card/README.md +256 -5
- package/hover-card/fesm2022/hover-card.mjs +19 -19
- package/icon/README.md +210 -34
- package/icon/fesm2022/icon.mjs +8 -8
- package/input/README.md +240 -34
- package/input/fesm2022/input.mjs +17 -12
- package/input/types/input.d.ts +2 -1
- package/input-group/README.md +335 -5
- package/input-group/fesm2022/input-group.mjs +37 -37
- package/input-group/types/input-group.d.ts +3 -3
- package/input-otp/README.md +375 -5
- package/input-otp/fesm2022/input-otp.mjs +27 -27
- package/item/README.md +385 -5
- package/item/fesm2022/item.mjs +54 -54
- package/kbd/README.md +291 -5
- package/kbd/fesm2022/kbd.mjs +13 -13
- package/label/README.md +243 -34
- package/label/fesm2022/label.mjs +9 -9
- package/label/types/label.d.ts +1 -1
- package/layout-simple/README.md +174 -44
- package/layout-simple/fesm2022/layout-simple.mjs +917 -398
- package/layout-simple/types/layout-simple.d.ts +180 -129
- package/menu/README.md +418 -0
- package/menu/fesm2022/menu.mjs +461 -0
- package/menu/package.json +24 -0
- package/menu/types/menu.d.ts +137 -0
- package/menubar/README.md +343 -5
- package/menubar/fesm2022/menubar.mjs +13 -13
- package/native-select/README.md +323 -5
- package/native-select/fesm2022/native-select.mjs +19 -19
- package/navigation-menu/README.md +369 -5
- package/navigation-menu/fesm2022/navigation-menu.mjs +37 -37
- package/package.json +183 -154
- package/pagination/README.md +388 -5
- package/pagination/fesm2022/pagination.mjs +63 -63
- package/popover/README.md +302 -34
- package/popover/fesm2022/popover.mjs +30 -30
- package/progress/README.md +311 -5
- package/progress/fesm2022/progress.mjs +13 -13
- package/radio-group/README.md +335 -34
- package/radio-group/fesm2022/radio-group.mjs +26 -19
- package/resizable/README.md +269 -5
- package/resizable/fesm2022/resizable.mjs +19 -19
- package/scroll-area/README.md +233 -5
- package/scroll-area/fesm2022/scroll-area.mjs +9 -9
- package/searchable-multiselect/README.md +294 -34
- package/searchable-multiselect/fesm2022/searchable-multiselect.mjs +63 -16
- package/searchable-multiselect/types/searchable-multiselect.d.ts +8 -2
- package/select/README.md +408 -34
- package/select/fesm2022/select.mjs +80 -72
- package/select/types/select.d.ts +3 -1
- package/separator/README.md +193 -34
- package/separator/fesm2022/separator.mjs +9 -9
- package/separator/types/separator.d.ts +1 -1
- package/sheet/README.md +282 -34
- package/sheet/fesm2022/sheet.mjs +30 -30
- package/sidebar/README.md +457 -5
- package/sidebar/fesm2022/sidebar.mjs +136 -134
- package/sidebar/types/sidebar.d.ts +3 -3
- package/skeleton/README.md +217 -5
- package/skeleton/fesm2022/skeleton.mjs +9 -9
- package/slider/README.md +273 -5
- package/slider/fesm2022/slider.mjs +29 -25
- package/sonner/README.md +317 -34
- package/sonner/fesm2022/sonner.mjs +9 -9
- package/spinner/README.md +255 -34
- package/spinner/fesm2022/spinner.mjs +8 -8
- package/switch/README.md +281 -34
- package/switch/fesm2022/switch.mjs +12 -14
- package/table/README.md +423 -5
- package/table/fesm2022/table.mjs +41 -41
- package/tabs/README.md +382 -34
- package/tabs/fesm2022/tabs.mjs +48 -38
- package/textarea/README.md +282 -5
- package/textarea/fesm2022/textarea.mjs +9 -9
- package/toggle/README.md +270 -5
- package/toggle/fesm2022/toggle.mjs +14 -14
- package/toggle-group/README.md +340 -5
- package/toggle-group/fesm2022/toggle-group.mjs +13 -13
- package/toggle-group/types/toggle-group.d.ts +13 -7
- package/tooltip/README.md +240 -34
- package/tooltip/fesm2022/tooltip.mjs +14 -14
- package/typography/README.md +271 -5
- package/typography/fesm2022/typography.mjs +53 -53
- package/typography/types/typography.d.ts +12 -12
- package/utils/README.md +274 -34
- package/utils/fesm2022/utils.mjs +68 -44
- package/accordion/fesm2022/accordion.mjs.map +0 -1
- package/alert/fesm2022/alert.mjs.map +0 -1
- package/alert-dialog/fesm2022/alert-dialog.mjs.map +0 -1
- package/aspect-ratio/fesm2022/aspect-ratio.mjs.map +0 -1
- package/autocomplete/fesm2022/autocomplete.mjs.map +0 -1
- package/avatar/fesm2022/avatar.mjs.map +0 -1
- package/badge/fesm2022/badge.mjs.map +0 -1
- package/basic-alert/fesm2022/basic-alert.mjs.map +0 -1
- package/breadcrumb/fesm2022/breadcrumb.mjs.map +0 -1
- package/button/fesm2022/button.mjs.map +0 -1
- package/button-group/fesm2022/button-group.mjs.map +0 -1
- package/calendar/fesm2022/calendar.mjs.map +0 -1
- package/card/fesm2022/card.mjs.map +0 -1
- package/carousel/fesm2022/carousel.mjs.map +0 -1
- package/checkbox/fesm2022/checkbox.mjs.map +0 -1
- package/collapsible/fesm2022/collapsible.mjs.map +0 -1
- package/combobox/fesm2022/combobox.mjs.map +0 -1
- package/command/fesm2022/command.mjs.map +0 -1
- package/confirmation-dialog/fesm2022/confirmation-dialog.mjs.map +0 -1
- package/context-menu/fesm2022/context-menu.mjs.map +0 -1
- package/date-picker/fesm2022/date-picker.mjs.map +0 -1
- package/dialog/fesm2022/dialog.mjs.map +0 -1
- package/drawer/fesm2022/drawer.mjs.map +0 -1
- package/dropdown-menu/fesm2022/dropdown-menu.mjs.map +0 -1
- package/empty/fesm2022/empty.mjs.map +0 -1
- package/field/fesm2022/field.mjs.map +0 -1
- package/form-checkbox/fesm2022/form-checkbox.mjs.map +0 -1
- package/form-date-picker/fesm2022/form-date-picker.mjs.map +0 -1
- package/form-field/fesm2022/form-field.mjs.map +0 -1
- package/form-field-simple/fesm2022/form-field-simple.mjs.map +0 -1
- package/form-searchable-multiselect/fesm2022/form-searchable-multiselect.mjs.map +0 -1
- package/form-select/fesm2022/form-select.mjs.map +0 -1
- package/form-text-input/fesm2022/form-text-input.mjs.map +0 -1
- package/form-textarea/fesm2022/form-textarea.mjs.map +0 -1
- package/hover-card/fesm2022/hover-card.mjs.map +0 -1
- package/icon/fesm2022/icon.mjs.map +0 -1
- package/input/fesm2022/input.mjs.map +0 -1
- package/input-group/fesm2022/input-group.mjs.map +0 -1
- package/input-otp/fesm2022/input-otp.mjs.map +0 -1
- package/item/fesm2022/item.mjs.map +0 -1
- package/kbd/fesm2022/kbd.mjs.map +0 -1
- package/label/fesm2022/label.mjs.map +0 -1
- package/layout-simple/fesm2022/layout-simple.mjs.map +0 -1
- package/menubar/fesm2022/menubar.mjs.map +0 -1
- package/native-select/fesm2022/native-select.mjs.map +0 -1
- package/navigation-menu/fesm2022/navigation-menu.mjs.map +0 -1
- package/pagination/fesm2022/pagination.mjs.map +0 -1
- package/popover/fesm2022/popover.mjs.map +0 -1
- package/progress/fesm2022/progress.mjs.map +0 -1
- package/radio-group/fesm2022/radio-group.mjs.map +0 -1
- package/resizable/fesm2022/resizable.mjs.map +0 -1
- package/scroll-area/fesm2022/scroll-area.mjs.map +0 -1
- package/searchable-multiselect/fesm2022/searchable-multiselect.mjs.map +0 -1
- package/select/fesm2022/select.mjs.map +0 -1
- package/separator/fesm2022/separator.mjs.map +0 -1
- package/sheet/fesm2022/sheet.mjs.map +0 -1
- package/sidebar/fesm2022/sidebar.mjs.map +0 -1
- package/skeleton/fesm2022/skeleton.mjs.map +0 -1
- package/slider/fesm2022/slider.mjs.map +0 -1
- package/sonner/fesm2022/sonner.mjs.map +0 -1
- package/spinner/fesm2022/spinner.mjs.map +0 -1
- package/switch/fesm2022/switch.mjs.map +0 -1
- package/table/fesm2022/table.mjs.map +0 -1
- package/tabs/fesm2022/tabs.mjs.map +0 -1
- package/textarea/fesm2022/textarea.mjs.map +0 -1
- package/toggle/fesm2022/toggle.mjs.map +0 -1
- package/toggle-group/fesm2022/toggle-group.mjs.map +0 -1
- package/tooltip/fesm2022/tooltip.mjs.map +0 -1
- package/typography/fesm2022/typography.mjs.map +0 -1
- package/utils/fesm2022/utils.mjs.map +0 -1
package/autocomplete/README.md
CHANGED
|
@@ -1,64 +1,503 @@
|
|
|
1
|
-
# Autocomplete
|
|
1
|
+
# Autocomplete (`@egose/shadcn-theme-ng/autocomplete`)
|
|
2
2
|
|
|
3
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
16
|
-
|
|
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
|
-
|
|
60
|
+
Standalone component — spread the `*Imports` array:
|
|
20
61
|
|
|
21
|
-
|
|
62
|
+
```ts
|
|
63
|
+
import { Component } from '@angular/core';
|
|
64
|
+
import { HlmAutocompleteImports } from '@egose/shadcn-theme-ng/autocomplete';
|
|
22
65
|
|
|
23
|
-
|
|
24
|
-
|
|
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
|
-
|
|
85
|
+
## Anatomy / Structure
|
|
28
86
|
|
|
29
|
-
|
|
87
|
+
```html
|
|
88
|
+
<div hlmAutocomplete [(value)]="selected" [(search)]="query">
|
|
89
|
+
<hlm-autocomplete-input placeholder="Search frameworks..." />
|
|
30
90
|
|
|
31
|
-
|
|
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
|
-
|
|
107
|
+
Real selectors (from source):
|
|
34
108
|
|
|
35
|
-
|
|
36
|
-
|
|
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
|
-
|
|
40
|
-
```bash
|
|
41
|
-
npm publish
|
|
42
|
-
```
|
|
124
|
+
## API reference
|
|
43
125
|
|
|
44
|
-
|
|
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
|
-
|
|
130
|
+
### `HlmAutocomplete` — `[hlmAutocomplete], hlm-autocomplete`
|
|
47
131
|
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
253
|
+
### 2. Grouped options with labels and separators
|
|
53
254
|
|
|
54
|
-
|
|
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
|
-
```
|
|
57
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
498
|
+
## Related subpaths
|
|
63
499
|
|
|
64
|
-
|
|
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
|