Browse Source

(feat) Offscreen nav + various responsive fixes

Signed-off-by: Muthu Kumar <muthukumar@thefeathers.in>
pull/1/head
Muthu Kumar 3 years ago
parent
commit
0bead94c77
Signed by: mkrhere GPG Key ID: 3FD688398897097E
  1. 51
      src/components/Container.tsx
  2. 96
      src/components/Menu.tsx
  3. 1
      src/components/RevealChildren.tsx
  4. 46
      src/util/useMediaQuery.ts

51
src/components/Container.tsx

@ -1,19 +1,20 @@
import React, { useState, useEffect, useRef, useLayoutEffect } from "react"; import React, { useState, useEffect, useRef, useLayoutEffect } from "react";
import { css, cx } from "emotion"; import { css, cx } from "emotion";
import { Link, useHistory } from "react-router-dom"; import { useHistory } from "react-router-dom";
import { ReactComponent as Logo } from "../assets/logo.svg"; import { ReactComponent as Logo } from "../assets/logo.svg";
import { ReactComponent as Right } from "../assets/arrow-right.svg"; import { ReactComponent as Right } from "../assets/arrow-right.svg";
import { getTimeout } from "../util"; import { getTimeout } from "../util";
import Menu from "./Menu"; import Menu from "./Menu";
import useMediaQuery from "../util/useMediaQuery";
const flash = css` const flash = css`
span& { span& {
width: 5.4rem; width: 5.6rem;
height: 5.4rem; height: 5.6rem;
opacity: 100%;
top: -0.2rem; top: -0.2rem;
left: -0.2rem; left: -0.05rem;
opacity: 1;
} }
`; `;
@ -35,7 +36,9 @@ const Container: React.FunctionComponent<{
}) => { }) => {
const history = useHistory(); const history = useHistory();
const logoContainer = useRef<HTMLAnchorElement>(null); const mobile = useMediaQuery("(max-width: 50rem)");
const logoContainer = useRef<HTMLButtonElement>(null);
const logo = useRef<SVGSVGElement>(null); const logo = useRef<SVGSVGElement>(null);
const highlightCircle = useRef<HTMLSpanElement>(null); const highlightCircle = useRef<HTMLSpanElement>(null);
const containerChild = useRef<HTMLDivElement>(null); const containerChild = useRef<HTMLDivElement>(null);
@ -127,25 +130,27 @@ const Container: React.FunctionComponent<{
position: relative; position: relative;
`}> `}>
{!hideNav && ( {!hideNav && (
<Link <button
to={"/"} ref={logoContainer}
innerRef={logoContainer}
className={css` className={css`
position: absolute; position: absolute;
top: 8rem; top: 8rem;
left: 5rem; left: 5rem;
background: none;
border: 0;
font-size: 1rem;
&:hover .logo-highlight, &:hover .logo-highlight,
&:active .logo-highlight { &:active .logo-highlight {
width: 5.2rem; width: 5.2rem;
height: 5.2rem; height: 5.2rem;
opacity: 100%; opacity: 1;
top: -0.1rem; top: -0.05rem;
left: -0.1rem; left: 0.15rem;
} }
`} `}
onMouseOver={() => setShowMenu(true)} onMouseOver={() => !mobile && setShowMenu(true)}
onMouseOut={() => setShowMenu(false)}> onMouseOut={() => !mobile && setShowMenu(false)}>
<span <span
ref={highlightCircle} ref={highlightCircle}
className={cx( className={cx(
@ -156,7 +161,8 @@ const Container: React.FunctionComponent<{
border-radius: 100%; border-radius: 100%;
background: var(--primary-colour); background: var(--primary-colour);
z-index: 0; z-index: 0;
opacity: 0%; opacity: 0;
cursor: pointer;
transition: all 600ms; transition: all 600ms;
`, `,
@ -171,10 +177,12 @@ const Container: React.FunctionComponent<{
width: 5rem; width: 5rem;
border-radius: 100%; border-radius: 100%;
box-shadow: 0px 0px 50px 0px rgba(100, 100, 100, 0.65); box-shadow: 0px 0px 50px 0px rgba(100, 100, 100, 0.65);
cursor: pointer;
`} `}
onClick={() => (mobile ? setShowMenu(true) : history.push("/"))}
/> />
<Menu show={showMenu} /> <Menu show={showMenu} setShowMenu={setShowMenu} />
</Link> </button>
)} )}
{next && ( {next && (
<button <button
@ -184,7 +192,7 @@ const Container: React.FunctionComponent<{
position: fixed; position: fixed;
right: 14vw; right: 14vw;
bottom: 10vh; bottom: 10vh;
z-index: 1000; z-index: 500;
background: none; background: none;
padding: 0; padding: 0;
font-weight: 500; font-weight: 500;
@ -202,7 +210,12 @@ const Container: React.FunctionComponent<{
fill: var(--primary-colour); fill: var(--primary-colour);
} }
`}> `}>
<Right height="2rem" width="2rem" /> <Right
className={css`
height: "2rem";
width: "2rem";
`}
/>
</button> </button>
)} )}
<div <div

96
src/components/Menu.tsx

@ -1,7 +1,9 @@
import React from "react"; import React from "react";
import { Link } from "react-router-dom"; import { Link } from "react-router-dom";
import { css } from "emotion"; import { css, cx } from "emotion";
import { motion } from "framer-motion";
import RevealChildren from "./RevealChildren"; import RevealChildren from "./RevealChildren";
import useMediaQuery from "../util/useMediaQuery";
const menu = [ const menu = [
{ name: "Home", link: "/" }, { name: "Home", link: "/" },
@ -10,15 +12,27 @@ const menu = [
{ name: "Contact", link: "/contact" }, { name: "Contact", link: "/contact" },
]; ];
const Menu: React.FunctionComponent<{ show?: boolean }> = ({ show = false }) => ( const desktopNav = css`
<div float: right;
className={css`
float: right; // remove from flow without position: absolute
padding-left: 6rem; padding-left: 6rem;
height: 5rem; height: 5rem;
`}> `;
<ul
className={css` const offscreenNav = css`
height: 100vh;
width: 100vw;
position: fixed;
top: 0;
left: 0;
z-index: 900;
background: var(--background-colour);
padding: 6rem;
opacity: 0;
top: -100%;
`;
const menuList = css`
z-index: 1000;
display: flex; display: flex;
padding: 0; padding: 0;
list-style: none; list-style: none;
@ -34,14 +48,68 @@ const Menu: React.FunctionComponent<{ show?: boolean }> = ({ show = false }) =>
text-decoration: none; text-decoration: none;
} }
} }
`}> `;
const mobileMenu = css`
flex-direction: column;
justify-content: center;
font-size: 2rem;
& > li + li {
margin-top: 2rem;
margin-left: 0;
}
`;
const Menu: React.FunctionComponent<{ show?: boolean; setShowMenu: (show: boolean) => void }> = ({
show = false,
setShowMenu,
}) => {
const mobile = useMediaQuery("(max-width: 50rem)");
const menuItems = menu.map(item => (
<Link key={item.link} to={item.link}>
{item.name}
</Link>
));
// TODO(mkr): refactor
return (
<motion.div
className={mobile ? offscreenNav : desktopNav}
{...(mobile && {
animate: {
top: show ? "0" : "-100%",
opacity: show ? 1 : 0,
},
})}>
<ul className={mobile ? cx(menuList, mobileMenu) : menuList}>
<RevealChildren type="li" show={show}> <RevealChildren type="li" show={show}>
{menu.map(item => ( {mobile
<Link to={item.link}>{item.name}</Link> ? menuItems.concat(
))} <div
role="button"
key="back"
onClick={() => setShowMenu(false)}
className={css`
background: 0;
border: 0;
font-size: 3rem;
color: var(--text-colour);
cursor: pointer;
&:hover {
color: var(--primary-colour);
}
`}>
</div>,
)
: menuItems}
</RevealChildren> </RevealChildren>
</ul> </ul>
</div> </motion.div>
); );
};
export default Menu; export default Menu;

1
src/components/RevealChildren.tsx

@ -14,6 +14,7 @@ const RevealChildren: React.FunctionComponent<{
<> <>
{items.map((item, i) => ( {items.map((item, i) => (
<Comp <Comp
key={i}
initial={{ opacity: 0, y: -2 }} initial={{ opacity: 0, y: -2 }}
animate={{ animate={{
opacity, opacity,

46
src/util/useMediaQuery.ts

@ -0,0 +1,46 @@
// SPDX-FileCopyrightText: (c) 2019 Antonio Russo
// SPDX-License-Identifier: MIT
// Refer: https://github.com/beautifulinteractions/beautiful-react-hooks/blob/5d100663f2b32e2c0c51edf35a05c7487b4b854f/src/useMediaQuery.js
import { useEffect, useState } from "react";
/**
* Accepts a media query string then uses the
* [window.matchMedia](https://developer.mozilla.org/en-US/docs/Web/API/Window/matchMedia) API to determine if it
* matches with the current document.<br />
* It also monitor the document changes to detect when it matches or stops matching the media query.<br />
* Returns the validity state of the given media query.
*
*/
const useMediaQuery = (mediaQuery: string) => {
const [isVerified, setIsVerified] = useState(!!window.matchMedia(mediaQuery).matches);
useEffect(() => {
const mediaQueryList = window.matchMedia(mediaQuery);
const documentChangeHandler = () => setIsVerified(!!mediaQueryList.matches);
try {
mediaQueryList.addEventListener("change", documentChangeHandler);
} catch (e) {
//Safari isn't supporting mediaQueryList.addEventListener
console.error(e);
mediaQueryList.addListener(documentChangeHandler);
}
documentChangeHandler();
return () => {
try {
mediaQueryList.removeEventListener("change", documentChangeHandler);
} catch (e) {
//Safari isn't supporting mediaQueryList.removeEventListener
console.error(e);
mediaQueryList.removeListener(documentChangeHandler);
}
};
}, [mediaQuery]);
return isVerified;
};
export default useMediaQuery;
Loading…
Cancel
Save