组件
输入框 Input
支持前后内容、浮动标签、下划线和错误态的输入框。
基础示例
Loading…
pnpm dlx wui@latest add @wui/input"use client"
import * as React from "react"
import { cva } from "class-variance-authority"
import { cn } from "@/lib/utils"
const inputShellVariants = cva(
"group relative flex w-full items-center transition-[border-color,box-shadow,background-color] duration-200 ease-out has-[:disabled]:cursor-not-allowed has-[:disabled]:opacity-50 has-[input[aria-invalid=true]]:border-destructive",
{
variants: {
variant: {
default:
"rounded-md border border-input bg-background shadow-xs focus-within:border-ring focus-within:ring-[3px] focus-within:ring-ring/30 has-[input[aria-invalid=true]]:ring-[3px] has-[input[aria-invalid=true]]:ring-destructive/20",
underline:
"border-b border-input bg-transparent focus-within:border-foreground has-[input[aria-invalid=true]]:border-destructive",
},
size: {
sm: "h-8",
default: "h-10",
lg: "h-12",
},
},
defaultVariants: {
variant: "default",
size: "default",
},
}
)
export interface InputProps extends React.ComponentProps<"input"> {
/** Surface treatment of the input. @default "default" */
variant?: "default" | "underline"
/** Visual height preset. @default "default" */
visualSize?: "sm" | "default" | "lg"
/** Floating label shown inside the field until focus or input. */
label?: React.ReactNode
/** Decorative or actionable content before the input. */
startContent?: React.ReactNode
/** Decorative or actionable content after the input. */
endContent?: React.ReactNode
/** Extra classes applied to the outer surface. */
wrapperClassName?: string
}
/** A refined text field with optional floating label and inline content. */
function Input({
className,
wrapperClassName,
variant = "default",
visualSize = "default",
label,
startContent,
endContent,
id,
placeholder,
...props
}: InputProps) {
const generatedId = React.useId()
const inputId = id ?? generatedId
return (
<div
data-slot="input-shell"
data-size={visualSize}
data-variant={variant}
className={cn(
inputShellVariants({ variant, size: visualSize }),
wrapperClassName
)}
>
{startContent ? (
<span
data-slot="input-start"
className="ml-3 flex shrink-0 items-center justify-center text-muted-foreground transition-colors group-focus-within:text-foreground [&_svg]:size-4"
>
{startContent}
</span>
) : null}
<input
id={inputId}
data-slot="input"
placeholder={label ? " " : placeholder}
className={cn(
"peer h-full min-w-0 flex-1 bg-transparent px-3 text-sm outline-none placeholder:text-muted-foreground/75 disabled:cursor-not-allowed",
visualSize === "lg" && "px-4 text-base",
startContent && "pl-2.5",
endContent && "pr-2.5",
label && "pb-1 pt-4",
variant === "underline" && "px-0",
variant === "underline" && startContent && "pl-2.5",
className
)}
{...props}
/>
{label ? (
<label
data-slot="input-label"
htmlFor={inputId}
className={cn(
"pointer-events-none absolute top-1.5 origin-left text-[10px] font-medium leading-none text-muted-foreground transition-[top,transform,font-size,color] duration-200 peer-placeholder-shown:top-1/2 peer-placeholder-shown:-translate-y-1/2 peer-placeholder-shown:text-sm peer-focus:top-1.5 peer-focus:translate-y-0 peer-focus:text-[10px] peer-focus:text-foreground",
variant === "underline" ? "left-0" : "left-3",
startContent && (variant === "underline" ? "left-6" : "left-9")
)}
>
{label}
</label>
) : null}
{endContent ? (
<span
data-slot="input-end"
className="mr-3 flex shrink-0 items-center justify-center text-muted-foreground transition-colors group-focus-within:text-foreground [&_button]:-mr-1 [&_svg]:size-4"
>
{endContent}
</span>
) : null}
</div>
)
}
export { Input, inputShellVariants }
组件作用
Input 用于录入单行文本、邮箱、密码或搜索内容。它保留原生 input 的全部语义,并用 startContent、endContent 承载图标、快捷键提示或清除按钮。聚焦反馈只改变边框与焦点环,不移动组件本身,避免页面产生跳动。
组件属性
| Prop | Type | Default | Description |
|---|---|---|---|
| variant | "default" | "underline" | default | Surface treatment of the input. |
| visualSize | "default" | "sm" | "lg" | default | Visual height preset. |
| label | ReactNode | — | Floating label shown inside the field until focus or input. |
| startContent | ReactNode | — | Decorative or actionable content before the input. |
| endContent | ReactNode | — | Decorative or actionable content after the input. |
| wrapperClassName | string | — | Extra classes applied to the outer surface. |
| disabled | boolean | — |
事件
所有原生输入事件均会透传,包括 onChange、onInput、onFocus、onBlur 与键盘事件。传入 aria-invalid="true" 会同步显示错误边框与光晕。
拓展使用
图标、错误与禁用态
Loading…
浮动标签
传入 label 后,标签初始位于输入区域中部;聚焦或输入内容时会缩小并移动到左上角。
Loading…
下划线样式
variant="underline" 适合密度更高、容器边界已经明确的表单区域。
Loading…
使用 visualSize="sm | default | lg" 调整视觉尺寸,原生 size 属性仍保留其 HTML 含义。