@@ -2,7 +2,7 @@ import * as React from '@theia/core/shared/react';
2
2
import classnames from 'classnames' ;
3
3
4
4
interface SettingsStepInputProps {
5
- value : number ;
5
+ initialValue : number ;
6
6
setSettingsStateValue : ( value : number ) => void ;
7
7
step : number ;
8
8
maxValue : number ;
@@ -15,7 +15,7 @@ const SettingsStepInput: React.FC<SettingsStepInputProps> = (
15
15
props : SettingsStepInputProps
16
16
) => {
17
17
const {
18
- value ,
18
+ initialValue ,
19
19
setSettingsStateValue,
20
20
step,
21
21
maxValue,
@@ -24,18 +24,35 @@ const SettingsStepInput: React.FC<SettingsStepInputProps> = (
24
24
classNames,
25
25
} = props ;
26
26
27
+ const [ valueState , setValueState ] = React . useState < {
28
+ currentValue : number ;
29
+ isEmptyString : boolean ;
30
+ } > ( {
31
+ currentValue : initialValue ,
32
+ isEmptyString : false ,
33
+ } ) ;
34
+ const { currentValue, isEmptyString } = valueState ;
35
+
27
36
const clamp = ( value : number , min : number , max : number ) : number => {
28
37
return Math . min ( Math . max ( value , min ) , max ) ;
29
38
} ;
30
39
40
+ const resetToInitialState = ( ) : void => {
41
+ setValueState ( {
42
+ currentValue : initialValue ,
43
+ isEmptyString : false ,
44
+ } ) ;
45
+ } ;
46
+
31
47
const onStep = (
32
48
roundingOperation : 'ceil' | 'floor' ,
33
49
stepOperation : ( a : number , b : number ) => number
34
50
) : void => {
35
- const valueRoundedToScale = Math [ roundingOperation ] ( value / step ) * step ;
51
+ const valueRoundedToScale =
52
+ Math [ roundingOperation ] ( currentValue / step ) * step ;
36
53
const calculatedValue =
37
- valueRoundedToScale === value
38
- ? stepOperation ( value , step )
54
+ valueRoundedToScale === currentValue
55
+ ? stepOperation ( currentValue , step )
39
56
: valueRoundedToScale ;
40
57
const newValue = clamp ( calculatedValue , minValue , maxValue ) ;
41
58
@@ -52,33 +69,53 @@ const SettingsStepInput: React.FC<SettingsStepInputProps> = (
52
69
53
70
const onUserInput = ( event : React . ChangeEvent < HTMLInputElement > ) : void => {
54
71
const { value : eventValue } = event . target ;
72
+ setValueState ( {
73
+ currentValue : Number ( eventValue ) ,
74
+ isEmptyString : eventValue === '' ,
75
+ } ) ;
76
+ } ;
55
77
56
- if ( eventValue === '' ) {
57
- setSettingsStateValue ( 0 ) ;
78
+ /* Prevent the user from entering invalid values */
79
+ const onBlur = ( event : React . FocusEvent ) : void => {
80
+ if (
81
+ ( currentValue === initialValue && ! isEmptyString ) ||
82
+ event . currentTarget . contains ( event . relatedTarget as Node )
83
+ ) {
84
+ return ;
58
85
}
59
86
60
- const number = Number ( eventValue ) ;
61
-
62
- if ( ! isNaN ( number ) && number !== value ) {
63
- const newValue = clamp ( number , minValue , maxValue ) ;
64
-
65
- setSettingsStateValue ( newValue ) ;
87
+ const clampedValue = clamp ( currentValue , minValue , maxValue ) ;
88
+ if ( clampedValue === initialValue || isNaN ( currentValue ) || isEmptyString ) {
89
+ resetToInitialState ( ) ;
90
+ return ;
66
91
}
92
+
93
+ setSettingsStateValue ( clampedValue ) ;
67
94
} ;
68
95
69
- const upDisabled = value >= maxValue ;
70
- const downDisabled = value <= minValue ;
96
+ const valueIsNotWithinRange =
97
+ currentValue < minValue || currentValue > maxValue ;
98
+ const isDisabledException =
99
+ valueIsNotWithinRange || isEmptyString || isNaN ( currentValue ) ;
100
+
101
+ const upDisabled = isDisabledException || currentValue >= maxValue ;
102
+ const downDisabled = isDisabledException || currentValue <= minValue ;
71
103
72
104
return (
73
- < div className = "settings-step-input-container" >
105
+ < div className = "settings-step-input-container" onBlur = { onBlur } >
74
106
< input
75
107
className = { classnames ( 'settings-step-input-element' , classNames ?. input ) }
76
- value = { value . toString ( ) }
108
+ value = { isEmptyString ? '' : String ( currentValue ) }
77
109
onChange = { onUserInput }
78
110
type = "number"
79
111
pattern = "[0-9]+"
80
112
/>
81
- < div className = "settings-step-input-buttons-container" >
113
+ < div
114
+ className = { classnames (
115
+ 'settings-step-input-buttons-container' ,
116
+ classNames ?. buttonsContainer
117
+ ) }
118
+ >
82
119
< button
83
120
className = "settings-step-input-button settings-step-input-up-button"
84
121
disabled = { upDisabled }
0 commit comments