Skip to content

Commit 32d904c

Browse files
Alberto Iannacconedavegarthsimpson
Alberto Iannaccone
andauthored
Let the user edit the font size settings with the keyboard (#1547)
* let the user edit the stepper input with keyboard * consider exceptions and fix styling * fix onBlur with empty strings * always set the internal state value * misc fixes Co-authored-by: David Simpson <[email protected]>
1 parent 5424dfc commit 32d904c

File tree

3 files changed

+74
-27
lines changed

3 files changed

+74
-27
lines changed

Diff for: arduino-ide-extension/src/browser/dialogs/settings/settings-component.tsx

+9-3
Original file line numberDiff line numberDiff line change
@@ -180,7 +180,8 @@ export class SettingsComponent extends React.Component<
180180
<div className="column">
181181
<div className="flex-line">
182182
<SettingsStepInput
183-
value={this.state.editorFontSize}
183+
key={`font-size-stepper-${String(this.state.editorFontSize)}`}
184+
initialValue={this.state.editorFontSize}
184185
setSettingsStateValue={this.setFontSize}
185186
step={fontSizeStep}
186187
maxValue={maxFontSize}
@@ -199,13 +200,18 @@ export class SettingsComponent extends React.Component<
199200
</label>
200201
<div>
201202
<SettingsStepInput
202-
value={scalePercentage}
203+
key={`scale-stepper-${String(scalePercentage)}`}
204+
initialValue={scalePercentage}
203205
setSettingsStateValue={this.setInterfaceScale}
204206
step={scaleStep}
205207
maxValue={maxScale}
206208
minValue={minScale}
207209
unitOfMeasure="%"
208-
classNames={{ input: 'theia-input small with-margin' }}
210+
classNames={{
211+
input: 'theia-input small with-margin',
212+
buttonsContainer:
213+
'settings-step-input-buttons-container-perc',
214+
}}
209215
/>
210216
</div>
211217
</div>

Diff for: arduino-ide-extension/src/browser/dialogs/settings/settings-step-input.tsx

+55-18
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import * as React from '@theia/core/shared/react';
22
import classnames from 'classnames';
33

44
interface SettingsStepInputProps {
5-
value: number;
5+
initialValue: number;
66
setSettingsStateValue: (value: number) => void;
77
step: number;
88
maxValue: number;
@@ -15,7 +15,7 @@ const SettingsStepInput: React.FC<SettingsStepInputProps> = (
1515
props: SettingsStepInputProps
1616
) => {
1717
const {
18-
value,
18+
initialValue,
1919
setSettingsStateValue,
2020
step,
2121
maxValue,
@@ -24,18 +24,35 @@ const SettingsStepInput: React.FC<SettingsStepInputProps> = (
2424
classNames,
2525
} = props;
2626

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+
2736
const clamp = (value: number, min: number, max: number): number => {
2837
return Math.min(Math.max(value, min), max);
2938
};
3039

40+
const resetToInitialState = (): void => {
41+
setValueState({
42+
currentValue: initialValue,
43+
isEmptyString: false,
44+
});
45+
};
46+
3147
const onStep = (
3248
roundingOperation: 'ceil' | 'floor',
3349
stepOperation: (a: number, b: number) => number
3450
): void => {
35-
const valueRoundedToScale = Math[roundingOperation](value / step) * step;
51+
const valueRoundedToScale =
52+
Math[roundingOperation](currentValue / step) * step;
3653
const calculatedValue =
37-
valueRoundedToScale === value
38-
? stepOperation(value, step)
54+
valueRoundedToScale === currentValue
55+
? stepOperation(currentValue, step)
3956
: valueRoundedToScale;
4057
const newValue = clamp(calculatedValue, minValue, maxValue);
4158

@@ -52,33 +69,53 @@ const SettingsStepInput: React.FC<SettingsStepInputProps> = (
5269

5370
const onUserInput = (event: React.ChangeEvent<HTMLInputElement>): void => {
5471
const { value: eventValue } = event.target;
72+
setValueState({
73+
currentValue: Number(eventValue),
74+
isEmptyString: eventValue === '',
75+
});
76+
};
5577

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;
5885
}
5986

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;
6691
}
92+
93+
setSettingsStateValue(clampedValue);
6794
};
6895

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;
71103

72104
return (
73-
<div className="settings-step-input-container">
105+
<div className="settings-step-input-container" onBlur={onBlur}>
74106
<input
75107
className={classnames('settings-step-input-element', classNames?.input)}
76-
value={value.toString()}
108+
value={isEmptyString ? '' : String(currentValue)}
77109
onChange={onUserInput}
78110
type="number"
79111
pattern="[0-9]+"
80112
/>
81-
<div className="settings-step-input-buttons-container">
113+
<div
114+
className={classnames(
115+
'settings-step-input-buttons-container',
116+
classNames?.buttonsContainer
117+
)}
118+
>
82119
<button
83120
className="settings-step-input-button settings-step-input-up-button"
84121
disabled={upDisabled}

Diff for: arduino-ide-extension/src/browser/style/settings-step-input.css

+10-6
Original file line numberDiff line numberDiff line change
@@ -2,17 +2,17 @@
22
position: relative
33
}
44

5-
.settings-step-input-element::-webkit-inner-spin-button,
5+
.settings-step-input-element::-webkit-inner-spin-button,
66
.settings-step-input-element::-webkit-outer-spin-button {
7-
-webkit-appearance: none;
8-
margin: 0;
7+
-webkit-appearance: none;
8+
margin: 0;
99
}
1010

1111
.settings-step-input-buttons-container {
1212
display: none;
1313
flex-direction: column;
1414
position: absolute;
15-
right: 14px;
15+
right: 0px;
1616
top: 50%;
1717
transform: translate(0px, -50%);
1818
height: calc(100% - 4px);
@@ -21,7 +21,11 @@
2121
background: var(--theia-input-background);
2222
}
2323

24-
.settings-step-input-container:hover > .settings-step-input-buttons-container {
24+
.settings-step-input-buttons-container-perc {
25+
right: 14px;
26+
}
27+
28+
.settings-step-input-container:hover>.settings-step-input-buttons-container {
2529
display: flex;
2630
}
2731

@@ -43,4 +47,4 @@
4347

4448
.settings-step-input-button:hover {
4549
background: rgba(128, 128, 128, 0.8);
46-
}
50+
}

0 commit comments

Comments
 (0)