Input · TimePicker

TimePicker

Users can easily select a compliant, formatted point of time using the time selector.

Demos

How to import

import { TimePicker } from '@fibrous/semi-ui-solid';

Basic Usage

Click TimePicker, and then you can select or enter a time in the floating layer.

Code
import { TimePicker } from '@fibrous/semi-ui-solid';

export default function TimePickerDemo() {
    return <TimePicker />;
}

Infinite Scroll

Starting from version V2.22.0, we changed the default mode of ScrollItem in TimePicker from wheel to normal. If you want to apply the effect of infinite scrolling back, please refer to the following example.

Code
import { TimePicker } from '@fibrous/semi-ui-solid';

export default function TimePickerDemo() {
    return <TimePicker scrollItemProps={{ mode: 'wheel', cycled: true }} />;
}

Controlled Component

When using value instead of defaultValue, it works as a controlled component. value and onChange must be used together. Pass null for a controlled empty value.

Code
import { createSignal } from 'solid-js';
import { TimePicker } from '@fibrous/semi-ui-solid';

export default function TimePickerDemo() {
    const [value, setValue] = createSignal<Date | Date[] | null>(null);

    const onChange = (time: Date | Date[]) => {
        console.log(time);
        setValue(time);
    };

    return <TimePicker value={value()} onChange={onChange} />;
}

Different Format

The columns in the TimePicker float will follow format Change, when omitted format At some point, the corresponding column in the floating layer will also disappear.

NOTE: format Follow the date-fns v4 format Format. https://date-fns.org/docs/format

Code
import { TimePicker } from '@fibrous/semi-ui-solid';

export default function TimePickerDemo() {
    return <TimePicker format="HH:mm" defaultValue="10:24" />;
}

Disable Time Selection

Code
import { TimePicker } from '@fibrous/semi-ui-solid';

export default function TimePickerDemo() {
    return <TimePicker defaultValue="12:08:23" disabled />;
}

Set Step Length

Available Hour Step, Minute Step, Second Step Show the optional minutes and seconds by step.

Code
import { TimePicker } from '@fibrous/semi-ui-solid';

export default function TimePickerDemo() {
    return <TimePicker minuteStep={15} secondStep={10} />;
}

12-hour System

12-hour time selector, default format for a h:mm:ss, an incoming format The format must be in dateFns date formatWithin range.

For example, the default 12-hour format string is:a h:mm:ss, if passed in A h:mm:ss This will result in an inability to format correctly.



Code
import { TimePicker } from '@fibrous/semi-ui-solid';

export default function TimePickerDemo() {
    return (
        <div>
            <TimePicker use12Hours />
            <br />
            <br />
            <TimePicker use12Hours format="a h:mm" />
        </div>
    );
}

Time Range

Pass type = "timeRange" to enable time range selection.



Code
import { TimePicker } from '@fibrous/semi-ui-solid';

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>
    );
}

Disable left/right panels in Range mode (disabledTime)

When type="timeRange", you can use disabledTime(value, panelType) to apply different disabled rules for the left/right panel.

  • value: the currently selected times, the unpicked side undefined (e.g. [date, undefined]); an empty array when there is no value, and length 1 when value, defaultValue or the input holds a single time
  • panelType: 'left' | 'right', representing start/end panel

In the following demo, after selecting a start time, the right (end) panel will disable options earlier than the start time.

Code
import { TimePicker } from '@fibrous/semi-ui-solid';

