mirror of
https://github.com/pheralb/svgl.git
synced 2025-12-29 08:01:36 +08:00
Merge branch 'main' into main
This commit is contained in:
+91
-26
@@ -96,6 +96,12 @@ export const svgs: iSVG[] = [
|
||||
route: '/library/animate.svg',
|
||||
url: 'https://www.adobe.com/products/animate'
|
||||
},
|
||||
{
|
||||
title: 'Apollo.io',
|
||||
category: 'Software',
|
||||
route: '/library/apollo.io.svg',
|
||||
url: 'https://www.apollo.io/'
|
||||
},
|
||||
{
|
||||
title: 'Blender',
|
||||
category: ['Software', 'Design'],
|
||||
@@ -162,8 +168,15 @@ export const svgs: iSVG[] = [
|
||||
{
|
||||
title: 'React',
|
||||
category: 'Library',
|
||||
route: '/library/react.svg',
|
||||
url: 'https://reactjs.org/'
|
||||
route: {
|
||||
light: '/library/react_light.svg',
|
||||
dark: '/library/react_dark.svg'
|
||||
},
|
||||
wordmark: {
|
||||
light: '/library/react_wordmark_light.svg',
|
||||
dark: '/library/react_wordmark_dark.svg'
|
||||
},
|
||||
url: 'https://react.dev/'
|
||||
},
|
||||
{
|
||||
title: 'Svelte',
|
||||
@@ -317,7 +330,12 @@ export const svgs: iSVG[] = [
|
||||
light: '/library/deno.svg',
|
||||
dark: '/library/deno_dark.svg'
|
||||
},
|
||||
url: 'https://deno.land/'
|
||||
wordmark: {
|
||||
light: '/library/deno_wordmark.svg',
|
||||
dark: '/library/deno_wordmark_dark.svg'
|
||||
},
|
||||
brandUrl: 'https://deno.com/brand',
|
||||
url: 'https://deno.com/'
|
||||
},
|
||||
{
|
||||
title: 'Gatsby',
|
||||
@@ -353,6 +371,11 @@ export const svgs: iSVG[] = [
|
||||
title: 'Tailwind CSS',
|
||||
category: 'Framework',
|
||||
route: '/library/tailwindcss.svg',
|
||||
wordmark: {
|
||||
light: '/library/tailwindcss-wordmark.svg',
|
||||
dark: '/library/tailwindcss-wordmark-dark.svg'
|
||||
},
|
||||
brandUrl: 'https://tailwindcss.com/brand',
|
||||
url: 'https://tailwindcss.com/'
|
||||
},
|
||||
{
|
||||
@@ -2135,9 +2158,13 @@ export const svgs: iSVG[] = [
|
||||
url: 'https://www.jetbrains.com/phpstorm/'
|
||||
},
|
||||
{
|
||||
title: 'MonkeyType',
|
||||
title: 'Monkeytype',
|
||||
category: 'Software',
|
||||
route: '/library/monkeytype.svg',
|
||||
wordmark: {
|
||||
dark: '/library/monkeytype-wordmark-dark.svg',
|
||||
light: '/library/monkeytype-wordmark-light.svg'
|
||||
},
|
||||
url: 'https://monkeytype.com/'
|
||||
},
|
||||
{
|
||||
@@ -2344,7 +2371,7 @@ export const svgs: iSVG[] = [
|
||||
},
|
||||
{
|
||||
title: 'Google Idx',
|
||||
category: 'Software',
|
||||
category: ['Software', 'Google'],
|
||||
route: '/library/google-idx.svg',
|
||||
url: 'https://idx.dev/'
|
||||
},
|
||||
@@ -2405,7 +2432,7 @@ export const svgs: iSVG[] = [
|
||||
},
|
||||
{
|
||||
title: 'Bitwarden',
|
||||
category: 'Software',
|
||||
category: ['Software', 'Authentication'],
|
||||
route: '/library/bitwarden.svg',
|
||||
url: 'https://bitwarden.com/'
|
||||
},
|
||||
@@ -2456,7 +2483,7 @@ export const svgs: iSVG[] = [
|
||||
},
|
||||
{
|
||||
title: 'Google PaLM',
|
||||
category: 'AI',
|
||||
category: ['AI', 'Google'],
|
||||
route: '/library/google-palm.svg',
|
||||
url: 'https://ai.google/discover/palm2/'
|
||||
},
|
||||
@@ -2656,20 +2683,6 @@ export const svgs: iSVG[] = [
|
||||
wordmark: '/library/tina_wordmark.svg',
|
||||
url: 'https://tina.io/'
|
||||
},
|
||||
{
|
||||
title: 'Vercel',
|
||||
category: ['Hosting', 'Vercel'],
|
||||
route: {
|
||||
light: '/library/vercel.svg',
|
||||
dark: '/library/vercel_dark.svg'
|
||||
},
|
||||
wordmark: {
|
||||
light: '/library/vercel_wordmark.svg',
|
||||
dark: '/library/vercel_wordmark_dark.svg'
|
||||
},
|
||||
brandUrl: 'https://vercel.com/geist/brands',
|
||||
url: 'https://vercel.com/'
|
||||
},
|
||||
{
|
||||
title: 'Next.js',
|
||||
category: ['Framework', 'Vercel'],
|
||||
@@ -2899,7 +2912,7 @@ export const svgs: iSVG[] = [
|
||||
},
|
||||
{
|
||||
title: 'Firebase',
|
||||
category: 'Hosting',
|
||||
category: ['Hosting', 'Google'],
|
||||
route: '/library/firebase.svg',
|
||||
wordmark: '/library/firebase-wordmark.svg',
|
||||
url: 'https://firebase.google.com/'
|
||||
@@ -2946,25 +2959,25 @@ export const svgs: iSVG[] = [
|
||||
},
|
||||
{
|
||||
title: 'Vite',
|
||||
category: ['Devtool', 'void(0)'],
|
||||
category: ['Devtool', 'VoidZero'],
|
||||
route: '/library/vitejs.svg',
|
||||
url: 'https://vitejs.dev'
|
||||
},
|
||||
{
|
||||
title: 'Vitest',
|
||||
category: ['Framework', 'void(0)'],
|
||||
category: ['Framework', 'VoidZero'],
|
||||
route: '/library/vitest.svg',
|
||||
url: 'https://vitest.dev/'
|
||||
},
|
||||
{
|
||||
title: 'Oxc',
|
||||
category: ['Devtool', 'void(0)'],
|
||||
category: ['Devtool', 'VoidZero'],
|
||||
route: '/library/oxc.svg',
|
||||
url: 'https://oxc.rs/'
|
||||
},
|
||||
{
|
||||
title: 'Rolldown',
|
||||
category: ['Compiler', 'void(0)'],
|
||||
category: ['Compiler', 'VoidZero'],
|
||||
route: '/library/rolldown.svg',
|
||||
url: 'https://rolldown.rs/'
|
||||
},
|
||||
@@ -3228,5 +3241,57 @@ export const svgs: iSVG[] = [
|
||||
category: 'Authentication',
|
||||
route: '/library/keycloak.svg',
|
||||
url: 'https://keycloak.org'
|
||||
},
|
||||
{
|
||||
title: 'DeepSeek',
|
||||
category: 'AI',
|
||||
route: '/library/deepseek.svg',
|
||||
wordmark: '/library/deepseek_wordmark.svg',
|
||||
url: 'https://deepseek.com/'
|
||||
},
|
||||
{
|
||||
title: 'Shiki',
|
||||
category: 'Library',
|
||||
route: '/library/shiki.svg',
|
||||
url: 'https://shiki.style/'
|
||||
},
|
||||
{
|
||||
title: 'Dropbox',
|
||||
category: ['Hosting', 'Software'],
|
||||
route: '/library/dropbox.svg',
|
||||
wordmark: {
|
||||
light: '/library/dropbox_wordmark.svg',
|
||||
dark: '/library/dropbox_wordmark_dark.svg'
|
||||
},
|
||||
url: 'https://www.dropbox.com/'
|
||||
},
|
||||
{
|
||||
title: 'Open WebUI',
|
||||
category: ['AI', 'Software'],
|
||||
route: '/library/openwebui.svg',
|
||||
url: 'https://openwebui.com/'
|
||||
},
|
||||
{
|
||||
title: 'Base UI',
|
||||
category: 'Library',
|
||||
route: {
|
||||
light: '/library/base-ui.svg',
|
||||
dark: '/library/base-ui-dark.svg'
|
||||
},
|
||||
url: 'https://base-ui.com/'
|
||||
},
|
||||
{
|
||||
title: 'Vercel',
|
||||
category: ['Hosting', 'Vercel'],
|
||||
route: {
|
||||
light: '/library/vercel.svg',
|
||||
dark: '/library/vercel_dark.svg'
|
||||
},
|
||||
wordmark: {
|
||||
light: '/library/vercel_wordmark.svg',
|
||||
dark: '/library/vercel_wordmark_dark.svg'
|
||||
},
|
||||
brandUrl: 'https://vercel.com/geist/brands',
|
||||
url: 'https://vercel.com/'
|
||||
}
|
||||
];
|
||||
|
||||
+70
-56
@@ -1,12 +1,27 @@
|
||||
<script lang="ts">
|
||||
import type { iSVG } from '@/types/svg';
|
||||
import { cn } from '@/utils/cn';
|
||||
import { onMount } from 'svelte';
|
||||
import { queryParam } from 'sveltekit-search-params';
|
||||
|
||||
import Fuse from 'fuse.js';
|
||||
|
||||
// Get all svgs:
|
||||
import { svgsData } from '@/data';
|
||||
const allSvgs = JSON.parse(JSON.stringify(svgsData));
|
||||
|
||||
// Cache sorted arrays
|
||||
const latestSorted = [...allSvgs].sort((a, b) => b.id! - a.id!);
|
||||
const alphabeticallySorted = [...allSvgs].sort((a, b) => a.title.localeCompare(b.title));
|
||||
|
||||
// Fuzzy search setup:
|
||||
const fuse = new Fuse<iSVG>(allSvgs, {
|
||||
keys: ['title'],
|
||||
threshold: 0.35,
|
||||
ignoreLocation: true,
|
||||
isCaseSensitive: false,
|
||||
shouldSort: true
|
||||
});
|
||||
|
||||
// Components:
|
||||
import Search from '@/components/search.svelte';
|
||||
import Container from '@/components/container.svelte';
|
||||
@@ -22,73 +37,66 @@
|
||||
import { buttonStyles } from '@/ui/styles';
|
||||
|
||||
let sorted: boolean = false;
|
||||
let isFirstLoad: boolean = true;
|
||||
let showAll: boolean = false;
|
||||
|
||||
// Search:
|
||||
let searchTerm = $searchParam || '';
|
||||
let searchTerm = '';
|
||||
let filteredSvgs: iSVG[] = [];
|
||||
let displaySvgs: iSVG[] = [];
|
||||
|
||||
// Order by last added:
|
||||
if (searchTerm.length === 0) {
|
||||
filteredSvgs = allSvgs.sort((a: iSVG, b: iSVG) => {
|
||||
return b.id! - a.id!;
|
||||
});
|
||||
}
|
||||
|
||||
const loadSvgs = () => {
|
||||
if (isFirstLoad || showAll) {
|
||||
filteredSvgs = allSvgs;
|
||||
isFirstLoad = false;
|
||||
} else {
|
||||
filteredSvgs = allSvgs.slice(0, 30);
|
||||
}
|
||||
const updateDisplaySvgs = () => {
|
||||
displaySvgs = showAll ? filteredSvgs : filteredSvgs.slice(0, 30);
|
||||
};
|
||||
|
||||
// Search svgs:
|
||||
// Hybrid search strategy:
|
||||
// - Simple string matching for queries < 3 chars
|
||||
// - Fuzzy search for longer queries (handle typos and partial matches)
|
||||
const searchSvgs = () => {
|
||||
$searchParam = searchTerm || null;
|
||||
loadSvgs();
|
||||
filteredSvgs = allSvgs.filter((svg: iSVG) => {
|
||||
let svgTitle = svg.title.toLowerCase();
|
||||
return svgTitle.includes(searchTerm.toLowerCase());
|
||||
});
|
||||
|
||||
if (!searchTerm) {
|
||||
filteredSvgs = sorted ? alphabeticallySorted : latestSorted;
|
||||
updateDisplaySvgs();
|
||||
return;
|
||||
}
|
||||
|
||||
if (searchTerm.length < 3) {
|
||||
filteredSvgs = allSvgs.filter((svg: iSVG) =>
|
||||
svg.title.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
);
|
||||
} else {
|
||||
filteredSvgs = fuse.search(searchTerm).map((result) => result.item);
|
||||
}
|
||||
|
||||
updateDisplaySvgs();
|
||||
};
|
||||
|
||||
// Clear search:
|
||||
const clearSearch = () => {
|
||||
searchTerm = '';
|
||||
searchSvgs();
|
||||
// Use current sort state to determine order
|
||||
filteredSvgs = sorted ? alphabeticallySorted : latestSorted;
|
||||
updateDisplaySvgs();
|
||||
};
|
||||
|
||||
// Sort:
|
||||
const sort = () => {
|
||||
if (sorted) {
|
||||
sortByLatest();
|
||||
} else {
|
||||
sortAlphabetically();
|
||||
}
|
||||
sorted = !sorted;
|
||||
filteredSvgs = sorted ? alphabeticallySorted : latestSorted;
|
||||
updateDisplaySvgs();
|
||||
};
|
||||
|
||||
// Sort alphabetically:
|
||||
const sortAlphabetically = () => {
|
||||
filteredSvgs = allSvgs.sort((a: iSVG, b: iSVG) => {
|
||||
return a.title.localeCompare(b.title);
|
||||
});
|
||||
};
|
||||
|
||||
// Sort by latest:
|
||||
const sortByLatest = () => {
|
||||
filteredSvgs = filteredSvgs.sort((a: iSVG, b: iSVG) => {
|
||||
return b.id! - a.id!;
|
||||
});
|
||||
};
|
||||
|
||||
if ($searchParam) {
|
||||
onMount(() => {
|
||||
if ($searchParam) {
|
||||
searchTerm = $searchParam;
|
||||
}
|
||||
searchSvgs();
|
||||
} else {
|
||||
loadSvgs();
|
||||
});
|
||||
|
||||
$: {
|
||||
if (showAll || filteredSvgs) {
|
||||
updateDisplaySvgs();
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -100,15 +108,15 @@
|
||||
bind:searchTerm
|
||||
on:input={searchSvgs}
|
||||
clearSearch={() => clearSearch()}
|
||||
placeholder={`Search ${filteredSvgs.length} logos...`}
|
||||
placeholder={`Search ${allSvgs.length} logos...`}
|
||||
/>
|
||||
|
||||
<Container>
|
||||
<div class={cn('flex items-center mb-4 justify-end', searchTerm.length > 0 && 'justify-between')}>
|
||||
<div class={cn('mb-4 flex items-center justify-end', searchTerm.length > 0 && 'justify-between')}>
|
||||
{#if searchTerm.length > 0}
|
||||
<button
|
||||
class={cn(
|
||||
'flex items-center justify-center space-x-1 rounded-md py-1.5 text-sm font-medium opacity-80 hover:opacity-100 transition-opacity',
|
||||
'flex items-center justify-center space-x-1 rounded-md py-1.5 text-sm font-medium opacity-80 transition-opacity hover:opacity-100',
|
||||
filteredSvgs.length === 0 && 'hidden'
|
||||
)}
|
||||
on:click={() => clearSearch()}
|
||||
@@ -119,7 +127,7 @@
|
||||
{/if}
|
||||
<button
|
||||
class={cn(
|
||||
'flex items-center justify-center space-x-1 rounded-md py-1.5 text-sm font-medium opacity-80 hover:opacity-100 transition-opacity',
|
||||
'flex items-center justify-center space-x-1 rounded-md py-1.5 text-sm font-medium opacity-80 transition-opacity hover:opacity-100',
|
||||
filteredSvgs.length === 0 && 'hidden'
|
||||
)}
|
||||
on:click={() => sort()}
|
||||
@@ -129,18 +137,24 @@
|
||||
{:else}
|
||||
<ArrowUpDownIcon size={16} strokeWidth={2} class="mr-1" />
|
||||
{/if}
|
||||
<span>{sorted ? 'Sort by latest' : 'Sort alphabetically'}</span>
|
||||
<span>{sorted ? 'Sort by latest' : 'Sort A-Z'}</span>
|
||||
</button>
|
||||
</div>
|
||||
<Grid>
|
||||
{#each filteredSvgs.slice(0, showAll ? undefined : 30) as svg}
|
||||
<SvgCard svgInfo={svg} searchTerm={searchTerm} />
|
||||
{#each displaySvgs as svg}
|
||||
<SvgCard svgInfo={svg} {searchTerm} />
|
||||
{/each}
|
||||
</Grid>
|
||||
{#if filteredSvgs.length > 30 && !showAll}
|
||||
<div class="flex items-center justify-center mt-4">
|
||||
<button class={buttonStyles} on:click={() => (showAll = true)}>
|
||||
<div class="flex items-center space-x-2 relative">
|
||||
<div class="mt-4 flex items-center justify-center">
|
||||
<button
|
||||
class={buttonStyles}
|
||||
on:click={() => {
|
||||
showAll = true;
|
||||
updateDisplaySvgs();
|
||||
}}
|
||||
>
|
||||
<div class="relative flex items-center space-x-2">
|
||||
<ArrowDown size={16} strokeWidth={2} />
|
||||
<span>Load All SVGs</span>
|
||||
<span class="opacity-70">
|
||||
|
||||
@@ -25,7 +25,7 @@ export type tCategory =
|
||||
| 'Vercel'
|
||||
| 'Google'
|
||||
| 'Payment'
|
||||
| 'void(0)'
|
||||
| 'VoidZero'
|
||||
| 'Authentication'
|
||||
| 'IoT'
|
||||
| 'Home Automation'
|
||||
|
||||
Reference in New Issue
Block a user