<script setup lang="ts">
import { ref } from 'vue'
import { Input, InputGroup, InputGroupAddon } from '@hina-ui/vue'
const site = ref('shion')
</script>
<template>
<InputGroup class="w-80">
<InputGroupAddon>https://</InputGroupAddon>
<Input v-model="site" aria-label="站点名" />
<InputGroupAddon>.hoshimi.moe</InputGroupAddon>
</InputGroup>
</template>
vue
用法
import { InputGroup, InputGroupAddon } from '@hina-ui/vue'
ts
组本身就是一个完整的输入面:边框、阴影与聚焦环都在组上,组内的输入框、附属段与按钮共用这一副外壳。尺寸与形态设置在组上,组内的输入框沿用组的设置。InputGroupAddon 用于固定的前缀或者后缀,文字与图标都可以,以分隔线与输入区区分;边框以内的图标与单位用输入框自己的 leading 与 trailing 插槽。
<script setup lang="ts">
import { ref } from 'vue'
import { AtSign } from '@lucide/vue'
import { Input, InputGroup, InputGroupAddon, Stack } from '@hina-ui/vue'
const handle = ref('')
const amount = ref('')
</script>
<template>
<Stack class="w-80">
<InputGroup>
<InputGroupAddon>
<AtSign />
</InputGroupAddon>
<Input v-model="handle" aria-label="用户名" placeholder="用户名" />
</InputGroup>
<InputGroup>
<Input v-model="amount" inputmode="decimal" aria-label="金额" placeholder="金额" />
<InputGroupAddon>元</InputGroupAddon>
</InputGroup>
</Stack>
</template>
vue
示例
尺寸
尺寸设置在组上,组内的输入框与按钮随之对齐。组内按钮的尺寸仍需与组一致。
<script setup lang="ts">
import { Button, Input, InputGroup, InputGroupAddon, Stack } from '@hina-ui/vue'
</script>
<template>
<Stack class="w-80">
<InputGroup size="sm">
<InputGroupAddon>https://</InputGroupAddon>
<Input aria-label="小号" />
<Button size="sm">确定</Button>
</InputGroup>
<InputGroup size="md">
<InputGroupAddon>https://</InputGroupAddon>
<Input aria-label="中号" />
<Button size="md">确定</Button>
</InputGroup>
<InputGroup size="lg">
<InputGroupAddon>https://</InputGroupAddon>
<Input aria-label="大号" />
<Button size="lg">确定</Button>
</InputGroup>
</Stack>
</template>
vue
形态
primary 带边框、背景与阴影;secondary 使用浅色背景;bare 背景透明,不绘制边框、阴影、悬停底色或容器聚焦环,尺寸与内边距仍由原有设置控制。
bare 保留禁用状态与 aria-invalid;错误信息可由 FormField 显示。组内分隔线也会隐藏,按钮保留自身的交互反馈。
<script setup lang="ts">
import { Input, InputGroup, InputGroupAddon, Stack } from '@hina-ui/vue'
</script>
<template>
<Stack class="w-full max-w-xs">
<InputGroup variant="primary">
<InputGroupAddon>@</InputGroupAddon>
<Input aria-label="primary" placeholder="primary" />
</InputGroup>
<InputGroup variant="secondary">
<InputGroupAddon>@</InputGroupAddon>
<Input aria-label="secondary" placeholder="secondary" />
</InputGroup>
<InputGroup variant="bare">
<InputGroupAddon>@</InputGroupAddon>
<Input aria-label="bare" placeholder="bare" />
</InputGroup>
</Stack>
</template>
vue
状态
invalid 与 disabled 设置在组上,下发到组内的每个输入框。
<script setup lang="ts">
import { Input, InputGroup, InputGroupAddon, Stack } from '@hina-ui/vue'
</script>
<template>
<Stack class="w-80">
<InputGroup invalid>
<InputGroupAddon>https://</InputGroupAddon>
<Input model-value="not a domain" aria-label="校验未通过" />
</InputGroup>
<InputGroup disabled>
<InputGroupAddon>https://</InputGroupAddon>
<Input model-value="shion.hoshimi.moe" aria-label="已禁用" />
</InputGroup>
</Stack>
</template>
vue
与其他输入框组合
数值、搜索等输入框都能放入组内。
<script setup lang="ts">
import { ref } from 'vue'
import { JapaneseYen } from '@lucide/vue'
import {
Button,
InputGroup,
InputGroupAddon,
NumberInput,
SearchInput,
Stack,
} from '@hina-ui/vue'
const price = ref(128)
const query = ref('')
</script>
<template>
<Stack class="w-80">
<InputGroup>
<InputGroupAddon>
<JapaneseYen />
</InputGroupAddon>
<NumberInput v-model="price" :min="0" aria-label="价格" />
</InputGroup>
<InputGroup>
<SearchInput v-model="query" aria-label="搜索" placeholder="搜索作品" />
<Button>搜索</Button>
</InputGroup>
</Stack>
</template>
vue
在表单中
整组放进 FormField 后,标签指向组内的输入框,错误信息由字段渲染;校验规则与提交交给 Form。一个字段里只放一个输入框。
<script setup lang="ts">
import { reactive, ref } from 'vue'
import * as v from 'valibot'
import { Button, Form, FormField, Input, InputGroup, InputGroupAddon, Text } from '@hina-ui/vue'
const schema = v.object({
site: v.pipe(
v.string('请输入网址'),
v.trim(),
v.nonEmpty('请输入网址'),
v.regex(/^[\w-]+(\.[\w-]+)+(\/.*)?$/, '网址格式不正确,不需要填写协议'),
),
})
const values = reactive({ site: '' })
const saved = ref('')
async function save(data: unknown) {
await new Promise(resolve => setTimeout(resolve, 600))
saved.value = `https://${(data as { site: string }).site}`
}
</script>
<template>
<Form v-slot="{ submitting }" :values="values" :rules="schema" class="w-80" @submit="save">
<FormField name="site" label="个人主页" required>
<InputGroup>
<InputGroupAddon>https://</InputGroupAddon>
<Input v-model="values.site" placeholder="example.com" />
</InputGroup>
</FormField>
<Button type="submit" :loading="submitting" class="self-start">保存</Button>
<Text v-if="saved" tone="muted" size="sm">已保存:{{ saved }}</Text>
</Form>
</template>
vue
行为
primary与secondary聚焦组内任一输入区时,整个组显示聚焦环。- 点击附属段会让相邻的输入区聚焦,前缀把光标放到开头,后缀放到末尾。按钮不受影响。
- 组内按钮键盘聚焦时轮廓向内收,不会被组的边缘裁掉。
- 悬停、错误与禁用的表现与 Input 相同。
无障碍
- 附属段是纯展示内容,不参与输入框的无障碍名称。需要说明时用
label元素或者aria-label给输入框命名。
API
InputGroup
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
variant | 'primary' | 'secondary' | 'bare' | 'primary' | 形态 |
size | 'sm' | 'md' | 'lg' | 'md' | 尺寸 |
invalid | boolean | false | 是否校验未通过 |
disabled | boolean | false | 是否禁用 |
class | string | — | 追加至根元素的类名 |
InputGroupAddon
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
class | string | — | 追加至根元素的类名 |