add date-picker

pull/165/head
tjz 2018-03-15 21:40:34 +08:00
parent fa0829e256
commit 4b2f56a7a9
31 changed files with 2895 additions and 1 deletions

View File

@ -0,0 +1,366 @@
/* tslint:disable jsx-no-multiline-js */
import * as React from 'react';
import * as moment from 'moment';
import RangeCalendar from 'rc-calendar/lib/RangeCalendar';
import RcDatePicker from 'rc-calendar/lib/Picker';
import classNames from 'classnames';
import Icon from '../icon';
import warning from '../_util/warning';
import callMoment from '../_util/callMoment';
import { RangePickerValue, RangePickerPresetRange } from './interface';
export interface RangePickerState {
value?: RangePickerValue;
showDate?: RangePickerValue;
open?: boolean;
hoverValue?: RangePickerValue;
}
function getShowDateFromValue(value: RangePickerValue) {
const [start, end] = value;
// value could be an empty array, then we should not reset showDate
if (!start && !end) {
return;
}
const newEnd = end && end.isSame(start, 'month') ? end.clone().add(1, 'month') : end;
return [start, newEnd] as RangePickerValue;
}
function formatValue(value: moment.Moment | undefined, format: string): string {
return (value && value.format(format)) || '';
}
function pickerValueAdapter(value?: moment.Moment | RangePickerValue): RangePickerValue | undefined {
if (!value) {
return;
}
if (Array.isArray(value)) {
return value;
}
return [value, value.clone().add(1, 'month')];
}
function isEmptyArray(arr: any) {
if (Array.isArray(arr)) {
return arr.length === 0 || arr.every(i => !i);
}
return false;
}
function fixLocale(value: RangePickerValue | undefined, localeCode: string) {
if (!localeCode) {
return;
}
if (!value || value.length === 0) {
return;
}
if (value[0]) {
value[0]!.locale(localeCode);
}
if (value[1]) {
value[1]!.locale(localeCode);
}
}
export default class RangePicker extends React.Component<any, RangePickerState> {
static defaultProps = {
prefixCls: 'ant-calendar',
allowClear: true,
showToday: false,
};
private picker: HTMLSpanElement;
constructor(props: any) {
super(props);
const value = props.value || props.defaultValue || [];
if (
value[0] && !moment.isMoment(value[0]) ||
value[1] && !moment.isMoment(value[1])
) {
throw new Error(
'The value/defaultValue of RangePicker must be a moment object array after `antd@2.0`, ' +
'see: https://u.ant.design/date-picker-value',
);
}
const pickerValue = !value || isEmptyArray(value) ? props.defaultPickerValue : value;
this.state = {
value,
showDate: pickerValueAdapter(pickerValue || callMoment(moment)),
open: props.open,
hoverValue: [],
};
}
componentWillReceiveProps(nextProps: any) {
if ('value' in nextProps) {
const state = this.state;
const value = nextProps.value || [];
this.setState({
value,
showDate: getShowDateFromValue(value) || state.showDate,
});
}
if ('open' in nextProps) {
this.setState({
open: nextProps.open,
});
}
}
clearSelection = (e: React.MouseEvent<HTMLElement>) => {
e.preventDefault();
e.stopPropagation();
this.setState({ value: [] });
this.handleChange([]);
}
clearHoverValue = () => this.setState({ hoverValue: [] });
handleChange = (value: RangePickerValue) => {
const props = this.props;
if (!('value' in props)) {
this.setState(({ showDate }) => ({
value,
showDate: getShowDateFromValue(value) || showDate,
}));
}
props.onChange(value, [
formatValue(value[0], props.format),
formatValue(value[1], props.format),
]);
}
handleOpenChange = (open: boolean) => {
if (!('open' in this.props)) {
this.setState({ open });
}
if (open === false) {
this.clearHoverValue();
}
const { onOpenChange } = this.props;
if (onOpenChange) {
onOpenChange(open);
}
}
handleShowDateChange = (showDate: RangePickerValue) => this.setState({ showDate });
handleHoverChange = (hoverValue: any) => this.setState({ hoverValue });
handleRangeMouseLeave = () => {
if (this.state.open) {
this.clearHoverValue();
}
}
handleCalendarInputSelect = (value: RangePickerValue) => {
if (!value[0]) {
return;
}
this.setState(({ showDate }) => ({
value,
showDate: getShowDateFromValue(value) || showDate,
}));
}
handleRangeClick = (value: RangePickerPresetRange) => {
if (typeof value === 'function') {
value = value();
}
this.setValue(value, true);
const { onOk } = this.props;
if (onOk) {
onOk(value);
}
}
setValue(value: RangePickerValue, hidePanel?: boolean) {
this.handleChange(value);
if ((hidePanel || !this.props.showTime) && !('open' in this.props)) {
this.setState({ open: false });
}
}
focus() {
this.picker.focus();
}
blur() {
this.picker.blur();
}
savePicker = (node: HTMLSpanElement) => {
this.picker = node;
}
renderFooter = (...args: any[]) => {
const { prefixCls, ranges, renderExtraFooter } = this.props;
if (!ranges && !renderExtraFooter) {
return null;
}
const customFooter = renderExtraFooter ? (
<div className={`${prefixCls}-footer-extra`} key="extra">
{renderExtraFooter(...args)}
</div>
) : null;
const operations = Object.keys(ranges || {}).map((range) => {
const value = ranges[range];
return (
<a
key={range}
onClick={() => this.handleRangeClick(value)}
onMouseEnter={() => this.setState({ hoverValue: value })}
onMouseLeave={this.handleRangeMouseLeave}
>
{range}
</a>
);
});
const rangeNode = (
<div className={`${prefixCls}-footer-extra ${prefixCls}-range-quick-selector`} key="range">
{operations}
</div>
);
return [rangeNode, customFooter];
}
render() {
const { state, props } = this;
const { value, showDate, hoverValue, open } = state;
const {
prefixCls, popupStyle, style,
disabledDate, disabledTime,
showTime, showToday,
ranges, onOk, locale, localeCode, format,
dateRender, onCalendarChange,
} = props;
fixLocale(value, localeCode);
fixLocale(showDate, localeCode);
warning(!('onOK' in props), 'It should be `RangePicker[onOk]`, instead of `onOK`!');
const calendarClassName = classNames({
[`${prefixCls}-time`]: showTime,
[`${prefixCls}-range-with-ranges`]: ranges,
});
// 需要选择时间时,点击 ok 时才触发 onChange
let pickerChangeHandler = {
onChange: this.handleChange,
};
let calendarProps: any = {
onOk: this.handleChange,
};
if (props.timePicker) {
pickerChangeHandler.onChange = changedValue => this.handleChange(changedValue);
} else {
calendarProps = {};
}
if ('mode' in props) {
calendarProps.mode = props.mode;
}
const startPlaceholder = ('placeholder' in props)
? props.placeholder[0] : locale.lang.rangePlaceholder[0];
const endPlaceholder = ('placeholder' in props)
? props.placeholder[1] : locale.lang.rangePlaceholder[1];
const calendar = (
<RangeCalendar
{...calendarProps}
onChange={onCalendarChange}
format={format}
prefixCls={prefixCls}
className={calendarClassName}
renderFooter={this.renderFooter}
timePicker={props.timePicker}
disabledDate={disabledDate}
disabledTime={disabledTime}
dateInputPlaceholder={[startPlaceholder, endPlaceholder]}
locale={locale.lang}
onOk={onOk}
dateRender={dateRender}
value={showDate}
onValueChange={this.handleShowDateChange}
hoverValue={hoverValue}
onHoverChange={this.handleHoverChange}
onPanelChange={props.onPanelChange}
showToday={showToday}
onInputSelect={this.handleCalendarInputSelect}
/>
);
// default width for showTime
const pickerStyle = {} as any;
if (props.showTime) {
pickerStyle.width = (style && style.width) || 350;
}
const clearIcon = (!props.disabled && props.allowClear && value && (value[0] || value[1])) ? (
<Icon
type="cross-circle"
className={`${prefixCls}-picker-clear`}
onClick={this.clearSelection}
/>
) : null;
const input = ({ value: inputValue }: { value: any }) => {
const start = inputValue[0];
const end = inputValue[1];
return (
<span className={props.pickerInputClass}>
<input
disabled={props.disabled}
readOnly
value={(start && start.format(props.format)) || ''}
placeholder={startPlaceholder}
className={`${prefixCls}-range-picker-input`}
tabIndex={-1}
/>
<span className={`${prefixCls}-range-picker-separator`}> ~ </span>
<input
disabled={props.disabled}
readOnly
value={(end && end.format(props.format)) || ''}
placeholder={endPlaceholder}
className={`${prefixCls}-range-picker-input`}
tabIndex={-1}
/>
{clearIcon}
<span className={`${prefixCls}-picker-icon`} />
</span>
);
};
return (
<span
ref={this.savePicker}
id={props.id}
className={classNames(props.className, props.pickerClass)}
style={{ ...style, ...pickerStyle }}
tabIndex={props.disabled ? -1 : 0}
onFocus={props.onFocus}
onBlur={props.onBlur}
>
<RcDatePicker
{...props}
{...pickerChangeHandler}
calendar={calendar}
value={value}
open={open}
onOpenChange={this.handleOpenChange}
prefixCls={`${prefixCls}-picker-container`}
style={popupStyle}
>
{input}
</RcDatePicker>
</span>
);
}
}

