Merge pull request #647 from ant-design/feat-range-datepicker

feat: add RangePicker
This commit is contained in:
afc163 2015-12-24 20:50:48 +08:00
commit 75048619a2
9 changed files with 324 additions and 42 deletions

View File

@ -0,0 +1,47 @@
import objectAssign from 'object-assign';
import defaultLocale from './locale/zh_CN';
import DateTimeFormat from 'gregorian-calendar-format';
import GregorianCalendar from 'gregorian-calendar';
export default {
getLocale() {
// Locale
let locale = objectAssign({}, defaultLocale, this.props.locale);
locale.lang = objectAssign({}, defaultLocale.lang, this.props.locale.lang);
return locale;
},
getFormatter() {
const formats = this.formats = this.formats || {};
const format = this.props.format;
if (formats[format]) {
return formats[format];
}
formats[format] = new DateTimeFormat(format, this.getLocale().lang.format);
return formats[format];
},
parseDateFromValue(value) {
if (value) {
if (typeof value === 'string') {
return this.getFormatter().parse(value, {locale: this.getLocale()});
} else if (value instanceof Date) {
let date = new GregorianCalendar(this.getLocale());
date.setTime(value);
return date;
}
} else if (value === null) {
return value;
}
return undefined;
},
// remove input readonly warning
handleInputChange() {
},
toggleOpen(e) {
this.setState({
open: e.open
});
},
};

View File

@ -0,0 +1,124 @@
import React from 'react';
import GregorianCalendar from 'gregorian-calendar';
import RangeCalendar from 'rc-calendar/lib/RangeCalendar';
import Datepicker from 'rc-calendar/lib/Picker';
import Timepicker from 'rc-time-picker';
import classNames from 'classnames';
import PickerMixin from './PickerMixin';
export default React.createClass({
getDefaultProps() {
return {
defaultValue: [],
format: 'yyyy-MM-dd HH:mm:ss',
startPlaceholder: '开始时间',
endPlaceholder: '结束时间',
transitionName: 'slide-up',
popupStyle: {},
onChange() {
}, // onChange Validator
locale: {},
align: {
offset: [0, -9],
},
open: false
};
},
getInitialState() {
const {value, defaultValue} = this.props;
const start = (value && value[0]) || defaultValue[0];
const end = (value && value[1]) || defaultValue[1];
return {
value: [
this.parseDateFromValue(start),
this.parseDateFromValue(end)
]
};
},
mixins: [ PickerMixin ],
componentWillReceiveProps(nextProps) {
if ('value' in nextProps) {
const start = this.parseDateFromValue(nextProps.value[0]);
const end = this.parseDateFromValue(nextProps.value[1]);
this.setState({
value: [start, end]
});
}
},
handleChange(value) {
this.setState({value});
const startTime = value[0] ? new Date(value[0].getTime()) : null;
const endTime = value[1] ? new Date(value[1].getTime()) : null;
this.props.onChange([startTime, endTime]);
},
render() {
const locale = this.getLocale();
//
//
//
let defaultCalendarValue = new GregorianCalendar(locale);
defaultCalendarValue.setTime(Date.now());
const {disabledDate, showTime, size, startPlaceholder, endPlaceholder,
transitionName, disabled, popupStyle, align, style} = this.props;
const state = this.state;
const calendar = (<RangeCalendar prefixCls="ant-calendar"
timePicker={<Timepicker prefixCls="ant-time-picker" />}
disabledDate={disabledDate}
locale={locale.lang}
defaultValue={defaultCalendarValue}
showTime={showTime}
showOk={showTime}
showClear />);
const pickerClass = classNames({
'ant-calendar-picker': true,
'ant-calendar-picker-open': state.open
});
const pickerInputClass = classNames({
'ant-input': true,
'ant-input-lg': size === 'large',
'ant-input-sm': size === 'small'
});
return (<span className={pickerClass}>
<Datepicker
transitionName={transitionName}
disabled={disabled}
calendar={calendar}
value={state.value}
prefixCls="ant-calendar-picker-container"
style={popupStyle}
align={align}
onOpen={this.toggleOpen}
onClose={this.toggleOpen}
onChange={this.handleChange}>
{
({value}) => {
const start = value[0];
const end = value[1];
return (<span className={pickerInputClass} disabled={disabled}>
<input disabled={disabled}
onChange={this.handleInputChange}
value={start && this.getFormatter().format(start)}
placeholder={startPlaceholder}
style={style}
className="ant-calendar-range-picker-input"/>
<span> ~ </span>
<input disabled={disabled}
onChange={this.handleInputChange}
value={end && this.getFormatter().format(end)}
placeholder={endPlaceholder}
style={style}
className="ant-calendar-range-picker-input"/>
<span className="ant-calendar-picker-icon"/>
</span>);
}
}
</Datepicker>
</span>);
}
});

