UNPKG

react-calendar-custom-date

Version:

A React component for choosing dates and date ranges.

152 lines (142 loc) 5.21 kB
import React, { Component } from 'react'; import PropTypes from 'prop-types'; import Calendar from './Calendar.js'; import { rangeShape } from './DayCell'; import { findNextRangeIndex, generateStyles } from '../utils.js'; import { isBefore, differenceInCalendarDays, addDays, min, isWithinInterval, max } from 'date-fns'; import classnames from 'classnames'; import coreStyles from '../styles'; class DateRange extends Component { constructor(props, context) { super(props, context); this.setSelection = this.setSelection.bind(this); this.handleRangeFocusChange = this.handleRangeFocusChange.bind(this); this.updatePreview = this.updatePreview.bind(this); this.calcNewSelection = this.calcNewSelection.bind(this); this.state = { focusedRange: props.initialFocusedRange || [findNextRangeIndex(props.ranges), 0], preview: null, }; this.styles = generateStyles([coreStyles, props.classNames]); } calcNewSelection(value, isSingleValue = true) { const focusedRange = this.props.focusedRange || this.state.focusedRange; const { ranges, onChange, maxDate, moveRangeOnFirstSelection, disabledDates } = this.props; const focusedRangeIndex = focusedRange[0]; const selectedRange = ranges[focusedRangeIndex]; if (!selectedRange || !onChange) return {}; let { startDate, endDate } = selectedRange; if (!endDate) endDate = new Date(startDate); let nextFocusRange; if (!isSingleValue) { startDate = value.startDate; endDate = value.endDate; } else if (focusedRange[1] === 0) { // startDate selection const dayOffset = differenceInCalendarDays(endDate, startDate); startDate = value; endDate = moveRangeOnFirstSelection ? addDays(value, dayOffset) : value; if (maxDate) endDate = min([endDate, maxDate]); nextFocusRange = [focusedRange[0], 1]; } else { endDate = value; } // reverse dates if startDate before endDate let isStartDateSelected = focusedRange[1] === 0; if (isBefore(endDate, startDate)) { isStartDateSelected = !isStartDateSelected; [startDate, endDate] = [endDate, startDate]; } const inValidDatesWithinRange = disabledDates.filter(disabledDate => isWithinInterval(disabledDate, { start: startDate, end: endDate, }) ); if (inValidDatesWithinRange.length > 0) { if (isStartDateSelected) { startDate = addDays(max(inValidDatesWithinRange), 1); } else { endDate = addDays(min(inValidDatesWithinRange), -1); } } if (!nextFocusRange) { const nextFocusRangeIndex = findNextRangeIndex(this.props.ranges, focusedRange[0]); nextFocusRange = [nextFocusRangeIndex, 0]; } return { wasValid: !(inValidDatesWithinRange.length > 0), range: { startDate, endDate }, nextFocusRange: nextFocusRange, }; } setSelection(value, isSingleValue) { const { onChange, ranges, onRangeFocusChange } = this.props; const focusedRange = this.props.focusedRange || this.state.focusedRange; const focusedRangeIndex = focusedRange[0]; const selectedRange = ranges[focusedRangeIndex]; if (!selectedRange) return; const newSelection = this.calcNewSelection(value, isSingleValue); onChange({ [selectedRange.key || `range${focusedRangeIndex + 1}`]: { ...selectedRange, ...newSelection.range, }, }); this.setState({ focusedRange: newSelection.nextFocusRange, preview: null, }); onRangeFocusChange && onRangeFocusChange(newSelection.nextFocusRange); } handleRangeFocusChange(focusedRange) { this.setState({ focusedRange }); this.props.onRangeFocusChange && this.props.onRangeFocusChange(focusedRange); } updatePreview(val) { if (!val) { this.setState({ preview: null }); return; } const { rangeColors, ranges } = this.props; const focusedRange = this.props.focusedRange || this.state.focusedRange; const color = ranges[focusedRange[0]].color || rangeColors[focusedRange[0]] || color; this.setState({ preview: { ...val.range, color } }); } render() { return ( <Calendar focusedRange={this.state.focusedRange} onRangeFocusChange={this.handleRangeFocusChange} preview={this.state.preview} onPreviewChange={value => { this.updatePreview(value ? this.calcNewSelection(value) : null); }} {...this.props} displayMode="dateRange" className={classnames(this.styles.dateRangeWrapper, this.props.className)} onChange={this.setSelection} updateRange={val => this.setSelection(val, false)} ref={target => { this.calendar = target; }} /> ); } } DateRange.defaultProps = { classNames: {}, ranges: [], moveRangeOnFirstSelection: false, rangeColors: ['#3d91ff', '#3ecf8e', '#fed14c'], disabledDates: [], }; DateRange.propTypes = { ...Calendar.propTypes, onChange: PropTypes.func, onRangeFocusChange: PropTypes.func, className: PropTypes.string, ranges: PropTypes.arrayOf(rangeShape), moveRangeOnFirstSelection: PropTypes.bool, }; export default DateRange;