mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-22 05:27:00 +02:00
feat(v2): add ability to hide doc sidebar (#3615)
* feat(v2): add ability to hide doc sidebar * Use relative imports
This commit is contained in:
parent
0ec5d533d6
commit
14cdd72ae4
12 changed files with 293 additions and 122 deletions
|
@ -78,8 +78,7 @@ function DocItem(props: Props): JSX.Element {
|
||||||
{permalink && <meta property="og:url" content={siteUrl + permalink} />}
|
{permalink && <meta property="og:url" content={siteUrl + permalink} />}
|
||||||
{permalink && <link rel="canonical" href={siteUrl + permalink} />}
|
{permalink && <link rel="canonical" href={siteUrl + permalink} />}
|
||||||
</Head>
|
</Head>
|
||||||
<div
|
|
||||||
className={clsx('container padding-vert--lg', styles.docItemWrapper)}>
|
|
||||||
<div className="row">
|
<div className="row">
|
||||||
<div
|
<div
|
||||||
className={clsx('col', {
|
className={clsx('col', {
|
||||||
|
@ -182,7 +181,6 @@ function DocItem(props: Props): JSX.Element {
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -21,14 +21,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media (min-width: 997px) and (max-width: 1320px) {
|
|
||||||
.docItemWrapper {
|
|
||||||
max-width: calc(
|
|
||||||
var(--ifm-container-width) - 300px - var(--ifm-spacing-horizontal) * 2
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@media only screen and (max-width: 996px) {
|
@media only screen and (max-width: 996px) {
|
||||||
.docItemContainer {
|
.docItemContainer {
|
||||||
padding: 0 0.3rem;
|
padding: 0 0.3rem;
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import React, {ReactNode} from 'react';
|
import React, {ReactNode, useState, useCallback} from 'react';
|
||||||
import {MDXProvider} from '@mdx-js/react';
|
import {MDXProvider} from '@mdx-js/react';
|
||||||
|
|
||||||
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
||||||
|
@ -19,6 +19,7 @@ import type {DocumentRoute} from '@theme/DocItem';
|
||||||
import type {Props} from '@theme/DocPage';
|
import type {Props} from '@theme/DocPage';
|
||||||
import {matchPath} from '@docusaurus/router';
|
import {matchPath} from '@docusaurus/router';
|
||||||
|
|
||||||
|
import clsx from 'clsx';
|
||||||
import styles from './styles.module.css';
|
import styles from './styles.module.css';
|
||||||
import {docVersionSearchTag} from '../../utils/searchUtils';
|
import {docVersionSearchTag} from '../../utils/searchUtils';
|
||||||
|
|
||||||
|
@ -37,6 +38,17 @@ function DocPageContent({
|
||||||
const {pluginId, permalinkToSidebar, docsSidebars, version} = versionMetadata;
|
const {pluginId, permalinkToSidebar, docsSidebars, version} = versionMetadata;
|
||||||
const sidebarName = permalinkToSidebar[currentDocRoute.path];
|
const sidebarName = permalinkToSidebar[currentDocRoute.path];
|
||||||
const sidebar = docsSidebars[sidebarName];
|
const sidebar = docsSidebars[sidebarName];
|
||||||
|
|
||||||
|
const [hiddenSidebarContainer, setHiddenSidebarContainer] = useState(false);
|
||||||
|
const [hiddenSidebar, setHiddenSidebar] = useState(false);
|
||||||
|
const toggleSidebar = useCallback(() => {
|
||||||
|
if (hiddenSidebar) {
|
||||||
|
setHiddenSidebar(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
setHiddenSidebarContainer(!hiddenSidebarContainer);
|
||||||
|
}, [hiddenSidebar]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout
|
<Layout
|
||||||
key={isClient}
|
key={isClient}
|
||||||
|
@ -46,7 +58,22 @@ function DocPageContent({
|
||||||
}}>
|
}}>
|
||||||
<div className={styles.docPage}>
|
<div className={styles.docPage}>
|
||||||
{sidebar && (
|
{sidebar && (
|
||||||
<div className={styles.docSidebarContainer} role="complementary">
|
<div
|
||||||
|
className={clsx(styles.docSidebarContainer, {
|
||||||
|
[styles.docSidebarContainerHidden]: hiddenSidebarContainer,
|
||||||
|
})}
|
||||||
|
onTransitionEnd={(e) => {
|
||||||
|
if (
|
||||||
|
!e.currentTarget.classList.contains(styles.docSidebarContainer)
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (hiddenSidebarContainer) {
|
||||||
|
setHiddenSidebar(true);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
role="complementary">
|
||||||
<DocSidebar
|
<DocSidebar
|
||||||
key={
|
key={
|
||||||
// Reset sidebar state on sidebar changes
|
// Reset sidebar state on sidebar changes
|
||||||
|
@ -58,11 +85,34 @@ function DocPageContent({
|
||||||
sidebarCollapsible={
|
sidebarCollapsible={
|
||||||
siteConfig.themeConfig?.sidebarCollapsible ?? true
|
siteConfig.themeConfig?.sidebarCollapsible ?? true
|
||||||
}
|
}
|
||||||
|
onCollapse={toggleSidebar}
|
||||||
|
isHidden={hiddenSidebar}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
{hiddenSidebar && (
|
||||||
|
<div
|
||||||
|
className={styles.collapsedDocSidebar}
|
||||||
|
title="Expand sidebar"
|
||||||
|
aria-label="Expand sidebar"
|
||||||
|
tabIndex={0}
|
||||||
|
role="button"
|
||||||
|
onKeyDown={toggleSidebar}
|
||||||
|
onClick={toggleSidebar}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
<main className={styles.docMainContainer}>
|
<main className={styles.docMainContainer}>
|
||||||
|
<div
|
||||||
|
className={clsx(
|
||||||
|
'container padding-vert--lg',
|
||||||
|
styles.docItemWrapper,
|
||||||
|
{
|
||||||
|
[styles.docItemWrapperEnhanced]: hiddenSidebarContainer,
|
||||||
|
},
|
||||||
|
)}>
|
||||||
<MDXProvider components={MDXComponents}>{children}</MDXProvider>
|
<MDXProvider components={MDXComponents}>{children}</MDXProvider>
|
||||||
|
</div>
|
||||||
</main>
|
</main>
|
||||||
</div>
|
</div>
|
||||||
</Layout>
|
</Layout>
|
||||||
|
|
|
@ -5,22 +5,66 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--doc-sidebar-width: 300px;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (min-width: 997px) {
|
||||||
.docPage {
|
.docPage {
|
||||||
display: flex;
|
display: flex;
|
||||||
}
|
}
|
||||||
|
|
||||||
.docSidebarContainer {
|
.docSidebarContainer {
|
||||||
border-right: 1px solid var(--ifm-toc-border-color);
|
width: var(--doc-sidebar-width);
|
||||||
box-sizing: border-box;
|
|
||||||
width: 300px;
|
|
||||||
position: relative;
|
|
||||||
margin-top: calc(-1 * var(--ifm-navbar-height));
|
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: 30px;
|
||||||
|
cursor: e-resize;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsedDocSidebar {
|
||||||
|
position: sticky;
|
||||||
|
top: 0;
|
||||||
|
height: 100%;
|
||||||
|
max-height: 100vh;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
transition: background-color var(--ifm-transition-fast) ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsedDocSidebar:hover,
|
||||||
|
.collapsedDocSidebar:focus {
|
||||||
|
background-color: var(--ifm-color-emphasis-200);
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-theme='dark'] .collapsedDocSidebar:hover,
|
||||||
|
html[data-theme='dark'] .collapsedDocSidebar:focus {
|
||||||
|
background-color: var(--collapse-button-bg-color-dark);
|
||||||
|
}
|
||||||
|
|
||||||
|
.collapsedDocSidebar:before {
|
||||||
|
content: '';
|
||||||
|
background-image: url('~!file-loader!../icons/arrow.svg');
|
||||||
|
width: 20px;
|
||||||
|
height: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
.docMainContainer {
|
.docMainContainer {
|
||||||
flex-grow: 1;
|
flex-grow: 1;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.docItemWrapperEnhanced {
|
||||||
|
max-width: calc(var(--ifm-container-width) + var(--doc-sidebar-width));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@media (max-width: 996px) {
|
@media (max-width: 996px) {
|
||||||
.docPage {
|
.docPage {
|
||||||
display: inherit;
|
display: inherit;
|
||||||
|
@ -30,3 +74,18 @@
|
||||||
margin-top: 0;
|
margin-top: 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@media (min-width: 997px) and (max-width: 1320px) {
|
||||||
|
.docItemWrapper {
|
||||||
|
max-width: calc(
|
||||||
|
var(--ifm-container-width) - var(--doc-sidebar-width) -
|
||||||
|
var(--ifm-spacing-horizontal) * 2
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
.docItemWrapperEnhanced {
|
||||||
|
max-width: calc(
|
||||||
|
var(--ifm-container-width) - var(--ifm-spacing-horizontal) * 2
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -189,10 +189,13 @@ function DocSidebar({
|
||||||
path,
|
path,
|
||||||
sidebar,
|
sidebar,
|
||||||
sidebarCollapsible = true,
|
sidebarCollapsible = true,
|
||||||
|
onCollapse,
|
||||||
|
isHidden,
|
||||||
}: Props): JSX.Element | null {
|
}: Props): JSX.Element | null {
|
||||||
const [showResponsiveSidebar, setShowResponsiveSidebar] = useState(false);
|
const [showResponsiveSidebar, setShowResponsiveSidebar] = useState(false);
|
||||||
const {
|
const {
|
||||||
navbar: {title, hideOnScroll},
|
navbar: {title, hideOnScroll},
|
||||||
|
hideableSidebar,
|
||||||
} = useThemeConfig();
|
} = useThemeConfig();
|
||||||
const {isClient} = useDocusaurusContext();
|
const {isClient} = useDocusaurusContext();
|
||||||
const {logoLink, logoLinkProps, logoImageUrl, logoAlt} = useLogo();
|
const {logoLink, logoLinkProps, logoImageUrl, logoAlt} = useLogo();
|
||||||
|
@ -212,6 +215,7 @@ function DocSidebar({
|
||||||
<div
|
<div
|
||||||
className={clsx(styles.sidebar, {
|
className={clsx(styles.sidebar, {
|
||||||
[styles.sidebarWithHideableNavbar]: hideOnScroll,
|
[styles.sidebarWithHideableNavbar]: hideOnScroll,
|
||||||
|
[styles.sidebarHidden]: isHidden,
|
||||||
})}>
|
})}>
|
||||||
{hideOnScroll && (
|
{hideOnScroll && (
|
||||||
<Link
|
<Link
|
||||||
|
@ -283,6 +287,19 @@ function DocSidebar({
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{hideableSidebar && (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
title="Collapse sidebar"
|
||||||
|
aria-label="Collapse sidebar"
|
||||||
|
className={clsx(
|
||||||
|
'button button--secondary button--outline',
|
||||||
|
styles.collapseSidebarButton,
|
||||||
|
)}
|
||||||
|
onClick={onCollapse}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,21 +5,32 @@
|
||||||
* LICENSE file in the root directory of this source tree.
|
* LICENSE file in the root directory of this source tree.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
:root {
|
||||||
|
--collapse-button-bg-color-dark: #2e333a;
|
||||||
|
}
|
||||||
|
|
||||||
@media (min-width: 997px) {
|
@media (min-width: 997px) {
|
||||||
.sidebar {
|
.sidebar {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
height: 100vh;
|
height: 100vh;
|
||||||
overflow-y: auto;
|
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
top: 0;
|
||||||
padding-top: var(--ifm-navbar-height);
|
padding-top: var(--ifm-navbar-height);
|
||||||
|
width: var(--doc-sidebar-width);
|
||||||
|
transition: opacity 50ms ease;
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebarWithHideableNavbar {
|
.sidebarWithHideableNavbar {
|
||||||
padding-top: 0;
|
padding-top: 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.sidebarHidden {
|
||||||
|
opacity: 0;
|
||||||
|
height: 0;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
.sidebarLogo {
|
.sidebarLogo {
|
||||||
display: flex !important;
|
display: flex !important;
|
||||||
align-items: center;
|
align-items: center;
|
||||||
|
@ -69,9 +80,32 @@
|
||||||
.menuWithAnnouncementBar {
|
.menuWithAnnouncementBar {
|
||||||
margin-bottom: var(--docusaurus-announcement-bar-height);
|
margin-bottom: var(--docusaurus-announcement-bar-height);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.collapseSidebarButton {
|
||||||
|
display: block !important;
|
||||||
|
background: url('~!file-loader!../icons/arrow.svg') no-repeat center center;
|
||||||
|
background-color: var(--ifm-button-background-color);
|
||||||
|
height: 40px;
|
||||||
|
position: sticky;
|
||||||
|
bottom: 0;
|
||||||
|
border-radius: 0;
|
||||||
|
transform: rotate(180deg);
|
||||||
}
|
}
|
||||||
|
|
||||||
.sidebarLogo {
|
html[data-theme='dark'] .collapseSidebarButton {
|
||||||
|
background-color: var(--collapse-button-bg-color-dark);
|
||||||
|
border: none;
|
||||||
|
border-left: 1px solid var(--ifm-toc-border-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
html[data-theme='dark'] .collapseSidebarButton:hover,
|
||||||
|
html[data-theme='dark'] .collapseSidebarButton:focus {
|
||||||
|
background-color: var(--ifm-color-emphasis-200);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebarLogo,
|
||||||
|
.collapseSidebarButton {
|
||||||
display: none;
|
display: none;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
<svg xmlns='http://www.w3.org/2000/svg' width='20' height='20'><g fill='#7a7a7a'><path d='M9.992 10.023c0 .2-.062.399-.172.547l-4.996 7.492a.982.982 0 01-.828.454H1c-.55 0-1-.453-1-1 0-.2.059-.403.168-.551l4.629-6.942L.168 3.078A.939.939 0 010 2.528c0-.548.45-.997 1-.997h2.996c.352 0 .649.18.828.45L9.82 9.472c.11.148.172.347.172.55zm0 0'/><path d='M19.98 10.023c0 .2-.058.399-.168.547l-4.996 7.492a.987.987 0 01-.828.454h-3c-.547 0-.996-.453-.996-1 0-.2.059-.403.168-.551l4.625-6.942-4.625-6.945a.939.939 0 01-.168-.55 1 1 0 01.996-.997h3c.348 0 .649.18.828.45l4.996 7.492c.11.148.168.347.168.55zm0 0'/></g></svg>
|
After Width: | Height: | Size: 615 B |
|
@ -79,6 +79,8 @@ declare module '@theme/DocSidebar' {
|
||||||
readonly path: string;
|
readonly path: string;
|
||||||
readonly sidebar: readonly PropSidebarItem[];
|
readonly sidebar: readonly PropSidebarItem[];
|
||||||
readonly sidebarCollapsible?: boolean;
|
readonly sidebarCollapsible?: boolean;
|
||||||
|
readonly onCollapse: () => void;
|
||||||
|
readonly isHidden: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
const DocSidebar: (props: Props) => JSX.Element;
|
const DocSidebar: (props: Props) => JSX.Element;
|
||||||
|
|
|
@ -23,6 +23,7 @@ export type ThemeConfig = {
|
||||||
announcementBar: any;
|
announcementBar: any;
|
||||||
prism: any;
|
prism: any;
|
||||||
footer: any;
|
footer: any;
|
||||||
|
hideableSidebar: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function useThemeConfig(): ThemeConfig {
|
export default function useThemeConfig(): ThemeConfig {
|
||||||
|
|
|
@ -40,6 +40,7 @@ const DEFAULT_CONFIG = {
|
||||||
hideOnScroll: false,
|
hideOnScroll: false,
|
||||||
items: [],
|
items: [],
|
||||||
},
|
},
|
||||||
|
hideableSidebar: false,
|
||||||
};
|
};
|
||||||
exports.DEFAULT_CONFIG = DEFAULT_CONFIG;
|
exports.DEFAULT_CONFIG = DEFAULT_CONFIG;
|
||||||
|
|
||||||
|
@ -283,6 +284,7 @@ const ThemeConfigSchema = Joi.object({
|
||||||
})
|
})
|
||||||
.default(DEFAULT_CONFIG.prism)
|
.default(DEFAULT_CONFIG.prism)
|
||||||
.unknown(),
|
.unknown(),
|
||||||
|
hideableSidebar: Joi.bool().default(DEFAULT_CONFIG.hideableSidebar),
|
||||||
});
|
});
|
||||||
exports.ThemeConfigSchema = ThemeConfigSchema;
|
exports.ThemeConfigSchema = ThemeConfigSchema;
|
||||||
|
|
||||||
|
|
|
@ -82,6 +82,20 @@ module.exports = {
|
||||||
};
|
};
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Hideable sidebar
|
||||||
|
|
||||||
|
Using the enabled `themeConfig.hideableSidebar` option, you can make the entire sidebar hided, allowing you to better focus your users on the content. This is especially useful when content consumption on medium screens (e.g. on tablets).
|
||||||
|
|
||||||
|
```js {4} title="docusaurus.config.js"
|
||||||
|
module.exports = {
|
||||||
|
// ...
|
||||||
|
themeConfig: {
|
||||||
|
hideableSidebar: true,
|
||||||
|
// ...
|
||||||
|
},
|
||||||
|
};
|
||||||
|
```
|
||||||
|
|
||||||
### Sidebar object
|
### Sidebar object
|
||||||
|
|
||||||
A sidebar object is defined like this:
|
A sidebar object is defined like this:
|
||||||
|
|
|
@ -229,6 +229,7 @@ module.exports = {
|
||||||
],
|
],
|
||||||
],
|
],
|
||||||
themeConfig: {
|
themeConfig: {
|
||||||
|
hideableSidebar: true,
|
||||||
colorMode: {
|
colorMode: {
|
||||||
defaultMode: 'light',
|
defaultMode: 'light',
|
||||||
disableSwitch: false,
|
disableSwitch: false,
|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue