值:2026-09-04
'use client'
import { useState } from 'react'
import { CalendarDays } from 'lucide-react'
import { DateField, Stack, Text } from '@hina-ui/react'
export default function Demo() {
const [date, setDate] = useState<string | null>('2026-09-04')
return (
<Stack gap="sm" align="start">
<DateField
value={date}
onValueChange={setDate}
clearable
aria-label="发布日期"
className="w-72"
leading={<CalendarDays />}
/>
<Text tone="muted" size="sm">
值:{date ?? '空'}
</Text>
</Stack>
)
}
tsx
用法
import { DateField } from '@hina-ui/react'
ts
日期输入框把日期拆成年、月、日三段,每段只接受数字,输入完一段后自动进入下一段,上下方向键逐段增减。value / onValueChange 绑定 ISO 8601 字符串:只有日期时是 YYYY-MM-DD,带时间时是 YYYY-MM-DDTHH:mm,与浏览器原生日期控件的格式相同,可以直接提交或者写入接口。段的顺序、分隔符与占位文字随当前语言变化,中文是「年 / 月 / 日」。未声明的属性都会传给包裹各段的组元素,请用 aria-label 或者 aria-labelledby 命名。
值:空
'use client'
import { useState } from 'react'
import { DateField, Stack, Text } from '@hina-ui/react'
export default function Demo() {
const [date, setDate] = useState<string | null>(null)
return (
<Stack gap="sm" align="start">
<DateField value={date} onValueChange={setDate} aria-label="发售日期" className="w-72" />
<Text tone="muted" size="sm">
值:{date ?? '空'}
</Text>
</Stack>
)
}
tsx
示例
日期与时间
granularity 决定精度:默认 day 只有日期,hour 增加小时,minute 增加小时与分钟,second 再增加秒。hourCycle 指定 12 小时制或者 24 小时制,默认随语言决定。
值:2026-09-04T20:00
'use client'
import { useState } from 'react'
import { DateField, Stack, Text } from '@hina-ui/react'
export default function Demo() {
const [at, setAt] = useState<string | null>('2026-09-04T20:00')
return (
<Stack gap="sm" align="start">
<DateField
value={at}
onValueChange={setAt}
granularity="minute"
aria-label="定时发布"
className="w-80"
/>
<Text tone="muted" size="sm">
值:{at ?? '空'}
</Text>
</Stack>
)
}
tsx
范围
min 与 max 限定可选范围,值超出范围时输入框显示为校验未通过,但值仍然会更新。placeholder 指定没有值时各段从哪个日期开始增减,默认是今天。
'use client'
import { useState } from 'react'
import { DateField } from '@hina-ui/react'
export default function Demo() {
const [date, setDate] = useState<string | null>('2026-10-15')
return (
<DateField
value={date}
onValueChange={setDate}
min="2026-09-01"
max="2026-09-30"
placeholder="2026-09-01"
aria-label="活动日期"
className="w-72"
/>
)
}
tsx
可清除
clearable 在末尾加一个清除按钮,清空后值为 null,焦点回到第一段。
'use client'
import { useState } from 'react'
import { DateField } from '@hina-ui/react'
export default function Demo() {
const [date, setDate] = useState<string | null>('2026-09-04')
return (
<DateField
value={date}
onValueChange={setDate}
clearable
aria-label="发布日期"
className="w-72"
/>
)
}
tsx
尺寸
size 有 sm、md、lg 三档,与同档输入框等高。
import { DateField, Stack } from '@hina-ui/react'
export default function Demo() {
return (
<Stack className="w-72">
<DateField size="sm" value="2026-09-04" aria-label="小号" />
<DateField size="md" value="2026-09-04" aria-label="中号" />
<DateField size="lg" value="2026-09-04" aria-label="大号" />
</Stack>
)
}
tsx
状态
invalid 给输入面加上警示色,disabled 禁用整组,readOnly 只读,仍然可以聚焦与选中。variant="secondary" 是放在 surface 之内的扁平形态。
import { DateField, Stack } from '@hina-ui/react'
export default function Demo() {
return (
<Stack className="w-72">
<DateField value="2026-09-04" invalid aria-label="校验未通过" />
<DateField value="2026-09-04" disabled aria-label="已禁用" />
<DateField value="2026-09-04" readonly aria-label="只读" />
<DateField value="2026-09-04" variant="secondary" aria-label="扁平形态" />
</Stack>
)
}
tsx
在表单中
放进 FormField 后,标签通过 aria-labelledby 关联到整个日期输入,说明与错误信息由字段渲染;校验规则与提交交给 Form。值是 ISO 字符串,可以直接按字符串比较。
'use client'
import { useState } from 'react'
import * as v from 'valibot'
import { Button, DateField, Form, FormField, Text } from '@hina-ui/react'
const today = new Date().toISOString().slice(0, 10)
const schema = v.object({
birthday: v.pipe(
v.string('请输入出生日期'),
v.check(value => value <= today, '出生日期不能晚于今天'),
),
})
export default function Demo() {
const [values, setValues] = useState<{ birthday: string | null }>({ birthday: null })
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="birthday" label="出生日期" required>
<DateField
value={values.birthday}
onValueChange={birthday => setValues({ birthday })}
/>
</FormField>
<Button type="submit" loading={submitting} className="self-start">
保存
</Button>
{saved && (
<Text tone="muted" size="sm">
已保存:{saved}
</Text>
)}
</>
)}
</Form>
)
}
tsx
行为
- 点击任意一段即可输入;点击输入面的空白处时,焦点落到第一个空段。
- 每段只接受数字,输入完一段后自动进入下一段;左右方向键在段之间移动,上下方向键增减,退格清空当前段。
- 所有段都填满时才会更新值;清空任意一段后,值变为
null。 - 超出
min或者max的值仍然会更新,同时标为校验未通过。
无障碍
- 各段是
role="spinbutton",带本地化名称与当前值,分隔符对辅助技术隐藏。 - 通过
aria-label或者aria-labelledby为整组命名。 invalid与超出范围都会在组元素上设置aria-invalid。
API
Props
| 属性 | 类型 | 默认值 | 说明 |
|---|---|---|---|
value | string | null | null | ISO 8601 字符串,精度随 granularity |
placeholder | string | 今天 | 空值时各段的起点,ISO 8601 字符串 |
min | string | — | 可选范围的下限 |
max | string | — | 可选范围的上限 |
granularity | 'day' | 'hour' | 'minute' | 'second' | 'day' | 精度 |
hourCycle | 12 | 24 | 随语言 | 小时制 |
clearable | boolean | false | 是否显示清除按钮 |
readOnly | boolean | false | 是否只读 |
name | string | — | 表单字段名 |
variant | 'primary' | 'secondary' | 'primary' | 形态 |
size | 'sm' | 'md' | 'lg' | 'md' | 尺寸 |
disabled | boolean | false | 是否禁用 |
invalid | boolean | false | 是否处于校验未通过状态 |
className | string | — | 追加至根元素的类名 |
内容属性
| 属性 | 说明 |
|---|---|
leading | 前置附属格的内容 |
trailing | 后置附属格的内容 |
回调
| 回调 | 参数 | 说明 |
|---|---|---|
onValueChange | value: string | null | 值变化 |
onClear | — | 点击清除 |