From 85f50c553026ba5a1c60d6b1645ea892d0e3a4e2 Mon Sep 17 00:00:00 2001 From: Kroonk Date: Thu, 21 May 2026 11:35:21 +0200 Subject: [PATCH] fix: make auth dropdown controlled in React to prevent password manager focus-loss from closing it --- client/src/components/Header.tsx | 50 ++++++++++++++----- .../components/auth/AuthDropdownContent.tsx | 5 +- 2 files changed, 42 insertions(+), 13 deletions(-) diff --git a/client/src/components/Header.tsx b/client/src/components/Header.tsx index cd7eb15..e9e1f03 100644 --- a/client/src/components/Header.tsx +++ b/client/src/components/Header.tsx @@ -2,6 +2,7 @@ import { IconUser, IconShield, IconUserCircle, IconSettings2, IconLogout } from "@tabler/icons-react"; import Link from "next/link"; +import { useState, useEffect, useRef } from "react"; import NotificationPanel from "./NotificationPanel"; import ThemeToggle from "./ThemeToggle"; import { useSession } from "@/context/session"; @@ -11,10 +12,29 @@ import AuthDropdownContent from "./auth/AuthDropdownContent"; export default function Header() { const { user, setUser } = useSession(); + const [isUserMenuOpen, setIsUserMenuOpen] = useState(false); + const [isAuthMenuOpen, setIsAuthMenuOpen] = useState(false); + + const userMenuRef = useRef(null); + const authMenuRef = useRef(null); + + useEffect(() => { + function handleClickOutside(event: MouseEvent) { + if (userMenuRef.current && !userMenuRef.current.contains(event.target as Node)) { + setIsUserMenuOpen(false); + } + if (authMenuRef.current && !authMenuRef.current.contains(event.target as Node)) { + setIsAuthMenuOpen(false); + } + } + document.addEventListener("mousedown", handleClickOutside); + return () => document.removeEventListener("mousedown", handleClickOutside); + }, []); async function handleLogout() { await logout(); setUser(null); + setIsUserMenuOpen(false); } return ( @@ -47,28 +67,31 @@ export default function Header() { {user?.id && typeof user.id === "number" ? ( -
-