Fix: Dropdown schloss nicht bei Klick ausserhalb (Liste war kuenstlich auf volle Bildschirmhoehe gestreckt)

This commit is contained in:
2026-07-20 23:24:48 +02:00
parent 724f076c8c
commit 7cf64404c5
2 changed files with 10 additions and 10 deletions

View File

@@ -97,7 +97,6 @@ export function HomePage() {
const { data: readLaterItems } = useReadLater(); const { data: readLaterItems } = useReadLater();
const inputRef = useRef<HTMLInputElement>(null); const inputRef = useRef<HTMLInputElement>(null);
const searchContainerRef = useRef<HTMLDivElement>(null); const searchContainerRef = useRef<HTMLDivElement>(null);
const resultsContainerRef = useRef<HTMLDivElement>(null);
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const isSearching = query.trim().length > 0; const isSearching = query.trim().length > 0;
const isLoading = servicesLoading || bookmarksLoading; const isLoading = servicesLoading || bookmarksLoading;
@@ -176,15 +175,16 @@ export function HomePage() {
}, [isSearching]); }, [isSearching]);
// Klick außerhalb von Suchfeld+Trefferliste klappt das Dropdown wieder ein // Klick außerhalb von Suchfeld+Trefferliste klappt das Dropdown wieder ein
// (Desktop-Verhalten, Text bleibt erhalten). Trefferliste liegt als // (Desktop-Verhalten, Text bleibt erhalten). Bewusst gegen das tatsächlich
// eigener Scroll-Container außerhalb von searchContainerRef, daher beide // gerenderte Listbox-Element geprüft (closest('[role="listbox"]')), nicht
// prüfen - sonst schließt ein Klick auf einen Treffer das Dropdown, bevor // gegen dessen umgebende Scroll-Wrapper-Divs - die sind aus Layoutgründen
// dessen eigener onClick überhaupt feuert. // auf volle Resthöhe gestreckt (siehe max-h-full-Trick in ResultsList),
// ein Ref darauf hätte fast den ganzen Bildschirm als "innerhalb" gezählt.
useEffect(() => { useEffect(() => {
function onPointerDown(e: MouseEvent) { function onPointerDown(e: MouseEvent) {
const target = e.target as Node; const target = e.target as HTMLElement;
const insideSearch = searchContainerRef.current?.contains(target); const insideSearch = searchContainerRef.current?.contains(target) ?? false;
const insideResults = resultsContainerRef.current?.contains(target); const insideResults = target.closest('[role="listbox"]') !== null;
if (!insideSearch && !insideResults) { if (!insideSearch && !insideResults) {
setResultsVisible(false); setResultsVisible(false);
} }
@@ -346,7 +346,7 @@ export function HomePage() {
{/* Scrollender Bereich: NUR die Trefferliste scrollt, nicht die ganze Seite */} {/* Scrollender Bereich: NUR die Trefferliste scrollt, nicht die ganze Seite */}
{isSearching && resultsVisible ? ( {isSearching && resultsVisible ? (
<div ref={resultsContainerRef} className="min-h-0 flex-1 px-6 pb-4"> <div className="min-h-0 flex-1 px-6 pb-4">
<div className="mx-auto h-full max-w-xl"> <div className="mx-auto h-full max-w-xl">
{isLoading ? ( {isLoading ? (
<p className="text-center text-sm text-black/40 dark:text-white/40">Lade </p> <p className="text-center text-sm text-black/40 dark:text-white/40">Lade </p>

View File

@@ -56,7 +56,7 @@ export function ResultsList({
return ( return (
<ul <ul
role="listbox" role="listbox"
className="flex h-full flex-col overflow-y-auto rounded-2xl border border-black/10 className="flex max-h-full flex-col overflow-y-auto rounded-2xl border border-black/10
bg-white/80 shadow-lg backdrop-blur-md dark:border-white/10 dark:bg-white/5" bg-white/80 shadow-lg backdrop-blur-md dark:border-white/10 dark:bg-white/5"
> >
{results.map((item, index) => { {results.map((item, index) => {