InputGroup 输入框组

把前后缀、按钮与输入框合为一个输入面。

https://
.hoshimi.moe
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 { Copy } from 'lucide-react'
import { Button, IconButton, Input, InputGroup, Stack } from '@hina-ui/react'

export default function Demo() {
  return (
    <Stack className="w-80">
      <InputGroup>
        <Input defaultValue="HINA-2026" readOnly aria-label="邀请码" />
        <IconButton label="复制" variant="outline" tone="neutral">
          <Copy />
        </IconButton>
      </InputGroup>
      <InputGroup>
        <Input defaultValue="" type="email" aria-label="邮箱" placeholder="邮箱" />
        <Button>订阅</Button>
      </InputGroup>
    </Stack>
  )
}
tsx

尺寸

尺寸设置在组上,组内的输入框与按钮随之对齐。组内按钮的尺寸仍需与组一致。

https://
https://
https://
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 设置在组上,下发到组内的每个输入框。

https://
https://
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。一个字段里只放一个输入框。

https://
'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
—
追加至根元素的类名