From a515a9899b96ef710980b022f437d8866d9bdfdc Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Mon, 28 Jun 2021 23:39:46 +0200 Subject: [PATCH] Don't allow Enter/Tab/Arrows for Autocomplete, if not active Don't show Autocomplete, if there are no items available --- ts/components/WithDropdownMenu.svelte | 5 ++ ts/editor/TagEditor.svelte | 41 +++++++--------- ts/editor/WithAutocomplete.svelte | 68 +++++++++++++++------------ 3 files changed, 59 insertions(+), 55 deletions(-) diff --git a/ts/components/WithDropdownMenu.svelte b/ts/components/WithDropdownMenu.svelte index e53bb15c9..14bcec8d0 100644 --- a/ts/components/WithDropdownMenu.svelte +++ b/ts/components/WithDropdownMenu.svelte @@ -25,6 +25,10 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html } } + function isVisible(): boolean { + return (dropdown as any)._menu.classList.contains("show"); + } + /* Normally dropdown and trigger are associated with a /* common ancestor with .dropdown class */ function createDropdown(element: HTMLElement): Dropdown { @@ -43,6 +47,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html /* Set custom menu without using common element with .dropdown */ (dropdown as any)._menu = menu; + Object.defineProperty(dropdown, "isVisible", { value: isVisible }); } return dropdown as Dropdown; diff --git a/ts/editor/TagEditor.svelte b/ts/editor/TagEditor.svelte index b87927c40..63405fa11 100644 --- a/ts/editor/TagEditor.svelte +++ b/ts/editor/TagEditor.svelte @@ -16,9 +16,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html import { attachId, getName } from "./tags"; export let size = isApplePlatform() ? 1.6 : 2.0; - export let tags: TagType[] = []; - export let suggestions = ["en::idioms", "anki::functionality", "math"]; + export let tags: TagType[] = []; export function resetTags(names: string[]): void { tags = names.map(attachId); @@ -35,31 +34,14 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html function onAutocomplete({ detail }) { const activeTag = tags[active!]; - const autocompletionChoice = detail.choice; + const selected = detail.selected; - console.log(autocompletionChoice, activeTag); - if (autocompletionChoice) { - activeName = autocompletionChoice; - } else { - activeName = activeTag.name; - } + activeName = selected ?? activeTag.name; } - let addOrPop = true; - function updateSuggestions(): void { - if (suggestions.length === 0) { - addOrPop = false; - } else if (suggestions.length > 3) { - addOrPop = true; - } + let suggestions: string[] = []; - if (addOrPop) { - suggestions.pop(); - suggestions = suggestions; - } else { - suggestions = suggestions.concat(["another"]); - } - } + function updateSuggestions(): void {} function updateWithTagName(tag: TagType): void { tag.name = activeName; @@ -225,7 +207,17 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html activeAfterBlur = null; } - function update(event: KeyboardEvent, autocomplete): void { + function update(event: KeyboardEvent, autocomplete: any): void { + const visible = autocomplete.isVisible(); + + if ( + !visible && + (event.location === KeyboardEvent.DOM_KEY_LOCATION_STANDARD || + event.location === KeyboardEvent.DOM_KEY_LOCATION_NUMPAD) + ) { + autocomplete.show(); + } + switch (event.code) { case "ArrowUp": autocomplete.selectNext(); @@ -277,6 +269,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html id={tag.id} bind:name={activeName} bind:input={activeInput} + on:click={(event) => event.stopPropagation()} on:focus={() => { activeName = tag.name; createAutocomplete(activeInput); diff --git a/ts/editor/WithAutocomplete.svelte b/ts/editor/WithAutocomplete.svelte index 8320da059..eaee1853b 100644 --- a/ts/editor/WithAutocomplete.svelte +++ b/ts/editor/WithAutocomplete.svelte @@ -16,7 +16,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html export let suggestions: string[]; - let dropdown: Dropdown | undefined; + let target: HTMLElement; + let dropdown: Dropdown; + let autocomplete: any; let selected: number | null = null; // blue highlight @@ -24,25 +26,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html const dispatch = createEventDispatcher(); - const createAutocomplete = - (createDropdown: (target: HTMLElement) => Dropdown) => - (target: HTMLElement): void => { - dropdown = createDropdown(target); - }; - - function selectPrevious() { - if (selected === null) { - selected = suggestions.length - 1; - } else if (selected === 0) { - selected = null; - } else { - selected--; - } - - const choice = suggestions[selected ?? -1]; - dispatch("dropdown", { choice }); - } - function selectNext() { if (selected === null) { selected = 0; @@ -52,8 +35,19 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html selected++; } - const choice = suggestions[selected ?? -1]; - dispatch("autocomplete", { choice }); + dispatch("autocomplete", { selected: suggestions[selected ?? -1] }); + } + + function selectPrevious() { + if (selected === null) { + selected = suggestions.length - 1; + } else if (selected === 0) { + selected = null; + } else { + selected--; + } + + dispatch("autocomplete", { selected: suggestions[selected ?? -1] }); } function chooseSelected() { @@ -69,20 +63,32 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html if (suggestions.length > 0) { dropdown.show(); + // disabled class will tell Bootstrap not to show menu on clicking + target.classList.remove("disabled"); } else { dropdown.hide(); + target.classList.add("disabled"); } } - const autocomplete = { - hide: () => dropdown!.hide(), - show: () => dropdown!.show(), - toggle: () => dropdown!.toggle(), - selectPrevious, - selectNext, - chooseSelected, - update, - }; + const createAutocomplete = + (createDropdown: (element: HTMLElement) => Dropdown) => + (element: HTMLElement): any => { + target = element; + dropdown = createDropdown(element); + autocomplete = { + hide: dropdown.hide.bind(dropdown), + show: dropdown.show.bind(dropdown), + toggle: dropdown.toggle.bind(dropdown), + isVisible: (dropdown as any).isVisible, + selectPrevious, + selectNext, + chooseSelected, + update, + }; + + return autocomplete; + }; onDestroy(() => dropdown?.dispose());