diff --git a/webui/src/components/utils/Input.svelte b/webui/src/components/utils/Input.svelte index 33fb4a9..c81ffed 100644 --- a/webui/src/components/utils/Input.svelte +++ b/webui/src/components/utils/Input.svelte @@ -29,6 +29,7 @@ on:input={onInput} on:focus={() => (focused = true)} on:blur={() => (focused = false)} + on:keydown {disabled} /> diff --git a/webui/src/components/utils/Selector.svelte b/webui/src/components/utils/Selector.svelte index a6b0af8..1a0e951 100644 --- a/webui/src/components/utils/Selector.svelte +++ b/webui/src/components/utils/Selector.svelte @@ -182,14 +182,59 @@ visible = false; } - let inputFocused = false; - let hover = false; - $: visible = (inputFocused || hover) && Boolean(options.length); + let listEl: HTMLUListElement; + let optionFocusIndex: number = -1; + function handleArrowKeys(ev: KeyboardEvent) { + if (!options.length) { + return; + } + + const optionEls = Array.from(listEl.children) as HTMLLIElement[]; + const currentIndex = optionEls.findIndex( + (el) => document.activeElement === el + ); + + let targetIndex = currentIndex; + switch (ev.key) { + case "ArrowDown": + targetIndex += 1; + + // pressed down on last + if (targetIndex >= optionEls.length) { + targetIndex = 0; + } + break; + case "ArrowUp": + targetIndex -= 1; + + // pressed up on input + if (targetIndex == -2) { + targetIndex = optionEls.length - 1; + } + + // pressed up on first + if (targetIndex == -1) { + focus(); + return; + } + break; + default: + return; // early return, stop processing + } + + if (optionEls[targetIndex]) { + optionEls[targetIndex].focus(); + } + } let input: Input; export function focus() { input.focus(); } + + let inputFocused = false; + $: visible = + (inputFocused || optionFocusIndex > -1) && Boolean(options.length);
@@ -206,18 +251,28 @@ bind:value={inputValue} on:input={onInput} on:focusChange={(ev) => (inputFocused = ev.detail)} + on:keydown={handleArrowKeys} {disabled} {placeholder} /> {/if} -