From dd76aed157805cb2420141bc8f237fe1757db1ed Mon Sep 17 00:00:00 2001 From: Usman Baig Date: Thu, 19 Mar 2026 00:54:50 +0100 Subject: [PATCH] fix: use bottom positioning for notification dropdown Instead of measuring panel height with unreliable RAF timing, use CSS bottom positioning when the button is in the lower half of the viewport. The dropdown grows upward, no measurement needed. --- .../notifications/NotificationCenter.tsx | 24 +++++++------------ 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/components/notifications/NotificationCenter.tsx b/components/notifications/NotificationCenter.tsx index cbbb0b3..ac416e5 100644 --- a/components/notifications/NotificationCenter.tsx +++ b/components/notifications/NotificationCenter.tsx @@ -56,17 +56,17 @@ export default function NotificationCenter({ anchor = 'bottom', variant = 'defau const dropdownRef = useRef(null) const panelRef = useRef(null) const buttonRef = useRef(null) - const [fixedPos, setFixedPos] = useState<{ left: number; top: number } | null>(null) + const [fixedPos, setFixedPos] = useState<{ left: number; top?: number; bottom?: number } | null>(null) const updatePosition = useCallback(() => { if (anchor === 'right' && buttonRef.current) { const rect = buttonRef.current.getBoundingClientRect() - let top = rect.top - if (panelRef.current) { - const maxTop = window.innerHeight - panelRef.current.offsetHeight - 8 - top = Math.min(top, Math.max(8, maxTop)) + const left = rect.right + 8 + if (rect.top > window.innerHeight / 2) { + setFixedPos({ left, bottom: window.innerHeight - rect.bottom }) + } else { + setFixedPos({ left, top: rect.top }) } - setFixedPos({ left: rect.right + 8, top }) } }, [anchor]) @@ -100,17 +100,9 @@ export default function NotificationCenter({ anchor = 'bottom', variant = 'defau if (open) { fetchNotifications() updatePosition() - requestAnimationFrame(() => updatePosition()) } }, [open, updatePosition]) - // Recalculate position after content changes (notifications load, loading state) - useEffect(() => { - if (open && anchor === 'right') { - requestAnimationFrame(() => updatePosition()) - } - }, [notifications, loading, open, anchor, updatePosition]) - // * Poll unread count in background (when authenticated) useEffect(() => { fetchUnreadCount() @@ -214,10 +206,10 @@ export default function NotificationCenter({ anchor = 'bottom', variant = 'defau aria-label="Notifications" className={`bg-white dark:bg-neutral-900 border border-neutral-200 dark:border-neutral-700 rounded-xl shadow-xl overflow-hidden z-[100] ${ anchor === 'right' - ? 'fixed w-96 origin-top-left' + ? `fixed w-96 ${fixedPos?.bottom !== undefined ? 'origin-bottom-left' : 'origin-top-left'}` : 'fixed left-4 right-4 top-16 sm:absolute sm:left-auto sm:right-0 sm:top-full sm:mt-2 sm:w-96' }`} - style={anchor === 'right' && fixedPos ? { left: fixedPos.left, top: fixedPos.top } : undefined} + style={anchor === 'right' && fixedPos ? { left: fixedPos.left, top: fixedPos.top, bottom: fixedPos.bottom } : undefined} >

Notifications