13
.eslintignore
Normal 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
@ -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
|
||||
}
|
||||
};
|
@ -1,3 +0,0 @@
|
||||
{
|
||||
"extends": ["next/core-web-vitals"]
|
||||
}
|
1
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1 @@
|
||||
github: pheralb
|
26
.github/workflows/ci.yml
vendored
Normal 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
@ -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-*
|
||||
|
13
.prettierignore
Normal 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
@ -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
@ -0,0 +1,3 @@
|
||||
{
|
||||
"recommendations": ["esbenp.prettier-vscode", "dbaeumer.vscode-eslint", "svelte.svelte-vscode"]
|
||||
}
|
8
.vscode/settings.json
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
{
|
||||
"editor.codeActionsOnSave": {
|
||||
"source.fixAll.eslint": true
|
||||
},
|
||||
"editor.defaultFormatter": "esbenp.prettier-vscode",
|
||||
"editor.formatOnSave": true,
|
||||
"tailwindCSS.experimental.classRegex": [["[\"'`]([^\"'`]*).*?[\"'`]"]]
|
||||
}
|
2
LICENSE
@ -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
@ -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> ● </span>
|
||||
<a href="#-getting-started">
|
||||
Submit logo
|
||||
</a>
|
||||
<span> ● </span>
|
||||
<a href="#%EF%B8%8F-stack">
|
||||
Stack
|
||||
</a>
|
||||
<span> ● </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).
|
||||
|
1010
data/svgs.json
@ -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
@ -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.
|
@ -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",
|
||||
},
|
||||
};
|
@ -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;
|
69
package.json
@ -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
@ -0,0 +1,6 @@
|
||||
module.exports = {
|
||||
plugins: {
|
||||
tailwindcss: {},
|
||||
autoprefixer: {},
|
||||
},
|
||||
}
|
4
prettier.config.cjs
Normal file
@ -0,0 +1,4 @@
|
||||
/** @type {import("prettier").Config} */
|
||||
module.exports = {
|
||||
plugins: [require.resolve('prettier-plugin-tailwindcss')]
|
||||
};
|
Before Width: | Height: | Size: 6.0 KiB |
Before Width: | Height: | Size: 8.8 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 6.2 KiB |
Before Width: | Height: | Size: 5.7 KiB |
Before Width: | Height: | Size: 14 KiB |
Before Width: | Height: | Size: 12 KiB |
Before Width: | Height: | Size: 6.9 KiB |
Before Width: | Height: | Size: 20 KiB |
Before Width: | Height: | Size: 7.1 KiB |
@ -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 |
@ -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 |
@ -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 |
@ -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 |
@ -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"
|
||||
}
|
||||
]
|
||||
}
|
@ -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();
|
||||
});
|
||||
});
|
@ -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;
|
@ -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
@ -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
@ -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
@ -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>
|
@ -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;
|
@ -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;
|
@ -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;
|
3
src/components/container.svelte
Normal file
@ -0,0 +1,3 @@
|
||||
<div class="container mx-auto px-6 pt-4 xl:px-4">
|
||||
<slot />
|
||||
</div>
|
@ -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;
|
5
src/components/grid.svelte
Normal 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>
|
@ -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;
|
21
src/components/search.svelte
Normal 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>
|
@ -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;
|
82
src/components/svgCard.svelte
Normal 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>
|
@ -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;
|
@ -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;
|
63
src/components/theme.svelte
Normal 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>
|
10
src/components/transition.svelte
Normal 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
@ -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/'
|
||||
}
|
||||
];
|
@ -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
@ -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();
|
||||
});
|
||||
});
|
@ -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;
|
||||
}
|
@ -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;
|
@ -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;
|
@ -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
|
@ -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,
|
||||
},
|
||||
];
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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;
|
@ -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>
|
||||
);
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
@ -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);
|
||||
}
|
@ -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." });
|
||||
}
|
@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
@ -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;
|
@ -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>
|
||||
</>
|
||||
);
|
||||
}
|
5
src/routes/+layout.server.ts
Normal 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
@ -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
@ -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>
|
47
src/routes/directory/[slug]/+page.svelte
Normal 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>
|
24
src/routes/directory/[slug]/+page.ts
Normal 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;
|
@ -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();
|
||||
};
|
@ -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=";
|
@ -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;
|
||||
}
|
@ -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,
|
||||
};
|
@ -1,5 +0,0 @@
|
||||
import Button from "./button";
|
||||
|
||||
export default {
|
||||
Button,
|
||||
};
|
@ -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;
|
@ -1,8 +0,0 @@
|
||||
export const ToastTheme = {
|
||||
icon: "🔔",
|
||||
style: {
|
||||
borderRadius: "10px",
|
||||
background: "#1F2023",
|
||||
color: "#fff",
|
||||
},
|
||||
};
|
@ -1,8 +1,7 @@
|
||||
export interface SvgData {
|
||||
export interface iSVG {
|
||||
id: number;
|
||||
slug: string;
|
||||
title: string;
|
||||
category: string;
|
||||
categories?: string[];
|
||||
route: string;
|
||||
url: string;
|
||||
}
|
7
src/utils/getSvgContent.ts
Normal 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;
|
||||
};
|
BIN
static/fonts/GeneralSans-Variable.woff2
Normal file
Before Width: | Height: | Size: 105 KiB After Width: | Height: | Size: 105 KiB |
Before Width: | Height: | Size: 187 KiB After Width: | Height: | Size: 187 KiB |
BIN
static/images/icons/icon-192x192.png
Normal file
After Width: | Height: | Size: 8.7 KiB |
BIN
static/images/icons/icon-256x256.png
Normal file
After Width: | Height: | Size: 11 KiB |
BIN
static/images/icons/icon-384x384.png
Normal file
After Width: | Height: | Size: 24 KiB |
BIN
static/images/icons/icon-512x512.png
Normal file
After Width: | Height: | Size: 32 KiB |
BIN
static/images/logo.png
Normal file
After Width: | Height: | Size: 44 KiB |