mirror of
https://github.com/arduino/arduino-ide.git
synced 2025-06-06 04:06: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,
|
||||
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 = (
|
||||
|
@ -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 './custom-codicon.css';
|
||||
@import './progress-bar.css';
|
||||
@import './settings-step-input.css';
|
||||
|
||||
.theia-input.warning:focus {
|
||||
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