UNPKG

@equinor/eds-core-react

Version:

The React implementation of the Equinor Design System

179 lines (175 loc) 5.6 kB
import { forwardRef, useState, useRef, useCallback, useEffect } from 'react'; import { Calendar } from './calendars/Calendar.js'; import { DateField } from './fields/DateField.js'; import { calendar, 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 { toCalendarDateTime, fromDate, toCalendarDate, createCalendar } from '@internationalized/date'; import { useDatePicker, I18nProvider } from 'react-aria'; import { useDatePickerState } from '@react-stately/datepicker'; 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 DatePicker = /*#__PURE__*/forwardRef(({ onChange, label, value, isDateUnavailable, minValue, maxValue, Footer, Header, timezone, locale: propLocale, defaultValue, showTimeInput, granularity, hideClearButton, disabled: isDisabled, readOnly: isReadOnly, formatOptions, helperProps, variant, ...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) { if (showTimeInput) { return toCalendarDateTime(fromDate(initialValue, timezone)); } else { return toCalendarDate(fromDate(initialValue, timezone)); } } return null; }); const [isOpen, setIsOpen] = useState(null); const inputRef = useRef(null); const pickerRef = useRef(null); const ref = forwardedRef || inputRef; const { _minValue, _maxValue, _isDateUnavailable } = useConvertedValidationFunctions(minValue, maxValue, isDateUnavailable, timezone); const _onChange = useCallback(value => { // Set internal value if (value) { setInnerValue(showTimeInput ? toCalendarDateTime(value) : toCalendarDate(value)); } else { setInnerValue(null); } // Close the picker after selecting a date if (isOpen) { setIsOpen(false); } // Call onChange callback from props if (onChange) { if (!value) { onChange(null); } else { const date = value.toDate(timezone); onChange(date); } } }, [onChange, isOpen, showTimeInput, timezone]); const _value = getCalendarDate(value, timezone, showTimeInput) ?? innerValue; const locale = useGetLocale(propLocale); const dateCreateProps = { helperProps, variant, isDisabled, value: _value, hideTimeZone: true, locale: locale, createCalendar, onChange: _onChange, minValue: _minValue, maxValue: _maxValue, isDateUnavailable: _isDateUnavailable, label: label || 'Date picker', isReadOnly, granularity }; const pickerState = useDatePickerState(dateCreateProps); const { groupProps, buttonProps, fieldProps, calendarProps } = useDatePicker(dateCreateProps, pickerState, ref); const helperPropsInvalid = pickerState.displayValidation.isInvalid ? { text: pickerState.displayValidation.validationErrors.join('\n'), color: tokens.colors.interactive.warning__text.rgba, icon: /*#__PURE__*/jsx(Icon, { size: 16, data: warning_outlined }) } : undefined; const showClearButton = pickerState.dateValue !== null && !hideClearButton; // innerValue is used as a fallback, especially for uncontrolled inputs, so it needs to be reset when value / defaultValue is reset useEffect(() => { if (!defaultValue && !value) 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, readonly: fieldProps.isReadOnly, pickerRef: pickerRef, ref: ref, setIsOpen: setIsOpen, label: label, calendar: /*#__PURE__*/jsx(Calendar, { ref: pickerRef, Footer: Footer, Header: Header, ...calendarProps }), disabled: isDisabled, readOnly: isReadOnly, color: pickerState.isInvalid ? 'warning' : variant, helperProps: helperPropsInvalid ?? helperProps, children: /*#__PURE__*/jsx(DateField, { fieldProps: fieldProps, groupProps: groupProps, dateCreateProps: dateCreateProps, ref: ref, onChange: _onChange, rightAdornments: /*#__PURE__*/jsx(Toggle, { showClearButton: showClearButton, setOpen: setIsOpen, open: isOpen, icon: calendar, disabled: isDisabled, readonly: isReadOnly, reset: () => _onChange(null), buttonProps: buttonProps, valueString: pickerState.formatValue(locale, { year: 'numeric', month: 'short', day: '2-digit' }) }), variant: variant }) }) }) }); }); DatePicker.displayName = 'DatePicker'; export { DatePicker };