| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396 | 
							- /* eslint-disable jsx-a11y/click-events-have-key-events,jsx-a11y/no-noninteractive-element-interactions */
 
- /* eslint-disable max-len */
 
- import React from 'react';
 
- import classNames from 'classnames';
 
- import PropTypes from 'prop-types';
 
- import MonthFoundation, { MonthAdapter, MonthDayInfo, MonthFoundationProps, MonthFoundationState } from '@douyinfe/semi-foundation/datePicker/monthFoundation';
 
- import { cssClasses, numbers } from '@douyinfe/semi-foundation/datePicker/constants';
 
- import BaseComponent, { BaseProps } from '../_base/baseComponent';
 
- import { isBefore, isAfter, isBetween, isSameDay } from '@douyinfe/semi-foundation/datePicker/_utils/index';
 
- import { noop, stubFalse, isFunction } from 'lodash';
 
- import { parseISO } from 'date-fns';
 
- import { Locale } from '../locale/interface';
 
- const prefixCls = cssClasses.PREFIX;
 
- export interface MonthProps extends MonthFoundationProps, BaseProps {
 
-     forwardRef: React.Ref<any>;
 
-     locale: Locale['DatePicker'];
 
-     focusRecordsRef: React.RefObject<{ rangeStart: boolean; rangeEnd: boolean }>
 
- }
 
- export type MonthState = MonthFoundationState;
 
