@egose/shadcn-theme-ng-tw 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 (244) hide show
  1. package/README.md +130 -13
  2. package/accordion/README.md +376 -34
  3. package/accordion/fesm2022/accordion.mjs +20 -20
  4. package/alert/README.md +343 -34
  5. package/alert/fesm2022/alert.mjs +18 -18
  6. package/alert-dialog/README.md +471 -5
  7. package/alert-dialog/fesm2022/alert-dialog.mjs +48 -48
  8. package/alert-dialog/types/alert-dialog.d.ts +1 -1
  9. package/aspect-ratio/README.md +272 -5
  10. package/aspect-ratio/fesm2022/aspect-ratio.mjs +9 -9
  11. package/autocomplete/README.md +473 -34
  12. package/autocomplete/fesm2022/autocomplete.mjs +54 -54
  13. package/autocomplete/types/autocomplete.d.ts +1 -1
  14. package/avatar/README.md +357 -5
  15. package/avatar/fesm2022/avatar.mjs +32 -32
  16. package/badge/README.md +289 -34
  17. package/badge/fesm2022/badge.mjs +4 -4
  18. package/basic-alert/README.md +324 -34
  19. package/basic-alert/fesm2022/basic-alert.mjs +4 -4
  20. package/basic-alert/types/basic-alert.d.ts +1 -1
  21. package/breadcrumb/README.md +406 -5
  22. package/breadcrumb/fesm2022/breadcrumb.mjs +34 -34
  23. package/button/README.md +453 -34
  24. package/button/fesm2022/button.mjs +96 -118
  25. package/button/types/button.d.ts +7 -10
  26. package/button-group/README.md +318 -5
  27. package/button-group/fesm2022/button-group.mjs +20 -20
  28. package/calendar/README.md +328 -34
  29. package/calendar/fesm2022/calendar.mjs +154 -154
  30. package/card/README.md +331 -5
  31. package/card/fesm2022/card.mjs +33 -33
  32. package/carousel/README.md +333 -5
  33. package/carousel/fesm2022/carousel.mjs +47 -44
  34. package/carousel/types/carousel.d.ts +1 -1
  35. package/checkbox/README.md +291 -34
  36. package/checkbox/fesm2022/checkbox.mjs +22 -20
  37. package/checkbox/types/checkbox.d.ts +5 -4
  38. package/collapsible/README.md +332 -5
  39. package/collapsible/fesm2022/collapsible.mjs +15 -15
  40. package/combobox/README.md +507 -5
  41. package/combobox/fesm2022/combobox.mjs +95 -92
  42. package/combobox/types/combobox.d.ts +1 -1
  43. package/command/README.md +435 -5
  44. package/command/fesm2022/command.mjs +56 -56
  45. package/confirmation-dialog/README.md +272 -34
  46. package/confirmation-dialog/fesm2022/confirmation-dialog.mjs +13 -12
  47. package/confirmation-dialog/types/confirmation-dialog.d.ts +9 -5
  48. package/context-menu/README.md +366 -5
  49. package/context-menu/fesm2022/context-menu.mjs +9 -9
  50. package/date-picker/README.md +436 -34
  51. package/date-picker/fesm2022/date-picker.mjs +79 -68
  52. package/date-picker/types/date-picker.d.ts +8 -4
  53. package/dialog/README.md +419 -34
  54. package/dialog/fesm2022/dialog.mjs +48 -48
  55. package/drawer/README.md +395 -5
  56. package/drawer/fesm2022/drawer.mjs +45 -45
  57. package/dropdown-menu/README.md +417 -5
  58. package/dropdown-menu/fesm2022/dropdown-menu.mjs +85 -73
  59. package/dropdown-menu/types/dropdown-menu.d.ts +4 -0
  60. package/empty/README.md +329 -5
  61. package/empty/fesm2022/empty.mjs +31 -31
  62. package/field/README.md +385 -5
  63. package/field/fesm2022/field.mjs +59 -59
  64. package/field/types/field.d.ts +1 -1
  65. package/form-checkbox/README.md +283 -34
  66. package/form-checkbox/fesm2022/form-checkbox.mjs +37 -19
  67. package/form-checkbox/types/form-checkbox.d.ts +7 -1
  68. package/form-date-picker/README.md +293 -34
  69. package/form-date-picker/fesm2022/form-date-picker.mjs +59 -24
  70. package/form-date-picker/types/form-date-picker.d.ts +7 -1
  71. package/form-field/README.md +327 -34
  72. package/form-field/fesm2022/form-field.mjs +30 -16
  73. package/form-field/types/form-field.d.ts +28 -20
  74. package/form-field-simple/README.md +311 -34
  75. package/form-field-simple/fesm2022/form-field-simple.mjs +4 -4
  76. package/form-searchable-multiselect/README.md +332 -34
  77. package/form-searchable-multiselect/fesm2022/form-searchable-multiselect.mjs +41 -21
  78. package/form-searchable-multiselect/types/form-searchable-multiselect.d.ts +8 -2
  79. package/form-select/README.md +321 -34
  80. package/form-select/fesm2022/form-select.mjs +62 -29
  81. package/form-select/types/form-select.d.ts +7 -1
  82. package/form-text-input/README.md +342 -34
  83. package/form-text-input/fesm2022/form-text-input.mjs +43 -55
  84. package/form-text-input/types/form-text-input.d.ts +9 -12
  85. package/form-textarea/README.md +318 -34
  86. package/form-textarea/fesm2022/form-textarea.mjs +42 -23
  87. package/form-textarea/types/form-textarea.d.ts +8 -1
  88. package/hover-card/README.md +256 -5
  89. package/hover-card/fesm2022/hover-card.mjs +19 -19
  90. package/icon/README.md +210 -34
  91. package/icon/fesm2022/icon.mjs +8 -8
  92. package/input/README.md +240 -34
  93. package/input/fesm2022/input.mjs +17 -12
  94. package/input/types/input.d.ts +2 -1
  95. package/input-group/README.md +335 -5
  96. package/input-group/fesm2022/input-group.mjs +37 -37
  97. package/input-group/types/input-group.d.ts +3 -3
  98. package/input-otp/README.md +375 -5
  99. package/input-otp/fesm2022/input-otp.mjs +27 -27
  100. package/item/README.md +385 -5
  101. package/item/fesm2022/item.mjs +54 -54
  102. package/kbd/README.md +291 -5
  103. package/kbd/fesm2022/kbd.mjs +13 -13
  104. package/label/README.md +243 -34
  105. package/label/fesm2022/label.mjs +9 -9
  106. package/label/types/label.d.ts +1 -1
  107. package/layout-simple/README.md +174 -44
  108. package/layout-simple/fesm2022/layout-simple.mjs +917 -398
  109. package/layout-simple/types/layout-simple.d.ts +180 -129
  110. package/menu/README.md +418 -0
  111. package/menu/fesm2022/menu.mjs +461 -0
  112. package/menu/package.json +24 -0
  113. package/menu/types/menu.d.ts +137 -0
  114. package/menubar/README.md +343 -5
  115. package/menubar/fesm2022/menubar.mjs +13 -13
  116. package/native-select/README.md +323 -5
  117. package/native-select/fesm2022/native-select.mjs +19 -19
  118. package/navigation-menu/README.md +369 -5
  119. package/navigation-menu/fesm2022/navigation-menu.mjs +37 -37
  120. package/package.json +183 -154
  121. package/pagination/README.md +388 -5
  122. package/pagination/fesm2022/pagination.mjs +63 -63
  123. package/popover/README.md +302 -34
  124. package/popover/fesm2022/popover.mjs +30 -30
  125. package/progress/README.md +311 -5
  126. package/progress/fesm2022/progress.mjs +13 -13
  127. package/radio-group/README.md +335 -34
  128. package/radio-group/fesm2022/radio-group.mjs +26 -19
  129. package/resizable/README.md +269 -5
  130. package/resizable/fesm2022/resizable.mjs +19 -19
  131. package/scroll-area/README.md +233 -5
  132. package/scroll-area/fesm2022/scroll-area.mjs +9 -9
  133. package/searchable-multiselect/README.md +294 -34
  134. package/searchable-multiselect/fesm2022/searchable-multiselect.mjs +63 -16
  135. package/searchable-multiselect/types/searchable-multiselect.d.ts +8 -2
  136. package/select/README.md +408 -34
  137. package/select/fesm2022/select.mjs +80 -72
  138. package/select/types/select.d.ts +3 -1
  139. package/separator/README.md +193 -34
  140. package/separator/fesm2022/separator.mjs +9 -9
  141. package/separator/types/separator.d.ts +1 -1
  142. package/sheet/README.md +282 -34
  143. package/sheet/fesm2022/sheet.mjs +30 -30
  144. package/sidebar/README.md +457 -5
  145. package/sidebar/fesm2022/sidebar.mjs +136 -134
  146. package/sidebar/types/sidebar.d.ts +3 -3
  147. package/skeleton/README.md +217 -5
  148. package/skeleton/fesm2022/skeleton.mjs +9 -9
  149. package/slider/README.md +273 -5
  150. package/slider/fesm2022/slider.mjs +29 -25
  151. package/sonner/README.md +317 -34
  152. package/sonner/fesm2022/sonner.mjs +9 -9
  153. package/spinner/README.md +255 -34
  154. package/spinner/fesm2022/spinner.mjs +8 -8
  155. package/switch/README.md +281 -34
  156. package/switch/fesm2022/switch.mjs +12 -14
  157. package/table/README.md +423 -5
  158. package/table/fesm2022/table.mjs +41 -41
  159. package/tabs/README.md +382 -34
  160. package/tabs/fesm2022/tabs.mjs +48 -38
  161. package/textarea/README.md +282 -5
  162. package/textarea/fesm2022/textarea.mjs +9 -9
  163. package/toggle/README.md +270 -5
  164. package/toggle/fesm2022/toggle.mjs +14 -14
  165. package/toggle-group/README.md +340 -5
  166. package/toggle-group/fesm2022/toggle-group.mjs +13 -13
  167. package/toggle-group/types/toggle-group.d.ts +13 -7
  168. package/tooltip/README.md +240 -34
  169. package/tooltip/fesm2022/tooltip.mjs +14 -14
  170. package/typography/README.md +271 -5
  171. package/typography/fesm2022/typography.mjs +53 -53
  172. package/typography/types/typography.d.ts +12 -12
  173. package/utils/README.md +274 -34
  174. package/utils/fesm2022/utils.mjs +68 -44
  175. package/accordion/fesm2022/accordion.mjs.map +0 -1
  176. package/alert/fesm2022/alert.mjs.map +0 -1
  177. package/alert-dialog/fesm2022/alert-dialog.mjs.map +0 -1
  178. package/aspect-ratio/fesm2022/aspect-ratio.mjs.map +0 -1
  179. package/autocomplete/fesm2022/autocomplete.mjs.map +0 -1
  180. package/avatar/fesm2022/avatar.mjs.map +0 -1
  181. package/badge/fesm2022/badge.mjs.map +0 -1
  182. package/basic-alert/fesm2022/basic-alert.mjs.map +0 -1
  183. package/breadcrumb/fesm2022/breadcrumb.mjs.map +0 -1
  184. package/button/fesm2022/button.mjs.map +0 -1
  185. package/button-group/fesm2022/button-group.mjs.map +0 -1
  186. package/calendar/fesm2022/calendar.mjs.map +0 -1
  187. package/card/fesm2022/card.mjs.map +0 -1
  188. package/carousel/fesm2022/carousel.mjs.map +0 -1
  189. package/checkbox/fesm2022/checkbox.mjs.map +0 -1
  190. package/collapsible/fesm2022/collapsible.mjs.map +0 -1
  191. package/combobox/fesm2022/combobox.mjs.map +0 -1
  192. package/command/fesm2022/command.mjs.map +0 -1
  193. package/confirmation-dialog/fesm2022/confirmation-dialog.mjs.map +0 -1
  194. package/context-menu/fesm2022/context-menu.mjs.map +0 -1
  195. package/date-picker/fesm2022/date-picker.mjs.map +0 -1
  196. package/dialog/fesm2022/dialog.mjs.map +0 -1
  197. package/drawer/fesm2022/drawer.mjs.map +0 -1
  198. package/dropdown-menu/fesm2022/dropdown-menu.mjs.map +0 -1
  199. package/empty/fesm2022/empty.mjs.map +0 -1
  200. package/field/fesm2022/field.mjs.map +0 -1
  201. package/form-checkbox/fesm2022/form-checkbox.mjs.map +0 -1
  202. package/form-date-picker/fesm2022/form-date-picker.mjs.map +0 -1
  203. package/form-field/fesm2022/form-field.mjs.map +0 -1
  204. package/form-field-simple/fesm2022/form-field-simple.mjs.map +0 -1
  205. package/form-searchable-multiselect/fesm2022/form-searchable-multiselect.mjs.map +0 -1
  206. package/form-select/fesm2022/form-select.mjs.map +0 -1
  207. package/form-text-input/fesm2022/form-text-input.mjs.map +0 -1
  208. package/form-textarea/fesm2022/form-textarea.mjs.map +0 -1
  209. package/hover-card/fesm2022/hover-card.mjs.map +0 -1
  210. package/icon/fesm2022/icon.mjs.map +0 -1
  211. package/input/fesm2022/input.mjs.map +0 -1
  212. package/input-group/fesm2022/input-group.mjs.map +0 -1
  213. package/input-otp/fesm2022/input-otp.mjs.map +0 -1
  214. package/item/fesm2022/item.mjs.map +0 -1
  215. package/kbd/fesm2022/kbd.mjs.map +0 -1
  216. package/label/fesm2022/label.mjs.map +0 -1
  217. package/layout-simple/fesm2022/layout-simple.mjs.map +0 -1
  218. package/menubar/fesm2022/menubar.mjs.map +0 -1
  219. package/native-select/fesm2022/native-select.mjs.map +0 -1
  220. package/navigation-menu/fesm2022/navigation-menu.mjs.map +0 -1
  221. package/pagination/fesm2022/pagination.mjs.map +0 -1
  222. package/popover/fesm2022/popover.mjs.map +0 -1
  223. package/progress/fesm2022/progress.mjs.map +0 -1
  224. package/radio-group/fesm2022/radio-group.mjs.map +0 -1
  225. package/resizable/fesm2022/resizable.mjs.map +0 -1
  226. package/scroll-area/fesm2022/scroll-area.mjs.map +0 -1
  227. package/searchable-multiselect/fesm2022/searchable-multiselect.mjs.map +0 -1
  228. package/select/fesm2022/select.mjs.map +0 -1
  229. package/separator/fesm2022/separator.mjs.map +0 -1
  230. package/sheet/fesm2022/sheet.mjs.map +0 -1
  231. package/sidebar/fesm2022/sidebar.mjs.map +0 -1
  232. package/skeleton/fesm2022/skeleton.mjs.map +0 -1
  233. package/slider/fesm2022/slider.mjs.map +0 -1
  234. package/sonner/fesm2022/sonner.mjs.map +0 -1
  235. package/spinner/fesm2022/spinner.mjs.map +0 -1
  236. package/switch/fesm2022/switch.mjs.map +0 -1
  237. package/table/fesm2022/table.mjs.map +0 -1
  238. package/tabs/fesm2022/tabs.mjs.map +0 -1
  239. package/textarea/fesm2022/textarea.mjs.map +0 -1
  240. package/toggle/fesm2022/toggle.mjs.map +0 -1
  241. package/toggle-group/fesm2022/toggle-group.mjs.map +0 -1
  242. package/tooltip/fesm2022/tooltip.mjs.map +0 -1
  243. package/typography/fesm2022/typography.mjs.map +0 -1
  244. package/utils/fesm2022/utils.mjs.map +0 -1
