import React from 'react'; import cls from 'classnames'; import PropTypes from 'prop-types'; // eslint-disable-next-line max-len import CalendarFoundation, { ParsedEvents, CalendarAdapter, RangeData, ParsedRangeEvent, ParsedEventsType } from '@douyinfe/semi-foundation/calendar/foundation'; import LocaleConsumer from '../locale/localeConsumer'; import localeContext from '../locale/context'; import { cssClasses } from '@douyinfe/semi-foundation/calendar/constants'; import BaseComponent from '../_base/baseComponent'; import DayCol from './dayCol'; import TimeCol from './timeCol'; import { isEqual } from 'lodash'; import { calcRowHeight } from '@douyinfe/semi-foundation/calendar/eventUtil'; import '@douyinfe/semi-foundation/calendar/calendar.scss'; import { RangeCalendarProps } from './interface'; import { Locale } from '../locale/interface'; const toPercent = (num: number) => { const res = num < 1 ? num * 100 : 100; return `${res}%`; }; const prefixCls = `${cssClasses.PREFIX}-week`; const allDayCls = `${cssClasses.PREFIX}-all-day`; export interface RangeCalendarState { scrollHeight: number; parsedEvents: ParsedEvents; cachedKeys: Array } export default class RangeCalendar extends BaseComponent { static propTypes = { // displayValue: PropTypes.instanceOf(Date), range: PropTypes.array, header: PropTypes.node, events: PropTypes.array, mode: PropTypes.string, showCurrTime: PropTypes.bool, markWeekend: PropTypes.bool, scrollTop: PropTypes.number, renderTimeDisplay: PropTypes.func, dateGridRender: PropTypes.func, width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), style: PropTypes.object, className: PropTypes.string, }; static defaultProps = { events: [] as ParsedEvents[], mode: 'range', }; static contextType = localeContext; dom: React.RefObject; scrollDom: React.RefObject; isWeekend: boolean; allDayRowHeight: number; RangeData: RangeData; foundation: CalendarFoundation; constructor(props: RangeCalendarProps) { super(props); this.state = { scrollHeight: 0, parsedEvents: { day: new Map(), allDay: new Map() }, cachedKeys: [], }; this.foundation = new CalendarFoundation(this.adapter); this.dom = React.createRef(); this.scrollDom = React.createRef(); this.handleClick = this.handleClick.bind(this); this.allDayRowHeight = 1; } get adapter(): CalendarAdapter { return { ...super.adapter, setRangeData: data => { this.RangeData = data; }, getRangeData: () => this.RangeData, updateScrollHeight: scrollHeight => { this.setState({ scrollHeight }); }, setParsedEvents: (parsedEvents: ParsedEventsType) => { this.setState({ parsedEvents: parsedEvents as ParsedEvents }); }, cacheEventKeys: cachedKeys => { this.setState({ cachedKeys }); } }; } componentDidMount() { this.foundation.init(); const { scrollHeight } = this.scrollDom.current; this.dom.current.scrollTop = this.props.scrollTop; this.foundation.notifyScrollHeight(scrollHeight); this.foundation.parseRangeEvents(); } componentDidUpdate(prevProps: RangeCalendarProps, prevState: RangeCalendarState) { const prevEventKeys = prevState.cachedKeys; const nowEventKeys = this.props.events.map(event => event.key); if (!isEqual(prevEventKeys, nowEventKeys)) { this.foundation.parseRangeEvents(); } } componentWillUnmount() { this.foundation.destroy(); } handleClick = (e: React.MouseEvent, val: [Date, number, number, number]) => { const { onClick } = this.props; const value = this.foundation.formatCbValue(val); onClick && onClick(e, value); }; renderDayGrid = () => { const { parsedEvents } = this.state; const events = parsedEvents.day; const { week } = this.RangeData; const { markWeekend, dateGridRender } = this.props; const inner = week.map(day => { const dateString = day.date.toString(); const dayEvents = events.has(dateString) ? events.get(dateString) : []; const parsed = this.foundation.getParseDailyEvents(dayEvents, day.date); return ( ); }); return inner; }; renderHeader = (dateFnsLocale: Locale['dateFnsLocale']) => { const { markWeekend, range } = this.props; const { month, week } = this.foundation.getRangeData(range[0], dateFnsLocale); return (
    {month}
    {week.map(day => { const { date, dayString, weekday, isToday } = day; const listCls = cls({ [`${cssClasses.PREFIX}-today`]: isToday, [`${cssClasses.PREFIX}-weekend`]: markWeekend && day.isWeekend, }); return (
  • {dayString} {weekday}
  • ); })}
); }; renderAllDayEvents = (events: Array) => { const list = events.map((event, ind) => { const { leftPos, width, topInd, children } = event; const top = `${topInd}em`; const style = { left: toPercent(leftPos), width: toPercent(width), top, }; return (
  • {children}
  • ); }); return list; }; renderAllDay = (locale: Locale['Calendar']) => { const { allDay } = this.state.parsedEvents; const parsed = this.foundation.parseRangeAllDayEvents(allDay); const maxRowHeight = calcRowHeight(parsed); const style = { height: `${maxRowHeight}em` }; const { markWeekend } = this.props; const { week } = this.RangeData; return (
      {locale.allDay}
      {Object.keys(week).map((date, ind) => { const listCls = cls({ [`${cssClasses.PREFIX}-weekend`]: markWeekend && week[date].isWeekend, }); return (
    • ); })}
      {this.renderAllDayEvents(parsed)}
    ); }; render() { const { renderTimeDisplay, className, height, width, style, header } = this.props; const weekCls = cls(prefixCls, className); const weekStyle = { height, width, ...style, }; return ( {(locale: Locale['Calendar'], localeCode: string, dateFnsLocale: Locale['dateFnsLocale']) => (
    {header} {this.renderHeader(dateFnsLocale)} {this.renderAllDay(locale)}
    {this.renderDayGrid()}
    )}
    ); } }