代码演示
如何引入
import { DatePicker } from '@douyinfe/semi-ui';
基本使用
import React from 'react';
import { DatePicker } from '@douyinfe/semi-ui';
() => <DatePicker onChange={(date, dateString) => console.log(dateString)} />;
小尺寸
使用 density 可以控制日期面板的尺寸,compact
为小尺寸,default
为默认尺寸。v1.17.0 后支持。
import React from 'react';
import { DatePicker } from '@douyinfe/semi-ui';
function Demo() {
return (
<div>
<DatePicker type="dateTime" density="compact" />
<br />
<br />
<DatePicker type="dateRange" density="compact" style={{ width: 260 }} />
</div>
);
}
带内嵌标签
import React from 'react';
import { DatePicker } from '@douyinfe/semi-ui';
function Demo() {
return <DatePicker insetLabel="结束日期" style={{ width: 240 }} />;
}
多个日期选择
将 multiple
设为 true
,可以多选日期
import React from 'react';
import { DatePicker } from '@douyinfe/semi-ui';
() => <DatePicker multiple={true} style={{ width: 240 }} />;
日期与时间选择
将 type
设定为 dateTime
,可以选择日期时间。
同时,如果希望去掉 TimePicker 的无限循环滚动交互,可以通过 timePickerOpts 传入特定配置关闭。
import React from 'react';
import { DatePicker } from '@douyinfe/semi-ui';
() => (
<>
<h4>默认日期与时间选择</h4>
<DatePicker type="dateTime" />
<br />
<br />
<h4>关闭时间列表无限循环</h4>
<DatePicker
type="dateTime"
timePickerOpts={{
scrollItemProps: { cycled: false },
}}
/>
</>
);
日期范围选择
将 type
设定为 dateRange
,可以选择日期范围
import React from 'react';
import { DatePicker } from '@douyinfe/semi-ui';
() => <DatePicker type="dateRange" style={{ width: 260 }} onChange={console.log} />;
注意事项
type=dateRange 或 dateTimeRange 时,只有开始日期和结束日期都被选择后才会触发 onChange。
日期范围时间选择
将 type
设定为 dateTimeRange
, 可以选择日期时间范围
当未传入 defaultValue 或 value时,底部面板默认时间为当前时间。如果你有特殊需求(如指定默认时分秒),可以通过 defaultPickerValue 指定
import React from 'react';
import { DatePicker } from '@douyinfe/semi-ui';
() => (
<>
<DatePicker type="dateTimeRange" style={{ width: 400, marginBottom: 8 }} onChange={console.log} />
<DatePicker
type="dateTimeRange"
style={{ width: 400 }}
defaultPickerValue={[new Date('2022-08-08 00:00'), new Date('2022-08-09 12:00')]}
onChange={console.log}
/>
</>
);
内嵌输入框
使用 insetInput 可以控制日期面板是否展示内嵌输入框,默认为 false。v2.7.0 后支持。内嵌输入框适用于以下场景:
- 日期时间选择,可以直接通过内嵌输入框单独修改时间,无须通过滚轮选择时间
- 自定义触发器时 + 范围选择,使用内嵌输入框可以单独对开始和结束日期进行修改
insetInput 开启后包括以下功能:
- 点击触发器后,面板默认在原有位置弹出。你可以通过 position 自定义弹出位置
- 点击内嵌日期输入框,面板切换到日期选择;点击内嵌时间输入框,面板切换到时间选择
- 和外部的输入框一致,如果输入了非法日期,面板关闭后日期会回到之前的合法日期
注意事项
注意,开启后会对组件做一些调整和限制:
1. 触发器样式:未打开面板时触发器只读,打开时触发器禁用
2. 面板样式:type 包括 time 时,隐藏底部的切换按钮
3. 开启 insetInput 后 format 只支持 `dateFormat[ timeFormat]` 格式,使用其他格式会影响内嵌输入框 placeholder 和触发器文本的展示
import React from 'react';
import { DatePicker } from '@douyinfe/semi-ui';
function Demo() {
return (
<div>
<DatePicker type="date" insetInput />
<br />
<br />
<DatePicker type="dateTime" insetInput />
<br />
<br />
<DatePicker type="dateRange" insetInput style={{ width: 260 }} />
<br />
<br />
<DatePicker type="dateTimeRange" insetInput style={{ width: 400 }} />
<br />
<br />
<DatePicker type="month" placeholder="请选择年月" insetInput style={{ width: 140 }} />
<br />
<br />
<DatePicker type="date" position="bottomLeft" insetInput />
<br />
<br />
<DatePicker type="dateTime" format="yyyy-MM-dd HH:mm" insetInput />
</div>
);
}
同步切换双面板月份
version: >= 1.28.0
在范围选择的场景中, 开启 syncSwitchMonth
则允许双面板同步切换。默认为 false。
Note:点击年份按钮也会同步切换两个面板,从滚轮里面切换年月不会同步切换面板,这保证了用户选择非固定间隔月份的能力。
import React from 'react';
import { DatePicker } from '@douyinfe/semi-ui';
() => (
<DatePicker
// 双面板同步切换
syncSwitchMonth={true}
type="dateTimeRange"
style={{ width: 400 }}
/>
);
切换面板日期的回调
版本:>=1.28.0
onPanelChange
回调函数会在面板的月份或年份切换改变时被调用。
import React from 'react';
import { DatePicker } from '@douyinfe/semi-ui';
() => (
<DatePicker
syncSwitchMonth={true}
type="dateTimeRange"
style={{ width: 400 }}
onPanelChange={(date, dateString) => console.log(date, dateString)}
/>
);
周选择
dateRange 搭配 startDateOffset 和 endDateOffset 可以进行单击范围选择,如周选择、双周选择。v1.10.0 后支持。
import React from 'react';
import { DatePicker } from '@douyinfe/semi-ui';
function Demo() {
const handleChange = date => {
console.log('date changed', date);
};
return (
<div>
<h4>选择自然周</h4>
<DatePicker
style={{ width: 260 }}
type="dateRange"
weekStartsOn={1}
startDateOffset={date => dateFns.startOfWeek(date, { weekStartsOn: 1 })}
endDateOffset={date => dateFns.endOfWeek(date, { weekStartsOn: 1 })}
onChange={handleChange}
/>
<br />
<br />
<h4>选择双周</h4>
<DatePicker
style={{ width: 260 }}
type="dateRange"
weekStartsOn={1}
startDateOffset={date => dateFns.startOfWeek(date, { weekStartsOn: 1 })}
endDateOffset={date => dateFns.add(dateFns.endOfWeek(date, { weekStartsOn: 1 }), { days: 7 })}
onChange={handleChange}
/>
<br />
<br />
<h4>选择当前日和后6日</h4>
<DatePicker
style={{ width: 260 }}
type="dateRange"
weekStartsOn={1}
endDateOffset={date => dateFns.add(date, { days: 6 })}
onChange={handleChange}
/>
<br />
<br />
</div>
);
}
年月选择
版本: >= 0.21.0
将 type
设定为 month
,可以进行年月选择。
import React from 'react';
import { DatePicker } from '@douyinfe/semi-ui';
() => <DatePicker defaultValue={new Date()} type="month" style={{ width: 140 }} />;
确认日期时间选择
版本: >= 0.18.0
对于“日期时间”(type="dateTime")或“日期时间范围”(type="dateTimeRange")的选择,可以进行确认后才将值写入输入框内,你可以通过传递 needConfirm=true 来开启这种行为。
同时支持 “确认”(onConfirm) 和 “取消”(onCancel) 两个按钮的点击回调。
下面这个例子绑定了 onChange、onConfirm、onCancel 三种回调,你可以打开控制台查看打印信息的区别。
注意:开启确认选择时,需要点击取消按钮关闭面板,点击空白区域不再关闭面板(v2.2.0)
import React from 'react';
import { DatePicker } from '@douyinfe/semi-ui';
() => (
<DatePicker
type="dateTime"
needConfirm={true}
onConfirm={(...args) => {
console.log('Confirmed: ', ...args);
}}
onCancel={(...args) => {
console.log('Canceled: ', ...args);
}}
onChange={(...args) => {
console.log('Changed: ', ...args);
}}
/>
);
带有快捷方式的日期时间选择
通过 presets
设定快捷日期选择
import React from 'react';
import { DatePicker } from '@douyinfe/semi-ui';
() => {
const presets = [
{
text: 'Today',
start: new Date(),
end: new Date(),
},
{
text: 'Tomorrow',
start: new Date(new Date().valueOf() + 1000 * 3600 * 24),
end: new Date(new Date().valueOf() + 1000 * 3600 * 24),
},
];
return <DatePicker type="dateTime" presets={presets} presetPosition="left"/>;
};
渲染顶部/底部额外区域
通过 topSlot
和 bottomSlot
可以自定义渲染顶部和底部额外区域。
import React, { useState, useMemo } from 'react';
import { DatePicker, Typography, Tabs, TabPane, Space } from '@douyinfe/semi-ui';
import { IconBulb } from '@douyinfe/semi-icons';
function Demo() {
const { Text } = Typography;
const [activeTab, setActiveTab] = useState('1');
const [date, setDate] = useState();
const uedDisabledDate = currentDate => currentDate && currentDate.getDate() > 10 && currentDate.getDate() < 15;
const testDisabledDate = currentDate => currentDate && currentDate.getDate() > 15 && currentDate.getDate() < 25;
const handleTabChange = tab => {
setActiveTab(tab);
setDate();
};
const handleDateChange = value => {
setDate(value);
};
const disabledDate = useMemo(() => (activeTab === '1' ? uedDisabledDate : testDisabledDate), [activeTab]);
const TopSlot = function(props) {
const { style } = props;
return (
<Tabs
size="small"
onChange={handleTabChange}
activeKey={activeTab}
style={{ padding: '12px 20px 0', ...style }}
>
<TabPane tab="UED 排期" itemKey="1" />
<TabPane tab="测试排期" itemKey="2" />
</Tabs>
);
};
const BottomSlot = function(props) {
const { style } = props;
return (
<Space style={{ padding: '12px 20px', ...style }}>
<IconBulb style={{ color: 'rgba(var(--semi-amber-5), 1)' }} />
<Text strong style={{ color: 'var(--semi-color-text-2)' }}>
定版前请阅读
</Text>
<Text link={{ href: 'https://semi.design/', target: '_blank' }}>发版须知</Text>
</Space>
);
};
const MonthBottomSlot = function(props) {
const { style } = props;
return (
<Space style={{ padding: '12px 20px', ...style }}>
<IconBulb style={{ color: 'rgba(var(--semi-amber-5), 1)' }} />
<Text strong style={{ color: 'var(--semi-color-text-2)' }}>
请阅读
</Text>
<Text link={{ href: 'https://semi.design/', target: '_blank' }}>须知</Text>
</Space>
);
};
return (
<div>
<DatePicker
topSlot={<TopSlot />}
disabledDate={disabledDate}
value={date}
onChange={handleDateChange}
dropdownClassName="components-datepicker-demo-slot"
placeholder="请选择排期"
/>
<br />
<br />
<DatePicker bottomSlot={<BottomSlot />} placeholder="请选择发版时间" />
<br />
<br />
<DatePicker type="month" bottomSlot={<MonthBottomSlot />} placeholder="请选择年月" />
<br />
<br />
<DatePicker
topSlot={<TopSlot style={{ padding: '8px 12px 0' }} />}
bottomSlot={<BottomSlot style={{ padding: '8px 12px' }} />}
density="compact"
placeholder="小尺寸"
dropdownClassName="components-datepicker-demo-slot"
/>
<br />
<br />
<DatePicker type="dateTimeRange" bottomSlot={<BottomSlot />} style={{ width: 400 }} />
<br />
<br />
</div>
);
}
.components-datepicker-demo-slot {
.semi-tabs-content {
padding: 0;
}
.semi-tabs-bar-line.semi-tabs-bar-top {
border-bottom: none;
}
}
禁用日期选择
import React from 'react';
import { DatePicker } from '@douyinfe/semi-ui';
() => <DatePicker disabled type="dateTime" defaultValue={new Date()} />;
禁用部分日期或时间
传入 disabledDate
可以禁用指定日期,传入 disabledTime
可以禁用指定时间,配合 defaultPickerValue
可以指定面板打开时所处的年月。
disabledDate
和
disabledTime
,接受的入参都为当前日期,前者返回一个
boolean
值,后者返回一个
对象,将会透传给
TimePicker
组件。
注意事项
当你使用 timeZone 时,第一个参数为你选择的时区下时间(与onChange的第一个返回值类似)
import React from 'react';
import { DatePicker } from '@douyinfe/semi-ui';
import * as dateFns from 'date-fns';
import { range } from 'lodash-es';
class App extends React.Component {
constructor(props = {}) {
super(props);
this.today = () => new Date();
this.nextValidMonth = () => {
const nextValidDate = this.today();
nextValidDate.setMonth((nextValidDate.getMonth() + 1) % 12);
return nextValidDate;
};
this.disabledTime = date =>
dateFns.isToday(date)
? {
disabledHours: () => [17, 18],
disabledMinutes: hour => (19 === hour ? range(0, 10, 1) : []),
disabledSeconds: (hour, minute) => (hour === 20 && minute === 20 ? range(0, 20, 1) : []),
}
: null;
this.disabledTime2 = (date, panelType) => {
if (panelType === 'left') {
return { disabledHours: () => [17, 18] };
} else {
return { disabledHours: () => [12, 13, 14, 15, 16, 17, 18] };
}
};
this.disabledDate = date => {
const deadDate = this.today();
const month = deadDate.getMonth();
deadDate.setDate(28);
deadDate.setMonth((month + 1) % 12);
return date.getTime() < deadDate.getTime();
};
}
render() {
return (
<div>
<div>
<h4>禁用时间:禁用今天下午5-6点</h4>
<DatePicker type="dateTime" hideDisabledOptions={false} disabledTime={this.disabledTime} />
</div>
<div>
<h4>禁用时间:两个面板禁用不同时间</h4>
<DatePicker
type="dateTimeRange"
hideDisabledOptions={false}
disabledTime={this.disabledTime2}
style={{ width: 400 }}
/>
</div>
<div>
<h4>禁用日期:禁用下个月28号之前的所有日期</h4>
<DatePicker
type="dateTimeRange"
disabledDate={this.disabledDate}
defaultPickerValue={this.nextValidMonth()}
style={{ width: 400 }}
/>
</div>
</div>
);
}
}
在 type 包含 range 时,可以根据当前选择动态禁止日期。options 参数 1.9.0 后支持。
import React from 'react';
import { DatePicker } from '@douyinfe/semi-ui';
import * as dateFns from 'date-fns';
() => (
<div>
<h4>动态禁用日期:禁止选择之前的日期</h4>
<DatePicker
type={'dateRange'}
disabledDate={(date, options) => {
const { rangeStart } = options;
const startDate = dateFns.parseISO(rangeStart);
return dateFns.isBefore(date, startDate);
}}
style={{ width: 260 }}
/>
</div>
);
范围选择时,可以根据 focus 状态禁用日期。focus 状态通过 options 中的 rangeInputFocus 参数传递。
import React from 'react';
import { DatePicker } from '@douyinfe/semi-ui';
import * as dateFns from 'date-fns';
function App() {
const today = new Date();
const disabledDate = (date, options) => {
const { rangeInputFocus } = options;
const baseDate = dateFns.set(today, { hours: 0, minutes: 0, seconds: 0, milliseconds: 0 });
if (rangeInputFocus === 'rangeStart') {
const disabledStart = dateFns.subDays(baseDate, 2);
const disabledEnd = dateFns.addDays(baseDate, 2);
return disabledStart <= date && date <= disabledEnd;
} else if (rangeInputFocus === 'rangeEnd') {
const disabledStart = dateFns.subDays(baseDate, 3);
const disabledEnd = dateFns.addDays(baseDate, 3);
return disabledStart <= date && date <= disabledEnd;
} else {
return false;
}
};
return (
<div>
<h4>{`开始日期禁用今天前2日和后2日,结束日期禁用今天前3天和后3天`}</h4>
<DatePicker motion={false} type='dateRange' disabledDate={disabledDate} defaultPickerValue={today} />
</div>
);
}
自定义显示格式
可以通过 format
自定义显示格式
import React from 'react';
import { DatePicker } from '@douyinfe/semi-ui';
() => <DatePicker format="yyyy年MM月dd日 HH:mm" type="dateTime" defaultValue={new Date()} />;
自定义触发器
版本:>=0.34.0
默认情况下我们使用 Input
组件作为 DatePicker
组件的触发器,通过传递 triggerRender
方法你可以自定义这个触发器。
自定义触发器是对触发器的完全自定义,默认的清除按钮将不生效,如果你需要清除功能,请自定义一个清除按钮。
import React, { useState, useCallback, useMemo } from 'react';
import * as dateFns from 'date-fns';
import { IconClose, IconChevronDown } from '@douyinfe/semi-icons';
import { DatePicker, Button } from '@douyinfe/semi-ui';
function Demo() {
const [date, setDate] = useState(new Date());
const formatToken = 'yyyy-MM-dd';
const onChange = useCallback(date => {
setDate(date);
}, []);
const onClear = useCallback(e => {
e && e.stopPropagation();
setDate(null);
}, []);
const closeIcon = useMemo(() => {
return date ? <IconClose onClick={onClear} /> : <IconChevronDown />;
}, [date]);
return (
<DatePicker
onChange={onChange}
value={date}
format={formatToken}
triggerRender={({ placeholder }) => (
<Button theme={'light'} icon={closeIcon} iconPosition={'right'}>
{(date && dateFns.format(date, formatToken)) || placeholder}
</Button>
)}
/>
);
}
注意事项
范围选择时,面板打开后默认选择的日期为开始日期,选择后会切到结束日期选择。面板关闭后焦点会重置。
我们建议提供一个清除按钮,当你给 DatePicker 传入空值时,DatePicker 内部也会重置焦点。这样用户可以在清除后重新选择日期范围。(from v2.15)
import React, { useState, useCallback, useMemo } from 'react';
import { DatePicker, Button, Icon } from '@douyinfe/semi-ui';
import { IconClose, IconChevronDown } from '@douyinfe/semi-icons';
function Demo() {
const [date, setDate] = useState();
const formatToken = 'yyyy-MM-dd HH:mm:ss';
const onChange = useCallback(date => {
setDate(date);
console.log(date);
}, []);
const onClear = useCallback(e => {
e && e.stopPropagation();
setDate();
}, []);
const closeIcon = useMemo(() => {
return date ? <IconClose onClick={onClear} /> : <IconChevronDown />;
}, [date]);
const triggerContent = (placeholder) => {
if (Array.isArray(date) && date.length) {
return `${dateFns.format(date[0], formatToken)} ~ ${dateFns.format(date[1], formatToken)}`;
} else {
return '请选择日期时间范围';
}
};
return (
<DatePicker
type='dateTimeRange'
onChange={onChange}
value={date}
triggerRender={({ placeholder }) => (
<Button theme={'light'} icon={closeIcon} iconPosition={'right'}>
{triggerContent(placeholder)}
</Button>
)}
/>
);
}
自定义日期显示内容
版本:>=1.4.0
renderDate: (dayNumber: number, fullDate: string) => ReactNode
,自定义日期内容。
dayNumber
:当前日。如 13
。fullDate
:当前日的完整日期。如 2020-08-13
。
import React from 'react';
import { DatePicker, Tooltip } from '@douyinfe/semi-ui';
function Demo() {
const dateStyle = {
width: '100%',
height: '100%',
border: '1px solid var(--semi-color-primary)',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
};
const renderDate = (dayNumber, fullDate) => {
if (dayNumber === 1) {
return (
<Tooltip content={'Always Day 1'}>
<div style={dateStyle}>{dayNumber}</div>
</Tooltip>
);
}
return dayNumber;
};
return <DatePicker renderDate={renderDate} />;
}
自定义日期格子渲染
版本:>=1.4.0
renderFullDate: (dayNumber: number, fullDate: string, dayStatus: object) => ReactNode
, 自定义日期格子的渲染内容。
dayStatus
表示当前格子的状态,包括的 key
有:
type DayStatusType = {
isToday?: boolean; // 当前日
isSelected?: boolean; // 被选中
isDisabled?: boolean; // 被禁用
isSelectedStart?: boolean; // 选中开始
isSelectedEnd?: boolean; // 选中结束
isInRange?: boolean; // 范围选中日期内
isHover?: boolean; // 日期在选择项和hover日期之间
isOffsetRangeStart?: boolean; // 周选择开始
isOffsetRangeEnd?: boolean; // 周选择结束
isHoverInOffsetRange?: boolean; // hover在周选择内
};
import React from 'react';
import { DatePicker } from '@douyinfe/semi-ui';
import classNames from 'classnames';
function Demo() {
const renderFullDate = (dayNumber, fullDate, dayStatus) => {
const { isInRange, isHover, isSelected, isSelectedStart, isSelectedEnd } = dayStatus;
const prefix = 'components-datepicker-demo';
const dateCls = classNames({
[`${prefix}-day-inrange`]: isInRange,
[`${prefix}-day-hover`]: isHover,
[`${prefix}-day-selected`]: isSelected,
[`${prefix}-day-selected-start`]: isSelectedStart,
[`${prefix}-day-selected-end`]: isSelectedEnd,
});
const dayStyle = {
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
width: '80%',
height: '80%',
borderRadius: 'var(--semi-border-radius-circle)',
};
return (
<div style={dayStyle} className={dateCls}>
{dayNumber}
</div>
);
};
return <DatePicker style={{ width: 260 }} type={'dateRange'} renderFullDate={renderFullDate} />;
}
.components-datepicker-demo-day-inrange,
.components-datepicker-demo-day-hover {
background: var(--semi-color-primary-light-hover);
}
.components-datepicker-demo-day-selected,
.components-datepicker-demo-day-selected-start,
.components-datepicker-demo-day-selected-end {
color: var(--semi-color-bg-2);
background: var(--semi-color-primary);
}