mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-07-10 12:56:32 +00:00
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 <45690499+davegarthsimpson@users.noreply.github.com>
This commit is contained in:
parent
5424dfcf70
commit
32d904ca36
@ -180,7 +180,8 @@ export class SettingsComponent extends React.Component<
|
|||||||
<div className="column">
|
<div className="column">
|
||||||
<div className="flex-line">
|
<div className="flex-line">
|
||||||
<SettingsStepInput
|
<SettingsStepInput
|
||||||
value={this.state.editorFontSize}
|
key={`font-size-stepper-${String(this.state.editorFontSize)}`}
|
||||||
|
initialValue={this.state.editorFontSize}
|
||||||
setSettingsStateValue={this.setFontSize}
|
setSettingsStateValue={this.setFontSize}
|
||||||
step={fontSizeStep}
|
step={fontSizeStep}
|
||||||
maxValue={maxFontSize}
|
maxValue={maxFontSize}
|
||||||
@ -199,13 +200,18 @@ export class SettingsComponent extends React.Component<
|
|||||||
</label>
|
</label>
|
||||||
<div>
|
<div>
|
||||||
<SettingsStepInput
|
<SettingsStepInput
|
||||||
value={scalePercentage}
|
key={`scale-stepper-${String(scalePercentage)}`}
|
||||||
|
initialValue={scalePercentage}
|
||||||
setSettingsStateValue={this.setInterfaceScale}
|
setSettingsStateValue={this.setInterfaceScale}
|
||||||
step={scaleStep}
|
step={scaleStep}
|
||||||
maxValue={maxScale}
|
maxValue={maxScale}
|
||||||
minValue={minScale}
|
minValue={minScale}
|
||||||
unitOfMeasure="%"
|
unitOfMeasure="%"
|
||||||
classNames={{ input: 'theia-input small with-margin' }}
|
classNames={{
|
||||||
|
input: 'theia-input small with-margin',
|
||||||
|
buttonsContainer:
|
||||||
|
'settings-step-input-buttons-container-perc',
|
||||||
|
}}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -2,7 +2,7 @@ import * as React from '@theia/core/shared/react';
|
|||||||
import classnames from 'classnames';
|
import classnames from 'classnames';
|
||||||
|
|
||||||
interface SettingsStepInputProps {
|
interface SettingsStepInputProps {
|
||||||
value: number;
|
initialValue: number;
|
||||||
setSettingsStateValue: (value: number) => void;
|
setSettingsStateValue: (value: number) => void;
|
||||||
step: number;
|
step: number;
|
||||||
maxValue: number;
|
maxValue: number;
|
||||||
@ -15,7 +15,7 @@ const SettingsStepInput: React.FC<SettingsStepInputProps> = (
|
|||||||
props: SettingsStepInputProps
|
props: SettingsStepInputProps
|
||||||
) => {
|
) => {
|
||||||
const {
|
const {
|
||||||
value,
|
initialValue,
|
||||||
setSettingsStateValue,
|
setSettingsStateValue,
|
||||||
step,
|
step,
|
||||||
maxValue,
|
maxValue,
|
||||||
@ -24,18 +24,35 @@ const SettingsStepInput: React.FC<SettingsStepInputProps> = (
|
|||||||
classNames,
|
classNames,
|
||||||
} = props;
|
} = 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 => {
|
const clamp = (value: number, min: number, max: number): number => {
|
||||||
return Math.min(Math.max(value, min), max);
|
return Math.min(Math.max(value, min), max);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const resetToInitialState = (): void => {
|
||||||
|
setValueState({
|
||||||
|
currentValue: initialValue,
|
||||||
|
isEmptyString: false,
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
const onStep = (
|
const onStep = (
|
||||||
roundingOperation: 'ceil' | 'floor',
|
roundingOperation: 'ceil' | 'floor',
|
||||||
stepOperation: (a: number, b: number) => number
|
stepOperation: (a: number, b: number) => number
|
||||||
): void => {
|
): void => {
|
||||||
const valueRoundedToScale = Math[roundingOperation](value / step) * step;
|
const valueRoundedToScale =
|
||||||
|
Math[roundingOperation](currentValue / step) * step;
|
||||||
const calculatedValue =
|
const calculatedValue =
|
||||||
valueRoundedToScale === value
|
valueRoundedToScale === currentValue
|
||||||
? stepOperation(value, step)
|
? stepOperation(currentValue, step)
|
||||||
: valueRoundedToScale;
|
: valueRoundedToScale;
|
||||||
const newValue = clamp(calculatedValue, minValue, maxValue);
|
const newValue = clamp(calculatedValue, minValue, maxValue);
|
||||||
|
|
||||||
@ -52,33 +69,53 @@ const SettingsStepInput: React.FC<SettingsStepInputProps> = (
|
|||||||
|
|
||||||
const onUserInput = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
const onUserInput = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
||||||
const { value: eventValue } = event.target;
|
const { value: eventValue } = event.target;
|
||||||
|
setValueState({
|
||||||
if (eventValue === '') {
|
currentValue: Number(eventValue),
|
||||||
setSettingsStateValue(0);
|
isEmptyString: eventValue === '',
|
||||||
}
|
});
|
||||||
|
|
||||||
const number = Number(eventValue);
|
|
||||||
|
|
||||||
if (!isNaN(number) && number !== value) {
|
|
||||||
const newValue = clamp(number, minValue, maxValue);
|
|
||||||
|
|
||||||
setSettingsStateValue(newValue);
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
const upDisabled = value >= maxValue;
|
/* Prevent the user from entering invalid values */
|
||||||
const downDisabled = value <= minValue;
|
const onBlur = (event: React.FocusEvent): void => {
|
||||||
|
if (
|
||||||
|
(currentValue === initialValue && !isEmptyString) ||
|
||||||
|
event.currentTarget.contains(event.relatedTarget as Node)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const clampedValue = clamp(currentValue, minValue, maxValue);
|
||||||
|
if (clampedValue === initialValue || isNaN(currentValue) || isEmptyString) {
|
||||||
|
resetToInitialState();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
setSettingsStateValue(clampedValue);
|
||||||
|
};
|
||||||
|
|
||||||
|
const valueIsNotWithinRange =
|
||||||
|
currentValue < minValue || currentValue > maxValue;
|
||||||
|
const isDisabledException =
|
||||||
|
valueIsNotWithinRange || isEmptyString || isNaN(currentValue);
|
||||||
|
|
||||||
|
const upDisabled = isDisabledException || currentValue >= maxValue;
|
||||||
|
const downDisabled = isDisabledException || currentValue <= minValue;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="settings-step-input-container">
|
<div className="settings-step-input-container" onBlur={onBlur}>
|
||||||
<input
|
<input
|
||||||
className={classnames('settings-step-input-element', classNames?.input)}
|
className={classnames('settings-step-input-element', classNames?.input)}
|
||||||
value={value.toString()}
|
value={isEmptyString ? '' : String(currentValue)}
|
||||||
onChange={onUserInput}
|
onChange={onUserInput}
|
||||||
type="number"
|
type="number"
|
||||||
pattern="[0-9]+"
|
pattern="[0-9]+"
|
||||||
/>
|
/>
|
||||||
<div className="settings-step-input-buttons-container">
|
<div
|
||||||
|
className={classnames(
|
||||||
|
'settings-step-input-buttons-container',
|
||||||
|
classNames?.buttonsContainer
|
||||||
|
)}
|
||||||
|
>
|
||||||
<button
|
<button
|
||||||
className="settings-step-input-button settings-step-input-up-button"
|
className="settings-step-input-button settings-step-input-up-button"
|
||||||
disabled={upDisabled}
|
disabled={upDisabled}
|
||||||
|
@ -4,15 +4,15 @@
|
|||||||
|
|
||||||
.settings-step-input-element::-webkit-inner-spin-button,
|
.settings-step-input-element::-webkit-inner-spin-button,
|
||||||
.settings-step-input-element::-webkit-outer-spin-button {
|
.settings-step-input-element::-webkit-outer-spin-button {
|
||||||
-webkit-appearance: none;
|
-webkit-appearance: none;
|
||||||
margin: 0;
|
margin: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-step-input-buttons-container {
|
.settings-step-input-buttons-container {
|
||||||
display: none;
|
display: none;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
position: absolute;
|
position: absolute;
|
||||||
right: 14px;
|
right: 0px;
|
||||||
top: 50%;
|
top: 50%;
|
||||||
transform: translate(0px, -50%);
|
transform: translate(0px, -50%);
|
||||||
height: calc(100% - 4px);
|
height: calc(100% - 4px);
|
||||||
@ -21,7 +21,11 @@
|
|||||||
background: var(--theia-input-background);
|
background: var(--theia-input-background);
|
||||||
}
|
}
|
||||||
|
|
||||||
.settings-step-input-container:hover > .settings-step-input-buttons-container {
|
.settings-step-input-buttons-container-perc {
|
||||||
|
right: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.settings-step-input-container:hover>.settings-step-input-buttons-container {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user