@@ -11,10 +11,9 @@ import { type Decimal } from '@ui-kit/utils'
1111 *
1212 * @param value - The new input value to validate
1313 * @param current - The current input value to fall back to if validation fails
14- * @param allowNegative - Whether to allow negative numbers
1514 * @returns The validated and normalized input value, or the current value if invalid
1615 */
17- const sanitize = ( value : string , current : string , allowNegative : boolean ) : string => {
16+ const sanitize = ( value : string , current : string ) : string => {
1817 const normalizedValue = value . replace ( / , / g, '.' )
1918
2019 // If more than one decimal point, return the current value (ignore the change)
@@ -28,13 +27,8 @@ const sanitize = (value: string, current: string, allowNegative: boolean): strin
2827 return current
2928 }
3029
31- // If negative numbers are not allowed and value starts with minus, ignore the change
32- if ( ! allowNegative && minusIndex === 0 ) {
33- return current
34- }
35-
3630 // Check if it contains only valid characters (numbers, optional minus at start if allowed, and one optional decimal)
37- const pattern = allowNegative ? / ^ - ? [ 0 - 9 ] * \. ? [ 0 - 9 ] * ( [ e E ] [ - + ] ? [ 0 - 9 ] + ) ? $ / : / ^ [ 0 - 9 ] * \. ? [ 0 - 9 ] * ( [ e E ] [ - + ] ? [ 0 - 9 ] + ) ? $ /
31+ const pattern = / ^ - ? [ 0 - 9 ] * \. ? [ 0 - 9 ] * ( [ e E ] [ - + ] ? [ 0 - 9 ] + ) ? $ /
3832 return pattern . test ( normalizedValue ) ? normalizedValue : current
3933}
4034
@@ -72,8 +66,8 @@ type NumericTextFieldProps = Omit<TextFieldProps, 'type' | 'value' | 'onChange'
7266 min ?: Decimal
7367 /** Maximum allowed value (default: Infinity) */
7468 max ?: Decimal
75- /** Callback fired when the numeric value changes */
76- onChange ?: ( value : Decimal | undefined ) => void
69+ /** Callback fired when the numeric value changes, can be a temporary non decimal value like "5." or "-" */
70+ onChange ?: ( value : string | undefined ) => void
7771 /** Callback fired when the numeric is being submitted */
7872 onBlur ?: ( value : Decimal | undefined ) => void
7973}
@@ -82,27 +76,14 @@ export const NumericTextField = ({ value, min, max, onChange, onBlur, onFocus, .
8276 // Internal value that might be incomplete, like "4.".
8377 const [ inputValue , setInputValue ] = useState ( getDisplayValue ( value ) )
8478
85- const [ lastChangeValue , setLastChangeValue ] = useState ( value )
79+ const [ lastChangeValue , setLastChangeValue ] = useState < string | undefined > ( value )
8680 const [ lastBlurValue , setLastBlurValue ] = useState ( value )
8781
8882 // Update input value when value changes externally
8983 useEffect ( ( ) => {
9084 setInputValue ( getDisplayValue ( value ) )
9185 } , [ value ] )
9286
93- /**
94- * Converts a string input to a numeric value with optional clamping.
95- *
96- * @param validatedValue - The sanitized string input
97- * @param shouldClamp - Whether to apply min/max bounds
98- * @returns Numeric value, undefined for empty/invalid input
99- */
100- const parseAndClamp = ( validatedValue : string , { shouldClamp = false } : { shouldClamp ?: boolean } ) => {
101- if ( validatedValue === '' ) return undefined
102- const result = shouldClamp ? clamp ( validatedValue , min , max ) : new BigNumber ( validatedValue )
103- return result . toString ( ) as Decimal
104- }
105-
10687 return (
10788 < TextField
10889 { ...props }
@@ -120,19 +101,17 @@ export const NumericTextField = ({ value, min, max, onChange, onBlur, onFocus, .
120101 onFocus ?.( e )
121102 } }
122103 onChange = { ( e ) => {
123- const sanitizedValue = sanitize ( e . target . value , inputValue , min == null || + min < 0 )
104+ const sanitizedValue = sanitize ( e . target . value , inputValue )
124105 setInputValue ( sanitizedValue )
125-
126- const changedValue = parseAndClamp ( sanitizedValue , { shouldClamp : false } )
127-
128- if ( changedValue !== lastChangeValue ) {
129- onChange ?.( changedValue )
130- setLastChangeValue ( changedValue )
131- }
106+ onChange ?.( sanitizedValue )
107+ setLastChangeValue ( sanitizedValue )
132108 } }
133109 onBlur = { ( ) => {
134- // Replace a sole minus with just empty input as it's not really valid.
135- const finalValue = parseAndClamp ( inputValue === '-' ? '' : inputValue , { shouldClamp : true } )
110+ // Replace a sole invalid values with just empty input as they're not really valid.
111+ const invalidValues = [ '-' , '.' , ',' , '' ]
112+ const finalValue = invalidValues . includes ( inputValue )
113+ ? undefined
114+ : ( clamp ( inputValue , min , max ) . toString ( ) as Decimal )
136115 setInputValue ( getDisplayValue ( finalValue ) )
137116
138117 // Also emit the changed event, because due to clamping and such the final value
0 commit comments