Create favorite store with localstorage

This commit is contained in:
pheralb
2025-08-26 11:03:57 +01:00
parent cf3918376f
commit da19647abf
4 changed files with 203 additions and 100 deletions
+40
View File
@@ -0,0 +1,40 @@
<script lang="ts">
import type { iSVG } from "@/types/svg";
import favoritesStore from "@/stores/favorites.store";
import HeartIcon from "@lucide/svelte/icons/heart";
import { cn } from "@/utils/cn";
interface Props {
svg: iSVG;
}
let { svg }: Props = $props();
let favorites = $derived($favoritesStore);
let isFavorite = $derived(favoritesStore.isFavorite(svg, favorites));
const toggleFavorite = () => {
favoritesStore.toggleFavorite(svg);
};
</script>
<button
class={cn(
"cursor-pointer transition-colors hover:animate-pulse",
"text-neutral-500 hover:text-red-700 dark:text-neutral-400 dark:hover:text-red-400",
isFavorite && "text-red-500",
)}
onclick={toggleFavorite}
title={isFavorite
? `Delete ${svg.title} from favorites`
: `Add ${svg.title} to favorites`}
aria-label={isFavorite
? `Delete ${svg.title} from favorites`
: `Add ${svg.title} to favorites`}
>
<HeartIcon
size={16}
strokeWidth={1.8}
class={cn(isFavorite && "fill-red-500")}
/>
</button>
+12 -6
View File
@@ -19,6 +19,8 @@
// Components:
import CopySvg from "@/components/copySvg.svelte";
import DownloadSvg from "@/components/downloadSvg.svelte";
import Heart from "@lucide/svelte/icons/heart";
import AddToFavorite from "./addToFavorite.svelte";
// Props:
interface Props {
@@ -37,16 +39,20 @@
let maxVisibleCategories = 1;
// Global Styles:
const globalImageStyles = "mb-4 mt-2 h-10 select-none pointer-events-none";
const globalImageStyles = "mb-4 mt-1.5 h-10 select-none pointer-events-none";
</script>
<div
class={cn(
"group flex flex-col items-center justify-center p-4",
"group flex flex-col items-center justify-center px-3.5 py-3",
"rounded-md border border-neutral-200 dark:border-neutral-800",
"transition-colors duration-100 hover:bg-neutral-100/80 dark:hover:bg-neutral-800/20",
)}
>
<!-- Image Options -->
<div class="flex w-full items-center justify-end space-x-0.5">
<AddToFavorite svg={svgInfo} />
</div>
<!-- Image -->
{#if wordmarkSvg == true && svgInfo.wordmark !== undefined}
<img
@@ -99,7 +105,7 @@
{#each svgInfo.category.slice(0, maxVisibleCategories) as c, index}
<a
href={`/directory/${c.toLowerCase()}`}
class={badgeVariants({ variant: "outline" })}
class={badgeVariants({ variant: "outline", class: "font-mono" })}
title={`This icon is part of the ${svgInfo.category} category`}
>
{c}
@@ -112,7 +118,7 @@
onOpenChange={(isOpen) => (moreTagsOptions = isOpen)}
>
<Popover.Trigger
class={badgeVariants({ variant: "outline" })}
class={badgeVariants({ variant: "outline", class: "font-mono" })}
title="More Tags"
>
{#if moreTagsOptions}
@@ -138,7 +144,7 @@
{:else}
<a
href={`/directory/${svgInfo.category.toLowerCase()}`}
class={badgeVariants({ variant: "outline" })}
class={badgeVariants({ variant: "outline", class: "font-mono" })}
>
{svgInfo.category}
</a>
@@ -146,7 +152,7 @@
</div>
</div>
<!-- Actions -->
<div class="flex items-center space-x-1">
<div class="flex items-center space-x-0.5">
{#if wordmarkSvg && svgInfo.wordmark !== undefined}
<CopySvg
size={iconSize}
@@ -1,94 +0,0 @@
<script>
/**
* @typedef {Object} Props
* @property {string} [color]
* @property {number} [size]
* @property {number} [strokeWidth]
* @property {boolean} [isHovered]
* @property {string} [class]
*/
/** @type {Props} */
let {
color = "currentColor",
size = 24,
strokeWidth = 2,
isHovered = false,
class: className = "",
} = $props();
function handleMouseEnter() {
isHovered = true;
setTimeout(() => {
isHovered = false;
}, 1200);
}
</script>
<div
class={className}
aria-label="heart"
role="img"
onmouseenter={handleMouseEnter}
>
<svg
xmlns="http://www.w3.org/2000/svg"
width={size}
height={size}
viewBox="0 0 24 24"
fill="none"
stroke={color}
stroke-width={strokeWidth}
stroke-linecap="round"
stroke-linejoin="round"
class="heart-icon"
class:animate={isHovered}
>
<path
d="M19 14c1.49-1.46 3-3.21 3-5.5A5.5 5.5 0 0 0 16.5 3c-1.76 0-3 .5-4.5 2-1.5-1.5-2.74-2-4.5-2A5.5 5.5 0 0 0 2 8.5c0 2.3 1.5 4.05 3 5.5l7 7Z"
class="heart-path"
/>
</svg>
</div>
<style>
div {
display: inline-block;
}
.heart-icon {
overflow: visible;
}
.heart-path {
transform-origin: center;
transition: transform 0.3s ease;
}
.heart-icon.animate .heart-path {
animation: heartBeat 1.2s ease-in-out;
}
@keyframes heartBeat {
0% {
transform: scale(1);
}
16.67% {
transform: scale(1.1);
}
33.33% {
transform: scale(1);
}
50% {
transform: scale(1.1);
}
66.67% {
transform: scale(1);
}
83.33% {
transform: scale(1.1);
}
100% {
transform: scale(1);
}
}
</style>