import {
FocusEventHandler,
HTMLProps,
KeyboardEventHandler,
MouseEventHandler,
PointerEventHandler,
TouchEventHandler
} from 'react';
import { useDayPicker } from 'contexts/DayPicker';
import { useFocusContext } from 'contexts/Focus';
import { useSelectMultiple } from 'contexts/SelectMultiple';
import { useSelectRange } from 'contexts/SelectRange';
import { useSelectSingle } from 'contexts/SelectSingle';
import { isDayPickerMultiple } from 'types/DayPickerMultiple';
import { isDayPickerRange } from 'types/DayPickerRange';
import { isDayPickerSingle } from 'types/DayPickerSingle';
import { ActiveModifiers } from 'types/Modifiers';
export type EventName =
| 'onClick'
| 'onFocus'
| 'onBlur'
| 'onKeyDown'
| 'onKeyUp'
| 'onMouseEnter'
| 'onMouseLeave'
| 'onPointerEnter'
| 'onPointerLeave'
| 'onTouchCancel'
| 'onTouchEnd'
| 'onTouchMove'
| 'onTouchStart';
export type DayEventName =
| 'onDayClick'
| 'onDayFocus'
| 'onDayBlur'
| 'onDayKeyDown'
| 'onDayKeyUp'
| 'onDayMouseEnter'
| 'onDayMouseLeave'
| 'onDayPointerEnter'
| 'onDayPointerLeave'
| 'onDayTouchCancel'
| 'onDayTouchEnd'
| 'onDayTouchMove'
| 'onDayTouchStart';
export type DayEventHandlers = Pick, EventName>;
/**
* This hook returns details about the content to render in the day cell.
*
*
* When a day cell is rendered in the table, DayPicker can either:
*
* - render nothing: when the day is outside the month or has matched the
* "hidden" modifier.
* - render a button when `onDayClick` or a selection mode is set.
* - render a non-interactive element: when no selection mode is set, the day
* cell shouldn’t respond to any interaction. DayPicker should render a `div`
* or a `span`.
*
* ### Usage
*
* Use this hook to customize the behavior of the {@link Day} component. Create a
* new `Day` component using this hook and pass it to the `components` prop.
* The source of {@link Day} can be a good starting point.
*
*/
export function useDayEventHandlers(
date: Date,
activeModifiers: ActiveModifiers
): DayEventHandlers {
const dayPicker = useDayPicker();
const single = useSelectSingle();
const multiple = useSelectMultiple();
const range = useSelectRange();
const {
focusDayAfter,
focusDayBefore,
focusWeekAfter,
focusWeekBefore,
blur,
focus,
focusMonthBefore,
focusMonthAfter,
focusYearBefore,
focusYearAfter,
focusStartOfWeek,
focusEndOfWeek
} = useFocusContext();
const onClick: MouseEventHandler = (e) => {
if (isDayPickerSingle(dayPicker)) {
single.onDayClick?.(date, activeModifiers, e);
} else if (isDayPickerMultiple(dayPicker)) {
multiple.onDayClick?.(date, activeModifiers, e);
} else if (isDayPickerRange(dayPicker)) {
range.onDayClick?.(date, activeModifiers, e);
} else {
dayPicker.onDayClick?.(date, activeModifiers, e);
}
};
const onFocus: FocusEventHandler = (e) => {
focus(date);
dayPicker.onDayFocus?.(date, activeModifiers, e);
};
const onBlur: FocusEventHandler = (e) => {
blur();
dayPicker.onDayBlur?.(date, activeModifiers, e);
};
const onMouseEnter: MouseEventHandler = (e) => {
dayPicker.onDayMouseEnter?.(date, activeModifiers, e);
};
const onMouseLeave: MouseEventHandler = (e) => {
dayPicker.onDayMouseLeave?.(date, activeModifiers, e);
};
const onPointerEnter: PointerEventHandler = (e) => {
dayPicker.onDayPointerEnter?.(date, activeModifiers, e);
};
const onPointerLeave: PointerEventHandler = (e) => {
dayPicker.onDayPointerLeave?.(date, activeModifiers, e);
};
const onTouchCancel: TouchEventHandler = (e) => {
dayPicker.onDayTouchCancel?.(date, activeModifiers, e);
};
const onTouchEnd: TouchEventHandler = (e) => {
dayPicker.onDayTouchEnd?.(date, activeModifiers, e);
};
const onTouchMove: TouchEventHandler = (e) => {
dayPicker.onDayTouchMove?.(date, activeModifiers, e);
};
const onTouchStart: TouchEventHandler = (e) => {
dayPicker.onDayTouchStart?.(date, activeModifiers, e);
};
const onKeyUp: KeyboardEventHandler = (e) => {
dayPicker.onDayKeyUp?.(date, activeModifiers, e);
};
const onKeyDown: KeyboardEventHandler = (e) => {
switch (e.key) {
case 'ArrowLeft':
e.preventDefault();
e.stopPropagation();
dayPicker.dir === 'rtl' ? focusDayAfter() : focusDayBefore();
break;
case 'ArrowRight':
e.preventDefault();
e.stopPropagation();
dayPicker.dir === 'rtl' ? focusDayBefore() : focusDayAfter();
break;
case 'ArrowDown':
e.preventDefault();
e.stopPropagation();
focusWeekAfter();
break;
case 'ArrowUp':
e.preventDefault();
e.stopPropagation();
focusWeekBefore();
break;
case 'PageUp':
e.preventDefault();
e.stopPropagation();
e.shiftKey ? focusYearBefore() : focusMonthBefore();
break;
case 'PageDown':
e.preventDefault();
e.stopPropagation();
e.shiftKey ? focusYearAfter() : focusMonthAfter();
break;
case 'Home':
e.preventDefault();
e.stopPropagation();
focusStartOfWeek();
break;
case 'End':
e.preventDefault();
e.stopPropagation();
focusEndOfWeek();
break;
}
dayPicker.onDayKeyDown?.(date, activeModifiers, e);
};
const eventHandlers: DayEventHandlers = {
onClick,
onFocus,
onBlur,
onKeyDown,
onKeyUp,
onMouseEnter,
onMouseLeave,
onPointerEnter,
onPointerLeave,
onTouchCancel,
onTouchEnd,
onTouchMove,
onTouchStart
};
return eventHandlers;
}