diff --git a/packages/docusaurus-theme-classic/src/theme/Navbar/index.js b/packages/docusaurus-theme-classic/src/theme/Navbar/index.js index 0a39da9f74f5..e317c97fc756 100644 --- a/packages/docusaurus-theme-classic/src/theme/Navbar/index.js +++ b/packages/docusaurus-theme-classic/src/theme/Navbar/index.js @@ -5,7 +5,7 @@ * LICENSE file in the root directory of this source tree. */ -import React, {useCallback, useState, useEffect} from 'react'; +import React, {useCallback, useState} from 'react'; import Link from '@docusaurus/Link'; import Head from '@docusaurus/Head'; import useDocusaurusContext from '@docusaurus/useDocusaurusContext'; @@ -17,6 +17,7 @@ import Toggle from '@theme/Toggle'; import classnames from 'classnames'; import useTheme from '@theme/hooks/useTheme'; +import useHideableNavbar from '@theme/hooks/useHideableNavbar'; import styles from './styles.module.css'; @@ -50,14 +51,8 @@ function Navbar() { const [sidebarShown, setSidebarShown] = useState(false); const [isSearchBarExpanded, setIsSearchBarExpanded] = useState(false); const [theme, setTheme] = useTheme(); - const [isNavbarVisible, setIsNavbarVisible] = useState(true); - const [lastScrollTop, setLastScrollTop] = useState(0); - const [navbarHeight, setNavbarHeight] = useState(0); - const navbarRef = useCallback(node => { - if (node !== null) { - setNavbarHeight(node.getBoundingClientRect().height); - } - }, []); + + const {navbarRef, isNavbarVisible} = useHideableNavbar(hideOnScroll); const showSidebar = useCallback(() => { setSidebarShown(true); @@ -71,36 +66,6 @@ function Navbar() { [setTheme], ); - const handleScroll = () => { - const scrollTop = window.pageYOffset || document.documentElement.scrollTop; - const documentHeight = document.documentElement.scrollHeight - navbarHeight; - const windowHeight = window.innerHeight; - - if (scrollTop < navbarHeight) { - return; - } - - if (lastScrollTop && scrollTop > lastScrollTop) { - setIsNavbarVisible(false); - } else if (scrollTop + windowHeight < documentHeight) { - setIsNavbarVisible(true); - } - - setLastScrollTop(scrollTop); - }; - - useEffect(() => { - if (!hideOnScroll) { - return undefined; - } - - window.addEventListener('scroll', handleScroll); - - return () => { - window.removeEventListener('scroll', handleScroll); - }; - }, [lastScrollTop, navbarHeight]); - const logoUrl = useBaseUrl(logo.src); return ( <> @@ -112,8 +77,8 @@ function Navbar() { ref={navbarRef} className={classnames('navbar', 'navbar--light', 'navbar--fixed-top', { 'navbar-sidebar--show': sidebarShown, - [styles.navbarHidable]: hideOnScroll, - [styles.navbarHided]: !isNavbarVisible, + [styles.navbarHideable]: hideOnScroll, + [styles.navbarHidden]: !isNavbarVisible, })}>
diff --git a/packages/docusaurus-theme-classic/src/theme/Navbar/styles.module.css b/packages/docusaurus-theme-classic/src/theme/Navbar/styles.module.css index c06bb8b1aa2e..2d7f71ff8c98 100644 --- a/packages/docusaurus-theme-classic/src/theme/Navbar/styles.module.css +++ b/packages/docusaurus-theme-classic/src/theme/Navbar/styles.module.css @@ -17,10 +17,10 @@ } } -.navbarHidable { +.navbarHideable { transition: top 0.2s ease-in-out; } -.navbarHided { +.navbarHidden { top: calc(var(--ifm-navbar-height) * -1) !important; } diff --git a/packages/docusaurus-theme-classic/src/theme/hooks/useHideableNavbar.js b/packages/docusaurus-theme-classic/src/theme/hooks/useHideableNavbar.js new file mode 100644 index 000000000000..a1941d1e5bee --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/hooks/useHideableNavbar.js @@ -0,0 +1,56 @@ +/** + * Copyright (c) 2017-present, Facebook, Inc. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import React, {useState, useCallback, useEffect} from 'react'; // eslint-disable-line no-unused-vars + +const useHideableNavbar = hideOnScroll => { + const [isNavbarVisible, setIsNavbarVisible] = useState(true); + const [lastScrollTop, setLastScrollTop] = useState(0); + const [navbarHeight, setNavbarHeight] = useState(0); + const navbarRef = useCallback(node => { + if (node !== null) { + setNavbarHeight(node.getBoundingClientRect().height); + } + }, []); + + const handleScroll = () => { + const scrollTop = window.pageYOffset || document.documentElement.scrollTop; + const documentHeight = document.documentElement.scrollHeight - navbarHeight; + const windowHeight = window.innerHeight; + + if (scrollTop < navbarHeight) { + return; + } + + if (lastScrollTop && scrollTop > lastScrollTop) { + setIsNavbarVisible(false); + } else if (scrollTop + windowHeight < documentHeight) { + setIsNavbarVisible(true); + } + + setLastScrollTop(scrollTop); + }; + + useEffect(() => { + if (!hideOnScroll) { + return undefined; + } + + window.addEventListener('scroll', handleScroll); + + return () => { + window.removeEventListener('scroll', handleScroll); + }; + }, [lastScrollTop, navbarHeight]); + + return { + navbarRef, + isNavbarVisible, + }; +}; + +export default useHideableNavbar; diff --git a/website/docs/theme-classic.md b/website/docs/theme-classic.md index f1bf6352368b..0fb981b2b362 100644 --- a/website/docs/theme-classic.md +++ b/website/docs/theme-classic.md @@ -90,6 +90,23 @@ module.exports = { Outbound links automatically get `target="_blank" rel="noopener noreferrer"`. +### Auto-hide sticky navbar + +You can enable this cool UI feature that automatically hides the navbar when a user starts scrolling down the page, and show it again when the user scrolls up. + +```js +// docusaurus/config.js +module.exports = { + ... + themeConfig: { + navbar: { + hideOnScroll: true, + }, + ... + }, +} +``` + ## Footer ## `CodeBlock`