@@ -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`.
@@ -1,45 +1,15 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, Directive, computed, Component } from '@angular/core';
2
+ import { inject, input, computed, Component } from '@angular/core';
3
3
  import * as i1 from '@angular/forms';
4
- import { ReactiveFormsModule, FormGroupDirective, ControlContainer } from '@angular/forms';
5
- import { HlmFormField, HlmError, HlmHint } from '@egose/shadcn-theme-ng-tw/form-field';
4
+ import { FormGroupDirective, ReactiveFormsModule, ControlContainer } from '@angular/forms';
5
+ import { HlmFormIdGenerator, HlmFormField, HlmError, HlmHint } from '@egose/shadcn-theme-ng-tw/form-field';
6
6
  import { HlmLabel } from '@egose/shadcn-theme-ng-tw/label';
7
7
  import { HlmInput } from '@egose/shadcn-theme-ng-tw/input';
8
8
  import { hlm } from '@egose/shadcn-theme-ng-tw/utils';
9
9
 
10
- // NOT USED
11
- class SpreadAttrsDirective {
12
- el;
13
- renderer;
14
- attrs = input({}, /* @ts-ignore */
15
- ...(ngDevMode ? [{ debugName: "attrs" }] : /* istanbul ignore next */ []));
16
- constructor(el, renderer) {
17
- this.el = el;
18
- this.renderer = renderer;
19
- }
20
- ngOnChanges() {
21
- const obj = this.attrs();
22
- for (const [key, value] of Object.entries(obj)) {
23
- if (value === null || value === undefined || value === false) {
24
- this.renderer.removeAttribute(this.el.nativeElement, key);
25
- }
26
- else {
27
- this.renderer.setAttribute(this.el.nativeElement, key, String(value));
28
- }
29
- }
30
- }
31
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: SpreadAttrsDirective, deps: [{ token: i0.ElementRef }, { token: i0.Renderer2 }], target: i0.ɵɵFactoryTarget.Directive });
32
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "17.1.0", version: "22.0.7", type: SpreadAttrsDirective, isStandalone: true, selector: "[spreadAttrs]", inputs: { attrs: { classPropertyName: "attrs", publicName: "attrs", isSignal: true, isRequired: false, transformFunction: null } }, usesOnChanges: true, ngImport: i0 });
33
- }
34
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: SpreadAttrsDirective, decorators: [{
35
- type: Directive,
36
- args: [{
37
- selector: '[spreadAttrs]',
38
- standalone: true,
39
- }]
40
- }], ctorParameters: () => [{ type: i0.ElementRef }, { type: i0.Renderer2 }], propDecorators: { attrs: [{ type: i0.Input, args: [{ isSignal: true, alias: "attrs", required: false }] }] } });
41
- let nextId = 0;
42
10
  class EgFormTextInput {
11
+ formGroupDirective = inject(FormGroupDirective);
12
+ generatedId = inject(HlmFormIdGenerator).generate('eg-form-text-input');
43
13
  label = input(undefined, /* @ts-ignore */
44
14
  ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
45
15
  controlId = input(undefined, /* @ts-ignore */
@@ -51,9 +21,8 @@ class EgFormTextInput {
51
21
  hint = input(undefined, /* @ts-ignore */
52
22
  ...(ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []));
53
23
  // HTML/input attributes
54
- id = input(crypto.randomUUID(), /* @ts-ignore */
24
+ id = input(undefined, /* @ts-ignore */
55
25
  ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
56
- // id = input<string>(`eg-form-text-input-${nextId++}`);
57
26
  name = input(undefined, /* @ts-ignore */
58
27
  ...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
59
28
  type = input('text', /* @ts-ignore */
@@ -80,6 +49,23 @@ class EgFormTextInput {
80
49
  ...(ngDevMode ? [{ debugName: "autofocus" }] : /* istanbul ignore next */ []));
81
50
  required = input(false, /* @ts-ignore */
82
51
  ...(ngDevMode ? [{ debugName: "required" }] : /* istanbul ignore next */ []));
52
+ effectiveId = computed(() => this.controlId() || this.id() || this.generatedId, /* @ts-ignore */
53
+ ...(ngDevMode ? [{ debugName: "effectiveId" }] : /* istanbul ignore next */ []));
54
+ errorId = computed(() => `${this.effectiveId()}-error`, /* @ts-ignore */
55
+ ...(ngDevMode ? [{ debugName: "errorId" }] : /* istanbul ignore next */ []));
56
+ hintId = computed(() => `${this.effectiveId()}-hint`, /* @ts-ignore */
57
+ ...(ngDevMode ? [{ debugName: "hintId" }] : /* istanbul ignore next */ []));
58
+ describedBy() {
59
+ const control = this.formGroupDirective.form.get(this.controlName());
60
+ return this.error() && control?.invalid && (control.dirty || control.touched)
61
+ ? this.errorId()
62
+ : this.hint()
63
+ ? this.hintId()
64
+ : null;
65
+ }
66
+ effectiveDisabled() {
67
+ return this.disabled() || !!this.formGroupDirective.form.get(this.controlName())?.disabled;
68
+ }
83
69
  // Styling classes
84
70
  userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
85
71
  labelClass = input('', /* @ts-ignore */
@@ -101,10 +87,9 @@ class EgFormTextInput {
101
87
  ...(ngDevMode ? [{ debugName: "$errorClass" }] : /* istanbul ignore next */ []));
102
88
  $hintClass = computed(() => hlm('tw:mt-0', this.hintClass()), /* @ts-ignore */
103
89
  ...(ngDevMode ? [{ debugName: "$hintClass" }] : /* istanbul ignore next */ []));
104
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: EgFormTextInput, deps: [], target: i0.ɵɵFactoryTarget.Component });
105
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: EgFormTextInput, isStandalone: true, selector: "eg-form-text-input", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, controlId: { classPropertyName: "controlId", publicName: "controlId", isSignal: true, isRequired: false, transformFunction: null }, controlName: { classPropertyName: "controlName", publicName: "controlName", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, maxlength: { classPropertyName: "maxlength", publicName: "maxlength", isSignal: true, isRequired: false, transformFunction: null }, minlength: { classPropertyName: "minlength", publicName: "minlength", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, pattern: { classPropertyName: "pattern", publicName: "pattern", isSignal: true, isRequired: false, transformFunction: null }, autocomplete: { classPropertyName: "autocomplete", publicName: "autocomplete", isSignal: true, isRequired: false, transformFunction: null }, autofocus: { classPropertyName: "autofocus", publicName: "autofocus", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, labelClass: { classPropertyName: "labelClass", publicName: "labelClass", isSignal: true, isRequired: false, transformFunction: null }, inputClass: { classPropertyName: "inputClass", publicName: "inputClass", isSignal: true, isRequired: false, transformFunction: null }, errorClass: { classPropertyName: "errorClass", publicName: "errorClass", isSignal: true, isRequired: false, transformFunction: null }, hintClass: { classPropertyName: "hintClass", publicName: "hintClass", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "tw:w-full" }, providers: [{ provide: ControlContainer, useExisting: FormGroupDirective }], ngImport: i0, template: `
90
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgFormTextInput, deps: [], target: i0.ɵɵFactoryTarget.Component });
91
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgFormTextInput, isStandalone: true, selector: "eg-form-text-input", inputs: { label: { classPropertyName: "label", publicName: "label", isSignal: true, isRequired: false, transformFunction: null }, controlId: { classPropertyName: "controlId", publicName: "controlId", isSignal: true, isRequired: false, transformFunction: null }, controlName: { classPropertyName: "controlName", publicName: "controlName", isSignal: true, isRequired: false, transformFunction: null }, error: { classPropertyName: "error", publicName: "error", isSignal: true, isRequired: false, transformFunction: null }, hint: { classPropertyName: "hint", publicName: "hint", isSignal: true, isRequired: false, transformFunction: null }, id: { classPropertyName: "id", publicName: "id", isSignal: true, isRequired: false, transformFunction: null }, name: { classPropertyName: "name", publicName: "name", isSignal: true, isRequired: false, transformFunction: null }, type: { classPropertyName: "type", publicName: "type", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, readonly: { classPropertyName: "readonly", publicName: "readonly", isSignal: true, isRequired: false, transformFunction: null }, disabled: { classPropertyName: "disabled", publicName: "disabled", isSignal: true, isRequired: false, transformFunction: null }, maxlength: { classPropertyName: "maxlength", publicName: "maxlength", isSignal: true, isRequired: false, transformFunction: null }, minlength: { classPropertyName: "minlength", publicName: "minlength", isSignal: true, isRequired: false, transformFunction: null }, max: { classPropertyName: "max", publicName: "max", isSignal: true, isRequired: false, transformFunction: null }, min: { classPropertyName: "min", publicName: "min", isSignal: true, isRequired: false, transformFunction: null }, pattern: { classPropertyName: "pattern", publicName: "pattern", isSignal: true, isRequired: false, transformFunction: null }, autocomplete: { classPropertyName: "autocomplete", publicName: "autocomplete", isSignal: true, isRequired: false, transformFunction: null }, autofocus: { classPropertyName: "autofocus", publicName: "autofocus", isSignal: true, isRequired: false, transformFunction: null }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, userClass: { classPropertyName: "userClass", publicName: "class", isSignal: true, isRequired: false, transformFunction: null }, labelClass: { classPropertyName: "labelClass", publicName: "labelClass", isSignal: true, isRequired: false, transformFunction: null }, inputClass: { classPropertyName: "inputClass", publicName: "inputClass", isSignal: true, isRequired: false, transformFunction: null }, errorClass: { classPropertyName: "errorClass", publicName: "errorClass", isSignal: true, isRequired: false, transformFunction: null }, hintClass: { classPropertyName: "hintClass", publicName: "hintClass", isSignal: true, isRequired: false, transformFunction: null } }, host: { classAttribute: "tw:w-full" }, providers: [{ provide: ControlContainer, useExisting: FormGroupDirective }], ngImport: i0, template: `
106
92
  @let lbl = label();
107
- @let cid = controlId();
108
93
  @let cnm = controlName();
109
94
  @let err = error();
110
95
  @let hnt = hint();
@@ -112,24 +97,26 @@ class EgFormTextInput {
112
97
 
113
98
  <hlm-form-field>
114
99
  @if (lbl) {
115
- <span hlmLabel [class]="$labelClass()"
100
+ <label hlmLabel [for]="effectiveId()" [class]="$labelClass()"
116
101
  >{{ lbl }}
117
102
  @if (rqrd) {
118
103
  <span class="tw:text-red-500">*</span>
119
104
  }
120
- </span>
105
+ </label>
121
106
  }
122
107
 
123
108
  <input
124
109
  hlmInput
125
110
  [attr.aria-label]="lbl"
126
- [id]="cid || id()"
111
+ [id]="effectiveId()"
127
112
  [name]="cnm || name()"
128
113
  [formControlName]="cnm"
129
114
  [class]="$inputClass()"
130
115
  [type]="type()"
131
116
  [placeholder]="placeholder()"
132
117
  [readonly]="readonly()"
118
+ [attr.disabled]="effectiveDisabled() ? '' : null"
119
+ [ariaDescribedby]="describedBy()"
133
120
  [max]="max()"
134
121
  [min]="min()"
135
122
  [maxlength]="maxlength()"
@@ -141,20 +128,20 @@ class EgFormTextInput {
141
128
  />
142
129
 
143
130
  @if (err) {
144
- <hlm-error [class]="$errorClass()">
131
+ <hlm-error [id]="errorId()" [class]="$errorClass()">
145
132
  {{ err }}
146
133
  </hlm-error>
147
134
  }
148
135
 
149
136
  @if (hnt) {
150
- <hlm-hint [class]="$hintClass()">
137
+ <hlm-hint [id]="hintId()" [class]="$hintClass()">
151
138
  {{ hnt }}
152
139
  </hlm-hint>
153
140
  }
154
141
  </hlm-form-field>
155
- `, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.MinLengthValidator, selector: "[minlength][formControlName],[minlength][formControl],[minlength][ngModel]", inputs: ["minlength"] }, { kind: "directive", type: i1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: HlmFormField, selector: "hlm-form-field", inputs: ["class"] }, { kind: "directive", type: HlmError, selector: "hlm-error", inputs: ["class"] }, { kind: "directive", type: HlmHint, selector: "hlm-hint", inputs: ["class"] }, { kind: "directive", type: HlmLabel, selector: "[hlmLabel]", inputs: ["class"] }, { kind: "directive", type: HlmInput, selector: "[hlmInput]" }] });
142
+ `, isInline: true, dependencies: [{ kind: "ngmodule", type: ReactiveFormsModule }, { kind: "directive", type: i1.DefaultValueAccessor, selector: "input:not([type=checkbox]):not([ngNoCva])[formControlName],textarea:not([ngNoCva])[formControlName],input:not([type=checkbox]):not([ngNoCva])[formControl],textarea:not([ngNoCva])[formControl],input:not([type=checkbox]):not([ngNoCva])[ngModel],textarea:not([ngNoCva])[ngModel],[ngDefaultControl]" }, { kind: "directive", type: i1.NgControlStatus, selector: "[formControlName],[ngModel],[formControl]" }, { kind: "directive", type: i1.RequiredValidator, selector: ":not([type=checkbox])[required][formControlName],:not([type=checkbox])[required][formControl],:not([type=checkbox])[required][ngModel]", inputs: ["required"] }, { kind: "directive", type: i1.MinLengthValidator, selector: "[minlength][formControlName],[minlength][formControl],[minlength][ngModel]", inputs: ["minlength"] }, { kind: "directive", type: i1.MaxLengthValidator, selector: "[maxlength][formControlName],[maxlength][formControl],[maxlength][ngModel]", inputs: ["maxlength"] }, { kind: "directive", type: i1.PatternValidator, selector: "[pattern][formControlName],[pattern][formControl],[pattern][ngModel]", inputs: ["pattern"] }, { kind: "directive", type: i1.FormControlName, selector: "[formControlName]", inputs: ["formControlName", "disabled", "ngModel"], outputs: ["ngModelChange"] }, { kind: "component", type: HlmFormField, selector: "hlm-form-field", inputs: ["class"] }, { kind: "directive", type: HlmError, selector: "hlm-error", inputs: ["class"] }, { kind: "directive", type: HlmHint, selector: "hlm-hint", inputs: ["class"] }, { kind: "directive", type: HlmLabel, selector: "[hlmLabel]", inputs: ["class"] }, { kind: "directive", type: HlmInput, selector: "[hlmInput]", inputs: ["ariaDescribedby"] }] });
156
143
  }
157
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: EgFormTextInput, decorators: [{
144
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgFormTextInput, decorators: [{
158
145
  type: Component,
159
146
  args: [{
160
147
  selector: 'eg-form-text-input',
@@ -166,7 +153,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
166
153
  providers: [{ provide: ControlContainer, useExisting: FormGroupDirective }],
167
154
  template: `
