mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-06-07 20:56:32 +00:00
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:
parent
119dfa78d9
commit
1f7c2eb52c
@ -21,7 +21,15 @@ import {
|
|||||||
AsyncLocalizationProvider,
|
AsyncLocalizationProvider,
|
||||||
LanguageInfo,
|
LanguageInfo,
|
||||||
} from '@theia/core/lib/common/i18n/localization';
|
} 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<
|
export class SettingsComponent extends React.Component<
|
||||||
SettingsComponent.Props,
|
SettingsComponent.Props,
|
||||||
SettingsComponent.State
|
SettingsComponent.State
|
||||||
@ -88,6 +96,8 @@ export class SettingsComponent extends React.Component<
|
|||||||
}
|
}
|
||||||
|
|
||||||
protected renderSettings(): React.ReactNode {
|
protected renderSettings(): React.ReactNode {
|
||||||
|
const scalePercentage = 100 + this.state.interfaceScale * 20;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="content noselect">
|
<div className="content noselect">
|
||||||
{nls.localize(
|
{nls.localize(
|
||||||
@ -160,14 +170,13 @@ export class SettingsComponent extends React.Component<
|
|||||||
</div>
|
</div>
|
||||||
<div className="column">
|
<div className="column">
|
||||||
<div className="flex-line">
|
<div className="flex-line">
|
||||||
<input
|
<SettingsStepInput
|
||||||
className="theia-input small"
|
|
||||||
type="number"
|
|
||||||
step={1}
|
|
||||||
pattern="[0-9]+"
|
|
||||||
onKeyDown={this.numbersOnlyKeyDown}
|
|
||||||
value={this.state.editorFontSize}
|
value={this.state.editorFontSize}
|
||||||
onChange={this.editorFontSizeDidChange}
|
setSettingsStateValue={this.setFontSize}
|
||||||
|
step={fontSizeStep}
|
||||||
|
maxValue={maxFontSize}
|
||||||
|
minValue={minFontSize}
|
||||||
|
classNames={{ input: 'theia-input small' }}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
<div className="flex-line">
|
<div className="flex-line">
|
||||||
@ -179,14 +188,13 @@ export class SettingsComponent extends React.Component<
|
|||||||
/>
|
/>
|
||||||
{nls.localize('arduino/preferences/automatic', 'Automatic')}
|
{nls.localize('arduino/preferences/automatic', 'Automatic')}
|
||||||
</label>
|
</label>
|
||||||
<input
|
<SettingsStepInput
|
||||||
className="theia-input small with-margin"
|
value={scalePercentage}
|
||||||
type="number"
|
setSettingsStateValue={this.setInterfaceScale}
|
||||||
step={20}
|
step={scaleStep}
|
||||||
pattern="[0-9]+"
|
maxValue={maxScale}
|
||||||
onKeyDown={this.noopKeyDown}
|
minValue={minScale}
|
||||||
value={100 + this.state.interfaceScale * 20}
|
classNames={{ input: 'theia-input small with-margin' }}
|
||||||
onChange={this.interfaceScaleDidChange}
|
|
||||||
/>
|
/>
|
||||||
%
|
%
|
||||||
</div>
|
</div>
|
||||||
@ -516,13 +524,8 @@ export class SettingsComponent extends React.Component<
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
protected editorFontSizeDidChange = (
|
private setFontSize = (editorFontSize: number) => {
|
||||||
event: React.ChangeEvent<HTMLInputElement>
|
this.setState({ editorFontSize });
|
||||||
): void => {
|
|
||||||
const { value } = event.target;
|
|
||||||
if (value) {
|
|
||||||
this.setState({ editorFontSize: parseInt(value, 10) });
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
protected rawAdditionalUrlsValueDidChange = (
|
protected rawAdditionalUrlsValueDidChange = (
|
||||||
@ -539,18 +542,10 @@ export class SettingsComponent extends React.Component<
|
|||||||
this.setState({ autoScaleInterface: event.target.checked });
|
this.setState({ autoScaleInterface: event.target.checked });
|
||||||
};
|
};
|
||||||
|
|
||||||
protected interfaceScaleDidChange = (
|
private setInterfaceScale = (percentage: number) => {
|
||||||
event: React.ChangeEvent<HTMLInputElement>
|
|
||||||
): void => {
|
|
||||||
const { value } = event.target;
|
|
||||||
const percentage = parseInt(value, 10);
|
|
||||||
if (isNaN(percentage)) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const interfaceScale = (percentage - 100) / 20;
|
const interfaceScale = (percentage - 100) / 20;
|
||||||
if (!isNaN(interfaceScale)) {
|
|
||||||
this.setState({ interfaceScale });
|
this.setState({ interfaceScale });
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
protected verifyAfterUploadDidChange = (
|
protected verifyAfterUploadDidChange = (
|
||||||
|
@ -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}
|
||||||
|
>
|
||||||
|
▾
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
className="settings-step-input-button"
|
||||||
|
disabled={stepDownDisabled}
|
||||||
|
onClick={onStepDown}
|
||||||
|
>
|
||||||
|
▾
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SettingsStepInput;
|
@ -18,6 +18,7 @@
|
|||||||
@import './fonts.css';
|
@import './fonts.css';
|
||||||
@import './custom-codicon.css';
|
@import './custom-codicon.css';
|
||||||
@import './progress-bar.css';
|
@import './progress-bar.css';
|
||||||
|
@import './settings-step-input.css';
|
||||||
|
|
||||||
.theia-input.warning:focus {
|
.theia-input.warning:focus {
|
||||||
outline-width: 1px;
|
outline-width: 1px;
|
||||||
|
@ -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);
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user