View File

@ -0,0 +1,18 @@
# 时间范围选择
- order: 7
使用 RangePicker 实现范围选择器。
---
````jsx
import { DatePicker } from 'antd';
const RangePicker = DatePicker.RangePicker;
function log(value) {
console.log('From: ', value[0], ', to: ', value[1]);
}
ReactDOM.render(<RangePicker defaultValue={['2011-11-11 11:11:11', '']} onChange={log} />
, document.getElementById('components-date-picker-demo-time-range'));
````

View File

@ -3,10 +3,9 @@ import Calendar from 'rc-calendar';
import MonthCalendar from 'rc-calendar/lib/MonthCalendar';
import DatePicker from 'rc-calendar/lib/Picker';
import GregorianCalendar from 'gregorian-calendar';
import defaultLocale from './locale/zh_CN';
import CalendarLocale from 'rc-calendar/lib/locale/zh_CN';
import DateTimeFormat from 'gregorian-calendar-format';
import objectAssign from 'object-assign';
import AntRangePicker from './RangePicker';
import PickerMixin from './Pickermixin';
function createPicker(TheCalendar, defaultFormat) {
return React.createClass({
@ -30,6 +29,7 @@ function createPicker(TheCalendar, defaultFormat) {
value: this.parseDateFromValue(this.props.value || this.props.defaultValue)
};
},
mixins: [ PickerMixin ],
componentWillReceiveProps(nextProps) {
if ('value' in nextProps) {
this.setState({
@ -37,43 +37,6 @@ function createPicker(TheCalendar, defaultFormat) {
});
}
},
getLocale() {
// Locale
let locale = objectAssign({}, defaultLocale, this.props.locale);
locale.lang = objectAssign({}, defaultLocale.lang, this.props.locale.lang);
return locale;
},
getFormatter() {
const formats = this.formats = this.formats || {};
const format = this.props.format;
if (formats[format]) {
return formats[format];
}
formats[format] = new DateTimeFormat(format, this.getLocale().lang.format);
return formats[format];
},
parseDateFromValue(value) {
if (value) {
if (typeof value === 'string') {
return this.getFormatter().parse(value, {locale: this.getLocale()});
} else if (value instanceof Date) {
let date = new GregorianCalendar(this.getLocale());
date.setTime(value);
return date;
}
} else if (value === null) {
return value;
}
return undefined;
},
// remove input readonly warning
handleInputChange() {
},
toggleOpen(e) {
this.setState({
open: e.open
});
},
handleChange(value) {
if (!('value' in this.props)) {
this.setState({ value });
@ -171,6 +134,7 @@ const AntCalendar = React.createClass({
});
AntDatePicker.Calendar = AntCalendar;
AntDatePicker.RangePicker = AntRangePicker;
AntDatePicker.MonthPicker = AntMonthPicker;
export default AntDatePicker;

View File

@ -14,6 +14,8 @@
## API
### DatePicker
```html
<DatePicker defaultValue="2015-01-01" />
```
@ -34,6 +36,17 @@
| size | 输入框大小,`large` 高度为 32px`small` 为 22px默认是 28px | string | 无 |
| locale | 国际化配置 | object | [默认配置](https://github.com/ant-design/ant-design/issues/424) |
### RangePicker
| 参数 | 说明 | 类型 | 默认值 |
|--------------|----------------|----------|--------------|
| value | 日期 | [string, string] | 无 |
| defaultValue | 默认日期 | [string, string] | 无 |
| format | 展示的日期格式 | string | "yyyy-MM-dd HH:mm:ss" |
| onChange | 时间发生变化的回调,发生在用户选择时间时 | function([Date start, Date end]) | 无 |
`disabled` `style` `popupStyle` `size` `locale` 属性与 DatePicker 的一致。
<style>
.code-box-demo .ant-calendar-picker {
margin: 0 8px 12px 0;

View File

@ -35,11 +35,11 @@
"dependencies": {
"classnames": "~2.2.0",
"css-animation": "1.1.x",
"gregorian-calendar": "~4.0.1",
"gregorian-calendar": "~4.1.0",
"gregorian-calendar-format": "~4.0.4",
"object-assign": "~4.0.1",
"rc-animate": "~2.0.2",
"rc-calendar": "~4.0.0",
"rc-calendar": "~5.0.2",
"rc-checkbox": "~1.1.1",
"rc-collapse": "~1.4.4",
"rc-dialog": "~5.2.2",

View File

@ -9,6 +9,7 @@
@import "datepicker/Picker";
@import "datepicker/Calendar";
@import "datepicker/RangePicker";
@import "datepicker/Time";
@import "datepicker/TimePanel";
@import "datepicker/MonthPanel";

View File

@ -0,0 +1,114 @@
@input-box-height: 35px;
.@{calendar-prefix-cls}-range-picker-input {
background-color: transparent;
border: 0;
height: 18px;
line-height: 18px;
width: 44%;
&[disabled] {
cursor: not-allowed;
}
}
.@{calendar-prefix-cls}-range {
width: 502px;
overflow: hidden;
&-part {
width: 250px;
position: relative;
}
&-left {
float: left;
}
&-right {
float: right;
}
&-middle {
position: absolute;
left: 50%;
width: 20px;
margin-left: -10px;
text-align: center;
height: @input-box-height;
line-height: @input-box-height;
}
.@{calendar-prefix-cls}-date-input-wrap,
.@{calendar-prefix-cls}-time-picker-wrap {
width: 47%;
}
.@{calendar-prefix-cls}-time-picker-wrap {
position: absolute;
left: 50%;
}
.@{css-prefix}time-picker-input {
width: 100%;
}
.@{calendar-prefix-cls}-input-wrap {
height: @input-box-height;
}
.@{calendar-prefix-cls}-input {
border: 1px solid @border-color-base;
border-radius: @border-radius-base;
}
.@{calendar-prefix-cls}-input,
.@{css-prefix}time-picker-input {
padding: 1px 7px;
height: @input-height-sm;
}
.@{css-prefix}time-picker-icon {
display: none;
}
.@{calendar-prefix-cls}-calendar-body,
.@{calendar-prefix-cls}-decade-panel-body,
.@{calendar-prefix-cls}-year-panel-body,
.@{calendar-prefix-cls}-month-panel-body {
border-bottom: 1px solid #e9e9e9;
}
&.@{calendar-prefix-cls}-week-number {
width: 574px;
.@{calendar-prefix-cls}-range-part {
width: 286px;
}
}
.@{calendar-prefix-cls}-year-panel,
.@{calendar-prefix-cls}-month-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 {
background: tint(@primary-color, 90%);
border-radius: 0;
}
&-bottom {
text-align: right;
}
.@{calendar-prefix-cls}-ok-btn {
position: static;
margin: 7px 9px 9px;
}
.@{calendar-prefix-cls}-today-btn {
margin: 9px;
}
}

View File

@ -1,4 +1,5 @@
.@{timepicker-prefix-cls}-panel {
min-width: 168px;
z-index: 1070;
position: absolute;