/** * 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, useCallback, useRef} from 'react'; import {useLocationChange} from '../utils/useLocationChange'; import {useScrollPosition} from '../utils/scrollUtils'; /** * Wires the imperative logic of a hideable navbar. * @param hideOnScroll If `false`, this hook is basically a no-op. */ export function useHideableNavbar(hideOnScroll: boolean): { /** A ref to the navbar component. Plug this into the actual element. */ readonly navbarRef: (node: HTMLElement | null) => void; /** If `false`, the navbar component should not be rendered. */ readonly isNavbarVisible: boolean; } { const [isNavbarVisible, setIsNavbarVisible] = useState(hideOnScroll); const isFocusedAnchor = useRef(false); const navbarHeight = useRef(0); const navbarRef = useCallback((node: HTMLElement | null) => { if (node !== null) { navbarHeight.current = node.getBoundingClientRect().height; } }, []); useScrollPosition(({scrollY: scrollTop}, lastPosition) => { if (!hideOnScroll) { return; } // Needed mostly for handling rubber band scrolling. // See https://github.com/facebook/docusaurus/pull/5721 if (scrollTop < navbarHeight.current) { setIsNavbarVisible(true); return; } if (isFocusedAnchor.current) { isFocusedAnchor.current = false; return; } const lastScrollTop = lastPosition?.scrollY; const documentHeight = document.documentElement.scrollHeight - navbarHeight.current; const windowHeight = window.innerHeight; if (lastScrollTop && scrollTop >= lastScrollTop) { setIsNavbarVisible(false); } else if (scrollTop + windowHeight < documentHeight) { setIsNavbarVisible(true); } }); useLocationChange((locationChangeEvent) => { if (!hideOnScroll) { return; } // See https://github.com/facebook/docusaurus/pull/8059#issuecomment-1239639480 const currentHash = locationChangeEvent.location.hash; const currentHashAnchor = currentHash ? document.getElementById(currentHash.substring(1)) : undefined; if (currentHashAnchor) { isFocusedAnchor.current = true; setIsNavbarVisible(false); return; } setIsNavbarVisible(true); }); return {navbarRef, isNavbarVisible}; }