Skip to content

Commit

Permalink
initial hover date range and no toggle button behaviour
Browse files Browse the repository at this point in the history
  • Loading branch information
stowball committed Dec 17, 2024
1 parent 5312a7a commit cef519b
Show file tree
Hide file tree
Showing 10 changed files with 281 additions and 59 deletions.
2 changes: 1 addition & 1 deletion docs/next-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information.
2 changes: 1 addition & 1 deletion example-site/next-env.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@
/// <reference types="next/image-types/global" />

// NOTE: This file should not be edited
// see https://nextjs.org/docs/basic-features/typescript for more information.
// see https://nextjs.org/docs/pages/building-your-application/configuring/typescript for more information.
107 changes: 96 additions & 11 deletions packages/react/src/date-picker/Calendar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ import {
type ChangeEvent,
type ChangeEventHandler,
type MouseEventHandler,
type RefObject,
// type RefObject,
useCallback,
useMemo,
useRef,
Expand Down Expand Up @@ -83,25 +83,104 @@ export type CalendarRangeProps = Omit<
DayPickerRangeProps,
'mode' | 'components'
> & {
returnFocusRef?: RefObject<HTMLButtonElement>;
// returnFocusRef?: RefObject<HTMLButtonElement>;
inputMode?: 'from' | 'to';
onHover?: (date: Date) => void;
};

export function CalendarRange({
returnFocusRef,
// returnFocusRef,
inputMode,
onHover,
...props
}: CalendarRangeProps) {
console.log(`props`, props);
// console.log(`props`, props);
// const { onHover } = props;
const combinedDayPickerProps = {
// ...defaultDayPickerProps,
components: {
...defaultDayPickerProps.components,
// Custom `Day` component to abide by the Date Picker Dialog ARIA pattern
// Key change: we no longer render <button>s, everything happens on <td>s
// Default: https://github.com/gpbl/react-day-picker/blob/9ad13dc72fff814dcf720a62f6e3b5ea38e8af6d/src/components/Day.tsx
Day: function Day(props: DayProps) {
const buttonRef = useRef<HTMLButtonElement>(null);
const { activeModifiers, buttonProps, isHidden } = useDayRender(
props.date,
props.displayMonth,
buttonRef
);

// console.log(`activeModifiers.qux`, activeModifiers.qux);
// console.log(`props.date`, props.date);
// console.log(`selectedDays`, selectedDays);

// @ts-expect-error: role is unused
const { children, onClick, onKeyDown, role, ...restButtonProps } =
buttonProps;

const handleKeyDown = (event: KeyboardEvent) => {
if (!isHidden && (event.key === 'Enter' || event.key === 'Space')) {
event.preventDefault();
event.stopPropagation();
// @ts-expect-error: Argument of type 'KeyboardEvent' is not assignable to parameter of type 'MouseEvent<HTMLButtonElement, MouseEvent>'.
onClick?.(event);
} else {
// @ts-expect-error: Argument of type 'KeyboardEvent' is not assignable to parameter of type 'KeyboardEvent<HTMLButtonElement>'
onKeyDown?.(event);
}
};

const interactiveProps = {
'aria-current': activeModifiers.today ? 'date' : undefined,
// Improve the aria labels of each button.
// Selected and dates within range are manually announced.
'aria-label': `${
activeModifiers.selected && !activeModifiers.range_middle
? 'Selected. '
: ''
}${formatHumanReadableDate(props.date)}${
activeModifiers.range_middle ? '. Between selected dates' : ''
}`,
'aria-selected':
// React Day Picker incorrectly marks ranges as selected
activeModifiers.range_middle ? undefined : activeModifiers.selected,
onClick,
...restButtonProps,
};

return (
<td
// @ts-expect-error: Type '(event: KeyboardEvent) => void' is not assignable to type 'KeyboardEventHandler<HTMLTableDataCellElement>'.
onKeyDown={handleKeyDown}
// @ts-expect-error: Type 'RefObject<HTMLButtonElement>' is not assignable to type 'LegacyRef<HTMLTableDataCellElement> | undefined'
ref={buttonRef}
tabIndex={-1}
{...(isHidden ? undefined : interactiveProps)}
// data-in-range={activeModifiers.qux}
// data-day-after-from={props.date > selectedDays?.from}
// data-day-before-to={props.date < selectedDays?.to}
onMouseEnter={onHover ? () => onHover(props.date) : undefined}
>
{/* Without this focusable span, left and right do not work in screen readers */}
<span tabIndex={-1}>{isHidden ? undefined : children}</span>
</td>
);
},
},
};

return (
<FocusLock
autoFocus={false}
onDeactivation={() => {
// https://github.com/theKashey/react-focus-lock#unmounting-and-focus-management
if (!returnFocusRef) return;
window.setTimeout(() => returnFocusRef.current?.focus(), 0);
}}
// onDeactivation={() => {
// // https://github.com/theKashey/react-focus-lock#unmounting-and-focus-management
// if (!returnFocusRef) return;
// window.setTimeout(() => returnFocusRef.current?.focus(), 0);
// }}
>
<CalendarRangeContainer dateRange={props.selected}>
<DayPicker mode="range" {...defaultDayPickerProps} {...props} />
<CalendarRangeContainer dateRange={props.selected} inputMode={inputMode}>
<DayPicker mode="range" {...combinedDayPickerProps} {...props} />
</CalendarRangeContainer>
</FocusLock>
);
Expand Down Expand Up @@ -369,6 +448,10 @@ const calendarComponents: CustomComponents = {
buttonRef
);

// console.log(`activeModifiers`, activeModifiers);
// console.log(`props.date`, props.date);
// console.log(`selectedDays`, selectedDays);

// @ts-expect-error: role is unused
const { children, onClick, onKeyDown, role, ...restButtonProps } =
buttonProps;
Expand Down Expand Up @@ -411,6 +494,8 @@ const calendarComponents: CustomComponents = {
ref={buttonRef}
tabIndex={-1}
{...(isHidden ? undefined : interactiveProps)}
// data-day-after-from={props.date > selectedDays?.from}
// data-day-before-to={props.date < selectedDays?.to}
>
{/* Without this focusable span, left and right do not work in screen readers */}
<span tabIndex={-1}>{isHidden ? undefined : children}</span>
Expand Down
7 changes: 6 additions & 1 deletion packages/react/src/date-picker/CalendarContainer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,17 +25,22 @@ export function CalendarContainer({ children }: CalendarContainerProps) {

export type CalendarRangeContainerProps = PropsWithChildren<{
dateRange?: { from?: Date; to?: Date };
inputMode?: 'from' | 'to';
}>;

export function CalendarRangeContainer({
children,
dateRange,
inputMode,
}: CalendarRangeContainerProps) {
return (
<Box
aria-label="Choose date range"
aria-modal="true"
css={[reactDayPickerStyles, reactDayRangePickerStyles(dateRange)]}
css={[
reactDayPickerStyles,
reactDayRangePickerStyles(dateRange, inputMode),
]}
display="inline-block"
paddingX={[0.25, 1]}
paddingY={1}
Expand Down
61 changes: 57 additions & 4 deletions packages/react/src/date-picker/reactDayPickerStyles.ts
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,7 @@ export const reactDayPickerStyles = {
'&:not([disabled], :focus):hover': {
backgroundColor: boxPalette.backgroundShade,
color: boxPalette.foregroundText,
fontWeight: 'bold',
textDecoration: 'underline',
zIndex: tokens.zIndex.elevated,
...highContrastOutlineStyles,
Expand Down Expand Up @@ -150,10 +151,33 @@ export const reactDayPickerStyles = {
},
} as const;

export const reactDayRangePickerStyles = (dateRange?: {
from?: Date;
to?: Date;
}) => {
// Start date only picked
// rdp-day rdp-day_selected rdp-day_range_end rdp-day_range_start
// rdp-day rdp-day_selected rdp-day_range_start

// Middle date
// rdp-day rdp-day_selected rdp-day_range_middle

// End date picked
// rdp-day rdp-day_selected rdp-day_range_end

// Start date is end date
// rdp-day rdp-day_selected rdp-day_range_end rdp-day_range_start

// type ReactDayRangePickerStyles = {
// ?: {
// from?: Date;
// to?: Date;
// }

// }
export const reactDayRangePickerStyles = (
dateRange?: {
from?: Date;
to?: Date;
},
inputMode?: 'from' | 'to'
) => {
const { from, to } = dateRange ?? {};
const startStyles = {
borderRadius: 0,
Expand All @@ -166,6 +190,9 @@ export const reactDayRangePickerStyles = (dateRange?: {
borderTopRightRadius: '50%',
};

// console.log(`from`, from);
// console.log(`to`, to);

return {
// Middle of the date range
'.rdp-day_selected:not([disabled]).rdp-day_range_middle': {
Expand All @@ -184,5 +211,31 @@ export const reactDayRangePickerStyles = (dateRange?: {
...(from && startStyles),
...(to && endStyles),
},
// '.rdp-day_range_start ~ .rdp-day': {
// ...(from &&
// !to && {
// backgroundColor: 'red',
// }),
// },
// '[data-in-range]': {

...(inputMode && {
'.rdp-day': {
...(inputMode === 'from' && startStyles),
...(inputMode === 'to' && endStyles),
'&:hover': {
backgroundColor: boxPalette.selected,
// border: '3px solid red',
},
},
}),

'.range': {
backgroundColor: boxPalette.selectedMuted,
borderRadius: 0,
color: boxPalette.foregroundText,
fontWeight: 'bold',
// fontSize: 30,
},
};
};
4 changes: 2 additions & 2 deletions packages/react/src/date-picker/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,8 @@ export const acceptedDateFormats = [

export type AcceptedDateFormats = (typeof acceptedDateFormats)[number];

export const formatDate = (date: Date, dateformat: AcceptedDateFormats) =>
format(date, dateformat);
export const formatDate = (date?: Date, dateformat?: AcceptedDateFormats) =>
date && dateformat ? format(date, dateformat) : '';

export const formatHumanReadableDate = (date: Date) =>
format(date, 'do MMMM yyyy EEEE');
Expand Down
Loading

0 comments on commit cef519b

Please sign in to comment.