- UI组件
- 前端
【免费下载链接】shadcn-vue
Vue port of shadcn-ui
导读
Input Group(输入组)是 shadcn-vue 中用于在输入框(Input)或文本域(Textarea)周围附加图标、文字、按钮、Tooltip、下拉菜单等辅助信息或操作的工具型组件。它把“输入控件 + 附加内容”封装为一个整体容器,自动处理布局、圆角、焦点环与错误态样式。本文以 input-group.md 文档为骨架,结合apps/v4/registry/new-york-v4/ui/input-group下的完整源码与 apps/v4/components/demo 中的 10 余个真实演示,讲解安装方式、全部子组件的 API 与对齐机制、常见实战组合,并深入剖析其基于data-slot与has-*选择器实现的“自动聚焦/错误态联动”底层原理。读完本文,你将能够独立搭建从简单搜索框到包含 Tooltip、Dropdown、Spinner 的复杂输入组,并理解其样式联动机制以便自定义扩展。
一、安装与项目结构
CLI 安装
Input Group 已收录在 shadcn-vue 的组件注册表中,一条命令即可将组件源码复制到项目:
npx shadcn-vue@latest add input-group该命令会把组件文件安装到components/ui/input-group目录下(本仓库内的源文件位于 apps/v4/registry/new-york-v4/ui/input-group)。
手动安装
若偏好手动集成,分三步:
- 安装依赖(Input Group 依赖
reka-ui提供无头 UI 能力,同时其内部复用了Button、Input、Textarea组件与class-variance-authority的cva变体机制):
npm install reka-ui将 InputGroup.vue、InputGroupAddon.vue、InputGroupButton.vue、InputGroupInput.vue、InputGroupText.vue、InputGroupTextarea.vue 及 index.ts 复制到你的项目中。
将组件内的
@/registry/new-york-v4/ui/...导入路径改为你项目实际的components/ui/...路径。
组件集共包含 6 个导出(见 index.ts):InputGroup、InputGroupAddon、InputGroupButton、InputGroupInput、InputGroupText、InputGroupTextarea。它们的分工如下:
| 组件 | 职责 |
|---|---|
InputGroup | 外层容器,负责整体边框、圆角、焦点/错误态与对齐布局 |
InputGroupAddon | 附加内容容器,可放图标、文本、按钮、Tooltip、Dropdown 等,支持四种对齐 |
InputGroupInput | 预置输入组样式的Input封装 |
InputGroupTextarea | 预置输入组样式的Textarea封装 |
InputGroupText | 纯文本/图标辅助展示 |
InputGroupButton | 输入组内的紧凑按钮,自带xs/sm/icon-*尺寸变体 |
二、基础用法
最小可用的输入组由InputGroup+InputGroupInput+InputGroupAddon组成,例如文档中的搜索框示例(可对照 InputGroupDemo.vue 的第一段实现):
<script setup lang="ts"> import { InputGroup, InputGroupAddon, InputGroupButton, InputGroupInput, InputGroupText, InputGroupTextarea, } from '@/components/ui/input-group' </script> <template> <InputGroup> <InputGroupInput placeholder="Search..." /> <InputGroupAddon> <SearchIcon /> </InputGroupAddon> <InputGroupAddon align="inline-end"> <InputGroupButton>Search</InputGroupButton> </InputGroupAddon> </InputGroup> </template>要点:
- 图标型 Addon 默认放在行首(
inline-start),按钮型 Addon 用align="inline-end"放到行尾。 - 建议在模板中将
InputGroupInput放在InputGroupAddon之前,以保证键盘焦点导航顺序正确(详见“焦点导航”小节)。
三、实战示例:从简单到复杂
1. 图标(Icon)
最简单的用法是在输入框前后放置图标。文档演示见 InputGroupWithIcon.vue,核心是<InputGroupAddon><SearchIcon /></InputGroupAddon>。图标通过 Addon 的样式规则自动获得统一的size-4尺寸,无需手动设置([&>svg:not([class*='size-'])]:size-4)。
2. 文本(Text)
用InputGroupText在输入框旁展示单位、前缀或后缀信息,典型场景是金额、域名与邮箱后缀(见 InputGroupWithText.vue):
<InputGroup> <InputGroupAddon> <InputGroupText>$</InputGroupText> </InputGroupAddon> <InputGroupInput placeholder="0.00" /> <InputGroupAddon align="inline-end"> <InputGroupText>USD</InputGroupText> </InputGroupAddon> </InputGroup>同一文件中还展示了https://+example.com+.com的域名拼接、@company.com邮箱后缀,以及 textarea 右下角的字符计数提示(120 characters left)。InputGroupText本身是一个<span>,自带text-sm、text-muted-foreground与图标尺寸处理(见 InputGroupText.vue)。
3. 按钮(Button)
在 Addon 内放置InputGroupButton即可执行操作。默认size="xs"、variant="ghost",适合紧凑布局;图标按钮可传size="icon-xs"配合aria-label(见 InputGroupWithButton.vue 与 InputGroupDemo.vue 中的发送按钮):
<InputGroupButton> Button </InputGroupButton> <InputGroupButton size="icon-xs" aria-label="Copy"> <CopyIcon /> </InputGroupButton>InputGroupButton基于项目通用的Button组件实现,但通过inputGroupButtonVariants收窄了尺寸范围(xs / icon-xs / sm / icon-sm)并重设了内边距与圆角,见 index.ts。variant 沿用按钮体系的"default" | "destructive" | "outline" | "secondary" | "ghost" | "link"。
4. Tooltip
给 Addon 内的按钮或图标附加 Tooltip 提供上下文说明。TooltipTrigger需配合as-child将触发行为透传给InputGroupButton(见 InputGroupWithTooltip.vue 与 InputGroupDemo.vue):
<TooltipProvider> <Tooltip> <TooltipTrigger as-child> <InputGroupButton class="rounded-full" size="icon-xs"> <InfoIcon class="size-4" /> </InputGroupButton> </TooltipTrigger> <TooltipContent>This is content in a tooltip.</TooltipContent> </Tooltip> </TooltipProvider>5. Textarea
Input Group 同样适用于多行输入。关键规则:InputGroupTextarea的附加内容应使用align="block-start"或align="block-end"(块级对齐),而不是行内对齐(见 InputGroupWithTextarea.vue):
<InputGroup> <InputGroupTextarea placeholder="Enter message..." /> <InputGroupAddon align="block-end"> <InputGroupButton>Send</InputGroupButton> </InputGroupAddon> </InputGroup>InputGroup容器检测到内部是 textarea 时(has-[>textarea]:h-auto)会自动放弃固定高度,Addon 置于底部后整体呈上下结构。
6. Spinner(加载指示)
处理异步请求时,可在输入框旁放一个Spinner展示加载状态。参考 InputGroupWithSpinner.vue,配合data-disabled属性与disabled输入框呈现“禁用 + 加载”语义:
<InputGroup><InputGroup> <InputGroupInput placeholder="Enter search query" /> <InputGroupAddon align="inline-end"> <DropdownMenu> <DropdownMenuTrigger as-child> <InputGroupButton variant="ghost" class="!pr-1.5 text-xs"> Search In... <ChevronDownIcon class="size-3" /> </InputGroupButton> </DropdownMenuTrigger> <DropdownMenuContent align="end"> <DropdownMenuItem>Documentation</DropdownMenuItem> <DropdownMenuItem>Blog Posts</DropdownMenuItem> </DropdownMenuContent> </DropdownMenu> </InputGroupAddon> </InputGroup>9. Button Group(按钮组前缀/后缀)
将ButtonGroup与 Input Group 结合,可以在输入框两侧形成按钮前缀或后缀(见 InputGroupWithButtonGroup.vue 与 ButtonGroupInputGroupDemo.vue)。
10. 自定义输入(Custom Input)
若内置的InputGroupInput/InputGroupTextarea无法满足需求,可以传入任意自定义元素,只需为其添加data-slot="input-group-control"属性,即可自动获得输入组的焦点环与错误态联动,同时不施加任何样式——样式完全由你自己的class控制:
<template> <div class="grid w-full max-w-sm gap-6"> <InputGroup> <textarea ><InputGroup> <InputGroupInput /> <InputGroupAddon /> </InputGroup>容器渲染为<div><InputGroupAddon align="inline-end"> <SearchIcon /> </InputGroupAddon>
对齐规则速记:InputGroupInput用inline-start/inline-end;InputGroupTextarea用block-start/block-end。
一个 Addon 内可以放置多个InputGroupButton与多个图标:
<InputGroupAddon> <InputGroupButton>Button</InputGroupButton> <InputGroupButton>Button</InputGroupButton> </InputGroupAddon>对齐变体的完整样式定义见 index.ts:
inline-start:order-first pl-3,并针对内部按钮/Kbd 做负外边距微调(has-[>button]:ml-[-0.45rem]);inline-end:order-last pr-3,对称处理;block-start/block-end:w-full justify-start,分别以pt-3/pb-3贴合上下边缘,textarea 场景下自动收紧(group-has-[>input]/input-group:pt-2.5)。
InputGroupButton
输入组内的按钮(实现见 InputGroupButton.vue),底层渲染为通用Button。
| Prop | 类型 | 默认值 |
|---|---|---|
size | "xs" \| "icon-xs" \| "sm" \| "icon-sm" | "xs" |
variant | "default" \| "destructive" \| "outline" \| "secondary" \| "ghost" \| "link" | "ghost" |
class | string | — |
<InputGroupButton> Button </InputGroupButton> <InputGroupButton size="icon-xs" aria-label="Copy"> <CopyIcon /> </InputGroupButton>InputGroupInput
构建输入组时替代<Input />的组件(实现见 InputGroupInput.vue)。已预置输入组样式,并使用统一的data-slot="input-group-control"参与焦点状态处理。
| Prop | 类型 | 默认值 |
|---|---|---|
class | string | — |
其余 props 全部透传给底层<Input />。
<InputGroup> <InputGroupInput placeholder="Enter text..." /> <InputGroupAddon> <SearchIcon /> </InputGroupAddon> </InputGroup>其预置样式包括:flex-1(占满剩余宽度)、rounded-none border-0 bg-transparent shadow-none focus-visible:ring-0(去边框去圆角,让容器统一负责外观,避免焦点环重复)。
InputGroupTextarea
构建输入组时替代<Textarea />的组件(实现见 InputGroupTextarea.vue),预置 textarea 输入组样式并复用data-slot="input-group-control"。
| Prop | 类型 | 默认值 |
|---|---|---|
class | string | — |
其余 props 透传给底层<Textarea />。额外样式为resize-none(禁止手动拉伸)与py-3(纵向内边距)。
<InputGroup> <InputGroupTextarea placeholder="Enter message..." /> <InputGroupAddon align="block-end"> <InputGroupButton>Send</InputGroupButton> </InputGroupAddon> </InputGroup>五、源码级原理:data-slot 联动机制
Input Group 最精巧的设计在于:容器不依赖 JS,仅凭 CSS 的has-*选择器 +data-slot属性即可完成布局、焦点环、错误态的自动联动(见 InputGroup.vue)。
1. 焦点状态自动高亮
所有输入控件(无论是内置的InputGroupInput/InputGroupTextarea,还是自定义元素)都带有data-slot="input-group-control"。容器通过后代选择器感知焦点:
has-[[data-slot=input-group-control]:focus-visible]:border-ring has-[[data-slot=input-group-control]:focus-visible]:ring-ring/50 has-[[data-slot=input-group-control]:focus-visible]:ring-3即“当容器内存在:focus-visible的输入控件时”,自动切换容器本身的边框颜色与 ring 光环,形成聚焦的“整体感”——这正是搜索框聚焦时整组高亮的效果来源。
2. 错误状态自动联动
同样的思路用于校验错误展示:
has-[[data-slot][aria-invalid=true]]:ring-destructive/20 has-[[data-slot][aria-invalid=true]]:border-destructive dark:has-[[data-slot][aria-invalid=true]]:ring-destructive/40只要输入控件带有aria-invalid="true"(表单校验库如 VeeValidate、TanStack Form、Formisch 集成时的标准做法),容器自动呈现红色错误边框与错误色 ring,无需手动切换样式。仓库中的 VeeValidatePasswordDemo.vue、TanStackFormDemo.vue、FormischDemo.vue 等表单示例均依赖此机制。
3. 对齐驱动的布局切换
容器通过检查 Addon 的data-align属性动态调整内部布局:
- 检测到
inline-start/inline-end时,分别给input加pl-2/pr-2防止文字贴着 Addon; - 检测到
block-start/block-end时,切换为纵向布局(flex-col)并调整输入框的pb-3/pt-3,同时容器高度变为h-auto; - 检测到 textarea 时(
has-[>textarea]:h-auto)同样放弃固定高度。
4. 点击 Addon 聚焦输入框
InputGroupAddon.vue 中定义了一个点击处理器:当点击 Addon(且点击目标不是内部按钮)时,自动在父容器中查找input并调用.focus()。这保证了用户点击前缀图标或后缀单位时,光标会落回输入框,交互更顺滑:
function handleInputGroupAddonClick(e: MouseEvent) { const target = e.target as HTMLElement | null if (target && target.closest("button")) return currentTarget?.parentElement?.querySelector("input")?.focus() }5. 禁用态视觉降级
在 index.ts 的 Addon 变体中,通过group-data-[disabled=true]/input-group:opacity-50让data-disabled的容器内所有 Addon 半透明,与 InputGroupWithSpinner.vue 中的禁用加载态配合使用。
六、可访问性与实践建议
- 顺序优先:始终把输入控件写在 Addon 之前,保证 Tab 键焦点顺序为“输入框 → 附加操作”,避免焦点跳跃。
- 语义标签:纯装饰图标可搭配
aria-hidden;有操作含义的图标按钮务必提供aria-label(如aria-label="Copy")。 - 错误提示:配合表单校验库为输入控件设置
aria-invalid,错误态样式会自动生效,无需额外逻辑。 - 自定义控件:任何自定义输入只需挂上
data-slot="input-group-control"即可接入整套联动;样式完全自理,通过class属性控制。 - 扩展阅读:完整示例可继续查看 apps/v4/components/demo 目录下的
InputGroupWith*系列文件;组件源码位于 apps/v4/registry/new-york-v4/ui/input-group,是理解cva变体组织与has-*选择器用法的良好范本。
- UI组件
- 前端
【免费下载链接】shadcn-vue
Vue port of shadcn-ui
相关推荐
把QQ空间历史说说完整存到本地:GetQzonehistory实操指南
把QQ空间历史说说完整存到本地:GetQzonehistory实操指南 QQ空间开了这么多年,你发的说说和照片却不在自己手里。GetQzonehistory 把
网页爬虫数据分析Ant Design Input 组件完全指南:从基础用法到源码级原理
Ant Design Input 组件完全指南:从基础用法到源码级原理 Ant Design(antd)的 Input 输入框是表单域的基础包装组件,通过鼠标或
前端UI组件设计系统Element(Vue 2.0)Switch 开关组件完全指南:从基础用法到源码级实现
Element(Vue 2.0)Switch 开关组件完全指南:从基础用法到源码级实现 Element 是饿了么团队开源的 Vue.js 2.0 UI 组件库(
前端UI组件设计系统
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考