UNPKG

22.1 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 * This will always flush input value for update.
372 * If it's invalidate, will fallback to last validate value.
373 */
374 var flushInputValue = function flushInputValue(userTyping) {
375 var parsedValue = (0, _miniDecimal.default)(mergedParser(inputValue));
376 var formatValue = parsedValue;
377 if (!parsedValue.isNaN()) {
378 // Only validate value or empty value can be re-fill to inputValue
379 // Reassign the formatValue within ranged of trigger control
380 formatValue = triggerValueUpdate(parsedValue, userTyping);
381 } else {
382 formatValue = triggerValueUpdate(decimalValue, userTyping);
383 }
384 if (value !== undefined) {
385 // Reset back with controlled value first
386 setInputValue(decimalValue, false);
387 } else if (!formatValue.isNaN()) {
388 // Reset input back since no validate value
389 setInputValue(formatValue, false);
390 }
391 };
392
393 // Solve the issue of the event triggering sequence when entering numbers in chinese input (Safari)
394 var onBeforeInput = function onBeforeInput() {
395 userTypingRef.current = true;
396 };
397 var onKeyDown = function onKeyDown(event) {
398 var key = event.key,
399 shiftKey = event.shiftKey;
400 userTypingRef.current = true;
401 shiftKeyRef.current = shiftKey;
402 if (key === 'Enter') {
403 if (!compositionRef.current) {
404 userTypingRef.current = false;
405 }
406 flushInputValue(false);
407 onPressEnter === null || onPressEnter === void 0 ? void 0 : onPressEnter(event);
408 }
409 if (keyboard === false) {
410 return;
411 }
412
413 // Do step
414 if (!compositionRef.current && ['Up', 'ArrowUp', 'Down', 'ArrowDown'].includes(key)) {
415 onInternalStep(key === 'Up' || key === 'ArrowUp');
416 event.preventDefault();
417 }
418 };
419 var onKeyUp = function onKeyUp() {
420 userTypingRef.current = false;
421 shiftKeyRef.current = false;
422 };
423
424 // >>> Focus & Blur
425 var onBlur = function onBlur() {
426 flushInputValue(false);
427 setFocus(false);
428 userTypingRef.current = false;
429 };
430
431 // ========================== Controlled ==========================
432 // Input by precision
433 (0, _useLayoutEffect.useLayoutUpdateEffect)(function () {
434 if (!decimalValue.isInvalidate()) {
435 setInputValue(decimalValue, false);
436 }
437 }, [precision]);
438
439 // Input by value
440 (0, _useLayoutEffect.useLayoutUpdateEffect)(function () {
441 var newValue = (0, _miniDecimal.default)(value);
442 setDecimalValue(newValue);
443 var currentParsedValue = (0, _miniDecimal.default)(mergedParser(inputValue));
444
445 // When user typing from `1.2` to `1.`, we should not convert to `1` immediately.
446 // But let it go if user set `formatter`
447 if (!newValue.equals(currentParsedValue) || !userTypingRef.current || formatter) {
448 // Update value as effect
449 setInputValue(newValue, userTypingRef.current);
450 }
451 }, [value]);
452
453 // ============================ Cursor ============================
454 (0, _useLayoutEffect.useLayoutUpdateEffect)(function () {
455 if (formatter) {
456 restoreCursor();
457 }
458 }, [inputValue]);
459
460 // ============================ Render ============================
461 return /*#__PURE__*/React.createElement("div", {
462 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)),
463 style: style,
464 onFocus: function onFocus() {
465 setFocus(true);
466 },
467 onBlur: onBlur,
468 onKeyDown: onKeyDown,
469 onKeyUp: onKeyUp,
470 onCompositionStart: onCompositionStart,
471 onCompositionEnd: onCompositionEnd,
472 onBeforeInput: onBeforeInput
473 }, controls && /*#__PURE__*/React.createElement(_StepHandler.default, {
474 prefixCls: prefixCls,
475 upNode: upHandler,
476 downNode: downHandler,
477 upDisabled: upDisabled,
478 downDisabled: downDisabled,
479 onStep: onInternalStep
480 }), /*#__PURE__*/React.createElement("div", {
481 className: "".concat(inputClassName, "-wrap")
482 }, /*#__PURE__*/React.createElement("input", (0, _extends2.default)({
483 autoComplete: "off",
484 role: "spinbutton",
485 "aria-valuemin": min,
486 "aria-valuemax": max,
487 "aria-valuenow": decimalValue.isInvalidate() ? null : decimalValue.toString(),
488 step: step
489 }, inputProps, {
490 ref: (0, _ref.composeRef)(inputRef, ref),
491 className: inputClassName,
492 value: inputValue,
493 onChange: onInternalInput,
494 disabled: disabled,
495 readOnly: readOnly
496 }))));
497});
498var InputNumber = /*#__PURE__*/React.forwardRef(function (props, ref) {
499 var disabled = props.disabled,
500 style = props.style,
501 prefixCls = props.prefixCls,
502 value = props.value,
503 prefix = props.prefix,
504 suffix = props.suffix,
505 addonBefore = props.addonBefore,
506 addonAfter = props.addonAfter,
507 classes = props.classes,
508 className = props.className,
509 classNames = props.classNames,
510 rest = (0, _objectWithoutProperties2.default)(props, _excluded2);
511 var inputFocusRef = React.useRef(null);
512 var focus = function focus(option) {
513 if (inputFocusRef.current) {
514 (0, _commonUtils.triggerFocus)(inputFocusRef.current, option);
515 }
516 };
517 return /*#__PURE__*/React.createElement(_rcInput.BaseInput, {
518 inputElement: /*#__PURE__*/React.createElement(InternalInputNumber, (0, _extends2.default)({
519 prefixCls: prefixCls,
520 disabled: disabled,
521 classNames: classNames,
522 ref: (0, _ref.composeRef)(inputFocusRef, ref)
523 }, rest)),
524 className: className,
525 triggerFocus: focus,
526 prefixCls: prefixCls,
527 value: value,
528 disabled: disabled,
529 style: style,
530 prefix: prefix,
531 suffix: suffix,
532 addonAfter: addonAfter,
533 addonBefore: addonBefore,
534 classes: classes,
535 classNames: classNames,
536 components: {
537 affixWrapper: 'div',
538 groupWrapper: 'div',
539 wrapper: 'div',
540 groupAddon: 'div'
541 }
542 });
543});
544InputNumber.displayName = 'InputNumber';
545var _default = InputNumber;
546exports.default = _default;
\No newline at end of file