Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 33 additions & 0 deletions src/theme/DocRoot/Layout/Sidebar/ExpandButton/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React, {type ReactNode} from 'react';
import {translate} from '@docusaurus/Translate';
import IconArrow from '@theme/Icon/Arrow';
import type {Props} from '@theme/DocRoot/Layout/Sidebar/ExpandButton';

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

export default function DocRootLayoutSidebarExpandButton({
toggleSidebar,
}: Props): ReactNode {
return (
<div
className={styles.expandButton}
title={translate({
id: 'theme.docs.sidebar.expandButtonTitle',
message: 'Expand sidebar',
description:
'The ARIA label and title attribute for expand button of doc sidebar',
})}
aria-label={translate({
id: 'theme.docs.sidebar.expandButtonAriaLabel',
message: 'Expand sidebar',
description:
'The ARIA label and title attribute for expand button of doc sidebar',
})}
tabIndex={0}
role="button"
onKeyDown={toggleSidebar}
onClick={toggleSidebar}>
<IconArrow className={styles.expandButtonIcon} />
</div>
);
}
27 changes: 27 additions & 0 deletions src/theme/DocRoot/Layout/Sidebar/ExpandButton/styles.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
@media (min-width: 997px) {
.expandButton {
position: absolute;
top: 0;
right: 0;
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
transition: background-color var(--ifm-transition-fast) ease;
background-color: var(--docusaurus-collapse-button-bg);
}

.expandButton:hover,
.expandButton:focus {
background-color: var(--docusaurus-collapse-button-bg-hover);
}

.expandButtonIcon {
transform: rotate(0);
}

[dir='rtl'] .expandButtonIcon {
transform: rotate(180deg);
}
}
88 changes: 88 additions & 0 deletions src/theme/DocRoot/Layout/Sidebar/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
import React, { type ReactNode, useState, useCallback } from "react";
import clsx from "clsx";
import {
prefersReducedMotion,
ThemeClassNames,
} from "@docusaurus/theme-common";
import { useDocsSidebar } from "@docusaurus/plugin-content-docs/client";
import { useLocation } from "@docusaurus/router";
import { translate } from "@docusaurus/Translate";
import DocSidebar from "@theme/DocSidebar";
import ExpandButton from "@theme/DocRoot/Layout/Sidebar/ExpandButton";
import type { Props } from "@theme/DocRoot/Layout/Sidebar";

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

// Reset sidebar state when sidebar changes
// Use React key to unmount/remount the children
// See https://github.com/facebook/docusaurus/issues/3414
function ResetOnSidebarChange({ children }: { children: ReactNode }) {
const sidebar = useDocsSidebar();
return (
<React.Fragment key={sidebar?.name ?? "noSidebar"}>
{children}
</React.Fragment>
);
}

export default function DocRootLayoutSidebar({
sidebar,
hiddenSidebarContainer,
setHiddenSidebarContainer,
}: Props): ReactNode {
const { pathname } = useLocation();

const [hiddenSidebar, setHiddenSidebar] = useState(false);
const toggleSidebar = useCallback(() => {
if (hiddenSidebar) {
setHiddenSidebar(false);
}
// onTransitionEnd won't fire when sidebar animation is disabled
// fixes https://github.com/facebook/docusaurus/issues/8918
if (!hiddenSidebar && prefersReducedMotion()) {
setHiddenSidebar(true);
}
setHiddenSidebarContainer((value) => !value);
}, [setHiddenSidebarContainer, hiddenSidebar]);

return (
<nav
aria-label={translate({
id: "theme.docs.sidebar.navAriaLabel",
message: "Docs sidebar",
description: "The ARIA label for the sidebar navigation",
})}
className={clsx(
ThemeClassNames.docs.docSidebarContainer,
styles.docSidebarContainer,
hiddenSidebarContainer && styles.docSidebarContainerHidden,
)}
onTransitionEnd={(e) => {
if (!e.currentTarget.classList.contains(styles.docSidebarContainer!)) {
return;
}

if (hiddenSidebarContainer) {
setHiddenSidebar(true);
}
}}
>
<ResetOnSidebarChange>
<div
className={clsx(
styles.sidebarViewport,
hiddenSidebar && styles.sidebarViewportHidden,
)}
>
<DocSidebar
sidebar={sidebar}
path={pathname}
onCollapse={toggleSidebar}
isHidden={hiddenSidebar}
/>
{hiddenSidebar && <ExpandButton toggleSidebar={toggleSidebar} />}
</div>
</ResetOnSidebarChange>
</nav>
);
}
32 changes: 32 additions & 0 deletions src/theme/DocRoot/Layout/Sidebar/styles.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
:root {
--doc-sidebar-width: 300px;
--doc-sidebar-hidden-width: 30px;
}

.docSidebarContainer {
display: none;
}

@media (min-width: 997px) {
.docSidebarContainer {
display: block;
width: var(--doc-sidebar-width);
margin-top: calc(-1 * var(--ifm-navbar-height));
border-right: 1px solid var(--ifm-toc-border-color);
will-change: width;
transition: width var(--ifm-transition-fast) ease;
clip-path: inset(0);
}

.docSidebarContainerHidden {
width: var(--doc-sidebar-hidden-width);
cursor: pointer;
}

.sidebarViewport {
top: 0;
position: sticky;
height: 100%;
max-height: 100vh;
}
}
48 changes: 48 additions & 0 deletions src/theme/DocSidebar/Desktop/Content/index.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import React, {type ReactNode, useState} from 'react';
import clsx from 'clsx';
import {ThemeClassNames} from '@docusaurus/theme-common';
import {
useAnnouncementBar,
useScrollPosition,
} from '@docusaurus/theme-common/internal';
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): ReactNode {
const showAnnouncementBar = useShowAnnouncementBar();

return (
<div
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>
</div>
);
}
16 changes: 16 additions & 0 deletions src/theme/DocSidebar/Desktop/Content/styles.module.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
@media (min-width: 997px) {
.menu {
flex-grow: 1;
padding: 0.5rem;
}
@supports (scrollbar-gutter: stable) {
.menu {
padding: 0.5rem 0 0.5rem 0.5rem;
scrollbar-gutter: stable;
}
}

.menuWithAnnouncementBar {
margin-bottom: var(--docusaurus-announcement-bar-height);
}
}
2 changes: 1 addition & 1 deletion src/theme/DocSidebar/Desktop/styles.module.css
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@
background-color: var(--color-background-depth-2);
border-right: 1px solid var(--color-tonal-neutral-0);

:global(nav.menu.thin-scrollbar) {
:global(div.menu.thin-scrollbar) {
padding: 0 0 var(--m-2) 0;
}

Expand Down
Loading