109 lines
3.4 KiB
TypeScript
109 lines
3.4 KiB
TypeScript
import { useMemo, useState } from 'react'
|
|
import { getWordSuggestions } from '@/lib/keyManagementBIP39'
|
|
import { decideAutocompleteKeyAction } from './autocompleteKeyDecision'
|
|
|
|
export interface WordAutocompleteState {
|
|
suggestions: string[]
|
|
showSuggestions: boolean
|
|
setShowSuggestions: (value: boolean) => void
|
|
selectedIndex: number
|
|
handleChange: (event: React.ChangeEvent<HTMLInputElement>) => void
|
|
handleKeyDown: (event: React.KeyboardEvent<HTMLInputElement>) => void
|
|
applySuggestion: (suggestion: string) => void
|
|
}
|
|
|
|
export function useWordAutocomplete(params: {
|
|
value: string
|
|
onChange: (value: string) => void
|
|
inputRef: React.RefObject<HTMLInputElement | null>
|
|
}): WordAutocompleteState {
|
|
const [showSuggestions, setShowSuggestions] = useState(false)
|
|
const [selectedIndex, setSelectedIndex] = useState(-1)
|
|
const suggestions = useWordSuggestions(params.value)
|
|
|
|
const applySuggestion = (suggestion: string): void => {
|
|
applySuggestionImpl({ suggestion, onChange: params.onChange, setShowSuggestions, inputRef: params.inputRef })
|
|
}
|
|
const handleChange = (event: React.ChangeEvent<HTMLInputElement>): void => {
|
|
handleChangeImpl({ event, onChange: params.onChange, setShowSuggestions, setSelectedIndex })
|
|
}
|
|
const handleKeyDown = (event: React.KeyboardEvent<HTMLInputElement>): void => {
|
|
handleKeyDownImpl({
|
|
event,
|
|
suggestions,
|
|
selectedIndex,
|
|
setSelectedIndex,
|
|
setShowSuggestions,
|
|
inputRef: params.inputRef,
|
|
applySuggestion,
|
|
})
|
|
}
|
|
|
|
return { suggestions, showSuggestions, setShowSuggestions, selectedIndex, handleChange, handleKeyDown, applySuggestion }
|
|
}
|
|
|
|
function useWordSuggestions(value: string): string[] {
|
|
return useMemo((): string[] => {
|
|
if (value.length === 0) {
|
|
return []
|
|
}
|
|
return getWordSuggestions(value, 5)
|
|
}, [value])
|
|
}
|
|
|
|
function applySuggestionImpl(params: {
|
|
suggestion: string
|
|
onChange: (value: string) => void
|
|
setShowSuggestions: (value: boolean) => void
|
|
inputRef: React.RefObject<HTMLInputElement | null>
|
|
}): void {
|
|
params.onChange(params.suggestion)
|
|
params.setShowSuggestions(false)
|
|
params.inputRef.current?.blur()
|
|
}
|
|
|
|
function handleChangeImpl(params: {
|
|
event: React.ChangeEvent<HTMLInputElement>
|
|
onChange: (value: string) => void
|
|
setShowSuggestions: (value: boolean) => void
|
|
setSelectedIndex: (value: number) => void
|
|
}): void {
|
|
const newValue = params.event.target.value.trim().toLowerCase()
|
|
params.setSelectedIndex(-1)
|
|
params.setShowSuggestions(newValue.length > 0)
|
|
params.onChange(newValue)
|
|
}
|
|
|
|
function handleKeyDownImpl(params: {
|
|
event: React.KeyboardEvent<HTMLInputElement>
|
|
suggestions: string[]
|
|
selectedIndex: number
|
|
setSelectedIndex: (value: number) => void
|
|
setShowSuggestions: (value: boolean) => void
|
|
inputRef: React.RefObject<HTMLInputElement | null>
|
|
applySuggestion: (suggestion: string) => void
|
|
}): void {
|
|
const decision = decideAutocompleteKeyAction({
|
|
key: params.event.key,
|
|
selectedIndex: params.selectedIndex,
|
|
suggestionsCount: params.suggestions.length,
|
|
})
|
|
if (decision.action === 'none') {
|
|
return
|
|
}
|
|
params.event.preventDefault()
|
|
if (decision.action === 'move') {
|
|
params.setSelectedIndex(decision.nextIndex)
|
|
return
|
|
}
|
|
if (decision.action === 'escape') {
|
|
params.setShowSuggestions(false)
|
|
params.inputRef.current?.blur()
|
|
return
|
|
}
|
|
const suggestion = params.suggestions[decision.index]
|
|
if (suggestion) {
|
|
params.applySuggestion(suggestion)
|
|
}
|
|
}
|