Merge fix/nav-focus-stuck: rail no longer latches open after a portaled menu closes

This commit is contained in:
2026-07-15 20:29:03 +02:00
+24
View File
@@ -67,6 +67,30 @@ export default function NavSidebar({
// in-flow like before. `collapsed` (persisted per-account) now means "unpinned".
const [hovered, setHovered] = useState(false);
const [focused, setFocused] = useState(false);
// onFocus/onBlur (below) track focus through the REACT tree, so the rail's portaled menus
// (language switcher, account popover) count as "inside" and keep it open while you use them.
// The case they miss: a portal unmounts while focused, and browsers fire NO blur when a focused
// node is removed — focus silently falls to the body and `focused` would latch on forever.
// Safety net: whenever focus has landed nowhere, clear it. Deferred a tick so activeElement has
// settled, and rescheduled rather than queued — only the latest state matters.
useEffect(() => {
let timer: number | undefined;
const clearIfFocusLandedNowhere = () => {
const el = document.activeElement;
if (!el || el === document.body || el === document.documentElement) setFocused(false);
};
const soon = () => {
window.clearTimeout(timer);
timer = window.setTimeout(clearIfFocusLandedNowhere, 0);
};
document.addEventListener("focusout", soon);
document.addEventListener("click", soon);
return () => {
document.removeEventListener("focusout", soon);
document.removeEventListener("click", soon);
window.clearTimeout(timer);
};
}, []);
const pinned = !collapsed;
const expanded = pinned || hovered || focused;
const slim = !expanded; // visual: icon-only rail