import React from 'react'; import cls from 'classnames'; import PropTypes from 'prop-types'; import CalendarFoundation, { CalendarAdapter, EventObject, ParsedEvents, ParsedRangeEvent, WeeklyData } 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-es'; import { calcRowHeight } from '@douyinfe/semi-foundation/calendar/eventUtil'; import { WeekCalendarProps } from './interface'; import '@douyinfe/semi-foundation/calendar/calendar.scss'; 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 WeekCalendarState { scrollHeight: number; parsedEvents: ParsedEvents; cachedKeys: Array; } export default class WeekCalendar extends BaseComponent { static propTypes = { displayValue: PropTypes.instanceOf(Date), 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 = { displayValue: new Date(), events: [] as Array, mode: 'week', }; static contextType = localeContext; dom: React.RefObject; scrollDom: React.RefObject; allDayRowHeight: number; weeklyData: WeeklyData; foundation: CalendarFoundation; constructor(props: WeekCalendarProps) { 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, setWeeklyData: data => { this.weeklyData = data; }, getWeeklyData: () => this.weeklyData, updateScrollHeight: scrollHeight => { this.setState({ scrollHeight }); }, setParsedEvents: (parsedEvents: ParsedEvents) => { this.setState({ 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.parseWeeklyEvents(); } componentDidUpdate(prevProps: WeekCalendarProps, prevState: WeekCalendarState) { const prevEventKeys = prevState.cachedKeys; const nowEventKeys = this.props.events.map(event => event.key); if (!isEqual(prevEventKeys, nowEventKeys)) { this.foundation.parseWeeklyEvents(); } } componentWillUnmount() { this.foundation.destroy(); } checkWeekend = (val: Date) => this.foundation.checkWeekend(val); 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.weeklyData; 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: any) => { const { markWeekend, displayValue } = this.props; const { month, week } = this.foundation.getWeeklyData(displayValue, 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: ParsedRangeEvent[]) => { const list = events.map((event, ind) => { const { leftPos, width, topInd, children, key } = 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.parseWeeklyAllDayEvents(allDay); const maxRowHeight = calcRowHeight(parsed); const style = { height: `${maxRowHeight}em` }; const { markWeekend } = this.props; const { week } = this.weeklyData; 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: any) => (
    {header} {this.renderHeader(dateFnsLocale)} {this.renderAllDay(locale)}
    {this.renderDayGrid()}
    )}
    ); } }