- export default class Month extends BaseComponent<MonthProps, MonthState> {
 
-     static propTypes = {
 
-         month: PropTypes.object,
 
-         selected: PropTypes.object,
 
-         rangeStart: PropTypes.string,
 
-         rangeEnd: PropTypes.string,
 
-         offsetRangeStart: PropTypes.string,
 
-         offsetRangeEnd: PropTypes.string,
 
-         onDayClick: PropTypes.func,
 
-         onDayHover: PropTypes.func,
 
-         weekStartsOn: PropTypes.number,
 
-         disabledDate: PropTypes.func,
 
-         weeksRowNum: PropTypes.number,
 
-         onWeeksRowNumChange: PropTypes.func,
 
-         renderDate: PropTypes.func,
 
-         renderFullDate: PropTypes.func,
 
-         hoverDay: PropTypes.string, // Real-time hover date
 
-         startDateOffset: PropTypes.func,
 
-         endDateOffset: PropTypes.func,
 
-         rangeInputFocus: PropTypes.oneOfType([PropTypes.string, PropTypes.bool]),
 
-         focusRecordsRef: PropTypes.object,
 
-         multiple: PropTypes.bool,
 
-     };
 
-     static defaultProps = {
 
-         month: new Date(),
 
-         selected: new Set(),
 
-         rangeStart: '',
 
-         rangeEnd: '',
 
-         onDayClick: noop,
 
-         onDayHover: noop,
 
-         onWeeksRowNumChange: noop,
 
-         weekStartsOn: numbers.WEEK_START_ON,
 
-         disabledDate: stubFalse,
 
-         weeksRowNum: 0,
 
-     };
 
-     monthRef: React.RefObject<HTMLDivElement>;
 
-     foundation: MonthFoundation;
 
-     constructor(props: MonthProps) {
 
-         super(props);
 
-         this.state = {
 
-             weekdays: [],
 
-             month: { weeks: [], monthText: '' },
 
-             todayText: '',
 
-             weeksRowNum: props.weeksRowNum,
 
-         };
 
-         this.monthRef = React.createRef();
 
-     }
 
-     get adapter(): MonthAdapter {
 
-         return {
 
-             ...super.adapter,
 
-             updateToday: todayText => this.setState({ todayText }),
 
-             setWeekDays: weekdays => this.setState({ weekdays }),
 
-             setWeeksRowNum: (weeksRowNum, callback) => this.setState({ weeksRowNum }, callback),
 
-             updateMonthTable: month => this.setState({ month }),
 
-             notifyDayClick: day => this.props.onDayClick(day),
 
-             notifyDayHover: day => this.props.onDayHover(day),
 
-             notifyWeeksRowNumChange: weeksRowNum => this.props.onWeeksRowNumChange(weeksRowNum),
 
-         };
 
-     }
 
-     componentDidMount() {
 
-         this.foundation = new MonthFoundation(this.adapter);
 
-         this.foundation.init();
 
-     }
 
-     componentWillUnmount() {
 
-         this.foundation.destroy();
 
-     }
 
-     componentDidUpdate(prevProps: MonthProps, prevState: MonthState) {
 
-         if (prevProps.month !== this.props.month) {
 
-             this.foundation.getMonthTable();
 
-         }
 
-     }
 
-     getSingleDayStatus(options: Partial<MonthProps> & { fullDate: string; todayText: string }) {
 
-         const { rangeInputFocus } = this.props;
 
-         const { fullDate, todayText, selected, disabledDate, rangeStart, rangeEnd } = options;
 
-         const disabledOptions = { rangeStart, rangeEnd, rangeInputFocus };
 
-         const isToday = fullDate === todayText;
 
-         const isSelected = selected.has(fullDate);
 
-         let isDisabled = disabledDate && disabledDate(parseISO(fullDate), disabledOptions);
 
-         if (
 
-             !isDisabled &&
 
-             this.props.rangeInputFocus === 'rangeStart' &&
 
-             rangeEnd &&
 
-             this.props.focusRecordsRef &&
 
-             this.props.focusRecordsRef.current.rangeEnd
 
-         ) {
 
-             // The reason for splitting is that the dateRangeTime format: 'yyyy-MM-dd HH:MM:SS'
 
-             isDisabled = isAfter(fullDate, rangeEnd.trim().split(/\s+/)[0]);
 
-         }
 
-         if (
 
-             !isDisabled &&
 
-             this.props.rangeInputFocus === 'rangeEnd' &&
 
-             rangeStart &&
 
-             this.props.focusRecordsRef &&
 
-             this.props.focusRecordsRef.current.rangeStart
 
-         ) {
 
-             // The reason for splitting is that the dateRangeTime format: 'yyyy-MM-dd HH:MM:SS'
 
-             isDisabled = isBefore(fullDate, rangeStart.trim().split(/\s+/)[0]);
 
-         }
 
-         return {
 
-             isToday, // Today
 
-             isSelected, // Selected
 
-             isDisabled // Disabled
 
-         };
 
-     }
 
-     getDateRangeStatus(options: Partial<MonthProps> & { fullDate: string }) {
 
-         const { rangeStart, rangeEnd, fullDate, hoverDay, offsetRangeStart, offsetRangeEnd, rangeInputFocus } = options;
 
-         // If no item is selected, return the empty object directly
 
-         const _isDateRangeAnySelected = Boolean(rangeStart || rangeEnd);
 
-         const _isDateRangeSelected = Boolean(rangeStart && rangeEnd);
 
-         const _isOffsetDateRangeAnyExist = offsetRangeStart || offsetRangeEnd;
 
-         if (!_isDateRangeAnySelected) {
 
-             return ({});
 
-         }
 
-         // The range selects the hover date, and the normal hover is .semi-datepicker-main: hover
 
-         const _isHoverDay = isSameDay(hoverDay, fullDate);
 
-         // When one is selected
 
-         // eslint-disable-next-line one-var
 
-         let _isHoverAfterStart, _isHoverBeforeEnd, isSelectedStart, isSelectedEnd, isHoverDayAroundOneSelected;
 
-         if (rangeStart) {
 
-             isSelectedStart = isSameDay(fullDate, rangeStart);
 
-             if (rangeInputFocus === 'rangeEnd') {
 
-                 _isHoverAfterStart = isBetween(fullDate, { start: rangeStart, end: hoverDay });
 
-             }
 
-         }
 
-         if (rangeEnd) {
 
-             isSelectedEnd = isSameDay(fullDate, rangeEnd);
 
-             if (rangeInputFocus === 'rangeStart') {
 
-                 _isHoverBeforeEnd = isBetween(fullDate, { start: hoverDay, end: rangeEnd });
 
-             }
 
-         }
 
-         if (!_isDateRangeSelected && _isDateRangeAnySelected) {
 
-             isHoverDayAroundOneSelected = _isHoverDay;
 
-         }
 
-         // eslint-disable-next-line one-var
 
-         let isHover;
 
-         if (!_isOffsetDateRangeAnyExist) {
 
-             isHover = _isHoverAfterStart || _isHoverBeforeEnd || _isHoverDay;
 
-         }
 
-         // Select all
 
-         // eslint-disable-next-line one-var
 
-         let isInRange, isSelectedStartAfterHover, isSelectedEndBeforeHover, isHoverDayInStartSelection, isHoverDayInEndSelection, isHoverDayInRange;
 
-         if (_isDateRangeSelected) {
 
-             isInRange = isBetween(fullDate, { start: rangeStart, end: rangeEnd });
 
-             if (!_isOffsetDateRangeAnyExist) {
 
-                 isSelectedStartAfterHover = isSelectedStart && isAfter(rangeStart, hoverDay);
 
-                 isSelectedEndBeforeHover = isSelectedEnd && isBefore(rangeEnd, hoverDay);
 
-                 isHoverDayInStartSelection = _isHoverDay && rangeInputFocus === 'rangeStart';
 
-                 isHoverDayInEndSelection = _isHoverDay && rangeInputFocus === 'rangeEnd';
 
-                 isHoverDayInRange = _isHoverDay && isBetween(hoverDay, { start: rangeStart, end: rangeEnd });
 
-             }
 
-         }
 
-         return {
 
-             isHoverDay: _isHoverDay, // Is the current hover date
 
-             isSelectedStart, // Select Start
 
-             isSelectedEnd, // End of selection
 
-             isInRange, // Range within the selected date
 
-             isHover, // Date between selection and hover date
 
-             isSelectedStartAfterHover, // Choose to start behind the hover
 
-             isSelectedEndBeforeHover, // Choose to end in front of the hover
 
-             isHoverDayInRange, // Hover date within range
 
-             isHoverDayInStartSelection, // Hover date when starting Date is selected
 
-             isHoverDayInEndSelection, // Hover date when endDate is selected
 
-             isHoverDayAroundOneSelected, // Hover date and select a date
 
-         };
 
-     }
 
-     getOffsetDateStatus(options: Partial<MonthProps> & { fullDate: string }) {
 
-         const { offsetRangeStart, offsetRangeEnd, rangeStart, rangeEnd, fullDate, hoverDay } = options;
 
-         // When there is no offset, return the empty object directly
 
-         const _isOffsetDateRangeNull = !(offsetRangeStart || offsetRangeEnd);
 
-         if (_isOffsetDateRangeNull) {
 
-             return ({});
 
-         }
 
-         // Range Select base date
 
-         const _isInRange = isBetween(fullDate, { start: rangeStart, end: rangeEnd });
 
-         const _isHoverDay = isSameDay(hoverDay, fullDate);
 
-         const _isSelectedStart = rangeStart && isSameDay(fullDate, rangeStart);
 
-         const _isSelectedEnd = rangeEnd && isSameDay(fullDate, rangeEnd);
 
-         const _isDateRangeSelected = Boolean(rangeStart && rangeEnd);
 
-         // Determine whether it is offsetStart or offsetRangeEnd
 
-         const isOffsetRangeStart = isSameDay(fullDate, offsetRangeStart);
 
-         const isOffsetRangeEnd = isSameDay(fullDate, offsetRangeEnd);
 
-         const isHoverDayOffset = _isHoverDay;
 
-         // When selected
 
-         let isHoverInOffsetRange, isInOffsetRange;
 
-         if (_isDateRangeSelected) {
 
-             isHoverInOffsetRange = _isInRange && _isHoverDay;
 
-         }
 
-         // When there is an offset area
 
-         const _isOffsetDateRangeSelected = Boolean(offsetRangeStart && offsetRangeEnd);
 
-         if (_isOffsetDateRangeSelected) {
 
-             isInOffsetRange = (_isSelectedStart || isBetween(fullDate, { start: offsetRangeStart, end: offsetRangeEnd }) || _isSelectedEnd);
 
-         }
 
-         return {
 
-             isOffsetRangeStart, // Week selection start
 
-             isOffsetRangeEnd, // End of week selection
 
-             isHoverInOffsetRange, // Hover in the week selection
 
-             isHoverDayOffset, // Week selection hover day
 
-             isInOffsetRange // Include start and end within the week selection (start and end styles are the same as other dates, so start and end are included)
 
-         };
 
-     }
 
-     /**
 
-      * get day current status
 
-      * @param {Object} fullDate
 
-      * @param {Object} options
 
-      * @returns {Object}
 
-      */
 
-     getDayStatus(currentDay: MonthDayInfo, options: MonthProps & { todayText: string }) {
 
-         const { fullDate } = currentDay;
 
-         const { hoverDay, rangeStart, rangeEnd, todayText, offsetRangeStart, offsetRangeEnd, disabledDate, selected, rangeInputFocus } = options;
 
-         const singleDayStatus = this.getSingleDayStatus({ fullDate, todayText, hoverDay, selected, disabledDate, rangeStart, rangeEnd });
 
-         const dateRangeStatus = this.getDateRangeStatus({ fullDate, rangeStart, rangeEnd, hoverDay, offsetRangeStart, offsetRangeEnd, rangeInputFocus, ...singleDayStatus });
 
-         const offsetDataStatus = this.getOffsetDateStatus({ offsetRangeStart, offsetRangeEnd, rangeStart, rangeEnd, fullDate, hoverDay, ...singleDayStatus, ...dateRangeStatus });
 
-         // this parameter will pass to the user when given renderFullDate function, do not delete or modify its key
 
-         const dayStatus = {
 
-             ...singleDayStatus,
 
-             ...dateRangeStatus,
 
-             ...offsetDataStatus,
 
-         };
 
-         return dayStatus;
 
-     }
 
-     renderDayOfWeek() {
 
-         const { locale } = this.props;
 
-         const weekdayCls = classNames(cssClasses.WEEKDAY);
 
-         const weekdayItemCls = classNames(`${prefixCls}-weekday-item`);
 
-         const { weekdays } = this.state;
 
-         // i18n
 
-         const weekdaysText = weekdays.map(key => locale.weeks[key]);
 
-         return (
 
-             <div role="row" className={weekdayCls}>
 
-                 {weekdaysText.map((E, i) => (
 
-                     <div role="columnheader" key={E + i} className={weekdayItemCls}>
 
-                         {E}
 
-                     </div>
 
-                 ))}
 
-             </div>
 
-         );
 
-     }
 
-     renderWeeks() {
 
-         const { month } = this.state;
 
-         const { weeks } = month;
 
-         const { weeksRowNum } = this.props;
 
-         let style = {};
 
-         if (weeksRowNum) {
 
-             const height = weeksRowNum * numbers.WEEK_HEIGHT;
 
-             style = { height };
 
-         }
 
-         const weeksCls = classNames(cssClasses.WEEKS);
 
-         return (
 
-             <div className={weeksCls} style={style}>
 
-                 {weeks.map((week, weekIndex) => this.renderWeek(week, weekIndex))}
 
-             </div>
 
-         );
 
-     }
 
-     renderWeek(week: MonthDayInfo[], weekIndex: number) {
 
-         const weekCls = cssClasses.WEEK;
 
-         return (
 
-             <div role="row" className={weekCls} key={weekIndex}>
 
-                 {week.map((day, dayIndex) => this.renderDay(day, dayIndex))}
 
-             </div>
 
-         );
 
-     }
 
-     renderDay(day: MonthDayInfo, dayIndex: number) {
 
-         const { todayText } = this.state;
 
-         const { renderFullDate, renderDate } = this.props;
 
-         const { fullDate, dayNumber } = day;
 
-         if (!fullDate) {
 
-             return (
 
-                 <div role="gridcell" tabIndex={-1} key={(dayNumber as number) + dayIndex} className={cssClasses.DAY}>
 
-                     <span />
 
-                 </div>
 
-             );
 
-         }
 
-         const dayStatus = this.getDayStatus(day, { todayText, ...this.props });
 
-         const dayCls = classNames(cssClasses.DAY, {
 
-             [cssClasses.DAY_TODAY]: dayStatus.isToday,
 
-             [cssClasses.DAY_IN_RANGE]: dayStatus.isInRange,
 
-             [cssClasses.DAY_HOVER]: dayStatus.isHover,
 
-             [cssClasses.DAY_SELECTED]: dayStatus.isSelected,
 
-             [cssClasses.DAY_SELECTED_START]: dayStatus.isSelectedStart,
 
-             [cssClasses.DAY_SELECTED_END]: dayStatus.isSelectedEnd,
 
-             [cssClasses.DAY_DISABLED]: dayStatus.isDisabled,
 
-             // offsetDate class
 
-             [cssClasses.DAY_HOVER_DAY]: dayStatus.isHoverDayOffset,
 
-             [cssClasses.DAY_IN_OFFSET_RANGE]: dayStatus.isInOffsetRange,
 
-             [cssClasses.DAY_SELECTED_RANGE_HOVER]: dayStatus.isHoverInOffsetRange,
 
-             [cssClasses.DAY_OFFSET_RANGE_START]: dayStatus.isOffsetRangeStart,
 
-             [cssClasses.DAY_OFFSET_RANGE_END]: dayStatus.isOffsetRangeEnd,
 
-             // range input class
 
-             [cssClasses.DAY_SELECTED_START_AFTER_HOVER]: dayStatus.isSelectedStartAfterHover,
 
-             [cssClasses.DAY_SELECTED_END_BEFORE_HOVER]: dayStatus.isSelectedEndBeforeHover,
 
-             [cssClasses.DAY_HOVER_DAY_BEFORE_RANGE]: dayStatus.isHoverDayInStartSelection,
 
-             [cssClasses.DAY_HOVER_DAY_AFTER_RANGE]: dayStatus.isHoverDayInEndSelection,
 
-             [cssClasses.DAY_HOVER_DAY_AROUND_SINGLE_SELECTED]: dayStatus.isHoverDayAroundOneSelected,
 
-         });
 
-         const dayMainCls = classNames({
 
-             [`${cssClasses.DAY}-main`]: true,
 
-         });
 
-         const fullDateArgs = [dayNumber, fullDate, dayStatus];
 
-         const customRender = isFunction(renderFullDate);
 
-         return (
 
-             <div
 
-                 role="gridcell"
 
-                 tabIndex={dayStatus.isDisabled ? -1 : 0}
 
-                 aria-disabled={dayStatus.isDisabled}
 
-                 aria-selected={dayStatus.isSelected}
 
-                 aria-label={fullDate}
 
-                 className={!customRender ? dayCls : cssClasses.DAY}
 
-                 title={fullDate}
 
-                 key={(dayNumber as number) + dayIndex}
 
-                 onClick={e => !dayStatus.isDisabled && this.foundation.handleClick(day)}
 
-                 onMouseEnter={() => this.foundation.handleHover(day)}
 
-                 onMouseLeave={() => this.foundation.handleHover()}
 
-             >
 
-                 {customRender ? renderFullDate(...fullDateArgs) : (
 
-                     <div className={dayMainCls}>
 
-                         {isFunction(renderDate) ? renderDate(dayNumber, fullDate) : <span>{dayNumber}</span>}
 
-                     </div>
 
-                 )}
 
-             </div>
 
-         );
 
-     }
 
-     render() {
 
-         const { forwardRef, multiple } = this.props;
 
-         const weekday = this.renderDayOfWeek();
 
-         const weeks = this.renderWeeks();
 
-         const monthCls = classNames(cssClasses.MONTH);
 
-         const ref = forwardRef || this.monthRef;
 
-         return (
 
-             <div role="grid" aria-multiselectable={multiple} ref={ref} className={monthCls} >
 
-                 {weekday}
 
-                 {weeks}
 
-             </div>
 
-         );
 
-     }
 
- }
 
 
  |