mirror of
https://github.com/wasp-lang/wasp.git
synced 2024-11-25 23:37:46 +03:00
Implemented hamburger menu for landing page on mobile.
This commit is contained in:
parent
ab2a87bd0d
commit
5160c5c34a
45
web/package-lock.json
generated
45
web/package-lock.json
generated
@ -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",
|
||||
|
@ -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": {
|
||||
|
@ -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
108
web/src/lib/Transition.js
Normal 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
|
Loading…
Reference in New Issue
Block a user