From 927e8107b324d11733e86f3ee498d93f2cd52c23 Mon Sep 17 00:00:00 2001
From: xuzhendong666 <810792608@Outlook.com>
Date: Wed, 30 Jun 2021 23:37:10 +0800
Subject: [PATCH] =?UTF-8?q?=E5=90=8C=E6=AD=A51.2=E7=89=88=E6=9C=AC?=
=?UTF-8?q?=E7=9A=84=E6=97=B6=E9=97=B4=E8=8C=83=E5=9B=B4=E9=80=89=E6=8B=A9?=
=?UTF-8?q?=E6=94=B9=E5=8A=A8=20(#2183)?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
---
src/components/DateRangePicker.tsx | 56 ++++++++++++-----
src/components/MonthRangePicker.tsx | 80 ++++++++++++++++++++++--
src/components/calendar/Calendar.tsx | 17 ++++-
src/components/calendar/QuartersView.tsx | 52 +++++----------
4 files changed, 148 insertions(+), 57 deletions(-)
diff --git a/src/components/DateRangePicker.tsx b/src/components/DateRangePicker.tsx
index ac6cb634f..14114f546 100644
--- a/src/components/DateRangePicker.tsx
+++ b/src/components/DateRangePicker.tsx
@@ -45,6 +45,7 @@ export interface DateRangePickerProps extends ThemeProps, LocaleProps {
popOverContainer?: any;
dateFormat?: string;
embed?: boolean;
+ viewMode?: 'days' | 'months' | 'years' | 'time' | 'quarters';
}
export interface DateRangePickerState {
@@ -54,7 +55,7 @@ export interface DateRangePickerState {
endDate?: moment.Moment;
}
-const availableRanges: {[propName: string]: any} = {
+export const availableRanges: {[propName: string]: any} = {
'today': {
label: 'Date.today',
startDate: (now: moment.Moment) => {
@@ -262,6 +263,7 @@ export class DateRangePicker extends React.Component<
this.handleKeyPress = this.handleKeyPress.bind(this);
this.handlePopOverClick = this.handlePopOverClick.bind(this);
this.renderDay = this.renderDay.bind(this);
+ this.renderQuarter = this.renderQuarter.bind(this);
const {format, joinValues, delimiter, value} = this.props;
this.state = {
@@ -271,11 +273,11 @@ export class DateRangePicker extends React.Component<
};
}
- componentWillReceiveProps(nextProps: DateRangePickerProps) {
+ componentDidUpdate(prevProps: DateRangePickerProps) {
const props = this.props;
- const {value, format, joinValues, delimiter} = nextProps;
+ const {value, format, joinValues, delimiter} = props;
- if (props.value !== value) {
+ if (prevProps.value !== value) {
this.setState({
...DateRangePicker.unFormatValue(value, format, joinValues, delimiter)
});
@@ -384,6 +386,8 @@ export class DateRangePicker extends React.Component<
value = value[type === 'start' ? 'startOf' : 'endOf']('minute');
} else if (typeof timeFormat === 'string' && /HH/i.test(timeFormat)) {
value = value[type === 'start' ? 'startOf' : 'endOf']('hour');
+ } else if (typeof timeFormat === 'string' && /Q/i.test(timeFormat)) {
+ value = value[type === 'start' ? 'startOf' : 'endOf']('quarter');
} else {
value = value[type === 'start' ? 'startOf' : 'endOf']('day');
}
@@ -527,8 +531,8 @@ export class DateRangePicker extends React.Component<
checkStartIsValidDate(currentDate: moment.Moment) {
let {endDate, startDate} = this.state;
-
- let {minDate, maxDate, minDuration, maxDuration} = this.props;
+ let {minDate, maxDate, minDuration, maxDuration, viewMode} = this.props;
+ const precision = viewMode === 'time' ? 'hours' : viewMode || 'day';
maxDate =
maxDate && endDate
@@ -537,9 +541,9 @@ export class DateRangePicker extends React.Component<
: endDate
: maxDate || endDate;
- if (minDate && currentDate.isBefore(minDate, 'day')) {
+ if (minDate && currentDate.isBefore(minDate, precision)) {
return false;
- } else if (maxDate && currentDate.isAfter(maxDate, 'day')) {
+ } else if (maxDate && currentDate.isAfter(maxDate, precision)) {
return false;
} else if (
// 如果配置了 minDuration 那么 EndDate - minDuration 之后的天数也不能选
@@ -561,8 +565,8 @@ export class DateRangePicker extends React.Component<
checkEndIsValidDate(currentDate: moment.Moment) {
let {startDate} = this.state;
-
- let {minDate, maxDate, minDuration, maxDuration} = this.props;
+ let {minDate, maxDate, minDuration, maxDuration, viewMode} = this.props;
+ const precision = viewMode === 'time' ? 'hours' : viewMode || 'day';
minDate =
minDate && startDate
@@ -571,9 +575,9 @@ export class DateRangePicker extends React.Component<
: startDate
: minDate || startDate;
- if (minDate && currentDate.isBefore(minDate, 'day')) {
+ if (minDate && currentDate.isBefore(minDate, precision)) {
return false;
- } else if (maxDate && currentDate.isAfter(maxDate, 'day')) {
+ } else if (maxDate && currentDate.isAfter(maxDate, precision)) {
return false;
} else if (
startDate &&
@@ -606,18 +610,38 @@ export class DateRangePicker extends React.Component<
return
{currentDate.date()} | ;
}
+ renderQuarter(props: any, quarter: number, year: number) {
+ const currentDate = moment().year(year).quarter(quarter);
+ const {startDate, endDate} = this.state;
+
+ if (
+ startDate &&
+ endDate &&
+ currentDate.isBetween(startDate, endDate, 'quarter', '[]')
+ ) {
+ props.className += ' rdtBetween';
+ }
+
+ return (
+
+ Q{quarter}
+ |
+ );
+ }
+
renderCalendar() {
const {
classPrefix: ns,
classnames: cx,
dateFormat,
timeFormat,
+ inputFormat,
ranges,
locale,
- embed
+ embed,
+ viewMode = 'days'
} = this.props;
const __ = this.props.translate;
- let viewMode: 'days' | 'months' | 'years' | 'time' = 'days';
const {startDate, endDate} = this.state;
return (
@@ -630,12 +654,14 @@ export class DateRangePicker extends React.Component<
onChange={this.handleStartChange}
requiredConfirm={false}
dateFormat={dateFormat}
+ inputFormat={inputFormat}
timeFormat={timeFormat}
isValidDate={this.checkStartIsValidDate}
viewMode={viewMode}
input={false}
onClose={this.close}
renderDay={this.renderDay}
+ renderQuarter={this.renderQuarter}
locale={locale}
/>
@@ -645,6 +671,7 @@ export class DateRangePicker extends React.Component<
onChange={this.handleEndChange}
requiredConfirm={false}
dateFormat={dateFormat}
+ inputFormat={inputFormat}
timeFormat={timeFormat}
viewDate={this.nextMonth}
isEndDate
@@ -653,6 +680,7 @@ export class DateRangePicker extends React.Component<
input={false}
onClose={this.close}
renderDay={this.renderDay}
+ renderQuarter={this.renderQuarter}
locale={locale}
/>
diff --git a/src/components/MonthRangePicker.tsx b/src/components/MonthRangePicker.tsx
index 23058d6aa..aab6e5a2d 100644
--- a/src/components/MonthRangePicker.tsx
+++ b/src/components/MonthRangePicker.tsx
@@ -18,6 +18,8 @@ import {noop} from '../utils/helper';
import {LocaleProps, localeable} from '../locale';
import {DateRangePicker} from './DateRangePicker';
import capitalize from 'lodash/capitalize';
+import {ShortCuts, ShortCutDateRange} from './DatePicker';
+import {availableRanges} from './DateRangePicker';
export interface MonthRangePickerProps extends ThemeProps, LocaleProps {
className?: string;
@@ -27,7 +29,8 @@ export interface MonthRangePickerProps extends ThemeProps, LocaleProps {
format: string;
utc?: boolean;
inputFormat?: string;
- // ranges?: string | Array;
+ timeFormat?: string;
+ ranges?: string | Array;
clearable?: boolean;
minDate?: moment.Moment;
maxDate?: moment.Moment;
@@ -103,11 +106,11 @@ export class MonthRangePicker extends React.Component<
};
}
- componentWillReceiveProps(nextProps: MonthRangePickerProps) {
+ componentDidUpdate(prevProps: MonthRangePickerProps) {
const props = this.props;
- const {value, format, joinValues, delimiter} = nextProps;
+ const {value, format, joinValues, delimiter} = props;
- if (props.value !== value) {
+ if (prevProps.value !== value) {
this.setState({
...DateRangePicker.unFormatValue(value, format, joinValues, delimiter)
});
@@ -275,6 +278,68 @@ export class MonthRangePicker extends React.Component<
);
}
+ selectRannge(range: PlainObject) {
+ const {closeOnSelect, minDate, maxDate} = this.props;
+ this.setState(
+ {
+ startDate: minDate
+ ? moment.max(range.startDate(moment()), minDate)
+ : range.startDate(moment()),
+ endDate: maxDate
+ ? moment.min(maxDate, range.endDate(moment()))
+ : range.endDate(moment())
+ },
+ closeOnSelect ? this.confirm : noop
+ );
+ }
+
+ renderRanges(ranges: string | Array | undefined) {
+ if (!ranges) {
+ return null;
+ }
+ const {classPrefix: ns} = this.props;
+ let rangeArr: Array;
+ if (typeof ranges === 'string') {
+ rangeArr = ranges.split(',');
+ } else {
+ rangeArr = ranges;
+ }
+ const __ = this.props.translate;
+
+ return (
+
+ {rangeArr.map(item => {
+ if (!item) {
+ return null;
+ }
+ let range: PlainObject = {};
+ if (typeof item === 'string') {
+ range = availableRanges[item];
+ range.key = item;
+ } else if (
+ (item as ShortCutDateRange).startDate &&
+ (item as ShortCutDateRange).endDate
+ ) {
+ range = {
+ ...item,
+ startDate: () => (item as ShortCutDateRange).startDate,
+ endDate: () => (item as ShortCutDateRange).endDate
+ };
+ }
+ return (
+ - this.selectRannge(range)}
+ key={range.key || range.label}
+ >
+ {__(range.label)}
+
+ );
+ })}
+
+ );
+ }
+
clearValue(e: React.MouseEvent) {
e.preventDefault();
e.stopPropagation();
@@ -375,7 +440,7 @@ export class MonthRangePicker extends React.Component<
}
renderCalendar() {
- const {classPrefix: ns, classnames: cx, locale, embed} = this.props;
+ const {classPrefix: ns, classnames: cx, locale, embed, ranges, inputFormat, timeFormat} = this.props;
const __ = this.props.translate;
const viewMode: 'months' = 'months';
const dateFormat = 'YYYY-MM';
@@ -383,12 +448,15 @@ export class MonthRangePicker extends React.Component<
return (
+ {this.renderRanges(ranges)}
JSX.Element;
+ renderQuarter?: (
+ props: any,
+ quartar: number,
+ year?: number,
+ date?: moment.Moment
+ ) => JSX.Element;
}
class BaseDatePicker extends ReactDatePicker {
@@ -106,7 +112,7 @@ class BaseDatePicker extends ReactDatePicker {
.startOf(type),
currentView: nextViews[type]
});
- this.props.onViewModeChange!(nextViews[type]);
+ this.props.onViewModeChange?.(nextViews[type]);
};
};
@@ -135,6 +141,7 @@ class BaseDatePicker extends ReactDatePicker {
date = viewDate
.clone()
.quarter(parseInt(target.getAttribute('data-value')!, 10))
+ .startOf('quarter')
.date(currentDate.date());
} else if (target.className.indexOf('rdtYear') !== -1) {
date = viewDate
@@ -173,12 +180,18 @@ class BaseDatePicker extends ReactDatePicker {
render() {
const Component = CustomCalendarContainer as any;
+ const viewProps = this.getComponentProps();
+
+ if (this.props.viewMode === 'quarters') {
+ [viewProps.updateOn, viewProps.renderQuarter] = ['quarters', this.props.renderQuarter];
+ }
+
return (
diff --git a/src/components/calendar/QuartersView.tsx b/src/components/calendar/QuartersView.tsx
index 331d6d0ea..f9895f88c 100644
--- a/src/components/calendar/QuartersView.tsx
+++ b/src/components/calendar/QuartersView.tsx
@@ -1,3 +1,4 @@
+import moment from 'moment';
import React from 'react';
import {localeable, LocaleProps} from '../../locale';
import {ThemeProps} from '../../theme';
@@ -21,12 +22,10 @@ export interface QuarterViewProps extends LocaleProps, ThemeProps {
showView: (view: string) => () => void;
updateSelectedDate: (e: any, close?: boolean) => void;
renderQuarter: any;
- isValidDate: any;
+ isValidDate: (date: moment.Moment) => boolean;
}
export class QuarterView extends React.Component {
- alwaysValidDate: any;
-
renderYear() {
const __ = this.props.translate;
const showYearHead = !/^mm$/i.test(this.props.inputFormat || '');
@@ -67,47 +66,24 @@ export class QuarterView extends React.Component {
}
renderQuarters() {
let date = this.props.selectedDate,
- month = this.props.viewDate.month(),
+ quarter = this.props.viewDate.quarter(),
year = this.props.viewDate.year(),
rows = [],
i = 1,
- months = [],
+ quarters = [],
renderer = this.props.renderQuarter || this.renderQuarter,
isValid = this.props.isValidDate || this.alwaysValidDate,
classes,
props: any,
- currentMonth: moment.Moment,
- isDisabled,
- noOfDaysInMonth,
- daysInMonth,
- validDay,
- // Date is irrelevant because we're only interested in month
- irrelevantDate = 1;
+ isDisabled;
+
while (i < 5) {
classes = 'rdtQuarter';
- currentMonth = this.props.viewDate
- .clone()
- .set({year: year, quarter: i, date: irrelevantDate});
-
- noOfDaysInMonth = currentMonth.endOf('quarter').format('Q');
- daysInMonth = Array.from(
- {length: parseInt(noOfDaysInMonth, 10)},
- function (e, i) {
- return i + 1;
- }
- );
-
- validDay = daysInMonth.find(function (d) {
- var day = currentMonth.clone().set('date', d);
- return isValid(day);
- });
-
- isDisabled = validDay === undefined;
+ isDisabled = !isValid(moment(`${year}-${i}`, 'YYYY-Q'));
if (isDisabled) classes += ' rdtDisabled';
- if (date && i === date.quarter() && year === date.year())
- classes += ' rdtActive';
+ if (date && i === date.quarter() && year === date.year()) classes += ' rdtActive';
props = {
'key': i,
@@ -122,13 +98,13 @@ export class QuarterView extends React.Component {
: this.props.setDate('quarter');
}
- months.push(renderer(props, i, year, date && date.clone()));
+ quarters.push(renderer(props, i, year, date && date.clone()));
- if (months.length === 2) {
+ if (quarters.length === 2) {
rows.push(
- React.createElement('tr', {key: month + '_' + rows.length}, months)
+ React.createElement('tr', {key: quarter + '_' + rows.length}, quarters)
);
- months = [];
+ quarters = [];
}
i++;
@@ -154,6 +130,10 @@ export class QuarterView extends React.Component {
this.props.updateSelectedDate(event);
};
+ alwaysValidDate() {
+ return true;
+ }
+
render() {
const {classnames: cx} = this.props;