@egose/shadcn-theme-ng 0.1.0 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (162) hide show
  1. package/README.md +2 -2
  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/autocomplete/fesm2022/autocomplete.mjs +1 -1
  8. package/avatar/README.md +357 -5
  9. package/badge/README.md +318 -2
  10. package/basic-alert/README.md +353 -2
  11. package/breadcrumb/README.md +406 -5
  12. package/button/README.md +482 -2
  13. package/button/fesm2022/button.mjs +85 -107
  14. package/button/types/button.d.ts +5 -8
  15. package/button-group/README.md +318 -5
  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/checkbox/fesm2022/checkbox.mjs +6 -7
  22. package/checkbox/types/checkbox.d.ts +1 -1
  23. package/collapsible/README.md +332 -5
  24. package/combobox/README.md +507 -5
  25. package/combobox/fesm2022/combobox.mjs +5 -2
  26. package/command/README.md +435 -5
  27. package/confirmation-dialog/README.md +301 -2
  28. package/context-menu/README.md +366 -5
  29. package/date-picker/README.md +469 -2
  30. package/date-picker/fesm2022/date-picker.mjs +150 -32
  31. package/date-picker/types/date-picker.d.ts +102 -9
  32. package/dialog/README.md +448 -2
  33. package/drawer/README.md +395 -5
  34. package/dropdown-menu/README.md +417 -5
  35. package/empty/README.md +329 -5
  36. package/field/README.md +385 -5
  37. package/form-autocomplete/README.md +177 -0
  38. package/form-autocomplete/fesm2022/form-autocomplete.mjs +125 -0
  39. package/form-autocomplete/package.json +24 -0
  40. package/form-autocomplete/types/form-autocomplete.d.ts +61 -0
  41. package/form-checkbox/README.md +322 -2
  42. package/form-checkbox/fesm2022/form-checkbox.mjs +22 -9
  43. package/form-checkbox/types/form-checkbox.d.ts +23 -4
  44. package/form-combobox/README.md +202 -0
  45. package/form-combobox/fesm2022/form-combobox.mjs +147 -0
  46. package/form-combobox/package.json +24 -0
  47. package/form-combobox/types/form-combobox.d.ts +73 -0
  48. package/form-date-picker/README.md +348 -2
  49. package/form-date-picker/fesm2022/form-date-picker.mjs +38 -12
  50. package/form-date-picker/types/form-date-picker.d.ts +17 -1
  51. package/form-date-picker-multi/README.md +191 -0
  52. package/form-date-picker-multi/fesm2022/form-date-picker-multi.mjs +129 -0
  53. package/form-date-picker-multi/package.json +24 -0
  54. package/form-date-picker-multi/types/form-date-picker-multi.d.ts +58 -0
  55. package/form-date-range-picker/README.md +253 -0
  56. package/form-date-range-picker/fesm2022/form-date-range-picker.mjs +130 -0
  57. package/form-date-range-picker/package.json +24 -0
  58. package/form-date-range-picker/types/form-date-range-picker.d.ts +55 -0
  59. package/form-field/README.md +356 -2
  60. package/form-field-simple/README.md +340 -2
  61. package/form-input-otp/README.md +194 -0
  62. package/form-input-otp/fesm2022/form-input-otp.mjs +106 -0
  63. package/form-input-otp/package.json +24 -0
  64. package/form-input-otp/types/form-input-otp.d.ts +58 -0
  65. package/form-month-year-picker/README.md +188 -0
  66. package/form-month-year-picker/fesm2022/form-month-year-picker.mjs +125 -0
  67. package/form-month-year-picker/package.json +24 -0
  68. package/form-month-year-picker/types/form-month-year-picker.d.ts +53 -0
  69. package/form-native-select/README.md +205 -0
  70. package/form-native-select/fesm2022/form-native-select.mjs +103 -0
  71. package/form-native-select/package.json +24 -0
  72. package/form-native-select/types/form-native-select.d.ts +57 -0
  73. package/form-phone-input/README.md +188 -0
  74. package/form-phone-input/fesm2022/form-phone-input.mjs +113 -0
  75. package/form-phone-input/package.json +24 -0
  76. package/form-phone-input/types/form-phone-input.d.ts +58 -0
  77. package/form-radio-group/README.md +198 -0
  78. package/form-radio-group/fesm2022/form-radio-group.mjs +111 -0
  79. package/form-radio-group/package.json +24 -0
  80. package/form-radio-group/types/form-radio-group.d.ts +63 -0
  81. package/form-searchable-multiselect/README.md +371 -2
  82. package/form-searchable-multiselect/fesm2022/form-searchable-multiselect.mjs +19 -10
  83. package/form-searchable-multiselect/types/form-searchable-multiselect.d.ts +18 -1
  84. package/form-select/README.md +360 -2
  85. package/form-select/fesm2022/form-select.mjs +20 -11
  86. package/form-select/types/form-select.d.ts +18 -1
  87. package/form-slider/README.md +182 -0
  88. package/form-slider/fesm2022/form-slider.mjs +106 -0
  89. package/form-slider/package.json +24 -0
  90. package/form-slider/types/form-slider.d.ts +60 -0
  91. package/form-switch/README.md +173 -0
  92. package/form-switch/fesm2022/form-switch.mjs +100 -0
  93. package/form-switch/package.json +24 -0
  94. package/form-switch/types/form-switch.d.ts +50 -0
  95. package/form-text-input/README.md +381 -2
  96. package/form-text-input/fesm2022/form-text-input.mjs +19 -10
  97. package/form-text-input/types/form-text-input.d.ts +18 -1
  98. package/form-textarea/README.md +357 -2
  99. package/form-textarea/fesm2022/form-textarea.mjs +19 -10
  100. package/form-textarea/types/form-textarea.d.ts +18 -1
  101. package/form-toggle/README.md +186 -0
  102. package/form-toggle/fesm2022/form-toggle.mjs +159 -0
  103. package/form-toggle/package.json +24 -0
  104. package/form-toggle/types/form-toggle.d.ts +82 -0
  105. package/form-toggle-group/README.md +176 -0
  106. package/form-toggle-group/fesm2022/form-toggle-group.mjs +116 -0
  107. package/form-toggle-group/package.json +24 -0
  108. package/form-toggle-group/types/form-toggle-group.d.ts +65 -0
  109. package/hover-card/README.md +256 -5
  110. package/icon/README.md +239 -2
  111. package/input/README.md +269 -2
  112. package/input-group/README.md +335 -5
  113. package/input-group/fesm2022/input-group.mjs +22 -12
  114. package/input-group/types/input-group.d.ts +4 -1
  115. package/input-otp/README.md +375 -5
  116. package/item/README.md +385 -5
  117. package/item/fesm2022/item.mjs +3 -3
  118. package/kbd/README.md +291 -5
  119. package/label/README.md +272 -2
  120. package/layout-simple/README.md +193 -2
  121. package/layout-simple/fesm2022/layout-simple.mjs +472 -236
  122. package/layout-simple/types/layout-simple.d.ts +174 -137
  123. package/menu/README.md +417 -2
  124. package/menubar/README.md +343 -5
  125. package/native-select/README.md +323 -5
  126. package/native-select/fesm2022/native-select.mjs +18 -6
  127. package/native-select/types/native-select.d.ts +7 -2
  128. package/navigation-menu/README.md +369 -5
  129. package/package.json +57 -1
  130. package/pagination/README.md +388 -5
  131. package/phone-input/README.md +114 -0
  132. package/phone-input/fesm2022/phone-input.mjs +191 -0
  133. package/phone-input/package.json +24 -0
  134. package/phone-input/types/phone-input.d.ts +67 -0
  135. package/popover/README.md +331 -2
  136. package/progress/README.md +311 -5
  137. package/radio-group/README.md +364 -2
  138. package/radio-group/fesm2022/radio-group.mjs +5 -1
  139. package/resizable/README.md +269 -5
  140. package/scroll-area/README.md +233 -5
  141. package/searchable-multiselect/README.md +323 -2
  142. package/select/README.md +437 -2
  143. package/separator/README.md +222 -2
  144. package/sheet/README.md +311 -2
  145. package/sidebar/README.md +457 -5
  146. package/skeleton/README.md +217 -5
  147. package/slider/README.md +273 -5
  148. package/slider/fesm2022/slider.mjs +3 -3
  149. package/sonner/README.md +346 -2
  150. package/spinner/README.md +284 -2
  151. package/switch/README.md +310 -2
  152. package/switch/fesm2022/switch.mjs +7 -5
  153. package/switch/types/switch.d.ts +2 -1
  154. package/table/README.md +423 -5
  155. package/tabs/README.md +411 -2
  156. package/tabs/fesm2022/tabs.mjs +2 -2
  157. package/textarea/README.md +282 -5
  158. package/toggle/README.md +270 -5
  159. package/toggle-group/README.md +340 -5
  160. package/tooltip/README.md +269 -2
  161. package/typography/README.md +271 -5
  162. package/utils/README.md +303 -2
