Merge pull request #4882 from meerkat-morecats/feat-4651

fix: 修复DateRangePicker交互问题
* 修复 DateRangePicker取消按钮无法点击的问题
* 修复DateTime组件,时间选择器高度与日期对齐
* 修复 DateRangeTimePicker选中开始时间后,结束时间跳转到23:59
* 优化 对无法选择的日期上增加disabled样式
This commit is contained in:
RUNZE LU 2022-07-20 17:57:16 +08:00 committed by GitHub
commit f848062a2c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
5 changed files with 150 additions and 72 deletions

View File

@ -17,8 +17,6 @@
}
&:not(.is-disabled) {
cursor: pointer;
&:hover {
background: var(--DatePicker-onHover-bg);
border-color: var(--DatePicker-onHover-borderColor);
@ -40,7 +38,7 @@
.#{$ns}DateRangePicker-input {
border: none;
border-bottom: 1px solid transparent;
border-bottom: 2px solid transparent;
outline: none;
padding: 0;
background: 0;
@ -49,7 +47,7 @@
}
.#{$ns}DateRangePicker-input.isActive {
border-bottom: 1px solid var(--DatePicker-onFocused-borderColor);
border-bottom: 2px solid var(--Form-input-onHover-borderColor);
}
.#{$ns}DateRangePicker-input-separator {

View File

