dayCalendar.tsx 7.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195
  1. import React from 'react';
  2. import { isEqual } from 'lodash';
  3. import cls from 'classnames';
  4. import PropTypes from 'prop-types';
  5. import CalendarFoundation, { CalendarAdapter, ParsedEventsType, ParsedEventsWithArray } from '@douyinfe/semi-foundation/calendar/foundation';
  6. import { cssClasses } from '@douyinfe/semi-foundation/calendar/constants';
  7. import DayCol from './dayCol';
  8. import TimeCol from './timeCol';
  9. import BaseComponent from '../_base/baseComponent';
  10. import LocaleConsumer from '../locale/localeConsumer';
  11. import localeContext from '../locale/context';
  12. import { Locale } from '../locale/interface';
  13. import { DayCalendarProps } from './interface';
  14. import '@douyinfe/semi-foundation/calendar/calendar.scss';
  15. const prefixCls = `${cssClasses.PREFIX}-day`;
  16. export interface DayCalendarState {
  17. scrollHeight: number;
  18. parsedEvents: ParsedEventsWithArray;
  19. cachedKeys: Array<string>
  20. }
  21. export default class DayCalendar extends BaseComponent<DayCalendarProps, DayCalendarState> {
  22. static propTypes = {
  23. displayValue: PropTypes.instanceOf(Date),
  24. events: PropTypes.array,
  25. header: PropTypes.node,
  26. showCurrTime: PropTypes.bool,
  27. onClick: PropTypes.func,
  28. mode: PropTypes.string,
  29. renderTimeDisplay: PropTypes.func,
  30. markWeekend: PropTypes.bool,
  31. minEventHeight: PropTypes.number,
  32. scrollTop: PropTypes.number,
  33. width: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
  34. height: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
  35. style: PropTypes.object,
  36. className: PropTypes.string,
  37. dateGridRender: PropTypes.func,
  38. allDayEventsRender: PropTypes.func,
  39. };
  40. static defaultProps = {
  41. events: [] as DayCalendarProps['events'],
  42. displayValue: new Date(),
  43. mode: 'day',
  44. };
  45. static contextType = localeContext;
  46. dom: React.RefObject<HTMLDivElement>;
  47. scrollDom: React.RefObject<HTMLDivElement>;
  48. isWeekend: boolean;
  49. foundation: CalendarFoundation;
  50. constructor(props: DayCalendarProps) {
  51. super(props);
  52. this.foundation = new CalendarFoundation(this.adapter);
  53. this.state = {
  54. scrollHeight: 0,
  55. parsedEvents: {
  56. day: [],
  57. allDay: []
  58. },
  59. cachedKeys: [],
  60. };
  61. this.dom = React.createRef();
  62. this.scrollDom = React.createRef();
  63. this.isWeekend = false;
  64. }
  65. get adapter(): CalendarAdapter<DayCalendarProps, DayCalendarState> {
  66. return {
  67. ...super.adapter,
  68. updateScrollHeight: scrollHeight => {
  69. this.setState({ scrollHeight });
  70. },
  71. setParsedEvents: (parsedEvents: ParsedEventsType) => {
  72. this.setState({ parsedEvents: parsedEvents as ParsedEventsWithArray });
  73. },
  74. cacheEventKeys: cachedKeys => {
  75. this.setState({ cachedKeys });
  76. },
  77. };
  78. }
  79. componentDidMount() {
  80. this.foundation.init();
  81. const { scrollHeight } = this.scrollDom.current;
  82. this.dom.current.scrollTop = this.props.scrollTop;
  83. this.foundation.notifyScrollHeight(scrollHeight);
  84. this.foundation.parseDailyEvents();
  85. }
  86. componentDidUpdate(prevProps: DayCalendarProps, prevState: DayCalendarState) {
  87. const prevEventKeys = prevState.cachedKeys;
  88. const nowEventKeys = this.props.events.map(event => event.key);
  89. if (!isEqual(prevEventKeys, nowEventKeys) || !isEqual(prevProps.displayValue, this.props.displayValue)) {
  90. this.foundation.parseDailyEvents();
  91. }
  92. }
  93. componentWillUnmount() {
  94. this.foundation.destroy();
  95. }
  96. checkWeekend = (val: Date) => this.foundation.checkWeekend(val);
  97. renderAllDayEvents = (events: ParsedEventsWithArray['allDay']) => {
  98. if (this.props.allDayEventsRender) {
  99. return this.props.allDayEventsRender(this.props.events);
  100. }
  101. const list = events.map((event, ind) => {
  102. const { children, key } = event;
  103. return (
  104. <li className={`${cssClasses.PREFIX}-event-item ${cssClasses.PREFIX}-event-allday`} key={key || `allDay-${ind}`}>
  105. {children}
  106. </li>
  107. );
  108. });
  109. return list;
  110. };
  111. handleClick = (e: React.MouseEvent, val: [Date, number, number, number]) => {
  112. const { onClick } = this.props;
  113. const value = this.foundation.formatCbValue(val);
  114. onClick && onClick(e, value);
  115. };
  116. renderAllDay = (events: ParsedEventsWithArray['allDay']) => {
  117. const allDayCls = `${cssClasses.PREFIX}-all-day`;
  118. const contentCls = cls(`${allDayCls}-content`, {
  119. [`${cssClasses.PREFIX}-weekend`]: this.isWeekend,
  120. });
  121. return (
  122. <LocaleConsumer componentName="Calendar">
  123. {(locale: Locale['Calendar']) => (
  124. <div className={`${allDayCls}`}>
  125. <ul className={`${cssClasses.PREFIX}-tag ${allDayCls}-tag ${prefixCls}-sticky-left`}>
  126. <span>{locale.allDay}</span>
  127. </ul>
  128. <div role="gridcell" className={contentCls}>
  129. <ul className={`${cssClasses.PREFIX}-event-items`}>
  130. {this.renderAllDayEvents(events)}
  131. </ul>
  132. </div>
  133. </div>
  134. )}
  135. </LocaleConsumer>
  136. );
  137. };
  138. render() {
  139. const { dateGridRender, displayValue, showCurrTime, renderTimeDisplay, markWeekend, className, height, width, style, header, minEventHeight } = this.props;
  140. const dayCls = cls(prefixCls, className);
  141. const dayStyle = {
  142. height,
  143. width,
  144. ...style,
  145. };
  146. const { parsedEvents, scrollHeight } = this.state;
  147. this.isWeekend = markWeekend && this.checkWeekend(displayValue);
  148. return (
  149. <div className={dayCls} style={dayStyle} ref={this.dom} {...this.getDataAttr(this.props)}>
  150. <div className={`${prefixCls}-sticky-top`}>
  151. {header}
  152. {this.renderAllDay(parsedEvents.allDay)}
  153. </div>
  154. <div className={`${prefixCls}-scroll-wrapper`}>
  155. <div className={`${prefixCls}-scroll`} ref={this.scrollDom}>
  156. <TimeCol
  157. className={`${prefixCls}-sticky-left`}
  158. renderTimeDisplay={renderTimeDisplay}
  159. />
  160. <DayCol
  161. events={parsedEvents.day}
  162. displayValue={displayValue}
  163. scrollHeight={scrollHeight}
  164. handleClick={this.handleClick}
  165. showCurrTime={showCurrTime}
  166. isWeekend={this.isWeekend}
  167. minEventHeight={minEventHeight}
  168. dateGridRender={dateGridRender}
  169. />
  170. </div>
  171. </div>
  172. </div>
  173. );
  174. }
  175. }