feat(v2): responsive sidebar (#1562)

* feat(v2): responsive sidebar

* feat(v2): separate css modules from Infima

* fix(v2): sidebar a11y
This commit is contained in:
Yangshun Tay 2019-06-05 10:58:04 -07:00 committed by GitHub
parent ec69460b4f
commit 5962cda8b7
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
9 changed files with 123 additions and 93 deletions

View file

@ -17,6 +17,7 @@
},
"peerDependencies": {
"@docusaurus/core": "^2.0.0",
"classnames": "^2.2.6",
"react": "^16.8.4",
"react-dom": "^16.8.4",
"react-router-config": "^5.0.0"

View file

@ -15,7 +15,7 @@ import styles from './styles.module.css';
function Headings({headings, isChild}) {
if (!headings.length) return null;
return (
<ul className={isChild ? 'contents' : 'contents contents__left-border'}>
<ul className={isChild ? '' : 'contents contents__left-border'}>
{headings.map(heading => (
<li key={heading.id}>
<a href={`#${heading.id}`} className="contents__link">
@ -32,32 +32,34 @@ function DocItem(props) {
const {metadata, content: DocContent, docsMetadata} = props;
return (
<div className={styles.docBody}>
<div>
<Head>
{metadata && metadata.title && <title>{metadata.title}</title>}
</Head>
<div className={`${styles.container} container margin-bottom--lg`}>
<div className="padding-vert--lg">
<div className="row">
<div className="col col--8">
<header>
<h1 className="margin-bottom--lg">{metadata.title}</h1>
</header>
<article>
<div className="markdown">
<DocContent />
<div className="col">
<div className={styles.docItemContainer}>
<header>
<h1 className="margin-bottom--lg">{metadata.title}</h1>
</header>
<article>
<div className="markdown">
<DocContent />
</div>
</article>
<div className="margin-top--xl margin-bottom--lg">
<DocPaginator docsMetadata={docsMetadata} metadata={metadata} />
</div>
</article>
<div className="margin-vert--lg" />
<DocPaginator docsMetadata={docsMetadata} metadata={metadata} />
</div>
</div>
<div className="col col--3 col--offset-1">
{DocContent.rightToc && (
{DocContent.rightToc && (
<div className="col col--3">
<div className={styles.tableOfContents}>
<Headings headings={DocContent.rightToc} />
</div>
)}
</div>
</div>
)}
</div>
</div>
</div>

View file

@ -5,25 +5,22 @@
* LICENSE file in the root directory of this source tree.
*/
.docBody {
min-height: calc(100vh - 50px);
}
.container {
padding-top: 2rem !important;
}
@media (min-width: 996px) {
.container {
@media (min-width: 997px) {
.docItemContainer {
padding-left: 2rem;
}
}
@media only screen and (min-width: 996px) {
.tableOfContents {
display: inherit;
max-height: calc(100vh - (var(--ifm-navbar-height) + 2rem));
overflow-y: auto;
position: sticky;
top: calc(var(--ifm-navbar-height) + 2rem);
}
@media only screen and (max-width: 996px) {
.tableOfContents {
max-height: calc(100vh - (var(--ifm-navbar-height) + 2rem));
overflow-y: auto;
position: sticky;
top: calc(var(--ifm-navbar-height) + 2rem);
display: none;
}
}

View file

@ -12,8 +12,6 @@ import Layout from '@theme/Layout'; // eslint-disable-line
import DocSidebar from '@theme/DocSidebar';
import './styles.css';
function DocPage(props) {
const {route, docsMetadata, location} = props;
const {permalinkToId} = docsMetadata;
@ -26,13 +24,13 @@ function DocPage(props) {
return (
<Layout noFooter description={description}>
<div className="container container--fluid">
<div>
<div className="sidebar__container">
<div className="row">
<div className="col col--3">
<DocSidebar docsMetadata={docsMetadata} sidebar={sidebar} />
</div>
<div className="content__container">
<main className="col">
{renderRoutes(route.routes, {docsMetadata})}
</div>
</main>
</div>
</div>
</Layout>

View file

@ -1,23 +0,0 @@
/**
* Copyright (c) 2017-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
/**
* width 19rem is calculated according to (sidebarWidth - sidebarPadding)
* i.e 20rem - ( 0.5rem + 0.5rem )
* Todo - 0.5rem and 20rem should be a css variable
*/
@media (min-width: 996px) {
.sidebar__container {
width: 19rem;
float: left;
min-height: calc(100vh - var(--ifm-navbar-height));
}
.content__container {
margin: 0 0 0 19rem;
}
}

View file

@ -5,13 +5,17 @@
* LICENSE file in the root directory of this source tree.
*/
import React from 'react';
import React, {useState} from 'react';
import classnames from 'classnames';
import Link from '@docusaurus/Link'; // eslint-disable-line
import './styles.css';
import styles from './styles.module.css';
const MOBILE_TOGGLE_SIZE = 24;
function DocSidebar(props) {
const [showResponsiveSidebar, setShowResponsiveSidebar] = useState(false);
const {docsMetadata, sidebar} = props;
if (!sidebar) {
@ -59,7 +63,10 @@ function DocSidebar(props) {
<Link
activeClassName="menu__link--active"
className="menu__link"
to={item.href}>
to={item.href}
onClick={() => {
setShowResponsiveSidebar(false);
}}>
{item.label}
</Link>
</li>
@ -72,10 +79,50 @@ function DocSidebar(props) {
};
return (
<div className="menu menu--responsive sidebar">
<ul className="menu__list">
{thisSidebar.map(item => renderItem(item, {root: true}))}
</ul>
<div className={styles.sidebar}>
<div
className={classnames('menu', 'menu--responsive', {
'menu--show': showResponsiveSidebar,
})}>
<button
aria-label={showResponsiveSidebar ? 'Close Menu' : 'Open Menu'}
className="button button--secondary button--sm menu__button"
type="button"
onClick={() => {
setShowResponsiveSidebar(!showResponsiveSidebar);
}}>
{showResponsiveSidebar ? (
<span
className={classnames(
styles.sidebarMenuIcon,
styles.sidebarMenuCloseIcon,
)}>
&times;
</span>
) : (
<svg
className={styles.sidebarMenuIcon}
xmlns="http://www.w3.org/2000/svg"
height={MOBILE_TOGGLE_SIZE}
width={MOBILE_TOGGLE_SIZE}
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>
<ul className="menu__list">
{thisSidebar.map(item => renderItem(item, {root: true}))}
</ul>
</div>
</div>
);
}

View file

@ -1,21 +0,0 @@
/**
* Copyright (c) 2017-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
@media (min-width: 996px) {
.sidebar {
border-right: 1px solid #dadde1;
bottom: 0;
box-sizing: border-box;
padding: 0.5rem;
left: 0;
position: fixed;
overflow-x: hidden;
overflow-y: auto;
top: var(--ifm-navbar-height);
width: 20rem;
}
}

View file

@ -0,0 +1,29 @@
/**
* Copyright (c) 2017-present, Facebook, Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*/
@media (min-width: 997px) {
.sidebar {
height: calc(100vh - var(--ifm-navbar-height));
overflow-y: auto;
padding: 0.5rem;
position: sticky;
top: var(--ifm-navbar-height);
}
}
.sidebarMenuIcon {
vertical-align: middle;
}
.sidebarMenuCloseIcon {
display: inline-block;
height: 24px;
font-size: 1.5rem;
font-weight: var(--ifm-font-weight-bold);
line-height: 0.9;
width: 24px;
}

View file

@ -5,11 +5,12 @@
* LICENSE file in the root directory of this source tree.
*/
@media screen and (max-width: 996px) {
@media screen and (max-width: 997px) {
.displayOnlyInLargeViewport {
display: none !important;
}
}
.toggle {
align-items: center;
display: flex;
@ -20,7 +21,7 @@
}
.toggle::before {
position: absolute;
}
}
.moon::before {
content: '\1F31C';
}
@ -28,7 +29,6 @@
content: '\1F31E';
}
/**
* styles for React Toggle
* copied over because we want to allow user to swizzle it and modify the css