diff --git a/frontend/src/components/UserMenu.jsx b/frontend/src/components/UserMenu.jsx index 823b36c..1544e68 100644 --- a/frontend/src/components/UserMenu.jsx +++ b/frontend/src/components/UserMenu.jsx @@ -121,15 +121,17 @@ export default function UserMenu() { const computePosition = useCallback(() => { if (!triggerRef.current) return; const r = triggerRef.current.getBoundingClientRect(); + // visualViewport est fiable sur Safari mobile (window.innerHeight ne l'est pas) + const vh = window.visualViewport ? window.visualViewport.height : window.innerHeight; let mainLeft, mainBottom, mainWidth; if (sidebarCollapsed) { mainLeft = r.right + 8; - mainBottom = window.innerHeight - r.bottom; + mainBottom = vh - r.bottom; mainWidth = POPUP_W; } else { mainLeft = r.left; - mainBottom = window.innerHeight - r.top + 6; + mainBottom = vh - r.top + 6; mainWidth = r.width; } @@ -164,9 +166,21 @@ export default function UserMenu() { return () => document.removeEventListener('mousedown', onDown); }, [open, closeMenu]); - /* Recalcul si sidebar change */ + /* Recalcul si sidebar change ou si visualViewport change (Safari mobile) */ useEffect(() => { if (open) computePosition(); }, [sidebarCollapsed, open, computePosition]); + useEffect(() => { + if (!open) return; + const vp = window.visualViewport; + if (!vp) return; + vp.addEventListener('resize', computePosition); + vp.addEventListener('scroll', computePosition); + return () => { + vp.removeEventListener('resize', computePosition); + vp.removeEventListener('scroll', computePosition); + }; + }, [open, computePosition]); + /* Handlers ouverture */ const onWrapEnter = () => { if (sidebarCollapsed) { clearClose(); openMenu(); } }; const onWrapLeave = () => { if (sidebarCollapsed) scheduleClose(); };