@egose/shadcn-theme-ng 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 +27 -39
- package/alert/README.md +343 -34
- package/alert/fesm2022/alert.mjs +47 -47
- package/alert-dialog/README.md +471 -5
- package/alert-dialog/fesm2022/alert-dialog.mjs +47 -47
- 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 +53 -71
- package/avatar/README.md +357 -5
- package/avatar/fesm2022/avatar.mjs +32 -32
- package/badge/README.md +289 -34
- package/badge/fesm2022/badge.mjs +57 -57
- package/basic-alert/README.md +324 -34
- package/basic-alert/fesm2022/basic-alert.mjs +6 -18
- package/breadcrumb/README.md +406 -5
- package/breadcrumb/fesm2022/breadcrumb.mjs +33 -39
- package/button/README.md +453 -34
- package/button/fesm2022/button.mjs +147 -219
- 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 +29 -743
- package/card/README.md +331 -5
- package/card/fesm2022/card.mjs +33 -33
- package/carousel/README.md +333 -5
- package/carousel/fesm2022/carousel.mjs +39 -78
- package/checkbox/README.md +291 -34
- package/checkbox/fesm2022/checkbox.mjs +20 -58
- 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 +91 -224
- package/command/README.md +435 -5
- package/command/fesm2022/command.mjs +49 -109
- package/confirmation-dialog/README.md +272 -34
- package/confirmation-dialog/fesm2022/confirmation-dialog.mjs +15 -40
- package/confirmation-dialog/types/confirmation-dialog.d.ts +8 -4
- 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 +64 -267
- package/date-picker/types/date-picker.d.ts +7 -3
- package/dialog/README.md +419 -34
- package/dialog/fesm2022/dialog.mjs +45 -71
- package/drawer/README.md +395 -5
- package/drawer/fesm2022/drawer.mjs +44 -54
- package/dropdown-menu/README.md +417 -5
- package/dropdown-menu/fesm2022/dropdown-menu.mjs +82 -70
- package/dropdown-menu/types/dropdown-menu.d.ts +4 -0
- package/empty/README.md +329 -5
- package/empty/fesm2022/empty.mjs +30 -30
- package/field/README.md +385 -5
- package/field/fesm2022/field.mjs +56 -72
- package/form-checkbox/README.md +283 -34
- package/form-checkbox/fesm2022/form-checkbox.mjs +25 -85
- 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 +31 -84
- 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 +33 -19
- 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 +31 -91
- package/form-searchable-multiselect/types/form-searchable-multiselect.d.ts +7 -1
- package/form-select/README.md +321 -34
- package/form-select/fesm2022/form-select.mjs +32 -149
- 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 +35 -149
- 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 +34 -109
- 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 +34 -34
- package/input-otp/README.md +375 -5
- package/input-otp/fesm2022/input-otp.mjs +26 -26
- package/item/README.md +385 -5
- package/item/fesm2022/item.mjs +53 -53
- package/kbd/README.md +291 -5
- package/kbd/fesm2022/kbd.mjs +12 -12
- package/label/README.md +243 -34
- package/label/fesm2022/label.mjs +10 -10
- package/label/types/label.d.ts +1 -1
- package/layout-simple/README.md +174 -44
- package/layout-simple/fesm2022/layout-simple.mjs +508 -603
- package/layout-simple/types/layout-simple.d.ts +179 -128
- package/menu/README.md +418 -0
- package/menu/fesm2022/menu.mjs +455 -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 +35 -47
- package/package.json +183 -154
- package/pagination/README.md +388 -5
- package/pagination/fesm2022/pagination.mjs +45 -331
- 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/radio-group/types/radio-group.d.ts +1 -1
- package/resizable/README.md +269 -5
- package/resizable/fesm2022/resizable.mjs +18 -26
- 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 +28 -117
- package/searchable-multiselect/types/searchable-multiselect.d.ts +8 -2
- package/select/README.md +408 -34
- package/select/fesm2022/select.mjs +71 -111
- 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 +41 -53
- package/sidebar/README.md +457 -5
- package/sidebar/fesm2022/sidebar.mjs +125 -227
- package/skeleton/README.md +217 -5
- package/skeleton/fesm2022/skeleton.mjs +9 -9
- package/slider/README.md +273 -5
- package/slider/fesm2022/slider.mjs +10 -80
- package/sonner/README.md +317 -34
- package/sonner/fesm2022/sonner.mjs +13 -13
- package/spinner/README.md +255 -34
- package/spinner/fesm2022/spinner.mjs +11 -11
- package/switch/README.md +281 -34
- package/switch/fesm2022/switch.mjs +13 -15
- package/table/README.md +423 -5
- package/table/fesm2022/table.mjs +41 -41
- package/tabs/README.md +382 -34
- package/tabs/fesm2022/tabs.mjs +35 -111
- package/textarea/README.md +282 -5
- package/textarea/fesm2022/textarea.mjs +9 -9
- package/toggle/README.md +270 -5
- package/toggle/fesm2022/toggle.mjs +13 -13
- 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 +19 -19
- 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/native-select/README.md
CHANGED
|
@@ -1,11 +1,329 @@
|
|
|
1
|
-
#
|
|
1
|
+
# Native Select (`@egose/shadcn-theme-ng/native-select`)
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
Zero-dependency HTML `<select>` with shadcn styling (shadcn/ui `native-select` equivalent). No overlay, no CDK menu, no listbox emulation — a real `<select>` inside a positioning wrapper with a chevron icon, wired as an Angular `ControlValueAccessor` and a spartan-ng `BrnFieldControl`/`BrnLabelable` so it participates in `formControl`/`ngModel`, `hlm-label` association, and field error states.
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
Ships as `@egose/shadcn-theme-ng/native-select` and `@egose/shadcn-theme-ng-tw/native-select` (tw: variant). See the [package README](../../README.md) for installation, peer dependencies, Tailwind setup, and testing. 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
|
|
15
|
+
```
|
|
16
|
+
|
|
17
|
+
Peer dependencies are inherited from the package root (see [package README](../../README.md)). This subpath itself declares `@angular/common`, `@angular/core`, `@spartan-ng/brain` as peers plus a `tslib` runtime dependency; at runtime it also uses `@ng-icons/lucide` (`lucideChevronDown`), `@spartan-ng/brain/field`, and `@spartan-ng/brain/forms` types. No extra install step is needed beyond the package install above.
|
|
18
|
+
|
|
19
|
+
## Imports
|
|
20
|
+
|
|
21
|
+
Real exported symbols (from `src/public-api.ts`):
|
|
22
|
+
|
|
23
|
+
```ts
|
|
24
|
+
import {
|
|
25
|
+
HlmNativeSelect, // component: hlm-native-select (CVA)
|
|
26
|
+
HlmNativeSelectOption, // directive: option[hlmNativeSelectOption]
|
|
27
|
+
HlmNativeSelectOptGroup, // directive: optgroup[hlmNativeSelectOptGroup]
|
|
28
|
+
HLM_NATIVE_SELECT_VALUE_ACCESSOR, // NG_VALUE_ACCESSOR provider const
|
|
29
|
+
HlmNativeSelectImports, // readonly [HlmNativeSelect, HlmNativeSelectOption, HlmNativeSelectOptGroup]
|
|
30
|
+
HlmNativeSelectModule, // NgModule wrapping HlmNativeSelectImports
|
|
31
|
+
} from '@egose/shadcn-theme-ng/native-select';
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
Standalone usage:
|
|
35
|
+
|
|
36
|
+
```ts
|
|
37
|
+
import { Component } from '@angular/core';
|
|
38
|
+
import { HlmNativeSelectImports } from '@egose/shadcn-theme-ng/native-select';
|
|
39
|
+
|
|
40
|
+
@Component({
|
|
41
|
+
selector: 'app-demo',
|
|
42
|
+
standalone: true,
|
|
43
|
+
imports: [...HlmNativeSelectImports],
|
|
44
|
+
template: `
|
|
45
|
+
<hlm-native-select>
|
|
46
|
+
<option hlmNativeSelectOption value="apple">Apple</option>
|
|
47
|
+
</hlm-native-select>
|
|
48
|
+
`,
|
|
49
|
+
})
|
|
50
|
+
export class DemoComponent {}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
NgModule usage:
|
|
54
|
+
|
|
55
|
+
```ts
|
|
56
|
+
import { NgModule } from '@angular/core';
|
|
57
|
+
import { HlmNativeSelectModule } from '@egose/shadcn-theme-ng/native-select';
|
|
58
|
+
|
|
59
|
+
@NgModule({ imports: [HlmNativeSelectModule] })
|
|
60
|
+
export class DemoModule {}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
For the `tw:` build, swap the specifier to `@egose/shadcn-theme-ng-tw/native-select`. Symbol names are identical.
|
|
64
|
+
|
|
65
|
+
## Anatomy / Structure
|
|
66
|
+
|
|
67
|
+
```html
|
|
68
|
+
<hlm-native-select>
|
|
69
|
+
<option hlmNativeSelectOption value="">Choose a fruit…</option>
|
|
70
|
+
<optgroup hlmNativeSelectOptGroup label="Citrus">
|
|
71
|
+
<option hlmNativeSelectOption value="orange">Orange</option>
|
|
72
|
+
<option hlmNativeSelectOption value="lemon">Lemon</option>
|
|
73
|
+
</optgroup>
|
|
74
|
+
<option hlmNativeSelectOption value="apple">Apple</option>
|
|
75
|
+
</hlm-native-select>
|
|
76
|
+
|
|
77
|
+
<!-- sizes + classes -->
|
|
78
|
+
<hlm-native-select size="sm" selectClass="tw:w-48" selectIconClass="tw:text-primary">
|
|
79
|
+
<option hlmNativeSelectOption value="s">Small</option>
|
|
80
|
+
</hlm-native-select>
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Rendered DOM: the host (`data-slot="native-select-wrapper"`, `group/native-select relative w-fit`) projects your options into an inner native `<select data-slot="native-select">` plus an absolutely-positioned `lucideChevronDown` icon (`data-slot="native-select-icon"`). The inner select carries `data-size`, `data-invalid`, `data-dirty`, `data-touched`, and `data-matches-spartan-invalid` attributes for styling hooks.
|
|
84
|
+
|
|
85
|
+
| Class | Selector | Role |
|
|
86
|
+
| ------------------------- | ----------------------------------- | ------------------------------------------------------------------------------------------ |
|
|
87
|
+
| `HlmNativeSelect` | `hlm-native-select` | Wrapper + CVA + chevron |
|
|
88
|
+
| `HlmNativeSelectOption` | `option[hlmNativeSelectOption]` | Styled `<option>` (`bg-[Canvas] text-[CanvasText]` so dropdown lists stay native-readable) |
|
|
89
|
+
| `HlmNativeSelectOptGroup` | `optgroup[hlmNativeSelectOptGroup]` | Styled `<optgroup>` (same Canvas treatment) |
|
|
90
|
+
|
|
91
|
+
## API reference
|
|
92
|
+
|
|
93
|
+
### `HlmNativeSelect` (`hlm-native-select`, implements `ControlValueAccessor`)
|
|
94
|
+
|
|
95
|
+
| Member | Kind | Type / Default | Notes |
|
|
96
|
+
| ------------------------------------------------ | ------------- | --------------------------------------- | ------------------------------------------------------------------------------------------------- |
|
|
97
|
+
| `selectId` | input | `string`, auto `hlm-native-select-${n}` | Applied as the inner `<select>`'s `id`; also exposed as `labelableId` for `hlm-label` association |
|
|
98
|
+
| `selectClass` | input | `ClassValue`, `''` | Merged over the inner select classes |
|
|
99
|
+
| `selectIconClass` | input | `ClassValue`, `''` | Merged over the chevron icon classes |
|
|
100
|
+
| `size` | input | `'sm' \| 'default'`, `'default'` | Reflected as `data-size` on host + select; `sm` → `h-8` instead of `h-9` |
|
|
101
|
+
| `disabled` | input | `boolean`, `false` (boolean coercion) | Initial disabled; reactive forms override via `setDisabledState` into `_disabled` linked signal |
|
|
102
|
+
| `forceInvalid` | input | `boolean`, `false` (boolean coercion) | Forces `data-matches-spartan-invalid` styling |
|
|
103
|
+
| `ariaInvalidOverride` (alias `aria-invalid`) | input | `boolean \| undefined`, `undefined` | Manual `aria-invalid`; defaults to the parent `BrnFieldControl.invalid` state |
|
|
104
|
+
| `valueInput` (alias `value`) | input | `string \| undefined \| null`, `''` | One-way value; mirrored into the `value` linked signal |
|
|
105
|
+
| `value` | linked signal | `string \| undefined \| null` | Current value; bound to the select's `[value]` |
|
|
106
|
+
| `valueChange` | output | `string \| undefined \| null` | Emitted on native `change` alongside the CVA callback |
|
|
107
|
+
| `labelableId` | property | = `selectId` | `BrnLabelable` contract for label association |
|
|
108
|
+
| `writeValue(v)` | CVA method | | Sets `value` |
|
|
109
|
+
| `registerOnChange(fn)` / `registerOnTouched(fn)` | CVA methods | | Stored as `_onChange` / `_onTouched`; invoked on `change` / `change`+`blur` |
|
|
110
|
+
| `setDisabledState(isDisabled)` | CVA method | | Sets `_disabled` |
|
|
111
|
+
|
|
112
|
+
Host directives: `BrnFieldControl` (provides `invalid`/`touched`/`dirty`/`spartanInvalid` read by the template). Providers: `NG_VALUE_ACCESSOR` (`HLM_NATIVE_SELECT_VALUE_ACCESSOR`), `provideIcons({ lucideChevronDown })`, `provideBrnLabelable(HlmNativeSelect)`.
|
|
113
|
+
|
|
114
|
+
### `HlmNativeSelectOption` / `HlmNativeSelectOptGroup`
|
|
115
|
+
|
|
116
|
+
No inputs/outputs/methods. Pure styling directives setting `data-slot` (`native-select-option` / `native-select-optgroup`).
|
|
117
|
+
|
|
118
|
+
## Examples
|
|
119
|
+
|
|
120
|
+
### 1. Basic uncontrolled select
|
|
121
|
+
|
|
122
|
+
```ts
|
|
123
|
+
import { Component } from '@angular/core';
|
|
124
|
+
import { HlmNativeSelectImports } from '@egose/shadcn-theme-ng/native-select';
|
|
125
|
+
|
|
126
|
+
@Component({
|
|
127
|
+
selector: 'app-native-basic',
|
|
128
|
+
standalone: true,
|
|
129
|
+
imports: [...HlmNativeSelectImports],
|
|
130
|
+
template: `
|
|
131
|
+
<hlm-native-select (valueChange)="onChange($event)">
|
|
132
|
+
<option hlmNativeSelectOption value="">Choose a fruit…</option>
|
|
133
|
+
<option hlmNativeSelectOption value="apple">Apple</option>
|
|
134
|
+
<option hlmNativeSelectOption value="banana">Banana</option>
|
|
135
|
+
</hlm-native-select>
|
|
136
|
+
<p class="tw:text-sm tw:text-muted-foreground">Picked: {{ picked ?? 'nothing' }}</p>
|
|
137
|
+
`,
|
|
138
|
+
})
|
|
139
|
+
export class NativeBasicComponent {
|
|
140
|
+
picked: string | null | undefined;
|
|
141
|
+
|
|
142
|
+
onChange(value: string | null | undefined): void {
|
|
143
|
+
this.picked = value;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
11
146
|
```
|
|
147
|
+
|
|
148
|
+
### 2. Sizes and optgroups
|
|
149
|
+
|
|
150
|
+
```ts
|
|
151
|
+
import { Component } from '@angular/core';
|
|
152
|
+
import { HlmNativeSelectImports } from '@egose/shadcn-theme-ng/native-select';
|
|
153
|
+
|
|
154
|
+
@Component({
|
|
155
|
+
selector: 'app-native-groups',
|
|
156
|
+
standalone: true,
|
|
157
|
+
imports: [...HlmNativeSelectImports],
|
|
158
|
+
template: `
|
|
159
|
+
<div class="tw:flex tw:flex-col tw:gap-4 tw:max-w-xs">
|
|
160
|
+
<hlm-native-select size="sm">
|
|
161
|
+
<option hlmNativeSelectOption value="s">Small</option>
|
|
162
|
+
<option hlmNativeSelectOption value="m">Medium</option>
|
|
163
|
+
</hlm-native-select>
|
|
164
|
+
|
|
165
|
+
<hlm-native-select size="default">
|
|
166
|
+
<option hlmNativeSelectOption value="">Choose a citrus…</option>
|
|
167
|
+
<optgroup hlmNativeSelectOptGroup label="Citrus">
|
|
168
|
+
<option hlmNativeSelectOption value="orange">Orange</option>
|
|
169
|
+
<option hlmNativeSelectOption value="lemon">Lemon</option>
|
|
170
|
+
</optgroup>
|
|
171
|
+
<optgroup hlmNativeSelectOptGroup label="Berries">
|
|
172
|
+
<option hlmNativeSelectOption value="strawberry">Strawberry</option>
|
|
173
|
+
<option hlmNativeSelectOption value="blueberry">Blueberry</option>
|
|
174
|
+
</optgroup>
|
|
175
|
+
</hlm-native-select>
|
|
176
|
+
</div>
|
|
177
|
+
`,
|
|
178
|
+
})
|
|
179
|
+
export class NativeGroupsComponent {}
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
### 3. Reactive form + label
|
|
183
|
+
|
|
184
|
+
```ts
|
|
185
|
+
import { Component } from '@angular/core';
|
|
186
|
+
import { ReactiveFormsModule, FormControl, FormGroup, Validators } from '@angular/forms';
|
|
187
|
+
import { HlmNativeSelectImports } from '@egose/shadcn-theme-ng/native-select';
|
|
188
|
+
import { HlmLabelImports } from '@egose/shadcn-theme-ng/label';
|
|
189
|
+
|
|
190
|
+
@Component({
|
|
191
|
+
selector: 'app-native-reactive',
|
|
192
|
+
standalone: true,
|
|
193
|
+
imports: [ReactiveFormsModule, ...HlmNativeSelectImports, ...HlmLabelImports],
|
|
194
|
+
template: `
|
|
195
|
+
<form [formGroup]="form" class="tw:grid tw:gap-1.5 tw:max-w-xs">
|
|
196
|
+
<label hlmLabel for="country">Country</label>
|
|
197
|
+
<hlm-native-select selectId="country" formControlName="country">
|
|
198
|
+
<option hlmNativeSelectOption value="">Select…</option>
|
|
199
|
+
<option hlmNativeSelectOption value="de">Germany</option>
|
|
200
|
+
<option hlmNativeSelectOption value="fr">France</option>
|
|
201
|
+
<option hlmNativeSelectOption value="jp">Japan</option>
|
|
202
|
+
</hlm-native-select>
|
|
203
|
+
@if (form.controls.country.touched && form.controls.country.invalid) {
|
|
204
|
+
<p class="tw:text-sm tw:text-destructive">Country is required.</p>
|
|
205
|
+
}
|
|
206
|
+
<button type="button" (click)="disable()">Toggle disabled</button>
|
|
207
|
+
</form>
|
|
208
|
+
`,
|
|
209
|
+
})
|
|
210
|
+
export class NativeReactiveComponent {
|
|
211
|
+
readonly form = new FormGroup({
|
|
212
|
+
country: new FormControl('', { validators: [Validators.required], nonNullable: true }),
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
disable(): void {
|
|
216
|
+
const c = this.form.controls.country;
|
|
217
|
+
c.disabled ? c.enable() : c.disable();
|
|
218
|
+
}
|
|
219
|
+
}
|
|
220
|
+
```
|
|
221
|
+
|
|
222
|
+
### 4. Template-driven (`ngModel`) + two-way `value`
|
|
223
|
+
|
|
224
|
+
```ts
|
|
225
|
+
import { Component } from '@angular/core';
|
|
226
|
+
import { FormsModule } from '@angular/forms';
|
|
227
|
+
import { HlmNativeSelectImports } from '@egose/shadcn-theme-ng/native-select';
|
|
228
|
+
|
|
229
|
+
@Component({
|
|
230
|
+
selector: 'app-native-ngmodel',
|
|
231
|
+
standalone: true,
|
|
232
|
+
imports: [FormsModule, ...HlmNativeSelectImports],
|
|
233
|
+
template: `
|
|
234
|
+
<hlm-native-select [(ngModel)]="flavor">
|
|
235
|
+
<option hlmNativeSelectOption value="vanilla">Vanilla</option>
|
|
236
|
+
<option hlmNativeSelectOption value="chocolate">Chocolate</option>
|
|
237
|
+
<option hlmNativeSelectOption value="mint">Mint</option>
|
|
238
|
+
</hlm-native-select>
|
|
239
|
+
<p class="tw:text-sm">Flavor: {{ flavor }}</p>
|
|
240
|
+
|
|
241
|
+
<!-- one-way value input + valueChange output (no forms): -->
|
|
242
|
+
<hlm-native-select [value]="flavor" (valueChange)="flavor = $event ?? ''">
|
|
243
|
+
<option hlmNativeSelectOption value="vanilla">Vanilla</option>
|
|
244
|
+
<option hlmNativeSelectOption value="chocolate">Chocolate</option>
|
|
245
|
+
</hlm-native-select>
|
|
246
|
+
`,
|
|
247
|
+
})
|
|
248
|
+
export class NativeNgModelComponent {
|
|
249
|
+
flavor = 'vanilla';
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### 5. Async options, invalid, and disabled states
|
|
254
|
+
|
|
255
|
+
```ts
|
|
256
|
+
import { Component, signal, OnInit } from '@angular/core';
|
|
257
|
+
import { HlmNativeSelectImports } from '@egose/shadcn-theme-ng/native-select';
|
|
258
|
+
|
|
259
|
+
@Component({
|
|
260
|
+
selector: 'app-native-async',
|
|
261
|
+
standalone: true,
|
|
262
|
+
imports: [...HlmNativeSelectImports],
|
|
263
|
+
template: `
|
|
264
|
+
<div class="tw:grid tw:gap-4 tw:max-w-xs">
|
|
265
|
+
<hlm-native-select [value]="selected()" (valueChange)="selected.set($event ?? '')">
|
|
266
|
+
<option hlmNativeSelectOption value="">Loading…</option>
|
|
267
|
+
@for (c of cities(); track c) {
|
|
268
|
+
<option hlmNativeSelectOption [value]="c">{{ c }}</option>
|
|
269
|
+
}
|
|
270
|
+
</hlm-native-select>
|
|
271
|
+
|
|
272
|
+
<hlm-native-select [forceInvalid]="true" value="bad">
|
|
273
|
+
<option hlmNativeSelectOption value="bad">Forced invalid</option>
|
|
274
|
+
<option hlmNativeSelectOption value="good">Good</option>
|
|
275
|
+
</hlm-native-select>
|
|
276
|
+
|
|
277
|
+
<hlm-native-select [disabled]="true" value="locked">
|
|
278
|
+
<option hlmNativeSelectOption value="locked">Disabled</option>
|
|
279
|
+
</hlm-native-select>
|
|
280
|
+
</div>
|
|
281
|
+
`,
|
|
282
|
+
})
|
|
283
|
+
export class NativeAsyncComponent implements OnInit {
|
|
284
|
+
readonly cities = signal<string[]>([]);
|
|
285
|
+
readonly selected = signal('');
|
|
286
|
+
|
|
287
|
+
async ngOnInit(): Promise<void> {
|
|
288
|
+
await new Promise((r) => setTimeout(r, 300));
|
|
289
|
+
this.cities.set(['Berlin', 'Paris', 'Tokyo']);
|
|
290
|
+
}
|
|
291
|
+
}
|
|
292
|
+
```
|
|
293
|
+
|
|
294
|
+
### 6. Custom widths and icon tint
|
|
295
|
+
|
|
296
|
+
```ts
|
|
297
|
+
import { Component } from '@angular/core';
|
|
298
|
+
import { HlmNativeSelectImports } from '@egose/shadcn-theme-ng/native-select';
|
|
299
|
+
|
|
300
|
+
@Component({
|
|
301
|
+
selector: 'app-native-styled',
|
|
302
|
+
standalone: true,
|
|
303
|
+
imports: [...HlmNativeSelectImports],
|
|
304
|
+
template: `
|
|
305
|
+
<hlm-native-select selectClass="tw:w-56" selectIconClass="tw:text-primary" size="sm">
|
|
306
|
+
<option hlmNativeSelectOption value="a">Compact tinted chevron</option>
|
|
307
|
+
<option hlmNativeSelectOption value="b">Option B</option>
|
|
308
|
+
</hlm-native-select>
|
|
309
|
+
`,
|
|
310
|
+
})
|
|
311
|
+
export class NativeStyledComponent {}
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
## Accessibility notes
|
|
315
|
+
|
|
316
|
+
- A native `<select>` gives you listbox semantics, keyboard support (arrows/typeahead), and mobile pickers for free — prefer it over a custom dropdown when the option list is simple.
|
|
317
|
+
- Associate with `<label hlmLabel for="…">` matching `selectId` (or nest contextually); invalid state flows to `aria-invalid` automatically via `BrnFieldControl` unless overridden with `aria-invalid`.
|
|
318
|
+
- Keep the first `<option>` a real placeholder with `value=""` when a choice is required, and validate accordingly.
|
|
319
|
+
- `forceInvalid` is visual + `data-*` styling; still set form errors so screen readers announce the problem.
|
|
320
|
+
|
|
321
|
+
## Theming / CSS variables
|
|
322
|
+
|
|
323
|
+
No theming inputs. The select uses `border-input`, `ring-ring`, `bg-muted`-adjacent tokens with dark-mode (`dark:bg-input/30`) and invalid (`border-destructive ring-destructive`) treatments. Extend via `selectClass` / `selectIconClass` (merged after base classes).
|
|
324
|
+
|
|
325
|
+
## Related subpaths
|
|
326
|
+
|
|
327
|
+
- `@egose/shadcn-theme-ng/select` — custom overlay select for searchable/rich options; use native-select when a plain `<select>` suffices.
|
|
328
|
+
- `@egose/shadcn-theme-ng/label` — label association via `selectId`/`labelableId`.
|
|
329
|
+
- `@egose/shadcn-theme-ng/form-field` — field wrapper with error display that reads the same `BrnFieldControl` state.
|
|
@@ -19,11 +19,11 @@ class HlmNativeSelect {
|
|
|
19
19
|
...(ngDevMode ? [{ debugName: "selectId" }] : /* istanbul ignore next */ []));
|
|
20
20
|
selectClass = input('', /* @ts-ignore */
|
|
21
21
|
...(ngDevMode ? [{ debugName: "selectClass" }] : /* istanbul ignore next */ []));
|
|
22
|
-
_computedSelectClass = computed(() => hlm(
|
|
22
|
+
_computedSelectClass = computed(() => hlm("border-input placeholder:text-muted-foreground selection:bg-primary selection:text-primary-foreground dark:bg-input/30 dark:hover:bg-input/50 focus-visible:border-ring focus-visible:ring-ring/50 data-[matches-spartan-invalid=true]:ring-destructive/20 dark:data-[matches-spartan-invalid=true]:ring-destructive/40 data-[matches-spartan-invalid=true]:border-destructive dark:data-[matches-spartan-invalid=true]:border-destructive/50 h-9 w-full min-w-0 appearance-none rounded-md border bg-transparent py-1 ps-2.5 pe-8 text-sm shadow-xs transition-[color,box-shadow] select-none focus-visible:ring-3 data-[matches-spartan-invalid=true]:ring-3 data-[size=sm]:h-8 outline-none disabled:pointer-events-none disabled:cursor-not-allowed", this.selectClass()), /* @ts-ignore */
|
|
23
23
|
...(ngDevMode ? [{ debugName: "_computedSelectClass" }] : /* istanbul ignore next */ []));
|
|
24
24
|
selectIconClass = input('', /* @ts-ignore */
|
|
25
25
|
...(ngDevMode ? [{ debugName: "selectIconClass" }] : /* istanbul ignore next */ []));
|
|
26
|
-
_computedSelectIconClass = computed(() => hlm(
|
|
26
|
+
_computedSelectIconClass = computed(() => hlm("text-muted-foreground end-2.5 top-1/2 -translate-y-1/2 text-[length:--spacing(4)] pointer-events-none absolute select-none", this.selectIconClass()), /* @ts-ignore */
|
|
27
27
|
...(ngDevMode ? [{ debugName: "_computedSelectIconClass" }] : /* istanbul ignore next */ []));
|
|
28
28
|
size = input('default', /* @ts-ignore */
|
|
29
29
|
...(ngDevMode ? [{ debugName: "size" }] : /* istanbul ignore next */ []));
|
|
@@ -50,7 +50,7 @@ class HlmNativeSelect {
|
|
|
50
50
|
_spartanInvalid = computed(() => this.forceInvalid() || this._fieldControl?.spartanInvalid(), /* @ts-ignore */
|
|
51
51
|
...(ngDevMode ? [{ debugName: "_spartanInvalid" }] : /* istanbul ignore next */ []));
|
|
52
52
|
constructor() {
|
|
53
|
-
classes(() =>
|
|
53
|
+
classes(() => "group/native-select relative w-fit has-[select:disabled]:opacity-50");
|
|
54
54
|
}
|
|
55
55
|
_valueChanged(event) {
|
|
56
56
|
const value = event.target.value;
|
|
@@ -75,8 +75,8 @@ class HlmNativeSelect {
|
|
|
75
75
|
setDisabledState(isDisabled) {
|
|
76
76
|
this._disabled.set(isDisabled);
|
|
77
77
|
}
|
|
78
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
79
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.
|
|
78
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: HlmNativeSelect, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
79
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.1.0", version: "22.1.3", type: HlmNativeSelect, isStandalone: true, selector: "hlm-native-select", inputs: { selectId: { classPropertyName: "selectId", publicName: "selectId", isSignal: true, isRequired: false, transformFunction: null }, selectClass: { classPropertyName: "selectClass", publicName: "selectClass", isSignal: true, isRequired: false, transformFunction: null }, selectIconClass: { classPropertyName: "selectIconClass", publicName: "selectIconClass", isSignal: true, isRequired: false, transformFunction: null }, size: { classPropertyName: "size", publicName: "size", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, forceInvalid: { classPropertyName: "forceInvalid", publicName: "forceInvalid", isSignal: true, isRequired: false, transformFunction: null }, ariaInvalidOverride: { classPropertyName: "ariaInvalidOverride", publicName: "aria-invalid", isSignal: true, isRequired: false, transformFunction: null }, valueInput: { classPropertyName: "valueInput", publicName: "value", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { valueChange: "valueChange" }, host: { attributes: { "data-slot": "native-select-wrapper" }, properties: { "attr.data-size": "size()" } }, providers: [
|
|
80
80
|
HLM_NATIVE_SELECT_VALUE_ACCESSOR,
|
|
81
81
|
provideIcons({ lucideChevronDown }),
|
|
82
82
|
provideBrnLabelable(HlmNativeSelect),
|
|
@@ -107,7 +107,7 @@ class HlmNativeSelect {
|
|
|
107
107
|
/>
|
|
108
108
|
`, isInline: true, dependencies: [{ kind: "component", type: NgIcon, selector: "ng-icon", inputs: ["name", "svg", "size", "strokeWidth", "color"] }], changeDetection: i0.ChangeDetectionStrategy.OnPush });
|
|
109
109
|
}
|
|
110
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
110
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: HlmNativeSelect, decorators: [{
|
|
111
111
|
type: Component,
|
|
112
112
|
args: [{
|
|
113
113
|
selector: 'hlm-native-select',
|
|
@@ -154,12 +154,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
154
154
|
|
|
155
155
|
class HlmNativeSelectOptGroup {
|
|
156
156
|
constructor() {
|
|
157
|
-
classes(() =>
|
|
157
|
+
classes(() => "bg-[Canvas] text-[CanvasText]");
|
|
158
158
|
}
|
|
159
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
160
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.
|
|
159
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: HlmNativeSelectOptGroup, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
160
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.3", type: HlmNativeSelectOptGroup, isStandalone: true, selector: "optgroup[hlmNativeSelectOptGroup]", host: { attributes: { "data-slot": "native-select-optgroup" } }, ngImport: i0 });
|
|
161
161
|
}
|
|
162
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
162
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: HlmNativeSelectOptGroup, decorators: [{
|
|
163
163
|
type: Directive,
|
|
164
164
|
args: [{
|
|
165
165
|
selector: 'optgroup[hlmNativeSelectOptGroup]',
|
|
@@ -169,12 +169,12 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
169
169
|
|
|
170
170
|
class HlmNativeSelectOption {
|
|
171
171
|
constructor() {
|
|
172
|
-
classes(() =>
|
|
172
|
+
classes(() => "bg-[Canvas] text-[CanvasText]");
|
|
173
173
|
}
|
|
174
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
175
|
-
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.
|
|
174
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: HlmNativeSelectOption, deps: [], target: i0.ɵɵFactoryTarget.Directive });
|
|
175
|
+
static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "22.1.3", type: HlmNativeSelectOption, isStandalone: true, selector: "option[hlmNativeSelectOption]", host: { attributes: { "data-slot": "native-select-option" } }, ngImport: i0 });
|
|
176
176
|
}
|
|
177
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
177
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: HlmNativeSelectOption, decorators: [{
|
|
178
178
|
type: Directive,
|
|
179
179
|
args: [{
|
|
180
180
|
selector: 'option[hlmNativeSelectOption]',
|
|
@@ -187,11 +187,11 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
187
187
|
*/
|
|
188
188
|
const HlmNativeSelectImports = [HlmNativeSelect, HlmNativeSelectOption, HlmNativeSelectOptGroup];
|
|
189
189
|
class HlmNativeSelectModule {
|
|
190
|
-
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.
|
|
191
|
-
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.
|
|
192
|
-
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "22.
|
|
190
|
+
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: HlmNativeSelectModule, deps: [], target: i0.ɵɵFactoryTarget.NgModule });
|
|
191
|
+
static ɵmod = i0.ɵɵngDeclareNgModule({ minVersion: "14.0.0", version: "22.1.3", ngImport: i0, type: HlmNativeSelectModule, imports: [HlmNativeSelect, HlmNativeSelectOption, HlmNativeSelectOptGroup], exports: [HlmNativeSelect, HlmNativeSelectOption, HlmNativeSelectOptGroup] });
|
|
192
|
+
static ɵinj = i0.ɵɵngDeclareInjector({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: HlmNativeSelectModule, imports: [HlmNativeSelect] });
|
|
193
193
|
}
|
|
194
|
-
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.
|
|
194
|
+
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: HlmNativeSelectModule, decorators: [{
|
|
195
195
|
type: NgModule,
|
|
196
196
|
args: [{
|
|
197
197
|
imports: [...HlmNativeSelectImports],
|
|
@@ -204,4 +204,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
|
|
|
204
204
|
*/
|
|
205
205
|
|
|
206
206
|
export { HLM_NATIVE_SELECT_VALUE_ACCESSOR, HlmNativeSelect, HlmNativeSelectImports, HlmNativeSelectModule, HlmNativeSelectOptGroup, HlmNativeSelectOption };
|
|
207
|
-
|
|
207
|
+
|