this repo has no description
at main 3.1 kB view raw
1import './TextInput.css'; 2 3import { createSignal, For, Show } from "solid-js" 4 5export interface TextInputProps{ 6 placeholder: string, 7 value?: string, 8 requestSuggestions?: ( text: string ) => Promise<string[]>, 9 change?: ( text: string ) => void 10} 11 12export let TextInput = ( props: TextInputProps ) => { 13 let [ suggestionsOpen, setSuggestionsOpen ] = createSignal(false); 14 let [ suggestions, setSuggestions ] = createSignal<string[]>([]) 15 16 let input!: HTMLInputElement; 17 18 let suggestionsContainer!: HTMLDivElement; 19 let suggestionsIndex = 0; 20 21 let onInput = async () => { 22 let s = null; 23 24 if(props.requestSuggestions){ 25 s = await props.requestSuggestions(input.value); 26 27 if(s != suggestions()){ 28 setSuggestions(s); 29 30 let open = s !== null && s.length > 0 && input.value.length > 0; 31 32 setSuggestionsOpen(open); 33 if(open)changeSelection(() => { suggestionsIndex = 0; }); 34 } 35 } 36 } 37 38 let onKeyDown = ( ev: KeyboardEvent ) => { 39 switch(ev.key){ 40 case 'ArrowDown': 41 changeSelection(() => { 42 suggestionsIndex++; 43 if(suggestionsIndex >= suggestionsContainer.children.length)suggestionsIndex = suggestionsContainer.children.length - 1; 44 }); 45 break; 46 case 'ArrowUp': 47 changeSelection(() => { 48 suggestionsIndex--; 49 if(suggestionsIndex < 0)suggestionsIndex = 0; 50 }); 51 break; 52 case 'Enter': 53 let currentDiv = suggestionsContainer.children[suggestionsIndex]; 54 if(currentDiv)input.value = currentDiv.innerHTML; 55 56 props.change ? props.change(input.value) : null 57 setSuggestionsOpen(false); 58 break; 59 } 60 } 61 62 let changeSelection = ( cb: () => void ) => { 63 for(let child of suggestionsContainer.children) 64 child.classList.remove('suggestion-selected'); 65 66 cb(); 67 68 let height = suggestionsIndex * 19; 69 suggestionsContainer.scrollTo(0, height - 150); 70 71 let currentDiv = suggestionsContainer.children[suggestionsIndex]; 72 if(currentDiv)currentDiv.classList.add('suggestion-selected'); 73 } 74 75 return ( 76 <> 77 <div style={{ width: '100%' }}> 78 <input 79 style={{ width: '100%' }} 80 type="text" 81 placeholder={ props.placeholder } 82 value={ props.value || "" } 83 onChange={() => props.change ? props.change(input.value) : null} 84 onInput={onInput} 85 onKeyDown={onKeyDown} 86 onFocusOut={() => setTimeout(() => { 87 setSuggestionsOpen(false); 88 suggestionsIndex = -1; 89 }, 100)} 90 ref={input} /> 91 92 <Show when={suggestionsOpen()}> 93 <div input-dropdown ref={suggestionsContainer}> 94 <For each={suggestions()}> 95 { item => <div onClick={( el ) => { 96 let thisEl = el.target; 97 98 input.value = thisEl.innerHTML; 99 setSuggestionsOpen(false); 100 101 props.change ? props.change(input.value) : null 102 suggestionsIndex = -1; 103 }}>{ item }</div> } 104 </For> 105 </div> 106 </Show> 107 </div> 108 </> 109 ) 110}