diff --git a/apps/web/src/components/features/notification/notification-center.tsx b/apps/web/src/components/features/notification/notification-center.tsx index 18e6b4b..a4ffedb 100644 --- a/apps/web/src/components/features/notification/notification-center.tsx +++ b/apps/web/src/components/features/notification/notification-center.tsx @@ -1,4 +1,5 @@ -import { useEffect, useRef } from "react"; +import { useEffect, useRef, useState, useCallback } from "react"; +import { createPortal } from "react-dom"; import { useNotifications } from "../../../hooks/use-notifications"; import { NotificationItem } from "./notification-item"; import { Icon } from "../../icon"; @@ -22,15 +23,30 @@ export function NotificationCenter({ setIsDropdownOpen, } = useNotifications(); + const bellRef = useRef(null); const dropdownRef = useRef(null); + const [dropdownStyle, setDropdownStyle] = useState({}); + + const updatePosition = useCallback(() => { + if (!bellRef.current) return; + const rect = bellRef.current.getBoundingClientRect(); + setDropdownStyle({ + top: rect.bottom + 6, + right: window.innerWidth - rect.right, + }); + }, []); useEffect(() => { if (!isDropdownOpen) return; + updatePosition(); + const handleMouseDown = (e: MouseEvent) => { + const target = e.target as Node; if ( dropdownRef.current && - !dropdownRef.current.contains(e.target as Node) + !dropdownRef.current.contains(target) && + !bellRef.current?.contains(target) ) { setIsDropdownOpen(false); } @@ -42,14 +58,20 @@ export function NotificationCenter({ } }; + const handleResize = () => { + updatePosition(); + }; + document.addEventListener("mousedown", handleMouseDown); document.addEventListener("keydown", handleKeyDown); + window.addEventListener("resize", handleResize); return () => { document.removeEventListener("mousedown", handleMouseDown); document.removeEventListener("keydown", handleKeyDown); + window.removeEventListener("resize", handleResize); }; - }, [isDropdownOpen, setIsDropdownOpen]); + }, [isDropdownOpen, setIsDropdownOpen, updatePosition]); useEffect(() => { if (isDropdownOpen) { @@ -66,6 +88,7 @@ export function NotificationCenter({ return (
- {isDropdownOpen && ( -
-
- Notifications -
- -
    - {notifications.length === 0 ? ( -
  • No notifications
  • - ) : ( - notifications.map((n) => ( - - )) - )} -
- - {notifications.length > 0 && ( -
- - + {isDropdownOpen && + createPortal( +
+
+ Notifications
- )} -
- )} + +
    + {notifications.length === 0 ? ( +
  • No notifications
  • + ) : ( + notifications.map((n) => ( + + )) + )} +
+ + {notifications.length > 0 && ( +
+ + +
+ )} +
, + document.body, + )}
); } diff --git a/apps/web/src/styles.css b/apps/web/src/styles.css index 38c14d8..7a0dc25 100644 --- a/apps/web/src/styles.css +++ b/apps/web/src/styles.css @@ -4670,9 +4670,7 @@ a:active, } .notification-dropdown { - position: absolute; - top: calc(100% + 6px); - right: 0; + position: fixed; width: 360px; max-width: calc(100vw - 2rem); max-height: 480px;