168
155
  @let lbl = label();
169
- @let cid = controlId();
170
156
  @let cnm = controlName();
171
157
  @let err = error();
172
158
  @let hnt = hint();
@@ -174,24 +160,26 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
174
160
 
175
161
  <hlm-form-field>
176
162
  @if (lbl) {
177
- <span hlmLabel [class]="$labelClass()"
163
+ <label hlmLabel [for]="effectiveId()" [class]="$labelClass()"
178
164
  >{{ lbl }}
179
165
  @if (rqrd) {
180
166
  <span class="tw:text-red-500">*</span>
181
167
  }
182
- </span>
168
+ </label>
183
169
  }
184
170
 
185
171
  <input
186
172
  hlmInput
187
173
  [attr.aria-label]="lbl"
188
- [id]="cid || id()"
174
+ [id]="effectiveId()"
189
175
  [name]="cnm || name()"
190
176
  [formControlName]="cnm"
191
177
  [class]="$inputClass()"
192
178
  [type]="type()"
193
179
  [placeholder]="placeholder()"
194
180
  [readonly]="readonly()"
181
+ [attr.disabled]="effectiveDisabled() ? '' : null"
182
+ [ariaDescribedby]="describedBy()"
195
183
  [max]="max()"
196
184
  [min]="min()"
197
185
  [maxlength]="maxlength()"
@@ -203,13 +191,13 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
203
191
  />
204
192
 
205
193
  @if (err) {
206
- <hlm-error [class]="$errorClass()">
194
+ <hlm-error [id]="errorId()" [class]="$errorClass()">
207
195
  {{ err }}
208
196
  </hlm-error>
209
197
  }
210
198
 
211
199
  @if (hnt) {
212
- <hlm-hint [class]="$hintClass()">
200
+ <hlm-hint [id]="hintId()" [class]="$hintClass()">
213
201
  {{ hnt }}
214
202
  </hlm-hint>
215
203
  }
@@ -226,5 +214,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
226
214
  * Generated bundle index. Do not edit.
227
215
  */
228
216
 
229
- export { EgFormTextInput, SpreadAttrsDirective };
230
- //# sourceMappingURL=form-text-input.mjs.map
217
+ export { EgFormTextInput };
218
+