From bbef2ab6b448852fce6e8df0b5f25887b9955ab8 Mon Sep 17 00:00:00 2001 From: Henrik Giesel Date: Mon, 28 Jun 2021 19:21:01 +0200 Subject: [PATCH] Have WithAutocomplete export its API allows it to be used in more contexts --- ts/editor/TagEditor.svelte | 58 +++++++++++++-- ts/editor/WithAutocomplete.svelte | 116 ++++++++++++++++-------------- 2 files changed, 115 insertions(+), 59 deletions(-) diff --git a/ts/editor/TagEditor.svelte b/ts/editor/TagEditor.svelte index f7866de1e..b87927c40 100644 --- a/ts/editor/TagEditor.svelte +++ b/ts/editor/TagEditor.svelte @@ -45,7 +45,21 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html } } - function updateSuggestions(): void {} + let addOrPop = true; + function updateSuggestions(): void { + if (suggestions.length === 0) { + addOrPop = false; + } else if (suggestions.length > 3) { + addOrPop = true; + } + + if (addOrPop) { + suggestions.pop(); + suggestions = suggestions; + } else { + suggestions = suggestions.concat(["another"]); + } + } function updateWithTagName(tag: TagType): void { tag.name = activeName; @@ -210,6 +224,38 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html active = activeAfterBlur; activeAfterBlur = null; } + + function update(event: KeyboardEvent, autocomplete): void { + switch (event.code) { + case "ArrowUp": + autocomplete.selectNext(); + event.preventDefault(); + break; + + case "ArrowDown": + autocomplete.selectPrevious(); + event.preventDefault(); + break; + + case "Tab": + if (event.shiftKey) { + autocomplete.selectNext(); + } else { + autocomplete.selectPrevious(); + } + event.preventDefault(); + break; + + case "Enter": + autocomplete.chooseSelected(); + event.preventDefault(); + break; + + default: + autocomplete.update(); + break; + } + } @@ -224,14 +270,18 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html {suggestions} on:autocomplete={onAutocomplete} on:update={updateSuggestions} - let:updateAutocomplete + let:createAutocomplete + let:autocomplete > (activeName = tag.name)} - on:keydown={updateAutocomplete} + on:focus={() => { + activeName = tag.name; + createAutocomplete(activeInput); + }} + on:keydown={(event) => update(event, autocomplete)} on:input={() => updateWithTagName(tag)} on:tagsplit={({ detail }) => splitTag(index, detail.start, detail.end)} diff --git a/ts/editor/WithAutocomplete.svelte b/ts/editor/WithAutocomplete.svelte index a85b4806d..8320da059 100644 --- a/ts/editor/WithAutocomplete.svelte +++ b/ts/editor/WithAutocomplete.svelte @@ -16,7 +16,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html export let suggestions: string[]; - let autocomplete: Dropdown | undefined; + let dropdown: Dropdown | undefined; let selected: number | null = null; // blue highlight @@ -24,65 +24,71 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html const dispatch = createEventDispatcher(); - const updateAutocomplete = - (createDropdown: (element: HTMLElement) => Dropdown) => - async (event: KeyboardEvent): Promise => { - const target = event.target as HTMLInputElement; - - if (!autocomplete) { - autocomplete = createDropdown(target); - } - - autocomplete.update(); - - if ( - event.code === "ArrowDown" || - (event.code === "Tab" && event.shiftKey) - ) { - event.preventDefault(); - if (selected === null) { - selected = suggestions.length - 1; - } else if (selected === 0) { - selected = null; - } else { - selected--; - } - - const choice = suggestions[selected ?? -1]; - dispatch("autocomplete", { choice }); - } else if (event.code === "ArrowUp" || event.code === "Tab") { - event.preventDefault(); - if (selected === null) { - selected = 0; - } else if (selected >= suggestions.length - 1) { - selected = null; - } else { - selected++; - } - - const choice = suggestions[selected ?? -1]; - dispatch("autocomplete", { choice }); - } else if (event.code === "Enter") { - event.preventDefault(); - active = true; - } else { - dispatch("update"); - } - - await tick(); - - if (suggestions.length > 0) { - autocomplete.show(); - } else { - autocomplete.hide(); - } + const createAutocomplete = + (createDropdown: (target: HTMLElement) => Dropdown) => + (target: HTMLElement): void => { + dropdown = createDropdown(target); }; - onDestroy(() => autocomplete?.dispose()); + 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; + } else if (selected >= suggestions.length - 1) { + selected = null; + } else { + selected++; + } + + const choice = suggestions[selected ?? -1]; + dispatch("autocomplete", { choice }); + } + + function chooseSelected() { + active = true; + } + + async function update() { + dropdown = dropdown as Dropdown; + dropdown.update(); + dispatch("update"); + + await tick(); + + if (suggestions.length > 0) { + dropdown.show(); + } else { + dropdown.hide(); + } + } + + const autocomplete = { + hide: () => dropdown!.hide(), + show: () => dropdown!.show(), + toggle: () => dropdown!.toggle(), + selectPrevious, + selectNext, + chooseSelected, + update, + }; + + onDestroy(() => dropdown?.dispose()); - + {#each suggestions as suggestion, i}