diff --git a/frontend/src/lib/components/PersonTypeahead.svelte b/frontend/src/lib/components/PersonTypeahead.svelte index e51eb7c4..985cdd42 100644 --- a/frontend/src/lib/components/PersonTypeahead.svelte +++ b/frontend/src/lib/components/PersonTypeahead.svelte @@ -3,6 +3,7 @@ import { untrack } from 'svelte'; import type { components } from '$lib/generated/api'; import { m } from '$lib/paraglide/messages.js'; import { clickOutside } from '$lib/actions/clickOutside'; +import { createTypeahead } from '$lib/hooks/useTypeahead.svelte'; type Person = components['schemas']['Person']; interface Props { @@ -50,10 +51,23 @@ $effect(() => { } }); -let results: Person[] = $state([]); -let showDropdown = $state(false); -let loading = $state(false); -let debounceTimer: ReturnType; +const typeahead = createTypeahead({ + fetchUrl: async (term) => { + const personId = restrictToCorrespondentsOf; + if (personId) { + const url = + term.length >= 1 + ? `/api/persons/${personId}/correspondents?q=${encodeURIComponent(term)}` + : `/api/persons/${personId}/correspondents`; + const res = await fetch(url); + return res.ok ? await res.json() : []; + } + if (term.length < 1) return []; + const res = await fetch(`/api/persons?q=${encodeURIComponent(term)}`); + return res.ok ? await res.json() : []; + }, + debounceMs: 300 +}); function handleInput() { if (value && searchTerm !== initialName) { @@ -61,69 +75,38 @@ function handleInput() { onchange?.(''); } - showDropdown = true; - clearTimeout(debounceTimer); - - debounceTimer = setTimeout(async () => { - const term = untrack(() => searchTerm); - const correspondentsOf = untrack(() => restrictToCorrespondentsOf); - loading = true; - try { - let url: string; - if (correspondentsOf) { - if (term.length >= 1) { - url = `/api/persons/${correspondentsOf}/correspondents?q=${encodeURIComponent(term)}`; - } else { - url = `/api/persons/${correspondentsOf}/correspondents`; - } - } else { - if (term.length < 1) { - results = []; - loading = false; - return; - } - url = `/api/persons?q=${encodeURIComponent(term)}`; - } - const res = await fetch(url); - results = res.ok ? await res.json() : []; - } catch (e) { - console.error('Suche fehlgeschlagen', e); - results = []; - } finally { - loading = false; - } - }, 300); + const term = untrack(() => searchTerm); + typeahead.setQuery(term); } function handleFocus() { onfocused?.(); - showDropdown = true; if (restrictToCorrespondentsOf) { const personId = untrack(() => restrictToCorrespondentsOf)!; - loading = true; (async () => { try { const res = await fetch(`/api/persons/${personId}/correspondents`); - results = res.ok ? await res.json() : []; + const persons: Person[] = res.ok ? await res.json() : []; + typeahead.openWith(persons); } catch (e) { console.error('Suche fehlgeschlagen', e); - results = []; - } finally { - loading = false; + typeahead.openWith([]); } })(); + } else { + typeahead.openWith(typeahead.results); } } function selectPerson(person: Person) { value = person.id!; searchTerm = person.displayName; - showDropdown = false; + typeahead.close(); onchange?.(person.id!); } -
(showDropdown = false)}> +
typeahead.close()}>