@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
@@ -1,64 +1,348 @@
1
- # FormTextarea
1
+ # Form Textarea (`@egose/shadcn-theme-ng/form-textarea`)
2
2
 
3
- This project was generated using [Angular CLI](https://github.com/angular/angular-cli) version 20.3.0.
3
+ `EgFormTextarea` is a reactive-forms wrapper that bundles a `<label>`, a styled `<textarea hlmInput>`, and error/hint text into one form-ready row. It is the Angular equivalent of shadcn/ui's `<FormField> + <Textarea>` pattern for multiline input (comments, descriptions, bios), 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-textarea` (plain Tailwind) and `@egose/shadcn-theme-ng-tw/form-textarea` (`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 { EgFormTextarea } from '@egose/shadcn-theme-ng/form-textarea';
21
+ // tw variant:
22
+ // import { EgFormTextarea } from '@egose/shadcn-theme-ng-tw/form-textarea';
17
23
  ```
18
24
 
19
- ## Building
25
+ ## Imports
20
26
 
21
- To build the library, run:
27
+ The public API (`src/public-api.ts`) exports exactly one symbol — the standalone component `EgFormTextarea`. There is no `*Imports` array and no `*Module` for this subpath; import the component class directly.
22
28
 
23
- ```bash
24
- ng build form-textarea
29
+ ```ts
30
+ import { EgFormTextarea } from '@egose/shadcn-theme-ng/form-textarea';
31
+
32
+ @Component({
33
+ standalone: true,
34
+ imports: [ReactiveFormsModule, EgFormTextarea],
35
+ template: `
36
+ <form [formGroup]="form">
37
+ <eg-form-textarea controlName="bio" label="Bio" placeholder="Tell us about yourself" />
38
+ </form>
39
+ `,
40
+ })
41
+ export class MyForm {}
42
+ ```
43
+
44
+ Requirements:
45
+
46
+ - Must sit inside a `<form [formGroup]>` (it injects `FormGroupDirective` and provides `ControlContainer`).
47
+ - `controlName` is required — forwarded as `[formControlName]` to the inner `<textarea>`.
48
+ - The bound `FormControl` holds a `string`.
49
+
50
+ ## Anatomy / Structure
51
+
52
+ ```html
53
+ <eg-form-textarea controlName="bio" label="Bio">
54
+ <!-- rendered internally -->
55
+ <hlm-form-field>
56
+ <label hlmLabel for="<effectiveId>">Bio <span>*</span></label>
57
+ <textarea
58
+ hlmInput
59
+ id="<effectiveId>"
60
+ name="bio"
61
+ formControlName="bio"
62
+ rows="3"
63
+ placeholder="…"
64
+ aria-describedby="<effectiveId>-error | <effectiveId>-hint"
65
+ ></textarea>
66
+ <hlm-error id="<effectiveId>-error">…</hlm-error>
67
+ <hlm-hint id="<effectiveId>-hint">…</hlm-hint>
68
+ </hlm-form-field>
69
+ </eg-form-textarea>
25
70
  ```
26
71
 
27
- This command will compile your project, and the build artifacts will be placed in the `dist/` directory.
72
+ Real selectors: `eg-form-textarea`, `hlm-form-field`, `label[hlmLabel]`, `textarea[hlmInput]`, `hlm-error`, `hlm-hint`. Note the inner textarea reuses the `hlmInput` styling directive (same class hook as single-line inputs).
28
73
 
29
- ### Publishing the Library
74
+ ## API reference
30
75
 
31
- Once the project is built, you can publish your library by following these steps:
76
+ ### `eg-form-textarea` — `EgFormTextarea`
32
77
 
33
- 1. Navigate to the `dist` directory:
78
+ | Input | Type | Default | Description |
79
+ | --------------------- | ------------------------------- | ----------- | ------------------------------------------------------------------------------------------------ |
80
+ | `label` | `string \| undefined` | `undefined` | Field label rendered as `<label hlmLabel>` bound to the textarea 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-textarea-…`. |
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
+ | `placeholder` | `string` | `''` | Placeholder text. |
88
+ | `readonly` | `boolean` | `false` | Native `readonly` attribute. |
89
+ | `disabled` | `boolean` | `false` | Wrapper-level disable (OR-ed with the reactive disabled state — see `effectiveDisabled()`). |
90
+ | `maxlength` | `string \| number \| null` | `null` | Native `maxlength`. |
91
+ | `minlength` | `string \| number \| null` | `null` | Native `minlength`. |
92
+ | `required` | `boolean` | `false` | Native `required` + red `*` on the label. |
93
+ | `rows` | `string \| number \| undefined` | `3` | Native `rows` (visible height). |
94
+ | `cols` | `string \| number \| undefined` | `undefined` | Native `cols` (visible width). |
95
+ | `class` (`userClass`) | `ClassValue` | `''` | Extra host classes (merged over `tw:flex tw:flex-col`). |
96
+ | `labelClass` | `string` | `''` | Extra label classes (merged over `tw:mb-1 tw:gap-0`). |
97
+ | `textareaClass` | `string` | `''` | Extra textarea classes (merged over `tw:mb-1`). |
98
+ | `errorClass` | `string` | `''` | Extra error classes (merged over `tw:mt-0`). |
99
+ | `hintClass` | `string` | `''` | Extra hint classes (merged over `tw:mt-0`). |
34
100
 
35
- ```bash
36
- cd dist/form-textarea
37
- ```
101
+ 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). Readonly computeds: `effectiveId()`, `errorId()`, `hintId()`.
38
102
 
39
- 2. Run the `npm publish` command to publish your library to the npm registry:
40
- ```bash
41
- npm publish
42
- ```
103
+ ## Examples
43
104
 
44
- ## Running unit tests
105
+ ### 1. Basic comment box
45
106
 
46
- To execute unit tests with the [Karma](https://karma-runner.github.io) test runner, use the following command:
107
+ ```ts
108
+ import { Component } from '@angular/core';
109
+ import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
110
+ import { EgFormTextarea } from '@egose/shadcn-theme-ng/form-textarea';
47
111
 
48
- ```bash
49
- ng test
112
+ @Component({
113
+ standalone: true,
114
+ imports: [ReactiveFormsModule, EgFormTextarea],
115
+ template: `
116
+ <form [formGroup]="form" (ngSubmit)="post()">
117
+ <eg-form-textarea controlName="comment" label="Comment" placeholder="Write a comment…" [rows]="4" />
118
+ <button type="submit">Post</button>
119
+ </form>
120
+ `,
121
+ })
122
+ export class BasicExample {
123
+ readonly form = new FormGroup({
124
+ comment: new FormControl<string>('', { nonNullable: true }),
125
+ });
126
+
127
+ post() {
128
+ console.log(this.form.value.comment);
129
+ }
130
+ }
50
131
  ```
51
132
 
52
- ## Running end-to-end tests
133
+ ### 2. Sizes via `rows` / `cols` / `textareaClass`
53
134
 
54
- For end-to-end (e2e) testing, run:
135
+ ```ts
136
+ import { Component } from '@angular/core';
137
+ import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
138
+ import { EgFormTextarea } from '@egose/shadcn-theme-ng/form-textarea';
55
139
 
56
- ```bash
57
- ng e2e
140
+ @Component({
141
+ standalone: true,
142
+ imports: [ReactiveFormsModule, EgFormTextarea],
143
+ template: `
144
+ <form [formGroup]="form" class="tw:grid tw:gap-4">
145
+ <eg-form-textarea controlName="short" label="Compact (2 rows)" [rows]="2" />
146
+ <eg-form-textarea controlName="default" label="Default (3 rows)" />
147
+ <eg-form-textarea controlName="tall" label="Tall composer" [rows]="8" textareaClass="tw:resize-y tw:min-h-40" />
148
+ </form>
149
+ `,
150
+ })
151
+ export class SizesExample {
152
+ readonly form = new FormGroup({
153
+ short: new FormControl<string>('', { nonNullable: true }),
154
+ default: new FormControl<string>('', { nonNullable: true }),
155
+ tall: new FormControl<string>('', { nonNullable: true }),
156
+ });
157
+ }
58
158
  ```
59
159
 
60
- Angular CLI does not come with an end-to-end testing framework by default. You can choose one that suits your needs.
160
+ ### 3. Validation with a live character counter
161
+
162
+ ```ts
163
+ import { Component } from '@angular/core';
164
+ import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
165
+ import { EgFormTextarea } from '@egose/shadcn-theme-ng/form-textarea';
166
+
167
+ @Component({
168
+ standalone: true,
169
+ imports: [ReactiveFormsModule, EgFormTextarea],
170
+ template: `
171
+ <form [formGroup]="form" (ngSubmit)="submit()">
172
+ <eg-form-textarea
173
+ controlName="bio"
174
+ label="Bio"
175
+ placeholder="A few sentences about you"
176
+ [rows]="5"
177
+ [maxlength]="280"
178
+ [hint]="counter()"
179
+ error="Bio must be 20–280 characters"
180
+ required
181
+ />
182
+ <button type="submit" [disabled]="form.invalid">Save</button>
183
+ </form>
184
+ `,
185
+ })
186
+ export class CounterExample {
187
+ readonly form = new FormGroup({
188
+ bio: new FormControl<string>('', {
189
+ nonNullable: true,
190
+ validators: [Validators.required, Validators.minLength(20), Validators.maxLength(280)],
191
+ }),
192
+ });
193
+
194
+ counter(): string {
195
+ return `${this.form.controls.bio.value.length}/280`;
196
+ }
197
+
198
+ submit() {
199
+ if (this.form.invalid) {
200
+ this.form.markAllAsTouched();
201
+ return;
202
+ }
203
+ }
204
+ }
205
+ ```
206
+
207
+ ### 4. Disabled and readonly states
208
+
209
+ ```ts
210
+ import { Component, signal } from '@angular/core';
211
+ import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
212
+ import { EgFormTextarea } from '@egose/shadcn-theme-ng/form-textarea';
213
+
214
+ @Component({
215
+ standalone: true,
216
+ imports: [ReactiveFormsModule, EgFormTextarea],
217
+ template: `
218
+ <form [formGroup]="form">
219
+ <eg-form-textarea
220
+ controlName="notes"
221
+ label="Internal notes"
222
+ [rows]="4"
223
+ [disabled]="locked()"
224
+ hint="Toggle the lock to edit"
225
+ />
226
+ <eg-form-textarea controlName="audit" label="Audit trail" [rows]="3" [readonly]="true" />
227
+ <button type="button" (click)="locked.update((v) => !v)">Toggle lock</button>
228
+ </form>
229
+ `,
230
+ })
231
+ export class StatesExample {
232
+ readonly form = new FormGroup({
233
+ notes: new FormControl<string>('Called back twice.', { nonNullable: true }),
234
+ audit: new FormControl<string>('Created 2026-09-01 · Updated 2026-09-20', { nonNullable: true }),
235
+ });
236
+ readonly locked = signal(true);
237
+ }
238
+ ```
239
+
240
+ ### 5. Feedback form composition (input + textarea + select-side hint)
241
+
242
+ ```ts
243
+ import { Component } from '@angular/core';
244
+ import { FormControl, FormGroup, ReactiveFormsModule, Validators } from '@angular/forms';
245
+ import { EgFormTextInput } from '@egose/shadcn-theme-ng/form-text-input';
246
+ import { EgFormTextarea } from '@egose/shadcn-theme-ng/form-textarea';
247
+
248
+ @Component({
249
+ standalone: true,
250
+ imports: [ReactiveFormsModule, EgFormTextInput, EgFormTextarea],
251
+ template: `
252
+ <form [formGroup]="form" (ngSubmit)="send()" class="tw:grid tw:gap-4">
253
+ <eg-form-text-input controlName="subject" label="Subject" required />
254
+ <eg-form-textarea
255
+ controlName="message"
256
+ label="Message"
257
+ placeholder="Describe the issue in detail…"
258
+ [rows]="6"
259
+ [maxlength]="2000"
260
+ [hint]="messageHint()"
261
+ error="Message is required (min 20 characters)"
262
+ required
263
+ />
264
+ <button type="submit">Send feedback</button>
265
+ </form>
266
+ `,
267
+ })
268
+ export class FeedbackExample {
269
+ readonly form = new FormGroup({
270
+ subject: new FormControl<string>('', { nonNullable: true, validators: Validators.required }),
271
+ message: new FormControl<string>('', {
272
+ nonNullable: true,
273
+ validators: [Validators.required, Validators.minLength(20), Validators.maxLength(2000)],
274
+ }),
275
+ });
276
+
277
+ messageHint(): string {
278
+ return `${this.form.controls.message.value.length}/2000 · Markdown supported`;
279
+ }
280
+
281
+ send() {
282
+ if (this.form.invalid) {
283
+ this.form.markAllAsTouched();
284
+ return;
285
+ }
286
+ console.log(this.form.getRawValue());
287
+ }
288
+ }
289
+ ```
290
+
291
+ ### 6. Programmatic control (templates, reset, autosize note)
292
+
293
+ There is no auto-grow behavior — height is fixed by `rows` (plus `textareaClass` resize utilities). Insert canned text and reset programmatically:
294
+
295
+ ```ts
296
+ import { Component } from '@angular/core';
297
+ import { FormControl, FormGroup, ReactiveFormsModule } from '@angular/forms';
298
+ import { EgFormTextarea } from '@egose/shadcn-theme-ng/form-textarea';
299
+
300
+ @Component({
301
+ standalone: true,
302
+ imports: [ReactiveFormsModule, EgFormTextarea],
303
+ template: `
304
+ <form [formGroup]="form">
305
+ <eg-form-textarea
306
+ #descField
307
+ controlName="description"
308
+ label="Description"
309
+ [rows]="6"
310
+ textareaClass="tw:resize-y"
311
+ hint="Tip: allow vertical resize for long drafts"
312
+ />
313
+ <div class="tw:flex tw:gap-2">
314
+ <button type="button" (click)="insertTemplate()">Insert template</button>
315
+ <button type="button" (click)="form.controls.description.reset()">Clear</button>
316
+ </div>
317
+ <p>Field id: {{ descField.effectiveId() }}</p>
318
+ </form>
319
+ `,
320
+ })
321
+ export class ProgrammaticExample {
322
+ readonly form = new FormGroup({
323
+ description: new FormControl<string>('', { nonNullable: true }),
324
+ });
325
+
326
+ insertTemplate() {
327
+ this.form.controls.description.setValue('## Summary\n\n## Steps to reproduce\n\n1. \n2. \n');
328
+ }
329
+ }
330
+ ```
331
+
332
+ ## Accessibility notes
333
+
334
+ - `<label [for]>` targets the textarea id (explicit `controlId`/`id` or generated), so label clicks focus the field.
335
+ - `aria-describedby` points at the error element only when invalid and dirty/touched, otherwise at the hint — pair long hints (formatting tips, counters) with concise `error` strings so screen-reader users hear the failure, not the help text, on error.
336
+ - The textarea reuses `HlmInput` styling (spartan invalid-state hooks included). Use `maxlength` + a counter hint for constrained fields so users get feedback before hitting the native limit.
337
+ - Avoid `autofocus`-style behavior here (the component exposes none) — move focus deliberately in wizards instead.
338
+
339
+ ## Theming / CSS variables
340
+
341
+ No component-specific CSS variables; visuals come from the shared theme tokens via the `hlmInput` class hook. Control height with `rows` and `textareaClass` (`tw:min-h-*`, `tw:resize-y` / `tw:resize-none`).
61
342
 
62
- ## Additional Resources
343
+ ## Related subpaths
63
344
 
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.
345
+ - `@egose/shadcn-theme-ng/form-text-input` — the single-line sibling.
346
+ - `@egose/shadcn-theme-ng/textarea` — the raw `HlmTextarea` directive for non-form usage.
347
+ - `@egose/shadcn-theme-ng/input` — `HlmInput`, the class hook shared by both.
348
+ - `@egose/shadcn-theme-ng/form-field` — `HlmFormField`, `HlmError`, `HlmHint`, `HlmFormIdGenerator`.
@@ -1,14 +1,15 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, 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/form-field';
4
+ import { FormGroupDirective, ReactiveFormsModule, ControlContainer } from '@angular/forms';
5
+ import { HlmFormIdGenerator, HlmFormField, HlmError, HlmHint } from '@egose/shadcn-theme-ng/form-field';
6
6
  import { HlmLabel } from '@egose/shadcn-theme-ng/label';
7
7
  import { HlmInput } from '@egose/shadcn-theme-ng/input';
8
8
  import { hlm } from '@egose/shadcn-theme-ng/utils';
9
9
 
10
- let nextId = 0;
11
10
  class EgFormTextarea {
11
+ formGroupDirective = inject(FormGroupDirective);
12
+ generatedId = inject(HlmFormIdGenerator).generate('eg-form-textarea');
12
13
  // General props
13
14
  label = input(undefined, /* @ts-ignore */
14
15
  ...(ngDevMode ? [{ debugName: "label" }] : /* istanbul ignore next */ []));
@@ -21,9 +22,8 @@ class EgFormTextarea {
21
22
  hint = input(undefined, /* @ts-ignore */
22
23
  ...(ngDevMode ? [{ debugName: "hint" }] : /* istanbul ignore next */ []));
23
24
  // HTML/textarea attributes
24
- id = input(crypto.randomUUID(), /* @ts-ignore */
25
+ id = input(undefined, /* @ts-ignore */
25
26
  ...(ngDevMode ? [{ debugName: "id" }] : /* istanbul ignore next */ []));
26
- // id = input<string>(`eg-form-text-input-${nextId++}`);
27
27
  name = input(undefined, /* @ts-ignore */
28
28
  ...(ngDevMode ? [{ debugName: "name" }] : /* istanbul ignore next */ []));
29
29
  placeholder = input('', /* @ts-ignore */
@@ -42,6 +42,23 @@ class EgFormTextarea {
42
42
  ...(ngDevMode ? [{ debugName: "rows" }] : /* istanbul ignore next */ []));
43
43
  cols = input(undefined, /* @ts-ignore */
44
44
  ...(ngDevMode ? [{ debugName: "cols" }] : /* istanbul ignore next */ []));
45
+ effectiveId = computed(() => this.controlId() || this.id() || this.generatedId, /* @ts-ignore */
46
+ ...(ngDevMode ? [{ debugName: "effectiveId" }] : /* istanbul ignore next */ []));
47
+ errorId = computed(() => `${this.effectiveId()}-error`, /* @ts-ignore */
48
+ ...(ngDevMode ? [{ debugName: "errorId" }] : /* istanbul ignore next */ []));
49
+ hintId = computed(() => `${this.effectiveId()}-hint`, /* @ts-ignore */
50
+ ...(ngDevMode ? [{ debugName: "hintId" }] : /* istanbul ignore next */ []));
51
+ describedBy() {
52
+ const control = this.formGroupDirective.form.get(this.controlName());
53
+ return this.error() && control?.invalid && (control.dirty || control.touched)
54
+ ? this.errorId()
55
+ : this.hint()
56
+ ? this.hintId()
57
+ : null;
58
+ }
59
+ effectiveDisabled() {
60
+ return this.disabled() || !!this.formGroupDirective.form.get(this.controlName())?.disabled;
61
+ }
45
62
  // Styling classes
46
63
  userClass = input('', { ...(ngDevMode ? { debugName: "userClass" } : /* istanbul ignore next */ {}), alias: 'class' });
47
64
  labelClass = input('', /* @ts-ignore */
@@ -53,122 +70,30 @@ class EgFormTextarea {
53
70
  hintClass = input('', /* @ts-ignore */
54
71
  ...(ngDevMode ? [{ debugName: "hintClass" }] : /* istanbul ignore next */ []));
55
72
  // Computed class bindings
56
- $userClass = computed(() => hlm('flex flex-col', this.userClass()), /* @ts-ignore */
73
+ $userClass = computed(() => hlm("flex flex-col", this.userClass()), /* @ts-ignore */
57
74
  ...(ngDevMode ? [{ debugName: "$userClass" }] : /* istanbul ignore next */ []));
58
- $labelClass = computed(() => hlm('mb-1 gap-0', this.labelClass()), /* @ts-ignore */
75
+ $labelClass = computed(() => hlm("mb-1 gap-0", this.labelClass()), /* @ts-ignore */
59
76
  ...(ngDevMode ? [{ debugName: "$labelClass" }] : /* istanbul ignore next */ []));
60
- $textareaClass = computed(() => hlm('mb-1', this.textareaClass()), /* @ts-ignore */
77
+ $textareaClass = computed(() => hlm("mb-1", this.textareaClass()), /* @ts-ignore */
61
78
  ...(ngDevMode ? [{ debugName: "$textareaClass" }] : /* istanbul ignore next */ []));
62
- $errorClass = computed(() => hlm('mt-0', this.errorClass()), /* @ts-ignore */
79
+ $errorClass = computed(() => hlm("mt-0", this.errorClass()), /* @ts-ignore */
63
80
  ...(ngDevMode ? [{ debugName: "$errorClass" }] : /* istanbul ignore next */ []));
64
- $hintClass = computed(() => hlm('mt-0', this.hintClass()), /* @ts-ignore */
81
+ $hintClass = computed(() => hlm("mt-0", this.hintClass()), /* @ts-ignore */
65
82
  ...(ngDevMode ? [{ debugName: "$hintClass" }] : /* istanbul ignore next */ []));
66
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: EgFormTextarea, deps: [], target: i0.ɵɵFactoryTarget.Component });
67
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.0.7", type: EgFormTextarea, isStandalone: true, selector: "eg-form-textarea", 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 }, 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 }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, cols: { classPropertyName: "cols", publicName: "cols", 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 }, textareaClass: { classPropertyName: "textareaClass", publicName: "textareaClass", 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: "w-full" }, providers: [{ provide: ControlContainer, useExisting: FormGroupDirective }], ngImport: i0, template: `
68
- @let lbl = label();
69
- @let cid = controlId();
70
- @let cnm = controlName();
71
- @let err = error();
72
- @let hnt = hint();
73
- @let rqrd = required();
74
-
75
- <hlm-form-field>
76
- @if (lbl) {
77
- <span hlmLabel [class]="$labelClass()"
78
- >{{ lbl }}
79
- @if (rqrd) {
80
- <span class="text-red-500">*</span>
81
- }
82
- </span>
83
- }
84
-
85
- <textarea
86
- hlmInput
87
- [attr.aria-label]="lbl"
88
- [id]="cid || id()"
89
- [name]="cnm || name()"
90
- [formControlName]="cnm"
91
- [class]="$textareaClass()"
92
- [placeholder]="placeholder()"
93
- [readonly]="readonly()"
94
- [maxlength]="maxlength()"
95
- [minlength]="minlength()"
96
- [required]="rqrd"
97
- [rows]="rows()"
98
- [cols]="cols()"
99
- ></textarea>
100
-
101
- @if (err) {
102
- <hlm-error [class]="$errorClass()">
103
- {{ err }}
104
- </hlm-error>
105
- }
106
-
107
- @if (hnt) {
108
- <hlm-hint [class]="$hintClass()">
109
- {{ hnt }}
110
- </hlm-hint>
111
- }
112
- </hlm-form-field>
113
- `, 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.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]" }] });
83
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgFormTextarea, deps: [], target: i0.ɵɵFactoryTarget.Component });
84
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "22.1.3", type: EgFormTextarea, isStandalone: true, selector: "eg-form-textarea", 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 }, 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 }, required: { classPropertyName: "required", publicName: "required", isSignal: true, isRequired: false, transformFunction: null }, rows: { classPropertyName: "rows", publicName: "rows", isSignal: true, isRequired: false, transformFunction: null }, cols: { classPropertyName: "cols", publicName: "cols", 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 }, textareaClass: { classPropertyName: "textareaClass", publicName: "textareaClass", 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: "w-full" }, providers: [{ provide: ControlContainer, useExisting: FormGroupDirective }], ngImport: i0, template: "\n @let lbl = label();\n @let cnm = controlName();\n @let err = error();\n @let hnt = hint();\n @let rqrd = required();\n\n <hlm-form-field>\n @if (lbl) {\n <label hlmLabel [for]=\"effectiveId()\" [class]=\"$labelClass()\"\n >{{ lbl }}\n @if (rqrd) {\n <span class=\"text-red-500\">*</span>\n }\n </label>\n }\n\n <textarea\n hlmInput\n [attr.aria-label]=\"lbl\"\n [id]=\"effectiveId()\"\n [name]=\"cnm || name()\"\n [formControlName]=\"cnm\"\n [class]=\"$textareaClass()\"\n [placeholder]=\"placeholder()\"\n [readonly]=\"readonly()\"\n [attr.disabled]=\"effectiveDisabled() ? '' : null\"\n [ariaDescribedby]=\"describedBy()\"\n [maxlength]=\"maxlength()\"\n [minlength]=\"minlength()\"\n [required]=\"rqrd\"\n [rows]=\"rows()\"\n [cols]=\"cols()\"\n ></textarea>\n\n @if (err) {\n <hlm-error [id]=\"errorId()\" [class]=\"$errorClass()\">\n {{ err }}\n </hlm-error>\n }\n\n @if (hnt) {\n <hlm-hint [id]=\"hintId()\" [class]=\"$hintClass()\">\n {{ hnt }}\n </hlm-hint>\n }\n </hlm-form-field>\n ", 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.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"] }] });
114
85
  }
115
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImport: i0, type: EgFormTextarea, decorators: [{
86
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.1.3", ngImport: i0, type: EgFormTextarea, decorators: [{
116
87
  type: Component,
117
88
  args: [{
118
89
  selector: 'eg-form-textarea',
119
90
  standalone: true,
120
91
  host: {
121
- class: 'w-full',
92
+ class: "w-full",
122
93
  },
123
94
  imports: [ReactiveFormsModule, HlmFormField, HlmError, HlmHint, HlmLabel, HlmInput],
124
95
  providers: [{ provide: ControlContainer, useExisting: FormGroupDirective }],
125
- template: `
126
- @let lbl = label();
127
- @let cid = controlId();
128
- @let cnm = controlName();
129
- @let err = error();
130
- @let hnt = hint();
131
- @let rqrd = required();
132
-
133
- <hlm-form-field>
134
- @if (lbl) {
135
- <span hlmLabel [class]="$labelClass()"
136
- >{{ lbl }}
137
- @if (rqrd) {
138
- <span class="text-red-500">*</span>
139
- }
140
- </span>
141
- }
142
-
143
- <textarea
144
- hlmInput
145
- [attr.aria-label]="lbl"
146
- [id]="cid || id()"
147
- [name]="cnm || name()"
148
- [formControlName]="cnm"
149
- [class]="$textareaClass()"
150
- [placeholder]="placeholder()"
151
- [readonly]="readonly()"
152
- [maxlength]="maxlength()"
153
- [minlength]="minlength()"
154
- [required]="rqrd"
155
- [rows]="rows()"
156
- [cols]="cols()"
157
- ></textarea>
158
-
159
- @if (err) {
160
- <hlm-error [class]="$errorClass()">
161
- {{ err }}
162
- </hlm-error>
163
- }
164
-
165
- @if (hnt) {
166
- <hlm-hint [class]="$hintClass()">
167
- {{ hnt }}
168
- </hlm-hint>
169
- }
170
- </hlm-form-field>
171
- `,
96
+ template: "\n @let lbl = label();\n @let cnm = controlName();\n @let err = error();\n @let hnt = hint();\n @let rqrd = required();\n\n <hlm-form-field>\n @if (lbl) {\n <label hlmLabel [for]=\"effectiveId()\" [class]=\"$labelClass()\"\n >{{ lbl }}\n @if (rqrd) {\n <span class=\"text-red-500\">*</span>\n }\n </label>\n }\n\n <textarea\n hlmInput\n [attr.aria-label]=\"lbl\"\n [id]=\"effectiveId()\"\n [name]=\"cnm || name()\"\n [formControlName]=\"cnm\"\n [class]=\"$textareaClass()\"\n [placeholder]=\"placeholder()\"\n [readonly]=\"readonly()\"\n [attr.disabled]=\"effectiveDisabled() ? '' : null\"\n [ariaDescribedby]=\"describedBy()\"\n [maxlength]=\"maxlength()\"\n [minlength]=\"minlength()\"\n [required]=\"rqrd\"\n [rows]=\"rows()\"\n [cols]=\"cols()\"\n ></textarea>\n\n @if (err) {\n <hlm-error [id]=\"errorId()\" [class]=\"$errorClass()\">\n {{ err }}\n </hlm-error>\n }\n\n @if (hnt) {\n <hlm-hint [id]=\"hintId()\" [class]=\"$hintClass()\">\n {{ hnt }}\n </hlm-hint>\n }\n </hlm-form-field>\n ",
172
97
  }]
173
98
  }], propDecorators: { label: [{ type: i0.Input, args: [{ isSignal: true, alias: "label", required: false }] }], controlId: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlId", required: false }] }], controlName: [{ type: i0.Input, args: [{ isSignal: true, alias: "controlName", required: false }] }], error: [{ type: i0.Input, args: [{ isSignal: true, alias: "error", required: false }] }], hint: [{ type: i0.Input, args: [{ isSignal: true, alias: "hint", required: false }] }], id: [{ type: i0.Input, args: [{ isSignal: true, alias: "id", required: false }] }], name: [{ type: i0.Input, args: [{ isSignal: true, alias: "name", required: false }] }], placeholder: [{ type: i0.Input, args: [{ isSignal: true, alias: "placeholder", required: false }] }], readonly: [{ type: i0.Input, args: [{ isSignal: true, alias: "readonly", required: false }] }], disabled: [{ type: i0.Input, args: [{ isSignal: true, alias: "disabled", required: false }] }], maxlength: [{ type: i0.Input, args: [{ isSignal: true, alias: "maxlength", required: false }] }], minlength: [{ type: i0.Input, args: [{ isSignal: true, alias: "minlength", required: false }] }], required: [{ type: i0.Input, args: [{ isSignal: true, alias: "required", required: false }] }], rows: [{ type: i0.Input, args: [{ isSignal: true, alias: "rows", required: false }] }], cols: [{ type: i0.Input, args: [{ isSignal: true, alias: "cols", required: false }] }], userClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "class", required: false }] }], labelClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "labelClass", required: false }] }], textareaClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "textareaClass", required: false }] }], errorClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "errorClass", required: false }] }], hintClass: [{ type: i0.Input, args: [{ isSignal: true, alias: "hintClass", required: false }] }] } });
174
99
 
@@ -181,4 +106,4 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "22.0.7", ngImpor
181
106
  */
182
107
 
183
108
  export { EgFormTextarea };
184
- //# sourceMappingURL=form-textarea.mjs.map
109
+
@@ -2,12 +2,14 @@ import * as _angular_core from '@angular/core';
2
2
  import { ClassValue } from 'clsx';
3
3
 
4
4
  declare class EgFormTextarea {
5
+ private readonly formGroupDirective;
6
+ private readonly generatedId;
5
7
  label: _angular_core.InputSignal<string | undefined>;
6
8
  controlId: _angular_core.InputSignal<string | undefined>;
7
9
  controlName: _angular_core.InputSignal<string>;
8
10
  error: _angular_core.InputSignal<string | undefined>;
9
11
  hint: _angular_core.InputSignal<string | undefined>;
10
- id: _angular_core.InputSignal<string>;
12
+ id: _angular_core.InputSignal<string | undefined>;
11
13
  name: _angular_core.InputSignal<string | undefined>;
12
14
  placeholder: _angular_core.InputSignal<string>;
13
15
  readonly: _angular_core.InputSignal<boolean>;
@@ -17,6 +19,11 @@ declare class EgFormTextarea {
17
19
  required: _angular_core.InputSignal<boolean>;
18
20
  rows: _angular_core.InputSignal<string | number | undefined>;
19
21
  cols: _angular_core.InputSignal<string | number | undefined>;
22
+ readonly effectiveId: _angular_core.Signal<string>;
23
+ readonly errorId: _angular_core.Signal<string>;
24
+ readonly hintId: _angular_core.Signal<string>;
25
+ describedBy(): string | null;
26
+ effectiveDisabled(): boolean;
20
27
  userClass: _angular_core.InputSignal<ClassValue>;
21
28
  labelClass: _angular_core.InputSignal<string>;
22
29
  textareaClass: _angular_core.InputSignal<string>;