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 ->
/node_modules
/.pnp
.pnp.js
# Dependencies
node_modules
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
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
Copyright (c) 2022 Pablo Hernández
Copyright (c) 2022 Pablo Hdez
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

147
README.md
View File

@ -1,108 +1,103 @@
<p align="center">
<div align="center">
<a href="https://svgl.vercel.app">
<img src="static/images/screenshot.png">
</a>
<p></p>
</div>
<div align="center">
<a href="https://svgl.vercel.app/" target="_blank">
<img src="https://i.postimg.cc/1tzrP2rg/banner-corner.png" width="800px" alt="SVGL Banner" />
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>
## 📦 Packages:
<div align="center">
- ⚡️ [Nextjs](https://nextjs.org/) - The React Framework for Production.
- ⚒️ [React 18](https://reactjs.org/) - A JavaScript library for building user interfaces.
- 💙 [Typescript](https://www.typescriptlang.org/) - A superset of JavaScript.
- ✅ [Vitest](https://vitest.dev/) - A blazing fast unit test framework.
- 💅 [Chakra UI](https://chakra-ui.com/) - Create accessible React apps with speed.
- 💥 [Framer Motion](https://www.framer.com/motion/) - Production-ready motion library.
- 💖 [Phosphor Icons](https://phosphoricons.com/) - A flexible icon family for everyone.
- ⬇️ [Next-PWA](https://github.com/shadowwalker/next-pwa) - Zero config PWA plugin for Next.js, with workbox.
[![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)
![GitHub stars](https://img.shields.io/github/stars/pheralb/svgl)
![GitHub issues](https://img.shields.io/github/issues/pheralb/svgl)
![GitHub forks](https://img.shields.io/github/forks/pheralb/svgl)
![GitHub license](https://img.shields.io/github/license/pheralb/svgl)
![GitHub PRs](https://img.shields.io/github/issues-pr/pheralb/svgl)
## 🚀 Getting started:
</div>
You need:
## 🛠️ Stack
- [Node.js 16+ (recommend: 16.15.1 LTS)](https://nodejs.org/en/)
- [Git](https://git-scm.com/book/en/v2/Getting-Started-Installing-Git)
- [**Sveltekit**](https://kit.svelte.dev/) - Web development, streamlined.
- [**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
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
# with npm:
npm install
# or
# with pnpm:
pnpm install
# with ultra:
ultra install
# with yarn:
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
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).
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:
```json
{
"id": 1,
"slug": "/library/your_logo.svg",
"title": "Logo Title",
"category": "Logo Category",
"url": "Your Website / app url"
}
"title": "Title",
"category": "Category",
"route": "/library/your_logo.svg",
"url": "Website"
},
```
5. Create a commit and push:
And create a pull request with your logo 🚀.
```bash
git add .
git commit -m "🥰 Added my logo"
git push origin main
```
## ✌️ Contributing
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
- /api/all: returns all the logos.
- /api/search?id=2: returns the logo with id 2.
- /api/search?q=logo: returns the logo with query.
```
## ⚒️ Shortcuts:
- ⭐ SVG Library: [/public/library/](https://github.com/pheralb/svgl/tree/main/public/library).
- ✍️ SVG JSON logos: [/data/](https://github.com/pheralb/svgl/tree/main/data).
## 🔑 License:
## 🔑 License
- [MIT](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",
"version": "2.0.1",
"author": "@pheralb_",
"version": "3.0.0",
"description": "A beautiful library with SVG logos.",
"private": true,
"author": "@pheralb_",
"license": "MIT",
"type": "module",
"keywords": [
"svgs",
"logos",
@ -12,45 +13,41 @@
"library"
],
"scripts": {
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint",
"test": "vitest",
"ready": "vitest && next build"
"dev": "vite dev",
"host": "vite dev --host",
"build": "vite build",
"preview": "vite preview",
"check": "svelte-kit sync && svelte-check --tsconfig ./tsconfig.json",
"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": {
"@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",
"framer-motion": "6.5.1",
"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"
"svelte-french-toast": "1.0.3"
},
"devDependencies": {
"@testing-library/jest-dom": "5.16.4",
"@testing-library/react": "13.3.0",
"@types/canvas-confetti": "1.4.3",
"@sveltejs/adapter-auto": "2.0.0",
"@sveltejs/kit": "1.11.0",
"@types/downloadjs": "1.4.3",
"@types/node": "18.6.3",
"@types/react": "18.0.15",
"@types/react-dom": "18.0.6",
"@vitejs/plugin-react": "2.0.0",
"eslint": "8.21.0",
"eslint-config-next": "12.2.3",
"jsdom": "20.0.0",
"typescript": "4.7.4",
"vitest": "0.20.2"
"@typescript-eslint/eslint-plugin": "5.55.0",
"@typescript-eslint/parser": "5.55.0",
"autoprefixer": "10.4.14",
"eslint": "8.36.0",
"eslint-config-prettier": "8.7.0",
"eslint-plugin-svelte3": "4.0.0",
"phosphor-svelte": "1.2.1",
"postcss": "8.4.21",
"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;
slug: string;
title: string;
category: string;
categories?: string[];
route: 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