代码演示
如何引入
import { TimePicker } from '@fibrous/semi-ui-react';
基础使用
点击 TimePicker,然后可以在浮层中选择或者输入某一时间。
代码
import { TimePicker } from '@fibrous/semi-ui-react';
export default function TimePickerDemo() {
return <TimePicker />;
}无限滚动
版本V2.22.0开始,我们将 TimePicker 内的 ScrollItem 的默认模式从 wheel 变更为了 normal, 若想应用回无限滚动的效果,可参考以下示例。
代码
import { TimePicker } from '@fibrous/semi-ui-react';
export default function TimePickerDemo() {
return <TimePicker scrollItemProps={{ mode: 'wheel', cycled: true }} />;
}受控组件
当使用 value 而不是 defaultValue 时,作为受控组件使用。value 和 onChange 需要配合使用。
代码
import { useState } from 'react';
import { TimePicker } from '@fibrous/semi-ui-react';
export default function TimePickerDemo() {
const [value, setValue] = useState<Date | Date[] | null>(null);
const onChange = (time: Date | Date[]) => {
console.log(time);
setValue(time);
};
return <TimePicker value={value} onChange={onChange} />;
}不同的 Format 格式
TimePicker 浮层中的列会随着 format 变化,当略去 format 中的某部分时,浮层中对应的列也会消失。
NOTE: format 遵循 date-fns v4 的 format 格式。 https://date-fns.org/docs/format
代码
import { TimePicker } from '@fibrous/semi-ui-react';
export default function TimePickerDemo() {
return <TimePicker format="HH:mm" defaultValue="10:24" />;
}设置面板头部,底部
禁用时间选择
代码
import { TimePicker } from '@fibrous/semi-ui-react';
export default function TimePickerDemo() {
return <TimePicker defaultValue="12:08:23" disabled />;
}设置步长
可以使用 hourStep, minuteStep, secondStep 按步长展示可选的时分秒。
代码
import { TimePicker } from '@fibrous/semi-ui-react';
export default function TimePickerDemo() {
return <TimePicker minuteStep={15} secondStep={10} />;
}12 小时制
12 小时制的时间选择器,默认的 format 为 a h:mm:ss,传入的 format 格式必须在 dateFns 日期格式范围之内。
例如默认的 12 小时制格式串为:
a h:mm:ss,如果传入A h:mm:ss则会导致无法正确格式化。
代码
import { TimePicker } from '@fibrous/semi-ui-react';
export default function TimePickerDemo() {
return (
<div>
<TimePicker use12Hours />
<br />
<br />
<TimePicker use12Hours format="a h:mm" />
</div>
);
}时间范围
传入 type="timeRange" 开启时间范围选择。
代码
import { TimePicker } from '@fibrous/semi-ui-react';
export default function TimePickerDemo() {
return (
<div>
<TimePicker type="timeRange" defaultValue={['10:23:15', '12:38:32']} />
<br />
<br />
<TimePicker type="timeRange" use12Hours format="a h:mm" defaultValue={['上午 08:11', '下午 11:21']} />
</div>
);
}Range 模式下分别禁用左右面板(disabledTime)
当 type="timeRange" 时,你可以通过 disabledTime(value, panelType) 对左右面板分别应用不同的禁用规则。
value:当前已选时间数组,未选的一侧为undefined(如[date, undefined]);没有值时为空数组,value、defaultValue或输入框只含一个时间时长度为 1panelType:'left' | 'right',分别代表开始/结束面板
下面示例实现:选择开始时间后,右侧结束时间面板会禁用早于开始时间的选项。
代码
import { TimePicker } from '@fibrous/semi-ui-react';
export default function TimePickerDemo() {
return (
<TimePicker
type="timeRange"
disabledTime={(value, panelType) => {
const start = Array.isArray(value) ? value[0] : value;
if (panelType === 'right' && start instanceof Date) {
const h = start.getHours();
const m = start.getMinutes();
return {
disabledHours: () => Array.from({ length: h }, (_, i) => i),
disabledMinutes: (hour) => (hour === h ? Array.from({ length: m }, (_, i) => i) : []),
};
}
return {};
}}
/>
);
}自定义触发器
默认情况下我们使用 Input 组件作为 TimePicker 组件的触发器,通过传递 triggerRender 方法你可以自定义这个触发器。
代码
import { useState } from 'react';
import * as dateFns from 'date-fns';
import { IconTimePicker } from '@fibrous/semi-icons-react/lab';
import { TimePicker, Tag } from '@fibrous/semi-ui-react';
export default function TimePickerDemo() {
const formatToken = 'HH:mm:ss';
const [time, setTime] = useState(() => new Date(2019, 6, 23, 10, 32, 18));
return (
<TimePicker
value={time}
format={formatToken}
onChange={(time: Date | Date[]) => setTime(time as Date)}
triggerRender={({ placeholder }) => (
<Tag color="cyan" size="large" shape="circle" style={{ padding: 12, paddingRight: 16, fontSize: 14 }} type="light" prefixIcon={<IconTimePicker />}>
{time ? dateFns.format(time, formatToken) : placeholder}
</Tag>
)}
/>
);
}时区设置
Semi 所有关于时区的配置都收敛在 ConfigProvider 中,详细使用可以参考 ConfigProvider
Select Time Zone:
TimePicker:
代码
import { useMemo, useState } from 'react';
import { ConfigProvider, Select, TimePicker } from '@fibrous/semi-ui-react';
export default function TimePickerDemo() {
const [timeZone, setTimeZone] = useState('GMT+08:00');
const defaultTimestamp = 1581599305265;
const gmtList = useMemo(() => {
const list: string[] = [];
for (let hourOffset = -11; hourOffset <= 14; hourOffset++) {
const prefix = hourOffset >= 0 ? '+' : '-';
const hOffset = Math.abs(hourOffset);
list.push(`GMT${prefix}${String(hOffset).padStart(2, '0')}:00`);
}
return list;
}, []);
return (
<ConfigProvider timeZone={timeZone}>
<div style={{ width: 300 }}>
<h5 style={{ margin: 10 }}>Select Time Zone:</h5>
<Select aria-label="时区" placeholder="请选择时区" style={{ width: 300 }} value={timeZone} showClear onSelect={(value) => setTimeZone(String(value))}>
{gmtList.map((gmt) => (
<Select.Option key={gmt} value={gmt}>
{gmt}
</Select.Option>
))}
</Select>
<br />
<br />
<h5 style={{ margin: 10 }}>TimePicker:</h5>
<TimePicker defaultValue={defaultTimestamp} onChange={(date, dateString) => console.log('DatePicker changed: ', date, dateString)} />
</div>
</ConfigProvider>
);
}API 参考
Props
| 属性 | 说明 | 类型 | 默认值 |
|---|---|---|---|
| autoAdjustOverflow | 浮层被遮挡时是否自动调整方向。 | boolean | true |
| autoFocus | 挂载后将焦点交给输入框,透传给输入框的 autoFocus。 | boolean | - |
| borderless | 无边框模式。 | boolean | false |
| className | 根元素的类名。 | string | "" |
| clearIcon | 自定义清除图标,showClear 为 true 时有效。 | React.ReactNode | - |
| defaultOpen | 非受控时面板是否默认打开。 | boolean | - |
| defaultValue | 非受控时的初始时间,形态同 value;type 为 timeRange 时为数组。 | BaseValueType | BaseValueType[] | - |
| disabled | 禁用全部操作。 | boolean | - |
| disabledHours | 返回禁止选择的小时数组。 | Type() => number[] | Type(): number[] => [] |
| disabledMinutes | 按所选小时返回禁止选择的分钟数组。 | Type(selectedHour: number) => number[] | Type(): number[] => [] |
| disabledSeconds | 按所选小时和分钟返回禁止选择的秒数组。 | Type(selectedHour: number, selectedMinute: number) => number[] | Type(): number[] => [] |
| disabledTime | 仅范围模式生效:按 value 与 panelType(left 或 right)返回左右面板各自的规则;value 没有值时为空数组,只含一个时间时长度为 1,未选的一侧为 undefined。返回的 disabledHours 等覆盖顶层同名属性,未返回的回退到顶层,关闭顶层规则需返回空数组的函数;单选模式下忽略。 | Type( value: Date[], panelType: PanelType ) => { disabledHours?: () => number[]; disabledMinutes?: (selectedHour: number) => number[]; disabledSeconds?: (selectedHour: number, selectedMinute: number) => number[]; } | - |
| focusOnOpen | 挂载后让输入框获得焦点并选中内容,同时打开面板。 | boolean | false |
| format | 展示的时间格式,遵循 date-fns 的 format。未设置时,use12Hours 为 true 取 a h:mm:ss,否则取 HH:mm:ss。 | string | - |
| hideDisabledOptions | 隐藏禁止选择的选项。 | boolean | false |
| hourStep | 小时选项的间隔,必须是正整数,未设置时为 1。 | number | - |
| id | 输入框的 id。 | string | - |
| inputReadOnly | 输入框只读,避免在移动设备上打开虚拟键盘;只读时清除按钮仍可用。 | boolean | false |
| inputStyle | 输入框的样式,对象的键用 camelCase。 | React.CSSProperties | - |
| insetLabel | 输入框内部的前缀标签,显示在输入框内。 | React.ReactNode | - |
| insetLabelId | insetLabel 的 id,用于 aria-labelledby 等关联。 | string | - |
| minuteStep | 分钟选项的间隔,必须是正整数,未设置时为 1。 | number | - |
| motion | 是否展示弹出层动画。 | boolean | true |
| onBlur | 点击外部关闭面板时的回调,参数为该次 mousedown 的原生事件。 | Type(e: MouseEvent) => void | - |
| onChange | 时间变化的回调。type 为 time 时参数为 (Date, 格式化字符串),为 timeRange 时两者都是数组。 | Type((value: Date, str: string) => void) | ((value: Date[], str: string[]) => void) | - |
| onFocus | 输入框获得焦点时的回调。 | TypeReact.FocusEventHandler<HTMLDivElement> | - |
| onOpenChange | 面板打开或关闭时的回调,参数为是否打开。 | Type(open: boolean) => void | - |
| open | 面板是否打开,受控属性;undefined 视为非受控。 | boolean | - |
| panelFooter | 面板底部的内容。范围模式下可传数组,第 i 项用于第 i 个面板。 | React.ReactNode | React.ReactNode[] | - |
| panelHeader | 面板头部的内容。范围模式下可传数组,第 i 项用于第 i 个面板;未设置时范围模式显示语言包中的起止文案。 | React.ReactNode | React.ReactNode[] | - |
| panels | 仅范围模式:第 i 项同时作为第 i 个面板的头部和底部,优先于 panelHeader 与 panelFooter。单选模式下忽略。 | React.ReactNode[] | - |
| placeholder | 没有值时显示的提示,默认取语言包中对应 type 的文案。 | string | - |
| popupClassName | 弹出层的类名。 | string | "" |
| popupStyle | 弹出层的样式,对象的键用 camelCase。 | React.CSSProperties | - |
| position | 浮层位置。未设置时,type 为 timeRange 取 bottom,为 time 取 bottomLeft;RTL 方向下取 bottomRight。 | Position | - |
| prefixCls | 样式类名的前缀。 | string | "semi-timepicker" |
| preventScroll | 聚焦时是否阻止浏览器滚动文档,直接作为 focus() 的 preventScroll 选项,作用于组件内部的聚焦调用。 | boolean | - |
| rangeSeparator | 时间范围的分隔符。 | string | " ~ " |
| ref | 组件句柄的引用,方法见 Methods 表。 | TypeReact.Ref<TimePickerHandle> | - |
| scrollItemProps | 传给各列的属性,只支持 cycled、mode、motion、transform,含义同 ScrollList 的 ScrollItem。 | TypePick<ScrollItemProps<any>, 'cycled' | 'mode' | 'motion' | 'transform'> | - |
| secondStep | 秒选项的间隔,必须是正整数,未设置时为 1。 | number | - |
| showClear | 是否展示清除按钮。 | boolean | true |
| size | 输入框的大小,可选 large、default 或 small。 | InputSize | "default" |
| stopPropagation | 是否阻止弹出层上的点击事件冒泡。 | boolean | true |
| style | 根元素的样式。 | React.CSSProperties | {} |
| timeZone | 时区:数字为相对 UTC 的小时偏移,字符串为 IANA 时区名;未设置时取 ConfigProvider 的 timeZone。 | string | number | - |
| triggerRender | 自定义触发器的渲染函数。入参含 disabled、placeholder、value(Date 数组,未选的一侧为 undefined)、inputValue、componentProps(TimePicker 的属性,不含 ref)和 onChange(更新输入框文本,效果同键入)。 | Type(props: TimePickerTriggerRenderProps) => React.ReactNode | - |
| type | 选择类型,time 为单个时间,timeRange 为时间范围。 | Type | "time" |
| use12Hours | 使用 12 小时制,为 true 时 format 默认为 a h:mm:ss。 | boolean | false |
| validateStatus | 校验状态,仅影响展示样式。 | ValidateStatus | - |
| value | 当前时间,可为 Date、时间戳或字符串;type 为 timeRange 时为数组。null 是受控的空值,undefined 视为非受控。 | BaseValueType | BaseValueType[] | null | - |
Methods
| 方法 | 说明 | 类型 |
|---|---|---|
| blur | 移除输入框的焦点。 | Type() => void |
| focus | 聚焦输入框,聚焦会打开面板;preventScroll 生效。 | Type() => void |
未声明的原生属性(如 title、data-*、aria-label、onKeyDown)落在内部输入框上,其中 prefix 作为输入框的前缀内容显示;使用 triggerRender 自定义触发器时,这些属性不落在任何元素上,可通过 componentProps 取用。
使用自定义触发器时,focus() 与 blur() 作用于触发器中的第一个 input,没有 input 时无效。
Accessibility
ARIA
- 输入框的 role 为
combobox,带aria-haspopup="dialog"、随面板开关变化的aria-expanded,aria-controls指向面板 - 时、分、秒与上午/下午列是 role 为
listbox的列表,名称分别为「小时」「分钟」「秒」「上午或下午」
键盘和焦点
- 输入框获得焦点时打开面板;面板打开后按
下箭头,焦点移入第一列 - 每列是一个 Tab 停靠点,
上箭头、下箭头、Home、End或输入字符选择该列的值 - 按
Esc关闭面板;焦点在面板内时回到输入框
文案规范
- 时间选择器至少包括时和分,如:11:30,它在本地化过程中,可以适应为12小时制或者24小时制
- 当选择12小时制,需要和AM/PM一起搭配使用
设计变量
| Sass 变量 | 值 | 用法 |
|---|---|---|
| $color-timePicker_range_picker_panel_split-border | var(--semi-color-border) | 时间选择器菜单分割线颜色 |
| $color-timePicker_range_panel-border | rgba(0, 0, 0, 0.1) | 时间选择器描边颜色 |
| $width-timePicker_range_panel-border | 1px | 时间选择器菜单分割线宽度 |
| $height-timePicker_panel_body | 252px | 时间选择器菜单高度 |
| $width-timePicker_panel_list_ampm | 72px | 时间选择器菜单中列宽度 - 上午下午 |
| $width-timePicker_panel_list_hour | 64px | 时间选择器菜单中列宽度 - 小时 |
| $width-timePicker_panel_list_minute | 64px | 时间选择器菜单中列宽度 - 分钟 |
| $width-timePicker_panel_list_second | 64px | 时间选择器菜单中列宽度 - 秒 |
| $width-timePicker_range_panel_scrolllist_body-border | 2px | 时间范围选择器双排菜单中间分割线宽度 |
| $spacing-timePicker_range_panel_scrolllist_header_body-padding | 0 | 时间范围选择器菜单header与内容内边距 |
| $radius-timePicker_range_panel | var(--semi-border-radius-medium) | 时间范围选择器菜单圆角 |
| $radius-timePicker_input | var(--semi-border-radius-small) | 时间范围选择器圆角 |
| $shadow-timePicker_range_panel | var(--semi-shadow-elevated) | 时间范围选择器菜单阴影 |