Skip to content

Commit 6a36271

Browse files
authored
TGUI maintenance (#1044)
* update some stuffs * maybe? * fixes * restricted input update
1 parent df9e59a commit 6a36271

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

42 files changed

+1075
-753
lines changed

tgui/packages/tgui-panel/settings/SettingsPanel.jsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -161,7 +161,7 @@ export const SettingsGeneral = (props) => {
161161
maxValue={5}
162162
value={lineHeight}
163163
format={(value) => toFixed(value, 2)}
164-
onDrag={(e, value) =>
164+
onDrag={(value) =>
165165
dispatch(
166166
updateSettings({
167167
lineHeight: value,

tgui/packages/tgui/components/Input.jsx

-154
This file was deleted.
+181
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
/**
2+
* @file
3+
* @copyright 2020 Aleksej Komarov
4+
* @license MIT
5+
*/
6+
7+
import { isEscape, KEY } from 'common/keys';
8+
import { classes } from 'common/react';
9+
import { debounce } from 'common/timer';
10+
import { KeyboardEvent, SyntheticEvent, useEffect, useRef } from 'react';
11+
12+
import { Box, BoxProps } from './Box';
13+
14+
type ConditionalProps =
15+
| {
16+
/**
17+
* Mark this if you want to debounce onInput.
18+
*
19+
* This is useful for expensive filters, large lists etc.
20+
*
21+
* Requires `onInput` to be set.
22+
*/
23+
expensive?: boolean;
24+
/**
25+
* Fires on each key press / value change. Used for searching.
26+
*
27+
* If it's a large list, consider using `expensive` prop.
28+
*/
29+
onInput: (event: SyntheticEvent<HTMLInputElement>, value: string) => void;
30+
}
31+
| {
32+
/** This prop requires onInput to be set */
33+
expensive?: never;
34+
onInput?: never;
35+
};
36+
37+
type OptionalProps = Partial<{
38+
/** Automatically focuses the input on mount */
39+
autoFocus: boolean;
40+
/** Automatically selects the input value on focus */
41+
autoSelect: boolean;
42+
/** The class name of the input */
43+
className: string;
44+
/** Disables the input */
45+
disabled: boolean;
46+
/** Mark this if you want the input to be as wide as possible */
47+
fluid: boolean;
48+
/** The maximum length of the input value */
49+
maxLength: number;
50+
/** Mark this if you want to use a monospace font */
51+
monospace: boolean;
52+
/** Fires when user is 'done typing': Clicked out, blur, enter key */
53+
onChange: (event: SyntheticEvent<HTMLInputElement>, value: string) => void;
54+
/** Fires once the enter key is pressed */
55+
onEnter?: (event: SyntheticEvent<HTMLInputElement>, value: string) => void;
56+
/** Fires once the escape key is pressed */
57+
onEscape: (event: SyntheticEvent<HTMLInputElement>) => void;
58+
/** The placeholder text when everything is cleared */
59+
placeholder: string;
60+
/** Clears the input value on enter */
61+
selfClear: boolean;
62+
/** The state variable of the input. */
63+
value: string | number;
64+
}>;
65+
66+
type Props = OptionalProps & ConditionalProps & BoxProps;
67+
68+
export function toInputValue(value: string | number | undefined) {
69+
return typeof value !== 'number' && typeof value !== 'string'
70+
? ''
71+
: String(value);
72+
}
73+
74+
const inputDebounce = debounce((onInput: () => void) => onInput(), 250);
75+
76+
/**
77+
* ### Input
78+
* A basic text input which allow users to enter text into a UI.
79+
* > Input does not support custom font size and height due to the way
80+
* > it's implemented in CSS. Eventually, this needs to be fixed.
81+
*/
82+
export function Input(props: Props) {
83+
const {
84+
autoFocus,
85+
autoSelect,
86+
className,
87+
disabled,
88+
expensive,
89+
fluid,
90+
maxLength,
91+
monospace,
92+
onChange,
93+
onEnter,
94+
onEscape,
95+
onInput,
96+
placeholder,
97+
selfClear,
98+
value,
99+
...rest
100+
} = props;
101+
102+
// The ref to the input field
103+
const inputRef = useRef<HTMLInputElement>(null);
104+
105+
function handleInput(event: SyntheticEvent<HTMLInputElement>) {
106+
if (!onInput) return;
107+
108+
const value = event.currentTarget?.value;
109+
110+
if (expensive) {
111+
inputDebounce(() => onInput(event, value));
112+
} else {
113+
onInput(event, value);
114+
}
115+
}
116+
117+
function handleKeyDown(event: KeyboardEvent<HTMLInputElement>) {
118+
if (event.key === KEY.Enter) {
119+
onEnter?.(event, event.currentTarget.value);
120+
if (selfClear) {
121+
event.currentTarget.value = '';
122+
} else {
123+
event.currentTarget.blur();
124+
onChange?.(event, event.currentTarget.value);
125+
}
126+
127+
return;
128+
}
129+
130+
if (isEscape(event.key)) {
131+
onEscape?.(event);
132+
133+
event.currentTarget.value = toInputValue(value);
134+
event.currentTarget.blur();
135+
}
136+
}
137+
138+
/** Focuses the input on mount */
139+
useEffect(() => {
140+
const input = inputRef.current;
141+
if (!input) return;
142+
143+
const newValue = toInputValue(value);
144+
145+
if (input.value !== newValue) input.value = newValue;
146+
147+
if (!autoFocus && !autoSelect) return;
148+
149+
setTimeout(() => {
150+
input.focus();
151+
152+
if (autoSelect) {
153+
input.select();
154+
}
155+
}, 1);
156+
}, []);
157+
158+
return (
159+
<Box
160+
className={classes([
161+
'Input',
162+
fluid && 'Input--fluid',
163+
monospace && 'Input--monospace',
164+
className,
165+
])}
166+
{...rest}
167+
>
168+
<div className="Input__baseline">.</div>
169+
<input
170+
className="Input__input"
171+
disabled={disabled}
172+
maxLength={maxLength}
173+
onBlur={(event) => onChange?.(event, event.target.value)}
174+
onChange={handleInput}
175+
onKeyDown={handleKeyDown}
176+
placeholder={placeholder}
177+
ref={inputRef}
178+
/>
179+
</Box>
180+
);
181+
}

0 commit comments

Comments
 (0)