Sidebar redesign, dropdown fixes, and soft-delete UI #57
@@ -56,17 +56,17 @@ export default function NotificationCenter({ anchor = 'bottom', variant = 'defau
|
|||||||
const dropdownRef = useRef<HTMLDivElement>(null)
|
const dropdownRef = useRef<HTMLDivElement>(null)
|
||||||
const panelRef = useRef<HTMLDivElement>(null)
|
const panelRef = useRef<HTMLDivElement>(null)
|
||||||
const buttonRef = useRef<HTMLButtonElement>(null)
|
const buttonRef = useRef<HTMLButtonElement>(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(() => {
|
const updatePosition = useCallback(() => {
|
||||||
if (anchor === 'right' && buttonRef.current) {
|
if (anchor === 'right' && buttonRef.current) {
|
||||||
const rect = buttonRef.current.getBoundingClientRect()
|
const rect = buttonRef.current.getBoundingClientRect()
|
||||||
let top = rect.top
|
const left = rect.right + 8
|
||||||
if (panelRef.current) {
|
if (rect.top > window.innerHeight / 2) {
|
||||||
const maxTop = window.innerHeight - panelRef.current.offsetHeight - 8
|
setFixedPos({ left, bottom: window.innerHeight - rect.bottom })
|
||||||
top = Math.min(top, Math.max(8, maxTop))
|
} else {
|
||||||
|
setFixedPos({ left, top: rect.top })
|
||||||
}
|
}
|
||||||
setFixedPos({ left: rect.right + 8, top })
|
|
||||||
}
|
}
|
||||||
}, [anchor])
|
}, [anchor])
|
||||||
|
|
||||||
@@ -100,17 +100,9 @@ export default function NotificationCenter({ anchor = 'bottom', variant = 'defau
|
|||||||
if (open) {
|
if (open) {
|
||||||
fetchNotifications()
|
fetchNotifications()
|
||||||
updatePosition()
|
updatePosition()
|
||||||
requestAnimationFrame(() => updatePosition())
|
|
||||||
}
|
}
|
||||||
}, [open, 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)
|
// * Poll unread count in background (when authenticated)
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchUnreadCount()
|
fetchUnreadCount()
|
||||||
@@ -214,10 +206,10 @@ export default function NotificationCenter({ anchor = 'bottom', variant = 'defau
|
|||||||
aria-label="Notifications"
|
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] ${
|
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'
|
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'
|
: '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}
|
||||||
>
|
>
|
||||||
<div className="flex items-center justify-between px-4 py-3 border-b border-neutral-200 dark:border-neutral-700">
|
<div className="flex items-center justify-between px-4 py-3 border-b border-neutral-200 dark:border-neutral-700">
|
||||||
<h3 className="font-semibold text-neutral-900 dark:text-white">Notifications</h3>
|
<h3 className="font-semibold text-neutral-900 dark:text-white">Notifications</h3>
|
||||||
|
|||||||
Reference in New Issue
Block a user