UNPKG

@atlaskit/calendar

Version:

An interactive calendar for date selection experiences.

239 lines (238 loc) 5.26 kB
import { useCallback, useEffect, useRef } from 'react'; import { daysPerWeek, monthsPerYear } from '../constants'; import dateToString from '../utils/date-to-string'; import getDaysInMonth from '../utils/get-days-in-month'; const getNextMonth = (monthValue, yearValue) => { let month = monthValue; let year = yearValue; if (month === monthsPerYear) { month = 1; year += 1; } else { month += 1; } return { month, year }; }; const getPrevMonth = (monthValue, yearValue) => { let month = monthValue; let year = yearValue; if (month === 1) { month = monthsPerYear; year -= 1; } else { month -= 1; } return { month, year }; }; // eslint-disable-next-line @repo/internal/react/require-jsdoc export default function useHandleDateChange({ day: [dayValue, setDayValue], month: [monthValue, setMonthValue], year: [yearValue, setYearValue], shouldSetFocus: [_shouldSetFocus, setShouldSetFocus], onChange }) { const dateRef = useRef({ day: dayValue, month: monthValue, year: yearValue }); useEffect(() => { dateRef.current = { day: dayValue, month: monthValue, year: yearValue }; }, [dayValue, monthValue, yearValue]); const triggerOnChange = useCallback(({ year, month, day, type }) => { const iso = dateToString({ year, month, day }); const isFocusableType = ['up', 'down', 'left', 'right'].includes(type); onChange({ day, month, year, iso, type }); setDayValue(day); setMonthValue(month); setYearValue(year); setShouldSetFocus(isFocusableType); }, [onChange, setDayValue, setMonthValue, setYearValue, setShouldSetFocus]); const navigate = useCallback(type => { const { day, month, year } = dateRef.current; if (type === 'down') { const next = day + daysPerWeek; const daysInMonth = getDaysInMonth(year, month - 1); if (next > daysInMonth) { const { month: nextMonth, year: nextYear } = getNextMonth(month, year); triggerOnChange({ year: nextYear, month: nextMonth, day: next - daysInMonth, type }); } else { triggerOnChange({ year, month, day: next, type }); } } else if (type === 'left') { const prev = day - 1; if (prev < 1) { const { month: prevMonth, year: prevYear } = getPrevMonth(month, year); const prevDay = getDaysInMonth(prevYear, prevMonth - 1); triggerOnChange({ year: prevYear, month: prevMonth, day: prevDay, type }); } else { triggerOnChange({ year, month, day: prev, type }); } } else if (type === 'right') { const next = day + 1; const daysInMonth = getDaysInMonth(year, month - 1); if (next > daysInMonth) { const { month: nextMonth, year: nextYear } = getNextMonth(month, year); triggerOnChange({ year: nextYear, month: nextMonth, day: 1, type }); } else { triggerOnChange({ year, month, day: next, type }); } } else if (type === 'up') { const prev = day - daysPerWeek; if (prev < 1) { const { month: prevMonth, year: prevYear } = getPrevMonth(month, year); const prevDay = getDaysInMonth(prevYear, prevMonth - 1) + prev; triggerOnChange({ year: prevYear, month: prevMonth, day: prevDay, type }); } else { triggerOnChange({ year, month, day: prev, type }); } } }, [triggerOnChange]); const handleClickNextMonth = useCallback(() => { const { day, month, year } = { ...dateRef.current, ...getNextMonth(dateRef.current.month, dateRef.current.year) }; triggerOnChange({ day, month, year, type: 'nextMonth' }); }, [triggerOnChange]); const handleClickNextYear = useCallback(() => { const { day, month, year } = dateRef.current; triggerOnChange({ day, month, year: year + 1, type: 'nextYear' }); }, [triggerOnChange]); const handleClickPrevMonth = useCallback(() => { const { day, month, year } = { ...dateRef.current, ...getPrevMonth(dateRef.current.month, dateRef.current.year) }; triggerOnChange({ day, month, year, type: 'prevMonth' }); }, [triggerOnChange]); const handleClickPrevYear = useCallback(() => { const { day, month, year } = dateRef.current; triggerOnChange({ day, month, year: year - 1, type: 'prevYear' }); }, [triggerOnChange]); return { navigate, handleClickNextMonth, handleClickNextYear, handleClickPrevMonth, handleClickPrevYear }; }