Merge pull request #39 from pheralb/v3

🚀 v3
This commit is contained in:
Pablo Hdez 2023-03-20 11:23:11 +00:00 committed by GitHub
commit b5a505c880
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
269 changed files with 1977 additions and 2540 deletions

13
.eslintignore Normal file
View File

@ -0,0 +1,13 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock

20
.eslintrc.cjs Normal file
View File

@ -0,0 +1,20 @@
module.exports = {
root: true,
parser: '@typescript-eslint/parser',
extends: ['eslint:recommended', 'plugin:@typescript-eslint/recommended', 'prettier'],
plugins: ['svelte3', '@typescript-eslint'],
ignorePatterns: ['*.cjs'],
overrides: [{ files: ['*.svelte'], processor: 'svelte3/svelte3' }],
settings: {
'svelte3/typescript': () => require('typescript')
},
parserOptions: {
sourceType: 'module',
ecmaVersion: 2020
},
env: {
browser: true,
es2017: true,
node: true
}
};

View File

@ -1,3 +0,0 @@
{
"extends": ["next/core-web-vitals"]
}

1
.github/FUNDING.yml vendored Normal file
View File

@ -0,0 +1 @@
github: pheralb

26
.github/workflows/ci.yml vendored Normal file
View File

@ -0,0 +1,26 @@
name: 🛠️ Check
on:
push:
branches:
- main
pull_request:
branches:
- main
jobs:
svelte-check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- name: Setup Node.js 18.x
uses: actions/setup-node@v3
with:
node-version: 18.x
- name: Install dependencies
run: npm install
- name: Run typecheck
run: npm run check

66
.gitignore vendored
View File

@ -1,48 +1,22 @@
# dependencies -> # Dependencies
/node_modules node_modules
/.pnp
.pnp.js
package-lock.json package-lock.json
# testing ->
/coverage
# next.js ->
/.next/
/out/
# production ->
/build
# misc ->
.DS_Store
*.pem
# debug ->
npm-debug.log*
yarn-debug.log*
yarn-error.log*
.pnpm-debug.log*
# local env files ->
.env*.local
# vercel ->
.vercel
# typescript ->
*.tsbuildinfo
# PWA files ->
**/public/sw.js
**/public/workbox-*.js
**/public/worker-*.js
**/public/sw.js.map
**/public/workbox-*.js.map
**/public/worker-*.js.map
# SWC files ->
.swc
# PNPM files ->
pnpm-lock.yaml pnpm-lock.yaml
yarn.lock
# Folders
/.svelte-kit
/build
/package
# Logs
.DS_Store
# Environment variables
.env
.env.*
!.env.example
# Vite files
vite.config.js.timestamp-*
vite.config.ts.timestamp-*

1
.npmrc Normal file
View File

@ -0,0 +1 @@
engine-strict=true

1
.nvmrc
View File

@ -1 +0,0 @@
v16.15.1

13
.prettierignore Normal file
View File

@ -0,0 +1,13 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock

9
.prettierrc Normal file
View File

@ -0,0 +1,9 @@
{
"useTabs": false,
"singleQuote": true,
"trailingComma": "none",
"printWidth": 100,
"plugins": ["prettier-plugin-svelte"],
"pluginSearchDirs": ["."],
"overrides": [{ "files": "*.svelte", "options": { "parser": "svelte" } }]
}

3
.vscode/extensions.json vendored Normal file
View File

@ -0,0 +1,3 @@
{
"recommendations": ["esbenp.prettier-vscode", "dbaeumer.vscode-eslint", "svelte.svelte-vscode"]
}

8
.vscode/settings.json vendored Normal file
View File

@ -0,0 +1,8 @@
{
"editor.codeActionsOnSave": {
"source.fixAll.eslint": true
},
"editor.defaultFormatter": "esbenp.prettier-vscode",
"editor.formatOnSave": true,
"tailwindCSS.experimental.classRegex": [["[\"'`]([^\"'`]*).*?[\"'`]"]]
}

View File

@ -1,6 +1,6 @@
MIT License MIT License
Copyright (c) 2022 Pablo Hernández Copyright (c) 2022 Pablo Hdez
Permission is hereby granted, free of charge, to any person obtaining a copy Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal of this software and associated documentation files (the "Software"), to deal
@ -18,4 +18,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE. SOFTWARE.

151
README.md
View File

