@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
|
@@ -1,64 +1,362 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Form Searchable Multiselect (`@egose/shadcn-theme-ng/form-searchable-multiselect`)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
`EgFormSearchableMultiselect` is a reactive-forms wrapper that pairs a label, a searchable multi-select popover, and error/hint text into a single form-ready row. It is the shadcn/ui equivalent of a _Form + Combobox (multiple)_ composition: the selection UI itself comes from `EgSearchableMultiselect` (`@egose/shadcn-theme-ng/searchable-multiselect`, a `ControlValueAccessor` built on the popover + checkbox primitives), while this wrapper supplies the surrounding `eg-form-field` layout, generated ids, and `aria-describedby` wiring.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
> **Ships as:** `@egose/shadcn-theme-ng/form-searchable-multiselect` (plain Tailwind) and `@egose/shadcn-theme-ng-tw/form-searchable-multiselect` (`tw:`-prefixed variant). See the [package README](../../README.md) for installation, peer dependencies, and the Tailwind-variant contract. Do not publish this project directory independently.
|
|
6
6
|
|
|
7
|
-
|
|
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
|
|
11
15
|
```
|
|
12
16
|
|
|
13
|
-
|
|
17
|
+
Peer dependencies (Angular, CDK, `@spartan-ng/brain`, `rxjs`, `@ng-icons/core`) are resolved from the versions documented in the [package README](../../README.md#peer-dependencies). Import the subpath — no extra install step per component:
|
|
14
18
|
|
|
15
|
-
```
|
|
16
|
-
|
|
19
|
+
```ts
|
|
20
|
+
import { EgFormSearchableMultiselect } from '@egose/shadcn-theme-ng/form-searchable-multiselect';
|
|
21
|
+
// tw variant:
|
|
22
|
+
// import { EgFormSearchableMultiselect } from '@egose/shadcn-theme-ng-tw/form-searchable-multiselect';
|
|
17
23
|
```
|
|
18
24
|
|
|
19
|
-
##
|
|
25
|
+
## Imports
|
|
20
26
|
|
|
21
|
-
|
|
27
|
+
The public API (`src/public-api.ts`) exports exactly one symbol — the standalone component. There is no `*Imports` array and no `*Module` for this subpath; import the component class directly.
|
|
22
28
|
|
|
23
|
-
```
|
|
24
|
-
|
|
29
|
+
```ts
|
|
30
|
+
import { EgFormSearchableMultiselect } from '@egose/shadcn-theme-ng/form-searchable-multiselect';
|
|
31
|
+
|
|
32
|
+
@Component({
|
|
33
|
+
standalone: true,
|
|
34
|
+
imports: [ReactiveFormsModule, EgFormSearchableMultiselect],
|
|
35
|
+
template: `
|
|
36
|
+
<form [formGroup]="form">
|
|
37
|
+
<eg-form-searchable-multiselect controlName="tags" label="Tags" [options]="tagOptions" />
|
|
38
|
+
</form>
|
|
39
|
+
`,
|
|
40
|
+
})
|
|
41
|
+
export class MyForm {}
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Requirements:
|
|
45
|
+
|
|
46
|
+
- The component **must** sit inside a `<form [formGroup]>` (or any `FormGroupDirective` ancestor). It injects `FormGroupDirective` and provides `ControlContainer`, so `controlName` resolves against the parent group.
|
|
47
|
+
- `controlName` is required — it is forwarded as `[formControlName]` to the inner `eg-searchable-multiselect`.
|
|
48
|
+
- The bound `FormControl` should hold a `string[]` (e.g. `new FormControl<string[]>([], { nonNullable: true })`).
|
|
49
|
+
|
|
50
|
+
## Anatomy / Structure
|
|
51
|
+
|
|
52
|
+
```html
|
|
53
|
+
<eg-form-searchable-multiselect
|
|
54
|
+
controlName="tags"
|
|
55
|
+
label="Tags"
|
|
56
|
+
error="Pick at least one tag"
|
|
57
|
+
hint="Choose all that apply"
|
|
58
|
+
>
|
|
59
|
+
<!-- rendered internally -->
|
|
60
|
+
<eg-form-field>
|
|
61
|
+
<label hlmLabel for="<effectiveId>">Tags <span>*</span></label>
|
|
62
|
+
<eg-searchable-multiselect
|
|
63
|
+
id="<effectiveId>"
|
|
64
|
+
[options]="[{ label, value }]"
|
|
65
|
+
placeholder="Start typing to add…"
|
|
66
|
+
formControlName="tags"
|
|
67
|
+
></eg-searchable-multiselect>
|
|
68
|
+
<hlm-error id="<effectiveId>-error">…</hlm-error>
|
|
69
|
+
<hlm-hint id="<effectiveId>-hint">…</hlm-hint>
|
|
70
|
+
</eg-form-field>
|
|
71
|
+
</eg-form-searchable-multiselect>
|
|
25
72
|
```
|
|
26
73
|
|
|
27
|
-
|
|
74
|
+
Real selectors involved: `eg-form-searchable-multiselect` (this wrapper), `eg-form-field` (`@egose/shadcn-theme-ng/form-field-simple`), `label[hlmLabel]`, `eg-searchable-multiselect`, `hlm-error` / `hlm-hint` (`@egose/shadcn-theme-ng/form-field`).
|
|
28
75
|
|
|
29
|
-
|
|
76
|
+
## API reference
|
|
30
77
|
|
|
31
|
-
|
|
78
|
+
### `eg-form-searchable-multiselect` — `EgFormSearchableMultiselect`
|
|
32
79
|
|
|
33
|
-
|
|
80
|
+
| Input | Type | Default | Description |
|
|
81
|
+
| --------------------- | --------------------- | ------------------------ | --------------------------------------------------------------------------------------------------- |
|
|
82
|
+
| `label` | `string \| undefined` | `undefined` | Field label. When set, a `<label hlmLabel>` bound to the control id is rendered. |
|
|
83
|
+
| `controlName` | `string` | `''` | **Required.** Name of the control in the parent `FormGroup`; forwarded as `formControlName`. |
|
|
84
|
+
| `controlId` | `string \| undefined` | `undefined` | Explicit control id. Falls back to `id`, then to a generated `eg-form-searchable-multiselect-…` id. |
|
|
85
|
+
| `id` | `string \| undefined` | `undefined` | Alias for an explicit control id (same fallback chain as `controlId`). |
|
|
86
|
+
| `error` | `string \| undefined` | `undefined` | Error text rendered in `<hlm-error>`. |
|
|
87
|
+
| `hint` | `string \| undefined` | `undefined` | Hint text rendered in `<hlm-hint>`. |
|
|
88
|
+
| `placeholder` | `string` | `'Start typing to add…'` | Placeholder forwarded to the inner multiselect. |
|
|
89
|
+
| `disabled` | `boolean` | `false` | Wrapper-level disable, forwarded as `wrapperDisabled` to the inner multiselect. |
|
|
90
|
+
| `required` | `boolean` | `false` | Adds `required` to the inner control and renders a red `*` next to the label. |
|
|
91
|
+
| `options` | `SelectOption[]` | `[]` | `{ label: string; value: string }[]` forwarded to the inner multiselect. |
|
|
92
|
+
| `class` (`userClass`) | `ClassValue` | `''` | Extra classes for the host (merged over `tw:flex tw:flex-col`). |
|
|
93
|
+
| `labelClass` | `string` | `''` | Extra classes for the `<label>` (merged over `tw:mb-1 tw:gap-0`). |
|
|
94
|
+
| `controlClass` | `string` | `''` | Extra classes for the inner multiselect (merged over `tw:w-full`). |
|
|
95
|
+
| `errorClass` | `string` | `''` | Extra classes for `<hlm-error>` (merged over `tw:mt-0`). |
|
|
96
|
+
| `hintClass` | `string` | `''` | Extra classes for `<hlm-hint>` (merged over `tw:mt-0`). |
|
|
34
97
|
|
|
35
|
-
|
|
36
|
-
cd dist/form-searchable-multiselect
|
|
37
|
-
```
|
|
98
|
+
No outputs. Readonly computeds/methods (usable via template ref): `effectiveId()`, `errorId()` (`<id>-error`), `hintId()` (`<id>-hint`), `describedBy(): string | null` (returns the error id when the control is invalid + dirty/touched, else the hint id, else `null`).
|
|
38
99
|
|
|
39
|
-
|
|
40
|
-
```bash
|
|
41
|
-
npm publish
|
|
42
|
-
```
|
|
100
|
+
`SelectOption` is `export interface SelectOption { label: string; value: string }` from `@egose/shadcn-theme-ng/searchable-multiselect`.
|
|
43
101
|
|
|
44
|
-
##
|
|
102
|
+
## Examples
|
|
45
103
|
|
|
46
|
-
|
|
104
|
+
### 1. Basic reactive form
|
|
47
105
|
|
|
48
|
-
```
|
|
49
|
-
|
|
106
|
+
```ts
|
|
107
|
+
import { Component } from '@angular/core';
|
|
108
|
+
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
|
109
|
+
import { EgFormSearchableMultiselect } from '@egose/shadcn-theme-ng/form-searchable-multiselect';
|
|
110
|
+
|
|
111
|
+
@Component({
|
|
112
|
+
standalone: true,
|
|
113
|
+
imports: [ReactiveFormsModule, EgFormSearchableMultiselect],
|
|
114
|
+
template: `
|
|
115
|
+
<form [formGroup]="form" (ngSubmit)="save()">
|
|
116
|
+
<eg-form-searchable-multiselect controlName="tags" label="Tags" [options]="tagOptions" />
|
|
117
|
+
<button type="submit">Save</button>
|
|
118
|
+
</form>
|
|
119
|
+
`,
|
|
120
|
+
})
|
|
121
|
+
export class BasicExample {
|
|
122
|
+
readonly form = new FormGroup({
|
|
123
|
+
tags: new FormControl<string[]>([], { nonNullable: true }),
|
|
124
|
+
});
|
|
125
|
+
readonly tagOptions = [
|
|
126
|
+
{ value: 'angular', label: 'Angular' },
|
|
127
|
+
{ value: 'react', label: 'React' },
|
|
128
|
+
{ value: 'vue', label: 'Vue' },
|
|
129
|
+
];
|
|
130
|
+
|
|
131
|
+
save() {
|
|
132
|
+
console.log(this.form.value.tags); // e.g. ['angular', 'vue']
|
|
133
|
+
}
|
|
134
|
+
}
|
|
50
135
|
```
|
|
51
136
|
|
|
52
|
-
|
|
137
|
+
### 2. Required field with validation error
|
|
53
138
|
|
|
54
|
-
|
|
139
|
+
The `error` string is always rendered when provided; `aria-describedby` flips from the hint id to the error id once the control is invalid and dirty/touched.
|
|
55
140
|
|
|
56
|
-
```
|
|
57
|
-
|
|
141
|
+
```ts
|
|
142
|
+
import { Component } from '@angular/core';
|
|
143
|
+
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
|
144
|
+
import { EgFormSearchableMultiselect } from '@egose/shadcn-theme-ng/form-searchable-multiselect';
|
|
145
|
+
|
|
146
|
+
@Component({
|
|
147
|
+
standalone: true,
|
|
148
|
+
imports: [ReactiveFormsModule, EgFormSearchableMultiselect],
|
|
149
|
+
template: `
|
|
150
|
+
<form [formGroup]="form">
|
|
151
|
+
<eg-form-searchable-multiselect
|
|
152
|
+
controlName="skills"
|
|
153
|
+
label="Skills"
|
|
154
|
+
[options]="skillOptions"
|
|
155
|
+
error="Pick at least one skill"
|
|
156
|
+
hint="Choose all that apply"
|
|
157
|
+
required
|
|
158
|
+
/>
|
|
159
|
+
</form>
|
|
160
|
+
`,
|
|
161
|
+
})
|
|
162
|
+
export class RequiredExample {
|
|
163
|
+
readonly form = new FormGroup({
|
|
164
|
+
skills: new FormControl<string[]>([], { nonNullable: true, validators: Validators.required }),
|
|
165
|
+
});
|
|
166
|
+
readonly skillOptions = [
|
|
167
|
+
{ value: 'ts', label: 'TypeScript' },
|
|
168
|
+
{ value: 'a11y', label: 'Accessibility' },
|
|
169
|
+
{ value: 'testing', label: 'Testing' },
|
|
170
|
+
];
|
|
171
|
+
}
|
|
58
172
|
```
|
|
59
173
|
|
|
60
|
-
|
|
174
|
+
### 3. Disabled states (wrapper input vs reactive disable)
|
|
175
|
+
|
|
176
|
+
```ts
|
|
177
|
+
import { Component, signal } from '@angular/core';
|
|
178
|
+
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
|
179
|
+
import { EgFormSearchableMultiselect } from '@egose/shadcn-theme-ng/form-searchable-multiselect';
|
|
180
|
+
|
|
181
|
+
@Component({
|
|
182
|
+
standalone: true,
|
|
183
|
+
imports: [ReactiveFormsModule, EgFormSearchableMultiselect],
|
|
184
|
+
template: `
|
|
185
|
+
<form [formGroup]="form">
|
|
186
|
+
<!-- static wrapper disable -->
|
|
187
|
+
<eg-form-searchable-multiselect
|
|
188
|
+
controlName="tags"
|
|
189
|
+
label="Tags (locked)"
|
|
190
|
+
[options]="tagOptions"
|
|
191
|
+
[disabled]="true"
|
|
192
|
+
/>
|
|
193
|
+
<!-- reactive disable driven by a signal -->
|
|
194
|
+
<eg-form-searchable-multiselect
|
|
195
|
+
controlName="extras"
|
|
196
|
+
label="Extras"
|
|
197
|
+
[options]="tagOptions"
|
|
198
|
+
[disabled]="locked()"
|
|
199
|
+
/>
|
|
200
|
+
<button type="button" (click)="toggle()">Toggle extras</button>
|
|
201
|
+
</form>
|
|
202
|
+
`,
|
|
203
|
+
})
|
|
204
|
+
export class DisabledExample {
|
|
205
|
+
readonly form = new FormGroup({
|
|
206
|
+
tags: new FormControl<string[]>(['angular'], { nonNullable: true }),
|
|
207
|
+
extras: new FormControl<string[]>([], { nonNullable: true }),
|
|
208
|
+
});
|
|
209
|
+
readonly tagOptions = [{ value: 'angular', label: 'Angular' }];
|
|
210
|
+
readonly locked = signal(true);
|
|
211
|
+
|
|
212
|
+
toggle() {
|
|
213
|
+
this.locked.update((v) => !v);
|
|
214
|
+
}
|
|
215
|
+
|
|
216
|
+
disableViaFormApi() {
|
|
217
|
+
this.form.controls.extras.disable(); // also disables the trigger (inner CVA)
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### 4. Custom ids, placeholder, and styling hooks
|
|
223
|
+
|
|
224
|
+
```ts
|
|
225
|
+
import { Component } from '@angular/core';
|
|
226
|
+
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
|
227
|
+
import { EgFormSearchableMultiselect } from '@egose/shadcn-theme-ng/form-searchable-multiselect';
|
|
228
|
+
|
|
229
|
+
@Component({
|
|
230
|
+
standalone: true,
|
|
231
|
+
imports: [ReactiveFormsModule, EgFormSearchableMultiselect],
|
|
232
|
+
template: `
|
|
233
|
+
<form [formGroup]="form">
|
|
234
|
+
<eg-form-searchable-multiselect
|
|
235
|
+
controlName="frameworks"
|
|
236
|
+
controlId="framework-picker"
|
|
237
|
+
label="Frameworks"
|
|
238
|
+
placeholder="Search frameworks…"
|
|
239
|
+
hint="Type to filter, Enter to add"
|
|
240
|
+
[options]="frameworks"
|
|
241
|
+
class="tw:max-w-md"
|
|
242
|
+
labelClass="tw:font-semibold"
|
|
243
|
+
controlClass="tw:min-h-11"
|
|
244
|
+
/>
|
|
245
|
+
</form>
|
|
246
|
+
`,
|
|
247
|
+
})
|
|
248
|
+
export class StyledExample {
|
|
249
|
+
readonly form = new FormGroup({
|
|
250
|
+
frameworks: new FormControl<string[]>([], { nonNullable: true }),
|
|
251
|
+
});
|
|
252
|
+
readonly frameworks = [
|
|
253
|
+
{ value: 'angular', label: 'Angular' },
|
|
254
|
+
{ value: 'svelte', label: 'Svelte' },
|
|
255
|
+
{ value: 'solid', label: 'Solid' },
|
|
256
|
+
];
|
|
257
|
+
}
|
|
258
|
+
```
|
|
259
|
+
|
|
260
|
+
### 5. Async options (server-driven list)
|
|
261
|
+
|
|
262
|
+
```ts
|
|
263
|
+
import { Component, inject, resource } from '@angular/core';
|
|
264
|
+
import { HttpClient } from '@angular/common/http';
|
|
265
|
+
import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
|
|
266
|
+
import { EgFormSearchableMultiselect } from '@egose/shadcn-theme-ng/form-searchable-multiselect';
|
|
267
|
+
|
|
268
|
+
@Component({
|
|
269
|
+
standalone: true,
|
|
270
|
+
imports: [ReactiveFormsModule, EgFormSearchableMultiselect],
|
|
271
|
+
template: `
|
|
272
|
+
<form [formGroup]="form">
|
|
273
|
+
<eg-form-searchable-multiselect
|
|
274
|
+
controlName="projects"
|
|
275
|
+
label="Projects"
|
|
276
|
+
[options]="projectOptions.value() ?? []"
|
|
277
|
+
[placeholder]="projectOptions.isLoading() ? 'Loading projects…' : 'Start typing to add…'"
|
|
278
|
+
[disabled]="projectOptions.isLoading()"
|
|
279
|
+
hint="Options load from the server"
|
|
280
|
+
/>
|
|
281
|
+
</form>
|
|
282
|
+
`,
|
|
283
|
+
})
|
|
284
|
+
export class AsyncExample {
|
|
285
|
+
private readonly http = inject(HttpClient);
|
|
286
|
+
readonly form = new FormGroup({
|
|
287
|
+
projects: new FormControl<string[]>([], { nonNullable: true }),
|
|
288
|
+
});
|
|
289
|
+
readonly projectOptions = resource({
|
|
290
|
+
loader: () => this.http.get<{ label: string; value: string }[]>('/api/projects').toPromise(),
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
```
|
|
294
|
+
|
|
295
|
+
### 6. Programmatic control (preselect, reset, submit gating)
|
|
296
|
+
|
|
297
|
+
```ts
|
|
298
|
+
import { Component } from '@angular/core';
|
|
299
|
+
import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
|
|
300
|
+
import { EgFormSearchableMultiselect } from '@egose/shadcn-theme-ng/form-searchable-multiselect';
|
|
301
|
+
|
|
302
|
+
@Component({
|
|
303
|
+
standalone: true,
|
|
304
|
+
imports: [ReactiveFormsModule, EgFormSearchableMultiselect],
|
|
305
|
+
template: `
|
|
306
|
+
<form [formGroup]="form" (ngSubmit)="submit()">
|
|
307
|
+
<eg-form-searchable-multiselect
|
|
308
|
+
#tagsField
|
|
309
|
+
controlName="tags"
|
|
310
|
+
label="Tags"
|
|
311
|
+
[options]="tagOptions"
|
|
312
|
+
error="Pick at least one tag"
|
|
313
|
+
/>
|
|
314
|
+
<div class="tw:flex tw:gap-2">
|
|
315
|
+
<button type="button" (click)="preselect()">Select all</button>
|
|
316
|
+
<button type="button" (click)="form.controls.tags.reset()">Clear</button>
|
|
317
|
+
<button type="submit" [disabled]="form.invalid">Submit ({{ form.value.tags?.length ?? 0 }})</button>
|
|
318
|
+
</div>
|
|
319
|
+
<p>Control id: {{ tagsField.effectiveId() }}</p>
|
|
320
|
+
</form>
|
|
321
|
+
`,
|
|
322
|
+
})
|
|
323
|
+
export class ProgrammaticExample {
|
|
324
|
+
readonly form = new FormGroup({
|
|
325
|
+
tags: new FormControl<string[]>([], { nonNullable: true, validators: Validators.required }),
|
|
326
|
+
});
|
|
327
|
+
readonly tagOptions = [
|
|
328
|
+
{ value: 'angular', label: 'Angular' },
|
|
329
|
+
{ value: 'react', label: 'React' },
|
|
330
|
+
];
|
|
331
|
+
|
|
332
|
+
preselect() {
|
|
333
|
+
this.form.controls.tags.setValue(this.tagOptions.map((o) => o.value));
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
submit() {
|
|
337
|
+
if (this.form.invalid) {
|
|
338
|
+
this.form.markAllAsTouched();
|
|
339
|
+
return;
|
|
340
|
+
}
|
|
341
|
+
console.log(this.form.getRawValue());
|
|
342
|
+
}
|
|
343
|
+
}
|
|
344
|
+
```
|
|
345
|
+
|
|
346
|
+
## Accessibility notes
|
|
347
|
+
|
|
348
|
+
- The `<label hlmLabel [for]="effectiveId()">` is bound to the inner multiselect trigger id, so clicking the label focuses/opens the control.
|
|
349
|
+
- `aria-describedby` points at the error element only when the control is invalid and dirty/touched; otherwise it points at the hint element (or is absent). Keep `error`/`hint` text concise and unique per field.
|
|
350
|
+
- The inner `eg-searchable-multiselect` renders a real `<button>` trigger plus checkbox options; keyboard users open with Enter/Space and pick with the checkbox semantics. The `required` input adds a visual `*` — also add `Validators.required` so assistive tech gets the invalid state.
|
|
351
|
+
- `ariaLabel` on the inner control falls back to the visible label; pass an explicit `controlId` when several multiselects share a page to keep ids stable for tests.
|
|
352
|
+
|
|
353
|
+
## Theming / CSS variables
|
|
354
|
+
|
|
355
|
+
No component-specific CSS variables; styling flows through the shared theme tokens (`--radius`, `--popover`, `--ring`, …) consumed by the inner popover/checkbox classes. Use `class` / `labelClass` / `controlClass` / `errorClass` / `hintClass` to append utilities.
|
|
61
356
|
|
|
62
|
-
##
|
|
357
|
+
## Related subpaths
|
|
63
358
|
|
|
64
|
-
|
|
359
|
+
- `@egose/shadcn-theme-ng/searchable-multiselect` — the underlying `EgSearchableMultiselect` + `SelectOption` (use it directly for template-driven or non-form usage).
|
|
360
|
+
- `@egose/shadcn-theme-ng/form-field-simple` — `EgFormField` layout used internally.
|
|
361
|
+
- `@egose/shadcn-theme-ng/form-field` — `HlmError`, `HlmHint`, `HlmFormIdGenerator`.
|
|
362
|
+
- `@egose/shadcn-theme-ng/label` — `HlmLabel`.
|
|
@@ -1,14 +1,16 @@
|
|
|
1
1
|
import * as i0 from '@angular/core';
|
|
2
|
-
import { input, computed, Component } from '@angular/core';
|
|
2
|
+
import { inject, input, computed, Component } from '@angular/core';
|
|
3
3
|
import * as i1 from '@angular/forms';
|
|
4
|
-
import {
|
|
4
|
+
import { FormGroupDirective, ReactiveFormsModule, ControlContainer } from '@angular/forms';
|
|
5
5
|
import { HlmLabel } from '@egose/shadcn-theme-ng-tw/label';
|
|
6
6
|
import { EgFormField } from '@egose/shadcn-theme-ng-tw/form-field-simple';
|
|
7
|
-
import { HlmError, HlmHint } from '@egose/shadcn-theme-ng-tw/form-field';
|
|
7
|
+
import { HlmFormIdGenerator, HlmError, HlmHint } from '@egose/shadcn-theme-ng-tw/form-field';
|
|
8
8
|
import { hlm } from '@egose/shadcn-theme-ng-tw/utils';
|
|
9
9
|
import { EgSearchableMultiselect } from '@egose/shadcn-theme-ng-tw/searchable-multiselect';
|
|
10
10
|
|
|
11
11
|
class EgFormSearchableMultiselect {
|
|
12
|
+
formGroupDirective = inject(FormGroupDirective);
|
|
13
|
+
generatedId = inject(HlmFormIdGenerator).generate('eg-form-searchable-multiselect');
|
|
12
14
|
// Form field inputs
|
|
13
15
|
label = input(undefined, /* @ts-ignore */
|
|
14
16
|
...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
|
|
@@ -21,7 +23,7 @@ class EgFormSearchableMultiselect {
|
|
|
21
23
|
hint = input(undefined, /* @ts-ignore */
|
|
22
24
|
...(ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []));
|
|
23
25
|
// HTML/control attributes
|
|
24
|
-
id = input(
|
|
26
|
+
id = input(undefined, /* @ts-ignore */
|
|
25
27
|
...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
|
|
26
28
|
placeholder = input('Start typing to add…', /* @ts-ignore */
|
|
27
29
|
...(ngDevMode ? [{ debugName: "placeholder" }] : /* istanbul ignore next */ []));
|
|
@@ -29,6 +31,20 @@ class EgFormSearchableMultiselect {
|
|
|
29
31
|
...(ngDevMode ? [{ debugName: "disabled" }] : /* istanbul ignore next */ []));
|
|
30
32
|
required = input(false, /* @ts-ignore */
|
|
31
33
|
...(ngDevMode ? [{ debugName: "required" }] : /* istanbul ignore next */ []));
|
|
34
|
+
effectiveId = computed(() => this.controlId() || this.id() || this.generatedId, /* @ts-ignore */
|
|
35
|
+
...(ngDevMode ? [{ debugName: "effectiveId" }] : /* istanbul ignore next */ []));
|
|
36
|
+
errorId = computed(() => `${this.effectiveId()}-error`, /* @ts-ignore */
|
|
37
|
+
...(ngDevMode ? [{ debugName: "errorId" }] : /* istanbul ignore next */ []));
|
|
38
|
+
hintId = computed(() => `${this.effectiveId()}-hint`, /* @ts-ignore */
|
|
39
|
+
...(ngDevMode ? [{ debugName: "hintId" }] : /* istanbul ignore next */ []));
|
|
40
|
+
describedBy() {
|
|
41
|
+
const control = this.formGroupDirective.form.get(this.controlName());
|
|
42
|
+
return this.error() && control?.invalid && (control.dirty || control.touched)
|
|
43
|
+
? this.errorId()
|
|
44
|
+
: this.hint()
|
|
45
|
+
? this.hintId()
|
|
46
|
+
: null;
|
|
47
|
+
}
|
|
32
48
|
// Multiselect-specific
|
|
33
49
|
options = input([], /* @ts-ignore */
|
|
34
50
|
...(ngDevMode ? [{ debugName: "options" }] : /* istanbul ignore next */ [])); // [{ label, value }]
|
|
@@ -53,10 +69,9 @@ class EgFormSearchableMultiselect {
|
|
|
53
69
|
...(ngDevMode ? [{ debugName: "$errorClass" }] : /* istanbul ignore next */ []));
|
|
54
70
|
$hintClass = computed(() => hlm('tw:mt-0', this.hintClass()), /* @ts-ignore */
|
|
55
71
|
...(ngDevMode ? [{ debugName: "$hintClass" }] : /* istanbul ignore next */ []));
|
|
56
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
57
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.
|
|
72
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgFormSearchableMultiselect, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
73
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgFormSearchableMultiselect, isStandalone: true, selector: "eg-form-searchable-multiselect", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, controlId: { classPropertyName: "controlId", publicName: "controlId", isSignal: true, isRequired: false, transformFunction: null }, controlName: { classPropertyName: "controlName", publicName: "controlName", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, options: { classPropertyName: "options", publicName: "options", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, labelClass: { classPropertyName: "labelClass", publicName: "labelClass", isSignal: true, isRequired: false, transformFunction: null }, controlClass: { classPropertyName: "controlClass", publicName: "controlClass", isSignal: true, isRequired: false, transformFunction: null }, errorClass: { classPropertyName: "errorClass", publicName: "errorClass", isSignal: true, isRequired: false, transformFunction: null }, hintClass: { classPropertyName: "hintClass", publicName: "hintClass", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "tw:w-full" }, providers: [{ provide: ControlContainer, useExisting: FormGroupDirective }], ngImport: i0, template: `
|
|
58
74
|
@let lbl = label();
|
|
59
|
-
@let cid = controlId();
|
|
60
75
|
@let cnm = controlName();
|
|
61
76
|
@let err = error();
|
|
62
77
|
@let hnt = hint();
|
|
@@ -64,37 +79,40 @@ class EgFormSearchableMultiselect {
|
|
|
64
79
|
|
|
65
80
|
<eg-form-field>
|
|
66
81
|
@if (lbl) {
|
|
67
|
-
<
|
|
82
|
+
<label hlmLabel [for]="effectiveId()" [class]="$labelClass()">
|
|
68
83
|
{{ lbl }}
|
|
69
84
|
@if (rqrd) {
|
|
70
85
|
<span class="tw:text-red-500">*</span>
|
|
71
86
|
}
|
|
72
|
-
</
|
|
87
|
+
</label>
|
|
73
88
|
}
|
|
74
89
|
|
|
75
90
|
<eg-searchable-multiselect
|
|
76
|
-
[id]="
|
|
91
|
+
[id]="effectiveId()"
|
|
77
92
|
[options]="options()"
|
|
78
93
|
[placeholder]="placeholder()"
|
|
79
94
|
[formControlName]="cnm"
|
|
80
95
|
[required]="rqrd"
|
|
96
|
+
[wrapperDisabled]="disabled()"
|
|
97
|
+
[ariaLabel]="lbl"
|
|
98
|
+
[ariaDescribedby]="describedBy()"
|
|
81
99
|
[class]="$controlClass()"
|
|
82
100
|
></eg-searchable-multiselect>
|
|
83
101
|
|
|
84
102
|
@if (err) {
|
|
85
|
-
<hlm-error [class]="$errorClass()">
|
|
103
|
+
<hlm-error [id]="errorId()" [class]="$errorClass()">
|
|
86
104
|
{{ err }}
|
|
87
105
|
</hlm-error>
|
|
88
106
|
}
|
|
89
107
|
@if (hnt) {
|
|
90
|
-
<hlm-hint [class]="$hintClass()">
|
|
108
|
+
<hlm-hint [id]="hintId()" [class]="$hintClass()">
|
|
91
109
|
{{ hnt }}
|
|
92
110
|
</hlm-hint>
|
|
93
111
|
}
|
|
94
112
|
</eg-form-field>
|
|
95
|
-
`, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: EgFormField, selector: "eg-form-field", inputs: ["class"] }, { kind: "directive", type: HlmError, selector: "hlm-error", inputs: ["class"] }, { kind: "directive", type: HlmHint, selector: "hlm-hint", inputs: ["class"] }, { kind: "directive", type: HlmLabel, selector: "[hlmLabel]", inputs: ["class"] }, { kind: "component", type: EgSearchableMultiselect, selector: "eg-searchable-multiselect", inputs: ["options", "placeholder", "class", "value"], outputs: ["valueChange"] }] });
|
|
113
|
+
`, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: EgFormField, selector: "eg-form-field", inputs: ["class"] }, { kind: "directive", type: HlmError, selector: "hlm-error", inputs: ["class"] }, { kind: "directive", type: HlmHint, selector: "hlm-hint", inputs: ["class"] }, { kind: "directive", type: HlmLabel, selector: "[hlmLabel]", inputs: ["class"] }, { kind: "component", type: EgSearchableMultiselect, selector: "eg-searchable-multiselect", inputs: ["options", "placeholder", "id", "disabled", "wrapperDisabled", "ariaLabel", "ariaDescribedby", "class", "value"], outputs: ["valueChange"] }] });
|
|
96
114
|
}
|
|
97
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
115
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgFormSearchableMultiselect, decorators: [{
|
|
98
116
|
type: Component,
|
|
99
117
|
args: [{
|
|
100
118
|
selector: 'eg-form-searchable-multiselect',
|
|
@@ -106,7 +124,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
106
124
|
providers: [{ provide: ControlContainer, useExisting: FormGroupDirective }],
|
|
107
125
|
template: `
|
|
108
126
|
@let lbl = label();
|
|
109
|
-
@let cid = controlId();
|
|
110
127
|
@let cnm = controlName();
|
|
111
128
|
@let err = error();
|
|
112
129
|
@let hnt = hint();
|
|
@@ -114,30 +131,33 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
114
131
|
|
|
115
132
|
<eg-form-field>
|
|
116
133
|
@if (lbl) {
|
|
117
|
-
<
|
|
134
|
+
<label hlmLabel [for]="effectiveId()" [class]="$labelClass()">
|
|
118
135
|
{{ lbl }}
|
|
119
136
|
@if (rqrd) {
|
|
120
137
|
<span class="tw:text-red-500">*</span>
|
|
121
138
|
}
|
|
122
|
-
</
|
|
139
|
+
</label>
|
|
123
140
|
}
|
|
124
141
|
|
|
125
142
|
<eg-searchable-multiselect
|
|
126
|
-
[id]="
|
|
143
|
+
[id]="effectiveId()"
|
|
127
144
|
[options]="options()"
|
|
128
145
|
[placeholder]="placeholder()"
|
|
129
146
|
[formControlName]="cnm"
|
|
130
147
|
[required]="rqrd"
|
|
148
|
+
[wrapperDisabled]="disabled()"
|
|
149
|
+
[ariaLabel]="lbl"
|
|
150
|
+
[ariaDescribedby]="describedBy()"
|
|
131
151
|
[class]="$controlClass()"
|
|
132
152
|
></eg-searchable-multiselect>
|
|
133
153
|
|
|
134
154
|
@if (err) {
|
|
135
|
-
<hlm-error [class]="$errorClass()">
|
|
155
|
+
<hlm-error [id]="errorId()" [class]="$errorClass()">
|
|
136
156
|
{{ err }}
|
|
137
157
|
</hlm-error>
|
|
138
158
|
}
|
|
139
159
|
@if (hnt) {
|
|
140
|
-
<hlm-hint [class]="$hintClass()">
|
|
160
|
+
<hlm-hint [id]="hintId()" [class]="$hintClass()">
|
|
141
161
|
{{ hnt }}
|
|
142
162
|
</hlm-hint>
|
|
143
163
|
}
|
|
@@ -155,4 +175,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
155
175
|
*/
|
|
156
176
|
|
|
157
177
|
export { EgFormSearchableMultiselect };
|
|
158
|
-
|
|
178
|
+
|
|
@@ -1,17 +1,23 @@
|
|
|
1
1
|
import * as _angular_core from '@angular/core';
|
|
2
2
|
import { ClassValue } from 'clsx';
|
|
3
|
-
import { SelectOption } from '@egose/shadcn-theme-ng/searchable-multiselect';
|
|
3
|
+
import { SelectOption } from '@egose/shadcn-theme-ng-tw/searchable-multiselect';
|
|
4
4
|
|
|
5
5
|
declare class EgFormSearchableMultiselect {
|
|
6
|
+
private readonly formGroupDirective;
|
|
7
|
+
private readonly generatedId;
|
|
6
8
|
label: _angular_core.InputSignal<string | undefined>;
|
|
7
9
|
controlId: _angular_core.InputSignal<string | undefined>;
|
|
8
10
|
controlName: _angular_core.InputSignal<string>;
|
|
9
11
|
error: _angular_core.InputSignal<string | undefined>;
|
|
10
12
|
hint: _angular_core.InputSignal<string | undefined>;
|
|
11
|
-
id: _angular_core.InputSignal<string>;
|
|
13
|
+
id: _angular_core.InputSignal<string | undefined>;
|
|
12
14
|
placeholder: _angular_core.InputSignal<string>;
|
|
13
15
|
disabled: _angular_core.InputSignal<boolean>;
|
|
14
16
|
required: _angular_core.InputSignal<boolean>;
|
|
17
|
+
readonly effectiveId: _angular_core.Signal<string>;
|
|
18
|
+
readonly errorId: _angular_core.Signal<string>;
|
|
19
|
+
readonly hintId: _angular_core.Signal<string>;
|
|
20
|
+
describedBy(): string | null;
|
|
15
21
|
options: _angular_core.InputSignal<SelectOption[]>;
|
|
16
22
|
userClass: _angular_core.InputSignal<ClassValue>;
|
|
17
23
|
labelClass: _angular_core.InputSignal<string>;
|