@equinor/eds-core-react
Version:
The React implementation of the Equinor Design System
179 lines (175 loc) • 5.6 kB
JavaScript
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 };