View File

@ -0,0 +1,158 @@
<script>
import * as moment from 'moment'
import Calendar from '../vc-calendar'
import VcDatePicker from '../vc-calendar/src/Picker'
import Icon from '../icon'
import { hasProp, getOptionProps } from '../_util/props-util'
import BaseMixin from '../_util/BaseMixin'
function formatValue (value, format) {
return (value && value.format(format)) || ''
}
function noop () {}
export default {
// static defaultProps = {
// format: 'YYYY-wo',
// allowClear: true,
// };
// private input: any;
mixins: [BaseMixin],
data () {
const value = this.value || this.defaultValue
if (value && !moment.isMoment(value)) {
throw new Error(
'The value/defaultValue of DatePicker or MonthPicker must be ' +
'a moment object',
)
}
return {
sValue: value,
}
},
watch: {
value (val) {
this.setState({ sValue: val })
},
},
methods: {
weekDateRender (current) {
const selectedValue = this.sValue
const { prefixCls } = this
if (selectedValue &&
current.year() === selectedValue.year() &&
current.week() === selectedValue.week()) {
return (
<div class={`${prefixCls}-selected-day`}>
<div class={`${prefixCls}-date`}>
{current.date()}
</div>
</div>
)
}
return (
<div class={`${prefixCls}-calendar-date`}>
{current.date()}
</div>
)
},
handleChange (value) {
if (!hasProp(this, 'value')) {
this.setState({ sValue: value })
}
this.$emit('change', value, formatValue(value, this.format))
},
clearSelection (e) {
e.preventDefault()
e.stopPropagation()
this.handleChange(null)
},
focus () {
this.$refs.input.focus()
},
blur () {
this.$refs.input.blur()
},
},
render () {
const props = getOptionProps(this)
const {
prefixCls, disabled, pickerClass, popupStyle,
pickerInputClass, format, allowClear, locale, localeCode, disabledDate,
sValue: pickerValue, $listeners,
} = this
const { focus = noop, blur = noop } = $listeners
if (pickerValue && localeCode) {
pickerValue.locale(localeCode)
}
const placeholder = hasProp(this, 'placeholder') ? this.placeholder : locale.lang.placeholder
const calendar = (
<Calendar
showWeekNumber
dateRender={this.weekDateRender}
prefixCls={prefixCls}
format={format}
locale={locale.lang}
showDateInput={false}
showToday={false}
disabledDate={disabledDate}
/>
)
const clearIcon = (!disabled && allowClear && this.sValue) ? (
<Icon
type='cross-circle'
class={`${prefixCls}-picker-clear`}
onClick={this.clearSelection}
/>
) : null
const input = ({ value }) => {
return (
<span>
<input
ref='input'
disabled={disabled}
readOnly
value={(value && value.format(format)) || ''}
placeholder={placeholder}
class={pickerInputClass}
onFocus={focus}
onBlur={blur}
/>
{clearIcon}
<span class={`${prefixCls}-picker-icon`} />
</span>
)
}
const vcDatePickerProps = {
props: {
...props,
calendar,
prefixCls: `${prefixCls}-picker-container`,
value: pickerValue,
},
on: {
...$listeners,
change: this.handleChange,
},
style: popupStyle,
}
return (
<span class={pickerClass}>
<VcDatePicker
{...vcDatePickerProps}
>
{input}
</VcDatePicker>
</span>
)
},
}
</script>

View File

@ -0,0 +1,196 @@
import * as React from 'react';
import * as moment from 'moment';
import MonthCalendar from 'rc-calendar/lib/MonthCalendar';
import RcDatePicker from 'rc-calendar/lib/Picker';
import classNames from 'classnames';
import omit from 'omit.js';
import Icon from '../icon';
import warning from '../_util/warning';
import callMoment from '../_util/callMoment';
export interface PickerProps {
value?: moment.Moment;
prefixCls: string;
}
export default function createPicker(TheCalendar: React.ComponentClass): any {
return class CalenderWrapper extends React.Component<any, any> {
static defaultProps = {
prefixCls: 'ant-calendar',
allowClear: true,
showToday: true,
};
private input: any;
constructor(props: any) {
super(props);
const value = props.value || props.defaultValue;
if (value && !moment.isMoment(value)) {
throw new Error(
'The value/defaultValue of DatePicker or MonthPicker must be ' +
'a moment object after `antd@2.0`, see: https://u.ant.design/date-picker-value',
);
}
this.state = {
value,
showDate: value,
};
}
componentWillReceiveProps(nextProps: PickerProps) {
if ('value' in nextProps) {
this.setState({
value: nextProps.value,
showDate: nextProps.value,
});
}
}
renderFooter = (...args: any[]) => {
const { prefixCls, renderExtraFooter } = this.props;
return renderExtraFooter ? (
<div className={`${prefixCls}-footer-extra`}>
{renderExtraFooter(...args)}
</div>
) : null;
}
clearSelection = (e: React.MouseEvent<HTMLElement>) => {
e.preventDefault();
e.stopPropagation();
this.handleChange(null);
}
handleChange = (value: moment.Moment | null) => {
const props = this.props;
if (!('value' in props)) {
this.setState({
value,
showDate: value,
});
}
props.onChange(value, (value && value.format(props.format)) || '');
}
handleCalendarChange = (value: moment.Moment) => {
this.setState({ showDate: value });
}
focus() {
this.input.focus();
}
blur() {
this.input.blur();
}
saveInput = (node: any) => {
this.input = node;
}
render() {
const { value, showDate } = this.state;
const props = omit(this.props, ['onChange']);
const { prefixCls, locale, localeCode } = props;
const placeholder = ('placeholder' in props)
? props.placeholder : locale.lang.placeholder;
const disabledTime = props.showTime ? props.disabledTime : null;
const calendarClassName = classNames({
[`${prefixCls}-time`]: props.showTime,
[`${prefixCls}-month`]: MonthCalendar === TheCalendar,
});
if (value && localeCode) {
value.locale(localeCode);
}
let pickerProps: Object = {};
let calendarProps: any = {};
if (props.showTime) {
calendarProps = {
// fix https://github.com/ant-design/ant-design/issues/1902
onSelect: this.handleChange,
};
} else {
pickerProps = {
onChange: this.handleChange,
};
}
if ('mode' in props) {
calendarProps.mode = props.mode;
}
warning(!('onOK' in props), 'It should be `DatePicker[onOk]` or `MonthPicker[onOk]`, instead of `onOK`!');
const calendar = (
<TheCalendar
{...calendarProps}
disabledDate={props.disabledDate}
disabledTime={disabledTime}
locale={locale.lang}
timePicker={props.timePicker}
defaultValue={props.defaultPickerValue || callMoment(moment)}
dateInputPlaceholder={placeholder}
prefixCls={prefixCls}
className={calendarClassName}
onOk={props.onOk}
dateRender={props.dateRender}
format={props.format}
showToday={props.showToday}
monthCellContentRender={props.monthCellContentRender}
renderFooter={this.renderFooter}
onPanelChange={props.onPanelChange}
onChange={this.handleCalendarChange}
value={showDate}
/>
);
const clearIcon = (!props.disabled && props.allowClear && value) ? (
<Icon
type="cross-circle"
className={`${prefixCls}-picker-clear`}
onClick={this.clearSelection}
/>
) : null;
const input = ({ value: inputValue }: { value: moment.Moment | null }) => (
<div>
<input
ref={this.saveInput}
disabled={props.disabled}
readOnly
value={(inputValue && inputValue.format(props.format)) || ''}
placeholder={placeholder}
className={props.pickerInputClass}
/>
{clearIcon}
<span className={`${prefixCls}-picker-icon`} />
</div>
);
return (
<span
id={props.id}
className={classNames(props.className, props.pickerClass)}
style={props.style}
onFocus={props.onFocus}
onBlur={props.onBlur}
>
<RcDatePicker
{...props}
{...pickerProps}
calendar={calendar}
value={value}
prefixCls={`${prefixCls}-picker-container`}
style={props.popupStyle}
>
{input}
</RcDatePicker>
</span>
);
}
};
}

View File

@ -0,0 +1,35 @@
---
order: 0
title:
zh-CN: 基本
en-US: Basic
---
## zh-CN
最简单的用法,在浮层中可以选择或者输入日期。
## en-US
Basic use case. Users can select or input a date in panel.
````jsx
import { DatePicker } from 'antd';
const { MonthPicker, RangePicker, WeekPicker } = DatePicker;
function onChange(date, dateString) {
console.log(date, dateString);
}
ReactDOM.render(
<div>
<DatePicker onChange={onChange} />
<br />
<MonthPicker onChange={onChange} placeholder="Select month" />
<br />
<RangePicker onChange={onChange} />
<br />
<WeekPicker onChange={onChange} placeholder="Select week" />
</div>
, mountNode);
````

View File

