mirror of
https://github.com/pheralb/svgl.git
synced 2025-02-05 22:48:17 +08:00
⚒️ Preparate boilerplate v2 - Typescript + React 18.
This commit is contained in:
parent
48e648a04b
commit
b21f464108
17
.gitignore
vendored
17
.gitignore
vendored
@ -23,20 +23,13 @@
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
.pnpm-debug.log*
|
||||
|
||||
# local env files
|
||||
.env.local
|
||||
.env.development.local
|
||||
.env.test.local
|
||||
.env.production.local
|
||||
|
||||
# PWA files
|
||||
**/public/sw.js
|
||||
**/public/workbox-*.js
|
||||
**/public/worker-*.js
|
||||
**/public/sw.js.map
|
||||
**/public/workbox-*.js.map
|
||||
**/public/worker-*.js.map
|
||||
.env*.local
|
||||
|
||||
# vercel
|
||||
.vercel
|
||||
|
||||
# typescript
|
||||
*.tsbuildinfo
|
||||
|
@ -1,82 +0,0 @@
|
||||
import { motion } from "framer-motion";
|
||||
import React from "react";
|
||||
|
||||
const LoadingDot = {
|
||||
display: "block",
|
||||
width: "1rem",
|
||||
height: "1rem",
|
||||
backgroundColor: "#6748E6",
|
||||
borderRadius: "50%",
|
||||
};
|
||||
|
||||
const LoadingContainer = {
|
||||
width: "5rem",
|
||||
height: "5rem",
|
||||
display: "flex",
|
||||
justifyContent: "space-around",
|
||||
};
|
||||
|
||||
const ContainerVariants = {
|
||||
initial: {
|
||||
transition: {
|
||||
staggerChildren: 0.2,
|
||||
},
|
||||
},
|
||||
animate: {
|
||||
transition: {
|
||||
staggerChildren: 0.2,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
const DotVariants = {
|
||||
initial: {
|
||||
y: "0%",
|
||||
},
|
||||
animate: {
|
||||
y: "100%",
|
||||
},
|
||||
};
|
||||
|
||||
const DotTransition = {
|
||||
duration: 0.5,
|
||||
yoyo: Infinity,
|
||||
ease: "easeInOut",
|
||||
};
|
||||
|
||||
export default function Loader() {
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
paddingTop: "5rem",
|
||||
width: "100%",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
}}
|
||||
>
|
||||
<motion.div
|
||||
style={LoadingContainer}
|
||||
variants={ContainerVariants}
|
||||
initial="initial"
|
||||
animate="animate"
|
||||
>
|
||||
<motion.span
|
||||
style={LoadingDot}
|
||||
variants={DotVariants}
|
||||
transition={DotTransition}
|
||||
/>
|
||||
<motion.span
|
||||
style={LoadingDot}
|
||||
variants={DotVariants}
|
||||
transition={DotTransition}
|
||||
/>
|
||||
<motion.span
|
||||
style={LoadingDot}
|
||||
variants={DotVariants}
|
||||
transition={DotTransition}
|
||||
/>
|
||||
</motion.div>
|
||||
</div>
|
||||
);
|
||||
}
|
@ -1,19 +0,0 @@
|
||||
import React, { FC } from "react";
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
const Show = ({ children, delay }) => {
|
||||
return (
|
||||
<motion.div
|
||||
initial={{ y: 10, opacity: 0 }}
|
||||
animate={{ y: 0, opacity: 1 }}
|
||||
transition={{
|
||||
duration: 0.4,
|
||||
delay: delay,
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</motion.div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Show;
|
@ -1,12 +0,0 @@
|
||||
import React from "react";
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
const Tap = ({ children }) => {
|
||||
return (
|
||||
<motion.div whileHover={{ scale: 1.040 }} whileTap={{ scale: 0.98 }}>
|
||||
{children}
|
||||
</motion.div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Tap;
|
@ -1,23 +0,0 @@
|
||||
import React from "react";
|
||||
import { motion } from "framer-motion";
|
||||
|
||||
const Transitions = ({ children }) => {
|
||||
return (
|
||||
<motion.div
|
||||
initial="initial"
|
||||
animate="animate"
|
||||
variants={{
|
||||
initial: {
|
||||
opacity: 0,
|
||||
},
|
||||
animate: {
|
||||
opacity: 1,
|
||||
},
|
||||
}}
|
||||
>
|
||||
{children}
|
||||
</motion.div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Transitions;
|
@ -1,26 +0,0 @@
|
||||
import { Box, VStack, Icon, Text, Divider } from "@chakra-ui/react";
|
||||
import React from "react";
|
||||
|
||||
const Design = ({ icon, title, children }) => {
|
||||
return (
|
||||
<Box
|
||||
borderWidth="1px"
|
||||
borderRadius="lg"
|
||||
overflow="hidden"
|
||||
_hover={{ shadow: "md", transition: "all .2s" }}
|
||||
>
|
||||
<Box p="6">
|
||||
<VStack spacing={3}>
|
||||
<Icon as={icon} w={16} h={16} />
|
||||
<Text fontSize="3xl" fontWeight="semibold">
|
||||
{title}
|
||||
</Text>
|
||||
<Divider />
|
||||
<Box fontSize="2xl">{children}</Box>
|
||||
</VStack>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Design;
|
@ -1,23 +0,0 @@
|
||||
import { Box, Image } from "@chakra-ui/react";
|
||||
import React from "react";
|
||||
|
||||
const CardImage = ({title, image, children}) => {
|
||||
return (
|
||||
<Box maxW="sm" borderWidth="1px" borderRadius="lg" overflow="hidden">
|
||||
<Image src={image} alt={title} width="60" />
|
||||
<Box p="6">
|
||||
<Box
|
||||
mt="1"
|
||||
as="h4"
|
||||
lineHeight="tight"
|
||||
isTruncated
|
||||
>
|
||||
{title}
|
||||
</Box>
|
||||
{children}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default CardImage;
|
@ -1,87 +0,0 @@
|
||||
import React from "react";
|
||||
import Link from "next/link";
|
||||
import {
|
||||
Box,
|
||||
Text,
|
||||
Image,
|
||||
Center,
|
||||
HStack,
|
||||
IconButton,
|
||||
useColorModeValue,
|
||||
} from "@chakra-ui/react";
|
||||
import { IoCloudDownloadOutline } from "react-icons/io5";
|
||||
import { FiExternalLink } from "react-icons/fi";
|
||||
import download from "downloadjs";
|
||||
import toast from "react-hot-toast";
|
||||
import Tap from "animations/tap";
|
||||
|
||||
const Index = ({ title, url, href }) => {
|
||||
const toastBg = useColorModeValue("#F2F2F2", "#1D1D1D");
|
||||
const toastColor = useColorModeValue("black", "white");
|
||||
const bgImage = useColorModeValue("transparent", "#E9E9E9");
|
||||
const borderRds = useColorModeValue("0", "15px");
|
||||
|
||||
const downloadSvg = (url) => {
|
||||
toast(`Downloading ${title}...`, {
|
||||
icon: "🥳",
|
||||
style: {
|
||||
borderRadius: "10px",
|
||||
background: toastBg,
|
||||
color: toastColor,
|
||||
},
|
||||
});
|
||||
download(url);
|
||||
};
|
||||
|
||||
return (
|
||||
<Box
|
||||
p={4}
|
||||
borderRadius="10px"
|
||||
borderWidth="1px"
|
||||
mb="2"
|
||||
_hover={{
|
||||
shadow: "md",
|
||||
}}
|
||||
transition="all 0.2s"
|
||||
>
|
||||
<Center>
|
||||
<Image
|
||||
src={href}
|
||||
alt={title}
|
||||
boxSize="45px"
|
||||
bg={bgImage}
|
||||
borderRadius={borderRds}
|
||||
p="1"
|
||||
/>
|
||||
</Center>
|
||||
<Text mt="2" fontWeight="light" textAlign="center">
|
||||
{title}
|
||||
</Text>
|
||||
<Center>
|
||||
<HStack spacing="1" mt="1">
|
||||
<Tap>
|
||||
<IconButton
|
||||
as="button"
|
||||
variant="ghost"
|
||||
aria-label="Download SVG"
|
||||
icon={<IoCloudDownloadOutline size="16" />}
|
||||
onClick={() => downloadSvg(href)}
|
||||
/>
|
||||
</Tap>
|
||||
<Tap>
|
||||
<Link href={url} passHref>
|
||||
<IconButton
|
||||
as="a"
|
||||
variant="ghost"
|
||||
aria-label="Go to Vue SVG page"
|
||||
icon={<FiExternalLink size="16" />}
|
||||
/>
|
||||
</Link>
|
||||
</Tap>
|
||||
</HStack>
|
||||
</Center>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Index;
|
@ -1,38 +0,0 @@
|
||||
import React from "react";
|
||||
import { Box, Flex, Button, Container, Text, Icon } from "@chakra-ui/react";
|
||||
import { IoHome, IoShapesOutline } from "react-icons/io5";
|
||||
import Link from "next/link";
|
||||
import Show from "animations/show";
|
||||
|
||||
const Error = () => {
|
||||
return (
|
||||
<>
|
||||
<Show delay="0">
|
||||
<Box px={{ base: 4, lg: 20 }} py={{ base: "3", md: "24" }}>
|
||||
<Flex align="center" justify="center" direction="column" w="full">
|
||||
<Icon name="error" boxSize="80px" mb="3" as={IoShapesOutline} />
|
||||
<Text fontSize="40px" mb="2">
|
||||
Oh no!
|
||||
</Text>
|
||||
<Text fontSize="20px" mb="3">
|
||||
This page does not exist.
|
||||
</Text>
|
||||
<Link href="/" passHref>
|
||||
<Button
|
||||
leftIcon={<IoHome />}
|
||||
borderWidth="1px"
|
||||
variant="outline"
|
||||
fontWeight="light"
|
||||
mb="4"
|
||||
>
|
||||
Go home
|
||||
</Button>
|
||||
</Link>
|
||||
</Flex>
|
||||
</Box>
|
||||
</Show>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Error;
|
@ -1,12 +0,0 @@
|
||||
import React from 'react'
|
||||
import { SimpleGrid } from '@chakra-ui/react'
|
||||
|
||||
const Index = ({children}) => {
|
||||
return (
|
||||
<SimpleGrid minChildWidth='200px' columns={3} spacing={5}>
|
||||
{children}
|
||||
</SimpleGrid>
|
||||
)
|
||||
}
|
||||
|
||||
export default Index
|
@ -1,32 +0,0 @@
|
||||
import React from "react";
|
||||
import useSWR from "swr";
|
||||
import Grid from "components/grid";
|
||||
import Card from "components/card";
|
||||
import Loader from "animations/loader";
|
||||
|
||||
const fetcher = (url) => fetch(url).then((res) => res.json());
|
||||
|
||||
const All = () => {
|
||||
const { data, error } = useSWR("/api/all", fetcher);
|
||||
if (error) return <div>failed to load</div>;
|
||||
if (!data) return <Loader />;
|
||||
return (
|
||||
<>
|
||||
<Grid>
|
||||
{data.map((link) => (
|
||||
<>
|
||||
<div key={link}>
|
||||
<Card
|
||||
title={link.title}
|
||||
url={`/svg/${link.id}`}
|
||||
href={link.href}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
))}
|
||||
</Grid>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default All;
|
@ -1,32 +0,0 @@
|
||||
import React from "react";
|
||||
import useSWR from "swr";
|
||||
import Grid from "components/grid";
|
||||
import Library from "components/card/library";
|
||||
import Loader from "animations/loader";
|
||||
|
||||
const fetcher = (url) => fetch(url).then((res) => res.json());
|
||||
|
||||
const Libraries = () => {
|
||||
const { data, error } = useSWR("/api/icons", fetcher);
|
||||
if (error) return <div>failed to load</div>;
|
||||
if (!data) return <Loader />;
|
||||
return (
|
||||
<>
|
||||
<Grid>
|
||||
{data.map((link) => (
|
||||
<>
|
||||
<div key={link}>
|
||||
<Library
|
||||
image={link.image}
|
||||
title={link.title}
|
||||
url={link.url}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
))}
|
||||
</Grid>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Libraries;
|
@ -1,12 +0,0 @@
|
||||
import React from "react";
|
||||
import { Box } from "@chakra-ui/react";
|
||||
|
||||
const Index = ({ children }) => {
|
||||
return (
|
||||
<Box as="main" px={{ base: 6, md: 16 }} pl={{ base: 6, md: 16 }}>
|
||||
{children}
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Index;
|
@ -1,133 +0,0 @@
|
||||
import { useMemo, useRef, useState } from "react";
|
||||
import { createAutocomplete } from "@algolia/autocomplete-core";
|
||||
import {
|
||||
Box,
|
||||
Input,
|
||||
InputLeftElement,
|
||||
InputGroup,
|
||||
Flex,
|
||||
HStack,
|
||||
Text,
|
||||
Image,
|
||||
Icon,
|
||||
Link,
|
||||
} from "@chakra-ui/react";
|
||||
import { IoSearch } from "react-icons/io5";
|
||||
import { FiExternalLink } from "react-icons/fi";
|
||||
import { Algolia } from "components/svg";
|
||||
import NextLink from "next/link";
|
||||
|
||||
const AutocompleteItem = ({ id, title, href, url }) => {
|
||||
return (
|
||||
<>
|
||||
<NextLink href={`/svg/${id}`} passHref>
|
||||
<Link
|
||||
href={`/svg/${id}`}
|
||||
style={{ textDecoration: "none" }}
|
||||
_focus={{ outline: "0" }}
|
||||
>
|
||||
<Box
|
||||
id={id}
|
||||
w="100%"
|
||||
borderWidth="1px"
|
||||
borderRadius="6px"
|
||||
mt="3"
|
||||
cursor="pointer"
|
||||
_hover={{ shadow: "md" }}
|
||||
transition="all 0.2s"
|
||||
>
|
||||
<HStack py={6} px={6} spacing={2}>
|
||||
<Image src={href} alt={title} boxSize="20px" mr="2" />
|
||||
<Text fontSize="18px" fontWeight="light">
|
||||
{title}
|
||||
</Text>
|
||||
<Icon as={FiExternalLink} />
|
||||
</HStack>
|
||||
</Box>
|
||||
</Link>
|
||||
</NextLink>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default function Search(props) {
|
||||
const [autocompleteState, setAutocompleteState] = useState({
|
||||
collections: [],
|
||||
});
|
||||
|
||||
const autocomplete = useMemo(
|
||||
() =>
|
||||
createAutocomplete({
|
||||
placeholder: "Search svgs...",
|
||||
onStateChange: ({ state }) => setAutocompleteState(state),
|
||||
getSources: () => [
|
||||
{
|
||||
sourceId: "svgs-next-api",
|
||||
getItems: ({ query }) => {
|
||||
if (!!query) {
|
||||
return fetch(`/api/search?q=${query}`).then((res) =>
|
||||
res.json()
|
||||
);
|
||||
}
|
||||
},
|
||||
},
|
||||
],
|
||||
...props,
|
||||
}),
|
||||
[props]
|
||||
);
|
||||
|
||||
const formRef = useRef(null);
|
||||
const inputRef = useRef(null);
|
||||
|
||||
const formProps = autocomplete.getFormProps({
|
||||
inputElement: inputRef.current,
|
||||
});
|
||||
const inputProps = autocomplete.getInputProps({
|
||||
inputElement: inputRef.current,
|
||||
});
|
||||
|
||||
return (
|
||||
<form ref={formRef} {...formProps}>
|
||||
<Flex>
|
||||
<InputGroup w="full">
|
||||
<InputLeftElement pointerEvents="none" mt="1">
|
||||
<IoSearch size="20" />
|
||||
</InputLeftElement>
|
||||
<Input
|
||||
w="100%"
|
||||
shadow="none"
|
||||
size="lg"
|
||||
type="tel"
|
||||
placeholder="Search icons..."
|
||||
_focus={{ shadow: "md" }}
|
||||
ref={inputRef}
|
||||
autoFocus
|
||||
{...inputProps}
|
||||
/>
|
||||
</InputGroup>
|
||||
<Box mt="4" ml="3" mr="2" cursor="pointer">
|
||||
<Link href="https://www.algolia.com/" passHref>
|
||||
<Algolia width="70px" />
|
||||
</Link>
|
||||
</Box>
|
||||
</Flex>
|
||||
<>
|
||||
{autocompleteState.collections.map((collection, index) => {
|
||||
const { items } = collection;
|
||||
return (
|
||||
<div key={`${index}`}>
|
||||
{items.length > 0 && (
|
||||
<ul {...autocomplete.getListProps()}>
|
||||
{items.map((item) => (
|
||||
<AutocompleteItem key={item.id} {...item} />
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
</form>
|
||||
);
|
||||
}
|
@ -1,41 +0,0 @@
|
||||
import React from "react";
|
||||
import {
|
||||
Modal,
|
||||
ModalOverlay,
|
||||
ModalContent,
|
||||
ModalHeader,
|
||||
ModalFooter,
|
||||
ModalBody,
|
||||
ModalCloseButton,
|
||||
useDisclosure,
|
||||
IconButton,
|
||||
Button,
|
||||
useColorModeValue,
|
||||
} from "@chakra-ui/react";
|
||||
import Search from "components/search";
|
||||
import { IoSearchOutline } from "react-icons/io5";
|
||||
import Item from "components/sidebar/item";
|
||||
|
||||
const ModalSearch = (props) => {
|
||||
const { isOpen, onOpen, onClose } = useDisclosure();
|
||||
const bg = useColorModeValue("light.100", "dark.800");
|
||||
return (
|
||||
<>
|
||||
<Item icon={IoSearchOutline} onClick={onOpen}>
|
||||
Search
|
||||
</Item>
|
||||
<Modal isOpen={isOpen} onClose={onClose} motionPreset="slideInBottom">
|
||||
<ModalOverlay />
|
||||
<ModalContent bg={bg}>
|
||||
<ModalHeader fontWeight="light">Search</ModalHeader>
|
||||
<ModalCloseButton />
|
||||
<ModalBody pb="5">
|
||||
<Search />
|
||||
</ModalBody>
|
||||
</ModalContent>
|
||||
</Modal>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ModalSearch;
|
@ -1,25 +0,0 @@
|
||||
import React from "react";
|
||||
import {
|
||||
Icon,
|
||||
Link,
|
||||
Center,
|
||||
useColorModeValue,
|
||||
HStack,
|
||||
} from "@chakra-ui/react";
|
||||
import { IoRocketOutline } from "react-icons/io5";
|
||||
|
||||
const Index = () => {
|
||||
const color = useColorModeValue("gray.400", "gray.600");
|
||||
return (
|
||||
<>
|
||||
<HStack color={color} ml="6" spacing="3">
|
||||
<Icon boxSize="6" as={IoRocketOutline} />
|
||||
<Link href="https://github.com/pheralb" isExternal="true">
|
||||
Built by Pablo
|
||||
</Link>
|
||||
</HStack>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Index;
|
@ -1,22 +0,0 @@
|
||||
import React from "react";
|
||||
import { useColorMode, useColorModeValue } from "@chakra-ui/react";
|
||||
import { IoMoonOutline, IoSunnyOutline } from "react-icons/io5";
|
||||
import Item from "./item";
|
||||
|
||||
const Index = () => {
|
||||
const { colorMode, toggleColorMode } = useColorMode();
|
||||
const iconChange = useColorModeValue(IoSunnyOutline, IoMoonOutline);
|
||||
const theme = useColorModeValue("Light", "Dark");
|
||||
|
||||
function toggleTheme() {
|
||||
toggleColorMode();
|
||||
}
|
||||
|
||||
return (
|
||||
<Item icon={iconChange} onClick={toggleTheme}>
|
||||
{theme}
|
||||
</Item>
|
||||
);
|
||||
};
|
||||
|
||||
export default Index;
|
@ -1,115 +0,0 @@
|
||||
import React from "react";
|
||||
import {
|
||||
Box,
|
||||
Drawer,
|
||||
DrawerContent,
|
||||
DrawerOverlay,
|
||||
DrawerCloseButton,
|
||||
DrawerHeader,
|
||||
DrawerBody,
|
||||
Flex,
|
||||
IconButton,
|
||||
useColorModeValue,
|
||||
useDisclosure,
|
||||
Link,
|
||||
} from "@chakra-ui/react";
|
||||
import NextLink from "next/link";
|
||||
import { IoApps } from "react-icons/io5";
|
||||
|
||||
import Logo from "components/sidebar/logo";
|
||||
import Item from "components/sidebar/item";
|
||||
import SidebarLinks from "components/sidebar/links";
|
||||
import Dark from "components/sidebar/dark";
|
||||
import By from "components/sidebar/by";
|
||||
import ModalSearch from "components/search/modal";
|
||||
|
||||
export default function Index({ children }) {
|
||||
const sidebar = useDisclosure();
|
||||
const border = useColorModeValue("gray.200", "dark.800");
|
||||
const bg = useColorModeValue("gray.100", "lightDark.900");
|
||||
|
||||
const SidebarContent = (props) => (
|
||||
<Box
|
||||
as="nav"
|
||||
pos="fixed"
|
||||
top="0"
|
||||
left="0"
|
||||
zIndex="sticky"
|
||||
h="full"
|
||||
pb="10"
|
||||
overflowX="hidden"
|
||||
overflowY="auto"
|
||||
borderColor={border}
|
||||
borderRightWidth="1px"
|
||||
shadow="sm"
|
||||
w="56"
|
||||
{...props}
|
||||
>
|
||||
<Box px="5" pt="8" pb="5" align="center">
|
||||
<Logo />
|
||||
</Box>
|
||||
<Flex direction="column" as="nav" aria-label="Main Navigation">
|
||||
{SidebarLinks.map((link) => (
|
||||
<NextLink key={link.id} href={link.href} passHref>
|
||||
<Link
|
||||
href={link.href}
|
||||
isExternal={link.external}
|
||||
style={{ textDecoration: "none" }}
|
||||
>
|
||||
<Item icon={link.icon} href={link.href} external={link.external}>
|
||||
{link.title}
|
||||
</Item>
|
||||
</Link>
|
||||
</NextLink>
|
||||
))}
|
||||
<ModalSearch />
|
||||
<Dark />
|
||||
</Flex>
|
||||
<Box mt="8" align="center">
|
||||
<By />
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
|
||||
return (
|
||||
<Box as="section" minH="100vh">
|
||||
<SidebarContent display={{ base: "none", md: "unset" }} />
|
||||
<Drawer
|
||||
isOpen={sidebar.isOpen}
|
||||
onClose={sidebar.onClose}
|
||||
placement="left"
|
||||
>
|
||||
<DrawerOverlay />
|
||||
<DrawerContent bg={bg}>
|
||||
<DrawerCloseButton borderWidth="1px" />
|
||||
<DrawerBody>
|
||||
<SidebarContent pt="6" borderRight="none" />
|
||||
</DrawerBody>
|
||||
</DrawerContent>
|
||||
</Drawer>
|
||||
<Box ml={{ base: 0, md: 56 }} transition=".3s ease">
|
||||
<Box
|
||||
as="header"
|
||||
align="center"
|
||||
justify="space-between"
|
||||
w="full"
|
||||
p="5"
|
||||
display={{ base: "inline-flex", md: "none" }}
|
||||
>
|
||||
<IconButton
|
||||
aria-label="Menu"
|
||||
onClick={sidebar.onOpen}
|
||||
icon={<IoApps />}
|
||||
size="md"
|
||||
w="100%"
|
||||
variant="ghost"
|
||||
borderWidth="1px"
|
||||
/>
|
||||
</Box>
|
||||
<Box as="main" pt={{ base: "0", md: "6" }} pb={{ base: "0", md: "6" }}>
|
||||
{children}
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
@ -1,35 +0,0 @@
|
||||
import React from "react";
|
||||
import { useRouter } from 'next/router';
|
||||
import { Flex, Icon, useColorModeValue } from "@chakra-ui/react";
|
||||
import { FiExternalLink } from "react-icons/fi";
|
||||
import Tap from "animations/tap";
|
||||
|
||||
const Item = (props) => {
|
||||
|
||||
const { icon, external, children, href, ...rest } = props;
|
||||
const { pathname } = useRouter();
|
||||
const isActive = pathname === href;
|
||||
const borderColor = useColorModeValue("dark.800", "white");
|
||||
|
||||
return (
|
||||
<Tap>
|
||||
<Flex
|
||||
align="center"
|
||||
px="5"
|
||||
pl="4"
|
||||
py="4"
|
||||
cursor="pointer"
|
||||
transition=".15s ease"
|
||||
borderColor={borderColor}
|
||||
borderLeftWidth={isActive ? "2px" : ''}
|
||||
{...rest}
|
||||
>
|
||||
{icon && <Icon ml="2" mr="4" boxSize="6" as={icon} />}
|
||||
{children}
|
||||
{external && <Icon ml="3" mr="4" boxSize="4" as={FiExternalLink} />}
|
||||
</Flex>
|
||||
</Tap>
|
||||
);
|
||||
};
|
||||
|
||||
export default Item;
|
@ -1,28 +0,0 @@
|
||||
import { IoAppsOutline, IoLogoGithub, IoBookOutline } from "react-icons/io5";
|
||||
import { FiTwitter } from "react-icons/fi";
|
||||
|
||||
const SidebarLinks = [
|
||||
{
|
||||
id: 1,
|
||||
href: "/",
|
||||
external: false,
|
||||
title: "Browse",
|
||||
icon: IoAppsOutline,
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
href: "https://github.com/pheralb/svgl/",
|
||||
external: true,
|
||||
title: "Github",
|
||||
icon: IoLogoGithub,
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
href: "https://twitter.com/pheralb_",
|
||||
external: true,
|
||||
title: "Twitter",
|
||||
icon: FiTwitter,
|
||||
},
|
||||
];
|
||||
|
||||
export default SidebarLinks;
|
@ -1,30 +0,0 @@
|
||||
import React from "react";
|
||||
import Link from "next/link";
|
||||
import { HStack, Icon, Text } from "@chakra-ui/react";
|
||||
import { svgl } from "components/svg";
|
||||
import Tap from "animations/tap";
|
||||
|
||||
const Logo = () => {
|
||||
return (
|
||||
<Tap>
|
||||
<Link href="/" passHref>
|
||||
<HStack cursor="pointer">
|
||||
<Icon
|
||||
as={svgl}
|
||||
name="logo"
|
||||
boxSize="30px"
|
||||
mr="2"
|
||||
ml="1"
|
||||
borderRadius="full"
|
||||
bg="transparent"
|
||||
/>
|
||||
<Text fontSize="2xl" ml="2">
|
||||
svgl
|
||||
</Text>
|
||||
</HStack>
|
||||
</Link>
|
||||
</Tap>
|
||||
);
|
||||
};
|
||||
|
||||
export default Logo;
|
File diff suppressed because one or more lines are too long
@ -1,5 +0,0 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"baseUrl": "./"
|
||||
}
|
||||
}
|
5
next-env.d.ts
vendored
Normal file
5
next-env.d.ts
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
/// <reference types="next" />
|
||||
/// <reference types="next/image-types/global" />
|
||||
|
||||
// NOTE: This file should not be edited
|
||||
// see https://nextjs.org/docs/basic-features/typescript for more information.
|
@ -1,15 +1,6 @@
|
||||
const withPWA = require("next-pwa");
|
||||
|
||||
/** @type {import('next').NextConfig} */
|
||||
const nextConfig = {
|
||||
reactStrictMode: true,
|
||||
};
|
||||
}
|
||||
|
||||
module.exports = withPWA({
|
||||
pwa: {
|
||||
dest: "public",
|
||||
register: true,
|
||||
skipWaiting: true,
|
||||
disable: process.env.NODE_ENV === "development",
|
||||
},
|
||||
nextConfig,
|
||||
});
|
||||
module.exports = nextConfig
|
||||
|
10896
package-lock.json
generated
10896
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
34
package.json
34
package.json
@ -1,9 +1,7 @@
|
||||
{
|
||||
"name": "svgl",
|
||||
"version": "1.2.0",
|
||||
"description": "Beautiful SVG vector logos",
|
||||
"author": "pheralb",
|
||||
"license": "MIT",
|
||||
"name": "svgl-new",
|
||||
"version": "0.1.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"dev": "next dev",
|
||||
"build": "next build",
|
||||
@ -11,24 +9,16 @@
|
||||
"lint": "next lint"
|
||||
},
|
||||
"dependencies": {
|
||||
"@algolia/autocomplete-core": "^1.5.6",
|
||||
"@chakra-ui/react": "^1.8.7",
|
||||
"@emotion/react": "^11.8.2",
|
||||
"@emotion/styled": "^11.8.1",
|
||||
"canvas-confetti": "^1.5.1",
|
||||
"downloadjs": "^1.4.7",
|
||||
"framer-motion": "^6.2.8",
|
||||
"next": "12.1.0",
|
||||
"next-pwa": "^5.5.0",
|
||||
"nextjs-progressbar": "^0.0.14",
|
||||
"react": "17.0.2",
|
||||
"react-dom": "17.0.2",
|
||||
"react-hot-toast": "^2.2.0",
|
||||
"react-icons": "^4.3.1",
|
||||
"swr": "^1.2.2"
|
||||
"next": "12.1.6",
|
||||
"react": "18.1.0",
|
||||
"react-dom": "18.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"eslint": "8.10.0",
|
||||
"eslint-config-next": "12.1.0"
|
||||
"@types/node": "17.0.38",
|
||||
"@types/react": "18.0.10",
|
||||
"@types/react-dom": "18.0.5",
|
||||
"eslint": "8.16.0",
|
||||
"eslint-config-next": "12.1.6",
|
||||
"typescript": "4.7.2"
|
||||
}
|
||||
}
|
||||
|
@ -1,8 +0,0 @@
|
||||
import Error from 'components/error';
|
||||
import React from 'react';
|
||||
|
||||
const Error404 = () => {
|
||||
return <Error />;
|
||||
};
|
||||
|
||||
export default Error404;
|
@ -1,78 +0,0 @@
|
||||
// 🖤 Next Head ->
|
||||
import Head from "next/head";
|
||||
|
||||
// 🌿 Chakra UI ->
|
||||
import { ChakraProvider, Container, useColorModeValue } from "@chakra-ui/react";
|
||||
|
||||
// ➡️ Nextjs Progressbar ->
|
||||
import NextNProgress from "nextjs-progressbar";
|
||||
|
||||
// 📦 Components ->
|
||||
import Sidebar from "components/sidebar";
|
||||
import Layout from "components/layout";
|
||||
import Footer from "components/sidebar/by";
|
||||
|
||||
// 💙 Global CSS ->
|
||||
import "styles/globals.css";
|
||||
|
||||
// 🎨 Theme ->
|
||||
import theme from "styles/theme";
|
||||
|
||||
// 🐢 Animations ->
|
||||
import Transitions from "animations/transitions";
|
||||
import { Toaster } from "react-hot-toast";
|
||||
|
||||
function MyApp({ Component, pageProps, router }) {
|
||||
const progress = useColorModeValue("#7B7B7B", "#D4D4D4");
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1" />
|
||||
<title>SVGL - Beautiful SVG vector logos</title>
|
||||
<meta property="og:title" content="SVGL - Beautiful SVG vector logos" />
|
||||
<meta
|
||||
property="og:description"
|
||||
content="Beautiful SVG logos. Free and open source."
|
||||
/>
|
||||
<meta property="og:type" content="website" />
|
||||
<meta property="og:url" content="https://svgl.vercel.app/" />
|
||||
<meta
|
||||
property="og:image"
|
||||
content="https://svgl.vercel.app/images/banner.png"
|
||||
/>
|
||||
<meta name="twitter:site" content="@pheralb_" />
|
||||
<meta
|
||||
property="twitter:title"
|
||||
content="SVGL - Beautiful SVG vector logos"
|
||||
/>
|
||||
<meta property="twitter:card" content="summary_large_image" />
|
||||
<meta property="twitter:creator" content="@pheralb" />
|
||||
<meta
|
||||
property="twitter:description"
|
||||
content="Beautiful SVG logos. Free and open source."
|
||||
/>
|
||||
<meta
|
||||
name="twitter:image"
|
||||
content="https://svgl.vercel.app/images/banner.png"
|
||||
/>
|
||||
|
||||
<meta name="keywords" content="svg,vector,logo,logos,download" />
|
||||
<meta content="#16161a" name="theme-color" />
|
||||
<link rel="icon" href="/icons/icon.ico" />
|
||||
</Head>
|
||||
<ChakraProvider theme={theme}>
|
||||
<Sidebar>
|
||||
<NextNProgress color={progress}/>
|
||||
<Layout>
|
||||
<Transitions key={router.route}>
|
||||
<Component {...pageProps} />
|
||||
</Transitions>
|
||||
</Layout>
|
||||
</Sidebar>
|
||||
</ChakraProvider>
|
||||
<Toaster position="bottom-center" reverseOrder={false} />
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
export default MyApp;
|
8
pages/_app.tsx
Normal file
8
pages/_app.tsx
Normal file
@ -0,0 +1,8 @@
|
||||
import '../styles/globals.css'
|
||||
import type { AppProps } from 'next/app'
|
||||
|
||||
function MyApp({ Component, pageProps }: AppProps) {
|
||||
return <Component {...pageProps} />
|
||||
}
|
||||
|
||||
export default MyApp
|
@ -1,22 +0,0 @@
|
||||
import { ColorModeScript } from "@chakra-ui/react";
|
||||
import NextDocument, { Html, Head, Main, NextScript } from "next/document";
|
||||
import theme from "styles/theme";
|
||||
|
||||
export default class Document extends NextDocument {
|
||||
render() {
|
||||
return (
|
||||
<Html>
|
||||
<Head>
|
||||
<link rel="manifest" href="/manifest.json" />
|
||||
<link rel="apple-touch-icon" href="/icons/icon-512x512.png"></link>
|
||||
<meta name="theme-color" content="#36558F" />
|
||||
</Head>
|
||||
<body>
|
||||
<ColorModeScript initialColorMode={theme.config.initialColorMode} />
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
);
|
||||
}
|
||||
}
|
@ -1,38 +0,0 @@
|
||||
import React from "react";
|
||||
import { Box, Flex, Button, Text, Icon } from "@chakra-ui/react";
|
||||
import { IoHome, IoWarning } from "react-icons/io5";
|
||||
import Link from "next/link";
|
||||
import Show from "animations/show";
|
||||
|
||||
const Offline = () => {
|
||||
return (
|
||||
<>
|
||||
<Show delay="0">
|
||||
<Box px={{ base: 4, lg: 20 }} py={{ base: "3", md: "24" }}>
|
||||
<Flex align="center" justify="center" direction="column" w="full">
|
||||
<Icon name="error" boxSize="80px" mb="3" as={IoWarning} />
|
||||
<Text fontSize="40px" mb="2">
|
||||
Oh no!
|
||||
</Text>
|
||||
<Text fontSize="20px" mb="3">
|
||||
No internet connection
|
||||
</Text>
|
||||
<Link href="/" passHref>
|
||||
<Button
|
||||
leftIcon={<IoHome />}
|
||||
borderWidth="1px"
|
||||
variant="outline"
|
||||
fontWeight="light"
|
||||
mb="4"
|
||||
>
|
||||
Refresh
|
||||
</Button>
|
||||
</Link>
|
||||
</Flex>
|
||||
</Box>
|
||||
</Show>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Offline;
|
@ -1,6 +0,0 @@
|
||||
import db from "data/svgs";
|
||||
|
||||
// 📦 Show all content ->
|
||||
export default function handler(req, res) {
|
||||
res.status(200).json(db);
|
||||
}
|
@ -1,13 +0,0 @@
|
||||
import db from "data/svgs";
|
||||
|
||||
// 📦 Show categories ->
|
||||
export default function handler(req, res) {
|
||||
try {
|
||||
const categories = db
|
||||
.map((item) => item.category)
|
||||
.filter((category, index, self) => self.indexOf(category) === index);
|
||||
return res.status(200).json(categories);
|
||||
} catch (err) {
|
||||
res.status(400).json({ message: err });
|
||||
}
|
||||
}
|
13
pages/api/hello.ts
Normal file
13
pages/api/hello.ts
Normal file
@ -0,0 +1,13 @@
|
||||
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
|
||||
import type { NextApiRequest, NextApiResponse } from 'next'
|
||||
|
||||
type Data = {
|
||||
name: string
|
||||
}
|
||||
|
||||
export default function handler(
|
||||
req: NextApiRequest,
|
||||
res: NextApiResponse<Data>
|
||||
) {
|
||||
res.status(200).json({ name: 'John Doe' })
|
||||
}
|
@ -1,32 +0,0 @@
|
||||
import db from "data/svgs";
|
||||
|
||||
export default function handler(req, res) {
|
||||
const { id, q, c } = req.query;
|
||||
|
||||
// 🔎 Search by id (ex: ?id=1) ->
|
||||
if (id) {
|
||||
const item = db.find((item) => item.id === +id);
|
||||
return res.status(200).json(item);
|
||||
}
|
||||
|
||||
// 🔎 Search by query (ex: ?q=d) ->
|
||||
if (q) {
|
||||
const results = db.filter((product) => {
|
||||
const { title } = product;
|
||||
return title.toLowerCase().includes(q.toLowerCase());
|
||||
});
|
||||
return res.status(200).json(results);
|
||||
}
|
||||
|
||||
// 🔎 Search by category (ex: ?c=library) ->
|
||||
if (c) {
|
||||
const results = db.filter((product) => {
|
||||
const { category } = product;
|
||||
return category.toLowerCase().includes(c.toLowerCase());
|
||||
});
|
||||
return res.status(200).json(results);
|
||||
}
|
||||
|
||||
// ✖ Error ->
|
||||
res.status(400).json({ info: 'Error: api query not found.' });
|
||||
}
|
116
pages/design.js
116
pages/design.js
@ -1,116 +0,0 @@
|
||||
import React from "react";
|
||||
import Head from "next/head";
|
||||
import download from "downloadjs";
|
||||
import { Box, Button, Flex, HStack, Link, Text } from "@chakra-ui/react";
|
||||
import Show from "animations/show";
|
||||
import Grid from "components/grid";
|
||||
import DesignCard from "components/card/design";
|
||||
import { RiFontSize, RiPaletteLine } from "react-icons/ri";
|
||||
import { IoMdImages } from "react-icons/io";
|
||||
import { HiOutlineExternalLink } from "react-icons/hi";
|
||||
import { IoCloudDownloadOutline } from "react-icons/io5";
|
||||
import CardImage from "components/card/image";
|
||||
|
||||
const Design = () => {
|
||||
const downloadSvg = (url) => {
|
||||
download(url);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Design - SVGL</title>
|
||||
</Head>
|
||||
<Box mt="6">
|
||||
<Box w="full" border="solid 1px transparent">
|
||||
<Show>
|
||||
<Text
|
||||
as="h1"
|
||||
fontSize={{ base: "25px", sm: "35px", md: "5xl", lg: "6xl" }}
|
||||
letterSpacing="tight"
|
||||
lineHeight="short"
|
||||
fontWeight="extrabold"
|
||||
mb="3"
|
||||
>
|
||||
Design
|
||||
</Text>
|
||||
</Show>
|
||||
<Show delay={0.3}>
|
||||
<Box mt={{ base: 4, md: 5 }}>
|
||||
<Grid>
|
||||
<DesignCard title="Fonts" icon={RiFontSize}>
|
||||
<HStack spacing={2}>
|
||||
<Link
|
||||
href="https://fonts.google.com/specimen/Poppins"
|
||||
isExternal
|
||||
>
|
||||
Poppins
|
||||
</Link>
|
||||
<HiOutlineExternalLink size="20px" />
|
||||
</HStack>
|
||||
</DesignCard>
|
||||
<DesignCard title="Colors" icon={RiPaletteLine}>
|
||||
<Flex color="white">
|
||||
<Box bg="lightDark.900" p="2">
|
||||
<Text fontSize="15px">#16161a</Text>
|
||||
</Box>
|
||||
<Box bg="light.100" p="2">
|
||||
<Text fontSize="15px" color="black">
|
||||
#f9f9f9
|
||||
</Text>
|
||||
</Box>
|
||||
<Box bg="#6748E6" p="2">
|
||||
<Text fontSize="15px" color="white">
|
||||
#6748E6
|
||||
</Text>
|
||||
</Box>
|
||||
</Flex>
|
||||
</DesignCard>
|
||||
</Grid>
|
||||
<Box mt="4">
|
||||
<DesignCard title="Images" icon={IoMdImages}>
|
||||
<HStack spacing={3}>
|
||||
<CardImage title="Banner" image="/images/banner.png">
|
||||
<Button
|
||||
w={{ base: "100%", md: "auto" }}
|
||||
leftIcon={<IoCloudDownloadOutline />}
|
||||
variant="primary"
|
||||
fontWeight="light"
|
||||
mt="2"
|
||||
onClick={() =>
|
||||
downloadSvg(
|
||||
"https://svgl.vercel.app/images/banner.png"
|
||||
)
|
||||
}
|
||||
>
|
||||
Download
|
||||
</Button>
|
||||
</CardImage>
|
||||
<CardImage title="Logo" image="/images/logo.png">
|
||||
<Button
|
||||
w={{ base: "100%", md: "auto" }}
|
||||
leftIcon={<IoCloudDownloadOutline />}
|
||||
variant="primary"
|
||||
fontWeight="light"
|
||||
mt="2"
|
||||
onClick={() =>
|
||||
downloadSvg(
|
||||
"https://svgl.vercel.app/images/logo.png"
|
||||
)
|
||||
}
|
||||
>
|
||||
Download
|
||||
</Button>
|
||||
</CardImage>
|
||||
</HStack>
|
||||
</DesignCard>
|
||||
</Box>
|
||||
</Box>
|
||||
</Show>
|
||||
</Box>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Design;
|
@ -1,19 +0,0 @@
|
||||
import { chakra, Box } from "@chakra-ui/react";
|
||||
import Search from "components/search";
|
||||
import Items from "components/items/all";
|
||||
import Loader from "animations/loader";
|
||||
|
||||
export default function Index() {
|
||||
return (
|
||||
<>
|
||||
<Box mt="6">
|
||||
<Box w="full" border="solid 1px transparent">
|
||||
<Search />
|
||||
<Box mt={{ base: 4, md: 8 }}>
|
||||
<Items />
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
</>
|
||||
);
|
||||
}
|
72
pages/index.tsx
Normal file
72
pages/index.tsx
Normal file
@ -0,0 +1,72 @@
|
||||
import type { NextPage } from 'next'
|
||||
import Head from 'next/head'
|
||||
import Image from 'next/image'
|
||||
import styles from '../styles/Home.module.css'
|
||||
|
||||
const Home: NextPage = () => {
|
||||
return (
|
||||
<div className={styles.container}>
|
||||
<Head>
|
||||
<title>Create Next App</title>
|
||||
<meta name="description" content="Generated by create next app" />
|
||||
<link rel="icon" href="/favicon.ico" />
|
||||
</Head>
|
||||
|
||||
<main className={styles.main}>
|
||||
<h1 className={styles.title}>
|
||||
Welcome to <a href="https://nextjs.org">Next.js!</a>
|
||||
</h1>
|
||||
|
||||
<p className={styles.description}>
|
||||
Get started by editing{' '}
|
||||
<code className={styles.code}>pages/index.tsx</code>
|
||||
</p>
|
||||
|
||||
<div className={styles.grid}>
|
||||
<a href="https://nextjs.org/docs" className={styles.card}>
|
||||
<h2>Documentation →</h2>
|
||||
<p>Find in-depth information about Next.js features and API.</p>
|
||||
</a>
|
||||
|
||||
<a href="https://nextjs.org/learn" className={styles.card}>
|
||||
<h2>Learn →</h2>
|
||||
<p>Learn about Next.js in an interactive course with quizzes!</p>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://github.com/vercel/next.js/tree/canary/examples"
|
||||
className={styles.card}
|
||||
>
|
||||
<h2>Examples →</h2>
|
||||
<p>Discover and deploy boilerplate example Next.js projects.</p>
|
||||
</a>
|
||||
|
||||
<a
|
||||
href="https://vercel.com/new?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
|
||||
className={styles.card}
|
||||
>
|
||||
<h2>Deploy →</h2>
|
||||
<p>
|
||||
Instantly deploy your Next.js site to a public URL with Vercel.
|
||||
</p>
|
||||
</a>
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<footer className={styles.footer}>
|
||||
<a
|
||||
href="https://vercel.com?utm_source=create-next-app&utm_medium=default-template&utm_campaign=create-next-app"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
Powered by{' '}
|
||||
<span className={styles.logo}>
|
||||
<Image src="/vercel.svg" alt="Vercel Logo" width={72} height={16} />
|
||||
</span>
|
||||
</a>
|
||||
</footer>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Home
|
@ -1,134 +0,0 @@
|
||||
import Head from "next/head";
|
||||
import {
|
||||
chakra,
|
||||
Box,
|
||||
Flex,
|
||||
SimpleGrid,
|
||||
Button,
|
||||
Image,
|
||||
Center,
|
||||
Link,
|
||||
useColorModeValue,
|
||||
} from "@chakra-ui/react";
|
||||
import { useRouter } from "next/router";
|
||||
import useSWR from "swr";
|
||||
import Error from "components/error";
|
||||
import { IoArrowBackOutline, IoCloudDownloadOutline } from "react-icons/io5";
|
||||
import { BiLinkExternal } from "react-icons/bi";
|
||||
import Show from "animations/show";
|
||||
import Loader from "animations/loader";
|
||||
import confetti from "canvas-confetti";
|
||||
import download from "downloadjs";
|
||||
import NextLink from 'next/link';
|
||||
|
||||
const fetcher = async (url) => {
|
||||
const res = await fetch(url);
|
||||
const data = await res.json();
|
||||
if (res.status !== 200) {
|
||||
throw new Error(data.message);
|
||||
}
|
||||
return data;
|
||||
};
|
||||
|
||||
export default function Icon() {
|
||||
const { query } = useRouter();
|
||||
const { data, error } = useSWR(
|
||||
() => query.id && `/api/search?id=${query.id}`,
|
||||
fetcher
|
||||
);
|
||||
const bgImage = useColorModeValue("transparent", "#E9E9E9");
|
||||
const borderRds = useColorModeValue("0", "15px");
|
||||
|
||||
if (error) return <Error />;
|
||||
if (!data) return <Loader />;
|
||||
|
||||
const downloadSvg = (url) => {
|
||||
confetti({
|
||||
particleCount: 200,
|
||||
startVelocity: 30,
|
||||
spread: 300,
|
||||
gravity: 1.2,
|
||||
origin: { y: 0 },
|
||||
});
|
||||
download(url);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{data.title} - SVGL</title>
|
||||
</Head>
|
||||
<Show delay="0">
|
||||
<NextLink href="/" passHref>
|
||||
<Button
|
||||
leftIcon={<IoArrowBackOutline />}
|
||||
fontWeight="light"
|
||||
variant="ghost"
|
||||
mb="4"
|
||||
>
|
||||
Continue discovering
|
||||
</Button>
|
||||
</NextLink>
|
||||
<SimpleGrid columns={{ base: 1, md: 1, lg: 2 }} spacing={0}>
|
||||
<Box py={{ base: "10", md: "24" }}>
|
||||
<Center>
|
||||
<Image
|
||||
src={data.href}
|
||||
alt={data.title}
|
||||
w={{ base: "30%", md: "20%", lg: "30%" }}
|
||||
fit="cover"
|
||||
loading="lazy"
|
||||
bg={bgImage}
|
||||
borderRadius={borderRds}
|
||||
p="1"
|
||||
/>
|
||||
</Center>
|
||||
</Box>
|
||||
<Flex
|
||||
direction="column"
|
||||
alignItems="start"
|
||||
justifyContent="center"
|
||||
px={{ base: 4, lg: 4 }}
|
||||
py={{ base: "3", md: "0", lg: "10" }}
|
||||
>
|
||||
<chakra.h1
|
||||
mb={3}
|
||||
fontSize={{ base: "4xl", md: "4xl", lg: "5xl" }}
|
||||
fontWeight="semibold"
|
||||
lineHeight="shorter"
|
||||
>
|
||||
{data.title}
|
||||
</chakra.h1>
|
||||
<Flex direction={{ base: "column", md: "row" }} w="100%" mt="2">
|
||||
<Button
|
||||
w={{ base: "100%", md: "auto" }}
|
||||
mb={{ base: "2", md: "0" }}
|
||||
leftIcon={<IoCloudDownloadOutline />}
|
||||
variant="primary"
|
||||
fontWeight="light"
|
||||
mr="2"
|
||||
onClick={() => downloadSvg(data.href)}
|
||||
>
|
||||
Download .svg
|
||||
</Button>
|
||||
<Link
|
||||
href={data.url}
|
||||
style={{ textDecoration: "none" }}
|
||||
isExternal
|
||||
>
|
||||
<Button
|
||||
w={{ base: "100%", md: "auto" }}
|
||||
fontWeight="light"
|
||||
borderWidth="1px"
|
||||
rightIcon={<BiLinkExternal />}
|
||||
>
|
||||
{data.title} website
|
||||
</Button>
|
||||
</Link>
|
||||
</Flex>
|
||||
</Flex>
|
||||
</SimpleGrid>
|
||||
</Show>
|
||||
</>
|
||||
);
|
||||
}
|
116
styles/Home.module.css
Normal file
116
styles/Home.module.css
Normal file
@ -0,0 +1,116 @@
|
||||
.container {
|
||||
padding: 0 2rem;
|
||||
}
|
||||
|
||||
.main {
|
||||
min-height: 100vh;
|
||||
padding: 4rem 0;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.footer {
|
||||
display: flex;
|
||||
flex: 1;
|
||||
padding: 2rem 0;
|
||||
border-top: 1px solid #eaeaea;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.footer a {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
flex-grow: 1;
|
||||
}
|
||||
|
||||
.title a {
|
||||
color: #0070f3;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
.title a:hover,
|
||||
.title a:focus,
|
||||
.title a:active {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.title {
|
||||
margin: 0;
|
||||
line-height: 1.15;
|
||||
font-size: 4rem;
|
||||
}
|
||||
|
||||
.title,
|
||||
.description {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.description {
|
||||
margin: 4rem 0;
|
||||
line-height: 1.5;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.code {
|
||||
background: #fafafa;
|
||||
border-radius: 5px;
|
||||
padding: 0.75rem;
|
||||
font-size: 1.1rem;
|
||||
font-family: Menlo, Monaco, Lucida Console, Liberation Mono, DejaVu Sans Mono,
|
||||
Bitstream Vera Sans Mono, Courier New, monospace;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-wrap: wrap;
|
||||
max-width: 800px;
|
||||
}
|
||||
|
||||
.card {
|
||||
margin: 1rem;
|
||||
padding: 1.5rem;
|
||||
text-align: left;
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
border: 1px solid #eaeaea;
|
||||
border-radius: 10px;
|
||||
transition: color 0.15s ease, border-color 0.15s ease;
|
||||
max-width: 300px;
|
||||
}
|
||||
|
||||
.card:hover,
|
||||
.card:focus,
|
||||
.card:active {
|
||||
color: #0070f3;
|
||||
border-color: #0070f3;
|
||||
}
|
||||
|
||||
.card h2 {
|
||||
margin: 0 0 1rem 0;
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.card p {
|
||||
margin: 0;
|
||||
font-size: 1.25rem;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.logo {
|
||||
height: 1em;
|
||||
margin-left: 0.5rem;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.grid {
|
||||
width: 100%;
|
||||
flex-direction: column;
|
||||
}
|
||||
}
|
@ -1,86 +0,0 @@
|
||||
const baseStyle = {
|
||||
borderRadius: "md",
|
||||
borderColor: "dark.400",
|
||||
boxShadow: "none",
|
||||
cursor: "pointer",
|
||||
display: "inline-flex",
|
||||
alignItems: "center",
|
||||
_disabled: {
|
||||
cursor: "not-allowed",
|
||||
},
|
||||
_focused: {
|
||||
borderColor: "dark.500",
|
||||
},
|
||||
};
|
||||
|
||||
function variantPrimary() {
|
||||
const disabled = {
|
||||
bg: "dark.300",
|
||||
color: "dark.500",
|
||||
};
|
||||
|
||||
const loading = {
|
||||
bg: "dark.600",
|
||||
color: "white",
|
||||
};
|
||||
|
||||
return {
|
||||
bg: "dark.700",
|
||||
color: "white",
|
||||
_hover: {
|
||||
bg: "dark.800",
|
||||
_disabled: {
|
||||
...disabled,
|
||||
_loading: loading,
|
||||
},
|
||||
},
|
||||
_active: {
|
||||
bg: "dark.800",
|
||||
},
|
||||
_disabled: {
|
||||
...disabled,
|
||||
_loading: loading,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
function variantSecondary() {
|
||||
const disabled = {
|
||||
bg: "dark.300",
|
||||
color: "gray.500",
|
||||
};
|
||||
|
||||
const loading = {
|
||||
bg: "dark.600",
|
||||
color: "white",
|
||||
};
|
||||
|
||||
return {
|
||||
bg: "dark.500",
|
||||
color: "white",
|
||||
_hover: {
|
||||
bg: "dark.700",
|
||||
_disabled: {
|
||||
...disabled,
|
||||
_loading: loading,
|
||||
},
|
||||
},
|
||||
_active: {
|
||||
bg: "dark.800",
|
||||
},
|
||||
_disabled: {
|
||||
...disabled,
|
||||
_loading: loading,
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
const variants = {
|
||||
primary: variantPrimary,
|
||||
secondary: variantSecondary,
|
||||
};
|
||||
|
||||
export default {
|
||||
baseStyle,
|
||||
variants,
|
||||
};
|
@ -1,5 +0,0 @@
|
||||
import Button from "./button";
|
||||
|
||||
export default {
|
||||
Button,
|
||||
};
|
@ -1,11 +1,16 @@
|
||||
@font-face {
|
||||
font-family: "Inter-Medium";
|
||||
src: url("/fonts/Inter-Medium.woff2") format('woff2');
|
||||
font-style: normal;
|
||||
font-weight: 400;
|
||||
font-display: swap;
|
||||
html,
|
||||
body {
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
font-family: -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, Oxygen,
|
||||
Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
|
||||
}
|
||||
|
||||
a:focus {
|
||||
outline: none;
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
* {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
@ -1,51 +0,0 @@
|
||||
import { 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: {
|
||||
dark: {
|
||||
50: "#fafafa",
|
||||
100: "#f5f5f5",
|
||||
200: "#e6e6e6",
|
||||
300: "#d6d6d6",
|
||||
400: "#a5a5a5",
|
||||
500: "#767676",
|
||||
600: "#575757",
|
||||
700: "#434343",
|
||||
800: "#292929",
|
||||
900: "#000000",
|
||||
},
|
||||
lightDark: {
|
||||
900: "#16161a",
|
||||
},
|
||||
light: {
|
||||
100: "#f9f9f9",
|
||||
},
|
||||
},
|
||||
fonts: {
|
||||
body: "Inter-Medium, sans-serif",
|
||||
heading: "Inter-Medium, sans-serif",
|
||||
},
|
||||
styles: {
|
||||
global: (props) => ({
|
||||
"html, body": {
|
||||
height: "100%",
|
||||
maxHeight: "100vh",
|
||||
backgroundColor: mode("light.100", "lightDark.900")(props),
|
||||
},
|
||||
}),
|
||||
borderColor: "red",
|
||||
},
|
||||
}
|
||||
);
|
||||
|
||||
export default theme;
|
20
tsconfig.json
Normal file
20
tsconfig.json
Normal file
@ -0,0 +1,20 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"target": "es5",
|
||||
"lib": ["dom", "dom.iterable", "esnext"],
|
||||
"allowJs": true,
|
||||
"skipLibCheck": true,
|
||||
"strict": true,
|
||||
"forceConsistentCasingInFileNames": true,
|
||||
"noEmit": true,
|
||||
"esModuleInterop": true,
|
||||
"module": "esnext",
|
||||
"moduleResolution": "node",
|
||||
"resolveJsonModule": true,
|
||||
"isolatedModules": true,
|
||||
"jsx": "preserve",
|
||||
"incremental": true
|
||||
},
|
||||
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx"],
|
||||
"exclude": ["node_modules"]
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user