137 lines
4.2 KiB
Svelte
137 lines
4.2 KiB
Svelte
<script lang="ts">
|
|
import { onMount, onDestroy } from 'svelte';
|
|
import type { PageProps } from './$types';
|
|
let { params, data }: PageProps = $props();
|
|
|
|
function ordinal(n: number) {
|
|
const s = ['th', 'st', 'nd', 'rd'];
|
|
const v = n % 100;
|
|
return n + (s[(v - 20) % 10] ?? s[v] ?? s[0]);
|
|
}
|
|
|
|
function marquee(node: HTMLElement) {
|
|
function measure() {
|
|
const inner = node.querySelector<HTMLElement>('.marquee-inner');
|
|
if (!inner) return;
|
|
const overflow = inner.scrollWidth - node.clientWidth;
|
|
if (overflow > 2) {
|
|
node.style.setProperty('--scroll-dist', `-${overflow + 6}px`);
|
|
inner.classList.add('scrolling');
|
|
} else {
|
|
inner.classList.remove('scrolling');
|
|
}
|
|
}
|
|
measure();
|
|
const ro = new ResizeObserver(measure);
|
|
ro.observe(node);
|
|
return { destroy: () => ro.disconnect() };
|
|
}
|
|
|
|
let eventId = params.eventId;
|
|
|
|
let eventEndpoint: EventSource;
|
|
|
|
let eventData = $state<any>(data.event);
|
|
|
|
onMount(() => {
|
|
eventEndpoint = new EventSource('/api/registeredEvents');
|
|
eventEndpoint.onmessage = (e) => {
|
|
const allEvents = JSON.parse(e.data);
|
|
const updated = allEvents.find((ev: any) => ev.id == eventId);
|
|
if (updated) {
|
|
if (updated.registeredPlayers) {
|
|
updated.registeredPlayers.forEach((bracket: any) => {
|
|
bracket.items.sort((a: any, b: any) => {
|
|
if (a.placement === 0 && b.placement === 0) return 0;
|
|
if (a.placement === 0) return 1;
|
|
if (b.placement === 0) return -1;
|
|
return a.placement - b.placement;
|
|
});
|
|
});
|
|
}
|
|
eventData = updated;
|
|
}
|
|
};
|
|
});
|
|
|
|
onDestroy(() => {
|
|
eventEndpoint?.close();
|
|
});
|
|
</script>
|
|
|
|
{#if eventData}
|
|
<div class="page-container">
|
|
<div class="event-card" class:ongoing-event={eventData.state == 1} class:completed-event={eventData.state == 2}
|
|
style={eventData.state == 2 ? `--event-color: ${eventData.winner?.color ?? 'currentColor'}` : ''}>
|
|
<div class="event-header">
|
|
<span class="event-name goldman">{eventData.name}</span>
|
|
<span class="event-division">{eventData.division}</span>
|
|
{#if eventData.state == 1}
|
|
<span class="event-status goldman">ONGOING</span>
|
|
{:else if eventData.state == 2}
|
|
<span class="event-status goldman">FINISHED</span>
|
|
{/if}
|
|
</div>
|
|
|
|
<div class="brackets px-[5vw]">
|
|
{#each eventData.registeredPlayers as bracket, bi}
|
|
{#if bi > 0}
|
|
<div class="bracket-sep" aria-hidden="true"></div>
|
|
{/if}
|
|
<div class="bracket-row">
|
|
<div class="brackets-name flex items-center">
|
|
<span class="brackets-name-text align-text-middle">{bracket.name}</span>
|
|
<div class="bracket-vertical-sep"></div>
|
|
</div>
|
|
{#each bracket.items as player}
|
|
<div class="player-box" style="--c:{player.teamColor}">
|
|
<div class="player-ghost" aria-hidden="true">
|
|
<svg viewBox="0 0.1 100 0.6" preserveAspectRatio="none" class="ghost-svg">
|
|
<text
|
|
x="0"
|
|
y="0.7"
|
|
font-size="1"
|
|
dominant-baseline="auto"
|
|
textLength="100"
|
|
lengthAdjust="spacingAndGlyphs"
|
|
font-family="'Black Ops One',system-ui">{player.firstName}</text
|
|
>
|
|
</svg>
|
|
</div>
|
|
<div class="player-name-wrap" use:marquee>
|
|
<a href="/stats/player/{player.id}" class="marquee-inner">
|
|
{player.firstName}
|
|
{player.lastName}
|
|
</a>
|
|
</div>
|
|
{#if player.placement !== 0}
|
|
<div class="player-placement goldman">{ordinal(player.placement)}</div>
|
|
<div class="flex flex-col justify-center">
|
|
{#each player.playerScores as score}
|
|
<div class="mx-1 my-0.5 rounded border px-1 text-xs" style="border-color:{player.teamColor}">
|
|
Run {score.resultIndex + 1}: {score.result}{eventData.resultPresets[0].unit}
|
|
</div>
|
|
{/each}
|
|
</div>
|
|
{:else}
|
|
<div class="player-placement-gap"></div>
|
|
{/if}
|
|
</div>
|
|
{/each}
|
|
</div>
|
|
{/each}
|
|
</div>
|
|
</div>
|
|
|
|
{#if data.user}
|
|
<div class="mt-10 flex w-full justify-center">
|
|
<a
|
|
class="flex justify-center rounded border-2 border-solid border-white bg-ctp-surface2 p-4"
|
|
href="/event/scoring/{eventId}">Score This Event</a
|
|
>
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
{:else}
|
|
<div class="page-container"><div>loading</div></div>
|
|
{/if} |