feat: add arrow key support to Selector

feat/type-attributes
Tomáš Mládek 2023-01-22 14:53:11 +01:00
parent e5fc315852
commit 8eb4466222
2 changed files with 73 additions and 12 deletions

View File

@ -29,6 +29,7 @@
on:input={onInput} on:input={onInput}
on:focus={() => (focused = true)} on:focus={() => (focused = true)}
on:blur={() => (focused = false)} on:blur={() => (focused = false)}
on:keydown
{disabled} {disabled}
/> />
</div> </div>

View File

@ -182,14 +182,59 @@
visible = false; visible = false;
} }
let inputFocused = false; let listEl: HTMLUListElement;
let hover = false; let optionFocusIndex: number = -1;
$: visible = (inputFocused || hover) && Boolean(options.length); 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; let input: Input;
export function focus() { export function focus() {
input.focus(); input.focus();
} }
let inputFocused = false;
$: visible =
(inputFocused || optionFocusIndex > -1) && Boolean(options.length);
</script> </script>
<div class="selector"> <div class="selector">
@ -206,18 +251,28 @@
bind:value={inputValue} bind:value={inputValue}
on:input={onInput} on:input={onInput}
on:focusChange={(ev) => (inputFocused = ev.detail)} on:focusChange={(ev) => (inputFocused = ev.detail)}
on:keydown={handleArrowKeys}
{disabled} {disabled}
{placeholder} {placeholder}
/> />
{/if} {/if}
<ul <ul class="options" class:visible bind:this={listEl}>
class="options" {#each options.slice(0, MAX_OPTIONS) as option, idx}
class:visible <!-- svelte-ignore a11y-no-noninteractive-tabindex -->
on:mouseenter={() => (hover = true)} <li
on:mouseleave={() => (hover = false)} tabindex="0"
> on:click={() => set(option)}
{#each options.slice(0, MAX_OPTIONS) as option} on:mousemove={() => focus()}
<li on:click={() => set(option)}> on:focus={() => (optionFocusIndex = idx)}
on:blur={() => (optionFocusIndex = -1)}
on:keydown={(ev) => {
if (ev.key === "Enter") {
set(option);
} else {
handleArrowKeys(ev);
}
}}
>
{#if option.attribute} {#if option.attribute}
{option.attribute} {option.attribute}
{:else if option.value} {:else if option.value}
@ -282,11 +337,16 @@
cursor: pointer; cursor: pointer;
padding: 0.5em; padding: 0.5em;
transition: background-color 0.2s; transition: background-color 0.1s;
&:hover { &:hover {
background-color: var(--background-lighter); background-color: var(--background-lighter);
} }
&:focus {
background-color: var(--background-lighter);
outline: none;
}
.type, .type,
.content { .content {
display: inline-block; display: inline-block;