'use client'
import { useState } from 'react'
import { PinInput } from '@hina-ui/react'
export default function Demo() {
const [code, setCode] = useState('')
return <PinInput value={code} onValueChange={setCode} type="number" otp aria-label="验证码" />
}
tsx
用法
import { PinInput } from '@hina-ui/react'
ts
验证码输入框把一段固定长度的字符拆成若干格,每格只容纳一个字符。value / onValueChange 绑定的是完整的字符串,length 决定格数,默认六格。输入一个字符后焦点自动进入下一格,退格回到上一格,粘贴整段内容时按位分配。未声明的属性都会传给根元素,请用 aria-label 或者 aria-labelledby 为整组命名。
当前:空
'use client'
import { useState } from 'react'
import { PinInput, Stack, Text } from '@hina-ui/react'
export default function Demo() {
const [code, setCode] = useState('')
const [submitted, setSubmitted] = useState('')
return (
<Stack gap="sm" align="start">
<PinInput
value={code}
onValueChange={setCode}
length={4}
aria-label="邀请码"
onComplete={setSubmitted}
/>
<Text tone="muted" size="sm">
{submitted ? `已填满:${submitted}` : `当前:${code || '空'}`}
</Text>
</Stack>
)
}
tsx
示例
数字与一次性验证码
type="number" 只接受数字。otp 把输入框的自动填充设为一次性验证码,浏览器与系统可以把收到的短信验证码直接填入。
'use client'
import { useState } from 'react'
import { PinInput } from '@hina-ui/react'
export default function Demo() {
const [code, setCode] = useState('')
return <PinInput value={code} onValueChange={setCode} type="number" otp aria-label="短信验证码" />
}
tsx
遮蔽
mask 把每一格变成密码输入,适合 PIN 码。
'use client'
import { useState } from 'react'
import { PinInput } from '@hina-ui/react'
export default function Demo() {
const [pin, setPin] = useState('')
return (
<PinInput
value={pin}
onValueChange={setPin}
length={4}
type="number"
mask
aria-label="PIN 码"
/>
)
}
tsx
占位符
placeholder 显示在空格子里,聚焦时隐去。
'use client'
import { useState } from 'react'
import { PinInput } from '@hina-ui/react'
export default function Demo() {
const [code, setCode] = useState('')
return <PinInput value={code} onValueChange={setCode} placeholder="○" aria-label="验证码" />
}
tsx
尺寸
size 有 sm、md、lg 三档,每一格是边长与同档输入框高度相等的正方形。
import { PinInput, Stack } from '@hina-ui/react'
export default function Demo() {
return (
<Stack gap="sm" align="start">
<PinInput size="sm" length={4} value="2048" aria-label="小号" />
<PinInput size="md" length={4} value="2048" aria-label="中号" />
<PinInput size="lg" length={4} value="2048" aria-label="大号" />
</Stack>
)
}
tsx
状态
invalid 给每一格加上警示色边框,disabled 禁用整组。variant="secondary" 是放在 surface 之内的扁平形态。
import { PinInput, Stack } from '@hina-ui/react'
export default function Demo() {
return (
<Stack gap="sm" align="start">
<PinInput length={4} value="1234" invalid aria-label="校验未通过" />
<PinInput length={4} value="1234" disabled aria-label="已禁用" />
<PinInput length={4} value="1234" variant="secondary" aria-label="扁平形态" />
</Stack>
)
}
tsx
在表单中
放进 FormField 后,标签通过 aria-labelledby 关联到整组格子,说明与错误信息由字段渲染;校验规则与提交交给 Form。
'use client'
import { useState } from 'react'
import * as v from 'valibot'
import { Button, Form, FormField, PinInput, Text } from '@hina-ui/react'
const schema = v.object({
code: v.pipe(v.string('请输入验证码'), v.length(6, '验证码是 6 位数字')),
})
export default function Demo() {
const [values, setValues] = useState({ code: '' })
const [saved, setSaved] = useState(false)
async function save() {
await new Promise(resolve => setTimeout(resolve, 600))
setSaved(true)
}
return (
<Form values={values} rules={schema} className="w-80" onSubmit={save}>
{({ submitting }) => (
<>
<FormField name="code" label="验证码" description="已发送到你的邮箱" required>
<PinInput
value={values.code}
onValueChange={code => setValues({ ...values, code })}
length={6}
type="number"
otp
/>
</FormField>
<Button type="submit" loading={submitting} className="self-start">
验证
</Button>
{saved && (
<Text tone="muted" size="sm">
验证通过。
</Text>
)}
</>
)}
</Form>
)
}
tsx
行为
- 每格只容纳一个字符,输入后焦点前进;在空格子里退格会回到上一格并清除它。
- 粘贴时从当前格开始按位分配,超出格数的部分丢弃。
- 所有格子填满时调用
onComplete,参数是完整的字符串。 - 左右方向键在格子之间移动焦点。
无障碍
- 根元素是
role="group",通过aria-label或者aria-labelledby命名。 - 每一格自带「第 n 位,共 N 位」的名称,取自语言包。
invalid会同时在每一格设置aria-invalid。
API
Props
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
value | string | '' | 完整的值 |
length | number | 6 | 格数 |
type | 'text' | 'number' | 'text' | 接受的字符类型 |
mask | boolean | false | 是否以密码方式显示 |
otp | boolean | false | 是否接收一次性验证码的自动填充 |
placeholder | string | '' | 空格子里显示的占位符 |
name | string | — | 表单字段名,提交时携带完整的值 |
variant | 'primary' | 'secondary' | 'primary' | 形态 |
size | 'sm' | 'md' | 'lg' | 'md' | 尺寸 |
disabled | boolean | false | 是否禁用 |
invalid | boolean | false | 是否处于校验未通过状态 |
className | string | — | 追加至根元素的类名 |
回调
| 回调 | 参数 | 说明 |
|---|---|---|
onValueChange | value: string | 值变化时 |
onComplete | value: string | 所有格子填满时 |