diff --git a/src/components/grid-views/Year.js b/src/components/grid-views/Year.js index 17a4f06..c442f09 100755 --- a/src/components/grid-views/Year.js +++ b/src/components/grid-views/Year.js @@ -1,22 +1,25 @@ import React from 'react'; import PropTypes from 'prop-types'; import classnames from 'classnames'; +import moment from 'moment'; +import { throttle } from 'lodash'; import YearMonth from './year-widgets/YearMonth'; import { chunk } from '../../utils/common'; import * as dates from '../../utils/dates'; import { navigate } from '../../constants'; import { DATE_UNIT, MONTHS } from '../../constants/date'; -import moment from 'moment'; + +const OFFSET_THRESHOLD = 200; // for vertical scroll +const PRE_LOADED = 2; // the number of pre-loaded years class YearView extends React.Component { constructor(props) { super(props); this.state = { - scroll: {scrollLeft: 0, scrollTop: 0}, dayEventsMap: this.getDayEventsMap(props.events), }; - this.rbcYearViewSize = {}; + this.handlingScroll = false; // false: the scroll is not to be handled by function 'handleScroll' } getDayEventsMap = (events) => { @@ -38,45 +41,115 @@ class YearView extends React.Component { return dayEventsMap; } - onYearViewScroll = (event) => { - const { scrollLeft, scrollTop } = event.target; - this.setState({scroll: {scrollLeft, scrollTop}}); + componentDidUpdate(prevProps) { + if (prevProps.events !== this.props.events) { + const newDayEventsMap = this.getDayEventsMap(this.props.events); + this.setState({dayEventsMap: newDayEventsMap}); + } + if (prevProps.date !== this.props.date) { + //setTimeout(this.setInitialScrollTop, 300); + } } componentDidMount() { - let { offsetWidth, offsetHeight } = this.rbcYearView; - this.rbcYearViewSize = {height: offsetHeight, width: offsetWidth}; + this.setInitialScrollTop(); + + // important: reduce the 'reflow' + const style = { + position: 'absolute', + width: '100%', + height: this.yearsParentContainer.scrollHeight, + top: 0, + bottom: 0 + }; + this.setState({ + style: style + }); } - componentDidUpdate(prevProps) { - if (prevProps.events !== this.props.events) { - const newDayEventsMap = this.getDayEventsMap(this.props.events); - this.setState({dayEventsMap: newDayEventsMap}); + setInitialScrollTop = () => { + this.initialScrollTop = document.getElementById(this.currentYear).offsetTop + 30; + this.rbcYearView.scrollTop = this.initialScrollTop; + this.handlingScroll = false; + } + + renderYear = (year) => { + const { localizer, className } = this.props; + const { dayEventsMap } = this.state; + const isCurrentYear = year == this.currentYear; + const style = { + minHeight: this.yearMinHeight + }; + + return ( +
+

{year}

