Skip to content

Commit e97dc0d

Browse files
slorberJosh-Cena
andauthored
refactor(theme-classic): split sidebar into smaller parts (#6844)
Co-authored-by: Joshua Chen <[email protected]>
1 parent e412d36 commit e97dc0d

File tree

12 files changed

+366
-228
lines changed

12 files changed

+366
-228
lines changed

packages/docusaurus-theme-classic/src/theme-classic.d.ts

+36
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,42 @@ declare module '@theme/DocSidebar' {
180180
export default function DocSidebar(props: Props): JSX.Element;
181181
}
182182

183+
declare module '@theme/DocSidebar/Mobile' {
184+
import type {Props as DocSidebarProps} from '@theme/DocSidebar';
185+
186+
export interface Props extends DocSidebarProps {}
187+
188+
export default function DocSidebarMobile(props: Props): JSX.Element;
189+
}
190+
191+
declare module '@theme/DocSidebar/Desktop' {
192+
import type {Props as DocSidebarProps} from '@theme/DocSidebar';
193+
194+
export interface Props extends DocSidebarProps {}
195+
196+
export default function DocSidebarDesktop(props: Props): JSX.Element;
197+
}
198+
199+
declare module '@theme/DocSidebar/Desktop/Content' {
200+
import type {PropSidebarItem} from '@docusaurus/plugin-content-docs';
201+
202+
export interface Props {
203+
readonly className?: string;
204+
readonly path: string;
205+
readonly sidebar: readonly PropSidebarItem[];
206+
}
207+
208+
export default function CollapseButton(props: Props): JSX.Element;
209+
}
210+
211+
declare module '@theme/DocSidebar/Desktop/CollapseButton' {
212+
export interface Props {
213+
onClick: React.MouseEventHandler;
214+
}
215+
216+
export default function CollapseButton(props: Props): JSX.Element;
217+
}
218+
183219
declare module '@theme/DocSidebarItem' {
184220
import type {PropSidebarItem} from '@docusaurus/plugin-content-docs';
185221

Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
import React from 'react';
9+
import clsx from 'clsx';
10+
import IconArrow from '@theme/IconArrow';
11+
import {translate} from '@docusaurus/Translate';
12+
import type {Props} from '@theme/DocSidebar/Desktop/CollapseButton';
13+
14+
import styles from './styles.module.css';
15+
16+
export default function CollapseButton({onClick}: Props): JSX.Element {
17+
return (
18+
<button
19+
type="button"
20+
title={translate({
21+
id: 'theme.docs.sidebar.collapseButtonTitle',
22+
message: 'Collapse sidebar',
23+
description: 'The title attribute for collapse button of doc sidebar',
24+
})}
25+
aria-label={translate({
26+
id: 'theme.docs.sidebar.collapseButtonAriaLabel',
27+
message: 'Collapse sidebar',
28+
description: 'The title attribute for collapse button of doc sidebar',
29+
})}
30+
className={clsx(
31+
'button button--secondary button--outline',
32+
styles.collapseSidebarButton,
33+
)}
34+
onClick={onClick}>
35+
<IconArrow className={styles.collapseSidebarButtonIcon} />
36+
</button>
37+
);
38+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
:root {
9+
--collapse-button-bg-color-dark: #2e333a;
10+
}
11+
12+
@media (min-width: 997px) {
13+
.collapseSidebarButton {
14+
display: block !important;
15+
background-color: var(--ifm-button-background-color);
16+
height: 40px;
17+
position: sticky;
18+
bottom: 0;
19+
border-radius: 0;
20+
border: 1px solid var(--ifm-toc-border-color);
21+
}
22+
23+
.collapseSidebarButtonIcon {
24+
transform: rotate(180deg);
25+
margin-top: 4px;
26+
}
27+
28+
[dir='rtl'] .collapseSidebarButtonIcon {
29+
transform: rotate(0);
30+
}
31+
32+
[data-theme='dark'] .collapseSidebarButton {
33+
background-color: var(--collapse-button-bg-color-dark);
34+
}
35+
36+
[data-theme='dark'] .collapseSidebarButton:hover,
37+
[data-theme='dark'] .collapseSidebarButton:focus {
38+
background-color: var(--ifm-color-emphasis-200);
39+
}
40+
}
41+
42+
.collapseSidebarButton {
43+
display: none;
44+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,55 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
import React, {useState} from 'react';
9+
import clsx from 'clsx';
10+
import {
11+
ThemeClassNames,
12+
useAnnouncementBar,
13+
useScrollPosition,
14+
} from '@docusaurus/theme-common';
15+
import DocSidebarItems from '@theme/DocSidebarItems';
16+
import type {Props} from '@theme/DocSidebar/Desktop/Content';
17+
18+
import styles from './styles.module.css';
19+
20+
function useShowAnnouncementBar() {
21+
const {isActive} = useAnnouncementBar();
22+
const [showAnnouncementBar, setShowAnnouncementBar] = useState(isActive);
23+
24+
useScrollPosition(
25+
({scrollY}) => {
26+
if (isActive) {
27+
setShowAnnouncementBar(scrollY === 0);
28+
}
29+
},
30+
[isActive],
31+
);
32+
return isActive && showAnnouncementBar;
33+
}
34+
35+
export default function DocSidebarDesktopContent({
36+
path,
37+
sidebar,
38+
className,
39+
}: Props): JSX.Element {
40+
const showAnnouncementBar = useShowAnnouncementBar();
41+
42+
return (
43+
<nav
44+
className={clsx(
45+
'menu thin-scrollbar',
46+
styles.menu,
47+
showAnnouncementBar && styles.menuWithAnnouncementBar,
48+
className,
49+
)}>
50+
<ul className={clsx(ThemeClassNames.docs.docSidebarMenu, 'menu__list')}>
51+
<DocSidebarItems items={sidebar} activePath={path} level={1} />
52+
</ul>
53+
</nav>
54+
);
55+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
@media (min-width: 997px) {
9+
.menu {
10+
flex-grow: 1;
11+
padding: 0.5rem;
12+
}
13+
14+
.menuWithAnnouncementBar {
15+
margin-bottom: var(--docusaurus-announcement-bar-height);
16+
}
17+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
import React from 'react';
9+
import clsx from 'clsx';
10+
import {useThemeConfig} from '@docusaurus/theme-common';
11+
import Logo from '@theme/Logo';
12+
import CollapseButton from '@theme/DocSidebar/Desktop/CollapseButton';
13+
import Content from '@theme/DocSidebar/Desktop/Content';
14+
import type {Props} from '@theme/DocSidebar/Desktop';
15+
16+
import styles from './styles.module.css';
17+
18+
function DocSidebarDesktop({path, sidebar, onCollapse, isHidden}: Props) {
19+
const {
20+
navbar: {hideOnScroll},
21+
hideableSidebar,
22+
} = useThemeConfig();
23+
24+
return (
25+
<div
26+
className={clsx(
27+
styles.sidebar,
28+
hideOnScroll && styles.sidebarWithHideableNavbar,
29+
isHidden && styles.sidebarHidden,
30+
)}>
31+
{hideOnScroll && <Logo tabIndex={-1} className={styles.sidebarLogo} />}
32+
<Content path={path} sidebar={sidebar} />
33+
{hideableSidebar && <CollapseButton onClick={onCollapse} />}
34+
</div>
35+
);
36+
}
37+
38+
export default React.memo(DocSidebarDesktop);
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
@media (min-width: 997px) {
9+
.sidebar {
10+
display: flex;
11+
flex-direction: column;
12+
max-height: 100vh;
13+
height: 100%;
14+
position: sticky;
15+
top: 0;
16+
padding-top: var(--ifm-navbar-height);
17+
width: var(--doc-sidebar-width);
18+
transition: opacity 50ms ease;
19+
}
20+
21+
.sidebarWithHideableNavbar {
22+
padding-top: 0;
23+
}
24+
25+
.sidebarHidden {
26+
opacity: 0;
27+
height: 0;
28+
overflow: hidden;
29+
visibility: hidden;
30+
}
31+
32+
.sidebarLogo {
33+
display: flex !important;
34+
align-items: center;
35+
margin: 0 var(--ifm-navbar-padding-horizontal);
36+
min-height: var(--ifm-navbar-height);
37+
max-height: var(--ifm-navbar-height);
38+
color: inherit !important;
39+
text-decoration: none !important;
40+
}
41+
42+
.sidebarLogo img {
43+
margin-right: 0.5rem;
44+
height: 2rem;
45+
}
46+
}
47+
48+
.sidebarLogo {
49+
display: none;
50+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,51 @@
1+
/**
2+
* Copyright (c) Facebook, Inc. and its affiliates.
3+
*
4+
* This source code is licensed under the MIT license found in the
5+
* LICENSE file in the root directory of this source tree.
6+
*/
7+
8+
import React from 'react';
9+
import clsx from 'clsx';
10+
import {
11+
MobileSecondaryMenuFiller,
12+
type MobileSecondaryMenuComponent,
13+
ThemeClassNames,
14+
} from '@docusaurus/theme-common';
15+
import DocSidebarItems from '@theme/DocSidebarItems';
16+
import type {Props} from '@theme/DocSidebar/Mobile';
17+
18+
// eslint-disable-next-line react/function-component-definition
19+
const DocSidebarMobileSecondaryMenu: MobileSecondaryMenuComponent<Props> = ({
20+
toggleSidebar,
21+
sidebar,
22+
path,
23+
}) => (
24+
<ul className={clsx(ThemeClassNames.docs.docSidebarMenu, 'menu__list')}>
25+
<DocSidebarItems
26+
items={sidebar}
27+
activePath={path}
28+
onItemClick={(item) => {
29+
// Mobile sidebar should only be closed if the category has a link
30+
if (item.type === 'category' && item.href) {
31+
toggleSidebar();
32+
}
33+
if (item.type === 'link') {
34+
toggleSidebar();
35+
}
36+
}}
37+
level={1}
38+
/>
39+
</ul>
40+
);
41+
42+
function DocSidebarMobile(props: Props) {
43+
return (
44+
<MobileSecondaryMenuFiller
45+
component={DocSidebarMobileSecondaryMenu}
46+
props={props}
47+
/>
48+
);
49+
}
50+
51+
export default React.memo(DocSidebarMobile);

0 commit comments

Comments
 (0)