@neynar/ui 0.1.1 → 0.1.2

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 (173) hide show
  1. package/dist/components/ui/accordion.d.ts +1 -25
  2. package/dist/components/ui/accordion.d.ts.map +1 -1
  3. package/dist/components/ui/alert-dialog.d.ts +240 -46
  4. package/dist/components/ui/alert-dialog.d.ts.map +1 -1
  5. package/dist/components/ui/alert.d.ts +73 -11
  6. package/dist/components/ui/alert.d.ts.map +1 -1
  7. package/dist/components/ui/aspect-ratio.d.ts +44 -10
  8. package/dist/components/ui/aspect-ratio.d.ts.map +1 -1
  9. package/dist/components/ui/avatar.d.ts +117 -33
  10. package/dist/components/ui/avatar.d.ts.map +1 -1
  11. package/dist/components/ui/badge.d.ts +50 -71
  12. package/dist/components/ui/badge.d.ts.map +1 -1
  13. package/dist/components/ui/breadcrumb.d.ts +231 -49
  14. package/dist/components/ui/breadcrumb.d.ts.map +1 -1
  15. package/dist/components/ui/button.d.ts +189 -71
  16. package/dist/components/ui/button.d.ts.map +1 -1
  17. package/dist/components/ui/calendar.d.ts +197 -40
  18. package/dist/components/ui/calendar.d.ts.map +1 -1
  19. package/dist/components/ui/card.d.ts +7 -22
  20. package/dist/components/ui/card.d.ts.map +1 -1
  21. package/dist/components/ui/carousel.d.ts +369 -99
  22. package/dist/components/ui/carousel.d.ts.map +1 -1
  23. package/dist/components/ui/chart.d.ts.map +1 -1
  24. package/dist/components/ui/checkbox.d.ts +110 -38
  25. package/dist/components/ui/checkbox.d.ts.map +1 -1
  26. package/dist/components/ui/collapsible.d.ts +246 -61
  27. package/dist/components/ui/collapsible.d.ts.map +1 -1
  28. package/dist/components/ui/combobox.d.ts +207 -159
  29. package/dist/components/ui/combobox.d.ts.map +1 -1
  30. package/dist/components/ui/command.d.ts +336 -67
  31. package/dist/components/ui/command.d.ts.map +1 -1
  32. package/dist/components/ui/container.d.ts +159 -64
  33. package/dist/components/ui/container.d.ts.map +1 -1
  34. package/dist/components/ui/context-menu.d.ts +321 -39
  35. package/dist/components/ui/context-menu.d.ts.map +1 -1
  36. package/dist/components/ui/date-picker.d.ts +113 -86
  37. package/dist/components/ui/date-picker.d.ts.map +1 -1
  38. package/dist/components/ui/dialog.d.ts +106 -25
  39. package/dist/components/ui/dialog.d.ts.map +1 -1
  40. package/dist/components/ui/drawer.d.ts +388 -59
  41. package/dist/components/ui/drawer.d.ts.map +1 -1
  42. package/dist/components/ui/dropdown-menu.d.ts +521 -74
  43. package/dist/components/ui/dropdown-menu.d.ts.map +1 -1
  44. package/dist/components/ui/empty-state.d.ts +148 -76
  45. package/dist/components/ui/empty-state.d.ts.map +1 -1
  46. package/dist/components/ui/hover-card.d.ts +253 -34
  47. package/dist/components/ui/hover-card.d.ts.map +1 -1
  48. package/dist/components/ui/input.d.ts +143 -44
  49. package/dist/components/ui/input.d.ts.map +1 -1
  50. package/dist/components/ui/label.d.ts +0 -8
  51. package/dist/components/ui/label.d.ts.map +1 -1
  52. package/dist/components/ui/menubar.d.ts +288 -46
  53. package/dist/components/ui/menubar.d.ts.map +1 -1
  54. package/dist/components/ui/navigation-menu.d.ts +444 -127
  55. package/dist/components/ui/navigation-menu.d.ts.map +1 -1
  56. package/dist/components/ui/pagination.d.ts +342 -66
  57. package/dist/components/ui/pagination.d.ts.map +1 -1
  58. package/dist/components/ui/popover.d.ts +0 -8
  59. package/dist/components/ui/popover.d.ts.map +1 -1
  60. package/dist/components/ui/progress.d.ts +88 -30
  61. package/dist/components/ui/progress.d.ts.map +1 -1
  62. package/dist/components/ui/radio-group.d.ts +189 -45
  63. package/dist/components/ui/radio-group.d.ts.map +1 -1
  64. package/dist/components/ui/resizable.d.ts +178 -62
  65. package/dist/components/ui/resizable.d.ts.map +1 -1
  66. package/dist/components/ui/scroll-area.d.ts +180 -21
  67. package/dist/components/ui/scroll-area.d.ts.map +1 -1
  68. package/dist/components/ui/select.d.ts +382 -60
  69. package/dist/components/ui/select.d.ts.map +1 -1
  70. package/dist/components/ui/separator.d.ts +52 -39
  71. package/dist/components/ui/separator.d.ts.map +1 -1
  72. package/dist/components/ui/sheet.d.ts +144 -27
  73. package/dist/components/ui/sheet.d.ts.map +1 -1
  74. package/dist/components/ui/sidebar.d.ts +81 -31
  75. package/dist/components/ui/sidebar.d.ts.map +1 -1
  76. package/dist/components/ui/skeleton.d.ts +94 -32
  77. package/dist/components/ui/skeleton.d.ts.map +1 -1
  78. package/dist/components/ui/slider.d.ts +37 -31
  79. package/dist/components/ui/slider.d.ts.map +1 -1
  80. package/dist/components/ui/sonner.d.ts +280 -46
  81. package/dist/components/ui/sonner.d.ts.map +1 -1
  82. package/dist/components/ui/stack.d.ts +289 -148
  83. package/dist/components/ui/stack.d.ts.map +1 -1
  84. package/dist/components/ui/stories/aspect-ratio.stories.d.ts +1 -2
  85. package/dist/components/ui/stories/aspect-ratio.stories.d.ts.map +1 -1
  86. package/dist/components/ui/stories/container.stories.d.ts +2 -3
  87. package/dist/components/ui/stories/container.stories.d.ts.map +1 -1
  88. package/dist/components/ui/stories/empty-state.stories.d.ts +2 -2
  89. package/dist/components/ui/stories/scroll-area.stories.d.ts +1 -2
  90. package/dist/components/ui/stories/scroll-area.stories.d.ts.map +1 -1
  91. package/dist/components/ui/stories/stack.stories.d.ts +1 -1
  92. package/dist/components/ui/stories/text-field.stories.d.ts +7 -1
  93. package/dist/components/ui/stories/text-field.stories.d.ts.map +1 -1
  94. package/dist/components/ui/switch.d.ts +44 -38
  95. package/dist/components/ui/switch.d.ts.map +1 -1
  96. package/dist/components/ui/table.d.ts +33 -0
  97. package/dist/components/ui/table.d.ts.map +1 -1
  98. package/dist/components/ui/tabs.d.ts +4 -22
  99. package/dist/components/ui/tabs.d.ts.map +1 -1
  100. package/dist/components/ui/text-field.d.ts +170 -84
  101. package/dist/components/ui/text-field.d.ts.map +1 -1
  102. package/dist/components/ui/textarea.d.ts +106 -29
  103. package/dist/components/ui/textarea.d.ts.map +1 -1
  104. package/dist/components/ui/theme-toggle.d.ts +190 -65
  105. package/dist/components/ui/theme-toggle.d.ts.map +1 -1
  106. package/dist/components/ui/theme.d.ts +107 -23
  107. package/dist/components/ui/theme.d.ts.map +1 -1
  108. package/dist/components/ui/toggle-group.d.ts +143 -67
  109. package/dist/components/ui/toggle-group.d.ts.map +1 -1
  110. package/dist/components/ui/toggle.d.ts +118 -30
  111. package/dist/components/ui/toggle.d.ts.map +1 -1
  112. package/dist/components/ui/tooltip.d.ts +152 -28
  113. package/dist/components/ui/tooltip.d.ts.map +1 -1
  114. package/dist/components/ui/typography.d.ts +452 -134
  115. package/dist/components/ui/typography.d.ts.map +1 -1
  116. package/dist/index.js +9388 -8281
  117. package/dist/index.js.map +1 -1
  118. package/dist/tsconfig.tsbuildinfo +1 -1
  119. package/llms.txt +173 -3
  120. package/package.json +5 -2
  121. package/src/components/ui/accordion.tsx +112 -27
  122. package/src/components/ui/alert-dialog.tsx +401 -46
  123. package/src/components/ui/alert.tsx +114 -11
  124. package/src/components/ui/aspect-ratio.tsx +69 -14
  125. package/src/components/ui/avatar.tsx +179 -33
  126. package/src/components/ui/badge.tsx +74 -75
  127. package/src/components/ui/breadcrumb.tsx +335 -50
  128. package/src/components/ui/button.tsx +198 -90
  129. package/src/components/ui/calendar.tsx +867 -43
  130. package/src/components/ui/card.tsx +140 -33
  131. package/src/components/ui/carousel.tsx +529 -98
  132. package/src/components/ui/chart.tsx +222 -1
  133. package/src/components/ui/checkbox.tsx +176 -38
  134. package/src/components/ui/collapsible.tsx +321 -67
  135. package/src/components/ui/combobox.tsx +284 -83
  136. package/src/components/ui/command.tsx +527 -67
  137. package/src/components/ui/container.tsx +217 -65
  138. package/src/components/ui/context-menu.tsx +716 -51
  139. package/src/components/ui/date-picker.tsx +228 -38
  140. package/src/components/ui/dialog.tsx +270 -33
  141. package/src/components/ui/drawer.tsx +546 -67
  142. package/src/components/ui/dropdown-menu.tsx +657 -74
  143. package/src/components/ui/empty-state.tsx +241 -82
  144. package/src/components/ui/hover-card.tsx +328 -39
  145. package/src/components/ui/input.tsx +207 -44
  146. package/src/components/ui/label.tsx +98 -8
  147. package/src/components/ui/menubar.tsx +587 -54
  148. package/src/components/ui/navigation-menu.tsx +557 -128
  149. package/src/components/ui/pagination.tsx +561 -79
  150. package/src/components/ui/popover.tsx +119 -8
  151. package/src/components/ui/progress.tsx +131 -29
  152. package/src/components/ui/radio-group.tsx +260 -51
  153. package/src/components/ui/resizable.tsx +289 -63
  154. package/src/components/ui/scroll-area.tsx +377 -66
  155. package/src/components/ui/select.tsx +545 -60
  156. package/src/components/ui/separator.tsx +146 -40
  157. package/src/components/ui/sheet.tsx +348 -31
  158. package/src/components/ui/sidebar.tsx +471 -29
  159. package/src/components/ui/skeleton.tsx +114 -32
  160. package/src/components/ui/slider.tsx +77 -31
  161. package/src/components/ui/sonner.tsx +574 -46
  162. package/src/components/ui/stack.tsx +423 -101
  163. package/src/components/ui/switch.tsx +78 -39
  164. package/src/components/ui/table.tsx +170 -4
  165. package/src/components/ui/tabs.tsx +108 -22
  166. package/src/components/ui/text-field.tsx +226 -81
  167. package/src/components/ui/textarea.tsx +180 -29
  168. package/src/components/ui/theme-toggle.tsx +313 -65
  169. package/src/components/ui/theme.tsx +117 -23
  170. package/src/components/ui/toggle-group.tsx +280 -69
  171. package/src/components/ui/toggle.tsx +124 -35
  172. package/src/components/ui/tooltip.tsx +239 -29
  173. package/src/components/ui/typography.tsx +1115 -165