@ -0,0 +1,52 @@
---
order: 12
title:
zh-CN: 定制日期单元格
en-US: Customized Date Rendering
---
## zh-CN
使用 `dateRender` 可以自定义日期单元格的内容和样式。
## en-US
We can customize the rendering of date cells in the calendar by providing a `dateRender` function to `DatePicker`.
````jsx
import { DatePicker } from 'antd';
const { RangePicker } = DatePicker;
ReactDOM.render(
<div>
<DatePicker
dateRender={(current) => {
const style = {};
if (current.date() === 1) {
style.border = '1px solid #1890ff';
style.borderRadius = '50%';
}
return (
<div className="ant-calendar-date" style={style}>
{current.date()}
</div>
);
}}
/>
<RangePicker
dateRender={(current) => {
const style = {};
if (current.date() === 1) {
style.border = '1px solid #1890ff';
style.borderRadius = '50%';
}
return (
<div className="ant-calendar-date" style={style}>
{current.date()}
</div>
);
}}
/>
</div>
, mountNode);
````

View File

@ -0,0 +1,80 @@
---
order: 6
title:
zh-CN: 不可选择日期和时间
en-US: Disabled Date & Time
---
## zh-CN
可用 `disabledDate``disabledTime` 分别禁止选择部分日期和时间,其中 `disabledTime` 需要和 `showTime` 一起使用。
## en-US
Disabled part of dates and time by `disabledDate` and `disabledTime` respectively, and `disabledTime` only works with `showTime`.
````jsx
import moment from 'moment';
import { DatePicker } from 'antd';
const { MonthPicker, RangePicker } = DatePicker;
function range(start, end) {
const result = [];
for (let i = start; i < end; i++) {
result.push(i);
}
return result;
}
function disabledDate(current) {
// Can not select days before today and today
return current && current < moment().endOf('day');
}
function disabledDateTime() {
return {
disabledHours: () => range(0, 24).splice(4, 20),
disabledMinutes: () => range(30, 60),
disabledSeconds: () => [55, 56],
};
}
function disabledRangeTime(_, type) {
if (type === 'start') {
return {
disabledHours: () => range(0, 60).splice(4, 20),
disabledMinutes: () => range(30, 60),
disabledSeconds: () => [55, 56],
};
}
return {
disabledHours: () => range(0, 60).splice(20, 4),
disabledMinutes: () => range(0, 31),
disabledSeconds: () => [55, 56],
};
}
ReactDOM.render(
<div>
<DatePicker
format="YYYY-MM-DD HH:mm:ss"
disabledDate={disabledDate}
disabledTime={disabledDateTime}
showTime={{ defaultValue: moment('00:00:00', 'HH:mm:ss') }}
/>
<br />
<MonthPicker disabledDate={disabledDate} placeholder="Select month" />
<br />
<RangePicker
disabledDate={disabledDate}
disabledTime={disabledRangeTime}
showTime={{
hideDisabledOptions: true,
defaultValue: [moment('00:00:00', 'HH:mm:ss'), moment('11:59:59', 'HH:mm:ss')],
}}
format="YYYY-MM-DD HH:mm:ss"
/>
</div>,
mountNode
);
````

View File

@ -0,0 +1,34 @@
---
order: 4
title:
zh-CN: 禁用
en-US: Disabled
---
## zh-CN
选择框的不可用状态。
## en-US
A disabled state of the `DatePicker`.
````jsx
import { DatePicker } from 'antd';
import moment from 'moment';
const { MonthPicker, RangePicker } = DatePicker;
const dateFormat = 'YYYY-MM-DD';
ReactDOM.render(
<div>
<DatePicker defaultValue={moment('2015-06-06', dateFormat)} disabled />
<br />
<MonthPicker defaultValue={moment('2015-06', 'YYYY-MM')} disabled />
<br />
<RangePicker
defaultValue={[moment('2015-06-06', dateFormat), moment('2015-06-06', dateFormat)]}
disabled
/>
</div>
, mountNode);
````

View File

@ -0,0 +1,29 @@
---
order: 10
title:
zh-CN: 额外的页脚
en-US: Extra Footer
---
## zh-CN
在浮层中加入额外的页脚,以满足某些定制信息的需求。
## en-US
Render extra footer in panel for customized requirements.
````jsx
import { DatePicker } from 'antd';
const { RangePicker, MonthPicker } = DatePicker;
ReactDOM.render(
<div>
<DatePicker renderExtraFooter={() => 'extra footer'} />
<DatePicker renderExtraFooter={() => 'extra footer'} showTime />
<RangePicker renderExtraFooter={() => 'extra footer'} />
<RangePicker renderExtraFooter={() => 'extra footer'} showTime />
<MonthPicker renderExtraFooter={() => 'extra footer'} placeholder="Select month" />
</div>
, mountNode);
````

View File

@ -0,0 +1,35 @@
---
order: 1
title:
zh-CN: 日期格式
en-US: Date Format
---
## zh-CN
使用 `format` 属性,可以自定义日期显示格式。
## en-US
We can set the date format by `format`.
````jsx
import { DatePicker } from 'antd';
import moment from 'moment';
const { MonthPicker, RangePicker } = DatePicker;
const dateFormat = 'YYYY/MM/DD';
const monthFormat = 'YYYY/MM';
ReactDOM.render(
<div>
<DatePicker defaultValue={moment('2015/01/01', dateFormat)} format={dateFormat} />
<br />
<MonthPicker defaultValue={moment('2015/01', monthFormat)} format={monthFormat} />
<br />
<RangePicker
defaultValue={[moment('2015/01/01', dateFormat), moment('2015/01/01', dateFormat)]}
format={dateFormat}
/>
</div>
, mountNode);
````

View File

@ -0,0 +1,82 @@
---
order: 11
title:
zh-CN: 受控面板
en-US: Controlled Panels
---
## zh-CN
通过组合 `mode``onPanelChange` 控制要展示的面板。
## en-US
Determing which panel to show with `mode` and `onPanelChange`.
````jsx
import { DatePicker } from 'antd';
const { RangePicker } = DatePicker;
class ControlledDatePicker extends React.Component {
state = { mode: 'time' };
handleOpenChange = (open) => {
if (open) {
this.setState({ mode: 'time' });
}
}
handlePanelChange = (value, mode) => {
this.setState({ mode });
}
render() {
return (
<DatePicker
mode={this.state.mode}
showTime
onOpenChange={this.handleOpenChange}
onPanelChange={this.handlePanelChange}
/>
);
}
}
class ControlledRangePicker extends React.Component {
state = {
mode: ['month', 'month'],
value: [],
};
handlePanelChange = (value, mode) => {
this.setState({
value,
mode: [
mode[0] === 'date' ? 'month' : mode[0],
mode[1] === 'date' ? 'month' : mode[1],
],
});
}
render() {
const { value, mode } = this.state;
return (
<RangePicker
placeholder={['Start month', 'End month']}
format="YYYY-MM"
value={value}
mode={mode}
onPanelChange={this.handlePanelChange}
/>
);
}
}
ReactDOM.render(
<div>
<ControlledDatePicker />
<br />
<ControlledRangePicker />
</div>
, mountNode);
````

View File

@ -0,0 +1,42 @@
---
order: 8
title:
zh-CN: 预设范围
en-US: Presetted Ranges
---
## zh-CN
RangePicker 可以设置常用的 预设范围 提高用户体验。
## en-US
We can set presetted ranges to RangePicker to improve user experience.
````jsx
import { DatePicker } from 'antd';
import moment from 'moment';
const RangePicker = DatePicker.RangePicker;
function onChange(dates, dateStrings) {
console.log('From: ', dates[0], ', to: ', dates[1]);
console.log('From: ', dateStrings[0], ', to: ', dateStrings[1]);
}
ReactDOM.render(
<div>
<RangePicker
ranges={{ Today: [moment(), moment()], 'This Month': [moment(), moment().endOf('month')] }}
onChange={onChange}
/>
<br />
<RangePicker
ranges={{ Today: [moment(), moment()], 'This Month': [moment(), moment().endOf('month')] }}
showTime
format="YYYY/MM/DD HH:mm:ss"
onChange={onChange}
/>
</div>,
mountNode
);
````

View File

@ -0,0 +1,53 @@
---
order: 2
title:
zh-CN: 三种大小
en-US: Three Sizes
---
## zh-CN
三种大小的输入框,若不设置,则为 `default`
## en-US
The input box comes in three sizes. `default` will be used if `size` is omitted.
````jsx
import { DatePicker, Radio } from 'antd';
const { MonthPicker, RangePicker, WeekPicker } = DatePicker;
class PickerSizesDemo extends React.Component {
state = {
size: 'default',
};
handleSizeChange = (e) => {
this.setState({ size: e.target.value });
}
render() {
const { size } = this.state;
return (
<div>
<Radio.Group value={size} onChange={this.handleSizeChange}>
<Radio.Button value="large">Large</Radio.Button>
<Radio.Button value="default">Default</Radio.Button>
<Radio.Button value="small">Small</Radio.Button>
</Radio.Group>
<br /><br />
<DatePicker size={size} />
<br />
<MonthPicker size={size} placeholder="Select Month" />
<br />
<RangePicker size={size} />
<br />
<WeekPicker size={size} placeholder="Select Week" />
</div>
);
}
}
ReactDOM.render(<PickerSizesDemo />, mountNode);
````

View File

