| import React, { type JSX, useEffect, useState, useCallback } from 'react'; | |
| import { createPortal } from 'react-dom'; | |
| import clsx from 'clsx'; | |
| import { translate } from '@docusaurus/Translate'; | |
| import useIsBrowser from '@docusaurus/useIsBrowser'; | |
| import { useLocation } from '@docusaurus/router'; | |
| import { useActivePlugin, useActiveVersion, useDocsSidebar } from '@docusaurus/plugin-content-docs/client'; | |
| import { ThemeClassNames } from '@docusaurus/theme-common'; | |
| import DocSidebarItems from '@theme/DocSidebarItems'; | |
| import SearchBar from '@theme/SearchBar'; | |
| import { getDocsSidebarScope, supportsDocsDomainNavigation } from '@site/src/utils/docs-sidebar-scope'; | |
| import styles from './MobileSidebarDrawer.module.css'; | |
| export default function MobileSidebarDrawer(): JSX.Element | null { | |
| const isBrowser = useIsBrowser(); | |
| const sidebar = useDocsSidebar(); | |
| const activePlugin = useActivePlugin(); | |
| const activeVersion = useActiveVersion(activePlugin?.pluginId); | |
| const { pathname } = useLocation(); | |
| const usesDomainNavigation = supportsDocsDomainNavigation(activePlugin?.pluginId, activeVersion?.name); | |
| const [open, setOpen] = useState(false); | |
| const close = useCallback(() => setOpen(false), []); | |
| useEffect(() => { | |
| if (!open) return undefined; | |
| const { style } = document.body; | |
| const previous = style.overflow; | |
| style.overflow = 'hidden'; | |
| const onKey = (e: KeyboardEvent) => { | |
| if (e.key === 'Escape') setOpen(false); | |
| }; | |
| window.addEventListener('keydown', onKey); | |
| return () => { | |
| style.overflow = previous; | |
| window.removeEventListener('keydown', onKey); | |
| }; | |
| }, [open]); | |
| useEffect(() => { | |
| setOpen(false); | |
| }, [pathname]); | |
| if (!sidebar) return null; | |
| const sidebarScope = getDocsSidebarScope(sidebar.items, pathname); | |
| const scopedSidebarItems = usesDomainNavigation ? sidebarScope.scopedItems : sidebar.items; | |
| const drawerLabel = usesDomainNavigation && sidebarScope.activeDomain | |
| ? sidebarScope.activeDomain.label | |
| : translate({id: 'docs.navigation.menu', message: 'Menu'}); | |
| const drawer = ( | |
| <div className={clsx(open && styles.open)} aria-hidden={!open}> | |
| <div | |
| className={styles.backdrop} | |
| onClick={close} | |
| role="presentation" | |
| /> | |
| <aside | |
| className={styles.drawer} | |
| aria-label={translate({id: 'docs.navigation.sidebar', message: 'Docs sidebar'})} | |
| > | |
| <div className={styles.header}> | |
| <span>{drawerLabel}</span> | |
| <button | |
| type="button" | |
| className={styles.close} | |
| onClick={close} | |
| aria-label={translate({id: 'docs.navigation.closeSidebar', message: 'Close docs sidebar'})} | |
| > | |
| <svg width="20" height="20" viewBox="0 0 20 20" fill="none" aria-hidden="true"> | |
| <path | |
| d="M5 5l10 10M15 5L5 15" | |
| stroke="currentColor" | |
| strokeWidth="1.6" | |
| strokeLinecap="round" | |
| /> | |
| </svg> | |
| </button> | |
| </div> | |
| <nav | |
| className={styles.body} | |
| aria-label={translate({id: 'docs.navigation.sidebarNavigation', message: 'Docs sidebar navigation'})} | |
| > | |
| <ul className={clsx(ThemeClassNames.docs.docSidebarMenu, 'menu__list', styles.menu)}> | |
| <DocSidebarItems | |
| items={scopedSidebarItems} | |
| activePath={pathname} | |
| onItemClick={(item) => { | |
| if (item.type === 'link') { | |
| close(); | |
| } | |
| if (item.type === 'category' && item.href) { | |
| close(); | |
| } | |
| }} | |
| level={1} | |
| /> | |
| </ul> | |
| </nav> | |
| </aside> | |
| </div> | |
| ); | |
| return ( | |
| <> | |
| <div className={styles.toolbar}> | |
| {!usesDomainNavigation && ( | |
| <div className={styles.toolbarSearch}> | |
| <SearchBar /> | |
| </div> | |
| )} | |
| <button | |
| type="button" | |
| className={clsx(styles.toolbarIconBtn, usesDomainNavigation && styles.toolbarMenuBtn)} | |
| onClick={() => setOpen(true)} | |
| aria-label={translate({id: 'docs.navigation.openSidebar', message: 'Open docs sidebar'})} | |
| aria-expanded={open} | |
| > | |
| <svg className={styles.toolbarIcon} viewBox="0 0 16 16" fill="none" aria-hidden="true"> | |
| <path d="M2 4h12M2 8h12M2 12h12" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" /> | |
| </svg> | |
| {usesDomainNavigation && ( | |
| <span className={styles.toolbarMenuLabel}> | |
| {translate({id: 'docs.navigation.browseSection', message: 'Browse this section'})} | |
| </span> | |
| )} | |
| </button> | |
| </div> | |
| {isBrowser && createPortal(drawer, document.body)} | |
| </> | |
| ); | |
| } |