wix-style-react
Version:
wix-style-react
485 lines (479 loc) • 19.2 kB
JavaScript
"use strict";
var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
exports.__esModule = true;
exports.default = exports.DEFAULT_TIME_STYLE = exports.DEFAULT_STEP = void 0;
var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
var _react = _interopRequireWildcard(require("react"));
var _propTypes = _interopRequireDefault(require("prop-types"));
var _Input = _interopRequireDefault(require("../Input"));
var _DropdownBase = _interopRequireDefault(require("../DropdownBase"));
var _constants = require("./constants");
var _TimeInputSt = require("./TimeInput.st.css");
var _wixDesignSystemsLocaleUtils = require("wix-design-systems-locale-utils");
var _context = require("../WixStyleReactEnvironmentProvider/context");
var _TimeInputUtils = require("./TimeInputUtils");
var _excluded = ["dataHook", "className", "size", "suffix", "prefix", "status", "statusMessage", "invalidMessage", "border", "disabled", "placeholder", "readOnly", "autoSelect", "width", "timeStyle", "onChange", "onInvalid", "step", "noRightBorderRadius", "noLeftBorderRadius", "popoverProps", "onFocus", "onBlur", "excludePastTimes", "filterTime"];
var _jsxFileName = "/home/builduser/work/a9c1ac8876d5057c/packages/wix-style-react/dist/cjs/TimeInput/TimeInput.js";
function _interopRequireWildcard(e, t) { if ("function" == typeof WeakMap) var r = new WeakMap(), n = new WeakMap(); return (_interopRequireWildcard = function _interopRequireWildcard(e, t) { if (!t && e && e.__esModule) return e; var o, i, f = { __proto__: null, default: e }; if (null === e || "object" != typeof e && "function" != typeof e) return f; if (o = t ? n : r) { if (o.has(e)) return o.get(e); o.set(e, f); } for (var _t in e) "default" !== _t && {}.hasOwnProperty.call(e, _t) && ((i = (o = Object.defineProperty) && Object.getOwnPropertyDescriptor(e, _t)) && (i.get || i.set) ? o(f, _t, i) : f[_t] = e[_t]); return f; })(e, t); }
function ownKeys(e, r) { var t = Object.keys(e); if (Object.getOwnPropertySymbols) { var o = Object.getOwnPropertySymbols(e); r && (o = o.filter(function (r) { return Object.getOwnPropertyDescriptor(e, r).enumerable; })), t.push.apply(t, o); } return t; }
function _objectSpread(e) { for (var r = 1; r < arguments.length; r++) { var t = null != arguments[r] ? arguments[r] : {}; r % 2 ? ownKeys(Object(t), !0).forEach(function (r) { (0, _defineProperty2.default)(e, r, t[r]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(e, Object.getOwnPropertyDescriptors(t)) : ownKeys(Object(t)).forEach(function (r) { Object.defineProperty(e, r, Object.getOwnPropertyDescriptor(t, r)); }); } return e; }
var DEFAULT_STEP = exports.DEFAULT_STEP = 15;
var DEFAULT_TIME_STYLE = exports.DEFAULT_TIME_STYLE = 'short';
var initialState = {
isDropdownOpen: false,
inputValue: '',
selectedId: null,
customInputId: null,
highlightedOptionId: null,
error: false,
selectionRange: false,
deleteEvent: false,
scrollToOption: null,
lastSavedDate: null
};
var TimeInput = /*#__PURE__*/_react.default.forwardRef((_ref, ref) => {
var {
dataHook,
className,
size,
suffix,
prefix,
status,
statusMessage,
invalidMessage,
border,
disabled,
placeholder,
readOnly,
autoSelect,
width = 'auto',
timeStyle = DEFAULT_TIME_STYLE,
onChange,
onInvalid,
step = DEFAULT_STEP,
noRightBorderRadius,
noLeftBorderRadius,
popoverProps,
onFocus,
onBlur,
excludePastTimes,
filterTime
} = _ref,
rest = (0, _objectWithoutProperties2.default)(_ref, _excluded);
var inputRef = (0, _react.useRef)();
var context = (0, _react.useContext)(_context.WixStyleReactEnvironmentContext);
var locale = rest.locale || context.locale || 'en';
// Resetting seconds so that one of timeSlots would be selected in case value with seconds is passed
var value = rest.value ? rest.value.setSeconds(0, 0) : undefined;
var timeFilter = (0, _react.useMemo)(() => (0, _TimeInputUtils.getTimeFilter)({
excludePastTimes,
filterTime
}), [excludePastTimes, filterTime]);
var timeSlots = (0, _react.useMemo)(() => (0, _TimeInputUtils.getTimeSlots)({
value,
timeStyle,
locale,
step
}).filter(_ref2 => {
var {
id
} = _ref2;
return timeFilter(new Date(id));
}), [value, timeStyle, locale, step, timeFilter]);
var [state, setState] = _react.default.useReducer((currentState, newState) => _objectSpread(_objectSpread({}, currentState), newState), initialState);
var shouldShowError = invalidMessage && state.error;
var autofillValue = state.selectionRange ? state.inputValue.substring(state.selectionRange.selectionStart) : null;
(0, _react.useEffect)(() => {
if (rest.value === null) {
setState({
inputValue: ''
});
return;
}
var timeSlot = value ? (0, _TimeInputUtils.getTimeSlot)({
value,
timeStyle,
locale
}) : (0, _TimeInputUtils.getClosestTimeSlot)({
value,
timeSlots
});
var selectedOption = timeSlot && timeSlots.find(slot => timeSlot.id === slot.id);
setState({
inputValue: timeSlot && timeSlot.value,
selectedId: selectedOption && selectedOption.id
});
}, [value, rest.value, timeStyle, locale, timeSlots]);
// select range when suggestedInputValue is changed
(0, _react.useEffect)(() => {
if (state.selectionRange) {
inputRef.current.setSelectionRange(state.selectionRange.selectionStart, state.selectionRange.selectionEnd);
}
}, [state.selectionRange]);
(0, _react.useEffect)(() => {
if (state.error) {
onInvalid == null || onInvalid({
validationType: state.validationType,
value: state.inputValue
});
}
}, [state.error, onInvalid, state.inputValue, state.validationType]);
var openDropdown = (0, _react.useCallback)(() => {
if (readOnly) {
return;
}
if (state.selectedId || state.highlightedOptionId) {
setState({
isDropdownOpen: true
});
return;
}
var closestTimeSlot = (0, _TimeInputUtils.getClosestTimeSlot)({
value: new Date(state.customInputId || value),
timeSlots
});
setState({
isDropdownOpen: true,
scrollToOption: closestTimeSlot ? closestTimeSlot.id : undefined
});
return;
}, [state.selectedId, state.highlightedOptionId, state.customInputId, timeSlots, readOnly, value]);
(0, _react.useImperativeHandle)(ref, () => ({
focus: () => {
inputRef.current.focus();
openDropdown();
},
blur: () => {
inputRef.current.blur();
}
}), [openDropdown]);
var findCurrentOption = (0, _react.useCallback)(() => {
if (state.selectedId) {
return {
id: state.selectedId,
value: state.inputValue
};
}
var highlightedOption = timeSlots.find(slot => slot.id === state.highlightedOptionId);
if (highlightedOption) {
return highlightedOption;
}
if (state.inputValue && !state.selectedId) {
var customValue = (0, _TimeInputUtils.getCustomTimeSlot)({
value: state.inputValue,
timeSlot: timeSlots[0].id,
timeStyle,
locale
});
if (customValue) {
return {
value: customValue.value,
customId: customValue.id
};
}
}
}, [locale, state.highlightedOptionId, state.inputValue, state.selectedId, timeSlots, timeStyle]);
var validateAndSetOption = (0, _react.useCallback)(opt => {
var _state$lastSavedDate$, _state$lastSavedDate, _date$getTime;
var option = opt ? opt : findCurrentOption();
if (!option) {
setState({
error: true,
validationType: 'formatError'
});
return;
} else if (!timeFilter(new Date(option.id || option.customId))) {
setState({
error: true,
validationType: 'outOfBoundsError'
});
return;
}
var date = option ? new Date(option.customId || option.id) : null;
var valueChanged = ((_state$lastSavedDate$ = (_state$lastSavedDate = state.lastSavedDate) == null || _state$lastSavedDate.getTime == null ? void 0 : _state$lastSavedDate.getTime()) !== null && _state$lastSavedDate$ !== void 0 ? _state$lastSavedDate$ : null) !== ((_date$getTime = date == null || date.getTime == null ? void 0 : date.getTime()) !== null && _date$getTime !== void 0 ? _date$getTime : null);
setState({
selectedId: option.id,
customInputId: option.customId,
inputValue: option.value,
selectionRange: false,
isDropdownOpen: false,
highlightedOptionId: null,
error: false,
lastSavedDate: date
});
if (option && valueChanged) {
onChange && onChange({
date
});
}
return option;
}, [findCurrentOption, timeFilter, onChange, state.lastSavedDate]);
var _onKeyDown = (0, _react.useCallback)((e, delegateKeyDown) => {
if (e.key === ' ' || e.key === 'Spacebar') {
return;
}
if (!state.isDropdownOpen && e.key === 'ArrowDown') {
openDropdown();
return e.preventDefault();
}
delegateKeyDown(e);
if (e.key === 'Backspace') {
setState({
deleteEvent: true
});
}
if (state.isDropdownOpen) {
if (e.key === 'Escape') {
setState({
isDropdownOpen: false
});
return e.preventDefault();
}
// should be handled by error validation
if (e.key === 'Enter') {
setState({
isDropdownOpen: false
});
inputRef.current.setSelectionRange(state.inputValue.length, state.inputValue.length);
if (!state.error) {
return validateAndSetOption();
}
}
}
}, [state.isDropdownOpen, state.inputValue, openDropdown, validateAndSetOption, state.error]);
var onInputChanged = (0, _react.useCallback)(e => {
var inputValue = e.target.value;
var isDropdownOpen = state.isDropdownOpen;
var selectedId = state.selectedId;
var selectionRange = false;
var error = false;
var validationType;
// open dropdown when value is selected with keyboard and value is typed again
if (!isDropdownOpen) {
isDropdownOpen = true;
}
// validate input
if ((0, _TimeInputUtils.isInputInvalid)(e.target.value)) {
error = true;
validationType = 'formatError';
isDropdownOpen = false;
}
// unselect dropdown option if input value doesn not match any available timeslot
if (inputValue !== timeSlots[state.selectedId]) {
selectedId = null;
}
var suggestedOption = (0, _TimeInputUtils.getSuggestedOption)({
inputValue,
timeSlots,
locale
});
if (!state.deleteEvent && suggestedOption) {
var autoFillValue = (0, _TimeInputUtils.getAutoFilledValue)({
inputValue,
suggestedOption,
locale
});
var inputWithAutoFill = inputValue + autoFillValue;
inputValue = inputValue + autoFillValue;
selectionRange = {
selectionStart: inputWithAutoFill.length - autoFillValue.length,
selectionEnd: inputWithAutoFill.length
};
}
var highlightedOptionId = suggestedOption ? suggestedOption.id : null;
setState({
isDropdownOpen,
error,
validationType,
selectedId,
highlightedOptionId,
selectionRange,
inputValue,
deleteEvent: false
});
}, [state.deleteEvent, timeSlots, state.isDropdownOpen, state.selectedId, locale]);
var onInputBlur = (0, _react.useCallback)(e => {
onBlur && onBlur(e);
if (state.isDropdownOpen) {
setState({
isDropdownOpen: false
});
}
if (state.error) {
return;
}
if (state.inputValue === '') {
return onChange && onChange({
date: null
});
}
validateAndSetOption();
}, [validateAndSetOption, onChange, onBlur, state.error, state.isDropdownOpen, state.inputValue]);
var getStatusMessage = () => {
if (shouldShowError) return invalidMessage;
return statusMessage;
};
return /*#__PURE__*/_react.default.createElement("div", {
className: (0, _TimeInputSt.st)(_TimeInputSt.classes.root, className),
style: {
width
},
"data-hook": dataHook,
"data-value": state.selectedId || value,
"data-locale": locale,
"data-time-style": timeStyle,
"data-autofill": autofillValue,
"data-scroll-to-option": state.scrollToOption,
"data-status-message": statusMessage,
__self: void 0,
__source: {
fileName: _jsxFileName,
lineNumber: 414,
columnNumber: 7
}
}, /*#__PURE__*/_react.default.createElement(_DropdownBase.default, (0, _extends2.default)({
dataHook: _constants.dataHooks.TimeInputDropdown,
open: state.isDropdownOpen,
onClickOutside: () => setState({
isDropdownOpen: false
}),
options: timeSlots,
onSelect: validateAndSetOption,
selectedId: state.selectedId,
maxHeight: "216px",
markedOptionId: state.highlightedOptionId,
focusOnOption: state.highlightedOptionId,
focusOnSelectedOption: true,
scrollToOption: state.scrollToOption,
onMouseDown: e => e.preventDefault(),
dynamicWidth: true
}, popoverProps, {
__self: void 0,
__source: {
fileName: _jsxFileName,
lineNumber: 425,
columnNumber: 9
}
}), _ref3 => {
var {
delegateKeyDown
} = _ref3;
return /*#__PURE__*/_react.default.createElement(_Input.default, {
dataHook: _constants.dataHooks.TimeInputInput,
size: size,
status: shouldShowError ? 'error' : status,
statusMessage: getStatusMessage(),
suffix: suffix && /*#__PURE__*/_react.default.createElement(_Input.default.Affix, {
__self: void 0,
__source: {
fileName: _jsxFileName,
lineNumber: 448,
columnNumber: 35
}
}, suffix),
prefix: prefix && /*#__PURE__*/_react.default.createElement(_Input.default.Affix, {
__self: void 0,
__source: {
fileName: _jsxFileName,
lineNumber: 449,
columnNumber: 35
}
}, prefix),
border: border,
disabled: disabled,
placeholder: placeholder,
value: state.inputValue,
onChange: onInputChanged,
onInputClicked: openDropdown,
onKeyDown: e => _onKeyDown(e, delegateKeyDown),
ref: inputRef,
readOnly: readOnly,
autoSelect: autoSelect,
noLeftBorderRadius: noLeftBorderRadius,
noRightBorderRadius: noRightBorderRadius,
onFocus: onFocus,
onBlur: onInputBlur,
__self: void 0,
__source: {
fileName: _jsxFileName,
lineNumber: 443,
columnNumber: 15
}
});
}));
});
TimeInput.displayName = 'TimeInput';
TimeInput.propTypes = {
/** Control the border style of input */
border: _propTypes.default.oneOf(['standard', 'round', 'bottomLine', 'none']),
/** Specifies a CSS class name to be appended to the component’s root element */
className: _propTypes.default.string,
/** Applies a data-hook HTML attribute that can be used in the tests */
dataHook: _propTypes.default.string,
/** Specifies whether component is disabled */
disabled: _propTypes.default.bool,
/** Sets locale and formats time according to it */
locale: _propTypes.default.oneOf(_wixDesignSystemsLocaleUtils.SupportedWixLocales),
/** Defines a callback function which is called on every time value changes */
// * - return {{ date: Date | null }}
onChange: _propTypes.default.func,
/** Defines a callback function which is called on cases when invalid time is typed or confirmed with an action.
* - return {{ validationType: 'formatError' | 'outOfBoundsError', value: string }}
* - `validationType` - type 'formatError'is set when value is in the wrong time format
* - type 'outOfBoundsError' is set when `excludePastTimes` or `filterTime` is used and value does not match the filters
* - `value` - is set to current input value
*/
onInvalid: _propTypes.default.func,
/** Sets a placeholder message to display */
placeholder: _propTypes.default.string,
/** Pass a component you want to show as the prefix of the input, e.g., text, icon */
prefix: _propTypes.default.node,
/** Specifies whether input is read only */
readOnly: _propTypes.default.bool,
/** Specifies whether input is auto selected on focus */
autoSelect: _propTypes.default.bool,
/** Controls the size of the input */
size: _propTypes.default.oneOf(['small', 'medium', 'large']),
/** Specify the status of a field */
status: _propTypes.default.oneOf(['error', 'warning', 'loading']),
/** Defines the message to display on status icon hover. If not given or empty there will be no tooltip. */
statusMessage: _propTypes.default.node,
/** Enables internal validation and defines a message to display when user types invalid time value */
invalidMessage: _propTypes.default.string,
/** Specifies the interval between time values shown in dropdown */
step: _propTypes.default.number,
/** Pass a component you want to show as the suffix of the input, e.g., text, icon */
suffix: _propTypes.default.node,
/** Specifies what time formatting style to use when calling `format()` */
timeStyle: _propTypes.default.string,
/** Specifies the current value of the input */
value: _propTypes.default.object,
/** Controls the width of the component. `auto` will resize the input to match width of its contents, while `100%` will take up the full parent container width. */
width: _propTypes.default.oneOf(['auto', '100%']),
/** Defines a standard input `onFocus` callback */
onFocus: _propTypes.default.func,
/** Defines a standard input `onBlur` callback */
onBlur: _propTypes.default.func,
/** Specify whether past time slots should be shown or not */
excludePastTimes: _propTypes.default.bool,
/**
* Specify selectable time slots:
* * `param` {Date} `value` - a time to check
* * `return` {boolean} - true if `value` should be shown in time slots dropdown, false otherwise
*/
filterTime: _propTypes.default.func,
/** Allows to pass all common popover props. */
popoverProps: _propTypes.default.shape({
appendTo: _propTypes.default.oneOf(['window', 'scrollParent', 'parent', 'viewport']),
maxWidth: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.number]),
minWidth: _propTypes.default.oneOfType([_propTypes.default.string, _propTypes.default.number]),
flip: _propTypes.default.bool,
fixed: _propTypes.default.bool,
placement: _propTypes.default.oneOf(['auto-start', 'auto', 'auto-end', 'top-start', 'top', 'top-end', 'right-start', 'right', 'right-end', 'bottom-end', 'bottom', 'bottom-start', 'left-end', 'left', 'left-start']),
dynamicWidth: _propTypes.default.bool
})
};
var _default = exports.default = TimeInput;
//# sourceMappingURL=TimeInput.js.map