ui: improve menu folding (#989)

* ui: improve menu folding

Fold/unfold the menu bar just by the amount of scroll, not by its
full width

* refactor: use a variable for the menu bar height

* Fix menu scroll jittering, remove hover folding smoothness

Rewrite it to use `position:` `sticky` and `relative` instead
of continuous programmatic position changes

On-hover folding-unfolding transition removal is a side-effect
This commit is contained in:
Dylan DPC 2020-03-06 01:11:37 +01:00 committed by GitHub
parent ca4b85b815
commit fd56a53e76
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
4 changed files with 105 additions and 65 deletions

View File

@ -580,26 +580,60 @@ function playpen_text(playpen) {
}); });
})(); })();
(function autoHideMenu() { (function controllMenu() {
var menu = document.getElementById('menu-bar'); var menu = document.getElementById('menu-bar');
var previousScrollTop = document.scrollingElement.scrollTop; (function controllPosition() {
var scrollTop = document.scrollingElement.scrollTop;
var prevScrollTop = scrollTop;
var minMenuY = -menu.clientHeight - 50;
// When the script loads, the page can be at any scroll (e.g. if you reforesh it).
menu.style.top = scrollTop + 'px';
// Same as parseInt(menu.style.top.slice(0, -2), but faster
var topCache = menu.style.top.slice(0, -2);
menu.classList.remove('sticky');
var stickyCache = false; // Same as menu.classList.contains('sticky'), but faster
document.addEventListener('scroll', function () { document.addEventListener('scroll', function () {
if (menu.classList.contains('folded') && document.scrollingElement.scrollTop < previousScrollTop) { scrollTop = Math.max(document.scrollingElement.scrollTop, 0);
menu.classList.remove('folded'); // `null` means that it doesn't need to be updated
} else if (!menu.classList.contains('folded') && document.scrollingElement.scrollTop > previousScrollTop) { var nextSticky = null;
menu.classList.add('folded'); var nextTop = null;
var scrollDown = scrollTop > prevScrollTop;
var menuPosAbsoluteY = topCache - scrollTop;
if (scrollDown) {
nextSticky = false;
if (menuPosAbsoluteY > 0) {
nextTop = prevScrollTop;
} }
} else {
if (!menu.classList.contains('bordered') && document.scrollingElement.scrollTop > 0) { if (menuPosAbsoluteY > 0) {
nextSticky = true;
} else if (menuPosAbsoluteY < minMenuY) {
nextTop = prevScrollTop + minMenuY;
}
}
if (nextSticky === true && stickyCache === false) {
menu.classList.add('sticky');
stickyCache = true;
} else if (nextSticky === false && stickyCache === true) {
menu.classList.remove('sticky');
stickyCache = false;
}
if (nextTop !== null) {
menu.style.top = nextTop + 'px';
topCache = nextTop;
}
prevScrollTop = scrollTop;
}, { passive: true });
})();
(function controllBorder() {
menu.classList.remove('bordered');
document.addEventListener('scroll', function () {
if (menu.offsetTop === 0) {
menu.classList.remove('bordered');
} else {
menu.classList.add('bordered'); menu.classList.add('bordered');
} }
if (menu.classList.contains('bordered') && document.scrollingElement.scrollTop === 0) {
menu.classList.remove('bordered');
}
previousScrollTop = Math.max(document.scrollingElement.scrollTop, 0);
}, { passive: true }); }, { passive: true });
})();
})(); })();

View File

@ -20,14 +20,13 @@ a > .hljs {
/* Menu Bar */ /* Menu Bar */
#menu-bar { #menu-bar,
position: -webkit-sticky; #menu-bar-hover-placeholder {
position: sticky;
top: 0;
z-index: 101; z-index: 101;
margin: auto calc(0px - var(--page-padding)); margin: auto calc(0px - var(--page-padding));
} }
#menu-bar > #menu-bar-sticky-container { #menu-bar {
position: relative;
display: flex; display: flex;
flex-wrap: wrap; flex-wrap: wrap;
background-color: var(--bg); background-color: var(--bg);
@ -35,10 +34,21 @@ a > .hljs {
border-bottom-width: 1px; border-bottom-width: 1px;
border-bottom-style: solid; border-bottom-style: solid;
} }
.js #menu-bar > #menu-bar-sticky-container { #menu-bar.sticky,
transition: transform 0.3s; .js #menu-bar-hover-placeholder:hover + #menu-bar,
.js #menu-bar:hover,
.js.sidebar-visible #menu-bar {
position: -webkit-sticky;
position: sticky;
top: 0 !important;
} }
#menu-bar.bordered > #menu-bar-sticky-container { #menu-bar-hover-placeholder {
position: sticky;
position: -webkit-sticky;
top: 0;
height: var(--menu-bar-height);
}
#menu-bar.bordered {
border-bottom-color: var(--table-border-color); border-bottom-color: var(--table-border-color);
} }
#menu-bar i, #menu-bar .icon-button { #menu-bar i, #menu-bar .icon-button {
@ -72,10 +82,6 @@ a > .hljs {
text-decoration: none; text-decoration: none;
} }
html:not(.sidebar-visible) #menu-bar:not(:hover).folded > #menu-bar-sticky-container {
transform: translateY(calc(-10px - var(--menu-bar-height)));
}
.left-buttons { .left-buttons {
display: flex; display: flex;
margin: 0 5px; margin: 0 5px;

View File

@ -60,6 +60,7 @@ h4 a.header:target {
.page { .page {
outline: 0; outline: 0;
padding: 0 var(--page-padding); padding: 0 var(--page-padding);
margin-top: calc(0px - var(--menu-bar-height)); /* Compensate for the #menu-bar-hover-placeholder */
} }
.page-wrapper { .page-wrapper {
box-sizing: border-box; box-sizing: border-box;

View File

@ -97,8 +97,8 @@
<div class="page"> <div class="page">
{{> header}} {{> header}}
<div id="menu-bar" class="menu-bar"> <div id="menu-bar-hover-placeholder"></div>
<div id="menu-bar-sticky-container"> <div id="menu-bar" class="menu-bar sticky bordered">
<div class="left-buttons"> <div class="left-buttons">
<button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar"> <button id="sidebar-toggle" class="icon-button" type="button" title="Toggle Table of Contents" aria-label="Toggle Table of Contents" aria-controls="sidebar">
<i class="fa fa-bars"></i> <i class="fa fa-bars"></i>
@ -133,7 +133,6 @@
{{/if}} {{/if}}
</div> </div>
</div> </div>
</div>
{{#if search_enabled}} {{#if search_enabled}}
<div id="search-wrapper" class="hidden"> <div id="search-wrapper" class="hidden">