/** * 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 React, {useCallback, useState, useEffect} from 'react'; import clsx from 'clsx'; import Translate from '@docusaurus/Translate'; import SearchBar from '@theme/SearchBar'; import Toggle from '@theme/Toggle'; import { useThemeConfig, useMobileSecondaryMenuRenderer, usePrevious, useHistoryPopHandler, useHideableNavbar, useLockBodyScroll, useWindowSize, useColorMode, } from '@docusaurus/theme-common'; import {useActivePlugin} from '@docusaurus/plugin-content-docs/client'; import NavbarItem from '@theme/NavbarItem'; import Logo from '@theme/Logo'; import IconMenu from '@theme/IconMenu'; import IconClose from '@theme/IconClose'; import styles from './styles.module.css'; // retrocompatible with v1 const DefaultNavItemPosition = 'right'; function useNavbarItems() { // TODO temporary casting until ThemeConfig type is improved return useThemeConfig().navbar.items; } // If split links by left/right // if position is unspecified, fallback to right (as v1) function splitNavItemsByPosition(items) { const leftItems = items.filter( (item) => (item.position ?? DefaultNavItemPosition) === 'left', ); const rightItems = items.filter( (item) => (item.position ?? DefaultNavItemPosition) === 'right', ); return { leftItems, rightItems, }; } function useMobileSidebar() { const windowSize = useWindowSize(); // Mobile sidebar not visible on hydration: can avoid SSR rendering const shouldRender = windowSize === 'mobile'; // || windowSize === 'ssr'; const [shown, setShown] = useState(false); // Close mobile sidebar on navigation pop // Most likely firing when using the Android back button (but not only) useHistoryPopHandler(() => { if (shown) { setShown(false); // Should we prevent the navigation here? // See https://github.com/facebook/docusaurus/pull/5462#issuecomment-911699846 return false; // prevent pop navigation } return undefined; }); const toggle = useCallback(() => { setShown((s) => !s); }, []); useEffect(() => { if (windowSize === 'desktop') { setShown(false); } }, [windowSize]); return { shouldRender, toggle, shown, }; } function useColorModeToggle() { const { colorMode: {disableSwitch}, } = useThemeConfig(); const {isDarkTheme, setLightTheme, setDarkTheme} = useColorMode(); const toggle = useCallback( (e) => (e.target.checked ? setDarkTheme() : setLightTheme()), [setLightTheme, setDarkTheme], ); return { isDarkTheme, toggle, disabled: disableSwitch, }; } function useSecondaryMenu({sidebarShown, toggleSidebar}) { const content = useMobileSecondaryMenuRenderer()?.({ toggleSidebar, }); const previousContent = usePrevious(content); const [shown, setShown] = useState( () => // /!\ content is set with useEffect, // so it's not available on mount anyway // "return !!content" => always returns false false, ); // When content is become available for the first time (set in useEffect) // we set this content to be shown! useEffect(() => { const contentBecameAvailable = content && !previousContent; if (contentBecameAvailable) { setShown(true); } }, [content, previousContent]); const hasContent = !!content; // On sidebar close, secondary menu is set to be shown on next re-opening // (if any secondary menu content available) useEffect(() => { if (!hasContent) { setShown(false); return; } if (!sidebarShown) { setShown(true); } }, [sidebarShown, hasContent]); const hide = useCallback(() => { setShown(false); }, []); return { shown, hide, content, }; } function NavbarMobileSidebar({sidebarShown, toggleSidebar}) { useLockBodyScroll(sidebarShown); const items = useNavbarItems(); const colorModeToggle = useColorModeToggle(); const secondaryMenu = useSecondaryMenu({ sidebarShown, toggleSidebar, }); return (