diff --git a/packages/docusaurus-theme-classic/src/theme/hooks/useHideableNavbar.js b/packages/docusaurus-theme-classic/src/theme/hooks/useHideableNavbar.js index 4e753c947f..d5085dc808 100644 --- a/packages/docusaurus-theme-classic/src/theme/hooks/useHideableNavbar.js +++ b/packages/docusaurus-theme-classic/src/theme/hooks/useHideableNavbar.js @@ -8,6 +8,7 @@ import {useState, useCallback, useEffect} from 'react'; import {useLocation} from '@docusaurus/router'; import useLocationHash from '@theme/hooks/useLocationHash'; +import useScrollPosition from '@theme/hooks/useScrollPosition'; const useHideableNavbar = (hideOnScroll) => { const [isNavbarVisible, setIsNavbarVisible] = useState(true); @@ -22,47 +23,41 @@ const useHideableNavbar = (hideOnScroll) => { const location = useLocation(); const [hash, setHash] = useLocationHash(location.hash); - const handleScroll = () => { - const scrollTop = window.pageYOffset || document.documentElement.scrollTop; + useScrollPosition( + ({scrollY: scrollTop}) => { + if (!hideOnScroll) { + return; + } - if (scrollTop === 0) { - setIsNavbarVisible(true); - } + if (scrollTop === 0) { + setIsNavbarVisible(true); + } - if (scrollTop < navbarHeight) { - return; - } + if (scrollTop < navbarHeight) { + return; + } + + if (isFocusedAnchor) { + setIsFocusedAnchor(false); + setIsNavbarVisible(false); + setLastScrollTop(scrollTop); + return; + } + + const documentHeight = + document.documentElement.scrollHeight - navbarHeight; + const windowHeight = window.innerHeight; + + if (lastScrollTop && scrollTop >= lastScrollTop) { + setIsNavbarVisible(false); + } else if (scrollTop + windowHeight < documentHeight) { + setIsNavbarVisible(true); + } - if (isFocusedAnchor) { - setIsFocusedAnchor(false); - setIsNavbarVisible(false); setLastScrollTop(scrollTop); - return; - } - - const documentHeight = document.documentElement.scrollHeight - navbarHeight; - const windowHeight = window.innerHeight; - - 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]); + }, + [lastScrollTop, navbarHeight], + ); useEffect(() => { if (!hideOnScroll) { diff --git a/packages/docusaurus-theme-classic/src/theme/hooks/useScrollPosition.js b/packages/docusaurus-theme-classic/src/theme/hooks/useScrollPosition.js new file mode 100644 index 0000000000..4295b1d9ff --- /dev/null +++ b/packages/docusaurus-theme-classic/src/theme/hooks/useScrollPosition.js @@ -0,0 +1,41 @@ +/** + * Copyright (c) Facebook, Inc. and its affiliates. + * + * This source code is licensed under the MIT license found in the + * LICENSE file in the root directory of this source tree. + */ + +import {useState, useEffect} from 'react'; +import ExecutionEnvironment from '@docusaurus/ExecutionEnvironment'; + +const getScrollPosition = () => ({ + scrollX: ExecutionEnvironment.canUseDOM ? window.pageXOffset : 0, + scrollY: ExecutionEnvironment.canUseDOM ? window.pageYOffset : 0, +}); + +const useScrollPosition = (effect, deps = []) => { + const [scrollPosition, setScrollPosition] = useState(getScrollPosition()); + + const handleScroll = () => { + const currentScrollPosition = getScrollPosition(); + + setScrollPosition(currentScrollPosition); + + if (effect) { + effect(currentScrollPosition); + } + }; + + useEffect(() => { + window.addEventListener('scroll', handleScroll); + + return () => + window.removeEventListener('scroll', handleScroll, { + passive: true, + }); + }, deps); + + return scrollPosition; +}; + +export default useScrollPosition;