diff --git a/src/components/MnemonicPhraseInput.tsx b/src/components/MnemonicPhraseInput.tsx index 9e971cc9..a614f6c2 100644 --- a/src/components/MnemonicPhraseInput.tsx +++ b/src/components/MnemonicPhraseInput.tsx @@ -1,4 +1,4 @@ -import { useEffect, useRef, useState } from 'react' +import { useCallback, useEffect, useRef, useState } from 'react' import { Bip39MnemonicWordInput } from './MnemonicWordInput' import { MNEMONIC_WORDS } from '../constants/bip39words' import * as rb from 'react-bootstrap' @@ -23,8 +23,8 @@ const MnemonicDropdown = forwardRef(({ sh {words.map((word) => ( -
- onSelect(word)} className="p-2"> +
+ onSelect(word)} className="p-2"> {word}
@@ -43,56 +43,64 @@ export default function MnemonicPhraseInput({ const [activeIndex, setActiveIndex] = useState(0) const inputRefs = useRef([]) const [showDropdown, setShowDropdown] = useState(null) - const [filteredWords, setFilteredWords] = useState(undefined) + const [filteredWords, setFilteredWords] = useState([]) const dropdownRef = useRef(null) + const focusNextInput = useCallback((index: number) => { + inputRefs.current[index]?.focus() + }, []) + useEffect(() => { if (activeIndex < mnemonicPhrase.length && isValid && isValid(activeIndex)) { const nextIndex = activeIndex + 1 setActiveIndex(nextIndex) - - if (inputRefs.current[nextIndex]) { - inputRefs.current[nextIndex].focus() - } + setShowDropdown(null) + focusNextInput(nextIndex) } - }, [mnemonicPhrase, activeIndex, isValid]) + }, [mnemonicPhrase, activeIndex, isValid, focusNextInput]) - const handleInputChange = (value: string, index: number) => { - const newPhrase = mnemonicPhrase.map((old, i) => (i === index ? value : old)) - onChange(newPhrase) - handleDropdownValue(value, index) - } - - const handleDropdownValue = (value: string, index: number) => { + const updateFilteredWords = useCallback((value: string, index: number) => { const matched = value ? MNEMONIC_WORDS.filter((word) => word.startsWith(value)) : [] - if (matched.length > 0) { - setShowDropdown(index) - setFilteredWords(matched) - } else { - setShowDropdown(null) - setFilteredWords(undefined) - } - } + setShowDropdown(matched.length > 0 ? index : null) + setFilteredWords(matched) + }, []) - const handleSelectWord = (word: string, index: number) => { - const newPhrase = mnemonicPhrase.map((old, i) => (i === index ? word : old)) - onChange(newPhrase) - setShowDropdown(null) - } + const handleInputChange = useCallback( + (value: string, index: number, selectWordFromDropdown = false) => { + const newPhrase = mnemonicPhrase.map((word, i) => (i === index ? value : word)) + onChange(newPhrase) + if (selectWordFromDropdown) { + setShowDropdown(null) + if (!isValid) { + focusNextInput(index + 1) + } + } else { + updateFilteredWords(value, index) + } + }, + [mnemonicPhrase, onChange, isValid, focusNextInput, updateFilteredWords], + ) - const handleKeyDown = (e: React.KeyboardEvent) => { - if (e.key === 'ArrowDown') { - e.preventDefault() - if (filteredWords && filteredWords.length > 0 && dropdownRef.current) { - const firstItem = dropdownRef.current.querySelector('.dropdown-item') - if (firstItem) { - ;(firstItem as HTMLElement).focus() + const handleKeyDown = useCallback( + (e: React.KeyboardEvent, value: string, wordIndex: number) => { + if (e.key === 'ArrowDown') { + e.preventDefault() + if (filteredWords.length > 0 && dropdownRef.current) { + const firstItem = dropdownRef.current.querySelector('.dropdown-item') + if (firstItem) { + ;(firstItem as HTMLElement).focus() + } + } + } else if (e.key === 'Enter') { + const matched = MNEMONIC_WORDS.filter((word) => word.startsWith(value)) + if (matched.length === 1) { + handleInputChange(matched[0], wordIndex, true) + e.preventDefault() } } - } else if (e.key === 'Enter') { - setShowDropdown(null) - } - } + }, + [filteredWords, handleInputChange], + ) return (
@@ -102,18 +110,18 @@ export default function MnemonicPhraseInput({ const wordGroup = mnemonicPhrase.slice(outerIndex, Math.min(outerIndex + columns, mnemonicPhrase.length)) return ( -
{ - handleKeyDown(e) - }} - > +
{wordGroup.map((givenWord, innerIndex) => { const wordIndex = outerIndex + innerIndex const isCurrentActive = wordIndex === activeIndex return ( -
+
{ + handleKeyDown(e, givenWord, wordIndex) + }} + > (inputRefs.current[wordIndex] = el)} index={wordIndex} @@ -123,7 +131,7 @@ export default function MnemonicPhraseInput({ disabled={isDisabled ? isDisabled(wordIndex) : undefined} onFocus={() => { setActiveIndex(wordIndex) - handleDropdownValue(givenWord, wordIndex) + updateFilteredWords(givenWord, wordIndex) }} autoFocus={isCurrentActive} /> @@ -132,7 +140,7 @@ export default function MnemonicPhraseInput({ ref={dropdownRef} show={showDropdown === wordIndex} words={filteredWords} - onSelect={(word) => handleSelectWord(word, wordIndex)} + onSelect={(word) => handleInputChange(word, wordIndex, true)} /> )}