feat: tap-to-preview stickers in gallery with glow effect
All checks were successful
CI / update (push) Successful in 3m33s
All checks were successful
CI / update (push) Successful in 3m33s
Add full rarity glow to gallery stickers matching the reward popup style. Tapping an owned sticker opens a large preview card. Allow calendar stickers to overdraw their cell on hover.
This commit is contained in:
@@ -1,6 +1,6 @@
|
|||||||
{
|
{
|
||||||
"name": "homepage",
|
"name": "homepage",
|
||||||
"version": "1.5.3",
|
"version": "1.6.0",
|
||||||
"private": true,
|
"private": true,
|
||||||
"type": "module",
|
"type": "module",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
|
|||||||
@@ -155,7 +155,6 @@
|
|||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
border: 1px solid transparent;
|
border: 1px solid transparent;
|
||||||
transition: background 120ms;
|
transition: background 120ms;
|
||||||
overflow: hidden;
|
|
||||||
}
|
}
|
||||||
.cal-day.outside {
|
.cal-day.outside {
|
||||||
opacity: 0.25;
|
opacity: 0.25;
|
||||||
|
|||||||
@@ -3,7 +3,7 @@
|
|||||||
import { elasticOut } from 'svelte/easing';
|
import { elasticOut } from 'svelte/easing';
|
||||||
import { getRarityColor } from '$lib/utils/stickers';
|
import { getRarityColor } from '$lib/utils/stickers';
|
||||||
|
|
||||||
let { sticker, onclose } = $props();
|
let { sticker, onclose, title = 'Sticker erhalten!', buttonText = 'Toll!', bounce = true } = $props();
|
||||||
|
|
||||||
const rarityLabels = /** @type {Record<string, string>} */ ({
|
const rarityLabels = /** @type {Record<string, string>} */ ({
|
||||||
common: 'Gewöhnlich',
|
common: 'Gewöhnlich',
|
||||||
@@ -17,19 +17,21 @@
|
|||||||
<div class="popup-backdrop" transition:fade={{ duration: 200 }} onclick={onclose} onkeydown={e => e.key === 'Escape' && onclose?.()}>
|
<div class="popup-backdrop" transition:fade={{ duration: 200 }} onclick={onclose} onkeydown={e => e.key === 'Escape' && onclose?.()}>
|
||||||
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||||
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||||
<div class="popup-card" transition:scale={{ start: 0.5, duration: 500, easing: elasticOut }} onclick={e => e.stopPropagation()}>
|
<div class="popup-card" transition:scale={bounce ? { start: 0.5, duration: 500, easing: elasticOut } : { start: 0.85, duration: 200 }} onclick={e => e.stopPropagation()}>
|
||||||
<div class="sticker-display" style="--rarity-color: {getRarityColor(sticker.rarity)}">
|
<div class="sticker-display" style="--rarity-color: {getRarityColor(sticker.rarity)}">
|
||||||
<img class="sticker-img" src="/stickers/{sticker.image}" alt={sticker.name} />
|
<img class="sticker-img" src="/stickers/{sticker.image}" alt={sticker.name} />
|
||||||
</div>
|
</div>
|
||||||
<div class="popup-text">
|
<div class="popup-text">
|
||||||
<h3>Sticker erhalten!</h3>
|
<h3>{title}</h3>
|
||||||
|
{#if title !== sticker.name}
|
||||||
<p class="sticker-name">{sticker.name}</p>
|
<p class="sticker-name">{sticker.name}</p>
|
||||||
|
{/if}
|
||||||
<p class="sticker-desc">{sticker.description}</p>
|
<p class="sticker-desc">{sticker.description}</p>
|
||||||
<span class="rarity-badge" style="color: {getRarityColor(sticker.rarity)}">
|
<span class="rarity-badge" style="color: {getRarityColor(sticker.rarity)}">
|
||||||
{rarityLabels[sticker.rarity] || sticker.rarity}
|
{rarityLabels[sticker.rarity] || sticker.rarity}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
<button class="btn-close" onclick={onclose}>Toll!</button>
|
<button class="btn-close" onclick={onclose}>{buttonText}</button>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
@@ -6,9 +6,13 @@
|
|||||||
import { flip } from 'svelte/animate';
|
import { flip } from 'svelte/animate';
|
||||||
import { Trash2 } from '@lucide/svelte';
|
import { Trash2 } from '@lucide/svelte';
|
||||||
import StickerCalendar from '$lib/components/tasks/StickerCalendar.svelte';
|
import StickerCalendar from '$lib/components/tasks/StickerCalendar.svelte';
|
||||||
|
import StickerPopup from '$lib/components/tasks/StickerPopup.svelte';
|
||||||
|
|
||||||
let { data } = $props();
|
let { data } = $props();
|
||||||
|
|
||||||
|
/** @type {import('$lib/utils/stickers').Sticker | null} */
|
||||||
|
let selectedSticker = $state(null);
|
||||||
|
|
||||||
let stats = $state(data.stats || { userStats: [], userStickers: [], recentCompletions: [] });
|
let stats = $state(data.stats || { userStats: [], userStickers: [], recentCompletions: [] });
|
||||||
let currentUser = $derived(data.session?.user?.nickname || '');
|
let currentUser = $derived(data.session?.user?.nickname || '');
|
||||||
|
|
||||||
@@ -99,12 +103,15 @@
|
|||||||
{#each sortedStickers as sticker (sticker.id)}
|
{#each sortedStickers as sticker (sticker.id)}
|
||||||
{@const count = displayedStickers.get(sticker.id) || 0}
|
{@const count = displayedStickers.get(sticker.id) || 0}
|
||||||
{@const owned = count > 0}
|
{@const owned = count > 0}
|
||||||
|
<!-- svelte-ignore a11y_no_static_element_interactions -->
|
||||||
|
<!-- svelte-ignore a11y_click_events_have_key_events -->
|
||||||
<div
|
<div
|
||||||
class="sticker-card"
|
class="sticker-card"
|
||||||
class:owned
|
class:owned
|
||||||
class:locked={!owned}
|
class:locked={!owned}
|
||||||
animate:flip={{ duration: 300 }}
|
animate:flip={{ duration: 300 }}
|
||||||
style="--rarity-color: {getRarityColor(sticker.rarity)}"
|
style="--rarity-color: {getRarityColor(sticker.rarity)}"
|
||||||
|
onclick={() => owned && (selectedSticker = sticker)}
|
||||||
>
|
>
|
||||||
<div class="sticker-visual">
|
<div class="sticker-visual">
|
||||||
{#if owned}
|
{#if owned}
|
||||||
@@ -156,6 +163,10 @@
|
|||||||
</section>
|
</section>
|
||||||
{/if}
|
{/if}
|
||||||
|
|
||||||
|
{#if selectedSticker}
|
||||||
|
<StickerPopup sticker={selectedSticker} title={selectedSticker.name} buttonText="Schließen" bounce={false} onclose={() => selectedSticker = null} />
|
||||||
|
{/if}
|
||||||
|
|
||||||
<div class="danger-zone">
|
<div class="danger-zone">
|
||||||
<button class="btn-clear" onclick={clearHistory}>
|
<button class="btn-clear" onclick={clearHistory}>
|
||||||
<Trash2 size={14} />
|
<Trash2 size={14} />
|
||||||
@@ -229,6 +240,7 @@
|
|||||||
.sticker-card.owned {
|
.sticker-card.owned {
|
||||||
border-color: var(--rarity-color);
|
border-color: var(--rarity-color);
|
||||||
border-width: 1.5px;
|
border-width: 1.5px;
|
||||||
|
cursor: pointer;
|
||||||
}
|
}
|
||||||
.sticker-card.owned:hover {
|
.sticker-card.owned:hover {
|
||||||
transform: translateY(-2px);
|
transform: translateY(-2px);
|
||||||
@@ -249,13 +261,15 @@
|
|||||||
margin-bottom: 0.4rem;
|
margin-bottom: 0.4rem;
|
||||||
}
|
}
|
||||||
.owned .sticker-visual {
|
.owned .sticker-visual {
|
||||||
background: radial-gradient(circle, color-mix(in srgb, var(--rarity-color) 20%, transparent) 0%, transparent 70%);
|
background: radial-gradient(circle, var(--rarity-color) 0%, transparent 70%);
|
||||||
border-radius: 50%;
|
border-radius: 50%;
|
||||||
|
opacity: 0.95;
|
||||||
}
|
}
|
||||||
.sticker-img {
|
.sticker-img {
|
||||||
width: 52px;
|
width: 52px;
|
||||||
height: 52px;
|
height: 52px;
|
||||||
object-fit: contain;
|
object-fit: contain;
|
||||||
|
filter: drop-shadow(0 2px 4px rgba(0,0,0,0.15));
|
||||||
}
|
}
|
||||||
.sticker-unknown {
|
.sticker-unknown {
|
||||||
font-size: 1.6rem;
|
font-size: 1.6rem;
|
||||||
|
|||||||
Reference in New Issue
Block a user