@ -1,108 +1,103 @@
<p align="center"> <div align="center">
<a href="https://svgl.vercel.app/" target="_blank"> <a href="https://svgl.vercel.app">
<img src="https://i.postimg.cc/1tzrP2rg/banner-corner.png" width="800px" alt="SVGL Banner" /> <img src="static/images/screenshot.png">
</a> </a>
<p></p>
</div>
<div align="center">
<a href="https://svgl.vercel.app/" target="_blank">
Discover
</a>
<span>&nbsp;&nbsp;</span>
<a href="#-getting-started">
Submit logo
</a>
<span>&nbsp;&nbsp;</span>
<a href="#%EF%B8%8F-stack">
Stack
</a>
<span>&nbsp;&nbsp;</span>
<a href="#%EF%B8%8F-contributing">
Contributing
</a>
</div>
</p> </p>
## 📦 Packages: <div align="center">
- ⚡️ [Nextjs](https://nextjs.org/) - The React Framework for Production. [![GitHub actions](https://img.shields.io/endpoint.svg?url=https%3A%2F%2Factions-badge.atrox.dev%2Fpheralb%2Fsvgl%2Fbadge%3Fref%3Dmain&style=flat)](https://actions-badge.atrox.dev/pheralb/svgl/goto?ref=main)
- ⚒️ [React 18](https://reactjs.org/) - A JavaScript library for building user interfaces. ![GitHub stars](https://img.shields.io/github/stars/pheralb/svgl)
- 💙 [Typescript](https://www.typescriptlang.org/) - A superset of JavaScript. ![GitHub issues](https://img.shields.io/github/issues/pheralb/svgl)
- ✅ [Vitest](https://vitest.dev/) - A blazing fast unit test framework. ![GitHub forks](https://img.shields.io/github/forks/pheralb/svgl)
- 💅 [Chakra UI](https://chakra-ui.com/) - Create accessible React apps with speed. ![GitHub license](https://img.shields.io/github/license/pheralb/svgl)
- 💥 [Framer Motion](https://www.framer.com/motion/) - Production-ready motion library. ![GitHub PRs](https://img.shields.io/github/issues-pr/pheralb/svgl)
- 💖 [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: </div>
You need: ## 🛠️ Stack
- [Node.js 16+ (recommend: 16.15.1 LTS)](https://nodejs.org/en/) - [**Sveltekit**](https://kit.svelte.dev/) - Web development, streamlined.
- [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git) - [**Typescript**](https://www.typescriptlang.org/) - JavaScript with syntax for types.
- [**Tailwindcss**](https://tailwindcss.com/) - A utility-first CSS framework for rapidly building custom designs.
- [**Prettier**](https://prettier.io/) + [prettier-plugin-tailwindcss](https://github.com/tailwindlabs/prettier-plugin-tailwindcss) - An opinionated code formatter.
- [**phosphor-svelte**](https://github.com/haruaki07/phosphor-svelte) - A clean and friendly icon family for Svelte.
- [**Vitest**](https://vitest.dev/) - Blazing Fast Unit Test Framework.
1. Clone the repository: ## 🚀 Getting Started
You will need:
- [Node.js 16+ (recommended 18 LTS)](https://nodejs.org/en/).
- [Git](https://git-scm.com/).
1. Clone or [fork](https://github.com/pheralb/svgl/fork) this repository:
```bash ```bash
git@github.com:pheralb/svgl.git git clone git@github.com:pheralb/svgl.git
``` ```
2. Install dependencies: 2. Install dependencies with your favorite package manager:
```bash ```bash
# with npm:
npm install npm install
# or
# with pnpm:
pnpm install
# with ultra:
ultra install
# with yarn:
yarn install yarn install
``` ```
3. Run: 3. Go to the [**`static/library`**](https://github.com/pheralb/svgl/blob/main/static/library) folder and add your `.svg` logo. Remember to optimize SVG for web use, you can use [SVGOMG](https://jakearchibald.github.io/svgomg/).
```bash 4. Go to the [**`src/data/svgs.ts`**](https://github.com/pheralb/svgl/blob/main/src/data/svgs.ts) and add the information about your logo, following the structure:
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/blob/main/data/svgs.json).
```json ```json
{ {
"id": 1, "id": 1,
"slug": "/library/your_logo.svg", "title": "Title",
"title": "Logo Title", "category": "Category",
"category": "Logo Category", "route": "/library/your_logo.svg",
"url": "Your Website / app url" "url": "Website"
} },
``` ```
5. Create a commit and push: And create a pull request with your logo 🚀.
```bash ## ✌️ Contributing
git add .
git commit -m "🥰 Added my logo"
git push origin main
```
6. Create a pull request with your changes and 🥳 ready. <a href="https://github.com/pheralb/svgl/graphs/contributors">
<img src="https://contrib.rocks/image?repo=pheralb/svgl" />
</a>
## 🚂 Api endpoints: <p></p>
```bash ## 🔑 License
- /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:
- [MIT](https://github.com/pheralb/svgl/blob/main/LICENSE). - [MIT](https://github.com/pheralb/svgl/blob/main/LICENSE).

View File

@ -1,1010 +0,0 @@
[
{
"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/"
},
{
"id": 50,
"slug": "/library/netlify.svg",
"title": "Netlify",
"category": "Hosting",
"url": "https://www.netlify.com/"
},
{
"id": 51,
"slug": "/library/solidjs.svg",
"title": "Solidjs",
"category": "Framework",
"url": "https://www.solidjs.com/"
},
{
"id": 52,
"slug": "/library/mongodb.svg",
"title": "MongoDB",
"category": "Database",
"url": "https://www.mongodb.com/"
},
{
"id": 53,
"slug": "/library/babel.svg",
"title": "Babel",
"category": "Compiler",
"url": "https://babeljs.io"
},
{
"id": 54,
"slug": "/library/moon.svg",
"title": "Moon",
"category": "Framework",
"url": "https://moonjs.org/"
},
{
"id": 55,
"slug": "/library/payload.svg",
"title": "Payload CMS",
"category": "CMS",
"url": "https://payloadcms.com"
},
{
"id": 56,
"slug": "/library/fly.svg",
"title": "Fly",
"category": "Hosting",
"url": "https://fly.io"
},
{
"id": 57,
"slug": "/library/learnthis.svg",
"title": "LearnThis",
"category": "Education",
"url": "https://learnthisacademy.com"
},
{
"id": 58,
"slug": "/library/speackme.svg",
"title": "Speack.me",
"category": "Social",
"url": "https://speack.me"
},
{
"id": 59,
"slug": "/library/chakra-ui.svg",
"title": "Chakra UI",
"category": "Library",
"url": "https://chakra-ui.com"
},
{
"id": 60,
"slug": "/library/expressjs.svg",
"title": "Express.js",
"category": "Framework",
"url": "https://expressjs.com"
},
{
"id": 61,
"slug": "/library/fastify.svg",
"title": "Fastify",
"category": "Framework",
"url": "https://www.fastify.io"
},
{
"id": 62,
"slug": "/library/javascript.svg",
"title": "JavaScript",
"category": "Language",
"url": "https://developer.mozilla.org/docs/Web/JavaScript"
},
{
"id": 63,
"slug": "/library/jquery.svg",
"title": "jQuery",
"category": "Language",
"url": "https://jquery.com"
},
{
"id": 64,
"slug": "/library/rapidapi.svg",
"title": "Rapid API",
"category": "Software",
"url": "https://rapidapi.com"
},
{
"id": 65,
"slug": "/library/typescript.svg",
"title": "TypeScript",
"category": "Language",
"url": "https://www.typescriptlang.org"
},
{
"id": 66,
"slug": "/library/bun.svg",
"title": "Bun",
"category": "Library",
"url": "https://bun.sh"
},
{
"id": 67,
"slug": "/library/twilio.svg",
"title": "Twilio",
"category": "Software",
"url": "https://twilio.com"
},
{
"id": 68,
"slug": "/library/arc.svg",
"title": "Arc",
"category": "Software",
"url": "https://arc.dev"
},
{
"id": 69,
"slug": "/library/qwik.svg",
"title": "Qwik",
"category": "Framework",
"url": "https://qwik.builder.io/"
},
{
"id": 70,
"slug": "/library/authy.svg",
"title": "Authy",
"category": "Software",
"url": "https://authy.com/"
},
{
"id": 71,
"slug": "/library/notion.svg",
"title": "Notion",
"category": "Software",
"url": "https://notion.so/"
},
{
"id": 72,
"slug": "/library/nestjs.svg",
"title": "NestJS",
"category": "Framework",
"url": "https://nestjs.com/"
},
{
"id": 73,
"slug": "/library/copilot.svg",
"title": "Github Copilot",
"category": "Software",
"url": "https://github.com/features/copilot"
},
{
"id": 74,
"slug": "/library/railway.svg",
"title": "Railway",
"category": "Software",
"url": "https://railway.app/"
},
{
"id": 75,
"slug": "/library/docusaurus.svg",
"title": "Docusaurus",
"category": "Software",
"url": "https://docusaurus.io/"
},
{
"id": 76,
"slug": "/library/twitch.svg",
"title": "Twitch",
"category": "Entertainment",
"url": "https://twitch.tv"
},
{
"id": 77,
"slug": "/library/godaddy.svg",
"title": "GoDaddy",
"category": "Hosting",
"url": "https://www.godaddy.com/"
},
{
"id": 78,
"slug": "/library/udemy.svg",
"title": "Udemy",
"category": "Education",
"url": "https://www.udemy.com/"
},
{
"id": 79,
"slug": "/library/graphql.svg",
"title": "GraphQL",
"category": "Language",
"url": "https://graphql.org/"
},
{
"id": 80,
"slug": "/library/gitlab.svg",
"title": "GitLab",
"category": "Software",
"url": "https://gitlab.com/"
},
{
"id": 81,
"slug": "/library/prisma.svg",
"title": "Prisma",
"category": "Software",
"url": "https://prisma.io/"
},
{
"id": 82,
"slug": "/library/golang.svg",
"title": "Go",
"category": "Language",
"url": "https://go.dev/"
},
{
"id": 83,
"slug": "/library/platzi.svg",
"title": "Platzi",
"category": "Education",
"url": "https://platzi.com/"
},
{
"id": 84,
"slug": "/library/coursera.svg",
"title": "Coursera",
"category": "Education",
"url": "https://www.coursera.org/"
},
{
"id": 85,
"slug": "/library/udacity.svg",
"title": "Udacity",
"category": "Education",
"url": "https://www.udacity.com/"
},
{
"id": 86,
"slug": "/library/kubernetes.svg",
"title": "Kubernetes",
"category": "Software",
"url": "https://kubernetes.io/"
},
{
"id": 87,
"slug": "/library/docker.svg",
"title": "Docker",
"category": "Software",
"url": "https://www.docker.com/"
},
{
"id": 88,
"slug": "/library/aws.svg",
"title": "Amazon Web Services",
"category": "Software",
"url": "https://aws.amazon.com/"
},
{
"id": 89,
"slug": "/library/azure.svg",
"title": "Microsoft Azure",
"category": "Software",
"url": "https://azure.microsoft.com/"
},
{
"id": 90,
"slug": "/library/heroku.svg",
"title": "Heroku",
"category": "Software",
"url": "https://www.heroku.com/"
},
{
"id": 91,
"slug": "/library/jetbrains.svg",
"title": "JetBrains",
"category": "Software",
"url": "https://www.jetbrains.com/"
},
{
"id": 92,
"slug": "/library/rider.svg",
"title": "JetBrains Rider",
"category": "Software",
"url": "https://www.jetbrains.com/rider/"
},
{
"id": 93,
"slug": "/library/planetscale.svg",
"title": "PlanetScale",
"category": "Database",
"url": "https://planetscale.com/"
},
{
"id": 94,
"slug": "/library/playwright.svg",
"title": "Playwright",
"category": "Framework",
"url": "https://playwright.dev/"
},
{
"id": 95,
"slug": "/library/atlassian.svg",
"title": "Atlassian",
"category": "Software",
"url": "https://www.atlassian.com/"
},
{
"id": 96,
"slug": "/library/discourse.svg",
"title": "Discourse",
"category": "Software",
"url": "https://discourse.org/"
},
{
"id": 97,
"slug": "/library/ember.svg",
"title": "Ember",
"category": "Framework",
"url": "https://emberjs.com/"
},
{
"id": 98,
"slug": "/library/expo.svg",
"title": "Expo",
"category": "Software",
"url": "https://expo.dev/"
},
{
"id": 99,
"slug": "/library/flutter.svg",
"title": "Flutter",
"category": "Framework",
"url": "https://flutter.dev/"
},
{
"id": 100,
"slug": "/library/fresh.svg",
"title": "Fresh",
"category": "Framework",
"url": "https://fresh.deno.dev/"
},
{
"id": 101,
"slug": "/library/git.svg",
"title": "Git",
"category": "Software",
"url": "https://git-scm.com/"
},
{
"id": 102,
"slug": "/library/hostgator.svg",
"title": "Hostgator",
"category": "Hosting",
"url": "https://www.hostgator.com/"
},
{
"id": 103,
"slug": "/library/intellijidea.svg",
"title": "IntelliJ IDEA",
"category": "Software",
"url": "https://www.jetbrains.com/idea/"
},
{
"id": 104,
"slug": "/library/jasmine.svg",
"title": "Jasmine",
"category": "Framework",
"url": "https://jasmine.github.io/"
},
{
"id": 105,
"slug": "/library/java.svg",
"title": "Java",
"category": "Language",
"url": "https://www.java.com/"
},
{
"id": 106,
"slug": "/library/jest.svg",
"title": "Jest",
"category": "Framework",
"url": "https://jestjs.io/"
},
{
"id": 107,
"slug": "/library/jetbrainsSolid.svg",
"title": "JetBrains",
"category": "Software",
"url": "https://www.jetbrains.com/"
},
{
"id": 108,
"slug": "/library/krakenjs.svg",
"title": "KrakenJS",
"category": "Framework",
"url": "https://krakenjs.com/"
},
{
"id": 109,
"slug": "/library/laravel.svg",
"title": "Laravel",
"category": "Framework",
"url": "https://laravel.com/"
},
{
"id": 110,
"slug": "/library/mariadb.svg",
"title": "MariaDB",
"category": "Database",
"url": "https://mariadb.org/"
},
{
"id": 111,
"slug": "/library/materialui.svg",
"title": "Material UI",
"category": "Framework",
"url": "https://mui.com/"
},
{
"id": 112,
"slug": "/library/mysql.svg",
"title": "MySQL",
"category": "Database",
"url": "https://www.mysql.com/"
},
{
"id": 113,
"slug": "/library/parcel.svg",
"title": "Parcel",
"category": "Compiler",
"url": "https://parceljs.org/"
},
{
"id": 114,
"slug": "/library/pm2.svg",
"title": "PM2",
"category": "Framework",
"url": "https://pm2.io/"
},
{
"id": 115,
"slug": "/library/postgresql.svg",
"title": "PostgreSQL",
"category": "Database",
"url": "https://www.postgresql.org/"
},
{
"id": 116,
"slug": "/library/reactquery.svg",
"title": "React Query",
"category": "Framework",
"url": "https://tanstack.com/query/v4"
},
{
"id": 117,
"slug": "/library/redis.svg",
"title": "Redis",
"category": "Database",
"url": "https://redis.io/"
},
{
"id": 118,
"slug": "/library/redwoodjs.svg",
"title": "RedwoodJS",
"category": "Framework",
"url": "https://redwoodjs.com/"
},
{
"id": 119,
"slug": "/library/ruby.svg",
"title": "Ruby",
"category": "Language",
"url": "https://www.ruby-lang.org/"
},
{
"id": 120,
"slug": "/library/scala.svg",
"title": "Scala",
"category": "Language",
"url": "https://www.scala-lang.org/"
},
{
"id": 121,
"slug": "/library/sequelize.svg",
"title": "Sequelize",
"category": "Framework",
"url": "https://sequelize.org/"
},
{
"id": 122,
"slug": "/library/spinnaker.svg",
"title": "Spinnaker",
"category": "Software",
"url": "https://spinnaker.io/"
},
{
"id": 123,
"slug": "/library/sqlite.svg",
"title": "SQLite",
"category": "Database",
"url": "https://www.sqlite.org/"
},
{
"id": 124,
"slug": "/library/swagger.svg",
"title": "Swagger",
"category": "Software",
"url": "https://swagger.io/"
},
{
"id": 125,
"slug": "/library/swift.svg",
"title": "Swift",
"category": "Language",
"url": "https://swift.org/"
},
{
"id": 126,
"slug": "/library/testinglibrary.svg",
"title": "Testing Library",
"category": "Framework",
"url": "https://testing-library.com/"
},
{
"id": 127,
"slug": "/library/typeorm.svg",
"title": "TypeORM",
"category": "Database",
"url": "https://typeorm.io/"
},
{
"id": 128,
"slug": "/library/unity.svg",
"title": "Unity",
"category": "Software",
"url": "https://unity.com/"
},
{
"id": 129,
"slug": "/library/vim.svg",
"title": "Vim",
"category": "Software",
"url": "https://www.vim.org/"
},
{
"id": 130,
"slug": "/library/wmr.svg",
"title": "WMR",
"category": "Compiler",
"url": "https://wmr.dev/"
},
{
"id": 131,
"slug": "/library/openbootcamp.svg",
"title": "OpenBootcamp",
"category": "Education",
"url": "https://open-bootcamp.com/"
},
{
"id": 132,
"slug": "/library/digitalocean.svg",
"title": "Digital Ocean",
"category": "Software",
"url": "https://www.digitalocean.com/"
},
{
"id": 133,
"slug": "/library/disneyplus.svg",
"title": "Disney+",
"category": "Entertainment",
"url": "https://www.disneyplus.com/"
},
{
"id": 134,
"slug": "/library/reactrouter.svg",
"title": "React Router",
"category": "Library",
"url": "https://reactrouter.com/en/main"
},
{
"id": 135,
"slug": "/library/gdsc.svg",
"title": "Google Student Developer Club",
"category": "Google",
"url": "https://gdsc.community.dev/"
},
{
"id": 136,
"slug": "/library/brave.svg",
"title": "Brave Browser",
"category": "Browser",
"url": "https://brave.com/"
},
{
"id": 137,
"slug": "/library/eclipse.svg",
"title": "Eclipse IDE",
"category": "IDE",
"url": "https://www.eclipse.org/"
},
{
"id": 138,
"slug": "/library/html5.svg",
"title": "HTML5",
"category": "Language",
"url": "https://es.wikipedia.org/wiki/HTML5"
},
{
"id": 139,
"slug": "/library/css.svg",
"title": "CSS",
"category": "Language",
"url": "https://es.wikipedia.org/wiki/CSS"
},
{
"id": 140,
"slug": "/library/midudev.svg",
"title": "midudev",
"category": "Education",
"url": "https://midu.dev"
},
{
"id": 141,
"slug": "/library/apple.svg",
"title": "Apple",
"category": "Software",
"url": "https://www.apple.com"
},
{
"id": 142,
"slug": "/library/android.svg",
"title": "Android",
"category": "Software",
"url": "https://www.android.com/"
},
{
"id": 143,
"slug": "/library/windows.svg",
"title": "Windows",
"category": "Software",
"url": "https://www.microsoft.com/windows"
},
{
"id": 144,
"slug": "/library/python.svg",
"title": "Python",
"category": "Language",
"url": "https://www.python.org/"
}
]

5
next-env.d.ts vendored
View File

@ -1,5 +0,0 @@
/// <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.

View File

@ -1,57 +0,0 @@
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",
},
};

View File

@ -1,14 +0,0 @@
/** @type {import('next').NextConfig} */
const withPWA = require("next-pwa");
const nextConfig = withPWA({
reactStrictMode: true,
pwa: {
dest: "public",
register: true,
skipWaiting: true,
disable: process.env.NODE_ENV === "development",
},
});
module.exports = nextConfig;

View File

@ -1,10 +1,11 @@
{ {
"name": "svgl", "name": "svgl",
"version": "2.0.1", "author": "@pheralb_",
"version": "3.0.0",
"description": "A beautiful library with SVG logos.", "description": "A beautiful library with SVG logos.",
"private": true, "private": true,
"author": "@pheralb_",
"license": "MIT", "license": "MIT",
"type": "module",
"keywords": [ "keywords": [
"svgs", "svgs",
"logos", "logos",
@ -12,45 +13,41 @@
"library" "library"
], ],
"scripts": { "scripts": {
"dev": "next dev", "dev": "vite dev",
"build": "next build", "host": "vite dev --host",
"start": "next start", "build": "vite build",
"lint": "next lint", "preview": "vite preview",
"test": "vitest", "check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"ready": "vitest && next build" "check:watch": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json --watch",
"test:unit": "vitest",
"lint": "prettier --plugin-search-dir . --check . && eslint .",
"format": "prettier --plugin-search-dir . --write ."
}, },
"dependencies": { "dependencies": {
"@chakra-ui/react": "2.2.4",
"@emotion/react": "11.10.0",
"@emotion/styled": "11.10.0",
"@uiball/loaders": "1.2.6",
"canvas-confetti": "1.5.1",
"downloadjs": "1.4.7", "downloadjs": "1.4.7",
"framer-motion": "6.5.1", "svelte-french-toast": "1.0.3"
"next": "12.2.3",
"next-pwa": "5.5.4",
"next-seo": "5.5.0",
"nextjs-progressbar": "0.0.14",
"phosphor-react": "1.4.1",
"react": "18.2.0",
"react-dom": "18.2.0",
"react-hot-toast": "2.3.0",
"react-hotkeys-hook": "3.4.7",
"swr": "1.3.0"
}, },
"devDependencies": { "devDependencies": {
"@testing-library/jest-dom": "5.16.4", "@sveltejs/adapter-auto": "2.0.0",
"@testing-library/react": "13.3.0", "@sveltejs/kit": "1.11.0",
"@types/canvas-confetti": "1.4.3",
"@types/downloadjs": "1.4.3", "@types/downloadjs": "1.4.3",
"@types/node": "18.6.3", "@typescript-eslint/eslint-plugin": "5.55.0",
"@types/react": "18.0.15", "@typescript-eslint/parser": "5.55.0",
"@types/react-dom": "18.0.6", "autoprefixer": "10.4.14",
"@vitejs/plugin-react": "2.0.0", "eslint": "8.36.0",
"eslint": "8.21.0", "eslint-config-prettier": "8.7.0",
"eslint-config-next": "12.2.3", "eslint-plugin-svelte3": "4.0.0",
"jsdom": "20.0.0", "phosphor-svelte": "1.2.1",
"typescript": "4.7.4", "postcss": "8.4.21",
"vitest": "0.20.2" "prettier": "2.8.4",
"prettier-plugin-svelte": "2.9.0",
"prettier-plugin-tailwindcss": "0.2.4",
"svelte": "3.56.0",
"svelte-check": "3.1.4",
"tailwindcss": "3.2.7",
"tslib": "2.5.0",
"typescript": "4.9.5",
"vite": "4.1.4",
"vitest": "0.29.2"
} }
} }

6
postcss.config.cjs Normal file
View File

@ -0,0 +1,6 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

4
prettier.config.cjs Normal file
View File

@ -0,0 +1,4 @@
/** @type {import("prettier").Config} */
module.exports = {
plugins: [require.resolve('prettier-plugin-tailwindcss')]
};

Binary file not shown.

Binary file not shown.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.0 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.8 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.2 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.7 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 12 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 20 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.1 KiB

View File

@ -1 +0,0 @@
<svg width="256" height="256" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid"><path d="M119.617.069c-.55.05-2.302.225-3.879.35-36.36 3.278-70.419 22.894-91.99 53.044-12.012 16.764-19.694 35.78-22.597 55.922C.125 116.415 0 118.492 0 128.025c0 9.533.125 11.61 1.151 18.64 6.957 48.065 41.165 88.449 87.56 103.411 8.309 2.678 17.067 4.504 27.027 5.605 3.879.425 20.645.425 24.524 0 17.192-1.902 31.756-6.155 46.12-13.486 2.202-1.126 2.628-1.426 2.327-1.677-.2-.15-9.584-12.735-20.845-27.948l-20.47-27.648-25.65-37.956c-14.114-20.868-25.725-37.932-25.825-37.932-.1-.025-.2 16.84-.25 37.431-.076 36.055-.1 37.506-.551 38.357-.65 1.226-1.151 1.727-2.202 2.277-.801.4-1.502.475-5.28.475h-4.33l-1.15-.725a4.679 4.679 0 0 1-1.677-1.827l-.526-1.126.05-50.166.075-50.192.776-.976c.4-.525 1.251-1.2 1.852-1.526 1.026-.5 1.426-.55 5.755-.55 5.105 0 5.956.2 7.282 1.651.376.4 14.264 21.318 30.88 46.514 16.617 25.195 39.34 59.599 50.5 76.488l20.27 30.7 1.026-.675c9.084-5.905 18.693-14.312 26.3-23.07 16.191-18.59 26.626-41.258 30.13-65.428 1.026-7.031 1.151-9.108 1.151-18.64 0-9.534-.125-11.61-1.151-18.641-6.957-48.065-41.165-88.449-87.56-103.411-8.184-2.652-16.892-4.479-26.652-5.58-2.402-.25-18.943-.525-21.02-.325Zm52.401 77.414c1.201.6 2.177 1.752 2.527 2.953.2.65.25 14.562.2 45.913l-.074 44.987-7.933-12.16-7.958-12.16v-32.702c0-21.143.1-33.028.25-33.603.4-1.401 1.277-2.502 2.478-3.153 1.026-.525 1.401-.575 5.33-.575 3.704 0 4.354.05 5.18.5Z"/></svg>

Before

Width:  |  Height:  |  Size: 1.4 KiB

View File

@ -1 +0,0 @@
<svg width="256" height="172" xmlns="http://www.w3.org/2000/svg" preserveAspectRatio="xMidYMid"><path d="M112.973 9.25c-7.172-12.333-25.104-12.333-32.277 0L2.524 143.66c-7.172 12.333 1.794 27.749 16.14 27.749h61.024c-6.13-5.357-8.4-14.625-3.76-22.576L135.13 47.348 112.973 9.25Z" fill="#80EEC0"/><path d="M162.505 38.733c5.936-10.09 20.776-10.09 26.712 0l64.694 109.971c5.936 10.091-1.484 22.705-13.357 22.705H111.167c-11.872 0-19.292-12.614-13.356-22.705l64.694-109.971Z" fill="#00DC82"/></svg>

Before

Width:  |  Height:  |  Size: 495 B

View File

@ -1 +0,0 @@
<svg data-name="Capa 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 118 118"><rect width="118" height="118" rx="15.9" style="fill:#8c52ff"/><path d="M30.43 75.91 70.38 60 30.43 42.22v-18.7l65.14 31.86v10.83L30.43 94.48Z" style="fill:#fff"/></svg>

Before

Width:  |  Height:  |  Size: 250 B

View File

@ -1 +0,0 @@
<svg width="256" height="254" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" preserveAspectRatio="xMidYMid"><defs><linearGradient x1="50%" y1="0%" x2="50%" y2="100%" id="c"><stop stop-color="#FFF" offset="0%"/><stop stop-color="#FFF" stop-opacity="0" offset="100%"/></linearGradient><path d="M180.828 252.605a15.872 15.872 0 0 0 12.65-.486l52.501-25.262a15.94 15.94 0 0 0 9.025-14.364V41.197a15.939 15.939 0 0 0-9.025-14.363l-52.5-25.263a15.877 15.877 0 0 0-18.115 3.084L74.857 96.35l-43.78-33.232a10.614 10.614 0 0 0-13.56.603L3.476 76.494c-4.63 4.211-4.635 11.495-.012 15.713l37.967 34.638-37.967 34.637c-4.623 4.219-4.618 11.502.012 15.714l14.041 12.772a10.614 10.614 0 0 0 13.56.604l43.78-33.233 100.507 91.695a15.853 15.853 0 0 0 5.464 3.571Zm10.464-183.649-76.262 57.889 76.262 57.888V68.956Z" id="a"/></defs><mask id="b" fill="#fff"><use xlink:href="#a"/></mask><path d="M246.135 26.873 193.593 1.575a15.885 15.885 0 0 0-18.123 3.08L3.466 161.482c-4.626 4.219-4.62 11.502.012 15.714l14.05 12.772a10.625 10.625 0 0 0 13.569.604L238.229 33.436c6.949-5.271 16.93-.315 16.93 8.407v-.61a15.938 15.938 0 0 0-9.024-14.36Z" fill="#0065A9" mask="url(#b)"/><path d="m246.135 226.816-52.542 25.298a15.887 15.887 0 0 1-18.123-3.08L3.466 92.207c-4.626-4.218-4.62-11.502.012-15.713l14.05-12.773a10.625 10.625 0 0 1 13.569-.603l207.132 157.135c6.949 5.271 16.93.315 16.93-8.408v.611a15.939 15.939 0 0 1-9.024 14.36Z" fill="#007ACC" mask="url(#b)"/><path d="M193.428 252.134a15.892 15.892 0 0 1-18.125-3.083c5.881 5.88 15.938 1.715 15.938-6.603V11.273c0-8.318-10.057-12.483-15.938-6.602a15.892 15.892 0 0 1 18.125-3.084l52.533 25.263a15.937 15.937 0 0 1 9.03 14.363V212.51c0 6.125-3.51 11.709-9.03 14.363l-52.533 25.262Z" fill="#1F9CF0" mask="url(#b)"/><path d="M180.828 252.605a15.874 15.874 0 0 0 12.65-.486l52.5-25.263a15.938 15.938 0 0 0 9.026-14.363V41.197a15.939 15.939 0 0 0-9.025-14.363L193.477 1.57a15.877 15.877 0 0 0-18.114 3.084L74.857 96.35l-43.78-33.232a10.614 10.614 0 0 0-13.56.603L3.476 76.494c-4.63 4.211-4.635 11.495-.012 15.713l37.967 34.638-37.967 34.637c-4.623 4.219-4.618 11.502.012 15.714l14.041 12.772a10.614 10.614 0 0 0 13.56.604l43.78-33.233 100.506 91.695a15.857 15.857 0 0 0 5.465 3.571Zm10.464-183.65-76.262 57.89 76.262 57.888V68.956Z" fill-opacity=".25" fill="url(#c)" mask="url(#b)"/></svg>

Before

Width:  |  Height:  |  Size: 2.3 KiB

View File

@ -1,38 +0,0 @@
{
"theme_color": "#4343E5",
"background_color": "#050505",
"display": "minimal-ui",
"scope": "/",
"start_url": "/",
"name": "SVGL",
"short_name": "SVGL",
"description": "Beautiful SVG vector logos",
"icons": [
{
"src": "/icons/icon-192x192.png",
"sizes": "192x192",
"type": "image/png"
},
{
"src": "/icons/icon-256x256.png",
"sizes": "256x256",
"type": "image/png"
},
{
"src": "/icons/icon-384x384.png",
"sizes": "384x384",
"type": "image/png"
},
{
"src": "/icons/icon-512x512.png",
"sizes": "512x512",
"type": "image/png"
},
{
"src": "/icons/maskable_icon.png",
"sizes": "196x196",
"type": "image/png",
"purpose": "maskable"
}
]
}

View File

@ -1,13 +0,0 @@
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();
});
});

View File

@ -1,24 +0,0 @@
import React, { FC } from "react";
import { motion } from "framer-motion";
type ShowProps = {
children: React.ReactNode;
delay?: number;
};
const Show = ({ children, delay }: ShowProps) => {
return (
<motion.div
initial={{ y: 10, opacity: 0 }}
animate={{ y: 0, opacity: 1 }}
transition={{
duration: 0.4,
delay: delay,
}}
>
{children}
</motion.div>
);
};
export default Show;

View File

@ -1,16 +0,0 @@
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;

31
src/app.css Normal file
View File

@ -0,0 +1,31 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@font-face {
font-family: 'General-Sans';
src: url('/fonts/GeneralSans-Variable.woff2') format('woff2');
font-weight: 200 700;
font-display: swap;
font-style: normal;
}
body {
--sb-track-color: #171717;
--sb-thumb-color: #404040;
--sb-size: 10px;
scrollbar-color: var(--sb-thumb-color) var(--sb-track-color);
}
body::-webkit-scrollbar {
width: var(--sb-size);
}
body::-webkit-scrollbar-track {
background: var(--sb-track-color);
}
body::-webkit-scrollbar-thumb {
background: var(--sb-thumb-color);
}

12
src/app.d.ts vendored Normal file
View File

@ -0,0 +1,12 @@
// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
declare global {
namespace App {
// interface Error {}
// interface Locals {}
// interface PageData {}
// interface Platform {}
}
}
export {};

33
src/app.html Normal file
View File

@ -0,0 +1,33 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width" />
<meta name="robots" content="index, follow" />
<meta name="author" content="@pheralb_" />
<!-- Favicon -->
<link rel="icon" type="image/svg+xml" href="%sveltekit.assets%/images/logo.svg" />
<!-- OG -->
<meta property="og:type" content="website" />
<meta property="og:title" content="svgl" />
<meta property="og:description" content="A beautiful library with SVG logos" />
<meta property="og:url" content="https://svgl.vercel.app/" />
<meta property="og:image" content="https://svgl.vercel.app/images/screenshot.png" />
<!-- Twitter -->
<meta name="twitter:card" content="summary_large_image" />
<meta name="twitter:title" content="Svgl" />
<meta name="twitter:description" content="A beautiful library with SVG logos" />
<meta name="twitter:creator" content="@pheralb_" />
<meta name="twitter:image" content="https://svgl.vercel.app/images/screenshot.png" />
<!-- Title -->
<title>A beautiful library with SVG logos - Svgl</title>
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div>%sveltekit.body%</div>
</body>
</html>

View File

@ -1,13 +0,0 @@
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;

View File

@ -1,18 +0,0 @@
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}
mr={props.mr}
ml={props.ml}
/>
);
};
export default CustomIconBtn;

View File

@ -1,23 +0,0 @@
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, font, mr, ml }: CustomLinkProps) => {
return (
<NextLink href={href} passHref>
<Link
isExternal={external}
_hover={{ textDecoration: "none" }}
_focus={{ border: "none" }}
fontFamily={font}
mr={mr}
ml={ml}
>
{children}
</Link>
</NextLink>
);
};
export default CustomLink;

View File

@ -0,0 +1,3 @@
<div class="container mx-auto px-6 pt-4 xl:px-4">
<slot />
</div>

View File

@ -1,47 +0,0 @@
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;

View File

@ -0,0 +1,5 @@
<div
class="mt-4 grid grid-cols-1 gap-4 sm:grid-cols-2 md:grid-cols-2 lg:grid-cols-4 xl:grid-cols-6"
>
<slot />
</div>

View File

@ -1,16 +0,0 @@
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;

View File

@ -0,0 +1,21 @@
<script lang="ts">
export let searchTerm: string;
export let placeholder: string = 'Search...';
import MagnifyingGlass from 'phosphor-svelte/lib/MagnifyingGlass';
</script>
<div class="relative w-full">
<div class="absolute inset-y-0 left-0 flex items-center pl-3 text-neutral-500">
<div class="pointer-events-none">
<MagnifyingGlass size={18} weight={searchTerm ? 'duotone' : 'regular'} />
</div>
</div>
<input
type="text"
{placeholder}
autocomplete="off"
class="w-full rounded-md border border-neutral-300 bg-neutral-200/50 p-3 pl-10 placeholder-neutral-500 focus:outline-none focus:ring-1 focus:ring-neutral-300 dark:border-neutral-800 dark:bg-neutral-700/10 dark:focus:ring-neutral-700"
bind:value={searchTerm}
on:input
/>
</div>

View File

@ -1,125 +0,0 @@
import { useEffect, useRef, useState } from "react";
import {
Input,
Text,
Image,
HStack,
Box,
Center,
Spinner,
} from "@chakra-ui/react";
import useDebounce from "@/hooks/useDebounce";
import { SearchProps, 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 = ({ availableFocus = false }: SearchProps) => {
const [search, setSearch] = useState("");
const [empty, setEmpty] = useState(false);
const [results, setResults] = useState<SVGCardProps[]>([]);
const debouncedSearch = useDebounce(search, 500);
const searchRef = useRef<HTMLInputElement>(null);
useEffect(() => {
if (debouncedSearch) {
fetch(getSvgByQuery + debouncedSearch).then((res) => {
if (res.ok) {
res.json().then((data) => {
setEmpty(data.length === 0);
setResults(data);
});
}
});
}
}, [debouncedSearch]);
useEffect(() => {
const isFocusAvailable = availableFocus && searchRef.current;
if (!isFocusAvailable) return;
const timeoutId = setTimeout(() => {
searchRef.current?.focus();
}, 100);
return () => clearTimeout(timeoutId);
}, [availableFocus]);
const handleFilter = (e: React.ChangeEvent<HTMLInputElement>) => {
setEmpty(false);
setSearch(e.target.value);
};
const handleClear = () => {
setSearch("");
setResults([]);
};
return (
<>
<Input
width="full"
variant="flushed"
size="lg"
placeholder="Search svgs..."
value={search}
onChange={handleFilter}
ref={searchRef}
/>
{search && !empty && results.length === 0 && (
<Box pt="4">
<Spinner />
</Box>
)}
{search && empty && <Box pt="3">No results found!</Box>}
{results && results.length > 0 && (
<>
<HStack
spacing={4}
mt={4}
overflowX="auto"
overflowY="hidden"
alignItems="start"
>
{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;

View File

@ -0,0 +1,82 @@
<script lang="ts">
import download from 'downloadjs';
import toast from 'svelte-french-toast';
import type { iSVG } from '../types/svg';
import { MIMETYPE, getSvgContent } from '../utils/getSvgContent';
// Icons:
import DownloadSimple from 'phosphor-svelte/lib/DownloadSimple';
import ArrowUpRight from 'phosphor-svelte/lib/ArrowUpRight';
import Copy from 'phosphor-svelte/lib/Copy';
// Props:
export let svgInfo: iSVG;
// Download SVG:
const downloadSvg = (url?: string) => {
download(url || '');
toast('Downloading', {
icon: '🎉',
style: 'border-radius: 200px; background: #333; color: #fff;'
});
};
// Copy SVG to clipboard:
const copyToClipboard = async (url?: string) => {
const data = {
[MIMETYPE]: getSvgContent(url, true)
};
try {
const clipboardItem = new ClipboardItem(data);
await navigator.clipboard.write([clipboardItem]);
} catch (error) {
const content = (await getSvgContent(url, false)) as string;
await navigator.clipboard.writeText(content);
}
toast('Copied to clipboard', {
icon: '👏',
style: 'border-radius: 200px; background: #333; color: #fff;'
});
};
</script>
<div
class="flex flex-col items-center justify-center rounded-md border border-neutral-300 bg-neutral-100 p-4 dark:border-neutral-800 dark:bg-neutral-700/10"
>
<img src={svgInfo.route} alt={svgInfo.title} class="mb-4 mt-2 h-10" />
<div class="mb-3 flex flex-col items-center justify-center">
<p class="truncate text-[15px] font-medium">{svgInfo.title}</p>
<a
href={`/directory/${svgInfo.category.toLowerCase()}`}
class="text-sm lowercase text-neutral-500 hover:underline">{svgInfo.category}</a
>
</div>
<div class="flex items-center space-x-1">
<button
title="Copy to clipboard"
on:click={() => {
copyToClipboard(svgInfo.route);
}}
class="flex items-center space-x-2 rounded-md p-2 duration-100 hover:bg-neutral-300 dark:hover:bg-neutral-700/40"
>
<Copy size={17} />
</button>
<button
title="Download"
on:click={() => {
downloadSvg(svgInfo.route);
}}
class="flex items-center space-x-2 rounded-md p-2 duration-100 hover:bg-neutral-300 dark:hover:bg-neutral-700/40"
>
<DownloadSimple size={17} />
</button>
<a
href={svgInfo.url}
title="Website"
target="_blank"
class="flex items-center space-x-2 rounded-md p-2 duration-100 hover:bg-neutral-300 dark:hover:bg-neutral-700/40"
>
<ArrowUpRight size={17} />
</a>
</div>
</div>

View File

@ -1,47 +0,0 @@
import React from "react";
import { SVGCardProps } from "@/interfaces/components";
import {
Box,
Center,
Image,
Text,
useColorModeValue,
useDisclosure,
} from "@chakra-ui/react";
import Tap from "@/animations/tap";
import CustomLink from "@/common/link";
import { Smiley } from "phosphor-react";
const SVGCard = (props: SVGCardProps) => {
const bg = useColorModeValue("bg.light", "bg.dark");
const color = useColorModeValue("rgb(0,0,0, .1)", "rgb(255,255,255, .1)");
return (
<>
<Tap>
<CustomLink href={`/svg/${props.id}`}>
<Box
bg={bg}
p={4}
cursor="pointer"
borderRadius="10px"
borderWidth="1px"
mb="2"
_hover={{
border:`1px solid ${color}`,
transform: "scale(1.03)",
}}
transition="all 0.2s" >
<Center>
<Image height="40px" src={props.svg} alt={props.title} />
</Center>
<Text mt="3" fontWeight="light" textAlign="center">
{props.title}
</Text>
</Box>
</CustomLink>
</Tap>
</>
);
};
export default SVGCard;

View File

@ -1,108 +0,0 @@
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 || "");
};
const MIMETYPE = 'text/plain';
// Return content of svg as blob =>
const getSvgContent = async (url: string | undefined, isSupported: boolean) => {
const response = await fetch(url || "");
const content = await response.text();
// It was necessary to use blob because in chrome there were issues with the copy to clipboard
const blob = new Blob([content], { type: MIMETYPE });
return isSupported ? blob : content;
}
// Copy to clipboard =>
const copyToClipboard = async (url?: string) => {
const data = {
[MIMETYPE]: getSvgContent(url, true)
};
try {
const clipboardItem = new ClipboardItem(data);
await navigator.clipboard.write([clipboardItem]);
} catch (error) {
// This section works as a fallback on Firefox
const content = await getSvgContent(url, false) as string;
await 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;

View File

@ -0,0 +1,63 @@
<script lang="ts">
import { onMount } from 'svelte';
let dark: boolean;
let hidden = true;
onMount(() => {
dark = document.documentElement.classList.contains('dark');
hidden = false;
const matcher = window.matchMedia('(prefers-color-scheme: dark)');
matcher.addEventListener('change', handleChange);
return () => matcher.removeEventListener('change', handleChange);
});
function handleChange({ matches: dark }: MediaQueryListEvent) {
if (!localStorage.theme) {
setMode(dark);
}
}
function toggle() {
setMode(!dark);
}
function setMode(value: boolean) {
dark = value;
if (dark) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
localStorage.theme = dark ? 'dark' : 'light';
if (window.matchMedia(`(prefers-color-scheme: ${localStorage.theme})`).matches) {
localStorage.removeItem('theme');
}
}
// Icons:
import Moon from 'phosphor-svelte/lib/Moon';
import Sun from 'phosphor-svelte/lib/Sun';
</script>
<svelte:head>
<!-- set dark mode class based on user preference / device settings (in head to avoid FOUC) -->
<script>
if (
localStorage.theme === 'dark' ||
(!localStorage.theme && window.matchMedia('(prefers-color-scheme: dark)').matches)
) {
document.documentElement.classList.add('dark');
} else {
document.documentElement.classList.remove('dark');
}
</script>
</svelte:head>
<button on:click={toggle} class="focus:outline-none" class:hidden>
<!-- moon icon -->
{#if dark}
<Sun size={18} />
{:else}
<Moon size={18} />
{/if}
</button>

View File

@ -0,0 +1,10 @@
<script lang="ts">
import { fly } from 'svelte/transition';
export let pathname: string = '';
</script>
{#key pathname}
<div in:fly={{ x: 0, y: 23, duration: 450 }}>
<slot />
</div>
{/key}

1096
src/data/svgs.ts Normal file
View File

@ -0,0 +1,1096 @@
import type { iSVG } from '../types/svg';
export const svgs: iSVG[] = [
{
id: 1,
title: 'Discord',
category: 'Software',
route: '/library/discord.svg',
url: 'https://discord.com/'
},
{
id: 2,
title: 'Github',
category: 'Software',
route: '/library/github.svg',
url: 'https://github.com/'
},
{
id: 3,
title: 'Preact',
category: 'Library',
route: '/library/preact.svg',
url: 'https://preactjs.com/'
},
{
id: 4,
title: 'React',
category: 'Library',
route: '/library/react.svg',
url: 'https://reactjs.org/'
},
{
id: 5,
title: 'Vercel',
category: 'Hosting',
route: '/library/vercel.svg',
url: 'https://vercel.com/'
},
{
id: 6,
title: 'Svelte',
category: 'Library',
route: '/library/svelte.svg',
url: 'https://svelte.dev/'
},
{
id: 7,
title: 'Vue',
category: 'Framework',
route: '/library/vue.svg',
url: 'https://vuejs.org/'
},
{
id: 8,
title: 'Nuxt',
category: 'Framework',
route: '/library/nuxt.svg',
url: 'https://nuxtjs.org/'
},
{
id: 9,
title: 'Nextjs',
category: 'Framework',
route: '/library/nextjs.svg',
url: 'https://nextjs.org/'
},
{
id: 10,
title: 'Visual Studio Code',
category: 'Software',
route: '/library/vscode.svg',
url: 'https://code.visualstudio.com/'
},
{
id: 11,
title: 'JWT',
category: 'Library',
route: '/library/jwt.svg',
url: 'https://jwt.io/'
},
{
id: 12,
title: 'Strapi',
category: 'CMS',
route: '/library/strapi.svg',
url: 'https://strapi.io/'
},
{
id: 13,
title: 'Figma',
category: 'Software',
route: '/library/figma.svg',
url: 'https://www.figma.com/'
},
{
id: 14,
title: 'Spotify',
category: 'Software',
route: '/library/spotify.svg',
url: 'https://www.spotify.com/'
},
{
id: 15,
title: 'Postman',
category: 'Software',
route: '/library/postman.svg',
url: 'https://www.getpostman.com/'
},
{
id: 16,
title: 'Algolia',
category: 'Library',
route: '/library/algolia.svg',
url: 'https://www.algolia.com/'
},
{
id: 17,
title: 'Bootstrap',
category: 'Framework',
route: '/library/bootstrap.svg',
url: 'https://getbootstrap.com/'
},
{
id: 18,
title: 'Firebase',
category: 'Hosting',
route: '/library/firebase.svg',
url: 'https://firebase.google.com/'
},
{
id: 19,
title: 'Supabase',
category: 'Database',
route: '/library/supabase.svg',
url: 'https://supabase.com/'
},
{
id: 20,
title: 'Vite.js',
category: 'Compiler',
route: '/library/vitejs.svg',
url: 'https://vitejs.dev'
},
{
id: 21,
title: 'Facebook',
category: 'Social',
route: '/library/facebook.svg',
url: 'https://www.facebook.com/'
},
{
id: 22,
title: 'Twitter',
category: 'Social',
route: '/library/twitter.svg',
url: 'https://twitter.com/'
},
{
id: 23,
title: 'Node.js',
category: 'Library',
route: '/library/nodejs.svg',
url: 'https://nodejs.org/'
},
{
id: 24,
title: 'Esbuild',
category: 'Compiler',
route: '/library/esbuild.svg',
url: 'https://esbuild.github.io/'
},
{
id: 25,
title: 'Deno',
category: 'Library',
route: '/library/deno.svg',
url: 'https://deno.land/'
},
{
id: 26,
title: 'Gatsby',
category: 'Framework',
route: '/library/gatsby.svg',
url: 'https://www.gatsbyjs.org/'
},
{
id: 27,
title: 'NPM',
category: 'Software',
route: '/library/npm.svg',
url: 'https://www.npmjs.com/'
},
{
id: 28,
title: 'Homebrew',
category: 'Software',
route: '/library/homebrew.svg',
url: 'https://brew.sh/'
},
{
id: 29,
title: 'Sublime Text',
category: 'Software',
route: '/library/sublimetext.svg',
url: 'https://www.sublimetext.com/'
},
{
id: 30,
title: 'TurboRepo',
category: 'Library',
route: '/library/turborepo.svg',
url: 'https://turborepo.org/'
},
{
id: 31,
title: 'Tailwind CSS',
category: 'Framework',
route: '/library/tailwindcss.svg',
url: 'https://tailwindcss.com/'
},
{
id: 32,
title: 'Styled Components',
category: 'Library',
route: '/library/styledcomponents.svg',
url: 'https://styled-components.com/'
},
{
id: 33,
title: 'Angular',
category: 'Framework',
route: '/library/angular.svg',
url: 'https://angular.io/'
},
{
id: 34,
title: 'Blitz',
category: 'Framework',
route: '/library/blitzjs.svg',
url: 'https://blitzjs.com/'
},
{
id: 35,
title: 'Lit',
category: 'Library',
route: '/library/lit.svg',
url: 'https://lit.dev/'
},
{
id: 36,
title: 'Atom',
category: 'Software',
route: '/library/atom.svg',
url: 'https://atom.io/'
},
{
id: 37,
title: 'YouTube',
category: 'Social',
route: '/library/youtube.svg',
url: 'https://www.youtube.com/'
},
{
id: 38,
title: 'Astro',
category: 'Framework',
route: '/library/astro.svg',
url: 'https://astro.build/'
},
{
id: 39,
title: 'Google',
category: 'Social',
route: '/library/google.svg',
url: 'https://www.google.com/'
},
{
id: 40,
title: 'Framer',
category: 'Software',
route: '/library/framer.svg',
url: 'https://framer.com/'
},
{
id: 41,
title: 'Netflix',
category: 'Entertainment',
route: '/library/netflix.svg',
url: 'https://www.netflix.com/'
},
{
id: 42,
title: 'Firefox',
category: 'Software',
route: '/library/firefox.svg',
url: 'https://www.mozilla.org/en-US/firefox/'
},
{
id: 43,
title: 'LinkedIn',
category: 'Social',
route: '/library/linkedin.svg',
url: 'https://www.linkedin.com/'
},
{
id: 44,
title: 'Telegram',
category: 'Social',
route: '/library/telegram.svg',
url: 'https://web.telegram.org/'
},
{
id: 45,
title: 'WhatsApp',
category: 'Social',
route: '/library/whatsapp.svg',
url: 'https://web.whatsapp.com/'
},
{
id: 46,
title: 'Headless UI',
category: 'Library',
route: '/library/headlessui.svg',
url: 'https://headlessui.dev/'
},
{
id: 47,
title: 'Kotlin',
category: 'Language',
route: '/library/kotlin.svg',
url: 'https://kotlinlang.org/'
},
{
id: 48,
title: 'Vitest',
category: 'Framework',
route: '/library/vitest.svg',
url: 'https://vitest.dev/'
},
{
id: 49,
title: 'Storybook',
category: 'Software',
route: '/library/storybook.svg',
url: 'https://storybook.js.org/'
},
{
id: 50,
title: 'Netlify',
category: 'Hosting',
route: '/library/netlify.svg',
url: 'https://www.netlify.com/'
},
{
id: 51,
title: 'Solidjs',
category: 'Framework',
route: '/library/solidjs.svg',
url: 'https://www.solidjs.com/'
},
{
id: 52,
title: 'MongoDB',
category: 'Database',
route: '/library/mongodb.svg',
url: 'https://www.mongodb.com/'
},
{
id: 53,
title: 'Babel',
category: 'Compiler',
route: '/library/babel.svg',
url: 'https://babeljs.io'
},
{
id: 54,
title: 'Moon',
category: 'Framework',
route: '/library/moon.svg',
url: 'https://moonjs.org/'
},
{
id: 55,
title: 'Payload CMS',
category: 'CMS',
route: '/library/payload.svg',
url: 'https://payloadcms.com'
},
{
id: 56,
title: 'Fly',
category: 'Hosting',
route: '/library/fly.svg',
url: 'https://fly.io'
},
{
id: 57,
title: 'LearnThis',
category: 'Education',
route: '/library/learnthis.svg',
url: 'https://learnthisacademy.com'
},
{
id: 58,
title: 'Visual Studio',
category: 'Software',
route: '/library/visual-studio.svg',
url: 'https://visualstudio.microsoft.com'
},
{
id: 59,
title: 'Chakra UI',
category: 'Library',
route: '/library/chakra-ui.svg',
url: 'https://chakra-ui.com'
},
{
id: 60,
title: 'Express.js',
category: 'Framework',
route: '/library/expressjs.svg',
url: 'https://expressjs.com'
},
{
id: 61,
title: 'Fastify',
category: 'Framework',
route: '/library/fastify.svg',
url: 'https://www.fastify.io'
},
{
id: 62,
title: 'JavaScript',
category: 'Language',
route: '/library/javascript.svg',
url: 'https://developer.mozilla.org/docs/Web/JavaScript'
},
{
id: 63,
title: 'jQuery',
category: 'Language',
route: '/library/jquery.svg',
url: 'https://jquery.com'
},
{
id: 64,
title: 'Rapid API',
category: 'Software',
route: '/library/rapidapi.svg',
url: 'https://rapidapi.com'
},
{
id: 65,
title: 'TypeScript',
category: 'Language',
route: '/library/typescript.svg',
url: 'https://www.typescriptlang.org'
},
{
id: 66,
title: 'Bun',
category: 'Library',
route: '/library/bun.svg',
url: 'https://bun.sh'
},
{
id: 67,
title: 'Twilio',
category: 'Software',
route: '/library/twilio.svg',
url: 'https://twilio.com'
},
{
id: 68,
title: 'Arc',
category: 'Software',
route: '/library/arc.svg',
url: 'https://arc.dev'
},
{
id: 69,
title: 'Qwik',
category: 'Framework',
route: '/library/qwik.svg',
url: 'https://qwik.builder.io/'
},
{
id: 70,
title: 'Authy',
category: 'Software',
route: '/library/authy.svg',
url: 'https://authy.com/'
},
{
id: 71,
title: 'Notion',
category: 'Software',
route: '/library/notion.svg',
url: 'https://notion.so/'
},
{
id: 72,
title: 'NestJS',
category: 'Framework',
route: '/library/nestjs.svg',
url: 'https://nestjs.com/'
},
{
id: 73,
title: 'Github Copilot',
category: 'Software',
route: '/library/copilot.svg',
url: 'https://github.com/features/copilot'
},
{
id: 74,
title: 'Railway',
category: 'Software',
route: '/library/railway.svg',
url: 'https://railway.app/'
},
{
id: 75,
title: 'Docusaurus',
category: 'Software',
route: '/library/docusaurus.svg',
url: 'https://docusaurus.io/'
},
{
id: 76,
title: 'Twitch',
category: 'Entertainment',
route: '/library/twitch.svg',
url: 'https://twitch.tv'
},
{
id: 77,
title: 'GoDaddy',
category: 'Hosting',
route: '/library/godaddy.svg',
url: 'https://www.godaddy.com/'
},
{
id: 78,
title: 'Udemy',
category: 'Education',
route: '/library/udemy.svg',
url: 'https://www.udemy.com/'
},
{
id: 79,
title: 'GraphQL',
category: 'Language',
route: '/library/graphql.svg',
url: 'https://graphql.org/'
},
{
id: 80,
title: 'GitLab',
category: 'Software',
route: '/library/gitlab.svg',
url: 'https://gitlab.com/'
},
{
id: 81,
title: 'Prisma',
category: 'Software',
route: '/library/prisma.svg',
url: 'https://prisma.io/'
},
{
id: 82,
title: 'Go',
category: 'Language',
route: '/library/golang.svg',
url: 'https://go.dev/'
},
{
id: 83,
title: 'Platzi',
category: 'Education',
route: '/library/platzi.svg',
url: 'https://platzi.com/'
},
{
id: 84,
title: 'Coursera',
category: 'Education',
route: '/library/coursera.svg',
url: 'https://www.coursera.org/'
},
{
id: 85,
title: 'Udacity',
category: 'Education',
route: '/library/udacity.svg',
url: 'https://www.udacity.com/'
},
{
id: 86,
title: 'Kubernetes',
category: 'Software',
route: '/library/kubernetes.svg',
url: 'https://kubernetes.io/'
},
{
id: 87,
title: 'Docker',
category: 'Software',
route: '/library/docker.svg',
url: 'https://www.docker.com/'
},
{
id: 88,
title: 'Amazon Web Services',
category: 'Software',
route: '/library/aws.svg',
url: 'https://aws.amazon.com/'
},
{
id: 89,
title: 'Microsoft Azure',
category: 'Software',
route: '/library/azure.svg',
url: 'https://azure.microsoft.com/'
},
{
id: 90,
title: 'Heroku',
category: 'Software',
route: '/library/heroku.svg',
url: 'https://www.heroku.com/'
},
{
id: 91,
title: 'JetBrains',
category: 'Software',
route: '/library/jetbrains.svg',
url: 'https://www.jetbrains.com/'
},
{
id: 92,
title: 'JetBrains Rider',
category: 'Software',
route: '/library/rider.svg',
url: 'https://www.jetbrains.com/rider/'
},
{
id: 93,
title: 'PlanetScale',
category: 'Database',
route: '/library/planetscale.svg',
url: 'https://planetscale.com/'
},
{
id: 94,
title: 'Playwright',
category: 'Framework',
route: '/library/playwright.svg',
url: 'https://playwright.dev/'
},
{
id: 95,
title: 'Atlassian',
category: 'Software',
route: '/library/atlassian.svg',
url: 'https://www.atlassian.com/'
},
{
id: 96,
title: 'Discourse',
category: 'Software',
route: '/library/discourse.svg',
url: 'https://discourse.org/'
},
{
id: 97,
title: 'Ember',
category: 'Framework',
route: '/library/ember.svg',
url: 'https://emberjs.com/'
},
{
id: 98,
title: 'Expo',
category: 'Software',
route: '/library/expo.svg',
url: 'https://expo.dev/'
},
{
id: 99,
title: 'Flutter',
category: 'Framework',
route: '/library/flutter.svg',
url: 'https://flutter.dev/'
},
{
id: 100,
title: 'Fresh',
category: 'Framework',
route: '/library/fresh.svg',
url: 'https://fresh.deno.dev/'
},
{
id: 101,
title: 'Git',
category: 'Software',
route: '/library/git.svg',
url: 'https://git-scm.com/'
},
{
id: 102,
title: 'Hostgator',
category: 'Hosting',
route: '/library/hostgator.svg',
url: 'https://www.hostgator.com/'
},
{
id: 103,
title: 'IntelliJ IDEA',
category: 'Software',
route: '/library/intellijidea.svg',
url: 'https://www.jetbrains.com/idea/'
},
{
id: 104,
title: 'Jasmine',
category: 'Framework',
route: '/library/jasmine.svg',
url: 'https://jasmine.github.io/'
},
{
id: 105,
title: 'Java',
category: 'Language',
route: '/library/java.svg',
url: 'https://www.java.com/'
},
{
id: 106,
title: 'Jest',
category: 'Framework',
route: '/library/jest.svg',
url: 'https://jestjs.io/'
},
{
id: 107,
title: 'JetBrains',
category: 'Software',
route: '/library/jetbrainsSolid.svg',
url: 'https://www.jetbrains.com/'
},
{
id: 108,
title: 'KrakenJS',
category: 'Framework',
route: '/library/krakenjs.svg',
url: 'https://krakenjs.com/'
},
{
id: 109,
title: 'Laravel',
category: 'Framework',
route: '/library/laravel.svg',
url: 'https://laravel.com/'
},
{
id: 110,
title: 'MariaDB',
category: 'Database',
route: '/library/mariadb.svg',
url: 'https://mariadb.org/'
},
{
id: 111,
title: 'Material UI',
category: 'Framework',
route: '/library/materialui.svg',
url: 'https://mui.com/'
},
{
id: 112,
title: 'MySQL',
category: 'Database',
route: '/library/mysql.svg',
url: 'https://www.mysql.com/'
},
{
id: 113,
title: 'Parcel',
category: 'Compiler',
route: '/library/parcel.svg',
url: 'https://parceljs.org/'
},
{
id: 114,
title: 'PM2',
category: 'Framework',
route: '/library/pm2.svg',
url: 'https://pm2.io/'
},
{
id: 115,
title: 'PostgreSQL',
category: 'Database',
route: '/library/postgresql.svg',
url: 'https://www.postgresql.org/'
},
{
id: 116,
title: 'React Query',
category: 'Framework',
route: '/library/reactquery.svg',
url: 'https://tanstack.com/query/v4'
},
{
id: 117,
title: 'Redis',
category: 'Database',
route: '/library/redis.svg',
url: 'https://redis.io/'
},
{
id: 118,
title: 'RedwoodJS',
category: 'Framework',
route: '/library/redwoodjs.svg',
url: 'https://redwoodjs.com/'
},
{
id: 119,
title: 'Ruby',
category: 'Language',
route: '/library/ruby.svg',
url: 'https://www.ruby-lang.org/'
},
{
id: 120,
title: 'Scala',
category: 'Language',
route: '/library/scala.svg',
url: 'https://www.scala-lang.org/'
},
{
id: 121,
title: 'Sequelize',
category: 'Framework',
route: '/library/sequelize.svg',
url: 'https://sequelize.org/'
},
{
id: 122,
title: 'Spinnaker',
category: 'Software',
route: '/library/spinnaker.svg',
url: 'https://spinnaker.io/'
},
{
id: 123,
title: 'SQLite',
category: 'Database',
route: '/library/sqlite.svg',
url: 'https://www.sqlite.org/'
},
{
id: 124,
title: 'Swagger',
category: 'Software',
route: '/library/swagger.svg',
url: 'https://swagger.io/'
},
{
id: 125,
title: 'Swift',
category: 'Language',
route: '/library/swift.svg',
url: 'https://swift.org/'
},
{
id: 126,
title: 'Testing Library',
category: 'Framework',
route: '/library/testinglibrary.svg',
url: 'https://testing-library.com/'
},
{
id: 127,
title: 'TypeORM',
category: 'Database',
route: '/library/typeorm.svg',
url: 'https://typeorm.io/'
},
{
id: 128,
title: 'Unity',
category: 'Software',
route: '/library/unity.svg',
url: 'https://unity.com/'
},
{
id: 129,
title: 'Vim',
category: 'Software',
route: '/library/vim.svg',
url: 'https://www.vim.org/'
},
{
id: 130,
title: 'WMR',
category: 'Compiler',
route: '/library/wmr.svg',
url: 'https://wmr.dev/'
},
{
id: 131,
title: 'OpenBootcamp',
category: 'Education',
route: '/library/openbootcamp.svg',
url: 'https://open-bootcamp.com/'
},
{
id: 132,
title: 'Digital Ocean',
category: 'Software',
route: '/library/digitalocean.svg',
url: 'https://www.digitalocean.com/'
},
{
id: 133,
title: 'Disney+',
category: 'Entertainment',
route: '/library/disneyplus.svg',
url: 'https://www.disneyplus.com/'
},
{
id: 134,
title: 'React Router',
category: 'Library',
route: '/library/reactrouter.svg',
url: 'https://reactrouter.com/en/main'
},
{
id: 135,
title: 'Google Student Developer Club',
category: 'Google',
route: '/library/gdsc.svg',
url: 'https://gdsc.community.dev/'
},
{
id: 136,
title: 'Brave Browser',
category: 'Browser',
route: '/library/brave.svg',
url: 'https://brave.com/'
},
{
id: 137,
title: 'Eclipse IDE',
category: 'IDE',
route: '/library/eclipse.svg',
url: 'https://www.eclipse.org/'
},
{
id: 138,
title: 'HTML5',
category: 'Language',
route: '/library/html5.svg',
url: 'https://es.wikipedia.org/wiki/HTML5'
},
{
id: 139,
title: 'CSS',
category: 'Language',
route: '/library/css.svg',
url: 'https://es.wikipedia.org/wiki/CSS'
},
{
id: 140,
title: 'midudev',
category: 'Education',
route: '/library/midudev.svg',
url: 'https://midu.dev'
},
{
id: 141,
title: 'Apple',
category: 'Software',
route: '/library/apple.svg',
url: 'https://www.apple.com'
},
{
id: 142,
title: 'Android',
category: 'Software',
route: '/library/android.svg',
url: 'https://www.android.com/'
},
{
id: 143,
title: 'Windows',
category: 'Software',
route: '/library/windows.svg',
url: 'https://www.microsoft.com/windows'
},
{
id: 144,
title: 'Python',
category: 'Language',
route: '/library/python.svg',
url: 'https://www.python.org/'
},
{
id: 145,
title: 'Turbopack',
category: 'Software',
route: '/library/turbopack.svg',
url: 'https://turbo.build/'
},
{
id: 146,
title: 'Builder',
category: 'CMS',
route: '/library/builder.svg',
url: 'https://builder.io/'
},
{
id: 147,
title: 'Surrealdb',
category: 'Database',
route: '/library/surrealdb.svg',
url: 'https://surrealdb.com/'
},
{
id: 148,
title: 'Jetbrains Space',
category: 'Software',
route: '/library/jetbrains-space.svg',
url: 'https://www.jetbrains.com/space/'
},
{
id: 149,
title: 'Gin',
category: 'Framework',
route: '/library/gin.svg',
url: 'https://gin-gonic.com/'
},
{
id: 150,
title: 'Stimulus',
category: 'Framework',
route: '/library/stimulus.svg',
url: 'https://stimulus.hotwired.dev/'
},
{
id: 151,
title: 'WindiCSS',
category: 'Framework',
route: '/library/windicss.svg',
url: 'https://windicss.org/'
},
{
id: 152,
title: 'Mastodon',
category: 'Social',
route: '/library/mastodon.svg',
url: 'https://joinmastodon.org/'
},
{
id: 153,
title: 'Upstash',
category: 'Database',
route: '/library/upstash.svg',
url: 'https://upstash.com/'
},
{
id: 154,
title: 'Storyblok',
category: 'CMS',
route: '/library/storyblok.svg',
url: 'https://www.storyblok.com/'
},
{
id: 155,
title: 'Cloudflare Workers',
category: 'Software',
route: '/library/cloudflare-workers.svg',
url: 'https://workers.cloudflare.com/'
},
{
id: 156,
title: 'Cloudflare',
category: 'Software',
route: '/library/cloudflare.svg',
url: 'https://www.cloudflare.com/'
}
];

View File

@ -1,17 +0,0 @@
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;

14
src/index.test.ts Normal file
View File

@ -0,0 +1,14 @@
import { describe, it, expect } from 'vitest';
import { svgs } from './data/svgs';
describe('Get svgs by category', () => {
it('should have a category named "Social"', () => {
expect(svgs.find((svg) => svg.category === 'Social')).toBeDefined();
});
});
describe('Get a specific svg', () => {
it('should have a svg named "Discord"', () => {
expect(svgs.find((svg) => svg.title === 'Discord')).toBeDefined();
});
});

View File

@ -1,48 +0,0 @@
export interface LayoutProps {
children: React.ReactNode;
}
export interface CustomLinkProps {
href: string;
children: React.ReactNode;
external?: boolean;
font?: string;
mr?: string;
ml?: string;
}
export interface CustomIconBtnProps {
title: string;
icon: React.ReactElement;
mr?: string;
ml?: string;
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;
}
export interface SearchProps {
availableFocus?: boolean;
}

View File

@ -1,21 +0,0 @@
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;

View File

@ -1,48 +0,0 @@
import React from "react";
import useSWR from "swr";
import { getCategorySvgs } from "@/services";
import CustomLink from "@/common/link";
import { Box, useColorModeValue } from "@chakra-ui/react";
import { RaceBy } from "@uiball/loaders";
import Tap from "@/animations/tap";
import { useRouter } from "next/router";
const Categories = () => {
const { data, error } = useSWR(getCategorySvgs);
const color = useColorModeValue("rgb(0,0,0, .1)", "rgb(255,255,255, .1)");
const router = useRouter();
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) => (
<Tap key={category}>
<CustomLink
href={`/category/${category}`}>
<Box
p={4}
borderRadius="4px"
borderWidth="1px"
__css={
router.asPath === `/category/${category}`
? {
backgroundColor: '#4343e5',
color: '#fff'
}
: {}
}
_hover={{
border:`1px solid ${color}`,
transform: "scale(0.98)",
}}>
{category}
</Box>
</CustomLink>
</Tap>
))}
</>
);
};
export default Categories;

View File

@ -1,120 +0,0 @@
import {
Box,
Flex,
useColorModeValue,
HStack,
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'
import { useHotkeys } from 'react-hotkeys-hook'
const Header = () => {
const bg = useColorModeValue('bg.light', 'bg.dark')
const { isOpen, onToggle } = useDisclosure()
useHotkeys('ctrl+k', (e) => {
e.preventDefault()
onToggle()
})
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={2}>
<Box display={{ base: 'none', md: 'inline-flex' }}>
{Links.map((link) => (
<CustomLink
key={link.title}
href={link.slug}
external={link.external}
font='Inter-Semibold'
mr='4'
ml='3'
>
<Tap>
{link.title}
{link.external ? (
<Icon as={ArrowSquareOut} ml='2' />
) : null}
</Tap>
</CustomLink>
))}
</Box>
<HStack
spacing={1}
mr={1}
display={{ base: 'none', md: 'inline-flex' }}
>
<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' display={{ base: 'none', md: 'block' }}>
<Search availableFocus={isOpen} />
</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', lg: 'center' }}
flexWrap={{ base: 'initial', lg: 'wrap' }}
spacing={4}
overflowX='auto'
overflowY='hidden'
bg={bg}
pb='4'
borderBottomWidth='1px'
>
<Categories />
</HStack>
</Box>
</>
)
}
export default Header

View File

@ -1,12 +0,0 @@
export const Links = [
{
title: "Github",
slug: "https://github.com/pheralb/svgl",
external: true,
},
{
title: "Twitter",
slug: "https://twitter.com/pheralb_",
external: true,
},
];

View File

@ -1,59 +0,0 @@
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";
import Theme from "./theme";
const Mobile = () => {
const bg = useColorModeValue("bg.light", "bg.dark");
const mobileNav = useDisclosure();
return (
<>
<Theme />
<IconButton
display={{ base: "flex", md: "none" }}
aria-label="Open menu navbar"
variant="ghost"
icon={<List size={22} />}
onClick={mobileNav.onOpen}
ml="2"
/>
<VStack
pos="absolute"
top={0}
left={0}
right={0}
display={mobileNav.isOpen ? "flex" : "none"}
flexDirection="column"
p={4}
pb={4}
bg={bg}
spacing={5}
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}
>
{link.title}
</CustomLink>
))}
</VStack>
</>
);
};
export default Mobile;

View File

@ -1,31 +0,0 @@
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;

View File

@ -1,17 +0,0 @@
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;

View File

@ -1,21 +0,0 @@
import CustomLink from "@/common/link";
import { Flex, Center, Heading, Text, Button } from "@chakra-ui/react";
import { House } from "phosphor-react";
const Error = () => {
return (
<>
<Center>
<Flex direction="column" justifyContent="center" alignItems="center">
<Heading mb="2">Error 404</Heading>
<Text mb="3">The page you are trying to access does not exist.</Text>
<CustomLink href="/">
<Text fontFamily="Inter-Semibold">Go home</Text>
</CustomLink>
</Flex>
</Center>
</>
);
};
export default Error;

View File

@ -1,64 +0,0 @@
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}
options={{ showSpinner: false }}
/>
<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;

View File

@ -1,18 +0,0 @@
import { ColorModeScript } from "@chakra-ui/react";
import NextDocument, { Html, Head, Main, NextScript } from "next/document";
import theme from "@/theme";
export default class Document extends NextDocument {
render() {
return (
<Html lang="en">
<Head />
<body>
<ColorModeScript initialColorMode={theme.config.initialColorMode} />
<Main />
<NextScript />
</body>
</Html>
);
}
}

View File

@ -1,12 +0,0 @@
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);
}

View File

@ -1,10 +0,0 @@
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);
}

View File

@ -1,33 +0,0 @@
import db from "data/svgs.json";
import { NextApiRequest, NextApiResponse } from "next";
export default function handler(req: NextApiRequest, res: NextApiResponse) {
const { id, q, c } = req.query;
// 🔎 Search by id (ex: ?id=1) ->
if (id) {
const item = db.find((item) => item.id === +id);
return res.status(200).json(item);
}
// 🔎 Search by query (ex: ?q=d) ->
if (q) {
const results = db.filter((product) => {
const { title } = product;
return title.toLowerCase().includes(q.toString().toLowerCase());
});
return res.status(200).json(results);
}
// 🔎 Search by category (ex: ?c=library) ->
if (c) {
const results = db.filter((product) => {
const { category } = product;
return category.toLowerCase().includes(c.toString().toLowerCase());
});
return res.status(200).json(results);
}
// ✖ Error ->
res.status(400).json({ info: "[/api/search] Error: api query not found." });
}

View File

@ -1,39 +0,0 @@
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>
</>
);
}

