2023-06-16 16:58:16 +02:00
|
|
|
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 {
|
2023-07-12 22:12:31 +02:00
|
|
|
convertDateValueToString,
|
2023-06-16 16:58:16 +02:00
|
|
|
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;
|
|
|
|
|
};
|
|
|
|
|
|
2023-06-16 17:08:12 +02:00
|
|
|
export const DateRangePicker = ({
|
2023-06-16 16:58:16 +02:00
|
|
|
startLabel,
|
|
|
|
|
endLabel,
|
|
|
|
|
...props
|
|
|
|
|
}: DateRangePickerProps) => {
|
|
|
|
|
if (props.defaultValue && props.value) {
|
|
|
|
|
throw new Error(
|
2023-07-18 15:43:56 +02:00
|
|
|
"You cannot use both defaultValue and value props on DateRangePicker component",
|
2023-06-16 16:58:16 +02:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
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
|
2023-07-12 22:12:31 +02:00
|
|
|
? [
|
|
|
|
|
convertDateValueToString(value.start),
|
|
|
|
|
convertDateValueToString(value.end),
|
|
|
|
|
]
|
2023-07-18 15:43:56 +02:00
|
|
|
: null,
|
2023-06-16 16:58:16 +02:00
|
|
|
);
|
|
|
|
|
},
|
|
|
|
|
};
|
|
|
|
|
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,
|
2023-07-18 15:43:56 +02:00
|
|
|
[pickerState.value, pickerState.isOpen, isFocused],
|
2023-06-16 16:58:16 +02:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
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>
|
|
|
|
|
);
|
|
|
|
|
};
|