@ -289,7 +289,6 @@
.rdtActive.rdtToday {
color: #151b26;
> span {
background: transparent;
border-color: #144bcc;
}
}
@ -334,12 +333,21 @@
}
}
.#{$ns}CalendarTimesWrapper {
display: flex;
flex-direction: column;
height: 100%;
.#{$ns}CalendarInputsWrapper {
height: #{px2rem(248px)};
}
}
.#{$ns}CalendarInputWrapper {
display: inline-block;
position: relative;
// padding-top: #{px2rem(4px)};
width: #{px2rem(64px)};
height: #{px2rem(224px)};
height: #{px2rem(248px)};
overflow: auto;
.#{$ns}CalendarInput-sugs {
list-style: none;
@ -350,10 +358,10 @@
top: 100%;
z-index: 10;
&Hours {
height: #{px2rem(868px)};
height: #{px2rem(892px)};
}
&Times {
height: #{px2rem(1876px)};
height: #{px2rem(1900px)};
border-left: 1px solid var(--Calendar-input-borderColor);
}
&Item {
@ -610,28 +618,39 @@ td.rdtQuarter {
}
}
.#{$ns}DateRangePicker-start {
.rdtPicker .rdtActive.rdtBetween {
background: linear-gradient(
to right,
transparent 0%,
transparent 50%,
var(--Calendar-cell-onBetween-bg) 51%,
var(--Calendar-cell-onBetween-bg) 100%
);
.rdtPicker .rdtActive.rdtBetween.rdtStartDay {
background: linear-gradient(
to right,
transparent 0%,
transparent 50%,
var(--Calendar-cell-onBetween-bg) 51%,
var(--Calendar-cell-onBetween-bg) 100%
);
}
.rdt .rdtPicker td.is-disabled {
cursor: not-allowed !important;
background: var(--Calendar-cell-onDisabled-bg);
color: var(--text--muted-color);
&:hover {
background: var(--Calendar-cell-onDisabled-bg);
color: var(--text--muted-color);
& > span {
background: inherit !important;
color: inherit !important;
}
}
}
.#{$ns}DateRangePicker-end {
.rdtPicker .rdtActive.rdtBetween {
background: linear-gradient(
to right,
var(--Calendar-cell-onBetween-bg) 0%,
var(--Calendar-cell-onBetween-bg) 50%,
transparent 51%,
transparent 100%
);
}
.rdtPicker .rdtActive.rdtBetween.rdtEndDay {
background: linear-gradient(
to right,
var(--Calendar-cell-onBetween-bg) 0%,
var(--Calendar-cell-onBetween-bg) 50%,
transparent 51%,
transparent 100%
);
}
.#{$ns}DateCalendar {

View File

@ -356,11 +356,14 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
this.handlePopOverClick = this.handlePopOverClick.bind(this);
this.renderShortCuts = this.renderShortCuts.bind(this);
this.inputChange = this.inputChange.bind(this);
this.onInputBlur = this.onInputBlur.bind(this);
}
dom: HTMLDivElement;
inputRef: React.RefObject<HTMLInputElement>;
// 缓存上一次的input值
inputValueCache: string;
componentDidMount() {
this.props?.onRef?.(this);
@ -378,7 +381,7 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
newState.inputValue =
newState.value?.format(this.props.inputFormat) || '';
this.inputValueCache = newState.inputValue;
this.setState(newState);
}
}
@ -533,6 +536,12 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
}
}
onInputBlur() {
this.setState({
inputValue: this.inputValueCache
});
}
selectRannge(item: any) {
const {closeOnSelect} = this.props;
const now = moment();
@ -775,6 +784,7 @@ export class DatePicker extends React.Component<DateProps, DatePickerState> {
<Input
className={cx('DatePicker-input')}
onChange={this.inputChange}
onBlur={this.onInputBlur}
ref={this.inputRef}
placeholder={__(placeholder)}
autoComplete="off"

View File

@ -68,6 +68,7 @@ export interface DateRangePickerState {
editState?: 'start' | 'end'; // 编辑开始时间还是结束时间
startInputValue?: string;
endInputValue?: string;
endDateOpenedFirst: boolean;
}
export const availableRanges: {[propName: string]: any} = {
@ -430,7 +431,8 @@ export class DateRangePicker extends React.Component<
ranges: 'yesterday,7daysago,prevweek,thismonth,prevmonth,prevquarter',
resetValue: '',
closeOnSelect: true,
overlayPlacement: 'auto'
overlayPlacement: 'auto',
endDateOpenedFirst: false
};
innerDom: any;
@ -486,7 +488,8 @@ export class DateRangePicker extends React.Component<
dom: React.RefObject<HTMLDivElement>;
calendarRef: React.RefObject<HTMLDivElement>;
nextMonth = moment().add(1, 'months');
nextMonth = moment().add(1, 'months').startOf('day');
currentMonth = moment().startOf('day');
startInputRef: React.RefObject<HTMLInputElement>;
endInputRef: React.RefObject<HTMLInputElement>;
@ -505,7 +508,7 @@ export class DateRangePicker extends React.Component<
this.endInputChange = this.endInputChange.bind(this);
this.handleDateChange = this.handleDateChange.bind(this);
this.handleStartDateChange = this.handleStartDateChange.bind(this);
this.handeleEndDateChange = this.handeleEndDateChange.bind(this);
this.handelEndDateChange = this.handelEndDateChange.bind(this);
this.handleTimeStartChange = this.handleTimeStartChange.bind(this);
this.handleTimeEndChange = this.handleTimeEndChange.bind(this);
this.handleFocus = this.handleFocus.bind(this);
@ -540,7 +543,8 @@ export class DateRangePicker extends React.Component<
oldStartDate: startDate,
oldEndDate: endDate,
startInputValue: startDate?.format(inputFormat),
endInputValue: endDate?.format(inputFormat)
endInputValue: endDate?.format(inputFormat),
endDateOpenedFirst: false
};
}
componentDidMount() {
@ -646,7 +650,8 @@ export class DateRangePicker extends React.Component<
}
this.setState({
isOpened: true,
editState: 'end'
editState: 'end',
endDateOpenedFirst: true
});
}
@ -660,11 +665,17 @@ export class DateRangePicker extends React.Component<
startDate: oldStartDate,
startInputValue: oldStartDate ? oldStartDate.format(inputFormat) : ''
});
} else {
this.setState({
oldStartDate: this.state.startDate,
oldEndDate: this.state.endDate
});
}
this.setState(
{
isOpened: false,
editState: undefined
editState: undefined,
endDateOpenedFirst: false
},
this.blur
);
@ -689,7 +700,10 @@ export class DateRangePicker extends React.Component<
confirm() {
if (!this.state.startDate && !this.state.endDate) {
return;
} else if (this.state.startDate?.isAfter(this.state.endDate)) {
} else if (
this.state.endDate &&
this.state.startDate?.isAfter(this.state.endDate)
) {
return;
}
@ -706,7 +720,7 @@ export class DateRangePicker extends React.Component<
)
);
if (this.state.startDate && !this.state.endDate) {
this.setState({editState: 'end'});
this.setState({editState: 'end', endDateOpenedFirst: false});
} else {
this.close(true);
}
@ -743,13 +757,13 @@ export class DateRangePicker extends React.Component<
if (editState === 'start') {
this.handleStartDateChange(newValue);
} else if (editState === 'end') {
this.handeleEndDateChange(newValue);
this.handelEndDateChange(newValue);
}
}
handleStartDateChange(newValue: moment.Moment) {
const {timeFormat, minDate, inputFormat, type} = this.props;
let {startDate} = this.state;
let {startDate, endDateOpenedFirst} = this.state;
if (minDate && newValue.isBefore(minDate)) {
newValue = minDate;
}
@ -761,46 +775,41 @@ export class DateRangePicker extends React.Component<
);
const newState = {
startDate: date,
oldStartDate: startDate,
startInputValue: date.format(inputFormat)
} as any;
// 这些没有时间的选择点第一次后第二次就是选结束时间
if (
type === 'input-date-range' ||
type === 'input-year-range' ||
type === 'input-quarter-range' ||
type === 'input-month-range'
!endDateOpenedFirst &&
(type === 'input-date-range' ||
type === 'input-year-range' ||
type === 'input-quarter-range' ||
type === 'input-month-range')
) {
newState.editState = 'end';
}
this.setState(newState);
}
handeleEndDateChange(newValue: moment.Moment) {
const {embed, timeFormat, inputFormat} = this.props;
let {startDate, endDate} = this.state;
handelEndDateChange(newValue: moment.Moment) {
const {embed, timeFormat, inputFormat, type} = this.props;
let {startDate, endDate, endDateOpenedFirst} = this.state;
newValue = this.getEndDateByDuration(newValue);
// 如果结束时间在前面,需要清空开始时间
if (newValue.isBefore(startDate)) {
this.setState({
startDate: undefined,
oldStartDate: startDate,
startInputValue: ''
});
}
const editState = endDateOpenedFirst ? 'start' : 'end';
const date = this.filterDate(newValue, endDate, timeFormat, 'end');
this.setState(
{
endDate: date,
oldEndDate: endDate,
endInputValue: date.format(inputFormat)
},
() => {
embed && this.confirm();
}
);
if (type !== 'input-datetime-range') {
this.setState({editState});
}
}
// 手动控制输入时间
@ -1155,7 +1164,7 @@ export class DateRangePicker extends React.Component<
}
renderDay(props: any, currentDate: moment.Moment) {
let {startDate, endDate} = this.state;
let {startDate, endDate, endDateOpenedFirst, editState} = this.state;
if (
startDate &&
@ -1165,8 +1174,19 @@ export class DateRangePicker extends React.Component<
props.className += ' rdtBetween';
}
if (startDate && currentDate.isSame(startDate, 'day')) {
props.className += ' rdtActive rdtStartDay';
}
if (endDate && currentDate.isSame(endDate, 'day')) {
props.className += ' rdtActive rdtEndDay';
}
const {className, ...others} = this.getDisabledElementProps(currentDate);
props.className += className;
return (
<td {...props}>
<td {...props} {...others}>
<span>{currentDate.date()}</span>
</td>
);
@ -1189,8 +1209,11 @@ export class DateRangePicker extends React.Component<
props.className += ' rdtBetween';
}
const {className, ...others} = this.getDisabledElementProps(currentDate);
props.className += className;
return (
<td {...props}>
<td {...props} {...others}>
<span>{monthStrFixedLength}</span>
</td>
);
@ -1208,8 +1231,11 @@ export class DateRangePicker extends React.Component<
props.className += ' rdtBetween';
}
const {className, ...others} = this.getDisabledElementProps(currentDate);
props.className += className;
return (
<td {...props}>
<td {...props} {...others}>
<span>Q{quarter}</span>
</td>
);
@ -1226,8 +1252,11 @@ export class DateRangePicker extends React.Component<
props.className += ' rdtBetween';
}
const {className, ...others} = this.getDisabledElementProps(currentDate);
props.className += className;
return (
<td {...props}>
<td {...props} {...others}>
<span>{year}</span>
</td>
);
@ -1251,8 +1280,9 @@ export class DateRangePicker extends React.Component<
const {startDate, endDate, editState} = this.state;
const isDateTimeRange = type === 'input-datetime-range';
// timeRange需要单独选择范围
const isTimeRange = type === 'input-datetime-range' || viewMode === 'time';
const isTimeRange = isDateTimeRange || viewMode === 'time';
return (
<div className={cx(`${ns}DateRangePicker-wrap`)} ref={this.calendarRef}>
@ -1264,7 +1294,7 @@ export class DateRangePicker extends React.Component<
// 区分的原因是 time-range 左侧就只能选起始时间,而其它都能在左侧同时同时选择起始和结束
// TODO: 后续得把 time-range 代码拆分出来
onChange={
type === 'input-datetime-range'
isDateTimeRange
? this.handleStartDateChange
: viewMode === 'time'
? this.handleTimeStartChange
@ -1291,8 +1321,8 @@ export class DateRangePicker extends React.Component<
className={`${ns}DateRangePicker-end`}
value={endDate}
onChange={
type === 'input-datetime-range'
? this.handeleEndDateChange
isDateTimeRange
? this.handelEndDateChange
: viewMode === 'time'
? this.handleTimeEndChange
: this.handleDateChange
@ -1301,8 +1331,8 @@ export class DateRangePicker extends React.Component<
dateFormat={dateFormat}
inputFormat={inputFormat}
timeFormat={timeFormat}
viewDate={this.nextMonth}
isEndDate
viewDate={isDateTimeRange ? this.currentMonth : this.nextMonth}
// isEndDate
isValidDate={this.checkEndIsValidDate}
viewMode={viewMode}
input={false}
@ -1320,7 +1350,7 @@ export class DateRangePicker extends React.Component<
<div key="button" className={`${ns}DateRangePicker-actions`}>
<a
className={cx('Button', 'Button--default')}
onClick={() => this.close}
onClick={() => this.close()}
>
{__('cancel')}
</a>
@ -1331,7 +1361,8 @@ export class DateRangePicker extends React.Component<
isTimeRange &&
editState === 'start') ||
(!this.state.endDate && isTimeRange && editState === 'end') ||
this.state.endDate?.isBefore(this.state.startDate)
(this.state.startDate &&
this.state.endDate?.isBefore(this.state.startDate))
})}
onClick={this.confirm}
>
@ -1343,6 +1374,23 @@ export class DateRangePicker extends React.Component<
);
}
getDisabledElementProps(currentDate: moment.Moment) {
const {endDateOpenedFirst, endDate, startDate, editState} = this.state;
const afterEndDate = editState === 'start' && currentDate > endDate!;
const beforeStartDate = editState === 'end' && currentDate < startDate!;
if (afterEndDate || beforeStartDate) {
return {
className: ' is-disabled',
onClick: undefined
};
}
return {
className: ''
};
}
render() {
const {
className,

View File

@ -531,8 +531,6 @@ export class CustomDaysView extends React.Component<CustomDaysViewProps> {
const inputs: Array<React.ReactNode> = [];
const timeConstraints = this.timeConstraints;
inputs.push(this.showTime());
timeFormat.split(':').forEach((format, i) => {
const type = /h/i.test(format)
? 'hours'
@ -631,7 +629,12 @@ export class CustomDaysView extends React.Component<CustomDaysViewProps> {
}
});
inputs.length && inputs.pop();
return <div>{inputs}</div>;
return (
<div className={cx('CalendarTimesWrapper')}>
{this.showTime()}
<div className={cx('CalendarInputsWrapper')}>{inputs}</div>
</div>
);
};
renderFooter = () => {