@rehookify/datepicker
Version:
The ultimate tool to create a date, range and time picker in your React applications.
729 lines (697 loc) • 28.9 kB
JavaScript
import React, { useMemo, useCallback, useReducer, createContext, useContext } from 'react';
// Year and Month is a minimum required arguments for creating a date
// == null is intentional to check also for undefined
const newDate = (Y, M, ...rest) => !Y || M == null ? new Date() : new Date(Y, M, ...rest);
const getDateParts = (d) => ({
D: d.getDate(),
M: d.getMonth(),
Y: d.getFullYear(),
});
// Days in order sun = 0 ... sat = 6
const getDay = (d) => d.getDay();
/*
* We need this function to eliminate time from the comparison.
* All date that comes to DP should go through this function.
*/
const getCleanDate = (d) => newDate(getDateParts(d).Y, getDateParts(d).M, getDateParts(d).D);
const daysInMonth = (d) => newDate(getDateParts(d).Y, getDateParts(d).M + 1, 0).getDate();
const addToDate = (d, value, part) => {
const { Y, M, D } = getDateParts(d);
// Cover case when offsetDate is 31 and next month doesn't have 31 days
// More details here https://github.com/rehookify/datepicker/issues/10
const nextDate = part === 'date'
? D + value
: part === 'month' && D > daysInMonth(newDate(Y, M + value, 1))
? daysInMonth(newDate(Y, M + value, 1))
: D;
return newDate(Y + (part === 'year' ? value : 0), M + (part === 'month' ? value : 0), nextDate);
};
const subtractFromDate = (d, value, part) => addToDate(d, 0 - value, part);
const sortDatesAsc = (a, b) => +a - +b;
const toLocaleDateString = (d, locale, options) => d.toLocaleDateString(locale, options);
const formatMonthName = (d, { locale, monthName }) => toLocaleDateString(d, locale, { month: monthName });
const formatDate = (d, { locale, options }) => toLocaleDateString(d, locale, options);
const getTimeDate = (Y, M, D, t) => t && t.h != null && t.m != null ? newDate(Y, M, D, t.h, t.m) : undefined;
const formatLocaleTime = (d, { locale, hour, minute, second, hour12 }) => d.toLocaleTimeString(locale, {
hour,
minute,
second,
hour12,
});
const addLeadingZero = (n) => `${n < 10 ? 0 : ''}${n}`;
const convertTo12H = (h, m) => {
const median = h >= 12 ? 'pm' : 'am';
return `${addLeadingZero(h % 12 || 12)}:${addLeadingZero(m)} ${median}`;
};
const formatTime = (d, { hour12 }) => {
const h = d.getHours();
const m = d.getMinutes();
return hour12
? convertTo12H(h, m)
: `${addLeadingZero(h)}:${addLeadingZero(m)}`;
};
const addAndSortAsc = (dates, d) => dates.concat(d).sort(sortDatesAsc);
const sortMinMax = (min, max, sortFunction) => {
let [mN, mX] = [min, max];
if (min && max) {
[mN, mX] = [min, max].sort(sortFunction);
}
return [mN, mX];
};
const isExcludedDay = (d, eDays) => eDays ? eDays.includes(d) : false;
const isExcludedDate = (d, dates = []) => {
const { M, D } = getDateParts(d);
return dates.some((date) => {
const { M: md, D: dd } = getDateParts(date);
return M === md && D === dd;
});
};
const isExcluded = (d, { day, date } = {}) => isExcludedDay(d.getDay(), day) || isExcludedDate(d, date);
var NUMBER_OF_STATIC_CALENDAR_DAYS = 42;
function getStartOffset(d, startDay) {
return (getDay(d) + 7 - startDay) % 7;
}
function getCalendarMonthParams(month, year, { mode, startDay }) {
const firstMonthDay = newDate(year, month, 1);
const lastDay = daysInMonth(firstMonthDay);
const startOffset = getStartOffset(firstMonthDay, startDay);
const length = mode === 'static'
? NUMBER_OF_STATIC_CALENDAR_DAYS
: startOffset +
lastDay +
6 -
getStartOffset(newDate(year, month, lastDay), startDay);
return {
start: startOffset,
length,
};
}
// Converting Date to Number is equal of calling Date.getTime
const isSame = (d1, d2) => +d1 === +d2;
const isBefore = (d1, d2) => d1 < d2;
const isAfter = (d1, d2) => d1 > d2;
const isBetween = (start, d, end) => (isAfter(d, start) && isBefore(d, end)) ||
(isBefore(d, start) && isAfter(d, end));
const maxDateAndAfter = (maxDate, date) => !!maxDate && isAfter(date, maxDate);
const minDateAndBefore = (minDate, date) => !!minDate && isBefore(date, minDate);
const includeDate = (dates, d) => dates.some((date) => isSame(getCleanDate(date), getCleanDate(d)));
const isBeforeMinMonth = (month, minDate) => !!minDate && month < getDateParts(minDate).M;
const isBeforeMinYear = (year, minDate) => !!minDate && year < getDateParts(minDate).Y;
const isAfterMaxMonth = (month, maxDate) => !!maxDate && month > getDateParts(maxDate).M;
const isAfterMaxYear = (year, maxDate) => !!maxDate && year > getDateParts(maxDate).Y;
const isSameOrAfterMaxYear = (year, maxDate) => !!maxDate && year >= getDateParts(maxDate).Y;
const isSameOrBeforeMinYear = (year, minDate) => !!minDate && year <= getDateParts(minDate).Y;
var DEFAULT_CALENDAR_CONFIG = {
mode: 'static',
offsets: [0],
startDay: 0,
};
var DEFAULT_YEARS_CONFIG = {
mode: 'decade',
/*
* The default value for the numberOfYears is 12
* it consists of 10 years + 1 year before + 1 year after
*/
numberOfYears: 12,
step: 10,
};
var DEFAULT_DATES_CONFIG = {
mode: 'single',
toggle: false,
selectSameDate: false,
};
var DEFAULT_TIME_CONFIG = {
interval: 30,
useLocales: false,
};
var DEFAULT_LOCALE_CONFIG = {
locale: 'en-GB',
day: '2-digit',
year: 'numeric',
weekday: 'short',
monthName: 'long',
hour: '2-digit',
minute: '2-digit',
hour12: undefined,
second: undefined,
};
function createConfig({ selectedDates = [], onDatesChange, focusDate, offsetDate, onOffsetChange, calendar = {}, dates = {}, locale, time = {}, exclude = {}, years, }) {
const { minDate, maxDate, ...restDates } = dates;
const { offsets = [], ...restCalendarParams } = calendar;
const { minTime, maxTime, ...restTime } = time;
const [minD, maxD] = sortMinMax(minDate, maxDate, sortDatesAsc);
const [minT, maxT] = sortMinMax(minTime, maxTime, (a, b) => a.h - b.h);
const focus = focusDate && includeDate(selectedDates, focusDate) ? focusDate : undefined;
return {
selectedDates,
onDatesChange,
offsetDate,
onOffsetChange,
focusDate: focus,
calendar: {
...DEFAULT_CALENDAR_CONFIG,
...restCalendarParams,
offsets: DEFAULT_CALENDAR_CONFIG.offsets.concat(offsets),
},
years: { ...DEFAULT_YEARS_CONFIG, ...years },
dates: {
...DEFAULT_DATES_CONFIG,
...restDates,
minDate: minD && getCleanDate(minD),
maxDate: maxD && getCleanDate(maxD),
},
locale: {
...DEFAULT_LOCALE_CONFIG,
...locale,
},
time: {
...DEFAULT_TIME_CONFIG,
minTime: minT,
maxTime: maxT,
...restTime,
},
exclude,
};
}
function isRange(mode) {
return mode === 'range';
}
const RANGE_START = 'range-start';
const RANGE_END = 'range-end';
const WILL_BE_IN_RANGE_START = 'will-be-range-start';
const WILL_BE_IN_RANGE_END = 'will-be-range-end';
const getDateRangeState = (date, rangeEnd, selectedDates, mode) => {
if (!isRange(mode) || selectedDates.length === 0)
return '';
const [start, end] = selectedDates;
// We have completed range
if (start && end) {
if (isSame(date, getCleanDate(start))) {
return isSame(getCleanDate(start), getCleanDate(end))
? `${RANGE_START} ${RANGE_END}`
: RANGE_START;
}
if (isSame(date, getCleanDate(end)))
return RANGE_END;
return isBetween(getCleanDate(start), date, getCleanDate(end))
? 'in-range'
: '';
}
// We have 1 date and rangeEnd date
if (!end && rangeEnd) {
if (isBetween(getCleanDate(start), date, rangeEnd))
return 'will-be-in-range';
// rangeEnd is before start
if (isBefore(rangeEnd, getCleanDate(start))) {
if (isSame(date, rangeEnd))
return WILL_BE_IN_RANGE_START;
return isSame(date, getCleanDate(start)) ? WILL_BE_IN_RANGE_END : '';
}
// rangeEnd is after start;
if (isSame(date, getCleanDate(start)))
return WILL_BE_IN_RANGE_START;
return isSame(date, rangeEnd) ? WILL_BE_IN_RANGE_END : '';
}
return '';
};
const createCalendar = (offsetDate, calendarStartDate, selectedDates, { rangeEnd }, config) => {
const { dates: { mode, minDate, maxDate }, locale, calendar, exclude, } = config;
const { locale: localeStr, day, year } = locale;
const { M, Y } = getDateParts(calendarStartDate);
const { start, length } = getCalendarMonthParams(M, Y, calendar);
const days = Array(length)
.fill(0)
.map((_, i) => {
const $date = newDate(Y, M, i + 1 - start);
return {
$date,
active: isSame(offsetDate, $date),
day: toLocaleDateString($date, localeStr, { day }),
now: isSame(getCleanDate(newDate()), $date),
range: getDateRangeState($date, rangeEnd, selectedDates, mode),
disabled: minDateAndBefore(minDate, $date) ||
maxDateAndAfter(maxDate, $date) ||
isExcluded($date, exclude),
selected: includeDate(selectedDates, $date),
inCurrentMonth: getDateParts($date).M === M,
};
});
return {
year: toLocaleDateString(calendarStartDate, localeStr, { year }),
month: formatMonthName(calendarStartDate, locale),
days,
};
};
const createCalendars = ({ selectedDates, state, config, offsetDate, }) => {
return config.calendar.offsets.map((offset) => createCalendar(offsetDate, addToDate(offsetDate, offset, 'month'), selectedDates, state, config));
};
var createWeekdays = ({ days }, { locale: { locale, weekday } }) => [0, 1, 2, 3, 4, 5, 6].map((day) => toLocaleDateString(days[day].$date, locale, { weekday }));
const useCalendars = (state) => {
const calendars = createCalendars(state);
return useMemo(() => ({
calendars,
weekDays: createWeekdays(calendars[0], state.config),
}), [calendars, state.config]);
};
const callAll = (...fns) => (...args) => fns.forEach((fn) => fn === null || fn === void 0 ? void 0 : fn(...args));
const skipFirst = (fn) => (_arg1, arg2) => fn(arg2);
const skipAll = (fn) => (..._) => {
fn();
};
const createPropGetter = (isDisabled, action, props = {}, selected = false) => ({
role: 'button',
tabIndex: 0,
...(isDisabled
? {
disabled: true,
'aria-disabled': true,
}
: {
onClick(evt) {
action(evt);
},
}),
...(selected ? { 'aria-selected': true } : {}),
...props,
});
var SET_FOCUS_DATE_ACTION = 'SET_FOCUS_DATE';
var SET_OFFSET_DATE_ACTION = 'SET_OFFSET_DATE';
var SET_RANGE_END_ACTION = 'SET_RANGE_END';
var SET_YEAR_ACTION = 'SET_YEAR';
var stateReducer = (state, action) => {
switch (action.type) {
case SET_FOCUS_DATE_ACTION:
return {
...state,
focusDate: action.date,
};
case SET_OFFSET_DATE_ACTION:
return {
...state,
offsetDate: action.date,
};
case SET_RANGE_END_ACTION:
return {
...state,
rangeEnd: action.date,
};
case SET_YEAR_ACTION:
return {
...state,
offsetYear: action.year,
};
default:
return state;
}
};
var setFocus = (dispatch, date) => {
dispatch({ type: SET_FOCUS_DATE_ACTION, date });
};
var setOffset = (dispatch, date) => {
dispatch({ type: SET_OFFSET_DATE_ACTION, date });
};
var setRangeEnd = (dispatch, date) => {
dispatch({ type: SET_RANGE_END_ACTION, date });
};
var setYear = (dispatch, year) => {
dispatch({ type: SET_YEAR_ACTION, year });
};
const setDPOffset = ({ dispatch, config: { onOffsetChange, offsetDate } }) => (d) => {
// Prevent to call reducer action if offsetDate is external
if (!onOffsetChange && !offsetDate)
setOffset(dispatch, d);
if (onOffsetChange)
onOffsetChange(d);
};
const getNextOffsetDate = (d, { days, months, years }) => {
let nextDate = d;
if (days && days !== 0) {
nextDate = addToDate(nextDate, days, 'date');
}
if (months && months !== 0) {
nextDate = addToDate(nextDate, months, 'month');
}
if (years && years !== 0) {
nextDate = addToDate(nextDate, years, 'year');
}
return nextDate;
};
const getEdgedOffsetDate = (offsetDate, { days = 0, months = 0, years = 0 }, dateEdge) => {
if (!dateEdge)
return offsetDate;
if (isSame(offsetDate, dateEdge))
return offsetDate;
if (days !== 0) {
return calculateNewDateWithOffset(offsetDate, dateEdge, days, 'date');
}
if (months !== 0) {
return calculateNewDateWithOffset(offsetDate, dateEdge, months, 'month');
}
if (years !== 0) {
return calculateNewDateWithOffset(offsetDate, dateEdge, years, 'year');
}
return offsetDate;
};
const calculateNewDateWithOffset = (offsetDate, dateEdge, offsetValue, unit) => {
const newDate = addToDate(offsetDate, offsetValue, unit);
const isPositiveOffsetValue = offsetValue > 0;
if (isPositiveOffsetValue) {
const isMaxDateAfterNewDate = maxDateAndAfter(dateEdge, newDate);
return isMaxDateAfterNewDate
? subtractFromDate(dateEdge, offsetValue, unit)
: offsetDate;
}
const isMinDateBeforeNewDate = minDateAndBefore(dateEdge, newDate);
return isMinDateBeforeNewDate
? subtractFromDate(dateEdge, offsetValue, unit)
: offsetDate;
};
const useDatePickerOffsetPropGetters = (state) => {
const { config: { dates }, } = state;
const { minDate, maxDate } = dates;
const addOffset = useCallback((offsetValue, { disabled, onClick, ...rest } = {}) => {
const offsetDate = getEdgedOffsetDate(state.offsetDate, offsetValue, maxDate);
const nextDate = getNextOffsetDate(offsetDate, offsetValue);
const isDisabled = !!disabled || maxDateAndAfter(maxDate, nextDate);
return createPropGetter(isDisabled, (evt) => callAll(onClick, skipFirst(setDPOffset(state)))(evt, nextDate), rest);
}, [maxDate, state]);
const subtractOffset = useCallback(({ days = 0, months = 0, years = 0 }, { disabled, onClick, ...rest } = {}) => {
const negativeOffsetValue = {
days: -days,
months: -months,
years: -years,
};
const offsetDate = getEdgedOffsetDate(state.offsetDate, negativeOffsetValue, minDate);
const nextDate = getNextOffsetDate(offsetDate, negativeOffsetValue);
const isDisabled = !!disabled || minDateAndBefore(minDate, nextDate);
return createPropGetter(isDisabled, (evt) => callAll(onClick, skipFirst(setDPOffset(state)))(evt, nextDate), rest);
}, [minDate, state]);
const setOffset = useCallback((d, { disabled, onClick, ...rest } = {}) => {
const isDisabled = !!disabled ||
minDateAndBefore(minDate, d) ||
maxDateAndAfter(maxDate, d);
return createPropGetter(isDisabled, (evt) => callAll(onClick, skipFirst(setDPOffset(state)))(evt, d), rest);
}, [state, maxDate, minDate]);
return {
addOffset,
setOffset,
subtractOffset,
};
};
const getCalendarStartDate = (minDate, maxDate, NOW) => {
if (maxDateAndAfter(maxDate, NOW))
return maxDate;
if (minDateAndBefore(minDate, NOW))
return minDate;
return NOW;
};
/*
* Default behavior years collection
* It get start of the decade -1
* It really comfortable to navigate through years
* because you have links to the previous and next decade
* 2019 2020 2021
* 2022 2023 2024
* 2025 2026 2027
* 2028 2029 2030
*/
const getStartDecadePosition = (year) => year - (year % 10) - 1;
/*
* If number of the year is default = 12 and current year is 2022
* It will show this matrix
* 2017 2018 2019
* 2020 2021 2022 👌
* 2023 2024 2025
* 2026 2027 2028
* I think it is nicer to look at the future more ;)
*/
const getFluidYearPosition = (year, numberOfYears) => year - (numberOfYears / 2 - (numberOfYears % 2 === 0 ? 1 : 0));
/*
* It will place offsetYear at the end of the collection
* 2015 2016 2017
* 2012 2013 2014
* 2018 2019 2020
* 2021 2022 2023
*/
const getStartExactPosition = (year, numberOfYears) => year - numberOfYears + 1;
const isExactMode = (mode) => mode === 'exact';
const getCurrentYearPosition = (year, { mode, numberOfYears }) => {
if (isExactMode(mode))
return getStartExactPosition(year, numberOfYears);
return mode === 'decade'
? getStartDecadePosition(year)
: getFluidYearPosition(year, numberOfYears);
};
const createInitialState = (config) => {
const { selectedDates, offsetDate, focusDate, dates: { minDate, maxDate }, years, } = config;
const oDate = offsetDate
? offsetDate
: selectedDates.length > 0
? selectedDates[selectedDates.length - 1]
: getCalendarStartDate(minDate, maxDate, getCleanDate(newDate()));
return {
focusDate,
rangeEnd: null,
offsetDate: oDate,
offsetYear: getCurrentYearPosition(getDateParts(oDate).Y, years),
};
};
const useDatePickerState = (config) => {
const dpConfig = createConfig(config);
const [state, dispatch] = useReducer(stateReducer, createInitialState(dpConfig));
return {
dispatch,
selectedDates: dpConfig.selectedDates,
offsetDate: dpConfig.offsetDate || state.offsetDate,
state,
config: dpConfig,
};
};
const getMultipleDates = (selectedDates, date, { mode, toggle, limit }) => {
// If toggle is active and we have already selected this date
// Then filter it out in all modes
if (toggle && includeDate(selectedDates, date))
return selectedDates.filter((d) => !isSame(getCleanDate(d), date));
if (mode === 'multiple')
return !limit || selectedDates.length < limit
? addAndSortAsc(selectedDates, date)
: selectedDates;
if (isRange(mode))
return selectedDates.length === 2
? [date]
: addAndSortAsc(selectedDates, date);
// mode === 'single'
return [date];
};
const useDays = ({ selectedDates, config: { locale } }) => useMemo(() => ({
selectedDates,
formattedDates: selectedDates.map((d) => formatDate(d, locale)),
}), [selectedDates, locale]);
const useDaysPropGetters = ({ config, selectedDates, dispatch, }) => {
const { onDatesChange, dates: { mode, toggle, selectSameDate }, } = config;
const dayButton = useCallback(({ $date, selected, disabled, active }, { onClick, disabled: disabledProps, ...rest } = {}) => createPropGetter(disabled || !!disabledProps, (evt) => {
if (selected && !toggle) {
selectedDates.forEach((d) => {
if (isSame(getCleanDate(d), $date))
setFocus(dispatch, d);
});
// Handle case when user could select same date in range mode
if (!isRange(mode) || !selectSameDate)
return;
}
if (isRange(mode) && selectedDates.length === 1) {
setRangeEnd(dispatch, null);
}
callAll(onClick, skipFirst((d) => {
const nextSelectedDates = getMultipleDates(selectedDates, d, config.dates);
setFocus(dispatch, includeDate(nextSelectedDates, d) ? d : undefined);
onDatesChange(nextSelectedDates);
}))(evt, $date);
}, {
...rest,
...(isRange(mode) &&
selectedDates.length === 1 && {
onMouseEnter() {
setRangeEnd(dispatch, $date);
},
}),
tabIndex: active ? 0 : -1,
}, selected), [
mode,
toggle,
config.dates,
onDatesChange,
selectedDates,
dispatch,
selectSameDate,
]);
return { dayButton };
};
var createMonths = (offsetDate, selectedDates, locale, { minDate, maxDate }) => {
const { M, Y, D } = getDateParts(offsetDate);
const { Y: nY, M: nM } = getDateParts(newDate());
// 12 is a number of months in the year
return Array(12)
.fill(0)
.map((_, i) => {
// Prevent situation when previous month has less days than current March -> February
const maxMonthDate = daysInMonth(newDate(Y, i, 1));
const $date = newDate(Y, i, D > maxMonthDate ? maxMonthDate : D);
return {
$date,
month: formatMonthName($date, locale),
selected: selectedDates.some((d) => {
const { M: dM, Y: dY } = getDateParts(d);
return dY === Y && dM === i;
}),
active: M === i,
now: i === nM && Y === nY,
disabled: (isBeforeMinMonth(i, minDate) && isSameOrBeforeMinYear(Y, minDate)) ||
(isAfterMaxMonth(i, maxDate) && isSameOrAfterMaxYear(Y, maxDate)),
};
});
};
const useMonths = ({ selectedDates, offsetDate, config: { locale, dates }, }) => useMemo(() => ({
months: createMonths(offsetDate, selectedDates, locale, dates),
}), [dates, locale, offsetDate, selectedDates]);
const useMonthsPropGetters = (dpState) => {
const monthButton = useCallback(({ $date, disabled, selected, active }, { onClick, disabled: disabledProps, ...rest } = {}) => createPropGetter(!!disabledProps || disabled, (evt) => callAll(onClick, skipFirst(setDPOffset(dpState)))(evt, $date), {
...rest,
tabIndex: active ? 0 : -1,
}, selected), [dpState]);
return { monthButton };
};
var createTime = (d, { time, locale }) => {
const NOW = newDate();
const { interval, minTime, maxTime, useLocales } = time;
const { Y, M, D } = getDateParts(d || NOW);
// 1440 is a number of minutes in the day 60 * 24
const segments = 1440 / interval;
const minDate = getTimeDate(Y, M, D, minTime);
const maxDate = getTimeDate(Y, M, D, maxTime);
return Array(segments)
.fill(0)
.map((_, i) => {
const $date = newDate(Y, M, D, 0, i * interval);
const disabled = !d ||
minDateAndBefore(minDate, $date) ||
maxDateAndAfter(maxDate, $date);
return {
$date,
disabled,
now: isSame($date, NOW),
selected: d ? isSame(d, $date) : false,
time: useLocales
? formatLocaleTime($date, locale)
: formatTime($date, locale),
};
});
};
const useTime = ({ state: { focusDate }, config }) => useMemo(() => ({
time: createTime(focusDate, config),
}), [focusDate, config]);
const useTimePropGetter = ({ selectedDates, state: { focusDate }, config: { onDatesChange }, dispatch, }) => {
const timeButton = useCallback(({ $date, selected, disabled, now }, { onClick, disabled: disabledProps, ...rest } = {}) => createPropGetter(disabled || !!disabledProps, (evt) => {
if (selected)
return;
callAll(onClick, skipFirst((d) => {
const newSelected = selectedDates.map((selected) => {
return isSame(focusDate, selected) ? d : selected;
});
setFocus(dispatch, d);
onDatesChange(newSelected);
}))(evt, $date);
}, {
...rest,
tabIndex: selected || now ? 0 : -1,
}, selected), [selectedDates, onDatesChange, dispatch, focusDate]);
return { timeButton };
};
const createYears = (currentYear, offsetDate, selectedDates, { numberOfYears }, { minDate, maxDate }) => {
const { Y, M, D } = getDateParts(offsetDate);
const { Y: nY } = getDateParts(newDate());
return Array(numberOfYears)
.fill(0)
.map((_, i) => {
const year = currentYear + i;
const $date = newDate(year, M, D);
return {
$date,
active: Y === year,
disabled: isBeforeMinYear(year, minDate) || isAfterMaxYear(year, maxDate),
now: year === nY,
selected: selectedDates.some((d) => getDateParts(d).Y === year),
year,
};
});
};
const useYears = ({ selectedDates, offsetDate, state: { offsetYear }, config: { years, dates }, }) => useMemo(() => ({
years: createYears(offsetYear, offsetDate, selectedDates, years, dates),
}), [offsetDate, offsetYear, selectedDates, years, dates]);
const useYearsPropGetters = (dpState) => {
const { offsetDate, state: { offsetYear }, config: { dates, years: yearsConfig }, dispatch, } = dpState;
const { minDate, maxDate } = dates;
const { step, numberOfYears, mode } = yearsConfig;
const { D, M } = getDateParts(offsetDate);
const yearButton = useCallback(({ $date, disabled, selected, active }, { onClick, disabled: disabledProps, ...rest } = {}) => createPropGetter(!!disabledProps || disabled, (evt) => callAll(onClick, skipFirst(setDPOffset(dpState)))(evt, $date), {
...rest,
tabIndex: active ? 0 : -1,
}, selected), [dpState]);
const nextYearsButton = useCallback(({ onClick, disabled, ...rest } = {}) => {
const endYearDate = newDate(offsetYear + numberOfYears - 1, M, D);
const isDisabled = !!disabled ||
maxDateAndAfter(maxDate, endYearDate) ||
(isExactMode(mode) && !!maxDate && isSame(maxDate, endYearDate));
return createPropGetter(isDisabled, (evt) => callAll(onClick, skipAll(() => setYear(dispatch, offsetYear + step)))(evt), rest);
}, [maxDate, dispatch, offsetYear, step, D, M, numberOfYears, mode]);
const previousYearsButton = useCallback(({ onClick, disabled, ...rest } = {}) => {
const isDisabled = !!disabled || minDateAndBefore(minDate, newDate(offsetYear, M, D));
return createPropGetter(isDisabled, (evt) => callAll(onClick, skipAll(() => setYear(dispatch, offsetYear - step)))(evt), rest);
}, [minDate, dispatch, offsetYear, step, M, D]);
return {
yearButton,
nextYearsButton,
previousYearsButton,
};
};
const useDatePicker = (config) => {
const dpState = useDatePickerState(config);
return {
data: {
...useCalendars(dpState),
...useDays(dpState),
...useMonths(dpState),
...useTime(dpState),
...useYears(dpState),
},
propGetters: {
...useDaysPropGetters(dpState),
...useMonthsPropGetters(dpState),
...useTimePropGetter(dpState),
...useYearsPropGetters(dpState),
...useDatePickerOffsetPropGetters(dpState),
},
};
};
var DatePickerContext = createContext({});
var useDatePickerContext = () => useContext(DatePickerContext);
var DatePickerProvider = ({ children, config, }) => {
return (React.createElement(DatePickerContext.Provider, { value: useDatePicker(config) }, children));
};
var DatePickerStateContext = createContext({});
var useDatePickerStateContext = () => useContext(DatePickerStateContext);
var DatePickerStateProvider = ({ children, config, }) => {
return (React.createElement(DatePickerStateContext.Provider, { value: useDatePickerState(config) }, children));
};
const useContextCalendars = () => useCalendars(useDatePickerStateContext());
const useContextDays = () => useDays(useDatePickerStateContext());
const useContextDaysPropGetters = () => useDaysPropGetters(useDatePickerStateContext());
const useContextMonths = () => useMonths(useDatePickerStateContext());
const useContextMonthsPropGetters = () => useMonthsPropGetters(useDatePickerStateContext());
const useContextTime = () => useTime(useDatePickerStateContext());
const useContextTimePropGetters = () => useTimePropGetter(useDatePickerStateContext());
const useContextYears = () => useYears(useDatePickerStateContext());
const useContextYearsPropGetters = () => useYearsPropGetters(useDatePickerStateContext());
const useContextDatePickerOffsetPropGetters = () => useDatePickerOffsetPropGetters(useDatePickerStateContext());
export { DatePickerProvider, DatePickerStateProvider, useCalendars, useContextCalendars, useContextDatePickerOffsetPropGetters, useContextDays, useContextDaysPropGetters, useContextMonths, useContextMonthsPropGetters, useContextTime, useContextTimePropGetters, useContextYears, useContextYearsPropGetters, useDatePicker, useDatePickerContext, useDatePickerOffsetPropGetters, useDatePickerState, useDatePickerStateContext, useDays, useDaysPropGetters, useMonths, useMonthsPropGetters, useTime, useTimePropGetter, useYears, useYearsPropGetters };
//# sourceMappingURL=index.esm.mjs.map