View File

@ -1,28 +0,0 @@
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;

View File

@ -1,29 +0,0 @@
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>
</>
);
}

View File

@ -0,0 +1,5 @@
import type { LayoutServerLoad } from './$types';
export const load: LayoutServerLoad = async ({ url: { pathname } }) => {
return { pathname };
};

114
src/routes/+layout.svelte Normal file
View File

@ -0,0 +1,114 @@
<script lang="ts">
import type { LayoutServerData } from './$types';
export let data: LayoutServerData;
// Global styles:
import '../app.css';
// Get categories:
import { svgs } from '@/data/svgs';
const categories = svgs
.map((svg) => svg.category)
.filter((category, index, array) => array.indexOf(category) === index);
// Icons:
import Heart from 'phosphor-svelte/lib/Heart';
import ArrowUpRight from 'phosphor-svelte/lib/ArrowUpRight';
import ArrowLeft from 'phosphor-svelte/lib/ArrowLeft';
// Toaster:
import { Toaster } from 'svelte-french-toast';
// Components for all pages:
import Transition from '@/components/transition.svelte';
import Container from '@/components/container.svelte';
import Theme from '@/components/theme.svelte';
</script>
<main class="min-h-screen bg-light font-sans text-mini dark:bg-dark dark:text-white">
<nav
class="z-50 w-full overflow-y-auto overflow-x-hidden border-b border-neutral-800 md:fixed md:top-0 md:left-0 md:h-full md:w-60 md:border-none md:pb-10"
>
<div class="px-6 py-6">
<div class="mb-3 border-b border-neutral-300 pb-3 dark:border-neutral-700/40">
<div class="flex items-center justify-between">
<a href="/">
<div
class="flex items-center space-x-2 duration-150 hover:text-neutral-500 dark:hover:text-neutral-300"
>
<h3 class="text-xl font-medium">svgl</h3>
<p class="text-neutral-500">v3.0.0</p>
</div>
</a>
<Theme />
</div>
<p class="mt-2 font-medium text-neutral-400">✨ Optimized SVGs for web</p>
</div>
<div
class="flex items-center space-x-1 overflow-y-auto border-b border-neutral-300 pb-3 dark:border-neutral-700/40 md:mb-3 md:flex-col md:space-x-0 md:space-y-1 md:overflow-y-visible"
>
{#each categories as category}
<a
href={`/directory/${category.toLowerCase()}`}
class={`flex w-full items-center rounded-md p-2 transition-none duration-100 hover:bg-neutral-200 dark:hover:bg-neutral-700/40
${
data.pathname === `/directory/${category.toLowerCase()}`
? 'bg-neutral-200 dark:bg-neutral-700/30'
: ''
}`}
data-sveltekit-preload-data>{category}</a
>
{/each}
</div>
<div
class="mt-3 flex flex-row items-center space-x-2 border-b border-neutral-300 pb-3 dark:border-neutral-700/40 md:mt-0 md:flex-col md:space-x-0 md:space-y-1"
>
<a
href="https://github.com/pheralb/svgl#-getting-started"
target="_blank"
class="flex w-full items-center space-x-2 rounded-md p-2 duration-100 hover:bg-neutral-200 dark:hover:bg-neutral-700/40"
>
<span>Submit logo</span>
<ArrowUpRight size={16} />
</a>
<a
href="https://github.com/pheralb/svgl#-getting-started"
target="_blank"
class="flex w-full items-center space-x-2 rounded-md p-2 duration-100 hover:bg-neutral-200 dark:hover:bg-neutral-700/40"
>
<span>Repository</span>
<ArrowUpRight size={16} />
</a>
</div>
<a
href="https://twitter.com/pheralb_"
target="_blank"
class="mt-5 flex items-center space-x-2 duration-100 hover:text-dark dark:text-neutral-400 dark:hover:text-white"
>
<Heart color="#991b1b" size={18} weight={'duotone'} />
<div class="flex items-center space-x-1">
<p class="text-muted text-sm">Created by pheralb</p>
<ArrowUpRight size={12} />
</div>
</a>
</div>
</nav>
<div class="py-2 md:ml-60 md:py-6">
{#if data.pathname !== '/'}
<Container>
<a href="/">
<div
class="flex items-center space-x-2 duration-100 dark:text-neutral-400 dark:hover:text-white"
>
<ArrowLeft size={20} />
<span>Back to home</span>
</div>
</a>
</Container>
{/if}
<Transition pathname={data.pathname}>
<slot />
</Transition>
<Toaster position="bottom-center" />
</div>
</main>

47
src/routes/+page.svelte Normal file
View File

@ -0,0 +1,47 @@
<script lang="ts">
import type { iSVG } from '@/types/svg';
// Get all svgs:
import { svgs } from '@/data/svgs';
const allSvgs = JSON.parse(JSON.stringify(svgs));
// Components:
import Search from '@/components/search.svelte';
import Container from '@/components/container.svelte';
import SvgCard from '@/components/svgCard.svelte';
import Grid from '@/components/grid.svelte';
// Search:
let searchTerm = '';
let filteredSvgs: iSVG[] = [];
if (searchTerm.length === 0) {
filteredSvgs = allSvgs.sort((a: iSVG, b: iSVG) => {
return b.id - a.id;
});
}
const searchSvgs = () => {
return (filteredSvgs = allSvgs.filter((svg: iSVG) => {
let svgTitle = svg.title.toLowerCase();
return svgTitle.includes(searchTerm.toLowerCase());
}));
};
</script>
<svelte:head>
<title>A beautiful library with SVG logos - Svgl</title>
</svelte:head>
<Container>
<Search
bind:searchTerm
on:input={searchSvgs}
placeholder={`Search ${filteredSvgs.length} logos...`}
/>
<Grid>
{#each filteredSvgs as svg}
<SvgCard svgInfo={svg} />
{/each}
</Grid>
</Container>

View File

@ -0,0 +1,47 @@
<script lang="ts">
import type { PageData } from './$types';
import type { iSVG } from '@/types/svg';
export let data: PageData;
let svgsByCategory = data.props?.svgs || [];
let category = data.props?.category || '';
// Components:
import Container from '@/components/container.svelte';
import Grid from '@/components/grid.svelte';
import Search from '@/components/search.svelte';
import SvgCard from '@/components/svgCard.svelte';
// Search:
let searchTerm = '';
let filteredSvgs: iSVG[] = [];
if (searchTerm.length === 0) {
filteredSvgs = svgsByCategory.sort((a: iSVG, b: iSVG) => {
return b.id - a.id;
});
}
const searchSvgs = () => {
return (filteredSvgs = svgsByCategory.filter((svg: iSVG) => {
let svgTitle = svg.title.toLowerCase();
return svgTitle.includes(searchTerm.toLowerCase());
}));
};
</script>
<svelte:head>
<title>{category} logos - Svgl</title>
</svelte:head>
<Container>
<Search
bind:searchTerm
on:input={searchSvgs}
placeholder={`Search ${filteredSvgs.length} ${category} logos...`}
/>
<Grid>
{#each filteredSvgs as svg}
<SvgCard svgInfo={svg} />
{/each}
</Grid>
</Container>

View File

@ -0,0 +1,24 @@
import { error } from '@sveltejs/kit';
import type { PageLoad } from './$types';
import { svgs } from '@/data/svgs';
import type { iSVG } from '@/types/svg';
export const load = (async ({ params }) => {
const { slug } = params;
// Check if slug is valid:
if (!slug) {
return error(404, 'Not found');
}
// Filter out the svg with the matching slug:
const svgsByCategory = svgs.filter((svg: iSVG) => svg.category.toLowerCase() === slug);
return {
props: {
category: slug,
svgs: svgsByCategory
}
};
}) satisfies PageLoad;

View File

@ -1,10 +0,0 @@
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();
};

View File

@ -1,6 +0,0 @@
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=";

View File

@ -1,16 +0,0 @@
/* 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;
}

View File

@ -1,44 +0,0 @@
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,
};

View File

@ -1,5 +0,0 @@
import Button from "./button";
export default {
Button,
};

View File

@ -1,44 +0,0 @@
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;

View File

@ -1,8 +0,0 @@
export const ToastTheme = {
icon: "🔔",
style: {
borderRadius: "10px",
background: "#1F2023",
color: "#fff",
},
};

View File

@ -1,8 +1,7 @@
export interface SvgData { export interface iSVG {
id: number; id: number;
slug: string;
title: string; title: string;
category: string; category: string;
categories?: string[]; route: string;
url: string; url: string;
} }

View File

@ -0,0 +1,7 @@
export const MIMETYPE = 'text/plain';
export const getSvgContent = async (url: string | undefined, isSupported: boolean) => {
const response = await fetch(url || '');
const content = await response.text();
const blob = new Blob([content], { type: MIMETYPE });
return isSupported ? blob : content;
};

Binary file not shown.

View File

Before

Width:  |  Height:  |  Size: 105 KiB

After

Width:  |  Height:  |  Size: 105 KiB

View File

Before

Width:  |  Height:  |  Size: 187 KiB

After

Width:  |  Height:  |  Size: 187 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 8.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 24 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 32 KiB

BIN
static/images/logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 44 KiB

Some files were not shown because too many files have changed in this diff Show More