From bfedd37d52bfc0adb048c0f3a8edeb8748a4b12f Mon Sep 17 00:00:00 2001 From: Jeremiah <42397676+jlsnow301@users.noreply.github.com> Date: Thu, 14 Dec 2023 10:01:48 -0800 Subject: [PATCH] Typescript textarea component (#80276) ## About The Pull Request This refactor has been on my radar for a long time. There are oddities between this and the input component that are frustrating to work with. An issue was brought to my attention with the Interview panel which sends byond data on EVERY keystroke. This has been changed, documented, and made into typescript. It now sends onEnter, and the user is informed that they must press enter to submit.. It should be more obvious what the events for textarea _do_ actually so as to not make similar mistakes again. ## Why It's Good For The Game Less laggy input More typescript components/better dev exp ## Changelog :cl: fix: Admin interview panel should feel snappier. /:cl: --- tgui/packages/tgui/components/TextArea.jsx | 240 ------------------ tgui/packages/tgui/components/TextArea.tsx | 181 +++++++++++++ tgui/packages/tgui/interfaces/AdminFax.jsx | 2 +- tgui/packages/tgui/interfaces/AdminPDA.jsx | 2 +- .../tgui/interfaces/CommandReport.tsx | 2 +- .../tgui/interfaces/CommunicationsConsole.jsx | 2 +- .../{Interview.jsx => Interview.tsx} | 124 +++++---- tgui/packages/tgui/interfaces/LingMMITalk.tsx | 2 +- .../tgui/interfaces/LuaEditor/index.jsx | 2 +- tgui/packages/tgui/interfaces/MafiaPanel.tsx | 4 +- .../tgui/interfaces/NtosMessenger/index.tsx | 2 +- tgui/packages/tgui/interfaces/NtosNotepad.tsx | 6 +- tgui/packages/tgui/interfaces/PaperSheet.tsx | 4 +- .../tgui/interfaces/TextInputModal.tsx | 2 +- 14 files changed, 270 insertions(+), 305 deletions(-) delete mode 100644 tgui/packages/tgui/components/TextArea.jsx create mode 100644 tgui/packages/tgui/components/TextArea.tsx rename tgui/packages/tgui/interfaces/{Interview.jsx => Interview.tsx} (57%) diff --git a/tgui/packages/tgui/components/TextArea.jsx b/tgui/packages/tgui/components/TextArea.jsx deleted file mode 100644 index 560915ffac1..00000000000 --- a/tgui/packages/tgui/components/TextArea.jsx +++ /dev/null @@ -1,240 +0,0 @@ -/** - * @file - * @copyright 2020 Aleksej Komarov - * @author Warlockd - * @license MIT - */ - -import { classes } from 'common/react'; -import { Component, createRef } from 'react'; -import { Box } from './Box'; -import { toInputValue } from './Input'; -import { KEY_ENTER, KEY_ESCAPE, KEY_TAB } from 'common/keycodes'; - -export class TextArea extends Component { - constructor(props) { - super(props); - this.textareaRef = props.innerRef || createRef(); - this.state = { - editing: false, - scrolledAmount: 0, - }; - 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 { onChange, onInput, onEnter, onKey } = 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 = ''; - e.target.blur(); - } - return; - } - if (e.keyCode === KEY_ESCAPE) { - if (this.props.onEscape) { - this.props.onEscape(e); - } - this.setEditing(false); - if (this.props.selfClear) { - e.target.value = ''; - } else { - e.target.value = toInputValue(this.props.value); - e.target.blur(); - } - return; - } - if (!editing) { - this.setEditing(true); - } - // Custom key handler - if (onKey) { - onKey(e, e.target.value); - } - if (!dontUseTabForIndent) { - const keyCode = e.keyCode || e.which; - if (keyCode === KEY_TAB) { - 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 (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.handleScroll = (e) => { - const { displayedValue } = this.props; - const input = this.textareaRef.current; - if (displayedValue && input) { - this.setState({ - scrolledAmount: input.scrollTop, - }); - } - }; - } - - 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 prevValue = prevProps.value; - const nextValue = this.props.value; - const input = this.textareaRef.current; - if (input && typeof nextValue === 'string' && 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, - scrollbar, - noborder, - displayedValue, - ...boxProps - } = this.props; - - // Box props - const { className, fluid, nowrap, ...rest } = boxProps; - const { scrolledAmount } = this.state; - return ( - - {!!displayedValue && ( - -
- {displayedValue} -
-
- )} -