@equinor/eds-core-react
Version:
The React implementation of the Equinor Design System
174 lines (170 loc) • 5.77 kB
JavaScript
import { forwardRef, useState, useRef, useCallback, useEffect } from 'react';
import { RangeCalendar } from './calendars/RangeCalendar.js';
import { calendar_date_range, warning_outlined } from '@equinor/eds-icons';
import { useConvertedValidationFunctions } from './utils/useConvertedValidationFunctions.js';
import { FieldWrapper } from './fields/FieldWrapper.js';
import { Toggle } from './fields/Toggle.js';
import { useDateRangePicker, I18nProvider } from 'react-aria';
import { useDateRangePickerState } from '@react-stately/datepicker';
import { DateRangeField } from './fields/DateRangeField.js';
import { toCalendarDate, fromDate } from '@internationalized/date';
import { DatePickerProvider, defaultTimezone } from './utils/context.js';
import { tokens } from '@equinor/eds-tokens';
import { Icon } from '../Icon/index.js';
import { getCalendarDate } from './utils/get-calendar-date.js';
import { useGetLocale } from './utils/useGetLocale.js';
import { jsx } from 'react/jsx-runtime';
const DateRangePicker = /*#__PURE__*/forwardRef(({
onChange,
label,
value,
isDateUnavailable,
minValue,
maxValue,
Footer,
Header,
timezone,
defaultValue,
locale: propLocale,
formatOptions,
hideClearButton,
disabled: isDisabled,
readOnly: isReadOnly,
...props
}, forwardedRef) => {
timezone = timezone ?? defaultTimezone;
formatOptions = formatOptions ?? {
year: 'numeric',
month: '2-digit',
day: '2-digit'
};
const [innerValue, setInnerValue] = useState(() => {
const initialValue = value ?? defaultValue;
if (initialValue) {
return {
start: initialValue.from ? toCalendarDate(fromDate(initialValue.from, timezone)) : null,
end: initialValue.to ? toCalendarDate(fromDate(initialValue.to, timezone)) : null
};
}
});
const [isOpen, setIsOpen] = useState(null);
const inputRef = useRef(null);
const pickerRef = useRef(null);
const ref = forwardedRef || inputRef;
const locale = useGetLocale(propLocale);
const {
_minValue,
_maxValue,
_isDateUnavailable
} = useConvertedValidationFunctions(minValue, maxValue, isDateUnavailable, timezone);
const _onChange = useCallback(rangeValue => {
setInnerValue(rangeValue);
// Close picker on selecting date
if (isOpen) {
setIsOpen(false);
}
// Propagate change
if (onChange) {
if (!rangeValue) onChange(null);else {
onChange({
from: rangeValue.start?.toDate(timezone) ?? null,
to: rangeValue.end?.toDate(timezone) ?? null
});
}
}
}, [onChange, isOpen, timezone]);
const _value = value ? {
start: getCalendarDate(value.from, timezone),
end: getCalendarDate(value.to, timezone)
} : innerValue;
const dateRangePickerStateProps = {
maxValue: _maxValue,
minValue: _minValue,
isDateUnavailable: _isDateUnavailable,
onChange: _onChange,
label: label ?? 'Date-range',
value: _value,
isDisabled: isDisabled,
isReadOnly: isReadOnly
};
const state = useDateRangePickerState(dateRangePickerStateProps);
const {
groupProps,
startFieldProps,
endFieldProps,
buttonProps,
calendarProps
} = useDateRangePicker(dateRangePickerStateProps, state, ref);
const helperProps = state.displayValidation.isInvalid ? {
text: state.displayValidation.validationErrors.join('\n'),
color: tokens.colors.interactive.warning__text.rgba,
icon: /*#__PURE__*/jsx(Icon, {
size: 16,
data: warning_outlined
})
} : undefined;
const showClearButton = state.dateRange !== null && !hideClearButton;
const formattedValue = state.formatValue(locale, {
year: 'numeric',
month: 'short',
day: '2-digit'
});
const valueString = formattedValue ? Object.values(formattedValue).join(' - ') : null;
// innerValue is used as a fallback, especially for uncontrolled inputs, so it needs to be reset when value / defaultValue is reset
useEffect(() => {
const val = defaultValue ?? value;
if (!defaultValue && !value) setInnerValue(null);
if (!val?.from && !val?.to) setInnerValue(null);
}, [defaultValue, value]);
return /*#__PURE__*/jsx(I18nProvider, {
locale: locale,
children: /*#__PURE__*/jsx(DatePickerProvider, {
timezone: timezone,
formatOptions: formatOptions,
children: /*#__PURE__*/jsx(FieldWrapper, {
...props,
isOpen: isOpen,
color: state.isInvalid ? 'warning' : props.variant,
helperProps: helperProps ?? props.helperProps,
readonly: startFieldProps.isReadOnly,
ref: ref,
pickerRef: pickerRef,
setIsOpen: setIsOpen,
label: label,
calendar: /*#__PURE__*/jsx(RangeCalendar, {
ref: pickerRef,
maxValue: _maxValue,
minValue: _minValue,
isDateUnavailable: _isDateUnavailable,
Footer: Footer,
Header: Header,
...calendarProps
}),
children: /*#__PURE__*/jsx(DateRangeField, {
startFieldProps: startFieldProps,
endFieldProps: endFieldProps,
groupProps: groupProps,
ref: ref,
variant: props.variant,
disabled: isDisabled,
locale: locale,
rightAdornments: /*#__PURE__*/jsx(Toggle, {
showClearButton: showClearButton,
buttonProps: buttonProps,
disabled: isDisabled,
readonly: isReadOnly,
reset: () => {
_onChange(null);
},
setOpen: setIsOpen,
open: isOpen,
icon: calendar_date_range,
valueString: valueString
})
})
})
})
});
});
DateRangePicker.displayName = 'DateRangePicker';
export { DateRangePicker };