uui/components/datePickers/RangeDatePicker.tsx (127 lines of code) (raw):

import React, { useState } from 'react'; import cx from 'classnames'; import { DropdownBodyProps, useUuiContext, } from '@epam/uui-core'; import { Dropdown } from '@epam/uui-components'; import { DropdownContainer } from '../overlays'; import { FlexRow } from '../layout'; import { RangeDatePickerBody } from './RangeDatePickerBody'; import { RangeDatePickerBodyValue, RangeDatePickerInputType, RangeDatePickerProps, RangeDatePickerValue } from './types'; import { defaultFormat, defaultRangeValue } from './helpers'; import { RangeDatePickerInput } from './RangeDatePickerInput'; import css from './RangeDatePicker.module.scss'; const modifiers = [{ name: 'offset', options: { offset: [0, 6] }, }]; function RangeDatePickerComponent(props: RangeDatePickerProps, ref: React.ForwardedRef<HTMLElement>): JSX.Element { const { value: _value, format = defaultFormat } = props; const value = _value || defaultRangeValue; // also handles null in comparison to default value const context = useUuiContext(); const [isOpen, setIsOpen] = useState(false); const [inFocus, setInFocus] = useState<RangeDatePickerInputType>(null); const targetRef = React.useRef<HTMLDivElement>(null); const onValueChange = (newValue: RangeDatePickerValue) => { const fromChanged = value?.from !== newValue?.from; const toChanged = value?.to !== newValue?.to; if (fromChanged || toChanged) { props.onValueChange(newValue); if (props.getValueChangeAnalyticsEvent) { const event = props.getValueChangeAnalyticsEvent(newValue, value); context.uuiAnalytics.sendEvent(event); } } }; const onOpenChange = (newIsOpen: boolean) => { setIsOpen(newIsOpen); props.onOpenChange?.(newIsOpen); if (!inFocus && newIsOpen) { setInFocus('from'); targetRef.current.querySelector<HTMLInputElement>('.uui-input').focus(); } }; const onBodyValueChange = (newValue: RangeDatePickerBodyValue<RangeDatePickerValue>) => { setInFocus(newValue.inFocus ?? inFocus); onValueChange(newValue.selectedDate); const toChanged = value.to !== newValue.selectedDate.to; const closeBody = newValue.selectedDate.from && newValue.selectedDate.to && inFocus === 'to' && toChanged; if (closeBody) { onOpenChange(false); } }; const renderBody = (renderProps: DropdownBodyProps): JSX.Element => { return ( <DropdownContainer { ...renderProps } cx={ cx(css.dropdownContainer) } shards={ [targetRef] } returnFocus={ (node) => { console.log('lock', node); return true; } } > <FlexRow> <RangeDatePickerBody cx={ cx(props.bodyCx) } value={ { selectedDate: _value, inFocus, } } onValueChange={ onBodyValueChange } filter={ props.filter } presets={ props.presets } renderDay={ props.renderDay } renderFooter={ () => { return props.renderFooter?.(value); } } isHoliday={ props.isHoliday } rawProps={ props.rawProps?.body } /> </FlexRow> </DropdownContainer> ); }; const handleEscape = (e: React.KeyboardEvent<HTMLElement>) => { if (e.key === 'Escape' && isOpen) { e.preventDefault(); onOpenChange(false); } }; return ( <Dropdown renderTarget={ (renderProps) => { return props.renderTarget?.(renderProps) || ( <RangeDatePickerInput id={ props.id } ref={ (node) => { (renderProps as any).ref(node); targetRef.current = node; } } cx={ props.inputCx } onClick={ () => renderProps.toggleDropdownOpening(true) } isDisabled={ props.isDisabled } isInvalid={ props.isInvalid } isReadonly={ props.isReadonly } size={ props.size } getPlaceholder={ props.getPlaceholder } disableClear={ props.disableClear } rawProps={ props.rawProps } inFocus={ inFocus } value={ value } format={ format } onValueChange={ onValueChange } onFocusInput={ (e, type) => { props.onFocus?.(e, type); setInFocus(type); } } onBlurInput={ (e, type) => { props.onBlur?.(e, type); !isOpen && setInFocus(null); } } onKeyDown={ handleEscape } /> ); } } renderBody={ (renderProps) => renderBody(renderProps) } onValueChange={ (v) => onOpenChange(v) } value={ isOpen } modifiers={ modifiers } placement={ props.placement } forwardedRef={ ref } /> ); } export const RangeDatePicker = React.forwardRef(RangeDatePickerComponent);