From 15f52f55955d56989b7b6039c04c0df427ff1bde Mon Sep 17 00:00:00 2001
From: Fuwn <contact@fuwn.me>
Date: Thu, 18 Apr 2024 18:38:14 -0700
Subject: feat(badges): badge preview pane

---
 src/lib/BadgeWall/BadgePreview.svelte | 129 ++++++++++++++++++++++++++++++++++
 1 file changed, 129 insertions(+)
 create mode 100644 src/lib/BadgeWall/BadgePreview.svelte

(limited to 'src/lib/BadgeWall/BadgePreview.svelte')

diff --git a/src/lib/BadgeWall/BadgePreview.svelte b/src/lib/BadgeWall/BadgePreview.svelte
new file mode 100644
index 00000000..0cf7bb9c
--- /dev/null
+++ b/src/lib/BadgeWall/BadgePreview.svelte
@@ -0,0 +1,129 @@
+<script lang="ts">
+	import type { Badge } from '$lib/Database/userBadges';
+	import cdn from '$lib/Utility/cdn';
+	import { databaseTimeToDate } from '$lib/Utility/time';
+	import locale from '$stores/locale';
+	import { cubicOut } from 'svelte/easing';
+	import { tweened } from 'svelte/motion';
+
+	export let badge: Badge | undefined;
+
+	let badgeReference: HTMLImageElement;
+	const mouse = tweened(
+		{ x: 0, y: 0 },
+		{
+			duration: 200 * 1.75,
+			easing: cubicOut
+		}
+	);
+
+	const handleMouseMove = (event: MouseEvent) => {
+		const boundingRectangle = badgeReference.getBoundingClientRect();
+		const factor = 1.25;
+
+		if ($mouse.x === 0 && $mouse.y === 0) $mouse = { x: event.clientX, y: event.clientY };
+
+		$mouse.x +=
+			(-(event.clientX - boundingRectangle.left - boundingRectangle.width / 2) - $mouse.x) * factor;
+		$mouse.y +=
+			(-(event.clientY - boundingRectangle.top - boundingRectangle.height / 2) - $mouse.y) * factor;
+	};
+
+	const handleMouseLeave = () => {
+		$mouse = { x: 0, y: 0 };
+	};
+</script>
+
+{#if badge}
+	<div class="badge-preview">
+		{#if badge.image}
+			<div
+				on:mousemove={handleMouseMove}
+				on:mouseleave={handleMouseLeave}
+				role="img"
+				class="badge-container"
+			>
+				<img
+					src={cdn(badge.image)}
+					bind:this={badgeReference}
+					style="transform: perspective(1000px) rotateX({$mouse.y / 10}deg) rotateY({-$mouse.x /
+						10}deg);"
+					alt={badge.description}
+				/>
+			</div>
+
+			<p />
+		{/if}
+
+		{#if badge.time}
+			{$locale().dateFormatter(databaseTimeToDate(badge.time))}
+
+			{#if (badge.designer || badge.source || badge.post) && !badge.description}
+				<p />
+			{:else if badge.description}
+				<br />
+			{/if}
+		{/if}
+
+		{#if badge.description}
+			{badge.description}
+
+			{#if badge.designer || badge.source || badge.post}
+				<p />
+			{/if}
+		{/if}
+
+		{#if badge.designer}
+			<b>Designer:</b>
+
+			{#if badge.designer.startsWith('http')}
+				<a href={badge.designer} target="_blank">
+					{badge.designer}
+				</a>
+			{:else if badge.designer.startsWith('@')}
+				<a href="https://anilist.co/user/{badge.designer.replace('@', '')}" target="_blank">
+					{badge.designer}
+				</a>
+			{:else}
+				{badge.designer}
+			{/if}
+
+			<br />
+		{/if}
+
+		{#if badge.post}
+			<b>{badge.post.includes('forum') ? 'Forum' : 'Activity'}:</b>
+
+			<a href={badge.post} target="_blank">
+				{badge.post}
+			</a>
+
+			<br />
+		{/if}
+
+		{#if badge.source}
+			<b>Source:</b>
+
+			{#if badge.source.startsWith('http')}
+				<a href={badge.source} target="_blank">
+					{badge.source}
+				</a>
+			{:else}
+				{badge.source}
+			{/if}
+		{/if}
+	</div>
+{/if}
+
+<style>
+	.badge-preview img {
+		border-radius: 8px;
+		max-width: 25vh;
+	}
+
+	.badge-container {
+		display: flex;
+		justify-content: center;
+		align-items: center;
+	}
+</style>
-- 
cgit v1.2.3

