diff --git a/packages/core-web/src/index.js b/packages/core-web/src/index.js index fd142a3fd4..5bd1770f60 100644 --- a/packages/core-web/src/index.js +++ b/packages/core-web/src/index.js @@ -52,38 +52,73 @@ function detectAndApplyFixedHeaderStyles() { }`); insertCss(`.nav-menu-open { max-height: calc(100% - ${headerHeight}px); }`); - const addResizeHeaderListener = () => { - const resizeObserver = new ResizeObserver(() => { - const newHeaderHeight = headerSelector.height(); - const sheets = document.styleSheets; - for (let i = 0; i < sheets.length; i += 1) { - const rules = sheets[i].cssRules; - // eslint-disable-next-line lodash/prefer-get - if (rules && rules[0] && rules[0].selectorText) { - switch (rules[0].selectorText) { - case '.fixed-header-padding': - sheets[i].deleteRule(0); - sheets[i].insertRule(`.fixed-header-padding { padding-top: ${newHeaderHeight}px !important }`); - break; - case 'span.anchor': - rules[0].style.top = `calc(-${newHeaderHeight}px - ${bufferHeight}rem)`; - break; - case 'span.card-container::before': - rules[0].style.marginTop = `calc(-${newHeaderHeight}px - ${bufferHeight}rem)`; - rules[0].style.height = `calc(${newHeaderHeight}px + ${bufferHeight}rem)`; - break; - case '.nav-menu-open': - rules[0].style.maxHeight = `calc(100% - ${newHeaderHeight}px + 50px)`; - break; - default: - break; - } + const adjustHeaderClasses = () => { + const newHeaderHeight = headerSelector.height(); + const sheets = document.styleSheets; + for (let i = 0; i < sheets.length; i += 1) { + const rules = sheets[i].cssRules; + // eslint-disable-next-line lodash/prefer-get + if (rules && rules[0] && rules[0].selectorText) { + switch (rules[0].selectorText) { + case '.fixed-header-padding': + sheets[i].deleteRule(0); + sheets[i].insertRule(`.fixed-header-padding { padding-top: ${newHeaderHeight}px !important }`); + break; + case 'span.anchor': + rules[0].style.top = `calc(-${newHeaderHeight}px - ${bufferHeight}rem)`; + break; + case 'span.card-container::before': + rules[0].style.marginTop = `calc(-${newHeaderHeight}px - ${bufferHeight}rem)`; + rules[0].style.height = `calc(${newHeaderHeight}px + ${bufferHeight}rem)`; + break; + case '.nav-menu-open': + rules[0].style.maxHeight = `calc(100% - ${newHeaderHeight}px + 50px)`; + break; + default: + break; } } - }); - resizeObserver.observe(headerSelector[0]); + } + }; + + const toggleHeaderOverflow = () => { + const headerMaxHeight = headerSelector.css('max-height'); + // reset overflow when header shows again to allow content + // in the header such as search dropdown etc. to overflow + if (headerMaxHeight === '100%') { + headerSelector.css('overflow', ''); + adjustHeaderClasses(); + } + }; + + let lastOffset = 0; + const toggleHeaderOnScroll = () => { + // prevent toggling of header on desktop site + if (window.innerWidth > 767) { return; } + const currentOffset = window.pageYOffset; + const isEndOfPage = (window.innerHeight + currentOffset) >= document.body.offsetHeight; + // to prevent page from auto scrolling when header is toggled at the end of page + if (isEndOfPage) { return; } + if (currentOffset > lastOffset) { + headerSelector.addClass('hide-header'); + } else { + headerSelector.removeClass('hide-header'); + } + lastOffset = currentOffset; }; - addResizeHeaderListener(); + + const resizeObserver = new ResizeObserver(() => { + const headerMaxHeight = headerSelector.css('max-height'); + // hide header overflow when user scrolls to support transition effect + if (headerMaxHeight !== '100%') { + headerSelector.css('overflow', 'hidden'); + return; + } + adjustHeaderClasses(); + }); + resizeObserver.observe(headerSelector[0]); + headerSelector[0].addEventListener('transitionend', toggleHeaderOverflow); + window.addEventListener('scroll', toggleHeaderOnScroll); } function updateSearchData(vm) { diff --git a/packages/core-web/src/styles/markbind.css b/packages/core-web/src/styles/markbind.css index 99bb72afb6..e6e1fb37c9 100644 --- a/packages/core-web/src/styles/markbind.css +++ b/packages/core-web/src/styles/markbind.css @@ -118,11 +118,18 @@ code.hljs.inline { /* Header */ header[fixed] { + max-height: 100%; position: fixed; + transition: max-height 0.6s ease-in; width: 100%; z-index: 1001; } +header[fixed].hide-header { + max-height: 0; + transition: max-height 0.6s ease-out; +} + /* #app is treated as the main container */ #app { display: flex; diff --git a/packages/vue-components/src/Overlay.vue b/packages/vue-components/src/Overlay.vue index 84581dacea..8e5815a7f1 100644 --- a/packages/vue-components/src/Overlay.vue +++ b/packages/vue-components/src/Overlay.vue @@ -57,7 +57,13 @@ export default { }, methods: { toggleNavMenu() { - if (!this.show) { publish('closeOverlay'); } + if (!this.show) { + publish('closeOverlay'); + // to prevent scrolling of the body when overlay is overscrolled + document.body.style.overflow = 'hidden'; + } else { + document.body.style.removeProperty('overflow'); + } this.show = !this.show; }, navMenuLoaded() {