325 lines
8.3 KiB
TypeScript
325 lines
8.3 KiB
TypeScript
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<AtCalendarState>
|
|
> {
|
|
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<AtCalendarState> => {
|
|
const { generateDate } = this.state
|
|
|
|
const stateValue: Partial<AtCalendarState> = {
|
|
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<AtCalendarState, 'selectedDate'> => {
|
|
const { selectedDate } = this.state
|
|
const { end, start } = selectedDate
|
|
|
|
const valueUnix: number = value.valueOf()
|
|
const state: Pick<AtCalendarState, 'selectedDate'> = {
|
|
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<AtCalendarState> = {}
|
|
|
|
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 (
|
|
<View className={classnames('at-calendar', className)}>
|
|
<AtCalendarController
|
|
minDate={minDate}
|
|
maxDate={maxDate}
|
|
hideArrow={hideArrow}
|
|
monthFormat={monthFormat}
|
|
generateDate={generateDate}
|
|
onPreMonth={this.handleClickPreMonth}
|
|
onNextMonth={this.handleClickNextMonth}
|
|
onSelectDate={this.handleSelectDate}
|
|
/>
|
|
<AtCalendarBody
|
|
validDates={validDates}
|
|
marks={marks}
|
|
format={format}
|
|
minDate={minDate}
|
|
maxDate={maxDate}
|
|
isSwiper={isSwiper}
|
|
isVertical={isVertical}
|
|
selectedDate={selectedDate}
|
|
selectedDates={selectedDates}
|
|
generateDate={generateDate}
|
|
onDayClick={this.handleDayClick}
|
|
onSwipeMonth={this.setMonth}
|
|
onLongClick={this.handleDayLongClick}
|
|
/>
|
|
</View>
|
|
)
|
|
}
|
|
}
|