import { NumberInput } from '@hina-ui/react'
export default function Demo() {
return <NumberInput defaultValue={3} min={0} max={10} aria-label="数量" className="w-40" />
}
tsx
用法
import { NumberInput } from '@hina-ui/react'
ts
数值输入框由输入区与一组步进按钮组成,value / onValueChange 绑定数值。方向键、Page Up 与 Page Down、Home 与 End 以及聚焦时的滚轮都能调整数值。未声明的属性都会传给内部的 input。
import { NumberInput } from '@hina-ui/react'
export default function Demo() {
return <NumberInput defaultValue={1} aria-label="数量" placeholder="数量" className="w-40" />
}
tsx
示例
范围与步长
min 与 max 限定范围,step 决定每次增减的幅度。默认把输入的值对齐到步长,关闭 stepSnapping 后只限制在范围内。
import { NumberInput, Stack } from '@hina-ui/react'
export default function Demo() {
return (
<Stack className="w-40">
<NumberInput defaultValue={2.5} min={0} max={10} step={0.5} aria-label="评分" />
<NumberInput defaultValue={12} min={1} step={5} stepSnapping={false} aria-label="页数" />
</Stack>
)
}
tsx
格式
formatOptions 接受 Intl.NumberFormat 的选项,货币、百分比与单位都能显示。locale 决定分隔符与符号的写法,未设置时跟随语言包的语言。
import { NumberInput, Stack } from '@hina-ui/react'
export default function Demo() {
return (
<Stack className="w-48">
<NumberInput
defaultValue={1280}
formatOptions={{ style: 'currency', currency: 'CNY' }}
aria-label="价格"
/>
<NumberInput
defaultValue={0.35}
step={0.01}
min={0}
max={1}
formatOptions={{ style: 'percent' }}
aria-label="折扣"
/>
<NumberInput
defaultValue={1234.5}
locale="de-DE"
formatOptions={{ style: 'currency', currency: 'EUR' }}
aria-label="欧元价格"
/>
</Stack>
)
}
tsx
尺寸
三档尺寸与输入框相同。
import { NumberInput, Stack } from '@hina-ui/react'
export default function Demo() {
return (
<Stack className="w-40">
<NumberInput size="sm" defaultValue={1} aria-label="小号" />
<NumberInput size="md" defaultValue={1} aria-label="中号" />
<NumberInput size="lg" defaultValue={1} aria-label="大号" />
</Stack>
)
}
tsx
形态
primary 带边框、背景与阴影;secondary 使用浅色背景;bare 背景透明,不绘制边框、阴影、悬停底色或容器聚焦环,尺寸与内边距仍由原有设置控制。
bare 保留禁用状态与 aria-invalid;错误信息可由 FormField 显示。步进按钮之间及其与输入区之间的分隔线也会隐藏,按钮仍可正常操作。
import { NumberInput, Stack } from '@hina-ui/react'
export default function Demo() {
return (
<Stack className="w-full max-w-xs">
<NumberInput variant="primary" aria-label="primary" defaultValue={1} />
<NumberInput variant="secondary" aria-label="secondary" defaultValue={1} />
<NumberInput variant="bare" aria-label="bare" defaultValue={1} />
</Stack>
)
}
tsx
状态
invalid 标出校验未通过,disabled 不可编辑,readOnly 可以聚焦与复制但不能修改。
import { NumberInput, Stack } from '@hina-ui/react'
export default function Demo() {
return (
<Stack className="w-40">
<NumberInput invalid defaultValue={120} max={99} aria-label="校验未通过" />
<NumberInput disabled defaultValue={3} aria-label="已禁用" />
<NumberInput readonly defaultValue={3} aria-label="只读" />
</Stack>
)
}
tsx
不带步进按钮
关闭 controls 后只保留输入区,键盘与滚轮仍然有效。
import { NumberInput } from '@hina-ui/react'
export default function Demo() {
return (
<NumberInput controls={false} min={1} placeholder="页码" aria-label="页码" className="w-40" />
)
}
tsx
在表单中
放进 FormField 后,标签指向输入框,说明与错误信息由字段渲染;校验规则与提交交给 Form。值是数字或者 null,范围规则写在数字层。
'use client'
import { useState } from 'react'
import * as v from 'valibot'
import { Button, Form, FormField, NumberInput, Text } from '@hina-ui/react'
const schema = v.object({
price: v.pipe(
v.number('请输入价格'),
v.minValue(1, '价格至少 1 元'),
v.maxValue(9999, '价格不超过 9999 元'),
),
})
export default function Demo() {
const [values, setValues] = useState({ price: null as number | null | undefined })
const [saved, setSaved] = useState('')
async function save(data: unknown) {
await new Promise(resolve => setTimeout(resolve, 600))
setSaved(JSON.stringify(data))
}
return (
<Form values={values} rules={schema} className="w-80" onSubmit={save}>
{({ submitting }) => (
<>
<FormField name="price" label="价格" description="1 到 9999 元" required>
<NumberInput
value={values.price}
onValueChange={price => setValues({ ...values, price })}
min={0}
step={1}
/>
</FormField>
<Button type="submit" loading={submitting} className="self-start">
上架
</Button>
{saved && (
<Text tone="muted" size="sm">
已上架:{saved}
</Text>
)}
</>
)}
</Form>
)
}
tsx
行为
- 失焦或者按 Enter 时解析输入。无法解析时恢复为上一个值,超出范围的值限制到边界。
- 输入时拒绝不能构成数字的字符。
- 步进按钮支持按住连续增减,到达边界的一侧会禁用。
- 悬停、聚焦、错误与禁用的表现与 Input 相同。
无障碍
- 根元素为
role="group",输入区为role="spinbutton",带aria-valuenow、aria-valuemin与aria-valuemax。 - 步进按钮不进入 Tab 序列,键盘用户用方向键调整。按钮名称随语言包本地化。
- 应当配合
label元素或者aria-label提供名称。invalid同时设置aria-invalid。
API
Props
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
value | number | null | — | 数值 |
defaultValue | number | — | 非受控时的初始值 |
min | number | — | 最小值 |
max | number | — | 最大值 |
step | number | 1 | 步长 |
stepSnapping | boolean | true | 是否把值对齐到步长 |
formatOptions | Intl.NumberFormatOptions | — | 显示格式 |
locale | string | — | 格式化所用的语言 |
controls | boolean | true | 是否显示步进按钮 |
variant | 'primary' | 'secondary' | 'bare' | 'primary' | 形态 |
size | 'sm' | 'md' | 'lg' | 'md' | 尺寸 |
invalid | boolean | false | 是否校验未通过 |
disabled | boolean | false | 是否禁用 |
readOnly | boolean | false | 是否只读 |
className | string | — | 追加至根元素的类名 |
| 回调 | 参数 | 说明 |
|---|---|---|
onValueChange | value: number | undefined | 数值变化 |