(react) extend Calendar functionalities with range selection

Extend the functionality of the DatePicker component to include support
for a range calendar. This enhancement allows users to select a date
range spanning multiple calendar cells, enabling more flexible date
selection.
This commit is contained in:
Lebaud Antoine
2023-06-15 16:05:37 +02:00
committed by aleb_the_flash
parent 219d08c82e
commit 3631367e14
2 changed files with 254 additions and 223 deletions

View File

@@ -1,4 +1,4 @@
import React, { useMemo, useRef } from "react"; import React, { forwardRef, Ref, useMemo, useRef } from "react";
import { import {
CalendarDate, CalendarDate,
createCalendar, createCalendar,
@@ -11,15 +11,24 @@ import {
useDateFormatter, useDateFormatter,
useLocale, useLocale,
} from "@react-aria/i18n"; } from "@react-aria/i18n";
import { useCalendarState } from "@react-stately/calendar"; import {
import { useCalendar } from "@react-aria/calendar"; CalendarState,
RangeCalendarState,
useCalendarState,
useRangeCalendarState,
} from "@react-stately/calendar";
import {
CalendarAria,
useCalendar,
useRangeCalendar,
} from "@react-aria/calendar";
import { import {
useSelect, useSelect,
UseSelectReturnValue, UseSelectReturnValue,
UseSelectStateChange, UseSelectStateChange,
} from "downshift"; } from "downshift";
import classNames from "classnames"; import classNames from "classnames";
import { CalendarProps } from "react-aria"; import { CalendarProps, RangeCalendarProps } from "react-aria";
import { range } from ":/utils"; import { range } from ":/utils";
import { Button } from ":/components/Button"; import { Button } from ":/components/Button";
import { CalendarGrid } from ":/components/Forms/DatePicker/CalendarGrid"; import { CalendarGrid } from ":/components/Forms/DatePicker/CalendarGrid";
@@ -75,29 +84,25 @@ const DropdownValues = ({ options, downShift }: DropdownValuesProps) => (
</div> </div>
); );
interface CalendarSubProps extends CalendarProps<DateValue> { interface CalendarAuxProps extends CalendarAria {
minYear?: number; minYear?: number;
maxYear?: number; maxYear?: number;
state: RangeCalendarState | CalendarState;
} }
export const Calendar = ({ const CalendarAux = forwardRef(
(
{
state,
minYear = 1900, // in gregorian calendar. minYear = 1900, // in gregorian calendar.
maxYear = 2050, // in gregorian calendar. maxYear = 2050, // in gregorian calendar.
...props prevButtonProps,
}: CalendarSubProps) => { nextButtonProps,
const { locale } = useLocale(); calendarProps,
}: CalendarAuxProps,
ref: Ref<HTMLDivElement>
) => {
const { t } = useCunningham(); const { t } = useCunningham();
const ref = useRef(null);
const state = useCalendarState({
...props,
locale,
createCalendar,
});
const { calendarProps, prevButtonProps, nextButtonProps } = useCalendar(
props,
state
);
const useTimeZoneFormatter = (formatOptions: DateFormatterOptions) => { const useTimeZoneFormatter = (formatOptions: DateFormatterOptions) => {
return useDateFormatter({ return useDateFormatter({
@@ -261,7 +266,8 @@ export const Calendar = ({
onClick={() => state.focusPreviousSection(true)} onClick={() => state.focusPreviousSection(true)}
disabled={ disabled={
!!state.minValue && !!state.minValue &&
state.minValue.year > state.focusedDate.add({ years: -1 }).year state.minValue.year >
state.focusedDate.add({ years: -1 }).year
} }
aria-label={t( aria-label={t(
"components.forms.date_picker.previous_year_button_aria_label" "components.forms.date_picker.previous_year_button_aria_label"
@@ -302,4 +308,29 @@ export const Calendar = ({
<DropdownValues options={yearItems} downShift={downshiftYear} /> <DropdownValues options={yearItems} downShift={downshiftYear} />
</div> </div>
); );
}
);
export const Calendar = (props: CalendarProps<DateValue>) => {
const { locale } = useLocale();
const ref = useRef<HTMLDivElement>(null);
const state = useCalendarState({
...props,
locale,
createCalendar,
});
const calendarProps = useCalendar(props, state);
return <CalendarAux {...calendarProps} state={state} ref={ref} />;
};
export const CalendarRange = (props: RangeCalendarProps<DateValue>) => {
const { locale } = useLocale();
const ref = useRef<HTMLDivElement>(null);
const state = useRangeCalendarState({
...props,
locale,
createCalendar,
});
const calendarProps = useRangeCalendar(props, state, ref);
return <CalendarAux {...calendarProps} state={state} ref={ref} />;
}; };

View File

@@ -7,12 +7,12 @@ import {
today, today,
} from "@internationalized/date"; } from "@internationalized/date";
import { useCalendarGrid } from "react-aria"; import { useCalendarGrid } from "react-aria";
import { CalendarState } from "@react-stately/calendar"; import { CalendarState, RangeCalendarState } from "@react-stately/calendar";
import { CalendarCell } from ":/components/Forms/DatePicker/CalendarCell"; import { CalendarCell } from ":/components/Forms/DatePicker/CalendarCell";
import { range } from ":/utils"; import { range } from ":/utils";
interface CalendarGridProps { interface CalendarGridProps {
state: CalendarState; state: CalendarState | RangeCalendarState;
defaultDaysInWeek?: number; defaultDaysInWeek?: number;
} }