@ -0,0 +1,99 @@
---
order: 6
title:
zh-CN: 自定义日期范围选择
en-US: Customized Range Picker
---
## zh-CN
`RangePicker` 无法满足业务需求时,可以使用两个 `DatePicker` 实现类似的功能。
> * 通过设置 `disabledDate` 方法,来约束开始和结束日期。
> * 通过 `open` `onOpenChange` 来优化交互。
## en-US
When `RangePicker` does not satisfied your requirements, try to implement similar functionality with two `DatePicker`.
> * Use the `disabledDate` property to limit the start and end dates.
> * Improve user experience with `open` and `onOpenChange`.
````jsx
import { DatePicker } from 'antd';
class DateRange extends React.Component {
state = {
startValue: null,
endValue: null,
endOpen: false,
};
disabledStartDate = (startValue) => {
const endValue = this.state.endValue;
if (!startValue || !endValue) {
return false;
}
return startValue.valueOf() > endValue.valueOf();
}
disabledEndDate = (endValue) => {
const startValue = this.state.startValue;
if (!endValue || !startValue) {
return false;
}
return endValue.valueOf() <= startValue.valueOf();
}
onChange = (field, value) => {
this.setState({
[field]: value,
});
}
onStartChange = (value) => {
this.onChange('startValue', value);
}
onEndChange = (value) => {
this.onChange('endValue', value);
}
handleStartOpenChange = (open) => {
if (!open) {
this.setState({ endOpen: true });
}
}
handleEndOpenChange = (open) => {
this.setState({ endOpen: open });
}
render() {
const { startValue, endValue, endOpen } = this.state;
return (
<div>
<DatePicker
disabledDate={this.disabledStartDate}
showTime
format="YYYY-MM-DD HH:mm:ss"
value={startValue}
placeholder="Start"
onChange={this.onStartChange}
onOpenChange={this.handleStartOpenChange}
/>
<DatePicker
disabledDate={this.disabledEndDate}
showTime
format="YYYY-MM-DD HH:mm:ss"
value={endValue}
placeholder="End"
onChange={this.onEndChange}
open={endOpen}
onOpenChange={this.handleEndOpenChange}
/>
</div>
);
}
}
ReactDOM.render(<DateRange />, mountNode);
````

View File

@ -0,0 +1,48 @@
---
order: 3
title:
zh-CN: 日期时间选择
en-US: Choose Time
---
## zh-CN
增加选择时间功能,当 `showTime` 为一个对象时,其属性会传递给内建的 `TimePicker`
## en-US
This property provide an additional time selection. When `showTime` is an Object, its properties will be passed on to built-in `TimePicker`.
````jsx
import { DatePicker } from 'antd';
const { RangePicker } = DatePicker;
function onChange(value, dateString) {
console.log('Selected Time: ', value);
console.log('Formatted Selected Time: ', dateString);
}
function onOk(value) {
console.log('onOk: ', value);
}
ReactDOM.render(
<div>
<DatePicker
showTime
format="YYYY-MM-DD HH:mm:ss"
placeholder="Select Time"
onChange={onChange}
onOk={onOk}
/>
<br />
<RangePicker
showTime={{ format: 'HH:mm' }}
format="YYYY-MM-DD HH:mm"
placeholder={['Start Time', 'End Time']}
onChange={onChange}
onOk={onOk}
/>
</div>
, mountNode);
````

View File

