123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368 |
- import _extends from "@babel/runtime/helpers/esm/extends";
- import _defineProperty from "@babel/runtime/helpers/esm/defineProperty";
- import _slicedToArray from "@babel/runtime/helpers/esm/slicedToArray";
- import _objectWithoutProperties from "@babel/runtime/helpers/esm/objectWithoutProperties";
- var _excluded = ["active", "showActiveCls", "suffixIcon", "format", "validateFormat", "onChange", "onInput", "helped", "onHelp", "onSubmit", "onKeyDown", "preserveInvalidOnBlur", "invalid", "clearIcon"];
- import classNames from 'classnames';
- import { useEvent } from 'rc-util';
- import useLayoutEffect from "rc-util/es/hooks/useLayoutEffect";
- import raf from "rc-util/es/raf";
- import * as React from 'react';
- import { leftPad } from "../../utils/miscUtil";
- import PickerContext from "../context";
- import useLockEffect from "../hooks/useLockEffect";
- import Icon from "./Icon";
- import MaskFormat from "./MaskFormat";
- import { getMaskRange } from "./util";
- // 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 = _objectWithoutProperties(props, _excluded);
- var value = props.value,
- onFocus = props.onFocus,
- onBlur = props.onBlur,
- onMouseUp = props.onMouseUp;
- var _React$useContext = React.useContext(PickerContext),
- 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 = _slicedToArray(_React$useState, 2),
- focused = _React$useState2[0],
- setFocused = _React$useState2[1];
- var _React$useState3 = React.useState(value),
- _React$useState4 = _slicedToArray(_React$useState3, 2),
- internalInputValue = _React$useState4[0],
- setInputValue = _React$useState4[1];
- var _React$useState5 = React.useState(''),
- _React$useState6 = _slicedToArray(_React$useState5, 2),
- focusCellText = _React$useState6[0],
- setFocusCellText = _React$useState6[1];
- var _React$useState7 = React.useState(null),
- _React$useState8 = _slicedToArray(_React$useState7, 2),
- focusCellIndex = _React$useState8[0],
- setFocusCellIndex = _React$useState8[1];
- var _React$useState9 = React.useState(null),
- _React$useState10 = _slicedToArray(_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(format || '');
- }, [format]);
- var _React$useMemo = React.useMemo(function () {
- if (helped) {
- return [0, 0];
- }
- return maskFormat.getSelection(focusCellIndex);
- }, [maskFormat, focusCellIndex, helped]),
- _React$useMemo2 = _slicedToArray(_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 = 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
- useLockEffect(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 = getMaskRange(cellFormat),
- _getMaskRange2 = _slicedToArray(_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
- 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();
- useLayoutEffect(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 = raf(function () {
- inputRef.current.setSelectionRange(selectionStart, selectionEnd);
- });
- return function () {
- raf.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: classNames(inputPrefixCls, _defineProperty(_defineProperty({}, "".concat(inputPrefixCls, "-active"), active && showActiveCls), "".concat(inputPrefixCls, "-placeholder"), helped))
- }, /*#__PURE__*/React.createElement(Component, _extends({
- 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, {
- type: "suffix",
- icon: suffixIcon
- }), clearIcon);
- });
- if (process.env.NODE_ENV !== 'production') {
- Input.displayName = 'Input';
- }
- export default Input;
|