Skip to content

Commit 1c0804c

Browse files
author
wanghao
committed
fix: should format max/min with precision
1 parent d65b0dc commit 1c0804c

File tree

4 files changed

+105
-10
lines changed

4 files changed

+105
-10
lines changed

src/InputNumber.tsx

Lines changed: 36 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,15 @@ import * as React from 'react';
22
import classNames from 'classnames';
33
import KeyCode from 'rc-util/lib/KeyCode';
44
import { composeRef } from 'rc-util/lib/ref';
5-
import getMiniDecimal, { DecimalClass, toFixed, ValueType } from './utils/MiniDecimal';
5+
import getMiniDecimal, {
6+
DecimalClass,
7+
roundDownUnsignedDecimal,
8+
roundUpUnsignedDecimal,
9+
toFixed,
10+
ValueType
11+
} from './utils/MiniDecimal';
612
import StepHandler from './StepHandler';
7-
import { getNumberPrecision, num2str, validateNumber } from './utils/numberUtil';
13+
import {getNumberPrecision, num2str, trimNumber, validateNumber} from './utils/numberUtil';
814
import useCursor from './hooks/useCursor';
915
import useUpdateEffect from './hooks/useUpdateEffect';
1016
import useFrame from './hooks/useFrame';
@@ -20,9 +26,32 @@ const getDecimalValue = (stringMode: boolean, decimalValue: DecimalClass) => {
2026
return decimalValue.toNumber();
2127
};
2228

