@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.
Files changed (237) hide show
  1. package/README.md +130 -13
  2. package/accordion/README.md +376 -34
  3. package/accordion/fesm2022/accordion.mjs +27 -39
  4. package/alert/README.md +343 -34
  5. package/alert/fesm2022/alert.mjs +47 -47
  6. package/alert-dialog/README.md +471 -5
  7. package/alert-dialog/fesm2022/alert-dialog.mjs +47 -47
  8. package/aspect-ratio/README.md +272 -5
  9. package/aspect-ratio/fesm2022/aspect-ratio.mjs +9 -9
  10. package/autocomplete/README.md +473 -34
  11. package/autocomplete/fesm2022/autocomplete.mjs +53 -71
  12. package/avatar/README.md +357 -5
  13. package/avatar/fesm2022/avatar.mjs +32 -32
  14. package/badge/README.md +289 -34
  15. package/badge/fesm2022/badge.mjs +57 -57
  16. package/basic-alert/README.md +324 -34
  17. package/basic-alert/fesm2022/basic-alert.mjs +6 -18
  18. package/breadcrumb/README.md +406 -5
  19. package/breadcrumb/fesm2022/breadcrumb.mjs +33 -39
  20. package/button/README.md +453 -34
  21. package/button/fesm2022/button.mjs +147 -219
  22. package/button/types/button.d.ts +7 -10
  23. package/button-group/README.md +318 -5
  24. package/button-group/fesm2022/button-group.mjs +20 -20
  25. package/calendar/README.md +328 -34
  26. package/calendar/fesm2022/calendar.mjs +29 -743
  27. package/card/README.md +331 -5
  28. package/card/fesm2022/card.mjs +33 -33
  29. package/carousel/README.md +333 -5
  30. package/carousel/fesm2022/carousel.mjs +39 -78
  31. package/checkbox/README.md +291 -34
  32. package/checkbox/fesm2022/checkbox.mjs +20 -58
  33. package/checkbox/types/checkbox.d.ts +5 -4
  34. package/collapsible/README.md +332 -5
  35. package/collapsible/fesm2022/collapsible.mjs +15 -15
  36. package/combobox/README.md +507 -5
  37. package/combobox/fesm2022/combobox.mjs +91 -224
  38. package/command/README.md +435 -5
  39. package/command/fesm2022/command.mjs +49 -109
  40. package/confirmation-dialog/README.md +272 -34
  41. package/confirmation-dialog/fesm2022/confirmation-dialog.mjs +15 -40
  42. package/confirmation-dialog/types/confirmation-dialog.d.ts +8 -4
  43. package/context-menu/README.md +366 -5
  44. package/context-menu/fesm2022/context-menu.mjs +9 -9
  45. package/date-picker/README.md +436 -34
  46. package/date-picker/fesm2022/date-picker.mjs +64 -267
  47. package/date-picker/types/date-picker.d.ts +7 -3
  48. package/dialog/README.md +419 -34
  49. package/dialog/fesm2022/dialog.mjs +45 -71
  50. package/drawer/README.md +395 -5
  51. package/drawer/fesm2022/drawer.mjs +44 -54
  52. package/dropdown-menu/README.md +417 -5
  53. package/dropdown-menu/fesm2022/dropdown-menu.mjs +82 -70
  54. package/dropdown-menu/types/dropdown-menu.d.ts +4 -0
  55. package/empty/README.md +329 -5
  56. package/empty/fesm2022/empty.mjs +30 -30
  57. package/field/README.md +385 -5
  58. package/field/fesm2022/field.mjs +56 -72
  59. package/form-checkbox/README.md +283 -34
  60. package/form-checkbox/fesm2022/form-checkbox.mjs +25 -85
  61. package/form-checkbox/types/form-checkbox.d.ts +7 -1
  62. package/form-date-picker/README.md +293 -34
  63. package/form-date-picker/fesm2022/form-date-picker.mjs +31 -84
  64. package/form-date-picker/types/form-date-picker.d.ts +7 -1
  65. package/form-field/README.md +327 -34
  66. package/form-field/fesm2022/form-field.mjs +33 -19
  67. package/form-field/types/form-field.d.ts +28 -20
  68. package/form-field-simple/README.md +311 -34
  69. package/form-field-simple/fesm2022/form-field-simple.mjs +4 -4
  70. package/form-searchable-multiselect/README.md +332 -34
  71. package/form-searchable-multiselect/fesm2022/form-searchable-multiselect.mjs +31 -91
  72. package/form-searchable-multiselect/types/form-searchable-multiselect.d.ts +7 -1
  73. package/form-select/README.md +321 -34
  74. package/form-select/fesm2022/form-select.mjs +32 -149
  75. package/form-select/types/form-select.d.ts +7 -1
  76. package/form-text-input/README.md +342 -34
  77. package/form-text-input/fesm2022/form-text-input.mjs +35 -149
  78. package/form-text-input/types/form-text-input.d.ts +9 -12
  79. package/form-textarea/README.md +318 -34
  80. package/form-textarea/fesm2022/form-textarea.mjs +34 -109
  81. package/form-textarea/types/form-textarea.d.ts +8 -1
  82. package/hover-card/README.md +256 -5
  83. package/hover-card/fesm2022/hover-card.mjs +19 -19
  84. package/icon/README.md +210 -34
  85. package/icon/fesm2022/icon.mjs +8 -8
  86. package/input/README.md +240 -34
  87. package/input/fesm2022/input.mjs +17 -12
  88. package/input/types/input.d.ts +2 -1
  89. package/input-group/README.md +335 -5
  90. package/input-group/fesm2022/input-group.mjs +34 -34
  91. package/input-otp/README.md +375 -5
  92. package/input-otp/fesm2022/input-otp.mjs +26 -26
  93. package/item/README.md +385 -5
  94. package/item/fesm2022/item.mjs +53 -53
  95. package/kbd/README.md +291 -5
  96. package/kbd/fesm2022/kbd.mjs +12 -12
  97. package/label/README.md +243 -34
  98. package/label/fesm2022/label.mjs +10 -10
  99. package/label/types/label.d.ts +1 -1
  100. package/layout-simple/README.md +174 -44
  101. package/layout-simple/fesm2022/layout-simple.mjs +508 -603
  102. package/layout-simple/types/layout-simple.d.ts +179 -128
  103. package/menu/README.md +418 -0
  104. package/menu/fesm2022/menu.mjs +455 -0
  105. package/menu/package.json +24 -0
  106. package/menu/types/menu.d.ts +137 -0
  107. package/menubar/README.md +343 -5
  108. package/menubar/fesm2022/menubar.mjs +13 -13
  109. package/native-select/README.md +323 -5
  110. package/native-select/fesm2022/native-select.mjs +19 -19
  111. package/navigation-menu/README.md +369 -5
  112. package/navigation-menu/fesm2022/navigation-menu.mjs +35 -47
  113. package/package.json +183 -154
  114. package/pagination/README.md +388 -5
  115. package/pagination/fesm2022/pagination.mjs +45 -331
  116. package/popover/README.md +302 -34
  117. package/popover/fesm2022/popover.mjs +30 -30
  118. package/progress/README.md +311 -5
  119. package/progress/fesm2022/progress.mjs +13 -13
  120. package/radio-group/README.md +335 -34
  121. package/radio-group/fesm2022/radio-group.mjs +26 -19
  122. package/radio-group/types/radio-group.d.ts +1 -1
  123. package/resizable/README.md +269 -5
  124. package/resizable/fesm2022/resizable.mjs +18 -26
  125. package/scroll-area/README.md +233 -5
  126. package/scroll-area/fesm2022/scroll-area.mjs +9 -9
  127. package/searchable-multiselect/README.md +294 -34
  128. package/searchable-multiselect/fesm2022/searchable-multiselect.mjs +28 -117
  129. package/searchable-multiselect/types/searchable-multiselect.d.ts +8 -2
  130. package/select/README.md +408 -34
  131. package/select/fesm2022/select.mjs +71 -111
  132. package/select/types/select.d.ts +3 -1
  133. package/separator/README.md +193 -34
  134. package/separator/fesm2022/separator.mjs +9 -9
  135. package/separator/types/separator.d.ts +1 -1
  136. package/sheet/README.md +282 -34
  137. package/sheet/fesm2022/sheet.mjs +41 -53
  138. package/sidebar/README.md +457 -5
  139. package/sidebar/fesm2022/sidebar.mjs +125 -227
  140. package/skeleton/README.md +217 -5
  141. package/skeleton/fesm2022/skeleton.mjs +9 -9
  142. package/slider/README.md +273 -5
  143. package/slider/fesm2022/slider.mjs +10 -80
  144. package/sonner/README.md +317 -34
  145. package/sonner/fesm2022/sonner.mjs +13 -13
  146. package/spinner/README.md +255 -34
  147. package/spinner/fesm2022/spinner.mjs +11 -11
  148. package/switch/README.md +281 -34
  149. package/switch/fesm2022/switch.mjs +13 -15
  150. package/table/README.md +423 -5
  151. package/table/fesm2022/table.mjs +41 -41
  152. package/tabs/README.md +382 -34
  153. package/tabs/fesm2022/tabs.mjs +35 -111
  154. package/textarea/README.md +282 -5
  155. package/textarea/fesm2022/textarea.mjs +9 -9
  156. package/toggle/README.md +270 -5
  157. package/toggle/fesm2022/toggle.mjs +13 -13
  158. package/toggle-group/README.md +340 -5
  159. package/toggle-group/fesm2022/toggle-group.mjs +13 -13
  160. package/toggle-group/types/toggle-group.d.ts +13 -7
  161. package/tooltip/README.md +240 -34
  162. package/tooltip/fesm2022/tooltip.mjs +14 -14
  163. package/typography/README.md +271 -5
  164. package/typography/fesm2022/typography.mjs +53 -53
  165. package/typography/types/typography.d.ts +19 -19
  166. package/utils/README.md +274 -34
  167. package/utils/fesm2022/utils.mjs +68 -44
  168. package/accordion/fesm2022/accordion.mjs.map +0 -1
  169. package/alert/fesm2022/alert.mjs.map +0 -1
  170. package/alert-dialog/fesm2022/alert-dialog.mjs.map +0 -1
  171. package/aspect-ratio/fesm2022/aspect-ratio.mjs.map +0 -1
  172. package/autocomplete/fesm2022/autocomplete.mjs.map +0 -1
  173. package/avatar/fesm2022/avatar.mjs.map +0 -1
  174. package/badge/fesm2022/badge.mjs.map +0 -1
  175. package/basic-alert/fesm2022/basic-alert.mjs.map +0 -1
  176. package/breadcrumb/fesm2022/breadcrumb.mjs.map +0 -1
  177. package/button/fesm2022/button.mjs.map +0 -1
  178. package/button-group/fesm2022/button-group.mjs.map +0 -1
  179. package/calendar/fesm2022/calendar.mjs.map +0 -1
  180. package/card/fesm2022/card.mjs.map +0 -1
  181. package/carousel/fesm2022/carousel.mjs.map +0 -1
  182. package/checkbox/fesm2022/checkbox.mjs.map +0 -1
  183. package/collapsible/fesm2022/collapsible.mjs.map +0 -1
  184. package/combobox/fesm2022/combobox.mjs.map +0 -1
  185. package/command/fesm2022/command.mjs.map +0 -1
  186. package/confirmation-dialog/fesm2022/confirmation-dialog.mjs.map +0 -1
  187. package/context-menu/fesm2022/context-menu.mjs.map +0 -1
  188. package/date-picker/fesm2022/date-picker.mjs.map +0 -1
  189. package/dialog/fesm2022/dialog.mjs.map +0 -1
  190. package/drawer/fesm2022/drawer.mjs.map +0 -1
  191. package/dropdown-menu/fesm2022/dropdown-menu.mjs.map +0 -1
  192. package/empty/fesm2022/empty.mjs.map +0 -1
  193. package/field/fesm2022/field.mjs.map +0 -1
  194. package/form-checkbox/fesm2022/form-checkbox.mjs.map +0 -1
  195. package/form-date-picker/fesm2022/form-date-picker.mjs.map +0 -1
  196. package/form-field/fesm2022/form-field.mjs.map +0 -1
  197. package/form-field-simple/fesm2022/form-field-simple.mjs.map +0 -1
  198. package/form-searchable-multiselect/fesm2022/form-searchable-multiselect.mjs.map +0 -1
  199. package/form-select/fesm2022/form-select.mjs.map +0 -1
  200. package/form-text-input/fesm2022/form-text-input.mjs.map +0 -1
  201. package/form-textarea/fesm2022/form-textarea.mjs.map +0 -1
  202. package/hover-card/fesm2022/hover-card.mjs.map +0 -1
  203. package/icon/fesm2022/icon.mjs.map +0 -1
  204. package/input/fesm2022/input.mjs.map +0 -1
  205. package/input-group/fesm2022/input-group.mjs.map +0 -1
  206. package/input-otp/fesm2022/input-otp.mjs.map +0 -1
  207. package/item/fesm2022/item.mjs.map +0 -1
  208. package/kbd/fesm2022/kbd.mjs.map +0 -1
  209. package/label/fesm2022/label.mjs.map +0 -1
  210. package/layout-simple/fesm2022/layout-simple.mjs.map +0 -1
  211. package/menubar/fesm2022/menubar.mjs.map +0 -1
  212. package/native-select/fesm2022/native-select.mjs.map +0 -1
  213. package/navigation-menu/fesm2022/navigation-menu.mjs.map +0 -1
  214. package/pagination/fesm2022/pagination.mjs.map +0 -1
  215. package/popover/fesm2022/popover.mjs.map +0 -1
  216. package/progress/fesm2022/progress.mjs.map +0 -1
  217. package/radio-group/fesm2022/radio-group.mjs.map +0 -1
  218. package/resizable/fesm2022/resizable.mjs.map +0 -1
  219. package/scroll-area/fesm2022/scroll-area.mjs.map +0 -1
  220. package/searchable-multiselect/fesm2022/searchable-multiselect.mjs.map +0 -1
  221. package/select/fesm2022/select.mjs.map +0 -1
  222. package/separator/fesm2022/separator.mjs.map +0 -1
  223. package/sheet/fesm2022/sheet.mjs.map +0 -1
  224. package/sidebar/fesm2022/sidebar.mjs.map +0 -1
  225. package/skeleton/fesm2022/skeleton.mjs.map +0 -1
  226. package/slider/fesm2022/slider.mjs.map +0 -1
  227. package/sonner/fesm2022/sonner.mjs.map +0 -1
  228. package/spinner/fesm2022/spinner.mjs.map +0 -1
  229. package/switch/fesm2022/switch.mjs.map +0 -1
  230. package/table/fesm2022/table.mjs.map +0 -1
  231. package/tabs/fesm2022/tabs.mjs.map +0 -1
  232. package/textarea/fesm2022/textarea.mjs.map +0 -1
  233. package/toggle/fesm2022/toggle.mjs.map +0 -1
  234. package/toggle-group/fesm2022/toggle-group.mjs.map +0 -1
  235. package/tooltip/fesm2022/tooltip.mjs.map +0 -1
  236. package/typography/fesm2022/typography.mjs.map +0 -1
  237. package/utils/fesm2022/utils.mjs.map +0 -1
