@catalystsoftware/ui 1.0.5 → 1.0.6

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 (155) hide show
  1. package/dist/data/data.tsx +29 -29
  2. package/dist/data/tailwind.config.js +3821 -261
  3. package/dist/data.tsx +29 -29
  4. package/package.json +4 -3
  5. package/components/catalyst-ui/buttons/burger.tsx +0 -207
  6. package/components/catalyst-ui/core/data-display/timeline.tsx +0 -210
  7. package/components/catalyst-ui/core/feedback/alert.tsx +0 -491
  8. package/components/catalyst-ui/core/feedback/spinner-1.tsx +0 -65
  9. package/components/catalyst-ui/core/feedback/toast.tsx +0 -1857
  10. package/components/catalyst-ui/core/navigation/menu.tsx +0 -164
  11. package/components/catalyst-ui/forms/toggle-class.tsx +0 -176
  12. package/components/catalyst-ui/hooks/use-copy-to-clipboard.tsx +0 -419
  13. package/components/catalyst-ui/hooks/use-counter.tsx +0 -13
  14. package/components/catalyst-ui/hooks/use-event-listener.tsx +0 -23
  15. package/components/catalyst-ui/hooks/use-export-markdown.tsx +0 -47
  16. package/components/catalyst-ui/hooks/use-focus.tsx +0 -17
  17. package/components/catalyst-ui/hooks/use-interval.tsx +0 -23
  18. package/components/catalyst-ui/hooks/use-is-client.tsx +0 -16
  19. package/components/catalyst-ui/hooks/use-media-query.tsx +0 -19
  20. package/components/catalyst-ui/hooks/use-mobile.tsx +0 -19
  21. package/components/catalyst-ui/hooks/use-resize-observer.tsx +0 -81
  22. package/components/catalyst-ui/hooks/use-timeout.tsx +0 -21
  23. package/components/catalyst-ui/hooks/use-timer.tsx +0 -209
  24. package/components/catalyst-ui/hooks/use-toggle.tsx +0 -12
  25. package/components/catalyst-ui/media/image.tsx +0 -13
  26. package/components/catalyst-ui/overlays/dual-sidebar.tsx +0 -4142
  27. package/components/catalyst-ui/overlays/sidebar-original.tsx +0 -726
  28. package/components/catalyst-ui/primitives/accordion.tsx +0 -250
  29. package/components/catalyst-ui/primitives/alert-dialog.tsx +0 -126
  30. package/components/catalyst-ui/primitives/aspect-ratio.tsx +0 -9
  31. package/components/catalyst-ui/primitives/avatar.tsx +0 -296
  32. package/components/catalyst-ui/primitives/badge.tsx +0 -57
  33. package/components/catalyst-ui/primitives/breadcrumb.tsx +0 -101
  34. package/components/catalyst-ui/primitives/button.tsx +0 -265
  35. package/components/catalyst-ui/primitives/calendar-v4.tsx +0 -208
  36. package/components/catalyst-ui/primitives/calendar.tsx +0 -295
  37. package/components/catalyst-ui/primitives/card.tsx +0 -618
  38. package/components/catalyst-ui/primitives/carousel.tsx +0 -238
  39. package/components/catalyst-ui/primitives/chart.tsx +0 -347
  40. package/components/catalyst-ui/primitives/checkbox.tsx +0 -225
  41. package/components/catalyst-ui/primitives/collapsible.tsx +0 -212
  42. package/components/catalyst-ui/primitives/command.tsx +0 -393
  43. package/components/catalyst-ui/primitives/context-menu.tsx +0 -236
  44. package/components/catalyst-ui/primitives/dialog.tsx +0 -471
  45. package/components/catalyst-ui/primitives/drawer.tsx +0 -761
  46. package/components/catalyst-ui/primitives/dropdown-menu.tsx +0 -290
  47. package/components/catalyst-ui/primitives/empty.tsx +0 -104
  48. package/components/catalyst-ui/primitives/field.tsx +0 -244
  49. package/components/catalyst-ui/primitives/hover-card.tsx +0 -124
  50. package/components/catalyst-ui/primitives/input-otp.tsx +0 -76
  51. package/components/catalyst-ui/primitives/input.tsx +0 -64
  52. package/components/catalyst-ui/primitives/item.tsx +0 -196
  53. package/components/catalyst-ui/primitives/kbd.tsx +0 -75
  54. package/components/catalyst-ui/primitives/label.tsx +0 -24
  55. package/components/catalyst-ui/primitives/navigation-menu.tsx +0 -150
  56. package/components/catalyst-ui/primitives/pagination.tsx +0 -198
  57. package/components/catalyst-ui/primitives/popover.tsx +0 -232
  58. package/components/catalyst-ui/primitives/progress.tsx +0 -34
  59. package/components/catalyst-ui/primitives/radio-group.tsx +0 -43
  60. package/components/catalyst-ui/primitives/resizable.tsx +0 -56
  61. package/components/catalyst-ui/primitives/select.tsx +0 -155
  62. package/components/catalyst-ui/primitives/separator.tsx +0 -74
  63. package/components/catalyst-ui/primitives/sheet.tsx +0 -126
  64. package/components/catalyst-ui/primitives/skeleton.tsx +0 -15
  65. package/components/catalyst-ui/primitives/slider.tsx +0 -27
  66. package/components/catalyst-ui/primitives/switch.tsx +0 -187
  67. package/components/catalyst-ui/primitives/tabs.tsx +0 -335
  68. package/components/catalyst-ui/primitives/textarea.tsx +0 -24
  69. package/components/catalyst-ui/primitives/toggle-group.tsx +0 -55
  70. package/components/catalyst-ui/primitives/toggle.tsx +0 -42
  71. package/components/catalyst-ui/primitives/tooltip.tsx +0 -116
  72. package/components/catalyst-ui/utils/basic-auth.tsx +0 -40
  73. package/components/catalyst-ui/utils/context-storage.tsx +0 -19
  74. package/components/catalyst-ui/utils/cors-middleware.tsx +0 -71
  75. package/components/catalyst-ui/utils/deferred-content.tsx +0 -595
  76. package/components/catalyst-ui/utils/honeypot-middleware.tsx +0 -38
  77. package/components/catalyst-ui/utils/incId.tsx +0 -75
  78. package/components/catalyst-ui/utils/jwk-auth.tsx +0 -36
  79. package/components/catalyst-ui/utils/request-id.tsx +0 -14
  80. package/components/catalyst-ui/utils/secure-headers.tsx +0 -37
  81. package/components/catalyst-ui/utils/server-timing.tsx +0 -23
  82. package/components/catalyst-ui/utils/utils.ts +0 -43
  83. package/components/catalyst-ui/utils/with-cookie.tsx +0 -43
  84. package/components/catalyst-ui/x/accordian-x.tsx +0 -428
  85. package/components/catalyst-ui/x/alert-x.tsx +0 -413
  86. package/components/catalyst-ui/x/animated-text-x.tsx +0 -2242
  87. package/components/catalyst-ui/x/avatar-x.tsx +0 -515
  88. package/components/catalyst-ui/x/badge-x.tsx +0 -670
  89. package/components/catalyst-ui/x/button-X.tsx +0 -2857
  90. package/components/catalyst-ui/x/button-group-x.tsx +0 -847
  91. package/components/catalyst-ui/x/calendar-x.tsx +0 -1910
  92. package/components/catalyst-ui/x/card-x.tsx +0 -2597
  93. package/components/catalyst-ui/x/checkbox-x.tsx +0 -656
  94. package/components/catalyst-ui/x/collapsible-x.tsx +0 -1360
  95. package/components/catalyst-ui/x/combobox-x.tsx +0 -911
  96. package/components/catalyst-ui/x/data-table-x.tsx +0 -1753
  97. package/components/catalyst-ui/x/date-picker-x.tsx +0 -648
  98. package/components/catalyst-ui/x/dialog-x.tsx +0 -659
  99. package/components/catalyst-ui/x/dropdown-menu-x.tsx +0 -612
  100. package/components/catalyst-ui/x/hover-card-x.tsx +0 -375
  101. package/components/catalyst-ui/x/icon-x.tsx +0 -840
  102. package/components/catalyst-ui/x/input-mask-x.tsx +0 -981
  103. package/components/catalyst-ui/x/input-otp-x.tsx +0 -659
  104. package/components/catalyst-ui/x/loader-x.tsx +0 -1757
  105. package/components/catalyst-ui/x/pagination-x.tsx +0 -622
  106. package/components/catalyst-ui/x/popover-x.tsx +0 -744
  107. package/components/catalyst-ui/x/radio-group-x.tsx +0 -499
  108. package/components/catalyst-ui/x/select-x.tsx +0 -1127
  109. package/components/catalyst-ui/x/sheet-x.tsx +0 -668
  110. package/components/catalyst-ui/x/switch-x.tsx +0 -681
  111. package/components/catalyst-ui/x/table-x.tsx +0 -574
  112. package/components/catalyst-ui/x/tabs-x.tsx +0 -839
  113. package/components/catalyst-ui/x/textarea-x.tsx +0 -1263
  114. package/components/catalyst-ui/x/tooltip-x.tsx +0 -396
  115. package/components/catalyst-ui/x/tracker-x.tsx +0 -560
  116. package/data/bg-data.tsx +0 -901
  117. package/data/buttons-data.tsx +0 -2327
  118. package/data/charts-data.tsx +0 -102
  119. package/data/chat-data.tsx +0 -83
  120. package/data/code-data.tsx +0 -1040
  121. package/data/comboboxes-data.tsx +0 -1843
  122. package/data/command-data.tsx +0 -1381
  123. package/data/core-data.tsx +0 -15953
  124. package/data/crm-data.tsx +0 -47
  125. package/data/data.tsx +0 -159
  126. package/data/date-and-time-data.tsx +0 -554
  127. package/data/dependencies.tsx +0 -7
  128. package/data/ecommerce-data.tsx +0 -1387
  129. package/data/forms-data.tsx +0 -7890
  130. package/data/hooks-data.tsx +0 -5487
  131. package/data/index.ts +0 -34
  132. package/data/inputs-data.tsx +0 -557
  133. package/data/interactive-data.tsx +0 -5394
  134. package/data/lofi-data.tsx +0 -18295
  135. package/data/marketing-data.tsx +0 -2546
  136. package/data/media-data.tsx +0 -1510
  137. package/data/motion-data.tsx +0 -5801
  138. package/data/overlay-data.tsx +0 -4136
  139. package/data/pdf-data.tsx +0 -124
  140. package/data/pos-data.tsx +0 -213
  141. package/data/postcss.config.js +0 -6
  142. package/data/primitive-data.tsx +0 -5170
  143. package/data/prompt-data.tsx +0 -1226
  144. package/data/requiredLibs.ts +0 -4
  145. package/data/sandbox-data.tsx +0 -1
  146. package/data/sidebars-data.tsx +0 -5421
  147. package/data/stacks-data.tsx +0 -32
  148. package/data/table-data.tsx +0 -706
  149. package/data/tailwind.config.js +0 -270
  150. package/data/tailwind.config.ngin.js +0 -3830
  151. package/data/tailwind.css +0 -431
  152. package/data/tools-data.tsx +0 -6910
  153. package/data/typography-data.tsx +0 -2050
  154. package/data/utils-data.tsx +0 -6500
  155. package/data/x-data.tsx +0 -1171
