mirror of
https://github.com/ant-design/ant-design.git
synced 2024-11-29 13:47:02 +08:00
Merge pull request #647 from ant-design/feat-range-datepicker
feat: add RangePicker
This commit is contained in:
commit
75048619a2
47
components/date-picker/PickerMixin.jsx
Normal file
47
components/date-picker/PickerMixin.jsx
Normal 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
|
||||
});
|
||||
},
|
||||
};
|
124
components/date-picker/RangePicker.jsx
Normal file
124
components/date-picker/RangePicker.jsx
Normal 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>);
|
||||
}
|
||||
});
|
18
components/date-picker/demo/time-range.md
Normal file
18
components/date-picker/demo/time-range.md
Normal 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'));
|
||||
````
|
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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",
|
||||
|
@ -9,6 +9,7 @@
|
||||
|
||||
@import "datepicker/Picker";
|
||||
@import "datepicker/Calendar";
|
||||
@import "datepicker/RangePicker";
|
||||
@import "datepicker/Time";
|
||||
@import "datepicker/TimePanel";
|
||||
@import "datepicker/MonthPanel";
|
||||
|
114
style/components/datepicker/RangePicker.less
Normal file
114
style/components/datepicker/RangePicker.less
Normal 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;
|
||||
}
|
||||
}
|
@ -1,4 +1,5 @@
|
||||
.@{timepicker-prefix-cls}-panel {
|
||||
min-width: 168px;
|
||||
z-index: 1070;
|
||||
position: absolute;
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user