@@ -6,15 +6,21 @@ interface SelectOption {
6
6
  label: string;
7
7
  }
8
8
  declare class EgFormSelect {
9
+ private readonly formGroupDirective;
10
+ private readonly generatedId;
9
11
  label: _angular_core.InputSignal<string | undefined>;
10
12
  controlId: _angular_core.InputSignal<string | undefined>;
11
13
  controlName: _angular_core.InputSignal<string>;
12
14
  error: _angular_core.InputSignal<string | undefined>;
13
15
  hint: _angular_core.InputSignal<string | undefined>;
14
- id: _angular_core.InputSignal<string>;
16
+ id: _angular_core.InputSignal<string | undefined>;
15
17
  placeholder: _angular_core.InputSignal<string>;
16
18
  disabled: _angular_core.InputSignal<boolean>;
17
19
  required: _angular_core.InputSignal<boolean>;
20
+ readonly effectiveId: _angular_core.Signal<string>;
21
+ readonly errorId: _angular_core.Signal<string>;
22
+ readonly hintId: _angular_core.Signal<string>;
23
+ describedBy(): string | null;
18
24
  multiple: _angular_core.InputSignal<boolean>;
19
25
  options: _angular_core.InputSignal<SelectOption[]>;
20
26
  optionsLabel: _angular_core.InputSignal<string | undefined>;
@@ -1,64 +1,372 @@
1
- # FormTextInput
1
+ # Form Text Input (`@egose/shadcn-theme-ng/form-text-input`)
2
2
 
3
- This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 20.3.0.
3
+ `EgFormTextInput` is a reactive-forms wrapper that bundles a `<label>`, a styled `<input hlmInput>`, and error/hint text into one form-ready row. It is the Angular equivalent of shadcn/ui's `<FormField> + <Input>` pattern for single-line text entry (text, email, password, number, …), with generated ids, `aria-describedby` wiring, and `disabled` resolution handled for you.
4
4
 
5
- ## Code scaffolding
5
+ > **Ships as:** `@egose/shadcn-theme-ng/form-text-input` (plain Tailwind) and `@egose/shadcn-theme-ng-tw/form-text-input` (`tw:`-prefixed variant). See the [package README](../../README.md) for installation, peer dependencies, and the Tailwind-variant contract. Do not publish this project directory independently.
6
6
 
7
- Angular CLI includes powerful code scaffolding tools. To generate a new component, run:
7
+ ## Installation
8
8
 
9
9
  ```bash
10
- ng generate component component-name
10
+ # Plain Tailwind (no prefix)
11
+ npm install @egose/shadcn-theme-ng
12
+
13
+ # Or the tw:-prefixed variant
14
+ npm install @egose/shadcn-theme-ng-tw
11
15
  ```
12
16
 
13
- For a complete list of available schematics (such as `components`, `directives`, or `pipes`), run:
17
+ Peer dependencies (Angular, `@angular/forms`, CDK, `@spartan-ng/brain`, `rxjs`) are documented in the [package README](../../README.md#peer-dependencies).
14
18
 
15
- ```bash
16
- ng generate --help
19
+ ```ts
20
+ import { EgFormTextInput } from '@egose/shadcn-theme-ng/form-text-input';
21
+ // tw variant:
22
+ // import { EgFormTextInput } from '@egose/shadcn-theme-ng-tw/form-text-input';
17
23
  ```
18
24
 
19
- ## Building
25
+ ## Imports
20
26
 
21
- To build the library, run:
27
+ The public API exports exactly one symbol — the standalone component `EgFormTextInput`. There is no `*Imports` array and no `*Module` for this subpath; import the component class directly.
22
28
 
23
- ```bash
24
- ng build form-text-input
29
+ ```ts
30
+ import { EgFormTextInput } from '@egose/shadcn-theme-ng/form-text-input';
31
+
32
+ @Component({
33
+ standalone: true,
34
+ imports: [ReactiveFormsModule, EgFormTextInput],
35
+ template: `
36
+ <form [formGroup]="form">
37
+ <eg-form-text-input controlName="username" label="Username" placeholder="jane_doe" />
38
+ </form>
39
+ `,
40
+ })
41
+ export class MyForm {}
25
42
  ```
26
43
 
27
- This command will compile your project, and the build artifacts will be placed in the `dist/` directory.
44
+ Requirements:
28
45
 
29
- ### Publishing the Library
46
+ - Must sit inside a `<form [formGroup]>` (it injects `FormGroupDirective` and provides `ControlContainer`).
47
+ - `controlName` is required — forwarded as `[formControlName]` to the inner `<input>`.
48
+ - The bound `FormControl` holds a `string` (or `number` for numeric inputs).
30
49
 
31
- Once the project is built, you can publish your library by following these steps:
50
+ ## Anatomy / Structure
32
51
 
33
- 1. Navigate to the `dist` directory:
52
+ ```html
53
+ <eg-form-text-input controlName="username" label="Username">
54
+ <!-- rendered internally -->
55
+ <hlm-form-field>
56
+ <label hlmLabel for="<effectiveId>">Username <span>*</span></label>
57
+ <input
58
+ hlmInput
59
+ id="<effectiveId>"
60
+ name="username"
61
+ formControlName="username"
62
+ type="text"
63
+ placeholder="…"
64
+ aria-describedby="<effectiveId>-error | <effectiveId>-hint"
65
+ />
66
+ <hlm-error id="<effectiveId>-error">…</hlm-error>
67
+ <hlm-hint id="<effectiveId>-hint">…</hlm-hint>
68
+ </hlm-form-field>
69
+ </eg-form-text-input>
70
+ ```
34
71
 
35
- ```bash
36
- cd dist/form-text-input
37
- ```
72
+ Real selectors: `eg-form-text-input`, `hlm-form-field`, `label[hlmLabel]`, `input[hlmInput]`, `hlm-error`, `hlm-hint`.
38
73
 
39
- 2. Run the `npm publish` command to publish your library to the npm registry:
40
- ```bash
41
- npm publish
42
- ```
74
+ ## API reference
43
75
 
44
- ## Running unit tests
76
+ ### `eg-form-text-input` — `EgFormTextInput`
45
77
 
46
- To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
78
+ | Input | Type | Default | Description |
79
+ | --------------------- | -------------------------- | ----------- | ------------------------------------------------------------------------------------------------ |
80
+ | `label` | `string \| undefined` | `undefined` | Field label rendered as `<label hlmLabel>` bound to the input id. |
81
+ | `controlName` | `string` | `''` | **Required.** Control name in the parent `FormGroup`; forwarded as `formControlName` and `name`. |
82
+ | `controlId` | `string \| undefined` | `undefined` | Explicit id; falls back to `id`, then generated `eg-form-text-input-…`. |
83
+ | `id` | `string \| undefined` | `undefined` | Alias for an explicit id (same fallback chain). |
84
+ | `error` | `string \| undefined` | `undefined` | Error text rendered in `<hlm-error>`. |
85
+ | `hint` | `string \| undefined` | `undefined` | Hint text rendered in `<hlm-hint>`. |
86
+ | `name` | `string \| undefined` | `undefined` | `name` attribute fallback when `controlName` is empty. |
87
+ | `type` | `string` | `'text'` | Native input `type` (`text`, `email`, `password`, `number`, `tel`, `url`, …). |
88
+ | `placeholder` | `string` | `''` | Placeholder text. |
89
+ | `readonly` | `boolean` | `false` | Native `readonly` attribute. |
90
+ | `disabled` | `boolean` | `false` | Wrapper-level disable (OR-ed with the reactive disabled state — see `effectiveDisabled()`). |
91
+ | `maxlength` | `string \| number \| null` | `null` | Native `maxlength`. |
92
+ | `minlength` | `string \| number \| null` | `null` | Native `minlength`. |
93
+ | `max` | `string \| number \| null` | `null` | Native `max` (number/date inputs). |
94
+ | `min` | `string \| number \| null` | `null` | Native `min` (number/date inputs). |
95
+ | `pattern` | `string \| RegExp` | `''` | Native `pattern` attribute. |
96
+ | `autocomplete` | `string \| undefined` | `undefined` | Native `autocomplete` (e.g. `email`, `current-password`). |
97
+ | `autofocus` | `boolean` | `false` | Native `autofocus`. |
98
+ | `required` | `boolean` | `false` | Native `required` + red `*` on the label. |
99
+ | `class` (`userClass`) | `ClassValue` | `''` | Extra host classes (merged over `tw:flex tw:flex-col`). |
100
+ | `labelClass` | `string` | `''` | Extra label classes (merged over `tw:mb-1 tw:gap-0`). |
101
+ | `inputClass` | `string` | `''` | Extra input classes (merged over `tw:mb-1`). |
102
+ | `errorClass` | `string` | `''` | Extra error classes (merged over `tw:mt-0`). |
103
+ | `hintClass` | `string` | `''` | Extra hint classes (merged over `tw:mt-0`). |
47
104
 
48
- ```bash
49
- ng test
105
+ No outputs. Methods: `describedBy(): string | null` (error id when invalid + dirty/touched, else hint id, else `null`); `effectiveDisabled(): boolean` (`disabled()` OR the control's reactive disabled state — bound as the native `disabled` attribute). Readonly computeds: `effectiveId()`, `errorId()`, `hintId()`.
106
+
107
+ ## Examples
108
+
109
+ ### 1. Basic login row (email + password)
110
+
111
+ ```ts
112
+ import { Component } from '@angular/core';
113
+ import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
114
+ import { EgFormTextInput } from '@egose/shadcn-theme-ng/form-text-input';
115
+
116
+ @Component({
117
+ standalone: true,
118
+ imports: [ReactiveFormsModule, EgFormTextInput],
119
+ template: `
120
+ <form [formGroup]="form" (ngSubmit)="login()">
121
+ <eg-form-text-input
122
+ controlName="email"
123
+ label="Email"
124
+ type="email"
125
+ placeholder="you@example.com"
126
+ autocomplete="email"
127
+ />
128
+ <eg-form-text-input
129
+ controlName="password"
130
+ label="Password"
131
+ type="password"
132
+ placeholder="••••••••"
133
+ autocomplete="current-password"
134
+ />
135
+ <button type="submit">Sign in</button>
136
+ </form>
137
+ `,
138
+ })
139
+ export class LoginExample {
140
+ readonly form = new FormGroup({
141
+ email: new FormControl<string>('', { nonNullable: true }),
142
+ password: new FormControl<string>('', { nonNullable: true }),
143
+ });
144
+
145
+ login() {
146
+ console.log(this.form.getRawValue());
147
+ }
148
+ }
50
149
  ```
51
150
 
52
- ## Running end-to-end tests
151
+ ### 2. Validation with per-rule error messages
53
152
 
54
- For end-to-end (e2e) testing, run:
153
+ ```ts
154
+ import { Component } from '@angular/core';
155
+ import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
156
+ import { EgFormTextInput } from '@egose/shadcn-theme-ng/form-text-input';
55
157
 
56
- ```bash
57
- ng e2e
158
+ @Component({
159
+ standalone: true,
160
+ imports: [ReactiveFormsModule, EgFormTextInput],
161
+ template: `
162
+ <form [formGroup]="form" (ngSubmit)="submit()">
163
+ <eg-form-text-input
164
+ controlName="username"
165
+ label="Username"
166
+ placeholder="jane_doe"
167
+ [error]="usernameError()"
168
+ hint="3–20 characters, letters and numbers"
169
+ required
170
+ />
171
+ <button type="submit">Continue</button>
172
+ </form>
173
+ `,
174
+ })
175
+ export class ValidationExample {
176
+ readonly form = new FormGroup({
177
+ username: new FormControl<string>('', {
178
+ nonNullable: true,
179
+ validators: [Validators.required, Validators.minLength(3), Validators.pattern(/^[a-z0-9_]+$/i)],
180
+ }),
181
+ });
182
+
183
+ usernameError(): string | undefined {
184
+ const c = this.form.controls.username;
185
+ if (c.valid || !(c.dirty || c.touched)) return undefined;
186
+ if (c.hasError('required')) return 'Username is required';
187
+ if (c.hasError('minlength')) return 'Use at least 3 characters';
188
+ return 'Only letters, numbers, and _';
189
+ }
190
+
191
+ submit() {
192
+ if (this.form.invalid) {
193
+ this.form.markAllAsTouched();
194
+ return;
195
+ }
196
+ }
197
+ }
198
+ ```
199
+
200
+ ### 3. Number input with min/max + readonly display field
201
+
202
+ ```ts
203
+ import { Component } from '@angular/core';
204
+ import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
205
+ import { EgFormTextInput } from '@egose/shadcn-theme-ng/form-text-input';
206
+
207
+ @Component({
208
+ standalone: true,
209
+ imports: [ReactiveFormsModule, EgFormTextInput],
210
+ template: `
211
+ <form [formGroup]="form">
212
+ <eg-form-text-input
213
+ controlName="quantity"
214
+ label="Quantity"
215
+ type="number"
216
+ [min]="1"
217
+ [max]="99"
218
+ hint="Between 1 and 99"
219
+ />
220
+ <eg-form-text-input controlName="orderId" label="Order ID" [readonly]="true" />
221
+ </form>
222
+ `,
223
+ })
224
+ export class NumberExample {
225
+ readonly form = new FormGroup({
226
+ quantity: new FormControl<number>(1, { nonNullable: true }),
227
+ orderId: new FormControl<string>('ORD-2026-001', { nonNullable: true }),
228
+ });
229
+ }
58
230
  ```
59
231
 
60
- Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
232
+ ### 4. Disabled via input vs via form API
233
+
234
+ ```ts
235
+ import { Component, signal } from '@angular/core';
236
+ import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
237
+ import { EgFormTextInput } from '@egose/shadcn-theme-ng/form-text-input';
238
+
239
+ @Component({
240
+ standalone: true,
241
+ imports: [ReactiveFormsModule, EgFormTextInput],
242
+ template: `
243
+ <form [formGroup]="form">
244
+ <eg-form-text-input
245
+ controlName="nickname"
246
+ label="Nickname"
247
+ placeholder="How should we call you?"
248
+ [disabled]="locked()"
249
+ />
250
+ <button type="button" (click)="locked.update((v) => !v)">Toggle input</button>
251
+ <button type="button" (click)="disableViaApi()">Disable via form API</button>
252
+ </form>
253
+ `,
254
+ })
255
+ export class DisabledExample {
256
+ readonly form = new FormGroup({
257
+ nickname: new FormControl<string>('', { nonNullable: true }),
258
+ });
259
+ readonly locked = signal(false);
260
+
261
+ disableViaApi() {
262
+ this.form.controls.nickname.disable(); // effectiveDisabled() picks this up too
263
+ }
264
+ }
265
+ ```
266
+
267
+ ### 5. Length constraints with a live counter hint
268
+
269
+ ```ts
270
+ import { Component } from '@angular/core';
271
+ import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
272
+ import { EgFormTextInput } from '@egose/shadcn-theme-ng/form-text-input';
273
+
274
+ @Component({
275
+ standalone: true,
276
+ imports: [ReactiveFormsModule, EgFormTextInput],
277
+ template: `
278
+ <form [formGroup]="form">
279
+ <eg-form-text-input
280
+ controlName="headline"
281
+ label="Headline"
282
+ placeholder="One-line summary"
283
+ [maxlength]="80"
284
+ [minlength]="10"
285
+ [hint]="counter()"
286
+ error="Headline must be 10–80 characters"
287
+ />
288
+ </form>
289
+ `,
290
+ })
291
+ export class CounterExample {
292
+ readonly form = new FormGroup({
293
+ headline: new FormControl<string>('', {
294
+ nonNullable: true,
295
+ validators: [Validators.minLength(10), Validators.maxLength(80)],
296
+ }),
297
+ });
298
+
299
+ counter(): string {
300
+ return `${this.form.controls.headline.value.length}/80`;
301
+ }
302
+ }
303
+ ```
304
+
305
+ ### 6. Full profile form (composition of several inputs)
306
+
307
+ ```ts
308
+ import { Component } from '@angular/core';
309
+ import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
310
+ import { EgFormTextInput } from '@egose/shadcn-theme-ng/form-text-input';
311
+
312
+ @Component({
313
+ standalone: true,
314
+ imports: [ReactiveFormsModule, EgFormTextInput],
315
+ template: `
316
+ <form [formGroup]="form" (ngSubmit)="save()" class="tw:grid tw:gap-4 sm:tw:grid-cols-2">
317
+ <eg-form-text-input controlName="firstName" label="First name" autocomplete="given-name" required />
318
+ <eg-form-text-input controlName="lastName" label="Last name" autocomplete="family-name" required />
319
+ <eg-form-text-input
320
+ class="sm:tw:col-span-2"
321
+ controlName="email"
322
+ label="Email"
323
+ type="email"
324
+ autocomplete="email"
325
+ error="Enter a valid email address"
326
+ required
327
+ />
328
+ <eg-form-text-input controlName="phone" label="Phone (optional)" type="tel" autocomplete="tel" />
329
+ <eg-form-text-input controlName="website" label="Website (optional)" type="url" placeholder="https://" />
330
+ <button class="sm:tw:col-span-2" type="submit" [disabled]="form.invalid">Save profile</button>
331
+ </form>
332
+ `,
333
+ })
334
+ export class ProfileExample {
335
+ readonly form = new FormGroup({
336
+ firstName: new FormControl<string>('', { nonNullable: true, validators: Validators.required }),
337
+ lastName: new FormControl<string>('', { nonNullable: true, validators: Validators.required }),
338
+ email: new FormControl<string>('', {
339
+ nonNullable: true,
340
+ validators: [Validators.required, Validators.email],
341
+ }),
342
+ phone: new FormControl<string>('', { nonNullable: true }),
343
+ website: new FormControl<string>('', { nonNullable: true }),
344
+ });
345
+
346
+ save() {
347
+ if (this.form.invalid) {
348
+ this.form.markAllAsTouched();
349
+ return;
350
+ }
351
+ console.log(this.form.getRawValue());
352
+ }
353
+ }
354
+ ```
355
+
356
+ ## Accessibility notes
357
+
358
+ - `<label [for]>` targets the input id (explicit `controlId`/`id` or generated), so label clicks focus the field.
359
+ - `aria-describedby` points at the error element only when the control is invalid and dirty/touched, otherwise at the hint — announce validation at the right moment without duplicate announcements.
360
+ - The inner input is `HlmInput` (spartan-ng `BrnInput` + field-described-by), so invalid styling via `data-matches-spartan-invalid` and described-by propagation come for free.
361
+ - Always pair the visual `required` star with a real validator; use native `type`/`autocomplete` values so password managers and mobile keyboards behave.
362
+
363
+ ## Theming / CSS variables
364
+
365
+ No component-specific CSS variables; the input look comes from the shared theme tokens consumed by `HlmInput`. Adjust density with `inputClass` (e.g. `tw:h-11`), label/error/hint spacing with their `*Class` inputs.
61
366
 
62
- ## Additional Resources
367
+ ## Related subpaths
63
368
 
64
- For more information on using the Angular CLI, including detailed command references, visit the [Angular CLI Overview and Command Reference](https://angular.dev/tools/cli) page.
369
+ - `@egose/shadcn-theme-ng/input` — the raw `HlmInput` directive for non-form or template-driven inputs.
370
+ - `@egose/shadcn-theme-ng/form-textarea` — the multiline sibling.
371
+ - `@egose/shadcn-theme-ng/form-field` — `HlmFormField`, `HlmError`, `HlmHint`, `HlmFormIdGenerator`.
372
+ - `@egose/shadcn-theme-ng/label` — `HlmLabel`.