@ -0,0 +1,116 @@
---
category: Components
type: Data Entry
title: DatePicker
---
To select or input a date.
## When To Use
By clicking the input box, you can select a date from a popup calendar.
## API
There are four kinds of picker:
- DatePicker
- MonthPicker
- RangePicker
- WeekPicker
**Note:** Part of locale of DatePicker, MonthPicker, RangePicker, WeekPicker is read from value. So, please set the locale of moment correctly.
```jsx
// The default locale is en-US, if you want to use other locale, just set locale in entry file globaly.
// import moment from 'moment';
// import 'moment/locale/zh-cn';
// moment.locale('zh-cn');
<DatePicker defaultValue={moment('2015-01-01', 'YYYY-MM-DD')} />
```
### Common API
The following APIs are shared by DatePicker, MonthPicker, RangePicker, WeekPicker.
| Property | Description | Type | Default |
| -------- | ----------- | ---- | ------- |
| allowClear | Whether to show clear button | boolean | true |
| autoFocus | get focus when component mounted | boolean | false |
| className | picker className | string | '' |
| dateRender | custom rendering function for date cells | function(currentDate: moment, today: moment) => React.ReactNode | - |
| disabled | determine whether the DatePicker is disabled | boolean | false |
| disabledDate | specify the date that cannot be selected | (currentDate: moment) => boolean | - |
| getCalendarContainer | to set the container of the floating layer, while the default is to create a `div` element in `body` | function(trigger) | - |
| locale | localization configuration | object | [default](https://github.com/ant-design/ant-design/blob/master/components/date-picker/locale/example.json) |
| open | open state of picker | boolean | - |
| placeholder | placeholder of date input | string\|RangePicker\[] | - |
| popupStyle | to customize the style of the popup calendar | object | {} |
| size | determine the size of the input box, the height of `large` and `small`, are 40px and 24px respectively, while default size is 32px | string | - |
| style | to customize the style of the input box | object | {} |
| onOpenChange | a callback function, can be executed whether the popup calendar is popped up or closed | function(status) | - |
### Common Methods
| Name | Description |
| ---- | ----------- |
| blur() | remove focus |
| focus() | get focus |
### DatePicker
| Property | Description | Type | Default |
| -------- | ----------- | ---- | ------- |
| defaultValue | to set default date | [moment](http://momentjs.com/) | - |
| disabledTime | to specify the time that cannot be selected | function(date) | - |
| format | to set the date format, refer to [moment.js](http://momentjs.com/) | string | "YYYY-MM-DD" |
| renderExtraFooter | render extra footer in panel | () => React.ReactNode | - |
| showTime | to provide an additional time selection | object\|boolean | [TimePicker Options](/components/time-picker/#API) |
| showTime.defaultValue | to set default time of selected date, [demo](https://ant.design/components/date-picker/#components-date-picker-demo-disabled-date) | [moment](http://momentjs.com/) | moment() |
| showToday | whether to show "Today" button | boolean | true |
| value | to set date | [moment](http://momentjs.com/) | - |
| onCalendarChange | a callback function, can be executed when the start time or the end time of the range is changing | function(dates: [moment, moment], dateStrings: [string, string]) | 无 |
| onChange | a callback function, can be executed when the selected time is changing | function(date: moment, dateString: string) | - |
| onOk | callback when click ok button | function() | - |
### MonthPicker
| Property | Description | Type | Default |
| -------- | ----------- | ---- | ------- |
| defaultValue | to set default date | [moment](http://momentjs.com/) | - |
| format | to set the date format, refer to [moment.js](http://momentjs.com/) | string | "YYYY-MM" |
| monthCellContentRender | Custom month cell content render method | function(date, locale): ReactNode | - |
| renderExtraFooter | render extra footer in panel | () => React.ReactNode | - |
| value | to set date | [moment](http://momentjs.com/) | - |
| onChange | a callback function, can be executed when the selected time is changing | function(date: moment, dateString: string) | - |
### WeekPicker
| Property | Description | Type | Default |
| -------- | ----------- | ---- | ------- |
| defaultValue | to set default date | [moment](http://momentjs.com/) | - |
| format | to set the date format, refer to [moment.js](http://momentjs.com/) | string | "YYYY-wo" |
| value | to set date | [moment](http://momentjs.com/) | - |
| onChange | a callback function, can be executed when the selected time is changing | function(date: moment, dateString: string) | - |
### RangePicker
| Property | Description | Type | Default |
| -------- | ----------- | ---- | ------- |
| defaultValue | to set default date | \[[moment](http://momentjs.com/), [moment](http://momentjs.com/)] | - |
| disabledTime | to specify the time that cannot be selected | function(dates: [moment, moment], partial: `'start'|'end'`) | - |
| format | to set the date format | string | "YYYY-MM-DD HH:mm:ss" |
| ranges | preseted ranges for quick selection | { \[range: string\]&#x3A; [moment](http://momentjs.com/)\[] } \| () => { \[range: string\]&#x3A; [moment](http://momentjs.com/)\[] } | - |
| renderExtraFooter | render extra footer in panel | () => React.ReactNode | - |
| showTime | to provide an additional time selection | object\|boolean | [TimePicker Options](/components/time-picker/#API) |
| showTime.defaultValue | to set default time of selected date, [demo](https://ant.design/components/date-picker/#components-date-picker-demo-disabled-date) | [moment](http://momentjs.com/)\[] | [moment(), moment()] |
| value | to set date | \[[moment](http://momentjs.com/), [moment](http://momentjs.com/)] | - |
| onChange | a callback function, can be executed when the selected time is changing | function(dates: [moment, moment], dateStrings: [string, string]) | - |
| onOk | callback when click ok button | function() | - |
<style>
.code-box-demo .ant-calendar-picker {
margin: 0 8px 12px 0;
}
</style>

18
components/date-picker/index.js Executable file
View File

@ -0,0 +1,18 @@
import VcCalendar from '../vc-calendar'
import MonthCalendar from '../vc-calendar/src/MonthCalendar'
import createPicker from './createPicker'
import wrapPicker from './wrapPicker'
import RangePicker from './RangePicker'
import WeekPicker from './WeekPicker'
const DatePicker = wrapPicker(createPicker(VcCalendar))
const MonthPicker = wrapPicker(createPicker(MonthCalendar), 'YYYY-MM')
Object.assign(DatePicker, {
RangePicker: wrapPicker(RangePicker),
MonthPicker,
WeekPicker: wrapPicker(WeekPicker, 'YYYY-wo'),
})
export default DatePicker

View File

@ -0,0 +1,117 @@
---
category: Components
type: Data Entry
title: DatePicker
subtitle: 日期选择框
---
输入或选择日期的控件。
## 何时使用
当用户需要输入一个日期,可以点击标准输入框,弹出日期面板进行选择。
## API
日期类组件包括以下四种形式。
- DatePicker
- MonthPicker
- RangePicker
- WeekPicker
**注意:**DatePicker、MonthPicker、RangePicker、WeekPicker 部分 locale 是从 value 中读取,所以请先正确设置 moment 的 locale。
```jsx
// 默认语言为 en-US如果你需要设置其他语言推荐在入口文件全局设置 locale
// import moment from 'moment';
// import 'moment/locale/zh-cn';
// moment.locale('zh-cn');
<DatePicker defaultValue={moment('2015-01-01', 'YYYY-MM-DD')} />
```
### 共同的 API
以下 API 为 DatePicker、MonthPicker、RangePicker, WeekPicker 共享的 API。
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| allowClear | 是否显示清除按钮 | boolean | true |
| autoFocus | 自动获取焦点 | boolean | false |
| className | 选择器 className | string | '' |
| dateRender | 自定义日期单元格的内容 | function(currentDate: moment, today: moment) => React.ReactNode | - |
| disabled | 禁用 | boolean | false |
| disabledDate | 不可选择的日期 | (currentDate: moment) => boolean | 无 |
| getCalendarContainer | 定义浮层的容器,默认为 body 上新建 div | function(trigger) | 无 |
| locale | 国际化配置 | object | [默认配置](https://github.com/ant-design/ant-design/blob/master/components/date-picker/locale/example.json) |
| open | 控制弹层是否展开 | boolean | - |
| placeholder | 输入框提示文字 | string\|RangePicker\[] | - |
| popupStyle | 格外的弹出日历样式 | object | {} |
| size | 输入框大小,`large` 高度为 40px`small` 为 24px默认是 32px | string | 无 |
| style | 自定义输入框样式 | object | {} |
| onOpenChange | 弹出日历和关闭日历的回调 | function(status) | 无 |
### 共同的方法
| 名称 | 描述 |
| --- | --- |
| blur() | 移除焦点 |
| focus() | 获取焦点 |
### DatePicker
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| defaultValue | 默认日期 | [moment](http://momentjs.com/) | 无 |
| disabledTime | 不可选择的时间 | function(date) | 无 |
| format | 展示的日期格式,配置参考 [moment.js](http://momentjs.com/) | string | "YYYY-MM-DD" |
| renderExtraFooter | 在面板中添加额外的页脚 | () => React.ReactNode | - |
| showTime | 增加时间选择功能 | Object\|boolean | [TimePicker Options](/components/time-picker/#API) |
| showTime.defaultValue | 设置用户选择日期时默认的时分秒,[例子](https://ant.design/components/date-picker/#components-date-picker-demo-disabled-date) | [moment](http://momentjs.com/) | moment() |
| showToday | 是否展示“今天”按钮 | boolean | true |
| value | 日期 | [moment](http://momentjs.com/) | 无 |
| onChange | 时间发生变化的回调 | function(date: moment, dateString: string) | 无 |
| onOk | 点击确定按钮的回调 | function() | - |
### MonthPicker
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| defaultValue | 默认日期 | [moment](http://momentjs.com/) | 无 |
| format | 展示的日期格式,配置参考 [moment.js](http://momentjs.com/) | string | "YYYY-MM" |
| monthCellContentRender | 自定义的月份内容渲染方法 | function(date, locale): ReactNode | - |
| renderExtraFooter | 在面板中添加额外的页脚 | () => React.ReactNode | - |
| value | 日期 | [moment](http://momentjs.com/) | 无 |
| onChange | 时间发生变化的回调,发生在用户选择时间时 | function(date: moment, dateString: string) | - |
### WeekPicker
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| defaultValue | 默认日期 | [moment](http://momentjs.com/) | - |
| format | 展示的日期格式,配置参考 [moment.js](http://momentjs.com/) | string | "YYYY-wo" |
| value | 日期 | [moment](http://momentjs.com/) | - |
| onChange | 时间发生变化的回调,发生在用户选择时间时 | function(date: moment, dateString: string) | - |
### RangePicker
| 参数 | 说明 | 类型 | 默认值 |
| --- | --- | --- | --- |
| defaultValue | 默认日期 | [moment](http://momentjs.com/)\[] | 无 |
| disabledTime | 不可选择的时间 | function(dates: [moment, moment], partial: `'start'|'end'`) | 无 |
| format | 展示的日期格式 | string | "YYYY-MM-DD HH:mm:ss" |
| ranges       | 预设时间范围快捷选择 | { \[range: string\]&#x3A; [moment](http://momentjs.com/)\[] } \| () => { \[range: string\]&#x3A; [moment](http://momentjs.com/)\[] } | 无 |
| renderExtraFooter | 在面板中添加额外的页脚 | () => React.ReactNode | - |
| showTime | 增加时间选择功能 | Object\|boolean | [TimePicker Options](/components/time-picker/#API) |
| showTime.defaultValue | 设置用户选择日期时默认的时分秒,[例子](https://ant.design/components/date-picker/#components-date-picker-demo-disabled-date) | [moment](http://momentjs.com/)\[] | [moment(), moment()] |
| value | 日期 | [moment](http://momentjs.com/)\[] | 无 |
| onCalendarChange | 待选日期发生变化的回调 | function(dates: [moment, moment], dateStrings: [string, string]) | 无 |
| onChange | 日期范围发生变化的回调 | function(dates: [moment, moment], dateStrings: [string, string]) | 无 |
| onOk | 点击确定按钮的回调 | function() | - |
<style>
.code-box-demo .ant-calendar-picker {
margin: 0 8px 12px 0;
}
</style>

View File

@ -0,0 +1,90 @@
import * as React from 'react';
import * as moment from 'moment';
import { TimePickerProps } from '../time-picker';
export interface PickerProps {
id?: number | string;
prefixCls?: string;
inputPrefixCls?: string;
format?: string;
disabled?: boolean;
allowClear?: boolean;
className?: string;
style?: React.CSSProperties;
popupStyle?: React.CSSProperties;
locale?: any;
size?: 'large' | 'small' | 'default';
getCalendarContainer?: (triggerNode: Element) => HTMLElement;
open?: boolean;
onOpenChange?: (status: boolean) => void;
disabledDate?: (current: moment.Moment) => boolean;
renderExtraFooter?: () => React.ReactNode;
dateRender?: (current: moment.Moment, today: moment.Moment) => React.ReactNode;
}
export interface SinglePickerProps {
value?: moment.Moment;
defaultValue?: moment.Moment;
defaultPickerValue?: moment.Moment;
onChange?: (date: moment.Moment, dateString: string) => void;
}
export interface DatePickerProps extends PickerProps, SinglePickerProps {
className?: string;
showTime?: TimePickerProps | boolean;
showToday?: boolean;
open?: boolean;
disabledTime?: (current: moment.Moment) => {
disabledHours?: () => number[],
disabledMinutes?: () => number[],
disabledSeconds?: () => number[],
};
onOpenChange?: (status: boolean) => void;
onOk?: (selectedTime: moment.Moment) => void;
placeholder?: string;
}
export interface MonthPickerProps extends PickerProps, SinglePickerProps {
className?: string;
placeholder?: string;
}
export type RangePickerValue =
undefined[] |
[moment.Moment] |
[undefined, moment.Moment] |
[moment.Moment, moment.Moment];
export type RangePickerPresetRange = RangePickerValue | (() => RangePickerValue);
export interface RangePickerProps extends PickerProps {
className?: string;
value?: RangePickerValue;
defaultValue?: RangePickerValue;
defaultPickerValue?: RangePickerValue;
onChange?: (dates: RangePickerValue, dateStrings: [string, string]) => void;
onCalendarChange?: (dates: RangePickerValue, dateStrings: [string, string]) => void;
onOk?: (selectedTime: moment.Moment) => void;
showTime?: TimePickerProps | boolean;
ranges?: {
[range: string]: RangePickerPresetRange,
};
placeholder?: [string, string];
mode?: string | string[];
disabledTime?: (current: moment.Moment, type: string) => {
disabledHours?: () => number[],
disabledMinutes?: () => number[],
disabledSeconds?: () => number[],
};
onPanelChange?: (value?: RangePickerValue, mode?: string | string[]) => void;
}
export interface WeexPickerProps extends PickerProps, SinglePickerProps {
className?: string;
placeholder?: string;
}
export interface DatePickerDecorator extends React.ClassicComponentClass<DatePickerProps> {
RangePicker: React.ClassicComponentClass<RangePickerProps>;
MonthPicker: React.ClassicComponentClass<MonthPickerProps>;
WeekPicker: React.ClassicComponentClass<WeexPickerProps>;
}

View File

@ -0,0 +1,336 @@
.calendarPanelHeader(@calendar-prefix-cls) {
height: 40px;
line-height: 40px;
text-align: center;
user-select: none;
border-bottom: @border-width-base @border-style-base @border-color-split;
a:hover {
color: @link-hover-color;
}
.@{calendar-prefix-cls}-century-select,
.@{calendar-prefix-cls}-decade-select,
.@{calendar-prefix-cls}-year-select,
.@{calendar-prefix-cls}-month-select {
padding: 0 2px;
font-weight: 500;
display: inline-block;
color: @heading-color;
line-height: 40px;
}
.@{calendar-prefix-cls}-century-select-arrow,
.@{calendar-prefix-cls}-decade-select-arrow,
.@{calendar-prefix-cls}-year-select-arrow,
.@{calendar-prefix-cls}-month-select-arrow {
display: none;
}
.@{calendar-prefix-cls}-prev-century-btn,
.@{calendar-prefix-cls}-next-century-btn,
.@{calendar-prefix-cls}-prev-decade-btn,
.@{calendar-prefix-cls}-next-decade-btn,
.@{calendar-prefix-cls}-prev-month-btn,
.@{calendar-prefix-cls}-next-month-btn,
.@{calendar-prefix-cls}-prev-year-btn,
.@{calendar-prefix-cls}-next-year-btn {
position: absolute;
top: 0;
color: @text-color-secondary;
font-family: Arial, "Hiragino Sans GB", "Microsoft Yahei", "Microsoft Sans Serif", sans-serif;
padding: 0 5px;
font-size: 16px;
display: inline-block;
line-height: 40px;
}
.@{calendar-prefix-cls}-prev-century-btn,
.@{calendar-prefix-cls}-prev-decade-btn,
.@{calendar-prefix-cls}-prev-year-btn {
left: 7px;
&:after {
content: '«';
}
}
.@{calendar-prefix-cls}-next-century-btn,
.@{calendar-prefix-cls}-next-decade-btn,
.@{calendar-prefix-cls}-next-year-btn {
right: 7px;
&:after {
content: '»';
}
}
.@{calendar-prefix-cls}-prev-month-btn {
left: 29px;
&:after {
content: '';
}
}
.@{calendar-prefix-cls}-next-month-btn {
right: 29px;
&:after {
content: '';
}
}
}
.@{calendar-prefix-cls} {
position: relative;
outline: none;
width: 280px;
border: @border-width-base @border-style-base #fff;
list-style: none;
font-size: @font-size-base;
text-align: left;
background-color: @component-background;
border-radius: @border-radius-base;
box-shadow: @box-shadow-base;
background-clip: padding-box;
line-height: @line-height-base;
&-input-wrap {
height: 34px;
padding: 6px @control-padding-horizontal - 2px;
border-bottom: @border-width-base @border-style-base @border-color-split;
}
&-input {
border: 0;
width: 100%;
cursor: auto;
outline: 0;
height: 22px;
color: @input-color;
background: @input-bg;
.placeholder;
}
&-week-number {
width: 286px;
&-cell {
text-align: center;
}
}
&-header {
.calendarPanelHeader(@calendar-prefix-cls);
}
&-body {
padding: 8px 12px;
}
table {
border-collapse: collapse;
max-width: 100%;
background-color: transparent;
width: 100%;
}
table,
th,
td {
border: 0;
text-align: center;
}
&-calendar-table {
border-spacing: 0;
margin-bottom: 0;
}
&-column-header {
line-height: 18px;
width: 33px;
padding: 6px 0;
text-align: center;
.@{calendar-prefix-cls}-column-header-inner {
display: block;
font-weight: normal;
}
}
&-week-number-header {
.@{calendar-prefix-cls}-column-header-inner {
display: none;
}
}
&-cell {
padding: 3px 0;
height: 30px;
}
&-date {
display: block;
margin: 0 auto;
color: @text-color;
border-radius: @border-radius-sm;
width: 24px;
height: 24px;
line-height: 22px;
border: @border-width-base @border-style-base transparent;
padding: 0;
background: transparent;
text-align: center;
transition: background 0.3s ease;
&-panel {
position: relative;
}
&:hover {
background: @item-hover-bg;
cursor: pointer;
}
&:active {
color: #fff;
background: @primary-5;
}
}
&-today &-date {
border-color: @primary-color;
font-weight: bold;
color: @primary-color;
}
&-last-month-cell &-date,
&-next-month-btn-day &-date {
color: @disabled-color;
}
&-selected-day &-date {
background: @primary-color;
color: #fff;
border: @border-width-base @border-style-base transparent;
&:hover {
background: @primary-color;
}
}
&-disabled-cell &-date {
cursor: not-allowed;
color: #bcbcbc;
background: @disabled-bg;
border-radius: 0;
width: auto;
border: @border-width-base @border-style-base transparent;
&:hover {
background: @disabled-bg;
}
}
&-disabled-cell&-today &-date {
position: relative;
margin-right: 5px;
padding-left: 5px;
&:before {
content: " ";
position: absolute;
top: -1px;
left: 5px;
width: 24px;
height: 24px;
border: @border-width-base @border-style-base #bcbcbc;
border-radius: @border-radius-sm;
}
}
&-disabled-cell-first-of-row &-date {
border-top-left-radius: 4px;
border-bottom-left-radius: 4px;
}
&-disabled-cell-last-of-row &-date {
border-top-right-radius: 4px;
border-bottom-right-radius: 4px;
}
&-footer {
border-top: @border-width-base @border-style-base @border-color-split;
line-height: 38px;
padding: 0 12px;
&:empty {
border-top: 0;
}
&-btn {
text-align: center;
display: block;
}
&-extra + &-btn {
border-top: @border-width-base @border-style-base @border-color-split;
margin: 0 -12px;
padding: 0 12px;
}
}
.@{calendar-prefix-cls}-today-btn,
.@{calendar-prefix-cls}-clear-btn {
display: inline-block;
text-align: center;
margin: 0 0 0 8px;
&-disabled {
color: @disabled-color;
cursor: not-allowed;
}
&:only-child {
margin: 0;
}
}
.@{calendar-prefix-cls}-clear-btn {
display: none;
position: absolute;
right: 5px;
text-indent: -76px;
overflow: hidden;
width: 20px;
height: 20px;
text-align: center;
line-height: 20px;
top: 7px;
margin: 0;
}
.@{calendar-prefix-cls}-clear-btn:after {
.iconfont-font("\e62e");
font-size: @font-size-base;
color: @disabled-color;
display: inline-block;
line-height: 1;
width: 20px;
text-indent: 43px;
transition: color 0.3s ease;
}
.@{calendar-prefix-cls}-clear-btn:hover:after {
color: @text-color-secondary;
}
.@{calendar-prefix-cls}-ok-btn {
.btn;
.btn-primary;
.button-size(@btn-height-sm; @btn-padding-sm; @font-size-base; @border-radius-base);
line-height: @btn-height-sm - 2px;
&-disabled {
.button-color(@btn-disable-color; @btn-disable-bg; @btn-disable-border);
cursor: not-allowed;
&:hover {
.button-color(@btn-disable-color; @btn-disable-bg; @btn-disable-border);
}
}
}
}

View File

@ -0,0 +1,71 @@
.@{calendar-prefix-cls}-decade-panel {
position: absolute;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 10;
background: @component-background;
border-radius: @border-radius-base;
outline: none;
}
.@{calendar-prefix-cls}-decade-panel-hidden {
display: none;
}
.@{calendar-prefix-cls}-decade-panel-header {
.calendarPanelHeader(~"@{calendar-prefix-cls}-decade-panel");
}
.@{calendar-prefix-cls}-decade-panel-body {
height: ~"calc(100% - 40px)";
}
.@{calendar-prefix-cls}-decade-panel-table {
table-layout: fixed;
width: 100%;
height: 100%;
border-collapse: separate;
}
.@{calendar-prefix-cls}-decade-panel-cell {
text-align: center;
white-space: nowrap;
}
.@{calendar-prefix-cls}-decade-panel-decade {
display: inline-block;
margin: 0 auto;
color: @text-color;
background: transparent;
text-align: center;
height: 24px;
line-height: 24px;
padding: 0 6px;
border-radius: @border-radius-sm;
transition: background 0.3s ease;
&:hover {
background: @item-hover-bg;
cursor: pointer;
}
}
.@{calendar-prefix-cls}-decade-panel-selected-cell .@{calendar-prefix-cls}-decade-panel-decade {
background: @primary-color;
color: #fff;
&:hover {
background: @primary-color;
color: #fff;
}
}
.@{calendar-prefix-cls}-decade-panel-last-century-cell,
.@{calendar-prefix-cls}-decade-panel-next-century-cell {
.@{calendar-prefix-cls}-decade-panel-decade {
user-select: none;
color: @disabled-color;
}
}

View File

@ -0,0 +1,75 @@
.@{calendar-prefix-cls}-month-panel {
position: absolute;
top: 1px;
right: 0;
bottom: 0;
left: 0;
z-index: 10;
border-radius: @border-radius-base;
background: @component-background;
outline: none;
> div { // TODO: this is a useless wrapper, and we need to remove it in rc-calendar
height: 100%;
}
}
.@{calendar-prefix-cls}-month-panel-hidden {
display: none;
}
.@{calendar-prefix-cls}-month-panel-header {
.calendarPanelHeader(~"@{calendar-prefix-cls}-month-panel");
}
.@{calendar-prefix-cls}-month-panel-body {
height: ~"calc(100% - 40px)";
}
.@{calendar-prefix-cls}-month-panel-table {
table-layout: fixed;
width: 100%;
height: 100%;
border-collapse: separate;
}
.@{calendar-prefix-cls}-month-panel-selected-cell .@{calendar-prefix-cls}-month-panel-month {
background: @primary-color;
color: #fff;
&:hover {
background: @primary-color;
color: #fff;
}
}
.@{calendar-prefix-cls}-month-panel-cell {
text-align: center;
&-disabled .@{calendar-prefix-cls}-month-panel-month {
&,
&:hover {
cursor: not-allowed;
color: #bcbcbc;
background: @disabled-bg;
}
}
}
.@{calendar-prefix-cls}-month-panel-month {
display: inline-block;
margin: 0 auto;
color: @text-color;
background: transparent;
text-align: center;
height: 24px;
line-height: 24px;
padding: 0 8px;
border-radius: @border-radius-sm;
transition: background 0.3s ease;
&:hover {
background: @item-hover-bg;
cursor: pointer;
}
}

View File

@ -0,0 +1,11 @@
.@{calendar-prefix-cls}-month {
.@{calendar-prefix-cls}-month-header-wrap {
position: relative;
height: 288px;
}
.@{calendar-prefix-cls}-month-panel,
.@{calendar-prefix-cls}-year-panel {
top: 0;
height: 100%;
}
}

View File

@ -0,0 +1,100 @@
@import "../../button/style/mixin";
.@{calendar-prefix-cls}-picker-container {
.reset-component;
position: absolute;
z-index: @zindex-picker;
&.slide-up-enter.slide-up-enter-active&-placement-topLeft,
&.slide-up-enter.slide-up-enter-active&-placement-topRight,
&.slide-up-appear.slide-up-appear-active&-placement-topLeft,
&.slide-up-appear.slide-up-appear-active&-placement-topRight {
animation-name: antSlideDownIn;
}
&.slide-up-enter.slide-up-enter-active&-placement-bottomLeft,
&.slide-up-enter.slide-up-enter-active&-placement-bottomRight,
&.slide-up-appear.slide-up-appear-active&-placement-bottomLeft,
&.slide-up-appear.slide-up-appear-active&-placement-bottomRight {
animation-name: antSlideUpIn;
}
&.slide-up-leave.slide-up-leave-active&-placement-topLeft,
&.slide-up-leave.slide-up-leave-active&-placement-topRight {
animation-name: antSlideDownOut;
}
&.slide-up-leave.slide-up-leave-active&-placement-bottomLeft,
&.slide-up-leave.slide-up-leave-active&-placement-bottomRight {
animation-name: antSlideUpOut;
}
}
.@{calendar-prefix-cls}-picker {
.reset-component;
position: relative;
display: inline-block;
outline: none;
transition: opacity 0.3s;
&-input {
outline: none;
display: block;
}
&:hover &-input:not(.@{ant-prefix}-input-disabled) {
border-color: @primary-color;
}
&:focus &-input:not(.@{ant-prefix}-input-disabled) {
.active();
}
&-clear,
&-icon {
position: absolute;
width: 14px;
height: 14px;
right: @control-padding-horizontal;
top: 50%;
margin-top: -7px;
line-height: 14px;
font-size: @font-size-sm;
transition: all .3s;
user-select: none;
}
&-clear {
opacity: 0;
z-index: 1;
color: @disabled-color;
background: @input-bg;
pointer-events: none;
cursor: pointer;
&:hover {
color: @text-color-secondary;
}
}
&:hover &-clear {
opacity: 1;
pointer-events: auto;
}
&-icon {
color: @disabled-color;
&:after {
content: "\e6bb";
font-family: "anticon";
font-size: @font-size-base;
color: @disabled-color;
display: inline-block;
line-height: 1;
}
}
&-small &-clear,
&-small &-icon {
right: @control-padding-horizontal-sm;
}
}

View File

@ -0,0 +1,222 @@
@input-box-height: 34px;
.@{calendar-prefix-cls}-range-picker-input {
background-color: transparent;
border: 0;
height: 99%;
outline: 0;
width: 44%;
text-align: center;
.placeholder();
&[disabled] {
cursor: not-allowed;
}
}
.@{calendar-prefix-cls}-range-picker-separator {
color: @text-color-secondary;
width: 10px;
display: inline-block;
height: 100%;
vertical-align: top;
}
.@{calendar-prefix-cls}-range {
width: 552px;
overflow: hidden;
.@{calendar-prefix-cls}-date-panel {
&::after {
content: ".";
display: block;
height: 0;
clear: both;
visibility: hidden;
}
}
&-part {
width: 50%;
position: relative;
}
&-left {
float: left;
.@{calendar-prefix-cls} {
&-time-picker-inner {
border-right: 1.5px solid @border-color-split;
}
}
}
&-right {
float: right;
.@{calendar-prefix-cls} {
&-time-picker-inner {
border-left: 1.5px solid @border-color-split;
}
}
}
&-middle {
position: absolute;
left: 50%;
width: 20px;
margin-left: -132px;
text-align: center;
height: @input-box-height;
line-height: @input-box-height;
color: @text-color-secondary;
}
&-right .@{calendar-prefix-cls}-date-input-wrap {
margin-left: -118px;
}
&.@{calendar-prefix-cls}-time &-middle {
margin-left: -12px;
}
&.@{calendar-prefix-cls}-time &-right .@{calendar-prefix-cls}-date-input-wrap {
margin-left: 0;
}
.@{calendar-prefix-cls}-input-wrap {
position: relative;
height: @input-box-height;
}
.@{calendar-prefix-cls}-input,
.@{calendar-timepicker-prefix-cls}-input {
.input;
height: @input-height-sm;
border: 0;
box-shadow: none;
padding-left: 0;
padding-right: 0;
&:focus {
box-shadow: none;
}
}
.@{calendar-timepicker-prefix-cls}-icon {
display: none;
}
&.@{calendar-prefix-cls}-week-number {
width: 574px;
.@{calendar-prefix-cls}-range-part {
width: 286px;
}
}
.@{calendar-prefix-cls}-year-panel,
.@{calendar-prefix-cls}-month-panel,
.@{calendar-prefix-cls}-decade-panel {
top: @input-box-height;
}
.@{calendar-prefix-cls}-month-panel .@{calendar-prefix-cls}-year-panel {
top: 0;
}
.@{calendar-prefix-cls}-decade-panel-table,
.@{calendar-prefix-cls}-year-panel-table,
.@{calendar-prefix-cls}-month-panel-table {
height: 208px;
}
.@{calendar-prefix-cls}-in-range-cell {
border-radius: 0;
position: relative;
> div {
position: relative;
z-index: 1;
}
&:before {
content: '';
display: block;
background: @item-active-bg;
border-radius: 0;
border: 0;
position: absolute;
top: 4px;
bottom: 4px;
left: 0;
right: 0;
}
}
// `div` for selector specificity
div&-quick-selector {
text-align: left;
> a {
margin-right: 8px;
}
}
.@{calendar-prefix-cls},
.@{calendar-prefix-cls}-month-panel,
.@{calendar-prefix-cls}-year-panel {
&-header {
border-bottom: 0;
}
&-body {
border-top: @border-width-base @border-style-base @border-color-split;
}
}
&.@{calendar-prefix-cls}-time {
.@{calendar-timepicker-prefix-cls} {
height: 207px;
width: 100%;
top: 68px;
z-index: 2; // cover .ant-calendar-range .ant-calendar-in-range-cell > div (z-index: 1)
&-panel {
height: 267px;
margin-top: -34px;
}
&-inner {
padding-top: 40px;
height: 100%;
background: none;
}
&-combobox {
display: inline-block;
height: 100%;
background-color: @component-background;
border-top: @border-width-base @border-style-base @border-color-split;
}
&-select {
height: 100%;
ul {
max-height: 100%;
}
}
}
.@{calendar-prefix-cls}-footer .@{calendar-prefix-cls}-time-picker-btn {
margin-right: 8px;
}
.@{calendar-prefix-cls}-today-btn {
margin: 8px 12px;
height: 22px;
line-height: 22px;
}
}
&-with-ranges.@{calendar-prefix-cls}-time .@{calendar-timepicker-prefix-cls} {
height: 247px;
&-panel {
height: 281px;
}
}
}
.@{calendar-prefix-cls}-range.@{calendar-prefix-cls}-show-time-picker {
.@{calendar-prefix-cls}-body {
border-top-color: transparent;
}
}

View File

@ -0,0 +1,148 @@
.@{calendar-timepicker-prefix-cls} {
position: absolute;
width: 100%;
top: 40px;
background-color: @component-background;
&-panel {
z-index: @zindex-picker;
position: absolute;
width: 100%;
}
&-inner {
display: inline-block;
position: relative;
outline: none;
list-style: none;
font-size: @font-size-base;
text-align: left;
background-color: @component-background;
background-clip: padding-box;
line-height: 1.5;
overflow: hidden;
width: 100%;
}
&-combobox {
width: 100%;
}
&-column-1,
&-column-1 &-select {
width: 100%;
}
&-column-2 &-select {
width: 50%;
}
&-column-3 &-select {
width: 33.33%;
}
&-column-4 &-select {
width: 25%;
}
&-input-wrap {
display: none;
}
&-select {
float: left;
font-size: @font-size-base;
border-right: @border-width-base @border-style-base @border-color-split;
box-sizing: border-box;
overflow: hidden;
position: relative; // Fix chrome weird render bug
height: 226px;
&:hover {
overflow-y: auto;
}
&:first-child {
border-left: 0;
margin-left: 0;
}
&:last-child {
border-right: 0;
}
ul {
list-style: none;
box-sizing: border-box;
margin: 0;
padding: 0;
width: 100%;
max-height: 206px;
}
li {
text-align: center;
list-style: none;
box-sizing: content-box;
margin: 0;
width: 100%;
height: 24px;
line-height: 24px;
cursor: pointer;
user-select: none;
transition: background 0.3s ease;
}
li:last-child:after {
content: '';
height: 202px;
display: block;
}
li:hover {
background: @item-hover-bg;
}
li&-option-selected {
background: @time-picker-selected-bg;
font-weight: bold;
}
li&-option-disabled {
color: @btn-disable-color;
&:hover {
background: transparent;
cursor: not-allowed;
}
}
}
}
.@{calendar-prefix-cls}-time {
.@{calendar-prefix-cls}-day-select {
padding: 0 2px;
font-weight: 500;
display: inline-block;
color: @heading-color;
line-height: 34px;
}
.@{calendar-prefix-cls}-footer {
position: relative;
height: auto;
&-btn {
text-align: right;
}
.@{calendar-prefix-cls}-today-btn {
float: left;
margin: 0;
}
.@{calendar-prefix-cls}-time-picker-btn {
display: inline-block;
margin-right: 8px;
&-disabled {
color: @disabled-color;
}
}
}
}

View File

@ -0,0 +1,21 @@
.@{calendar-prefix-cls}-week-number {
&-cell {
opacity: 0.5;
}
.@{calendar-prefix-cls}-body tr {
transition: all .3s;
cursor: pointer;
&:hover {
background: @primary-1;
}
&.@{calendar-prefix-cls}-active-week {
background: @primary-2;
font-weight: bold;
}
.@{calendar-prefix-cls}-selected-day .@{calendar-prefix-cls}-date,
.@{calendar-prefix-cls}-selected-day:hover .@{calendar-prefix-cls}-date {
background: transparent;
color: @text-color;
}
}
}

View File

@ -0,0 +1,74 @@
.@{calendar-prefix-cls}-year-panel {
position: absolute;
top: 1px;
right: 0;
bottom: 0;
left: 0;
z-index: 10;
border-radius: @border-radius-base;
background: @component-background;
outline: none;
> div { // TODO: this is a useless wrapper, and we need to remove it in rc-calendar
height: 100%;
}
}
.@{calendar-prefix-cls}-year-panel-hidden {
display: none;
}
.@{calendar-prefix-cls}-year-panel-header {
.calendarPanelHeader(~"@{calendar-prefix-cls}-year-panel");
}
.@{calendar-prefix-cls}-year-panel-body {
height: ~"calc(100% - 40px)";
}
.@{calendar-prefix-cls}-year-panel-table {
table-layout: fixed;
width: 100%;
height: 100%;
border-collapse: separate;
}
.@{calendar-prefix-cls}-year-panel-cell {
text-align: center;
}
.@{calendar-prefix-cls}-year-panel-year {
display: inline-block;
margin: 0 auto;
color: @text-color;
background: transparent;
text-align: center;
height: 24px;
line-height: 24px;
padding: 0 8px;
border-radius: @border-radius-sm;
transition: background 0.3s ease;
&:hover {
background: @item-hover-bg;
cursor: pointer;
}
}
.@{calendar-prefix-cls}-year-panel-selected-cell .@{calendar-prefix-cls}-year-panel-year {
background: @primary-color;
color: #fff;
&:hover {
background: @primary-color;
color: #fff;
}
}
.@{calendar-prefix-cls}-year-panel-last-decade-cell,
.@{calendar-prefix-cls}-year-panel-next-decade-cell {
.@{calendar-prefix-cls}-year-panel-year {
user-select: none;
color: @disabled-color;
}
}

View File

@ -0,0 +1,6 @@
import '../../style/index.less'
import './index.less'
// style dependencies
import '../../input/style'
import '../../time-picker/style'

View File

@ -0,0 +1,17 @@
@import "../../style/themes/default";
@import "../../style/mixins/index";
@import "../../input/style/mixin";
@import "../../button/style/mixin";
@calendar-prefix-cls: ~"@{ant-prefix}-calendar";
@calendar-timepicker-prefix-cls: ~"@{ant-prefix}-calendar-time-picker";
@import "Picker";
@import "Calendar";
@import "RangePicker";
@import "TimePicker";
@import "MonthPanel";
@import "YearPanel";
@import "DecadePanel";
@import "MonthPicker";
@import "WeekPicker";

View File

@ -0,0 +1,161 @@
<script>
import TimePickerPanel from '../vc-time-picker/src/Panel'
import classNames from 'classnames'
import LocaleReceiver from '../locale-provider/LocaleReceiver'
import { generateShowHourMinuteSecond } from '../time-picker'
import enUS from './locale/en_US'
import { getOptionProps } from '../_util/props-util'
function getColumns ({ showHour, showMinute, showSecond, use12Hours }) {
let column = 0
if (showHour) {
column += 1
}
if (showMinute) {
column += 1
}
if (showSecond) {
column += 1
}
if (use12Hours) {
column += 1
}
return column
}
export default function wrapPicker (Picker, defaultFormat) {
return {
props: {
},
// static defaultProps = {
// format: defaultFormat || 'YYYY-MM-DD',
// transitionName: 'slide-up',
// popupStyle: {},
// onChange() {
// },
// onOk() {
// },
// onOpenChange() {
// },
// locale: {},
// prefixCls: 'ant-calendar',
// inputPrefixCls: 'ant-input',
// };
mounted () {
const { autoFocus, disabled } = this
if (autoFocus && !disabled) {
this.$nextTick(() => {
this.focus()
})
}
},
methods: {
handleOpenChange (open) {
this.$emit('openChange', open)
},
handleFocus (e) {
this.$emit('focus', e)
},
handleBlur (e) {
this.$emit('blur', e)
},
focus () {
this.$refs.picker.focus()
},
blur () {
this.$refs.picker.blur()
},
getDefaultLocale () {
const result = {
...enUS,
...this.locale,
}
result.lang = {
...result.lang,
...(this.locale || {}).lang,
}
return result
},
renderPicker (locale, localeCode) {
const props = getOptionProps(this)
const { prefixCls, inputPrefixCls, size, showTime, disabled } = props
const pickerClass = classNames(`${prefixCls}-picker`, {
[`${prefixCls}-picker-${size}`]: !!size,
})
const pickerInputClass = classNames(`${prefixCls}-picker-input`, inputPrefixCls, {
[`${inputPrefixCls}-lg`]: size === 'large',
[`${inputPrefixCls}-sm`]: size === 'small',
[`${inputPrefixCls}-disabled`]: disabled,
})
const timeFormat = (showTime && showTime.format) || 'HH:mm:ss'
const vcTimePickerProps = {
...generateShowHourMinuteSecond(timeFormat),
format: timeFormat,
use12Hours: (showTime && showTime.use12Hours),
}
const columns = getColumns(vcTimePickerProps)
const timePickerCls = `${prefixCls}-time-picker-column-${columns}`
const timePickerPanelProps = {
props: {
...vcTimePickerProps,
...showTime,
prefixCls: `${prefixCls}-time-picker`,
placeholder: locale.timePickerLocale.placeholder,
transitionName: 'slide-up',
},
class: timePickerCls,
}
const timePicker = showTime ? (
<TimePickerPanel
{...timePickerPanelProps}
/>
) : null
const pickerProps = {
props: {
...props,
pickerClass,
pickerInputClass,
locale,
localeCode,
timePicker,
},
on: {
...this.$listeners,
openChange: this.handleOpenChange,
focus: this.handleFocus,
blur: this.handleBlur,
},
ref: 'picker',
}
return (
<Picker
{...pickerProps}
/>
)
},
},
render () {
return (
<LocaleReceiver
componentName='DatePicker'
defaultLocale={this.getDefaultLocale}
scopedSlots={
{ default: this.renderPicker }
}
/>
)
},
}
}
</script>

View File

@ -36,7 +36,9 @@ export default {
},
render () {
return this.children(this.getLocale(), this.getLocaleCode())
const { $scopedSlots } = this
const children = this.children || $scopedSlots.default
return children(this.getLocale(), this.getLocaleCode())
},
}