-
Notifications
You must be signed in to change notification settings - Fork 2.5k
/
preference-number-input.tsx
101 lines (88 loc) · 3.9 KB
/
preference-number-input.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
/********************************************************************************
* Copyright (C) 2020 Ericsson and others.
*
* This program and the accompanying materials are made available under the
* terms of the Eclipse Public License v. 2.0 which is available at
* http://www.eclipse.org/legal/epl-2.0.
*
* This Source Code may also be made available under the following Secondary
* Licenses when the conditions for such availability set forth in the Eclipse
* Public License v. 2.0 are satisfied: GNU General Public License, version 2
* with the GNU Classpath Exception which is available at
* https://www.gnu.org/software/classpath/license.html.
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
import * as React from '@theia/core/shared/react';
import { Preference } from '../../util/preference-types';
interface PreferenceNumberInputProps {
preferenceDisplayNode: Preference.NodeWithValueInSingleScope;
setPreference(preferenceName: string, preferenceValue: number): void;
}
export const PreferenceNumberInput: React.FC<PreferenceNumberInputProps> = ({ preferenceDisplayNode, setPreference }) => {
const { id } = preferenceDisplayNode;
const { data, value } = preferenceDisplayNode.preference;
const externalValue = (value !== undefined ? value : data.defaultValue) ?? '';
const [currentTimeout, setCurrentTimetout] = React.useState<number>(0);
const [currentValue, setCurrentValue] = React.useState<string>(externalValue);
const [validationMessage, setValidationMessage] = React.useState<string>('');
React.useEffect(() => {
setCurrentValue(externalValue);
}, [externalValue]);
const onBlur = React.useCallback(() => {
setCurrentValue(externalValue);
setValidationMessage('');
}, [externalValue]);
const onChange = React.useCallback(e => {
clearTimeout(currentTimeout);
const { value: newValue } = e.target;
setCurrentValue(newValue);
const { value: inputValue , message } = getInputValidation(newValue);
setValidationMessage(message);
if (!isNaN(inputValue)) {
const newTimeout = setTimeout(() => setPreference(id, inputValue), 750);
setCurrentTimetout(Number(newTimeout));
}
}, [currentTimeout]);
/**
* Validate the input value.
* @param input the input value.
*/
const getInputValidation = (input: string): {
value: number, // the numeric value of the input. `NaN` if there is an error.
message: string // the error message to display.
} => {
const inputValue = Number(input);
const errorMessages: string[] = [];
if (input === '' || isNaN(inputValue)) {
return { value: NaN, message: 'Value must be a number.' };
}
if (data.minimum && inputValue < data.minimum) {
errorMessages.push(`Value must be greater than or equal to ${data.minimum}.`);
};
if (data.maximum && inputValue > data.maximum) {
errorMessages.push(`Value must be less than or equal to ${data.maximum}.`);
};
if (data.type === 'integer' && inputValue % 1 !== 0) {
errorMessages.push('Value must be an integer.');
}
return {
value: errorMessages.length ? NaN : inputValue,
message: errorMessages.join(' ')
};
};
return (
<div className='pref-input-container'>
<input
type="number"
className="theia-input"
pattern="[0-9]*"
value={currentValue}
onChange={onChange}
onBlur={onBlur}
data-preference-id={id}
/>
{!!validationMessage.length ? <div className='pref-error-notification'>{validationMessage}</div> : undefined}
</div>
);
};