@catalystsoftware/ui 1.0.5 → 1.0.7
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.
- package/dist/data/data.tsx +29 -29
- package/dist/data/tailwind.config.js +3821 -261
- package/dist/data.tsx +29 -29
- package/package.json +4 -3
- package/components/catalyst-ui/buttons/burger.tsx +0 -207
- package/components/catalyst-ui/core/data-display/timeline.tsx +0 -210
- package/components/catalyst-ui/core/feedback/alert.tsx +0 -491
- package/components/catalyst-ui/core/feedback/spinner-1.tsx +0 -65
- package/components/catalyst-ui/core/feedback/toast.tsx +0 -1857
- package/components/catalyst-ui/core/navigation/menu.tsx +0 -164
- package/components/catalyst-ui/forms/toggle-class.tsx +0 -176
- package/components/catalyst-ui/hooks/use-copy-to-clipboard.tsx +0 -419
- package/components/catalyst-ui/hooks/use-counter.tsx +0 -13
- package/components/catalyst-ui/hooks/use-event-listener.tsx +0 -23
- package/components/catalyst-ui/hooks/use-export-markdown.tsx +0 -47
- package/components/catalyst-ui/hooks/use-focus.tsx +0 -17
- package/components/catalyst-ui/hooks/use-interval.tsx +0 -23
- package/components/catalyst-ui/hooks/use-is-client.tsx +0 -16
- package/components/catalyst-ui/hooks/use-media-query.tsx +0 -19
- package/components/catalyst-ui/hooks/use-mobile.tsx +0 -19
- package/components/catalyst-ui/hooks/use-resize-observer.tsx +0 -81
- package/components/catalyst-ui/hooks/use-timeout.tsx +0 -21
- package/components/catalyst-ui/hooks/use-timer.tsx +0 -209
- package/components/catalyst-ui/hooks/use-toggle.tsx +0 -12
- package/components/catalyst-ui/media/image.tsx +0 -13
- package/components/catalyst-ui/overlays/dual-sidebar.tsx +0 -4142
- package/components/catalyst-ui/overlays/sidebar-original.tsx +0 -726
- package/components/catalyst-ui/primitives/accordion.tsx +0 -250
- package/components/catalyst-ui/primitives/alert-dialog.tsx +0 -126
- package/components/catalyst-ui/primitives/aspect-ratio.tsx +0 -9
- package/components/catalyst-ui/primitives/avatar.tsx +0 -296
- package/components/catalyst-ui/primitives/badge.tsx +0 -57
- package/components/catalyst-ui/primitives/breadcrumb.tsx +0 -101
- package/components/catalyst-ui/primitives/button.tsx +0 -265
- package/components/catalyst-ui/primitives/calendar-v4.tsx +0 -208
- package/components/catalyst-ui/primitives/calendar.tsx +0 -295
- package/components/catalyst-ui/primitives/card.tsx +0 -618
- package/components/catalyst-ui/primitives/carousel.tsx +0 -238
- package/components/catalyst-ui/primitives/chart.tsx +0 -347
- package/components/catalyst-ui/primitives/checkbox.tsx +0 -225
- package/components/catalyst-ui/primitives/collapsible.tsx +0 -212
- package/components/catalyst-ui/primitives/command.tsx +0 -393
- package/components/catalyst-ui/primitives/context-menu.tsx +0 -236
- package/components/catalyst-ui/primitives/dialog.tsx +0 -471
- package/components/catalyst-ui/primitives/drawer.tsx +0 -761
- package/components/catalyst-ui/primitives/dropdown-menu.tsx +0 -290
- package/components/catalyst-ui/primitives/empty.tsx +0 -104
- package/components/catalyst-ui/primitives/field.tsx +0 -244
- package/components/catalyst-ui/primitives/hover-card.tsx +0 -124
- package/components/catalyst-ui/primitives/input-otp.tsx +0 -76
- package/components/catalyst-ui/primitives/input.tsx +0 -64
- package/components/catalyst-ui/primitives/item.tsx +0 -196
- package/components/catalyst-ui/primitives/kbd.tsx +0 -75
- package/components/catalyst-ui/primitives/label.tsx +0 -24
- package/components/catalyst-ui/primitives/navigation-menu.tsx +0 -150
- package/components/catalyst-ui/primitives/pagination.tsx +0 -198
- package/components/catalyst-ui/primitives/popover.tsx +0 -232
- package/components/catalyst-ui/primitives/progress.tsx +0 -34
- package/components/catalyst-ui/primitives/radio-group.tsx +0 -43
- package/components/catalyst-ui/primitives/resizable.tsx +0 -56
- package/components/catalyst-ui/primitives/select.tsx +0 -155
- package/components/catalyst-ui/primitives/separator.tsx +0 -74
- package/components/catalyst-ui/primitives/sheet.tsx +0 -126
- package/components/catalyst-ui/primitives/skeleton.tsx +0 -15
- package/components/catalyst-ui/primitives/slider.tsx +0 -27
- package/components/catalyst-ui/primitives/switch.tsx +0 -187
- package/components/catalyst-ui/primitives/tabs.tsx +0 -335
- package/components/catalyst-ui/primitives/textarea.tsx +0 -24
- package/components/catalyst-ui/primitives/toggle-group.tsx +0 -55
- package/components/catalyst-ui/primitives/toggle.tsx +0 -42
- package/components/catalyst-ui/primitives/tooltip.tsx +0 -116
- package/components/catalyst-ui/utils/basic-auth.tsx +0 -40
- package/components/catalyst-ui/utils/context-storage.tsx +0 -19
- package/components/catalyst-ui/utils/cors-middleware.tsx +0 -71
- package/components/catalyst-ui/utils/deferred-content.tsx +0 -595
- package/components/catalyst-ui/utils/honeypot-middleware.tsx +0 -38
- package/components/catalyst-ui/utils/incId.tsx +0 -75
- package/components/catalyst-ui/utils/jwk-auth.tsx +0 -36
- package/components/catalyst-ui/utils/request-id.tsx +0 -14
- package/components/catalyst-ui/utils/secure-headers.tsx +0 -37
- package/components/catalyst-ui/utils/server-timing.tsx +0 -23
- package/components/catalyst-ui/utils/utils.ts +0 -43
- package/components/catalyst-ui/utils/with-cookie.tsx +0 -43
- package/components/catalyst-ui/x/accordian-x.tsx +0 -428
- package/components/catalyst-ui/x/alert-x.tsx +0 -413
- package/components/catalyst-ui/x/animated-text-x.tsx +0 -2242
- package/components/catalyst-ui/x/avatar-x.tsx +0 -515
- package/components/catalyst-ui/x/badge-x.tsx +0 -670
- package/components/catalyst-ui/x/button-X.tsx +0 -2857
- package/components/catalyst-ui/x/button-group-x.tsx +0 -847
- package/components/catalyst-ui/x/calendar-x.tsx +0 -1910
- package/components/catalyst-ui/x/card-x.tsx +0 -2597
- package/components/catalyst-ui/x/checkbox-x.tsx +0 -656
- package/components/catalyst-ui/x/collapsible-x.tsx +0 -1360
- package/components/catalyst-ui/x/combobox-x.tsx +0 -911
- package/components/catalyst-ui/x/data-table-x.tsx +0 -1753
- package/components/catalyst-ui/x/date-picker-x.tsx +0 -648
- package/components/catalyst-ui/x/dialog-x.tsx +0 -659
- package/components/catalyst-ui/x/dropdown-menu-x.tsx +0 -612
- package/components/catalyst-ui/x/hover-card-x.tsx +0 -375
- package/components/catalyst-ui/x/icon-x.tsx +0 -840
- package/components/catalyst-ui/x/input-mask-x.tsx +0 -981
- package/components/catalyst-ui/x/input-otp-x.tsx +0 -659
- package/components/catalyst-ui/x/loader-x.tsx +0 -1757
- package/components/catalyst-ui/x/pagination-x.tsx +0 -622
- package/components/catalyst-ui/x/popover-x.tsx +0 -744
- package/components/catalyst-ui/x/radio-group-x.tsx +0 -499
- package/components/catalyst-ui/x/select-x.tsx +0 -1127
- package/components/catalyst-ui/x/sheet-x.tsx +0 -668
- package/components/catalyst-ui/x/switch-x.tsx +0 -681
- package/components/catalyst-ui/x/table-x.tsx +0 -574
- package/components/catalyst-ui/x/tabs-x.tsx +0 -839
- package/components/catalyst-ui/x/textarea-x.tsx +0 -1263
- package/components/catalyst-ui/x/tooltip-x.tsx +0 -396
- package/components/catalyst-ui/x/tracker-x.tsx +0 -560
- package/data/bg-data.tsx +0 -901
- package/data/buttons-data.tsx +0 -2327
- package/data/charts-data.tsx +0 -102
- package/data/chat-data.tsx +0 -83
- package/data/code-data.tsx +0 -1040
- package/data/comboboxes-data.tsx +0 -1843
- package/data/command-data.tsx +0 -1381
- package/data/core-data.tsx +0 -15953
- package/data/crm-data.tsx +0 -47
- package/data/data.tsx +0 -159
- package/data/date-and-time-data.tsx +0 -554
- package/data/dependencies.tsx +0 -7
- package/data/ecommerce-data.tsx +0 -1387
- package/data/forms-data.tsx +0 -7890
- package/data/hooks-data.tsx +0 -5487
- package/data/index.ts +0 -34
- package/data/inputs-data.tsx +0 -557
- package/data/interactive-data.tsx +0 -5394
- package/data/lofi-data.tsx +0 -18295
- package/data/marketing-data.tsx +0 -2546
- package/data/media-data.tsx +0 -1510
- package/data/motion-data.tsx +0 -5801
- package/data/overlay-data.tsx +0 -4136
- package/data/pdf-data.tsx +0 -124
- package/data/pos-data.tsx +0 -213
- package/data/postcss.config.js +0 -6
- package/data/primitive-data.tsx +0 -5170
- package/data/prompt-data.tsx +0 -1226
- package/data/requiredLibs.ts +0 -4
- package/data/sandbox-data.tsx +0 -1
- package/data/sidebars-data.tsx +0 -5421
- package/data/stacks-data.tsx +0 -32
- package/data/table-data.tsx +0 -706
- package/data/tailwind.config.js +0 -270
- package/data/tailwind.config.ngin.js +0 -3830
- package/data/tailwind.css +0 -431
- package/data/tools-data.tsx +0 -6910
- package/data/typography-data.tsx +0 -2050
- package/data/utils-data.tsx +0 -6500
- 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
|
-
} */
|