rendering layout

This commit is contained in:
Kalle (Sendou)
2020-10-15 01:00:21 +03:00
parent cce21efaae
commit a05eb7cb2d
24 changed files with 3454 additions and 11 deletions

1579
lib/useMockT.ts Normal file

File diff suppressed because it is too large Load Diff

54
lib/useMyTheme.ts Normal file
View File

@@ -0,0 +1,54 @@
import { useColorMode } from "@chakra-ui/core";
/*
const theme = {
light: {
colorMode: "light",
bgColor: "#eff0f3",
darkerBgColor: "#FFFAFA",
textColor: "blackAlpha.900",
borderStyle: "1px solid rgba(0, 0, 0, .2)",
themeColorWithShade: `${themeColor}.500`,
grayWithShade: "gray.600",
themeColorHex: chakraTheme.colors[themeColor]["500"],
themeColorHexLighter: chakraTheme.colors[themeColor]["200"],
themeColor,
} as Theme,
dark: {
colorMode: "dark",
bgColor: "#031e3e",
darkerBgColor: "#0e2a56",
textColor: "whiteAlpha.900",
borderStyle: "1px solid rgba(255, 255, 255, .2)",
themeColorWithShade: `${themeColor}.200`,
grayWithShade: "gray.300",
themeColorHex: chakraTheme.colors[themeColor]["500"],
themeColorHexLighter: chakraTheme.colors[themeColor]["200"],
themeColor,
} as Theme,
}; */
const theme = {
light: {
themeColor: "#79ff61", // light lime green
bgColor: "#eff0f3",
secondaryBgColor: "#FFFAFA",
// FIXME
textColor: "blackAlpha.900",
gray: "gray.600",
},
dark: {
themeColor: "#79ff61", // light lime green
bgColor: "#031e3e",
secondaryBgColor: "#0e2a56",
textColor: "whiteAlpha.900",
gray: "gray.300",
},
} as const
export const useMyTheme = () => {
const { colorMode } = useColorMode()
return theme[colorMode]
}

2
next.config.js Normal file
View File

@@ -0,0 +1,2 @@
const withImages = require('next-images')
module.exports = withImages()

View File

