@egose/shadcn-theme-ng-tw 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/README.md +1 -1
  2. package/accordion/README.md +405 -2
  3. package/alert/README.md +372 -2
  4. package/alert-dialog/README.md +471 -5
  5. package/aspect-ratio/README.md +272 -5
  6. package/autocomplete/README.md +502 -2
  7. package/avatar/README.md +357 -5
  8. package/badge/README.md +318 -2
  9. package/basic-alert/README.md +353 -2
  10. package/breadcrumb/README.md +406 -5
  11. package/button/README.md +482 -2
  12. package/button/fesm2022/button.mjs +85 -107
  13. package/button/types/button.d.ts +5 -8
  14. package/button-group/README.md +318 -5
  15. package/button-group/fesm2022/button-group.mjs +1 -1
  16. package/calendar/README.md +357 -2
  17. package/card/README.md +331 -5
  18. package/carousel/README.md +333 -5
  19. package/carousel/fesm2022/carousel.mjs +4 -1
  20. package/checkbox/README.md +320 -2
  21. package/collapsible/README.md +332 -5
  22. package/combobox/README.md +507 -5
  23. package/combobox/fesm2022/combobox.mjs +4 -1
  24. package/command/README.md +435 -5
  25. package/confirmation-dialog/README.md +301 -2
  26. package/context-menu/README.md +366 -5
  27. package/date-picker/README.md +465 -2
  28. package/date-picker/fesm2022/date-picker.mjs +2 -2
  29. package/dialog/README.md +448 -2
  30. package/drawer/README.md +395 -5
  31. package/dropdown-menu/README.md +417 -5
  32. package/empty/README.md +329 -5
  33. package/field/README.md +385 -5
  34. package/form-checkbox/README.md +312 -2
  35. package/form-date-picker/README.md +322 -2
  36. package/form-field/README.md +356 -2
  37. package/form-field-simple/README.md +340 -2
  38. package/form-searchable-multiselect/README.md +361 -2
  39. package/form-select/README.md +350 -2
  40. package/form-text-input/README.md +371 -2
  41. package/form-textarea/README.md +347 -2
  42. package/hover-card/README.md +256 -5
  43. package/icon/README.md +239 -2
  44. package/input/README.md +269 -2
  45. package/input-group/README.md +335 -5
  46. package/input-group/fesm2022/input-group.mjs +3 -3
  47. package/input-otp/README.md +375 -5
  48. package/item/README.md +385 -5
  49. package/item/fesm2022/item.mjs +3 -3
  50. package/kbd/README.md +291 -5
  51. package/label/README.md +272 -2
  52. package/layout-simple/README.md +193 -2
  53. package/layout-simple/fesm2022/layout-simple.mjs +877 -409
  54. package/layout-simple/types/layout-simple.d.ts +174 -137
  55. package/menu/README.md +417 -2
  56. package/menubar/README.md +343 -5
  57. package/native-select/README.md +323 -5
  58. package/navigation-menu/README.md +369 -5
  59. package/package.json +1 -1
  60. package/pagination/README.md +388 -5
  61. package/popover/README.md +331 -2
  62. package/progress/README.md +311 -5
  63. package/radio-group/README.md +364 -2
  64. package/radio-group/fesm2022/radio-group.mjs +5 -1
  65. package/resizable/README.md +269 -5
  66. package/scroll-area/README.md +233 -5
  67. package/searchable-multiselect/README.md +323 -2
  68. package/select/README.md +437 -2
  69. package/separator/README.md +222 -2
  70. package/sheet/README.md +311 -2
  71. package/sidebar/README.md +457 -5
  72. package/skeleton/README.md +217 -5
  73. package/slider/README.md +273 -5
  74. package/slider/fesm2022/slider.mjs +17 -13
  75. package/sonner/README.md +346 -2
  76. package/spinner/README.md +284 -2
  77. package/switch/README.md +310 -2
  78. package/table/README.md +423 -5
  79. package/tabs/README.md +411 -2
  80. package/tabs/fesm2022/tabs.mjs +12 -2
  81. package/textarea/README.md +282 -5
  82. package/toggle/README.md +270 -5
  83. package/toggle-group/README.md +340 -5
  84. package/tooltip/README.md +269 -2
  85. package/typography/README.md +271 -5
  86. package/utils/README.md +303 -2
@@ -1,11 +1,329 @@
1
- # NativeSelect
1
+ # Native Select (`@egose/shadcn-theme-ng/native-select`)
2
2
 
3
- This project was generated using [Angular CLI](https://github.com/angular/angular-cli).
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
- ## Building
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
- To build the library, run:
7
+ ## Installation
8
8
 
9
9
  ```bash
10
- ng build native-select
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.