Merge branch 'develop'
@ -1,3 +1,3 @@
|
|||||||
{
|
{
|
||||||
"extends": "next/core-web-vitals"
|
"extends": ["next/core-web-vitals"]
|
||||||
}
|
}
|
||||||
|
34
.gitignore
vendored
@ -1,42 +1,42 @@
|
|||||||
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.
|
# dependencies ->
|
||||||
|
|
||||||
# dependencies
|
|
||||||
/node_modules
|
/node_modules
|
||||||
/.pnp
|
/.pnp
|
||||||
.pnp.js
|
.pnp.js
|
||||||
|
package-lock.json
|
||||||
|
|
||||||
# testing
|
# testing ->
|
||||||
/coverage
|
/coverage
|
||||||
|
|
||||||
# next.js
|
# next.js ->
|
||||||
/.next/
|
/.next/
|
||||||
/out/
|
/out/
|
||||||
|
|
||||||
# production
|
# production ->
|
||||||
/build
|
/build
|
||||||
|
|
||||||
# misc
|
# misc ->
|
||||||
.DS_Store
|
.DS_Store
|
||||||
*.pem
|
*.pem
|
||||||
|
|
||||||
# debug
|
# debug ->
|
||||||
npm-debug.log*
|
npm-debug.log*
|
||||||
yarn-debug.log*
|
yarn-debug.log*
|
||||||
yarn-error.log*
|
yarn-error.log*
|
||||||
|
.pnpm-debug.log*
|
||||||
|
|
||||||
# local env files
|
# local env files ->
|
||||||
.env.local
|
.env*.local
|
||||||
.env.development.local
|
|
||||||
.env.test.local
|
|
||||||
.env.production.local
|
|
||||||
|
|
||||||
# PWA files
|
# vercel ->
|
||||||
|
.vercel
|
||||||
|
|
||||||
|
# typescript ->
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# PWA files ->
|
||||||
**/public/sw.js
|
**/public/sw.js
|
||||||
**/public/workbox-*.js
|
**/public/workbox-*.js
|
||||||
**/public/worker-*.js
|
**/public/worker-*.js
|
||||||
**/public/sw.js.map
|
**/public/sw.js.map
|
||||||
**/public/workbox-*.js.map
|
**/public/workbox-*.js.map
|
||||||
**/public/worker-*.js.map
|
**/public/worker-*.js.map
|
||||||
|
|
||||||
# vercel
|
|
||||||
.vercel
|
|
||||||
|
99
README.md
@ -1,27 +1,108 @@
|
|||||||
<p align="center">
|
<p align="center">
|
||||||
<a href="https://svgl.vercel.app/">
|
<a href="https://svgl.vercel.app/" target="_blank">
|
||||||
<img src="https://raw.githubusercontent.com/pheralb/svgl/main/public/images/banner.png" width="800px" alt="svgl preview" />
|
<img src="https://i.postimg.cc/1tzrP2rg/banner-corner.png" width="800px" alt="SVGL Banner" />
|
||||||
</a>
|
</a>
|
||||||
</p>
|
</p>
|
||||||
|
|
||||||
## 🚀 Getting started:
|
## 📦 Packages:
|
||||||
|
|
||||||
[SVGL](https://svgl.vercel.app/) is a beautiful collection of SVG logos. Free and open source.
|
- ⚡️ [Nextjs](https://nextjs.org/) - The React Framework for Production.
|
||||||
|
- ⚒️ [React 18](https://reactjs.org/) - A JavaScript library for building user interfaces.
|
||||||
|
- 💙 [Typescript](https://www.typescriptlang.org/) - A superset of JavaScript.
|
||||||
|
- ✅ [Vitest](https://vitest.dev/) - A blazing fast unit test framework.
|
||||||
|
- 💅 [Chakra UI](https://chakra-ui.com/) - Create accessible React apps with speed.
|
||||||
|
- 💥 [Framer Motion](https://www.framer.com/motion/) - Production-ready motion library.
|
||||||
|
- 💖 [Phosphor Icons](https://phosphoricons.com/) - A flexible icon family for everyone.
|
||||||
|
- ⬇️ [Next-PWA](https://github.com/shadowwalker/next-pwa) - Zero config PWA plugin for Next.js, with workbox.
|
||||||
|
|
||||||
|
## 🚀 Getting started:
|
||||||
|
|
||||||
You need:
|
You need:
|
||||||
|
|
||||||
- [Node.js 16+ (recommend: 16.14.0 LTS)](https://nodejs.org/en/)
|
- [Node.js 16+ (recommend: 16.15.1 LTS)](https://nodejs.org/en/)
|
||||||
- [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
|
- [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
|
||||||
|
|
||||||
and run:
|
1. Clone the repository:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git@github.com:pheralb/svgl.git
|
||||||
|
```
|
||||||
|
|
||||||
|
2. Install dependencies:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
npm install
|
npm install
|
||||||
npm run dev
|
# or
|
||||||
|
yarn install
|
||||||
```
|
```
|
||||||
|
|
||||||
Open [http://localhost:3000](http://localhost:3000) with your browser to see the result.
|
3. Run:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run dev
|
||||||
|
# or
|
||||||
|
yarn dev
|
||||||
|
```
|
||||||
|
|
||||||
|
4. Test & Build:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
npm run ready
|
||||||
|
# or
|
||||||
|
yarn ready
|
||||||
|
```
|
||||||
|
|
||||||
|
Open [localhost:3000](localhost:3000) with your browser to see the result.
|
||||||
|
|
||||||
|
## 🤔 Can I add my logo?
|
||||||
|
|
||||||
|
Yes! Here is a guide for you 🥳:
|
||||||
|
|
||||||
|
1. [Fork the repository](https://github.com/pheralb/svgl/fork).
|
||||||
|
|
||||||
|
2. Clone the forked repository:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git@github.com:YOUR_USERNAME/svgl.git
|
||||||
|
```
|
||||||
|
|
||||||
|
3. Add the **.svg** logo here: [`/public/library`](https://github.com/pheralb/svgl/tree/main/public/library).
|
||||||
|
|
||||||
|
4. Add your logo information here following the structure: [`/data/svgs.json`](https://github.com/pheralb/svgl/tree/main/public/library).
|
||||||
|
|
||||||
|
```json
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"slug": "/library/your_logo.svg",
|
||||||
|
"title": "Logo Title",
|
||||||
|
"category": "Logo Category",
|
||||||
|
"url": "Your Website / app url"
|
||||||
|
}
|
||||||
|
```
|
||||||
|
|
||||||
|
5. Create a commit and push:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
git add .
|
||||||
|
git commit -m "🥰 Added my logo"
|
||||||
|
git push origin main
|
||||||
|
```
|
||||||
|
|
||||||
|
6. Create a pull request with your changes and 🥳 ready.
|
||||||
|
|
||||||
|
## 🚂 Api endpoints:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
- /api/all: returns all the logos.
|
||||||
|
- /api/search?id=2: returns the logo with id 2.
|
||||||
|
- /api/search?q=logo: returns the logo with query.
|
||||||
|
```
|
||||||
|
|
||||||
|
## ⚒️ Shortcuts:
|
||||||
|
|
||||||
|
- ⭐ SVG Library: [/public/library/](https://github.com/pheralb/svgl/tree/main/public/library).
|
||||||
|
- ✍️ SVG JSON logos: [/data/](https://github.com/pheralb/svgl/tree/main/data).
|
||||||
|
|
||||||
## 🔑 License:
|
## 🔑 License:
|
||||||
|
|
||||||
MIT
|
- [MIT](https://github.com/pheralb/svgl/blob/main/LICENSE).
|
||||||
|
@ -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,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;
|
|
347
data/svgs.js
@ -1,347 +0,0 @@
|
|||||||
const SVGSLogos = [
|
|
||||||
{
|
|
||||||
id: 1,
|
|
||||||
href: "/library/discord.svg",
|
|
||||||
title: "Discord",
|
|
||||||
category: "Videocall",
|
|
||||||
url: "https://discord.com/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 2,
|
|
||||||
href: "/library/github.svg",
|
|
||||||
title: "Github",
|
|
||||||
category: "Repository",
|
|
||||||
url: "https://github.com/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 3,
|
|
||||||
href: "/library/preact.svg",
|
|
||||||
title: "Preact",
|
|
||||||
category: "Library",
|
|
||||||
url: "https://preactjs.com/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 4,
|
|
||||||
href: "/library/react.svg",
|
|
||||||
title: "React",
|
|
||||||
category: "Library",
|
|
||||||
url: "https://reactjs.org/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 5,
|
|
||||||
href: "/library/vercel.svg",
|
|
||||||
title: "Vercel",
|
|
||||||
category: "Hosting",
|
|
||||||
url: "https://vercel.com/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 6,
|
|
||||||
href: "/library/svelte.svg",
|
|
||||||
title: "Svelte",
|
|
||||||
category: "Framework",
|
|
||||||
url: "https://svelte.dev/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 7,
|
|
||||||
href: "/library/vue.svg",
|
|
||||||
title: "Vue",
|
|
||||||
category: "Framework",
|
|
||||||
url: "https://vuejs.org/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 8,
|
|
||||||
href: "/library/nuxt.svg",
|
|
||||||
title: "Nuxt",
|
|
||||||
category: "Framework",
|
|
||||||
url: "https://nuxtjs.org/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 9,
|
|
||||||
href: "/library/nextjs.svg",
|
|
||||||
title: "Nextjs",
|
|
||||||
category: "Framework",
|
|
||||||
url: "https://nextjs.org/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 10,
|
|
||||||
href: "/library/vscode.svg",
|
|
||||||
title: "VSCode",
|
|
||||||
category: "Text Editor",
|
|
||||||
url: "https://code.visualstudio.com/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 11,
|
|
||||||
href: "/library/jwt.svg",
|
|
||||||
title: "JWT",
|
|
||||||
category: "Security",
|
|
||||||
url: "https://jwt.io/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 12,
|
|
||||||
href: "/library/strapi.svg",
|
|
||||||
title: "Strapi",
|
|
||||||
category: "CMS",
|
|
||||||
url: "https://strapi.io/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 13,
|
|
||||||
href: "/library/figma.svg",
|
|
||||||
title: "Figma",
|
|
||||||
category: "Design",
|
|
||||||
url: "https://www.figma.com/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 14,
|
|
||||||
href: "/library/spotify.svg",
|
|
||||||
title: "Spotify",
|
|
||||||
category: "Music",
|
|
||||||
url: "https://www.spotify.com/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 15,
|
|
||||||
href: "/library/postman.svg",
|
|
||||||
title: "Postman",
|
|
||||||
category: "API",
|
|
||||||
url: "https://www.getpostman.com/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 16,
|
|
||||||
href: "/library/algolia.svg",
|
|
||||||
title: "Algolia",
|
|
||||||
category: "Search",
|
|
||||||
url: "https://www.algolia.com/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 17,
|
|
||||||
href: "/library/bootstrap.svg",
|
|
||||||
title: "Bootstrap",
|
|
||||||
category: "CSS Framework",
|
|
||||||
url: "https://getbootstrap.com/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 18,
|
|
||||||
href: "/library/firebase.svg",
|
|
||||||
title: "Firebase",
|
|
||||||
category: "Hosting",
|
|
||||||
url: "https://firebase.google.com/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 19,
|
|
||||||
href: "/library/supabase.svg",
|
|
||||||
title: "Supabase",
|
|
||||||
category: "Database",
|
|
||||||
url: "https://supabase.com/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 20,
|
|
||||||
href: "/library/vitejs.svg",
|
|
||||||
title: "Vite.js",
|
|
||||||
category: "JavaScript Compiler",
|
|
||||||
url: "https://vitejs.dev",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 21,
|
|
||||||
href: "/library/facebook.svg",
|
|
||||||
title: "Facebook",
|
|
||||||
category: "Social",
|
|
||||||
url: "https://www.facebook.com/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 22,
|
|
||||||
href: "/library/twitter.svg",
|
|
||||||
title: "Twitter",
|
|
||||||
category: "Social",
|
|
||||||
url: "https://twitter.com/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 23,
|
|
||||||
href: "/library/nodejs.svg",
|
|
||||||
title: "Node.js",
|
|
||||||
category: "JavaScript Runtime",
|
|
||||||
url: "https://nodejs.org/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 24,
|
|
||||||
href: "/library/esbuild.svg",
|
|
||||||
title: "Esbuild",
|
|
||||||
category: "JavaScript Compiler",
|
|
||||||
url: "https://esbuild.github.io/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 25,
|
|
||||||
href: "/library/deno.svg",
|
|
||||||
title: "Deno",
|
|
||||||
category: "JavaScript Runtime",
|
|
||||||
url: "https://deno.land/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 26,
|
|
||||||
href: "/library/gatsby.svg",
|
|
||||||
title: "Gatsby",
|
|
||||||
category: "Static Site Generator",
|
|
||||||
url: "https://www.gatsbyjs.org/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 27,
|
|
||||||
href: "/library/npm.svg",
|
|
||||||
title: "NPM",
|
|
||||||
category: "Package Manager",
|
|
||||||
url: "https://www.npmjs.com/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 28,
|
|
||||||
href: "/library/homebrew.svg",
|
|
||||||
title: "Homebrew",
|
|
||||||
category: "Package Manager",
|
|
||||||
url: "https://brew.sh/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 29,
|
|
||||||
href: "/library/sublimetext.svg",
|
|
||||||
title: "Sublime Text",
|
|
||||||
category: "Text Editor",
|
|
||||||
url: "https://www.sublimetext.com/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 30,
|
|
||||||
href: "/library/turborepo.svg",
|
|
||||||
title: "TurboRepo",
|
|
||||||
category: "Package Manager",
|
|
||||||
url: "https://turborepo.org/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 31,
|
|
||||||
href: "/library/tailwindcss.svg",
|
|
||||||
title: "Tailwind CSS",
|
|
||||||
category: "CSS Framework",
|
|
||||||
url: "https://tailwindcss.com/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 32,
|
|
||||||
href: "/library/styledcomponents.svg",
|
|
||||||
title: "Styled Components",
|
|
||||||
category: "CSS-in-JS",
|
|
||||||
url: "https://styled-components.com/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 33,
|
|
||||||
href: "/library/angular.svg",
|
|
||||||
title: "Angular",
|
|
||||||
category: "Framework",
|
|
||||||
url: "https://angular.io/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 34,
|
|
||||||
href: "/library/blitzjs.svg",
|
|
||||||
title: "Blitz",
|
|
||||||
category: "Framework",
|
|
||||||
url: "https://blitzjs.com/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 35,
|
|
||||||
href: "/library/lit.svg",
|
|
||||||
title: "Lit",
|
|
||||||
category: "Web Components",
|
|
||||||
url: "https://lit.dev/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 36,
|
|
||||||
href: "/library/atom.svg",
|
|
||||||
title: "Atom",
|
|
||||||
category: "Text Editor",
|
|
||||||
url: "https://atom.io/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 37,
|
|
||||||
href: "/library/youtube.svg",
|
|
||||||
title: "YouTube",
|
|
||||||
category: "Video Platform",
|
|
||||||
url: "https://www.youtube.com/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 38,
|
|
||||||
href: "/library/astro.svg",
|
|
||||||
title: "Astro",
|
|
||||||
category: "Framework",
|
|
||||||
url: "https://astro.build/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 39,
|
|
||||||
href: "/library/google.svg",
|
|
||||||
title: "Google",
|
|
||||||
category: "Search",
|
|
||||||
url: "https://www.google.com/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 40,
|
|
||||||
href: "/library/framer.svg",
|
|
||||||
title: "Framer",
|
|
||||||
category: "Design",
|
|
||||||
url: "https://framer.com/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 41,
|
|
||||||
href: "/library/netflix.svg",
|
|
||||||
title: "Netflix",
|
|
||||||
category: "Video Platform",
|
|
||||||
url: "https://www.netflix.com/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 42,
|
|
||||||
href: "/library/firefox.svg",
|
|
||||||
title: "Firefox",
|
|
||||||
category: "Browser",
|
|
||||||
url: "https://www.mozilla.org/en-US/firefox/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 43,
|
|
||||||
href: "/library/linkedin.svg",
|
|
||||||
title: "LinkedIn",
|
|
||||||
category: "Social",
|
|
||||||
url: "https://www.linkedin.com/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 44,
|
|
||||||
href:"/library/telegram.svg",
|
|
||||||
title: "Telegram",
|
|
||||||
category: "Social",
|
|
||||||
url: "https://web.telegram.org/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 45,
|
|
||||||
href: "/library/whatsapp.svg",
|
|
||||||
title: "WhatsApp",
|
|
||||||
category: "Social",
|
|
||||||
url: "https://web.whatsapp.com/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 46,
|
|
||||||
href: "/library/headlessui.svg",
|
|
||||||
title: "Headless UI",
|
|
||||||
category: "Design",
|
|
||||||
url: "https://headlessui.dev/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 47,
|
|
||||||
href: "/library/kotlin.svg",
|
|
||||||
title: "Kotlin",
|
|
||||||
category: "Java",
|
|
||||||
url: "https://kotlinlang.org/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 48,
|
|
||||||
href: "/library/tiktok.svg",
|
|
||||||
title: "TikTok",
|
|
||||||
category: "Video Platform",
|
|
||||||
url: "https://tiktok.com/",
|
|
||||||
},
|
|
||||||
{
|
|
||||||
id: 49,
|
|
||||||
href: "/library/storybook.svg",
|
|
||||||
title: "Storybook",
|
|
||||||
category: "UI component explorer",
|
|
||||||
url: "https://storybook.js.org/",
|
|
||||||
}
|
|
||||||
];
|
|
||||||
|
|
||||||
export default SVGSLogos;
|
|
345
data/svgs.json
Normal file
@ -0,0 +1,345 @@
|
|||||||
|
[
|
||||||
|
{
|
||||||
|
"id": 1,
|
||||||
|
"slug": "/library/discord.svg",
|
||||||
|
"title": "Discord",
|
||||||
|
"category": "Software",
|
||||||
|
"url": "https://discord.com/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 2,
|
||||||
|
"slug": "/library/github.svg",
|
||||||
|
"title": "Github",
|
||||||
|
"category": "Software",
|
||||||
|
"url": "https://github.com/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 3,
|
||||||
|
"slug": "/library/preact.svg",
|
||||||
|
"title": "Preact",
|
||||||
|
"category": "Library",
|
||||||
|
"url": "https://preactjs.com/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 4,
|
||||||
|
"slug": "/library/react.svg",
|
||||||
|
"title": "React",
|
||||||
|
"category": "Library",
|
||||||
|
"url": "https://reactjs.org/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 5,
|
||||||
|
"slug": "/library/vercel.svg",
|
||||||
|
"title": "Vercel",
|
||||||
|
"category": "Hosting",
|
||||||
|
"url": "https://vercel.com/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 6,
|
||||||
|
"slug": "/library/svelte.svg",
|
||||||
|
"title": "Svelte",
|
||||||
|
"category": "Library",
|
||||||
|
"url": "https://svelte.dev/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 7,
|
||||||
|
"slug": "/library/vue.svg",
|
||||||
|
"title": "Vue",
|
||||||
|
"category": "Framework",
|
||||||
|
"url": "https://vuejs.org/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 8,
|
||||||
|
"slug": "/library/nuxt.svg",
|
||||||
|
"title": "Nuxt",
|
||||||
|
"category": "Framework",
|
||||||
|
"url": "https://nuxtjs.org/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 9,
|
||||||
|
"slug": "/library/nextjs.svg",
|
||||||
|
"title": "Nextjs",
|
||||||
|
"category": "Framework",
|
||||||
|
"url": "https://nextjs.org/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 10,
|
||||||
|
"slug": "/library/vscode.svg",
|
||||||
|
"title": "VSCode",
|
||||||
|
"category": "Software",
|
||||||
|
"url": "https://code.visualstudio.com/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 11,
|
||||||
|
"slug": "/library/jwt.svg",
|
||||||
|
"title": "JWT",
|
||||||
|
"category": "Library",
|
||||||
|
"url": "https://jwt.io/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 12,
|
||||||
|
"slug": "/library/strapi.svg",
|
||||||
|
"title": "Strapi",
|
||||||
|
"category": "CMS",
|
||||||
|
"url": "https://strapi.io/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 13,
|
||||||
|
"slug": "/library/figma.svg",
|
||||||
|
"title": "Figma",
|
||||||
|
"category": "Software",
|
||||||
|
"url": "https://www.figma.com/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 14,
|
||||||
|
"slug": "/library/spotify.svg",
|
||||||
|
"title": "Spotify",
|
||||||
|
"category": "Software",
|
||||||
|
"url": "https://www.spotify.com/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 15,
|
||||||
|
"slug": "/library/postman.svg",
|
||||||
|
"title": "Postman",
|
||||||
|
"category": "Software",
|
||||||
|
"url": "https://www.getpostman.com/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 16,
|
||||||
|
"slug": "/library/algolia.svg",
|
||||||
|
"title": "Algolia",
|
||||||
|
"category": "Library",
|
||||||
|
"url": "https://www.algolia.com/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 17,
|
||||||
|
"slug": "/library/bootstrap.svg",
|
||||||
|
"title": "Bootstrap",
|
||||||
|
"category": "Framework",
|
||||||
|
"url": "https://getbootstrap.com/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 18,
|
||||||
|
"slug": "/library/firebase.svg",
|
||||||
|
"title": "Firebase",
|
||||||
|
"category": "Hosting",
|
||||||
|
"url": "https://firebase.google.com/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 19,
|
||||||
|
"slug": "/library/supabase.svg",
|
||||||
|
"title": "Supabase",
|
||||||
|
"category": "Database",
|
||||||
|
"url": "https://supabase.com/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 20,
|
||||||
|
"slug": "/library/vitejs.svg",
|
||||||
|
"title": "Vite.js",
|
||||||
|
"category": "Compiler",
|
||||||
|
"url": "https://vitejs.dev"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 21,
|
||||||
|
"slug": "/library/facebook.svg",
|
||||||
|
"title": "Facebook",
|
||||||
|
"category": "Social",
|
||||||
|
"url": "https://www.facebook.com/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 22,
|
||||||
|
"slug": "/library/twitter.svg",
|
||||||
|
"title": "Twitter",
|
||||||
|
"category": "Social",
|
||||||
|
"url": "https://twitter.com/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 23,
|
||||||
|
"slug": "/library/nodejs.svg",
|
||||||
|
"title": "Node.js",
|
||||||
|
"category": "Library",
|
||||||
|
"url": "https://nodejs.org/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 24,
|
||||||
|
"slug": "/library/esbuild.svg",
|
||||||
|
"title": "Esbuild",
|
||||||
|
"category": "Compiler",
|
||||||
|
"url": "https://esbuild.github.io/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 25,
|
||||||
|
"slug": "/library/deno.svg",
|
||||||
|
"title": "Deno",
|
||||||
|
"category": "Library",
|
||||||
|
"url": "https://deno.land/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 26,
|
||||||
|
"slug": "/library/gatsby.svg",
|
||||||
|
"title": "Gatsby",
|
||||||
|
"category": "Framework",
|
||||||
|
"url": "https://www.gatsbyjs.org/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 27,
|
||||||
|
"slug": "/library/npm.svg",
|
||||||
|
"title": "NPM",
|
||||||
|
"category": "Software",
|
||||||
|
"url": "https://www.npmjs.com/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 28,
|
||||||
|
"slug": "/library/homebrew.svg",
|
||||||
|
"title": "Homebrew",
|
||||||
|
"category": "Software",
|
||||||
|
"url": "https://brew.sh/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 29,
|
||||||
|
"slug": "/library/sublimetext.svg",
|
||||||
|
"title": "Sublime Text",
|
||||||
|
"category": "Software",
|
||||||
|
"url": "https://www.sublimetext.com/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 30,
|
||||||
|
"slug": "/library/turborepo.svg",
|
||||||
|
"title": "TurboRepo",
|
||||||
|
"category": "Library",
|
||||||
|
"url": "https://turborepo.org/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 31,
|
||||||
|
"slug": "/library/tailwindcss.svg",
|
||||||
|
"title": "Tailwind CSS",
|
||||||
|
"category": "Framework",
|
||||||
|
"url": "https://tailwindcss.com/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 32,
|
||||||
|
"slug": "/library/styledcomponents.svg",
|
||||||
|
"title": "Styled Components",
|
||||||
|
"category": "Library",
|
||||||
|
"url": "https://styled-components.com/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 33,
|
||||||
|
"slug": "/library/angular.svg",
|
||||||
|
"title": "Angular",
|
||||||
|
"category": "Framework",
|
||||||
|
"url": "https://angular.io/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 34,
|
||||||
|
"slug": "/library/blitzjs.svg",
|
||||||
|
"title": "Blitz",
|
||||||
|
"category": "Framework",
|
||||||
|
"url": "https://blitzjs.com/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 35,
|
||||||
|
"slug": "/library/lit.svg",
|
||||||
|
"title": "Lit",
|
||||||
|
"category": "Library",
|
||||||
|
"url": "https://lit.dev/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 36,
|
||||||
|
"slug": "/library/atom.svg",
|
||||||
|
"title": "Atom",
|
||||||
|
"category": "Software",
|
||||||
|
"url": "https://atom.io/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 37,
|
||||||
|
"slug": "/library/youtube.svg",
|
||||||
|
"title": "YouTube",
|
||||||
|
"category": "Social",
|
||||||
|
"url": "https://www.youtube.com/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 38,
|
||||||
|
"slug": "/library/astro.svg",
|
||||||
|
"title": "Astro",
|
||||||
|
"category": "Framework",
|
||||||
|
"url": "https://astro.build/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 39,
|
||||||
|
"slug": "/library/google.svg",
|
||||||
|
"title": "Google",
|
||||||
|
"category": "Social",
|
||||||
|
"url": "https://www.google.com/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 40,
|
||||||
|
"slug": "/library/framer.svg",
|
||||||
|
"title": "Framer",
|
||||||
|
"category": "Software",
|
||||||
|
"url": "https://framer.com/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 41,
|
||||||
|
"slug": "/library/netflix.svg",
|
||||||
|
"title": "Netflix",
|
||||||
|
"category": "Entertainment",
|
||||||
|
"url": "https://www.netflix.com/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 42,
|
||||||
|
"slug": "/library/firefox.svg",
|
||||||
|
"title": "Firefox",
|
||||||
|
"category": "Software",
|
||||||
|
"url": "https://www.mozilla.org/en-US/firefox/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 43,
|
||||||
|
"slug": "/library/linkedin.svg",
|
||||||
|
"title": "LinkedIn",
|
||||||
|
"category": "Social",
|
||||||
|
"url": "https://www.linkedin.com/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 44,
|
||||||
|
"slug": "/library/telegram.svg",
|
||||||
|
"title": "Telegram",
|
||||||
|
"category": "Social",
|
||||||
|
"url": "https://web.telegram.org/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 45,
|
||||||
|
"slug": "/library/whatsapp.svg",
|
||||||
|
"title": "WhatsApp",
|
||||||
|
"category": "Social",
|
||||||
|
"url": "https://web.whatsapp.com/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 46,
|
||||||
|
"slug": "/library/headlessui.svg",
|
||||||
|
"title": "Headless UI",
|
||||||
|
"category": "Library",
|
||||||
|
"url": "https://headlessui.dev/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 47,
|
||||||
|
"slug": "/library/kotlin.svg",
|
||||||
|
"title": "Kotlin",
|
||||||
|
"category": "Language",
|
||||||
|
"url": "https://kotlinlang.org/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 48,
|
||||||
|
"slug": "/library/vitest.svg",
|
||||||
|
"title": "Vitest",
|
||||||
|
"category": "Framework",
|
||||||
|
"url": "https://vitest.dev/"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id": 49,
|
||||||
|
"slug": "/library/storybook.svg",
|
||||||
|
"title": "Storybook",
|
||||||
|
"category": "Software",
|
||||||
|
"url": "https://storybook.js.org/"
|
||||||
|
}
|
||||||
|
]
|
@ -1,5 +0,0 @@
|
|||||||
{
|
|
||||||
"compilerOptions": {
|
|
||||||
"baseUrl": "./"
|
|
||||||
}
|
|
||||||
}
|
|
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.
|
57
next-seo.config.ts
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
export default {
|
||||||
|
title: "A beautiful library with SVG logos",
|
||||||
|
titleTemplate: "%s - Svgl",
|
||||||
|
description: "Svgl is a library of free and open source SVG logos.",
|
||||||
|
defaultTitle: "svgl",
|
||||||
|
additionalLinkTags: [
|
||||||
|
{
|
||||||
|
rel: "icon",
|
||||||
|
href: "/icons/icon.ico",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rel: "apple-touch-icon",
|
||||||
|
href: "/icons/apple-touch-icon-180x180.png",
|
||||||
|
sizes: "180x180",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rel: "apple-touch-icon",
|
||||||
|
href: "/icons/apple-touch-icon-152x152.png",
|
||||||
|
sizes: "152x152",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rel: "apple-touch-icon",
|
||||||
|
href: "/icons/apple-touch-icon-114x114.png",
|
||||||
|
sizes: "114x114",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rel: "manifest",
|
||||||
|
href: "/manifest.json",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rel: "preload",
|
||||||
|
href: "/fonts/Inter-Regular.woff2",
|
||||||
|
as: "font",
|
||||||
|
type: "font/woff2",
|
||||||
|
crossOrigin: "anonymous",
|
||||||
|
},
|
||||||
|
],
|
||||||
|
openGraph: {
|
||||||
|
site_name: "Svgl",
|
||||||
|
url: "https://svgl.vercel.app/",
|
||||||
|
type: "website",
|
||||||
|
locale: "en_IE",
|
||||||
|
images: [
|
||||||
|
{
|
||||||
|
url: "/images/banner.png",
|
||||||
|
width: 1920,
|
||||||
|
height: 1080,
|
||||||
|
type: "image/png",
|
||||||
|
}
|
||||||
|
],
|
||||||
|
},
|
||||||
|
twitter: {
|
||||||
|
handle: "@pheralb_",
|
||||||
|
site: "@pheralb_",
|
||||||
|
cardType: "summary_large_image",
|
||||||
|
},
|
||||||
|
};
|
@ -1,15 +1,12 @@
|
|||||||
|
/** @type {import('next').NextConfig} */
|
||||||
const withPWA = require("next-pwa");
|
const withPWA = require("next-pwa");
|
||||||
|
|
||||||
const nextConfig = {
|
|
||||||
reactStrictMode: true,
|
|
||||||
};
|
|
||||||
|
|
||||||
module.exports = withPWA({
|
module.exports = withPWA({
|
||||||
|
reactStrictMode: true,
|
||||||
pwa: {
|
pwa: {
|
||||||
dest: "public",
|
dest: "public",
|
||||||
register: true,
|
register: true,
|
||||||
skipWaiting: true,
|
skipWaiting: true,
|
||||||
disable: process.env.NODE_ENV === "development",
|
disable: process.env.NODE_ENV === "development",
|
||||||
},
|
},
|
||||||
nextConfig,
|
|
||||||
});
|
});
|
||||||
|
9617
package-lock.json
generated
63
package.json
@ -1,34 +1,55 @@
|
|||||||
{
|
{
|
||||||
"name": "svgl",
|
"name": "svgl",
|
||||||
"version": "1.2.0",
|
"version": "2.0.0",
|
||||||
"description": "Beautiful SVG vector logos",
|
"description": "A beautiful library with SVG logos.",
|
||||||
"author": "pheralb",
|
"private": true,
|
||||||
|
"author": "@pheralb_",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
|
"keywords": [
|
||||||
|
"svgs",
|
||||||
|
"logos",
|
||||||
|
"images",
|
||||||
|
"library"
|
||||||
|
],
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"dev": "next dev",
|
"dev": "next dev",
|
||||||
"build": "next build",
|
"build": "next build",
|
||||||
"start": "next start",
|
"start": "next start",
|
||||||
"lint": "next lint"
|
"lint": "next lint",
|
||||||
|
"test": "vitest",
|
||||||
|
"ready": "vitest && next build"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@algolia/autocomplete-core": "^1.5.6",
|
"@chakra-ui/react": "2.1.2",
|
||||||
"@chakra-ui/react": "^1.8.7",
|
"@emotion/react": "11.9.0",
|
||||||
"@emotion/react": "^11.8.2",
|
"@emotion/styled": "11.8.1",
|
||||||
"@emotion/styled": "^11.8.1",
|
"@uiball/loaders": "1.2.6",
|
||||||
"canvas-confetti": "^1.5.1",
|
"canvas-confetti": "1.5.1",
|
||||||
"downloadjs": "^1.4.7",
|
"downloadjs": "1.4.7",
|
||||||
"framer-motion": "^6.2.8",
|
"framer-motion": "6.3.9",
|
||||||
"next": "12.1.0",
|
"next": "12.1.6",
|
||||||
"next-pwa": "^5.5.0",
|
"next-pwa": "5.5.4",
|
||||||
"nextjs-progressbar": "^0.0.14",
|
"next-seo": "5.4.0",
|
||||||
"react": "17.0.2",
|
"nextjs-progressbar": "0.0.14",
|
||||||
"react-dom": "17.0.2",
|
"phosphor-react": "1.4.1",
|
||||||
"react-hot-toast": "^2.2.0",
|
"react": "18.1.0",
|
||||||
"react-icons": "^4.3.1",
|
"react-dom": "18.1.0",
|
||||||
"swr": "^1.2.2"
|
"react-hot-toast": "2.2.0",
|
||||||
|
"swr": "1.3.0"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"eslint": "8.10.0",
|
"@testing-library/jest-dom": "5.16.4",
|
||||||
"eslint-config-next": "12.1.0"
|
"@testing-library/react": "13.3.0",
|
||||||
|
"@types/canvas-confetti": "1.4.2",
|
||||||
|
"@types/downloadjs": "1.4.3",
|
||||||
|
"@types/node": "17.0.38",
|
||||||
|
"@types/react": "18.0.10",
|
||||||
|
"@types/react-dom": "18.0.5",
|
||||||
|
"@vitejs/plugin-react": "1.3.2",
|
||||||
|
"eslint": "8.16.0",
|
||||||
|
"eslint-config-next": "12.1.6",
|
||||||
|
"jsdom": "20.0.0",
|
||||||
|
"typescript": "4.7.2",
|
||||||
|
"vitest": "0.15.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;
|
|
@ -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 });
|
|
||||||
}
|
|
||||||
}
|
|
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>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
@ -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>
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
}
|
|
BIN
public/fonts/Inter-Regular.woff2
Normal file
BIN
public/fonts/Inter-SemiBold.woff2
Normal file
BIN
public/icons/apple-touch-icon-114x114.png
Normal file
After Width: | Height: | Size: 6.0 KiB |
BIN
public/icons/apple-touch-icon-152x152.png
Normal file
After Width: | Height: | Size: 8.8 KiB |
BIN
public/icons/apple-touch-icon-180x180.png
Normal file
After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 9.2 KiB After Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 22 KiB After Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 21 KiB After Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 14 KiB After Width: | Height: | Size: 6.9 KiB |
Before Width: | Height: | Size: 18 KiB After Width: | Height: | Size: 20 KiB |
BIN
public/icons/maskable_icon.png
Normal file
After Width: | Height: | Size: 7.1 KiB |
Before Width: | Height: | Size: 887 KiB After Width: | Height: | Size: 742 KiB |
BIN
public/images/banner_corner.png
Normal file
After Width: | Height: | Size: 187 KiB |
@ -1 +1 @@
|
|||||||
<svg viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg"><path d="M12 2C6.486 2 2 6.486 2 12s4.486 10 10 10 10-4.486 10-10S17.514 2 12 2zm0 18c-4.411 0-8-3.589-8-8s3.589-8 8-8 8 3.589 8 8-3.589 8-8 8z" fill="#3856cf" class="fill-000000"></path><circle cx="7.5" cy="10.5" r="1.5" fill="#3856cf" class="fill-000000"></circle><circle cx="10.5" cy="7.5" r="1.5" fill="#3856cf" class="fill-000000"></circle><circle cx="11.5" cy="11.5" r="1.5" fill="#3856cf" class="fill-000000"></circle></svg>
|
<svg xmlns="http://www.w3.org/2000/svg" width="192" height="192" fill="#4343e5" viewBox="0 0 256 256"><rect width="256" height="256" fill="none"></rect><path d="M128,92a20,20,0,0,0-40,0v24" fill="none" stroke="#4343e5" stroke-linecap="round" stroke-linejoin="round" stroke-width="24"></path><path d="M168,108V92a20,20,0,0,0-40,0v32" fill="none" stroke="#4343e5" stroke-linecap="round" stroke-linejoin="round" stroke-width="24"></path><path d="M88,148V116H68a20.1,20.1,0,0,0-20,20v16a80,80,0,0,0,160,0V108a20,20,0,0,0-40,0v16" fill="none" stroke="#4343e5" stroke-linecap="round" stroke-linejoin="round" stroke-width="24"></path></svg>
|
Before Width: | Height: | Size: 491 B After Width: | Height: | Size: 633 B |
9
public/library/vitest.svg
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="256px" height="234px" viewBox="0 0 256 234" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid">
|
||||||
|
<title>vitest</title>
|
||||||
|
<g>
|
||||||
|
<path d="M192.115018,70.8083821 L130.914358,159.296327 C130.265568,160.234541 129.327185,160.937354 128.241634,161.298075 C127.156084,161.658796 125.982639,161.655409 124.899291,161.292995 C123.815942,160.928886 122.881962,160.222686 122.23876,159.282779 C121.595728,158.341178 121.278531,157.216676 121.335772,156.08032 L123.78512,107.225806 L84.2429694,98.839802 C83.3978997,98.6607962 82.6105792,98.2768738 81.950612,97.7222438 C81.2906447,97.1677833 80.7783529,96.4595505 80.4592925,95.6603753 C80.1402321,94.8612002 80.0242256,93.9958081 80.121434,93.1407466 C80.2186424,92.2858544 80.5260175,91.4675424 81.0164627,90.7584629 L142.217801,2.27034881 C142.86676,1.33162707 143.805143,0.628982928 144.890693,0.268769843 C145.976413,-0.0914432423 147.149858,-0.0895803631 148.233376,0.27435848 C149.316725,0.638127971 150.250874,1.34398981 150.893906,2.28491313 C151.536938,3.2256671 151.853966,4.34999934 151.796556,5.48737173 L149.347208,54.3423934 L188.88885,62.7277204 C189.73392,62.9067262 190.52141,63.2906486 191.181885,63.8451092 C191.842361,64.3997391 192.353806,65.1079719 192.672189,65.9071471 C192.992265,66.7063222 193.107425,67.5717143 193.010894,68.4266064 C192.914363,69.281668 192.606141,70.09998 192.115018,70.8090595 L192.115018,70.8083821 Z" fill="#FCC72B"></path>
|
||||||
|
<path d="M128.024524,233.537148 C126.396707,233.538835 124.784639,233.220452 123.280787,232.597234 C121.776936,231.974016 120.410937,231.059512 119.261541,229.907914 L61.4337079,172.084145 C59.1203507,169.758934 57.8236174,166.610668 57.8278409,163.330307 C57.8322544,160.049946 59.1372859,156.903374 61.4570785,154.584936 C63.7767018,152.264805 66.9217498,150.959096 70.20228,150.954015 C73.4829795,150.950628 76.631584,152.246176 78.9576426,154.559533 L128.024524,203.620996 L234.917207,96.7333937 C237.247499,94.4406975 240.388991,93.1617463 243.657497,93.1750213 C246.927697,93.1883347 250.059027,94.4928582 252.368997,96.8043525 C254.680661,99.1160161 255.98637,102.247177 256,105.516191 C256.011773,108.785206 254.73316,111.927036 252.440126,114.257159 L136.785306,229.907914 C135.636079,231.061206 134.270419,231.974016 132.766907,232.597234 C131.263563,233.220452 129.651834,233.538835 128.024524,233.537148 Z" fill="#729B1B"></path>
|
||||||
|
<path d="M127.974735,233.537148 C129.602552,233.538835 131.21462,233.220452 132.718472,232.597234 C134.222323,231.974016 135.588152,231.059512 136.737718,229.907914 L194.565551,172.084145 C196.878908,169.758934 198.17615,166.610668 198.171084,163.330307 C198.167682,160.049946 196.861973,156.903374 194.541842,154.584936 C192.221711,152.264805 189.076832,150.959096 185.796471,150.954015 C182.51611,150.950628 179.367844,152.246176 177.040939,154.559533 L127.974735,203.620996 L21.0822215,96.7333937 C18.751929,94.4406975 15.6100986,93.1617463 12.3412538,93.1750213 C9.07223962,93.1883347 5.94090913,94.4928582 3.62943176,96.8043525 C1.31790358,99.1160161 0.013413991,102.247177 -1.20332295e-14,105.516191 C-0.0132082455,108.785206 1.26574296,111.927036 3.55837139,114.257159 L119.213783,229.907914 C120.36318,231.061206 121.72884,231.974016 123.232183,232.597234 C124.735696,233.220452 126.347425,233.538835 127.974735,233.537148 Z" fill-opacity="0.5" fill="#729B1B"></path>
|
||||||
|
</g>
|
||||||
|
</svg>
|
After Width: | Height: | Size: 3.4 KiB |
@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"theme_color": "#36558F",
|
"theme_color": "#4343E5",
|
||||||
"background_color": "#36558F",
|
"background_color": "#050505",
|
||||||
"display": "minimal-ui",
|
"display": "minimal-ui",
|
||||||
"scope": "/",
|
"scope": "/",
|
||||||
"start_url": "/",
|
"start_url": "/",
|
||||||
@ -27,6 +27,12 @@
|
|||||||
"src": "/icons/icon-512x512.png",
|
"src": "/icons/icon-512x512.png",
|
||||||
"sizes": "512x512",
|
"sizes": "512x512",
|
||||||
"type": "image/png"
|
"type": "image/png"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"src": "/icons/maskable_icon.png",
|
||||||
|
"sizes": "196x196",
|
||||||
|
"type": "image/png",
|
||||||
|
"purpose": "maskable"
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
}
|
}
|
13
src/_tests_/categories.test.tsx
Normal file
@ -0,0 +1,13 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { test, expect, describe } from "vitest";
|
||||||
|
import { render, screen } from "@testing-library/react";
|
||||||
|
import "@testing-library/jest-dom";
|
||||||
|
import Categories from "@/layout/header/categories";
|
||||||
|
|
||||||
|
describe("Categories", () => {
|
||||||
|
test("renders learn react link", () => {
|
||||||
|
render(<Categories />);
|
||||||
|
const showText = screen.getByText(/software/i);
|
||||||
|
expect(showText).toBeInTheDocument();
|
||||||
|
});
|
||||||
|
});
|
@ -1,7 +1,12 @@
|
|||||||
import React, { FC } from "react";
|
import React, { FC } from "react";
|
||||||
import { motion } from "framer-motion";
|
import { motion } from "framer-motion";
|
||||||
|
|
||||||
const Show = ({ children, delay }) => {
|
type ShowProps = {
|
||||||
|
children: React.ReactNode;
|
||||||
|
delay?: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Show = ({ children, delay }: ShowProps) => {
|
||||||
return (
|
return (
|
||||||
<motion.div
|
<motion.div
|
||||||
initial={{ y: 10, opacity: 0 }}
|
initial={{ y: 10, opacity: 0 }}
|
16
src/animations/tap.tsx
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
|
||||||
|
type TapAnimation = {
|
||||||
|
children: React.ReactNode;
|
||||||
|
};
|
||||||
|
|
||||||
|
const Tap = ({ children } : TapAnimation) => {
|
||||||
|
return (
|
||||||
|
<motion.div whileTap={{ scale: 0.97 }}>
|
||||||
|
{children}
|
||||||
|
</motion.div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Tap;
|
13
src/common/grid.tsx
Normal 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="160px" spacing="30px" >
|
||||||
|
{props.children}
|
||||||
|
</SimpleGrid>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Grid;
|
16
src/common/iconBtn.tsx
Normal 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;
|
20
src/common/link.tsx
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
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" }}
|
||||||
|
_focus={{ border: "none" }}
|
||||||
|
>
|
||||||
|
{children}
|
||||||
|
</Link>
|
||||||
|
</NextLink>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CustomLink;
|
47
src/components/error.tsx
Normal file
@ -0,0 +1,47 @@
|
|||||||
|
import { ErrorProps } from "@/interfaces/components";
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Center,
|
||||||
|
Heading,
|
||||||
|
HStack,
|
||||||
|
Text,
|
||||||
|
VStack,
|
||||||
|
} from "@chakra-ui/react";
|
||||||
|
import { ArrowClockwise, ArrowSquareOut, Warning } from "phosphor-react";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import CustomLink from "@/common/link";
|
||||||
|
|
||||||
|
const Error = (props: ErrorProps) => {
|
||||||
|
const router = useRouter();
|
||||||
|
|
||||||
|
const handleRefresh = () => {
|
||||||
|
router.reload();
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Center>
|
||||||
|
<VStack>
|
||||||
|
<Warning size={90} />
|
||||||
|
<Heading fontSize="3xl">{props.title}</Heading>
|
||||||
|
<Text>{props.description}</Text>
|
||||||
|
<HStack>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
borderWidth="1px"
|
||||||
|
leftIcon={<ArrowClockwise />}
|
||||||
|
onClick={handleRefresh}
|
||||||
|
>
|
||||||
|
Refresh
|
||||||
|
</Button>
|
||||||
|
<CustomLink href="https://github.com/pheralb/svgl/issues/new" external={true}>
|
||||||
|
<Button variant="ghost" rightIcon={<ArrowSquareOut />}>
|
||||||
|
Create issue
|
||||||
|
</Button>
|
||||||
|
</CustomLink>
|
||||||
|
</HStack>
|
||||||
|
</VStack>
|
||||||
|
</Center>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Error;
|
16
src/components/loading.tsx
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import { LoadingProps } from "@/interfaces/components";
|
||||||
|
import { Center, Spinner, Text, VStack } from "@chakra-ui/react";
|
||||||
|
import { LeapFrog } from "@uiball/loaders";
|
||||||
|
|
||||||
|
const Loading = (props: LoadingProps) => {
|
||||||
|
return (
|
||||||
|
<Center>
|
||||||
|
<VStack spacing={3} mt="3">
|
||||||
|
<LeapFrog size={32} speed={2.5} color="#4343E5" />
|
||||||
|
<Text>{props.text}</Text>
|
||||||
|
</VStack>
|
||||||
|
</Center>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Loading;
|
84
src/components/search.tsx
Normal file
@ -0,0 +1,84 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
import { Input, Text, Image, HStack, Box, Center } from "@chakra-ui/react";
|
||||||
|
import useDebounce from "@/hooks/useDebounce";
|
||||||
|
import { SVGCardProps } from "@/interfaces/components";
|
||||||
|
import CustomLink from "@/common/link";
|
||||||
|
import { getSvgByQuery } from "@/services";
|
||||||
|
import CustomIconBtn from "@/common/iconBtn";
|
||||||
|
import { Trash } from "phosphor-react";
|
||||||
|
import Tap from "@/animations/tap";
|
||||||
|
|
||||||
|
const Search = () => {
|
||||||
|
const [search, setSearch] = useState("");
|
||||||
|
const [results, setResults] = useState<SVGCardProps[]>([]);
|
||||||
|
const debouncedSearch = useDebounce(search, 500);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (debouncedSearch) {
|
||||||
|
fetch(getSvgByQuery + debouncedSearch).then((res) => {
|
||||||
|
if (res.ok) {
|
||||||
|
res.json().then((data) => {
|
||||||
|
setResults(data);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}, [debouncedSearch]);
|
||||||
|
|
||||||
|
const handleClear = () => {
|
||||||
|
setSearch("");
|
||||||
|
setResults([]);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Input
|
||||||
|
width="full"
|
||||||
|
variant="flushed"
|
||||||
|
size="lg"
|
||||||
|
placeholder="Search svgs..."
|
||||||
|
value={search}
|
||||||
|
onChange={(e) => setSearch(e.target.value)}
|
||||||
|
/>
|
||||||
|
{results && results.length > 0 && (
|
||||||
|
<>
|
||||||
|
<HStack spacing={4} mt={4} overflowX="auto" overflowY="hidden">
|
||||||
|
{results.map((item: SVGCardProps) => (
|
||||||
|
<Tap key={item.title}>
|
||||||
|
<CustomLink href={`/svg/${item.id}`}>
|
||||||
|
<Box
|
||||||
|
mb="2"
|
||||||
|
p="3"
|
||||||
|
shadow="sm"
|
||||||
|
borderWidth="1px"
|
||||||
|
borderRadius="5px"
|
||||||
|
width="100%"
|
||||||
|
>
|
||||||
|
<Center>
|
||||||
|
<Image
|
||||||
|
width="25px"
|
||||||
|
mb="2"
|
||||||
|
src={item.slug}
|
||||||
|
alt={item.title}
|
||||||
|
/>
|
||||||
|
</Center>
|
||||||
|
<Text>{item.title}</Text>
|
||||||
|
</Box>
|
||||||
|
</CustomLink>
|
||||||
|
</Tap>
|
||||||
|
))}
|
||||||
|
</HStack>
|
||||||
|
<Box p="3">
|
||||||
|
<CustomIconBtn
|
||||||
|
title="clear"
|
||||||
|
icon={<Trash size={16} />}
|
||||||
|
onClick={handleClear}
|
||||||
|
/>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Search;
|
36
src/components/svgCard.tsx
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { SVGCardProps } from "@/interfaces/components";
|
||||||
|
import { Box, Center, Image, Text, useColorModeValue } from "@chakra-ui/react";
|
||||||
|
import Tap from "@/animations/tap";
|
||||||
|
import CustomLink from "@/common/link";
|
||||||
|
|
||||||
|
const SVGCard = (props: SVGCardProps) => {
|
||||||
|
const bg = useColorModeValue("bg.light", "bg.dark");
|
||||||
|
return (
|
||||||
|
<Tap>
|
||||||
|
<CustomLink href={`/svg/${props.id}`}>
|
||||||
|
<Box
|
||||||
|
bg={bg}
|
||||||
|
p={4}
|
||||||
|
cursor="pointer"
|
||||||
|
borderRadius="10px"
|
||||||
|
borderWidth="1px"
|
||||||
|
mb="2"
|
||||||
|
_hover={{
|
||||||
|
shadow: "md",
|
||||||
|
}}
|
||||||
|
transition="all 0.2s"
|
||||||
|
>
|
||||||
|
<Center>
|
||||||
|
<Image boxSize="50px" src={props.svg} alt={props.title} />
|
||||||
|
</Center>
|
||||||
|
<Text mt="2" fontWeight="light" textAlign="center">
|
||||||
|
{props.title}
|
||||||
|
</Text>
|
||||||
|
</Box>
|
||||||
|
</CustomLink>
|
||||||
|
</Tap>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SVGCard;
|
89
src/components/svgInfo.tsx
Normal file
@ -0,0 +1,89 @@
|
|||||||
|
import React from "react";
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Flex,
|
||||||
|
Heading,
|
||||||
|
HStack,
|
||||||
|
Icon,
|
||||||
|
Image,
|
||||||
|
Link,
|
||||||
|
} from "@chakra-ui/react";
|
||||||
|
import { ArrowSquareOut, Copy, DownloadSimple } from "phosphor-react";
|
||||||
|
import confetti from "canvas-confetti";
|
||||||
|
import download from "downloadjs";
|
||||||
|
import { toast } from "react-hot-toast";
|
||||||
|
import { ToastTheme } from "@/theme/toast";
|
||||||
|
import { SVGCardProps } from "@/interfaces/components";
|
||||||
|
|
||||||
|
// Download SVG =>
|
||||||
|
const downloadSvg = (url?: string) => {
|
||||||
|
confetti({
|
||||||
|
particleCount: 200,
|
||||||
|
startVelocity: 30,
|
||||||
|
spread: 300,
|
||||||
|
gravity: 1.2,
|
||||||
|
origin: { y: 0 },
|
||||||
|
});
|
||||||
|
download(url || "");
|
||||||
|
};
|
||||||
|
|
||||||
|
// Copy to clipboard =>
|
||||||
|
const copyToClipboard = (url?: string) => {
|
||||||
|
fetch(url || "").then((response) => {
|
||||||
|
response.text().then((content) => {
|
||||||
|
navigator.clipboard.writeText(content);
|
||||||
|
toast("Copied to clipboard", ToastTheme);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const SVGInfo = (props: SVGCardProps) => {
|
||||||
|
return (
|
||||||
|
<Flex
|
||||||
|
pt="7"
|
||||||
|
pb="7"
|
||||||
|
direction="column"
|
||||||
|
align="center"
|
||||||
|
justify="center"
|
||||||
|
borderWidth="1px"
|
||||||
|
borderRadius="10px"
|
||||||
|
>
|
||||||
|
<Image
|
||||||
|
src={props.slug}
|
||||||
|
alt={props.title}
|
||||||
|
fit="cover"
|
||||||
|
loading="lazy"
|
||||||
|
width="85px"
|
||||||
|
/>
|
||||||
|
<Heading mt={6} mb={6} fontSize="4xl">
|
||||||
|
{props.title}
|
||||||
|
</Heading>
|
||||||
|
<Flex direction={{ base: "column", md: "row" }}>
|
||||||
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
borderWidth="1px"
|
||||||
|
leftIcon={<Copy />}
|
||||||
|
onClick={() => copyToClipboard(props.slug)}
|
||||||
|
mb={{ base: "2", md: "0" }}
|
||||||
|
mr={{ base: "0", md: "3" }}
|
||||||
|
>
|
||||||
|
Copy to clipboard
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
leftIcon={<DownloadSimple />}
|
||||||
|
variant="primary"
|
||||||
|
onClick={() => downloadSvg(props.slug)}
|
||||||
|
mb={{ base: "2", md: "0" }}
|
||||||
|
mr={{ base: "0", md: "3" }}
|
||||||
|
>
|
||||||
|
Download .svg
|
||||||
|
</Button>
|
||||||
|
<Link href={props.url} isExternal={true}>
|
||||||
|
{props.title} website <Icon as={ArrowSquareOut} mt="2" />
|
||||||
|
</Link>
|
||||||
|
</Flex>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default SVGInfo;
|
17
src/hooks/useDebounce.tsx
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import { useEffect, useState } from "react";
|
||||||
|
|
||||||
|
function useDebounce<T>(value: T, delay?: number): T {
|
||||||
|
const [debouncedValue, setDebouncedValue] = useState<T>(value);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const timer = setTimeout(() => setDebouncedValue(value), delay || 500);
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
clearTimeout(timer);
|
||||||
|
};
|
||||||
|
}, [value, delay]);
|
||||||
|
|
||||||
|
return debouncedValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useDebounce;
|
39
src/interfaces/components.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
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 {
|
||||||
|
id: number;
|
||||||
|
svg: string;
|
||||||
|
title: string;
|
||||||
|
slug?: string;
|
||||||
|
url?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface SidebarContentProps {
|
||||||
|
display?: object;
|
||||||
|
w?: string;
|
||||||
|
borderRight?: string;
|
||||||
|
children?: React.ReactNode;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface LoadingProps {
|
||||||
|
text: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export interface ErrorProps {
|
||||||
|
title: string;
|
||||||
|
description: string;
|
||||||
|
}
|
8
src/interfaces/svgData.ts
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
export interface SvgData {
|
||||||
|
id: number;
|
||||||
|
slug: string;
|
||||||
|
title: string;
|
||||||
|
category: string;
|
||||||
|
categories?: string[];
|
||||||
|
url: string;
|
||||||
|
}
|
21
src/layout/footer/index.tsx
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
import CustomLink from "@/common/link";
|
||||||
|
import { Flex, Heading, HStack, Icon, Text } from "@chakra-ui/react";
|
||||||
|
import { RocketLaunch, TwitterLogo } from "phosphor-react";
|
||||||
|
import React from "react";
|
||||||
|
|
||||||
|
type Props = {};
|
||||||
|
|
||||||
|
const Index = (props: Props) => {
|
||||||
|
return (
|
||||||
|
<Flex direction="column" pt="8" pb="8" justifyContent="center" alignItems="center">
|
||||||
|
<HStack>
|
||||||
|
<Icon as={RocketLaunch} />
|
||||||
|
<CustomLink href="https://twitter.com/pheralb_" external={true}>
|
||||||
|
Created by Pablo
|
||||||
|
</CustomLink>
|
||||||
|
</HStack>
|
||||||
|
</Flex>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Index;
|
26
src/layout/header/categories.tsx
Normal file
@ -0,0 +1,26 @@
|
|||||||
|
import React from "react";
|
||||||
|
import useSWR from "swr";
|
||||||
|
import { getCategorySvgs } from "@/services";
|
||||||
|
import CustomLink from "@/common/link";
|
||||||
|
import { Box } from "@chakra-ui/react";
|
||||||
|
import { RaceBy } from "@uiball/loaders";
|
||||||
|
|
||||||
|
const Categories = () => {
|
||||||
|
const { data, error } = useSWR(getCategorySvgs);
|
||||||
|
|
||||||
|
if (error) return <div>failed to load</div>;
|
||||||
|
if (!data)
|
||||||
|
return <RaceBy size={52} lineWeight={3} speed={1.4} color="#4343E5" />;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{data.map((category: string) => (
|
||||||
|
<Box key={category} p="4" borderRadius="5px" borderWidth="1px">
|
||||||
|
<CustomLink href={`/category/${category}`}>{category}</CustomLink>
|
||||||
|
</Box>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Categories;
|
111
src/layout/header/index.tsx
Normal file
@ -0,0 +1,111 @@
|
|||||||
|
import React from "react";
|
||||||
|
import {
|
||||||
|
Box,
|
||||||
|
Flex,
|
||||||
|
useColorModeValue,
|
||||||
|
HStack,
|
||||||
|
Button,
|
||||||
|
Container,
|
||||||
|
Heading,
|
||||||
|
Icon,
|
||||||
|
useDisclosure,
|
||||||
|
Collapse,
|
||||||
|
} from "@chakra-ui/react";
|
||||||
|
import { ArrowSquareOut, MagnifyingGlass, Sticker, X } from "phosphor-react";
|
||||||
|
import Theme from "./theme";
|
||||||
|
import Tap from "@/animations/tap";
|
||||||
|
import Mobile from "./mobile";
|
||||||
|
import { Links } from "./links";
|
||||||
|
import CustomLink from "@/common/link";
|
||||||
|
import Categories from "./categories";
|
||||||
|
import Search from "@/components/search";
|
||||||
|
import CustomIconBtn from "@/common/iconBtn";
|
||||||
|
|
||||||
|
const Header = () => {
|
||||||
|
const bg = useColorModeValue("bg.light", "bg.dark");
|
||||||
|
const { isOpen, onToggle } = useDisclosure();
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Box
|
||||||
|
as="header"
|
||||||
|
position="sticky"
|
||||||
|
top="0"
|
||||||
|
bg={bg}
|
||||||
|
borderBottomWidth="1px"
|
||||||
|
w="full"
|
||||||
|
py={6}
|
||||||
|
zIndex={1}
|
||||||
|
shadow="sm"
|
||||||
|
>
|
||||||
|
<Container maxW={{ base: "full", md: "70%" }}>
|
||||||
|
<Flex alignItems="center" justifyContent="space-between" mx="auto">
|
||||||
|
<CustomLink href="/">
|
||||||
|
<Tap>
|
||||||
|
<HStack spacing={3} cursor="pointer">
|
||||||
|
<Sticker size={32} color="#4343e5" weight="bold" />
|
||||||
|
<Heading fontSize="19px">svgl</Heading>
|
||||||
|
</HStack>
|
||||||
|
</Tap>
|
||||||
|
</CustomLink>
|
||||||
|
<HStack display="flex" alignItems="center" spacing={1}>
|
||||||
|
<HStack
|
||||||
|
spacing={1}
|
||||||
|
mr={1}
|
||||||
|
display={{ base: "none", md: "inline-flex" }}
|
||||||
|
>
|
||||||
|
{Links.map((link) => (
|
||||||
|
<CustomLink
|
||||||
|
key={link.title}
|
||||||
|
href={link.slug}
|
||||||
|
external={link.external}
|
||||||
|
>
|
||||||
|
<Button variant="ghost" fontFamily="Inter-Semibold">
|
||||||
|
{link.title}
|
||||||
|
{link.external ? (
|
||||||
|
<Icon as={ArrowSquareOut} ml="2" />
|
||||||
|
) : null}
|
||||||
|
</Button>
|
||||||
|
</CustomLink>
|
||||||
|
))}
|
||||||
|
<CustomIconBtn
|
||||||
|
title="Toggle Search bar"
|
||||||
|
icon={
|
||||||
|
isOpen ? <X size={22} /> : <MagnifyingGlass size={22} />
|
||||||
|
}
|
||||||
|
onClick={onToggle}
|
||||||
|
/>
|
||||||
|
<Theme />
|
||||||
|
</HStack>
|
||||||
|
<Box display={{ base: "inline-flex", md: "none" }}>
|
||||||
|
<Mobile />
|
||||||
|
</Box>
|
||||||
|
</HStack>
|
||||||
|
</Flex>
|
||||||
|
<Collapse in={isOpen} animateOpacity>
|
||||||
|
<Box mt="3">
|
||||||
|
<Search />
|
||||||
|
</Box>
|
||||||
|
</Collapse>
|
||||||
|
<Box mt="2" display={{ base: "block", md: "none" }}>
|
||||||
|
<Search />
|
||||||
|
</Box>
|
||||||
|
</Container>
|
||||||
|
</Box>
|
||||||
|
<Box p="4" overflowX="hidden" overflowY="auto">
|
||||||
|
<HStack
|
||||||
|
justifyContent={{ base: "none", md: "center" }}
|
||||||
|
spacing={4}
|
||||||
|
overflowX="auto"
|
||||||
|
overflowY="hidden"
|
||||||
|
bg={bg}
|
||||||
|
pb="4"
|
||||||
|
borderBottomWidth="1px"
|
||||||
|
>
|
||||||
|
<Categories />
|
||||||
|
</HStack>
|
||||||
|
</Box>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Header;
|
12
src/layout/header/links.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
export const Links = [
|
||||||
|
{
|
||||||
|
title: "Github",
|
||||||
|
slug: "https://github.com/pheralb/svgl",
|
||||||
|
external: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: "Twitter",
|
||||||
|
slug: "https://twitter.com/pheralb_",
|
||||||
|
external: true,
|
||||||
|
},
|
||||||
|
];
|
57
src/layout/header/mobile.tsx
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
import CustomLink from "@/common/link";
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
CloseButton,
|
||||||
|
IconButton,
|
||||||
|
useColorModeValue,
|
||||||
|
useDisclosure,
|
||||||
|
VStack,
|
||||||
|
} from "@chakra-ui/react";
|
||||||
|
import { List } from "phosphor-react";
|
||||||
|
import { Links } from "./links";
|
||||||
|
|
||||||
|
const Mobile = () => {
|
||||||
|
const bg = useColorModeValue("bg.light", "bg.dark");
|
||||||
|
const mobileNav = useDisclosure();
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<IconButton
|
||||||
|
display={{ base: "flex", md: "none" }}
|
||||||
|
aria-label="Open menu navbar"
|
||||||
|
variant="ghost"
|
||||||
|
icon={<List size={22} />}
|
||||||
|
onClick={mobileNav.onOpen}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<VStack
|
||||||
|
pos="absolute"
|
||||||
|
top={0}
|
||||||
|
left={0}
|
||||||
|
right={0}
|
||||||
|
display={mobileNav.isOpen ? "flex" : "none"}
|
||||||
|
flexDirection="column"
|
||||||
|
p={4}
|
||||||
|
pb={4}
|
||||||
|
bg={bg}
|
||||||
|
spacing={3}
|
||||||
|
rounded="sm"
|
||||||
|
shadow="sm"
|
||||||
|
borderWidth="1px"
|
||||||
|
zIndex={2}
|
||||||
|
>
|
||||||
|
<CloseButton aria-label="Close menu" onClick={mobileNav.onClose} />
|
||||||
|
{Links.map((link) => (
|
||||||
|
<CustomLink
|
||||||
|
key={link.title}
|
||||||
|
href={link.slug}
|
||||||
|
external={link.external}
|
||||||
|
>
|
||||||
|
<Button variant="ghost">{link.title}</Button>
|
||||||
|
</CustomLink>
|
||||||
|
))}
|
||||||
|
</VStack>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Mobile;
|
33
src/layout/header/theme.tsx
Normal file
@ -0,0 +1,33 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { AnimatePresence, motion } from "framer-motion";
|
||||||
|
import { useColorMode, useColorModeValue } from "@chakra-ui/react";
|
||||||
|
import CustomIconBtn from "@/common/iconBtn";
|
||||||
|
import { Moon, Sun } from "phosphor-react";
|
||||||
|
|
||||||
|
const Theme = () => {
|
||||||
|
const { toggleColorMode } = useColorMode();
|
||||||
|
const key = useColorModeValue("light", "dark");
|
||||||
|
const icon = useColorModeValue(
|
||||||
|
<Moon size={22} />,
|
||||||
|
<Sun size={22} />
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<AnimatePresence exitBeforeEnter initial={false}>
|
||||||
|
<motion.div
|
||||||
|
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;
|
17
src/layout/index.tsx
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
import React from "react";
|
||||||
|
import { LayoutProps } from "@/interfaces/components";
|
||||||
|
import { Container } from "@chakra-ui/react";
|
||||||
|
import Header from "./header";
|
||||||
|
import Footer from "./footer";
|
||||||
|
|
||||||
|
const Index = ({ children }: LayoutProps) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Header />
|
||||||
|
<Container maxW={{ base: "100%", md: "70%" }} mt={{ base: "1", md: "3" }}>{children}</Container>
|
||||||
|
<Footer />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Index;
|
9
src/pages/404.tsx
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import React from "react";
|
||||||
|
|
||||||
|
type Props = {};
|
||||||
|
|
||||||
|
const Error = (props: Props) => {
|
||||||
|
return <div>Error 404</div>;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Error;
|
63
src/pages/_app.tsx
Normal file
@ -0,0 +1,63 @@
|
|||||||
|
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";
|
||||||
|
|
||||||
|
// Nextjs Progressbar ->
|
||||||
|
import NextNProgress from "nextjs-progressbar";
|
||||||
|
|
||||||
|
// Framer ->
|
||||||
|
import { motion } from "framer-motion";
|
||||||
|
|
||||||
|
// SWR Config & services ->
|
||||||
|
import { SWRConfig } from "swr";
|
||||||
|
import { fetcher } from "@/services/fetcher";
|
||||||
|
|
||||||
|
// React Hot Toast ->
|
||||||
|
import { Toaster } from "react-hot-toast";
|
||||||
|
import { DefaultSeo } from "next-seo";
|
||||||
|
import nextSeoConfig from "next-seo.config";
|
||||||
|
|
||||||
|
function MyApp({ Component, pageProps, router }: AppProps) {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<DefaultSeo {...nextSeoConfig} />
|
||||||
|
<NextNProgress
|
||||||
|
color="#4343E5"
|
||||||
|
startPosition={0.3}
|
||||||
|
stopDelayMs={200}
|
||||||
|
height={2}
|
||||||
|
showOnShallow={true}
|
||||||
|
/>
|
||||||
|
<ChakraProvider theme={theme}>
|
||||||
|
<SWRConfig value={{ fetcher }}>
|
||||||
|
<Layout>
|
||||||
|
<motion.div
|
||||||
|
key={router.route}
|
||||||
|
initial="initial"
|
||||||
|
animate="animate"
|
||||||
|
variants={{
|
||||||
|
initial: {
|
||||||
|
opacity: 0,
|
||||||
|
},
|
||||||
|
animate: {
|
||||||
|
opacity: 1,
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Component {...pageProps} />
|
||||||
|
</motion.div>
|
||||||
|
</Layout>
|
||||||
|
</SWRConfig>
|
||||||
|
</ChakraProvider>
|
||||||
|
<Toaster position="bottom-center" reverseOrder={false} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default MyApp;
|
@ -1,16 +1,12 @@
|
|||||||
import { ColorModeScript } from "@chakra-ui/react";
|
import { ColorModeScript } from "@chakra-ui/react";
|
||||||
import NextDocument, { Html, Head, Main, NextScript } from "next/document";
|
import NextDocument, { Html, Head, Main, NextScript } from "next/document";
|
||||||
import theme from "styles/theme";
|
import theme from "@/theme";
|
||||||
|
|
||||||
export default class Document extends NextDocument {
|
export default class Document extends NextDocument {
|
||||||
render() {
|
render() {
|
||||||
return (
|
return (
|
||||||
<Html>
|
<Html lang="en">
|
||||||
<Head>
|
<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>
|
<body>
|
||||||
<ColorModeScript initialColorMode={theme.config.initialColorMode} />
|
<ColorModeScript initialColorMode={theme.config.initialColorMode} />
|
||||||
<Main />
|
<Main />
|
12
src/pages/api/all.ts
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
import db from "data/svgs.json";
|
||||||
|
import type { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
import { SvgData } from "@/interfaces/svgData";
|
||||||
|
|
||||||
|
export default function handler(
|
||||||
|
req: NextApiRequest,
|
||||||
|
res: NextApiResponse<SvgData[]>
|
||||||
|
) {
|
||||||
|
// Begin with the last id in the db:
|
||||||
|
const svgs = db.sort((a, b) => b.id - a.id);
|
||||||
|
return res.status(200).json(svgs);
|
||||||
|
}
|
10
src/pages/api/categories.ts
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
import type { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
import db from "data/svgs.json";
|
||||||
|
|
||||||
|
export default function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
|
// Get unique categories:
|
||||||
|
const categories = db
|
||||||
|
.map((svg) => svg.category)
|
||||||
|
.filter((category, index, array) => array.indexOf(category) === index);
|
||||||
|
res.status(200).json(categories);
|
||||||
|
}
|
@ -1,6 +1,7 @@
|
|||||||
import db from "data/svgs";
|
import db from "data/svgs.json";
|
||||||
|
import { NextApiRequest, NextApiResponse } from "next";
|
||||||
|
|
||||||
export default function handler(req, res) {
|
export default function handler(req: NextApiRequest, res: NextApiResponse) {
|
||||||
const { id, q, c } = req.query;
|
const { id, q, c } = req.query;
|
||||||
|
|
||||||
// 🔎 Search by id (ex: ?id=1) ->
|
// 🔎 Search by id (ex: ?id=1) ->
|
||||||
@ -13,7 +14,7 @@ export default function handler(req, res) {
|
|||||||
if (q) {
|
if (q) {
|
||||||
const results = db.filter((product) => {
|
const results = db.filter((product) => {
|
||||||
const { title } = product;
|
const { title } = product;
|
||||||
return title.toLowerCase().includes(q.toLowerCase());
|
return title.toLowerCase().includes(q.toString().toLowerCase());
|
||||||
});
|
});
|
||||||
return res.status(200).json(results);
|
return res.status(200).json(results);
|
||||||
}
|
}
|
||||||
@ -22,11 +23,11 @@ export default function handler(req, res) {
|
|||||||
if (c) {
|
if (c) {
|
||||||
const results = db.filter((product) => {
|
const results = db.filter((product) => {
|
||||||
const { category } = product;
|
const { category } = product;
|
||||||
return category.toLowerCase().includes(c.toLowerCase());
|
return category.toLowerCase().includes(c.toString().toLowerCase());
|
||||||
});
|
});
|
||||||
return res.status(200).json(results);
|
return res.status(200).json(results);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ✖ Error ->
|
// ✖ Error ->
|
||||||
res.status(400).json({ info: 'Error: api query not found.' });
|
res.status(400).json({ info: "[/api/search] Error: api query not found." });
|
||||||
}
|
}
|
39
src/pages/category/[category].tsx
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
import Head from "next/head";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import useSWR from "swr";
|
||||||
|
import { getSvgByCategory } from "@/services";
|
||||||
|
import Loading from "@/components/loading";
|
||||||
|
import Grid from "@/common/grid";
|
||||||
|
import SVGCard from "@/components/svgCard";
|
||||||
|
import { SvgData } from "@/interfaces/svgData";
|
||||||
|
import { Center, Heading } from "@chakra-ui/react";
|
||||||
|
import Show from "@/animations/show";
|
||||||
|
|
||||||
|
export default function Category() {
|
||||||
|
const router = useRouter();
|
||||||
|
const { data, error } = useSWR(
|
||||||
|
() => router.query.category && `${getSvgByCategory}${router.query.category}`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (error) router.push("/404");
|
||||||
|
if (!data) return <Loading text="Loading..." />;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>{router.query.category} logos - svgl</title>
|
||||||
|
</Head>
|
||||||
|
<Show>
|
||||||
|
<Center>
|
||||||
|
<Heading mb="5">{router.query.category}</Heading>
|
||||||
|
</Center>
|
||||||
|
</Show>
|
||||||
|
|
||||||
|
<Grid>
|
||||||
|
{data.map((svg: SvgData) => (
|
||||||
|
<SVGCard key={svg.id} id={svg.id} svg={svg.slug} title={svg.title} />
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
28
src/pages/index.tsx
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
import type { NextPage } from "next";
|
||||||
|
import useSWR from "swr";
|
||||||
|
import { getAllSvgs } from "@/services";
|
||||||
|
import { SvgData } from "@/interfaces/svgData";
|
||||||
|
import SVGCard from "@/components/svgCard";
|
||||||
|
import Grid from "@/common/grid";
|
||||||
|
import Loading from "@/components/loading";
|
||||||
|
import Error from "@/components/error";
|
||||||
|
|
||||||
|
const Home: NextPage = () => {
|
||||||
|
const { data, error } = useSWR(getAllSvgs);
|
||||||
|
|
||||||
|
if (error)
|
||||||
|
return (
|
||||||
|
<Error title="Error" description="An unexpected error has occurred" />
|
||||||
|
);
|
||||||
|
if (!data) return <Loading text="Loading..." />;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Grid>
|
||||||
|
{data.map((svg: SvgData) => (
|
||||||
|
<SVGCard key={svg.id} id={svg.id} svg={svg.slug} title={svg.title} />
|
||||||
|
))}
|
||||||
|
</Grid>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Home;
|
29
src/pages/svg/[id].tsx
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
import Head from "next/head";
|
||||||
|
import { useRouter } from "next/router";
|
||||||
|
import useSWR from "swr";
|
||||||
|
|
||||||
|
import Show from "@/animations/show";
|
||||||
|
import { getSvgById } from "@/services";
|
||||||
|
import Loading from "@/components/loading";
|
||||||
|
import SVGInfo from "@/components/svgInfo";
|
||||||
|
|
||||||
|
export default function Icon() {
|
||||||
|
const router = useRouter();
|
||||||
|
const { data, error } = useSWR(
|
||||||
|
() => router.query.id && `${getSvgById}${router.query.id}`
|
||||||
|
);
|
||||||
|
|
||||||
|
if (error) router.push("/404");
|
||||||
|
if (!data) return <Loading text="Loading..." />;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
<Head>
|
||||||
|
<title>{data.title} - svgl</title>
|
||||||
|
</Head>
|
||||||
|
<Show>
|
||||||
|
<SVGInfo {...data} />
|
||||||
|
</Show>
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
}
|
10
src/services/fetcher.ts
Normal 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();
|
||||||
|
};
|
6
src/services/index.ts
Normal file
@ -0,0 +1,6 @@
|
|||||||
|
export const githubVersionPackage = 'https://api.github.com/repos/pheralb/svgl/releases/latest';
|
||||||
|
export const getAllSvgs = "/api/all";
|
||||||
|
export const getCategorySvgs = "/api/categories";
|
||||||
|
export const getSvgById = "/api/search?id=";
|
||||||
|
export const getSvgByQuery = "/api/search?q=";
|
||||||
|
export const getSvgByCategory = "/api/search?c=";
|
16
src/styles/globals.css
Normal 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;
|
||||||
|
}
|
44
src/theme/components/button.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
const baseStyle = {
|
||||||
|
borderRadius: "md",
|
||||||
|
fontWeight: "light",
|
||||||
|
};
|
||||||
|
|
||||||
|
function variantPrimary() {
|
||||||
|
const disabled = {
|
||||||
|
bg: "purple.900",
|
||||||
|
color: "white",
|
||||||
|
};
|
||||||
|
|
||||||
|
const loading = {
|
||||||
|
bg: "purple.800",
|
||||||
|
color: "white",
|
||||||
|
};
|
||||||
|
|
||||||
|
return {
|
||||||
|
bg: "brand.purple",
|
||||||
|
color: "white",
|
||||||
|
_hover: {
|
||||||
|
bg: "purple.900",
|
||||||
|
_disabled: {
|
||||||
|
...disabled,
|
||||||
|
_loading: loading,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
_active: {
|
||||||
|
bg: "purple.700",
|
||||||
|
},
|
||||||
|
_disabled: {
|
||||||
|
...disabled,
|
||||||
|
_loading: loading,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
const variants = {
|
||||||
|
primary: variantPrimary,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default {
|
||||||
|
baseStyle,
|
||||||
|
variants,
|
||||||
|
};
|
44
src/theme/index.ts
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
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: "#F2F2F2",
|
||||||
|
dark: "#1F2023",
|
||||||
|
},
|
||||||
|
full: {
|
||||||
|
light: "#ffffff",
|
||||||
|
dark: "#000000",
|
||||||
|
},
|
||||||
|
brand: {
|
||||||
|
purple: "#4343E5",
|
||||||
|
},
|
||||||
|
},
|
||||||
|
fonts: {
|
||||||
|
body: "Inter-Regular, sans-serif",
|
||||||
|
heading: "Inter-Semibold, sans-serif",
|
||||||
|
},
|
||||||
|
styles: {
|
||||||
|
global: (props: ChakraProps) => ({
|
||||||
|
"html, body": {
|
||||||
|
height: "100%",
|
||||||
|
maxHeight: "100vh",
|
||||||
|
bg: mode("bg.light", "bg.dark")(props),
|
||||||
|
fontSize: "14px",
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
export default theme;
|