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