From ad329c33fa90c3238dd490399d7c49324f12d57a Mon Sep 17 00:00:00 2001 From: Alex Gleason Date: Wed, 11 Oct 2023 13:38:58 -0500 Subject: [PATCH] Remove AutosuggestTextarea --- package.json | 1 - src/components/autosuggest-textarea.tsx | 288 ------------------ .../compose/components/compose-form.tsx | 27 +- yarn.lock | 28 +- 4 files changed, 2 insertions(+), 342 deletions(-) delete mode 100644 src/components/autosuggest-textarea.tsx diff --git a/package.json b/package.json index aac07953f..c97a7ae5d 100644 --- a/package.json +++ b/package.json @@ -152,7 +152,6 @@ "react-sparklines": "^1.7.0", "react-sticky-box": "^2.0.0", "react-swipeable-views": "^0.14.0", - "react-textarea-autosize": "^8.3.4", "react-virtuoso": "^4.3.11", "redux": "^4.1.1", "redux-immutable": "^4.0.0", diff --git a/src/components/autosuggest-textarea.tsx b/src/components/autosuggest-textarea.tsx deleted file mode 100644 index 233c1e4e3..000000000 --- a/src/components/autosuggest-textarea.tsx +++ /dev/null @@ -1,288 +0,0 @@ -import clsx from 'clsx'; -import React from 'react'; -import ImmutablePureComponent from 'react-immutable-pure-component'; -import Textarea from 'react-textarea-autosize'; - -import { Portal } from 'soapbox/components/ui'; -import AutosuggestAccount from 'soapbox/features/compose/components/autosuggest-account'; -import { isRtl } from 'soapbox/utils/rtl'; -import { textAtCursorMatchesToken } from 'soapbox/utils/suggestions'; - -import AutosuggestEmoji from './autosuggest-emoji'; - -import type { List as ImmutableList } from 'immutable'; -import type { Emoji } from 'soapbox/features/emoji'; - -interface IAutosuggesteTextarea { - id?: string; - value: string; - suggestions: ImmutableList; - disabled: boolean; - placeholder: string; - onSuggestionSelected: (tokenStart: number, token: string | null, value: string | undefined) => void; - onSuggestionsClearRequested: () => void; - onSuggestionsFetchRequested: (token: string | number) => void; - onChange: React.ChangeEventHandler; - onKeyUp?: React.KeyboardEventHandler; - onKeyDown?: React.KeyboardEventHandler; - onPaste: (files: FileList) => void; - autoFocus: boolean; - onFocus: () => void; - onBlur?: () => void; - condensed?: boolean; - children: React.ReactNode; -} - -class AutosuggestTextarea extends ImmutablePureComponent { - - textarea: HTMLTextAreaElement | null = null; - - static defaultProps = { - autoFocus: true, - }; - - state = { - suggestionsHidden: true, - focused: false, - selectedSuggestion: 0, - lastToken: null, - tokenStart: 0, - }; - - onChange: React.ChangeEventHandler = (e) => { - const [tokenStart, token] = textAtCursorMatchesToken( - e.target.value, - e.target.selectionStart, - ['@', ':', '#'], - ); - - if (token !== null && this.state.lastToken !== token) { - this.setState({ lastToken: token, selectedSuggestion: 0, tokenStart }); - this.props.onSuggestionsFetchRequested(token); - } else if (token === null) { - this.setState({ lastToken: null }); - this.props.onSuggestionsClearRequested(); - } - - this.props.onChange(e); - }; - - onKeyDown: React.KeyboardEventHandler = (e) => { - const { suggestions, disabled } = this.props; - const { selectedSuggestion, suggestionsHidden } = this.state; - - if (disabled) { - e.preventDefault(); - return; - } - - if (e.which === 229 || (e as any).isComposing) { - // Ignore key events during text composition - // e.key may be a name of the physical key even in this case (e.x. Safari / Chrome on Mac) - return; - } - - switch (e.key) { - case 'Escape': - if (suggestions.size === 0 || suggestionsHidden) { - document.querySelector('.ui')?.parentElement?.focus(); - } else { - e.preventDefault(); - this.setState({ suggestionsHidden: true }); - } - - break; - case 'ArrowDown': - if (suggestions.size > 0 && !suggestionsHidden) { - e.preventDefault(); - this.setState({ selectedSuggestion: Math.min(selectedSuggestion + 1, suggestions.size - 1) }); - } - - break; - case 'ArrowUp': - if (suggestions.size > 0 && !suggestionsHidden) { - e.preventDefault(); - this.setState({ selectedSuggestion: Math.max(selectedSuggestion - 1, 0) }); - } - - break; - case 'Enter': - case 'Tab': - // Select suggestion - if (this.state.lastToken !== null && suggestions.size > 0 && !suggestionsHidden) { - e.preventDefault(); - e.stopPropagation(); - this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestions.get(selectedSuggestion)); - } - - break; - } - - if (e.defaultPrevented || !this.props.onKeyDown) { - return; - } - - this.props.onKeyDown(e); - }; - - onBlur = () => { - this.setState({ suggestionsHidden: true, focused: false }); - - if (this.props.onBlur) { - this.props.onBlur(); - } - }; - - onFocus = () => { - this.setState({ focused: true }); - - if (this.props.onFocus) { - this.props.onFocus(); - } - }; - - onSuggestionClick: React.MouseEventHandler = (e) => { - const suggestion = this.props.suggestions.get(e.currentTarget.getAttribute('data-index') as any); - e.preventDefault(); - this.props.onSuggestionSelected(this.state.tokenStart, this.state.lastToken, suggestion); - this.textarea?.focus(); - }; - - shouldComponentUpdate(nextProps: IAutosuggesteTextarea, nextState: any) { - // Skip updating when only the lastToken changes so the - // cursor doesn't jump around due to re-rendering unnecessarily - const lastTokenUpdated = this.state.lastToken !== nextState.lastToken; - const valueUpdated = this.props.value !== nextProps.value; - - if (lastTokenUpdated && !valueUpdated) { - return false; - } else { - // https://stackoverflow.com/a/35962835 - return super.shouldComponentUpdate!.bind(this)(nextProps, nextState, undefined); - } - } - - componentDidUpdate(prevProps: IAutosuggesteTextarea, prevState: any) { - const { suggestions } = this.props; - if (suggestions !== prevProps.suggestions && suggestions.size > 0 && prevState.suggestionsHidden && prevState.focused) { - this.setState({ suggestionsHidden: false }); - } - } - - setTextarea: React.Ref = (c) => { - this.textarea = c; - }; - - onPaste: React.ClipboardEventHandler = (e) => { - if (e.clipboardData && e.clipboardData.files.length === 1) { - this.props.onPaste(e.clipboardData.files); - e.preventDefault(); - } - }; - - renderSuggestion = (suggestion: string | Emoji, i: number) => { - const { selectedSuggestion } = this.state; - let inner, key; - - if (typeof suggestion === 'object') { - inner = ; - key = suggestion.id; - } else if (suggestion[0] === '#') { - inner = suggestion; - key = suggestion; - } else { - inner = ; - key = suggestion; - } - - return ( -
- {inner} -
- ); - }; - - setPortalPosition() { - if (!this.textarea) { - return {}; - } - - const { top, height, left, width } = this.textarea.getBoundingClientRect(); - - return { - top: top + height, - left, - width, - }; - } - - render() { - const { value, suggestions, disabled, placeholder, onKeyUp, autoFocus, children, condensed, id } = this.props; - const { suggestionsHidden } = this.state; - const style = { direction: 'ltr', minRows: 10 }; - - // TODO: convert to functional component and use `useLocale()` hook instead of checking placeholder text. - if (isRtl(value) || (!value && placeholder && isRtl(placeholder))) { - style.direction = 'rtl'; - } - - return [ -
-
-