Skip to content

Commit d85892d

Browse files
committed
load icons from devicons repo, show them in a grid
1 parent 16c1d8d commit d85892d

11 files changed

Lines changed: 397 additions & 25 deletions

File tree

src/app.html

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@
77
<link rel="preconnect" href="https://fonts.googleapis.com">
88
<link rel="stylesheet" href="https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined&family=Damion&display=swap" />
99
<meta name="viewport" content="width=device-width, initial-scale=1" />
10+
<script>
11+
// On page load, check for dark mode preference
12+
if (localStorage.getItem('darkMode') === 'true') {
13+
document.documentElement.classList.add('dark');
14+
}
15+
</script>
1016
%sveltekit.head%
1117
</head>
1218
<body data-sveltekit-preload-data="hover">
Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<script>
2+
import { isDarkMode, toggleDarkMode } from '$lib/stores/iconStore';
3+
</script>
4+
5+
<button
6+
class="p-2 rounded-full hover:bg-gray-200 dark:hover:bg-gray-700"
7+
on:click={toggleDarkMode}
8+
aria-label={$isDarkMode ? "Switch to light mode" : "Switch to dark mode"}
9+
>
10+
{#if $isDarkMode}
11+
<!-- Sun icon for light mode -->
12+
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-yellow-300" fill="none" viewBox="0 0 24 24" stroke="currentColor">
13+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M12 3v1m0 16v1m9-9h-1M4 12H3m15.364 6.364l-.707-.707M6.343 6.343l-.707-.707m12.728 0l-.707.707M6.343 17.657l-.707.707M16 12a4 4 0 11-8 0 4 4 0 018 0z" />
14+
</svg>
15+
{:else}
16+
<!-- Moon icon for dark mode -->
17+
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6 text-gray-700" fill="none" viewBox="0 0 24 24" stroke="currentColor">
18+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M20.354 15.354A9 9 0 018.646 3.646 9.003 9.003 0 0012 21a9.003 9.003 0 008.354-5.646z" />
19+
</svg>
20+
{/if}
21+
</button>

src/components/Footer.svelte

Lines changed: 2 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
<footer class="footer footer-center p-10 bg-base-300 text-base-content rounded">
2-
<div class="w-auto sm:w-[700px]">
1+
<footer class="footer footer-center p-10 bg-base-300 text-base-content bg-white dark:bg-gray-800 dark:text-gray-400 text-center">
2+
<div class="container mx-auto sm:w-[700px]">
33
<p class="mb-2 italic">
44
All product names, logos, and brandsare property of their respective
55
owners. All company, product and service names used in this website are
@@ -48,12 +48,5 @@
4848
class="underline">Icomoon</a
4949
>
5050
</p>
51-
<p class="mt-2">
52-
Copyright © 2015 by <a
53-
href="https://github.com/konpa"
54-
target="_blank"
55-
class="underline">Konpa</a
56-
>
57-
</p>
5851
</div>
5952
</footer>

src/components/Header.svelte

Lines changed: 19 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
1-
<header class="flex flex-col justify-center items-center py-8 px-4 bg-base-300">
2-
<div id="title" class="mb-2 text-center text-8xl sm:text-9xl">
1+
<header class="flex flex-col justify-center items-center py-8 px-4 bg-base-300 bg-white dark:bg-gray-800 dark:text-white">
2+
<div id="title" class="mb-2 text-center text-8xl sm:text-9xl font-damion text-green-600">
33
Devicon
44
<span class="block text-5xl -mt-6 sm:text-6xl sm:inline sm:mt-auto">
55
v2.15.1
66
</span>
77
</div>
8-
<p class="mt-4 text-xl text-center sm:mt-auto">
8+
<p class="mt-4 text-xl text-center sm:mt-auto text-gray-400">
99
Icons about programming languages, software development, and graphic design tools.
1010
</p>
11-
<div class="flex flex-row my-2">
11+
<div class="flex flex-row my-2 text-gray-400">
1212
<p class="flex flex-row items-center mr-2">
1313
<i class="devicon-github-original text-2xl mr-1"></i>
1414
<a href="https://github.com/devicons/devicon/" target="_blank">
@@ -22,23 +22,23 @@
2222
<a href="https://discord.com/invite/hScy8KWACQ" target="_blank">Discord</a>
2323
</p>
2424
</div>
25-
<div class="stats mt-2 mb-8">
26-
<div class="stat place-items-center">
27-
<div class="stat-title">
25+
<div class="stats mt-2 mb-8 flex rounded-2xl dark:bg-gray-700">
26+
<div class="stat place-items-center p-4 border-r border-gray-600">
27+
<div class="stat-title text-gray-400">
2828
Total Icons
2929
</div>
30-
<div class="stat-value">277</div>
30+
<div class="stat-value text-4xl font-bold text-green-600">277</div>
3131
</div>
32-
<div class="stat place-items-center">
33-
<div class="stat-title">Total Versions</div>
34-
<div class="stat-value">1300</div>
32+
<div class="stat place-items-center p-4">
33+
<div class="stat-title text-gray-400">Total Versions</div>
34+
<div class="stat-value text-4xl font-bold text-green-600">1300</div>
3535
</div>
3636
</div>
37-
<p class="text-center">
37+
<p class="text-center text-gray-400">
3838
For CSS usage, paste the code in the &lt;head&gt; of your HTML
3939
</p>
4040
<app-code-snippet>
41-
<div class="code-snippet relative rounded w-auto p-4 pr-10 mt-4">
41+
<div class="code-snippet relative rounded-md w-auto p-4 pr-10 mt-4 text-gray-400 bg-gray-900 ">
4242
<div class="tooltip absolute right-2 top-4 w-7">
4343
<button class="material-symbols-outlined copy rounded-full text-xl hover:cursor-pointer hover:hover-text-color">
4444
content_copy
@@ -49,4 +49,9 @@
4949
</code>
5050
</div>
5151
</app-code-snippet>
52-
</header>
52+
</header>
53+
<style>
54+
.font-damion {
55+
font-family: 'Damion', Times, serif;
56+
}
57+
</style>

src/components/IconGrid.svelte

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<script>
2+
import { openIconModal } from '$lib/stores/iconStore';
3+
4+
export let icons = [];
5+
6+
function getIconUrl(icon, version) {
7+
return `https://cdn.jsdelivr.net/gh/devicons/devicon/icons/${icon.name}/${icon.name}-${version}.svg`;
8+
}
9+
</script>
10+
11+
<div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-6 gap-4">
12+
{#each icons as icon}
13+
<div
14+
class="bg-white dark:bg-gray-800 p-4 rounded-lg shadow-md hover:shadow-lg transition-shadow cursor-pointer"
15+
on:click={() => openIconModal(icon)}
16+
on:keydown={(e) => e.key === 'Enter' && openIconModal(icon)}
17+
tabindex="0"
18+
role="button"
19+
aria-label={`View ${icon.name} details`}
20+
>
21+
<div class="flex justify-center items-center h-16 mb-2">
22+
<i class="text-4xl sm:text-6xl sm:w-16 sm:h-16 devicon-{ icon.name }-{ icon.versions.font[0] ?? 'plain' }"></i>
23+
</div>
24+
25+
<div class="text-center">
26+
<h3 class="font-medium text-sm truncate dark:text-white">{icon.name}</h3>
27+
</div>
28+
</div>
29+
{/each}
30+
</div>

src/components/IconModal.svelte

Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
<script>
2+
import { onMount } from 'svelte';
3+
import { selectedIcon, isModalOpen, closeIconModal } from '$lib/stores/iconStore';
4+
5+
let selectedVersion = '';
6+
let versions = [];
7+
8+
$: if ($selectedIcon && $selectedIcon.versions && $selectedIcon.versions.svg) {
9+
versions = $selectedIcon.versions.svg;
10+
selectedVersion = versions.includes('original') ? 'original' : versions[0];
11+
}
12+
13+
function getIconUrl(icon, version) {
14+
return `https://cdn.jsdelivr.net/gh/devicons/devicon/icons/${icon.name}/${icon.name}-${version}.svg`;
15+
}
16+
17+
function handleKeydown(e) {
18+
if (e.key === 'Escape') {
19+
closeIconModal();
20+
}
21+
}
22+
23+
onMount(() => {
24+
document.addEventListener('keydown', handleKeydown);
25+
return () => {
26+
document.removeEventListener('keydown', handleKeydown);
27+
};
28+
});
29+
</script>
30+
31+
<div class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-4" on:click|self={closeIconModal}>
32+
<div
33+
class="bg-white dark:bg-gray-800 p-6 rounded-lg shadow-xl max-w-lg w-full max-h-[90vh] overflow-y-auto"
34+
role="dialog"
35+
aria-modal="true"
36+
aria-labelledby="modal-title"
37+
>
38+
{#if $selectedIcon}
39+
<div class="flex justify-between items-start mb-4">
40+
<h2 id="modal-title" class="text-xl font-bold dark:text-white">{$selectedIcon.name}</h2>
41+
<button
42+
class="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
43+
on:click={closeIconModal}
44+
aria-label="Close modal"
45+
>
46+
<svg xmlns="http://www.w3.org/2000/svg" class="h-6 w-6" fill="none" viewBox="0 0 24 24" stroke="currentColor">
47+
<path stroke-linecap="round" stroke-linejoin="round" stroke-width="2" d="M6 18L18 6M6 6l12 12" />
48+
</svg>
49+
</button>
50+
</div>
51+
52+
<div class="flex flex-col items-center mb-6">
53+
{#if versions.length > 0}
54+
<div class="bg-gray-100 dark:bg-gray-700 p-8 rounded-lg mb-4 w-full flex justify-center">
55+
<img
56+
src={getIconUrl($selectedIcon, selectedVersion)}
57+
alt={`${$selectedIcon.name} (${selectedVersion})`}
58+
class="h-24"
59+
/>
60+
</div>
61+
62+
<div class="w-full">
63+
<label for="version-select" class="block text-sm font-medium mb-2 dark:text-gray-200">Select version:</label>
64+
<select
65+
id="version-select"
66+
bind:value={selectedVersion}
67+
class="w-full p-2 border rounded-md dark:bg-gray-700 dark:border-gray-600 dark:text-white"
68+
>
69+
{#each versions as version}
70+
<option value={version}>{version}</option>
71+
{/each}
72+
</select>
73+
</div>
74+
{:else}
75+
<p class="text-gray-500 dark:text-gray-400">No SVG versions available</p>
76+
{/if}
77+
</div>
78+
79+
{#if $selectedIcon.tags && $selectedIcon.tags.length > 0}
80+
<div class="mb-4">
81+
<h3 class="font-medium mb-2 dark:text-white">Tags:</h3>
82+
<div class="flex flex-wrap gap-1">
83+
{#each $selectedIcon.tags as tag}
84+
<span class="px-2 py-1 bg-gray-100 dark:bg-gray-700 text-gray-600 dark:text-gray-300 rounded text-sm">{tag}</span>
85+
{/each}
86+
</div>
87+
</div>
88+
{/if}
89+
90+
<div class="pt-4 border-t border-gray-200 dark:border-gray-700">
91+
<button
92+
class="px-4 py-2 bg-blue-500 text-white rounded hover:bg-blue-600 transition-colors"
93+
on:click={closeIconModal}
94+
>
95+
Close
96+
</button>
97+
</div>
98+
{/if}
99+
</div>
100+
</div>

src/components/SearchFilter.svelte

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,59 @@
1+
<script>
2+
import {
3+
availableTags,
4+
searchTerm,
5+
selectedTags,
6+
setSearchTerm,
7+
setSelectedTags,
8+
clearFilters,
9+
} from "$lib/stores/iconStore";
10+
11+
let selectElement;
12+
13+
function handleTagsChange() {
14+
const selected = Array.from(selectElement.selectedOptions).map(
15+
(option) => option.value
16+
);
17+
setSelectedTags(selected);
18+
}
19+
</script>
20+
21+
<div
22+
class="bg-white dark:bg-gray-800 p-4 rounded-lg shadow-md flex gap-4 justify-between"
23+
>
24+
<div class="mb-4 grow">
25+
<input
26+
type="text"
27+
id="search"
28+
bind:value={$searchTerm}
29+
on:input={(e) => setSearchTerm(e.target.value)}
30+
placeholder="Type to search..."
31+
class="w-full p-2 border rounded-md dark:bg-gray-700 dark:border-gray-600 dark:text-white"
32+
/>
33+
</div>
34+
35+
<div class="grow">
36+
<select
37+
id="tags"
38+
bind:this={selectElement}
39+
multiple
40+
size="1"
41+
on:change={handleTagsChange}
42+
class="w-full p-2.5 border rounded-md dark:bg-gray-700 dark:border-gray-600 dark:text-white"
43+
>
44+
{#each $availableTags as tag}
45+
<option value={tag} selected={$selectedTags.includes(tag)}>
46+
{tag}
47+
</option>
48+
{/each}
49+
</select>
50+
{#if $selectedTags.length > 0}
51+
<button
52+
class="mt-4 px-3 py-1 text-sm bg-gray-200 dark:bg-gray-600 hover:bg-gray-300 dark:hover:bg-gray-500 rounded-md dark:text-white"
53+
on:click={clearFilters}
54+
>
55+
Clear filters
56+
</button>
57+
{/if}
58+
</div>
59+
</div>

0 commit comments

Comments
 (0)