Take most recent input as basis for suggestions

This commit is contained in:
Henrik Giesel 2021-09-08 12:25:26 +02:00
parent 4da1c77220
commit 9e1f2aa262
2 changed files with 47 additions and 55 deletions

View file

@ -62,8 +62,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
} }
function updateSuggestions(): void { function updateSuggestions(): void {
const currentInput = tags[tags.length - 1].name; suggestionsPromise = fetchSuggestions(activeName).then(
suggestionsPromise = fetchSuggestions(currentInput).then(
(names: string[]): string[] => names.map(replaceWithUnicodeSeparator) (names: string[]): string[] => names.map(replaceWithUnicodeSeparator)
); );
} }
@ -78,6 +77,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
function updateTagName(tag: TagType): void { function updateTagName(tag: TagType): void {
tag.name = activeName; tag.name = activeName;
tags = tags; tags = tags;
autocomplete.update();
} }
function setActiveAfterBlur(value: number): void { function setActiveAfterBlur(value: number): void {
@ -287,13 +288,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
autocomplete.chooseSelected(); autocomplete.chooseSelected();
event.preventDefault(); event.preventDefault();
break; break;
default:
if (printable || deletion) {
autocomplete.update();
}
break;
} }
} }

View file

@ -15,67 +15,71 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
export { className as class }; export { className as class };
export let drop: "down" | "up" | "left" | "right" = "down"; export let drop: "down" | "up" | "left" | "right" = "down";
export let suggestionsPromise: Promise<string[]>; export let suggestionsPromise: Promise<string[]>;
let target: HTMLElement; let target: HTMLElement;
let dropdown: Dropdown; let dropdown: Dropdown;
let suggestionsItems: string[] = [];
$: suggestionsPromise.then((items) => {
if (items.length > 0) {
// disabled class will tell Bootstrap not to show menu on clicking
target.classList.remove("disabled");
dropdown.show();
} else {
dropdown.hide();
target.classList.add("disabled");
}
suggestionsItems = items;
});
let selected: number | null = null; let selected: number | null = null;
let active: boolean = false; let active: boolean = false;
const dispatch = createEventDispatcher(); const dispatch = createEventDispatcher();
/**
* select as currently highlighted item
*/
async function selectNext() { async function selectNext() {
const suggestions = await suggestionsPromise;
if (selected === null) { if (selected === null) {
selected = 0; selected = 0;
} else if (selected >= suggestions.length - 1) { } else if (selected >= suggestionsItems.length - 1) {
selected = null; selected = null;
} else { } else {
selected++; selected++;
} }
dispatch("select", { selected: suggestions[selected ?? -1] }); dispatch("select", { selected: suggestionsItems[selected ?? -1] });
} }
async function selectPrevious() { async function selectPrevious() {
const suggestions = await suggestionsPromise;
if (selected === null) { if (selected === null) {
selected = suggestions.length - 1; selected = suggestionsItems.length - 1;
} else if (selected === 0) { } else if (selected === 0) {
selected = null; selected = null;
} else { } else {
selected--; selected--;
} }
dispatch("select", { selected: suggestions[selected ?? -1] }); dispatch("select", { selected: suggestionsItems[selected ?? -1] });
} }
/**
* choose as accepted suggestion
*/
async function chooseSelected() { async function chooseSelected() {
const suggestions = await suggestionsPromise;
active = true; active = true;
dispatch("choose", { chosen: suggestions[selected ?? -1] }); dispatch("choose", { chosen: suggestionsItems[selected ?? -1] });
} }
async function update() { async function update() {
dropdown = dropdown as Dropdown; dropdown = dropdown as Dropdown;
dropdown.update(); dropdown.update();
await tick();
dispatch("update"); dispatch("update");
const [, suggestions] = await Promise.all([tick(), suggestionsPromise]);
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");
}
} }
function hasSelected(): boolean { function hasSelected(): boolean {
@ -85,8 +89,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
const createAutocomplete = const createAutocomplete =
(createDropdown: (element: HTMLElement) => Dropdown) => (createDropdown: (element: HTMLElement) => Dropdown) =>
(element: HTMLElement): any => { (element: HTMLElement): any => {
target = element;
dropdown = createDropdown(element); dropdown = createDropdown(element);
target = element;
const api = { const api = {
hide: dropdown.hide, hide: dropdown.hide,
@ -103,8 +107,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
return api; return api;
}; };
onDestroy(() => dropdown?.dispose());
function setSelected(index: number): void { function setSelected(index: number): void {
selected = index; selected = index;
active = true; active = true;
@ -115,9 +117,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
} }
async function selectIndex(index: number): Promise<void> { async function selectIndex(index: number): Promise<void> {
const suggestions = await suggestionsPromise;
active = false; active = false;
dispatch("select", { selected: suggestions[index] }); dispatch("select", { selected: suggestionsItems[index] });
} }
function selectIfMousedown(event: MouseEvent, index: number): void { function selectIfMousedown(event: MouseEvent, index: number): void {
@ -125,16 +126,15 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
setSelected(index); setSelected(index);
} }
} }
onDestroy(() => dropdown?.dispose());
</script> </script>
<WithDropdown {drop} toggleOpen={false} let:createDropdown> <WithDropdown {drop} toggleOpen={false} let:createDropdown>
<slot createAutocomplete={createAutocomplete(createDropdown)} /> <slot createAutocomplete={createAutocomplete(createDropdown)} />
<DropdownMenu class={className}> <DropdownMenu class={className}>
{#await suggestionsPromise} {#each suggestionsItems as suggestion, index}
<AutocompleteItem>...</AutocompleteItem>
{:then suggestions}
{#each suggestions as suggestion, index}
<AutocompleteItem <AutocompleteItem
selected={index === selected} selected={index === selected}
active={index === selected && active} active={index === selected && active}
@ -144,10 +144,8 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
chooseSelected(); chooseSelected();
}} }}
on:mouseenter={(event) => selectIfMousedown(event, index)} on:mouseenter={(event) => selectIfMousedown(event, index)}
on:mouseleave={() => (active = false)} on:mouseleave={() => (active = false)}>{suggestion}</AutocompleteItem
>{suggestion}</AutocompleteItem
> >
{/each} {/each}
{/await}
</DropdownMenu> </DropdownMenu>
</WithDropdown> </WithDropdown>