import React, { useContext, useRef } from "react"; import moment, { Moment } from "moment"; import { DateTable, DateTableProps } from "./tables/DateTable"; import { ControlledProps } from "../form/controlled"; import { CellStatus } from "./tables/BaseTable"; import { MonthTable } from "./tables/MonthTable"; import { FadeTransition } from "../transition/FadeTransition"; import { YearTable } from "./tables/YearTable"; import { QuarterTable } from "./tables/QuarterTable"; import { TimeDisabledProps } from "../timepicker/TimeProps"; import { TimeTable } from "../timepicker/TimeTable"; import { useTranslation } from "../i18n"; import { RangeDateType, showTimeType, CalendarTableType, DateChangeContext, } from "./DateProps"; import { RangePickerContext } from "../datepicker/RangePickerContext"; import { isAfter, isBefore, isSame } from "../datepicker/util"; export interface CalendarTableProps extends ControlledProps< Moment | RangeDateType, React.SyntheticEvent, DateChangeContext > { /** * 允许选择的时间范围限制 */ range?: RangeDateType; /** * 不可选的日期 */ disabledDate?: (date: Moment, startDate?: Moment) => boolean; /** * 不可选的月份 * @since 2.5.0 */ disabledMonth?: (date: Moment, startDate?: Moment) => boolean; /** * 不可选的季度 */ disabledQuarter?: (date: Moment, startDate?: Moment) => boolean; /** * 不可选的时间 */ disabledTime?: ( date: Moment | RangeDateType, partial?: "start" | "end" ) => TimeDisabledProps; /** * 是否开启时间选择 */ showTime?: showTimeType; /** * 当前展示日期 */ curViewMoment?: Moment; /** * 当前展示日期改变回调 */ onCurViewMomentChange?: DateTableProps["onCurrentChange"]; /** * 当前展示的日历类型 */ type: CalendarTableType; /** * 日历类型改变回调 */ onTypeChange?: (types: CalendarTableType) => void; /** * 作为范围选择部分时的位置 */ rangeType?: "start" | "end"; /** * 是否只选择到月份 */ monthOnly?: boolean; /** * 当前 Hover 的日期 */ hovered?: Moment; /** * 当前 Hover 的日期 */ onHoveredChange?: DateTableProps["onCurrentChange"]; } /** * 获取当前时间选择范围 */ export function getTimeRange( value: Moment, range: RangeDateType = [null, null] ): RangeDateType { if (!Array.isArray(range) || !value) { return undefined; } const [min, max] = range; const timeRange: RangeDateType = [ moment().startOf("day"), moment().endOf("day"), ]; if (moment.isMoment(min) && isSame(value, min, "day")) { timeRange[0] = min; } if (moment.isMoment(max) && isSame(value, max, "day")) { timeRange[1] = max; } return timeRange; } export function CalendarTable({ value, onChange, type, onTypeChange, curViewMoment = moment(), onCurViewMomentChange, range, disabledDate = () => true, disabledMonth = () => true, disabledQuarter = () => true, disabledTime = () => ({}), showTime, rangeType, monthOnly, hovered, ...props }: CalendarTableProps) { const t = useTranslation(); const { range: rangePickerRange } = useContext(RangePickerContext); // 记录 Table 切换 const prevTypeRef = useRef(null); const prevType = prevTypeRef.current; if (type === "month" || type === "date" || type === "quarter") { prevTypeRef.current = type; } const tableProps = { ...props, range, onTypeChange, current: curViewMoment, onCurrentChange: onCurViewMomentChange, }; const timeProps = typeof showTime === "object" ? showTime : {}; function getValue(): Moment { if (!Array.isArray(value)) { return value; } if (rangeType === "start") { return value[0]; } return value[1]; } function getCellStatus(date: Moment, type: "date" | "month" | "quarter") { // 单日选择 if (!Array.isArray(value)) { if (moment.isMoment(value) && isSame(date, value, type)) { return CellStatus.Selected; } return CellStatus.Common; } // 范围选择 if (moment.isMoment(value[0]) && isSame(date, value[0], type)) { // 只选中了开始或开始结束在同一天返回 Selected if (!moment.isMoment(value[1])) { return CellStatus.Selected; } if (isSame(value[0], value[1], type)) { return CellStatus.Selected; } return CellStatus.RangeStart; } if (moment.isMoment(value[1]) && isSame(date, value[1], type)) { return CellStatus.RangeEnd; } if (moment.isMoment(value[0]) && moment.isMoment(value[1])) { if (isBefore(value[0], date) && isAfter(value[1], date)) { return CellStatus.InRange; } } if ( moment.isMoment(value[0]) && !moment.isMoment(value[1]) && moment.isMoment(hovered) ) { if (isBefore(value[0], date) && isAfter(hovered, date)) { return CellStatus.InRangeHover; } if (isBefore(hovered, date) && isAfter(value[0], date)) { return CellStatus.InRangeHover; } } return CellStatus.Common; } return ( <> {/* Year */} { onCurViewMomentChange(moment(curViewMoment).year(value.year())); onTypeChange(prevType || (monthOnly ? "date" : "month")); }} cellStatus={date => { if (!Array.isArray(value)) { if (moment.isMoment(value) && isSame(date, value, "year")) { return CellStatus.Selected; } } return CellStatus.Common; }} /> {/* Quarter */} { onCurViewMomentChange( moment(curViewMoment).quarter(newValue.quarter()) ); context.type = "quarter"; if (!Array.isArray(value)) { return onChange(newValue, context); } // 范围选择 if (moment.isMoment(value[0]) && !moment.isMoment(value[1])) { if (isBefore(value[0], newValue)) { return onChange([value[0], newValue], context); } return onChange([newValue, value[0]], context); } return onChange([newValue, null], context); }} cellStatus={date => getCellStatus(date, "quarter")} disabledQuarter={disabledQuarter} /> {/* Month */} { onCurViewMomentChange( moment(curViewMoment).month(newValue.month()) ); if (monthOnly) { context.type = "quarter"; if (!Array.isArray(value)) { return onChange(newValue, context); } // 范围选择 if (moment.isMoment(value[0]) && !moment.isMoment(value[1])) { if (isBefore(value[0], newValue)) { return onChange([value[0], newValue], context); } return onChange([newValue, value[0]], context); } return onChange([newValue, null], context); } return onTypeChange("date"); }} cellStatus={date => getCellStatus(date, "month")} disabledMonth={disabledMonth} /> {/* Date */} { // 范围选择已选择 start,未选择 end if ( Array.isArray(value) && moment.isMoment(value[0]) && !moment.isMoment(value[1]) ) { return disabledDate(date, value[0]); } return disabledDate(date); }} onSelect={(v, context) => { // 单日选择 context.type = "date"; if (!Array.isArray(value)) { return onChange(v, context); } // 范围选择 if (moment.isMoment(value[0]) && !moment.isMoment(value[1])) { if (isBefore(value[0], v)) { return onChange([value[0], v], context); } return onChange([v, value[0]], context); } return onChange([v, null], context); }} cellStatus={date => getCellStatus(date, "date")} /> {/* Time */} { context.type = "time"; // 日期选择完才可选择时间,所以 value 必定合法 if (!Array.isArray(value)) { return onChange(v, context); } if (rangeType === "start") { return onChange([v, value[1]], context); } return onChange([value[0], v], context); }} range={getTimeRange(getValue(), rangePickerRange || range)} {...(disabledTime(value, rangeType) || {})} {...timeProps} /> ); } CalendarTable.displayName = "CalendarTable";