375 lines
13 KiB
JavaScript
375 lines
13 KiB
JavaScript
"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; |