zahlzerlegung/src/lib/components/home/HighscoreColumn.svelte

105 lines
3.0 KiB
Svelte

<script lang="ts">
import type { Target } from '../../game/tasks';
import type { Stage } from '../../game/stages';
import { progress } from '../../stores/progress';
import Crown from '../svg/Crown.svelte';
import Balloon from '../svg/Balloon.svelte';
import Cloud from '../svg/Cloud.svelte';
import Moon from '../svg/Moon.svelte';
import Star from '../svg/Star.svelte';
import Rainbow from '../svg/Rainbow.svelte';
type Props = { target: Target; highlightDate?: string | null };
let { target, highlightDate = null }: Props = $props();
const top5 = $derived($progress.perTarget[target]?.top5 ?? []);
// Slots immer 5 lang darstellen — leere Plätze bleiben grau.
const slots = $derived(Array.from({ length: 5 }, (_, i) => top5[i] ?? null));
// Erreichte Stufe → passendes Symbol. Stufe 0 (Boden) hat kein Symbol (siehe Markup).
// Größen pro Symbol leicht angeglichen, da die viewBoxes unterschiedlich proportioniert sind.
const SYMBOL: Record<number, { c: typeof Balloon; size: number }> = {
1: { c: Balloon, size: 38 },
2: { c: Cloud, size: 52 },
3: { c: Moon, size: 40 },
4: { c: Star, size: 36 },
5: { c: Rainbow, size: 56 },
};
</script>
<div class="column" aria-label="Beste Flüge">
{#each slots as slot, i (i)}
<!-- Bester Versuch (i===0) wird nur durch die Krone markiert, nicht zusätzlich hervorgehoben. -->
<div
class="slot"
class:empty={!slot}
class:current={slot && i !== 0 && highlightDate && slot.date === highlightDate}
>
{#if slot}
{#if i === 0}
<span class="crown"><Crown size={28} /></span>
{/if}
<span class="symbol" aria-label={`Stufe ${slot.stage}`}>
{#if slot.stage === 0}
<span class="ground" aria-hidden="true"></span>
{:else}
{@const Symbol = SYMBOL[slot.stage as Stage].c}
<Symbol size={SYMBOL[slot.stage as Stage].size} />
{/if}
</span>
{:else}
<div class="placeholder"></div>
{/if}
</div>
{/each}
</div>
<style>
.column {
display: grid;
grid-template-rows: repeat(5, 1fr);
gap: 10px;
height: 100%;
}
.slot {
position: relative;
background: rgba(255, 255, 255, 0.16);
border-radius: 14px;
display: flex;
align-items: center;
justify-content: center;
padding: 4px 8px;
min-height: 56px;
}
.slot.empty {
background: rgba(255, 255, 255, 0.06);
}
.slot.current {
background: rgba(255, 229, 102, 0.3);
box-shadow: 0 0 0 3px var(--c-rocket-window);
}
.crown {
position: absolute;
top: -10px;
left: 50%;
transform: translateX(-50%);
}
.symbol {
display: flex;
align-items: center;
justify-content: center;
}
.ground {
width: 40px;
height: 18px;
border-radius: 40px 40px 0 0;
background: linear-gradient(to top, #4ea34e, var(--c-ground));
}
.placeholder {
width: 32px;
height: 32px;
border-radius: 50%;
border: 2px dashed rgba(255, 255, 255, 0.3);
}
</style>