web/ChangelogEntry: refactor to svelte 5 style, fade in banner

This commit is contained in:
wukko 2025-05-04 16:01:10 +06:00
parent d717cf1aaa
commit e18664e879
No known key found for this signature in database
GPG Key ID: 3E30B3F26C7B4AA2

View File

@ -1,17 +1,22 @@
<script lang="ts"> <script lang="ts">
import { onMount } from "svelte"; import { onMount, type Snippet } from "svelte";
import type { Optional } from "$lib/types/generic"; import type { Optional } from "$lib/types/generic";
import Skeleton from "$components/misc/Skeleton.svelte"; import Skeleton from "$components/misc/Skeleton.svelte";
export let version: string; type Props = {
export let title: string = ""; version: string;
export let date: string = ""; title?: string;
export let banner: Optional<{ file: string; alt: string }> = undefined; date?: string;
export let skeleton = false; banner?: Optional<{ file: string; alt: string }>;
skeleton?: boolean;
children?: Snippet
};
let bannerLoaded = false; const { version, title, date, banner, skeleton, children }: Props = $props();
let bannerLoaded = $state(false);
let hideSkeleton = $state(false);
const formatDate = (dateString: string) => { const formatDate = (dateString: string) => {
const date = new Date(dateString); const date = new Date(dateString);
@ -26,6 +31,15 @@
].join(" "); ].join(" ");
}; };
const loaded = () => {
bannerLoaded = true;
// remove the skeleton after the image is done fading in
setTimeout(() => {
hideSkeleton = true;
}, 200)
}
onMount(() => { onMount(() => {
const to_focus: HTMLElement | null = const to_focus: HTMLElement | null =
document.querySelector("[data-first-focus]"); document.querySelector("[data-first-focus]");
@ -46,7 +60,7 @@
<div class="changelog-date"> <div class="changelog-date">
{#if skeleton} {#if skeleton}
<Skeleton width="8em" height="16px" /> <Skeleton width="8em" height="16px" />
{:else} {:else if date}
{formatDate(date)} {formatDate(date)}
{/if} {/if}
</div> </div>
@ -59,15 +73,16 @@
</div> </div>
<div class="changelog-content"> <div class="changelog-content">
{#if banner} {#if banner}
<img <div class="changelog-banner-container">
src={`/update-banners/${banner.file}`} <img
alt={banner.alt} src={`/update-banners/${banner.file}`}
class:loading={!bannerLoaded} alt={banner.alt}
on:load={() => bannerLoaded = true} class:loading={!bannerLoaded}
class="changelog-banner" onload={loaded}
/> class="changelog-banner"
/>
<Skeleton class="big changelog-banner" hidden={bannerLoaded} /> <Skeleton class="big changelog-banner" hidden={hideSkeleton} />
</div>
{/if} {/if}
{#if skeleton} {#if skeleton}
@ -84,7 +99,7 @@
</p> </p>
{/each} {/each}
{:else} {:else}
<slot></slot> {@render children?.()}
{/if} {/if}
</div> </div>
</div> </div>
@ -137,19 +152,37 @@
-webkit-user-select: text; -webkit-user-select: text;
} }
:global(.changelog-banner) { .changelog-banner-container {
display: block;
object-fit: cover; object-fit: cover;
max-height: 350pt; max-height: 350pt;
min-height: 180pt; min-height: 180pt;
width: 100%; width: 100%;
aspect-ratio: 16/9; aspect-ratio: 16/9;
position: relative;
}
:global(.changelog-banner) {
object-fit: cover;
width: 100%;
height: 100%;
aspect-ratio: 16/9;
border-radius: var(--padding); border-radius: var(--padding);
pointer-events: all; pointer-events: all;
opacity: 1;
transition: opacity 0.15s;
position: absolute;
z-index: 2;
}
:global(.skeleton.changelog-banner) {
z-index: 1;
position: relative;
} }
.changelog-banner.loading { .changelog-banner.loading {
display: none; opacity: 0;
} }
.changelog-content { .changelog-content {