UNPKG

22 kBJavaScriptView Raw
1"use strict";
2
3var _interopRequireDefault = require("@babel/runtime/helpers/interopRequireDefault");
4var _typeof3 = require("@babel/runtime/helpers/typeof");
5Object.defineProperty(exports, "__esModule", {
6 value: true
7});
8exports.default = void 0;
9var _extends2 = _interopRequireDefault(require("@babel/runtime/helpers/extends"));
10var _defineProperty2 = _interopRequireDefault(require("@babel/runtime/helpers/defineProperty"));
11var _typeof2 = _interopRequireDefault(require("@babel/runtime/helpers/typeof"));
12var _slicedToArray2 = _interopRequireDefault(require("@babel/runtime/helpers/slicedToArray"));
13var _objectWithoutProperties2 = _interopRequireDefault(require("@babel/runtime/helpers/objectWithoutProperties"));
14var _miniDecimal = _interopRequireWildcard(require("@rc-component/mini-decimal"));
15var _classnames = _interopRequireDefault(require("classnames"));
16var _rcInput = require("rc-input");
17var _useLayoutEffect = require("rc-util/lib/hooks/useLayoutEffect");
18var _ref = require("rc-util/lib/ref");
19var React = _interopRequireWildcard(require("react"));
20var _useCursor3 = _interopRequireDefault(require("./hooks/useCursor"));
21var _StepHandler = _interopRequireDefault(require("./StepHandler"));
22var _numberUtil = require("./utils/numberUtil");
23var _commonUtils = require("rc-input/lib/utils/commonUtils");
24var _useFrame = _interopRequireDefault(require("./hooks/useFrame"));
25var _excluded = ["prefixCls", "className", "style", "min", "max", "step", "defaultValue", "value", "disabled", "readOnly", "upHandler", "downHandler", "keyboard", "controls", "classNames", "stringMode", "parser", "formatter", "precision", "decimalSeparator", "onChange", "onInput", "onPressEnter", "onStep"],
26 _excluded2 = ["disabled", "style", "prefixCls", "value", "prefix", "suffix", "addonBefore", "addonAfter", "classes", "className", "classNames"];
27function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
28function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || _typeof3(obj) !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && 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; }
29/**
30 * We support `stringMode` which need handle correct type when user call in onChange
31 * format max or min value
32 * 1. if isInvalid return null
33 * 2. if precision is undefined, return decimal
34 * 3. format with precision
35 * I. if max > 0, round down with precision. Example: max= 3.5, precision=0 afterFormat: 3
36 * II. if max < 0, round up with precision. Example: max= -3.5, precision=0 afterFormat: -4
37 * III. if min > 0, round up with precision. Example: min= 3.5, precision=0 afterFormat: 4
38 * IV. if min < 0, round down with precision. Example: max= -3.5, precision=0 afterFormat: -3
39 */
40var getDecimalValue = function getDecimalValue(stringMode, decimalValue) {
41 if (stringMode || decimalValue.isEmpty()) {
42 return decimalValue.toString();
43 }
44 return decimalValue.toNumber();
45};
46var getDecimalIfValidate = function getDecimalIfValidate(value) {
47 var decimal = (0, _miniDecimal.default)(value);
48 return decimal.isInvalidate() ? null : decimal;
49};
50var InternalInputNumber = /*#__PURE__*/React.forwardRef(function (props, ref) {
51 var _clsx;
52 var _props$prefixCls = props.prefixCls,
53 prefixCls = _props$prefixCls === void 0 ? 'rc-input-number' : _props$prefixCls,
54 className = props.className,
55 style = props.style,
56 min = props.min,
57 max = props.max,
58 _props$step = props.step,
59 step = _props$step === void 0 ? 1 : _props$step,
60 defaultValue = props.defaultValue,
61 value = props.value,
62 disabled = props.disabled,
63 readOnly = props.readOnly,
64 upHandler = props.upHandler,
65 downHandler = props.downHandler,
66 keyboard = props.keyboard,
67 _props$controls = props.controls,
68 controls = _props$controls === void 0 ? true : _props$controls,
69 classNames = props.classNames,
70 stringMode = props.stringMode,
71 parser = props.parser,
72 formatter = props.formatter,
73 precision = props.precision,
74 decimalSeparator = props.decimalSeparator,
75 onChange = props.onChange,
76 onInput = props.onInput,
77 onPressEnter = props.onPressEnter,
78 onStep = props.onStep,
79 inputProps = (0, _objectWithoutProperties2.default)(props, _excluded);
80 var inputClassName = "".concat(prefixCls, "-input");
81 var inputRef = React.useRef(null);
82 var _React$useState = React.useState(false),
83 _React$useState2 = (0, _slicedToArray2.default)(_React$useState, 2),
84 focus = _React$useState2[0],
85 setFocus = _React$useState2[1];
86 var userTypingRef = React.useRef(false);
87 var compositionRef = React.useRef(false);
88 var shiftKeyRef = React.useRef(false);
89
90 // ============================ Value =============================
91 // Real value control
92 var _React$useState3 = React.useState(function () {
93 return (0, _miniDecimal.default)(value !== null && value !== void 0 ? value : defaultValue);
94 }),
95 _React$useState4 = (0, _slicedToArray2.default)(_React$useState3, 2),
96 decimalValue = _React$useState4[0],
97 setDecimalValue = _React$useState4[1];
98 function setUncontrolledDecimalValue(newDecimal) {
99 if (value === undefined) {
100 setDecimalValue(newDecimal);
101 }
102 }
103
104 // ====================== Parser & Formatter ======================
105 /**
106 * `precision` is used for formatter & onChange.
107 * It will auto generate by `value` & `step`.
108 * But it will not block user typing.
109 *
110 * Note: Auto generate `precision` is used for legacy logic.
111 * We should remove this since we already support high precision with BigInt.
112 *
113 * @param number Provide which number should calculate precision
114 * @param userTyping Change by user typing
115 */
116 var getPrecision = React.useCallback(function (numStr, userTyping) {
117 if (userTyping) {
118 return undefined;
119 }
120 if (precision >= 0) {
121 return precision;
122 }
123 return Math.max((0, _miniDecimal.getNumberPrecision)(numStr), (0, _miniDecimal.getNumberPrecision)(step));
124 }, [precision, step]);
125
126 // >>> Parser
127 var mergedParser = React.useCallback(function (num) {
128 var numStr = String(num);
129 if (parser) {
130 return parser(numStr);
131 }
132 var parsedStr = numStr;
133 if (decimalSeparator) {
134 parsedStr = parsedStr.replace(decimalSeparator, '.');
135 }
136
137 // [Legacy] We still support auto convert `$ 123,456` to `123456`
138 return parsedStr.replace(/[^\w.-]+/g, '');
139 }, [parser, decimalSeparator]);
140
141 // >>> Formatter
142 var inputValueRef = React.useRef('');
143 var mergedFormatter = React.useCallback(function (number, userTyping) {
144 if (formatter) {
145 return formatter(number, {
146 userTyping: userTyping,
147 input: String(inputValueRef.current)
148 });
149 }
150 var str = typeof number === 'number' ? (0, _miniDecimal.num2str)(number) : number;
151
152 // User typing will not auto format with precision directly
153 if (!userTyping) {
154 var mergedPrecision = getPrecision(str, userTyping);
155 if ((0, _miniDecimal.validateNumber)(str) && (decimalSeparator || mergedPrecision >= 0)) {
156 // Separator
157 var separatorStr = decimalSeparator || '.';
158 str = (0, _miniDecimal.toFixed)(str, separatorStr, mergedPrecision);
159 }
160 }
161 return str;
162 }, [formatter, getPrecision, decimalSeparator]);
163
164 // ========================== InputValue ==========================
165 /**
166 * Input text value control
167 *
168 * User can not update input content directly. It updates with follow rules by priority:
169 * 1. controlled `value` changed
170 * * [SPECIAL] Typing like `1.` should not immediately convert to `1`
171 * 2. User typing with format (not precision)
172 * 3. Blur or Enter trigger revalidate
173 */
174 var _React$useState5 = React.useState(function () {
175 var initValue = defaultValue !== null && defaultValue !== void 0 ? defaultValue : value;
176 if (decimalValue.isInvalidate() && ['string', 'number'].includes((0, _typeof2.default)(initValue))) {
177 return Number.isNaN(initValue) ? '' : initValue;
178 }
179 return mergedFormatter(decimalValue.toString(), false);
180 }),
181 _React$useState6 = (0, _slicedToArray2.default)(_React$useState5, 2),
182 inputValue = _React$useState6[0],
183 setInternalInputValue = _React$useState6[1];
184 inputValueRef.current = inputValue;
185
186 // Should always be string
187 function setInputValue(newValue, userTyping) {
188 setInternalInputValue(mergedFormatter(
189 // Invalidate number is sometime passed by external control, we should let it go
190 // Otherwise is controlled by internal interactive logic which check by userTyping
191 // You can ref 'show limited value when input is not focused' test for more info.
192 newValue.isInvalidate() ? newValue.toString(false) : newValue.toString(!userTyping), userTyping));
193 }
194
195 // >>> Max & Min limit
196 var maxDecimal = React.useMemo(function () {
197 return getDecimalIfValidate(max);
198 }, [max, precision]);
199 var minDecimal = React.useMemo(function () {
200 return getDecimalIfValidate(min);
201 }, [min, precision]);
202 var upDisabled = React.useMemo(function () {
203 if (!maxDecimal || !decimalValue || decimalValue.isInvalidate()) {
204 return false;
205 }
206 return maxDecimal.lessEquals(decimalValue);
207 }, [maxDecimal, decimalValue]);
208 var downDisabled = React.useMemo(function () {
209 if (!minDecimal || !decimalValue || decimalValue.isInvalidate()) {
210 return false;
211 }
212 return decimalValue.lessEquals(minDecimal);
213 }, [minDecimal, decimalValue]);
214
215 // Cursor controller
216 var _useCursor = (0, _useCursor3.default)(inputRef.current, focus),
217 _useCursor2 = (0, _slicedToArray2.default)(_useCursor, 2),
218 recordCursor = _useCursor2[0],
219 restoreCursor = _useCursor2[1];
220
221 // ============================= Data =============================
222 /**
223 * Find target value closet within range.
224 * e.g. [11, 28]:
225 * 3 => 11
226 * 23 => 23
227 * 99 => 28
228 */
229 var getRangeValue = function getRangeValue(target) {
230 // target > max
231 if (maxDecimal && !target.lessEquals(maxDecimal)) {
232 return maxDecimal;
233 }
234
235 // target < min
236 if (minDecimal && !minDecimal.lessEquals(target)) {
237 return minDecimal;
238 }
239 return null;
240 };
241
242 /**
243 * Check value is in [min, max] range
244 */
245 var isInRange = function isInRange(target) {
246 return !getRangeValue(target);
247 };
248
249 /**
250 * Trigger `onChange` if value validated and not equals of origin.
251 * Return the value that re-align in range.
252 */
253 var triggerValueUpdate = function triggerValueUpdate(newValue, userTyping) {
254 var updateValue = newValue;
255 var isRangeValidate = isInRange(updateValue) || updateValue.isEmpty();
256
257 // Skip align value when trigger value is empty.
258 // We just trigger onChange(null)
259 // This should not block user typing
260 if (!updateValue.isEmpty() && !userTyping) {
261 // Revert value in range if needed
262 updateValue = getRangeValue(updateValue) || updateValue;
263 isRangeValidate = true;
264 }
265 if (!readOnly && !disabled && isRangeValidate) {
266 var numStr = updateValue.toString();
267 var mergedPrecision = getPrecision(numStr, userTyping);
268 if (mergedPrecision >= 0) {
269 updateValue = (0, _miniDecimal.default)((0, _miniDecimal.toFixed)(numStr, '.', mergedPrecision));
270
271 // When to fixed. The value may out of min & max range.
272 // 4 in [0, 3.8] => 3.8 => 4 (toFixed)
273 if (!isInRange(updateValue)) {
274 updateValue = (0, _miniDecimal.default)((0, _miniDecimal.toFixed)(numStr, '.', mergedPrecision, true));
275 }
276 }
277
278 // Trigger event
279 if (!updateValue.equals(decimalValue)) {
280 setUncontrolledDecimalValue(updateValue);
281 onChange === null || onChange === void 0 ? void 0 : onChange(updateValue.isEmpty() ? null : getDecimalValue(stringMode, updateValue));
282
283 // Reformat input if value is not controlled
284 if (value === undefined) {
285 setInputValue(updateValue, userTyping);
286 }
287 }
288 return updateValue;
289 }
290 return decimalValue;
291 };
292
293 // ========================== User Input ==========================
294 var onNextPromise = (0, _useFrame.default)();
295
296 // >>> Collect input value
297 var collectInputValue = function collectInputValue(inputStr) {
298 recordCursor();
299
300 // Update inputValue in case input can not parse as number
301 // Refresh ref value immediately since it may used by formatter
302 inputValueRef.current = inputStr;
303 setInternalInputValue(inputStr);
304
305 // Parse number
306 if (!compositionRef.current) {
307 var finalValue = mergedParser(inputStr);
308 var finalDecimal = (0, _miniDecimal.default)(finalValue);
309 if (!finalDecimal.isNaN()) {
310 triggerValueUpdate(finalDecimal, true);
311 }
312 }
313
314 // Trigger onInput later to let user customize value if they want to handle something after onChange
315 onInput === null || onInput === void 0 ? void 0 : onInput(inputStr);
316
317 // optimize for chinese input experience
318 // https://github.com/ant-design/ant-design/issues/8196
319 onNextPromise(function () {
320 var nextInputStr = inputStr;
321 if (!parser) {
322 nextInputStr = inputStr.replace(/。/g, '.');
323 }
324 if (nextInputStr !== inputStr) {
325 collectInputValue(nextInputStr);
326 }
327 });
328 };
329
330 // >>> Composition
331 var onCompositionStart = function onCompositionStart() {
332 compositionRef.current = true;
333 };
334 var onCompositionEnd = function onCompositionEnd() {
335 compositionRef.current = false;
336 collectInputValue(inputRef.current.value);
337 };
338
339 // >>> Input
340 var onInternalInput = function onInternalInput(e) {
341 collectInputValue(e.target.value);
342 };
343
344 // ============================= Step =============================
345 var onInternalStep = function onInternalStep(up) {
346 var _inputRef$current;
347 // Ignore step since out of range
348 if (up && upDisabled || !up && downDisabled) {
349 return;
350 }
351
352 // Clear typing status since it may be caused by up & down key.
353 // We should sync with input value.
354 userTypingRef.current = false;
355 var stepDecimal = (0, _miniDecimal.default)(shiftKeyRef.current ? (0, _numberUtil.getDecupleSteps)(step) : step);
356 if (!up) {
357 stepDecimal = stepDecimal.negate();
358 }
359 var target = (decimalValue || (0, _miniDecimal.default)(0)).add(stepDecimal.toString());
360 var updatedValue = triggerValueUpdate(target, false);
361 onStep === null || onStep === void 0 ? void 0 : onStep(getDecimalValue(stringMode, updatedValue), {
362 offset: shiftKeyRef.current ? (0, _numberUtil.getDecupleSteps)(step) : step,
363 type: up ? 'up' : 'down'
364 });
365 (_inputRef$current = inputRef.current) === null || _inputRef$current === void 0 ? void 0 : _inputRef$current.focus();
366 };
367
368 // ============================ Flush =============================
369 /**
370 * Flush current input content to trigger value change & re-formatter input if needed
371 */
372 var flushInputValue = function flushInputValue(userTyping) {
373 var parsedValue = (0, _miniDecimal.default)(mergedParser(inputValue));
374 var formatValue = parsedValue;
375 if (!parsedValue.isNaN()) {
376 // Only validate value or empty value can be re-fill to inputValue
377 // Reassign the formatValue within ranged of trigger control
378 formatValue = triggerValueUpdate(parsedValue, userTyping);
379 } else {
380 formatValue = decimalValue;
381 }
382 if (value !== undefined) {
383 // Reset back with controlled value first
384 setInputValue(decimalValue, false);
385 } else if (!formatValue.isNaN()) {
386 // Reset input back since no validate value
387 setInputValue(formatValue, false);
388 }
389 };
390
391 // Solve the issue of the event triggering sequence when entering numbers in chinese input (Safari)
392 var onBeforeInput = function onBeforeInput() {
393 userTypingRef.current = true;
394 };
395 var onKeyDown = function onKeyDown(event) {
396 var key = event.key,
397 shiftKey = event.shiftKey;
398 userTypingRef.current = true;
399 shiftKeyRef.current = shiftKey;
400 if (key === 'Enter') {
401 if (!compositionRef.current) {
402 userTypingRef.current = false;
403 }
404 flushInputValue(false);
405 onPressEnter === null || onPressEnter === void 0 ? void 0 : onPressEnter(event);
406 }
407 if (keyboard === false) {
408 return;
409 }
410
411 // Do step
412 if (!compositionRef.current && ['Up', 'ArrowUp', 'Down', 'ArrowDown'].includes(key)) {
413 onInternalStep(key === 'Up' || key === 'ArrowUp');
414 event.preventDefault();
415 }
416 };
417 var onKeyUp = function onKeyUp() {
418 userTypingRef.current = false;
419 shiftKeyRef.current = false;
420 };
421
422 // >>> Focus & Blur
423 var onBlur = function onBlur() {
424 flushInputValue(false);
425 setFocus(false);
426 userTypingRef.current = false;
427 };
428
429 // ========================== Controlled ==========================
430 // Input by precision
431 (0, _useLayoutEffect.useLayoutUpdateEffect)(function () {
432 if (!decimalValue.isInvalidate()) {
433 setInputValue(decimalValue, false);
434 }
435 }, [precision]);
436
437 // Input by value
438 (0, _useLayoutEffect.useLayoutUpdateEffect)(function () {
439 var newValue = (0, _miniDecimal.default)(value);
440 setDecimalValue(newValue);
441 var currentParsedValue = (0, _miniDecimal.default)(mergedParser(inputValue));
442
443 // When user typing from `1.2` to `1.`, we should not convert to `1` immediately.
444 // But let it go if user set `formatter`
445 if (!newValue.equals(currentParsedValue) || !userTypingRef.current || formatter) {
446 // Update value as effect
447 setInputValue(newValue, userTypingRef.current);
448 }
449 }, [value]);
450
451 // ============================ Cursor ============================
452 (0, _useLayoutEffect.useLayoutUpdateEffect)(function () {
453 if (formatter) {
454 restoreCursor();
455 }
456 }, [inputValue]);
457
458 // ============================ Render ============================
459 return /*#__PURE__*/React.createElement("div", {
460 className: (0, _classnames.default)(prefixCls, classNames === null || classNames === void 0 ? void 0 : classNames.input, className, (_clsx = {}, (0, _defineProperty2.default)(_clsx, "".concat(prefixCls, "-focused"), focus), (0, _defineProperty2.default)(_clsx, "".concat(prefixCls, "-disabled"), disabled), (0, _defineProperty2.default)(_clsx, "".concat(prefixCls, "-readonly"), readOnly), (0, _defineProperty2.default)(_clsx, "".concat(prefixCls, "-not-a-number"), decimalValue.isNaN()), (0, _defineProperty2.default)(_clsx, "".concat(prefixCls, "-out-of-range"), !decimalValue.isInvalidate() && !isInRange(decimalValue)), _clsx)),
461 style: style,
462 onFocus: function onFocus() {
463 setFocus(true);
464 },
465 onBlur: onBlur,
466 onKeyDown: onKeyDown,
467 onKeyUp: onKeyUp,
468 onCompositionStart: onCompositionStart,
469 onCompositionEnd: onCompositionEnd,
470 onBeforeInput: onBeforeInput
471 }, controls && /*#__PURE__*/React.createElement(_StepHandler.default, {
472 prefixCls: prefixCls,
473 upNode: upHandler,
474 downNode: downHandler,
475 upDisabled: upDisabled,
476 downDisabled: downDisabled,
477 onStep: onInternalStep
478 }), /*#__PURE__*/React.createElement("div", {
479 className: "".concat(inputClassName, "-wrap")
480 }, /*#__PURE__*/React.createElement("input", (0, _extends2.default)({
481 autoComplete: "off",
482 role: "spinbutton",
483 "aria-valuemin": min,
484 "aria-valuemax": max,
485 "aria-valuenow": decimalValue.isInvalidate() ? null : decimalValue.toString(),
486 step: step
487 }, inputProps, {
488 ref: (0, _ref.composeRef)(inputRef, ref),
489 className: inputClassName,
490 value: inputValue,
491 onChange: onInternalInput,
492 disabled: disabled,
493 readOnly: readOnly
494 }))));
495});
496var InputNumber = /*#__PURE__*/React.forwardRef(function (props, ref) {
497 var disabled = props.disabled,
498 style = props.style,
499 prefixCls = props.prefixCls,
500 value = props.value,
501 prefix = props.prefix,
502 suffix = props.suffix,
503 addonBefore = props.addonBefore,
504 addonAfter = props.addonAfter,
505 classes = props.classes,
506 className = props.className,
507 classNames = props.classNames,
508 rest = (0, _objectWithoutProperties2.default)(props, _excluded2);
509 var inputFocusRef = React.useRef(null);
510 var focus = function focus(option) {
511 if (inputFocusRef.current) {
512 (0, _commonUtils.triggerFocus)(inputFocusRef.current, option);
513 }
514 };
515 return /*#__PURE__*/React.createElement(_rcInput.BaseInput, {
516 inputElement: /*#__PURE__*/React.createElement(InternalInputNumber, (0, _extends2.default)({
517 prefixCls: prefixCls,
518 disabled: disabled,
519 classNames: classNames,
520 ref: (0, _ref.composeRef)(inputFocusRef, ref)
521 }, rest)),
522 className: className,
523 triggerFocus: focus,
524 prefixCls: prefixCls,
525 value: value,
526 disabled: disabled,
527 style: style,
528 prefix: prefix,
529 suffix: suffix,
530 addonAfter: addonAfter,
531 addonBefore: addonBefore,
532 classes: classes,
533 classNames: classNames,
534 components: {
535 affixWrapper: 'div',
536 groupWrapper: 'div',
537 wrapper: 'div',
538 groupAddon: 'div'
539 }
540 });
541});
542InputNumber.displayName = 'InputNumber';
543var _default = InputNumber;
544exports.default = _default;
\No newline at end of file