diff --git a/arduino-ide-extension/src/browser/dialogs/settings/settings-component.tsx b/arduino-ide-extension/src/browser/dialogs/settings/settings-component.tsx index bbc73b8c3..3138c7242 100644 --- a/arduino-ide-extension/src/browser/dialogs/settings/settings-component.tsx +++ b/arduino-ide-extension/src/browser/dialogs/settings/settings-component.tsx @@ -180,7 +180,8 @@ export class SettingsComponent extends React.Component<
diff --git a/arduino-ide-extension/src/browser/dialogs/settings/settings-step-input.tsx b/arduino-ide-extension/src/browser/dialogs/settings/settings-step-input.tsx index e6eef4b9c..1470ec192 100644 --- a/arduino-ide-extension/src/browser/dialogs/settings/settings-step-input.tsx +++ b/arduino-ide-extension/src/browser/dialogs/settings/settings-step-input.tsx @@ -2,7 +2,7 @@ import * as React from '@theia/core/shared/react'; import classnames from 'classnames'; interface SettingsStepInputProps { - value: number; + initialValue: number; setSettingsStateValue: (value: number) => void; step: number; maxValue: number; @@ -15,7 +15,7 @@ const SettingsStepInput: React.FC = ( props: SettingsStepInputProps ) => { const { - value, + initialValue, setSettingsStateValue, step, maxValue, @@ -24,18 +24,35 @@ const SettingsStepInput: React.FC = ( classNames, } = props; + const [valueState, setValueState] = React.useState<{ + currentValue: number; + isEmptyString: boolean; + }>({ + currentValue: initialValue, + isEmptyString: false, + }); + const { currentValue, isEmptyString } = valueState; + const clamp = (value: number, min: number, max: number): number => { return Math.min(Math.max(value, min), max); }; + const resetToInitialState = (): void => { + setValueState({ + currentValue: initialValue, + isEmptyString: false, + }); + }; + const onStep = ( roundingOperation: 'ceil' | 'floor', stepOperation: (a: number, b: number) => number ): void => { - const valueRoundedToScale = Math[roundingOperation](value / step) * step; + const valueRoundedToScale = + Math[roundingOperation](currentValue / step) * step; const calculatedValue = - valueRoundedToScale === value - ? stepOperation(value, step) + valueRoundedToScale === currentValue + ? stepOperation(currentValue, step) : valueRoundedToScale; const newValue = clamp(calculatedValue, minValue, maxValue); @@ -52,33 +69,53 @@ const SettingsStepInput: React.FC = ( const onUserInput = (event: React.ChangeEvent): void => { const { value: eventValue } = event.target; + setValueState({ + currentValue: Number(eventValue), + isEmptyString: eventValue === '', + }); + }; - if (eventValue === '') { - setSettingsStateValue(0); + /* Prevent the user from entering invalid values */ + const onBlur = (event: React.FocusEvent): void => { + if ( + (currentValue === initialValue && !isEmptyString) || + event.currentTarget.contains(event.relatedTarget as Node) + ) { + return; } - const number = Number(eventValue); - - if (!isNaN(number) && number !== value) { - const newValue = clamp(number, minValue, maxValue); - - setSettingsStateValue(newValue); + const clampedValue = clamp(currentValue, minValue, maxValue); + if (clampedValue === initialValue || isNaN(currentValue) || isEmptyString) { + resetToInitialState(); + return; } + + setSettingsStateValue(clampedValue); }; - const upDisabled = value >= maxValue; - const downDisabled = value <= minValue; + const valueIsNotWithinRange = + currentValue < minValue || currentValue > maxValue; + const isDisabledException = + valueIsNotWithinRange || isEmptyString || isNaN(currentValue); + + const upDisabled = isDisabledException || currentValue >= maxValue; + const downDisabled = isDisabledException || currentValue <= minValue; return ( -
+
-
+