(react) introduce DateRangePicker component

Introduce a flexible and reusable DateRangePicker component to facilitate the
selection of date ranges in the design system.
This commit is contained in:
Lebaud Antoine
2023-06-16 16:58:16 +02:00
committed by aleb_the_flash
parent 0775490a60
commit 0d6b98ee1f
3 changed files with 1146 additions and 0 deletions

View File

@@ -0,0 +1,5 @@
---
"@openfun/cunningham-react": minor
---
Introduce a DateRangePicker component

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,107 @@
import React, { useMemo, useRef, useState } from "react";
import { DateValue } from "@internationalized/date";
import {
DateRangePickerStateOptions,
useDateRangePickerState,
} from "@react-stately/datepicker";
import { useDateRangePicker, DateRange } from "@react-aria/datepicker";
import { CalendarRange } from ":/components/Forms/DatePicker/Calendar";
import DatePickerAux, {
DatePickerAuxSubProps,
} from ":/components/Forms/DatePicker/DatePickerAux";
import DateFieldBox from ":/components/Forms/DatePicker/DateField";
import { StringsOrDateRange } from ":/components/Forms/DatePicker/types";
import {
getDefaultPickerOptions,
parseRangeCalendarDate,
} from ":/components/Forms/DatePicker/utils";
export type DateRangePickerProps = DatePickerAuxSubProps & {
startLabel: string;
endLabel: string;
value?: null | StringsOrDateRange;
defaultValue?: StringsOrDateRange;
onChange?: (value: [string, string] | null) => void;
};
const DateRangePicker = ({
startLabel,
endLabel,
...props
}: DateRangePickerProps) => {
if (props.defaultValue && props.value) {
throw new Error(
"You cannot use both defaultValue and value props on DateRangePicker component"
);
}
const ref = useRef<HTMLDivElement>(null);
const [isFocused, setIsFocused] = useState(false);
const options: DateRangePickerStateOptions<DateValue> = {
...getDefaultPickerOptions(props),
value: props.value === null ? null : parseRangeCalendarDate(props.value),
defaultValue: parseRangeCalendarDate(props.defaultValue),
onChange: (value: DateRange) => {
props.onChange?.(
value?.start && value.end
? [value.start.toString(), value.end.toString()]
: null
);
},
};
const pickerState = useDateRangePickerState(options);
const { startFieldProps, endFieldProps, calendarProps, ...pickerProps } =
useDateRangePicker(options, pickerState, ref);
const labelAsPlaceholder = useMemo(
() =>
!isFocused &&
!pickerState.isOpen &&
!pickerState.value.start &&
!pickerState.value.end,
[pickerState.value, pickerState.isOpen, isFocused]
);
const calendar = <CalendarRange {...calendarProps} />;
return (
<DatePickerAux
{...{
...props,
labelAsPlaceholder,
isFocused,
pickerState,
pickerProps,
optionalClassName: "c__date-picker__range",
onClear: () => {
pickerState.setValue({
start: null as unknown as DateValue,
end: null as unknown as DateValue,
});
},
calendar,
}}
ref={ref}
>
<DateFieldBox
{...{
...startFieldProps,
label: startLabel,
labelAsPlaceholder,
onFocusChange: setIsFocused,
}}
/>
<div className="c__date-picker__range__separator" />
<DateFieldBox
{...{
...endFieldProps,
label: endLabel,
labelAsPlaceholder,
onFocusChange: setIsFocused,
}}
/>
</DatePickerAux>
);
};
export default DateRangePicker;