| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935 | 
							- /* eslint-disable max-len */
 
- import BaseFoundation, { DefaultAdapter } from '../base/foundation';
 
- import { strings } from './constants';
 
- import {
 
-     format,
 
-     set,
 
-     addMonths,
 
-     subMonths,
 
-     subYears,
 
-     addYears,
 
-     differenceInCalendarMonths,
 
-     differenceInCalendarYears,
 
-     isSameDay,
 
-     parseISO
 
- } from 'date-fns';
 
- import { isBefore, isValidDate, getDefaultFormatToken, getFullDateOffset } from './_utils/index';
 
- import { formatFullDate, WeekStartNumber } from './_utils/getMonthTable';
 
- import { compatiableParse } from './_utils/parser';
 
- import { includes, isSet, isEqual } from 'lodash';
 
- import { zonedTimeToUtc } from '../utils/date-fns-extra';
 
- import { getDefaultFormatTokenByType } from './_utils/getDefaultFormatToken';
 
- import isNullOrUndefined from '../utils/isNullOrUndefined';
 
- import { BaseValueType, ValueType } from './foundation';
 
- import { MonthDayInfo } from './monthFoundation';
 
- import { ArrayElement } from '../utils/type';
 
- const dateDiffFns = {
 
-     month: differenceInCalendarMonths,
 
-     year: differenceInCalendarYears,
 
- };
 
- const dateCalcFns = {
 
-     prevMonth: subMonths,
 
-     nextMonth: addMonths,
 
-     prevYear: subYears,
 
-     nextYear: addYears,
 
- };
 
- type Type = ArrayElement<typeof strings.TYPE_SET>;
 
- interface MonthsGridElementProps {
 
-     navPrev?: React.ReactNode;
 
-     navNext?: React.ReactNode;
 
-     renderDate?: () => React.ReactNode;
 
-     renderFullDate?: () => React.ReactNode;
 
- }
 
- export type PanelType = 'left' | 'right';
 
- export type YearMonthChangeType = 'prevMonth' | 'nextMonth' | 'prevYear' | 'nextYear';
 
- export interface MonthsGridFoundationProps extends MonthsGridElementProps {
 
-     type?: Type;
 
-     defaultValue?: ValueType;
 
-     defaultPickerValue?: ValueType;
 
-     multiple?: boolean;
 
-     max?: number;
 
-     splitPanels?: boolean;
 
-     weekStartsOn?: WeekStartNumber;
 
-     disabledDate?: (date: Date, options?: { rangeStart: string; rangeEnd: string }) => boolean;
 
-     disabledTime?: (date: Date | Date[], panelType: PanelType) => void;
 
-     disabledTimePicker?: boolean;
 
-     hideDisabledOptions?: boolean;
 
-     onMaxSelect?: (v?: any) => void;
 
-     timePickerOpts?: any;
 
-     isControlledComponent?: boolean;
 
-     rangeStart?: string;
 
-     rangeInputFocus?: boolean | string;
 
-     locale?: any;
 
-     localeCode?: string;
 
-     format?: string;
 
-     startDateOffset?: () => void;
 
-     endDateOffset?: () => void;
 
-     autoSwitchDate?: boolean;
 
-     motionEnd?: boolean;
 
-     density?: string;
 
-     dateFnsLocale?: any;
 
-     timeZone?: string | number;
 
-     syncSwitchMonth?: boolean;
 
-     onChange?: (
 
-         value: [Date] | [Date, Date],
 
-         options?: { closePanel?: boolean; needCheckFocusRecord?: boolean }
 
-     ) => void;
 
-     onPanelChange?: (date: Date | Date[], dateString: string | string[]) => void;
 
-     setRangeInputFocus?: (rangeInputFocus: 'rangeStart' | 'rangeEnd') => void;
 
-     isAnotherPanelHasOpened?: (currentRangeInput: 'rangeStart' | 'rangeEnd') => boolean;
 
-     focusRecordsRef?: any;
 
- }
 
- export interface MonthInfo {
 
-     pickerDate: Date;
 
-     showDate: Date;
 
-     isTimePickerOpen: boolean;
 
-     isYearPickerOpen: boolean;
 
- }
 
- export interface MonthsGridFoundationState {
 
-     selected: Set<string>;
 
-     monthLeft: MonthInfo;
 
-     monthRight: MonthInfo;
 
-     maxWeekNum: number; // Maximum number of weeks left and right for manual height adjustment
 
-     hoverDay: string; // Real-time hover date
 
-     rangeStart: string; // Start date for range selection
 
-     rangeEnd: string; // End date of range selection
 
-     currentPanelHeight: number; // current month panel height,
 
-     offsetRangeStart: string;
 
-     offsetRangeEnd: string;
 
-     weeksRowNum?: number;
 
- }
 
- export interface MonthsGridDateAdapter {
 
-     updateDaySelected: (selected: Set<string>) => void;
 
- }
 
- export interface MonthsGridRangeAdapter {
 
-     setRangeStart: (rangeStart: string) => void;
 
-     setRangeEnd: (rangeEnd: string) => void;
 
-     setHoverDay: (hoverDay: string) => void;
 
-     setWeeksHeight: (maxWeekNum: number) => void;
 
-     setOffsetRangeStart: (offsetRangeStart: string) => void;
 
-     setOffsetRangeEnd: (offsetRangeEnd: string) => void;
 
- }
 
