UNPKG

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