UNPKG

react-dates-rtl

Version:

Based on react-dates by airbnb [with RTL support]

241 lines (205 loc) 7.09 kB
import React from 'react'; import PropTypes from 'prop-types'; import shallowCompare from 'react-addons-shallow-compare'; import momentPropTypes from 'react-moment-proptypes'; import { forbidExtraProps, nonNegativeInteger } from 'airbnb-prop-types'; import moment from 'moment'; import cx from 'classnames'; import { addEventListener, removeEventListener } from 'consolidated-events'; import { CalendarDayPhrases } from '../defaultPhrases'; import getPhrasePropTypes from '../utils/getPhrasePropTypes'; import CalendarMonth from './CalendarMonth'; import isTransitionEndSupported from '../utils/isTransitionEndSupported'; import getTransformStyles from '../utils/getTransformStyles'; import getCalendarMonthWidth from '../utils/getCalendarMonthWidth'; import ScrollableOrientationShape from '../shapes/ScrollableOrientationShape'; import { HORIZONTAL_ORIENTATION, VERTICAL_ORIENTATION, VERTICAL_SCROLLABLE, DAY_SIZE, } from '../../constants'; const propTypes = forbidExtraProps({ enableOutsideDays: PropTypes.bool, firstVisibleMonthIndex: PropTypes.number, initialMonth: momentPropTypes.momentObj, isAnimating: PropTypes.bool, numberOfMonths: PropTypes.number, modifiers: PropTypes.object, orientation: ScrollableOrientationShape, onDayClick: PropTypes.func, onDayMouseEnter: PropTypes.func, onDayMouseLeave: PropTypes.func, onMonthTransitionEnd: PropTypes.func, renderDay: PropTypes.func, transformValue: PropTypes.string, daySize: nonNegativeInteger, focusedDate: momentPropTypes.momentObj, // indicates focusable day isFocused: PropTypes.bool, // indicates whether or not to move focus to focusable day // i18n monthFormat: PropTypes.string, phrases: PropTypes.shape(getPhrasePropTypes(CalendarDayPhrases)), isRTL: PropTypes.bool, }); const defaultProps = { enableOutsideDays: false, firstVisibleMonthIndex: 0, initialMonth: moment(), isAnimating: false, numberOfMonths: 1, modifiers: {}, orientation: HORIZONTAL_ORIENTATION, onDayClick() {}, onDayMouseEnter() {}, onDayMouseLeave() {}, onMonthTransitionEnd() {}, renderDay: null, transformValue: 'none', daySize: DAY_SIZE, focusedDate: null, isFocused: false, // i18n monthFormat: 'MMMM YYYY', // english locale phrases: CalendarDayPhrases, isRTL: false, }; function getMonths(initialMonth, numberOfMonths) { let month = initialMonth.clone().subtract(1, 'month'); const months = []; for (let i = 0; i < numberOfMonths + 2; i += 1) { months.push(month); month = month.clone().add(1, 'month'); } return months; } export default class CalendarMonthGrid extends React.Component { constructor(props) { super(props); this.state = { months: getMonths(props.initialMonth, props.numberOfMonths), }; this.isTransitionEndSupported = isTransitionEndSupported(); this.onTransitionEnd = this.onTransitionEnd.bind(this); } componentDidMount() { this.eventHandle = addEventListener( this.container, 'transitionend', this.onTransitionEnd, ); } componentWillReceiveProps(nextProps) { const { initialMonth, numberOfMonths } = nextProps; const { months } = this.state; const hasMonthChanged = !this.props.initialMonth.isSame(initialMonth, 'month'); const hasNumberOfMonthsChanged = this.props.numberOfMonths !== numberOfMonths; let newMonths = months; if (hasMonthChanged && !hasNumberOfMonthsChanged) { if (initialMonth.isAfter(this.props.initialMonth)) { newMonths = months.slice(1); newMonths.push(months[months.length - 1].clone().add(1, 'month')); } else { newMonths = months.slice(0, months.length - 1); newMonths.unshift(months[0].clone().subtract(1, 'month')); } } if (hasNumberOfMonthsChanged) { newMonths = getMonths(initialMonth, numberOfMonths); } this.setState({ months: newMonths, }); } shouldComponentUpdate(nextProps, nextState) { return shallowCompare(this, nextProps, nextState); } componentDidUpdate() { const { isAnimating, onMonthTransitionEnd } = this.props; // For IE9, immediately call onMonthTransitionEnd instead of // waiting for the animation to complete if (!this.isTransitionEndSupported && isAnimating) { onMonthTransitionEnd(); } } componentWillUnmount() { removeEventListener(this.eventHandle); } onTransitionEnd() { this.props.onMonthTransitionEnd(); } render() { const { enableOutsideDays, firstVisibleMonthIndex, isAnimating, modifiers, numberOfMonths, monthFormat, orientation, transformValue, daySize, onDayMouseEnter, onDayMouseLeave, onDayClick, renderDay, onMonthTransitionEnd, focusedDate, isFocused, phrases, isRTL, } = this.props; const { months } = this.state; const isVertical = orientation === VERTICAL_ORIENTATION; const isVerticalScrollable = orientation === VERTICAL_SCROLLABLE; const isHorizontal = orientation === HORIZONTAL_ORIENTATION; const className = cx('CalendarMonthGrid', { 'CalendarMonthGrid--horizontal': isHorizontal, 'CalendarMonthGrid--vertical': isVertical, 'CalendarMonthGrid--vertical-scrollable': isVerticalScrollable, 'CalendarMonthGrid--animating': isAnimating, }); const calendarMonthWidth = getCalendarMonthWidth(daySize); const width = isVertical || isVerticalScrollable ? calendarMonthWidth : (numberOfMonths + 2) * calendarMonthWidth; const style = { ...getTransformStyles(transformValue), width, }; return ( <div ref={(ref) => { this.container = ref; }} className={className} style={style} onTransitionEnd={onMonthTransitionEnd} > {months.map((month, i) => { const isVisible = (i >= firstVisibleMonthIndex) && (i < firstVisibleMonthIndex + numberOfMonths); return ( <CalendarMonth key={month.format('YYYY-MM')} month={month} isVisible={isVisible} enableOutsideDays={enableOutsideDays} modifiers={modifiers} monthFormat={monthFormat} orientation={orientation} onDayMouseEnter={onDayMouseEnter} onDayMouseLeave={onDayMouseLeave} onDayClick={onDayClick} renderDay={renderDay} daySize={daySize} focusedDate={isVisible ? focusedDate : null} isFocused={isFocused} phrases={phrases} isRTL={isRTL} /> ); })} </div> ); } } CalendarMonthGrid.propTypes = propTypes; CalendarMonthGrid.defaultProps = defaultProps;