diff --git a/frontend/src/AddSeries/AddNewSeries/AddNewSeries.tsx b/frontend/src/AddSeries/AddNewSeries/AddNewSeries.tsx index 64b57dc4e..581023798 100644 --- a/frontend/src/AddSeries/AddNewSeries/AddNewSeries.tsx +++ b/frontend/src/AddSeries/AddNewSeries/AddNewSeries.tsx @@ -1,4 +1,4 @@ -import React, { useCallback, useEffect, useState } from 'react'; +import React, { useCallback, useEffect, useRef, useState } from 'react'; import Alert from 'Components/Alert'; import TextInput from 'Components/Form/TextInput'; import Icon from 'Components/Icon'; @@ -22,6 +22,7 @@ function AddNewSeries() { const { term: initialTerm = '' } = useQueryParams<{ term: string }>(); const hasSeries = useHasSeries(); const [term, setTerm] = useState(initialTerm); + const searchInputRef = useRef(null); const [isFetching, setIsFetching] = useState(false); const query = useDebounce(term, term ? 300 : 0); @@ -36,6 +37,7 @@ function AddNewSeries() { const handleClearSeriesLookupPress = useCallback(() => { setTerm(''); setIsFetching(false); + searchInputRef.current?.focus(); }, []); const { isFetching: isFetchingApi, error, data } = useLookupSeries(query); @@ -57,6 +59,7 @@ function AddNewSeries() { void; } -function TextInput({ - className = styles.input, - type = 'text', - readOnly = false, - autoFocus = false, - placeholder, - name, - value = '', - hasError, - hasWarning, - hasButton, - step, - min, - max, - onBlur, - onFocus, - onCopy, - onChange, - onSelectionChange, -}: TextInputProps | FileInputProps): JSX.Element { - const inputRef = useRef(null); - const selectionTimeout = useRef>(); - const selectionStart = useRef(); - const selectionEnd = useRef(); - const isMouseTarget = useRef(false); +const TextInput = forwardRef( + ( + { + className = styles.input, + type = 'text', + readOnly = false, + autoFocus = false, + placeholder, + name, + value = '', + hasError, + hasWarning, + hasButton, + step, + min, + max, + onBlur, + onFocus, + onCopy, + onChange, + onSelectionChange, + }: TextInputProps | FileInputProps, + ref + ) => { + const inputRef = useRef(null); + const combinedRef = useCombinedRefs(ref, inputRef); + const selectionTimeout = useRef>(); + const selectionStart = useRef(); + const selectionEnd = useRef(); + const isMouseTarget = useRef(false); - const selectionChanged = useCallback(() => { - if (selectionTimeout.current) { - clearTimeout(selectionTimeout.current); - } - - selectionTimeout.current = setTimeout(() => { - if (!inputRef.current) { - return; + const selectionChanged = useCallback(() => { + if (selectionTimeout.current) { + clearTimeout(selectionTimeout.current); } - const start = inputRef.current.selectionStart; - const end = inputRef.current.selectionEnd; + selectionTimeout.current = setTimeout(() => { + if (!inputRef.current) { + return; + } - const selectionChanged = - selectionStart.current !== start || selectionEnd.current !== end; + const start = inputRef.current.selectionStart; + const end = inputRef.current.selectionEnd; - selectionStart.current = start; - selectionEnd.current = end; + const selectionChanged = + selectionStart.current !== start || selectionEnd.current !== end; - if (selectionChanged) { - onSelectionChange?.(start, end); + selectionStart.current = start; + selectionEnd.current = end; + + if (selectionChanged) { + onSelectionChange?.(start, end); + } + }, 10); + }, [onSelectionChange]); + + const handleChange = useCallback( + (event: ChangeEvent) => { + onChange({ + name, + value: event.target.value, + files: type === 'file' ? event.target.files : undefined, + }); + }, + [name, type, onChange] + ); + + const handleFocus = useCallback( + (event: FocusEvent) => { + onFocus?.(event); + + selectionChanged(); + }, + [selectionChanged, onFocus] + ); + + const handleKeyUp = useCallback(() => { + selectionChanged(); + }, [selectionChanged]); + + const handleMouseDown = useCallback(() => { + isMouseTarget.current = true; + }, []); + + const handleMouseUp = useCallback(() => { + selectionChanged(); + }, [selectionChanged]); + + const handleWheel = useCallback(() => { + if (type === 'number') { + inputRef.current?.blur(); } - }, 10); - }, [onSelectionChange]); + }, [type]); - const handleChange = useCallback( - (event: ChangeEvent) => { - onChange({ - name, - value: event.target.value, - files: type === 'file' ? event.target.files : undefined, - }); - }, - [name, type, onChange] - ); + const handleDocumentMouseUp = useCallback(() => { + if (isMouseTarget.current) { + selectionChanged(); + } - const handleFocus = useCallback( - (event: FocusEvent) => { - onFocus?.(event); + isMouseTarget.current = false; + }, [selectionChanged]); - selectionChanged(); - }, - [selectionChanged, onFocus] - ); + useEffect(() => { + window.addEventListener('mouseup', handleDocumentMouseUp); - const handleKeyUp = useCallback(() => { - selectionChanged(); - }, [selectionChanged]); + return () => { + window.removeEventListener('mouseup', handleDocumentMouseUp); + }; + }, [handleDocumentMouseUp]); - const handleMouseDown = useCallback(() => { - isMouseTarget.current = true; - }, []); + useEffect(() => { + return () => { + clearTimeout(selectionTimeout.current); + }; + }, []); - const handleMouseUp = useCallback(() => { - selectionChanged(); - }, [selectionChanged]); - - const handleWheel = useCallback(() => { - if (type === 'number') { - inputRef.current?.blur(); - } - }, [type]); - - const handleDocumentMouseUp = useCallback(() => { - if (isMouseTarget.current) { - selectionChanged(); - } - - isMouseTarget.current = false; - }, [selectionChanged]); - - useEffect(() => { - window.addEventListener('mouseup', handleDocumentMouseUp); - - return () => { - window.removeEventListener('mouseup', handleDocumentMouseUp); - }; - }, [handleDocumentMouseUp]); - - useEffect(() => { - return () => { - clearTimeout(selectionTimeout.current); - }; - }, []); - - return ( - - ); -} + return ( + + ); + } +); export default TextInput;