Add typing support to steppers (#1209)

* add typing support to steppers

* logic cleanup

* misc cleanup

* account for lack of unmount
This commit is contained in:
David Simpson 2022-07-19 13:07:39 +02:00 committed by GitHub
parent 119dfa78d9
commit 1f7c2eb52c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 223 additions and 33 deletions

View File

@ -21,7 +21,15 @@ import {
AsyncLocalizationProvider,
LanguageInfo,
} from '@theia/core/lib/common/i18n/localization';
import SettingsStepInput from './settings-step-input';
const maxScale = 200;
const minScale = -100;
const scaleStep = 20;
const maxFontSize = 72;
const minFontSize = 0;
const fontSizeStep = 2;
export class SettingsComponent extends React.Component<
SettingsComponent.Props,
SettingsComponent.State
@ -88,6 +96,8 @@ export class SettingsComponent extends React.Component<
}
protected renderSettings(): React.ReactNode {
const scalePercentage = 100 + this.state.interfaceScale * 20;
return (
<div className="content noselect">
{nls.localize(
@ -160,14 +170,13 @@ export class SettingsComponent extends React.Component<
</div>
<div className="column">
<div className="flex-line">
<input
className="theia-input small"
type="number"
step={1}
pattern="[0-9]+"
onKeyDown={this.numbersOnlyKeyDown}
<SettingsStepInput
value={this.state.editorFontSize}
onChange={this.editorFontSizeDidChange}
setSettingsStateValue={this.setFontSize}
step={fontSizeStep}
maxValue={maxFontSize}
minValue={minFontSize}
classNames={{ input: 'theia-input small' }}
/>
</div>
<div className="flex-line">
@ -179,14 +188,13 @@ export class SettingsComponent extends React.Component<
/>
{nls.localize('arduino/preferences/automatic', 'Automatic')}
</label>
<input
className="theia-input small with-margin"
type="number"
step={20}
pattern="[0-9]+"
onKeyDown={this.noopKeyDown}
value={100 + this.state.interfaceScale * 20}
onChange={this.interfaceScaleDidChange}
<SettingsStepInput
value={scalePercentage}
setSettingsStateValue={this.setInterfaceScale}
step={scaleStep}
maxValue={maxScale}
minValue={minScale}
classNames={{ input: 'theia-input small with-margin' }}
/>
%
</div>
@ -516,13 +524,8 @@ export class SettingsComponent extends React.Component<
}
};
protected editorFontSizeDidChange = (
event: React.ChangeEvent<HTMLInputElement>
): void => {
const { value } = event.target;
if (value) {
this.setState({ editorFontSize: parseInt(value, 10) });
}
private setFontSize = (editorFontSize: number) => {
this.setState({ editorFontSize });
};
protected rawAdditionalUrlsValueDidChange = (
@ -539,18 +542,10 @@ export class SettingsComponent extends React.Component<
this.setState({ autoScaleInterface: event.target.checked });
};
protected interfaceScaleDidChange = (
event: React.ChangeEvent<HTMLInputElement>
): void => {
const { value } = event.target;
const percentage = parseInt(value, 10);
if (isNaN(percentage)) {
return;
}
private setInterfaceScale = (percentage: number) => {
const interfaceScale = (percentage - 100) / 20;
if (!isNaN(interfaceScale)) {
this.setState({ interfaceScale });
}
this.setState({ interfaceScale });
};
protected verifyAfterUploadDidChange = (

View File

@ -0,0 +1,147 @@
import * as React from '@theia/core/shared/react';
import classnames from 'classnames';
interface SettingsStepInputProps {
value: number;
setSettingsStateValue: (value: number) => void;
step: number;
maxValue: number;
minValue: number;
classNames?: { [key: string]: string };
}
const SettingsStepInput: React.FC<SettingsStepInputProps> = (
props: SettingsStepInputProps
) => {
const { value, setSettingsStateValue, step, maxValue, minValue, classNames } =
props;
const [stepUpDisabled, setStepUpDisabled] = React.useState(false);
const [stepDownDisabled, setStepDownDisabled] = React.useState(false);
const onStepUp = (): void => {
const valueRoundedToScale = Math.ceil(value / step) * step;
const calculatedValue =
valueRoundedToScale === value ? value + step : valueRoundedToScale;
const newValue = limitValueByCondition(
calculatedValue,
maxValue,
calculatedValue >= maxValue,
disableStepUp
);
setSettingsStateValue(newValue);
};
const onStepDown = (): void => {
const valueRoundedToScale = Math.floor(value / step) * step;
const calculatedValue =
valueRoundedToScale === value ? value - step : valueRoundedToScale;
const newValue = limitValueByCondition(
calculatedValue,
minValue,
calculatedValue <= minValue,
disableStepDown
);
setSettingsStateValue(newValue);
};
const limitValueByCondition = (
calculatedValue: number,
limitedValue: number,
condition: boolean,
onConditionTrue: () => void,
onConditionFalse = enableButtons
): number => {
if (condition) {
onConditionTrue();
return limitedValue;
} else {
onConditionFalse();
return calculatedValue;
}
};
const enableButtons = (): void => {
setStepUpDisabled(false);
setStepDownDisabled(false);
};
const disableStepUp = (): void => {
setStepUpDisabled(true);
};
const disableStepDown = (): void => {
setStepDownDisabled(true);
};
const onUserInput = (event: React.ChangeEvent<HTMLInputElement>): void => {
const { value: eventValue } = event.target;
if (eventValue === '') {
setSettingsStateValue(0);
}
const number = Number(eventValue);
if (!isNaN(number) && number !== value) {
let newValue;
if (number > value) {
newValue = limitValueByCondition(
number,
maxValue,
number >= maxValue,
disableStepUp
);
} else {
newValue = limitValueByCondition(
number,
minValue,
number <= minValue,
disableStepDown
);
}
setSettingsStateValue(newValue);
}
};
// the component does not unmount when we close the settings dialog
// in theia which necessitates the below useEffect
React.useEffect(() => {
if (value > minValue && value < maxValue) {
enableButtons();
}
}, [value, minValue, maxValue]);
return (
<div className="settings-step-input-container">
<input
className={classnames('settings-step-input-element', classNames?.input)}
value={value.toString()}
onChange={onUserInput}
type="number"
pattern="[0-9]+"
/>
<div className="settings-step-input-buttons-container">
<button
className="settings-step-input-button settings-step-input-up-button"
disabled={stepUpDisabled}
onClick={onStepUp}
>
&#9662;
</button>
<button
className="settings-step-input-button"
disabled={stepDownDisabled}
onClick={onStepDown}
>
&#9662;
</button>
</div>
</div>
);
};
export default SettingsStepInput;

View File

@ -18,6 +18,7 @@
@import './fonts.css';
@import './custom-codicon.css';
@import './progress-bar.css';
@import './settings-step-input.css';
.theia-input.warning:focus {
outline-width: 1px;

View File

@ -0,0 +1,47 @@
.settings-step-input-container {
position: relative
}
.settings-step-input-element::-webkit-inner-spin-button,
.settings-step-input-element::-webkit-outer-spin-button {
-webkit-appearance: none;
margin: 0;
}
.settings-step-input-buttons-container {
display: none;
flex-direction: column;
position: absolute;
right: 0px;
top: 50%;
transform: translate(0px, -50%);
height: calc(100% - 4px);
width: 14px;
padding: 2px;
background: white;
}
.settings-step-input-container:hover > .settings-step-input-buttons-container {
display: flex;
}
.settings-step-input-up-button {
transform: rotate(-180deg);
}
.settings-step-input-button {
border: none;
border-radius: 0;
height: 50%;
width: 1;
display: flex;
align-items: center;
justify-content: center;
user-select: none;
cursor: pointer;
line-height: 12px;
}
.settings-step-input-button:hover {
background: rgba(128, 128, 128, 0.8);
}