mirror of
https://github.com/Sendouc/sendou.ink.git
synced 2026-08-23 03:26:16 -05:00
Add mobile nav
This commit is contained in:
@@ -12,10 +12,10 @@ const S_Container = styled("div", {
|
||||
position: "relative",
|
||||
userSelect: "none",
|
||||
overflow: "hidden",
|
||||
width: "38px",
|
||||
minWidth: "38px",
|
||||
height: "38px",
|
||||
borderRadius: "16px",
|
||||
width: "var(--item-size)",
|
||||
minWidth: "var(--item-size)",
|
||||
height: "var(--item-size)",
|
||||
borderRadius: "$rounded",
|
||||
});
|
||||
|
||||
const S_Avatar = styled("img", {
|
||||
|
||||
121
frontend/components/layout/HamburgerButton.tsx
Normal file
121
frontend/components/layout/HamburgerButton.tsx
Normal file
@@ -0,0 +1,121 @@
|
||||
import { styled } from "stitches.config";
|
||||
|
||||
export function HamburgerButton({
|
||||
isExpanded,
|
||||
onClick,
|
||||
}: {
|
||||
onClick: () => void;
|
||||
isExpanded: boolean;
|
||||
}) {
|
||||
return (
|
||||
<S_TransformingBurger
|
||||
aria-label="Toggle menu visibility"
|
||||
aria-expanded={isExpanded ? "true" : "false"}
|
||||
onClick={onClick}
|
||||
>
|
||||
<svg
|
||||
width="32"
|
||||
height="32"
|
||||
viewBox="0 0 32 32"
|
||||
fill="none"
|
||||
xmlns="http://www.w3.org/2000/svg"
|
||||
>
|
||||
<S_TopLine
|
||||
x="6"
|
||||
y="9"
|
||||
width="20"
|
||||
height="2"
|
||||
rx="1"
|
||||
fill="currentColor"
|
||||
type={isExpanded ? "expanded" : undefined}
|
||||
/>
|
||||
<S_MiddleLine
|
||||
x="6"
|
||||
y="15"
|
||||
width="20"
|
||||
height="2"
|
||||
rx="1"
|
||||
fill="currentColor"
|
||||
type={isExpanded ? "expanded" : undefined}
|
||||
/>
|
||||
<S_BottomLine
|
||||
x="6"
|
||||
y="21"
|
||||
width="20"
|
||||
height="2"
|
||||
rx="1"
|
||||
fill="currentColor"
|
||||
type={isExpanded ? "expanded" : undefined}
|
||||
/>
|
||||
</svg>
|
||||
</S_TransformingBurger>
|
||||
);
|
||||
}
|
||||
|
||||
const S_TransformingBurger = styled("button", {
|
||||
display: "flex",
|
||||
width: "var(--item-size)",
|
||||
height: "var(--item-size)",
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
padding: "$1",
|
||||
border: "3px solid",
|
||||
borderColor: "$bgLighter",
|
||||
backgroundColor: "transparent",
|
||||
borderRadius: "$rounded",
|
||||
color: "inherit",
|
||||
cursor: "pointer",
|
||||
gap: "2px",
|
||||
|
||||
"@sm": {
|
||||
display: "none",
|
||||
},
|
||||
});
|
||||
|
||||
const S_TopLine = styled("rect", {
|
||||
transform: "none",
|
||||
transformOrigin: "16px 10px",
|
||||
transitionDuration: "150ms",
|
||||
transitionProperty: "transform",
|
||||
transitionTimingFunction: "cubic-bezier(0.4, 0, 0.2, 1)",
|
||||
|
||||
variants: {
|
||||
type: {
|
||||
expanded: {
|
||||
transform: "translateY(7px) rotate(45deg)",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const S_MiddleLine = styled("rect", {
|
||||
opacity: 1,
|
||||
transitionDuration: "150ms",
|
||||
transitionProperty: "opacity",
|
||||
transitionTimingFunction: "cubic-bezier(0.4, 0, 0.2, 1)",
|
||||
|
||||
variants: {
|
||||
type: {
|
||||
expanded: {
|
||||
opacity: 0,
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const S_BottomLine = styled("rect", {
|
||||
transform: "none",
|
||||
transformOrigin: "16px 22px",
|
||||
transitionDuration: "150ms",
|
||||
transitionProperty: "transform",
|
||||
transitionTimingFunction: "cubic-bezier(0.4, 0, 0.2, 1)",
|
||||
|
||||
variants: {
|
||||
type: {
|
||||
expanded: {
|
||||
transform: "translateY(-5px) rotate(-45deg)",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -1,41 +1,36 @@
|
||||
import { ReactNode } from "react";
|
||||
import { ReactNode, useState } from "react";
|
||||
import NextImage from "next/image";
|
||||
import logo from "../assets/img/logo.png";
|
||||
import { Avatar } from "./common/Avatar";
|
||||
import NextLink from "next/link";
|
||||
import logo from "assets/img/logo.png";
|
||||
import { Avatar } from "components/common/Avatar";
|
||||
import { styled } from "stitches.config";
|
||||
import { SearchInput } from "./common/SearchInput";
|
||||
|
||||
const navItems = [
|
||||
{
|
||||
title: "builds",
|
||||
items: ["browse", "gear", "analyzer"],
|
||||
},
|
||||
{
|
||||
title: "play",
|
||||
items: ["calendar", "battle", "Rankings"],
|
||||
},
|
||||
{
|
||||
title: "tools",
|
||||
items: ["planner", "rotations", "top 500"],
|
||||
},
|
||||
{
|
||||
title: "misc",
|
||||
items: ["badges", "links"],
|
||||
},
|
||||
];
|
||||
import { SearchInput } from "components/common/SearchInput";
|
||||
import { HamburgerButton } from "./HamburgerButton";
|
||||
import { navItems } from "utils/constants";
|
||||
import { MobileNav } from "./MobileNav";
|
||||
|
||||
export function Layout({ children }: { children: ReactNode }) {
|
||||
const [menuExpanded, setMenuExpanded] = useState(false);
|
||||
return (
|
||||
<>
|
||||
<S_Header>
|
||||
<S_LogoContainer>
|
||||
<NextImage src={logo} width={30} height={30} />
|
||||
</S_LogoContainer>
|
||||
<SearchInput />
|
||||
<NextLink href="/">
|
||||
<S_LogoContainer>
|
||||
<NextImage src={logo} />
|
||||
</S_LogoContainer>
|
||||
</NextLink>
|
||||
<S_SearchContainer>
|
||||
<SearchInput />
|
||||
</S_SearchContainer>
|
||||
<S_RightContainer>
|
||||
<HamburgerButton
|
||||
isExpanded={menuExpanded}
|
||||
onClick={() => setMenuExpanded((expanded) => !expanded)}
|
||||
/>
|
||||
<Avatar src="https://cdn.discordapp.com/avatars/79237403620945920/fcfd65a3bea598905abb9ca25296816b.png?size=80" />
|
||||
</S_RightContainer>
|
||||
</S_Header>
|
||||
<MobileNav isExpanded={menuExpanded} />
|
||||
<S_Nav>
|
||||
<S_NavItems>
|
||||
{navItems.map((navItem) => (
|
||||
@@ -59,10 +54,19 @@ export function Layout({ children }: { children: ReactNode }) {
|
||||
}
|
||||
|
||||
const S_Header = styled("header", {
|
||||
position: "relative",
|
||||
display: "grid",
|
||||
gridTemplateColumns: "repeat(3, 1fr)",
|
||||
gridTemplateColumns: "repeat(2, 1fr)",
|
||||
alignItems: "center",
|
||||
padding: "$4",
|
||||
"--item-size": "3rem",
|
||||
zIndex: 10,
|
||||
backgroundColor: "$bg",
|
||||
|
||||
"@sm": {
|
||||
gridTemplateColumns: "repeat(3, 1fr)",
|
||||
"--item-size": "38px",
|
||||
},
|
||||
});
|
||||
|
||||
const S_LogoContainer = styled("div", {
|
||||
@@ -73,6 +77,18 @@ const S_LogoContainer = styled("div", {
|
||||
padding: "$1",
|
||||
borderRadius: "$rounded",
|
||||
justifySelf: "flex-start",
|
||||
cursor: "pointer",
|
||||
width: "var(--item-size)",
|
||||
minWidth: "var(--item-size)",
|
||||
height: "var(--item-size)",
|
||||
});
|
||||
|
||||
const S_SearchContainer = styled("div", {
|
||||
display: "none",
|
||||
|
||||
"@sm": {
|
||||
display: "block",
|
||||
},
|
||||
});
|
||||
|
||||
const S_RightContainer = styled("div", {
|
||||
@@ -82,10 +98,14 @@ const S_RightContainer = styled("div", {
|
||||
});
|
||||
|
||||
const S_Nav = styled("nav", {
|
||||
display: "flex",
|
||||
display: "none",
|
||||
justifyContent: "center",
|
||||
backgroundColor: "$bgLighter",
|
||||
backgroundImage: `url(/svg/background-pattern.svg)`,
|
||||
|
||||
"@sm": {
|
||||
display: "flex",
|
||||
},
|
||||
});
|
||||
|
||||
const S_NavItems = styled("div", {
|
||||
113
frontend/components/layout/MobileNav.tsx
Normal file
113
frontend/components/layout/MobileNav.tsx
Normal file
@@ -0,0 +1,113 @@
|
||||
import { SearchInput } from "components/common/SearchInput";
|
||||
import { styled } from "stitches.config";
|
||||
import { navItems } from "utils/constants";
|
||||
import NextLink from "next/link";
|
||||
import { Fragment } from "react";
|
||||
|
||||
export function MobileNav({ isExpanded }: { isExpanded: boolean }) {
|
||||
return (
|
||||
<S_Container
|
||||
aria-hidden={isExpanded ? "false" : "true"}
|
||||
type={isExpanded ? "expanded" : undefined}
|
||||
>
|
||||
<S_TopAction>
|
||||
<SearchInput />
|
||||
</S_TopAction>
|
||||
<S_LinksContainer>
|
||||
{navItems.map((navItem) => {
|
||||
return (
|
||||
<Fragment key={navItem.title}>
|
||||
<S_GroupTitle>{navItem.title}</S_GroupTitle>
|
||||
{navItem.items.map((item) => {
|
||||
return (
|
||||
<NextLink key={item} href="/">
|
||||
<S_NavLink>
|
||||
<S_NavLinkImage
|
||||
src={`/img/nav-icons/${item.replace(" ", "")}.png`}
|
||||
/>
|
||||
<div>{item}</div>
|
||||
</S_NavLink>
|
||||
</NextLink>
|
||||
);
|
||||
})}
|
||||
</Fragment>
|
||||
);
|
||||
})}
|
||||
</S_LinksContainer>
|
||||
</S_Container>
|
||||
);
|
||||
}
|
||||
|
||||
const S_Container = styled("nav", {
|
||||
position: "absolute",
|
||||
bottom: "100vh",
|
||||
width: "100%",
|
||||
height: "100%",
|
||||
transition: "bottom 0.5s",
|
||||
transitionTimingFunction: "cubic-bezier(0.4, 0, 0.2, 1)",
|
||||
backgroundImage: `url(/svg/background-pattern.svg)`,
|
||||
backgroundColor: "$bgLighter",
|
||||
zIndex: 5,
|
||||
overflowY: "auto",
|
||||
paddingBottom: "$6",
|
||||
|
||||
"@sm": {
|
||||
display: "none",
|
||||
},
|
||||
|
||||
variants: {
|
||||
type: {
|
||||
expanded: {
|
||||
bottom: "0",
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
const S_TopAction = styled("div", {
|
||||
display: "grid",
|
||||
gap: "$4",
|
||||
placeItems: "center",
|
||||
backgroundColor: "$bg",
|
||||
paddingTop: "$24",
|
||||
paddingBottom: "$8",
|
||||
});
|
||||
|
||||
const S_LinksContainer = styled("div", {
|
||||
display: "grid",
|
||||
justifyContent: "center",
|
||||
gridAutoColumns: "1fr",
|
||||
gridAutoRows: "4rem",
|
||||
paddingX: "$24",
|
||||
});
|
||||
|
||||
const S_NavLinkImage = styled("img", {
|
||||
width: "2.5rem",
|
||||
marginRight: "$3",
|
||||
});
|
||||
|
||||
const S_GroupTitle = styled("div", {
|
||||
textTransform: "uppercase",
|
||||
alignSelf: "flex-end",
|
||||
justifySelf: "center",
|
||||
paddingX: "$3",
|
||||
paddingY: "$2",
|
||||
marginBottom: "$1",
|
||||
backgroundColor: "$bg",
|
||||
fontWeight: "$bold",
|
||||
});
|
||||
|
||||
const S_NavLink = styled("div", {
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
textTransform: "capitalize",
|
||||
borderTop: "3px solid $bgLighter",
|
||||
fontWeight: "$bold",
|
||||
backgroundColor: "$bg",
|
||||
paddingX: "$2",
|
||||
fontSize: "$sm",
|
||||
|
||||
"&:last-child": {
|
||||
borderBottom: "3px solid $bgLighter",
|
||||
},
|
||||
});
|
||||
@@ -4,7 +4,7 @@ import "./_app.css";
|
||||
import { AppProps } from "next/app";
|
||||
import Head from "next/head";
|
||||
import { SWRConfig } from "swr";
|
||||
import { Layout } from "../components/layout";
|
||||
import { Layout } from "components/layout/Layout";
|
||||
import { globalCss } from "stitches.config";
|
||||
|
||||
const globalStyles = globalCss({
|
||||
|
||||
@@ -41,6 +41,7 @@ export const {
|
||||
"0.5": "0.125rem",
|
||||
"1": "0.25rem",
|
||||
"2": "0.5rem",
|
||||
// TODO: fix vars with . "invalid property"
|
||||
"2.5": "0.625rem",
|
||||
"3": "0.75rem",
|
||||
"3.5": "0.875rem",
|
||||
|
||||
18
frontend/utils/constants.ts
Normal file
18
frontend/utils/constants.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
export const navItems = [
|
||||
{
|
||||
title: "builds",
|
||||
items: ["browse", "gear", "analyzer"],
|
||||
},
|
||||
{
|
||||
title: "play",
|
||||
items: ["calendar", "battle", "Rankings"],
|
||||
},
|
||||
{
|
||||
title: "tools",
|
||||
items: ["planner", "rotations", "top 500"],
|
||||
},
|
||||
{
|
||||
title: "misc",
|
||||
items: ["badges", "links"],
|
||||
},
|
||||
];
|
||||
Reference in New Issue
Block a user