✨(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:
committed by
aleb_the_flash
parent
219d08c82e
commit
3631367e14
@@ -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,231 +84,253 @@ 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(
|
||||||
minYear = 1900, // in gregorian calendar.
|
(
|
||||||
maxYear = 2050, // in gregorian calendar.
|
{
|
||||||
...props
|
state,
|
||||||
}: CalendarSubProps) => {
|
minYear = 1900, // in gregorian calendar.
|
||||||
const { locale } = useLocale();
|
maxYear = 2050, // in gregorian calendar.
|
||||||
const { t } = useCunningham();
|
prevButtonProps,
|
||||||
const ref = useRef(null);
|
nextButtonProps,
|
||||||
|
calendarProps,
|
||||||
|
}: CalendarAuxProps,
|
||||||
|
ref: Ref<HTMLDivElement>
|
||||||
|
) => {
|
||||||
|
const { t } = useCunningham();
|
||||||
|
|
||||||
|
const useTimeZoneFormatter = (formatOptions: DateFormatterOptions) => {
|
||||||
|
return useDateFormatter({
|
||||||
|
...formatOptions,
|
||||||
|
timeZone: state.timeZone,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const monthItemsFormatter = useTimeZoneFormatter({ month: "long" });
|
||||||
|
const selectedMonthItemFormatter = useTimeZoneFormatter({ month: "short" });
|
||||||
|
const yearItemsFormatter = useTimeZoneFormatter({ year: "numeric" });
|
||||||
|
|
||||||
|
const monthItems: Array<Option> = useMemo(() => {
|
||||||
|
// Note that in some calendar systems, such as the Hebrew, the number of months may differ between years.
|
||||||
|
const numberOfMonths = state.focusedDate.calendar.getMonthsInYear(
|
||||||
|
state.focusedDate
|
||||||
|
);
|
||||||
|
return range(1, numberOfMonths).map((monthNumber) => {
|
||||||
|
const date = state.focusedDate.set({ month: monthNumber });
|
||||||
|
return {
|
||||||
|
value: monthNumber,
|
||||||
|
label: monthItemsFormatter.format(date.toDate(state.timeZone)),
|
||||||
|
disabled:
|
||||||
|
(!!state.minValue && state.minValue.month > monthNumber) ||
|
||||||
|
(!!state.maxValue && state.maxValue.month < monthNumber),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}, [state.maxValue, state.minValue, state.focusedDate.year]);
|
||||||
|
|
||||||
|
const yearItems: Array<Option> = useMemo(() => {
|
||||||
|
const calendarCurrentUser = createCalendar(
|
||||||
|
new Intl.DateTimeFormat().resolvedOptions().calendar
|
||||||
|
);
|
||||||
|
const minDate = toCalendar(
|
||||||
|
new CalendarDate(new GregorianCalendar(), minYear, 1, 1),
|
||||||
|
calendarCurrentUser
|
||||||
|
);
|
||||||
|
const maxDate = toCalendar(
|
||||||
|
new CalendarDate(new GregorianCalendar(), maxYear, 12, 31),
|
||||||
|
calendarCurrentUser
|
||||||
|
);
|
||||||
|
return range(minDate.year, maxDate.year).map((yearNumber) => {
|
||||||
|
const date = state.focusedDate.set({ year: yearNumber });
|
||||||
|
return {
|
||||||
|
value: yearNumber,
|
||||||
|
label: yearItemsFormatter.format(date.toDate(state.timeZone)),
|
||||||
|
disabled:
|
||||||
|
(!!state.minValue && state.minValue.year > yearNumber) ||
|
||||||
|
(!!state.maxValue && state.maxValue.year < yearNumber),
|
||||||
|
};
|
||||||
|
});
|
||||||
|
}, [state.focusedDate, state.timeZone, state.maxValue, state.minValue]);
|
||||||
|
|
||||||
|
const useDownshiftSelect = (
|
||||||
|
key: string,
|
||||||
|
items: Array<Option>
|
||||||
|
): UseSelectReturnValue<Option> => {
|
||||||
|
return useSelect({
|
||||||
|
items,
|
||||||
|
itemToString: optionToString,
|
||||||
|
onSelectedItemChange: (e: UseSelectStateChange<Option>) => {
|
||||||
|
const updatedFocusedDate = state.focusedDate.set({
|
||||||
|
[key]: e?.selectedItem?.value,
|
||||||
|
});
|
||||||
|
state.setFocusedDate(updatedFocusedDate);
|
||||||
|
},
|
||||||
|
});
|
||||||
|
};
|
||||||
|
const downshiftMonth = useDownshiftSelect("month", monthItems);
|
||||||
|
const downshiftYear = useDownshiftSelect("year", yearItems);
|
||||||
|
|
||||||
|
// isDisabled and onPress props don't exist on the <Button /> component.
|
||||||
|
// remove them to avoid any warning.
|
||||||
|
const {
|
||||||
|
isDisabled: isPrevButtonDisabled,
|
||||||
|
onPress: onPressPrev,
|
||||||
|
...prevButtonOtherProps
|
||||||
|
} = prevButtonProps;
|
||||||
|
const {
|
||||||
|
isDisabled: isNextButtonDisabled,
|
||||||
|
onPress: onPressNext,
|
||||||
|
...nextButtonOtherProps
|
||||||
|
} = nextButtonProps;
|
||||||
|
|
||||||
|
const getToggleButtonProps = (
|
||||||
|
key: string,
|
||||||
|
items: Array<Option>,
|
||||||
|
downshift: UseSelectReturnValue<Option>
|
||||||
|
) => ({
|
||||||
|
...downshift.getToggleButtonProps(),
|
||||||
|
onClick: () => {
|
||||||
|
const selectedItem = items.find(
|
||||||
|
(item) => item.value === state.focusedDate[key as keyof CalendarDate]
|
||||||
|
);
|
||||||
|
if (selectedItem) {
|
||||||
|
downshift.selectItem(selectedItem);
|
||||||
|
}
|
||||||
|
downshift.toggleMenu();
|
||||||
|
},
|
||||||
|
"aria-label": t(
|
||||||
|
`components.forms.date_picker.${key}_select_button_aria_label`
|
||||||
|
),
|
||||||
|
});
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="c__calendar">
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
{...calendarProps}
|
||||||
|
className={classNames("c__calendar__wrapper", {
|
||||||
|
"c__calendar__wrapper--opened":
|
||||||
|
!downshiftMonth.isOpen && !downshiftYear.isOpen,
|
||||||
|
})}
|
||||||
|
>
|
||||||
|
<div className="c__calendar__wrapper__header">
|
||||||
|
<div className="c__calendar__wrapper__header__actions">
|
||||||
|
<Button
|
||||||
|
color="tertiary"
|
||||||
|
size="small"
|
||||||
|
icon={<span className="material-icons">navigate_before</span>}
|
||||||
|
{...{
|
||||||
|
...prevButtonOtherProps,
|
||||||
|
"aria-label": t(
|
||||||
|
"components.forms.date_picker.previous_month_button_aria_label"
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
disabled={isPrevButtonDisabled}
|
||||||
|
onClick={() => state.focusPreviousSection()}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
className="c__calendar__wrapper__header__actions__dropdown"
|
||||||
|
color="tertiary"
|
||||||
|
size="small"
|
||||||
|
iconPosition="right"
|
||||||
|
icon={<span className="material-icons">arrow_drop_down</span>}
|
||||||
|
{...getToggleButtonProps("month", monthItems, downshiftMonth)}
|
||||||
|
>
|
||||||
|
{selectedMonthItemFormatter.format(
|
||||||
|
state.focusedDate.toDate(state.timeZone)
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
color="tertiary"
|
||||||
|
size="small"
|
||||||
|
icon={<span className="material-icons">navigate_next</span>}
|
||||||
|
{...{
|
||||||
|
...nextButtonOtherProps,
|
||||||
|
"aria-label": t(
|
||||||
|
"components.forms.date_picker.next_month_button_aria_label"
|
||||||
|
),
|
||||||
|
}}
|
||||||
|
disabled={isNextButtonDisabled}
|
||||||
|
onClick={() => state.focusNextSection()}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div className="c__calendar__wrapper__header__actions">
|
||||||
|
<Button
|
||||||
|
color="tertiary"
|
||||||
|
size="small"
|
||||||
|
icon={<span className="material-icons">navigate_before</span>}
|
||||||
|
onClick={() => state.focusPreviousSection(true)}
|
||||||
|
disabled={
|
||||||
|
!!state.minValue &&
|
||||||
|
state.minValue.year >
|
||||||
|
state.focusedDate.add({ years: -1 }).year
|
||||||
|
}
|
||||||
|
aria-label={t(
|
||||||
|
"components.forms.date_picker.previous_year_button_aria_label"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<Button
|
||||||
|
className="c__calendar__wrapper__header__actions__dropdown"
|
||||||
|
color="tertiary"
|
||||||
|
size="small"
|
||||||
|
iconPosition="right"
|
||||||
|
icon={<span className="material-icons">arrow_drop_down</span>}
|
||||||
|
{...getToggleButtonProps("year", yearItems, downshiftYear)}
|
||||||
|
>
|
||||||
|
{yearItemsFormatter.format(
|
||||||
|
state.focusedDate.toDate(state.timeZone)
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
color="tertiary"
|
||||||
|
size="small"
|
||||||
|
icon={<span className="material-icons">navigate_next</span>}
|
||||||
|
onClick={() => state.focusNextSection(true)}
|
||||||
|
disabled={
|
||||||
|
!!state.maxValue &&
|
||||||
|
state.maxValue.year < state.focusedDate.add({ years: 1 }).year
|
||||||
|
}
|
||||||
|
aria-label={t(
|
||||||
|
"components.forms.date_picker.next_year_button_aria_label"
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
{!downshiftMonth.isOpen && !downshiftYear.isOpen && (
|
||||||
|
<CalendarGrid state={state} />
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<DropdownValues options={monthItems} downShift={downshiftMonth} />
|
||||||
|
<DropdownValues options={yearItems} downShift={downshiftYear} />
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export const Calendar = (props: CalendarProps<DateValue>) => {
|
||||||
|
const { locale } = useLocale();
|
||||||
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
const state = useCalendarState({
|
const state = useCalendarState({
|
||||||
...props,
|
...props,
|
||||||
locale,
|
locale,
|
||||||
createCalendar,
|
createCalendar,
|
||||||
});
|
});
|
||||||
const { calendarProps, prevButtonProps, nextButtonProps } = useCalendar(
|
const calendarProps = useCalendar(props, state);
|
||||||
props,
|
return <CalendarAux {...calendarProps} state={state} ref={ref} />;
|
||||||
state
|
};
|
||||||
);
|
|
||||||
|
export const CalendarRange = (props: RangeCalendarProps<DateValue>) => {
|
||||||
const useTimeZoneFormatter = (formatOptions: DateFormatterOptions) => {
|
const { locale } = useLocale();
|
||||||
return useDateFormatter({
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
...formatOptions,
|
const state = useRangeCalendarState({
|
||||||
timeZone: state.timeZone,
|
...props,
|
||||||
});
|
locale,
|
||||||
};
|
createCalendar,
|
||||||
|
});
|
||||||
const monthItemsFormatter = useTimeZoneFormatter({ month: "long" });
|
const calendarProps = useRangeCalendar(props, state, ref);
|
||||||
const selectedMonthItemFormatter = useTimeZoneFormatter({ month: "short" });
|
return <CalendarAux {...calendarProps} state={state} ref={ref} />;
|
||||||
const yearItemsFormatter = useTimeZoneFormatter({ year: "numeric" });
|
|
||||||
|
|
||||||
const monthItems: Array<Option> = useMemo(() => {
|
|
||||||
// Note that in some calendar systems, such as the Hebrew, the number of months may differ between years.
|
|
||||||
const numberOfMonths = state.focusedDate.calendar.getMonthsInYear(
|
|
||||||
state.focusedDate
|
|
||||||
);
|
|
||||||
return range(1, numberOfMonths).map((monthNumber) => {
|
|
||||||
const date = state.focusedDate.set({ month: monthNumber });
|
|
||||||
return {
|
|
||||||
value: monthNumber,
|
|
||||||
label: monthItemsFormatter.format(date.toDate(state.timeZone)),
|
|
||||||
disabled:
|
|
||||||
(!!state.minValue && state.minValue.month > monthNumber) ||
|
|
||||||
(!!state.maxValue && state.maxValue.month < monthNumber),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}, [state.maxValue, state.minValue, state.focusedDate.year]);
|
|
||||||
|
|
||||||
const yearItems: Array<Option> = useMemo(() => {
|
|
||||||
const calendarCurrentUser = createCalendar(
|
|
||||||
new Intl.DateTimeFormat().resolvedOptions().calendar
|
|
||||||
);
|
|
||||||
const minDate = toCalendar(
|
|
||||||
new CalendarDate(new GregorianCalendar(), minYear, 1, 1),
|
|
||||||
calendarCurrentUser
|
|
||||||
);
|
|
||||||
const maxDate = toCalendar(
|
|
||||||
new CalendarDate(new GregorianCalendar(), maxYear, 12, 31),
|
|
||||||
calendarCurrentUser
|
|
||||||
);
|
|
||||||
return range(minDate.year, maxDate.year).map((yearNumber) => {
|
|
||||||
const date = state.focusedDate.set({ year: yearNumber });
|
|
||||||
return {
|
|
||||||
value: yearNumber,
|
|
||||||
label: yearItemsFormatter.format(date.toDate(state.timeZone)),
|
|
||||||
disabled:
|
|
||||||
(!!state.minValue && state.minValue.year > yearNumber) ||
|
|
||||||
(!!state.maxValue && state.maxValue.year < yearNumber),
|
|
||||||
};
|
|
||||||
});
|
|
||||||
}, [state.focusedDate, state.timeZone, state.maxValue, state.minValue]);
|
|
||||||
|
|
||||||
const useDownshiftSelect = (
|
|
||||||
key: string,
|
|
||||||
items: Array<Option>
|
|
||||||
): UseSelectReturnValue<Option> => {
|
|
||||||
return useSelect({
|
|
||||||
items,
|
|
||||||
itemToString: optionToString,
|
|
||||||
onSelectedItemChange: (e: UseSelectStateChange<Option>) => {
|
|
||||||
const updatedFocusedDate = state.focusedDate.set({
|
|
||||||
[key]: e?.selectedItem?.value,
|
|
||||||
});
|
|
||||||
state.setFocusedDate(updatedFocusedDate);
|
|
||||||
},
|
|
||||||
});
|
|
||||||
};
|
|
||||||
const downshiftMonth = useDownshiftSelect("month", monthItems);
|
|
||||||
const downshiftYear = useDownshiftSelect("year", yearItems);
|
|
||||||
|
|
||||||
// isDisabled and onPress props don't exist on the <Button /> component.
|
|
||||||
// remove them to avoid any warning.
|
|
||||||
const {
|
|
||||||
isDisabled: isPrevButtonDisabled,
|
|
||||||
onPress: onPressPrev,
|
|
||||||
...prevButtonOtherProps
|
|
||||||
} = prevButtonProps;
|
|
||||||
const {
|
|
||||||
isDisabled: isNextButtonDisabled,
|
|
||||||
onPress: onPressNext,
|
|
||||||
...nextButtonOtherProps
|
|
||||||
} = nextButtonProps;
|
|
||||||
|
|
||||||
const getToggleButtonProps = (
|
|
||||||
key: string,
|
|
||||||
items: Array<Option>,
|
|
||||||
downshift: UseSelectReturnValue<Option>
|
|
||||||
) => ({
|
|
||||||
...downshift.getToggleButtonProps(),
|
|
||||||
onClick: () => {
|
|
||||||
const selectedItem = items.find(
|
|
||||||
(item) => item.value === state.focusedDate[key as keyof CalendarDate]
|
|
||||||
);
|
|
||||||
if (selectedItem) {
|
|
||||||
downshift.selectItem(selectedItem);
|
|
||||||
}
|
|
||||||
downshift.toggleMenu();
|
|
||||||
},
|
|
||||||
"aria-label": t(
|
|
||||||
`components.forms.date_picker.${key}_select_button_aria_label`
|
|
||||||
),
|
|
||||||
});
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="c__calendar">
|
|
||||||
<div
|
|
||||||
ref={ref}
|
|
||||||
{...calendarProps}
|
|
||||||
className={classNames("c__calendar__wrapper", {
|
|
||||||
"c__calendar__wrapper--opened":
|
|
||||||
!downshiftMonth.isOpen && !downshiftYear.isOpen,
|
|
||||||
})}
|
|
||||||
>
|
|
||||||
<div className="c__calendar__wrapper__header">
|
|
||||||
<div className="c__calendar__wrapper__header__actions">
|
|
||||||
<Button
|
|
||||||
color="tertiary"
|
|
||||||
size="small"
|
|
||||||
icon={<span className="material-icons">navigate_before</span>}
|
|
||||||
{...{
|
|
||||||
...prevButtonOtherProps,
|
|
||||||
"aria-label": t(
|
|
||||||
"components.forms.date_picker.previous_month_button_aria_label"
|
|
||||||
),
|
|
||||||
}}
|
|
||||||
disabled={isPrevButtonDisabled}
|
|
||||||
onClick={() => state.focusPreviousSection()}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
className="c__calendar__wrapper__header__actions__dropdown"
|
|
||||||
color="tertiary"
|
|
||||||
size="small"
|
|
||||||
iconPosition="right"
|
|
||||||
icon={<span className="material-icons">arrow_drop_down</span>}
|
|
||||||
{...getToggleButtonProps("month", monthItems, downshiftMonth)}
|
|
||||||
>
|
|
||||||
{selectedMonthItemFormatter.format(
|
|
||||||
state.focusedDate.toDate(state.timeZone)
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
color="tertiary"
|
|
||||||
size="small"
|
|
||||||
icon={<span className="material-icons">navigate_next</span>}
|
|
||||||
{...{
|
|
||||||
...nextButtonOtherProps,
|
|
||||||
"aria-label": t(
|
|
||||||
"components.forms.date_picker.next_month_button_aria_label"
|
|
||||||
),
|
|
||||||
}}
|
|
||||||
disabled={isNextButtonDisabled}
|
|
||||||
onClick={() => state.focusNextSection()}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div className="c__calendar__wrapper__header__actions">
|
|
||||||
<Button
|
|
||||||
color="tertiary"
|
|
||||||
size="small"
|
|
||||||
icon={<span className="material-icons">navigate_before</span>}
|
|
||||||
onClick={() => state.focusPreviousSection(true)}
|
|
||||||
disabled={
|
|
||||||
!!state.minValue &&
|
|
||||||
state.minValue.year > state.focusedDate.add({ years: -1 }).year
|
|
||||||
}
|
|
||||||
aria-label={t(
|
|
||||||
"components.forms.date_picker.previous_year_button_aria_label"
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
<Button
|
|
||||||
className="c__calendar__wrapper__header__actions__dropdown"
|
|
||||||
color="tertiary"
|
|
||||||
size="small"
|
|
||||||
iconPosition="right"
|
|
||||||
icon={<span className="material-icons">arrow_drop_down</span>}
|
|
||||||
{...getToggleButtonProps("year", yearItems, downshiftYear)}
|
|
||||||
>
|
|
||||||
{yearItemsFormatter.format(
|
|
||||||
state.focusedDate.toDate(state.timeZone)
|
|
||||||
)}
|
|
||||||
</Button>
|
|
||||||
<Button
|
|
||||||
color="tertiary"
|
|
||||||
size="small"
|
|
||||||
icon={<span className="material-icons">navigate_next</span>}
|
|
||||||
onClick={() => state.focusNextSection(true)}
|
|
||||||
disabled={
|
|
||||||
!!state.maxValue &&
|
|
||||||
state.maxValue.year < state.focusedDate.add({ years: 1 }).year
|
|
||||||
}
|
|
||||||
aria-label={t(
|
|
||||||
"components.forms.date_picker.next_year_button_aria_label"
|
|
||||||
)}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
{!downshiftMonth.isOpen && !downshiftYear.isOpen && (
|
|
||||||
<CalendarGrid state={state} />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<DropdownValues options={monthItems} downShift={downshiftMonth} />
|
|
||||||
<DropdownValues options={yearItems} downShift={downshiftYear} />
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user