@wix/design-system
Version:
@wix/design-system
237 lines • 11.4 kB
JavaScript
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