diff --git a/src/Calendar/withMultipleRanges.js b/src/Calendar/withMultipleRanges.js index 2c9c705b..bb6ba91d 100644 --- a/src/Calendar/withMultipleRanges.js +++ b/src/Calendar/withMultipleRanges.js @@ -3,14 +3,14 @@ import classNames from 'classnames'; import {withDefaultProps} from './'; import {sanitizeDate, withImmutableProps} from '../utils'; import isBefore from 'date-fns/is_before'; -import enhanceHeader from '../Header/withRange'; +import enhanceHeader from '../Header/withMultipleRanges'; import format from 'date-fns/format'; import parse from 'date-fns/parse'; import styles from '../Day/Day.scss'; let isTouchDevice = false; -export const EVENT_TYPE = { +export const EVENT_TYPES = { DELETE: 4, END: 3, HOVER: 2, @@ -50,6 +50,7 @@ export const enhanceDay = withPropsOnChange(['selected'], ({date, selected, them // Enhancer to handle selecting and displaying multiple dates export const withMultipleRanges = compose( withDefaultProps, + withState('displayIndex', 'setDisplayIndex', 0), withState('scrollDate', 'setScrollDate', getInitialDate), withState('displayKey', 'setDisplayKey', getInitialDate), withState('selectionStart', 'setSelectionStart', null), @@ -62,12 +63,12 @@ export const withMultipleRanges = compose( DayComponent: enhanceDay(DayComponent), HeaderComponent: enhanceHeader(HeaderComponent), })), - withProps(({displayKey, passThrough, selected, setDisplayKey, ...props}) => ({ + withProps(({displayKey, passThrough, selected, setDisplayKey, setDisplayIndex, displayIndex, ...props}) => ({ /* eslint-disable sort-keys */ passThrough: { ...passThrough, Day: { - onClick: (date) => handleSelect(date, {selected, ...props}), + onClick: (date) => handleSelect(date, {selected, setDisplayIndex, ...props}), handlers: { onMouseOver: !isTouchDevice && props.selectionStart ? (e) => handleMouseOver(e, {selected, ...props}) @@ -75,10 +76,12 @@ export const withMultipleRanges = compose( }, }, Years: { - selected: selected && selected[displayKey], - onSelect: (date) => handleYearSelect(date, {displayKey, selected, ...props}), + selected: selected[displayIndex] && parse(selected[displayIndex][displayKey]), + onSelect: (date, e, callback) => handleYearSelect(date, callback), }, Header: { + setDisplayIndex, + displayIndex, onYearClick: (date, e, key) => setDisplayKey(key || 'start'), }, }, @@ -98,42 +101,40 @@ function getSortedSelection({start, end}) { : {start: end, end: start}; } -function handleSelect(date, {onSelect, selected, selectionStart, setSelectionStart, selectionStartIdx, setSelectionStartIdx}) { +function handleSelect(date, {onSelect, selected, selectionStart, setSelectionStart, selectionStartIdx, setSelectionStartIdx, setDisplayIndex}) { const positionOfDate = determineIfDateAlreadySelected(date, selected); + const funcs = {onSelect, setSelectionStart, setSelectionStartIdx, setDisplayIndex}; if(positionOfDate.value && !selectionStart) { //selecting an already defined range const selectedDate = selected[positionOfDate.index];//not clone so modding this is modding selected selectedDate.end = date; //not possible to have start/end reversed when clicking on already set range - selectedDate.eventType = EVENT_TYPE.START; - onSelect(selected); - setSelectionStart(selectedDate.start); - setSelectionStartIdx(positionOfDate.index);//grab index of selected and set in state - } else if (selectionStart) { //ending new date range + updateSelectedState(positionOfDate.index, selectedDate.start, positionOfDate.index, selected, funcs);//grab index of selected and set in state + } else if (selectionStart) { //ending date range selection if (positionOfDate.value === PositionTypes.START && !(date < selectionStart)) { //if in process and selecting start, assume they want to cancel - selected[selectionStartIdx].eventType = EVENT_TYPE.DELETE - onSelect(selected); //call twice to notify parent component something is about to be deleted - onSelect([...selected.slice(0, positionOfDate.index), ...selected.slice(positionOfDate.index+1)]); + const displayIdx = positionOfDate.index > 0 ? positionOfDate.index -1 : 0; + updateSelectedState(displayIdx, null, null, [...selected.slice(0, positionOfDate.index), ...selected.slice(positionOfDate.index+1)], funcs); } else { selected[selectionStartIdx] = { //modifying passed in object without clone due to immediate set state - eventType: EVENT_TYPE.END, ...getSortedSelection({ start: selectionStart, end: date, }), }; - onSelect(selected); + updateSelectedState(positionOfDate.index, null, null, selected, funcs); } - setSelectionStart(null); - setSelectionStartIdx(null); } else { //starting new date range - onSelect(selected.concat({eventType:EVENT_TYPE.START, start: date, end: date})); - - setSelectionStart(date); - setSelectionStartIdx(selected.length);//accounts for increase due to concat + updateSelectedState(selected.length, date, selected.length, selected.concat({eventType:EVENT_TYPES.START, start: date, end: date}), funcs)//length accounts for increase due to concat } } +function updateSelectedState(displayIdx, selectStart, selectStartIdx, selected, {onSelect, setSelectionStart, setSelectionStartIdx, setDisplayIndex}) { + onSelect(selected); + setDisplayIndex(displayIdx); + setSelectionStart(selectStart); + setSelectionStartIdx(selectStartIdx); +} + function handleMouseOver(e, {onSelect, selectionStart, selectionStartIdx, selected}) { const dateStr = e.target.getAttribute('data-date'); const date = dateStr && parse(dateStr); @@ -142,7 +143,7 @@ function handleMouseOver(e, {onSelect, selectionStart, selectionStartIdx, select if (selectionStartIdx === null) { return; } selected[selectionStartIdx] = { - eventType: EVENT_TYPE.HOVER, + eventType: EVENT_TYPES.HOVER, ...getSortedSelection({ start: selectionStart, end: date, @@ -151,12 +152,8 @@ function handleMouseOver(e, {onSelect, selectionStart, selectionStartIdx, select onSelect(selected); } -function handleYearSelect(date, {displayKey, onSelect, selected, setScrollDate}) { - - setScrollDate(date); - onSelect(getSortedSelection( - Object.assign({}, selected, {[displayKey]: parse(date)})) - ); +function handleYearSelect(date, callback) { + callback(parse(date)); } function getInitialDate({selected}) { //add diff --git a/src/Calendar/withRange.js b/src/Calendar/withRange.js index 7bd0c6f3..b207a22b 100644 --- a/src/Calendar/withRange.js +++ b/src/Calendar/withRange.js @@ -69,7 +69,7 @@ export const withRange = compose( onSelect: (date) => handleYearSelect(date, {displayKey, selected, ...props}), }, Header: { - onYearClick: (date, e, key) => setDisplayKey(key || 'start'), + onYearClick: (date, e, key) => setDisplayKey( key || 'start'), }, }, selected: { diff --git a/src/Header/defaultSelectionRenderer.js b/src/Header/defaultSelectionRenderer.js index d7810ff2..c2572706 100644 --- a/src/Header/defaultSelectionRenderer.js +++ b/src/Header/defaultSelectionRenderer.js @@ -15,6 +15,7 @@ export default function defaultSelectionRenderer(value, { scrollToDate, setDisplay, shouldAnimate, + idx, }) { const date = parse(value); const values = date && [ diff --git a/src/Header/withMultipleRanges.js b/src/Header/withMultipleRanges.js new file mode 100644 index 00000000..e1eb71e0 --- /dev/null +++ b/src/Header/withMultipleRanges.js @@ -0,0 +1,45 @@ +import React from 'react'; +import {withImmutableProps} from '../utils'; +import defaultSelectionRenderer from './defaultSelectionRenderer'; +import styles from './Header.scss'; +import Slider from './Slider'; + +export default withImmutableProps(({renderSelection}) => ({ + renderSelection: (values, props) => { + if (!values.length || !values[0].start && !values[0].end) { + return null; + } + + const dates = values.sort(function (a, b) { + return a.start > b.start; + }); + const index = props.displayIndex; + + const dateFormat = props.locale && props.locale.headerFormat || 'MMM Do'; + + const dateElements = values.map((value, idx) => { + if (value.start === value.end) { + return defaultSelectionRenderer(value.start, {...props, key: value.start+idx}); + } else { + return ( +
+ {defaultSelectionRenderer(value.start, {...props, dateFormat, idx, key: 'start', shouldAnimate: false})} + {defaultSelectionRenderer(value.end, {...props, dateFormat, idx, key: 'end', shouldAnimate: false})} +
+ ); + } + }); + return ( + + props.setDisplayIndex(chIndex, () => + setTimeout(() => props.scrollToDate(dates[chIndex].start, 0, true), 50) + ) + } + > + {dateElements} + + ); + }, +})); diff --git a/src/index.js b/src/index.js index ceba5317..5c771ba2 100644 --- a/src/index.js +++ b/src/index.js @@ -7,7 +7,7 @@ export {withDateSelection} from './Calendar/withDateSelection'; export {withKeyboardSupport} from './Calendar/withKeyboardSupport'; export {withMultipleDates, defaultMultipleDateInterpolation} from './Calendar/withMultipleDates'; export {withRange, EVENT_TYPE} from './Calendar/withRange'; -export {withMultipleRanges} from './Calendar/withMultipleRanges'; +export {withMultipleRanges, EVENT_TYPES} from './Calendar/withMultipleRanges'; /* * By default, Calendar is a controlled component.