123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270 |
- 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<string>;
- }
- export default class RangeCalendar extends BaseComponent<RangeCalendarProps, RangeCalendarState> {
- 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<HTMLDivElement>;
- scrollDom: React.RefObject<HTMLDivElement>;
- 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<RangeCalendarProps, RangeCalendarState> {
- 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 (
- <DayCol
- key={`${dateString}-weekday`}
- displayValue={day.date}
- scrollHeight={this.state.scrollHeight}
- handleClick={this.handleClick}
- events={parsed.day}
- showCurrTime={this.props.showCurrTime}
- isWeekend={markWeekend && day.isWeekend}
- dateGridRender={dateGridRender}
- />
- );
- });
- return inner;
- };
- renderHeader = (dateFnsLocale: Locale['dateFnsLocale']) => {
- const { markWeekend, range } = this.props;
- const { month, week } = this.foundation.getRangeData(range[0], dateFnsLocale);
- return (
- <div className={`${prefixCls}-header`}>
- <ul className={`${cssClasses.PREFIX}-tag ${prefixCls}-tag ${prefixCls}-sticky-left`}>
- <span>{month}</span>
- </ul>
- <div role="gridcell" className={`${prefixCls}-grid`}>
- <ul className={`${prefixCls}-grid-row`}>
- {week.map(day => {
- const { date, dayString, weekday, isToday } = day;
- const listCls = cls({
- [`${cssClasses.PREFIX}-today`]: isToday,
- [`${cssClasses.PREFIX}-weekend`]: markWeekend && day.isWeekend,
- });
- return (
- <li key={`${date.toString()}-weekheader`} className={listCls}>
- <span className={`${cssClasses.PREFIX}-today-date`}>{dayString}</span>
- <span>{weekday}</span>
- </li>
- );
- })}
- </ul>
- </div>
- </div>
- );
- };
- renderAllDayEvents = (events: Array<ParsedRangeEvent>) => {
- 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 (
- <li
- className={`${cssClasses.PREFIX}-event-item ${cssClasses.PREFIX}-event-allday`}
- key={`allDay-${ind}`}
- style={style}
- >
- {children}
- </li>
- );
- });
- 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 (
- <div className={`${allDayCls}`} style={style}>
- <ul className={`${cssClasses.PREFIX}-tag ${allDayCls}-tag ${prefixCls}-sticky-left`}>
- <span>{locale.allDay}</span>
- </ul>
- <div role="gridcell" className={`${cssClasses.PREFIX}-content ${allDayCls}-content`}>
- <ul className={`${allDayCls}-skeleton`}>
- {Object.keys(week).map((date, ind) => {
- const listCls = cls({
- [`${cssClasses.PREFIX}-weekend`]: markWeekend && week[date].isWeekend,
- });
- return (
- <li key={`${date}-weekgrid`} className={listCls} />
- );
- })}
- </ul>
- <ul className={`${cssClasses.PREFIX}-event-items`}>
- {this.renderAllDayEvents(parsed)}
- </ul>
- </div>
- </div>
- );
- };
- render() {
- const { renderTimeDisplay, className, height, width, style, header } = this.props;
- const weekCls = cls(prefixCls, className);
- const weekStyle = {
- height,
- width,
- ...style,
- };
- return (
- <LocaleConsumer componentName="Calendar">
- {(locale: Locale['Calendar'], localeCode: string, dateFnsLocale: Locale['dateFnsLocale']) => (
- <div className={weekCls} style={weekStyle} ref={this.dom}>
- <div className={`${prefixCls}-sticky-top`}>
- {header}
- {this.renderHeader(dateFnsLocale)}
- {this.renderAllDay(locale)}
- </div>
- <div className={`${prefixCls}-scroll-wrapper`}>
- <div className={`${prefixCls}-scroll`} ref={this.scrollDom}>
- <TimeCol
- className={`${prefixCls}-sticky-left`}
- renderTimeDisplay={renderTimeDisplay}
- />
- {this.renderDayGrid()}
- </div>
- </div>
- </div>
- )}
- </LocaleConsumer>
- );
- }
- }
|