Skip to content

Commit

Permalink
refactor(theme-classic): split sidebar into smaller parts (#6844)
Browse files Browse the repository at this point in the history
Co-authored-by: Joshua Chen <sidachen2003@gmail.com>
  • Loading branch information
slorber and Josh-Cena committed Mar 10, 2022
1 parent e412d36 commit e97dc0d
Show file tree
Hide file tree
Showing 12 changed files with 366 additions and 228 deletions.
36 changes: 36 additions & 0 deletions packages/docusaurus-theme-classic/src/theme-classic.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -180,6 +180,42 @@ declare module '@theme/DocSidebar' {
export default function DocSidebar(props: Props): JSX.Element;
}

declare module '@theme/DocSidebar/Mobile' {
import type {Props as DocSidebarProps} from '@theme/DocSidebar';

export interface Props extends DocSidebarProps {}

export default function DocSidebarMobile(props: Props): JSX.Element;
}

declare module '@theme/DocSidebar/Desktop' {
import type {Props as DocSidebarProps} from '@theme/DocSidebar';

export interface Props extends DocSidebarProps {}

export default function DocSidebarDesktop(props: Props): JSX.Element;
}

declare module '@theme/DocSidebar/Desktop/Content' {
import type {PropSidebarItem} from '@docusaurus/plugin-content-docs';

export interface Props {
readonly className?: string;
readonly path: string;
readonly sidebar: readonly PropSidebarItem[];
}

export default function CollapseButton(props: Props): JSX.Element;
}

declare module '@theme/DocSidebar/Desktop/CollapseButton' {
export interface Props {
onClick: React.MouseEventHandler;
}

export default function CollapseButton(props: Props): JSX.Element;
}

declare module '@theme/DocSidebarItem' {
import type {PropSidebarItem} from '@docusaurus/plugin-content-docs';

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import React from 'react';
import clsx from 'clsx';
import IconArrow from '@theme/IconArrow';
import {translate} from '@docusaurus/Translate';
import type {Props} from '@theme/DocSidebar/Desktop/CollapseButton';

import styles from './styles.module.css';

export default function CollapseButton({onClick}: Props): JSX.Element {
return (
<button
type="button"
title={translate({
id: 'theme.docs.sidebar.collapseButtonTitle',
message: 'Collapse sidebar',
description: 'The title attribute for collapse button of doc sidebar',
})}
aria-label={translate({
id: 'theme.docs.sidebar.collapseButtonAriaLabel',
message: 'Collapse sidebar',
description: 'The title attribute for collapse button of doc sidebar',
})}
className={clsx(
'button button--secondary button--outline',
styles.collapseSidebarButton,
)}
onClick={onClick}>
<IconArrow className={styles.collapseSidebarButtonIcon} />
</button>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

:root {
--collapse-button-bg-color-dark: #2e333a;
}

@media (min-width: 997px) {
.collapseSidebarButton {
display: block !important;
background-color: var(--ifm-button-background-color);
height: 40px;
position: sticky;
bottom: 0;
border-radius: 0;
border: 1px solid var(--ifm-toc-border-color);
}

.collapseSidebarButtonIcon {
transform: rotate(180deg);
margin-top: 4px;
}

[dir='rtl'] .collapseSidebarButtonIcon {
transform: rotate(0);
}

[data-theme='dark'] .collapseSidebarButton {
background-color: var(--collapse-button-bg-color-dark);
}

[data-theme='dark'] .collapseSidebarButton:hover,
[data-theme='dark'] .collapseSidebarButton:focus {
background-color: var(--ifm-color-emphasis-200);
}
}

.collapseSidebarButton {
display: none;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import React, {useState} from 'react';
import clsx from 'clsx';
import {
ThemeClassNames,
useAnnouncementBar,
useScrollPosition,
} from '@docusaurus/theme-common';
import DocSidebarItems from '@theme/DocSidebarItems';
import type {Props} from '@theme/DocSidebar/Desktop/Content';

import styles from './styles.module.css';

function useShowAnnouncementBar() {
const {isActive} = useAnnouncementBar();
const [showAnnouncementBar, setShowAnnouncementBar] = useState(isActive);

useScrollPosition(
({scrollY}) => {
if (isActive) {
setShowAnnouncementBar(scrollY === 0);
}
},
[isActive],
);
return isActive && showAnnouncementBar;
}

export default function DocSidebarDesktopContent({
path,
sidebar,
className,
}: Props): JSX.Element {
const showAnnouncementBar = useShowAnnouncementBar();

return (
<nav
className={clsx(
'menu thin-scrollbar',
styles.menu,
showAnnouncementBar && styles.menuWithAnnouncementBar,
className,
)}>
<ul className={clsx(ThemeClassNames.docs.docSidebarMenu, 'menu__list')}>
<DocSidebarItems items={sidebar} activePath={path} level={1} />
</ul>
</nav>
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

@media (min-width: 997px) {
.menu {
flex-grow: 1;
padding: 0.5rem;
}

.menuWithAnnouncementBar {
margin-bottom: var(--docusaurus-announcement-bar-height);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import React from 'react';
import clsx from 'clsx';
import {useThemeConfig} from '@docusaurus/theme-common';
import Logo from '@theme/Logo';
import CollapseButton from '@theme/DocSidebar/Desktop/CollapseButton';
import Content from '@theme/DocSidebar/Desktop/Content';
import type {Props} from '@theme/DocSidebar/Desktop';

import styles from './styles.module.css';

function DocSidebarDesktop({path, sidebar, onCollapse, isHidden}: Props) {
const {
navbar: {hideOnScroll},
hideableSidebar,
} = useThemeConfig();

return (
<div
className={clsx(
styles.sidebar,
hideOnScroll && styles.sidebarWithHideableNavbar,
isHidden && styles.sidebarHidden,
)}>
{hideOnScroll && <Logo tabIndex={-1} className={styles.sidebarLogo} />}
<Content path={path} sidebar={sidebar} />
{hideableSidebar && <CollapseButton onClick={onCollapse} />}
</div>
);
}

export default React.memo(DocSidebarDesktop);
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

@media (min-width: 997px) {
.sidebar {
display: flex;
flex-direction: column;
max-height: 100vh;
height: 100%;
position: sticky;
top: 0;
padding-top: var(--ifm-navbar-height);
width: var(--doc-sidebar-width);
transition: opacity 50ms ease;
}

.sidebarWithHideableNavbar {
padding-top: 0;
}

.sidebarHidden {
opacity: 0;
height: 0;
overflow: hidden;
visibility: hidden;
}

.sidebarLogo {
display: flex !important;
align-items: center;
margin: 0 var(--ifm-navbar-padding-horizontal);
min-height: var(--ifm-navbar-height);
max-height: var(--ifm-navbar-height);
color: inherit !important;
text-decoration: none !important;
}

.sidebarLogo img {
margin-right: 0.5rem;
height: 2rem;
}
}

.sidebarLogo {
display: none;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/

import React from 'react';
import clsx from 'clsx';
import {
MobileSecondaryMenuFiller,
type MobileSecondaryMenuComponent,
ThemeClassNames,
} from '@docusaurus/theme-common';
import DocSidebarItems from '@theme/DocSidebarItems';
import type {Props} from '@theme/DocSidebar/Mobile';

// eslint-disable-next-line react/function-component-definition
const DocSidebarMobileSecondaryMenu: MobileSecondaryMenuComponent<Props> = ({
toggleSidebar,
sidebar,
path,
}) => (
<ul className={clsx(ThemeClassNames.docs.docSidebarMenu, 'menu__list')}>
<DocSidebarItems
items={sidebar}
activePath={path}
onItemClick={(item) => {
// Mobile sidebar should only be closed if the category has a link
if (item.type === 'category' && item.href) {
toggleSidebar();
}
if (item.type === 'link') {
toggleSidebar();
}
}}
level={1}
/>
</ul>
);

function DocSidebarMobile(props: Props) {
return (
<MobileSecondaryMenuFiller
component={DocSidebarMobileSecondaryMenu}
props={props}
/>
);
}

export default React.memo(DocSidebarMobile);
Loading

0 comments on commit e97dc0d

Please sign in to comment.