|
1 | 1 | (() => { |
2 | | - const desktopQuery = '(min-width: 50em)'; |
| 2 | + const ACTIVE_SELECTOR = "[data-starlight-sidebar] a[aria-current='page']"; |
3 | 3 |
|
4 | | - const isDesktopSidebar = () => matchMedia(desktopQuery).matches; |
| 4 | + const scrollActiveLink = (behavior = 'auto') => { |
| 5 | + const activeLink = document.querySelector(ACTIVE_SELECTOR); |
| 6 | + if (!activeLink || typeof activeLink.scrollIntoView !== 'function') return false; |
| 7 | + |
| 8 | + const sidebar = activeLink.closest('[data-starlight-sidebar]'); |
| 9 | + if (!sidebar) return false; |
5 | 10 |
|
6 | | - const isActiveLinkVisible = (sidebar, activeLink) => { |
7 | 11 | const sidebarRect = sidebar.getBoundingClientRect(); |
8 | 12 | const linkRect = activeLink.getBoundingClientRect(); |
| 13 | + const isVisible = linkRect.top >= sidebarRect.top && linkRect.bottom <= sidebarRect.bottom; |
9 | 14 |
|
10 | | - return linkRect.top >= sidebarRect.top && linkRect.bottom <= sidebarRect.bottom; |
11 | | - }; |
12 | | - |
13 | | - const centerLinkInSidebar = () => { |
14 | | - const sidebar = document.getElementById('starlight__sidebar'); |
15 | | - if (!sidebar || !isDesktopSidebar()) return true; |
| 15 | + if (!isVisible) { |
| 16 | + activeLink.scrollIntoView({ block: 'center', inline: 'nearest', behavior }); |
| 17 | + } |
16 | 18 |
|
17 | | - const activeLink = sidebar.querySelector("a[aria-current='page']"); |
18 | | - if (!activeLink) return false; |
| 19 | + return true; |
| 20 | + }; |
19 | 21 |
|
20 | | - if (isActiveLinkVisible(sidebar, activeLink)) return true; |
| 22 | + const runWhenReady = () => { |
| 23 | + if (scrollActiveLink('smooth')) return; |
21 | 24 |
|
22 | | - const sidebarRect = sidebar.getBoundingClientRect(); |
23 | | - const linkRect = activeLink.getBoundingClientRect(); |
24 | | - const linkOffset = linkRect.top - sidebarRect.top + sidebar.scrollTop; |
25 | | - const targetTop = Math.max(linkOffset - sidebar.clientHeight / 2 + linkRect.height / 2, 0); |
| 25 | + const sidebar = document.querySelector('[data-starlight-sidebar]') || document.body; |
| 26 | + const observer = new MutationObserver(() => { |
| 27 | + if (scrollActiveLink('smooth')) observer.disconnect(); |
| 28 | + }); |
26 | 29 |
|
27 | | - sidebar.scrollTo({ top: targetTop, behavior: 'auto' }); |
28 | | - return true; |
| 30 | + observer.observe(sidebar, { childList: true, subtree: true }); |
29 | 31 | }; |
30 | 32 |
|
31 | | - const scheduleScroll = () => { |
32 | | - if (document.readyState === 'complete' || document.readyState === 'interactive') { |
33 | | - requestAnimationFrame(() => { |
34 | | - if (!centerLinkInSidebar()) { |
35 | | - const sidebar = document.getElementById('starlight__sidebar') || document.body; |
36 | | - const observer = new MutationObserver(() => { |
37 | | - if (centerLinkInSidebar()) observer.disconnect(); |
38 | | - }); |
39 | | - observer.observe(sidebar, { childList: true, subtree: true }); |
40 | | - } |
41 | | - }); |
| 33 | + const schedule = () => { |
| 34 | + if (document.readyState === 'loading') { |
| 35 | + document.addEventListener('DOMContentLoaded', runWhenReady, { once: true }); |
42 | 36 | } else { |
43 | | - window.addEventListener('DOMContentLoaded', scheduleScroll, { once: true }); |
| 37 | + runWhenReady(); |
44 | 38 | } |
45 | 39 | }; |
46 | 40 |
|
47 | | - scheduleScroll(); |
| 41 | + schedule(); |
| 42 | + window.addEventListener('astro:after-swap', schedule); |
48 | 43 | })(); |
0 commit comments