mirror of
https://github.com/facebook/docusaurus.git
synced 2025-05-24 06:27:02 +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',
|
||||
},
|
||||
links: [
|
||||
{
|
||||
to: 'docs/doc1',
|
||||
activeBasePath: 'docs',
|
||||
label: 'Docs',
|
||||
position: 'left',
|
||||
},
|
||||
{to: 'blog', label: 'Blog', position: 'left'},
|
||||
{
|
||||
href: 'https://github.com/facebook/docusaurus',
|
||||
|
|
|
@ -9,6 +9,7 @@
|
|||
},
|
||||
"dependencies": {
|
||||
"bootstrap": "^4.4.1",
|
||||
"classnames": "^2.2.6",
|
||||
"prism-react-renderer": "^1.1.0",
|
||||
"reactstrap": "^8.4.1",
|
||||
"@mdx-js/react": "^1.5.8"
|
||||
|
|
|
@ -46,7 +46,7 @@ function DocItem(props) {
|
|||
)}
|
||||
{permalink && <meta property="og:url" content={siteUrl + permalink} />}
|
||||
</Head>
|
||||
<main className="col col-8 p-0">
|
||||
<main className="col col-md-8 p-0">
|
||||
<DocContent />
|
||||
<DocPaginator metadata={metadata} />
|
||||
</main>
|
||||
|
|
|
@ -7,22 +7,34 @@
|
|||
|
||||
import React from 'react';
|
||||
import renderRoutes from '@docusaurus/renderRoutes';
|
||||
import DocSidebar from '@theme/DocSidebar';
|
||||
import MDXComponents from '@theme/MDXComponents';
|
||||
import Layout from '@theme/Layout';
|
||||
import {MDXProvider} from '@mdx-js/react';
|
||||
import {matchPath} from '@docusaurus/router';
|
||||
|
||||
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 (
|
||||
<Layout title="Blog page" description="My blog page">
|
||||
<div className="container mt-4">
|
||||
<section className="row justify-content-center">
|
||||
<Layout title="Doc page" description="My Doc page">
|
||||
<DocSidebar
|
||||
docsSidebars={docsSidebars}
|
||||
path={currentRoute.path}
|
||||
sidebar={sidebar}
|
||||
/>
|
||||
<section className="offset-1 mr-4 mt-4 col-xl-6 offset-xl-4 p-0 justify-content-center align-self-center overflow-hidden">
|
||||
<MDXProvider components={MDXComponents}>
|
||||
{renderRoutes(baseRoute.routes)}
|
||||
</MDXProvider>
|
||||
</section>
|
||||
</div>
|
||||
</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 || {};
|
||||
|
||||
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">
|
||||
{links && links.length > 0 && (
|
||||
<>
|
||||
|
|
|
@ -40,7 +40,6 @@ function Layout(props) {
|
|||
if (!isInternalUrl(metaImage)) {
|
||||
metaImageUrl = metaImage;
|
||||
}
|
||||
|
||||
const faviconUrl = useBaseUrl(favicon);
|
||||
|
||||
return (
|
||||
|
@ -69,7 +68,9 @@ function Layout(props) {
|
|||
<meta name="twitter:card" content="summary_large_image" />
|
||||
</Head>
|
||||
<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 />}
|
||||
</div>
|
||||
);
|
||||
|
|
|
@ -49,10 +49,11 @@ function Navbar() {
|
|||
isClient,
|
||||
} = useDocusaurusContext();
|
||||
|
||||
const [sidebarShown, setSidebarShown] = useState(false);
|
||||
const handleToggle = useCallback(() => {
|
||||
setSidebarShown(!sidebarShown);
|
||||
}, [sidebarShown, setSidebarShown]);
|
||||
const [navbarShown, setNavbarShown] = useState(false);
|
||||
const handleNavbarToggle = useCallback(() => {
|
||||
setNavbarShown(!navbarShown);
|
||||
}, [navbarShown, setNavbarShown]);
|
||||
|
||||
const {logoLink, logoLinkProps, logoImageUrl, logoAlt} = useLogo();
|
||||
|
||||
return (
|
||||
|
@ -76,12 +77,8 @@ function Navbar() {
|
|||
)}
|
||||
{title != null && <span className="ml-2">{title}</span>}
|
||||
</Link>
|
||||
|
||||
<NavbarToggler onClick={handleToggle} />
|
||||
<Collapse
|
||||
isOpen={sidebarShown}
|
||||
navbar
|
||||
className="justify-content-between">
|
||||
<NavbarToggler onClick={handleNavbarToggle} />
|
||||
<Collapse isOpen={navbarShown} navbar className="justify-content-between">
|
||||
<Nav navbar>
|
||||
{links != null &&
|
||||
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