From 5ed768593857fbd22ba50bc916b09f4b621d8538 Mon Sep 17 00:00:00 2001 From: Kapu1178 <75460809+Kapu1178@users.noreply.github.com> Date: Fri, 2 Aug 2024 13:18:18 -0400 Subject: [PATCH] update some stuffs --- tgui/packages/tgui/components/Input.jsx | 154 -------------- tgui/packages/tgui/components/Input.tsx | 181 ++++++++++++++++ tgui/packages/tgui/components/TextArea.jsx | 173 --------------- tgui/packages/tgui/components/TextArea.tsx | 198 ++++++++++++++++++ .../tgui/interfaces/NumberInputModal.tsx | 39 ++-- .../tgui/interfaces/TextInputModal.tsx | 62 ++++-- 6 files changed, 443 insertions(+), 364 deletions(-) delete mode 100644 tgui/packages/tgui/components/Input.jsx create mode 100644 tgui/packages/tgui/components/Input.tsx delete mode 100644 tgui/packages/tgui/components/TextArea.jsx create mode 100644 tgui/packages/tgui/components/TextArea.tsx diff --git a/tgui/packages/tgui/components/Input.jsx b/tgui/packages/tgui/components/Input.jsx deleted file mode 100644 index f4bb8dae6433..000000000000 --- a/tgui/packages/tgui/components/Input.jsx +++ /dev/null @@ -1,154 +0,0 @@ -/** - * @file - * @copyright 2020 Aleksej Komarov - * @license MIT - */ - -import { KEY_ENTER, KEY_ESCAPE } from 'common/keycodes'; -import { classes } from 'common/react'; -import { Component, createRef } from 'react'; - -import { Box } from './Box'; - -export const toInputValue = (value) => - typeof value !== 'number' && typeof value !== 'string' ? '' : String(value); - -export class Input extends Component { - constructor(props) { - super(props); - this.inputRef = createRef(); - this.state = { - editing: false, - }; - this.handleInput = (e) => { - const { editing } = this.state; - const { onInput } = this.props; - if (!editing) { - this.setEditing(true); - } - if (onInput) { - onInput(e, e.target.value); - } - }; - this.handleFocus = (e) => { - const { editing } = this.state; - if (!editing) { - this.setEditing(true); - } - }; - this.handleBlur = (e) => { - const { editing } = this.state; - const { onChange } = this.props; - if (editing) { - this.setEditing(false); - if (onChange) { - onChange(e, e.target.value); - } - } - }; - this.handleKeyDown = (e) => { - const { onInput, onChange, onEnter } = this.props; - if (e.keyCode === KEY_ENTER) { - this.setEditing(false); - if (onChange) { - onChange(e, e.target.value); - } - if (onInput) { - onInput(e, e.target.value); - } - if (onEnter) { - onEnter(e, e.target.value); - } - if (this.props.selfClear) { - e.target.value = ''; - } else { - e.target.blur(); - } - return; - } - if (e.keyCode === KEY_ESCAPE) { - if (this.props.onEscape) { - this.props.onEscape(e); - return; - } - - this.setEditing(false); - e.target.value = toInputValue(this.props.value); - e.target.blur(); - return; - } - }; - } - - componentDidMount() { - const nextValue = this.props.value; - const input = this.inputRef.current; - if (input) { - input.value = toInputValue(nextValue); - } - - if (this.props.autoFocus || this.props.autoSelect) { - setTimeout(() => { - input.focus(); - - if (this.props.autoSelect) { - input.select(); - } - }, 1); - } - } - - componentDidUpdate(prevProps, prevState) { - const { editing } = this.state; - const prevValue = prevProps.value; - const nextValue = this.props.value; - const input = this.inputRef.current; - if (input && !editing && prevValue !== nextValue) { - input.value = toInputValue(nextValue); - } - } - - setEditing(editing) { - this.setState({ editing }); - } - - render() { - const { props } = this; - // Input only props - const { - selfClear, - onInput, - onChange, - onEnter, - value, - maxLength, - placeholder, - ...boxProps - } = props; - // Box props - const { className, fluid, monospace, ...rest } = boxProps; - return ( - -
.
- -
- ); - } -} diff --git a/tgui/packages/tgui/components/Input.tsx b/tgui/packages/tgui/components/Input.tsx new file mode 100644 index 000000000000..9bc48aa80940 --- /dev/null +++ b/tgui/packages/tgui/components/Input.tsx @@ -0,0 +1,181 @@ +/** + * @file + * @copyright 2020 Aleksej Komarov + * @license MIT + */ + +import { isEscape, KEY } from 'common/keys'; +import { classes } from 'common/react'; +import { debounce } from 'common/timer'; +import { KeyboardEvent, SyntheticEvent, useEffect, useRef } from 'react'; + +import { Box, BoxProps } from './Box'; + +type ConditionalProps = + | { + /** + * Mark this if you want to debounce onInput. + * + * This is useful for expensive filters, large lists etc. + * + * Requires `onInput` to be set. + */ + expensive?: boolean; + /** + * Fires on each key press / value change. Used for searching. + * + * If it's a large list, consider using `expensive` prop. + */ + onInput: (event: SyntheticEvent, value: string) => void; + } + | { + /** This prop requires onInput to be set */ + expensive?: never; + onInput?: never; + }; + +type OptionalProps = Partial<{ + /** Automatically focuses the input on mount */ + autoFocus: boolean; + /** Automatically selects the input value on focus */ + autoSelect: boolean; + /** The class name of the input */ + className: string; + /** Disables the input */ + disabled: boolean; + /** Mark this if you want the input to be as wide as possible */ + fluid: boolean; + /** The maximum length of the input value */ + maxLength: number; + /** Mark this if you want to use a monospace font */ + monospace: boolean; + /** Fires when user is 'done typing': Clicked out, blur, enter key */ + onChange: (event: SyntheticEvent, value: string) => void; + /** Fires once the enter key is pressed */ + onEnter?: (event: SyntheticEvent, value: string) => void; + /** Fires once the escape key is pressed */ + onEscape: (event: SyntheticEvent) => void; + /** The placeholder text when everything is cleared */ + placeholder: string; + /** Clears the input value on enter */ + selfClear: boolean; + /** The state variable of the input. */ + value: string | number; +}>; + +type Props = OptionalProps & ConditionalProps & BoxProps; + +export function toInputValue(value: string | number | undefined) { + return typeof value !== 'number' && typeof value !== 'string' + ? '' + : String(value); +} + +const inputDebounce = debounce((onInput: () => void) => onInput(), 250); + +/** + * ### Input + * A basic text input which allow users to enter text into a UI. + * > Input does not support custom font size and height due to the way + * > it's implemented in CSS. Eventually, this needs to be fixed. + */ +export function Input(props: Props) { + const { + autoFocus, + autoSelect, + className, + disabled, + expensive, + fluid, + maxLength, + monospace, + onChange, + onEnter, + onEscape, + onInput, + placeholder, + selfClear, + value, + ...rest + } = props; + + // The ref to the input field + const inputRef = useRef(null); + + function handleInput(event: SyntheticEvent) { + if (!onInput) return; + + const value = event.currentTarget?.value; + + if (expensive) { + inputDebounce(() => onInput(event, value)); + } else { + onInput(event, value); + } + } + + function handleKeyDown(event: KeyboardEvent) { + if (event.key === KEY.Enter) { + onEnter?.(event, event.currentTarget.value); + if (selfClear) { + event.currentTarget.value = ''; + } else { + event.currentTarget.blur(); + onChange?.(event, event.currentTarget.value); + } + + return; + } + + if (isEscape(event.key)) { + onEscape?.(event); + + event.currentTarget.value = toInputValue(value); + event.currentTarget.blur(); + } + } + + /** Focuses the input on mount */ + useEffect(() => { + const input = inputRef.current; + if (!input) return; + + const newValue = toInputValue(value); + + if (input.value !== newValue) input.value = newValue; + + if (!autoFocus && !autoSelect) return; + + setTimeout(() => { + input.focus(); + + if (autoSelect) { + input.select(); + } + }, 1); + }, []); + + return ( + +
.
+ onChange?.(event, event.target.value)} + onChange={handleInput} + onKeyDown={handleKeyDown} + placeholder={placeholder} + ref={inputRef} + /> +
+ ); +} diff --git a/tgui/packages/tgui/components/TextArea.jsx b/tgui/packages/tgui/components/TextArea.jsx deleted file mode 100644 index 4a3a5145ff93..000000000000 --- a/tgui/packages/tgui/components/TextArea.jsx +++ /dev/null @@ -1,173 +0,0 @@ -/** - * @file - * @copyright 2020 Aleksej Komarov - * @author Warlockd - * @license MIT - */ - -import { KEY_ESCAPE } from 'common/keycodes'; -import { classes } from 'common/react'; -import { Component, createRef } from 'react'; - -import { Box } from './Box'; -import { toInputValue } from './Input'; - -export class TextArea extends Component { - constructor(props) { - super(props); - this.textareaRef = createRef(); - this.fillerRef = createRef(); - this.state = { - editing: false, - }; - const { dontUseTabForIndent = false } = props; - this.handleOnInput = (e) => { - const { editing } = this.state; - const { onInput } = this.props; - if (!editing) { - this.setEditing(true); - } - if (onInput) { - onInput(e, e.target.value); - } - }; - this.handleOnChange = (e) => { - const { editing } = this.state; - const { onChange } = this.props; - if (editing) { - this.setEditing(false); - } - if (onChange) { - onChange(e, e.target.value); - } - }; - this.handleKeyPress = (e) => { - const { editing } = this.state; - const { onKeyPress } = this.props; - if (!editing) { - this.setEditing(true); - } - if (onKeyPress) { - onKeyPress(e, e.target.value); - } - }; - this.handleKeyDown = (e) => { - const { editing } = this.state; - const { onKeyDown } = this.props; - if (e.keyCode === KEY_ESCAPE) { - this.setEditing(false); - e.target.value = toInputValue(this.props.value); - e.target.blur(); - return; - } - if (!editing) { - this.setEditing(true); - } - if (!dontUseTabForIndent) { - const keyCode = e.keyCode || e.which; - if (keyCode === 9) { - e.preventDefault(); - const { value, selectionStart, selectionEnd } = e.target; - e.target.value = - value.substring(0, selectionStart) + - '\t' + - value.substring(selectionEnd); - e.target.selectionEnd = selectionStart + 1; - } - } - if (onKeyDown) { - onKeyDown(e, e.target.value); - } - }; - this.handleFocus = (e) => { - const { editing } = this.state; - if (!editing) { - this.setEditing(true); - } - }; - this.handleBlur = (e) => { - const { editing } = this.state; - const { onChange } = this.props; - if (editing) { - this.setEditing(false); - if (onChange) { - onChange(e, e.target.value); - } - } - }; - } - - componentDidMount() { - const nextValue = this.props.value; - const input = this.textareaRef.current; - if (input) { - input.value = toInputValue(nextValue); - } - - if (this.props.autoFocus || this.props.autoSelect) { - setTimeout(() => { - input.focus(); - - if (this.props.autoSelect) { - input.select(); - } - }, 1); - } - } - - componentDidUpdate(prevProps, prevState) { - const { editing } = this.state; - const prevValue = prevProps.value; - const nextValue = this.props.value; - const input = this.textareaRef.current; - if (input && !editing && prevValue !== nextValue) { - input.value = toInputValue(nextValue); - } - } - - setEditing(editing) { - this.setState({ editing }); - } - - getValue() { - return this.textareaRef.current && this.textareaRef.current.value; - } - - render() { - // Input only props - const { - onChange, - onKeyDown, - onKeyPress, - onInput, - onFocus, - onBlur, - onEnter, - value, - maxLength, - placeholder, - ...boxProps - } = this.props; - // Box props - const { className, fluid, ...rest } = boxProps; - return ( - -