23-
const getDecimalIfValidate = (value: ValueType) => {
29+
/**
30+
* format max or min value
31+
* 1. if isInvalid return null
32+
* 2. if precision is undefined, return decimal
33+
* 3. format with precision
34+
* I. if max > 0, round down with precision. Example: max= 3.5, precision=0 afterFormat: 3
35+
* II. if max < 0, round up with precision. Example: max= -3.5, precision=0 afterFormat: -4
36+
* III. if min > 0, round up with precision. Example: min= 3.5, precision=0 afterFormat: 4
37+
* IV. if min < 0, round down with precision. Example: max= -3.5, precision=0 afterFormat: -3
38+
*/
39+
const getDecimalIfValidate = (value: ValueType, precision: number | undefined, isMax?: boolean) => {
2440
const decimal = getMiniDecimal(value);
25-
return decimal.isInvalidate() ? null : decimal;
41+
if (decimal.isInvalidate()) {
42+
return null;
43+
}
44+
if (precision === undefined) {
45+
return decimal;
46+
}
47+
const {negative, integerStr, decimalStr, negativeStr} = trimNumber(decimal.toString())
48+
49+
const unSignedNumberStr = integerStr +'.' + decimalStr
50+
if ((isMax && !negative) || (!isMax && negative)) {
51+
return getMiniDecimal(negativeStr + roundDownUnsignedDecimal(unSignedNumberStr, precision))
52+
} else {
53+
return getMiniDecimal(negativeStr + roundUpUnsignedDecimal(unSignedNumberStr, precision))
54+
}
2655
};
2756

2857
export interface InputNumberProps<T extends ValueType = ValueType>
@@ -232,8 +261,8 @@ const InputNumber = React.forwardRef(
232261
}
233262

234263
// >>> Max & Min limit
235-
const maxDecimal = React.useMemo(() => getDecimalIfValidate(max), [max]);
236-
const minDecimal = React.useMemo(() => getDecimalIfValidate(min), [min]);
264+
const maxDecimal = React.useMemo(() => getDecimalIfValidate(max, precision, true), [max, precision]);
265+
const minDecimal = React.useMemo(() => getDecimalIfValidate(min, precision, false), [min, precision]);
237266

238267
const upDisabled = React.useMemo(() => {
239268
if (!maxDecimal || !decimalValue || decimalValue.isInvalidate()) {
@@ -504,7 +533,7 @@ const InputNumber = React.forwardRef(
504533
[`${prefixCls}-not-a-number`]: decimalValue.isNaN(),
505534
[`${prefixCls}-out-of-range`]: !decimalValue.isInvalidate() && !isInRange(decimalValue),
506535
})}
507-
style={style}
536+
style={{...style, color: 'red'}}
508537
onFocus={() => {
509538
setFocus(true);
510539
}}

src/utils/MiniDecimal.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
/* eslint-disable max-classes-per-file */
22

3-
import { getNumberPrecision, isE, num2str, trimNumber, validateNumber } from './numberUtil';
4-
import { supportBigInt } from './supportUtil';
3+
import {getNumberPrecision, isE, num2str, trimNumber, validateNumber} from './numberUtil';
4+
import {supportBigInt} from './supportUtil';
55

66
export type ValueType = string | number;
77

@@ -264,6 +264,31 @@ export default function getMiniDecimal(value: ValueType): DecimalClass {
264264
return new NumberDecimal(value);
265265
}
266266

267+
/**
268+
* round up an unsigned number str, like: 1.4 -> 2, 1.5 -> 2
269+
*/
270+
export function roundUpUnsignedDecimal(numStr: string, precision: number) {
271+
const {integerStr, decimalStr} = trimNumber(numStr);
272+
const advancedDecimal = getMiniDecimal(integerStr + '.' + decimalStr).add(
273+
`0.${'0'.repeat(precision)}${5}`,
274+
);
275+
return toFixed(advancedDecimal.toString(), '.', precision);
276+
}
277+
278+
/**
279+
* round up an unsigned number str, like: 1.4 -> 1, 1.5 -> 1
280+
*/
281+
export function roundDownUnsignedDecimal(numStr: string, precision: number) {
282+
const {negativeStr, integerStr, decimalStr} = trimNumber(numStr);
283+
const numberWithoutDecimal = `${negativeStr}${integerStr}`;
284+
if (precision === 0) {
285+
return integerStr;
286+
}
287+
return `${numberWithoutDecimal}.${decimalStr
288+
.padEnd(precision, '0')
289+
.slice(0, precision)}`;
290+
}
291+
267292
/**
268293
* Align the logic of toFixed to around like 1.5 => 2
269294
*/

tests/input.test.tsx

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,27 @@ describe('InputNumber.Input', () => {
5959
expect(wrapper.getInputValue()).toEqual('-98');
6060
});
6161

62+
it('negative min with higher precision', () => {
63+
const wrapper = prepareWrapper('-4', {min: -3.5, precision: 0});
64+
expect(wrapper.getInputValue()).toEqual('-3');
65+
});
66+
67+
it('positive min with higher precision', () => {
68+
const wrapper = prepareWrapper('4', {min: 3.5, precision: 0});
69+
expect(wrapper.getInputValue()).toEqual('4');
70+
});
71+
72+
it('negative max with higher precision', () => {
73+
const wrapper = prepareWrapper('-4', {max: -3.5, precision: 0});
74+
expect(wrapper.getInputValue()).toEqual('-4');
75+
});
76+
77+
it('positive max with higher precision', () => {
78+
const wrapper = prepareWrapper('4', {max: 3.5, precision: 0});
79+
expect(wrapper.getInputValue()).toEqual('3');
80+
});
81+
82+
6283
// https://github.com/ant-design/ant-design/issues/9439
6384
it('input negative zero', () => {
6485
const wrapper = prepareWrapper('-0', {}, true);

tests/util.test.tsx

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,8 +2,10 @@ import getMiniDecimal, {
22
BigIntDecimal,
33
DecimalClass,
44
NumberDecimal,
5-
ValueType,
5+
roundDownUnsignedDecimal,
6+
roundUpUnsignedDecimal,
67
toFixed,
8+
ValueType,
79
} from '../src/utils/MiniDecimal';
810

911
jest.mock('../src/utils/supportUtil');
@@ -150,5 +152,23 @@ describe('InputNumber.Util', () => {
150152
// expect(toFixed('77.88', '.', 1)).toEqual('77.9');
151153
expect(toFixed('-77.88', '.', 1)).toEqual('-77.9');
152154
});
155+
156+
157+
it('round down', () => {
158+
expect(roundDownUnsignedDecimal('77.89', 1)).toEqual('77.8');
159+
expect(roundDownUnsignedDecimal('77.1', 2)).toEqual('77.10');
160+
expect(roundDownUnsignedDecimal('77.81', 1)).toEqual('77.8');
161+
expect(roundDownUnsignedDecimal('77.50', 1)).toEqual('77.5');
162+
expect(roundDownUnsignedDecimal('77.5999', 0)).toEqual('77');
163+
expect(roundDownUnsignedDecimal('77.0001', 0)).toEqual('77');
164+
})
165+
it('round up', () => {
166+
expect(roundUpUnsignedDecimal('77.89', 1)).toEqual('77.9');
167+
expect(roundUpUnsignedDecimal('77.81', 1)).toEqual('77.9');
168+
169+
expect(roundUpUnsignedDecimal('77.89', 0)).toEqual('78');
170+
expect(roundUpUnsignedDecimal('77.599', 0)).toEqual('78');
171+
expect(roundUpUnsignedDecimal('77.01', 0)).toEqual('78');
172+
})
153173
});
154174
});

0 commit comments

Comments
 (0)