"use strict"; var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault").default; var _interopRequireWildcard = require("@babel/runtime/helpers/interopRequireWildcard").default; Object.defineProperty(exports, "__esModule", { value: true }); exports.default = void 0; var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends")); var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty")); var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray")); var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties")); var _classnames = _interopRequireDefault(require("classnames")); var _rcUtil = require("rc-util"); var _useLayoutEffect = _interopRequireDefault(require("rc-util/lib/hooks/useLayoutEffect")); var _raf = _interopRequireDefault(require("rc-util/lib/raf")); var React = _interopRequireWildcard(require("react")); var _miscUtil = require("../../utils/miscUtil"); var _context = _interopRequireDefault(require("../context")); var _useLockEffect = _interopRequireDefault(require("../hooks/useLockEffect")); var _Icon = _interopRequireDefault(require("./Icon")); var _MaskFormat = _interopRequireDefault(require("./MaskFormat")); var _util = require("./util"); var _excluded = ["active", "showActiveCls", "suffixIcon", "format", "validateFormat", "onChange", "onInput", "helped", "onHelp", "onSubmit", "onKeyDown", "preserveInvalidOnBlur", "invalid", "clearIcon"]; // Format logic // // First time on focus: // 1. check if the text is valid, if not fill with format // 2. set highlight cell to the first cell // Cells // 1. Selection the index cell, set inner `cacheValue` to '' // 2. Key input filter non-number char, patch after the `cacheValue` // 1. Replace the `cacheValue` with input align the cell length // 2. Re-selection the mask cell // 3. If `cacheValue` match the limit length or cell format (like 1 ~ 12 month), go to next cell var Input = /*#__PURE__*/React.forwardRef(function (props, ref) { var active = props.active, _props$showActiveCls = props.showActiveCls, showActiveCls = _props$showActiveCls === void 0 ? true : _props$showActiveCls, suffixIcon = props.suffixIcon, format = props.format, validateFormat = props.validateFormat, onChange = props.onChange, onInput = props.onInput, helped = props.helped, onHelp = props.onHelp, onSubmit = props.onSubmit, onKeyDown = props.onKeyDown, _props$preserveInvali = props.preserveInvalidOnBlur, preserveInvalidOnBlur = _props$preserveInvali === void 0 ? false : _props$preserveInvali, invalid = props.invalid, clearIcon = props.clearIcon, restProps = (0, _objectWithoutProperties2.default)(props, _excluded); var value = props.value, onFocus = props.onFocus, onBlur = props.onBlur, onMouseUp = props.onMouseUp; var _React$useContext = React.useContext(_context.default), prefixCls = _React$useContext.prefixCls, _React$useContext$inp = _React$useContext.input, Component = _React$useContext$inp === void 0 ? 'input' : _React$useContext$inp; var inputPrefixCls = "".concat(prefixCls, "-input"); // ======================== Value ========================= var _React$useState = React.useState(false), _React$useState2 = (0, _slicedToArray2.default)(_React$useState, 2), focused = _React$useState2[0], setFocused = _React$useState2[1]; var _React$useState3 = React.useState(value), _React$useState4 = (0, _slicedToArray2.default)(_React$useState3, 2), internalInputValue = _React$useState4[0], setInputValue = _React$useState4[1]; var _React$useState5 = React.useState(''), _React$useState6 = (0, _slicedToArray2.default)(_React$useState5, 2), focusCellText = _React$useState6[0], setFocusCellText = _React$useState6[1]; var _React$useState7 = React.useState(null), _React$useState8 = (0, _slicedToArray2.default)(_React$useState7, 2), focusCellIndex = _React$useState8[0], setFocusCellIndex = _React$useState8[1]; var _React$useState9 = React.useState(null), _React$useState10 = (0, _slicedToArray2.default)(_React$useState9, 2), forceSelectionSyncMark = _React$useState10[0], forceSelectionSync = _React$useState10[1]; var inputValue = internalInputValue || ''; // Sync value if needed React.useEffect(function () { setInputValue(value); }, [value]); // ========================= Refs ========================= var holderRef = React.useRef(); var inputRef = React.useRef(); React.useImperativeHandle(ref, function () { return { nativeElement: holderRef.current, inputElement: inputRef.current, focus: function focus(options) { inputRef.current.focus(options); }, blur: function blur() { inputRef.current.blur(); } }; }); // ======================== Format ======================== var maskFormat = React.useMemo(function () { return new _MaskFormat.default(format || ''); }, [format]); var _React$useMemo = React.useMemo(function () { if (helped) { return [0, 0]; } return maskFormat.getSelection(focusCellIndex); }, [maskFormat, focusCellIndex, helped]), _React$useMemo2 = (0, _slicedToArray2.default)(_React$useMemo, 2), selectionStart = _React$useMemo2[0], selectionEnd = _React$useMemo2[1]; // ======================== Modify ======================== // When input modify content, trigger `onHelp` if is not the format var onModify = function onModify(text) { if (text && text !== format && text !== value) { onHelp(); } }; // ======================== Change ======================== /** * Triggered by paste, keyDown and focus to show format */ var triggerInputChange = (0, _rcUtil.useEvent)(function (text) { if (validateFormat(text)) { onChange(text); } setInputValue(text); onModify(text); }); // Directly trigger `onChange` if `format` is empty var onInternalChange = function onInternalChange(event) { // Hack `onChange` with format to do nothing if (!format) { var text = event.target.value; onModify(text); setInputValue(text); onChange(text); } }; var onFormatPaste = function onFormatPaste(event) { // Get paste text var pasteText = event.clipboardData.getData('text'); if (validateFormat(pasteText)) { triggerInputChange(pasteText); } }; // ======================== Mouse ========================= // When `mouseDown` get focus, it's better to not to change the selection // Since the up position maybe not is the first cell var mouseDownRef = React.useRef(false); var onFormatMouseDown = function onFormatMouseDown() { mouseDownRef.current = true; }; var onFormatMouseUp = function onFormatMouseUp(event) { var _ref = event.target, start = _ref.selectionStart; var closeMaskIndex = maskFormat.getMaskCellIndex(start); setFocusCellIndex(closeMaskIndex); // Force update the selection forceSelectionSync({}); onMouseUp === null || onMouseUp === void 0 || onMouseUp(event); mouseDownRef.current = false; }; // ====================== Focus Blur ====================== var onFormatFocus = function onFormatFocus(event) { setFocused(true); setFocusCellIndex(0); setFocusCellText(''); onFocus(event); }; var onSharedBlur = function onSharedBlur(event) { onBlur(event); }; var onFormatBlur = function onFormatBlur(event) { setFocused(false); onSharedBlur(event); }; // ======================== Active ======================== // Check if blur need reset input value (0, _useLockEffect.default)(active, function () { if (!active && !preserveInvalidOnBlur) { setInputValue(value); } }); // ======================= Keyboard ======================= var onSharedKeyDown = function onSharedKeyDown(event) { if (event.key === 'Enter' && validateFormat(inputValue)) { onSubmit(); } onKeyDown === null || onKeyDown === void 0 || onKeyDown(event); }; var onFormatKeyDown = function onFormatKeyDown(event) { onSharedKeyDown(event); var key = event.key; // Save the cache with cell text var nextCellText = null; // Fill in the input var nextFillText = null; var maskCellLen = selectionEnd - selectionStart; var cellFormat = format.slice(selectionStart, selectionEnd); // Cell Index var offsetCellIndex = function offsetCellIndex(offset) { setFocusCellIndex(function (idx) { var nextIndex = idx + offset; nextIndex = Math.max(nextIndex, 0); nextIndex = Math.min(nextIndex, maskFormat.size() - 1); return nextIndex; }); }; // Range var offsetCellValue = function offsetCellValue(offset) { var _getMaskRange = (0, _util.getMaskRange)(cellFormat), _getMaskRange2 = (0, _slicedToArray2.default)(_getMaskRange, 3), rangeStart = _getMaskRange2[0], rangeEnd = _getMaskRange2[1], rangeDefault = _getMaskRange2[2]; var currentText = inputValue.slice(selectionStart, selectionEnd); var currentTextNum = Number(currentText); if (isNaN(currentTextNum)) { return String(rangeDefault ? rangeDefault : offset > 0 ? rangeStart : rangeEnd); } var num = currentTextNum + offset; var range = rangeEnd - rangeStart + 1; return String(rangeStart + (range + num - rangeStart) % range); }; switch (key) { // =============== Remove =============== case 'Backspace': case 'Delete': nextCellText = ''; nextFillText = cellFormat; break; // =============== Arrows =============== // Left key case 'ArrowLeft': nextCellText = ''; offsetCellIndex(-1); break; // Right key case 'ArrowRight': nextCellText = ''; offsetCellIndex(1); break; // Up key case 'ArrowUp': nextCellText = ''; nextFillText = offsetCellValue(1); break; // Down key case 'ArrowDown': nextCellText = ''; nextFillText = offsetCellValue(-1); break; // =============== Number =============== default: if (!isNaN(Number(key))) { nextCellText = focusCellText + key; nextFillText = nextCellText; } break; } // Update cell text if (nextCellText !== null) { setFocusCellText(nextCellText); if (nextCellText.length >= maskCellLen) { // Go to next cell offsetCellIndex(1); setFocusCellText(''); } } // Update the input text if (nextFillText !== null) { // Replace selection range with `nextCellText` var nextFocusValue = // before inputValue.slice(0, selectionStart) + // replace (0, _miscUtil.leftPad)(nextFillText, maskCellLen) + // after inputValue.slice(selectionEnd); triggerInputChange(nextFocusValue.slice(0, format.length)); } // Always trigger selection sync after key down forceSelectionSync({}); }; // ======================== Format ======================== var rafRef = React.useRef(); (0, _useLayoutEffect.default)(function () { if (!focused || !format || mouseDownRef.current) { return; } // Reset with format if not match if (!maskFormat.match(inputValue)) { triggerInputChange(format); return; } // Match the selection range inputRef.current.setSelectionRange(selectionStart, selectionEnd); // Chrome has the bug anchor position looks not correct but actually correct rafRef.current = (0, _raf.default)(function () { inputRef.current.setSelectionRange(selectionStart, selectionEnd); }); return function () { _raf.default.cancel(rafRef.current); }; }, [maskFormat, format, focused, inputValue, focusCellIndex, selectionStart, selectionEnd, forceSelectionSyncMark, triggerInputChange]); // ======================== Render ======================== // Input props for format var inputProps = format ? { onFocus: onFormatFocus, onBlur: onFormatBlur, onKeyDown: onFormatKeyDown, onMouseDown: onFormatMouseDown, onMouseUp: onFormatMouseUp, onPaste: onFormatPaste } : {}; return /*#__PURE__*/React.createElement("div", { ref: holderRef, className: (0, _classnames.default)(inputPrefixCls, (0, _defineProperty2.default)((0, _defineProperty2.default)({}, "".concat(inputPrefixCls, "-active"), active && showActiveCls), "".concat(inputPrefixCls, "-placeholder"), helped)) }, /*#__PURE__*/React.createElement(Component, (0, _extends2.default)({ ref: inputRef, "aria-invalid": invalid, autoComplete: "off" }, restProps, { onKeyDown: onSharedKeyDown, onBlur: onSharedBlur // Replace with format }, inputProps, { // Value value: inputValue, onChange: onInternalChange })), /*#__PURE__*/React.createElement(_Icon.default, { type: "suffix", icon: suffixIcon }), clearIcon); }); if (process.env.NODE_ENV !== 'production') { Input.displayName = 'Input'; } var _default = exports.default = Input;