+
+ {MONTHS.map(item => { + const isCurrentMonth = isCurrentYear && parseInt(item) == this.currentMonth; + let monthDate = new Date(`${year}-${item}`); + let month = dates.visibleYearDays(monthDate, localizer); + let weeks = chunk(month, 7); + return
+ +
; + })} +
+
+ ); + } + + handleScroll = () => { + if (!this.handlingScroll) { + this.handlingScroll = true; + return; + } + let currentScrollTop = this.rbcYearView.scrollTop; + let newDate; + if (currentScrollTop < this.initialScrollTop && + this.initialScrollTop - currentScrollTop > OFFSET_THRESHOLD) { + newDate = moment(this.props.date).subtract(1, DATE_UNIT.YEAR).toDate(); + } + if (currentScrollTop > this.initialScrollTop && + currentScrollTop - this.initialScrollTop > document.getElementById(this.currentYear).scrollHeight + 10) { + newDate = moment(this.props.date).add(1, DATE_UNIT.YEAR).toDate(); + } + if (newDate) { + this.props.updateCurrentDate(newDate); + this.handlingScroll = false; } } render() { - let { date: todayDate, localizer, className } = this.props; - const { scroll, dayEventsMap } = this.state; + const { date } = this.props; + const currentYear = dates.year(date); + const renderedYears = [currentYear]; + for (let i = 0; i < PRE_LOADED; i++) { + renderedYears.unshift(currentYear - i - 1); + renderedYears.push(currentYear + i + 1); + } + this.currentYear = currentYear; + this.currentMonth = dates.month(date) + 1; + + // make sure 1 year can take the height of '1 screen'(the height of `this.rbcYearsContainer`) + this.yearMinHeight = this.rbcYearsContainer ? this.rbcYearsContainer.clientHeight : 0; + const { style } = this.state; return ( -
this.rbcYearView = ref} > - {MONTHS.map(item => { - let year = dates.year(todayDate); - let monthDate = new Date(`${year}-${item}`); - let month = dates.visibleYearDays(monthDate, localizer); - let weeks = chunk(month, 7); - return
- -
; - })} +
this.rbcYearsContainer = ref}> +
this.rbcYearView = ref} onScroll={throttle(this.handleScroll, 280)}> +
+
this.yearsParentContainer = ref} style={style}> + {renderedYears.map(this.renderYear)} +
+
+
); } diff --git a/src/components/grid-views/year-widgets/YearDay.js b/src/components/grid-views/year-widgets/YearDay.js index a5e2dbd..cf88f2c 100755 --- a/src/components/grid-views/year-widgets/YearDay.js +++ b/src/components/grid-views/year-widgets/YearDay.js @@ -9,20 +9,24 @@ class YearDay extends React.Component { constructor(props) { super(props); this.state = { - isShowEvents: false, + isShowEvents: false }; - this.position = {}; } - componentDidMount() { - let { offsetParent: rbcYearMonth, offsetLeft: rbcYearMonthLeft } = this.rbcYearDayItem; - let { offsetTop: rbcYearTop, offsetParent: rbcYear } = rbcYearMonth; - let { offsetTop, offsetLeft } = rbcYear; - - this.position = {left: offsetLeft + rbcYearMonthLeft + 34, top: offsetTop + rbcYearTop}; + getPosition = () => { + const { top, right } = this.rbcYearDayItem.getBoundingClientRect(); + const innerWidth = window.innerWidth; + let posLeft = right + 5; + if (innerWidth > 1100) { + posLeft = posLeft - (innerWidth - 1100) / 2; + } + this.position = {top: top - 80, left: posLeft}; } onEventsToggle = () => { + if (!this.state.isShowEvents) { + this.getPosition(); + } this.setState({isShowEvents: !this.state.isShowEvents}); } @@ -31,16 +35,16 @@ class YearDay extends React.Component { } render() { - let { day, monthDate, localizer, accessors, selected, getters, components, popupOffset, onRowExpand, dayEvents } = this.props; + let { day, monthDate, isCurrentMonth, localizer, accessors, selected, getters, components, popupOffset, onRowExpand, dayEvents } = this.props; let { isShowEvents } = this.state; let isOffRange = dates.month(day) !== dates.month(monthDate); - let isCurrentDay = dates.eq(day, new Date(), 'day'); + let isCurrentDay = isCurrentMonth && dates.eq(day, new Date(), 'day'); let label = localizer.format(day, 'yearMonthDateFormat'); return (
this.rbcYearDayItem = ref}>
-
{label}
+
{label}
{dayEvents.length > 0 && } {isShowEvents && @@ -52,7 +56,7 @@ class YearDay extends React.Component { selected={selected} components={components} localizer={localizer} - position={this.position} + position={this.position || {}} events={dayEvents} slotStart={day} onSelect={onRowExpand} @@ -68,7 +72,6 @@ YearDay.propTypes = { dayEvents: PropTypes.array, monthDate: PropTypes.instanceOf(Date), localizer: PropTypes.object, - rbcYearViewScroll: PropTypes.object, accessors: PropTypes.object, getters: PropTypes.object.isRequired, selected: PropTypes.bool, diff --git a/src/components/grid-views/year-widgets/YearMonth.js b/src/components/grid-views/year-widgets/YearMonth.js index 2fd5860..0626ee9 100755 --- a/src/components/grid-views/year-widgets/YearMonth.js +++ b/src/components/grid-views/year-widgets/YearMonth.js @@ -1,5 +1,4 @@ import React, { Fragment } from 'react'; -import { findDOMNode } from 'react-dom'; import PropTypes from 'prop-types'; import Header from '../../header/Header'; import * as dates from '../../../utils/dates'; @@ -58,10 +57,6 @@ class YearMonth extends React.Component { ); } - getContainer = () => { - return findDOMNode(this); - } - render() { let { weeks } = this.props; diff --git a/src/css/react-big-calendar.css b/src/css/react-big-calendar.css index 81e7137..71345cf 100644 --- a/src/css/react-big-calendar.css +++ b/src/css/react-big-calendar.css @@ -412,10 +412,9 @@ button.rbc-input::-moz-focus-inner { flex-direction: column; flex: 1 0 0; width: 100%; - padding: 0 18px; + padding: 12px 18px 32px; user-select: none; -webkit-user-select: none; - overflow-y: auto; } .rbc-month-view {