Implemented hamburger menu for landing page on mobile.

This commit is contained in:
Matija Sosic 2022-11-27 11:20:23 +01:00
parent ab2a87bd0d
commit 5160c5c34a
4 changed files with 286 additions and 5 deletions

45
web/package-lock.json generated
View File

@ -23,6 +23,7 @@
"react-feather": "^2.0.10",
"react-modal": "^3.14.3",
"react-syntax-highlighter": "^15.5.0",
"react-transition-group": "^4.4.5",
"tailwindcss": "^3.2.4"
},
"devDependencies": {
@ -5462,6 +5463,15 @@
"utila": "~0.4"
}
},
"node_modules/dom-helpers": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
"integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
"dependencies": {
"@babel/runtime": "^7.8.7",
"csstype": "^3.0.2"
}
},
"node_modules/dom-serializer": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
@ -9834,6 +9844,21 @@
"react": "^16.8.0 || ^17.0.0 || ^18.0.0"
}
},
"node_modules/react-transition-group": {
"version": "4.4.5",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
"integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
"dependencies": {
"@babel/runtime": "^7.5.5",
"dom-helpers": "^5.0.1",
"loose-envify": "^1.4.0",
"prop-types": "^15.6.2"
},
"peerDependencies": {
"react": ">=16.6.0",
"react-dom": ">=16.6.0"
}
},
"node_modules/read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
@ -16717,6 +16742,15 @@
"utila": "~0.4"
}
},
"dom-helpers": {
"version": "5.2.1",
"resolved": "https://registry.npmjs.org/dom-helpers/-/dom-helpers-5.2.1.tgz",
"integrity": "sha512-nRCa7CK3VTrM2NmGkIy4cbK7IZlgBE/PYMn55rrXefr5xXDP0LdtfPnblFDoVdcAfslJ7or6iqAUnx0CCGIWQA==",
"requires": {
"@babel/runtime": "^7.8.7",
"csstype": "^3.0.2"
}
},
"dom-serializer": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz",
@ -19794,6 +19828,17 @@
"use-latest": "^1.2.1"
}
},
"react-transition-group": {
"version": "4.4.5",
"resolved": "https://registry.npmjs.org/react-transition-group/-/react-transition-group-4.4.5.tgz",
"integrity": "sha512-pZcd1MCJoiKiBR2NRxeCRg13uCXbydPnmB4EOeRrY7480qNWO8IIgQG6zlDkm6uRMsURXPuKq0GWtiM59a5Q6g==",
"requires": {
"@babel/runtime": "^7.5.5",
"dom-helpers": "^5.0.1",
"loose-envify": "^1.4.0",
"prop-types": "^15.6.2"
}
},
"read-cache": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",

View File

