import { ComponentProps, forwardRef, ReactElement, useEffect, useImperativeHandle, useRef, useState, } from 'react'; import ReactDatePicker, { registerLocale } from 'react-datepicker'; import { limitShift, shift } from '@floating-ui/react'; import { Box, Icon, Label, Text, useDefaultLabel, useExperimentalTheme } from 'morphe'; import DateInput from './DateInput'; import { Props } from '../DatePicker'; import styles from '../DatePicker.css'; type InternalProps = Props & { inline?: ComponentProps['inline']; inputOnly?: boolean; onFocus?: () => void; onSelect?: () => void; }; // eslint-disable-next-line @typescript-eslint/no-unused-vars function MyNoContainer({ className, children }: { className: string; children: ReactElement }) { return
; } const InternalDatePickerWithForwardRef = forwardRef( function InternalDatePicker( { disabled, inline = false, inputOnly = false, errorMessage, excludeDates, helperText, id, idealDirection = 'down', includeDates, label, localeData, maxDate, minDate, name, nextRef, onChange, onFocus, onSelect, placeholder, rangeEndDate, rangeSelector, rangeStartDate, readOnly, selectLists, size, value: controlledValue, _overrideRangeDateFix, }: InternalProps, ref, ): ReactElement { const innerRef = useRef(null); // @ts-expect-error - TS2322 - Type 'HTMLDivElement | HTMLInputElement | null' is not assignable to type 'HTMLInputElement'. useImperativeHandle(ref, () => innerRef.current); const { nextMonth, previousMonth } = useDefaultLabel('DatePicker'); const theme = useExperimentalTheme(); // This state is only used if the component is uncontrolled or value === undefined. If uncontrolled, DatePicker manages the selected Date value internally const [uncontrolledValue, setUncontrolledValue] = useState(null); // We keep month in state to trigger a re-render when month changes since height will vary by where days fall // in the month and we need to keep the popover pointed at the input correctly const [, setMonth] = useState(); const [format, setFormat] = useState(); const [updatedLocale, setUpdatedLocale] = useState['locale']>(); const [initRangeHighlight, setInitRangeHighlight] = useState(); useEffect(() => { if (rangeSelector) { setInitRangeHighlight(rangeStartDate || rangeEndDate); } }, [rangeStartDate, rangeEndDate, rangeSelector]); useEffect(() => { if (localeData && localeData.code) { registerLocale(localeData.code, localeData); setUpdatedLocale(localeData.code); setFormat( localeData?.formatLong ?.date({ width: 'short' }) .replace(/(y{1,4})/, 'yyyy') .replace(/(d{1,2})/, 'dd') .replace(/(M{1,2})/, 'MM') ?? 'MM/dd/yyyy', ); } }, [localeData]); const popperPlacement = { up: 'top', right: 'right', down: 'bottom', left: 'left', } as const; // This logic is making the component uncontrolled and causes unexpected behaviour. We need to deprecate it when all instances of DatePicker set _overrideRangeDateFix to true const controlledMaxDate = rangeSelector === 'end' ? maxDate : rangeEndDate || maxDate; const controlledMinDate = rangeSelector === 'start' ? minDate : rangeStartDate || minDate; return (
{label && !theme.MAIN && !inline && ( )} } dateFormat={format} dayClassName={() => styles['react-datepicker__days']} disabled={disabled} dropdownMode="select" endDate={rangeEndDate ?? undefined} excludeDates={excludeDates && [...excludeDates]} highlightDates={initRangeHighlight ? [initRangeHighlight] : []} id={id} includeDates={includeDates && [...includeDates]} inline={inline} locale={updatedLocale} maxDate={_overrideRangeDateFix ? maxDate : controlledMaxDate} minDate={_overrideRangeDateFix ? minDate : controlledMinDate} nextMonthButtonLabel={ } onChange={( value: Date | null, event: | React.MouseEvent | React.KeyboardEvent | undefined, ) => { if (controlledValue === undefined) setUncontrolledValue(value); onChange({ event, value }); if (event?.type === 'click') { nextRef?.current?.focus(); onSelect?.(); } }} onKeyDown={(event) => { if (event.key === 'Enter') { nextRef?.current?.focus(); } }} onMonthChange={(newMonth: Date) => setMonth(newMonth.getMonth())} placeholderText={placeholder ?? format?.toUpperCase()} popperClassName={styles['react-datepicker-popper']} popperModifiers={[ shift({ padding: 8, limiter: limitShift({ offset: 5, }), }), ]} popperPlacement={popperPlacement[idealDirection] ?? idealDirection} previousMonthButtonLabel={ } readOnly={readOnly} selected={controlledValue ?? uncontrolledValue} selectsEnd={rangeSelector === 'end'} selectsStart={rangeSelector === 'start'} showMonthDropdown={selectLists?.includes('month')} showPopperArrow={false} showYearDropdown={selectLists?.includes('year')} startDate={rangeStartDate ?? undefined} />
); }, ); InternalDatePickerWithForwardRef.displayName = 'InternalDatePicker'; export default InternalDatePickerWithForwardRef;