Bladeren bron

Improve sidebar behavior on scroll and adjust styling (#3391)

Yuri Sizov 5 jaren geleden
bovenliggende
commit
98e360ffec
2 gewijzigde bestanden met toevoegingen van 84 en 30 verwijderingen
  1. 23 5
      _static/css/custom.css
  2. 61 25
      _static/js/custom.js

+ 23 - 5
_static/css/custom.css

@@ -21,6 +21,9 @@
     --navbar-level-2-color: #b8d6f0;
     --navbar-level-3-color: #a3c4e1;
     --navbar-heading-color: #ff7381;
+    --navbar-scrollbar-color: #d45a66;
+    --navbar-scrollbar-hover-color: #b14550;
+    --navbar-scrollbar-active-color: #72383e;
     --navbar-scrollbar-background: #131e2b;
 
     --link-color: #2980b9;
@@ -102,6 +105,9 @@
         --navbar-level-2-color: #ccc;
         --navbar-level-3-color: #bbb;
         --navbar-heading-color: #ee7381;
+        --navbar-scrollbar-color: #be5460;
+        --navbar-scrollbar-hover-color: #963e48;
+        --navbar-scrollbar-active-color: #5f3034;
         --navbar-scrollbar-background: #1c1e21;
 
         --link-color: #8cf;
@@ -845,7 +851,7 @@ kbd, .kbd {
 
 /* Banner panel in sidebar */
 .wy-nav-side .ethical-rtd.fixed {
-    position: fixed
+    position: fixed;
 }
 
 /* Version selector (only visible on Read the Docs) */
@@ -899,11 +905,16 @@ kbd, .kbd {
     padding: 0;
 }
 
-/* Allows the navbar's scrollbar to be shown */
+/* Allows the scrollbar to be shown in the sidebar */
 @media only screen and (min-width: 769px) {
     .wy-side-scroll {
         overflow: hidden;
     }
+
+    .wy-nav-side .wy-side-scroll .ethical-rtd {
+        width: calc(300px - 1.25em);
+        padding: 0 0 0 1em;
+    }
 }
 .wy-menu.wy-menu-vertical {
     overflow-y: auto;
@@ -920,9 +931,9 @@ kbd, .kbd {
     }
 }
 
-/* Navbar's scrollbar styling */
+/* Scrollbar styling */
 .wy-menu.wy-menu-vertical {
-    scrollbar-color: var(--navbar-heading-color) var(--navbar-scrollbar-background);
+    scrollbar-color: var(--navbar-scrollbar-color) var(--navbar-scrollbar-background);
 }
 .wy-menu.wy-menu-vertical::-webkit-scrollbar {
     width: .75rem;
@@ -931,5 +942,12 @@ kbd, .kbd {
     background-color: var(--navbar-scrollbar-background);
 }
 .wy-menu.wy-menu-vertical::-webkit-scrollbar-thumb {
-    background-color: var(--navbar-heading-color);
+    background-color: var(--navbar-scrollbar-color);
+}
+/* Firefox does the dimming on hover automatically. We emulate it for Webkit-based browsers. */
+.wy-menu.wy-menu-vertical::-webkit-scrollbar-thumb:hover {
+    background-color: var(--navbar-scrollbar-hover-color);
+}
+.wy-menu.wy-menu-vertical::-webkit-scrollbar-thumb:active {
+    background-color: var(--navbar-scrollbar-active-color);
 }

+ 61 - 25
_static/js/custom.js

@@ -31,16 +31,25 @@ const registerOnScrollEvent = (function(){
     // to their initial state.
 
     const $window = $(window);
-    const $menu = $('.wy-menu-vertical');
-    const $search = $('.wy-side-nav-search');
-    const $ethical = $('.ethical-rtd');
+    const $sidebar = $('.wy-side-scroll');
+    const $search = $sidebar.children('.wy-side-nav-search');
+    const $menu = $sidebar.children('.wy-menu-vertical');
+    const $ethical = $sidebar.children('.ethical-rtd');
+
+    // This padding is needed to correctly adjust the height of the scrollable area in the sidebar.
+    // It has to have the same height as the ethical block, if there is one.
+    let $menuPadding = $menu.children('.wy-menu-ethical-padding');
+    if ($menuPadding.length == 0) {
+      $menuPadding = $('<div class="wy-menu-ethical-padding"></div>');
+      $menu.append($menuPadding);
+    }
 
     if (mediaQuery.matches) {
       // Entering the "desktop" state.
 
-      // The scroll event handler.
+      // The main scroll event handler.
       // Executed as the page is scrolled and once immediatelly as the page enters this state.
-      const handleScroll = (currentScroll) => {
+      const handleMainScroll = (currentScroll) => {
         if (currentScroll >= scrollTopPixels) {
           // After the page is scrolled below the threshold, we fix everything in place.
           $search.css('margin-top', `-${scrollTopPixels}px`);
@@ -63,24 +72,13 @@ const registerOnScrollEvent = (function(){
         }
       };
 
-      $search.addClass('fixed');
-      $ethical.addClass('fixed');
-
-      // Adjust the inner height of navigation so that the banner can be overlaid there later.
-      const ethicalOffsetBottom = $ethical.height() || 0;
-      if (ethicalOffsetBottom) {
-        $menu.css('padding-bottom', `${ethicalOffsetBottom}px`);
-      } else {
-        $menu.css('padding-bottom', `0px`);
-      }
-
-      $window.scroll(function() {
-        handleScroll(window.scrollY);
-      });
-
-      $menu.scroll(function() {
-        const menuScrollTop = $(this).scrollTop();
-        const menuScrollBottom = this.scrollHeight - (menuScrollTop + this.offsetHeight);
+      // The sidebar scroll event handler.
+      // Executed as the sidebar is scrolled as well as after the main scroll. This is needed
+      // because the main scroll can affect the scrollable area of the sidebar.
+      const handleSidebarScroll = () => {
+        const menuElement = $menu.get(0);
+        const menuScrollTop = $menu.scrollTop();
+        const menuScrollBottom = menuElement.scrollHeight - (menuScrollTop + menuElement.offsetHeight);
 
         // As the navigation is scrolled we add a shadow to the top bar hanging over it.
         if (menuScrollTop > 0) {
@@ -95,9 +93,30 @@ const registerOnScrollEvent = (function(){
         } else {
           $ethical.css('margin-top', '0px');
         }
+      };
+
+      $search.addClass('fixed');
+      $ethical.addClass('fixed');
+
+      // Adjust the inner height of navigation so that the banner can be overlaid there later.
+      const ethicalOffsetBottom = $ethical.height() || 0;
+      if (ethicalOffsetBottom) {
+        $menuPadding.css('height', `${ethicalOffsetBottom}px`);
+      } else {
+        $menuPadding.css('height', `0px`);
+      }
+
+      $window.scroll(function() {
+        handleMainScroll(window.scrollY);
+        handleSidebarScroll();
+      });
+
+      $menu.scroll(function() {
+        handleSidebarScroll();
       })
 
-      handleScroll(window.scrollY);
+      handleMainScroll(window.scrollY);
+      handleSidebarScroll();
     } else {
       // Entering the "mobile" state.
 
@@ -109,15 +128,32 @@ const registerOnScrollEvent = (function(){
 
       $search.css('margin-top', `0px`);
       $menu.css('margin-top', `0px`);
-      $menu.css('padding-bottom', `0px`);
       $menu.css('max-height', 'initial');
+      $menuPadding.css('height', `0px`);
       $ethical.css('margin-top', '0px');
     }
   }
 })();
 
 $(document).ready(() => {
+  const sidebarContainer = document.querySelector('.wy-side-scroll');
   const mediaQuery = window.matchMedia('only screen and (min-width: 769px)');
+  
+  // Subscribe to DOM changes in the sidebar container, because there is a 
+  // banner that gets added at a later point, that we might not catch otherwise.
+  const observerConfig = { childList: true };
+  const observerCallback = (mutationsList, observer) => {
+    for (let mutation of mutationsList) {
+      if (mutation.type !== 'childList') {
+        continue;
+      }
+
+      registerOnScrollEvent(mediaQuery);
+    }
+  };
+  const observer = new MutationObserver(observerCallback);
+  observer.observe(sidebarContainer, observerConfig);
+
   registerOnScrollEvent(mediaQuery);
   mediaQuery.addListener(registerOnScrollEvent);
 });