export default function TimePickerDemo() {
    return (
        <TimePicker
            type="timeRange"
            disabledTime={(value, panelType) => {
                const start = value[0];
                if (panelType === 'right' && start) {
                    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 {};
            }}
        />
    );
}

Custom Trigger

By default we use the Input component as the trigger for the TimePicker component. You can customize this trigger by passing a triggerRender function. triggerRender is called only once and its fields are live: read them in the returned JSX (such as props.placeholder) to update them.

Code
import { createSignal, Show } from 'solid-js';
import * as dateFns from 'date-fns';
import { IconTimePicker } from '@fibrous/semi-icons-solid/lab';
import { Tag, TimePicker } from '@fibrous/semi-ui-solid';

export default function TimePickerDemo() {
    const formatToken = 'HH:mm:ss';
    const [time, setTime] = createSignal<Date | undefined>(new Date(2024, 4, 6, 12));

    return (
        <TimePicker
            value={time()}
            format={formatToken}
            onChange={(value: Date) => setTime(value)}
            triggerRender={(props) => (
                <Tag color="cyan" size="large" shape="circle" style={{ padding: '12px', 'padding-right': '16px', 'font-size': '14px' }} type="light" prefixIcon={<IconTimePicker />}>
                    <Show when={time()} fallback={props.placeholder}>
                        {(date) => dateFns.format(date(), formatToken)}
                    </Show>
                </Tag>
            )}
        />
    );
}

TimeZone Config

Semi All configuration about time zone is converged in ConfigProvider

Select Time Zone:


TimePicker:
Code
import { createSignal, For } from 'solid-js';
import { ConfigProvider, Select, TimePicker } from '@fibrous/semi-ui-solid';

const defaultTimestamp = 1581599305265;

const gmtList = Array.from({ length: 26 }, (_, i) => {
    const hourOffset = i - 11;
    const prefix = hourOffset >= 0 ? '+' : '-';
    return `GMT${prefix}${String(Math.abs(hourOffset)).padStart(2, '0')}:00`;
});

export default function TimePickerDemo() {
    const [timeZone, setTimeZone] = createSignal('GMT+08:00');

    return (
        <ConfigProvider timeZone={timeZone()}>
            <div style={{ width: '300px' }}>
                <h5 style={{ margin: '10px' }}>Select Time Zone:</h5>
                <Select aria-label="时区" placeholder="请选择时区" style={{ width: '300px' }} value={timeZone()} showClear onSelect={(value) => setTimeZone(String(value))}>
                    <For each={gmtList}>{(gmt) => <Select.Option value={gmt}>{gmt}</Select.Option>}</For>
                </Select>
                <br />
                <br />
                <h5 style={{ margin: '10px' }}>TimePicker:</h5>
                <TimePicker defaultValue={defaultTimestamp} onChange={(date: Date, dateString: string) => console.log('DatePicker changed: ', date, dateString)} />
            </div>
        </ConfigProvider>
    );
}

API Reference

Props

PropertyDescriptionTypeDefault
autoAdjustOverflowWhether to flip the popup automatically when it is clipped.booleantrue
borderlessBorderless mode.booleanfalse
classClass name of the root element.string-
clearIconCustom clear icon; effective when showClear is true.JSX.Element-
defaultOpenWhether the panel is open by default when uncontrolled.boolean-
defaultValueInitial time when uncontrolled, in the same forms as value; an array when type is timeRange.BaseValueType | BaseValueType[]-
disabledDisables all interaction.boolean-
disabledHoursReturns the hours that cannot be selected.Type() => number[]-
disabledMinutesReturns the minutes that cannot be selected for the selected hour.Type(selectedHour: number) => number[]-
disabledSecondsReturns the seconds that cannot be selected for the selected hour and minute.Type(selectedHour: number, selectedMinute: number) => number[]-
disabledTimeRange mode only: returns the disabled rules of the left and right panel from the current value and the panel. The unpicked side of value is undefined; a returned disabledHours, disabledMinutes or disabledSeconds overrides the prop of the same name, and one left out falls back to it. Ignored in single mode.Type( value: Date[], panelType: PanelType ) => { disabledHours?: () => number[]; disabledMinutes?: (selectedHour: number) => number[]; disabledSeconds?: (selectedHour: number, selectedMinute: number) => number[]; }-
focusOnOpenFocuses the input and selects its content once mounted, which opens the panel.booleanfalse
formatDisplay format, following date-fns format. Defaults to a h:mm:ss with use12Hours, else HH:mm:ss.string-
hideDisabledOptionsHides the options that cannot be selected.booleanfalse
hourStepInterval of the hour options; a positive integer.number-
inputReadOnlyMakes the input read-only, which avoids opening the virtual keyboard on mobile devices; the clear button stays usable.booleanfalse
inputStyleStyle of the input's root element; an object (kebab-case keys) or a string.JSX.CSSProperties | string-
insetLabelPrefix label shown inside the input.JSX.Element-
insetLabelIdid of the insetLabel, referenced through aria-labelledby and similar attributes.string-
maxLengthMaximum number of characters of the input, passed through to the input.number-
minLengthMinimum number of characters of the input, passed through to the input.number-
minuteStepInterval of the minute options; a positive integer.number-
motionWhether the popup animates.booleantrue
onBlurCallback when a click outside closes the panel, with the native event of that mousedown.Type(e: MouseEvent) => void-
onChangeCallback when the time changes. With type time it gets (Date, formatted string); with timeRange both are arrays.Type((value: Date, str: string) => void) | ((value: Date[], str: string[]) => void)-
onFocusCallback when the input gains focus.Type(e: FocusEvent) => void-
onOpenChangeCallback when the panel opens or closes, with whether it is open.Type(open: boolean) => void-
openWhether the panel is open; a controlled property, and undefined means uncontrolled.boolean-
panelFooterContent below the panel. In range mode an array, whose item i is used for panel i.JSX.Element-
panelHeaderContent above the panel. In range mode an array, whose item i is used for panel i.JSX.Element-
panelsRange mode only: item i is used as both the header and the footer of panel i, taking precedence over panelHeader and panelFooter. Ignored in single mode.JSX.Element[]-
placeholderHint shown without a value; defaults to the locale text of type.string-
popupClassNameClass name of the popup.string-
popupStyleStyle of the popup; an object (kebab-case keys) or a string.JSX.CSSProperties | string-
positionPopup position. Defaults to bottom when type is timeRange, and bottomLeft when it is time; bottomRight under RTL.Position-
prefixPrefix content of the input.JSX.Element-
prefixClsPrefix of the class names.string"semi-timepicker"
preventScrollWhether the browser should scroll the document to show the newly focused element; applies to the focus call inside the component.boolean-
rangeSeparatorSeparator between the two times of a range.string" ~ "
refReference to the root element; its focus() and blur() act on the input, and do nothing with a custom trigger.TypeRef<HTMLDivElement>-
scrollItemPropsProps passed to the columns; only cycled, mode, motion and transform, as on the ScrollItem of ScrollList.TypePick<ScrollItemProps, 'cycled' | 'mode' | 'motion' | 'transform'>-
secondStepInterval of the second options; a positive integer.number-
showClearWhether to show the clear button.booleantrue
sizeSize of the input: large, default or small.InputSize"default"
stopPropagationWhether to stop click events on the popup from bubbling.booleantrue
styleStyle of the root element; an object (kebab-case keys) or a string.JSX.CSSProperties | string-
timeZoneTime zone: a number is the hour offset from UTC, a string is an IANA time zone name; defaults to the timeZone of ConfigProvider.string | number-
triggerRenderRenders a custom trigger. It is called once, and its fields are live: read them in the returned JSX to update. The argument has disabled, placeholder, value (an array of Date, the unpicked side undefined), inputValue, componentProps (the props of the TimePicker, without ref) and onChange (updates the input text, as typing would).Type(props: TimePickerTriggerRenderProps) => JSX.Element-
typeSelection type: time for a single time, timeRange for a range.Type"time"
use12HoursUses the 12-hour clock; when true, format defaults to a h:mm:ss.booleanfalse
validateStatusValidation status, which only affects the display style.ValidateStatus-
valueCurrent time; an array when type is timeRange. null is a controlled empty value, and undefined means uncontrolled.BaseValueType | BaseValueType[] | null-

class, style and ref land on the root element, and the other undeclared attributes (id, title, aria-*, data-*, native events and so on) land on the inner input; with triggerRender those attributes land on no element. ref points to the root element, and its focus() and blur() act on the input; they do nothing with a custom trigger.

Accessibility

ARIA

  • The input has the role combobox with aria-haspopup="dialog", an aria-expanded that follows the panel, and aria-controls pointing at the panel
  • The hour, minute, second and AM/PM columns are listbox lists named "Hour", "Minute", "Second" and "AM/PM"

Keyboard and Focus

  • Focusing the input opens the panel; once it is open, Down Arrow moves focus into the first column
  • Each column is one Tab stop: Up Arrow, Down Arrow, Home, End and typed characters select its value
  • Esc closes the panel; focus inside the panel returns to the input

Content Guidelines

  • The time selector includes at least hours and minutes, such as: 11:30, which can be adapted to 12-hour or 24-hour format during localization
  • When 12-hour clock is selected, it needs to be used together with AM/PM

Design Tokens

These are the component's internal Sass variables and cannot be overridden. To customize styles, override the --semi-* CSS variables (see the Design Token page) or write unlayered CSS rules.
Sass variableValueUsage
$color-timePicker_range_picker_panel_split-bordervar(--semi-color-border)时间选择器菜单分割线颜色
$color-timePicker_range_panel-borderrgba(0, 0, 0, 0.1)时间选择器描边颜色
$width-timePicker_range_panel-border1px时间选择器菜单分割线宽度
$height-timePicker_panel_body252px时间选择器菜单高度
$width-timePicker_panel_list_ampm72px时间选择器菜单中列宽度 - 上午下午
$width-timePicker_panel_list_hour64px时间选择器菜单中列宽度 - 小时
$width-timePicker_panel_list_minute64px时间选择器菜单中列宽度 - 分钟
$width-timePicker_panel_list_second64px时间选择器菜单中列宽度 - 秒
$width-timePicker_range_panel_scrolllist_body-border2px时间范围选择器双排菜单中间分割线宽度
$spacing-timePicker_range_panel_scrolllist_header_body-padding0时间范围选择器菜单header与内容内边距
$radius-timePicker_range_panelvar(--semi-border-radius-medium)时间范围选择器菜单圆角
$radius-timePicker_inputvar(--semi-border-radius-small)时间范围选择器圆角
$shadow-timePicker_range_panelvar(--semi-shadow-elevated)时间范围选择器菜单阴影