@ -29,6 +29,7 @@
"react-feather": "^2.0.10",
"react-modal": "^3.14.3",
"react-syntax-highlighter": "^15.5.0",
"react-transition-group": "^4.4.5",
"tailwindcss": "^3.2.4"
},
"devDependencies": {

View File

@ -1,11 +1,14 @@
import React from 'react'
import React, { useState } from 'react'
import Link from '@docusaurus/Link'
import { Star, Twitter } from 'react-feather'
import Transition from '../../lib/Transition'
import { DiscordIcon, TwitterIcon } from './SocialIcons'
const Nav = () => {
const [open, setOpen] = useState(false)
const Logo = () => (
<div className='flex flex-shrink-0 items-center'>
<Link to='/'>
@ -57,8 +60,11 @@ const Nav = () => {
</a>
)
const HamburgerButton = () => (
<div className='absolute inset-y-0 left-0 px-2 flex items-center lg:hidden'>
const HamburgerButton = ({ toggleFlyOut }) => (
<div
className='absolute inset-y-0 left-0 px-2 flex items-center lg:hidden'
onClick={() => toggleFlyOut()}
>
<button
className={`
inline-flex items-center p-2
@ -101,7 +107,7 @@ const Nav = () => {
lg:container lg:px-16 xl:px-20
'
>
<HamburgerButton />
<HamburgerButton toggleFlyOut={() => setOpen(true)} />
<div className='
flex flex-1
items-center justify-center
@ -200,10 +206,131 @@ const Nav = () => {
url='https://twitter.com/WaspLang'
/>
</div> {/* EOF right side */}
</div>
</div>
{/* Mobile Nav Menu */}
<Transition
appear={true}
show={open}
enter="transition ease-out duration-200"
enterFrom="opacity-0 translate-y-1"
enterTo="opacity-100 translate-y-0"
leave="transition ease-in duration-150"
leaveFrom="opacity-100 translate-y-0"
leaveTo="opacity-0 translate-y-1"
>
<div
className={`
fixed -inset-y-0 z-50 h-screen w-screen transform overflow-y-scroll bg-white p-4 md:p-8
`}
>
<div className='absolute right-4 top-4 items-center justify-between'>
<div className="-mr-2">
<button
type="button"
onClick={() => setOpen(false)}
className={`
text-neutral-700 inline-flex items-center justify-center rounded-md
bg-white p-2 hover:bg-gray-100 focus:outline-none focus:ring-2 focus:ring-inset
`}
>
<span className="sr-only">Close menu</span>
<svg
className="h-6 w-6"
xmlns="http://www.w3.org/2000/svg"
fill="none"
viewBox="0 0 24 24"
stroke="currentColor"
aria-hidden="true"
>
<path
strokeLinecap="round"
strokeLinejoin="round"
strokeWidth="2"
d="M6 18L18 6M6 6l12 12"
/>
</svg>
</button>
</div>
</div>
<div className='mt-6 mb-12'>
{/* Docs */}
<div className='space-y-1 pt-2 pb-4'>
<Link to="/docs">
<span className="text-neutral-700 block pl-3 pr-4 text-base font-medium">
Docs
</span>
</Link>
</div>
{/* Docs */}
<div className='space-y-1 pt-2 pb-4'>
<Link to="/blog">
<span className="text-neutral-700 block pl-3 pr-4 text-base font-medium">
Blog
</span>
</Link>
</div>
{/* FAQ */}
<div className='space-y-1 pt-2 pb-4'>
<Link to="#faq" onClick={() => setOpen(false)}>
<span className="text-neutral-700 block pl-3 pr-4 text-base font-medium">
FAQ
</span>
</Link>
</div>
{/* Join the list */}
<div className='space-y-1 pt-2 pb-4'>
<Link to="#signup" onClick={() => setOpen(false)}>
<span
className={`
text-neutral-700 block pl-3 pr-4 text-base font-medium
px-2 py-1 rounded bg-yellow-500/25 hover:bg-yellow-500/10
`}>
📬 Join the list
</span>
</Link>
</div>
{/* GitHub */}
<div className='space-y-1 pt-2 pb-4'>
<Link to="https://github.com/wasp-lang/wasp">
<span className="text-neutral-700 block pl-3 pr-4 text-base font-medium">
GitHub
</span>
</Link>
</div>
{/* Discord */}
<div className='space-y-1 pt-2 pb-4'>
<Link to="https://discord.gg/rzdnErX">
<span className="text-neutral-700 block pl-3 pr-4 text-base font-medium">
👾 Discord
</span>
</Link>
</div>
{/* Twitter */}
<div className='space-y-1 pt-2 pb-4'>
<Link to="https://twitter.com/WaspLang">
<span className="text-neutral-700 block pl-3 pr-4 text-base font-medium">
🐦 Twitter
</span>
</Link>
</div>
</div>
</div>
</Transition>
</div>
</nav>

108
web/src/lib/Transition.js Normal file
View File

@ -0,0 +1,108 @@
// From: https://gist.github.com/adamwathan/3b9f3ad1a285a2d1b482769aeb862467
import { CSSTransition as ReactCSSTransition } from 'react-transition-group'
import React, { 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