UNPKG

@equinor/eds-core-react

Version:

The React implementation of the Equinor Design System

174 lines (170 loc) 5.77 kB
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 };