DatePicker 组件优化

+ DatePicker showTime: 面板上的日期展示输入框统一为一个,格式和外面的输入框同步,并且支持手动修改。
 面板上的 TimePicker
 + DatePicker showTime: 输入框不再展示,改造为『选择时间』,点击后不再展开浮层,直接盖住日期区域。
 + RangePicker showTime : 只选中开始日期,“确定”和“选择时间”灰置。 如未选择日期直接选择时间,开始日期和结束日期默认选中当天。
 + RangePicker showTime : 点击框外和确定均为确定操作。
 + RangePicker showTime : 在时间页面,开始时间的默认状态为当前时间,结束时间的默认状态跟随开始时间。
 + RangePicker showTime : 开始时间的选择范围没有限制,结束时间的选择范围必须大于等于开始时间。
 + RangePicker showTime : 当开始时间选择了结束时间之后的时间(发生冲突),结束时间则自动切换到与开始时间相同的时间。
This commit is contained in:
RaoHai 2016-07-18 16:44:30 +08:00
parent 3342aae9f3
commit 7dd4a019d5
9 changed files with 367 additions and 42 deletions

View File

@ -78,7 +78,9 @@ export default class RangePicker extends React.Component {
onOk: this.handleChange, onOk: this.handleChange,
}; };
if (props.timePicker) { if (props.timePicker) {
pickerChangeHandler = {}; pickerChangeHandler.onChange = (value) => {
this.handleChange(value);
};
} else { } else {
calendarHandler = {}; calendarHandler = {};
} }
@ -91,6 +93,7 @@ export default class RangePicker extends React.Component {
const calendar = ( const calendar = (
<RangeCalendar <RangeCalendar
prefixCls="ant-calendar" prefixCls="ant-calendar"
formatter={props.getFormatter()}
className={calendarClassName} className={calendarClassName}
timePicker={props.timePicker} timePicker={props.timePicker}
disabledDate={disabledDate} disabledDate={disabledDate}

View File

@ -29,7 +29,7 @@ const DateRange = React.createClass({
if (!startValue || !this.state.endValue) { if (!startValue || !this.state.endValue) {
return false; return false;
} }
return startValue.getTime() >= this.state.endValue.getTime(); return startValue.getTime() > this.state.endValue.getTime();
}, },
disabledEndDate(endValue) { disabledEndDate(endValue) {
if (!endValue || !this.state.startValue) { if (!endValue || !this.state.startValue) {
@ -38,7 +38,6 @@ const DateRange = React.createClass({
return endValue.getTime() <= this.state.startValue.getTime(); return endValue.getTime() <= this.state.startValue.getTime();
}, },
onChange(field, value) { onChange(field, value) {
console.log(field, 'change', value);
this.setState({ this.setState({
[field]: value, [field]: value,
}); });

View File

@ -9,7 +9,7 @@ const DatePicker = wrapPicker(createPicker(RcCalendar));
const MonthPicker = wrapPicker(createPicker(MonthCalendar), 'yyyy-MM'); const MonthPicker = wrapPicker(createPicker(MonthCalendar), 'yyyy-MM');
DatePicker.Calendar = Calendar; DatePicker.Calendar = Calendar;
DatePicker.RangePicker = wrapPicker(RangePicker, 'yyyy-MM-dd'); DatePicker.RangePicker = wrapPicker(RangePicker);
DatePicker.MonthPicker = MonthPicker; DatePicker.MonthPicker = MonthPicker;
export default DatePicker; export default DatePicker;

View File

@ -106,7 +106,7 @@
.calendarPanelHeader(@calendar-prefix-cls); .calendarPanelHeader(@calendar-prefix-cls);
} }
&-calendar-body { &-body {
padding: 4px 8px; padding: 4px 8px;
} }
@ -163,6 +163,10 @@
text-align: center; text-align: center;
transition: background 0.3s ease; transition: background 0.3s ease;
&-panel {
position: relative;
}
&:hover { &:hover {
background: tint(@primary-color, 90%); background: tint(@primary-color, 90%);
cursor: pointer; cursor: pointer;
@ -286,6 +290,9 @@
&-disabled { &-disabled {
.button-color(@btn-disable-color; @btn-disable-bg; @btn-disable-border); .button-color(@btn-disable-color; @btn-disable-bg; @btn-disable-border);
cursor: not-allowed; cursor: not-allowed;
&:hover {
.button-color(@btn-disable-color; @btn-disable-bg; @btn-disable-border);
}
} }
} }
} }

View File

@ -26,16 +26,40 @@
width: 470px; width: 470px;
overflow: hidden; overflow: hidden;
.@{calendar-prefix-cls}-date-panel {
&::after {
content: ".";
display: block;
height: 0;
clear: both;
visibility: hidden;
}
}
&-part { &-part {
width: 50%; width: 50%;
position: relative;
} }
&-left { &-left {
float: left; float: left;
.@{calendar-prefix-cls} {
&-time-picker-inner {
border-right: 1px solid #e9e9e9;
}
}
} }
&-right { &-right {
float: right; float: right;
.@{calendar-prefix-cls} {
&-time-picker-inner {
margin-left: 21px;
border-left: 1px solid #e9e9e9;
}
&-time-picker-panel {
// padding-left: 22px;
}
}
} }
&-middle { &-middle {
@ -74,7 +98,12 @@
.input; .input;
border-radius: @border-radius-sm; border-radius: @border-radius-sm;
height: @input-height-sm; height: @input-height-sm;
width: 96px; border: 0;
box-shadow: none;
&:focus {
box-shadow: none;
}
} }
.@{timepicker-prefix-cls}-icon { .@{timepicker-prefix-cls}-icon {
display: none; display: none;
@ -132,14 +161,68 @@
&-bottom { &-bottom {
text-align: right; text-align: right;
} }
.@{calendar-prefix-cls} {
&-header {
border-bottom: 0;
}
&-body {
border-top: 1px solid #e9e9e9;
}
}
&.@{calendar-prefix-cls}-time {
.@{timepicker-prefix-cls} {
height: 207px;
top: 68px;
z-index: 2; // in order to cover .ant-calendar-range .ant-calendar-in-range-cell > div (z-index: 1)
&-panel {
height: 241px;
margin-top: -34px;
}
&-inner {
padding-top: 34px;
height: 241px;
background: none;
}
&-combobox {
display: inline-block;
background-color: white;
border-top: 1px solid #e9e9e9;
}
&-select {
width: 71px;
ul {
max-height: 206px;
}
}
}
.@{calendar-prefix-cls}-footer-btn {
padding-right: 12px;
display: block;
&::after {
content: 'x';
height: 0;
font-size: 0;
overflow: hidden;
clear: both;
}
}
.@{calendar-prefix-cls}-ok-btn { .@{calendar-prefix-cls}-ok-btn {
position: static; position: static;
height: 22px; height: 22px;
margin: 8px; }
.@{calendar-prefix-cls}-footer .@{calendar-prefix-cls}-time-picker-btn {
margin-right: 12px;
} }
.@{calendar-prefix-cls}-today-btn { .@{calendar-prefix-cls}-today-btn {
margin: 8px 12px; margin: 8px 12px;
height: 22px; height: 22px;
line-height: 22px; line-height: 22px;
} }
}
} }

View File

@ -1,32 +1,264 @@
.@{calendar-prefix-cls}-time {
// Change display order in DOM
.@{calendar-prefix-cls}-input-wrap { .@{timepicker-prefix-cls} {
direction: rtl; position: absolute;
.@{calendar-prefix-cls}-date-input-wrap, width: 100%;
.@{calendar-prefix-cls}-time-picker-wrap { top: 34px;
direction: ltr; background-color: white;
height: 206px;
&-panel {
z-index: @zindex-picker;
position: absolute;
width: 100%;
}
&-inner {
display: inline-block; display: inline-block;
position: relative;
outline: none;
list-style: none;
font-size: 12px;
text-align: left;
background-color: #fff;
background-clip: padding-box;
line-height: 1.5;
overflow: hidden;
} }
.@{timepicker-prefix-cls}-panel,
.@{calendar-prefix-cls}-clear-btn { &-input {
direction: ltr; margin: 0;
} padding: 0;
} border: 0;
.@{calendar-prefix-cls}-input, width: 100%;
.@{timepicker-prefix-cls}-input { cursor: auto;
.input; line-height: 1.5;
border-radius: @border-radius-sm; outline: 0;
height: @input-height-sm;
width: 96px; &-wrap {
margin-right: 6px;
}
.@{calendar-prefix-cls}-input {
padding-right: 21px;
}
.@{timepicker-prefix-cls}-panel {
min-width: 168px;
}
.@{timepicker-prefix-cls}-icon {
display: none; display: none;
box-sizing: border-box;
position: relative;
padding: 6px;
border-bottom: 1px solid @border-color-split;
}
&-invalid {
border-color: red;
}
}
&-clear-btn {
position: absolute;
right: 5px;
cursor: pointer;
overflow: hidden;
width: 20px;
height: 20px;
text-align: center;
line-height: 20px;
top: 5px;
margin: 0;
}
&-clear-btn:after {
content: "\e631";
font-family: "anticon";
font-size: 12px;
color: #ccc;
display: inline-block;
line-height: 1;
width: 20px;
transition: color 0.3s ease;
}
&-clear-btn:hover:after {
color: #999;
}
&-narrow &-input-wrap {
max-width: 111px;
}
&-select {
float: left;
font-size: 12px;
border: 1px solid @border-color-split;
border-width: 0 1px;
margin-left: -1px;
box-sizing: border-box;
width: 77px;
overflow: hidden;
position: relative; // Fix chrome weird render bug
&: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: 120px;
display: block;
}
li:hover {
background: tint(@primary-color, 90%);
}
li&-option-selected {
background: #f7f7f7;
font-weight: bold;
}
li&-option-disabled {
color: @btn-disable-color;
&:hover {
background: transparent;
cursor: @cursor-disabled;
}
}
}
&.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;
}
}
.@{timepicker-prefix-cls} {
display: inline-block;
outline: none;
font-size: @font-size-base;
&-input {
.input;
width: 100px;
}
&-large &-input {
.input-lg;
}
&-small &-input {
.input-sm;
}
&-open {
opacity: 0;
}
&-icon {
position: absolute;
user-select: none;
transition: all .3s @ease-in-out;
width: 12px;
height: 12px;
line-height: 12px;
right: 8px;
color: #999;
top: 50%;
margin-top: -6px;
&:after {
content: "\e643";
font-family: "anticon";
font-size: 12px;
color: #999;
display: inline-block;
line-height: 1;
vertical-align: bottom;
}
}
}
.@{calendar-prefix-cls} {
&-time {
.@{calendar-prefix-cls}-day-select {
padding: 0 2px;
font-weight: bold;
display: inline-block;
color: #666;
line-height: 34px;
}
.@{calendar-prefix-cls}-footer {
border-top: 1px solid #ccc;
padding: 10px 0;
text-align: right;
position: relative;
height: auto;
line-height: auto;
&-btn {
line-height: 1.5;
text-align: right;
}
.@{calendar-prefix-cls}-today-btn {
float: left;
margin: 0;
padding-left: 12px;
}
.@{calendar-prefix-cls}-time-picker-btn {
display: inline-block;
text-align: center;
margin-right: 60px;
&-disabled {
color: #ccc
}
}
}
} }
} }

View File

@ -4,7 +4,7 @@
@import "../../button/style/mixin"; @import "../../button/style/mixin";
@calendar-prefix-cls: ant-calendar; @calendar-prefix-cls: ant-calendar;
@timepicker-prefix-cls: ant-time-picker; @timepicker-prefix-cls: ant-calendar-time-picker;
@import "Picker"; @import "Picker";
@import "Calendar"; @import "Calendar";

View File

@ -1,6 +1,6 @@
import { PropTypes } from 'react'; import { PropTypes } from 'react';
import * as React from 'react'; import * as React from 'react';
import TimePicker from 'rc-time-picker'; import TimePickerPanel from 'rc-time-picker/lib/module/Panel';
import DateTimeFormat from 'gregorian-calendar-format'; import DateTimeFormat from 'gregorian-calendar-format';
import GregorianCalendar from 'gregorian-calendar'; import GregorianCalendar from 'gregorian-calendar';
import classNames from 'classnames'; import classNames from 'classnames';
@ -87,10 +87,10 @@ export default function wrapPicker(Picker, defaultFormat) {
showHour: timeFormat && timeFormat.indexOf('HH') >= 0, showHour: timeFormat && timeFormat.indexOf('HH') >= 0,
}; };
const timePicker = props.showTime ? ( const timePicker = props.showTime ? (
<TimePicker <TimePickerPanel
{...rcTimePickerProps} {...rcTimePickerProps}
{...props.showTime} {...props.showTime}
prefixCls="ant-time-picker" prefixCls="ant-calendar-time-picker"
placeholder={locale.timePickerLocale.placeholder} placeholder={locale.timePickerLocale.placeholder}
locale={locale.timePickerLocale} locale={locale.timePickerLocale}
transitionName="slide-up" transitionName="slide-up"

View File

@ -1,3 +1,4 @@
import * as React from 'react';
import { Component, PropTypes } from 'react'; import { Component, PropTypes } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import calculateNodeHeight from './calculateNodeHeight'; import calculateNodeHeight from './calculateNodeHeight';