UNPKG

grommet

Version:

focus on the essential experience

406 lines (345 loc) 14.5 kB
"use strict"; exports.__esModule = true; exports.MaskedInput = void 0; var _react = _interopRequireWildcard(require("react")); var _styledComponents = require("styled-components"); var _defaultProps = require("../../default-props"); var _Box = require("../Box"); var _Button = require("../Button"); var _Drop = require("../Drop"); var _FormContext = require("../Form/FormContext"); var _Keyboard = require("../Keyboard"); var _utils = require("../../utils"); var _StyledMaskedInput = require("./StyledMaskedInput"); function _getRequireWildcardCache() { if (typeof WeakMap !== "function") return null; var cache = new WeakMap(); _getRequireWildcardCache = function _getRequireWildcardCache() { return cache; }; return cache; } function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { "default": obj }; } var cache = _getRequireWildcardCache(); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj["default"] = obj; if (cache) { cache.set(obj, newObj); } return newObj; } function _extends() { _extends = Object.assign || function (target) { for (var i = 1; i < arguments.length; i++) { var source = arguments[i]; for (var key in source) { if (Object.prototype.hasOwnProperty.call(source, key)) { target[key] = source[key]; } } } return target; }; return _extends.apply(this, arguments); } function _objectWithoutPropertiesLoose(source, excluded) { if (source == null) return {}; var target = {}; var sourceKeys = Object.keys(source); var key, i; for (i = 0; i < sourceKeys.length; i++) { key = sourceKeys[i]; if (excluded.indexOf(key) >= 0) continue; target[key] = source[key]; } return target; } var parseValue = function parseValue(mask, value) { // break the value up into mask parts var valueParts = []; // { part, beginIndex, endIndex } var valueIndex = 0; var maskIndex = 0; while (value !== undefined && valueIndex < value.length && maskIndex < mask.length) { var item = mask[maskIndex]; var found = void 0; if (item.fixed) { var length = item.fixed.length; valueParts.push({ part: item.fixed, beginIndex: valueIndex, endIndex: valueIndex + length - 1 }); var part = value.slice(valueIndex, valueIndex + length); if (part === item.fixed) { valueIndex += length; } maskIndex += 1; found = true; } else if (item.options) { // reverse assuming larger is later found = item.options.slice(0).reverse() // eslint-disable-next-line no-loop-func .some(function (option) { var length = option.length; var part = value.slice(valueIndex, valueIndex + length); if (part === option) { valueParts.push({ part: part, beginIndex: valueIndex, endIndex: valueIndex + length - 1 }); valueIndex += length; maskIndex += 1; return true; } return false; }); } if (!found) { if (item.regexp) { var minLength = Array.isArray(item.length) && item.length[0] || item.length || 1; var maxLength = Array.isArray(item.length) && item.length[1] || item.length || value.length - valueIndex; var _length = maxLength; while (!found && _length >= minLength) { var _part = value.slice(valueIndex, valueIndex + _length); if (item.regexp.test(_part)) { valueParts.push({ part: _part, beginIndex: valueIndex, endIndex: valueIndex + _length - 1 }); valueIndex += _length; maskIndex += 1; found = true; } _length -= 1; } if (!found) { valueIndex = value.length; } } else { var _length2 = Array.isArray(item.length) ? item.length[1] : item.length || value.length - valueIndex; var _part2 = value.slice(valueIndex, valueIndex + _length2); valueParts.push({ part: _part2, beginIndex: valueIndex, endIndex: valueIndex + _length2 - 1 }); valueIndex += _length2; maskIndex += 1; } } } return valueParts; }; var defaultMask = []; var dropAlign = { top: 'bottom', left: 'left' }; var MaskedInput = /*#__PURE__*/(0, _react.forwardRef)(function (_ref, ref) { var focusProp = _ref.focus, icon = _ref.icon, id = _ref.id, _ref$mask = _ref.mask, mask = _ref$mask === void 0 ? defaultMask : _ref$mask, name = _ref.name, _onBlur = _ref.onBlur, onChange = _ref.onChange, _onFocus = _ref.onFocus, onKeyDown = _ref.onKeyDown, placeholder = _ref.placeholder, plain = _ref.plain, reverse = _ref.reverse, valueProp = _ref.value, rest = _objectWithoutPropertiesLoose(_ref, ["focus", "icon", "id", "mask", "name", "onBlur", "onChange", "onFocus", "onKeyDown", "placeholder", "plain", "reverse", "value"]); var theme = (0, _react.useContext)(_styledComponents.ThemeContext) || _defaultProps.defaultProps.theme; var formContext = (0, _react.useContext)(_FormContext.FormContext); var _formContext$useFormI = formContext.useFormInput(name, valueProp), value = _formContext$useFormI[0], setValue = _formContext$useFormI[1]; var _useState = (0, _react.useState)(parseValue(mask, value)), valueParts = _useState[0], setValueParts = _useState[1]; (0, _react.useEffect)(function () { setValueParts(parseValue(mask, value)); }, [mask, value]); var inputRef = (0, _utils.useForwardedRef)(ref); var dropRef = (0, _react.useRef)(); var _useState2 = (0, _react.useState)(focusProp), focus = _useState2[0], setFocus = _useState2[1]; var _useState3 = (0, _react.useState)(), activeMaskIndex = _useState3[0], setActiveMaskIndex = _useState3[1]; var _useState4 = (0, _react.useState)(), activeOptionIndex = _useState4[0], setActiveOptionIndex = _useState4[1]; var _useState5 = (0, _react.useState)(), showDrop = _useState5[0], setShowDrop = _useState5[1]; (0, _react.useEffect)(function () { if (focus) { var timer = setTimeout(function () { // determine which mask element the caret is at var caretIndex = inputRef.current.selectionStart; var maskIndex; valueParts.some(function (part, index) { if (part.beginIndex <= caretIndex && part.endIndex >= caretIndex) { maskIndex = index; return true; } return false; }); if (maskIndex === undefined && valueParts.length < mask.length) { maskIndex = valueParts.length; // first unused one } if (maskIndex && mask[maskIndex].fixed) { maskIndex -= 1; // fixed mask parts are never "active" } if (maskIndex !== activeMaskIndex) { setActiveMaskIndex(maskIndex); setActiveOptionIndex(-1); setShowDrop(maskIndex >= 0 && mask[maskIndex].options && true); } }, 10); // 10ms empirically chosen return function () { return clearTimeout(timer); }; } return undefined; }, [activeMaskIndex, focus, inputRef, mask, valueParts]); var setInputValue = (0, _react.useCallback)(function (nextValue) { // Calling set value function directly on input because React library // overrides setter `event.target.value =` and loses original event // target fidelity. // https://stackoverflow.com/a/46012210 && // https://github.com/grommet/grommet/pull/3171#discussion_r296415239 var nativeInputValueSetter = Object.getOwnPropertyDescriptor(window.HTMLInputElement.prototype, 'value').set; nativeInputValueSetter.call(inputRef.current, nextValue); var event = new Event('input', { bubbles: true }); inputRef.current.dispatchEvent(event); }, [inputRef]); // This could be due to a paste or as the user is typing. var onChangeInput = (0, _react.useCallback)(function (event) { // Align with the mask. var nextValueParts = parseValue(mask, event.target.value); var nextValue = nextValueParts.map(function (part) { return part.part; }).join(''); if (value !== nextValue) { setInputValue(nextValue); setValue(nextValue); if (onChange) onChange(event); } }, [mask, onChange, setInputValue, setValue, value]); var onOption = (0, _react.useCallback)(function (option) { return function () { var nextValueParts = [].concat(valueParts); nextValueParts[activeMaskIndex] = { part: option }; // add any fixed parts that follow var index = activeMaskIndex + 1; while (index < mask.length && !nextValueParts[index] && mask[index].fixed) { nextValueParts[index] = { part: mask[index].fixed }; index += 1; } var nextValue = nextValueParts.map(function (part) { return part.part; }).join(''); setInputValue(nextValue); // restore focus to input inputRef.current.focus(); }; }, [activeMaskIndex, inputRef, mask, setInputValue, valueParts]); var onNextOption = (0, _react.useCallback)(function (event) { var item = mask[activeMaskIndex]; if (item && item.options) { event.preventDefault(); var index = Math.min(activeOptionIndex + 1, item.options.length - 1); setActiveOptionIndex(index); } }, [activeMaskIndex, activeOptionIndex, mask]); var onPreviousOption = (0, _react.useCallback)(function (event) { if (activeMaskIndex >= 0 && mask[activeMaskIndex].options) { event.preventDefault(); var index = Math.max(activeOptionIndex - 1, 0); setActiveOptionIndex(index); } }, [activeMaskIndex, activeOptionIndex, mask]); var onSelectOption = (0, _react.useCallback)(function (event) { if (activeMaskIndex >= 0 && activeOptionIndex >= 0) { event.preventDefault(); var option = mask[activeMaskIndex].options[activeOptionIndex]; onOption(option)(); } }, [activeMaskIndex, activeOptionIndex, mask, onOption]); var onEsc = (0, _react.useCallback)(function (event) { if (showDrop) { // we have to stop both synthetic events and native events // drop and layer should not close by pressing esc on this input event.stopPropagation(); event.nativeEvent.stopImmediatePropagation(); setShowDrop(false); } }, [showDrop]); var onHideDrop = (0, _react.useCallback)(function () { return setShowDrop(false); }, []); var renderPlaceholder = function renderPlaceholder() { return mask.map(function (item) { return item.placeholder || item.fixed; }).join(''); }; return /*#__PURE__*/_react["default"].createElement(_StyledMaskedInput.StyledMaskedInputContainer, { plain: plain }, icon && /*#__PURE__*/_react["default"].createElement(_StyledMaskedInput.StyledIcon, { reverse: reverse, theme: theme }, icon), /*#__PURE__*/_react["default"].createElement(_Keyboard.Keyboard, { onEsc: onEsc, onTab: showDrop ? function () { return setShowDrop(false); } : undefined, onLeft: undefined, onRight: undefined, onUp: onPreviousOption, onDown: showDrop ? onNextOption : function () { return setShowDrop(true); }, onEnter: onSelectOption, onKeyDown: onKeyDown }, /*#__PURE__*/_react["default"].createElement(_StyledMaskedInput.StyledMaskedInput, _extends({ ref: inputRef, id: id, name: name, autoComplete: "off", plain: plain, placeholder: placeholder || renderPlaceholder(), icon: icon, reverse: reverse, focus: focus }, rest, { value: value, theme: theme, onFocus: function onFocus(event) { setFocus(true); setShowDrop(true); if (_onFocus) _onFocus(event); }, onBlur: function onBlur(event) { setFocus(false); // This will be called when the user clicks on a suggestion, // check for that and don't remove the drop in that case. // Drop will already have removed itself if the user has focused // outside of the Drop. if (!dropRef.current) setShowDrop(false); if (_onBlur) _onBlur(event); }, onChange: onChangeInput }))), showDrop && mask[activeMaskIndex] && mask[activeMaskIndex].options && /*#__PURE__*/_react["default"].createElement(_Drop.Drop, { id: id ? "masked-input-drop__" + id : undefined, align: dropAlign, responsive: false, target: inputRef.current, onClickOutside: onHideDrop, onEsc: onHideDrop }, /*#__PURE__*/_react["default"].createElement(_Box.Box, { ref: dropRef }, mask[activeMaskIndex].options.map(function (option, index) { // Determine whether the label is done as a child or // as an option Button kind property. var child = !theme.button.option ? /*#__PURE__*/_react["default"].createElement(_Box.Box, { pad: { horizontal: 'small', vertical: 'xsmall' } }, option) : undefined; // if we have a child, turn on plain, and hoverIndicator return /*#__PURE__*/_react["default"].createElement(_Box.Box, { key: option, flex: false }, /*#__PURE__*/_react["default"].createElement(_Button.Button, { tabIndex: "-1", onClick: onOption(option), onMouseOver: function onMouseOver() { return setActiveOptionIndex(index); }, onFocus: function onFocus() {}, active: index === activeOptionIndex, plain: !child ? undefined : true, align: "start", kind: !child ? 'option' : undefined, hoverIndicator: !child ? undefined : 'background', label: !child ? option : undefined }, child)); })))); }); MaskedInput.displayName = 'MaskedInput'; var MaskedInputDoc; if (process.env.NODE_ENV !== 'production') { // eslint-disable-next-line global-require MaskedInputDoc = require('./doc').doc(MaskedInput); } var MaskedInputWrapper = MaskedInputDoc || MaskedInput; exports.MaskedInput = MaskedInputWrapper;