diff --git a/src/components/focus-active-link.tsx b/src/components/focus-active-link.tsx index 1862e1b7c7fce..6a173d025bdd2 100644 --- a/src/components/focus-active-link.tsx +++ b/src/components/focus-active-link.tsx @@ -1,23 +1,59 @@ 'use client'; import {useEffect} from 'react'; +import {debounce} from 'sentry-docs/utils'; + type Props = { activeLinkSelector: string; }; /** Make sure the active link is visible in the sidebar */ export function ScrollActiveLink({activeLinkSelector}: Props) { - const isVsibible = (link: Element) => { - const rect = link.getBoundingClientRect(); - return rect.top >= 0 && rect.bottom <= window.innerHeight; - }; + useEffect(() => { + const sidebar = document.querySelector('[data-sidebar-link]')?.closest('aside'); + if (!sidebar) { + const noOp = () => {}; + return noOp; + } + const onLinkClick = (e: Event) => { + const target = e.target as HTMLElement; + if (target.hasAttribute('data-sidebar-link')) { + const top = target.getBoundingClientRect().top; + sessionStorage.setItem('sidebar-link-poisition', top.toString()); + } + }; + sidebar.addEventListener('click', onLinkClick); + // track active link position on scroll as well + const onSidebarSroll = debounce(() => { + const activeLink = document.querySelector(activeLinkSelector); + if (activeLink) { + const top = activeLink.getBoundingClientRect().top.toString(); + sessionStorage.setItem('sidebar-link-poisition', top); + } + }, 50); + + sidebar.addEventListener('scroll', onSidebarSroll); + return () => { + sidebar.removeEventListener('click', onLinkClick); + sidebar.removeEventListener('scroll', onSidebarSroll); + }; + }, [activeLinkSelector]); useEffect(() => { const activeLink = document.querySelector(activeLinkSelector); - if (activeLink && !isVsibible(activeLink)) { - // try to center the active link in the sidebar - activeLink.scrollIntoView({block: 'center', behavior: 'smooth'}); + const sidebar = activeLink?.closest('aside')!; + if (!activeLink || !sidebar) { + return; + } + const previousBoundingRectTop = sessionStorage.getItem('sidebar-link-poisition'); + const currentBoundingRectTop = activeLink.getBoundingClientRect().top; + // scroll the sidebar to make sure the active link is visible & has the same position as when it was clicked + if (!previousBoundingRectTop) { + return; } + const scrollX = 0; + const scrollY = sidebar.scrollTop + currentBoundingRectTop - +previousBoundingRectTop; + sidebar?.scrollTo(scrollX, scrollY); }, [activeLinkSelector]); // don't render anything, just exist as a client-side component for the useEffect. return null;