文本域
多行文本输入框,支持受控模式、禁用状态与自定义行数。
安装
bash
npx polyui add textarea基础
tsx
import { Textarea } from "@polyui/react/textarea"
export function TextareaBasic() {
return <Textarea placeholder="Type your message here." className="w-full max-w-xs" />
}带标签
tsx
import { useId } from "react"
import { Textarea } from "@polyui/react/textarea"
import { Label } from "@polyui/react/label"
export function TextareaWithLabel() {
const id = useId()
return (
<div className="w-full max-w-xs space-y-2">
<Label htmlFor={id}>Your message</Label>
<Textarea placeholder="Type your feedback here." id={id} />
</div>
)
}错误状态
Please provide useful feedback.
tsx
import { useId } from "react"
import { Textarea } from "@polyui/react/textarea"
import { Label } from "@polyui/react/label"
export function TextareaInvalid() {
const id = useId()
return (
<div className="w-full max-w-xs space-y-2">
<Label htmlFor={id}>Invalid</Label>
<Textarea aria-invalid placeholder="Type your feedback here." id={id} />
<p className="text-destructive text-xs">Please provide useful feedback.</p>
</div>
)
}占位符
tsx
import { useId } from "react"
import { Textarea } from "@polyui/react/textarea"
import { Label } from "@polyui/react/label"
export function TextareaPlaceholder() {
const id = useId()
return (
<div className="w-full max-w-xs space-y-2">
<Label htmlFor={id}>Placeholder</Label>
<Textarea id={id} placeholder="Enter your message..." />
</div>
)
}禁用
tsx
import { useId } from "react"
import { Textarea } from "@polyui/react/textarea"
import { Label } from "@polyui/react/label"
export function TextareaDisabled() {
const id = useId()
return (
<div className="w-full max-w-xs space-y-2">
<Label htmlFor={id}>Disabled</Label>
<Textarea placeholder="Type your feedback here." disabled id={id} />
</div>
)
}只读
tsx
import { useId } from "react"
import { Textarea } from "@polyui/react/textarea"
import { Label } from "@polyui/react/label"
export function TextareaReadOnly() {
const id = useId()
return (
<div className="w-full max-w-xs space-y-2">
<Label htmlFor={id}>Read Only</Label>
<Textarea
className="read-only:bg-muted"
defaultValue="Read only text"
placeholder="Type your feedback here."
id={id}
readOnly
/>
</div>
)
}带按钮
tsx
import { useId } from "react"
import { Textarea } from "@polyui/react/textarea"
import { Label } from "@polyui/react/label"
import { Button } from "@polyui/react/button"
export function TextareaWithButton() {
const id = useId()
return (
<div className="w-full max-w-xs space-y-2">
<Label htmlFor={id}>With Button</Label>
<Textarea id={id} placeholder="Type your feedback here." />
<Button size="sm">Submit Feedback</Button>
</div>
)
}自动增高
tsx
import { useId } from "react"
import { Textarea } from "@polyui/react/textarea"
import { Label } from "@polyui/react/label"
export function TextareaAutoGrow() {
const id = useId()
return (
<div className="w-full max-w-xs space-y-2">
<Label htmlFor={id}>Auto Grow</Label>
<Textarea
id={id}
placeholder="Type your feedback here."
className="field-sizing-content max-h-30 min-h-0 resize-none py-1.75"
/>
</div>
)
}填充样式
tsx
import { useId } from "react"
import { Textarea } from "@polyui/react/textarea"
import { Label } from "@polyui/react/label"
export function TextareaFilled() {
const id = useId()
return (
<div className="w-full max-w-xs space-y-2">
<Label htmlFor={id}>Filled</Label>
<Textarea className="bg-muted border-transparent shadow-none" placeholder="Type your feedback here." id={id} />
</div>
)
}尺寸
tsx
import { Textarea } from "@polyui/react/textarea"
export function TextareaSizes() {
return (
<div className="w-full max-w-xs space-y-2">
<Textarea className="min-h-10 py-1.5" placeholder="Small" />
<Textarea placeholder="Medium" />
<Textarea className="min-h-20 py-2.5" placeholder="Large" />
</div>
)
}前置图标
Address
tsx
import { useId } from "react"
import { Textarea } from "@polyui/react/textarea"
import { Label } from "@polyui/react/label"
import { HomeIcon } from "lucide-react"
export function TextareaStartIcon() {
const id = useId()
return (
<div className="w-full max-w-xs space-y-2">
<Label htmlFor={id}>Start Icon</Label>
<div className="relative">
<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">
<HomeIcon className="size-4" />
<span className="sr-only">Address</span>
</div>
<Textarea id={id} placeholder="Address" className="peer pl-9" />
</div>
</div>
)
}末端图标
Address
tsx
import { useId } from "react"
import { Textarea } from "@polyui/react/textarea"
import { Label } from "@polyui/react/label"
import { HomeIcon } from "lucide-react"
export function TextareaEndIcon() {
const id = useId()
return (
<div className="w-full max-w-xs space-y-2">
<Label htmlFor={id}>End Icon</Label>
<div className="relative">
<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">
<HomeIcon className="size-4" />
<span className="sr-only">Address</span>
</div>
<Textarea id={id} placeholder="Address" className="peer pr-9" />
</div>
</div>
)
}悬浮标签
tsx
import { useId } from "react"
import { Textarea } from "@polyui/react/textarea"
import { Label } from "@polyui/react/label"
export function TextareaOverlappingLabel() {
const id = useId()
return (
<div className="relative w-full max-w-xs space-y-2">
<Label
htmlFor={id}
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"
>
Overlapping Label
</Label>
<Textarea id={id} className="!bg-background" />
</div>
)
}浮动标签
tsx
import { useId } from "react"
import { Textarea } from "@polyui/react/textarea"
import { Label } from "@polyui/react/label"
export function TextareaFloatingLabel() {
const id = useId()
return (
<div className="group relative w-full max-w-xs space-y-2">
<label
htmlFor={id}
className="origin-start text-muted-foreground/70 group-focus-within:text-foreground has-[+textarea:not(:placeholder-shown)]:text-foreground 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"
>
<span className="bg-background inline-flex px-1">Floating Label</span>
</label>
<Textarea id={id} placeholder=" " className="!bg-background" />
</div>
)
}内嵌标签
tsx
import { useId } from "react"
import { Label } from "@polyui/react/label"
export function TextareaInsetLabel() {
const id = useId()
return (
<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">
<label htmlFor={id} className="text-foreground block px-3 pt-1 text-xs font-medium">
Inset Label
</label>
<textarea
id={id}
className="text-foreground placeholder:text-muted-foreground/70 flex min-h-14 w-full px-3 pb-2 text-sm focus-visible:outline-none"
/>
</div>
)
}禁止缩放
tsx
import { useId } from "react"
import { Textarea } from "@polyui/react/textarea"
import { Label } from "@polyui/react/label"
export function TextareaNoResize() {
const id = useId()
return (
<div className="w-full max-w-xs space-y-2">
<Label htmlFor={id}>No Resize</Label>
<Textarea id={id} placeholder="Type your feedback here." className="[resize:none]" />
</div>
)
}字符计数
200 characters left
tsx
import { useId, useState } from "react"
import { Textarea } from "@polyui/react/textarea"
import { Label } from "@polyui/react/label"
export function TextareaCharacterCount() {
const [value, setValue] = useState("")
const id = useId()
const handleChange = (e: ChangeEvent<HTMLTextAreaElement>) => {
if (e.target.value.length <= MAX_LENGTH) setValue(e.target.value)
}
return (
<div className="w-full max-w-xs space-y-2">
<Label htmlFor={id}>Character Count</Label>
<Textarea
placeholder="Type your feedback here."
value={value}
maxLength={MAX_LENGTH}
onChange={handleChange}
id={id}
/>
<p className="text-muted-foreground text-xs">
<span className="tabular-nums">{MAX_LENGTH - value.length}</span> characters left
</p>
</div>
)
}属性
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
placeholder | string | — | 输入框的占位文本,内容为空时显示。 |
disabled | boolean | false | 禁用文本域,阻止交互并降低视觉透明度。 |
rows | number | 5 | 文本域的初始可见行数,默认为 5。 |
value | string | — | 受控模式下的文本内容。 |
onChange | (e: React.ChangeEvent<HTMLTextAreaElement>) => void | — | 文本内容变化时的回调函数。 |