diff --git a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DropdownNavbarItem.tsx b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DropdownNavbarItem.tsx index b8ab9d7cd5..7dec9776e9 100644 --- a/packages/docusaurus-theme-classic/src/theme/NavbarItem/DropdownNavbarItem.tsx +++ b/packages/docusaurus-theme-classic/src/theme/NavbarItem/DropdownNavbarItem.tsx @@ -7,21 +7,49 @@ import React, {useState, useRef, useEffect} from 'react'; import clsx from 'clsx'; -import {useLocation} from '@docusaurus/router'; import { isSamePath, useCollapsible, Collapsible, + useLocalPathname, } from '@docusaurus/theme-common'; import type { DesktopOrMobileNavBarItemProps, Props, } from '@theme/NavbarItem/DropdownNavbarItem'; +import type {LinkLikeNavbarItemProps} from '@theme/NavbarItem'; + import {NavLink} from '@theme/NavbarItem/DefaultNavbarItem'; import NavbarItem from '@theme/NavbarItem'; const dropdownLinkActiveClass = 'dropdown__link--active'; +function isItemActive( + item: LinkLikeNavbarItemProps, + localPathname: string, +): boolean { + if (isSamePath(item.to, localPathname)) { + return true; + } + if ( + item.activeBaseRegex && + new RegExp(item.activeBaseRegex).test(localPathname) + ) { + return true; + } + if (item.activeBasePath && localPathname.startsWith(item.activeBasePath)) { + return true; + } + return false; +} + +function containsActiveItems( + items: readonly LinkLikeNavbarItemProps[], + localPathname: string, +): boolean { + return items.some((item) => isItemActive(item, localPathname)); +} + function DropdownNavbarItemDesktop({ items, position, @@ -102,12 +130,20 @@ function DropdownNavbarItemMobile({ position: _position, // Need to destructure position from props so that it doesn't get passed on. ...props }: DesktopOrMobileNavBarItemProps) { - const {pathname} = useLocation(); - const {collapsed, toggleCollapsed} = useCollapsible({ - initialState: () => - !items?.some((item) => isSamePath(item.to, pathname)) ?? true, + const localPathname = useLocalPathname(); + const containsActive = containsActiveItems(items, localPathname); + + const {collapsed, toggleCollapsed, setCollapsed} = useCollapsible({ + initialState: () => !containsActive, }); + // Expand/collapse if any item active after a navigation + useEffect(() => { + if (containsActive) { + setCollapsed(!containsActive); + } + }, [localPathname, containsActive]); + return (