@adamosuiteservices/ui 2.11.17 → 2.11.19
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/accordion-rounded.cjs +1 -1
- package/dist/accordion-rounded.js +1 -1
- package/dist/badge.cjs +3 -3
- package/dist/badge.js +2 -2
- package/dist/breadcrumb.cjs +5 -5
- package/dist/breadcrumb.js +16 -16
- package/dist/components/ui/date-picker-selector/date-picker-selector.d.ts +2 -2
- package/dist/components/ui/sheet/sheet.d.ts +2 -1
- package/dist/date-picker-selector.cjs +1 -1
- package/dist/date-picker-selector.js +29 -30
- package/dist/dialog.cjs +1 -1
- package/dist/dialog.js +1 -1
- package/dist/select.cjs +2 -2
- package/dist/select.js +2 -2
- package/dist/{sheet-DVT_djHX.cjs → sheet-CvcCaGSl.cjs} +11 -11
- package/dist/{sheet-CPf9Guon.js → sheet-IRIc3TJ1.js} +33 -19
- package/dist/sheet.cjs +1 -1
- package/dist/sheet.js +7 -6
- package/dist/sidebar.cjs +1 -1
- package/dist/sidebar.js +1 -1
- package/dist/styles.css +1 -1
- package/dist/tabs-underline.cjs +3 -3
- package/dist/tabs-underline.js +13 -13
- package/dist/tabs.cjs +5 -4
- package/dist/tabs.js +3 -2
- package/dist/themes.css +1 -1
- package/docs/AI-GUIDE.md +321 -321
- package/docs/components/layout/sidebar.md +399 -399
- package/docs/components/layout/toaster.md +436 -436
- package/docs/components/ui/accordion-rounded.md +584 -584
- package/docs/components/ui/accordion.md +269 -269
- package/docs/components/ui/badge.md +2 -1
- package/docs/components/ui/button-group.md +984 -984
- package/docs/components/ui/button.md +1137 -1137
- package/docs/components/ui/calendar.md +1159 -1159
- package/docs/components/ui/card.md +1455 -1455
- package/docs/components/ui/checkbox.md +292 -292
- package/docs/components/ui/collapsible.md +323 -323
- package/docs/components/ui/command.md +454 -454
- package/docs/components/ui/context-menu.md +540 -540
- package/docs/components/ui/date-picker-selector.md +0 -2
- package/docs/components/ui/dialog.md +628 -628
- package/docs/components/ui/dropdown-menu.md +709 -709
- package/docs/components/ui/field.md +706 -706
- package/docs/components/ui/hover-card.md +446 -446
- package/docs/components/ui/input.md +362 -362
- package/docs/components/ui/kbd.md +434 -434
- package/docs/components/ui/label.md +359 -359
- package/docs/components/ui/pagination.md +650 -650
- package/docs/components/ui/popover.md +536 -536
- package/docs/components/ui/progress.md +182 -182
- package/docs/components/ui/radio-group.md +311 -311
- package/docs/components/ui/select.md +352 -352
- package/docs/components/ui/separator.md +214 -214
- package/docs/components/ui/sheet.md +174 -142
- package/docs/components/ui/skeleton.md +140 -140
- package/docs/components/ui/slider.md +341 -341
- package/docs/components/ui/spinner.md +170 -170
- package/docs/components/ui/switch.md +408 -408
- package/docs/components/ui/tabs-underline.md +106 -106
- package/docs/components/ui/tabs.md +122 -122
- package/docs/components/ui/textarea.md +243 -243
- package/docs/components/ui/toggle.md +237 -237
- package/docs/components/ui/tooltip.md +317 -317
- package/docs/components/ui/typography.md +280 -280
- package/package.json +1 -1
|
@@ -1,243 +1,243 @@
|
|
|
1
|
-
# Textarea
|
|
2
|
-
|
|
3
|
-
Campo de texto multilínea con auto-resize. Wrapper nativo de `<textarea>` HTML.
|
|
4
|
-
|
|
5
|
-
## Descripción
|
|
6
|
-
|
|
7
|
-
El componente `Textarea` proporciona un campo de entrada de texto multilínea.
|
|
8
|
-
|
|
9
|
-
## Importación
|
|
10
|
-
|
|
11
|
-
```typescript
|
|
12
|
-
import { Textarea } from "@adamosuiteservices/ui/textarea";
|
|
13
|
-
```
|
|
14
|
-
|
|
15
|
-
## Anatomía
|
|
16
|
-
|
|
17
|
-
```tsx
|
|
18
|
-
<Textarea placeholder="Type your message here." />
|
|
19
|
-
```
|
|
20
|
-
|
|
21
|
-
**Componentes**: 1 (Textarea)
|
|
22
|
-
|
|
23
|
-
## Props
|
|
24
|
-
|
|
25
|
-
| Prop | Tipo | Descripción |
|
|
26
|
-
| -------------- | -------------------------- | ---------------------------- |
|
|
27
|
-
| `placeholder` | `string` | Texto placeholder |
|
|
28
|
-
| `value` | `string` | Valor controlado |
|
|
29
|
-
| `defaultValue` | `string` | Valor inicial (uncontrolled) |
|
|
30
|
-
| `onChange` | `(e: ChangeEvent) => void` | Callback al cambiar |
|
|
31
|
-
| `disabled` | `boolean` | Deshabilita el textarea |
|
|
32
|
-
| `readOnly` | `boolean` | Solo lectura |
|
|
33
|
-
| `required` | `boolean` | Campo requerido |
|
|
34
|
-
| `rows` | `number` | Número de filas |
|
|
35
|
-
| `maxLength` | `number` | Longitud máxima |
|
|
36
|
-
| `className` | `string` | Clases CSS adicionales |
|
|
37
|
-
|
|
38
|
-
**Nota**: Acepta todas las props HTML de `<textarea>`
|
|
39
|
-
|
|
40
|
-
## Patrones de Uso
|
|
41
|
-
|
|
42
|
-
### Básico
|
|
43
|
-
|
|
44
|
-
```tsx
|
|
45
|
-
import { Textarea } from "@adamosuiteservices/ui/textarea";
|
|
46
|
-
|
|
47
|
-
<Textarea placeholder="Type your message here." />;
|
|
48
|
-
```
|
|
49
|
-
|
|
50
|
-
### Con Label
|
|
51
|
-
|
|
52
|
-
```tsx
|
|
53
|
-
import { Label } from "@adamosuiteservices/ui/label";
|
|
54
|
-
|
|
55
|
-
<div className="grid w-full gap-2">
|
|
56
|
-
<Label htmlFor="message">Your message</Label>
|
|
57
|
-
<Textarea placeholder="Type your message here." id="message" />
|
|
58
|
-
</div>;
|
|
59
|
-
```
|
|
60
|
-
|
|
61
|
-
### Controlado con Contador
|
|
62
|
-
|
|
63
|
-
```tsx
|
|
64
|
-
import { useState } from "react";
|
|
65
|
-
|
|
66
|
-
function App() {
|
|
67
|
-
const [value, setValue] = useState("");
|
|
68
|
-
|
|
69
|
-
return (
|
|
70
|
-
<div className="grid w-full gap-2">
|
|
71
|
-
<Label htmlFor="feedback">
|
|
72
|
-
Your feedback ({value.length}/500 characters)
|
|
73
|
-
</Label>
|
|
74
|
-
<Textarea
|
|
75
|
-
id="feedback"
|
|
76
|
-
placeholder="Share your thoughts..."
|
|
77
|
-
value={value}
|
|
78
|
-
onChange={(e) => setValue(e.target.value)}
|
|
79
|
-
maxLength={500}
|
|
80
|
-
/>
|
|
81
|
-
<div className="flex justify-between text-sm text-muted-foreground">
|
|
82
|
-
<span>{value.length} characters</span>
|
|
83
|
-
<span>{500 - value.length} remaining</span>
|
|
84
|
-
</div>
|
|
85
|
-
</div>
|
|
86
|
-
);
|
|
87
|
-
}
|
|
88
|
-
```
|
|
89
|
-
|
|
90
|
-
### Con Validación
|
|
91
|
-
|
|
92
|
-
```tsx
|
|
93
|
-
import { Button } from "@adamosuiteservices/ui/button";
|
|
94
|
-
import {
|
|
95
|
-
Card,
|
|
96
|
-
CardContent,
|
|
97
|
-
CardFooter,
|
|
98
|
-
CardHeader,
|
|
99
|
-
CardTitle,
|
|
100
|
-
} from "@adamosuiteservices/ui/card";
|
|
101
|
-
|
|
102
|
-
function CommentForm() {
|
|
103
|
-
const [comment, setComment] = useState("");
|
|
104
|
-
const [error, setError] = useState("");
|
|
105
|
-
|
|
106
|
-
const handleSubmit = () => {
|
|
107
|
-
if (comment.trim().length < 10) {
|
|
108
|
-
setError("Comment must be at least 10 characters long");
|
|
109
|
-
return;
|
|
110
|
-
}
|
|
111
|
-
setError("");
|
|
112
|
-
// Submit logic
|
|
113
|
-
};
|
|
114
|
-
|
|
115
|
-
return (
|
|
116
|
-
<Card>
|
|
117
|
-
<CardHeader>
|
|
118
|
-
<CardTitle>Leave a Comment</CardTitle>
|
|
119
|
-
</CardHeader>
|
|
120
|
-
<CardContent className="space-y-2">
|
|
121
|
-
<Textarea
|
|
122
|
-
placeholder="What did you think?"
|
|
123
|
-
value={comment}
|
|
124
|
-
onChange={(e) => {
|
|
125
|
-
setComment(e.target.value);
|
|
126
|
-
if (error) setError("");
|
|
127
|
-
}}
|
|
128
|
-
aria-invalid={!!error}
|
|
129
|
-
rows={4}
|
|
130
|
-
/>
|
|
131
|
-
{error && <p className="text-destructive text-sm">{error}</p>}
|
|
132
|
-
</CardContent>
|
|
133
|
-
<CardFooter>
|
|
134
|
-
<Button onClick={handleSubmit}>Submit Comment</Button>
|
|
135
|
-
</CardFooter>
|
|
136
|
-
</Card>
|
|
137
|
-
);
|
|
138
|
-
}
|
|
139
|
-
```
|
|
140
|
-
|
|
141
|
-
### Chat Input con Enter
|
|
142
|
-
|
|
143
|
-
```tsx
|
|
144
|
-
function ChatInput() {
|
|
145
|
-
const [message, setMessage] = useState("");
|
|
146
|
-
|
|
147
|
-
const handleSend = () => {
|
|
148
|
-
if (message.trim()) {
|
|
149
|
-
// Send message logic
|
|
150
|
-
setMessage("");
|
|
151
|
-
}
|
|
152
|
-
};
|
|
153
|
-
|
|
154
|
-
const handleKeyDown = (e: React.KeyboardEvent) => {
|
|
155
|
-
if (e.key === "Enter" && !e.shiftKey) {
|
|
156
|
-
e.preventDefault();
|
|
157
|
-
handleSend();
|
|
158
|
-
}
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
return (
|
|
162
|
-
<div className="relative">
|
|
163
|
-
<Textarea
|
|
164
|
-
placeholder="Type a message... (Enter to send, Shift+Enter for new line)"
|
|
165
|
-
value={message}
|
|
166
|
-
onChange={(e) => setMessage(e.target.value)}
|
|
167
|
-
onKeyDown={handleKeyDown}
|
|
168
|
-
rows={3}
|
|
169
|
-
className="pr-12 resize-none"
|
|
170
|
-
/>
|
|
171
|
-
<Button
|
|
172
|
-
size="sm"
|
|
173
|
-
className="absolute bottom-2 right-2"
|
|
174
|
-
onClick={handleSend}
|
|
175
|
-
>
|
|
176
|
-
Send
|
|
177
|
-
</Button>
|
|
178
|
-
</div>
|
|
179
|
-
);
|
|
180
|
-
}
|
|
181
|
-
```
|
|
182
|
-
|
|
183
|
-
### Resize Options
|
|
184
|
-
|
|
185
|
-
```tsx
|
|
186
|
-
// Auto-resize (default con field-sizing-content)
|
|
187
|
-
<Textarea placeholder="Auto-resize..." className="min-h-[80px] resize-none" />
|
|
188
|
-
|
|
189
|
-
// Manual resize (ambas direcciones)
|
|
190
|
-
<Textarea placeholder="Drag corner to resize..." className="min-h-[80px] resize-both" />
|
|
191
|
-
|
|
192
|
-
// Vertical resize only
|
|
193
|
-
<Textarea placeholder="Resize vertically..." className="min-h-[80px] resize-y" />
|
|
194
|
-
|
|
195
|
-
// No resize
|
|
196
|
-
<Textarea placeholder="Fixed size..." rows={4} className="resize-none" />
|
|
197
|
-
```
|
|
198
|
-
|
|
199
|
-
## Casos de Uso
|
|
200
|
-
|
|
201
|
-
**Comentarios**: Feedback, reviews, mensajes
|
|
202
|
-
**Formularios**: Descripción, bio, notas
|
|
203
|
-
**Chat**: Mensajes multilínea
|
|
204
|
-
**Code editor**: Snippets de código
|
|
205
|
-
**Feedback**: Formularios de opinión
|
|
206
|
-
|
|
207
|
-
## Estilos Base
|
|
208
|
-
|
|
209
|
-
- **Min height**: `min-h-16` default
|
|
210
|
-
- **Border**: `border-input` con `shadow-xs`
|
|
211
|
-
- **Focus**: `ring-ring/50` con `ring-[3px]`
|
|
212
|
-
- **Padding**: `px-3 py-2`
|
|
213
|
-
- **Font**: `text-base` en móvil, `text-sm` en md+
|
|
214
|
-
- **Field sizing**: `field-sizing-content` para auto-resize
|
|
215
|
-
- **Invalid**: `border-destructive` con `ring-destructive/20`
|
|
216
|
-
|
|
217
|
-
## Accesibilidad
|
|
218
|
-
|
|
219
|
-
- ✅ **Label**: Asociar con `htmlFor` e `id`
|
|
220
|
-
- ✅ **ARIA**: `aria-invalid` para errores
|
|
221
|
-
- ✅ **Placeholder**: No usar como label única
|
|
222
|
-
- ✅ **Error messages**: Describir errores claramente
|
|
223
|
-
- ✅ **Keyboard**: Enter, Tab, Shift+Enter funcionan nativamente
|
|
224
|
-
|
|
225
|
-
## Notas de Implementación
|
|
226
|
-
|
|
227
|
-
- **HTML nativo**: Wrapper de `<textarea>` HTML nativo
|
|
228
|
-
- **Field sizing**: `field-sizing-content` permite auto-resize basado en contenido
|
|
229
|
-
- **No external library**: Sin dependencias de Radix UI
|
|
230
|
-
- **Data attribute**: `data-slot="textarea"` para identificación
|
|
231
|
-
|
|
232
|
-
## Troubleshooting
|
|
233
|
-
|
|
234
|
-
**No auto-resize**: Verifica `field-sizing-content` en className (Chrome 123+)
|
|
235
|
-
**Resize manual no funciona**: Usa `resize-y`, `resize-both`, o remueve `resize-none`
|
|
236
|
-
**Height fijo**: Usa prop `rows` para altura específica
|
|
237
|
-
**Contador de caracteres**: Usa `value.length` y `maxLength`
|
|
238
|
-
**Enter no funciona en chat**: Usa `onKeyDown` con `e.key === "Enter" && !e.shiftKey`
|
|
239
|
-
|
|
240
|
-
## Referencias
|
|
241
|
-
|
|
242
|
-
- **MDN textarea**: <https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea>
|
|
243
|
-
- **shadcn/ui Textarea**: <https://ui.shadcn.com/docs/components/textarea>
|
|
1
|
+
# Textarea
|
|
2
|
+
|
|
3
|
+
Campo de texto multilínea con auto-resize. Wrapper nativo de `<textarea>` HTML.
|
|
4
|
+
|
|
5
|
+
## Descripción
|
|
6
|
+
|
|
7
|
+
El componente `Textarea` proporciona un campo de entrada de texto multilínea.
|
|
8
|
+
|
|
9
|
+
## Importación
|
|
10
|
+
|
|
11
|
+
```typescript
|
|
12
|
+
import { Textarea } from "@adamosuiteservices/ui/textarea";
|
|
13
|
+
```
|
|
14
|
+
|
|
15
|
+
## Anatomía
|
|
16
|
+
|
|
17
|
+
```tsx
|
|
18
|
+
<Textarea placeholder="Type your message here." />
|
|
19
|
+
```
|
|
20
|
+
|
|
21
|
+
**Componentes**: 1 (Textarea)
|
|
22
|
+
|
|
23
|
+
## Props
|
|
24
|
+
|
|
25
|
+
| Prop | Tipo | Descripción |
|
|
26
|
+
| -------------- | -------------------------- | ---------------------------- |
|
|
27
|
+
| `placeholder` | `string` | Texto placeholder |
|
|
28
|
+
| `value` | `string` | Valor controlado |
|
|
29
|
+
| `defaultValue` | `string` | Valor inicial (uncontrolled) |
|
|
30
|
+
| `onChange` | `(e: ChangeEvent) => void` | Callback al cambiar |
|
|
31
|
+
| `disabled` | `boolean` | Deshabilita el textarea |
|
|
32
|
+
| `readOnly` | `boolean` | Solo lectura |
|
|
33
|
+
| `required` | `boolean` | Campo requerido |
|
|
34
|
+
| `rows` | `number` | Número de filas |
|
|
35
|
+
| `maxLength` | `number` | Longitud máxima |
|
|
36
|
+
| `className` | `string` | Clases CSS adicionales |
|
|
37
|
+
|
|
38
|
+
**Nota**: Acepta todas las props HTML de `<textarea>`
|
|
39
|
+
|
|
40
|
+
## Patrones de Uso
|
|
41
|
+
|
|
42
|
+
### Básico
|
|
43
|
+
|
|
44
|
+
```tsx
|
|
45
|
+
import { Textarea } from "@adamosuiteservices/ui/textarea";
|
|
46
|
+
|
|
47
|
+
<Textarea placeholder="Type your message here." />;
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
### Con Label
|
|
51
|
+
|
|
52
|
+
```tsx
|
|
53
|
+
import { Label } from "@adamosuiteservices/ui/label";
|
|
54
|
+
|
|
55
|
+
<div className="grid w-full gap-2">
|
|
56
|
+
<Label htmlFor="message">Your message</Label>
|
|
57
|
+
<Textarea placeholder="Type your message here." id="message" />
|
|
58
|
+
</div>;
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
### Controlado con Contador
|
|
62
|
+
|
|
63
|
+
```tsx
|
|
64
|
+
import { useState } from "react";
|
|
65
|
+
|
|
66
|
+
function App() {
|
|
67
|
+
const [value, setValue] = useState("");
|
|
68
|
+
|
|
69
|
+
return (
|
|
70
|
+
<div className="grid w-full gap-2">
|
|
71
|
+
<Label htmlFor="feedback">
|
|
72
|
+
Your feedback ({value.length}/500 characters)
|
|
73
|
+
</Label>
|
|
74
|
+
<Textarea
|
|
75
|
+
id="feedback"
|
|
76
|
+
placeholder="Share your thoughts..."
|
|
77
|
+
value={value}
|
|
78
|
+
onChange={(e) => setValue(e.target.value)}
|
|
79
|
+
maxLength={500}
|
|
80
|
+
/>
|
|
81
|
+
<div className="flex justify-between text-sm text-muted-foreground">
|
|
82
|
+
<span>{value.length} characters</span>
|
|
83
|
+
<span>{500 - value.length} remaining</span>
|
|
84
|
+
</div>
|
|
85
|
+
</div>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
### Con Validación
|
|
91
|
+
|
|
92
|
+
```tsx
|
|
93
|
+
import { Button } from "@adamosuiteservices/ui/button";
|
|
94
|
+
import {
|
|
95
|
+
Card,
|
|
96
|
+
CardContent,
|
|
97
|
+
CardFooter,
|
|
98
|
+
CardHeader,
|
|
99
|
+
CardTitle,
|
|
100
|
+
} from "@adamosuiteservices/ui/card";
|
|
101
|
+
|
|
102
|
+
function CommentForm() {
|
|
103
|
+
const [comment, setComment] = useState("");
|
|
104
|
+
const [error, setError] = useState("");
|
|
105
|
+
|
|
106
|
+
const handleSubmit = () => {
|
|
107
|
+
if (comment.trim().length < 10) {
|
|
108
|
+
setError("Comment must be at least 10 characters long");
|
|
109
|
+
return;
|
|
110
|
+
}
|
|
111
|
+
setError("");
|
|
112
|
+
// Submit logic
|
|
113
|
+
};
|
|
114
|
+
|
|
115
|
+
return (
|
|
116
|
+
<Card>
|
|
117
|
+
<CardHeader>
|
|
118
|
+
<CardTitle>Leave a Comment</CardTitle>
|
|
119
|
+
</CardHeader>
|
|
120
|
+
<CardContent className="space-y-2">
|
|
121
|
+
<Textarea
|
|
122
|
+
placeholder="What did you think?"
|
|
123
|
+
value={comment}
|
|
124
|
+
onChange={(e) => {
|
|
125
|
+
setComment(e.target.value);
|
|
126
|
+
if (error) setError("");
|
|
127
|
+
}}
|
|
128
|
+
aria-invalid={!!error}
|
|
129
|
+
rows={4}
|
|
130
|
+
/>
|
|
131
|
+
{error && <p className="text-destructive text-sm">{error}</p>}
|
|
132
|
+
</CardContent>
|
|
133
|
+
<CardFooter>
|
|
134
|
+
<Button onClick={handleSubmit}>Submit Comment</Button>
|
|
135
|
+
</CardFooter>
|
|
136
|
+
</Card>
|
|
137
|
+
);
|
|
138
|
+
}
|
|
139
|
+
```
|
|
140
|
+
|
|
141
|
+
### Chat Input con Enter
|
|
142
|
+
|
|
143
|
+
```tsx
|
|
144
|
+
function ChatInput() {
|
|
145
|
+
const [message, setMessage] = useState("");
|
|
146
|
+
|
|
147
|
+
const handleSend = () => {
|
|
148
|
+
if (message.trim()) {
|
|
149
|
+
// Send message logic
|
|
150
|
+
setMessage("");
|
|
151
|
+
}
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const handleKeyDown = (e: React.KeyboardEvent) => {
|
|
155
|
+
if (e.key === "Enter" && !e.shiftKey) {
|
|
156
|
+
e.preventDefault();
|
|
157
|
+
handleSend();
|
|
158
|
+
}
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
return (
|
|
162
|
+
<div className="relative">
|
|
163
|
+
<Textarea
|
|
164
|
+
placeholder="Type a message... (Enter to send, Shift+Enter for new line)"
|
|
165
|
+
value={message}
|
|
166
|
+
onChange={(e) => setMessage(e.target.value)}
|
|
167
|
+
onKeyDown={handleKeyDown}
|
|
168
|
+
rows={3}
|
|
169
|
+
className="pr-12 resize-none"
|
|
170
|
+
/>
|
|
171
|
+
<Button
|
|
172
|
+
size="sm"
|
|
173
|
+
className="absolute bottom-2 right-2"
|
|
174
|
+
onClick={handleSend}
|
|
175
|
+
>
|
|
176
|
+
Send
|
|
177
|
+
</Button>
|
|
178
|
+
</div>
|
|
179
|
+
);
|
|
180
|
+
}
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
### Resize Options
|
|
184
|
+
|
|
185
|
+
```tsx
|
|
186
|
+
// Auto-resize (default con field-sizing-content)
|
|
187
|
+
<Textarea placeholder="Auto-resize..." className="min-h-[80px] resize-none" />
|
|
188
|
+
|
|
189
|
+
// Manual resize (ambas direcciones)
|
|
190
|
+
<Textarea placeholder="Drag corner to resize..." className="min-h-[80px] resize-both" />
|
|
191
|
+
|
|
192
|
+
// Vertical resize only
|
|
193
|
+
<Textarea placeholder="Resize vertically..." className="min-h-[80px] resize-y" />
|
|
194
|
+
|
|
195
|
+
// No resize
|
|
196
|
+
<Textarea placeholder="Fixed size..." rows={4} className="resize-none" />
|
|
197
|
+
```
|
|
198
|
+
|
|
199
|
+
## Casos de Uso
|
|
200
|
+
|
|
201
|
+
**Comentarios**: Feedback, reviews, mensajes
|
|
202
|
+
**Formularios**: Descripción, bio, notas
|
|
203
|
+
**Chat**: Mensajes multilínea
|
|
204
|
+
**Code editor**: Snippets de código
|
|
205
|
+
**Feedback**: Formularios de opinión
|
|
206
|
+
|
|
207
|
+
## Estilos Base
|
|
208
|
+
|
|
209
|
+
- **Min height**: `min-h-16` default
|
|
210
|
+
- **Border**: `border-input` con `shadow-xs`
|
|
211
|
+
- **Focus**: `ring-ring/50` con `ring-[3px]`
|
|
212
|
+
- **Padding**: `px-3 py-2`
|
|
213
|
+
- **Font**: `text-base` en móvil, `text-sm` en md+
|
|
214
|
+
- **Field sizing**: `field-sizing-content` para auto-resize
|
|
215
|
+
- **Invalid**: `border-destructive` con `ring-destructive/20`
|
|
216
|
+
|
|
217
|
+
## Accesibilidad
|
|
218
|
+
|
|
219
|
+
- ✅ **Label**: Asociar con `htmlFor` e `id`
|
|
220
|
+
- ✅ **ARIA**: `aria-invalid` para errores
|
|
221
|
+
- ✅ **Placeholder**: No usar como label única
|
|
222
|
+
- ✅ **Error messages**: Describir errores claramente
|
|
223
|
+
- ✅ **Keyboard**: Enter, Tab, Shift+Enter funcionan nativamente
|
|
224
|
+
|
|
225
|
+
## Notas de Implementación
|
|
226
|
+
|
|
227
|
+
- **HTML nativo**: Wrapper de `<textarea>` HTML nativo
|
|
228
|
+
- **Field sizing**: `field-sizing-content` permite auto-resize basado en contenido
|
|
229
|
+
- **No external library**: Sin dependencias de Radix UI
|
|
230
|
+
- **Data attribute**: `data-slot="textarea"` para identificación
|
|
231
|
+
|
|
232
|
+
## Troubleshooting
|
|
233
|
+
|
|
234
|
+
**No auto-resize**: Verifica `field-sizing-content` en className (Chrome 123+)
|
|
235
|
+
**Resize manual no funciona**: Usa `resize-y`, `resize-both`, o remueve `resize-none`
|
|
236
|
+
**Height fijo**: Usa prop `rows` para altura específica
|
|
237
|
+
**Contador de caracteres**: Usa `value.length` y `maxLength`
|
|
238
|
+
**Enter no funciona en chat**: Usa `onKeyDown` con `e.key === "Enter" && !e.shiftKey`
|
|
239
|
+
|
|
240
|
+
## Referencias
|
|
241
|
+
|
|
242
|
+
- **MDN textarea**: <https://developer.mozilla.org/en-US/docs/Web/HTML/Element/textarea>
|
|
243
|
+
- **shadcn/ui Textarea**: <https://ui.shadcn.com/docs/components/textarea>
|