UNPKG

react-cm-ui

Version:
431 lines (377 loc) 14.3 kB
import _ from 'lodash'; import ClassNames from 'classnames'; import moment from 'moment-timezone'; import PropTypes from 'prop-types'; import React from 'react'; import DatePickerDropdownMonth from './datePickerDropdownMonth'; import DatePickerDropdownYear from './datePickerDropdownYear'; import DatePickerMonth from './datePickerMonth'; import DatePickerUtils from '../../utils/datePickerUtils'; import Icon from '../../dataDisplay/icon'; class DatePickerCalendar extends React.PureComponent { constructor(props) { super(props); this.state = { date: _.cloneDeep(props.date), dateFrom: _.cloneDeep(props.dateFrom), dateTo: _.cloneDeep(props.dateTo), dateInView: this._localizeMoment( this._getDateInView( props.date, props.dateFrom, props.maxDate, props.minDate, ), ), showDropdownMonth: false, showDropdownYear: false, }; this._onMonthChange = this._onMonthChange.bind(this); this._onYearChange = this._onYearChange.bind(this); this._onDayClick = this._onDayClick.bind(this); this._onDropdownMonthClick = this._onDropdownMonthClick.bind(this); this._onDropdownYearClick = this._onDropdownYearClick.bind(this); this._onPreviousMonthChange = this._onPreviousMonthChange.bind(this); this._onNextMonthChange = this._onNextMonthChange.bind(this); } componentDidUpdate(prevProps, prevState) { const { date, dateFrom, dateTo, maxDate, minDate, } = this.props; if (!DatePickerUtils.isSameDay(prevProps.date, date) || !DatePickerUtils.isSameDay(prevProps.dateFrom, dateFrom) || !DatePickerUtils.isSameDay(prevProps.dateTo, dateTo) ) { this.setState({ date, dateFrom, dateTo, dateInView: this._localizeMoment( this._getDateInView( date, dateFrom, maxDate, minDate, ), ), }); } } render() { const { events, excludeDates, filterDates, id, includeDates, maxDate, minDate, mode, range, rangeFrom, rangeTo, style, } = this.props; const { date, dateFrom, dateTo, dateInView } = this.state; const containerClasses = ClassNames('ui', 'date-picker-calendar', { 'date-picker-calendar-mode-input': mode === 'input', 'date-picker-calendar-mode-calendar': mode === 'calendar', 'date-picker-calendar-date-range': !_.isEmpty(dateFrom) || !_.isEmpty(dateTo), 'date-picker-calendar-single-date': _.isEmpty(dateFrom) && _.isEmpty(dateTo), }); return ( <div className={containerClasses} id={id} style={style}> {mode === 'input' ? ( <div className="date-picker-pointer" /> ) : null} {this._renderControls()} <div className="date-picker-month-container"> {this._renderHeaderDays()} <DatePickerMonth date={date} dateFrom={dateFrom} dateInView={dateInView} dateTo={dateTo} events={events} excludeDates={excludeDates} filterDates={filterDates} includeDates={includeDates} maxDate={maxDate} minDate={minDate} onDayClick={this._onDayClick} mode={mode} range={range} rangeFrom={rangeFrom} rangeTo={rangeTo} /> </div> </div> ); } _getDateInView(date, dateFrom, maxDate, minDate) { const today = moment(); if (moment.isMoment(date)) { return date; } else if (moment.isMoment(dateFrom)) { return dateFrom; } if (moment.isMoment(minDate) && minDate.isAfter(today)) { return minDate; } else if (moment.isMoment(maxDate) && maxDate.isBefore(today)) { return maxDate; } else { return today; } } _localizeMoment(value) { const { locale } = this.props; return moment.isMoment(value) && value.clone().locale(locale || moment.locale()); } _onMonthChange(month) { const { onMonthChange } = this.props; const { dateInView } = this.state; if (!_.isUndefined(onMonthChange)) { onMonthChange(month, dateInView.year()); } this.setState({ dateInView: dateInView.clone().set('month', month), showDropdownMonth: false, }); } _onYearChange(year) { const { dateInView } = this.state; this.setState({ dateInView: dateInView.clone().set('year', year), showDropdownYear: false, }); } _onDayClick(date) { const { onChange, range, rangeFrom, rangeTo } = this.props; const { dateFrom, dateTo } = this.state; const isDateSame = DatePickerUtils.isSameDay(this.state.date, date); const isDateBeforeFrom = !_.isUndefined(dateFrom) && date.isBefore(dateFrom); const isDateBetweenToAndFrom = date.isAfter(dateFrom) && date.isBefore(dateTo); const isDateAfterTo = !_.isUndefined(dateTo) && date.isAfter(dateTo); const isDateSameAsFrom = DatePickerUtils.isSameDay(date, dateFrom); const isDateSameAsTo = DatePickerUtils.isSameDay(date, dateTo); const isDateSameAsFromAndTo = isDateSameAsFrom && isDateSameAsTo; if (range) { if (isDateBeforeFrom) { if (!_.isUndefined(onChange)) { onChange({ date: undefined, dateFrom: date, dateTo: dateTo }); } else { this.setState({ dateFrom: date }); } } if (isDateSameAsTo || isDateSameAsFrom || _.isUndefined(dateFrom) && _.isUndefined(dateTo)) { if (!_.isUndefined(onChange)) { onChange({ date: undefined, dateFrom: date, dateTo: date }); } else { this.setState({ dateFrom: date, dateTo: date, }); } } if (isDateAfterTo || isDateBetweenToAndFrom) { if (!_.isUndefined(onChange)) { onChange({ date: undefined, dateFrom: dateFrom, dateTo: date }); } else { this.setState({ dateTo: date }); } } if (isDateSameAsFromAndTo) { if (!_.isUndefined(onChange)) { onChange({ date: undefined, dateFrom: null, dateTo: null }); } else { this.setState({ dateFrom: null, dateTo: null, }); } } } else if (rangeFrom || rangeTo) { let newDateFrom, newDateTo; if (rangeFrom && isDateSameAsTo || isDateAfterTo) { newDateFrom = !isDateSameAsFromAndTo ? date : null; newDateTo = !isDateSameAsFromAndTo ? date : null; } else if (rangeFrom && !isDateSameAsTo && !isDateAfterTo) { newDateFrom = date; newDateTo = dateTo; } if (rangeTo && isDateSameAsFrom || isDateBeforeFrom) { newDateFrom = !isDateSameAsFromAndTo ? date : null; newDateTo = !isDateSameAsFromAndTo ? date : null; } else if (rangeTo && !isDateSameAsFrom && !isDateBeforeFrom) { newDateFrom = dateFrom; newDateTo = date; } onChange({ date: undefined, dateFrom: newDateFrom, dateTo: newDateTo }); } else { if (!_.isUndefined(onChange)) { onChange({ date: !isDateSame ? date : null, dateFrom: undefined, dateTo: undefined, }); } else { this.setState({ date: !isDateSame ? date : null }); } } } _onDropdownMonthClick() { const { showDropdownMonth } = this.state; this.setState({ showDropdownMonth: !showDropdownMonth }); } _onDropdownYearClick() { const { showDropdownYear } = this.state; this.setState({ showDropdownYear: !showDropdownYear }); } _onPreviousMonthChange() { this.setState((prev) => { const { onMonthChange } = this.props; const dateInView = prev.dateInView.clone().subtract(1, 'months'); if (!_.isUndefined(onMonthChange)) { onMonthChange(dateInView.month(), dateInView.year()); } return { dateInView, showDropdownMonth: false, }; }); } _onNextMonthChange() { this.setState((prev) => { const { onMonthChange } = this.props; const dateInView = prev.dateInView.clone().add(1, 'months'); if (!_.isUndefined(onMonthChange)) { onMonthChange(dateInView.month(), dateInView.year()); } return { dateInView, showDropdownMonth: false, }; }); } _renderControls() { const { controls } = this.props; const { dateInView, showDropdownMonth, showDropdownYear, } = this.state; if (controls === 'arrows') { return ( <div className="date-picker-selected-month-year-arrows"> <Icon align="left" onClick={this._onPreviousMonthChange} size="medium" type="chevron-left" /> <div className="title"> {dateInView.format('MMMM YYYY')} </div> <Icon align="right" onClick={this._onNextMonthChange} size="medium" type="chevron-right" /> </div> ); } else { return ( <div className="date-picker-selected-month-year-dropdowns"> <div className="date-picker-selected-month" onClick={this._onDropdownMonthClick} > <span> {dateInView.format('MMMM')} <Icon align="right" size="xxsmall" type="caret-down" /> </span> {showDropdownMonth ? ( <DatePickerDropdownMonth month={dateInView.month()} onChange={this._onMonthChange} onClose={this._onDropdownMonthClick} /> ) : null} </div> <div className="date-picker-selected-year" onClick={this._onDropdownYearClick} > <span> {dateInView.format('YYYY')} <Icon align="right" size="xxsmall" type="caret-down" /> </span> {showDropdownYear ? ( <DatePickerDropdownYear onChange={this._onYearChange} onClose={this._onDropdownYearClick} year={dateInView.year()} /> ) : null} </div> </div> ); } } _renderHeaderDays() { const { dateInView } = this.state; const startOfWeek = dateInView.clone().startOf('week'); const days = [ 0, 1, 2, 3, 4, 5, 6 ].map(offset => { const day = startOfWeek.clone().add(offset, 'days'); return ( <div className="ui date-picker-day" key={offset} > {day.localeData().weekdaysMin(day)} </div> ); }); return ( <div className="ui date-picker-header-days"> {days} </div> ); } } DatePickerCalendar.defaultProps = { className: '', controls: 'arrows', locale: 'en-US', mode: 'calendar', rangeFrom: false, rangeTo: false, }; DatePickerCalendar.propTypes = { className: PropTypes.string, controls: PropTypes.oneOf([ 'dropdowns', 'arrows' ]), date: PropTypes.shape({}), dateFrom: PropTypes.shape({}), dateTo: PropTypes.shape({}), events: PropTypes.array, excludeDates: PropTypes.array, filterDates: PropTypes.func, id: PropTypes.oneOfType([ PropTypes.number, PropTypes.string, ]), includeDates: PropTypes.array, locale: PropTypes.string, maxDate: PropTypes.shape({}), minDate: PropTypes.shape({}), mode: PropTypes.oneOf([ 'calendar', 'input' ]), onChange: PropTypes.func, onMonthChange: PropTypes.func, range: PropTypes.bool, rangeFrom: PropTypes.bool, rangeTo: PropTypes.bool, style: PropTypes.shape({}), }; export default DatePickerCalendar;