import { View } from '@tarojs/components' import classnames from 'classnames' import type { Dayjs } from 'dayjs' import dayjs from 'dayjs' import React from 'react' import type { BaseEventOrig } from '@tarojs/components/types/common' import type { AtCalendarDefaultProps, AtCalendarProps, AtCalendarPropsWithDefaults, AtCalendarState, Calendar, } from '../../types/calendar' import AtCalendarBody from './body/index' import AtCalendarController from './controller/index' import './index.scss' const defaultProps: AtCalendarDefaultProps = { validDates: [], marks: [], isSwiper: true, hideArrow: false, isVertical: false, selectedDates: [], isMultiSelect: false, format: 'YYYY/MM/DD', currentDate: Date.now(), monthFormat: 'YYYY年MM月', } export default class AtCalendar extends React.Component< AtCalendarProps, Readonly > { static defaultProps: AtCalendarDefaultProps = defaultProps public constructor(props: AtCalendarProps) { super(props) const { currentDate, isMultiSelect } = props as AtCalendarPropsWithDefaults this.state = this.getInitializeState(currentDate, isMultiSelect) } public UNSAFE_componentWillReceiveProps(nextProps: AtCalendarProps): void { const { currentDate, isMultiSelect } = nextProps if (!currentDate || currentDate === this.props.currentDate) { return } if (isMultiSelect && this.props.isMultiSelect) { const { start, end } = currentDate as Calendar.SelectedDate const { start: preStart, end: preEnd } = this.props .currentDate as Calendar.SelectedDate if (start === preStart && preEnd === end) { return } } const stateValue: AtCalendarState = this.getInitializeState( currentDate, isMultiSelect, ) this.setState(stateValue) } private getSingleSelectdState = (value: Dayjs): Partial => { const { generateDate } = this.state const stateValue: Partial = { selectedDate: this.getSelectedDate(value.valueOf()), } const dayjsGenerateDate: Dayjs = value.startOf('month') const generateDateValue: number = dayjsGenerateDate.valueOf() if (generateDateValue !== generateDate) { this.triggerChangeDate(dayjsGenerateDate) stateValue.generateDate = generateDateValue } return stateValue } private getMultiSelectedState = ( value: Dayjs, ): Pick => { const { selectedDate } = this.state const { end, start } = selectedDate const valueUnix: number = value.valueOf() const state: Pick = { selectedDate, } if (end) { state.selectedDate = this.getSelectedDate(valueUnix, 0) } else { state.selectedDate.end = Math.max(valueUnix, +start) state.selectedDate.start = Math.min(valueUnix, +start) } return state } private getSelectedDate = ( start: number, end?: number, ): Calendar.SelectedDate => { const stateValue: Calendar.SelectedDate = { start, end: start, } if (typeof end !== 'undefined') { stateValue.end = end } return stateValue } private getInitializeState( currentDate: Calendar.DateArg | Calendar.SelectedDate, isMultiSelect?: boolean, ): AtCalendarState { let end: number let start: number let generateDateValue: number if (!currentDate) { const dayjsStart = dayjs() start = dayjsStart.startOf('day').valueOf() generateDateValue = dayjsStart.startOf('month').valueOf() return { generateDate: generateDateValue, selectedDate: { start: '', }, } } if (isMultiSelect) { const { start: cStart, end: cEnd } = currentDate as Calendar.SelectedDate const dayjsStart = dayjs(cStart) start = dayjsStart.startOf('day').valueOf() generateDateValue = dayjsStart.startOf('month').valueOf() end = cEnd ? dayjs(cEnd).startOf('day').valueOf() : start } else { const dayjsStart = dayjs(currentDate as Calendar.DateArg) start = dayjsStart.startOf('day').valueOf() generateDateValue = dayjsStart.startOf('month').valueOf() end = start } return { generateDate: generateDateValue, selectedDate: this.getSelectedDate(start, end), } } private triggerChangeDate = (value: Dayjs): void => { const { format } = this.props if (typeof this.props.onMonthChange !== 'function') { return } this.props.onMonthChange(value.format(format)) } private setMonth = (vectorCount: number): void => { const { format } = this.props const { generateDate } = this.state const _generateDate: Dayjs = dayjs(generateDate).add(vectorCount, 'month') this.setState({ generateDate: _generateDate.valueOf(), }) if (vectorCount && typeof this.props.onMonthChange === 'function') { this.props.onMonthChange(_generateDate.format(format)) } } private handleClickPreMonth = (isMinMonth?: boolean): void => { if (isMinMonth === true) { return } this.setMonth(-1) if (typeof this.props.onClickPreMonth === 'function') { this.props.onClickPreMonth() } } private handleClickNextMonth = (isMaxMonth?: boolean): void => { if (isMaxMonth === true) { return } this.setMonth(1) if (typeof this.props.onClickNextMonth === 'function') { this.props.onClickNextMonth() } } // picker 选择时间改变时触发 private handleSelectDate = (e: BaseEventOrig<{ value: string }>): void => { const { value } = e.detail const _generateDate: Dayjs = dayjs(value) const _generateDateValue: number = _generateDate.valueOf() if (this.state.generateDate === _generateDateValue) { return } this.triggerChangeDate(_generateDate) this.setState({ generateDate: _generateDateValue, }) } private handleDayClick = (item: Calendar.Item): void => { const { isMultiSelect } = this.props const { isDisabled, value } = item if (isDisabled) { return } const dayjsDate: Dayjs = dayjs(value) let stateValue: Partial = {} if (isMultiSelect) { stateValue = this.getMultiSelectedState(dayjsDate) } else { stateValue = this.getSingleSelectdState(dayjsDate) } this.setState(stateValue as AtCalendarState, () => { this.handleSelectedDate() }) if (typeof this.props.onDayClick === 'function') { this.props.onDayClick({ value: item.value }) } } private handleSelectedDate = (): void => { const selectDate = this.state.selectedDate if (typeof this.props.onSelectDate === 'function') { const info: Calendar.SelectedDate = { start: dayjs(selectDate.start).format(this.props.format), } if (selectDate.end) { info.end = dayjs(selectDate.end).format(this.props.format) } this.props.onSelectDate({ value: info, }) } } private handleDayLongClick = (item: Calendar.Item): void => { if (typeof this.props.onDayLongClick === 'function') { this.props.onDayLongClick({ value: item.value }) } } public render(): JSX.Element { const { generateDate, selectedDate } = this.state const { validDates, marks, format, minDate, maxDate, isSwiper, className, hideArrow, isVertical, monthFormat, selectedDates, } = this.props as AtCalendarPropsWithDefaults return ( ) } }