diff --git a/src/packages/inputnumber/__tests__/inputnumber.spec.tsx b/src/packages/inputnumber/__tests__/inputnumber.spec.tsx index 606fec2100..cff6e24306 100644 --- a/src/packages/inputnumber/__tests__/inputnumber.spec.tsx +++ b/src/packages/inputnumber/__tests__/inputnumber.spec.tsx @@ -1,5 +1,5 @@ import * as React from 'react' -import { render, fireEvent, waitFor } from '@testing-library/react' +import { render, fireEvent, waitFor, act } from '@testing-library/react' import '@testing-library/jest-dom' import { InputNumber } from '../inputnumber' @@ -9,7 +9,7 @@ test('should render modelValue', () => { expect(container.querySelector('input')?.value).toBe('12') }) -test('should add step 2 when trigger click plus button', () => { +test('should add step 2 when trigger click plus button', async () => { const overlimit = vi.fn() const add = vi.fn() const change = vi.fn() @@ -23,13 +23,16 @@ test('should add step 2 when trigger click plus button', () => { /> ) const iconPlus = container.querySelectorAll('.nut-icon-Plus')[0] - fireEvent.click(iconPlus) + await act(async () => { + fireEvent.click(iconPlus) + }) + expect(overlimit).not.toBeCalled() - expect(add).toBeCalled() + expect(add).toHaveBeenCalled() expect(change.mock.calls[0][0]).toBe(3) }) -test('should minis step 2 when trigger click minis button', () => { +test('should minis step 2 when trigger click minis button', async () => { const overlimit = vi.fn() const reduce = vi.fn() const change = vi.fn() @@ -43,13 +46,15 @@ test('should minis step 2 when trigger click minis button', () => { /> ) const iconMinus = container.querySelectorAll('.nut-icon-Minus')[0] - fireEvent.click(iconMinus) + await act(async () => { + fireEvent.click(iconMinus) + }) expect(overlimit).not.toBeCalled() expect(reduce).toBeCalled() expect(change.mock.calls[0][0]).toBe(1) }) -test('should render max props', () => { +test('should render max props', async () => { const overlimit = vi.fn() const add = vi.fn() const change = vi.fn() @@ -64,13 +69,15 @@ test('should render max props', () => { /> ) const iconPlus = container.querySelectorAll('.nut-icon-Plus')[0] - fireEvent.click(iconPlus) + await act(async () => { + fireEvent.click(iconPlus) + }) expect(overlimit).toBeCalled() expect(add).toBeCalled() expect(change).not.toBeCalled() }) -test('should render min props', () => { +test('should render min props', async () => { const overlimit = vi.fn() const reduce = vi.fn() const change = vi.fn() @@ -85,7 +92,9 @@ test('should render min props', () => { /> ) const iconMinus = container.querySelectorAll('.nut-icon-Minus')[0] - fireEvent.click(iconMinus) + await act(async () => { + fireEvent.click(iconMinus) + }) expect(overlimit).toBeCalled() expect(reduce).toBeCalled() expect(change).not.toBeCalled() @@ -104,23 +113,27 @@ test('should not trigger click when disabled props to be true', () => { expect(container.querySelector('input')?.value).toBe('1') }) -test('should not focus input when readOnly props to be true', () => { +test('should not focus input when readOnly props to be true', async () => { const focus = vi.fn() const { container } = render( ) const iconMinus = container.querySelectorAll('.nut-icon-Minus')[0] - fireEvent.click(iconMinus) + await act(async () => { + fireEvent.click(iconMinus) + }) expect(container.querySelector('input')?.value).toBe('1') expect(focus).not.toBeCalled() }) -test('should render decimal when step props to be 0.2', () => { +test('should render decimal when step props to be 0.2', async () => { const { container } = render( ) const iconPlus = container.querySelectorAll('.nut-icon-Plus')[0] - fireEvent.click(iconPlus) + await act(async () => { + fireEvent.click(iconPlus) + }) expect(container.querySelector('input')?.value).toBe('2.2') }) @@ -158,19 +171,15 @@ test('allowEmpty', () => { }) }) -test('should overlimit when input', () => { - const change = vi.fn() +test('should overlimit when input', async () => { const overlimit = vi.fn() const { container } = render( - + ) const input = container.querySelectorAll('input')[0] input.value = '200' - fireEvent.input(input) - expect(change).toBeCalled() + await act(async () => { + fireEvent.input(input) + }) + expect(overlimit).toBeCalled() }) diff --git a/src/packages/inputnumber/demos/h5/demo8.tsx b/src/packages/inputnumber/demos/h5/demo8.tsx index e66949dddf..76c5beee75 100644 --- a/src/packages/inputnumber/demos/h5/demo8.tsx +++ b/src/packages/inputnumber/demos/h5/demo8.tsx @@ -6,22 +6,25 @@ const Demo8 = () => { const overlimit = (e: any) => { console.log('超出限制事件触发', e) } - const onChange = (value: string | number) => { + + const beforeChange = (value: number | string): Promise => { Toast.show({ icon: 'loading', content: '异步演示2秒后更改' }) - console.log('onChange', value) - setTimeout(() => { - setInputValue(Number(value)) - Toast.clear() - }, 2000) + + return new Promise((resolve) => { + setTimeout(() => { + Toast.clear() + resolve(true) + }, 500) + }) } + return ( setInputValue(Number(value))} onOverlimit={overlimit} - async /> ) } diff --git a/src/packages/inputnumber/demos/taro/demo8.tsx b/src/packages/inputnumber/demos/taro/demo8.tsx index 6b59ffac5e..d157476fdc 100644 --- a/src/packages/inputnumber/demos/taro/demo8.tsx +++ b/src/packages/inputnumber/demos/taro/demo8.tsx @@ -3,42 +3,45 @@ import { InputNumber, Toast } from '@nutui/nutui-react-taro' const Demo8 = () => { const [inputValue, setInputValue] = useState(0) - const [show, SetShow] = useState(false) - const [toastMsg, SetToastMsg] = useState('') - const [toastType, SetToastType] = useState('text') + const [show, setShow] = useState(false) + const [toastMsg, setToastMsg] = useState('') + const [toastType, setToastType] = useState('text') const toastShow = (msg: any, type: string) => { - SetToastMsg(msg) - SetToastType(type) - SetShow(true) + setToastMsg(msg) + setToastType(type) + setShow(true) } const overlimit = (e: any) => { console.log('超出限制事件触发', e) } - const onChange = (value: string | number) => { + + const beforeChange = (value: number | string): Promise => { toastShow('异步演示 2 秒后更改', 'loading') - console.log('onChange', value) - setTimeout(() => { - setInputValue(Number(value)) - SetShow(false) - }, 2000) + + return new Promise((resolve) => { + setTimeout(() => { + setShow(false) + resolve(true) + }, 500) + }) } + return ( <> setInputValue(Number(value))} onOverlimit={overlimit} - async /> { - SetShow(false) + setShow(false) }} /> diff --git a/src/packages/inputnumber/doc.en-US.md b/src/packages/inputnumber/doc.en-US.md index beb3542914..c241362a7d 100644 --- a/src/packages/inputnumber/doc.en-US.md +++ b/src/packages/inputnumber/doc.en-US.md @@ -121,9 +121,10 @@ Asynchronous modification through `change` event and `model-value` | digits | Set reserved decimal places | `string` \| `number` | `0` | | disabled | Disable all features | `boolean` | `false` | | readOnly | Read only status disables input box operation behavior | `boolean` | `false` | -| async | Support for asynchronous modification | `boolean` | `false` | -| select | Support deselect all text | `boolean` | `true` | +| ~~async~~`2.8.0` | Support for asynchronous modification | `boolean` | `false` | +| select`2.7.0` | Support deselect all text | `boolean` | `true` | | formatter | Specifies the format of the value displayed in the input box | `function(value: number \| string): string` | `-` | +| beforeChange`2.8.0` | Callback function before the input value changes, return false to prevent input, support returning Promise | `(value: number \| string) => boolean \| Promise` | `-` | | onPlus | Triggered when the Add button is clicked | `(e: MouseEvent) => void` | `-` | | onMinus | Triggered when the decrease button is clicked | `(e: MouseEvent) => void` | `-` | | onOverlimit | Triggered when an unavailable button is clicked | `(e: MouseEvent) => void` | `-` | diff --git a/src/packages/inputnumber/doc.md b/src/packages/inputnumber/doc.md index d2afd0b4d1..796702b0cf 100644 --- a/src/packages/inputnumber/doc.md +++ b/src/packages/inputnumber/doc.md @@ -121,9 +121,10 @@ import { InputNumber } from '@nutui/nutui-react' | digits | 设置保留的小数位 | `string` \| `number` | `0` | | disabled | 禁用所有功能 | `boolean` | `false` | | readOnly | 只读状态禁用输入框操作行为 | `boolean` | `false` | -| async | 支持异步修改 | `boolean` | `false` | -| select | 支持取消文本全选中 | `boolean` | `true` | +| ~~async~~`2.8.0` | 支持异步修改 | `boolean` | `false` | +| select`2.7.0` | 支持取消文本全选中 | `boolean` | `true` | | formatter | 指定输入框展示值的格式 | `function(value: number \| string): string` | `-` | +| beforeChange`2.8.0` | 输入值变化前的回调函数,返回 false 可阻止输入,支持返回 Promise | `(value: number \| string) => boolean \| Promise` | `-` | | onPlus | 点击增加按钮时触发 | `(e: MouseEvent) => void` | `-` | | onMinus | 点击减少按钮时触发 | `(e: MouseEvent) => void` | `-` | | onOverlimit | 点击不可用的按钮时触发 | `(e: MouseEvent) => void` | `-` | diff --git a/src/packages/inputnumber/doc.taro.md b/src/packages/inputnumber/doc.taro.md index fb9a619b1c..5c4c446245 100644 --- a/src/packages/inputnumber/doc.taro.md +++ b/src/packages/inputnumber/doc.taro.md @@ -114,8 +114,9 @@ import { InputNumber } from '@nutui/nutui-react-taro' | digits | 设置保留的小数位 | `string` \| `number` | `0` | | disabled | 禁用所有功能 | `boolean` | `false` | | readOnly | 只读状态禁用输入框操作行为 | `boolean` | `false` | -| async | 支持异步修改 | `boolean` | `false` | +| ~~async~~`2.8.0` | 支持异步修改 | `boolean` | `false` | | formatter | 指定输入框展示值的格式 | `function(value: number \| string): string` | `-` | +| beforeChange`2.8.0` | 输入值变化前的回调函数,返回 false 可阻止输入,支持返回 Promise | `(value: number \| string) => boolean \| Promise` | `-` | | onPlus | 点击增加按钮时触发 | `(e: MouseEvent) => void` | `-` | | onMinus | 点击减少按钮时触发 | `(e: MouseEvent) => void` | `-` | | onOverlimit | 点击不可用的按钮时触发 | `(e: MouseEvent) => void` | `-` | diff --git a/src/packages/inputnumber/doc.zh-TW.md b/src/packages/inputnumber/doc.zh-TW.md index 5a1cb78e4c..4d7f253b40 100644 --- a/src/packages/inputnumber/doc.zh-TW.md +++ b/src/packages/inputnumber/doc.zh-TW.md @@ -113,8 +113,10 @@ import { InputNumber } from '@nutui/nutui-react' | digits | 設置保留的小數位 | `string` \| `number` | `0` | | disabled | 禁用所有功能 | `boolean` | `false` | | readOnly | 只讀狀態禁用輸入框操作行為 | `boolean` | `false` | -| async | 支持異步修改 | `boolean` | `false` | +| ~~async~~`2.8.0` | 支持異步修改 | `boolean` | `false` | +| select`2.7.0` | 支持取消文本全选中 | `boolean` | `true` | | formatter | 指定輸入框展示值的格式 | `function(value: number \| string): string` | `-` | +| beforeChange`2.8.0` | 输入值变化前的回调函数,返回 false 可阻止输入,支持返回 Promise | `(value: number \| string) => boolean \| Promise` | `-` | | onPlus | 點擊增加按鈕時觸發 | `(e: MouseEvent) => void` | `-` | | onMinus | 點擊減少按鈕時觸發 | `(e: MouseEvent) => void` | `-` | | onOverlimit | 點擊不可用的按鈕時觸發 | `(e: MouseEvent) => void` | `-` | diff --git a/src/packages/inputnumber/inputnumber.taro.tsx b/src/packages/inputnumber/inputnumber.taro.tsx index 451057b9ff..3c3ab17b26 100644 --- a/src/packages/inputnumber/inputnumber.taro.tsx +++ b/src/packages/inputnumber/inputnumber.taro.tsx @@ -10,6 +10,7 @@ import classNames from 'classnames' import { InputProps } from '@tarojs/components' import { usePropsValue } from '@/utils/use-props-value' import { BasicComponent, ComponentDefaults } from '@/utils/typings' +import { bound } from '@/utils/bound' export interface InputNumberProps extends BasicComponent { value: number | string @@ -22,7 +23,6 @@ export interface InputNumberProps extends BasicComponent { readOnly: boolean step: number digits: number - async: boolean select: boolean formatter?: (value?: string | number) => string onPlus: (e: React.MouseEvent) => void @@ -30,6 +30,7 @@ export interface InputNumberProps extends BasicComponent { onOverlimit: (e: React.MouseEvent | ChangeEvent) => void onBlur: (e: React.FocusEvent) => void onFocus: (e: React.FocusEvent) => void + beforeChange: (value: number | string) => boolean | Promise onChange: ( param: string | number, e: React.MouseEvent | ChangeEvent @@ -43,11 +44,10 @@ const defaultProps = { allowEmpty: false, min: 1, max: 9999, - type: 'digit', step: 1, digits: 0, - async: false, select: true, + beforeChange: (value) => Promise.resolve(true), } as InputNumberProps const classPrefix = `nut-inputnumber` @@ -67,7 +67,6 @@ export const InputNumber: FunctionComponent< allowEmpty, digits, step, - async, select, className, style, @@ -78,6 +77,7 @@ export const InputNumber: FunctionComponent< onBlur, onFocus, onChange, + beforeChange, ...restProps } = { ...defaultProps, @@ -101,23 +101,16 @@ export const InputNumber: FunctionComponent< ? parseFloat(defaultValue) : defaultValue, finalValue: 0, - onChange: (value) => {}, }) - const bound = (value: number, min: number, max: number) => { - let res = value - if (min !== undefined) { - res = Math.max(Number(min), res) - } - if (max !== undefined) { - res = Math.min(Number(max), res) - } - return res - } + const format = (value: number | null | string): string => { if (value === null) return '' // 如果超过 min 或 max, 需要纠正 - if (typeof value === 'string') value = parseFloat(value) - const fixedValue = bound(value, Number(min), Number(max)) + const fixedValue = bound( + typeof value === 'string' ? parseFloat(value) : value, + Number(min), + Number(max) + ) if (formatter) { return formatter(fixedValue) } @@ -126,57 +119,53 @@ export const InputNumber: FunctionComponent< } return fixedValue.toString() } + const [inputValue, setInputValue] = useState(format(shadowValue)) useEffect(() => { - if (!focused && !async) { - setShadowValue(bound(Number(shadowValue), Number(min), Number(max))) + if (!focused) { setInputValue(format(shadowValue)) } }, [focused, shadowValue]) - useEffect(() => { - if (async) { - setShadowValue(bound(Number(value), Number(min), Number(max))) - setInputValue(format(value)) - } - }, [value]) - - const calcNextValue = (current: any, step: any, symbol: number) => { + const calcNextValue = (current: any, stepValue: any, symbol: number) => { const dig = digits + 1 - return ( - (parseFloat(current || '0') * dig + parseFloat(step) * dig * symbol) / dig - ) + const currentValue = parseFloat(current || '0') + const stepAmount = parseFloat(stepValue) * symbol + return (currentValue * dig + stepAmount * dig) / dig } - const update = (negative: boolean, e: React.MouseEvent) => { - if (step !== undefined) { - const shouldOverBoundary = calcNextValue( - shadowValue, - step, - negative ? -1 : 1 - ) - const nextValue = bound(shouldOverBoundary, Number(min), Number(max)) - setShadowValue(nextValue) - if ( - negative - ? shouldOverBoundary < Number(min) - : shouldOverBoundary > Number(max) - ) { - onOverlimit?.(e) - } else { - onChange?.(nextValue, e) - } + + const update = async (negative: boolean, e: React.MouseEvent) => { + if (step === undefined) return + negative ? onMinus?.(e) : onPlus?.(e) + + const shouldOverBoundary = calcNextValue( + shadowValue, + step, + negative ? -1 : 1 + ) + const maybeResume = await beforeChange(Number(shouldOverBoundary)) + if (!maybeResume) return + + const nextValue = bound(shouldOverBoundary, Number(min), Number(max)) + setShadowValue(nextValue) + if ( + negative + ? shouldOverBoundary < Number(min) + : shouldOverBoundary > Number(max) + ) { + onOverlimit?.(e) + } else { + onChange?.(nextValue, e) } } - const handleReduce = (e: React.MouseEvent) => { + const handleReduce = async (e: React.MouseEvent) => { if (disabled) return - onMinus?.(e) - update(true, e) + await update(true, e) } - const handlePlus = (e: React.MouseEvent) => { + const handlePlus = async (e: React.MouseEvent) => { if (disabled) return - onPlus?.(e) - update(false, e) + await update(false, e) } const parseValue = (text: string) => { @@ -184,38 +173,26 @@ export const InputNumber: FunctionComponent< if (text === '-') return null return text } - const clampValue = (valueStr: string | null) => { - if (valueStr === null) return defaultValue - const val = Number(parseFloat(valueStr || '0').toFixed(digits)) - return Math.max(Number(min), Math.min(Number(max), val)) - } - const handleValueChange = ( - valueStr: string | null, - e: React.ChangeEvent - ) => { - const val = clampValue(valueStr) - // input暂不触发onOverlimit - // if (val !== Number(e.target.value)) { - // onOverlimit?.(e) - // } - if (val !== Number(shadowValue)) { - onChange?.(val, e) - } - } - const handleInputChange = (e: ChangeEvent) => { + const handleInputChange = async (e: React.ChangeEvent) => { // 设置 input 值, 在 blur 时格式化 setInputValue(e.target.value) const valueStr = parseValue(e.target.value) - if (valueStr === null) { - if (allowEmpty) { - setShadowValue(null) - } else { - setShadowValue(defaultValue) - } + const maybeResume = await beforeChange(Number(valueStr)) + if (!maybeResume) return + + setShadowValue( + // eslint-disable-next-line no-nested-ternary + valueStr === null ? (allowEmpty ? null : defaultValue) : valueStr + ) + + if ( + valueStr !== null && + (Number(valueStr) < Number(min) || Number(valueStr) > Number(max)) + ) { + onOverlimit?.(e) } else { - setShadowValue(clampValue(valueStr) as any) + onChange?.(parseFloat(valueStr || '0').toFixed(digits), e) } - !async && handleValueChange(valueStr, e) } const handleFocus = (e: React.FocusEvent) => { setFocused(true) @@ -228,18 +205,9 @@ export const InputNumber: FunctionComponent< } const handleBlur = (e: React.FocusEvent) => { setFocused(false) - onBlur?.(e) + onBlur && onBlur(e) const valueStr = parseValue(e.target.value) - if (valueStr === null) { - if (allowEmpty) { - setShadowValue(null) - } else { - setShadowValue(defaultValue) - } - } else { - setShadowValue(clampValue(valueStr) as any) - } - async && handleValueChange(valueStr, e) + onChange?.(parseFloat(valueStr || '0').toFixed(digits) as any, e) } return ( @@ -252,8 +220,6 @@ export const InputNumber: FunctionComponent< /> string onPlus: (e: React.MouseEvent) => void @@ -28,6 +28,7 @@ export interface InputNumberProps extends BasicComponent { onOverlimit: (e: React.MouseEvent | ChangeEvent) => void onBlur: (e: React.FocusEvent) => void onFocus: (e: React.FocusEvent) => void + beforeChange: (value: number | string) => boolean | Promise onChange: ( param: string | number, e: React.MouseEvent | ChangeEvent @@ -43,8 +44,8 @@ const defaultProps = { max: 9999, step: 1, digits: 0, - async: false, select: true, + beforeChange: (value) => Promise.resolve(true), } as InputNumberProps const classPrefix = `nut-inputnumber` @@ -63,7 +64,6 @@ export const InputNumber: FunctionComponent< allowEmpty, digits, step, - async, select, className, style, @@ -74,6 +74,7 @@ export const InputNumber: FunctionComponent< onBlur, onFocus, onChange, + beforeChange, ...restProps } = { ...defaultProps, @@ -97,23 +98,16 @@ export const InputNumber: FunctionComponent< ? parseFloat(defaultValue) : defaultValue, finalValue: 0, - onChange: (value) => {}, }) - const bound = (value: number, min: number, max: number) => { - let res = value - if (min !== undefined) { - res = Math.max(Number(min), res) - } - if (max !== undefined) { - res = Math.min(Number(max), res) - } - return res - } + const format = (value: number | null | string): string => { if (value === null) return '' // 如果超过 min 或 max, 需要纠正 - if (typeof value === 'string') value = parseFloat(value) - const fixedValue = bound(value, Number(min), Number(max)) + const fixedValue = bound( + typeof value === 'string' ? parseFloat(value) : value, + Number(min), + Number(max) + ) if (formatter) { return formatter(fixedValue) } @@ -122,57 +116,53 @@ export const InputNumber: FunctionComponent< } return fixedValue.toString() } + const [inputValue, setInputValue] = useState(format(shadowValue)) useEffect(() => { - if (!focused && !async) { - setShadowValue(bound(Number(shadowValue), Number(min), Number(max))) + if (!focused) { setInputValue(format(shadowValue)) } }, [focused, shadowValue]) - useEffect(() => { - if (async) { - setShadowValue(bound(Number(value), Number(min), Number(max))) - setInputValue(format(value)) - } - }, [value]) - - const calcNextValue = (current: any, step: any, symbol: number) => { + const calcNextValue = (current: any, stepValue: any, symbol: number) => { const dig = digits + 1 - return ( - (parseFloat(current || '0') * dig + parseFloat(step) * dig * symbol) / dig - ) + const currentValue = parseFloat(current || '0') + const stepAmount = parseFloat(stepValue) * symbol + return (currentValue * dig + stepAmount * dig) / dig } - const update = (negative: boolean, e: React.MouseEvent) => { - if (step !== undefined) { - const shouldOverBoundary = calcNextValue( - shadowValue, - step, - negative ? -1 : 1 - ) - const nextValue = bound(shouldOverBoundary, Number(min), Number(max)) - setShadowValue(nextValue) - if ( - negative - ? shouldOverBoundary < Number(min) - : shouldOverBoundary > Number(max) - ) { - onOverlimit?.(e) - } else { - onChange?.(nextValue, e) - } + + const update = async (negative: boolean, e: React.MouseEvent) => { + if (step === undefined) return + negative ? onMinus?.(e) : onPlus?.(e) + + const shouldOverBoundary = calcNextValue( + shadowValue, + step, + negative ? -1 : 1 + ) + const maybeResume = await beforeChange(Number(shouldOverBoundary)) + if (!maybeResume) return + + const nextValue = bound(shouldOverBoundary, Number(min), Number(max)) + setShadowValue(nextValue) + if ( + negative + ? shouldOverBoundary < Number(min) + : shouldOverBoundary > Number(max) + ) { + onOverlimit?.(e) + } else { + onChange?.(nextValue, e) } } - const handleReduce = (e: React.MouseEvent) => { + const handleReduce = async (e: React.MouseEvent) => { if (disabled) return - onMinus?.(e) - update(true, e) + await update(true, e) } - const handlePlus = (e: React.MouseEvent) => { + const handlePlus = async (e: React.MouseEvent) => { if (disabled) return - onPlus?.(e) - update(false, e) + await update(false, e) } const parseValue = (text: string) => { @@ -180,38 +170,26 @@ export const InputNumber: FunctionComponent< if (text === '-') return null return text } - const clampValue = (valueStr: string | null) => { - if (valueStr === null) return defaultValue - const val = Number(parseFloat(valueStr || '0').toFixed(digits)) - return Math.max(Number(min), Math.min(Number(max), val)) - } - const handleValueChange = ( - valueStr: string | null, - e: React.ChangeEvent - ) => { - const val = clampValue(valueStr) - // input暂不触发onOverlimit - // if (val !== Number(e.target.value)) { - // onOverlimit?.(e) - // } - if (val !== Number(shadowValue)) { - onChange?.(val, e) - } - } - const handleInputChange = (e: React.ChangeEvent) => { + const handleInputChange = async (e: React.ChangeEvent) => { // 设置 input 值, 在 blur 时格式化 setInputValue(e.target.value) const valueStr = parseValue(e.target.value) - if (valueStr === null) { - if (allowEmpty) { - setShadowValue(null) - } else { - setShadowValue(defaultValue) - } + const maybeResume = await beforeChange(Number(valueStr)) + if (!maybeResume) return + + setShadowValue( + // eslint-disable-next-line no-nested-ternary + valueStr === null ? (allowEmpty ? null : defaultValue) : valueStr + ) + + if ( + valueStr !== null && + (Number(valueStr) < Number(min) || Number(valueStr) > Number(max)) + ) { + onOverlimit?.(e) } else { - setShadowValue(clampValue(valueStr) as any) + onChange?.(parseFloat(valueStr || '0').toFixed(digits), e) } - !async && handleValueChange(valueStr, e) } const handleFocus = (e: React.FocusEvent) => { setFocused(true) @@ -224,18 +202,9 @@ export const InputNumber: FunctionComponent< } const handleBlur = (e: React.FocusEvent) => { setFocused(false) - onBlur?.(e) + onBlur && onBlur(e) const valueStr = parseValue(e.target.value) - if (valueStr === null) { - if (allowEmpty) { - setShadowValue(null) - } else { - setShadowValue(defaultValue) - } - } else { - setShadowValue(clampValue(valueStr) as any) - } - async && handleValueChange(valueStr, e) + onChange?.(parseFloat(valueStr || '0').toFixed(digits) as any, e) } return (