⚒️ Project structure.

This commit is contained in:
pheralb
2022-06-04 23:48:13 +01:00
parent be4eae31af
commit 427f4a2d30
41 changed files with 4732 additions and 302 deletions
+13
View File
@@ -0,0 +1,13 @@
import React from "react";
import { LayoutProps } from "@/interfaces/components";
import { SimpleGrid } from "@chakra-ui/react";
const Grid = (props: LayoutProps) => {
return (
<SimpleGrid minChildWidth="200px" spacing="30px" >
{props.children}
</SimpleGrid>
);
};
export default Grid;
+16
View File
@@ -0,0 +1,16 @@
import React from "react";
import { CustomIconBtnProps } from "@/interfaces/components";
import { IconButton } from "@chakra-ui/react";
const CustomIconBtn = (props: CustomIconBtnProps) => {
return (
<IconButton
variant="ghost"
aria-label={props.title}
icon={props.icon}
onClick={props.onClick}
/>
);
};
export default CustomIconBtn;
+16
View File
@@ -0,0 +1,16 @@
import React from "react";
import { Link } from "@chakra-ui/react";
import NextLink from "next/link";
import { CustomLinkProps } from "@/interfaces/components";
const CustomLink = ({ href, children, external }: CustomLinkProps) => {
return (
<NextLink href={href} passHref>
<Link isExternal={external} _hover={{ textDecoration: "none" }}>
{children}
</Link>
</NextLink>
);
};
export default CustomLink;
+16
View File
@@ -0,0 +1,16 @@
import React from "react";
import { SVGCardProps } from "@/interfaces/components";
import { Box, Flex, Image, useColorModeValue } from "@chakra-ui/react";
const SVGCard = (props: SVGCardProps) => {
const bg = useColorModeValue("bg.light", "bg.dark");
return (
<Box bg={bg} shadow="sm" maxW="50%" borderWidth="2px" borderRadius="10px" p="5">
<Flex direction="column" justifyContent="center" alignItems="center">
<Image boxSize="50px" src={props.svg} alt={props.title} />
</Flex>
</Box>
);
};
export default SVGCard;
+21
View File
@@ -0,0 +1,21 @@
export interface LayoutProps {
children: React.ReactNode;
}
export interface CustomLinkProps {
href: string;
children: React.ReactNode;
external?: boolean;
}
export interface CustomIconBtnProps {
title: string;
icon: React.ReactElement;
onClick?: () => void;
}
export interface SVGCardProps {
title: string;
svg: string;
url: string;
}
+7
View File
@@ -0,0 +1,7 @@
export interface SvgData {
id: number;
href: string;
title: string;
category: string;
url: string;
}
+90
View File
@@ -0,0 +1,90 @@
import React from "react";
import {
Box,
Flex,
useColorModeValue,
HStack,
Button,
useDisclosure,
VStack,
IconButton,
CloseButton,
Heading,
Container,
} from "@chakra-ui/react";
import { IoMenu } from "react-icons/io5";
import Items from "@/layout/header/items";
import VersionItem from "@/layout/header/version";
const Header = () => {
const bg = useColorModeValue("bg.light", "bg.dark");
const mobileNav = useDisclosure();
return (
<Box as="header" position="sticky" top="0" bg={bg} w="full" py={6} mb={10}>
<Container maxW="85%">
<Flex alignItems="center" justifyContent="space-between" mx="auto">
<HStack>
<Heading fontSize="18px">svgl</Heading>
</HStack>
<HStack display="flex" alignItems="center" spacing={1}>
<HStack
spacing={2}
mr={1}
display={{ base: "none", md: "inline-flex" }}
>
<Items />
</HStack>
<Box display={{ base: "inline-flex", md: "none" }}>
<IconButton
display={{ base: "flex", md: "none" }}
aria-label="Open menu header"
variant="ghost"
icon={<IoMenu size={32} />}
onClick={mobileNav.onOpen}
/>
<VStack
pos="absolute"
top={0}
left={0}
right={0}
display={mobileNav.isOpen ? "flex" : "none"}
flexDirection="column"
p={2}
pb={4}
m={2}
bg={bg}
spacing={3}
rounded="sm"
shadow="sm"
>
<CloseButton
aria-label="Close menu"
onClick={mobileNav.onClose}
/>
<Button w="full" variant="ghost">
Features
</Button>
<Button w="full" variant="ghost">
Pricing
</Button>
<Button w="full" variant="ghost">
Blog
</Button>
<Button w="full" variant="ghost">
Company
</Button>
<Button w="full" variant="ghost">
Sign in
</Button>
</VStack>
</Box>
</HStack>
</Flex>
</Container>
</Box>
);
};
export default Header;
+16
View File
@@ -0,0 +1,16 @@
import React from "react";
import VersionItem from "@/layout/header/version";
import ThemeItem from "@/layout/header/theme";
import Socials from "@/layout/header/socials";
const Items = () => {
return (
<>
<VersionItem />
<ThemeItem />
<Socials />
</>
);
};
export default Items;
+19
View File
@@ -0,0 +1,19 @@
import React from "react";
import CustomIconBtn from "@/common/iconBtn";
import CustomLink from "@/common/link";
import { IoLogoGithub, IoLogoTwitter } from "react-icons/io5";
const Socials = () => {
return (
<>
<CustomLink href="https://github.com/pheralb/svgl" external={true}>
<CustomIconBtn title="Github" icon={<IoLogoGithub size={20} />} />
</CustomLink>
<CustomLink href="https://twitter.com/pheralb_" external={true}>
<CustomIconBtn title="Twitter" icon={<IoLogoTwitter size={20} />} />
</CustomLink>
</>
);
};
export default Socials;
+34
View File
@@ -0,0 +1,34 @@
import React from "react";
import { AnimatePresence, motion } from "framer-motion";
import { useColorMode, useColorModeValue } from "@chakra-ui/react";
import CustomIconBtn from "@/common/iconBtn";
import { IoMoonOutline, IoSunnyOutline } from "react-icons/io5";
const Theme = () => {
const { toggleColorMode } = useColorMode();
const key = useColorModeValue("light", "dark");
const icon = useColorModeValue(
<IoMoonOutline size={20} />,
<IoSunnyOutline size={20} />
);
return (
<AnimatePresence exitBeforeEnter initial={false}>
<motion.div
style={{ display: "inline-block" }}
key={key}
initial={{ y: -20, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
exit={{ y: 20, opacity: 0 }}
transition={{ duration: 0.2 }}
>
<CustomIconBtn
title="Toggle theme"
icon={icon}
onClick={toggleColorMode}
/>
</motion.div>
</AnimatePresence>
);
};
export default Theme;
+51
View File
@@ -0,0 +1,51 @@
import React from "react";
import {
Menu,
Button,
MenuButton,
MenuList,
useDisclosure,
MenuItem,
Text,
useColorModeValue,
} from "@chakra-ui/react";
import useSWR from "swr";
import { IoCaretDownOutline } from "react-icons/io5";
import { githubVersionPackage } from "@/services";
import CustomLink from "@/common/link";
const VersionItem = () => {
const { data, error } = useSWR(githubVersionPackage);
const { isOpen, onOpen, onClose } = useDisclosure();
const bg = useColorModeValue("bg.light", "bg.dark");
if (error) return <Text>Error loading version</Text>;
if (!data) return <Text>loading...</Text>;
return (
<Menu isOpen={isOpen}>
<MenuButton
as={Button}
variant="ghost"
borderWidth="1px"
borderRadius={5}
aria-label="Version dropdown"
rightIcon={<IoCaretDownOutline size={14} />}
_focus={{
borderColor: "transparent",
}}
onMouseEnter={onOpen}
onMouseLeave={onClose}
>
{data?.tag_name}
</MenuButton>
<MenuList onMouseEnter={onOpen} onMouseLeave={onClose} bg={bg}>
<CustomLink href={data?.html_url} external={true}>
<MenuItem>Releases</MenuItem>
</CustomLink>
</MenuList>
</Menu>
);
};
export default VersionItem;
+15
View File
@@ -0,0 +1,15 @@
import { LayoutProps } from "@/interfaces/components";
import React from "react";
import Header from "@/layout/header";
import { Box } from "@chakra-ui/react";
const Index = ({ children }: LayoutProps) => {
return (
<>
<Header />
{children}
</>
);
};
export default Index;
+27
View File
@@ -0,0 +1,27 @@
import type { AppProps } from "next/app";
// Chakra UI & custom styles ->
import { ChakraProvider } from "@chakra-ui/react";
import theme from "@/theme";
import "@/styles/globals.css";
// Layout ->
import Layout from "@/layout";
// SWR Config & services ->
import { SWRConfig } from "swr";
import { fetcher } from "@/services/fetcher";
function MyApp({ Component, pageProps }: AppProps) {
return (
<ChakraProvider theme={theme}>
<SWRConfig value={{ fetcher }}>
<Layout>
<Component {...pageProps} />
</Layout>
</SWRConfig>
</ChakraProvider>
);
}
export default MyApp;
+18
View File
@@ -0,0 +1,18 @@
import { ColorModeScript } from "@chakra-ui/react";
import NextDocument, { Html, Head, Main, NextScript } from "next/document";
import theme from "@/theme";
export default class Document extends NextDocument {
render() {
return (
<Html lang="en">
<Head />
<body>
<ColorModeScript initialColorMode={theme.config.initialColorMode} />
<Main />
<NextScript />
</body>
</Html>
);
}
}
+10
View File
@@ -0,0 +1,10 @@
import type { NextApiRequest, NextApiResponse } from "next";
import db from "data/svgs";
import { SvgData } from "@/interfaces/svgData";
export default function handler(
req: NextApiRequest,
res: NextApiResponse<SvgData[]>
) {
res.status(200).json(db);
}
+36
View File
@@ -0,0 +1,36 @@
import type { NextPage } from "next";
import { Center, Container, Heading } from "@chakra-ui/react";
import useSWR from "swr";
import { getAllSvgs } from "@/services";
import { SvgData } from "@/interfaces/svgData";
import SVGCard from "@/components/svgCard";
import Grid from "@/common/grid";
const Home: NextPage = () => {
const { data, error } = useSWR(getAllSvgs);
if (error) return <div>failed to load</div>;
if (!data) return <div>loading...</div>;
return (
<>
<Center>
<Heading fontSize="5xl">Beautifully SVG logos</Heading>
</Center>
<Container maxW="85%">
<Grid>
{data.map((svg: SvgData) => (
<SVGCard
key={svg.id}
title={svg.title}
url={svg.href}
svg={svg.href}
/>
))}
</Grid>
</Container>
</>
);
};
export default Home;
+10
View File
@@ -0,0 +1,10 @@
export const fetcher = async (url: string) => {
const res = await fetch(url);
if (!res.ok) {
const error = new Error("An error occurred while fetching the data.");
throw error;
}
return res.json();
};
+2
View File
@@ -0,0 +1,2 @@
export const githubVersionPackage = 'https://api.github.com/repos/pheralb/svgl/releases/latest';
export const getAllSvgs = "/api/all";
+16
View File
@@ -0,0 +1,16 @@
/* Fonts -> */
@font-face {
font-family: "Inter-Regular";
src: url("/fonts/Inter-Regular.woff2") format("woff2");
font-style: normal;
font-weight: 400;
font-display: swap;
}
@font-face {
font-family: "Inter-Semibold";
src: url("/fonts/Inter-Semibold.woff2") format("woff2");
font-style: normal;
font-weight: 400;
font-display: swap;
}
+8
View File
@@ -0,0 +1,8 @@
const baseStyle = {
borderRadius: "md",
fontWeight: "light",
};
export default {
baseStyle,
};
+5
View File
@@ -0,0 +1,5 @@
import Button from "./button";
export default {
Button,
};
+46
View File
@@ -0,0 +1,46 @@
import { ChakraProps, extendTheme } from "@chakra-ui/react";
import { mode } from "@chakra-ui/theme-tools";
import components from "./components";
const theme = extendTheme(
{
components,
},
{
config: {
initialColorMode: "light",
useSystemColorMode: false,
},
colors: {
bg: {
light: "#f2f2f3",
dark: "#242424",
},
full: {
light: "#ffffff",
dark: "#000000",
}
},
fonts: {
body: "Inter-Regular, sans-serif",
heading: "Inter-Semibold, sans-serif",
},
styles: {
global: (props: ChakraProps) => ({
"html, body": {
height: "100%",
maxHeight: "100vh",
background: mode(
"radial-gradient(circle at 1px 1px, #E7E7E7 1px, #f2f2f3 0)",
"radial-gradient(circle at 1px 1px, #303030 1px, #242424 0)"
)(props),
backgroundSize: "40px 40px",
fontSize: "14px",
},
}),
borderColor: "red",
},
}
);
export default theme;