made the scrolling work

This commit is contained in:
2026-05-30 16:14:53 +01:00
parent 83d9b78d2b
commit 685fba8c71
2 changed files with 73 additions and 363 deletions

View File

@@ -18,14 +18,21 @@
let eventRefs = $state<Record<number, HTMLElement>>({});
onMount(() => {
// Get the teams endpoint
scoreEndpoint = new EventSource('/api/teams');
// When the endpoint sends something
scoreEndpoint.onmessage = (e) => {
// Parse the json
const teamsData = JSON.parse(e.data);
// If its teams info, then update the teams thing
if (teamsData['teams']) teams = teamsData['teams'];
};
// Basically same for events
eventEndpoint = new EventSource('/api/registeredEvents');
eventEndpoint.onmessage = (e) => {
eventTable = JSON.parse(e.data);
console.log(eventTable);
};
});
@@ -44,20 +51,18 @@
}
// When focusEventId changes, scroll to and highlight that event card
// TODO make this scrolling shit work idk
$effect(() => {
if (focusEventId == null) return;
tick().then(() => {
// Get focused element
const el = eventRefs[focusEventId!];
const container = el?.closest('.events-scroll') as HTMLElement;
if (el && container) {
const elMid = el.offsetTop + el.offsetHeight / 2;
const targetScroll = elMid - container.clientHeight / 2 - 60;
container.scrollTo({ top: targetScroll, behavior: 'smooth' });
el.classList.remove('highlight-pulse');
void el.offsetWidth;
el.classList.add('highlight-pulse');
}
// Scroll it to the top of the box
el.scrollIntoView({ alignToTop: true, behavior: 'instant', container: 'nearest' });
// Wait for that to finish
tick().then(() => {
// Scroll the window back to the top
window.scrollTo(0, 0);
});
});
});
// Svelte action: measures text overflow and drives CSS marquee
@@ -159,10 +164,22 @@
<section class="events-scroll">
<div class="events">
{#each eventTable as event (event.id)}
<div class="event-card" bind:this={eventRefs[event.id]}>
<div
class="event-card"
class:ongoing-event={event.state == 1}
bind:this={eventRefs[event.id]}
>
<div class="event-header">
<a href="/event/{event.id}" class="event-name goldman">{event.name}</a>
<span class="event-division">{event.division}</span>
{#if event.state == 1}
<span class="event-status goldman">ONGOING</span>
{:else if event.state == 2}
<span
class="event-winner event-status goldman"
style="--winner-color:{event.winner.color}">Won By {event.winner.name}</span
>
{/if}
</div>
<div class="brackets">
@@ -211,355 +228,3 @@
</div>
</section>
</div>
<!-- <style> -->
<!-- @import url('https://fonts.googleapis.com/css2?family=Black+Ops+One&display=swap'); -->
<!---->
<!-- :global(html), -->
<!-- :global(body) { -->
<!-- min-height: 100vh; -->
<!-- } -->
<!---->
<!-- :global(body > div), /* SvelteKit's root wrapper */ -->
<!-- :global(#svelte) { -->
<!-- min-height: 100vh; -->
<!-- display: flex; -->
<!-- flex-direction: column; -->
<!-- } -->
<!---->
<!-- .page { -->
<!-- display: flex; -->
<!-- flex-direction: column; -->
<!-- max-height: 150vh; -->
<!-- max-width: 1000px; -->
<!-- margin: 0 auto; -->
<!-- width: 100%; -->
<!-- } -->
<!-- .goldman { -->
<!-- font-family: 'Black Ops One', system-ui; -->
<!-- font-weight: 400; -->
<!-- } -->
<!---->
<!-- /* ── Leaderboard ── */ -->
<!-- .leaderboard { -->
<!-- display: flex; -->
<!-- flex-direction: column; -->
<!-- gap: 10px; -->
<!-- padding: 14px 14px 6px; -->
<!-- } -->
<!---->
<!-- .score-box { -->
<!-- position: relative; -->
<!-- overflow: hidden; -->
<!-- border-radius: 14px; -->
<!-- border: 2px solid var(--c); -->
<!-- color: var(--c); -->
<!-- background: color-mix(in srgb, var(--c) 10%, transparent); -->
<!-- /* Grid stacking: ghost + fg share the same cell */ -->
<!-- display: grid; -->
<!-- grid-template-columns: 1fr; -->
<!-- grid-template-rows: 1fr; -->
<!-- text-decoration: none; -->
<!-- transition: filter 0.15s ease; -->
<!-- } -->
<!-- .score-box:hover { -->
<!-- filter: brightness(1.15); -->
<!-- } -->
<!-- .score-box > * { -->
<!-- grid-column: 1; -->
<!-- grid-row: 1; -->
<!-- } -->
<!---->
<!-- .winner { -->
<!-- aspect-ratio: 2 / 1; -->
<!-- border-width: 3px; -->
<!-- min-height: 80px; -->
<!-- } -->
<!-- .runner { -->
<!-- aspect-ratio: 4 / 1; -->
<!-- min-height: 56px; -->
<!-- } -->
<!---->
<!-- /* Ghost SVG: fill the cell edge-to-edge, text flush to bottom */ -->
<!-- .score-ghost { -->
<!-- width: 100%; -->
<!-- height: 100%; -->
<!-- opacity: 0.18; -->
<!-- fill: currentColor; -->
<!-- } -->
<!-- .ghost-svg { -->
<!-- width: 100%; -->
<!-- height: 100%; -->
<!-- display: block; -->
<!-- } -->
<!---->
<!-- /* Foreground */ -->
<!-- .score-fg { -->
<!-- position: relative; -->
<!-- z-index: 1; -->
<!-- display: flex; -->
<!-- align-items: center; -->
<!-- justify-content: space-between; -->
<!-- padding: 0 14px; -->
<!-- gap: 8px; -->
<!-- } -->
<!-- .score-meta { -->
<!-- display: flex; -->
<!-- flex-direction: column; -->
<!-- min-width: 0; -->
<!-- flex: 1; -->
<!-- } -->
<!-- .score-rank { -->
<!-- font-size: 10px; -->
<!-- letter-spacing: 1.5px; -->
<!-- text-transform: uppercase; -->
<!-- opacity: 0.5; -->
<!-- line-height: 1; -->
<!-- margin-bottom: 3px; -->
<!-- } -->
<!-- .score-name { -->
<!-- font-size: clamp(11px, 3vw, 20px); -->
<!-- white-space: nowrap; -->
<!-- overflow: hidden; -->
<!-- text-overflow: ellipsis; -->
<!-- } -->
<!-- .score-pts { -->
<!-- font-size: clamp(20px, 5.5vw, 40px); -->
<!-- letter-spacing: 2px; -->
<!-- flex-shrink: 0; -->
<!-- } -->
<!-- .winner .score-name { -->
<!-- font-size: clamp(16px, 5vw, 32px); -->
<!-- } -->
<!-- .winner .score-pts { -->
<!-- font-size: clamp(26px, 7.5vw, 52px); -->
<!-- } -->
<!---->
<!-- /* -->
<!-- * Runners-up grid: -->
<!-- * - 1 column on small screens (<480px) -->
<!-- * - 2 columns on medium (480699px) -->
<!-- * - 4 columns on large (≥700px), max 5 -->
<!-- * --runner-count drives the actual column count on large screens -->
<!-- * so fewer than 5 teams still fill the whole row. -->
<!-- */ -->
<!-- .runners-grid { -->
<!-- display: grid; -->
<!-- gap: 10px; -->
<!-- grid-template-columns: 1fr; -->
<!-- } -->
<!-- @media (min-width: 480px) { -->
<!-- .runners-grid { -->
<!-- grid-template-columns: repeat(2, 1fr); -->
<!-- } -->
<!---->
<!-- .winner { -->
<!-- aspect-ratio: 2 / 1; -->
<!-- } -->
<!---->
<!-- .runner { -->
<!-- aspect-ratio: 2 / 1; -->
<!-- } -->
<!-- } -->
<!-- @media (min-width: 700px) { -->
<!-- .runners-grid { -->
<!-- /* clamp actual count to 4 on large, but use --runner-count -->
<!-- so e.g. 2 teams still fill 2 equal columns not 2 of 4 */ -->
<!-- grid-template-columns: repeat(min(var(--runner-count), 4), 1fr); -->
<!-- } -->
<!-- .winner { -->
<!-- aspect-ratio: 3 / 1; -->
<!-- } -->
<!-- .runner { -->
<!-- aspect-ratio: 3 / 2; -->
<!-- } -->
<!-- } -->
<!---->
<!-- /* ── Section label ── */ -->
<!-- .section-label { -->
<!-- font-size: 10px; -->
<!-- letter-spacing: 2.5px; -->
<!-- text-transform: uppercase; -->
<!-- opacity: 0.4; -->
<!-- padding: 12px 16px 4px; -->
<!-- margin: 0; -->
<!-- } -->
<!---->
<!-- /* ── Events scrollable container ── */ -->
<!-- .events-scroll { -->
<!-- flex: 1; -->
<!-- overflow-y: auto; -->
<!-- /* max-height: 900px; */ -->
<!-- min-height: 0; -->
<!-- padding: 0 14px 24px; -->
<!-- /* Thin custom scrollbar */ -->
<!-- scrollbar-width: thin; -->
<!-- scrollbar-color: color-mix(in srgb, currentColor 30%, transparent) transparent; -->
<!-- } -->
<!-- .events { -->
<!-- display: flex; -->
<!-- flex-direction: column; -->
<!-- gap: 10px; -->
<!-- } -->
<!---->
<!-- /* ── Event card ── */ -->
<!-- .event-card { -->
<!-- border-radius: 12px; -->
<!-- border: 1px solid color-mix(in srgb, currentColor 18%, transparent); -->
<!-- overflow: hidden; -->
<!-- transition: box-shadow 0.3s ease; -->
<!-- } -->
<!-- /* Focus highlight pulse — added/removed by $effect */ -->
<!-- .event-card.highlight-pulse { -->
<!-- animation: card-pulse 1.2s ease-out forwards; -->
<!-- } -->
<!-- @keyframes card-pulse { -->
<!-- 0% { -->
<!-- box-shadow: 0 0 0 3px currentColor; -->
<!-- } -->
<!-- 100% { -->
<!-- box-shadow: 0 0 0 0px currentColor; -->
<!-- } -->
<!-- } -->
<!---->
<!-- .event-header { -->
<!-- display: flex; -->
<!-- align-items: baseline; -->
<!-- gap: 10px; -->
<!-- padding: 9px 13px 7px; -->
<!-- border-bottom: 1px solid color-mix(in srgb, currentColor 10%, transparent); -->
<!-- flex-wrap: wrap; -->
<!-- } -->
<!-- .event-name { -->
<!-- font-size: 15px; -->
<!-- text-decoration: none; -->
<!-- color: inherit; -->
<!-- } -->
<!-- .event-name:hover { -->
<!-- text-decoration: underline; -->
<!-- } -->
<!-- .event-division { -->
<!-- font-size: 10px; -->
<!-- letter-spacing: 1.2px; -->
<!-- text-transform: uppercase; -->
<!-- opacity: 0.4; -->
<!-- } -->
<!---->
<!-- /* ── Brackets ── */ -->
<!-- .brackets { -->
<!-- display: flex; -->
<!-- flex-direction: column; -->
<!-- } -->
<!-- .brackets-name { -->
<!-- font-size: 20px; -->
<!-- letter-spacing: 1.2px; -->
<!-- text-transform: uppercase; -->
<!-- opacity: 0.4; -->
<!-- margin-left: 1px; -->
<!-- } -->
<!-- .brackets-name-text { -->
<!-- display: table-cell; -->
<!-- vertical-align: middle; -->
<!-- } -->
<!-- .bracket-vertical-sep { -->
<!-- width: 1px; -->
<!-- height: 100%; -->
<!-- background: var(--ctp-mocha-surface2); -->
<!-- margin: 10px 5px; -->
<!-- } -->
<!-- .bracket-sep { -->
<!-- height: 1px; -->
<!-- background: color-mix(in srgb, currentColor 10%, transparent); -->
<!-- margin: 0 10px; -->
<!-- } -->
<!-- .bracket-row { -->
<!-- display: flex; -->
<!-- gap: 6px; -->
<!-- padding: 7px 9px; -->
<!-- flex-wrap: wrap; -->
<!-- } -->
<!---->
<!-- /* ── Player boxes ── */ -->
<!-- .player-box { -->
<!-- flex: 1 1 0; /* equal widths, no min-content bias */ -->
<!-- max-width: 160px; -->
<!-- position: relative; -->
<!-- overflow: hidden; -->
<!-- border-radius: 8px; -->
<!-- border: 1.5px solid var(--c); -->
<!-- color: var(--c); -->
<!-- background: color-mix(in srgb, var(--c) 10%, transparent); -->
<!-- padding: 5px 7px 5px; -->
<!-- display: flex; -->
<!-- flex-direction: column; -->
<!-- min-height: 52px; -->
<!-- } -->
<!---->
<!-- /* 1 col on small screens, 4 across on large */ -->
<!-- @media (max-width: 479px) { -->
<!-- .bracket-row { -->
<!-- flex-direction: column; -->
<!-- } -->
<!-- .player-box { -->
<!-- max-width: 100%; -->
<!-- } -->
<!-- } -->
<!-- @media (min-width: 700px) { -->
<!-- .player-box { -->
<!-- max-width: calc(25% - 6px); -->
<!-- } /* 4 per row */ -->
<!-- } -->
<!---->
<!-- .player-ghost { -->
<!-- position: absolute; -->
<!-- inset: 0; -->
<!-- opacity: 0.15; -->
<!-- fill: currentColor; -->
<!-- pointer-events: none; -->
<!-- } -->
<!-- .ghost-svg { -->
<!-- width: 100%; -->
<!-- height: 100%; -->
<!-- display: block; -->
<!-- } -->
<!---->
<!-- .player-name-wrap { -->
<!-- position: relative; -->
<!-- z-index: 1; -->
<!-- overflow: hidden; -->
<!-- white-space: nowrap; -->
<!-- } -->
<!-- .marquee-inner { -->
<!-- display: inline-block; -->
<!-- font-size: 11px; -->
<!-- font-weight: 600; -->
<!-- white-space: nowrap; -->
<!-- } -->
<!-- .marquee-inner.scrolling { -->
<!-- animation: marquee-scroll 7s ease-in-out infinite; -->
<!-- } -->
<!-- @keyframes marquee-scroll { -->
<!-- 0%, -->
<!-- 20% { -->
<!-- transform: translateX(0); -->
<!-- } -->
<!-- 70%, -->
<!-- 90% { -->
<!-- transform: translateX(var(--scroll-dist, 0px)); -->
<!-- } -->
<!-- 100% { -->
<!-- transform: translateX(0); -->
<!-- } -->
<!-- } -->
<!---->
<!-- .player-placement { -->
<!-- position: relative; -->
<!-- z-index: 1; -->
<!-- font-size: 17px; -->
<!-- line-height: 1.1; -->
<!-- margin-top: 2px; -->
<!-- } -->
<!-- .player-placement-gap { -->
<!-- height: 20px; -->
<!-- } -->
<!-- </style> -->

View File

@@ -179,6 +179,8 @@
/* ── Events scrollable container ── */
.events-scroll {
flex: 1;
box-shadow: inset 0px 48px 20px -26px rgba(0, 0, 0, 0.35);
border-radius: 25px;
overflow-y: auto;
/* max-height: 900px; */
min-height: 0;
@@ -193,6 +195,17 @@
gap: 10px;
}
.event-status {
align-self: center;
justify-self: end;
text-align: end;
flex: 1;
}
.event-winner {
color: var(--winner-color);
}
/* ── Event card ── */
.event-card {
border-radius: 12px;
@@ -200,6 +213,11 @@
overflow: hidden;
transition: box-shadow 0.3s ease;
}
.ongoing-event {
background-color: color-mix(in srgb, currentColor 18%, transparent);
color: var(--ctp-latte-peach);
}
/* Focus highlight pulse — added/removed by $effect */
.event-card.highlight-pulse {
animation: card-pulse 1.2s ease-out forwards;
@@ -271,13 +289,40 @@
/* 1 col on small screens, 4 across on large */
@media (max-width: 479px) {
.brackets {
flex-direction: row;
align-items: stretch; /* was flex-start — lets columns fill full height */
}
.bracket-sep {
width: 1px;
height: auto;
margin: 10px 0;
align-self: stretch;
}
.bracket-row {
flex-direction: column;
min-width: 0;
flex: 1;
}
.player-box {
max-width: 100%;
flex: 1; /* equal height across all player boxes in the column */
}
.bracket-row {
flex: 1;
flex-direction: column;
align-items: stretch; /* player boxes fill the full column width */
}
.brackets-name {
text-align: center;
align-items: center;
}
.brackets-name-text {
text-align: center;
}
}
@media (min-width: 700px) {
.player-box {
max-width: calc(25% - 6px);