@egose/shadcn-theme-ng-tw 0.1.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (86) hide show
  1. package/README.md +1 -1
  2. package/accordion/README.md +405 -2
  3. package/alert/README.md +372 -2
  4. package/alert-dialog/README.md +471 -5
  5. package/aspect-ratio/README.md +272 -5
  6. package/autocomplete/README.md +502 -2
  7. package/avatar/README.md +357 -5
  8. package/badge/README.md +318 -2
  9. package/basic-alert/README.md +353 -2
  10. package/breadcrumb/README.md +406 -5
  11. package/button/README.md +482 -2
  12. package/button/fesm2022/button.mjs +85 -107
  13. package/button/types/button.d.ts +5 -8
  14. package/button-group/README.md +318 -5
  15. package/button-group/fesm2022/button-group.mjs +1 -1
  16. package/calendar/README.md +357 -2
  17. package/card/README.md +331 -5
  18. package/carousel/README.md +333 -5
  19. package/carousel/fesm2022/carousel.mjs +4 -1
  20. package/checkbox/README.md +320 -2
  21. package/collapsible/README.md +332 -5
  22. package/combobox/README.md +507 -5
  23. package/combobox/fesm2022/combobox.mjs +4 -1
  24. package/command/README.md +435 -5
  25. package/confirmation-dialog/README.md +301 -2
  26. package/context-menu/README.md +366 -5
  27. package/date-picker/README.md +465 -2
  28. package/date-picker/fesm2022/date-picker.mjs +2 -2
  29. package/dialog/README.md +448 -2
  30. package/drawer/README.md +395 -5
  31. package/dropdown-menu/README.md +417 -5
  32. package/empty/README.md +329 -5
  33. package/field/README.md +385 -5
  34. package/form-checkbox/README.md +312 -2
  35. package/form-date-picker/README.md +322 -2
  36. package/form-field/README.md +356 -2
  37. package/form-field-simple/README.md +340 -2
  38. package/form-searchable-multiselect/README.md +361 -2
  39. package/form-select/README.md +350 -2
  40. package/form-text-input/README.md +371 -2
  41. package/form-textarea/README.md +347 -2
  42. package/hover-card/README.md +256 -5
  43. package/icon/README.md +239 -2
  44. package/input/README.md +269 -2
  45. package/input-group/README.md +335 -5
  46. package/input-group/fesm2022/input-group.mjs +3 -3
  47. package/input-otp/README.md +375 -5
  48. package/item/README.md +385 -5
  49. package/item/fesm2022/item.mjs +3 -3
  50. package/kbd/README.md +291 -5
  51. package/label/README.md +272 -2
  52. package/layout-simple/README.md +193 -2
  53. package/layout-simple/fesm2022/layout-simple.mjs +877 -409
  54. package/layout-simple/types/layout-simple.d.ts +174 -137
  55. package/menu/README.md +417 -2
  56. package/menubar/README.md +343 -5
  57. package/native-select/README.md +323 -5
  58. package/navigation-menu/README.md +369 -5
  59. package/package.json +1 -1
  60. package/pagination/README.md +388 -5
  61. package/popover/README.md +331 -2
  62. package/progress/README.md +311 -5
  63. package/radio-group/README.md +364 -2
  64. package/radio-group/fesm2022/radio-group.mjs +5 -1
  65. package/resizable/README.md +269 -5
  66. package/scroll-area/README.md +233 -5
  67. package/searchable-multiselect/README.md +323 -2
  68. package/select/README.md +437 -2
  69. package/separator/README.md +222 -2
  70. package/sheet/README.md +311 -2
  71. package/sidebar/README.md +457 -5
  72. package/skeleton/README.md +217 -5
  73. package/slider/README.md +273 -5
  74. package/slider/fesm2022/slider.mjs +17 -13
  75. package/sonner/README.md +346 -2
  76. package/spinner/README.md +284 -2
  77. package/switch/README.md +310 -2
  78. package/table/README.md +423 -5
  79. package/tabs/README.md +411 -2
  80. package/tabs/fesm2022/tabs.mjs +12 -2
  81. package/textarea/README.md +282 -5
  82. package/toggle/README.md +270 -5
  83. package/toggle-group/README.md +340 -5
  84. package/tooltip/README.md +269 -2
  85. package/typography/README.md +271 -5
  86. package/utils/README.md +303 -2
@@ -1,3 +1,372 @@
1
- # Form Text Input Subpath
1
+ # Form Text Input (`@egose/shadcn-theme-ng/form-text-input`)
2
2
 
3
- This project ships only as `@egose/shadcn-theme-ng/form-text-input` or `@egose/shadcn-theme-ng-tw/form-text-input`. See the [package README](../../README.md) for installation, compatibility, Tailwind variant, test, and release guidance. Do not publish this project directory independently.
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
+
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
+
7
+ ## Installation
8
+
9
+ ```bash
10
+ # Plain Tailwind (no prefix)
11
+ npm install @egose/shadcn-theme-ng
12
+
13
+ # Or the tw:-prefixed variant
14
+ npm install @egose/shadcn-theme-ng-tw
15
+ ```
16
+
17
+ Peer dependencies (Angular, `@angular/forms`, CDK, `@spartan-ng/brain`, `rxjs`) are documented in the [package README](../../README.md#peer-dependencies).
18
+
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';
23
+ ```
24
+
25
+ ## Imports
26
+
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.
28
+
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 {}
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 `<input>`.
48
+ - The bound `FormControl` holds a `string` (or `number` for numeric inputs).
49
+
50
+ ## Anatomy / Structure
51
+
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
+ ```
71
+
72
+ Real selectors: `eg-form-text-input`, `hlm-form-field`, `label[hlmLabel]`, `input[hlmInput]`, `hlm-error`, `hlm-hint`.
73
+
74
+ ## API reference
75
+
76
+ ### `eg-form-text-input` — `EgFormTextInput`
77
+
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`). |
104
+
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
+ }
149
+ ```
150
+
151
+ ### 2. Validation with per-rule error messages
152
+
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';
157
+
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
+ }
230
+ ```
231
+
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.
366
+
367
+ ## Related subpaths
368
+
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`.