123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584 |
- "use strict";
- var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
- var _typeof3 = require("@babel/runtime/helpers/typeof");
- 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 _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
- var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
- var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
- var _miniDecimal = _interopRequireWildcard(require("@rc-component/mini-decimal"));
- var _classnames = _interopRequireDefault(require("classnames"));
- var _rcInput = require("rc-input");
- var _useLayoutEffect = require("rc-util/lib/hooks/useLayoutEffect");
- var _proxyObject = _interopRequireDefault(require("rc-util/lib/proxyObject"));
- var _ref = require("rc-util/lib/ref");
- var React = _interopRequireWildcard(require("react"));
- var _useCursor3 = _interopRequireDefault(require("./hooks/useCursor"));
- var _StepHandler = _interopRequireDefault(require("./StepHandler"));
- var _numberUtil = require("./utils/numberUtil");
- var _commonUtils = require("rc-input/lib/utils/commonUtils");
- var _useFrame = _interopRequireDefault(require("./hooks/useFrame"));
- var _excluded = ["prefixCls", "className", "style", "min", "max", "step", "defaultValue", "value", "disabled", "readOnly", "upHandler", "downHandler", "keyboard", "changeOnWheel", "controls", "classNames", "stringMode", "parser", "formatter", "precision", "decimalSeparator", "onChange", "onInput", "onPressEnter", "onStep", "changeOnBlur", "domRef"],
- _excluded2 = ["disabled", "style", "prefixCls", "value", "prefix", "suffix", "addonBefore", "addonAfter", "className", "classNames"];
- function _getRequireWildcardCache(e) { if ("function" != typeof WeakMap) return null; var r = new WeakMap(), t = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(e) { return e ? t : r; })(e); }
- function _interopRequireWildcard(e, r) { if (!r && e && e.__esModule) return e; if (null === e || "object" != _typeof3(e) && "function" != typeof e) return { default: e }; var t = _getRequireWildcardCache(r); if (t && t.has(e)) return t.get(e); var n = { __proto__: null }, a = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var u in e) if ("default" !== u && Object.prototype.hasOwnProperty.call(e, u)) { var i = a ? Object.getOwnPropertyDescriptor(e, u) : null; i && (i.get || i.set) ? Object.defineProperty(n, u, i) : n[u] = e[u]; } return n.default = e, t && t.set(e, n), n; }
- /**
- * We support `stringMode` which need handle correct type when user call in onChange
- * format max or min value
- * 1. if isInvalid return null
- * 2. if precision is undefined, return decimal
- * 3. format with precision
- * I. if max > 0, round down with precision. Example: max= 3.5, precision=0 afterFormat: 3
- * II. if max < 0, round up with precision. Example: max= -3.5, precision=0 afterFormat: -4
- * III. if min > 0, round up with precision. Example: min= 3.5, precision=0 afterFormat: 4
- * IV. if min < 0, round down with precision. Example: max= -3.5, precision=0 afterFormat: -3
- */
- var getDecimalValue = function getDecimalValue(stringMode, decimalValue) {
- if (stringMode || decimalValue.isEmpty()) {
- return decimalValue.toString();
- }
- return decimalValue.toNumber();
- };
- var getDecimalIfValidate = function getDecimalIfValidate(value) {
- var decimal = (0, _miniDecimal.default)(value);
- return decimal.isInvalidate() ? null : decimal;
- };
- var InternalInputNumber = /*#__PURE__*/React.forwardRef(function (props, ref) {
- var prefixCls = props.prefixCls,
- className = props.className,
- style = props.style,
- min = props.min,
- max = props.max,
- _props$step = props.step,
- step = _props$step === void 0 ? 1 : _props$step,
- defaultValue = props.defaultValue,
- value = props.value,
- disabled = props.disabled,
- readOnly = props.readOnly,
- upHandler = props.upHandler,
- downHandler = props.downHandler,
- keyboard = props.keyboard,
- _props$changeOnWheel = props.changeOnWheel,
- changeOnWheel = _props$changeOnWheel === void 0 ? false : _props$changeOnWheel,
- _props$controls = props.controls,
- controls = _props$controls === void 0 ? true : _props$controls,
- classNames = props.classNames,
- stringMode = props.stringMode,
- parser = props.parser,
- formatter = props.formatter,
- precision = props.precision,
- decimalSeparator = props.decimalSeparator,
- onChange = props.onChange,
- onInput = props.onInput,
- onPressEnter = props.onPressEnter,
- onStep = props.onStep,
- _props$changeOnBlur = props.changeOnBlur,
- changeOnBlur = _props$changeOnBlur === void 0 ? true : _props$changeOnBlur,
- domRef = props.domRef,
- inputProps = (0, _objectWithoutProperties2.default)(props, _excluded);
- var inputClassName = "".concat(prefixCls, "-input");
- var inputRef = React.useRef(null);
- var _React$useState = React.useState(false),
- _React$useState2 = (0, _slicedToArray2.default)(_React$useState, 2),
- focus = _React$useState2[0],
- setFocus = _React$useState2[1];
- var userTypingRef = React.useRef(false);
- var compositionRef = React.useRef(false);
- var shiftKeyRef = React.useRef(false);
- // ============================ Value =============================
- // Real value control
- var _React$useState3 = React.useState(function () {
- return (0, _miniDecimal.default)(value !== null && value !== void 0 ? value : defaultValue);
- }),
- _React$useState4 = (0, _slicedToArray2.default)(_React$useState3, 2),
- decimalValue = _React$useState4[0],
- setDecimalValue = _React$useState4[1];
- function setUncontrolledDecimalValue(newDecimal) {
- if (value === undefined) {
- setDecimalValue(newDecimal);
- }
- }
- // ====================== Parser & Formatter ======================
- /**
- * `precision` is used for formatter & onChange.
- * It will auto generate by `value` & `step`.
- * But it will not block user typing.
- *
- * Note: Auto generate `precision` is used for legacy logic.
- * We should remove this since we already support high precision with BigInt.
- *
- * @param number Provide which number should calculate precision
- * @param userTyping Change by user typing
- */
- var getPrecision = React.useCallback(function (numStr, userTyping) {
- if (userTyping) {
- return undefined;
- }
- if (precision >= 0) {
- return precision;
- }
- return Math.max((0, _miniDecimal.getNumberPrecision)(numStr), (0, _miniDecimal.getNumberPrecision)(step));
- }, [precision, step]);
- // >>> Parser
- var mergedParser = React.useCallback(function (num) {
- var numStr = String(num);
- if (parser) {
- return parser(numStr);
- }
- var parsedStr = numStr;
- if (decimalSeparator) {
- parsedStr = parsedStr.replace(decimalSeparator, '.');
- }
- // [Legacy] We still support auto convert `$ 123,456` to `123456`
- return parsedStr.replace(/[^\w.-]+/g, '');
- }, [parser, decimalSeparator]);
- // >>> Formatter
- var inputValueRef = React.useRef('');
- var mergedFormatter = React.useCallback(function (number, userTyping) {
- if (formatter) {
- return formatter(number, {
- userTyping: userTyping,
- input: String(inputValueRef.current)
- });
- }
- var str = typeof number === 'number' ? (0, _miniDecimal.num2str)(number) : number;
- // User typing will not auto format with precision directly
- if (!userTyping) {
- var mergedPrecision = getPrecision(str, userTyping);
- if ((0, _miniDecimal.validateNumber)(str) && (decimalSeparator || mergedPrecision >= 0)) {
- // Separator
- var separatorStr = decimalSeparator || '.';
- str = (0, _miniDecimal.toFixed)(str, separatorStr, mergedPrecision);
- }
- }
- return str;
- }, [formatter, getPrecision, decimalSeparator]);
- // ========================== InputValue ==========================
- /**
- * Input text value control
- *
- * User can not update input content directly. It updates with follow rules by priority:
- * 1. controlled `value` changed
- * * [SPECIAL] Typing like `1.` should not immediately convert to `1`
- * 2. User typing with format (not precision)
- * 3. Blur or Enter trigger revalidate
- */
- var _React$useState5 = React.useState(function () {
- var initValue = defaultValue !== null && defaultValue !== void 0 ? defaultValue : value;
- if (decimalValue.isInvalidate() && ['string', 'number'].includes((0, _typeof2.default)(initValue))) {
- return Number.isNaN(initValue) ? '' : initValue;
- }
- return mergedFormatter(decimalValue.toString(), false);
- }),
- _React$useState6 = (0, _slicedToArray2.default)(_React$useState5, 2),
- inputValue = _React$useState6[0],
- setInternalInputValue = _React$useState6[1];
- inputValueRef.current = inputValue;
- // Should always be string
- function setInputValue(newValue, userTyping) {
- setInternalInputValue(mergedFormatter(
- // Invalidate number is sometime passed by external control, we should let it go
- // Otherwise is controlled by internal interactive logic which check by userTyping
- // You can ref 'show limited value when input is not focused' test for more info.
- newValue.isInvalidate() ? newValue.toString(false) : newValue.toString(!userTyping), userTyping));
- }
- // >>> Max & Min limit
- var maxDecimal = React.useMemo(function () {
- return getDecimalIfValidate(max);
- }, [max, precision]);
- var minDecimal = React.useMemo(function () {
- return getDecimalIfValidate(min);
- }, [min, precision]);
- var upDisabled = React.useMemo(function () {
- if (!maxDecimal || !decimalValue || decimalValue.isInvalidate()) {
- return false;
- }
- return maxDecimal.lessEquals(decimalValue);
- }, [maxDecimal, decimalValue]);
- var downDisabled = React.useMemo(function () {
- if (!minDecimal || !decimalValue || decimalValue.isInvalidate()) {
- return false;
- }
- return decimalValue.lessEquals(minDecimal);
- }, [minDecimal, decimalValue]);
- // Cursor controller
- var _useCursor = (0, _useCursor3.default)(inputRef.current, focus),
- _useCursor2 = (0, _slicedToArray2.default)(_useCursor, 2),
- recordCursor = _useCursor2[0],
- restoreCursor = _useCursor2[1];
- // ============================= Data =============================
- /**
- * Find target value closet within range.
- * e.g. [11, 28]:
- * 3 => 11
- * 23 => 23
- * 99 => 28
- */
- var getRangeValue = function getRangeValue(target) {
- // target > max
- if (maxDecimal && !target.lessEquals(maxDecimal)) {
- return maxDecimal;
- }
- // target < min
- if (minDecimal && !minDecimal.lessEquals(target)) {
- return minDecimal;
- }
- return null;
- };
- /**
- * Check value is in [min, max] range
- */
- var isInRange = function isInRange(target) {
- return !getRangeValue(target);
- };
- /**
- * Trigger `onChange` if value validated and not equals of origin.
- * Return the value that re-align in range.
- */
- var triggerValueUpdate = function triggerValueUpdate(newValue, userTyping) {
- var updateValue = newValue;
- var isRangeValidate = isInRange(updateValue) || updateValue.isEmpty();
- // Skip align value when trigger value is empty.
- // We just trigger onChange(null)
- // This should not block user typing
- if (!updateValue.isEmpty() && !userTyping) {
- // Revert value in range if needed
- updateValue = getRangeValue(updateValue) || updateValue;
- isRangeValidate = true;
- }
- if (!readOnly && !disabled && isRangeValidate) {
- var numStr = updateValue.toString();
- var mergedPrecision = getPrecision(numStr, userTyping);
- if (mergedPrecision >= 0) {
- updateValue = (0, _miniDecimal.default)((0, _miniDecimal.toFixed)(numStr, '.', mergedPrecision));
- // When to fixed. The value may out of min & max range.
- // 4 in [0, 3.8] => 3.8 => 4 (toFixed)
- if (!isInRange(updateValue)) {
- updateValue = (0, _miniDecimal.default)((0, _miniDecimal.toFixed)(numStr, '.', mergedPrecision, true));
- }
- }
- // Trigger event
- if (!updateValue.equals(decimalValue)) {
- setUncontrolledDecimalValue(updateValue);
- onChange === null || onChange === void 0 || onChange(updateValue.isEmpty() ? null : getDecimalValue(stringMode, updateValue));
- // Reformat input if value is not controlled
- if (value === undefined) {
- setInputValue(updateValue, userTyping);
- }
- }
- return updateValue;
- }
- return decimalValue;
- };
- // ========================== User Input ==========================
- var onNextPromise = (0, _useFrame.default)();
- // >>> Collect input value
- var collectInputValue = function collectInputValue(inputStr) {
- recordCursor();
- // Update inputValue in case input can not parse as number
- // Refresh ref value immediately since it may used by formatter
- inputValueRef.current = inputStr;
- setInternalInputValue(inputStr);
- // Parse number
- if (!compositionRef.current) {
- var finalValue = mergedParser(inputStr);
- var finalDecimal = (0, _miniDecimal.default)(finalValue);
- if (!finalDecimal.isNaN()) {
- triggerValueUpdate(finalDecimal, true);
- }
- }
- // Trigger onInput later to let user customize value if they want to handle something after onChange
- onInput === null || onInput === void 0 || onInput(inputStr);
- // optimize for chinese input experience
- // https://github.com/ant-design/ant-design/issues/8196
- onNextPromise(function () {
- var nextInputStr = inputStr;
- if (!parser) {
- nextInputStr = inputStr.replace(/。/g, '.');
- }
- if (nextInputStr !== inputStr) {
- collectInputValue(nextInputStr);
- }
- });
- };
- // >>> Composition
- var onCompositionStart = function onCompositionStart() {
- compositionRef.current = true;
- };
- var onCompositionEnd = function onCompositionEnd() {
- compositionRef.current = false;
- collectInputValue(inputRef.current.value);
- };
- // >>> Input
- var onInternalInput = function onInternalInput(e) {
- collectInputValue(e.target.value);
- };
- // ============================= Step =============================
- var onInternalStep = function onInternalStep(up) {
- var _inputRef$current;
- // Ignore step since out of range
- if (up && upDisabled || !up && downDisabled) {
- return;
- }
- // Clear typing status since it may be caused by up & down key.
- // We should sync with input value.
- userTypingRef.current = false;
- var stepDecimal = (0, _miniDecimal.default)(shiftKeyRef.current ? (0, _numberUtil.getDecupleSteps)(step) : step);
- if (!up) {
- stepDecimal = stepDecimal.negate();
- }
- var target = (decimalValue || (0, _miniDecimal.default)(0)).add(stepDecimal.toString());
- var updatedValue = triggerValueUpdate(target, false);
- onStep === null || onStep === void 0 || onStep(getDecimalValue(stringMode, updatedValue), {
- offset: shiftKeyRef.current ? (0, _numberUtil.getDecupleSteps)(step) : step,
- type: up ? 'up' : 'down'
- });
- (_inputRef$current = inputRef.current) === null || _inputRef$current === void 0 || _inputRef$current.focus();
- };
- // ============================ Flush =============================
- /**
- * Flush current input content to trigger value change & re-formatter input if needed.
- * This will always flush input value for update.
- * If it's invalidate, will fallback to last validate value.
- */
- var flushInputValue = function flushInputValue(userTyping) {
- var parsedValue = (0, _miniDecimal.default)(mergedParser(inputValue));
- var formatValue;
- if (!parsedValue.isNaN()) {
- // Only validate value or empty value can be re-fill to inputValue
- // Reassign the formatValue within ranged of trigger control
- formatValue = triggerValueUpdate(parsedValue, userTyping);
- } else {
- formatValue = triggerValueUpdate(decimalValue, userTyping);
- }
- if (value !== undefined) {
- // Reset back with controlled value first
- setInputValue(decimalValue, false);
- } else if (!formatValue.isNaN()) {
- // Reset input back since no validate value
- setInputValue(formatValue, false);
- }
- };
- // Solve the issue of the event triggering sequence when entering numbers in chinese input (Safari)
- var onBeforeInput = function onBeforeInput() {
- userTypingRef.current = true;
- };
- var onKeyDown = function onKeyDown(event) {
- var key = event.key,
- shiftKey = event.shiftKey;
- userTypingRef.current = true;
- shiftKeyRef.current = shiftKey;
- if (key === 'Enter') {
- if (!compositionRef.current) {
- userTypingRef.current = false;
- }
- flushInputValue(false);
- onPressEnter === null || onPressEnter === void 0 || onPressEnter(event);
- }
- if (keyboard === false) {
- return;
- }
- // Do step
- if (!compositionRef.current && ['Up', 'ArrowUp', 'Down', 'ArrowDown'].includes(key)) {
- onInternalStep(key === 'Up' || key === 'ArrowUp');
- event.preventDefault();
- }
- };
- var onKeyUp = function onKeyUp() {
- userTypingRef.current = false;
- shiftKeyRef.current = false;
- };
- React.useEffect(function () {
- if (changeOnWheel && focus) {
- var onWheel = function onWheel(event) {
- // moving mouse wheel rises wheel event with deltaY < 0
- // scroll value grows from top to bottom, as screen Y coordinate
- onInternalStep(event.deltaY < 0);
- event.preventDefault();
- };
- var input = inputRef.current;
- if (input) {
- // React onWheel is passive and we can't preventDefault() in it.
- // That's why we should subscribe with DOM listener
- // https://stackoverflow.com/questions/63663025/react-onwheel-handler-cant-preventdefault-because-its-a-passive-event-listenev
- input.addEventListener('wheel', onWheel, {
- passive: false
- });
- return function () {
- return input.removeEventListener('wheel', onWheel);
- };
- }
- }
- });
- // >>> Focus & Blur
- var onBlur = function onBlur() {
- if (changeOnBlur) {
- flushInputValue(false);
- }
- setFocus(false);
- userTypingRef.current = false;
- };
- // ========================== Controlled ==========================
- // Input by precision & formatter
- (0, _useLayoutEffect.useLayoutUpdateEffect)(function () {
- if (!decimalValue.isInvalidate()) {
- setInputValue(decimalValue, false);
- }
- }, [precision, formatter]);
- // Input by value
- (0, _useLayoutEffect.useLayoutUpdateEffect)(function () {
- var newValue = (0, _miniDecimal.default)(value);
- setDecimalValue(newValue);
- var currentParsedValue = (0, _miniDecimal.default)(mergedParser(inputValue));
- // When user typing from `1.2` to `1.`, we should not convert to `1` immediately.
- // But let it go if user set `formatter`
- if (!newValue.equals(currentParsedValue) || !userTypingRef.current || formatter) {
- // Update value as effect
- setInputValue(newValue, userTypingRef.current);
- }
- }, [value]);
- // ============================ Cursor ============================
- (0, _useLayoutEffect.useLayoutUpdateEffect)(function () {
- if (formatter) {
- restoreCursor();
- }
- }, [inputValue]);
- // ============================ Render ============================
- return /*#__PURE__*/React.createElement("div", {
- ref: domRef,
- className: (0, _classnames.default)(prefixCls, className, (0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)((0, _defineProperty2.default)({}, "".concat(prefixCls, "-focused"), focus), "".concat(prefixCls, "-disabled"), disabled), "".concat(prefixCls, "-readonly"), readOnly), "".concat(prefixCls, "-not-a-number"), decimalValue.isNaN()), "".concat(prefixCls, "-out-of-range"), !decimalValue.isInvalidate() && !isInRange(decimalValue))),
- style: style,
- onFocus: function onFocus() {
- setFocus(true);
- },
- onBlur: onBlur,
- onKeyDown: onKeyDown,
- onKeyUp: onKeyUp,
- onCompositionStart: onCompositionStart,
- onCompositionEnd: onCompositionEnd,
- onBeforeInput: onBeforeInput
- }, controls && /*#__PURE__*/React.createElement(_StepHandler.default, {
- prefixCls: prefixCls,
- upNode: upHandler,
- downNode: downHandler,
- upDisabled: upDisabled,
- downDisabled: downDisabled,
- onStep: onInternalStep
- }), /*#__PURE__*/React.createElement("div", {
- className: "".concat(inputClassName, "-wrap")
- }, /*#__PURE__*/React.createElement("input", (0, _extends2.default)({
- autoComplete: "off",
- role: "spinbutton",
- "aria-valuemin": min,
- "aria-valuemax": max,
- "aria-valuenow": decimalValue.isInvalidate() ? null : decimalValue.toString(),
- step: step
- }, inputProps, {
- ref: (0, _ref.composeRef)(inputRef, ref),
- className: inputClassName,
- value: inputValue,
- onChange: onInternalInput,
- disabled: disabled,
- readOnly: readOnly
- }))));
- });
- var InputNumber = /*#__PURE__*/React.forwardRef(function (props, ref) {
- var disabled = props.disabled,
- style = props.style,
- _props$prefixCls = props.prefixCls,
- prefixCls = _props$prefixCls === void 0 ? 'rc-input-number' : _props$prefixCls,
- value = props.value,
- prefix = props.prefix,
- suffix = props.suffix,
- addonBefore = props.addonBefore,
- addonAfter = props.addonAfter,
- className = props.className,
- classNames = props.classNames,
- rest = (0, _objectWithoutProperties2.default)(props, _excluded2);
- var holderRef = React.useRef(null);
- var inputNumberDomRef = React.useRef(null);
- var inputFocusRef = React.useRef(null);
- var focus = function focus(option) {
- if (inputFocusRef.current) {
- (0, _commonUtils.triggerFocus)(inputFocusRef.current, option);
- }
- };
- React.useImperativeHandle(ref, function () {
- return (0, _proxyObject.default)(inputFocusRef.current, {
- focus: focus,
- nativeElement: holderRef.current.nativeElement || inputNumberDomRef.current
- });
- });
- return /*#__PURE__*/React.createElement(_rcInput.BaseInput, {
- className: className,
- triggerFocus: focus,
- prefixCls: prefixCls,
- value: value,
- disabled: disabled,
- style: style,
- prefix: prefix,
- suffix: suffix,
- addonAfter: addonAfter,
- addonBefore: addonBefore,
- classNames: classNames,
- components: {
- affixWrapper: 'div',
- groupWrapper: 'div',
- wrapper: 'div',
- groupAddon: 'div'
- },
- ref: holderRef
- }, /*#__PURE__*/React.createElement(InternalInputNumber, (0, _extends2.default)({
- prefixCls: prefixCls,
- disabled: disabled,
- ref: inputFocusRef,
- domRef: inputNumberDomRef,
- className: classNames === null || classNames === void 0 ? void 0 : classNames.input
- }, rest)));
- });
- if (process.env.NODE_ENV !== 'production') {
- InputNumber.displayName = 'InputNumber';
- }
- var _default = exports.default = InputNumber;
|