@@ -4,102 +4,136 @@ import { Input } from "./input";
4
4
  import { Label } from "./label";
5
5
 
6
6
  /**
7
- * Props for the TextField component
8
- *
9
- * Extends standard HTML input properties while omitting 'id' to allow
10
- * automatic ID generation for accessibility.
7
+ * Props for TextField component (Documentation only - NOT used in component implementation)
8
+ * These types are for documentation generation and should not replace inferred types
11
9
  */
12
- export type TextFieldProps = Omit<React.ComponentProps<"input">, "id"> & {
13
- /**
14
- * Label text displayed above the input
15
- *
16
- * When provided, creates a semantic label-input relationship
17
- * for improved accessibility and user experience.
18
- */
10
+ // eslint-disable-next-line unused-imports/no-unused-vars
11
+ type TextFieldDocsProps = {
12
+ /** Label text displayed above the input field @example "Email Address" */
19
13
  label?: string;
20
- /**
21
- * Helper text displayed below the input
22
- *
23
- * Provides additional context or instructions to help users
24
- * understand the expected input format or purpose.
25
- */
14
+ /** Helper text displayed below the input field for additional context */
26
15
  helperText?: string;
27
- /**
28
- * Error message displayed below the input
29
- *
30
- * When provided, sets the input to an error state with
31
- * destructive styling and proper ARIA attributes.
32
- * Takes precedence over helperText when both are present.
33
- */
16
+ /** Error message displayed below the input field, takes precedence over helperText */
34
17
  error?: string;
35
- /**
36
- * Whether the field is required
37
- *
38
- * Adds a visual asterisk (*) indicator to the label
39
- * and semantic meaning for form validation.
40
- *
41
- * @default false
42
- */
18
+ /** Whether the form field is required for validation @default false */
43
19
  required?: boolean;
44
- /**
45
- * Custom ID for the input field
46
- *
47
- * If not provided, an automatic ID will be generated
48
- * using React.useId() for accessibility compliance.
49
- */
20
+ /** Custom identifier for the input element (auto-generated if not provided) */
50
21
  id?: string;
51
- };
22
+ /** The type of input field to render @default "text" */
23
+ type?: React.HTMLInputTypeAttribute;
24
+ /** Placeholder text displayed when the input is empty */
25
+ placeholder?: string;
26
+ /** Current value of the input field (controlled component) */
27
+ value?: string | ReadonlyArray<string> | number;
28
+ /** Default value for uncontrolled input usage */
29
+ defaultValue?: string | ReadonlyArray<string> | number;
30
+ /** Whether the input field is disabled and non-interactive @default false */
31
+ disabled?: boolean;
32
+ /** Whether the input field is read-only @default false */
33
+ readOnly?: boolean;
34
+ /** Name attribute for form submission and validation */
35
+ name?: string;
36
+ /** Auto-complete hint for browsers */
37
+ autoComplete?: string;
38
+ /** Whether to automatically focus this input when mounted @default false */
39
+ autoFocus?: boolean;
40
+ /** Maximum number of characters allowed in text inputs */
41
+ maxLength?: number;
42
+ /** Minimum number of characters required for text inputs */
43
+ minLength?: number;
44
+ /** Regular expression pattern for client-side validation */
45
+ pattern?: string;
46
+ /** Minimum value for numeric input types */
47
+ min?: string | number;
48
+ /** Maximum value for numeric input types */
49
+ max?: string | number;
50
+ /** Step value for numeric inputs */
51
+ step?: string | number;
52
+ /** Callback fired when the input value changes */
53
+ onChange?: (event: React.ChangeEvent<HTMLInputElement>) => void;
54
+ /** Callback fired when the input receives focus */
55
+ onFocus?: (event: React.FocusEvent<HTMLInputElement>) => void;
56
+ /** Callback fired when the input loses focus */
57
+ onBlur?: (event: React.FocusEvent<HTMLInputElement>) => void;
58
+ /** Callback fired when a key is pressed down */
59
+ onKeyDown?: (event: React.KeyboardEvent<HTMLInputElement>) => void;
60
+ /** Callback fired when a key is released */
61
+ onKeyUp?: (event: React.KeyboardEvent<HTMLInputElement>) => void;
62
+ /** Additional CSS classes for custom styling */
63
+ className?: string;
64
+ } & React.InputHTMLAttributes<HTMLInputElement>;
65
+
66
+ type TextFieldProps = {
67
+ label?: string;
68
+ helperText?: string;
69
+ error?: string;
70
+ required?: boolean;
71
+ className?: string;
72
+ } & Omit<React.ComponentProps<typeof Input>, "className">;
52
73
 
