UNPKG

@wix/design-system

Version:

@wix/design-system

237 lines 11.4 kB
import React from 'react'; import isSameDay from 'date-fns/isSameDay'; import setYear from 'date-fns/setYear'; import setMonth from 'date-fns/setMonth'; import setDate from 'date-fns/setDate'; import PopoverNext from '../PopoverNext'; import Calendar from '../Calendar'; import DateInput from './DateInput'; import Divider from '../Divider'; import { st, classes } from './DatePicker.st.css.js'; import { dataHooks, MAX_DATE } from './constants'; import { WixStyleReactEnvironmentContext } from '../WixStyleReactEnvironmentProvider/context'; import { SupportedWixLocales } from '@wix/design-systems-locale-utils'; import { ZIndex } from '../common/ZIndex'; import { generateID } from '../utils/generateId'; import { assignRef } from '../utils/mergeRefs'; class DatePicker extends React.PureComponent { constructor(props) { super(props); this.componentDidUpdate = prevProps => { if (prevProps.inputRef !== this.props.inputRef) { assignRef(prevProps.inputRef, null); assignRef(this.props.inputRef, this.state.inputRef); } const { value: prevValue } = prevProps; const { value: newValue } = this.props; if (newValue !== prevValue) { if (newValue) { if (newValue !== this.state.value) { this.setState({ inputValue: newValue }); } this._saveNewValue(newValue); return; } const value = this._transformDate(new Date(), prevValue); return this.setState({ inputValue: null, value }); } }; this._openCalendar = () => { if (!this.state.isOpen && !this.props.readOnly && !this.props.disabled && this.state.shouldOpenCalendar) { return this.setState({ isOpen: true, value: this.props.value || new Date(), }, () => { this.props.onOpen?.(); }); } if (!this.state.shouldOpenCalendar) { return this.setState({ shouldOpenCalendar: true, }); } }; this.openCalendar = this._openCalendar; this._closeCalendar = (shouldOpenCalendar = true, shouldFocusInput = false) => { this.setState({ isOpen: false, autoFocus: false, shouldOpenCalendar }, () => { this.props.onClose?.(); if (shouldFocusInput) { this.state.inputRef?.focus(); } }); }; this._handleInputChange = ({ dateVal }) => { this._saveNewValue(dateVal); }; this._transformDate = (value, oldValue) => [ [value.getFullYear(), setYear], [value.getMonth(), setMonth], [value.getDate(), setDate], ].reduce((_value, [datePart, setter]) => setter(_value, datePart), oldValue); this._saveNewValue = (value, modifiers = {}) => { if (modifiers.disabled) { return; } const oldValue = this.props.value || new Date(new Date().setHours(0, 0, 0, 0)); const newValue = this._transformDate(value, oldValue); if (!isSameDay(value, this.props.value)) { this.setState({ value: newValue }, () => this.props.onChange(newValue)); } }; this._filterDate = date => date < MAX_DATE && this.props.filterDate(date); this._handleKeyDown = (event, newDate) => { if (!this.state.isOpen) { this._openCalendar(); } switch (event.key) { case 'Tab': case 'Escape': { this._closeCalendar(); return; } case 'Enter': { event.preventDefault(); if (newDate) { const transformedDate = this._transformDate(newDate, new Date(new Date().setHours(0, 0, 0, 0))); this.setState({ value: transformedDate, inputValue: transformedDate, }, () => this.props.onChange(transformedDate)); } this._closeCalendar(); return; } case 'ArrowUp': case 'ArrowDown': { event.preventDefault(); this.setState({ autoFocus: true }); return; } default: return; } }; this._handleCalendarKeyDown = event => { switch (event.key) { case 'Escape': { event.stopPropagation(); this._closeCalendar(false, true); return; } default: return; } }; this._handleChange = (value, modifiers) => { this.setState({ inputValue: value, isOpen: !this.props.shouldCloseOnSelect || false, shouldOpenCalendar: !this.props.shouldCloseOnSelect || false, }); this._saveNewValue(value, modifiers); }; this._handleRef = ref => { this.setState({ inputRef: ref }); assignRef(this.props.inputRef, ref); }; this._handleInputFocus = event => { if (!event.isTrusted) { return; } this._openCalendar(); this.setState({ autoFocus: false }, () => { this.state.inputRef?.focus(); this.props.inputProps?.onFocus?.(event); }); }; this._renderInput = () => { const { inputDataHook, disabled, placeholderText, readOnly, status, statusMessage, customInput, inputProps = {}, size, clearButton, onClear, disableKeyboardType, dateStyle, validate, onValidate, excludePastDates, clearButtonTooltipContent, clearButtonTooltipProps, clearButtonAriaLabel, } = this.props; const { onFocus, ...inputPropsRest } = inputProps; return (React.createElement(DateInput, { dataHook: inputDataHook, className: classes.input, value: this.state.inputValue, onInputClicked: this._openCalendar, disabled: disabled, readOnly: readOnly, placeholder: placeholderText, onFocus: this._handleInputFocus, onKeyDown: this._handleKeyDown, tabIndex: 0, status: status, statusMessage: statusMessage, autoSelect: false, customInput: customInput, locale: this._getLocale(), dateStyle: dateStyle, size: size, clearButton: clearButton, onClear: onClear, onChange: this._handleInputChange, disableEditing: disableKeyboardType, validate: validate, onValidate: onValidate, excludePastDates: excludePastDates, clearButtonTooltipContent: clearButtonTooltipContent, clearButtonTooltipProps: clearButtonTooltipProps, clearButtonAriaLabel: clearButtonAriaLabel, inputRef: this._handleRef, focusOnClearClick: true, ...(customInput ? customInput.props : {}), ...inputPropsRest })); }; this._combineDataHooks = (start, end) => { return end ? `${start}-${end}` : start; }; this.rootId = generateID(); this.state = { value: props.value || new Date(), isOpen: props.initialOpen && !props.disabled, inputValue: props.value, autoFocus: false, shouldOpenCalendar: true, }; } _getLocale() { return this.props.locale || this.context.locale || 'en'; } render() { const { className, showMonthDropdown, showYearDropdown, excludePastDates, rtl, shouldCloseOnSelect, width, calendarDataHook, twoMonths, zIndex, dataHook, popoverProps, firstDayOfWeek, leftArrowAriaLabel, leftArrowAriaLabelledBy, rightArrowAriaLabel, rightArrowAriaLabelledBy, monthDropdownAriaLabel, monthDropdownAriaLabelledBy, yearDropdownAriaLabel, yearDropdownAriaLabelledBy, disableKeyboardType, dateIndication, disabled, renderFooter, } = this.props; const { isOpen, value } = this.state; const calendarProps = { dataHook: this._combineDataHooks(dataHooks.datePickerCalendar, this.props.dataHook), className: classes.calendar, locale: this._getLocale(), showMonthDropdown, showYearDropdown, filterDate: this._filterDate, excludePastDates, rtl, onChange: this._handleChange, onClose: () => this._closeCalendar(false), value, shouldCloseOnSelect, numOfMonths: twoMonths ? 2 : 1, firstDayOfWeek, leftArrowAriaLabel, leftArrowAriaLabelledBy, rightArrowAriaLabel, rightArrowAriaLabelledBy, monthDropdownAriaLabel, monthDropdownAriaLabelledBy, yearDropdownAriaLabel, yearDropdownAriaLabelledBy, dateIndication, autoFocus: this.state.autoFocus || false, containFocus: true, onKeyDown: this._handleCalendarKeyDown, size: this.props.calendarProps?.size, }; return (React.createElement("div", { "data-date-picker-id": this.rootId, className: st(classes.root, { disableKeyboardType, disabled }, className), "data-hook": dataHook, style: { width } }, React.createElement(PopoverNext, { className: classes.popover, dataHook: dataHooks.datePickerPopover, onOpenChange: (open, reason) => { if (!open && (reason === 'outside-press' || reason === 'escape-key')) { this._closeCalendar(false); } }, appendTo: "parent", open: isOpen, zIndex: zIndex, tabIndex: -1, focusManagerEnabled: false, placement: rtl ? 'top-end' : 'top-start', autoUpdateOptions: { animationFrame: true }, ...popoverProps }, React.createElement(PopoverNext.Trigger, null, React.createElement("div", { className: classes.inputContainer }, this._renderInput())), React.createElement(PopoverNext.Content, null, React.createElement("div", { "data-hook": calendarDataHook, "data-calendar-id": this.rootId }, React.createElement(Calendar, { ...calendarProps }), renderFooter && (React.createElement(React.Fragment, null, React.createElement(Divider, null), React.createElement("div", { "data-hook": dataHooks.datePickerFooter, className: classes.footer }, renderFooter())))))))); } } DatePicker.displayName = 'DatePicker'; DatePicker.defaultProps = { filterDate: () => true, rtl: false, width: '150px', zIndex: ZIndex.datePicker, disabled: false, inputDataHook: dataHooks.datePickerInput, popoverProps: { zIndex: ZIndex.datePickerPopover, }, disableKeyboardType: false, onChange: () => { }, dateStyle: 'short', shouldCloseOnSelect: true, }; export default DatePicker; DatePicker.contextType = WixStyleReactEnvironmentContext; //# sourceMappingURL=DatePicker.js.map