add sidebar layout to modules
This commit is contained in:
parent
3bce9b3dcd
commit
1e4555036e
4 changed files with 313 additions and 30 deletions
|
@ -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"
|
||||
|
|
107
src/components/Transition.js
Normal file
107
src/components/Transition.js
Normal 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;
|
|
@ -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 + "/"}>← 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>
|
||||
|
|
27
yarn.lock
27
yarn.lock
|
@ -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"
|
||||
|
|
Reference in a new issue