@@ -1,1263 +0,0 @@
1
- import React, { useId, useState, ChangeEvent } from 'react'
2
- import { cva } from 'class-variance-authority'
3
- import { cn, Field, FieldLabel, FieldDescription ,FieldError ,Button , Label, Textarea , } from "~/components/catalyst-ui"
4
- import { Home } from 'lucide-react'
5
-
6
- /**
7
- * ★ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ TextareaX Component ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ★
8
- * ★ ━━━━ ☆ ━━━━ ━━━━ ☆ ━━━━ ★
9
- * TextareaX: A versatile textarea component with 20+ pre-built variations and customizable styles
10
- *
11
- * ★ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━ Basic Textarea ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ★
12
- * ```jsx
13
- * // Default textarea
14
- * <TextareaX placeholder="Type your message here." />
15
- *
16
- * // With label
17
- * <TextareaX textarea="WithLabel" label="Description" placeholder="Enter description" />
18
- *
19
- * // With helper text
20
- * <TextareaX textarea="WithHelperText" label="Feedback" description="Your feedback helps us improve." />
21
- * ```
22
- *
23
- * ★ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━ Validation States ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ★
24
- * ```jsx
25
- * // Invalid/error state
26
- * <TextareaX textarea="Invalid" label="Required Field" error="This field is required" />
27
- *
28
- * // With hint text
29
- * <TextareaX textarea="WithHintText" label="Optional Field" hintText="Optional" />
30
- *
31
- * // Required field
32
- * <TextareaX textarea="Required" label="Required Field" placeholder="This field is required" />
33
- * ```
34
- *
35
- * ★ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━ Visual Variations ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ★
36
- * ```jsx
37
- * // Filled variant
38
- * <TextareaX textarea="Filled" label="Filled Textarea" placeholder="Has filled background" />
39
- *
40
- * // With colored border
41
- * <TextareaX textarea="WithColoredBorder" label="Colored Focus" placeholder="Custom focus ring" />
42
- *
43
- * // Different sizes
44
- * <TextareaX textarea="Small" label="Small" placeholder="Small textarea" />
45
- * <TextareaX textarea="Large" label="Large" placeholder="Large textarea" />
46
- * ```
47
- *
48
- * ★ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━ With Icons ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ★
49
- * ```jsx
50
- * // Start icon (left side)
51
- * <TextareaX
52
- * textarea="StartIcon"
53
- * label="Address"
54
- * icon={Home}
55
- * placeholder="Enter your address"
56
- * />
57
- *
58
- * // End icon (right side)
59
- * <TextareaX
60
- * textarea="EndIcon"
61
- * label="Search"
62
- * icon={Search}
63
- * placeholder="Search here..."
64
- * />
65
- *
66
- * // Custom icon styling
67
- * <TextareaX
68
- * textarea="StartIcon"
69
- * label="Custom Icon"
70
- * icon={Mail}
71
- * iconCN="size-5 text-blue-500"
72
- * placeholder="Email content"
73
- * />
74
- * ```
75
- *
76
- * ★ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━ Label Styles ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ★
77
- * ```jsx
78
- * // Overlapping label
79
- * <TextareaX textarea="WithOverlappingLabel" placeholder="Content here" />
80
- *
81
- * // Floating label
82
- * <TextareaX textarea="WithFloatingLabel" placeholder=" " />
83
- *
84
- * // Inset label (inside border)
85
- * <TextareaX textarea="WithInsetLabel" placeholder="Type here" />
86
- * ```
87
- *
88
- * ★ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━ Enhanced Features ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ★
89
- * ```jsx
90
- * // With action button
91
- * <TextareaX
92
- * textarea="WithButton"
93
- * label="Feedback"
94
- * buttonText="Submit"
95
- * placeholder="Your feedback here"
96
- * />
97
- *
98
- * // Auto-growing (expands with content)
99
- * <TextareaX textarea="AutoGrow" label="Auto-growing" placeholder="Type and watch it grow" />
100
- *
101
- * // No resize
102
- * <TextareaX textarea="NoResize" label="Fixed size" placeholder="Cannot resize this textarea" />
103
- *
104
- * // Character counter
105
- * <TextareaX
106
- * textarea="CharacterLeft"
107
- * label="Character Limit"
108
- * maxLength={500}
109
- * placeholder="Type up to 500 characters"
110
- * />
111
- * ```
112
- *
113
- * ★ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━ State Variations ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ★
114
- * ```jsx
115
- * // Read-only
116
- * <TextareaX
117
- * textarea="ReadOnly"
118
- * label="Read Only Content"
119
- * defaultValue="This content cannot be edited"
120
- * />
121
- *
122
- * // Disabled
123
- * <TextareaX textarea="Disabled" label="Disabled Field" placeholder="Cannot edit this field" />
124
- * ```
125
- *
126
- * ★ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━ Custom Styling ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ★
127
- * ```jsx
128
- * // Custom variants
129
- * <TextareaX
130
- * variant="filled"
131
- * size="lg"
132
- * rounded="lg"
133
- * state="success"
134
- * label="Success State"
135
- * placeholder="Successfully validated"
136
- * />
137
- *
138
- * // With custom className
139
- * <TextareaX
140
- * label="Custom Styled"
141
- * className="border-2 border-purple-500 focus:ring-purple-500"
142
- * placeholder="Purple themed"
143
- * />
144
- *
145
- * // Compact layout
146
- * <TextareaX
147
- * textarea="Small"
148
- * variant="ghost"
149
- * rounded="none"
150
- * label="Compact"
151
- * placeholder="Minimal styling"
152
- * />
153
- * ```
154
- *
155
- * ★ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━ Props Reference ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ★
156
- * - TextareaX Props:
157
- * - `textarea`: string - Pre-built variation name (see list below)
158
- * - `label`: string - Label text for the textarea
159
- * - `description`: string - Helper/description text
160
- * - `error`: string - Error message text
161
- * - `hintText`: string - Hint text (usually "Optional")
162
- * - `maxLength`: number - Maximum character count
163
- * - `buttonText`: string - Text for action button
164
- * - `icon`: LucideIcon - Icon component
165
- * - `iconCN`: string - Icon class names
166
- * - `initialValue`: string - Initial text value
167
- *
168
- * - Variant Props (passed to textareaVariants):
169
- * - `variant`: 'default' | 'filled' | 'ghost'
170
- * - `size`: 'sm' | 'default' | 'lg'
171
- * - `rounded`: 'sm' | 'default' | 'lg' | 'full' | 'none'
172
- * - `state`: 'default' | 'error' | 'success'
173
- * - `className`: string - Additional CSS classes
174
- *
175
- * ★ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━ Available Textarea Variations ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ★
176
- * 1. **Default** - Basic textarea
177
- * 2. **WithLabel** - Textarea with label
178
- * 3. **WithHelperText** - With helper text below
179
- * 4. **WithHelperTextRight** - Helper text aligned right
180
- * 5. **Invalid** - Error state with error message
181
- * 6. **WithHintText** - Label with optional hint
182
- * 7. **Required** - Required field indicator
183
- * 8. **WithColoredBorder** - Custom focus border colors
184
- * 9. **Filled** - Filled background variant
185
- * 10. **Small** - Small size textarea
186
- * 11. **Large** - Large size textarea
187
- * 12. **StartIcon** - Textarea with left icon
188
- * 13. **EndIcon** - Textarea with right icon
189
- * 14. **WithOverlappingLabel** - Label overlaps border
190
- * 15. **WithFloatingLabel** - Animated floating label
191
- * 16. **WithInsetLabel** - Label inside border
192
- * 17. **WithButton** - Textarea with action button
193
- * 18. **AutoGrow** - Automatically expands with content
194
- * 19. **NoResize** - Disabled resize handle
195
- * 20. **CharacterLeft** - Character counter
196
- * 21. **ReadOnly** - Read-only state
197
- * 22. **Disabled** - Disabled state
198
- *
199
- * ★ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━ Accessibility Features ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ★
200
- * - **Auto IDs**: Automatic ID generation for label association
201
- * - **ARIA Labels**: Proper aria-invalid for error states
202
- * - **Keyboard Navigation**: Full keyboard support
203
- * - **Screen Reader**: All text content accessible to screen readers
204
- * - **Focus Management**: Clear focus indicators and rings
205
- *
206
- * ★ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━ Form Integration Examples ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ★
207
- * ```jsx
208
- * // Contact form
209
- * <form className="space-y-4">
210
- * <TextareaX textarea="WithLabel" label="Message" placeholder="Your message" required />
211
- * <TextareaX textarea="WithHintText" label="Comments" hintText="Optional" />
212
- * <button type="submit">Submit</button>
213
- * </form>
214
- *
215
- * // Feedback form with character limit
216
- * <form className="space-y-4">
217
- * <TextareaX
218
- * textarea="CharacterLeft"
219
- * label="Feedback"
220
- * maxLength={1000}
221
- * placeholder="Your feedback (max 1000 characters)"
222
- * />
223
- * <TextareaX textarea="WithButton" buttonText="Submit Feedback" />
224
- * </form>
225
- *
226
- * // Settings form with read-only fields
227
- * <form className="space-y-4">
228
- * <TextareaX
229
- * textarea="ReadOnly"
230
- * label="System Information"
231
- * defaultValue="System: macOS\nVersion: 14.0"
232
- * />
233
- * <TextareaX textarea="Disabled" label="Coming Soon" placeholder="Feature not available yet" />
234
- * </form>
235
- * ```
236
- *
237
- * ★ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━ Advanced Usage Patterns ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ★
238
- * ```jsx
239
- * // Dynamic state management
240
- * function FeedbackForm() {
241
- * const [feedback, setFeedback] = useState('')
242
- * const [characterCount, setCharacterCount] = useState(0)
243
- *
244
- * return (
245
- * <TextareaX
246
- * textarea="CharacterLeft"
247
- * label="Your Feedback"
248
- * maxLength={500}
249
- * initialValue={feedback}
250
- * value={feedback}
251
- * onChange={(e) => {
252
- * setFeedback(e.target.value)
253
- * setCharacterCount(e.target.value.length)
254
- * }}
255
- * />
256
- * )
257
- * }
258
- *
259
- * // Controlled component with validation
260
- * function ContactForm() {
261
- * const [message, setMessage] = useState('')
262
- * const [error, setError] = useState('')
263
- *
264
- * const validate = () => {
265
- * if (message.length < 10) {
266
- * setError('Message must be at least 10 characters')
267
- * return false
268
- * }
269
- * setError('')
270
- * return true
271
- * }
272
- *
273
- * return (
274
- * <TextareaX
275
- * textarea="Invalid"
276
- * label="Message"
277
- * error={error}
278
- * value={message}
279
- * onChange={(e) => setMessage(e.target.value)}
280
- * onBlur={validate}
281
- * />
282
- * )
283
- * }
284
- *
285
- * // Themed textarea variations
286
- * function ThemedTextareas() {
287
- * return (
288
- * <div className="space-y-6">
289
- * <TextareaX
290
- * variant="filled"
291
- * size="lg"
292
- * rounded="lg"
293
- * state="success"
294
- * label="Success Theme"
295
- * placeholder="Success state"
296
- * />
297
- * <TextareaX
298
- * textarea="WithColoredBorder"
299
- * label="Brand Theme"
300
- * className="focus-visible:border-brand-500 focus-visible:ring-brand-500/20"
301
- * placeholder="Brand colored"
302
- * />
303
- * </div>
304
- * )
305
- * }
306
- * ```
307
- *
308
- * ★ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━ Complete Example ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ☆ ━━━━━━━━━━━━━━ ★
309
- * ```jsx
310
- * function CompleteFormExample() {
311
- * const [formData, setFormData] = useState({
312
- * feedback: '',
313
- * suggestions: '',
314
- * comments: ''
315
- * })
316
- *
317
- * return (
318
- * <div className="max-w-2xl space-y-6">
319
- * <h2 className="text-2xl font-bold">Feedback Form</h2>
320
- *
321
- * <TextareaX
322
- * textarea="Required"
323
- * label="Your Feedback"
324
- * placeholder="Please provide detailed feedback"
325
- * value={formData.feedback}
326
- * onChange={(e) => setFormData({...formData, feedback: e.target.value})}
327
- * />
328
- *
329
- * <TextareaX
330
- * textarea="AutoGrow"
331
- * label="Suggestions"
332
- * placeholder="Your suggestions (auto-expands)"
333
- * value={formData.suggestions}
334
- * onChange={(e) => setFormData({...formData, suggestions: e.target.value})}
335
- * />
336
- *
337
- * <TextareaX
338
- * textarea="CharacterLeft"
339
- * label="Additional Comments"
340
- * hintText="Optional"
341
- * maxLength={250}
342
- * placeholder="Additional comments (max 250 characters)"
343
- * value={formData.comments}
344
- * onChange={(e) => setFormData({...formData, comments: e.target.value})}
345
- * />
346
- *
347
- * <TextareaX
348
- * textarea="WithButton"
349
- * buttonText="Submit Feedback"
350
- * />
351
- * </div>
352
- * )
353
- * }
354
- * ```
355
- *
356
- */
357
-
358
- export const textareaVariants = cva(
359
- 'flex min-h-20 w-full rounded-md border border-input bg-background px-3 py-2 text-sm ring-offset-background placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50',
360
- {
361
- variants: {
362
- variant: {
363
- default: '',
364
- filled: 'bg-muted border-transparent shadow-none',
365
- ghost: 'border-transparent shadow-none',
366
- },
367
- size: {
368
- sm: 'min-h-10 py-1.5 text-xs',
369
- default: 'min-h-20 py-2',
370
- lg: 'min-h-30 py-2.5 text-base',
371
- },
372
- rounded: {
373
- default: 'rounded-md',
374
- sm: 'rounded-sm',
375
- lg: 'rounded-lg',
376
- full: 'rounded-full',
377
- none: 'rounded-none',
378
- },
379
- state: {
380
- default: '',
381
- error: 'border-destructive focus-visible:ring-destructive',
382
- success: 'border-green-500 focus-visible:ring-green-500',
383
- },
384
- },
385
- defaultVariants: {
386
- variant: 'default',
387
- size: 'default',
388
- rounded: 'default',
389
- state: 'default',
390
- },
391
- }
392
- )
393
-
394
- export function TextareaX({ textarea = 'Default', ...props }: any) {
395
- switch (textarea) {
396
- case 'WithLabel':
397
- return <WithLabel {...props} />
398
- case 'WithHelperText':
399
- return <WithHelperText {...props} />
400
- case 'WithHelperTextRight':
401
- return <WithHelperTextRight {...props} />
402
- case 'Invalid':
403
- return <Invalid {...props} />
404
- case 'WithHintText':
405
- return <WithHintText {...props} />
406
- case 'Required':
407
- return <Required {...props} />
408
- case 'WithColoredBorder':
409
- return <WithColoredBorder {...props} />
410
- case 'Filled':
411
- return <Filled {...props} />
412
- case 'Small':
413
- return <Small {...props} />
414
- case 'Large':
415
- return <Large {...props} />
416
- case 'StartIcon':
417
- return <StartIcon {...props} />
418
- case 'EndIcon':
419
- return <EndIcon {...props} />
420
- case 'WithOverlappingLabel':
421
- return <WithOverlappingLabel {...props} />
422
- case 'WithFloatingLabel':
423
- return <WithFloatingLabel {...props} />
424
- case 'WithInsetLabel':
425
- return <WithInsetLabel {...props} />
426
- case 'WithButton':
427
- return <WithButton {...props} />
428
- case 'AutoGrow':
429
- return <AutoGrow {...props} />
430
- case 'NoResize':
431
- return <NoResize {...props} />
432
- case 'CharacterLeft':
433
- return <CharacterLeft {...props} />
434
- case 'ReadOnly':
435
- return <ReadOnly {...props} />
436
- case 'Disabled':
437
- return <Disabled {...props} />
438
- default:
439
- return <DefaultTextarea {...props} />
440
- }
441
- }
442
-
443
-
444
-
445
- const DefaultTextarea = ({ label, description, error, variant, size, rounded, state, className, ...props }: any) => {
446
- const id = useId()
447
- return (
448
- <Field className='w-full max-w-xs space-y-2'>
449
- {label && <FieldLabel htmlFor={id}>{label}</FieldLabel>}
450
- <Textarea
451
- id={id}
452
- className={cn(textareaVariants({ variant, size, rounded, state }), className)}
453
- {...props}
454
- />
455
- {description && <FieldDescription>{description}</FieldDescription>}
456
- {error && <FieldError>{error}</FieldError>}
457
- </Field>
458
- )
459
- }
460
-
461
- const WithLabel = ({ label = 'Textarea with label', description, error, variant, size, rounded, state, className, ...props }: any) => {
462
- const id = useId()
463
- return (
464
- <Field className='w-full max-w-xs space-y-2'>
465
- <FieldLabel htmlFor={id}>{label}</FieldLabel>
466
- <Textarea
467
- id={id}
468
- className={cn(textareaVariants({ variant, size, rounded, state }), className)}
469
- {...props}
470
- />
471
- {description && <FieldDescription>{description}</FieldDescription>}
472
- {error && <FieldError>{error}</FieldError>}
473
- </Field>
474
- )
475
- }
476
-
477
- const WithHelperText = ({ label, description = 'Your feedback is useful for us.', error, variant, size, rounded, state, className, ...props }: any) => {
478
- const id = useId()
479
- return (
480
- <Field className='w-full max-w-xs space-y-2'>
481
- {label && <FieldLabel htmlFor={id}>{label}</FieldLabel>}
482
- <Textarea
483
- id={id}
484
- className={cn(textareaVariants({ variant, size, rounded, state }), className)}
485
- {...props}
486
- />
487
- <FieldDescription>{description}</FieldDescription>
488
- {error && <FieldError>{error}</FieldError>}
489
- </Field>
490
- )
491
- }
492
-
493
- const WithHelperTextRight = ({ label, description = 'Your feedback is useful for us.', error, variant, size, rounded, state, className, ...props }: any) => {
494
- const id = useId()
495
- return (
496
- <Field className='w-full max-w-xs space-y-2'>
497
- {label && <FieldLabel htmlFor={id}>{label}</FieldLabel>}
498
- <Textarea
499
- id={id}
500
- className={cn(textareaVariants({ variant, size, rounded, state }), className)}
501
- {...props}
502
- />
503
- <p className='text-muted-foreground text-end text-xs'>{description}</p>
504
- {error && <FieldError>{error}</FieldError>}
505
- </Field>
506
- )
507
- }
508
-
509
- const Invalid = ({ label = 'Invalid Textarea', description, error = 'Your feedback is useful for us.', variant, size, rounded, className, ...props }: any) => {
510
- const id = useId()
511
- return (
512
- <Field className='w-full max-w-xs space-y-2'>
513
- <FieldLabel htmlFor={id}>{label}</FieldLabel>
514
- <Textarea
515
- id={id}
516
- aria-invalid
517
- className={cn(textareaVariants({ variant, size, rounded, state: 'error' }), className)}
518
- {...props}
519
- />
520
- {description && <FieldDescription>{description}</FieldDescription>}
521
- <FieldError>{error}</FieldError>
522
- </Field>
523
- )
524
- }
525
-
526
- const WithHintText = ({ label = 'Textarea with hint text', hintText = 'Optional field', description, error, variant, size, rounded, state, className, ...props }: any) => {
527
- const id = useId()
528
- return (
529
- <Field className='w-full max-w-xs space-y-2'>
530
- <div className='flex items-center justify-between gap-1'>
531
- <FieldLabel htmlFor={id}>{label}</FieldLabel>
532
- <span className='text-muted-foreground text-xs'>{hintText}</span>
533
- </div>
534
- <Textarea
535
- id={id}
536
- className={cn(textareaVariants({ variant, size, rounded, state }), className)}
537
- {...props}
538
- />
539
- {description && <FieldDescription>{description}</FieldDescription>}
540
- {error && <FieldError>{error}</FieldError>}
541
- </Field>
542
- )
543
- }
544
-
545
- const Required = ({ label = 'Required textarea', description, error, variant, size, rounded, state, className, ...props }: any) => {
546
- const id = useId()
547
- return (
548
- <Field className='w-full max-w-xs space-y-2'>
549
- <FieldLabel htmlFor={id}>
550
- {label} <span className='text-destructive'>*</span>
551
- </FieldLabel>
552
- <Textarea
553
- id={id}
554
- required
555
- className={cn(textareaVariants({ variant, size, rounded, state }), className)}
556
- {...props}
557
- />
558
- {description && <FieldDescription>{description}</FieldDescription>}
559
- {error && <FieldError>{error}</FieldError>}
560
- </Field>
561
- )
562
- }
563
-
564
- const WithColoredBorder = ({ label = 'Textarea with colored border and ring', description, error, variant, size, rounded, state, className, ...props }: any) => {
565
- const id = useId()
566
- return (
567
- <Field className='w-full max-w-xs space-y-2'>
568
- <FieldLabel htmlFor={id}>{label}</FieldLabel>
569
- <Textarea
570
- id={id}
571
- className={cn(
572
- textareaVariants({ variant, size, rounded, state }),
573
- 'focus-visible:border-indigo-500 focus-visible:ring-indigo-500/20 dark:focus-visible:ring-indigo-500/40',
574
- className
575
- )}
576
- {...props}
577
- />
578
- {description && <FieldDescription>{description}</FieldDescription>}
579
- {error && <FieldError>{error}</FieldError>}
580
- </Field>
581
- )
582
- }
583
-
584
- const Filled = ({ label = 'Filled Textarea', description, error, variant, size, rounded, state, className, ...props }: any) => {
585
- const id = useId()
586
- return (
587
- <Field className='w-full max-w-xs space-y-2'>
588
- <FieldLabel htmlFor={id}>{label}</FieldLabel>
589
- <Textarea
590
- id={id}
591
- className={cn(textareaVariants({ variant: 'filled', size, rounded, state }), className)}
592
- {...props}
593
- />
594
- {description && <FieldDescription>{description}</FieldDescription>}
595
- {error && <FieldError>{error}</FieldError>}
596
- </Field>
597
- )
598
- }
599
-
600
- const Small = ({ label, description, error, variant, rounded, state, className, ...props }: any) => {
601
- const id = useId()
602
- return (
603
- <Field className='w-full max-w-xs space-y-2'>
604
- {label && <FieldLabel htmlFor={id}>{label}</FieldLabel>}
605
- <Textarea
606
- id={id}
607
- className={cn(textareaVariants({ variant, size: 'sm', rounded, state }), className)}
608
- {...props}
609
- />
610
- {description && <FieldDescription>{description}</FieldDescription>}
611
- {error && <FieldError>{error}</FieldError>}
612
- </Field>
613
- )
614
- }
615
-
616
- const Large = ({ label, description, error, variant, rounded, state, className, ...props }: any) => {
617
- const id = useId()
618
- return (
619
- <Field className='w-full max-w-xs space-y-2'>
620
- {label && <FieldLabel htmlFor={id}>{label}</FieldLabel>}
621
- <Textarea
622
- id={id}
623
- className={cn(textareaVariants({ variant, size: 'lg', rounded, state }), className)}
624
- {...props}
625
- />
626
- {description && <FieldDescription>{description}</FieldDescription>}
627
- {error && <FieldError>{error}</FieldError>}
628
- </Field>
629
- )
630
- }
631
-
632
- const StartIcon = ({ icon: Icon = Home, iconCN = 'size-4', label = 'Textarea with start icon', description, error, variant, size, rounded, state, className, ...props }: any) => {
633
- const id = useId()
634
- return (
635
- <Field className='w-full max-w-xs space-y-2'>
636
- <FieldLabel htmlFor={id}>{label}</FieldLabel>
637
- <div className='relative'>
638
- <div className='text-muted-foreground pointer-events-none absolute top-2.5 left-0 flex items-center justify-center pl-3 peer-disabled:opacity-50'>
639
- <Icon className={iconCN} />
640
- <span className='sr-only'>Icon</span>
641
- </div>
642
- <Textarea
643
- id={id}
644
- className={cn(textareaVariants({ variant, size, rounded, state }), 'peer pl-9', className)}
645
- {...props}
646
- />
647
- </div>
648
- {description && <FieldDescription>{description}</FieldDescription>}
649
- {error && <FieldError>{error}</FieldError>}
650
- </Field>
651
- )
652
- }
653
-
654
- const EndIcon = ({ icon: Icon = Home, iconCN = 'size-4', label = 'Textarea with end icon', description, error, variant, size, rounded, state, className, ...props }: any) => {
655
- const id = useId()
656
- return (
657
- <Field className='w-full max-w-xs space-y-2'>
658
- <FieldLabel htmlFor={id}>{label}</FieldLabel>
659
- <div className='relative'>
660
- <Textarea
661
- id={id}
662
- className={cn(textareaVariants({ variant, size, rounded, state }), 'peer pr-9', className)}
663
- {...props}
664
- />
665
- <div className='text-muted-foreground pointer-events-none absolute top-2.5 right-0 flex items-center justify-center pr-3 peer-disabled:opacity-50'>
666
- <Icon className={iconCN} />
667
- <span className='sr-only'>Icon</span>
668
- </div>
669
- </div>
670
- {description && <FieldDescription>{description}</FieldDescription>}
671
- {error && <FieldError>{error}</FieldError>}
672
- </Field>
673
- )
674
- }
675
-
676
- const WithOverlappingLabel = ({ label = 'Textarea with overlapping label', description, error, variant, size, rounded, state, className, ...props }: any) => {
677
- const id = useId()
678
- return (
679
- <Field className='relative w-full max-w-xs space-y-2'>
680
- <Label
681
- htmlFor={id}
682
- className='bg-background text-foreground absolute top-0 left-2 z-10 block -translate-y-1/2 px-1 text-xs font-medium group-has-disabled:opacity-50'
683
- >
684
- {label}
685
- </Label>
686
- <Textarea
687
- id={id}
688
- className={cn(textareaVariants({ variant, size, rounded, state }), '!bg-background', className)}
689
- {...props}
690
- />
691
- {description && <FieldDescription>{description}</FieldDescription>}
692
- {error && <FieldError>{error}</FieldError>}
693
- </Field>
694
- )
695
- }
696
-
697
- const WithFloatingLabel = ({ label = 'Textarea with floating label', description, error, variant, size, rounded, state, className, ...props }: any) => {
698
- const id = useId()
699
- return (
700
- <Field className='w-full max-w-xs space-y-2'>
701
- <div className='group relative w-full max-w-xs'>
702
- <label
703
- htmlFor={id}
704
- className='origin-start text-muted-foreground/70 group-focus-within:text-foreground has-[+textarea:not(:placeholder-shown)]:text-foreground has-aria-invalid:ring-destructive/20 dark:has-aria-invalid:ring-destructive/40 has-aria-invalid:border-destructive absolute top-0 block translate-y-2 cursor-text px-2 text-sm transition-all group-focus-within:pointer-events-none group-focus-within:-translate-y-1/2 group-focus-within:cursor-default group-focus-within:text-xs group-focus-within:font-medium has-[+textarea:not(:placeholder-shown)]:pointer-events-none has-[+textarea:not(:placeholder-shown)]:-translate-y-1/2 has-[+textarea:not(:placeholder-shown)]:cursor-default has-[+textarea:not(:placeholder-shown)]:text-xs has-[+textarea:not(:placeholder-shown)]:font-medium'
705
- >
706
- <span className='bg-background inline-flex px-1'>{label}</span>
707
- </label>
708
- <Textarea
709
- id={id}
710
- placeholder=' '
711
- className={cn(textareaVariants({ variant, size, rounded, state }), '!bg-background', className)}
712
- {...props}
713
- />
714
- </div>
715
- {description && <FieldDescription>{description}</FieldDescription>}
716
- {error && <FieldError>{error}</FieldError>}
717
- </Field>
718
- )
719
- }
720
-
721
- const WithInsetLabel = ({ label = 'Textarea with inset label', description, error, variant, size, rounded, state, className, ...props }: any) => {
722
- const id = useId()
723
- return (
724
- <Field className='w-full max-w-xs space-y-2'>
725
- <div className='border-input bg-background focus-within:border-ring focus-within:ring-ring/50 has-aria-invalid:ring-destructive/20 dark:has-aria-invalid:ring-destructive/40 has-aria-invalid:border-destructive relative w-full max-w-xs rounded-md border shadow-xs transition-[color,box-shadow] outline-none focus-within:ring-[3px] has-disabled:pointer-events-none has-disabled:cursor-not-allowed has-disabled:opacity-50 has-[textarea:is(:disabled)]:*:pointer-events-none'>
726
- <label htmlFor={id} className='text-foreground block px-3 pt-1 text-xs font-medium'>
727
- {label}
728
- </label>
729
- <textarea
730
- id={id}
731
- className='text-foreground placeholder:text-muted-foreground/70 flex min-h-14 w-full px-3 pb-2 text-sm focus-visible:outline-none bg-transparent'
732
- {...props}
733
- />
734
- </div>
735
- {description && <FieldDescription>{description}</FieldDescription>}
736
- {error && <FieldError>{error}</FieldError>}
737
- </Field>
738
- )
739
- }
740
-
741
- const WithButton = ({ label = 'Textarea with button', buttonText = 'Submit Feedback', description, error, variant, size, rounded, state, className, ...props }: any) => {
742
- const id = useId()
743
- return (
744
- <Field className='w-full max-w-xs space-y-2'>
745
- <FieldLabel htmlFor={id}>{label}</FieldLabel>
746
- <Textarea
747
- id={id}
748
- className={cn(textareaVariants({ variant, size, rounded, state }), className)}
749
- {...props}
750
- />
751
- <Button size='sm'>{buttonText}</Button>
752
- {description && <FieldDescription>{description}</FieldDescription>}
753
- {error && <FieldError>{error}</FieldError>}
754
- </Field>
755
- )
756
- }
757
-
758
- const AutoGrow = ({ label = 'Auto growing textarea', description, error, variant, rounded, state, className, ...props }: any) => {
759
- const id = useId()
760
- return (
761
- <Field className='w-full max-w-xs space-y-2'>
762
- <FieldLabel htmlFor={id}>{label}</FieldLabel>
763
- <Textarea
764
- id={id}
765
- className={cn(
766
- textareaVariants({ variant, size: 'sm', rounded, state }),
767
- 'field-sizing-content max-h-30 min-h-0 resize-none py-1.75',
768
- className
769
- )}
770
- {...props}
771
- />
772
- {description && <FieldDescription>{description}</FieldDescription>}
773
- {error && <FieldError>{error}</FieldError>}
774
- </Field>
775
- )
776
- }
777
-
778
- const NoResize = ({ label = 'No resize textarea', description, error, variant, size, rounded, state, className, ...props }: any) => {
779
- const id = useId()
780
- return (
781
- <Field className='w-full max-w-xs space-y-2'>
782
- <FieldLabel htmlFor={id}>{label}</FieldLabel>
783
- <Textarea
784
- id={id}
785
- className={cn(textareaVariants({ variant, size, rounded, state }), '[resize:none]', className)}
786
- {...props}
787
- />
788
- {description && <FieldDescription>{description}</FieldDescription>}
789
- {error && <FieldError>{error}</FieldError>}
790
- </Field>
791
- )
792
- }
793
-
794
- const CharacterLeft = ({ label = 'Textarea with characters left', maxLength = 200, initialValue = '', description, error, variant, size, rounded, state, className, ...props }: any) => {
795
- const [value, setValue] = useState(initialValue)
796
- const [characterCount, setCharacterCount] = useState(initialValue.length)
797
- const id = useId()
798
-
799
- const handleChange = (e: ChangeEvent<HTMLTextAreaElement>) => {
800
- if (e.target.value.length <= maxLength) {
801
- setValue(e.target.value)
802
- setCharacterCount(e.target.value.length)
803
- }
804
- }
805
-
806
- return (
807
- <Field className='w-full max-w-xs space-y-2'>
808
- <FieldLabel htmlFor={id}>{label}</FieldLabel>
809
- <Textarea
810
- id={id}
811
- value={value}
812
- maxLength={maxLength}
813
- onChange={handleChange}
814
- className={cn(textareaVariants({ variant, size, rounded, state }), className)}
815
- {...props}
816
- />
817
- <p className='text-muted-foreground text-xs'>
818
- <span className='tabular-nums'>{maxLength - characterCount}</span> characters left
819
- </p>
820
- {description && <FieldDescription>{description}</FieldDescription>}
821
- {error && <FieldError>{error}</FieldError>}
822
- </Field>
823
- )
824
- }
825
-
826
- const ReadOnly = ({ label = 'Read only textarea', description, error, variant, size, rounded, state, className, ...props }: any) => {
827
- const id = useId()
828
- return (
829
- <Field className='w-full max-w-xs space-y-2'>
830
- <FieldLabel htmlFor={id}>{label}</FieldLabel>
831
- <Textarea
832
- id={id}
833
- readOnly
834
- className={cn(textareaVariants({ variant, size, rounded, state }), 'read-only:bg-muted', className)}
835
- {...props}
836
- />
837
- {description && <FieldDescription>{description}</FieldDescription>}
838
- {error && <FieldError>{error}</FieldError>}
839
- </Field>
840
- )
841
- }
842
-
843
- const Disabled = ({ label = 'Disabled textarea', description, error, variant, size, rounded, state, className, ...props }: any) => {
844
- const id = useId()
845
- return (
846
- <Field className='w-full max-w-xs space-y-2'>
847
- <FieldLabel htmlFor={id}>{label}</FieldLabel>
848
- <Textarea
849
- id={id}
850
- disabled
851
- className={cn(textareaVariants({ variant, size, rounded, state }), className)}
852
- {...props}
853
- />
854
- {description && <FieldDescription>{description}</FieldDescription>}
855
- {error && <FieldError>{error}</FieldError>}
856
- </Field>
857
- )
858
- }
859
-
860
- export default function TextareaXDemo() {
861
- return (
862
- <div className='space-y-12 p-6 max-w-3xl mx-auto'>
863
- <div>
864
- <h3 className='text-lg font-semibold mb-4'>Default</h3>
865
- <TextareaX placeholder='Type your message here.' />
866
- </div>
867
-
868
- <div>
869
- <h3 className='text-lg font-semibold mb-4'>With Label</h3>
870
- <TextareaX textarea='WithLabel' label='Textarea with label' placeholder='Type your feedback here' />
871
- </div>
872
-
873
- <div>
874
- <h3 className='text-lg font-semibold mb-4'>With Helper Text</h3>
875
- <TextareaX textarea='WithHelperText' label='Textarea with helper text' placeholder='Type your feedback here' />
876
- </div>
877
-
878
- <div>
879
- <h3 className='text-lg font-semibold mb-4'>With Helper Text Right</h3>
880
- <TextareaX textarea='WithHelperTextRight' label='Textarea with right helper text' placeholder='Type your feedback here' />
881
- </div>
882
-
883
- <div>
884
- <h3 className='text-lg font-semibold mb-4'>Invalid</h3>
885
- <TextareaX textarea='Invalid' placeholder='Type your feedback here' />
886
- </div>
887
-
888
- <div>
889
- <h3 className='text-lg font-semibold mb-4'>With Hint Text</h3>
890
- <TextareaX textarea='WithHintText' placeholder='Type your feedback here' />
891
- </div>
892
-
893
- <div>
894
- <h3 className='text-lg font-semibold mb-4'>Required</h3>
895
- <TextareaX textarea='Required' placeholder='Type your feedback here' />
896
- </div>
897
-
898
- <div>
899
- <h3 className='text-lg font-semibold mb-4'>With Colored Border</h3>
900
- <TextareaX textarea='WithColoredBorder' placeholder='Type your feedback here' />
901
- </div>
902
-
903
- <div>
904
- <h3 className='text-lg font-semibold mb-4'>Filled</h3>
905
- <TextareaX textarea='Filled' placeholder='Type your feedback here' />
906
- </div>
907
-
908
- <div>
909
- <h3 className='text-lg font-semibold mb-4'>Small Size</h3>
910
- <TextareaX textarea='Small' placeholder='Small Textarea' />
911
- </div>
912
-
913
- <div>
914
- <h3 className='text-lg font-semibold mb-4'>Large Size</h3>
915
- <TextareaX textarea='Large' placeholder='Large Textarea' />
916
- </div>
917
-
918
- <div>
919
- <h3 className='text-lg font-semibold mb-4'>Start Icon</h3>
920
- <TextareaX textarea='StartIcon' placeholder='Address' />
921
- </div>
922
-
923
- <div>
924
- <h3 className='text-lg font-semibold mb-4'>End Icon</h3>
925
- <TextareaX textarea='EndIcon' placeholder='Address' />
926
- </div>
927
-
928
- <div>
929
- <h3 className='text-lg font-semibold mb-4'>With Overlapping Label</h3>
930
- <TextareaX textarea='WithOverlappingLabel' />
931
- </div>
932
-
933
- <div>
934
- <h3 className='text-lg font-semibold mb-4'>With Floating Label</h3>
935
- <TextareaX textarea='WithFloatingLabel' />
936
- </div>
937
-
938
- <div>
939
- <h3 className='text-lg font-semibold mb-4'>With Inset Label</h3>
940
- <TextareaX textarea='WithInsetLabel' />
941
- </div>
942
-
943
- <div>
944
- <h3 className='text-lg font-semibold mb-4'>With Button</h3>
945
- <TextareaX textarea='WithButton' placeholder='Type your feedback here' />
946
- </div>
947
-
948
- <div>
949
- <h3 className='text-lg font-semibold mb-4'>Auto Grow</h3>
950
- <TextareaX textarea='AutoGrow' placeholder='Type your feedback here' />
951
- </div>
952
-
953
- <div>
954
- <h3 className='text-lg font-semibold mb-4'>No Resize</h3>
955
- <TextareaX textarea='NoResize' placeholder='Type your feedback here' />
956
- </div>
957
-
958
- <div>
959
- <h3 className='text-lg font-semibold mb-4'>Character Left</h3>
960
- <TextareaX textarea='CharacterLeft' placeholder='Type your feedback here' />
961
- </div>
962
-
963
- <div>
964
- <h3 className='text-lg font-semibold mb-4'>Read Only</h3>
965
- <TextareaX textarea='ReadOnly' defaultValue='Read only text' placeholder='Type your feedback here' />
966
- </div>
967
-
968
- <div>
969
- <h3 className='text-lg font-semibold mb-4'>Disabled</h3>
970
- <TextareaX textarea='Disabled' placeholder='Type your feedback here' />
971
- </div>
972
- </div>
973
- )
974
- }
975
- /**import { Label, Textarea } from "~/components/catalyst-ui"
976
- import { useId} from 'react'
977
- import { HomeIcon } from "lucide-react"
978
-
979
- const TextareaDemo = () => {
980
- return <Textarea placeholder='Type your message here.' className='w-full max-w-xs' />
981
- }
982
-
983
- const TextareaWithLabelDemo = () => {
984
- const id = useId()
985
-
986
- return (
987
- <div className='w-full max-w-xs space-y-2'>
988
- <Label htmlFor={id}>Textarea with label</Label>
989
- <Textarea placeholder='Type your feedback here' id={id} />
990
- </div>
991
- )
992
- }
993
-
994
- const TextareaWithHelperTextDemo = () => {
995
- const id = useId()
996
-
997
- return (
998
- <div className='w-full max-w-xs space-y-2'>
999
- <Label htmlFor={id}>Textarea with helper text</Label>
1000
- <Textarea placeholder='Type your feedback here' id={id} />
1001
- <p className='text-muted-foreground text-xs'>Your feedback is useful for us.</p>
1002
- </div>
1003
- )
1004
- }
1005
-
1006
- const TextareaWithHelperTextRightDemo = () => {
1007
- const id = useId()
1008
-
1009
- return (
1010
- <div className='w-full max-w-xs space-y-2'>
1011
- <Label htmlFor={id}>Textarea with right helper text</Label>
1012
- <Textarea placeholder='Type your feedback here' id={id} />
1013
- <p className='text-muted-foreground text-end text-xs'>Your feedback is useful for us.</p>
1014
- </div>
1015
- )
1016
- }
1017
-
1018
- const TextareaInvalidDemo = () => {
1019
- const id = useId()
1020
-
1021
- return (
1022
- <div className='w-full max-w-xs space-y-2'>
1023
- <Label htmlFor={id}>Invalid Textarea</Label>
1024
- <Textarea aria-invalid placeholder='Type your feedback here' id={id} />
1025
- <p className='text-destructive text-xs'>Your feedback is useful for us.</p>
1026
- </div>
1027
- )
1028
- }
1029
-
1030
- const TextareaWithHintTextDemo = () => {
1031
- const id = useId()
1032
-
1033
- return (
1034
- <div className='w-full max-w-xs space-y-2'>
1035
- <div className='flex items-center justify-between gap-1'>
1036
- <Label htmlFor={id}>Input with hint text</Label>
1037
- <span className='text-muted-foreground text-xs'>Optional field</span>
1038
- </div>
1039
- <Textarea placeholder='Type your feedback here' id={id} />
1040
- </div>
1041
- )
1042
- }
1043
-
1044
- const TextareaRequiredDemo = () => {
1045
- const id = useId()
1046
-
1047
- return (
1048
- <div className='w-full max-w-xs space-y-2'>
1049
- <Label htmlFor={id}>
1050
- Required textarea <span className='text-destructive'>*</span>
1051
- </Label>
1052
- <Textarea placeholder='Type your feedback here' id={id} required />
1053
- </div>
1054
- )
1055
- }
1056
-
1057
- const TextareaWithColoredBorderDemo = () => {
1058
- const id = useId()
1059
-
1060
- return (
1061
- <div className='w-full max-w-xs space-y-2'>
1062
- <Label htmlFor={id}>Textarea with colored border and ring</Label>
1063
- <Textarea
1064
- placeholder='Type your feedback here'
1065
- id={id}
1066
- className='focus-visible:border-indigo-500 focus-visible:ring-indigo-500/20 dark:focus-visible:ring-indigo-500/40'
1067
- />
1068
- </div>
1069
- )
1070
- }
1071
-
1072
- const TextareaFilledDemo = () => {
1073
- const id = useId()
1074
-
1075
- return (
1076
- <div className='w-full max-w-xs space-y-2'>
1077
- <Label htmlFor={id}>Filled Textarea</Label>
1078
- <Textarea className='bg-muted border-transparent shadow-none' placeholder='Type your feedback here' id={id} />
1079
- </div>
1080
- )
1081
- }
1082
-
1083
- const TextareaSizesDemo = () => {
1084
- return (
1085
- <div className='w-full max-w-xs space-y-2'>
1086
- <Textarea className='min-h-10 py-1.5' placeholder='Small Textarea' />
1087
- <Textarea placeholder='Default(Medium) Textarea' />
1088
- <Textarea className='min-h-20 py-2.5' placeholder='Large Textarea' />
1089
- </div>
1090
- )
1091
- }
1092
-
1093
- const TextareaStartIconDemo = () => {
1094
- const id = useId()
1095
-
1096
- return (
1097
- <div className='w-full max-w-xs space-y-2'>
1098
- <Label htmlFor={id}>Textarea with start icon</Label>
1099
- <div className='relative'>
1100
- <div className='text-muted-foreground pointer-events-none absolute top-2.5 left-0 flex items-center justify-center pl-3 peer-disabled:opacity-50'>
1101
- <HomeIcon className='size-4' />
1102
- <span className='sr-only'>Address</span>
1103
- </div>
1104
- <Textarea id={id} placeholder='Address' className='peer pl-9' />
1105
- </div>
1106
- </div>
1107
- )
1108
- }
1109
-
1110
- const TextareaEndIconDemo = () => {
1111
- const id = useId()
1112
-
1113
- return (
1114
- <div className='w-full max-w-xs space-y-2'>
1115
- <Label htmlFor={id}>Textarea with end icon</Label>
1116
- <div className='relative'>
1117
- <div className='text-muted-foreground pointer-events-none absolute top-2.5 right-0 flex items-center justify-center pr-3 peer-disabled:opacity-50'>
1118
- <HomeIcon className='size-4' />
1119
- <span className='sr-only'>Address</span>
1120
- </div>
1121
- <Textarea id={id} placeholder='Address' className='peer pr-9' />
1122
- </div>
1123
- </div>
1124
- )
1125
- }
1126
-
1127
- const TextareaWithOverlappingLabelDemo = () => {
1128
- const id = useId()
1129
-
1130
- return (
1131
- <div className='relative w-full max-w-xs space-y-2'>
1132
- <Label
1133
- htmlFor={id}
1134
- className='bg-background text-foreground absolute top-0 left-2 z-10 block -translate-y-1/2 px-1 text-xs font-medium group-has-disabled:opacity-50'
1135
- >
1136
- Textarea with overlapping label
1137
- </Label>
1138
- <Textarea id={id} className='!bg-background' />
1139
- </div>
1140
- )
1141
- }
1142
-
1143
- const TextareaWithFloatingLabelDemo = () => {
1144
- const id = useId()
1145
-
1146
- return (
1147
- <div className='group relative w-full max-w-xs space-y-2'>
1148
- <label
1149
- htmlFor={id}
1150
- className='origin-start text-muted-foreground/70 group-focus-within:text-foreground has-[+textarea:not(:placeholder-shown)]:text-foreground has-aria-invalid:ring-destructive/20 dark:has-aria-invalid:ring-destructive/40 has-aria-invalid:border-destructive absolute top-0 block translate-y-2 cursor-text px-2 text-sm transition-all group-focus-within:pointer-events-none group-focus-within:-translate-y-1/2 group-focus-within:cursor-default group-focus-within:text-xs group-focus-within:font-medium has-[+textarea:not(:placeholder-shown)]:pointer-events-none has-[+textarea:not(:placeholder-shown)]:-translate-y-1/2 has-[+textarea:not(:placeholder-shown)]:cursor-default has-[+textarea:not(:placeholder-shown)]:text-xs has-[+textarea:not(:placeholder-shown)]:font-medium'
1151
- >
1152
- <span className='bg-background inline-flex px-1'>Textarea with floating label</span>
1153
- </label>
1154
- <Textarea id={id} placeholder=' ' className='!bg-background' />
1155
- </div>
1156
- )
1157
- }
1158
-
1159
- const TextareaWithInsetLabelDemo = () => {
1160
- const id = useId()
1161
-
1162
- return (
1163
- <div className='border-input bg-background focus-within:border-ring focus-within:ring-ring/50 has-aria-invalid:ring-destructive/20 dark:has-aria-invalid:ring-destructive/40 has-aria-invalid:border-destructive relative w-full max-w-xs rounded-md border shadow-xs transition-[color,box-shadow] outline-none focus-within:ring-[3px] has-disabled:pointer-events-none has-disabled:cursor-not-allowed has-disabled:opacity-50 has-[input:is(:disabled)]:*:pointer-events-none'>
1164
- <label htmlFor={id} className='text-foreground block px-3 pt-1 text-xs font-medium'>
1165
- Textarea with inset label
1166
- </label>
1167
- <textarea
1168
- id={id}
1169
- className='text-foreground placeholder:text-muted-foreground/70 flex min-h-14 w-full px-3 pb-2 text-sm focus-visible:outline-none'
1170
- />
1171
- </div>
1172
- )
1173
- }
1174
-
1175
- const TextareaWithButtonDemo = () => {
1176
- const id = useId()
1177
-
1178
- return (
1179
- <div className='w-full max-w-xs space-y-2'>
1180
- <Label htmlFor={id}>Textarea with button</Label>
1181
- <Textarea id={id} placeholder='Type your feedback here' />
1182
- <Button size='sm'>Submit Feedback</Button>
1183
- </div>
1184
- )
1185
- }
1186
-
1187
- const TextareaAutoGrowDemo = () => {
1188
- const id = useId()
1189
-
1190
- return (
1191
- <div className='w-full max-w-xs space-y-2'>
1192
- <Label htmlFor={id}>Auto growing textarea</Label>
1193
- <Textarea
1194
- id={id}
1195
- placeholder='Type your feedback here'
1196
- className='field-sizing-content max-h-30 min-h-0 resize-none py-1.75'
1197
- />
1198
- </div>
1199
- )
1200
- }
1201
-
1202
- const TextareaNoResizeDemo = () => {
1203
- const id = useId()
1204
-
1205
- return (
1206
- <div className='w-full max-w-xs space-y-2'>
1207
- <Label htmlFor={id}>No resize textarea</Label>
1208
- <Textarea id={id} placeholder='Type your feedback here' className='[resize:none]' />
1209
- </div>
1210
- )
1211
- }
1212
-
1213
- const TextareaCharacterLeftDemo = () => {
1214
- const [value, setValue] = useState(initialValue)
1215
- const [characterCount, setCharacterCount] = useState(initialValue.length)
1216
-
1217
- const id = useId()
1218
-
1219
- const handleChange = (e: ChangeEvent<HTMLTextAreaElement>) => {
1220
- if (e.target.value.length <= maxLength) {
1221
- setValue(e.target.value)
1222
- setCharacterCount(e.target.value.length)
1223
- }
1224
- }
1225
-
1226
- return (
1227
- <div className='w-full max-w-xs space-y-2'>
1228
- <Label htmlFor={id}>Textarea with characters left</Label>
1229
- <Textarea placeholder='Type your feedback here' value={value} maxLength={maxLength} onChange={handleChange} />
1230
- <p className='text-muted-foreground text-xs'>
1231
- <span className='tabular-nums'>{maxLength - characterCount}</span> characters left
1232
- </p>
1233
- </div>
1234
- )
1235
- }
1236
-
1237
- const TextareaReadOnlyDemo = () => {
1238
- const id = useId()
1239
-
1240
- return (
1241
- <div className='w-full max-w-xs space-y-2'>
1242
- <Label htmlFor={id}>Read only textarea</Label>
1243
- <Textarea
1244
- className='read-only:bg-muted'
1245
- defaultValue='Read only text'
1246
- placeholder='Type your feedback here'
1247
- id={id}
1248
- readOnly
1249
- />
1250
- </div>
1251
- )
1252
- }
1253
-
1254
- const TextareaDisabledDemo = () => {
1255
- const id = useId()
1256
-
1257
- return (
1258
- <div className='w-full max-w-xs space-y-2'>
1259
- <Label htmlFor={id}>Disabled textarea</Label>
1260
- <Textarea placeholder='Type your feedback here' disabled id={id} />
1261
- </div>
1262
- )
1263
- } */