diff --git a/src/apps/officer-web/Screens/Dashboard/Layout/DashboardLayout.tsx b/src/apps/officer-web/Screens/Dashboard/Layout/DashboardLayout.tsx index 7d59a627..150cda6e 100644 --- a/src/apps/officer-web/Screens/Dashboard/Layout/DashboardLayout.tsx +++ b/src/apps/officer-web/Screens/Dashboard/Layout/DashboardLayout.tsx @@ -1,3 +1,4 @@ +import { useRef } from 'react'; import { useDock, MusicPlayerHost } from 'officerdev'; import { Background } from './Background'; import { Header } from './Header'; @@ -12,20 +13,19 @@ export function DashboardLayout({ children }: DashboardLayoutProps) { const { items: visibleItems } = useDock(ALL_DOCK_ITEMS, DEFAULT_DOCK_PATHS); const isTouch = useIsTouch(); usePageTitleSync(); + // The content region shrinks when the (in-flow) music dock takes its space; the nav dock measures its + // reveal boundary from this element, so it always sits just above whatever's at the bottom. + const regionRef = useRef(null); return ( -
+
-
+
- {!isTouch && } -
- {children} -
+ {!isTouch && } +
{children}
); -}; - - +} diff --git a/src/apps/officer-web/Screens/Dashboard/Layout/Dock.tsx b/src/apps/officer-web/Screens/Dashboard/Layout/Dock.tsx index 8aebb6bc..7205d8e6 100644 --- a/src/apps/officer-web/Screens/Dashboard/Layout/Dock.tsx +++ b/src/apps/officer-web/Screens/Dashboard/Layout/Dock.tsx @@ -1,7 +1,6 @@ import { useEffect, useRef, useState } from 'react'; import { Link, useLocation } from 'react-router'; import type { LucideIcon } from 'lucide-react'; -import { useMusicPlayer, MUSIC_DOCK_HEIGHT } from 'officerdev'; export type DockItem = { label: string; @@ -13,6 +12,9 @@ export type DockItem = { type DockProps = { items: DockItem[]; className?: string; + // The content region the dock lives in. Reveal/hide is measured from ITS bottom edge, which the + // in-flow music dock shrinks when present — so the dock clears the music dock with no magic offset. + boundaryRef: React.RefObject; }; const ICON_SIZE = 48; @@ -30,30 +32,22 @@ const getScale = (mouseX: number | null, iconCenterX: number) => { return 1 + (MAX_SCALE - 1) * Math.cos((distance / MAX_DISTANCE) * (Math.PI / 2)); }; -export const Dock = ({ items, className }: DockProps) => { +export const Dock = ({ items, className, boundaryRef }: DockProps) => { const [mouseX, setMouseX] = useState(null); const [visible, setVisible] = useState(false); const dockRef = useRef(null); const location = useLocation(); - const { current } = useMusicPlayer(); - const musicDockPresent = !!current; - // Read the latest value inside the (once-registered) mousemove handler, so it reacts to the music - // dock appearing/disappearing without re-binding the listener. - const musicPresentRef = useRef(musicDockPresent); - musicPresentRef.current = musicDockPresent; const isActive = (to: string) => (to === '/' ? location.pathname === '/' : location.pathname.startsWith(to)); useEffect(() => { const handleMouseMove = (ev: MouseEvent) => { - const distFromBottom = window.innerHeight - ev.clientY; - // When the music dock is present the nav dock rides MUSIC_DOCK_HEIGHT higher, so lift the hide/ - // magnify zone by the same amount — otherwise hovering the raised dock counts as past the hide - // threshold and it slides away under the cursor. The reveal trigger stays at the bottom edge. - const lift = musicPresentRef.current ? MUSIC_DOCK_HEIGHT : 0; - const hideAt = HIDE_THRESHOLD + lift; - setVisible((prev) => (prev ? distFromBottom <= hideAt : distFromBottom <= SHOW_THRESHOLD)); - if (distFromBottom <= hideAt) { + // Distance from the bottom of the content region (which the in-flow music dock shrinks when + // present) — so reveal/hide triggers just above wherever the region ends, above the music dock. + const bottom = boundaryRef.current?.getBoundingClientRect().bottom ?? window.innerHeight; + const distFromBottom = bottom - ev.clientY; + setVisible((prev) => (prev ? distFromBottom <= HIDE_THRESHOLD : distFromBottom <= SHOW_THRESHOLD)); + if (distFromBottom <= HIDE_THRESHOLD) { const rect = dockRef.current?.getBoundingClientRect(); if (rect) setMouseX(ev.clientX - rect.left); } else { @@ -63,20 +57,17 @@ export const Dock = ({ items, className }: DockProps) => { document.addEventListener('mousemove', handleMouseMove); return () => document.removeEventListener('mousemove', handleMouseMove); - }, []); + }, [boundaryRef]); return (
{items.map((item, index) => { diff --git a/src/workspaces/officerdev/src/MusicPlayer/MusicPlayerHost.tsx b/src/workspaces/officerdev/src/MusicPlayer/MusicPlayerHost.tsx index 0dd75db9..04fdab45 100644 --- a/src/workspaces/officerdev/src/MusicPlayer/MusicPlayerHost.tsx +++ b/src/workspaces/officerdev/src/MusicPlayer/MusicPlayerHost.tsx @@ -249,8 +249,10 @@ export const MusicPlayerHost = () => { if (!current) return null; + // In-flow bottom bar (NOT position:fixed) — it reserves its own height so the content above shrinks to + // fit and the nav dock naturally sits above it, no overlap hacks needed. return ( -
+
{/* cover + info — click to open this album in /music */}