diff --git a/src/components/MnemonicPhraseInput.module.css b/src/components/MnemonicPhraseInput.module.css new file mode 100644 index 00000000..8e6b9a47 --- /dev/null +++ b/src/components/MnemonicPhraseInput.module.css @@ -0,0 +1,4 @@ +.dropdownMenu { + min-width: 100% !important; + max-height: 12.7rem; +} diff --git a/src/components/MnemonicPhraseInput.tsx b/src/components/MnemonicPhraseInput.tsx index c3ae4d73..9e971cc9 100644 --- a/src/components/MnemonicPhraseInput.tsx +++ b/src/components/MnemonicPhraseInput.tsx @@ -1,5 +1,9 @@ import { useEffect, useRef, useState } from 'react' import { Bip39MnemonicWordInput } from './MnemonicWordInput' +import { MNEMONIC_WORDS } from '../constants/bip39words' +import * as rb from 'react-bootstrap' +import { forwardRef } from 'react' +import style from './MnemonicPhraseInput.module.css' interface MnemonicPhraseInputProps { columns?: number @@ -9,6 +13,26 @@ interface MnemonicPhraseInputProps { onChange: (value: MnemonicPhrase) => void } +interface MnemonicDropdownProps { + show: boolean + words: string[] + onSelect: (word: string) => void +} + +const MnemonicDropdown = forwardRef(({ show, words, onSelect }, ref) => ( + + + {words.map((word) => ( +
+ onSelect(word)} className="p-2"> + {word} + +
+ ))} +
+
+)) + export default function MnemonicPhraseInput({ columns = 3, mnemonicPhrase, @@ -18,6 +42,9 @@ export default function MnemonicPhraseInput({ }: MnemonicPhraseInputProps) { const [activeIndex, setActiveIndex] = useState(0) const inputRefs = useRef([]) + const [showDropdown, setShowDropdown] = useState(null) + const [filteredWords, setFilteredWords] = useState(undefined) + const dropdownRef = useRef(null) useEffect(() => { if (activeIndex < mnemonicPhrase.length && isValid && isValid(activeIndex)) { @@ -30,6 +57,43 @@ export default function MnemonicPhraseInput({ } }, [mnemonicPhrase, activeIndex, isValid]) + 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 matched = value ? MNEMONIC_WORDS.filter((word) => word.startsWith(value)) : [] + if (matched.length > 0) { + setShowDropdown(index) + setFilteredWords(matched) + } else { + setShowDropdown(null) + setFilteredWords(undefined) + } + } + + const handleSelectWord = (word: string, index: number) => { + const newPhrase = mnemonicPhrase.map((old, i) => (i === index ? word : old)) + onChange(newPhrase) + setShowDropdown(null) + } + + 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() + } + } + } else if (e.key === 'Enter') { + setShowDropdown(null) + } + } + return (
{mnemonicPhrase.map((_, outerIndex) => { @@ -38,7 +102,13 @@ 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 @@ -48,15 +118,23 @@ export default function MnemonicPhraseInput({ forwardRef={(el: HTMLInputElement) => (inputRefs.current[wordIndex] = el)} index={wordIndex} value={givenWord} - setValue={(value, i) => { - const newPhrase = mnemonicPhrase.map((old, index) => (index === i ? value : old)) - onChange(newPhrase) - }} + setValue={(value) => handleInputChange(value, wordIndex)} isValid={isValid ? isValid(wordIndex) : undefined} disabled={isDisabled ? isDisabled(wordIndex) : undefined} - onFocus={() => setActiveIndex(wordIndex)} + onFocus={() => { + setActiveIndex(wordIndex) + handleDropdownValue(givenWord, wordIndex) + }} autoFocus={isCurrentActive} /> + {filteredWords && ( + handleSelectWord(word, wordIndex)} + /> + )}
) })}