diff --git a/packages/theme-default/src/assets/panel-left-close.svg b/packages/theme-default/src/assets/panel-left-close.svg new file mode 100644 index 000000000..7ec44f678 --- /dev/null +++ b/packages/theme-default/src/assets/panel-left-close.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/theme-default/src/assets/panel-left-open.svg b/packages/theme-default/src/assets/panel-left-open.svg new file mode 100644 index 000000000..36c0dc972 --- /dev/null +++ b/packages/theme-default/src/assets/panel-left-open.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/packages/theme-default/src/components/Nav/index.tsx b/packages/theme-default/src/components/Nav/index.tsx index e87e07371..ac735ff45 100644 --- a/packages/theme-default/src/components/Nav/index.tsx +++ b/packages/theme-default/src/components/Nav/index.tsx @@ -1,10 +1,15 @@ import { useLocation, usePageData, useWindowSize } from '@rspress/runtime'; import type { NavItem } from '@rspress/shared'; import { Search } from '@theme'; +import PanelLeftClose from '@theme-assets/panel-left-close'; +import PanelLeftOpen from '@theme-assets/panel-left-open'; import { useHiddenNav } from '../../logic/useHiddenNav'; +import { useMediaQuery } from '../../logic/useMediaQuery'; import { useNavData } from '../../logic/useNav'; +import { useUISwitch } from '../../logic/useUISwitch'; import { NavHamburger } from '../NavHamburger'; import { SocialLinks } from '../SocialLinks'; +import { SvgWrapper } from '../SvgWrapper'; import { SwitchAppearance } from '../SwitchAppearance'; import { NavBarTitle } from './NavBarTitle'; import { NavMenuGroup } from './NavMenuGroup'; @@ -19,13 +24,23 @@ export interface NavProps { navTitle?: React.ReactNode; afterNavTitle?: React.ReactNode; afterNavMenu?: React.ReactNode; + showSidebar?: boolean; + toggleShowSidebar?: () => void; } const DEFAULT_NAV_POSITION = 'right'; export function Nav(props: NavProps) { - const { beforeNavTitle, afterNavTitle, beforeNav, afterNavMenu, navTitle } = - props; + const { + beforeNavTitle, + afterNavTitle, + beforeNav, + afterNavMenu, + navTitle, + showSidebar, + toggleShowSidebar, + } = props; + const uiSwitch = useUISwitch(); const { siteData, page } = usePageData(); const { base } = siteData; const { pathname } = useLocation(); @@ -122,6 +137,11 @@ export function Nav(props: NavProps) { } return styles.relative; }; + // sync opacity with sidebar + const is960 = useMediaQuery('(min-width: 960px)'); + const showToggleBtn = + uiSwitch.showSidebar && page.pageType === 'doc' && is960; + const toggleSidebarIcon = showSidebar ? PanelLeftClose : PanelLeftOpen; return ( <> @@ -133,11 +153,23 @@ export function Nav(props: NavProps) { } ${computeNavPosition()}`} >
{beforeNavTitle} {navTitle || } {afterNavTitle} + + {showToggleBtn ? ( + + ) : null} +
{leftNav()} {rightNav()} diff --git a/packages/theme-default/src/layout/Layout/index.tsx b/packages/theme-default/src/layout/Layout/index.tsx index ade29f248..b199f75ad 100644 --- a/packages/theme-default/src/layout/Layout/index.tsx +++ b/packages/theme-default/src/layout/Layout/index.tsx @@ -9,6 +9,7 @@ import { import { useHead } from '@unhead/react'; import { Head } from '@unhead/react'; import type React from 'react'; +import { useMemo, useState } from 'react'; import type { NavProps } from '../../components/Nav'; import { useSetup } from '../../logic/sideEffects'; import { useLocaleSiteData } from '../../logic/useLocaleSiteData'; @@ -129,12 +130,24 @@ export function Layout(props: LayoutProps) { siteData.description || localesData.description; + const [showSidebar, setShowSidebar] = useState( + page.frontmatter.sidebar !== false, + ); + + const toggleShowSideBar = () => setShowSidebar(prev => !prev); + // Control whether or not to display the navbar, sidebar, outline and footer // `props.uiSwitch` has higher priority and allows user to override the default value - const uiSwitch = { - ...useUISwitch(), - ...props.uiSwitch, - }; + const defaultUiSwitch = useUISwitch(); + + const uiSwitch = useMemo( + () => ({ + ...defaultUiSwitch, + showSidebar: defaultUiSwitch.showSidebar !== false && showSidebar, + ...props.uiSwitch, + }), + [defaultUiSwitch, showSidebar, props.uiSwitch], + ); // Use doc layout by default const getContentLayout = () => { @@ -177,6 +190,8 @@ export function Layout(props: LayoutProps) { navTitle={navTitle} beforeNav={beforeNav} afterNavMenu={afterNavMenu} + showSidebar={uiSwitch.showSidebar} + toggleShowSidebar={toggleShowSideBar} /> )} diff --git a/packages/theme-default/src/logic/useUISwitch.ts b/packages/theme-default/src/logic/useUISwitch.ts index dd55a4459..fb7ca5942 100644 --- a/packages/theme-default/src/logic/useUISwitch.ts +++ b/packages/theme-default/src/logic/useUISwitch.ts @@ -47,9 +47,15 @@ export function useUISwitch(): UISwitchResult { // 1. frontmatter.sidebar // 2. themeConfig.locales.sidebar // 3. themeConfig.sidebar - const showSidebar = + const calcShowSidebar = frontmatter?.sidebar !== false && Object.keys(sidebar).length > 0; + const [showSidebar, setShowSidebar] = useState(calcShowSidebar); + + useEffect(() => { + setShowSidebar(calcShowSidebar); + }, [calcShowSidebar]); + const { width } = useWindowSize(); const showSidebarMenu = @@ -80,6 +86,7 @@ export function useUISwitch(): UISwitchResult { if (sidebar === QueryStatus.Hide) { document.documentElement.style.setProperty('--rp-sidebar-width', '0px'); + setShowSidebar(false); } if (aside === QueryStatus.Hide) {