TimeField 时间输入框

分段输入时间。

20
00

值:20:00

'use client'

import { useState } from 'react'
import { Clock } from 'lucide-react'
import { Stack, Text, TimeField } from '@hina-ui/react'

export default function Demo() {
  const [time, setTime] = useState<string | null>('20:00')
  return (
    <Stack gap="sm" align="start">
      <TimeField
        value={time}
        onValueChange={setTime}
        clearable
        aria-label="开播时间"
        className="w-44"
        leading={<Clock />}
      />
      <Text tone="muted" size="sm">
        值:{time ?? '空'}
      </Text>
    </Stack>
  )
}
tsx

用法

import { TimeField } from '@hina-ui/react'
ts

时间输入框把时间拆成时、分两段,每段只接受数字,输入完一段后自动进入下一段,上下方向键逐段增减。value / onValueChange 绑定 HH:mm 格式的字符串,精度到秒时是 HH:mm:ss,与浏览器原生时间控件的格式相同。段的顺序、分隔符与占位文字随当前语言变化,12 小时制的语言会多出上午与下午一段。未声明的属性都会传给包裹各段的组元素,请用 aria-label 或者 aria-labelledby 命名。

––
––

值:空

'use client'

import { useState } from 'react'
import { Stack, Text, TimeField } from '@hina-ui/react'

export default function Demo() {
  const [time, setTime] = useState<string | null>(null)
  return (
    <Stack gap="sm" align="start">
      <TimeField value={time} onValueChange={setTime} aria-label="开播时间" className="w-40" />
      <Text tone="muted" size="sm">
        值:{time ?? '空'}
      </Text>
    </Stack>
  )
}
tsx

示例

精度

granularity 决定精度:默认 minute 有时与分,second 再增加秒,hour 只有小时。hourCycle 指定 12 小时制或者 24 小时制,默认随语言决定。

20
20
00
30
AM
8
00
'use client'

import { useState } from 'react'
import { Stack, TimeField } from '@hina-ui/react'

export default function Demo() {
  const [hour, setHour] = useState<string | null>('20:00')
  const [second, setSecond] = useState<string | null>('20:00:30')
  const [twelve, setTwelve] = useState<string | null>('20:00')
  return (
    <Stack className="w-56">
      <TimeField value={hour} onValueChange={setHour} granularity="hour" aria-label="整点" />
      <TimeField value={second} onValueChange={setSecond} granularity="second" aria-label="到秒" />
      <TimeField value={twelve} onValueChange={setTwelve} hourCycle={12} aria-label="12 小时制" />
    </Stack>
  )
}
tsx

分钟步长

minuteStep 让分段按固定步长增减,键入的分钟也会吸附到最近的步长,适合按刻钟或者半小时安排的时段。

09
00

值:09:00

'use client'

import { useState } from 'react'
import { Stack, Text, TimeField } from '@hina-ui/react'

export default function Demo() {
  const [time, setTime] = useState<string | null>('09:00')
  return (
    <Stack gap="sm" align="start">
      <TimeField
        value={time}
        onValueChange={setTime}
        minuteStep={15}
        aria-label="预约时段"
        className="w-40"
      />
      <Text tone="muted" size="sm">
        值:{time ?? '空'}
      </Text>
    </Stack>
  )
}
tsx

范围

min 与 max 限定可选范围,值超出范围时输入框显示为校验未通过,但值仍然会更新。placeholder 指定没有值时各段从哪个时间开始增减,默认是当前时间。

21
30
'use client'

import { useState } from 'react'
import { TimeField } from '@hina-ui/react'

export default function Demo() {
  const [time, setTime] = useState<string | null>('21:30')
  return (
    <TimeField
      value={time}
      onValueChange={setTime}
      min="09:00"
      max="18:00"
      placeholder="09:00"
      aria-label="营业时间"
      className="w-40"
    />
  )
}
tsx

可清除

clearable 在末尾加一个清除按钮,清空后值为 null,焦点回到第一段。

20
00
'use client'

import { useState } from 'react'
import { TimeField } from '@hina-ui/react'

export default function Demo() {
  const [time, setTime] = useState<string | null>('20:00')
  return (
    <TimeField
      value={time}
      onValueChange={setTime}
      clearable
      aria-label="开播时间"
      className="w-40"
    />
  )
}
tsx

尺寸

size 有 sm、md、lg 三档,与同档输入框等高。

20
00
20
00
20
00
import { Stack, TimeField } from '@hina-ui/react'

export default function Demo() {
  return (
    <Stack className="w-40">
      <TimeField size="sm" value="20:00" aria-label="小号" />
      <TimeField size="md" value="20:00" aria-label="中号" />
      <TimeField size="lg" value="20:00" aria-label="大号" />
    </Stack>
  )
}
tsx

状态

invalid 给输入面加上警示色,disabled 禁用整组,readOnly 只读,仍然可以聚焦与选中。variant="secondary" 是放在 surface 之内的扁平形态。

20
00
20
00
20
00
20
00
import { Stack, TimeField } from '@hina-ui/react'

export default function Demo() {
  return (
    <Stack className="w-40">
      <TimeField value="20:00" invalid aria-label="校验未通过" />
      <TimeField value="20:00" disabled aria-label="已禁用" />
      <TimeField value="20:00" readonly aria-label="只读" />
      <TimeField value="20:00" variant="secondary" aria-label="扁平形态" />
    </Stack>
  )
}
tsx

在表单中

放进 FormField 后,标签通过 aria-labelledby 关联到整个时间输入,错误信息由字段渲染;校验规则与提交交给 Form。值是 HH:mm 字符串,可以直接按字符串比较。

––
––
'use client'

import { useState } from 'react'
import * as v from 'valibot'
import { Button, Form, FormField, Text, TimeField } from '@hina-ui/react'

const schema = v.object({
  opensAt: v.pipe(
    v.string('请填写开门时间'),
    v.check(value => value >= '06:00', '开门不早于 06:00'),
  ),
})

export default function Demo() {
  const [values, setValues] = useState<{ opensAt: string | null }>({ opensAt: 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="opensAt" label="开门时间" required>
            <TimeField value={values.opensAt} onValueChange={opensAt => setValues({ opensAt })} />
          </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
HH:mm 字符串,精度到秒时是 HH:mm:ss
placeholder
string
当前时间
空值时各段的起点,HH:mm 字符串
min
string
—
可选范围的下限
max
string
—
可选范围的上限
granularity
'hour' | 'minute' | 'second'
'minute'
精度
hourCycle
12 | 24
随语言
小时制
minuteStep
number
—
分段的步长
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
—
点击清除