add sidebar layout to modules

This commit is contained in:
Nathan Wang 2020-06-20 00:16:07 -07:00
parent 3bce9b3dcd
commit 1e4555036e
4 changed files with 313 additions and 30 deletions

View file

@ -30,6 +30,7 @@
"react": "^16.12.0",
"react-dom": "^16.12.0",
"react-helmet": "^6.0.0",
"react-transition-group": "^4.4.1",
"rehype-react": "^5.0.1",
"remark": "^12.0.0",
"tailwindcss": "^1.4.6"

View file

@ -0,0 +1,107 @@
import { CSSTransition as ReactCSSTransition } from 'react-transition-group'
import React from "react";
import { useRef, useEffect, useContext } from 'react'
const TransitionContext = React.createContext({
parent: {},
})
function useIsInitialRender() {
const isInitialRender = useRef(true)
useEffect(() => {
isInitialRender.current = false
}, [])
return isInitialRender.current
}
function CSSTransition({
show,
enter = '',
enterFrom = '',
enterTo = '',
leave = '',
leaveFrom = '',
leaveTo = '',
appear,
children,
}) {
const enterClasses = enter.split(' ').filter((s) => s.length)
const enterFromClasses = enterFrom.split(' ').filter((s) => s.length)
const enterToClasses = enterTo.split(' ').filter((s) => s.length)
const leaveClasses = leave.split(' ').filter((s) => s.length)
const leaveFromClasses = leaveFrom.split(' ').filter((s) => s.length)
const leaveToClasses = leaveTo.split(' ').filter((s) => s.length)
function addClasses(node, classes) {
classes.length && node.classList.add(...classes)
}
function removeClasses(node, classes) {
classes.length && node.classList.remove(...classes)
}
return (
<ReactCSSTransition
appear={appear}
unmountOnExit
in={show}
addEndListener={(node, done) => {
node.addEventListener('transitionend', done, false)
}}
onEnter={(node) => {
addClasses(node, [...enterClasses, ...enterFromClasses])
}}
onEntering={(node) => {
removeClasses(node, enterFromClasses)
addClasses(node, enterToClasses)
}}
onEntered={(node) => {
removeClasses(node, [...enterToClasses, ...enterClasses])
}}
onExit={(node) => {
addClasses(node, [...leaveClasses, ...leaveFromClasses])
}}
onExiting={(node) => {
removeClasses(node, leaveFromClasses)
addClasses(node, leaveToClasses)
}}
onExited={(node) => {
removeClasses(node, [...leaveToClasses, ...leaveClasses])
}}
>
{children}
</ReactCSSTransition>
)
}
function Transition({ show, appear, ...rest }) {
const { parent } = useContext(TransitionContext);
const isInitialRender = useIsInitialRender();
const isChild = show === undefined;
if (isChild) {
return (
<CSSTransition
appear={parent.appear || !parent.isInitialRender}
show={parent.show}
{...rest}
/>
)
}
return (
<TransitionContext.Provider
value={{
parent: {
show,
isInitialRender,
appear,
},
}}
>
<CSSTransition appear={appear} show={show} {...rest} />
</TransitionContext.Provider>
)
}
export default Transition;

View file