@@ -1,7 +1,7 @@
import React, { useContext } from "react";
import MyThemeContext from "../../themeContext";
export const FooterWaves = () => {
const FooterWaves = () => {
const { themeColorHex, themeColorHexLighter, colorMode } = useContext(
MyThemeContext
);
@@ -15,3 +15,5 @@ export const FooterWaves = () => {
</svg>
);
};
export default FooterWaves;

1254
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -13,14 +13,17 @@
"seed": "ts-node prisma/seed.ts"
},
"dependencies": {
"@chakra-ui/core": "^1.0.0-rc.5",
"@nexus/schema": "0.15.0",
"@prisma/client": "2.8.1",
"apollo-server-micro": "^2.17.0",
"graphql": "14.6.0",
"next": "^9.5.5",
"next-images": "^1.6.0",
"nexus-plugin-prisma": "^0.21.0",
"react": "16.13.1",
"react-dom": "16.13.1"
"react-dom": "16.13.1",
"react-icons": "^3.11.0"
},
"devDependencies": {
"@prisma/cli": "^2.8.0",

View File

@@ -1,5 +1,13 @@
const MyApp = ({ Component, pageProps }) => {
return <Component {...pageProps} />
}
import { ChakraProvider } from "@chakra-ui/core";
import type { AppProps } from "next/app";
import Layout from "scenes/Layout";
export default MyApp
const MyApp = (props: AppProps) => {
return (
<ChakraProvider>
<Layout {...props} />
</ChakraProvider>
);
};
export default MyApp;

31
pages/_document.tsx Normal file
View File

@@ -0,0 +1,31 @@
import { ColorModeScript } from "@chakra-ui/core";
import Document, {
DocumentContext,
Head,
Html,
Main,
NextScript,
} from "next/document";
class MyDocument extends Document {
static async getInitialProps(ctx: DocumentContext) {
const initialProps = await Document.getInitialProps(ctx);
return initialProps;
}
render() {
return (
<Html>
<Head />
<body>
<ColorModeScript />
<Main />
<NextScript />
</body>
</Html>
);
}
}
export default MyDocument;

BIN
public/navIcons/builds.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.8 KiB

BIN
public/navIcons/calendar.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
public/navIcons/freeagents.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.0 KiB

BIN
public/navIcons/plans.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

BIN
public/navIcons/plus.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
public/navIcons/sr.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.1 KiB

BIN
public/navIcons/teams.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

BIN
public/navIcons/tournaments.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

BIN
public/navIcons/xsearch.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

View File

@@ -0,0 +1,40 @@
import { Box, Image, useColorModeValue } from "@chakra-ui/core";
import footerOctoDark from "assets/b8ing_dark.png";
import footerOctoLight from "assets/b8ing_light.png";
import footerSquidDark from "assets/boing_dark.png";
import footerSquidLight from "assets/boing_light.png";
import { useMyTheme } from "lib/useMyTheme";
import { useState } from "react";
import FooterContent from "./FooterContent";
import FooterWaves from "./FooterWaves";
const Footer: React.FC = () => {
const [species] = useState<"octo" | "squid">(
Math.random() > 0.5 ? "octo" : "squid"
);
const { themeColor } = useMyTheme();
const footerImageSrc = useColorModeValue(
{ octo: footerOctoLight, squid: footerSquidLight },
{ octo: footerOctoDark, squid: footerSquidDark }
)[species];
return (
<Box mt="auto">
<Image
src={footerImageSrc}
bg={themeColor}
w="80px"
ml="auto"
mr="35%"
mb="-5.1%"
mt="5rem"
userSelect="none"
loading="lazy"
/>
<FooterWaves />
<FooterContent />
</Box>
);
};
export default Footer;

View File

@@ -0,0 +1,41 @@
import { Box, Flex } from "@chakra-ui/core";
import { DiscordIcon } from "assets/icons";
import { useTranslation } from "lib/useMockT";
import { useMyTheme } from "lib/useMyTheme";
import Link from "next/link";
import { FaGithub } from "react-icons/fa";
const FooterContent: React.FC = () => {
const { t } = useTranslation();
const { themeColor } = useMyTheme();
return (
<Flex
pb="50px"
flexShrink={0}
alignItems="center"
fontWeight="bold"
letterSpacing="1px"
flexWrap="wrap"
justifyContent="space-evenly"
bg={themeColor}
color="black"
>
<Flex flexDirection="column" fontSize="1.2rem">
<Box my="1em">
<Link href="/about">{t("footer;About")}</Link>
</Box>
<Link href="/links">{t("footer;External links")}</Link>
</Flex>
<Flex alignItems="center" flexWrap="wrap" justifyContent="center">
<a href="https://discord.gg/sendou">
<DiscordIcon h="30px" w="30px" m="1em" />
</a>
<a href="https://github.com/Sendouc/sendou.ink">
<Box as={FaGithub} size="30px" m="1em" />
</a>
</Flex>
</Flex>
);
};
export default FooterContent;

View File

@@ -0,0 +1,17 @@
import { useMyTheme } from "lib/useMyTheme";
import React from "react";
const FooterWaves = () => {
const { themeColor } = useMyTheme();
return (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1440 225">
<path
fill={themeColor}
fillOpacity="1"
d="M0,128L60,138.7C120,149,240,171,360,160C480,149,600,107,720,85.3C840,64,960,64,1080,90.7C1200,117,1320,171,1380,197.3L1440,224L1440,320L1380,320C1320,320,1200,320,1080,320C960,320,840,320,720,320C600,320,480,320,360,320C240,320,120,320,60,320L0,320Z"
></path>
</svg>
);
};
export default FooterWaves;

View File

@@ -0,0 +1,203 @@
import {
Box,
Flex,
Image,
Menu,
MenuButton,
MenuGroup,
MenuItem,
MenuList,
} from "@chakra-ui/core";
import { useTranslation } from "lib/useMockT";
import { useMyTheme } from "lib/useMyTheme";
import { useRouter } from "next/dist/client/router";
import Link from "next/link";
const getFirstFridayDate = () => {
const today = new Date();
const month =
today.getDate() - ((1 + today.getDay()) % 7) <= 0
? today.getMonth()
: today.getMonth() + 1;
let day = 1;
while (day <= 7) {
const dateOfVoting = new Date(
Date.UTC(today.getFullYear(), month, day, 15, 0, 0)
);
if (dateOfVoting.getDay() === 5) return dateOfVoting;
day++;
}
console.error("Couldn't resolve first friday of the month for voting");
return new Date(2000, 1, 1);
};
export const navIcons: {
code: string;
displayName: string;
menuItems: {
code: string;
displayName: string;
toAppend?: string;
}[];
}[] = [
{
code: "xsearch",
displayName: "Top 500",
menuItems: [
{ code: "xsearch", displayName: "Browser" },
{ code: "xtrends", displayName: "Trends" },
{ code: "xleaderboards", displayName: "Leaderboards" },
],
},
//{ code: "sr", displayName: "Salmon Run", menuItems: [] },
{
code: "builds",
displayName: "Builds",
menuItems: [
{ code: "builds", displayName: "Browser" },
{ code: "analyzer", displayName: "Analyzer" },
],
},
{ code: "calendar", displayName: "Calendar", menuItems: [] },
{ code: "freeagents", displayName: "Free Agents", menuItems: [] },
//{ name: "teams", displayName: "Teams" },
{ code: "plans", displayName: "Map Planner", menuItems: [] },
{ code: "tournaments", displayName: "Tournaments", menuItems: [] },
{
code: "plus",
displayName: "Plus Server",
menuItems: [
{
code: "plus/voting",
displayName: "Voting",
toAppend:
": " +
getFirstFridayDate().toLocaleString("default", {
month: "short",
day: "numeric",
}),
},
{ code: "plus", displayName: "Suggested and vouched players" },
{ code: "plus/history", displayName: "Voting history" },
{ code: "draft", displayName: "Draft Cup" },
{ code: "plus/faq", displayName: "FAQ" },
],
},
];
const IconNavBar = () => {
const { t } = useTranslation();
const { secondaryBgColor, textColor, themeColor, gray } = useMyTheme();
const pathname = useRouter().pathname;
// FIXME
//const isVoting = !!plusInfoData?.plusInfo?.voting_ends;
const isVoting = false;
return (
<Flex bg={secondaryBgColor} py={2} justifyContent="center" flexWrap="wrap">
{navIcons.map(({ displayName, code, menuItems }) => {
const codesTogether =
"/" +
code +
menuItems.reduce((acc, { code }) => acc + "/" + code, "");
const isActive = pathname !== "/" && codesTogether.includes(pathname);
const MenuNavIcon = () => (
<Flex
flexDirection="column"
alignItems="center"
justifyContent="center"
m="5px 15px"
>
<Box color={isActive ? themeColor : gray} fontSize="0.75em">
{t(`navigation;${displayName}`)}
</Box>
<Image
src={`navIcons/${code}.png`}
h={12}
w={12}
alt={code}
cursor="pointer"
userSelect="none"
ignoreFallback
/>
{menuItems.length > 0 && (
<Box ml="0.1rem" lineHeight="0.5rem">
</Box>
)}
</Flex>
);
if (!menuItems.length) {
return (
<Link key={code} href={code}>
<MenuNavIcon />
</Link>
);
}
return (
<Menu key={code}>
<MenuButton>
<MenuNavIcon />
</MenuButton>
<MenuList bg={secondaryBgColor} color={textColor}>
<MenuGroup title={t(`navigation;${displayName}`)}>
{menuItems.map((item) => (
<Link key={item.code} href={item.code}>
<MenuItem
disabled={item.displayName === "Voting" && !isVoting}
>
{pathname === "/" + item.code ? (
<Box
h="7px"
w="7px"
mb={1}
bgColor={themeColor}
borderRadius="50%"
lineHeight="0.5rem"
mr="7px"
mt="4px"
/>
) : (
<Box
h="7px"
w="7px"
mb={1}
borderRadius="50%"
lineHeight="0.5rem"
mr="7px"
mt="4px"
opacity={1}
/>
)}
<Box>
{t(
`navigation;${
item.displayName !== "Voting"
? item.displayName
: isVoting
? "Voting"
: "Next voting"
}`
)}
{!isVoting && item.toAppend}
</Box>
</MenuItem>
</Link>
))}
</MenuGroup>
</MenuList>
</Menu>
);
})}
</Flex>
);
};
export default IconNavBar;

View File

@@ -0,0 +1,67 @@
import {
IconButton,
Menu,
MenuButton,
MenuItemOption,
MenuList,
MenuOptionGroup,
} from "@chakra-ui/core";
import { useTranslation } from "lib/useMockT";
import { useMyTheme } from "lib/useMyTheme";
import React from "react";
import { FiGlobe } from "react-icons/fi";
export const languages = [
{ code: "de", name: "Deutsch" },
{ code: "en", name: "English" },
{ code: "es-ES", name: "Español" },
{ code: "fr", name: "Français" },
{ code: "it", name: "Italiano" },
{ code: "nl", name: "Nederlands" },
{ code: "pt-BR", name: "Português" },
{ code: "sv", name: "Svenska" },
{ code: "el", name: "Ελληνικά" },
{ code: "ru", name: "Русский" },
{ code: "ja", name: "日本語" },
{ code: "ko", name: "한국어" },
{ code: "zh-TW", name: "繁體中文" },
] as const;
export const LanguageSelector = () => {
const { t } = useTranslation();
const { secondaryBgColor, textColor } = useMyTheme();
return (
<Menu>
<MenuButton
as={IconButton}
aria-label="Switch language"
variant="ghost"
fontSize="20px"
icon={<FiGlobe />}
borderRadius="50%"
color={textColor}
/>
<MenuList bg={secondaryBgColor} color={textColor}>
<MenuOptionGroup
title={t("navigation;Choose language")}
// FIXME
//value={i18n.language}
value="en"
>
{languages.map((lang) => (
<MenuItemOption
key={lang.code}
value={lang.code}
// FIXME
//onClick={() => i18n.changeLanguage(lang.code)}
onClick={() => console.log(lang.code)}
>
{lang.name}
</MenuItemOption>
))}
</MenuOptionGroup>
</MenuList>
</Menu>
);
};

View File

@@ -0,0 +1,107 @@
import {
Box,
Button,
Flex,
Grid,
IconButton,
useColorMode,
} from "@chakra-ui/core";
import { DiscordIcon } from "assets/icons";
import { useTranslation } from "lib/useMockT";
import { useMyTheme } from "lib/useMyTheme";
import Link from "next/link";
import { FiMoon, FiSun } from "react-icons/fi";
import { LanguageSelector } from "./LanguageSelector";
const TopNav = () => {
const { bgColor } = useMyTheme();
const { colorMode, toggleColorMode } = useColorMode();
const UserItem = () => {
const { t } = useTranslation();
// FIXME
return (
<a href="/auth/discord">
<Button leftIcon={<DiscordIcon />} variant="ghost" size="sm">
{t("navigation;Log in via Discord")}
</Button>
</a>
);
/*if (loading) return <Box />;
if (!data?.user)
return (
<a href="/auth/discord">
<Button leftIcon={<DiscordIcon />} variant="ghost" size="sm">
{t("navigation;Log in via Discord")}
</Button>
</a>
);
<Link to={`/u/${data.user.discord_id}`}>
return (
<Menu>
<MenuButton>
<UserAvatar
src={data.user.avatar}
name={data.user.username}
size="sm"
m={1}
cursor="pointer"
/>
</MenuButton>
<MenuList bg={darkerBgColor} color={textColor}>
<MenuGroup title={data.user.username}>
<Link to={`/u/${data.user.discord_id}`}>
<MenuItem>{t("navigation;Profile")}</MenuItem>
</Link>
<a href="/logout">
<MenuItem>{t("navigation;Log out")}</MenuItem>
</a>
</MenuGroup>
</MenuList>
</Menu>
);*/
};
return (
<Grid
templateColumns={["1fr 1fr", null, "1fr 1fr 1fr"]}
bg={bgColor}
w="100%"
alignItems="center"
justifyContent="space-between"
p={1}
>
<Flex alignItems="center">
<IconButton
aria-label={`Switch to ${
colorMode === "light" ? "dark" : "light"
} mode`}
variant="ghost"
color="current"
fontSize="20px"
onClick={toggleColorMode}
icon={colorMode === "light" ? <FiSun /> : <FiMoon />}
borderRadius="50%"
/>
<LanguageSelector />
</Flex>
<Box
justifySelf="center"
fontFamily="Rubik, sans-serif"
color="gray.600"
fontWeight="bold"
letterSpacing={1}
display={["none", null, "block"]}
>
{" "}
<Link href="/">sendou.ink </Link>
</Box>
<Box justifySelf="flex-end">
<UserItem />
</Box>
</Grid>
);
};
export default TopNav;

45
scenes/Layout/index.tsx Normal file
View File

@@ -0,0 +1,45 @@
import { Container, Flex } from "@chakra-ui/core";
import { useMyTheme } from "lib/useMyTheme";
import { AppProps } from "next/app";
import { useRouter } from "next/dist/client/router";
import { useEffect } from "react";
import Footer from "./components/Footer";
import IconNavBar from "./components/IconNavBar";
import TopNav from "./components/TopNav";
const PAGES_WITH_WIDE_CONTAINER = [
"/analyzer",
"/xsearch",
"/builds",
"/plans",
"/xleaderboards",
];
const Layout = ({ Component, pageProps }: AppProps) => {
const { bgColor, textColor } = useMyTheme();
const pathname = useRouter().pathname;
// FIXME
useEffect(() => {
document.body.style.backgroundColor = bgColor;
}, [bgColor]);
return (
<>
<TopNav />
<IconNavBar />
<Flex flexDirection="column" color={textColor} minH="100vh" pt="1rem">
<Container
maxWidth={
PAGES_WITH_WIDE_CONTAINER.includes(pathname) ? "120ch" : "70ch"
}
>
<Component {...pageProps} />
</Container>
<Footer />
</Flex>
</>
);
};
export default Layout;