From f8cb3076612f105e2d254f1fd8e241c50546b4ac Mon Sep 17 00:00:00 2001 From: Artea Date: Sat, 10 May 2025 18:56:54 +0800 Subject: [PATCH 1/2] feat(theme/sidebar): collapsible sidebar --- .../src/assets/panel-left-close.svg | 1 + .../src/assets/panel-left-open.svg | 1 + .../src/components/Nav/index.tsx | 38 +++++++++++++++++-- .../theme-default/src/layout/Layout/index.tsx | 10 +++++ .../theme-default/src/logic/useUISwitch.ts | 5 ++- 5 files changed, 51 insertions(+), 4 deletions(-) create mode 100644 packages/theme-default/src/assets/panel-left-close.svg create mode 100644 packages/theme-default/src/assets/panel-left-open.svg 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..a35d43000 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 { useState } from 'react'; import type { NavProps } from '../../components/Nav'; import { useSetup } from '../../logic/sideEffects'; import { useLocaleSiteData } from '../../logic/useLocaleSiteData'; @@ -129,10 +130,17 @@ 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(), + showSidebar, ...props.uiSwitch, }; @@ -177,6 +185,8 @@ export function Layout(props: LayoutProps) { navTitle={navTitle} beforeNav={beforeNav} afterNavMenu={afterNavMenu} + showSidebar={showSidebar} + toggleShowSidebar={toggleShowSideBar} /> )} diff --git a/packages/theme-default/src/logic/useUISwitch.ts b/packages/theme-default/src/logic/useUISwitch.ts index dd55a4459..cfc513261 100644 --- a/packages/theme-default/src/logic/useUISwitch.ts +++ b/packages/theme-default/src/logic/useUISwitch.ts @@ -47,9 +47,11 @@ export function useUISwitch(): UISwitchResult { // 1. frontmatter.sidebar // 2. themeConfig.locales.sidebar // 3. themeConfig.sidebar - const showSidebar = + const initShowSidebar = frontmatter?.sidebar !== false && Object.keys(sidebar).length > 0; + const [showSidebar, setShowSidebar] = useState(initShowSidebar); + const { width } = useWindowSize(); const showSidebarMenu = @@ -80,6 +82,7 @@ export function useUISwitch(): UISwitchResult { if (sidebar === QueryStatus.Hide) { document.documentElement.style.setProperty('--rp-sidebar-width', '0px'); + setShowSidebar(false); } if (aside === QueryStatus.Hide) { From 631c1c6e02fee7a696feb19aa85eb934c5ba0743 Mon Sep 17 00:00:00 2001 From: Artea Date: Sat, 10 May 2025 21:10:22 +0800 Subject: [PATCH 2/2] fix(theme/sidebar): update layout when switch page --- .../theme-default/src/layout/Layout/index.tsx | 19 ++++++++++++------- .../theme-default/src/logic/useUISwitch.ts | 8 ++++++-- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/packages/theme-default/src/layout/Layout/index.tsx b/packages/theme-default/src/layout/Layout/index.tsx index a35d43000..b199f75ad 100644 --- a/packages/theme-default/src/layout/Layout/index.tsx +++ b/packages/theme-default/src/layout/Layout/index.tsx @@ -9,7 +9,7 @@ import { import { useHead } from '@unhead/react'; import { Head } from '@unhead/react'; import type React from 'react'; -import { useState } from 'react'; +import { useMemo, useState } from 'react'; import type { NavProps } from '../../components/Nav'; import { useSetup } from '../../logic/sideEffects'; import { useLocaleSiteData } from '../../logic/useLocaleSiteData'; @@ -138,11 +138,16 @@ export function Layout(props: LayoutProps) { // 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(), - showSidebar, - ...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 = () => { @@ -185,7 +190,7 @@ export function Layout(props: LayoutProps) { navTitle={navTitle} beforeNav={beforeNav} afterNavMenu={afterNavMenu} - showSidebar={showSidebar} + showSidebar={uiSwitch.showSidebar} toggleShowSidebar={toggleShowSideBar} /> )} diff --git a/packages/theme-default/src/logic/useUISwitch.ts b/packages/theme-default/src/logic/useUISwitch.ts index cfc513261..fb7ca5942 100644 --- a/packages/theme-default/src/logic/useUISwitch.ts +++ b/packages/theme-default/src/logic/useUISwitch.ts @@ -47,10 +47,14 @@ export function useUISwitch(): UISwitchResult { // 1. frontmatter.sidebar // 2. themeConfig.locales.sidebar // 3. themeConfig.sidebar - const initShowSidebar = + const calcShowSidebar = frontmatter?.sidebar !== false && Object.keys(sidebar).length > 0; - const [showSidebar, setShowSidebar] = useState(initShowSidebar); + const [showSidebar, setShowSidebar] = useState(calcShowSidebar); + + useEffect(() => { + setShowSidebar(calcShowSidebar); + }, [calcShowSidebar]); const { width } = useWindowSize();