mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-24 14:36:59 +02:00
feat(v2): bootstrap doc sidebar (#2735)
* feat(v2): add minor adjustements and footer component * fix(v2): margin and spacing of footer * feat(v2): add navbar component * ádd collapse classname * feat(v2): add dependencies * feat(v2): remove unused code * feat(v2): remove unused links * feat(v2): add reactstrap components :| * feat(v2): add brand and other nav componnets * feat(v2): Add the layout tag * feat(v2): bootstrap start doc components * feat(v2: Add syntax highlight * Ádd Page components * feat(v2): Bootstrap MDX Componnets * fix(v2): Fix layout height * fix(v2): Fix spacings * feat:(v2): Add the layout in doc content * feat(v2): Start the pagination * feat(v2): Finish pagination * Fix margins in mobile * feat(v2): Add the doc sidebar * feat(v2): Add sidebar * feat(v2): Makes sidebar responsive * feat(v2): Add context * feat(v2): Add new hook and docs to the template sidebar * feat(v2): Add css modules * feat(v2): add sidebar class * feat(v2): add floating action button * fix fab styles * fix(v2): fix height * feat(v2): Remove context * feat(v2): Finish sidebar * feat(v2): Add resize window hook * feat(v2): Remove unsed fields * feat(v2): Add theme * feat(v2): improve sidebar definitions * feat(v2): fix conflicts
This commit is contained in:
parent
463efec20d
commit
7e97d40075
10 changed files with 253 additions and 23 deletions
|
@ -14,6 +14,12 @@ module.exports = {
|
||||||
src: 'img/logo.svg',
|
src: 'img/logo.svg',
|
||||||
},
|
},
|
||||||
links: [
|
links: [
|
||||||
|
{
|
||||||
|
to: 'docs/doc1',
|
||||||
|
activeBasePath: 'docs',
|
||||||
|
label: 'Docs',
|
||||||
|
position: 'left',
|
||||||
|
},
|
||||||
{to: 'blog', label: 'Blog', position: 'left'},
|
{to: 'blog', label: 'Blog', position: 'left'},
|
||||||
{
|
{
|
||||||
href: 'https://github.com/facebook/docusaurus',
|
href: 'https://github.com/facebook/docusaurus',
|
||||||
|
|
|
@ -9,6 +9,7 @@
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"bootstrap": "^4.4.1",
|
"bootstrap": "^4.4.1",
|
||||||
|
"classnames": "^2.2.6",
|
||||||
"prism-react-renderer": "^1.1.0",
|
"prism-react-renderer": "^1.1.0",
|
||||||
"reactstrap": "^8.4.1",
|
"reactstrap": "^8.4.1",
|
||||||
"@mdx-js/react": "^1.5.8"
|
"@mdx-js/react": "^1.5.8"
|
||||||
|
|
|
@ -46,7 +46,7 @@ function DocItem(props) {
|
||||||
)}
|
)}
|
||||||
{permalink && <meta property="og:url" content={siteUrl + permalink} />}
|
{permalink && <meta property="og:url" content={siteUrl + permalink} />}
|
||||||
</Head>
|
</Head>
|
||||||
<main className="col col-8 p-0">
|
<main className="col col-md-8 p-0">
|
||||||
<DocContent />
|
<DocContent />
|
||||||
<DocPaginator metadata={metadata} />
|
<DocPaginator metadata={metadata} />
|
||||||
</main>
|
</main>
|
||||||
|
|
|
@ -7,22 +7,34 @@
|
||||||
|
|
||||||
import React from 'react';
|
import React from 'react';
|
||||||
import renderRoutes from '@docusaurus/renderRoutes';
|
import renderRoutes from '@docusaurus/renderRoutes';
|
||||||
|
import DocSidebar from '@theme/DocSidebar';
|
||||||
import MDXComponents from '@theme/MDXComponents';
|
import MDXComponents from '@theme/MDXComponents';
|
||||||
import Layout from '@theme/Layout';
|
import Layout from '@theme/Layout';
|
||||||
import {MDXProvider} from '@mdx-js/react';
|
import {MDXProvider} from '@mdx-js/react';
|
||||||
|
import {matchPath} from '@docusaurus/router';
|
||||||
|
|
||||||
function DocPage(props) {
|
function DocPage(props) {
|
||||||
const {route: baseRoute} = props;
|
const {route: baseRoute, docsMetadata, location} = props;
|
||||||
|
// case-sensitive route such as it is defined in the sidebar
|
||||||
|
const currentRoute =
|
||||||
|
baseRoute.routes.find((route) => {
|
||||||
|
return matchPath(location.pathname, route);
|
||||||
|
}) || {};
|
||||||
|
const {permalinkToSidebar, docsSidebars} = docsMetadata;
|
||||||
|
const sidebar = permalinkToSidebar[currentRoute.path];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout title="Blog page" description="My blog page">
|
<Layout title="Doc page" description="My Doc page">
|
||||||
<div className="container mt-4">
|
<DocSidebar
|
||||||
<section className="row justify-content-center">
|
docsSidebars={docsSidebars}
|
||||||
<MDXProvider components={MDXComponents}>
|
path={currentRoute.path}
|
||||||
{renderRoutes(baseRoute.routes)}
|
sidebar={sidebar}
|
||||||
</MDXProvider>
|
/>
|
||||||
</section>
|
<section className="offset-1 mr-4 mt-4 col-xl-6 offset-xl-4 p-0 justify-content-center align-self-center overflow-hidden">
|
||||||
</div>
|
<MDXProvider components={MDXComponents}>
|
||||||
|
{renderRoutes(baseRoute.routes)}
|
||||||
|
</MDXProvider>
|
||||||
|
</section>
|
||||||
</Layout>
|
</Layout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,135 @@
|
||||||
|
/**
|
||||||
|
* 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, useCallback} from 'react';
|
||||||
|
import Link from '@docusaurus/Link';
|
||||||
|
import isInternalUrl from '@docusaurus/isInternalUrl';
|
||||||
|
import {NavItem, Nav, Button} from 'reactstrap';
|
||||||
|
import useLockBodyScroll from '@theme/hooks/useLockBodyScroll';
|
||||||
|
import classNames from 'classnames';
|
||||||
|
|
||||||
|
import styles from './styles.module.css';
|
||||||
|
|
||||||
|
const DocSidebarItem = ({item, onItemClick, ...props}) => {
|
||||||
|
const {items, href, label, type} = item;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case 'category':
|
||||||
|
return (
|
||||||
|
items.length > 0 && (
|
||||||
|
<div>
|
||||||
|
<h4 className="ml-2">{label}</h4>
|
||||||
|
{items.map((childItem) => (
|
||||||
|
<DocSidebarItem
|
||||||
|
key={childItem.label}
|
||||||
|
item={childItem}
|
||||||
|
onItemClick={onItemClick}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
case 'link':
|
||||||
|
default:
|
||||||
|
return (
|
||||||
|
<NavItem>
|
||||||
|
<Link
|
||||||
|
key={label}
|
||||||
|
className="sidebar-item m-4 text-white"
|
||||||
|
to={href}
|
||||||
|
{...(isInternalUrl(href)
|
||||||
|
? {
|
||||||
|
isNavLink: true,
|
||||||
|
activeClassName: 'active',
|
||||||
|
exact: true,
|
||||||
|
onClick: onItemClick,
|
||||||
|
}
|
||||||
|
: {
|
||||||
|
target: '_blank',
|
||||||
|
rel: 'noreferrer noopener',
|
||||||
|
})}
|
||||||
|
{...props}>
|
||||||
|
{label}
|
||||||
|
</Link>
|
||||||
|
</NavItem>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const DocSidebar = (props) => {
|
||||||
|
const {docsSidebars, sidebar: currentSidebar} = props;
|
||||||
|
|
||||||
|
const [sidebarShown, setSidebarShown] = useState(false);
|
||||||
|
const handleSidebarToggle = useCallback(() => {
|
||||||
|
setSidebarShown(!sidebarShown);
|
||||||
|
}, [sidebarShown, setSidebarShown]);
|
||||||
|
|
||||||
|
useLockBodyScroll(sidebarShown);
|
||||||
|
|
||||||
|
if (!currentSidebar) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
const sidebarData = docsSidebars[currentSidebar];
|
||||||
|
|
||||||
|
if (!sidebarData) {
|
||||||
|
throw new Error(
|
||||||
|
`Cannot find the sidebar "${currentSidebar}" in the sidebar config!`,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classNames('bg-info', styles.sidebar)}>
|
||||||
|
<div
|
||||||
|
className={classNames('text-white', {
|
||||||
|
[styles.isOpen]: sidebarShown,
|
||||||
|
})}>
|
||||||
|
<div className="d-flex w-100 justify-content-end mr-5">
|
||||||
|
<Button
|
||||||
|
color="secondary"
|
||||||
|
onClick={handleSidebarToggle}
|
||||||
|
className={classNames('mr-2', styles.sidebarFAB)}>
|
||||||
|
<svg
|
||||||
|
aria-label="Menu"
|
||||||
|
xmlns="http://www.w3.org/2000/svg"
|
||||||
|
height={24}
|
||||||
|
width={24}
|
||||||
|
viewBox="0 0 32 32"
|
||||||
|
role="img"
|
||||||
|
focusable="false">
|
||||||
|
<title>Menu</title>
|
||||||
|
<path
|
||||||
|
stroke="currentColor"
|
||||||
|
strokeLinecap="round"
|
||||||
|
strokeMiterlimit="10"
|
||||||
|
strokeWidth="2"
|
||||||
|
d="M4 7h22M4 15h22M4 23h22"
|
||||||
|
/>
|
||||||
|
</svg>
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
<div className={classNames(styles.sideMenu)}>
|
||||||
|
<Nav vertical className="list-unstyled p-3 mr-auto">
|
||||||
|
{sidebarData.map((item) => (
|
||||||
|
<DocSidebarItem
|
||||||
|
key={item.label}
|
||||||
|
item={item}
|
||||||
|
onItemClick={(e) => {
|
||||||
|
e.target.blur();
|
||||||
|
setSidebarShown(false);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
))}
|
||||||
|
</Nav>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DocSidebar;
|
|
@ -0,0 +1,58 @@
|
||||||
|
@media only screen and (min-width: 997px) {
|
||||||
|
.sidebar {
|
||||||
|
min-width: 300px;
|
||||||
|
max-width: 300px;
|
||||||
|
transition: all 0.5s;
|
||||||
|
min-height: 100vh;
|
||||||
|
margin-left: 0;
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.isOpen {
|
||||||
|
margin-left: 0;
|
||||||
|
transition: .5s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar ul p {
|
||||||
|
color: #fff;
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
.sidebarFAB {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@media only screen and (max-width: 500px) {
|
||||||
|
.isOpen .sideMenu{
|
||||||
|
min-width: 100%;
|
||||||
|
max-width: 100%;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
position: absolute;
|
||||||
|
z-index: 1;
|
||||||
|
top: 0;
|
||||||
|
display: inherit;
|
||||||
|
background-color: #17a2b8;
|
||||||
|
align-items: stretch;
|
||||||
|
overflow: hidden;
|
||||||
|
transition: all 0.5s, height 0s;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sideMenu {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebar {
|
||||||
|
width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sidebarFAB {
|
||||||
|
display: inline-flex;
|
||||||
|
position: fixed;
|
||||||
|
z-index: 2;
|
||||||
|
right: 1rem;
|
||||||
|
bottom: 2rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -39,7 +39,7 @@ function Footer() {
|
||||||
const {links} = footer || {};
|
const {links} = footer || {};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<footer className="container-fluid p-0 mt-3 align-self-end">
|
<footer className="container-fluid p-0 align-self-end">
|
||||||
<div className="row bg-light no-gutters justify-content-center">
|
<div className="row bg-light no-gutters justify-content-center">
|
||||||
{links && links.length > 0 && (
|
{links && links.length > 0 && (
|
||||||
<>
|
<>
|
||||||
|
|
|
@ -40,7 +40,6 @@ function Layout(props) {
|
||||||
if (!isInternalUrl(metaImage)) {
|
if (!isInternalUrl(metaImage)) {
|
||||||
metaImageUrl = metaImage;
|
metaImageUrl = metaImage;
|
||||||
}
|
}
|
||||||
|
|
||||||
const faviconUrl = useBaseUrl(favicon);
|
const faviconUrl = useBaseUrl(favicon);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -69,7 +68,9 @@ function Layout(props) {
|
||||||
<meta name="twitter:card" content="summary_large_image" />
|
<meta name="twitter:card" content="summary_large_image" />
|
||||||
</Head>
|
</Head>
|
||||||
<Navbar />
|
<Navbar />
|
||||||
<div className="vw-100 align-self-center">{children}</div>
|
<div className="container-fluid px-0 d-inline-flex flex-row">
|
||||||
|
{children}
|
||||||
|
</div>
|
||||||
{!noFooter && <Footer />}
|
{!noFooter && <Footer />}
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
|
|
@ -49,10 +49,11 @@ function Navbar() {
|
||||||
isClient,
|
isClient,
|
||||||
} = useDocusaurusContext();
|
} = useDocusaurusContext();
|
||||||
|
|
||||||
const [sidebarShown, setSidebarShown] = useState(false);
|
const [navbarShown, setNavbarShown] = useState(false);
|
||||||
const handleToggle = useCallback(() => {
|
const handleNavbarToggle = useCallback(() => {
|
||||||
setSidebarShown(!sidebarShown);
|
setNavbarShown(!navbarShown);
|
||||||
}, [sidebarShown, setSidebarShown]);
|
}, [navbarShown, setNavbarShown]);
|
||||||
|
|
||||||
const {logoLink, logoLinkProps, logoImageUrl, logoAlt} = useLogo();
|
const {logoLink, logoLinkProps, logoImageUrl, logoAlt} = useLogo();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -76,12 +77,8 @@ function Navbar() {
|
||||||
)}
|
)}
|
||||||
{title != null && <span className="ml-2">{title}</span>}
|
{title != null && <span className="ml-2">{title}</span>}
|
||||||
</Link>
|
</Link>
|
||||||
|
<NavbarToggler onClick={handleNavbarToggle} />
|
||||||
<NavbarToggler onClick={handleToggle} />
|
<Collapse isOpen={navbarShown} navbar className="justify-content-between">
|
||||||
<Collapse
|
|
||||||
isOpen={sidebarShown}
|
|
||||||
navbar
|
|
||||||
className="justify-content-between">
|
|
||||||
<Nav navbar>
|
<Nav navbar>
|
||||||
{links != null &&
|
{links != null &&
|
||||||
links.length !== 0 &&
|
links.length !== 0 &&
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
/**
|
||||||
|
* 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 {useEffect} from 'react';
|
||||||
|
|
||||||
|
function useLockBodyScroll(lock = true) {
|
||||||
|
useEffect(() => {
|
||||||
|
document.body.style.overflow = lock ? 'hidden' : 'visible';
|
||||||
|
window.scrollTo(0, 0);
|
||||||
|
return () => {
|
||||||
|
document.body.style.overflow = 'visible';
|
||||||
|
};
|
||||||
|
}, [lock]);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useLockBodyScroll;
|
Loading…
Add table
Add a link
Reference in a new issue