53
74
  /**
54
- * A complete form field component combining input, label, and descriptive text
75
+ * TextField - Complete form field with label, input, and descriptive text
55
76
  *
56
- * TextField provides a complete form field solution by composing the base Input
57
- * and Label components with additional helper text and error handling. It automatically
58
- * manages accessibility attributes, ID generation, and ARIA relationships to ensure
59
- * forms are usable by all users including those using assistive technologies.
77
+ * A comprehensive form field component that combines Input and Label components with
78
+ * additional helper text and error handling capabilities. Automatically manages
79
+ * accessibility attributes, ID generation, and ARIA relationships to ensure forms
80
+ * are usable by all users including those using assistive technologies.
60
81
  *
61
- * This component follows form design patterns similar to those found in modern UI
62
- * libraries while providing Neynar-specific styling and behavior. It supports all
63
- * standard HTML input types and attributes while adding enhanced UX features.
82
+ * Built on top of the Input and Label primitives, TextField provides a complete
83
+ * form field solution with consistent styling, proper semantic markup, and enhanced
84
+ * UX features. Supports all HTML input types while adding form validation states,
85
+ * descriptive text, and automatic accessibility compliance.
64
86
  *
65
- * @example Basic usage
87
+ * @example
66
88
  * ```tsx
89
+ * // Basic text input with label
67
90
  * <TextField
68
91
  * label="Email Address"
69
92
  * type="email"
70
93
  * placeholder="Enter your email"
94
+ * name="email"
71
95
  * />
72
96
  * ```
73
97
  *
74
- * @example With helper text
98
+ * @example
75
99
  * ```tsx
100
+ * // Required field with helper text
76
101
  * <TextField
77
102
  * label="Username"
78
103
  * placeholder="@username"
79
- * helperText="Choose a unique username (3-20 characters)"
104
+ * helperText="Choose a unique username (3-20 characters, letters and numbers only)"
105
+ * required
106
+ * minLength={3}
107
+ * maxLength={20}
108
+ * pattern="[a-zA-Z0-9]+"
80
109
  * />
81
110
  * ```
82
111
  *
83
- * @example Error state
112
+ * @example
84
113
  * ```tsx
114
+ * // Password field with validation error
85
115
  * <TextField
86
116
  * label="Password"
87
117
  * type="password"
88
118
  * required
89
- * error="Password must be at least 8 characters"
119
+ * error="Password must be at least 8 characters long"
90
120
  * value={password}
91
121
  * onChange={(e) => setPassword(e.target.value)}
122
+ * minLength={8}
123
+ * autoComplete="new-password"
92
124
  * />
93
125
  * ```
94
126
  *
95
- * @example Controlled component with validation
127
+ * @example
96
128
  * ```tsx
129
+ * // Controlled component with real-time validation
97
130
  * const [email, setEmail] = useState("");
98
131
  * const [error, setError] = useState("");
99
132
  *
100
133
  * const validateEmail = (value: string) => {
101
- * if (!value.includes("@")) {
102
- * setError("Please enter a valid email");
134
+ * const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/;
135
+ * if (!emailRegex.test(value)) {
136
+ * setError("Please enter a valid email address");
103
137
  * } else {
104
138
  * setError("");
105
139
  * }
@@ -113,50 +147,161 @@ export type TextFieldProps = Omit<React.ComponentProps<"input">, "id"> & {
113
147
  * setEmail(e.target.value);
114
148
  * validateEmail(e.target.value);
115
149
  * }}
150
+ * onBlur={(e) => validateEmail(e.target.value)}
116
151
  * error={error}
117
152
  * required
153
+ * autoComplete="email"
118
154
  * />
119
155
  * ```
120
156
  *
121
- * @example Form integration
157
+ * @example
122
158
  * ```tsx
123
- * <form onSubmit={handleSubmit}>
159
+ * // Number input with constraints
160
+ * <TextField
161
+ * label="Age"
162
+ * type="number"
163
+ * min={0}
164
+ * max={120}
165
+ * step={1}
166
+ * helperText="Enter your age in years"
167
+ * placeholder="25"
168
+ * />
169
+ * ```
170
+ *
171
+ * @example
172
+ * ```tsx
173
+ * // Search input with custom styling
174
+ * <TextField
175
+ * label="Search"
176
+ * type="search"
177
+ * placeholder="Search products..."
178
+ * className="max-w-md"
179
+ * onKeyDown={(e) => {
180
+ * if (e.key === 'Enter') {
181
+ * handleSearch(e.currentTarget.value);
182
+ * }
183
+ * }}
184
+ * />
185
+ * ```
186
+ *
187
+ * @example
188
+ * ```tsx
189
+ * // Form integration with React Hook Form
190
+ * const { register, formState: { errors } } = useForm();
191
+ *
192
+ * <form onSubmit={handleSubmit(onSubmit)}>
124
193
  * <TextField
125
194
  * label="Full Name"
126
- * name="fullName"
127
- * required
128
- * helperText="First and last name"
195
+ * {...register("fullName", { required: "Full name is required" })}
196
+ * error={errors.fullName?.message}
197
+ * helperText="Enter your first and last name"
129
198
  * />
199
+ *
130
200
  * <TextField
131
- * label="API Key"
132
- * type="password"
133
- * name="apiKey"
134
- * required
135
- * helperText="Get your key from the Neynar dashboard"
201
+ * label="Phone Number"
202
+ * type="tel"
203
+ * {...register("phone", {
204
+ * pattern: {
205
+ * value: /^[\+]?[1-9][\d]{0,15}$/,
206
+ * message: "Please enter a valid phone number"
207
+ * }
208
+ * })}
209
+ * error={errors.phone?.message}
210
+ * autoComplete="tel"
136
211
  * />
137
212
  * </form>
138
213
  * ```
139
214
  *
140
- * ## Accessibility Features
215
+ * @example
216
+ * ```tsx
217
+ * // File input with custom validation
218
+ * <TextField
219
+ * label="Profile Picture"
220
+ * type="file"
221
+ * accept="image/*"
222
+ * helperText="Upload a PNG, JPG or GIF (max 5MB)"
223
+ * onChange={(e) => {
224
+ * const file = e.target.files?.[0];
225
+ * if (file && file.size > 5 * 1024 * 1024) {
226
+ * setError("File size must be less than 5MB");
227
+ * } else {
228
+ * setError("");
229
+ * handleFileUpload(file);
230
+ * }
231
+ * }}
232
+ * error={fileError}
233
+ * />
234
+ * ```
235
+ *
236
+ * @example
237
+ * ```tsx
238
+ * // Date input with range constraints
239
+ * <TextField
240
+ * label="Birth Date"
241
+ * type="date"
242
+ * min="1900-01-01"
243
+ * max={new Date().toISOString().split('T')[0]}
244
+ * helperText="You must be 18 or older to register"
245
+ * required
246
+ * />
247
+ * ```
248
+ *
249
+ * @example
250
+ * ```tsx
251
+ * // Disabled field with explanation
252
+ * <TextField
253
+ * label="Account ID"
254
+ * value="ACC-12345"
255
+ * disabled
256
+ * helperText="This value is automatically generated and cannot be changed"
257
+ * />
258
+ * ```
259
+ *
260
+ * @accessibility
261
+ * - **Semantic Association**: Proper label-input relationship via `htmlFor` and `id` attributes
262
+ * - **ARIA Support**: Implements `aria-invalid`, `aria-describedby`, and `aria-required` attributes
263
+ * - **Focus Management**: Keyboard navigation with visible focus indicators and proper tab order
264
+ * - **Screen Reader Support**: Helper text and error messages are properly announced via `aria-describedby`
265
+ * - **Required Fields**: Visual asterisk indicator and semantic `aria-required` for screen readers
266
+ * - **Error States**: Clear visual styling and programmatic error indication with `aria-invalid`
267
+ * - **Automatic ID Generation**: Uses `React.useId()` to prevent conflicts and ensure accessibility
268
+ * - **Click-to-Focus**: Clicking the label automatically focuses the input field
269
+ * - **Disabled State**: Proper disabled styling and screen reader announcements
270
+ * - **Validation Support**: Works with native HTML5 validation and custom validation libraries
271
+ *
272
+ * @remarks
273
+ * The TextField component automatically handles:
274
+ * - ID generation and label-input association for accessibility compliance
275
+ * - ARIA attribute management based on component state (error, required, helper text)
276
+ * - Error state precedence over helper text display
277
+ * - Focus management and keyboard navigation patterns
278
+ * - Consistent styling inheritance from Input and Label components
279
+ * - Screen reader compatibility with proper announcements
141
280
  *
142
- * - **Semantic HTML**: Uses proper label-input association via `htmlFor` attribute
143
- * - **ARIA Support**: Implements `aria-invalid`, `aria-describedby`, and `aria-required`
144
- * - **Focus Management**: Keyboard navigation and focus indicators work correctly
145
- * - **Screen Readers**: Helper text and error messages are properly announced
146
- * - **Required Fields**: Visual and semantic indication with asterisk and ARIA attributes
147
- * - **Error States**: Clear visual and programmatic error indication
281
+ * ## Component Composition
148
282
  *
149
- * ## Styling & Variants
283
+ * TextField is composed of:
284
+ * - **Root Container**: `div` with `space-y-2` for consistent spacing
285
+ * - **Label**: Radix UI Label primitive with proper association and styling
286
+ * - **Input**: Enhanced HTML input with design system styling and validation states
287
+ * - **Helper/Error Text**: Conditional paragraph elements with proper ARIA linking
150
288
  *
151
- * The component inherits styling from the base Input component and adds:
152
- * - Error state styling with destructive colors
153
- * - Consistent spacing with `space-y-2` utility
154
- * - Muted foreground color for helper text
155
- * - Required field indicator styling
289
+ * ## Input Type Support
156
290
  *
157
- * @see {@link Input} The underlying input component
158
- * @see {@link Label} The label component used for field labels
159
- * @since 1.0.0 */
291
+ * Supports all HTML input types including:
292
+ * - **Text Types**: `text`, `email`, `password`, `tel`, `url`, `search`
293
+ * - **Numeric Types**: `number`, `range` (with min/max/step support)
294
+ * - **Date/Time Types**: `date`, `datetime-local`, `month`, `time`, `week`
295
+ * - **Other Types**: `file`, `hidden`, `color`, `checkbox`, `radio`
296
+ *
297
+ * @see {@link Input} The underlying input component with styling and validation
298
+ * @see {@link Label} The label component used for accessible field labels
299
+ * @see {@link https://ui.shadcn.com/docs/components/input} shadcn/ui Input documentation
300
+ * @see {@link https://www.radix-ui.com/primitives/docs/components/label} Radix UI Label primitive
301
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input} MDN Input element reference
302
+ * @see {@link https://www.w3.org/WAI/tutorials/forms/} W3C Forms accessibility tutorial
303
+ * @since 1.0.0
304
+ */
160
305
  const TextField = React.forwardRef<HTMLInputElement, TextFieldProps>(
161
306
  ({ label, helperText, error, required, className, id, ...props }, ref) => {
162
307
  const idIfNeeded = React.useId();
@@ -3,20 +3,95 @@ import * as React from "react";
3
3
  import { cn } from "@/lib/utils";
4
4
 
5
5
  /**
6
- * A multi-line text input control built on the native HTML textarea element
6
+ * Props for Textarea (Documentation only - NOT used in component implementation)
7
7
  *
8
- * The Textarea component provides a styled textarea for entering longer pieces
9
- * of text that span multiple lines, such as comments, descriptions, or messages.
10
- * Features automatic content-based resizing using CSS field-sizing when supported
11
- * by the browser.
8
+ * Multi-line text input for extended content entry. Built on the native HTML
9
+ * textarea element with enhanced styling and accessibility features.
10
+ */
11
+ // eslint-disable-next-line unused-imports/no-unused-vars
12
+ type TextareaDocsProps = {
13
+ /** Additional CSS classes for styling customization */
14
+ className?: string;
15
+ /** Placeholder text displayed when the textarea is empty */
16
+ placeholder?: string;
17
+ /** Whether the textarea is disabled and non-interactive @default false */
18
+ disabled?: boolean;
19
+ /** Whether the textarea is read-only (non-editable) @default false */
20
+ readOnly?: boolean;
21
+ /** Number of visible text lines for initial height @default 2 */
22
+ rows?: number;
23
+ /** Number of visible character columns for initial width */
24
+ cols?: number;
25
+ /** Maximum number of characters allowed (UTF-16 code units) */
26
+ maxLength?: number;
27
+ /** Minimum number of characters required */
28
+ minLength?: number;
29
+ /** Whether the textarea is required for form submission @default false */
30
+ required?: boolean;
31
+ /** Form element ID that this textarea belongs to */
32
+ form?: string;
33
+ /** Name attribute for form submission */
34
+ name?: string;
35
+ /** Unique identifier for this textarea element */
36
+ id?: string;
37
+ /** Current value of the textarea (for controlled components) */
38
+ value?: string;
39
+ /** Initial value for uncontrolled components */
40
+ defaultValue?: string;
41
+ /** How text wrapping should be handled @default "soft" */
42
+ wrap?: "soft" | "hard" | "off";
43
+ /** Enable automatic focusing when component mounts @default false */
44
+ autoFocus?: boolean;
45
+ /** Hint for form autofill feature */
46
+ autoComplete?: string;
47
+ /** Control automatic capitalization behavior */
48
+ autoCapitalize?: "none" | "sentences" | "words" | "characters";
49
+ /** Enable spell checking @default true */
50
+ spellCheck?: boolean;
51
+ /** Callback fired when the value changes @param event - The change event */
52
+ onChange?: (event: React.ChangeEvent<HTMLTextAreaElement>) => void;
53
+ /** Callback fired when the textarea gains focus @param event - The focus event */
54
+ onFocus?: (event: React.FocusEvent<HTMLTextAreaElement>) => void;
55
+ /** Callback fired when the textarea loses focus @param event - The blur event */
56
+ onBlur?: (event: React.FocusEvent<HTMLTextAreaElement>) => void;
57
+ /** Callback fired on keydown events @param event - The keyboard event */
58
+ onKeyDown?: (event: React.KeyboardEvent<HTMLTextAreaElement>) => void;
59
+ /** Callback fired on keyup events @param event - The keyboard event */
60
+ onKeyUp?: (event: React.KeyboardEvent<HTMLTextAreaElement>) => void;
61
+ /** Callback fired on keypress events @param event - The keyboard event */
62
+ onKeyPress?: (event: React.KeyboardEvent<HTMLTextAreaElement>) => void;
63
+ /** Indicates if the input fails validation @default false */
64
+ "aria-invalid"?: boolean | "false" | "true" | "grammar" | "spelling";
65
+ /** References element(s) that describe this textarea */
66
+ "aria-describedby"?: string;
67
+ /** References element that labels this textarea */
68
+ "aria-labelledby"?: string;
69
+ /** Accessible label when no visible label is present */
70
+ "aria-label"?: string;
71
+ /** Tab index for keyboard navigation order */
72
+ tabIndex?: number;
73
+ /** CSS resize behavior control */
74
+ style?: React.CSSProperties & {
75
+ resize?: "none" | "both" | "horizontal" | "vertical";
76
+ };
77
+ } & React.TextareaHTMLAttributes<HTMLTextAreaElement>;
78
+
79
+ /**
80
+ * Textarea - Multi-line text input control for extended content entry
81
+ *
82
+ * A styled textarea component built on the native HTML textarea element for entering
83
+ * longer pieces of text that span multiple lines, such as comments, descriptions,
84
+ * messages, or any content that requires multiple lines of input. Features automatic
85
+ * content-based resizing using CSS field-sizing when supported by the browser.
12
86
  *
13
87
  * Built following shadcn/ui design patterns with consistent styling and behavior
14
- * across form elements.
88
+ * across form elements. Inherits all native HTML textarea functionality while
89
+ * providing enhanced visual design and better accessibility patterns.
15
90
  *
16
91
  * @example
17
92
  * ```tsx
18
93
  * // Basic usage
19
- * <Textarea placeholder="Type your message here." />
94
+ * <Textarea placeholder="Type your message here..." />
20
95
  * ```
21
96
  *
22
97
  * @example
@@ -28,14 +103,16 @@ import { cn } from "@/lib/utils";
28
103
  * id="description"
29
104
  * placeholder="Enter a description..."
30
105
  * rows={4}
106
+ * required
31
107
  * />
32
108
  * </div>
33
109
  * ```
34
110
  *
35
111
  * @example
36
112
  * ```tsx
37
- * // Controlled with character limit
113
+ * // Controlled with character limit and validation
38
114
  * const [bio, setBio] = useState("");
115
+ * const [error, setError] = useState("");
39
116
  * const maxLength = 160;
40
117
  *
41
118
  * <div className="space-y-2">
@@ -43,43 +120,117 @@ import { cn } from "@/lib/utils";
43
120
  * <Textarea
44
121
  * id="bio"
45
122
  * value={bio}
46
- * onChange={(e) => setBio(e.target.value)}
123
+ * onChange={(e) => {
124
+ * setBio(e.target.value);
125
+ * if (e.target.value.length > maxLength) {
126
+ * setError("Bio is too long");
127
+ * } else {
128
+ * setError("");
129
+ * }
130
+ * }}
47
131
  * placeholder="Tell us about yourself..."
48
132
  * maxLength={maxLength}
133
+ * aria-invalid={!!error}
134
+ * aria-describedby={error ? "bio-error" : "bio-count"}
49
135
  * />
50
- * <p className="text-sm text-muted-foreground">
51
- * {bio.length}/{maxLength} characters
52
- * </p>
136
+ * <div className="flex justify-between items-center">
137
+ * <p id="bio-count" className="text-sm text-muted-foreground">
138
+ * {bio.length}/{maxLength} characters
139
+ * </p>
140
+ * {error && (
141
+ * <p id="bio-error" className="text-sm text-destructive">
142
+ * {error}
143
+ * </p>
144
+ * )}
145
+ * </div>
53
146
  * </div>
54
147
  * ```
55
148
  *
56
149
  * @example
57
150
  * ```tsx
58
- * // With error state
151
+ * // Auto-resizing with minimum height
59
152
  * <Textarea
60
- * placeholder="Enter your feedback"
61
- * aria-invalid="true"
62
- * aria-describedby="feedback-error"
63
- * className="border-destructive"
153
+ * placeholder="This textarea grows with content"
154
+ * className="min-h-[100px] max-h-[300px]"
155
+ * style={{ resize: "vertical" }}
64
156
  * />
65
- * <p id="feedback-error" className="text-sm text-destructive">
66
- * This field is required
67
- * </p>
68
157
  * ```
69
158
  *
70
- * @param className - Additional CSS classes to apply
71
- * @param ...props - All standard HTML textarea attributes are supported
159
+ * @example
160
+ * ```tsx
161
+ * // Form integration with validation
162
+ * <form onSubmit={handleSubmit}>
163
+ * <div className="space-y-2">
164
+ * <Label htmlFor="feedback">Feedback</Label>
165
+ * <Textarea
166
+ * id="feedback"
167
+ * name="feedback"
168
+ * placeholder="Share your thoughts..."
169
+ * required
170
+ * minLength={10}
171
+ * maxLength={500}
172
+ * rows={3}
173
+ * />
174
+ * </div>
175
+ * <Button type="submit">Submit Feedback</Button>
176
+ * </form>
177
+ * ```
178
+ *
179
+ * @example
180
+ * ```tsx
181
+ * // Different wrap behaviors for code/text
182
+ * <div className="space-y-4">
183
+ * <div>
184
+ * <Label>Code (no wrapping)</Label>
185
+ * <Textarea
186
+ * wrap="off"
187
+ * placeholder="Enter code here..."
188
+ * className="font-mono text-sm"
189
+ * style={{ resize: "both" }}
190
+ * />
191
+ * </div>
192
+ * <div>
193
+ * <Label>Text (soft wrapping)</Label>
194
+ * <Textarea
195
+ * wrap="soft"
196
+ * placeholder="Enter text content..."
197
+ * />
198
+ * </div>
199
+ * </div>
200
+ * ```
72
201
  *
73
202
  * @accessibility
74
- * - Inherits all native textarea accessibility features
75
- * - Supports keyboard navigation with visible focus indicators
76
- * - Compatible with screen readers and assistive technologies
77
- * - Properly handles aria-invalid for validation states
78
- * - Works seamlessly with Label components via htmlFor/id association
79
- * - Respects disabled and readonly states
80
- * - Auto-resizing maintains proper focus and scroll behavior
203
+ * - Full keyboard navigation with Tab to focus and Shift+Tab to unfocus
204
+ * - Screen reader compatible with proper labeling support
205
+ * - ARIA state management for validation (aria-invalid, aria-describedby)
206
+ * - Supports assistive technology form field recognition
207
+ * - Visible focus indicators with ring outline for keyboard users
208
+ * - Respects disabled and read-only states for interaction prevention
209
+ * - Automatic association with Label components via htmlFor/id pattern
210
+ * - Auto-resizing maintains proper focus and scroll position
211
+ * - Character limits announced to screen readers when using aria-describedby
212
+ * - Spell checking integration with assistive technologies
213
+ *
214
+ * @behavior
215
+ * - Auto-resizing based on content using CSS field-sizing property
216
+ * - Smooth transitions for focus states and validation changes
217
+ * - Native browser resize handle (unless overridden with CSS resize property)
218
+ * - Form validation integration with HTML5 constraint validation API
219
+ * - Clipboard operations (cut, copy, paste) work as expected
220
+ * - Text selection and manipulation using standard keyboard shortcuts
221
+ *
222
+ * @styling
223
+ * - Base styles provide consistent appearance across form elements
224
+ * - Focus-visible styles with ring and border color changes
225
+ * - Error state styling via aria-invalid attribute with destructive colors
226
+ * - Dark mode support with appropriate background and text colors
227
+ * - Shadow and border transitions for interactive feedback
228
+ * - Disabled state with reduced opacity and pointer-events disabled
81
229
  *
230
+ * @see {@link https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea} MDN textarea documentation
82
231
  * @see {@link https://ui.shadcn.com/docs/components/textarea} shadcn/ui Textarea documentation
232
+ * @see {@link Label} For proper labeling and accessibility association
233
+ * @see {@link Input} Single-line text input alternative
83
234
  * @since 1.0.0
84
235
  */
85
236
  function Textarea({ className, ...props }: React.ComponentProps<"textarea">) {