- export interface MonthsGridAdapter extends DefaultAdapter<MonthsGridFoundationProps, MonthsGridFoundationState>, MonthsGridRangeAdapter, MonthsGridDateAdapter {
 
-     updateMonthOnLeft: (v: MonthInfo) => void;
 
-     updateMonthOnRight: (v: MonthInfo) => void;
 
-     notifySelectedChange: MonthsGridFoundationProps['onChange'];
 
-     notifyMaxLimit: MonthsGridFoundationProps['onMaxSelect'];
 
-     notifyPanelChange: MonthsGridFoundationProps['onPanelChange'];
 
-     setRangeInputFocus: MonthsGridFoundationProps['setRangeInputFocus'];
 
-     isAnotherPanelHasOpened: MonthsGridFoundationProps['isAnotherPanelHasOpened'];
 
- }
 
- export default class MonthsGridFoundation extends BaseFoundation<MonthsGridAdapter> {
 
-     newBiMonthPanelDate: [Date, Date];
 
-     constructor(adapter: MonthsGridAdapter) {
 
-         super({ ...adapter });
 
-         // Date change data when double panels
 
-         this.newBiMonthPanelDate = [this.getState('monthLeft').pickerDate, this.getState('monthRight').pickerDate];
 
-     }
 
-     init() {
 
-         const defaultValue = this.getProp('defaultValue');
 
-         this.initDefaultPickerValue();
 
-         this.updateSelectedFromProps(defaultValue);
 
-     }
 
-     initDefaultPickerValue() {
 
-         const defaultPickerValue = compatiableParse(this.getProp('defaultPickerValue'));
 
-         if (defaultPickerValue && isValidDate(defaultPickerValue)) {
 
-             this._updatePanelDetail(strings.PANEL_TYPE_LEFT, {
 
-                 pickerDate: defaultPickerValue,
 
-             });
 
-             this._updatePanelDetail(strings.PANEL_TYPE_RIGHT, {
 
-                 pickerDate: addMonths(defaultPickerValue, 1),
 
-             });
 
-         }
 
-     }
 
-     updateSelectedFromProps(values: ValueType, refreshPicker = true) {
 
-         const type: Type = this.getProp('type');
 
-         const { selected, rangeStart, rangeEnd } = this.getStates();
 
-         if (values && (values as BaseValueType[]).length) {
 
-             switch (type) {
 
-                 case 'date':
 
-                     this._initDatePickerFromValue(values as BaseValueType[], refreshPicker);
 
-                     break;
 
-                 case 'dateRange':
 
-                     this._initDateRangePickerFromValue(values as BaseValueType[]);
 
-                     break;
 
-                 case 'dateTime':
 
-                     this._initDateTimePickerFromValue(values as BaseValueType[]);
 
-                     break;
 
-                 case 'dateTimeRange':
 
-                     this._initDateTimeRangePickerFormValue(values as BaseValueType[]);
 
-                     break;
 
-                 default:
 
-                     break;
 
-             }
 
-         } else if (Array.isArray(values) && !values.length || !values) {
 
-             // Empty panel when value is empty Select date
 
-             if (isSet(selected) && selected.size) {
 
-                 this._adapter.updateDaySelected(new Set());
 
-             }
 
-             if (rangeStart) {
 
-                 this._adapter.setRangeStart('');
 
-             }
 
-             if (rangeEnd) {
 
-                 this._adapter.setRangeEnd('');
 
-             }
 
-         }
 
-     }
 
-     calcDisabledTime(panelType: PanelType) {
 
-         const { disabledTime, type } = this.getProps();
 
-         if (typeof disabledTime === 'function' && panelType && ['dateTime', 'dateTimeRange'].includes(type)) {
 
-             const { rangeStart, rangeEnd, monthLeft } = this.getStates();
 
-             const selected = [];
 
-             if (type === 'dateTimeRange') {
 
-                 if (rangeStart) {
 
-                     selected.push(rangeStart);
 
-                 }
 
-                 if (rangeStart && rangeEnd) {
 
-                     selected.push(rangeEnd);
 
-                 }
 
-             } else if (monthLeft && monthLeft.showDate) {
 
-                 selected.push(monthLeft.showDate);
 
-             }
 
-             const selectedDates = selected.map(str => (str instanceof Date ? str : parseISO(str)));
 
-             const cbDates = type === 'dateTimeRange' ? selectedDates : selectedDates[0];
 
-             return disabledTime(cbDates, panelType);
 
-         }
 
-     }
 
-     _initDatePickerFromValue(values: BaseValueType[], refreshPicker = true) {
 
-         const monthLeft = this.getState('monthLeft');
 
-         const newMonthLeft = { ...monthLeft };
 
-         this._adapter.updateMonthOnLeft(newMonthLeft);
 
-         const newSelected = new Set<string>();
 
-         if (!this._isMultiple()) {
 
-             newSelected.add(format(values[0] as Date, strings.FORMAT_FULL_DATE));
 
-         } else {
 
-             values.forEach(date => {
 
-                 newSelected.add(format(date as Date, strings.FORMAT_FULL_DATE));
 
-             });
 
-         }
 
-         if (refreshPicker) {
 
-             this.handleShowDateAndTime(strings.PANEL_TYPE_LEFT, values[0] || newMonthLeft.pickerDate);
 
-         } else {
 
-             this.handleShowDateAndTime(strings.PANEL_TYPE_LEFT, newMonthLeft.pickerDate);
 
-         }
 
-         this._adapter.updateDaySelected(newSelected);
 
-     }
 
-     _initDateRangePickerFromValue(values: BaseValueType[], withTime = false) {
 
-         // init month panel
 
-         const monthLeft = this.getState('monthLeft');
 
-         const monthRight = this.getState('monthRight');
 
-         const adjustResult = this._autoAdjustMonth(
 
-             { ...monthLeft, pickerDate: values[0] || monthLeft.pickerDate },
 
-             { ...monthRight, pickerDate: values[1] || monthRight.pickerDate }
 
-         );
 
-         this.handleShowDateAndTime(strings.PANEL_TYPE_LEFT, adjustResult.monthLeft.pickerDate);
 
-         this.handleShowDateAndTime(strings.PANEL_TYPE_RIGHT, adjustResult.monthRight.pickerDate);
 
-         // init range
 
-         const formatToken = withTime ? strings.FORMAT_DATE_TIME : strings.FORMAT_FULL_DATE;
 
-         let rangeStart = values[0] && format(values[0] as Date, formatToken);
 
-         let rangeEnd = values[1] && format(values[1] as Date, formatToken);
 
-         if (this._isNeedSwap(rangeStart, rangeEnd)) {
 
-             [rangeStart, rangeEnd] = [rangeEnd, rangeStart];
 
-         }
 
-         this._adapter.setRangeStart(rangeStart);
 
-         this._adapter.setRangeEnd(rangeEnd);
 
-         this._adapter.setHoverDay(rangeEnd);
 
-     }
 
-     _initDateTimePickerFromValue(values: BaseValueType[]) {
 
-         this._initDatePickerFromValue(values);
 
-     }
 
-     _initDateTimeRangePickerFormValue(values: BaseValueType[]) {
 
-         this._initDateRangePickerFromValue(values, true);
 
-     }
 
-     // eslint-disable-next-line @typescript-eslint/no-empty-function
 
-     destroy() { }
 
-     /**
 
-      * sync change another panel month when change months from the else yam panel
 
-      * call it when
 
-      *  - current change panel targe date month is same with another panel date
 
-      * 
 
-      * @example
 
-      *  - panelType=right, target=new Date('2022-09-01') and left panel is in '2022-09' => call it, left panel minus one month to '2022-08'
 
-      *  - panelType=left, target=new Date('2021-12-01') and right panel is in '2021-12' => call it, right panel add one month to '2021-01'
 
-      */
 
-     handleSyncChangeMonths(options: { panelType: PanelType, target: Date }) {
 
-         const { panelType, target } = options;
 
-         const { type } = this._adapter.getProps();
 
-         const { monthLeft, monthRight } = this._adapter.getStates();
 
-         if (this.isRangeType(type)) {
 
-             if (panelType === 'right' && differenceInCalendarMonths(target, monthLeft.pickerDate) === 0) {
 
-                 this.handleYearOrMonthChange('prevMonth', 'left', 1, true);
 
-             } else if (panelType === 'left' && differenceInCalendarMonths(monthRight.pickerDate, target) === 0) {
 
-                 this.handleYearOrMonthChange('nextMonth', 'right', 1, true);
 
-             }
 
-         }
 
-     }
 
-     /**
 
-      * Get the target date based on the panel type and switch type
 
-      */
 
-     getTargetChangeDate(options: { panelType: PanelType, switchType: YearMonthChangeType }) {
 
-         const { panelType, switchType } = options;
 
-         const { monthRight, monthLeft } = this._adapter.getStates();
 
-         const currentDate = panelType === 'left' ? monthLeft.pickerDate : monthRight.pickerDate;
 
-         let target: Date;
 
-         
 
-         switch (switchType) {
 
-             case 'prevMonth':
 
-                 target = addMonths(currentDate, -1);
 
-                 break;
 
-             case 'nextMonth':
 
-                 target = addMonths(currentDate, 1);
 
-                 break;
 
-             case 'prevYear':
 
-                 target = addYears(currentDate, -1);
 
-                 break;
 
-             case 'nextYear':
 
-                 target = addYears(currentDate, 1);
 
-                 break;
 
-         }
 
-         return target;
 
-     }
 
-     /**
 
-      * Change month by yam panel
 
-      */
 
-     toMonth(panelType: PanelType, target: Date) {
 
-         const { type } = this._adapter.getProps();
 
-         const diff = this._getDiff('month', target, panelType);
 
-         this.handleYearOrMonthChange(diff < 0 ? 'prevMonth' : 'nextMonth', panelType, Math.abs(diff), false);
 
-     
 
-         if (this.isRangeType(type)) {
 
-             this.handleSyncChangeMonths({ panelType, target });
 
-         }
 
-     }
 
-     toYear(panelType: PanelType, target: Date) {
 
-         const diff = this._getDiff('year', target, panelType);
 
-         this.handleYearOrMonthChange(diff < 0 ? 'prevYear' : 'nextYear', panelType, Math.abs(diff), false);
 
-     }
 
-     toYearMonth(panelType: PanelType, target: Date) {
 
-         this.toYear(panelType, target);
 
-         this.toMonth(panelType, target);
 
-     }
 
-     isRangeType(type?: Type) {
 
-         const { type: typeFromProp } = this.getProps();
 
-         const realType = type ? type : typeFromProp;
 
-         return typeof realType === 'string' && /range/i.test(realType);
 
-     }
 
-     handleSwitchMonthOrYear(switchType: YearMonthChangeType, panelType: PanelType) {
 
-         const { type, syncSwitchMonth } = this.getProps();
 
-         const rangeType = this.isRangeType(type);
 
-         // range type and syncSwitchMonth, we should change panels at same time
 
-         if (rangeType && syncSwitchMonth) {
 
-             this.handleYearOrMonthChange(switchType, 'left', 1, true);
 
-             this.handleYearOrMonthChange(switchType, 'right', 1, true);
 
-         } else {
 
-             this.handleYearOrMonthChange(switchType, panelType);
 
-             /**
 
-              * default behavior (v2.2.0)
 
-              * In order to prevent the two panels from being the same month, this will confuse the user when selecting the range
 
-              * https://github.com/DouyinFE/semi-design/issues/260
 
-              */
 
-             if (rangeType) {
 
-                 const target = this.getTargetChangeDate({ panelType, switchType });
 
-                 this.handleSyncChangeMonths({ panelType, target });
 
-             }
 
-         }
 
-     }
 
-     prevMonth(panelType: PanelType) {
 
-         this.handleSwitchMonthOrYear('prevMonth', panelType);
 
-     }
 
-     nextMonth(panelType: PanelType) {
 
-         this.handleSwitchMonthOrYear('nextMonth', panelType);
 
-     }
 
-     prevYear(panelType: PanelType) {
 
-         this.handleSwitchMonthOrYear('prevYear', panelType);
 
-     }
 
-     nextYear(panelType: PanelType) {
 
-         this.handleSwitchMonthOrYear('nextYear', panelType);
 
-     }
 
-     /**
 
-      * Calculate the year and month difference
 
-      */
 
-     _getDiff(type: 'month' | 'year', target: Date, panelType: PanelType) {
 
-         const panelDetail = this._getPanelDetail(panelType);
 
-         const diff = dateDiffFns[type] && dateDiffFns[type](target, panelDetail.pickerDate);
 
-         return diff;
 
-     }
 
-     _getPanelDetail(panelType: PanelType) {
 
-         return panelType === strings.PANEL_TYPE_RIGHT ? this.getState('monthRight') : this.getState('monthLeft');
 
-     }
 
-     /**
 
-      * Format locale date
 
-      * locale get from LocaleProvider
 
-      * @param {Date} date
 
-      * @param {String} token
 
-      * @returns
 
-      */
 
-     localeFormat(date: Date, token: string) {
 
-         const dateFnsLocale = this._adapter.getProp('dateFnsLocale');
 
-         return format(date, token, { locale: dateFnsLocale });
 
-     }
 
-     isValidTimeZone(timeZone?: string | number) {
 
-         const propTimeZone = this.getProp('timeZone');
 
-         const _timeZone = isNullOrUndefined(timeZone) ? propTimeZone : timeZone;
 
-         return ['string', 'number'].includes(typeof _timeZone) && _timeZone !== '';
 
-     }
 
-     /**
 
-      * 根据 type 处理 onChange 返回的参数
 
-      *
 
-      *  - 返回的日期需要把用户时间转换为设置的时区时间
 
-      *      - 用户时间:用户计算机系统时间
 
-      *      - 时区时间:通过 ConfigProvider 设置的 timeZone
 
-      *  - 例子:用户设置时区为+9,计算机所在时区为+8区,然后用户选择了22:00
 
-      *      - DatePicker 内部保存日期 state 为 +8 的 22:00 => a = new Date("2021-05-25 22:00:00")
 
-      *      - 传出去时,需要把 +8 的 22:00 => +9 的 22:00 => b = zonedTimeToUtc(a, "+09:00");
 
-      *
 
-      * The parameters returned by onChange are processed according to type
 
-      *
 
-      *  -The returned date needs to convert the user time to the set time zone time
 
-      *      -User time: user computer system time
 
-      *      -Time zone: timeZone set by ConfigProvider
 
-      *  -Example: The user sets the time zone to + 9, and the time zone where the computer is located is + 8, and then the user selects 22:00
 
-      *      -DatePicker internal save date state is + 8 22:00 = > a = new Date ("2021-05-25 22:00:00")
 
-      *      -When passing out, you need to put + 8's 22:00 = > + 9's 22:00 = > b = zonedTimeToUtc (a, "+ 09:00");
 
-      *
 
-      *  e.g.
 
-      *  let a = new Date ("2021-05-25 22:00:00");
 
-      *       = > Tue May 25 2021 22:00:00 GMT + 0800 (China Standard Time)
 
-      *  let b = zonedTimeToUtc (a, "+ 09:00");
 
-      *       = > Tue May 25 2021 21:00:00 GMT + 0800 (China Standard Time)
 
-      *
 
-      * @param {Date|Date[]} value
 
-      */
 
-     disposeCallbackArgs(value: Date | Date[]) {
 
-         let _value = Array.isArray(value) ? value : (value && [value]) || [];
 
-         if (this.isValidTimeZone()) {
 
-             const timeZone = this.getProp('timeZone');
 
-             _value = _value.map(date => zonedTimeToUtc(date, timeZone));
 
-         }
 
-         const type = this.getProp('type');
 
-         const formatToken = this.getProp('format') || getDefaultFormatTokenByType(type);
 
-         let notifyValue,
 
-             notifyDate;
 
-         switch (type) {
 
-             case 'date':
 
-             case 'dateTime':
 
-             case 'month':
 
-                 if (!this._isMultiple()) {
 
-                     notifyValue = _value[0] && this.localeFormat(_value[0], formatToken);
 
-                     [notifyDate] = _value;
 
-                 } else {
 
-                     notifyValue = _value.map(v => v && this.localeFormat(v, formatToken));
 
-                     notifyDate = [..._value];
 
-                 }
 
-                 break;
 
-             case 'dateRange':
 
-             case 'dateTimeRange':
 
-                 notifyValue = _value.map(v => v && this.localeFormat(v, formatToken));
 
-                 notifyDate = [..._value];
 
-                 break;
 
-             default:
 
-                 break;
 
-         }
 
-         return {
 
-             notifyValue,
 
-             notifyDate,
 
-         };
 
-     }
 
-     handleYearOrMonthChange(
 
-         type: YearMonthChangeType,
 
-         panelType: PanelType = strings.PANEL_TYPE_LEFT,
 
-         step = 1,
 
-         notSeparateInRange = false
 
-     ) {
 
-         const { autoSwitchDate, type: datePanelType } = this.getProps();
 
-         const { monthLeft, monthRight } = this.getStates();
 
-         const isRangeType = this.isRangeType(datePanelType);
 
-         const isLeftPanelInRange = isRangeType && panelType === strings.PANEL_TYPE_LEFT;
 
-         const panelDetail = this._getPanelDetail(panelType);
 
-         const { pickerDate } = panelDetail;
 
-         const fn = dateCalcFns[type];
 
-         const targetMonth = fn(pickerDate, step);
 
-         // Determine if the date has changed
 
-         const panelDateHasUpdate = (panelType === strings.PANEL_TYPE_LEFT && !isEqual(targetMonth, monthLeft.pickerDate)) ||
 
-             (panelType === strings.PANEL_TYPE_RIGHT && !isEqual(targetMonth, monthRight.pickerDate));
 
-         this._updatePanelDetail(panelType, { pickerDate: targetMonth });
 
-         if (panelDateHasUpdate) { // When the date changes
 
-             if (!isRangeType) { // Single Panel Type
 
-                 const { notifyValue, notifyDate } = this.disposeCallbackArgs(targetMonth);
 
-                 this._adapter.notifyPanelChange(notifyDate, notifyValue);
 
-             } else { // Double Panel Type
 
-                 if (isLeftPanelInRange) { // Left panel
 
-                     this.newBiMonthPanelDate[0] = targetMonth;
 
-                 } else { // Right panel
 
-                     this.newBiMonthPanelDate[1] = targetMonth;
 
-                 }
 
-                 if (!(isLeftPanelInRange && notSeparateInRange)) { // Not synchronously switching the left panel in the scene
 
-                     const { notifyValue, notifyDate } = this.disposeCallbackArgs(this.newBiMonthPanelDate);
 
-                     this._adapter.notifyPanelChange(notifyDate, notifyValue);
 
-                 }
 
-             }
 
-         }
 
-         if (autoSwitchDate) {
 
-             this.updateDateAfterChangeYM(type, targetMonth);
 
-         }
 
-     }
 
-     /**
 
-      * You have chosen to switch the year and month in the future to directly update the Date without closing the date panel
 
-      * @param {*} type
 
-      * @param {*} targetDate
 
-      */
 
-     updateDateAfterChangeYM(
 
-         type: YearMonthChangeType,
 
-         targetDate: Date
 
-     ) {
 
-         const { multiple, disabledDate } = this.getProps();
 
-         const { selected: selectedSet, rangeStart, rangeEnd } = this.getStates();
 
-         const includeRange = ['dateRange', 'dateTimeRange'].includes(type);
 
-         const options = { closePanel: false };
 
-         if (!multiple && !includeRange && selectedSet.size) {
 
-             const selectedStr = Array.from(selectedSet)[0] as string;
 
-             const selectedDate = new Date(selectedStr);
 
-             const year = targetDate.getFullYear();
 
-             const month = targetDate.getMonth();
 
-             const fullDate = set(selectedDate, { year, month });
 
-             if (disabledDate(fullDate, { rangeStart, rangeEnd })) {
 
-                 return;
 
-             }
 
-             this._adapter.notifySelectedChange([fullDate], options);
 
-         }
 
-     }
 
-     _isMultiple() {
 
-         return Boolean(this.getProp('multiple')) && this.getProp('type') === 'date';
 
-     }
 
-     _isRange() {
 
-         // return this._adapter.getProp('type') === dateRangeTypeKey;
 
-     }
 
-     handleDayClick(day: MonthDayInfo, panelType: PanelType) {
 
-         const type = this.getProp('type');
 
-         switch (true) {
 
-             case type === 'date' || type === 'dateTime':
 
-                 this.handleDateSelected(day, panelType);
 
-                 break;
 
-             case type === 'dateRange' || type === 'dateTimeRange':
 
-                 this.handleRangeSelected(day);
 
-                 break;
 
-             default:
 
-                 break;
 
-         }
 
-     }
 
-     handleDateSelected(day: { fullDate: string; fullValidDate?: Date }, panelType: PanelType) {
 
-         const { max, type, isControlledComponent, dateFnsLocale } = this.getProps();
 
-         const multiple = this._isMultiple();
 
-         const { selected } = this.getStates();
 
-         const monthDetail = this._getPanelDetail(panelType);
 
-         const newSelected = new Set(multiple ? [...selected] : []);
 
-         const { fullDate } = day;
 
-         const time = monthDetail.pickerDate;
 
-         const dateStr = type === 'dateTime' ? this._mergeDateAndTime(fullDate, time) : fullDate;
 
-         if (!multiple) {
 
-             newSelected.add(dateStr);
 
-         } else {
 
-             if (newSelected.has(dateStr)) {
 
-                 newSelected.delete(dateStr);
 
-             } else if (max && newSelected.size === max) {
 
-                 this._adapter.notifyMaxLimit();
 
-             } else {
 
-                 newSelected.add(dateStr);
 
-             }
 
-         }
 
-         const dateFormat = this.getValidDateFormat();
 
-         // When passed to the upper layer, it is converted into a Date object to ensure that the input parameter format of initFormDefaultValue is consistent
 
-         const newSelectedDates = [...newSelected].map(_dateStr => compatiableParse(_dateStr, dateFormat, undefined, dateFnsLocale));
 
-         this.handleShowDateAndTime(panelType, time);
 
-         if (!isControlledComponent) {
 
-             // Uncontrolled components, update internal values when operating, and notify external
 
-             // MonthGrid internally uses string to represent fullDate for easy rendering
 
-             this._adapter.updateDaySelected(newSelected);
 
-         }
 
-         this._adapter.notifySelectedChange(newSelectedDates as [Date]);
 
-     }
 
-     handleShowDateAndTime(panelType: PanelType, pickerDate: number | Date, showDate?: Date) {
 
-         const _showDate = showDate || pickerDate;
 
-         this._updatePanelDetail(panelType, { showDate: _showDate, pickerDate });
 
-     }
 
-     /**
 
-      * link date and time
 
-      *
 
-      * @param {Date|string} date
 
-      * @param {Date|string} time
 
-      * @returns {Date}
 
-      */
 
-     _mergeDateAndTime(date: Date | string, time: Date | string) {
 
-         const dateFnsLocale = this._adapter.getProp('dateFnsLocale');
 
-         const dateStr = format(
 
-             isValidDate(date) ? date as Date : compatiableParse(date as string, strings.FORMAT_FULL_DATE, undefined, dateFnsLocale),
 
-             strings.FORMAT_FULL_DATE
 
-         );
 
-         const timeStr = format(
 
-             isValidDate(time) ? time as Date : compatiableParse(time as string, strings.FORMAT_TIME_PICKER, undefined, dateFnsLocale),
 
-             strings.FORMAT_TIME_PICKER
 
-         );
 
-         const timeFormat = this.getValidTimeFormat();
 
-         return compatiableParse(`${dateStr} ${timeStr}`, timeFormat, undefined, dateFnsLocale);
 
-     }
 
-     handleRangeSelected(day: MonthDayInfo) {
 
-         let { rangeStart, rangeEnd } = this.getStates();
 
-         const { startDateOffset, endDateOffset, type, dateFnsLocale, rangeInputFocus } = this.getProps();
 
-         const { fullDate } = day;
 
-         let rangeStartReset = false;
 
-         let rangeEndReset = false;
 
-         const isDateRangeAndHasOffset = (startDateOffset || endDateOffset) && type === 'dateRange';
 
-         if (isDateRangeAndHasOffset) {
 
-             rangeStart = getFullDateOffset(startDateOffset, fullDate);
 
-             rangeEnd = getFullDateOffset(endDateOffset, fullDate);
 
-         } else {
 
-             if (rangeInputFocus === 'rangeEnd') {
 
-                 rangeEnd = fullDate;
 
-                 // rangStart Parten in dateTime: 'yyyy-MM-dd HH:MM:SS', rangeEnd parten: 'yyyy-MM-dd'
 
-                 if ((rangeStart && rangeEnd) && isBefore(rangeEnd, rangeStart.trim().split(/\s+/)[0])) {
 
-                     rangeStart = null;
 
-                     rangeStartReset = true;
 
-                 }
 
-                 // Compatible to select date after opening the panel without click input
 
-             } else if (rangeInputFocus === 'rangeStart' || !rangeInputFocus) {
 
-                 rangeStart = fullDate;
 
-                 // rangEnd Parten in dateTime: 'yyyy-MM-dd HH:MM:SS', rangeStart parten: 'yyyy-MM-dd'
 
-                 if ((rangeStart && rangeEnd) && isBefore(rangeEnd.trim().split(/\s+/)[0], rangeStart)) {
 
-                     rangeEnd = null;
 
-                     rangeEndReset = true;
 
-                 }
 
-             }
 
-         }
 
-         // next focus logic
 
-         const isRangeType = /range/i.test(type);
 
-         if (isRangeType) {
 
-             if (isDateRangeAndHasOffset) {
 
-                 this._adapter.setRangeStart(rangeStart);
 
-                 this._adapter.setRangeEnd(rangeEnd);
 
-             } else {
 
-                 if (rangeInputFocus === 'rangeEnd') {
 
-                     this._adapter.setRangeEnd(rangeEnd);
 
-                     if (rangeStartReset) {
 
-                         this._adapter.setRangeStart(rangeStart);
 
-                     }
 
-                     if (!this._adapter.isAnotherPanelHasOpened('rangeEnd') || !rangeStart) {
 
-                         this._adapter.setRangeInputFocus('rangeStart');
 
-                     }
 
-                 } else if (rangeInputFocus === 'rangeStart' || !rangeInputFocus) {
 
-                     this._adapter.setRangeStart(rangeStart);
 
-                     if (rangeEndReset) {
 
-                         this._adapter.setRangeEnd(rangeEnd);
 
-                     }
 
-                     if (!this._adapter.isAnotherPanelHasOpened('rangeStart') || !rangeEnd) {
 
-                         this._adapter.setRangeInputFocus('rangeEnd');
 
-                     }
 
-                 }
 
-             }
 
-         }
 
-         const dateFormat = this.getValidDateFormat();
 
-         // only notify when choose completed
 
-         if (rangeStart || rangeEnd) {
 
-             const [startDate, endDate] = [
 
-                 compatiableParse(rangeStart, dateFormat, undefined, dateFnsLocale),
 
-                 compatiableParse(rangeEnd, dateFormat, undefined, dateFnsLocale),
 
-             ];
 
-             let date: [Date, Date] = [startDate, endDate];
 
-             // If the type is dateRangeTime, add the value of time
 
-             if (type === 'dateTimeRange') {
 
-                 const startTime = this.getState('monthLeft').pickerDate;
 
-                 const endTime = this.getState('monthRight').pickerDate;
 
-                 const start = rangeStart ? this._mergeDateAndTime(rangeStart, startTime) : null;
 
-                 const end = rangeEnd ? this._mergeDateAndTime(rangeEnd, endTime) : null;
 
-                 if (isSameDay(startDate, endDate) && isBefore(end, start)) {
 
-                     date = [start, start];
 
-                 } else {
 
-                     date = [start, end];
 
-                 }
 
-             }
 
-             this._adapter.notifySelectedChange(date, { needCheckFocusRecord: !(type === 'dateRange' && isDateRangeAndHasOffset) });
 
-         }
 
-     }
 
-     _isNeedSwap(rangeStart: Date | string, rangeEnd: Date | string) {
 
-         // Check whether the start and end are reasonable and whether they need to be reversed
 
-         return rangeStart && rangeEnd && isBefore(rangeEnd, rangeStart);
 
-     }
 
-     /**
 
-      * Day may be empty, this is unhover state
 
-      * @param {*} day
 
-      */
 
-     handleDayHover(day = { fullDate: '' }, panelType?: PanelType) {
 
-         const { fullDate } = day;
 
-         const { startDateOffset, endDateOffset, type } = this.getProps();
 
-         this._adapter.setHoverDay(fullDate);
 
-         if ((startDateOffset || endDateOffset) && type === 'dateRange') {
 
-             const offsetRangeStart = getFullDateOffset(startDateOffset, fullDate);
 
-             const offsetRangeEnd = getFullDateOffset(endDateOffset, fullDate);
 
-             this._adapter.setOffsetRangeStart(offsetRangeStart);
 
-             this._adapter.setOffsetRangeEnd(offsetRangeEnd);
 
-         }
 
-     }
 
-     // Guarantee that monthLeft, monthRight will not appear in the same month or monthLeft is greater than MonthRight
 
-     _autoAdjustMonth(monthLeft: MonthInfo, monthRight: MonthInfo) {
 
-         let newMonthLeft = monthLeft;
 
-         let newMonthRight = monthRight;
 
-         const difference = differenceInCalendarMonths(monthLeft.pickerDate, monthRight.pickerDate);
 
-         if (difference > 0) {
 
-             // The month on the left is larger than the month on the right, swap
 
-             newMonthLeft = { ...monthRight };
 
-             newMonthRight = { ...monthLeft };
 
-         } else if (difference === 0) {
 
-             // Around the same month, the number of months on the right + 1
 
-             newMonthLeft = monthLeft;
 
-             newMonthRight = { ...monthRight, pickerDate: addMonths(monthRight.pickerDate, 1) };
 
-         }
 
-         return { monthLeft: newMonthLeft, monthRight: newMonthRight };
 
-     }
 
-     getValidTimeFormat() {
 
-         const formatProp = this.getProp('format') || strings.FORMAT_TIME_PICKER;
 
-         const timeFormatTokens = [];
 
-         if (includes(formatProp, 'h') || includes(formatProp, 'H')) {
 
-             timeFormatTokens.push('HH');
 
-         }
 
-         if (includes(formatProp, 'm')) {
 
-             timeFormatTokens.push('mm');
 
-         }
 
-         if (includes(formatProp, 's')) {
 
-             timeFormatTokens.push('ss');
 
-         }
 
-         return timeFormatTokens.join(':');
 
-     }
 
-     getValidDateFormat() {
 
-         return this.getProp('format') || getDefaultFormatToken(this.getProp('type'));
 
-     }
 
-     handleTimeChange(newTime: { timeStampValue: number }, panelType: PanelType) {
 
-         const { rangeEnd, rangeStart } = this.getStates();
 
-         const dateFnsLocale = this.getProp('dateFnsLocale');
 
-         const ts = newTime.timeStampValue;
 
-         const type = this.getProp('type');
 
-         const panelDetail = this._getPanelDetail(panelType);
 
-         const { showDate } = panelDetail;
 
-         const timeDate = new Date(ts);
 
-         const dateFormat = this.getValidDateFormat();
 
-         const destRange = panelType === strings.PANEL_TYPE_RIGHT ? rangeEnd : rangeStart;
 
-         let year,
 
-             monthNo,
 
-             date;
 
-         // if (pickerDate && isValidDate(pickerDate)) {
 
-         //     year = pickerDate.getFullYear();
 
-         //     monthNo = pickerDate.getMonth();
 
-         //     date = pickerDate.getDate();
 
-         // } else
 
-         if (type === 'dateTimeRange' && destRange) {
 
-             const rangeDate = compatiableParse(destRange, dateFormat, undefined, dateFnsLocale);
 
-             year = rangeDate.getFullYear();
 
-             monthNo = rangeDate.getMonth();
 
-             date = rangeDate.getDate();
 
-         } else {
 
-             year = showDate.getFullYear();
 
-             monthNo = showDate.getMonth();
 
-             date = showDate.getDate();
 
-         }
 
-         const hours = timeDate.getHours();
 
-         const minutes = timeDate.getMinutes();
 
-         const seconds = timeDate.getSeconds();
 
-         const milSeconds = timeDate.getMilliseconds();
 
-         const dateArgs = [year, monthNo, date, hours, minutes, seconds, milSeconds] as const;
 
-         const fullValidDate = new Date(...dateArgs);
 
-         if (type === 'dateTimeRange') {
 
-             this.handleShowDateAndTime(panelType, fullValidDate, showDate);
 
-             this._updateTimeInDateRange(panelType, fullValidDate);
 
-         } else {
 
-             const fullDate = formatFullDate(year, monthNo + 1, date);
 
-             this.handleDateSelected(
 
-                 {
 
-                     fullDate,
 
-                     fullValidDate,
 
-                 },
 
-                 panelType
 
-             );
 
-             this.handleShowDateAndTime(panelType, fullValidDate);
 
-             this._adapter.notifySelectedChange([fullValidDate]);
 
-         }
 
-     }
 
-     /**
 
-      * Update the time part in the range
 
-      * @param {string} panelType
 
-      * @param {Date} timeDate
 
-      */
 
-     _updateTimeInDateRange(panelType: PanelType, timeDate: Date) {
 
-         const { isControlledComponent, dateFnsLocale } = this.getProps();
 
-         let rangeStart = this.getState('rangeStart');
 
-         let rangeEnd = this.getState('rangeEnd');
 
-         const dateFormat = this.getValidDateFormat();
 
-         // TODO: Modify a time individually
 
-         if (rangeStart && rangeEnd) {
 
-             let startDate = compatiableParse(rangeStart, dateFormat, undefined, dateFnsLocale);
 
-             let endDate = compatiableParse(rangeEnd, dateFormat, undefined, dateFnsLocale);
 
-             // console.log('_updateTimeInDateRange()', rangeStart, rangeEnd, startDate, endDate);
 
-             if (panelType === strings.PANEL_TYPE_RIGHT) {
 
-                 endDate = this._mergeDateAndTime(timeDate, timeDate);
 
-                 rangeEnd = format(endDate, strings.FORMAT_DATE_TIME);
 
-                 if (this._isNeedSwap(rangeStart, rangeEnd)) {
 
-                     [rangeStart, rangeEnd] = [rangeEnd, rangeStart];
 
-                     [startDate, endDate] = [endDate, startDate];
 
-                 }
 
-                 if (!isControlledComponent) {
 
-                     this._adapter.setRangeEnd(rangeEnd);
 
-                 }
 
-             } else {
 
-                 startDate = this._mergeDateAndTime(timeDate, timeDate);
 
-                 rangeStart = format(startDate, strings.FORMAT_DATE_TIME);
 
-                 if (this._isNeedSwap(rangeStart, rangeEnd)) {
 
-                     [rangeStart, rangeEnd] = [rangeEnd, rangeStart];
 
-                     [startDate, endDate] = [endDate, startDate];
 
-                 }
 
-                 if (!isControlledComponent) {
 
-                     this._adapter.setRangeStart(rangeStart);
 
-                 }
 
-             }
 
-             // console.log('_updateTimeInDateRange()', rangeStart, rangeEnd, startDate, endDate);
 
-             this._adapter.notifySelectedChange([startDate, endDate]);
 
-         }
 
-     }
 
-     _updatePanelDetail(
 
-         panelType: PanelType,
 
-         kvs: {
 
-             showDate?: number | Date;
 
-             pickerDate?: number | Date;
 
-             isTimePickerOpen?: boolean;
 
-             isYearPickerOpen?: boolean;
 
-         }
 
-     ) {
 
-         const { monthLeft, monthRight } = this.getStates();
 
-         if (panelType === strings.PANEL_TYPE_RIGHT) {
 
-             this._adapter.updateMonthOnRight({ ...monthRight, ...kvs });
 
-         } else {
 
-             this._adapter.updateMonthOnLeft({ ...monthLeft, ...kvs });
 
-         }
 
-     }
 
-     showYearPicker(panelType: PanelType) {
 
-         this._updatePanelDetail(panelType, { isTimePickerOpen: false, isYearPickerOpen: true });
 
-     }
 
-     showTimePicker(panelType: PanelType, opt?: boolean) {
 
-         if (this.getProp('disabledTimePicker')) {
 
-             return;
 
-         }
 
-         this._updatePanelDetail(panelType, { isTimePickerOpen: true, isYearPickerOpen: false });
 
-     }
 
-     showDatePanel(panelType: PanelType) {
 
-         this._updatePanelDetail(panelType, { isTimePickerOpen: false, isYearPickerOpen: false });
 
-     }
 
-     /**
 
-      * Get year and month panel open type
 
-      * 
 
-      * It is useful info to set minHeight of weeks.
 
-      *  - When yam open type is 'left' or 'right', weeks minHeight should be set
 
-      *    If the minHeight is not set, the change of the number of weeks will cause the scrollList to be unstable
 
-      */
 
-     getYAMOpenType() {
 
-         const { monthLeft, monthRight } = this._adapter.getStates();
 
-         const leftYearPickerOpen = monthLeft.isYearPickerOpen;
 
-         const rightYearPickerOpen = monthRight.isYearPickerOpen;
 
-         if (leftYearPickerOpen && rightYearPickerOpen) {
 
-             return 'both';
 
-         } else if (leftYearPickerOpen) {
 
-             return 'left';
 
-         } else if (rightYearPickerOpen) {
 
-             return 'right';
 
-         } else {
 
-             return 'none';
 
-         }
 
-     }
 
- }
 
 
  |