Fix bug where sometimes Autocomplete menu would not show even though suggestions exist

This commit is contained in:
Henrik Giesel 2021-09-08 19:18:37 +02:00
parent 8d8a559f73
commit 8fd4412dbe
4 changed files with 26 additions and 51 deletions

View File

@ -11,11 +11,12 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
export { className as class };
export let labelledby: string | undefined = undefined;
export let show = false;
setContext(dropdownKey, null);
</script>
<div {id} class="dropdown-menu" aria-labelledby={labelledby}>
<div {id} class="dropdown-menu" class:show aria-labelledby={labelledby}>
<div class="dropdown-content {className}">
<slot />
</div>

View File

@ -14,7 +14,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
export let toggleOpen = true;
export let drop: "down" | "up" = "down";
let placement: string;
let placement: "bottom" | "top";
$: switch (drop) {
case "down":
placement = "bottom";
@ -52,10 +52,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
dropdown = new Dropdown(toggle, {
autoClose,
popperConfig: (defaultConfig: Record<string, any>) => ({
...defaultConfig,
placement,
}),
popperConfig: { placement },
} as any);
if (autoOpen) {

View File

@ -14,7 +14,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
import Tag from "./Tag.svelte";
import WithAutocomplete from "./WithAutocomplete.svelte";
import ButtonToolbar from "components/ButtonToolbar.svelte";
import { controlPressed } from "lib/keys";
import type { Tag as TagType } from "./tags";
import {
attachId,
@ -34,12 +33,17 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
tags = names.map(replaceWithUnicodeSeparator).map(attachId);
}
const noSuggestions = Promise.resolve([]);
let suggestionsPromise: Promise<string[]> = noSuggestions;
function saveTags(): void {
bridgeCommand(
`saveTags:${JSON.stringify(
tags.map((tag) => tag.name).map(replaceWithColons)
)}`
);
suggestionsPromise = noSuggestions;
}
let active: number | null = null;
@ -48,9 +52,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
let activeInput: HTMLInputElement;
let autocomplete: any;
const noSuggestions = Promise.resolve([]);
let suggestionsPromise: Promise<string[]> = noSuggestions;
let autocompleteDisabled: boolean = false;
async function fetchSuggestions(input: string): Promise<string[]> {
const data = await postRequest(
@ -67,12 +69,13 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
const activeTag = tags[active!];
const activeName = activeTag.name;
suggestionsPromise =
activeName.length === 0
autocompleteDisabled = activeName.length === 0;
suggestionsPromise = autocompleteDisabled
? noSuggestions
: fetchSuggestions(activeTag.name).then((names: string[]): string[] =>
names.map(replaceWithUnicodeSeparator)
);
: fetchSuggestions(activeTag.name).then((names: string[]): string[] => {
autocompleteDisabled = names.length === 0;
return names.map(replaceWithUnicodeSeparator);
});
}
function onAutocomplete(selected: string): void {
@ -254,27 +257,7 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
activeAfterBlur = null;
}
function isPrintableKey(event: KeyboardEvent): boolean {
return event.key.length === 1 && !controlPressed(event);
}
function isDeletionKey(event: KeyboardEvent): boolean {
return event.code === "Backspace" || event.code === "Delete";
}
function onKeydown(event: KeyboardEvent): void {
const visible = autocomplete.isVisible();
const printable = isPrintableKey(event);
const deletion = isDeletionKey(event);
if (!visible) {
if (printable || deletion) {
autocomplete.show();
} else {
return;
}
}
switch (event.code) {
case "ArrowUp":
autocomplete.selectPrevious();
@ -464,12 +447,11 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
on:select={({ detail }) => onAutocomplete(detail.selected)}
on:choose={({ detail }) => onAutocomplete(detail.chosen)}
let:createAutocomplete
let:disabled
>
<TagInput
id={tag.id}
class="tag-input position-absolute start-0 top-0 ps-2 py-0"
{disabled}
disabled={autocompleteDisabled}
bind:name={activeName}
bind:input={activeInput}
on:focus={() => {

View File

@ -17,17 +17,17 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
export let drop: "down" | "up" = "down";
export let suggestionsPromise: Promise<string[]>;
let disabled = true;
let dropdown: Dropdown;
let show = false;
let suggestionsItems: string[] = [];
$: suggestionsPromise.then((items) => {
if (items.length > 0) {
disabled = false;
show = items.length > 0;
if (show) {
dropdown.show();
} else {
dropdown.hide();
disabled = true;
}
suggestionsItems = items;
@ -83,7 +83,9 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
async function chooseSelected() {
active = true;
dispatch("choose", { chosen: suggestionsItems[selected ?? -1] });
await tick();
show = false;
}
async function update() {
@ -97,10 +99,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
return selected !== null;
}
function disable(): void {
disabled = true;
}
const createAutocomplete =
(createDropdown: (element: HTMLElement) => Dropdown) =>
(element: HTMLElement): any => {
@ -116,7 +114,6 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
chooseSelected,
update,
hasSelected,
disable,
};
return api;
@ -141,14 +138,12 @@ License: GNU AGPL, version 3 or later; http://www.gnu.org/licenses/agpl.html
setSelected(index);
}
}
onDestroy(() => dropdown?.dispose());
</script>
<WithDropdown {drop} toggleOpen={false} let:createDropdown>
<slot createAutocomplete={createAutocomplete(createDropdown)} {disabled} />
<slot createAutocomplete={createAutocomplete(createDropdown)} />
<DropdownMenu class={className}>
<DropdownMenu class={className} {show}>
{#each suggestionsItems as suggestion, index}
<AutocompleteItem
selected={index === selected}