1/** 2 * Copyright (c) 2020-2023, The Godot community 3 * Copyright (c) 2023, Benjamin Cabé <benjamin@zephyrproject.org> 4 * SPDX-License-Identifier: CC-BY-3.0 5 */ 6 7 8// Handle page scroll and adjust sidebar accordingly. 9 10// Each page has two scrolls: the main scroll, which is moving the content of the page; 11// and the sidebar scroll, which is moving the navigation in the sidebar. 12// We want the logo to gradually disappear as the main content is scrolled, giving 13// more room to the navigation on the left. This means adjusting the height 14// available to the navigation on the fly. 15const registerOnScrollEvent = (function(){ 16 // Configuration. 17 18 // The number of pixels the user must scroll by before the logo is completely hidden. 19 const scrollTopPixels = 156; 20 // The target margin to be applied to the navigation bar when the logo is hidden. 21 const menuTopMargin = 54; 22 // The max-height offset when the logo is completely visible. 23 const menuHeightOffset_default = 210; 24 // The max-height offset when the logo is completely hidden. 25 const menuHeightOffset_fixed = 63; 26 // The distance between the two max-height offset values above; used for intermediate values. 27 const menuHeightOffset_diff = (menuHeightOffset_default - menuHeightOffset_fixed); 28 29 // Media query handler. 30 return function(mediaQuery) { 31 // We only apply this logic to the "desktop" resolution (defined by a media query at the bottom). 32 // This handler is executed when the result of the query evaluation changes, which means that 33 // the page has moved between "desktop" and "mobile" states. 34 35 // When entering the "desktop" state, we register scroll events and adjust elements on the page. 36 // When entering the "mobile" state, we clean up any registered events and restore elements on the page 37 // to their initial state. 38 39 const $window = $(window); 40 const $sidebar = $('.wy-side-scroll'); 41 const $search = $sidebar.children('.wy-side-nav-search'); 42 const $menu = $sidebar.children('.wy-menu-vertical'); 43 44 if (mediaQuery.matches) { 45 // Entering the "desktop" state. 46 47 // The main scroll event handler. 48 // Executed as the page is scrolled and once immediately as the page enters this state. 49 const handleMainScroll = (currentScroll) => { 50 if (currentScroll >= scrollTopPixels) { 51 // After the page is scrolled below the threshold, we fix everything in place. 52 $search.css('margin-top', `-${scrollTopPixels}px`); 53 $menu.css('margin-top', `${menuTopMargin}px`); 54 $menu.css('max-height', `calc(100% - ${menuHeightOffset_fixed}px)`); 55 } 56 else { 57 // Between the top of the page and the threshold we calculate intermediate values 58 // to guarantee a smooth transition. 59 $search.css('margin-top', `-${currentScroll}px`); 60 $menu.css('margin-top', `${menuTopMargin + (scrollTopPixels - currentScroll)}px`); 61 62 if (currentScroll > 0) { 63 const scrolledPercent = (scrollTopPixels - currentScroll) / scrollTopPixels; 64 const offsetValue = menuHeightOffset_fixed + menuHeightOffset_diff * scrolledPercent; 65 $menu.css('max-height', `calc(100% - ${offsetValue}px)`); 66 } else { 67 $menu.css('max-height', `calc(100% - ${menuHeightOffset_default}px)`); 68 } 69 } 70 }; 71 72 // The sidebar scroll event handler. 73 // Executed as the sidebar is scrolled as well as after the main scroll. This is needed 74 // because the main scroll can affect the scrollable area of the sidebar. 75 const handleSidebarScroll = () => { 76 const menuElement = $menu.get(0); 77 const menuScrollTop = $menu.scrollTop(); 78 const menuScrollBottom = menuElement.scrollHeight - (menuScrollTop + menuElement.offsetHeight); 79 80 // As the navigation is scrolled we add a shadow to the top bar hanging over it. 81 if (menuScrollTop > 0) { 82 $search.addClass('fixed-and-scrolled'); 83 } else { 84 $search.removeClass('fixed-and-scrolled'); 85 } 86 }; 87 88 $search.addClass('fixed'); 89 90 $window.scroll(function() { 91 handleMainScroll(window.scrollY); 92 handleSidebarScroll(); 93 }); 94 95 $menu.scroll(function() { 96 handleSidebarScroll(); 97 }); 98 99 handleMainScroll(window.scrollY); 100 handleSidebarScroll(); 101 } else { 102 // Entering the "mobile" state. 103 104 $window.unbind('scroll'); 105 $menu.unbind('scroll'); 106 107 $search.removeClass('fixed'); 108 109 $search.css('margin-top', `0px`); 110 $menu.css('margin-top', `0px`); 111 $menu.css('max-height', 'initial'); 112 } 113 }; 114 })(); 115 116 $(document).ready(() => { 117 // Initialize handlers for page scrolling and our custom sidebar. 118 const mediaQuery = window.matchMedia('only screen and (min-width: 769px)'); 119 120 registerOnScrollEvent(mediaQuery); 121 mediaQuery.addListener(registerOnScrollEvent); 122 }); 123