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

136
src/components/Menu.tsx

@ -1,7 +1,9 @@
import React from "react";
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 useMediaQuery from "../util/useMediaQuery";
const menu = [
{ name: "Home", link: "/" },
@ -10,38 +12,104 @@ const menu = [
{ name: "Contact", link: "/contact" },
];
const Menu: React.FunctionComponent<{ show?: boolean }> = ({ show = false }) => (
<div
className={css`
float: right; // remove from flow without position: absolute
padding-left: 6rem;
height: 5rem;
`}>
<ul
className={css`
display: flex;
padding: 0;
list-style: none;
height: 100%;
margin: 0;
align-items: center;
font-weight: 800;
& > li {
margin-left: 1rem;
& > a {
text-decoration: none;
}
}
`}>
<RevealChildren type="li" show={show}>
{menu.map(item => (
<Link to={item.link}>{item.name}</Link>
))}
</RevealChildren>
</ul>
</div>
);
const desktopNav = css`
float: right;
padding-left: 6rem;
height: 5rem;
`;
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;
padding: 0;
list-style: none;
height: 100%;
margin: 0;
align-items: center;
font-weight: 800;
& > li {
margin-left: 1rem;
& > a {
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}>
{mobile
? 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>
</ul>
</motion.div>
);
};
export default Menu;

1
src/components/RevealChildren.tsx

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