@ -1,8 +1,10 @@
import React from "react"
import React, { useState } from "react"
import { graphql, Link } from "gatsby"
import Layout from "../components/layout";
import Markdown from "../components/Markdown";
import ModuleOrdering, { divisionLabels } from "../../content/ordering";
import Transition from "../components/Transition";
const renderPrerequisite = (prerequisite) => {
const link = prerequisite.length > 1 ? prerequisite[1] : null;
@ -14,46 +16,194 @@ const renderPrerequisite = (prerequisite) => {
);
};
const Breadcrumbs = ({division}) => (
<nav className="flex items-center text-sm leading-5 font-medium">
<Link to="/" className="text-gray-500 hover:text-gray-700 transition duration-150 ease-in-out">
Home
</Link>
<svg className="flex-shrink-0 mx-2 h-5 w-5 text-gray-400" viewBox="0 0 20 20" fill="currentColor">
<path fillRule="evenodd"
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
clipRule="evenodd" />
</svg>
<Link to={`/${division}`} className="text-gray-500 hover:text-gray-700 transition duration-150 ease-in-out">
{divisionLabels[division]}
</Link>
<svg className="flex-shrink-0 mx-2 h-5 w-5 text-gray-400" viewBox="0 0 20 20" fill="currentColor">
<path fillRule="evenodd"
d="M7.293 14.707a1 1 0 010-1.414L10.586 10 7.293 6.707a1 1 0 011.414-1.414l4 4a1 1 0 010 1.414l-4 4a1 1 0 01-1.414 0z"
clipRule="evenodd" />
</svg>
<span className="text-gray-500">
Getting Started
</span>
</nav>
);
const SidebarBottomButtons = () => (
<>
<div className="flex-shrink-0 border-t border-gray-200 flex">
<button className="group flex-1 flex items-center p-4 text-sm leading-5 font-medium text-gray-600 hover:text-gray-900 hover:bg-gray-50 focus:outline-none focus:bg-gray-100 transition ease-in-out duration-150">
<svg className="mr-4 h-6 w-6 text-gray-400 group-hover:text-gray-500 group-focus:text-gray-500 transition ease-in-out duration-150" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2">
<path d="M12 8v4m0 4h.01M21 12a9 9 0 11-18 0 9 9 0 0118 0z"/>
</svg>
Report an Issue
</button>
</div>
<div className="flex-shrink-0 border-t border-gray-200 flex">
<button className="group flex-1 flex items-center p-4 text-sm leading-5 font-medium text-gray-600 hover:text-gray-900 hover:bg-gray-50 focus:outline-none focus:bg-gray-100 transition ease-in-out duration-150">
<svg className="mr-4 h-6 w-6 text-gray-400 group-hover:text-gray-500 group-focus:text-gray-500 transition ease-in-out duration-150" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeLinecap="round" strokeLinejoin="round" strokeWidth="2">
<path d="M8 10h.01M12 10h.01M16 10h.01M9 16H5a2 2 0 01-2-2V6a2 2 0 012-2h14a2 2 0 012 2v8a2 2 0 01-2 2h-5l-5 5v-5z"/>
</svg>
Get Help
</button>
</div>
</>
);
const SidebarNavLinks = ({ division }) => (
<>
{ModuleOrdering[division].map((val, idx) => (
<Link to={`/${division}/${val}`} className={`flex items-center px-6 py-3 text-sm leading-5 text-gray-600 hover:text-gray-900 hover:bg-gray-100 focus:outline-none focus:bg-gray-200 transition ease-in-out duration-150`}>
{val.hasOwnProperty("name") ? val.name + " (todo dropdown)": val}
</Link>
))}
</>
);
export default function Template(props) {
const { mdx } = props.data; // data.markdownRemark holds your post data
const { body } = mdx;
const prereqs = mdx.frontmatter.prerequisites;
const division = props.pageContext.division;
const [isMobileNavOpen, setIsMobileNavOpen] = useState(false);
return (
<Layout>
<div className="max-w-4xl mx-auto my-8">
<div className="px-4">
<Link className="underline text-blue-600" to={"/" + division + "/"}>&larr; Back to Home</Link>
<h1 className="mt-8 text-3xl font-bold">{mdx.frontmatter.title}</h1>
<p className={`${prereqs ? "mb-4" : "mb-8"} text-gray-500`}>Author: {mdx.frontmatter.author}</p>
{prereqs &&
<div className="rounded-md bg-blue-50 p-4 mb-12">
<div className="flex">
<div className="flex-shrink-0">
<svg className="h-5 w-5 text-blue-400" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd"
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z"
clipRule="evenodd" />
</svg>
</div>
<div className="ml-3">
<h3 className="text-sm leading-5 font-medium text-blue-800">
Prerequisites
</h3>
<div className="mt-2 text-sm leading-5 text-blue-800">
<ul className="list-disc list-inside pl-3 space-y-1">
{prereqs.map(renderPrerequisite)}
</ul>
<div className="h-screen flex overflow-hidden bg-white">
{/* Off-canvas menu for mobile */}
<Transition show={isMobileNavOpen}>
<div className="lg:hidden">
<div className="fixed inset-0 flex z-40">
<Transition
enter="transition-opacity ease-linear duration-300"
enterFrom="opacity-0"
enterTo="opacity-100"
leave="transition-opacity ease-linear duration-300"
leaveFrom="opacity-100"
leaveTo="opacity-0"
>
<div className="fixed inset-0" onClick={() => setIsMobileNavOpen(false)}>
<div className="absolute inset-0 bg-gray-600 opacity-75" />
</div>
</Transition>
<Transition
enter="transition ease-in-out duration-300 transform"
enterFrom="-translate-x-full"
enterTo="translate-x-0"
leave="transition ease-in-out duration-300 transform"
leaveFrom="translate-x-0"
leaveTo="-translate-x-full"
>
<div className="relative flex-1 flex flex-col max-w-xs w-full bg-white">
<div className="absolute top-0 right-0 -mr-14 p-1">
<button className="flex items-center justify-center h-12 w-12 rounded-full focus:outline-none focus:bg-gray-600" aria-label="Close sidebar" onClick={() => setIsMobileNavOpen(false)}>
<svg className="h-6 w-6 text-white" stroke="currentColor" fill="none" viewBox="0 0 24 24">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M6 18L18 6M6 6l12 12" />
</svg>
</button>
</div>
<div className="flex-1 h-0 pt-5 pb-4 overflow-y-auto">
<div className="flex-shrink-0 flex items-center px-4">
<img className="h-8 w-auto" src="https://tailwindui.com/img/logos/workflow-logo-on-white.svg" alt="Workflow" />
</div>
<div className="mt-8 px-6">
<Breadcrumbs division={division} />
</div>
<nav className="mt-2">
<SidebarNavLinks division={division} />
</nav>
</div>
<SidebarBottomButtons />
</div>
</Transition>
<div className="flex-shrink-0 w-14">
{/* Force sidebar to shrink to fit close icon */}
</div>
</div>
</div>}
</div>
</Transition>
{/* Static sidebar for desktop */}
<div className="hidden lg:flex lg:flex-shrink-0">
<div className="flex flex-col w-64 border-r border-gray-200 bg-white">
<div className="h-0 flex-1 flex flex-col pt-5 pb-4 overflow-y-auto">
<div className="flex items-center flex-shrink-0 px-4">
<img className="h-8 w-auto" src="https://tailwindui.com/img/logos/workflow-logo-on-white.svg" alt="Workflow" />
</div>
{/* Sidebar component, swap this element with another sidebar if you like */}
<nav className="mt-5 flex-1 bg-white">
<SidebarNavLinks division={division} />
</nav>
</div>
<SidebarBottomButtons />
</div>
</div>
<div className="flex flex-col w-0 flex-1 overflow-hidden">
<div className="lg:hidden pl-1 pt-1 sm:pl-3 sm:pt-3">
<button className="-ml-0.5 -mt-0.5 h-12 w-12 inline-flex items-center justify-center rounded-md text-gray-500 hover:text-gray-900 focus:outline-none focus:bg-gray-200 transition ease-in-out duration-150" aria-label="Open sidebar"
onClick={() => setIsMobileNavOpen(true)}
>
<svg className="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M4 6h16M4 12h16M4 18h16" />
</svg>
</button>
</div>
<main className="flex-1 relative z-0 overflow-y-auto sm:pt-2 pb-6 focus:outline-none lg:py-6" tabIndex={0}>
<div className="max-w-4xl mx-auto px-4 sm:px-6 lg:px-8">
<div>
<div className="hidden lg:block">
<Breadcrumbs division={division} />
</div>
<div className="lg:mt-12 mb-4">
<div className="flex-1 min-w-0">
<h1 className="text-2xl font-bold text-gray-900 sm:text-3xl">
{mdx.frontmatter.title}
</h1>
<p className={`text-gray-500`}>Author: {mdx.frontmatter.author}</p>
</div>
</div>
</div>
<div className="py-4">
{prereqs &&
<div className="rounded-md bg-blue-50 p-4 mb-12">
<div className="flex">
<div className="flex-shrink-0">
<svg className="h-5 w-5 text-blue-400" fill="currentColor" viewBox="0 0 20 20">
<path fillRule="evenodd"
d="M18 10a8 8 0 11-16 0 8 8 0 0116 0zm-7-4a1 1 0 11-2 0 1 1 0 012 0zM9 9a1 1 0 000 2v3a1 1 0 001 1h1a1 1 0 100-2v-3a1 1 0 00-1-1H9z"
clipRule="evenodd" />
</svg>
</div>
<div className="ml-3">
<h3 className="text-sm leading-5 font-medium text-blue-800">
Prerequisites
</h3>
<div className="mt-2 text-sm leading-5 text-blue-800">
<ul className="list-disc list-inside pl-3 space-y-1">
{prereqs.map(renderPrerequisite)}
</ul>
</div>
</div>
</div>
</div>}
<Markdown body={body} className="markdown--module" />
<Markdown body={body} className="markdown--module" />
</div>
</div>
</main>
</div>
</div>
</Layout>

View file

@ -956,6 +956,13 @@
dependencies:
regenerator-runtime "^0.13.4"
"@babel/runtime@^7.8.7":
version "7.10.3"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.10.3.tgz#670d002655a7c366540c67f6fd3342cd09500364"
integrity sha512-RzGO0RLSdokm9Ipe/YD+7ww8X2Ro79qiXZF3HU9ljrM+qnJmH1Vqth+hbiQZy761LnMJTMitHDuKVYTk3k4dLw==
dependencies:
regenerator-runtime "^0.13.4"
"@babel/standalone@^7.9.6":
version "7.10.2"
resolved "https://registry.yarnpkg.com/@babel/standalone/-/standalone-7.10.2.tgz#49dbbadcbc4b199df064d7d8b3e21c915b84abdb"
@ -4971,7 +4978,7 @@ csso@^4.0.2:
dependencies:
css-tree "1.0.0-alpha.39"
csstype@^2.2.0, csstype@^2.5.7, csstype@^2.6.10, csstype@^2.6.6, csstype@^2.6.9:
csstype@^2.2.0, csstype@^2.5.7, csstype@^2.6.10, csstype@^2.6.6, csstype@^2.6.7, csstype@^2.6.9:
version "2.6.10"
resolved "https://registry.yarnpkg.com/csstype/-/csstype-2.6.10.tgz#e63af50e66d7c266edb6b32909cfd0aabe03928b"
integrity sha512-D34BqZU4cIlMCY93rZHbrq9pjTAQJ3U8S8rfBqjwHxkGPThWFjzZDQpgMJY0QViLxth6ZKYiwFBo14RdN44U/w==
@ -5462,6 +5469,14 @@ dom-helpers@^3.4.0:
dependencies:
"@babel/runtime" "^7.1.2"
dom-helpers@^5.0.1:
version "5.1.4"
resolved "https://registry.yarnpkg.com/dom-helpers/-/dom-helpers-5.1.4.tgz#4609680ab5c79a45f2531441f1949b79d6587f4b"
integrity sha512-TjMyeVUvNEnOnhzs6uAn9Ya47GmMo3qq7m+Lr/3ON0Rs5kHvb8I+SQYjLUSYn7qhEm0QjW0yrBkvz9yOrwwz1A==
dependencies:
"@babel/runtime" "^7.8.7"
csstype "^2.6.7"
dom-serializer@0, dom-serializer@^0.2.1:
version "0.2.2"
resolved "https://registry.yarnpkg.com/dom-serializer/-/dom-serializer-0.2.2.tgz#1afb81f533717175d478655debc5e332d9f9bb51"
@ -12708,6 +12723,16 @@ react-style-singleton@^2.1.0:
invariant "^2.2.4"
tslib "^1.0.0"
react-transition-group@^4.4.1:
version "4.4.1"
resolved "https://registry.yarnpkg.com/react-transition-group/-/react-transition-group-4.4.1.tgz#63868f9325a38ea5ee9535d828327f85773345c9"
integrity sha512-Djqr7OQ2aPUiYurhPalTrVy9ddmFCCzwhqQmtN+J3+3DzLO209Fdr70QrN8Z3DsglWql6iY1lDWAfpFiBtuKGw==
dependencies:
"@babel/runtime" "^7.5.5"
dom-helpers "^5.0.1"
loose-envify "^1.4.0"
prop-types "^15.6.2"
react@^16.12.0, react@^16.8.0:
version "16.13.1"
resolved "https://registry.yarnpkg.com/react/-/react-16.13.1.tgz#2e818822f1a9743122c063d6410d85c1e3afe48e"