package/field/README.md CHANGED
@@ -1,11 +1,391 @@
1
- # Field
1
+ # Field (`@egose/shadcn-theme-ng/field`)
2
2
 
3
- This project was generated using [Angular CLI](https://github.com/angular/angular-cli).
3
+ The shadcn/ui Field layout system for Angular: composable label / title / description / error / content wrappers, groups, fieldsets, legends, and separators for building accessible forms — including card-style selectable rows. Equivalent to shadcn/ui `Field`.
4
4
 
5
- ## Building
5
+ The Angular implementation layers shadcn classes over headless
6
+ [`BrnField` / `BrnFieldA11yService` from `@spartan-ng/brain/field`](https://www.spartan-ng.com/):
7
+ `HlmField` hosts `BrnField` (validation state), `HlmFieldDescription` / `HlmFieldError` register
8
+ `aria-describedby` ids with the a11y service, and `HlmFieldLabel` composes `HlmLabel`. Pure-layout
9
+ pieces (content, group, set, separator, title) carry no behavior.
6
10
 
7
- To build the library, run:
11
+ > **Ships as:** `@egose/shadcn-theme-ng/field` and `@egose/shadcn-theme-ng-tw/field`
12
+ > (the `tw:`-prefixed Tailwind variant). Both expose the identical TypeScript surface; only the
13
+ > emitted Tailwind class strings differ. See the [package README](../../README.md) for install
14
+ > steps, peer dependencies, and Tailwind setup. Do not publish this project directory independently.
15
+
16
+ ## Installation
8
17
 
9
18
  ```bash
10
- ng build field
19
+ # Plain Tailwind (no prefix)
20
+ npm install @egose/shadcn-theme-ng
21
+
22
+ # tw:-prefixed Tailwind variant
23
+ npm install @egose/shadcn-theme-ng-tw
24
+ ```
25
+
26
+ Peer dependencies (Angular, `@spartan-ng/brain`, `@ng-icons/*`, `rxjs`, …) are documented in the
27
+ [package README](../../README.md#peer-dependencies). This subpath additionally relies at runtime on
28
+ `@egose/shadcn-theme-ng/label`, `@egose/shadcn-theme-ng/separator`, and
29
+ `@egose/shadcn-theme-ng/utils`.
30
+
31
+ ## Imports
32
+
33
+ All symbols are exported from the subpath root (`projects/field/src/public-api.ts`):
34
+
35
+ ```ts
36
+ import {
37
+ HlmField,
38
+ HlmFieldLabel,
39
+ HlmFieldTitle,
40
+ HlmFieldDescription,
41
+ HlmFieldError,
42
+ HlmFieldContent,
43
+ HlmFieldGroup,
44
+ HlmFieldSet,
45
+ HlmFieldLegend,
46
+ HlmFieldSeparator,
47
+ HlmFieldImports,
48
+ HlmFieldModule,
49
+ } from '@egose/shadcn-theme-ng/field';
50
+ // tw variant: replace with '@egose/shadcn-theme-ng-tw/field'
51
+ ```
52
+
53
+ Standalone component — spread the `*Imports` array:
54
+
55
+ ```ts
56
+ import { Component } from '@angular/core';
57
+ import { HlmFieldImports } from '@egose/shadcn-theme-ng/field';
58
+
59
+ @Component({
60
+ selector: 'app-demo',
61
+ standalone: true,
62
+ imports: [...HlmFieldImports],
63
+ template: `...`,
64
+ })
65
+ export class DemoComponent {}
66
+ ```
67
+
68
+ NgModule-based consumer — import the module:
69
+
70
+ ```ts
71
+ import { NgModule } from '@angular/core';
72
+ import { HlmFieldModule } from '@egose/shadcn-theme-ng/field';
73
+
74
+ @NgModule({ imports: [HlmFieldModule] })
75
+ export class DemoModule {}
76
+ ```
77
+
78
+ ## Anatomy / Structure
79
+
80
+ ```html
81
+ <div hlmFieldGroup>
82
+ <div hlmField>
83
+ <label hlmFieldLabel for="name">Name</label>
84
+ <div hlmFieldContent>
85
+ <input hlmInput id="name" placeholder="Ada Lovelace" />
86
+ <p hlmFieldDescription>Your public display name.</p>
87
+ <hlm-field-error validator="required">Name is required.</hlm-field-error>
88
+ </div>
89
+ </div>
90
+
91
+ <hlm-field-separator>or continue with</hlm-field-separator>
92
+
93
+ <fieldset hlmFieldSet>
94
+ <legend hlmFieldLegend>Notifications</legend>
95
+ <div hlmField orientation="horizontal">
96
+ <div hlmFieldContent>
97
+ <span hlmFieldTitle>Email alerts</span>
98
+ <p hlmFieldDescription>Get emailed on mentions.</p>
99
+ </div>
100
+ <hlm-switch />
101
+ </div>
102
+ </fieldset>
103
+ </div>
104
+ ```
105
+
106
+ Real selectors (from source):
107
+
108
+ | Class | Selector(s) | Kind |
109
+ | --------------------- | ---------------------------------------------- | ------------------------------------- |
110
+ | `HlmField` | `[hlmField], hlm-field` | Directive (hosts `BrnField`) |
111
+ | `HlmFieldLabel` | `[hlmFieldLabel], hlm-field-label` | Directive (hosts `HlmLabel`) |
112
+ | `HlmFieldTitle` | `[hlmFieldTitle], hlm-field-title` | Directive (`data-slot="field-label"`) |
113
+ | `HlmFieldDescription` | `[hlmFieldDescription], hlm-field-description` | Directive |
114
+ | `HlmFieldError` | `hlm-field-error` | Component (element only) |
115
+ | `HlmFieldContent` | `[hlmFieldContent], hlm-field-content` | Directive |
116
+ | `HlmFieldGroup` | `[hlmFieldGroup], hlm-field-group` | Directive |
117
+ | `HlmFieldSet` | `fieldset[hlmFieldSet]` | Directive (fieldset only) |
118
+ | `HlmFieldLegend` | `legend[hlmFieldLegend]` | Directive (legend only) |
119
+ | `HlmFieldSeparator` | `hlm-field-separator` | Component (element only) |
120
+
121
+ ## API reference
122
+
123
+ ### `HlmField` — `[hlmField], hlm-field`
124
+
125
+ Field row. Hosts `BrnField` (`role="group"`, `data-slot="field"`, `data-orientation`).
126
+
127
+ | Input | Type | Default | Description |
128
+ | ------------------------------- | -------------------------------------------- | ------------ | ---------------------------------------------------------------------------- |
129
+ | `orientation` | `'vertical' \| 'horizontal' \| 'responsive'` | `'vertical'` | Row layout; `responsive` stacks on small screens (`@container/field-group`). |
130
+ | `data-invalid` / `forceInvalid` | forwarded to `BrnField` | — | Manual invalid-state control. |
131
+
132
+ ### `HlmFieldLabel` — `[hlmFieldLabel], hlm-field-label`
133
+
134
+ Label for a control. Hosts `HlmLabel` (all label inputs forwarded). Card-style when wrapping a nested `[data-slot=field]`. No new inputs.
135
+
136
+ ### `HlmFieldTitle` — `[hlmFieldTitle], hlm-field-title`
137
+
138
+ Non-`<label>` title (used beside switches/checkboxes inside `hlmFieldContent`). No inputs. Note: its `data-slot` is `field-label`, not `field-title`.
139
+
140
+ ### `HlmFieldDescription` — `[hlmFieldDescription], hlm-field-description`
141
+
142
+ Hint text. Registers its id with `BrnFieldA11yService` so controls pick it up via `aria-describedby`.
143
+
144
+ | Input | Type | Default | Description |
145
+ | ----- | -------- | --------------------------- | ----------------------- |
146
+ | `id` | `string` | `hlm-field-description-<n>` | Description element id. |
147
+
148
+ ### `HlmFieldError` — `hlm-field-error`
149
+
150
+ Error message (`role="alert"`, hidden until it should display). Reads validation state from the parent `BrnField`; registers with the a11y service only while visible.
151
+
152
+ | Input | Type | Default | Description |
153
+ | ----------- | --------- | --------------------- | -------------------------------------------------------------------------------------------- |
154
+ | `id` | `string` | `hlm-field-error-<n>` | Error element id. |
155
+ | `validator` | `string` | — | Show only when this validator key (e.g. `'required'`) is present; omit to show on any error. |
156
+ | `forceShow` | `boolean` | `false` | Show regardless of control state. |
157
+
158
+ Without a parent field it always displays (useful for static demos).
159
+
160
+ ### `HlmFieldContent` — `[hlmFieldContent], hlm-field-content`
161
+
162
+ Flex column for control + description + errors. No inputs.
163
+
164
+ ### `HlmFieldGroup` — `[hlmFieldGroup], hlm-field-group`
165
+
166
+ Vertical stack of fields (`gap-7`, container-query scope for `responsive` fields). No inputs.
167
+
168
+ ### `HlmFieldSet` — `fieldset[hlmFieldSet]` / `HlmFieldLegend` — `legend[hlmFieldLegend]`
169
+
170
+ Native grouping elements with shadcn spacing. Legend only:
171
+
172
+ | Input | Type | Default | Description |
173
+ | --------- | --------------------- | ---------- | ------------------------------------- |
174
+ | `variant` | `'label' \| 'legend'` | `'legend'` | Text size (`text-sm` vs `text-base`). |
175
+
176
+ ### `HlmFieldSeparator` — `hlm-field-separator`
177
+
178
+ Centered labelled divider (hosts `hlm-separator` + centered text span). Content is the label text. No inputs.
179
+
180
+ ## Examples
181
+
182
+ ### 1. Basic labelled field with hint + error
183
+
184
+ ```ts
185
+ import { Component, inject } from '@angular/core';
186
+ import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
187
+ import { HlmFieldImports } from '@egose/shadcn-theme-ng/field';
188
+ import { HlmInput } from '@egose/shadcn-theme-ng/input';
189
+
190
+ @Component({
191
+ selector: 'app-basic-field',
192
+ standalone: true,
193
+ imports: [ReactiveFormsModule, HlmInput, ...HlmFieldImports],
194
+ template: `
195
+ <div hlmFieldGroup [formGroup]="form">
196
+ <div hlmField>
197
+ <label hlmFieldLabel for="username">Username</label>
198
+ <div hlmFieldContent>
199
+ <input hlmInput id="username" formControlName="username" placeholder="ada" />
200
+ <p hlmFieldDescription>Letters and numbers only.</p>
201
+ <hlm-field-error validator="required">Username is required.</hlm-field-error>
202
+ </div>
203
+ </div>
204
+ </div>
205
+ `,
206
+ })
207
+ export class BasicFieldComponent {
208
+ private readonly fb = inject(FormBuilder);
209
+ readonly form = this.fb.group({ username: ['', Validators.required] });
210
+ }
211
+ ```
212
+
213
+ > Note: `hlmField` hosts `BrnField`; controls that implement `BrnFieldControl` feed its error state.
214
+ > Plain `hlmInput` + reactive forms drive `hlm-field-error` through the parent field where supported;
215
+ > for guaranteed wiring use `hlm-form-field` or a brain field control.
216
+
217
+ ### 2. Horizontal switch row
218
+
219
+ ```ts
220
+ import { Component, signal } from '@angular/core';
221
+ import { HlmFieldImports } from '@egose/shadcn-theme-ng/field';
222
+ import { HlmSwitchImports } from '@egose/shadcn-theme-ng/switch';
223
+
224
+ @Component({
225
+ selector: 'app-switch-row',
226
+ standalone: true,
227
+ imports: [...HlmFieldImports, ...HlmSwitchImports],
228
+ template: `
229
+ <div hlmField orientation="horizontal">
230
+ <div hlmFieldContent>
231
+ <span hlmFieldTitle>Email notifications</span>
232
+ <p hlmFieldDescription>Receive an email on every mention.</p>
233
+ </div>
234
+ <hlm-switch [checked]="on()" (checkedChange)="on.set($event)" />
235
+ </div>
236
+ `,
237
+ })
238
+ export class SwitchRowComponent {
239
+ readonly on = signal(true);
240
+ }
11
241
  ```
242
+
243
+ ### 3. Fieldset + legend + responsive rows
244
+
245
+ ```ts
246
+ import { Component } from '@angular/core';
247
+ import { HlmFieldImports } from '@egose/shadcn-theme-ng/field';
248
+ import { HlmInput } from '@egose/shadcn-theme-ng/input';
249
+
250
+ @Component({
251
+ selector: 'app-fieldset',
252
+ standalone: true,
253
+ imports: [...HlmFieldImports, HlmInput],
254
+ template: `
255
+ <fieldset hlmFieldSet>
256
+ <legend hlmFieldLegend>Profile</legend>
257
+ <div hlmField orientation="responsive">
258
+ <label hlmFieldLabel for="first">First name</label>
259
+ <div hlmFieldContent>
260
+ <input hlmInput id="first" placeholder="Ada" />
261
+ </div>
262
+ </div>
263
+ <div hlmField orientation="responsive">
264
+ <label hlmFieldLabel for="last">Last name</label>
265
+ <div hlmFieldContent>
266
+ <input hlmInput id="last" placeholder="Lovelace" />
267
+ </div>
268
+ </div>
269
+ </fieldset>
270
+ `,
271
+ })
272
+ export class FieldsetComponent {}
273
+ ```
274
+
275
+ ### 4. Selectable card rows (label wrapping a field)
276
+
277
+ ```ts
278
+ import { Component, signal } from '@angular/core';
279
+ import { HlmFieldImports } from '@egose/shadcn-theme-ng/field';
280
+ import { HlmRadioGroupImports } from '@egose/shadcn-theme-ng/radio-group';
281
+
282
+ @Component({
283
+ selector: 'app-plan-cards',
284
+ standalone: true,
285
+ imports: [...HlmFieldImports, ...HlmRadioGroupImports],
286
+ template: `
287
+ <div hlmFieldGroup data-slot="checkbox-group">
288
+ @for (plan of plans; track plan) {
289
+ <label hlmFieldLabel>
290
+ <input
291
+ type="radio"
292
+ name="plan"
293
+ [value]="plan"
294
+ [checked]="selected() === plan"
295
+ (change)="selected.set(plan)"
296
+ />
297
+ <div hlmFieldContent>
298
+ <span hlmFieldTitle>{{ plan }}</span>
299
+ <p hlmFieldDescription>{{ plan }} billing, cancel anytime.</p>
300
+ </div>
301
+ </label>
302
+ }
303
+ </div>
304
+ `,
305
+ })
306
+ export class PlanCardsComponent {
307
+ readonly plans = ['Monthly', 'Yearly'];
308
+ readonly selected = signal('Monthly');
309
+ }
310
+ ```
311
+
312
+ ### 5. Separator between groups + per-validator errors
313
+
314
+ ```ts
315
+ import { Component, inject } from '@angular/core';
316
+ import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
317
+ import { HlmFieldImports } from '@egose/shadcn-theme-ng/field';
318
+ import { HlmInput } from '@egose/shadcn-theme-ng/input';
319
+
320
+ @Component({
321
+ selector: 'app-validators',
322
+ standalone: true,
323
+ imports: [ReactiveFormsModule, HlmInput, ...HlmFieldImports],
324
+ template: `
325
+ <div hlmFieldGroup [formGroup]="form">
326
+ <div hlmField>
327
+ <label hlmFieldLabel for="email">Email</label>
328
+ <div hlmFieldContent>
329
+ <input hlmInput id="email" formControlName="email" type="email" />
330
+ <hlm-field-error validator="required">Email is required.</hlm-field-error>
331
+ <hlm-field-error validator="email">Enter a valid email address.</hlm-field-error>
332
+ </div>
333
+ </div>
334
+ <hlm-field-separator>or continue with</hlm-field-separator>
335
+ <div hlmField>
336
+ <label hlmFieldLabel for="phone">Phone (optional)</label>
337
+ <div hlmFieldContent>
338
+ <input hlmInput id="phone" formControlName="phone" />
339
+ <p hlmFieldDescription>We'll only call about your order.</p>
340
+ </div>
341
+ </div>
342
+ </div>
343
+ `,
344
+ })
345
+ export class ValidatorsComponent {
346
+ private readonly fb = inject(FormBuilder);
347
+ readonly form = this.fb.group({ email: ['', [Validators.required, Validators.email]], phone: [''] });
348
+ }
349
+ ```
350
+
351
+ ### 6. Forced error preview (docs / visual testing)
352
+
353
+ ```ts
354
+ import { Component } from '@angular/core';
355
+ import { HlmFieldImports } from '@egose/shadcn-theme-ng/field';
356
+ import { HlmInput } from '@egose/shadcn-theme-ng/input';
357
+
358
+ @Component({
359
+ selector: 'app-forced-error',
360
+ standalone: true,
361
+ imports: [...HlmFieldImports, HlmInput],
362
+ template: `
363
+ <div hlmField forceInvalid>
364
+ <label hlmFieldLabel for="demo">API key</label>
365
+ <div hlmFieldContent>
366
+ <input hlmInput id="demo" value="sk-…" />
367
+ <hlm-field-error forceShow>This key has been revoked.</hlm-field-error>
368
+ </div>
369
+ </div>
370
+ `,
371
+ })
372
+ export class ForcedErrorComponent {}
373
+ ```
374
+
375
+ ## Accessibility notes
376
+
377
+ - `HlmField` sets `role="group"`; always give the group an accessible name via `HlmFieldLabel` / `HlmFieldTitle` / `legend`.
378
+ - Descriptions and visible errors are registered with `BrnFieldA11yService` and referenced from controls via `aria-describedby`; keep ids unique (defaults auto-increment).
379
+ - Errors use `role="alert"` and only render when the parent field reports a matching validation error — screen readers announce them on appearance.
380
+ - Native `fieldset` + `legend` remain the most robust grouping for related controls; prefer `hlmFieldSet` for notification groups and radio sets.
381
+
382
+ ## Theming / CSS variables
383
+
384
+ No component-specific CSS variables. Orientation, invalid color (`text-destructive` on `data-matches-spartan-invalid`), and spacing derive from `fieldVariants` cva + global tokens. Extend with `class` on any directive.
385
+
386
+ ## Related subpaths
387
+
388
+ - `@egose/shadcn-theme-ng/form-field` — `hlm-form-field` reactive-form wrapper with automatic hint/error switching.
389
+ - `@egose/shadcn-theme-ng/label` — `HlmLabel` hosted by `HlmFieldLabel`.
390
+ - `@egose/shadcn-theme-ng/separator` — `HlmSeparator` rendered inside `hlm-field-separator`.
391
+ - `@egose/shadcn-theme-ng/field` pairs with `@egose/shadcn-theme-ng/checkbox`, `.../radio-group`, `.../switch` for selectable rows.
@@ -0,0 +1,177 @@
1
+ # Form Autocomplete (`@egose/shadcn-theme-ng/form-autocomplete`)
2
+
3
+ A ready-made reactive-form autocomplete field: label + `hlm-autocomplete` text input with suggestion list + validation error/hint display in one tag. The model is a `string`.
4
+
5
+ The Angular implementation is a standalone wrapper: it renders `HlmFormField` / `HlmError` / `HlmHint` from `@egose/shadcn-theme-ng/form-field`, `HlmLabel`, and the `HlmAutocomplete*` parts from `@egose/shadcn-theme-ng/autocomplete`. The autocomplete root is bound with `[formControlName]="controlName()"` (`BrnAutocomplete` implements `ControlValueAccessor`), so the parent `FormGroup` owns the value. It must live inside a `FormGroupDirective` (`[formGroup]` parent); ids come from `HlmFormIdGenerator` unless overridden.
6
+
7
+ > **Ships as:** `@egose/shadcn-theme-ng/form-autocomplete` and `@egose/shadcn-theme-ng-tw/form-autocomplete`
8
+ > (the `tw:`-prefixed Tailwind variant). Both expose the identical TypeScript surface; only the
9
+ > emitted Tailwind class strings differ. See the [package README](../../README.md) for install
10
+ > steps, peer dependencies, and Tailwind setup. Do not publish this project directory independently.
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ # Plain Tailwind (no prefix)
16
+ npm install @egose/shadcn-theme-ng
17
+
18
+ # tw:-prefixed Tailwind variant
19
+ npm install @egose/shadcn-theme-ng-tw
20
+ ```
21
+
22
+ Peer dependencies (Angular, `@spartan-ng/brain`, `@ng-icons/*`, `rxjs`, …) are documented in the
23
+ [package README](../../README.md#peer-dependencies). This subpath additionally relies at runtime on
24
+ `@egose/shadcn-theme-ng/autocomplete`, `@egose/shadcn-theme-ng/form-field`, and
25
+ `@egose/shadcn-theme-ng/label`.
26
+
27
+ ## Imports
28
+
29
+ Exported from the subpath root (`projects/form-autocomplete/src/public-api.ts`):
30
+
31
+ ```ts
32
+ import { EgFormAutocomplete, provideEgFormAutocompleteConfig } from '@egose/shadcn-theme-ng/form-autocomplete';
33
+ // tw variant: replace with '@egose/shadcn-theme-ng-tw/form-autocomplete'
34
+ ```
35
+
36
+ ## Anatomy / Structure
37
+
38
+ ```html
39
+ <form [formGroup]="form">
40
+ <eg-form-autocomplete
41
+ controlName="fruit"
42
+ label="Fruit"
43
+ [options]="fruits"
44
+ [required]="true"
45
+ error="Fruit is required."
46
+ />
47
+ </form>
48
+ ```
49
+
50
+ Selector (from source): `eg-form-autocomplete` (standalone component, host `tw:w-full`).
51
+
52
+ ## API reference
53
+
54
+ ### `EgFormAutocomplete` — `eg-form-autocomplete`
55
+
56
+ | Input | Type | Default | Description |
57
+ | -------------- | --------------------- | ------------------- | ------------------------------------------------------------------ |
58
+ | `controlName` | `string` | `''` | `formControlName` key inside the parent `FormGroup`. **Required.** |
59
+ | `label` | `string \| undefined` | — | Label text (hidden when omitted). |
60
+ | `error` | `string \| undefined` | — | Error text shown when invalid. |
61
+ | `hint` | `string \| undefined` | — | Hint text shown otherwise. |
62
+ | `controlId` | `string \| undefined` | — | Explicit id (first priority for `effectiveId`). |
63
+ | `id` | `string \| undefined` | — | Fallback id (second priority). |
64
+ | `placeholder` | `string` | `'Type to search…'` | Input placeholder. |
65
+ | `emptyText` | `string` | `'No result.'` | Text shown when no option matches. |
66
+ | `disabled` | `boolean` | `false` | Locks interaction (form control stays enabled). |
67
+ | `required` | `boolean` | `false` | Shows a red `*` next to the label. |
68
+ | `options` | `string[]` | `[]` | Rendered as `hlm-autocomplete-item` entries. |
69
+ | `class` | `ClassValue` | `''` | Extra host classes (base `tw:w-full`). |
70
+ | `labelClass` | `string` | `''` | Extra label classes (base `tw:mb-1`). |
71
+ | `controlClass` | `string` | `''` | Extra autocomplete-root classes. |
72
+ | `inputClass` | `string` | `''` | Extra text-input classes. |
73
+ | `errorClass` | `string` | `''` | Extra error classes (base `tw:mt-0`). |
74
+ | `hintClass` | `string` | `''` | Extra hint classes (base `tw:mt-0`). |
75
+
76
+ | Member | Description |
77
+ | -------------------- | ---------------------------------------------------------------------------------- |
78
+ | `effectiveId` | `controlId() \|\| id() \|\| generated` — wired to input `inputId` and label `for`. |
79
+ | `errorId` / `hintId` | `effectiveId + '-error' / '-hint'`. |
80
+
81
+ Requires a `[formGroup]` ancestor (injects `FormGroupDirective`, provides `ControlContainer → FormGroupDirective`).
82
+
83
+ ## Examples
84
+
85
+ ### 1. Basic required autocomplete
86
+
87
+ ```ts
88
+ import { Component, inject } from '@angular/core';
89
+ import { FormBuilder, ReactiveFormsModule, Validators } from '@angular/forms';
90
+ import { EgFormAutocomplete } from '@egose/shadcn-theme-ng/form-autocomplete';
91
+
92
+ @Component({
93
+ selector: 'app-fruit',
94
+ standalone: true,
95
+ imports: [ReactiveFormsModule, EgFormAutocomplete],
96
+ template: `
97
+ <form [formGroup]="form" (ngSubmit)="submit()">
98
+ <eg-form-autocomplete
99
+ controlName="fruit"
100
+ label="Fruit"
101
+ [options]="fruits"
102
+ [required]="true"
103
+ error="Fruit is required."
104
+ />
105
+ <button type="submit" [disabled]="form.invalid">Continue</button>
106
+ </form>
107
+ `,
108
+ })
109
+ export class FruitComponent {
110
+ private readonly fb = inject(FormBuilder);
111
+ readonly form = this.fb.group({ fruit: ['', Validators.required] });
112
+ readonly fruits = ['Apple', 'Banana', 'Cherry'];
113
+ submit() {
114
+ console.log(this.form.value.fruit);
115
+ }
116
+ }
117
+ ```
118
+
119
+ ### 2. Custom empty text + hint
120
+
121
+ ```ts
122
+ import { Component, inject } from '@angular/core';
123
+ import { FormBuilder, ReactiveFormsModule } from '@angular/forms';
124
+ import { EgFormAutocomplete } from '@egose/shadcn-theme-ng/form-autocomplete';
125
+
126
+ @Component({
127
+ selector: 'app-city',
128
+ standalone: true,
129
+ imports: [ReactiveFormsModule, EgFormAutocomplete],
130
+ template: `
131
+ <form [formGroup]="form">
132
+ <eg-form-autocomplete
133
+ controlName="city"
134
+ label="City"
135
+ [options]="cities"
136
+ placeholder="Start typing…"
137
+ emptyText="No matching city."
138
+ hint="We ship to these cities."
139
+ />
140
+ </form>
141
+ `,
142
+ })
143
+ export class CityComponent {
144
+ private readonly fb = inject(FormBuilder);
145
+ readonly form = this.fb.group({ city: [''] });
146
+ readonly cities = ['Berlin', 'Paris', 'Rome'];
147
+ }
148
+ ```
149
+
150
+ ## Accessibility notes
151
+
152
+ - Label `for` ↔ input `inputId` wiring is automatic via `effectiveId` — always pass a `label`.
153
+ - The autocomplete input exposes no `aria-describedby`, so error/hint ids render without an input-level link.
154
+ - The required `*` is visual; pair with `Validators.required` so assistive tech and validation agree.
155
+
156
+ ## Theming / CSS variables
157
+
158
+ No component-specific CSS variables. Style per-instance via `class` / `labelClass` / `controlClass` / `inputClass` / `errorClass` / `hintClass` (precedence: library base < global config < per-instance).
159
+
160
+ Global defaults per styling slot via the wrapper config:
161
+
162
+ ```ts
163
+ import { provideEgFormAutocompleteConfig } from '@egose/shadcn-theme-ng/form-autocomplete';
164
+
165
+ await bootstrapApplication(App, {
166
+ providers: [provideEgFormAutocompleteConfig({ inputClass: 'tw:text-sm', labelClass: 'tw:font-medium' })],
167
+ });
168
+ ```
169
+
170
+ The host keeps `tw:w-full`; constrain width with `class` (e.g. `tw:max-w-sm`) or a wrapping container.
171
+
172
+ ## Related subpaths
173
+
174
+ - `@egose/shadcn-theme-ng/autocomplete` — raw `HlmAutocomplete*` parts for custom layouts.
175
+ - `@egose/shadcn-theme-ng/form-combobox` — multi-select counterpart with chips (`eg-form-combobox`).
176
+ - `@egose/shadcn-theme-ng/form-field` — `hlm-form-field` / `hlm-error` / `hlm-hint` used internally.
177
+ - `@egose/shadcn-theme-ng/form-field-simple` — alternative minimal wrapper when you compose controls by hand.