Skip to content

Commit 3f59e06

Browse files
committed
enable selecting different versions and copying code
1 parent a105975 commit 3f59e06

6 files changed

Lines changed: 306 additions & 113 deletions

File tree

src/app.css

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
--color-green-800: #16592D;
1212
--color-green-900: #0C421C;
1313
--color-green-950: #052B0F;
14+
--color-black-950: #000000AA;
1415

1516
--primaryColor: #3D9561;
1617
--primaryColorDark: #2D804E;

src/components/IconModal.svelte

Lines changed: 56 additions & 87 deletions
Original file line numberDiff line numberDiff line change
@@ -1,100 +1,69 @@
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) {
1+
<script lang="ts">
2+
import { onMount } from "svelte";
3+
import {
4+
selectedIcon,
5+
closeIconModal,
6+
} from "$lib/stores/iconStore";
7+
import type { IconType } from "$lib/types/icon";
8+
import Modal from "./Modal.svelte";
9+
import IconModalHeader from "./IconModalHeader.svelte";
10+
import IconVersions from "../components/IconVersions.svelte";
11+
12+
let selectedVersion = "" as string;
13+
let versions = [] as string[];
14+
15+
interface Icon {
16+
name: string;
17+
versions: {
18+
svg: string[];
19+
font: string[];
20+
};
21+
}
22+
23+
$: if (
24+
$selectedIcon &&
25+
$selectedIcon.versions &&
26+
$selectedIcon.versions.svg
27+
) {
928
versions = $selectedIcon.versions.svg;
10-
selectedVersion = versions.includes('original') ? 'original' : versions[0];
29+
selectedVersion = versions.includes("original") ? "original" : versions[0];
1130
}
12-
13-
function getIconUrl(icon, version) {
31+
32+
function getIconUrl(icon: IconType, version: string) {
1433
return `https://cdn.jsdelivr.net/gh/devicons/devicon/icons/${icon.name}/${icon.name}-${version}.svg`;
1534
}
16-
17-
function handleKeydown(e) {
18-
if (e.key === 'Escape') {
35+
36+
function handleKeydown(e: KeyboardEvent) {
37+
if (e.key === "Escape") {
1938
closeIconModal();
2039
}
2140
}
22-
41+
2342
onMount(() => {
24-
document.addEventListener('keydown', handleKeydown);
43+
document.addEventListener("keydown", handleKeydown);
2544
return () => {
26-
document.removeEventListener('keydown', handleKeydown);
45+
document.removeEventListener("keydown", handleKeydown);
2746
};
2847
});
2948
</script>
3049

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>
50+
<Modal isOpen={!!$selectedIcon} onClose={closeIconModal} title={$selectedIcon?.name}>
51+
<IconModalHeader title={$selectedIcon?.name} onClose={closeIconModal} />
52+
53+
{#if $selectedIcon}
54+
<IconVersions
55+
title="Font versions"
56+
versions={$selectedIcon?.versions?.font || []}
57+
type="font"
58+
selectedIcon={$selectedIcon}
59+
/>
60+
61+
<IconVersions
62+
title="SVG versions"
63+
versions={versions}
64+
type="svg"
65+
selectedIcon={$selectedIcon}
66+
getIconUrl={getIconUrl}
67+
/>
68+
{/if}
69+
</Modal>
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
<script>
2+
export let title;
3+
export let onClose;
4+
</script>
5+
6+
<div class="flex justify-between items-start mb-4">
7+
<h2 id="modal-title" class="text-xl font-bold dark:text-white">
8+
{title}
9+
</h2>
10+
<button
11+
class="text-gray-500 hover:text-gray-700 dark:text-gray-400 dark:hover:text-gray-200"
12+
on:click={onClose}
13+
aria-label="Close modal"
14+
>
15+
<svg
16+
xmlns="http://www.w3.org/2000/svg"
17+
class="h-6 w-6"
18+
fill="none"
19+
viewBox="0 0 24 24"
20+
stroke="currentColor"
21+
>
22+
<path
23+
stroke-linecap="round"
24+
stroke-linejoin="round"
25+
stroke-width="2"
26+
d="M6 18L18 6M6 6l12 12"
27+
/>
28+
</svg>
29+
</button>
30+
</div>

src/components/IconVersions.svelte

Lines changed: 88 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,88 @@
1+
<script>
2+
export let title;
3+
export let versions;
4+
export let type; // 'font' or 'svg'
5+
export let selectedIcon;
6+
export let getIconUrl; // Only needed for SVG type
7+
8+
// Track selected version
9+
let selectedVersion = versions[0] ?? null;
10+
11+
// Handle version selection
12+
function selectVersion(version) {
13+
selectedVersion = version;
14+
}
15+
const copyToClipboard = async (text) => {
16+
if (navigator.clipboard) {
17+
navigator.clipboard.writeText(text);
18+
}
19+
}
20+
21+
function getIconCode() {
22+
if (type === "font") {
23+
return `<i class=devicon-${selectedIcon?.name}-${selectedVersion ?? 'plain'}></i>`
24+
}
25+
return `<img src="https://cdn.jsdelivr.net/gh/devicons/devicon/icons/${selectedIcon?.name}/${selectedIcon?.name}-${selectedVersion}.svg"/>`
26+
}
27+
</script>
28+
29+
<h3 class="mb-2">{title}</h3>
30+
<div class="flex flex-col items-center mb-6">
31+
{#if versions.length > 0}
32+
<div
33+
class="bg-gray-100 dark:bg-gray-700 p-4 rounded-lg mb-4 w-full flex flex-wrap"
34+
>
35+
{#each versions as version}
36+
{#if type === "font"}
37+
<button
38+
class="cursor-pointer transition-all p-4
39+
{selectedVersion === version
40+
? 'ring-2 ring-green-500 rounded-lg'
41+
: 'text-gray-700 hover:text-gray-900 dark:text-gray-300 dark:hover:text-gray-100'}"
42+
on:click={() => selectVersion(version)}
43+
aria-label={`Select ${version} version`}
44+
>
45+
<i
46+
class="text-4xl sm:text-6xl sm:w-16 sm:h-16 devicon-{selectedIcon.name}-{version ??
47+
'plain'} hover:text-green-700 transition-colors"
48+
></i>
49+
</button>
50+
{:else}
51+
<button
52+
class="h-24 p-1 cursor-pointer transition-all
53+
{selectedVersion === version
54+
? 'ring-2 ring-green-500 rounded-lg bg-green-50 dark:bg-green-950/10'
55+
: 'hover:opacity-80'}"
56+
on:click={() => selectVersion(version)}
57+
aria-label={`Select ${version} version`}
58+
>
59+
<img
60+
src={getIconUrl(selectedIcon, version)}
61+
alt={selectedIcon ? `${selectedIcon.name} (${version})` : ""}
62+
class="h-full"
63+
/>
64+
</button>
65+
{/if}
66+
{/each}
67+
</div>
68+
<div
69+
class="code-snippet relative rounded-md w-full p-4 pr-10 mt-4 dark:text-gray-400 bg-gray-200 dark:bg-gray-900"
70+
>
71+
<div class="tooltip absolute right-2 top-4 w-7">
72+
<button
73+
class="material-symbols-outlined copy rounded-full text-xl hover:cursor-pointer hover:hover-text-color"
74+
on:click={() => copyToClipboard(getIconCode())}
75+
>
76+
content_copy
77+
</button>
78+
</div>
79+
<code class="break-all">
80+
{getIconCode()}
81+
</code>
82+
</div>
83+
{:else}
84+
<p class="text-gray-500 dark:text-gray-400">
85+
No {type === "font" ? "font" : "SVG"} versions available
86+
</p>
87+
{/if}
88+
</div>

src/components/Modal.svelte

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
<script>
2+
export let isOpen = false;
3+
export let onClose;
4+
export let title;
5+
</script>
6+
7+
{#if isOpen}
8+
<div
9+
class="fixed inset-0 bg-black-950 bg-opacity-50 flex items-center justify-center z-50 p-4"
10+
on:click|self={onClose}
11+
>
12+
<div
13+
class="bg-white dark:bg-gray-800 p-6 rounded-lg shadow-xl max-w-lg w-full max-h-[90vh] overflow-y-auto"
14+
role="dialog"
15+
aria-modal="true"
16+
aria-labelledby="modal-title"
17+
>
18+
<slot {title} {onClose} />
19+
</div>
20+
</div>
21+
{/if}

0 commit comments

Comments
 (0)