@egose/shadcn-theme-ng 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 (87) 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/calendar/README.md +357 -2
  16. package/card/README.md +331 -5
  17. package/carousel/README.md +333 -5
  18. package/carousel/fesm2022/carousel.mjs +4 -1
  19. package/checkbox/README.md +320 -2
  20. package/collapsible/README.md +332 -5
  21. package/combobox/README.md +507 -5
  22. package/combobox/fesm2022/combobox.mjs +4 -1
  23. package/command/README.md +435 -5
  24. package/confirmation-dialog/README.md +301 -2
  25. package/context-menu/README.md +366 -5
  26. package/date-picker/README.md +465 -2
  27. package/date-picker/fesm2022/date-picker.mjs +2 -2
  28. package/dialog/README.md +448 -2
  29. package/drawer/README.md +395 -5
  30. package/dropdown-menu/README.md +417 -5
  31. package/empty/README.md +329 -5
  32. package/field/README.md +385 -5
  33. package/form-checkbox/README.md +312 -2
  34. package/form-date-picker/README.md +322 -2
  35. package/form-field/README.md +356 -2
  36. package/form-field-simple/README.md +340 -2
  37. package/form-searchable-multiselect/README.md +361 -2
  38. package/form-select/README.md +350 -2
  39. package/form-text-input/README.md +371 -2
  40. package/form-textarea/README.md +347 -2
  41. package/hover-card/README.md +256 -5
  42. package/icon/README.md +239 -2
  43. package/input/README.md +269 -2
  44. package/input-group/README.md +335 -5
  45. package/input-group/fesm2022/input-group.mjs +3 -3
  46. package/input-otp/README.md +375 -5
  47. package/item/README.md +385 -5
  48. package/item/fesm2022/item.mjs +3 -3
  49. package/kbd/README.md +291 -5
  50. package/label/README.md +272 -2
  51. package/layout-simple/README.md +193 -2
  52. package/layout-simple/fesm2022/layout-simple.mjs +472 -236
  53. package/layout-simple/types/layout-simple.d.ts +174 -137
  54. package/menu/README.md +417 -2
  55. package/menubar/README.md +343 -5
  56. package/native-select/README.md +323 -5
  57. package/navigation-menu/README.md +369 -5
  58. package/package.json +1 -1
  59. package/pagination/README.md +388 -5
  60. package/popover/README.md +331 -2
  61. package/progress/README.md +311 -5
  62. package/radio-group/README.md +364 -2
  63. package/radio-group/fesm2022/radio-group.mjs +5 -1
  64. package/radio-group/types/radio-group.d.ts +1 -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 +3 -3
  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 +2 -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/typography/types/typography.d.ts +8 -8
  87. package/utils/README.md +303 -2
@@ -1,3 +1,362 @@
1
- # Form Searchable Multiselect Subpath
1
+ # Form Searchable Multiselect (`@egose/shadcn-theme-ng/form-searchable-multiselect`)
2
2
 
3
- This project ships only as `@egose/shadcn-theme-ng/form-searchable-multiselect` or `@egose/shadcn-theme-ng-tw/form-searchable-multiselect`. See the [package README](../../README.md) for installation, compatibility, Tailwind variant, test, and release guidance. Do not publish this project directory independently.
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
+
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
+
7
+ ## Installation
8
+
9
+ ```bash
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 (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:
18
+
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';
23
+ ```
24
+
25
+ ## Imports
26
+
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.
28
+
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>
72
+ ```
73
+
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`).
75
+
76
+ ## API reference
77
+
78
+ ### `eg-form-searchable-multiselect` — `EgFormSearchableMultiselect`
79
+
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`). |
97
+
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`).
99
+
100
+ `SelectOption` is `export interface SelectOption { label: string; value: string }` from `@egose/shadcn-theme-ng/searchable-multiselect`.
101
+
102
+ ## Examples
103
+
104
+ ### 1. Basic reactive form
105
+
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
+ }
135
+ ```
136
+
137
+ ### 2. Required field with validation error
138
+
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.
140
+
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
+ }
172
+ ```
173
+
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.
356
+
357
+ ## Related subpaths
358
+
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`.