import * as React from 'react'; import { addMonths, isSameDay, isAfter, isBefore, isSameMonth, addYears, max, min, } from 'date-fns'; import { DateRange, NavigationAction, DefinedRange, MARKERS, Marker } from './types'; import { getValidatedMonths, isWithinRange, parseOptionalDate } from './utils'; import { defaultRanges } from './defaults'; import { Menu } from './picker.menu'; interface DateRangePickerProps { open: boolean; initialDateRange?: DateRange; definedRanges?: DefinedRange[]; minDate?: Date | string; maxDate?: Date | string; onChange: (dateRange: DateRange) => void; } export const DateRangePicker: React.FunctionComponent = ( props: DateRangePickerProps, ) => { const today = new Date(); const { open, onChange, initialDateRange, minDate, maxDate, definedRanges = defaultRanges, } = props; const minDateValid = parseOptionalDate(minDate, addYears(today, -10)); const maxDateValid = parseOptionalDate(maxDate, addYears(today, 10)); const [intialFirstMonth, initialSecondMonth] = getValidatedMonths( initialDateRange || {}, minDateValid, maxDateValid, ); const [dateRange, setDateRange] = React.useState({ ...initialDateRange }); const [hoverDay, setHoverDay] = React.useState(); const [firstMonth, setFirstMonth] = React.useState(intialFirstMonth || today); const [secondMonth, setSecondMonth] = React.useState( initialSecondMonth || addMonths(firstMonth, 1), ); const { startDate, endDate } = dateRange; const setFirstMonthValidated = (date: Date) => { if (isBefore(date, secondMonth)) { setFirstMonth(date); } }; const setSecondMonthValidated = (date: Date) => { if (isAfter(date, firstMonth)) { setSecondMonth(date); } }; const setDateRangeValidated = (range: DateRange) => { let { startDate: newStart, endDate: newEnd } = range; if (newStart && newEnd) { range.startDate = newStart = max([newStart, minDateValid]); range.endDate = newEnd = min([newEnd, maxDateValid]); setDateRange(range); onChange(range); setFirstMonth(newStart); setSecondMonth(isSameMonth(newStart, newEnd) ? addMonths(newStart, 1) : newEnd); } else { const emptyRange = {}; setDateRange(emptyRange); onChange(emptyRange); setFirstMonth(today); setSecondMonth(addMonths(firstMonth, 1)); } }; const onDayClick = (day: Date) => { if (startDate && !endDate && !isBefore(day, startDate)) { const newRange = { startDate, endDate: day }; onChange(newRange); setDateRange(newRange); } else { setDateRange({ startDate: day, endDate: undefined }); } setHoverDay(day); }; const onMonthNavigate = (marker: Marker, action: NavigationAction) => { if (marker === MARKERS.FIRST_MONTH) { const firstNew = addMonths(firstMonth, action); if (isBefore(firstNew, secondMonth)) setFirstMonth(firstNew); } else { const secondNew = addMonths(secondMonth, action); if (isBefore(firstMonth, secondNew)) setSecondMonth(secondNew); } }; const onDayHover = (date: Date) => { if (startDate && !endDate) { if (!hoverDay || !isSameDay(date, hoverDay)) { setHoverDay(date); } } }; const inHoverRange = (day: Date) => (startDate && !endDate && hoverDay && isAfter(hoverDay, startDate) && isWithinRange(day, startDate, hoverDay)) as boolean; const helpers = { inHoverRange, }; const handlers = { onDayClick, onDayHover, onMonthNavigate, }; return open ? ( ) : null; }