@egose/shadcn-theme-ng-tw 0.1.0 → 0.3.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 +2 -2
- package/accordion/README.md +405 -2
- package/alert/README.md +372 -2
- package/alert/fesm2022/alert.mjs +1 -1
- package/alert-dialog/README.md +471 -5
- package/aspect-ratio/README.md +272 -5
- package/autocomplete/README.md +502 -2
- package/autocomplete/fesm2022/autocomplete.mjs +1 -1
- package/avatar/README.md +357 -5
- package/badge/README.md +318 -2
- package/basic-alert/README.md +353 -2
- package/breadcrumb/README.md +406 -5
- package/button/README.md +482 -2
- package/button/fesm2022/button.mjs +85 -107
- package/button/types/button.d.ts +5 -8
- package/button-group/README.md +318 -5
- package/button-group/fesm2022/button-group.mjs +1 -1
- package/calendar/README.md +357 -2
- package/card/README.md +331 -5
- package/carousel/README.md +333 -5
- package/carousel/fesm2022/carousel.mjs +4 -1
- package/checkbox/README.md +320 -2
- package/checkbox/fesm2022/checkbox.mjs +6 -7
- package/checkbox/types/checkbox.d.ts +1 -1
- package/collapsible/README.md +332 -5
- package/combobox/README.md +507 -5
- package/combobox/fesm2022/combobox.mjs +5 -2
- package/command/README.md +435 -5
- package/confirmation-dialog/README.md +301 -2
- package/context-menu/README.md +366 -5
- package/date-picker/README.md +469 -2
- package/date-picker/fesm2022/date-picker.mjs +150 -32
- package/date-picker/types/date-picker.d.ts +102 -9
- package/dialog/README.md +448 -2
- package/drawer/README.md +395 -5
- package/dropdown-menu/README.md +417 -5
- package/empty/README.md +329 -5
- package/field/README.md +385 -5
- package/form-autocomplete/README.md +177 -0
- package/form-autocomplete/fesm2022/form-autocomplete.mjs +211 -0
- package/form-autocomplete/package.json +24 -0
- package/form-autocomplete/types/form-autocomplete.d.ts +61 -0
- package/form-checkbox/README.md +322 -2
- package/form-checkbox/fesm2022/form-checkbox.mjs +22 -9
- package/form-checkbox/types/form-checkbox.d.ts +23 -4
- package/form-combobox/README.md +202 -0
- package/form-combobox/fesm2022/form-combobox.mjs +273 -0
- package/form-combobox/package.json +24 -0
- package/form-combobox/types/form-combobox.d.ts +73 -0
- package/form-date-picker/README.md +348 -2
- package/form-date-picker/fesm2022/form-date-picker.mjs +60 -16
- package/form-date-picker/types/form-date-picker.d.ts +17 -1
- package/form-date-picker-multi/README.md +191 -0
- package/form-date-picker-multi/fesm2022/form-date-picker-multi.mjs +239 -0
- package/form-date-picker-multi/package.json +24 -0
- package/form-date-picker-multi/types/form-date-picker-multi.d.ts +58 -0
- package/form-date-range-picker/README.md +253 -0
- package/form-date-range-picker/fesm2022/form-date-range-picker.mjs +236 -0
- package/form-date-range-picker/package.json +24 -0
- package/form-date-range-picker/types/form-date-range-picker.d.ts +55 -0
- package/form-field/README.md +356 -2
- package/form-field-simple/README.md +340 -2
- package/form-input-otp/README.md +194 -0
- package/form-input-otp/fesm2022/form-input-otp.mjs +194 -0
- package/form-input-otp/package.json +24 -0
- package/form-input-otp/types/form-input-otp.d.ts +58 -0
- package/form-month-year-picker/README.md +188 -0
- package/form-month-year-picker/fesm2022/form-month-year-picker.mjs +229 -0
- package/form-month-year-picker/package.json +24 -0
- package/form-month-year-picker/types/form-month-year-picker.d.ts +53 -0
- package/form-native-select/README.md +205 -0
- package/form-native-select/fesm2022/form-native-select.mjs +187 -0
- package/form-native-select/package.json +24 -0
- package/form-native-select/types/form-native-select.d.ts +57 -0
- package/form-phone-input/README.md +188 -0
- package/form-phone-input/fesm2022/form-phone-input.mjs +203 -0
- package/form-phone-input/package.json +24 -0
- package/form-phone-input/types/form-phone-input.d.ts +58 -0
- package/form-radio-group/README.md +198 -0
- package/form-radio-group/fesm2022/form-radio-group.mjs +211 -0
- package/form-radio-group/package.json +24 -0
- package/form-radio-group/types/form-radio-group.d.ts +63 -0
- package/form-searchable-multiselect/README.md +371 -2
- package/form-searchable-multiselect/fesm2022/form-searchable-multiselect.mjs +19 -10
- package/form-searchable-multiselect/types/form-searchable-multiselect.d.ts +18 -1
- package/form-select/README.md +360 -2
- package/form-select/fesm2022/form-select.mjs +24 -15
- package/form-select/types/form-select.d.ts +18 -1
- package/form-slider/README.md +182 -0
- package/form-slider/fesm2022/form-slider.mjs +186 -0
- package/form-slider/package.json +24 -0
- package/form-slider/types/form-slider.d.ts +60 -0
- package/form-switch/README.md +173 -0
- package/form-switch/fesm2022/form-switch.mjs +178 -0
- package/form-switch/package.json +24 -0
- package/form-switch/types/form-switch.d.ts +50 -0
- package/form-text-input/README.md +381 -2
- package/form-text-input/fesm2022/form-text-input.mjs +19 -10
- package/form-text-input/types/form-text-input.d.ts +18 -1
- package/form-textarea/README.md +357 -2
- package/form-textarea/fesm2022/form-textarea.mjs +19 -10
- package/form-textarea/types/form-textarea.d.ts +18 -1
- package/form-toggle/README.md +186 -0
- package/form-toggle/fesm2022/form-toggle.mjs +241 -0
- package/form-toggle/package.json +24 -0
- package/form-toggle/types/form-toggle.d.ts +82 -0
- package/form-toggle-group/README.md +176 -0
- package/form-toggle-group/fesm2022/form-toggle-group.mjs +216 -0
- package/form-toggle-group/package.json +24 -0
- package/form-toggle-group/types/form-toggle-group.d.ts +65 -0
- package/hover-card/README.md +256 -5
- package/icon/README.md +239 -2
- package/input/README.md +269 -2
- package/input-group/README.md +335 -5
- package/input-group/fesm2022/input-group.mjs +23 -13
- package/input-group/types/input-group.d.ts +4 -1
- package/input-otp/README.md +375 -5
- package/item/README.md +385 -5
- package/item/fesm2022/item.mjs +3 -3
- package/kbd/README.md +291 -5
- package/label/README.md +272 -2
- package/layout-simple/README.md +193 -2
- package/layout-simple/fesm2022/layout-simple.mjs +877 -409
- package/layout-simple/types/layout-simple.d.ts +174 -137
- package/menu/README.md +417 -2
- package/menubar/README.md +343 -5
- package/native-select/README.md +323 -5
- package/native-select/fesm2022/native-select.mjs +18 -6
- package/native-select/types/native-select.d.ts +7 -2
- package/navigation-menu/README.md +369 -5
- package/package.json +57 -1
- package/pagination/README.md +388 -5
- package/phone-input/README.md +114 -0
- package/phone-input/fesm2022/phone-input.mjs +191 -0
- package/phone-input/package.json +24 -0
- package/phone-input/types/phone-input.d.ts +67 -0
- package/popover/README.md +331 -2
- package/progress/README.md +311 -5
- package/radio-group/README.md +364 -2
- package/radio-group/fesm2022/radio-group.mjs +5 -1
- package/resizable/README.md +269 -5
- package/scroll-area/README.md +233 -5
- package/searchable-multiselect/README.md +323 -2
- package/select/README.md +437 -2
- package/separator/README.md +222 -2
- package/sheet/README.md +311 -2
- package/sheet/fesm2022/sheet.mjs +1 -1
- package/sidebar/README.md +457 -5
- package/skeleton/README.md +217 -5
- package/slider/README.md +273 -5
- package/slider/fesm2022/slider.mjs +17 -13
- package/sonner/README.md +346 -2
- package/spinner/README.md +284 -2
- package/switch/README.md +310 -2
- package/switch/fesm2022/switch.mjs +7 -5
- package/switch/types/switch.d.ts +2 -1
- package/table/README.md +423 -5
- package/tabs/README.md +411 -2
- package/tabs/fesm2022/tabs.mjs +12 -2
- package/textarea/README.md +282 -5
- package/toggle/README.md +270 -5
- package/toggle-group/README.md +340 -5
- package/tooltip/README.md +269 -2
- package/typography/README.md +271 -5
- package/utils/README.md +303 -2
package/combobox/README.md
CHANGED
|
@@ -1,11 +1,513 @@
|
|
|
1
|
-
# Combobox
|
|
1
|
+
# Combobox (`@egose/shadcn-theme-ng/combobox`)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A filterable select / autocomplete-dropdown — the Angular port of shadcn/ui `Combobox`. Single mode (`HlmCombobox`) and multi mode (`HlmComboboxMultiple`) wrap `@spartan-ng/brain/combobox` + `@spartan-ng/brain/popover` host directives, with two anchor styles: a button-like `hlm-combobox-trigger` and an input-like `hlm-combobox-input` (or free-form `hlm-combobox-chips` + `hlm-combobox-chip` tag lists). The dropdown is built from `hlm-combobox-content` + `hlm-combobox-portal` + `hlm-combobox-list` + `hlm-combobox-item` + group/label/empty/status/separator/value-template primitives.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Ships as `@egose/shadcn-theme-ng/combobox` and `@egose/shadcn-theme-ng-tw/combobox` (the `tw:`-prefixed variant). See the [package README](../../README.md) for install, peer dependencies, and Tailwind setup. Do not publish this project directory independently — it is consumed via the published subpath only.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
## Installation
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
|
|
10
|
+
# Plain Tailwind (no prefix):
|
|
11
|
+
npm install @egose/shadcn-theme-ng
|
|
12
|
+
|
|
13
|
+
# Or the tw:-prefixed variant:
|
|
14
|
+
npm install @egose/shadcn-theme-ng-tw
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Peers are listed in the [package README](../../README.md). This subpath declares `@angular/common`, `@angular/core`, and `@spartan-ng/brain` as peers (see `projects/combobox/package.json`).
|
|
18
|
+
|
|
19
|
+
## Imports
|
|
20
|
+
|
|
21
|
+
```ts
|
|
22
|
+
import {
|
|
23
|
+
HlmCombobox,
|
|
24
|
+
HlmComboboxMultiple,
|
|
25
|
+
HlmComboboxTrigger,
|
|
26
|
+
HlmComboboxInput,
|
|
27
|
+
HlmComboboxContent,
|
|
28
|
+
HlmComboboxPortal,
|
|
29
|
+
HlmComboboxList,
|
|
30
|
+
HlmComboboxItem,
|
|
31
|
+
HlmComboboxGroup,
|
|
32
|
+
HlmComboboxLabel,
|
|
33
|
+
HlmComboboxEmpty,
|
|
34
|
+
HlmComboboxStatus,
|
|
35
|
+
HlmComboboxSeparator,
|
|
36
|
+
HlmComboboxPlaceholder,
|
|
37
|
+
HlmComboboxValue,
|
|
38
|
+
HlmComboboxValues,
|
|
39
|
+
HlmComboboxValueTemplate,
|
|
40
|
+
HlmComboboxChips,
|
|
41
|
+
HlmComboboxChip,
|
|
42
|
+
HlmComboboxChipInput,
|
|
43
|
+
HlmComboboxImports,
|
|
44
|
+
HlmComboboxModule,
|
|
45
|
+
} from '@egose/shadcn-theme-ng/combobox';
|
|
46
|
+
// tw variant:
|
|
47
|
+
// import { ... } from '@egose/shadcn-theme-ng-tw/combobox';
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
> `HlmComboboxChipRemove` (`button[hlmComboboxChipRemove]`) exists in source but is **not** exported from `public-api.ts` — it is rendered internally by `hlm-combobox-chip` when `showRemove` is true.
|
|
51
|
+
|
|
52
|
+
Standalone:
|
|
53
|
+
|
|
54
|
+
```ts
|
|
55
|
+
import { Component } from '@angular/core';
|
|
56
|
+
import { HlmComboboxImports } from '@egose/shadcn-theme-ng/combobox';
|
|
57
|
+
|
|
58
|
+
@Component({
|
|
59
|
+
selector: 'app-demo',
|
|
60
|
+
standalone: true,
|
|
61
|
+
imports: [...HlmComboboxImports],
|
|
62
|
+
template: `...`,
|
|
63
|
+
})
|
|
64
|
+
export class DemoComponent {}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
NgModule:
|
|
68
|
+
|
|
69
|
+
```ts
|
|
70
|
+
import { NgModule } from '@angular/core';
|
|
71
|
+
import { HlmComboboxModule } from '@egose/shadcn-theme-ng/combobox';
|
|
72
|
+
|
|
73
|
+
@NgModule({ imports: [HlmComboboxModule] })
|
|
74
|
+
export class DemoModule {}
|
|
75
|
+
```
|
|
76
|
+
|
|
77
|
+
## Anatomy / Structure
|
|
78
|
+
|
|
79
|
+
```html
|
|
80
|
+
<!-- Single, button trigger -->
|
|
81
|
+
<hlm-combobox [value]="value" [search]="search" (valueChange)="value = $event" (searchChange)="search = $event">
|
|
82
|
+
<hlm-combobox-trigger>Select fruit…</hlm-combobox-trigger>
|
|
83
|
+
<hlm-combobox-content *hlmComboboxPortal>
|
|
84
|
+
<hlm-combobox-list>
|
|
85
|
+
<hlm-combobox-item value="apple">Apple</hlm-combobox-item>
|
|
86
|
+
<hlm-combobox-item value="banana">Banana</hlm-combobox-item>
|
|
87
|
+
</hlm-combobox-list>
|
|
88
|
+
<div hlmComboboxEmpty>No results.</div>
|
|
89
|
+
</hlm-combobox-content>
|
|
90
|
+
</hlm-combobox>
|
|
91
|
+
|
|
92
|
+
<!-- Single, input trigger with clear button -->
|
|
93
|
+
<hlm-combobox [value]="v" (valueChange)="v = $event">
|
|
94
|
+
<hlm-combobox-input placeholder="Search…" [showClear]="true" />
|
|
95
|
+
<hlm-combobox-content *hlmComboboxPortal>
|
|
96
|
+
<hlm-combobox-list>
|
|
97
|
+
<div hlmComboboxGroup>
|
|
98
|
+
<span hlmComboboxLabel>Fruits</span>
|
|
99
|
+
<hlm-combobox-item value="apple">Apple</hlm-combobox-item>
|
|
100
|
+
</div>
|
|
101
|
+
</hlm-combobox-list>
|
|
102
|
+
</hlm-combobox-content>
|
|
103
|
+
</hlm-combobox>
|
|
104
|
+
|
|
105
|
+
<!-- Multi, chips anchor -->
|
|
106
|
+
<hlm-combobox-multiple [value]="selected" (valueChange)="selected = $event">
|
|
107
|
+
<hlm-combobox-chips>
|
|
108
|
+
@for (v of selected; track v) {
|
|
109
|
+
<hlm-combobox-chip [value]="v">{{ v }}</hlm-combobox-chip>
|
|
110
|
+
}
|
|
111
|
+
<input hlmComboboxChipInput placeholder="Pick…" />
|
|
112
|
+
</hlm-combobox-chips>
|
|
113
|
+
<hlm-combobox-content *hlmComboboxPortal>
|
|
114
|
+
<hlm-combobox-list>
|
|
115
|
+
<hlm-combobox-item value="a">A</hlm-combobox-item>
|
|
116
|
+
</hlm-combobox-list>
|
|
117
|
+
</hlm-combobox-content>
|
|
118
|
+
</hlm-combobox-multiple>
|
|
119
|
+
```
|
|
120
|
+
|
|
121
|
+
Real selectors:
|
|
122
|
+
|
|
123
|
+
| Class | Selector |
|
|
124
|
+
| -------------------------- | -------------------------------------------------------------- |
|
|
125
|
+
| `HlmCombobox` | `[hlmCombobox]`, `hlm-combobox` |
|
|
126
|
+
| `HlmComboboxMultiple` | `[hlmComboboxMultiple]`, `hlm-combobox-multiple` |
|
|
127
|
+
| `HlmComboboxTrigger` | `hlm-combobox-trigger` (element only) |
|
|
128
|
+
| `HlmComboboxInput` | `hlm-combobox-input` (element only) |
|
|
129
|
+
| `HlmComboboxItem` | `hlm-combobox-item` (element only) |
|
|
130
|
+
| `HlmComboboxContent` | `[hlmComboboxContent]`, `hlm-combobox-content` |
|
|
131
|
+
| `HlmComboboxList` | `[hlmComboboxList]` |
|
|
132
|
+
| `HlmComboboxGroup` | `[hlmComboboxGroup]` |
|
|
133
|
+
| `HlmComboboxLabel` | `[hlmComboboxLabel]` |
|
|
134
|
+
| `HlmComboboxEmpty` | `[hlmComboboxEmpty]`, `hlm-combobox-empty` |
|
|
135
|
+
| `HlmComboboxStatus` | `[hlmComboboxStatus]`, `hlm-combobox-status` |
|
|
136
|
+
| `HlmComboboxSeparator` | `[hlmComboboxSeparator]` |
|
|
137
|
+
| `HlmComboboxPlaceholder` | `[hlmComboboxPlaceholder]`, `hlm-combobox-placeholder` |
|
|
138
|
+
| `HlmComboboxValue` | `[hlmComboboxValue]`, `hlm-combobox-value` |
|
|
139
|
+
| `HlmComboboxValues` | `[hlmComboboxValues]` |
|
|
140
|
+
| `HlmComboboxValueTemplate` | `[hlmComboboxValueTemplate]` |
|
|
141
|
+
| `HlmComboboxChips` | `[hlmComboboxChips]`, `hlm-combobox-chips` |
|
|
142
|
+
| `HlmComboboxChip` | `hlm-combobox-chip` (element only) |
|
|
143
|
+
| `HlmComboboxChipInput` | `input[hlmComboboxChipInput]` (input only) |
|
|
144
|
+
| `HlmComboboxPortal` | `[hlmComboboxPortal]` (use as `*hlmComboboxPortal` on content) |
|
|
145
|
+
|
|
146
|
+
## API reference
|
|
147
|
+
|
|
148
|
+
### `HlmCombobox` / `HlmComboboxMultiple` (roots)
|
|
149
|
+
|
|
150
|
+
Both provide `provideBrnPopoverConfig({ align: 'start', sideOffset: 6 })` and `provideBrnPopoverDefaultOptions({ role: null })`.
|
|
151
|
+
|
|
152
|
+
| Brain input (`BrnCombobox` / `BrnComboboxMultiple`) | Description |
|
|
153
|
+
| --------------------------------------------------- | ------------------------------------------------------------------------------------ |
|
|
154
|
+
| `autoHighlight` | Auto-highlight first match as the search changes. |
|
|
155
|
+
| `disabled` | Locks trigger + list. |
|
|
156
|
+
| `filter` | Custom filter fn (default substring match). Set to `null` for server-filtered lists. |
|
|
157
|
+
| `search` | Controlled search text. |
|
|
158
|
+
| `value` | Controlled value (single: item; multiple: array). |
|
|
159
|
+
| `itemToString` | `(item) => string` label mapper for object values. |
|
|
160
|
+
| `filterOptions` | Extra filter configuration object. |
|
|
161
|
+
| `isItemEqualToValue` | Equality predicate for object values. |
|
|
162
|
+
|
|
163
|
+
| Brain output | Description |
|
|
164
|
+
| -------------- | ------------------------------ |
|
|
165
|
+
| `searchChange` | Emitted on search-text change. |
|
|
166
|
+
| `valueChange` | Emitted on selection change. |
|
|
167
|
+
|
|
168
|
+
Popover passthrough (via `BrnPopover` hostDirective): inputs `align`, `closeOnOutsidePointerEvents`, `sideOffset`, `state`, `offsetX`; outputs `stateChanged`, `closed`.
|
|
169
|
+
|
|
170
|
+
### `HlmComboboxTrigger` (`hlm-combobox-trigger`)
|
|
171
|
+
|
|
172
|
+
Renders an `hlmBtn`-styled `<button>` (`brnComboboxTrigger` + anchor + popover-trigger) with a chevron.
|
|
173
|
+
|
|
174
|
+
| Input | Type | Default | Description |
|
|
175
|
+
| --------------------- | --------------------------- | ------------------------------- | --------------------------------------------------------------------- |
|
|
176
|
+
| `class` (`userClass`) | `ClassValue` | `''` | Merged onto the button. Placeholder text dims via `data-placeholder`. |
|
|
177
|
+
| `buttonId` | `string` | auto (`hlm-combobox-trigger-N`) | Button id. |
|
|
178
|
+
| `variant` | `ButtonVariants['variant']` | `'outline'` | `HlmBtn` variant. |
|
|
179
|
+
| `forceInvalid` | `boolean` | `false` | Force error ring regardless of form state. |
|
|
180
|
+
|
|
181
|
+
### `HlmComboboxInput` (`hlm-combobox-input`)
|
|
182
|
+
|
|
183
|
+
Input-group anchor with chevron trigger + optional clear button.
|
|
184
|
+
|
|
185
|
+
| Input | Type | Default | Description |
|
|
186
|
+
| -------------------------------------- | ---------------------- | ----------------------------- | ------------------------------------------------------------- |
|
|
187
|
+
| `inputId` | `string` | auto (`hlm-combobox-input-N`) | Input id. |
|
|
188
|
+
| `placeholder` | `string` | `''` | Placeholder. |
|
|
189
|
+
| `showTrigger` | `boolean` | `true` | Show the chevron popover button. |
|
|
190
|
+
| `showClear` | `boolean` | `false` | Show the `*brnComboboxClear` X button when a value exists. |
|
|
191
|
+
| `forceInvalid` | `boolean` | `false` | Force error ring. |
|
|
192
|
+
| `aria-invalid` (`ariaInvalidOverride`) | `boolean \| undefined` | `undefined` | Manual override; unset = auto-detect from parent error state. |
|
|
193
|
+
|
|
194
|
+
### Items / list / groups
|
|
195
|
+
|
|
196
|
+
| Directive | Brain inputs | Notes |
|
|
197
|
+
| ---------------------- | --------------------------- | ---------------------------------------------------------------------- |
|
|
198
|
+
| `HlmComboboxItem` | `id`, `disabled`, `value` | Shows `lucideCheck` at `end-2` when `active`. |
|
|
199
|
+
| `HlmComboboxList` | `id` | Scroll container (`max-h-72`-ish). |
|
|
200
|
+
| `HlmComboboxGroup` | (none, `BrnComboboxGroup`) | `data-hidden` hides empty groups. |
|
|
201
|
+
| `HlmComboboxLabel` | `id` | Muted `text-xs` group heading. |
|
|
202
|
+
| `HlmComboboxEmpty` | (none, `BrnComboboxEmpty`) | Shown only when the content is `data-empty` (`group-data-empty:flex`). |
|
|
203
|
+
| `HlmComboboxStatus` | (none, `BrnComboboxStatus`) | Loading/status row (`"Loading…"`, counts). |
|
|
204
|
+
| `HlmComboboxSeparator` | `orientation` | 1px `bg-border` divider. |
|
|
205
|
+
|
|
206
|
+
### Values / placeholders / chips
|
|
207
|
+
|
|
208
|
+
| Directive | Notes |
|
|
209
|
+
| -------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
|
|
210
|
+
| `HlmComboboxPlaceholder` | Visible when no value (`data-hidden` hides it once selected). |
|
|
211
|
+
| `HlmComboboxValue` (`inputs: ['placeholder']`) | Value display wrapper. |
|
|
212
|
+
| `HlmComboboxValues` | Multi-value container (brain). |
|
|
213
|
+
| `HlmComboboxValueTemplate` | Custom selected-value template marker (brain). |
|
|
214
|
+
| `HlmComboboxChips` (`input forceInvalid=false`) | Wrap-around anchor (`BrnComboboxAnchor` + popover-trigger) for tag UIs; reflects `data-matches-spartan-invalid`. |
|
|
215
|
+
| `HlmComboboxChip` (`input showRemove=true`) | Tag pill with internal remove button. |
|
|
216
|
+
| `HlmComboboxChipInput` (`inputs id, aria-invalid`) | The free-text `<input>` inside chips. |
|
|
217
|
+
| `HlmComboboxContent` | Popover panel (`w-(--brn-combobox-width)`, `max-h-72`). Pair with `*hlmComboboxPortal`. |
|
|
218
|
+
| `HlmComboboxPortal` (`inputs context, class`) | Structural portal directive — `*hlmComboboxPortal` teleports content to the popover overlay. |
|
|
219
|
+
|
|
220
|
+
## Examples
|
|
221
|
+
|
|
222
|
+
### 1. Basic single combobox (button trigger)
|
|
223
|
+
|
|
224
|
+
```ts
|
|
225
|
+
import { Component, signal } from '@angular/core';
|
|
226
|
+
import { HlmComboboxImports } from '@egose/shadcn-theme-ng/combobox';
|
|
227
|
+
|
|
228
|
+
const FRUITS = ['Apple', 'Banana', 'Blueberry', 'Grapes', 'Pineapple'];
|
|
229
|
+
|
|
230
|
+
@Component({
|
|
231
|
+
selector: 'app-basic-combobox',
|
|
232
|
+
standalone: true,
|
|
233
|
+
imports: [...HlmComboboxImports],
|
|
234
|
+
template: `
|
|
235
|
+
<hlm-combobox [value]="value()" (valueChange)="value.set($event)">
|
|
236
|
+
<hlm-combobox-trigger>{{ value() ?? 'Select fruit…' }}</hlm-combobox-trigger>
|
|
237
|
+
<hlm-combobox-content *hlmComboboxPortal>
|
|
238
|
+
<hlm-combobox-list>
|
|
239
|
+
@for (f of FRUITS; track f) {
|
|
240
|
+
<hlm-combobox-item [value]="f">{{ f }}</hlm-combobox-item>
|
|
241
|
+
}
|
|
242
|
+
</hlm-combobox-list>
|
|
243
|
+
<div hlmComboboxEmpty>No fruit found.</div>
|
|
244
|
+
</hlm-combobox-content>
|
|
245
|
+
</hlm-combobox>
|
|
246
|
+
<p class="mt-2 text-sm">Value: {{ value() ?? 'none' }}</p>
|
|
247
|
+
`,
|
|
248
|
+
})
|
|
249
|
+
export class BasicComboboxComponent {
|
|
250
|
+
readonly value = signal<string | undefined>(undefined);
|
|
251
|
+
protected readonly FRUITS = FRUITS;
|
|
252
|
+
}
|
|
253
|
+
```
|
|
254
|
+
|
|
255
|
+
### 2. Searchable input trigger with clear button + groups
|
|
256
|
+
|
|
257
|
+
```ts
|
|
258
|
+
import { Component, signal } from '@angular/core';
|
|
259
|
+
import { HlmComboboxImports } from '@egose/shadcn-theme-ng/combobox';
|
|
260
|
+
|
|
261
|
+
@Component({
|
|
262
|
+
selector: 'app-grouped-combobox',
|
|
263
|
+
standalone: true,
|
|
264
|
+
imports: [...HlmComboboxImports],
|
|
265
|
+
template: `
|
|
266
|
+
<hlm-combobox
|
|
267
|
+
[value]="value()"
|
|
268
|
+
[search]="search()"
|
|
269
|
+
(valueChange)="value.set($event)"
|
|
270
|
+
(searchChange)="search.set($event)"
|
|
271
|
+
>
|
|
272
|
+
<hlm-combobox-input placeholder="Search timezone…" [showClear]="true" />
|
|
273
|
+
<hlm-combobox-content *hlmComboboxPortal>
|
|
274
|
+
<hlm-combobox-list>
|
|
275
|
+
<div hlmComboboxGroup>
|
|
276
|
+
<span hlmComboboxLabel>Americas</span>
|
|
277
|
+
@for (tz of americas; track tz) {
|
|
278
|
+
<hlm-combobox-item [value]="tz">{{ tz }}</hlm-combobox-item>
|
|
279
|
+
}
|
|
280
|
+
</div>
|
|
281
|
+
<div hlmComboboxSeparator></div>
|
|
282
|
+
<div hlmComboboxGroup>
|
|
283
|
+
<span hlmComboboxLabel>Europe</span>
|
|
284
|
+
@for (tz of europe; track tz) {
|
|
285
|
+
<hlm-combobox-item [value]="tz">{{ tz }}</hlm-combobox-item>
|
|
286
|
+
}
|
|
287
|
+
</div>
|
|
288
|
+
</hlm-combobox-list>
|
|
289
|
+
<div hlmComboboxEmpty>No timezone found.</div>
|
|
290
|
+
</hlm-combobox-content>
|
|
291
|
+
</hlm-combobox>
|
|
292
|
+
`,
|
|
293
|
+
})
|
|
294
|
+
export class GroupedComboboxComponent {
|
|
295
|
+
readonly value = signal<string | undefined>(undefined);
|
|
296
|
+
readonly search = signal('');
|
|
297
|
+
readonly americas = ['America/New_York', 'America/Chicago', 'America/Los_Angeles'];
|
|
298
|
+
readonly europe = ['Europe/London', 'Europe/Berlin', 'Europe/Bucharest'];
|
|
299
|
+
}
|
|
11
300
|
```
|
|
301
|
+
|
|
302
|
+
### 3. Multi-select with removable chips
|
|
303
|
+
|
|
304
|
+
```ts
|
|
305
|
+
import { Component, signal } from '@angular/core';
|
|
306
|
+
import { HlmComboboxImports } from '@egose/shadcn-theme-ng/combobox';
|
|
307
|
+
|
|
308
|
+
@Component({
|
|
309
|
+
selector: 'app-multi-combobox',
|
|
310
|
+
standalone: true,
|
|
311
|
+
imports: [...HlmComboboxImports],
|
|
312
|
+
template: `
|
|
313
|
+
<hlm-combobox-multiple [value]="selected()" (valueChange)="selected.set($event ?? [])">
|
|
314
|
+
<hlm-combobox-chips>
|
|
315
|
+
@for (v of selected(); track v) {
|
|
316
|
+
<hlm-combobox-chip [value]="v">{{ v }}</hlm-combobox-chip>
|
|
317
|
+
}
|
|
318
|
+
<input hlmComboboxChipInput placeholder="Add tag…" />
|
|
319
|
+
</hlm-combobox-chips>
|
|
320
|
+
<hlm-combobox-content *hlmComboboxPortal>
|
|
321
|
+
<hlm-combobox-list>
|
|
322
|
+
@for (opt of options; track opt) {
|
|
323
|
+
<hlm-combobox-item [value]="opt">{{ opt }}</hlm-combobox-item>
|
|
324
|
+
}
|
|
325
|
+
</hlm-combobox-list>
|
|
326
|
+
<div hlmComboboxEmpty>Nothing left to add.</div>
|
|
327
|
+
</hlm-combobox-content>
|
|
328
|
+
</hlm-combobox-multiple>
|
|
329
|
+
<p class="mt-2 text-sm">{{ selected().length }} selected</p>
|
|
330
|
+
`,
|
|
331
|
+
})
|
|
332
|
+
export class MultiComboboxComponent {
|
|
333
|
+
readonly selected = signal<string[]>(['Angular']);
|
|
334
|
+
readonly options = ['Angular', 'React', 'Vue', 'Svelte', 'Solid'];
|
|
335
|
+
}
|
|
336
|
+
```
|
|
337
|
+
|
|
338
|
+
### 4. Disabled + invalid + custom filter states
|
|
339
|
+
|
|
340
|
+
```ts
|
|
341
|
+
import { Component, signal } from '@angular/core';
|
|
342
|
+
import { HlmComboboxImports } from '@egose/shadcn-theme-ng/combobox';
|
|
343
|
+
|
|
344
|
+
@Component({
|
|
345
|
+
selector: 'app-states-combobox',
|
|
346
|
+
standalone: true,
|
|
347
|
+
imports: [...HlmComboboxImports],
|
|
348
|
+
template: `
|
|
349
|
+
<div class="space-y-4">
|
|
350
|
+
<hlm-combobox [disabled]="true">
|
|
351
|
+
<hlm-combobox-trigger>Disabled</hlm-combobox-trigger>
|
|
352
|
+
</hlm-combobox>
|
|
353
|
+
|
|
354
|
+
<hlm-combobox [value]="v()" (valueChange)="v.set($event)">
|
|
355
|
+
<hlm-combobox-trigger [forceInvalid]="submitted() && !v()">Pick (required)</hlm-combobox-trigger>
|
|
356
|
+
<hlm-combobox-content *hlmComboboxPortal>
|
|
357
|
+
<hlm-combobox-list>
|
|
358
|
+
<hlm-combobox-item value="a">Option A</hlm-combobox-item>
|
|
359
|
+
<hlm-combobox-item value="b" [disabled]="true">Option B (disabled item)</hlm-combobox-item>
|
|
360
|
+
</hlm-combobox-list>
|
|
361
|
+
</hlm-combobox-content>
|
|
362
|
+
</hlm-combobox>
|
|
363
|
+
<button type="button" (click)="submitted.set(true)">Submit</button>
|
|
364
|
+
|
|
365
|
+
<!-- Case-insensitive startsWith filter -->
|
|
366
|
+
<hlm-combobox [filter]="startsWith" [value]="v2()" (valueChange)="v2.set($event)">
|
|
367
|
+
<hlm-combobox-input placeholder="startsWith filter…" />
|
|
368
|
+
<hlm-combobox-content *hlmComboboxPortal>
|
|
369
|
+
<hlm-combobox-list>
|
|
370
|
+
<hlm-combobox-item value="apple">apple</hlm-combobox-item>
|
|
371
|
+
<hlm-combobox-item value="apricot">apricot</hlm-combobox-item>
|
|
372
|
+
<hlm-combobox-item value="banana">banana</hlm-combobox-item>
|
|
373
|
+
</hlm-combobox-list>
|
|
374
|
+
</hlm-combobox-content>
|
|
375
|
+
</hlm-combobox>
|
|
376
|
+
</div>
|
|
377
|
+
`,
|
|
378
|
+
})
|
|
379
|
+
export class StatesComboboxComponent {
|
|
380
|
+
readonly v = signal<string | undefined>(undefined);
|
|
381
|
+
readonly v2 = signal<string | undefined>(undefined);
|
|
382
|
+
readonly submitted = signal(false);
|
|
383
|
+
readonly startsWith = (item: string, search: string) => item.toLowerCase().startsWith(search.toLowerCase());
|
|
384
|
+
}
|
|
385
|
+
```
|
|
386
|
+
|
|
387
|
+
### 5. Object values (`itemToString` + equality) with custom value template
|
|
388
|
+
|
|
389
|
+
```ts
|
|
390
|
+
import { Component, signal } from '@angular/core';
|
|
391
|
+
import { HlmComboboxImports } from '@egose/shadcn-theme-ng/combobox';
|
|
392
|
+
|
|
393
|
+
interface User {
|
|
394
|
+
id: number;
|
|
395
|
+
name: string;
|
|
396
|
+
email: string;
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
@Component({
|
|
400
|
+
selector: 'app-object-combobox',
|
|
401
|
+
standalone: true,
|
|
402
|
+
imports: [...HlmComboboxImports],
|
|
403
|
+
template: `
|
|
404
|
+
<hlm-combobox
|
|
405
|
+
[value]="assignee()"
|
|
406
|
+
[itemToString]="toName"
|
|
407
|
+
[isItemEqualToValue]="byId"
|
|
408
|
+
(valueChange)="assignee.set($event)"
|
|
409
|
+
>
|
|
410
|
+
<hlm-combobox-trigger>
|
|
411
|
+
<span hlmComboboxValue [placeholder]="'Assign to…'">
|
|
412
|
+
{{ assignee()?.name ?? 'Assign to…' }}
|
|
413
|
+
</span>
|
|
414
|
+
</hlm-combobox-trigger>
|
|
415
|
+
<hlm-combobox-content *hlmComboboxPortal>
|
|
416
|
+
<hlm-combobox-list>
|
|
417
|
+
@for (u of users; track u.id) {
|
|
418
|
+
<hlm-combobox-item [value]="u">
|
|
419
|
+
<span class="flex flex-col">
|
|
420
|
+
<span>{{ u.name }}</span>
|
|
421
|
+
<span class="text-muted-foreground text-xs">{{ u.email }}</span>
|
|
422
|
+
</span>
|
|
423
|
+
</hlm-combobox-item>
|
|
424
|
+
}
|
|
425
|
+
</hlm-combobox-list>
|
|
426
|
+
<div hlmComboboxEmpty>No users.</div>
|
|
427
|
+
</hlm-combobox-content>
|
|
428
|
+
</hlm-combobox>
|
|
429
|
+
`,
|
|
430
|
+
})
|
|
431
|
+
export class ObjectComboboxComponent {
|
|
432
|
+
readonly users: User[] = [
|
|
433
|
+
{ id: 1, name: 'Ada Lovelace', email: 'ada@example.com' },
|
|
434
|
+
{ id: 2, name: 'Grace Hopper', email: 'grace@example.com' },
|
|
435
|
+
];
|
|
436
|
+
readonly assignee = signal<User | undefined>(undefined);
|
|
437
|
+
readonly toName = (u: User | undefined) => u?.name ?? '';
|
|
438
|
+
readonly byId = (a: User, b: User) => a.id === b.id;
|
|
439
|
+
}
|
|
440
|
+
```
|
|
441
|
+
|
|
442
|
+
### 6. Async search (server filtering + status row)
|
|
443
|
+
|
|
444
|
+
Set `[filter]="null"` to disable client filtering, then feed options from your own observable/signal.
|
|
445
|
+
|
|
446
|
+
```ts
|
|
447
|
+
import { Component, signal } from '@angular/core';
|
|
448
|
+
import { HlmComboboxImports } from '@egose/shadcn-theme-ng/combobox';
|
|
449
|
+
|
|
450
|
+
@Component({
|
|
451
|
+
selector: 'app-async-combobox',
|
|
452
|
+
standalone: true,
|
|
453
|
+
imports: [...HlmComboboxImports],
|
|
454
|
+
template: `
|
|
455
|
+
<hlm-combobox
|
|
456
|
+
[filter]="null"
|
|
457
|
+
[search]="search()"
|
|
458
|
+
[value]="value()"
|
|
459
|
+
(searchChange)="onSearch($event)"
|
|
460
|
+
(valueChange)="value.set($event)"
|
|
461
|
+
>
|
|
462
|
+
<hlm-combobox-input placeholder="Search repos…" />
|
|
463
|
+
<hlm-combobox-content *hlmComboboxPortal>
|
|
464
|
+
@if (loading()) {
|
|
465
|
+
<div hlmComboboxStatus>Searching…</div>
|
|
466
|
+
}
|
|
467
|
+
<hlm-combobox-list>
|
|
468
|
+
@for (r of results(); track r) {
|
|
469
|
+
<hlm-combobox-item [value]="r">{{ r }}</hlm-combobox-item>
|
|
470
|
+
}
|
|
471
|
+
</hlm-combobox-list>
|
|
472
|
+
@if (!loading() && results().length === 0) {
|
|
473
|
+
<div hlmComboboxEmpty>No repositories found.</div>
|
|
474
|
+
}
|
|
475
|
+
</hlm-combobox-content>
|
|
476
|
+
</hlm-combobox>
|
|
477
|
+
`,
|
|
478
|
+
})
|
|
479
|
+
export class AsyncComboboxComponent {
|
|
480
|
+
readonly search = signal('');
|
|
481
|
+
readonly value = signal<string | undefined>(undefined);
|
|
482
|
+
readonly results = signal<string[]>([]);
|
|
483
|
+
readonly loading = signal(false);
|
|
484
|
+
|
|
485
|
+
async onSearch(q: string) {
|
|
486
|
+
this.search.set(q);
|
|
487
|
+
this.loading.set(true);
|
|
488
|
+
// Simulate a debounced fetch — replace with HttpClient in real apps:
|
|
489
|
+
await new Promise((r) => setTimeout(r, 250));
|
|
490
|
+
const ALL = ['shadcn-theme', 'spartan-ng', 'angular', 'ember-tracker'];
|
|
491
|
+
this.results.set(ALL.filter((r) => r.includes(q.toLowerCase())));
|
|
492
|
+
this.loading.set(false);
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
```
|
|
496
|
+
|
|
497
|
+
## Accessibility notes
|
|
498
|
+
|
|
499
|
+
- Anchors are real controls: trigger is a `<button>`, input mode is a real `<input>`, chips mode keeps a focusable text input — keyboard users can always reach and operate the list.
|
|
500
|
+
- Items expose active/highlighted + disabled states via brain directives; keep the `lucideCheck` selected indicator (it is `aria-hidden`, state comes from semantics, not the icon).
|
|
501
|
+
- `HlmComboboxEmpty` / `HlmComboboxStatus` give screen-reader users feedback for no-results and loading — always render one of them.
|
|
502
|
+
- Label the control: `hlm-combobox-trigger` content, `placeholder`, or an external `<label [for]="inputId">`. The popover defaults to `role: null` (list semantics come from the combobox directives) — do not add a redundant dialog role.
|
|
503
|
+
|
|
504
|
+
## Theming / CSS variables
|
|
505
|
+
|
|
506
|
+
No component-specific CSS variables. The panel width tracks the anchor (`w-(--brn-combobox-width)`), with `bg-popover` / `text-popover-foreground`, `ring-foreground/10`, `max-h-72` scrolling, and `data-open`/`data-closed` enter/exit animations. Invalid states use `border-destructive` / `ring-destructive/20` via `forceInvalid` or the parent spartan error state.
|
|
507
|
+
|
|
508
|
+
## Related subpaths
|
|
509
|
+
|
|
510
|
+
- `@egose/shadcn-theme-ng/popover` — underlying overlay positioning (`align`, `sideOffset`).
|
|
511
|
+
- `@egose/shadcn-theme-ng/input-group` — powers `hlm-combobox-input` chrome (chevron/clear addons).
|
|
512
|
+
- `@egose/shadcn-theme-ng/button` — trigger button variants.
|
|
513
|
+
- `@egose/shadcn-theme-ng/command` — command-palette alternative for action search (vs item picking).
|
|
@@ -63,7 +63,10 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImpor
|
|
|
63
63
|
|
|
64
64
|
class HlmComboboxChipRemove {
|
|
65
65
|
constructor() {
|
|
66
|
-
classes(() => [
|
|
66
|
+
classes(() => [
|
|
67
|
+
'tw:-ms-1 tw:opacity-50 tw:hover:opacity-100',
|
|
68
|
+
buttonVariants({ variant: 'ghost', size: 'icon-xs' }),
|
|
69
|
+
]);
|
|
67
70
|
}
|
|
68
71
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: HlmComboboxChipRemove, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
69
72
|
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.3", type: HlmComboboxChipRemove, isStandalone: true, selector: "button[hlmComboboxChipRemove]", host: { attributes: { "data-slot": "combobox-chip-remove" } }, hostDirectives: [{ directive: i1.BrnComboboxChipRemove }], ngImport: i0 });
|
|
@@ -261,7 +264,7 @@ class HlmComboboxInput {
|
|
|
261
264
|
</hlm-input-group-addon>
|
|
262
265
|
|
|
263
266
|
<ng-content />
|
|
264
|
-
`, isInline: true, dependencies: [{ kind: "directive", type: i2$1.HlmInputGroupAddon, selector: "[hlmInputGroupAddon],hlm-input-group-addon", inputs: ["align"] }, { kind: "directive", type: i2$1.HlmInputGroupButton, selector: "button[hlmInputGroupButton]", inputs: ["size", "type"] }, { kind: "directive", type: i2$1.HlmInputGroupInput, selector: "input[hlmInputGroupInput]" }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "directive", type: i1.BrnComboboxClear, selector: "[brnComboboxClear]" }, { kind: "directive", type: i1.BrnComboboxInput, selector: "input[brnComboboxInput]", inputs: ["id", "aria-invalid", "forceInvalid"], exportAs: ["brnComboboxInput"] }, { kind: "directive", type: i1.BrnComboboxPopoverTrigger, selector: "[brnComboboxPopoverTrigger]", inputs: ["closeOnTriggerClick"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
267
|
+
`, isInline: true, dependencies: [{ kind: "directive", type: i2$1.HlmInputGroupAddon, selector: "[hlmInputGroupAddon],hlm-input-group-addon", inputs: ["align"] }, { kind: "directive", type: i2$1.HlmInputGroupButton, selector: "button[hlmInputGroupButton]", inputs: ["size", "type", "class"] }, { kind: "directive", type: i2$1.HlmInputGroupInput, selector: "input[hlmInputGroupInput]" }, { kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }, { kind: "directive", type: i1.BrnComboboxClear, selector: "[brnComboboxClear]" }, { kind: "directive", type: i1.BrnComboboxInput, selector: "input[brnComboboxInput]", inputs: ["id", "aria-invalid", "forceInvalid"], exportAs: ["brnComboboxInput"] }, { kind: "directive", type: i1.BrnComboboxPopoverTrigger, selector: "[brnComboboxPopoverTrigger]", inputs: ["closeOnTriggerClick"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
265
268
|
}
|
|
266
269
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: HlmComboboxInput, decorators: [{
|
|
267
270
|
type: Component,
|