mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-22 21:47:01 +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 && <link rel="canonical" href={siteUrl + permalink} />}
|
||||
</Head>
|
||||
<div
|
||||
className={clsx('container padding-vert--lg', styles.docItemWrapper)}>
|
||||
|
||||
<div className="row">
|
||||
<div
|
||||
className={clsx('col', {
|
||||
|
@ -182,7 +181,6 @@ function DocItem(props: Props): JSX.Element {
|
|||
</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) {
|
||||
.docItemContainer {
|
||||
padding: 0 0.3rem;
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
* 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 useDocusaurusContext from '@docusaurus/useDocusaurusContext';
|
||||
|
@ -19,6 +19,7 @@ import type {DocumentRoute} from '@theme/DocItem';
|
|||
import type {Props} from '@theme/DocPage';
|
||||
import {matchPath} from '@docusaurus/router';
|
||||
|
||||
import clsx from 'clsx';
|
||||
import styles from './styles.module.css';
|
||||
import {docVersionSearchTag} from '../../utils/searchUtils';
|
||||
|
||||
|
@ -37,6 +38,17 @@ function DocPageContent({
|
|||
const {pluginId, permalinkToSidebar, docsSidebars, version} = versionMetadata;
|
||||
const sidebarName = permalinkToSidebar[currentDocRoute.path];
|
||||
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 (
|
||||
<Layout
|
||||
key={isClient}
|
||||
|
@ -46,7 +58,22 @@ function DocPageContent({
|
|||
}}>
|
||||
<div className={styles.docPage}>
|
||||
{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
|
||||
key={
|
||||
// Reset sidebar state on sidebar changes
|
||||
|
@ -58,11 +85,34 @@ function DocPageContent({
|
|||
sidebarCollapsible={
|
||||
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>
|
||||
)}
|
||||
<main className={styles.docMainContainer}>
|
||||
<div
|
||||
className={clsx(
|
||||
'container padding-vert--lg',
|
||||
styles.docItemWrapper,
|
||||
{
|
||||
[styles.docItemWrapperEnhanced]: hiddenSidebarContainer,
|
||||
},
|
||||
)}>
|
||||
<MDXProvider components={MDXComponents}>{children}</MDXProvider>
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</Layout>
|
||||
|
|
|
@ -5,22 +5,66 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
:root {
|
||||
--doc-sidebar-width: 300px;
|
||||
}
|
||||
|
||||
@media (min-width: 997px) {
|
||||
.docPage {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.docSidebarContainer {
|
||||
border-right: 1px solid var(--ifm-toc-border-color);
|
||||
box-sizing: border-box;
|
||||
width: 300px;
|
||||
position: relative;
|
||||
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: 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 {
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.docItemWrapperEnhanced {
|
||||
max-width: calc(var(--ifm-container-width) + var(--doc-sidebar-width));
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 996px) {
|
||||
.docPage {
|
||||
display: inherit;
|
||||
|
@ -30,3 +74,18 @@
|
|||
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,
|
||||
sidebar,
|
||||
sidebarCollapsible = true,
|
||||
onCollapse,
|
||||
isHidden,
|
||||
}: Props): JSX.Element | null {
|
||||
const [showResponsiveSidebar, setShowResponsiveSidebar] = useState(false);
|
||||
const {
|
||||
navbar: {title, hideOnScroll},
|
||||
hideableSidebar,
|
||||
} = useThemeConfig();
|
||||
const {isClient} = useDocusaurusContext();
|
||||
const {logoLink, logoLinkProps, logoImageUrl, logoAlt} = useLogo();
|
||||
|
@ -212,6 +215,7 @@ function DocSidebar({
|
|||
<div
|
||||
className={clsx(styles.sidebar, {
|
||||
[styles.sidebarWithHideableNavbar]: hideOnScroll,
|
||||
[styles.sidebarHidden]: isHidden,
|
||||
})}>
|
||||
{hideOnScroll && (
|
||||
<Link
|
||||
|
@ -283,6 +287,19 @@ function DocSidebar({
|
|||
))}
|
||||
</ul>
|
||||
</div>
|
||||
|
||||
{hideableSidebar && (
|
||||
<button
|
||||
type="button"
|
||||
title="Collapse sidebar"
|
||||
aria-label="Collapse sidebar"
|
||||
className={clsx(
|
||||
'button button--secondary button--outline',
|
||||
styles.collapseSidebarButton,
|
||||
)}
|
||||
onClick={onCollapse}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -5,21 +5,32 @@
|
|||
* LICENSE file in the root directory of this source tree.
|
||||
*/
|
||||
|
||||
:root {
|
||||
--collapse-button-bg-color-dark: #2e333a;
|
||||
}
|
||||
|
||||
@media (min-width: 997px) {
|
||||
.sidebar {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100vh;
|
||||
overflow-y: auto;
|
||||
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;
|
||||
}
|
||||
|
||||
.sidebarLogo {
|
||||
display: flex !important;
|
||||
align-items: center;
|
||||
|
@ -69,9 +80,32 @@
|
|||
.menuWithAnnouncementBar {
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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 sidebar: readonly PropSidebarItem[];
|
||||
readonly sidebarCollapsible?: boolean;
|
||||
readonly onCollapse: () => void;
|
||||
readonly isHidden: boolean;
|
||||
};
|
||||
|
||||
const DocSidebar: (props: Props) => JSX.Element;
|
||||
|
|
|
@ -23,6 +23,7 @@ export type ThemeConfig = {
|
|||
announcementBar: any;
|
||||
prism: any;
|
||||
footer: any;
|
||||
hideableSidebar: any;
|
||||
};
|
||||
|
||||
export default function useThemeConfig(): ThemeConfig {
|
||||
|
|
|
@ -40,6 +40,7 @@ const DEFAULT_CONFIG = {
|
|||
hideOnScroll: false,
|
||||
items: [],
|
||||
},
|
||||
hideableSidebar: false,
|
||||
};
|
||||
exports.DEFAULT_CONFIG = DEFAULT_CONFIG;
|
||||
|
||||
|
@ -283,6 +284,7 @@ const ThemeConfigSchema = Joi.object({
|
|||
})
|
||||
.default(DEFAULT_CONFIG.prism)
|
||||
.unknown(),
|
||||
hideableSidebar: Joi.bool().default(DEFAULT_CONFIG.hideableSidebar),
|
||||
});
|
||||
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
|
||||
|
||||
A sidebar object is defined like this:
|
||||
|
|
|
@ -229,6 +229,7 @@ module.exports = {
|
|||
],
|
||||
],
|
||||
themeConfig: {
|
||||
hideableSidebar: true,
|
||||
colorMode: {
|
||||
defaultMode: 'light',
|
||||
disableSwitch: false,
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue