mirror of
https://github.com/wukko/cobalt.git
synced 2025-04-29 22:14:26 +02:00
226 lines
6.1 KiB
Svelte
226 lines
6.1 KiB
Svelte
<script lang="ts">
|
|
import { page } from "$app/stores";
|
|
import { browser } from "$app/environment";
|
|
|
|
import { t } from "$lib/i18n/translations";
|
|
import { getAllChangelogs } from "$lib/changelogs";
|
|
import type { Optional } from "$lib/types/generic";
|
|
import type { ChangelogImport } from "$lib/types/changelogs";
|
|
|
|
import ChangelogEntry from "$components/changelog/ChangelogEntry.svelte";
|
|
|
|
import IconArrowLeft from "@tabler/icons-svelte/IconArrowLeft.svelte";
|
|
import IconArrowRight from "@tabler/icons-svelte/IconArrowRight.svelte";
|
|
|
|
const changelogs = getAllChangelogs();
|
|
const versions = Object.keys(changelogs);
|
|
|
|
let changelog: Optional<{
|
|
version: string;
|
|
page: Promise<ChangelogImport>;
|
|
}>;
|
|
let currentIndex = 0;
|
|
let wrapper: HTMLDivElement;
|
|
|
|
{
|
|
const hash = $page.url.hash.replace("#", "");
|
|
const versionIndex = versions.indexOf(hash);
|
|
if (versionIndex !== -1 && currentIndex !== versionIndex) {
|
|
currentIndex = versionIndex;
|
|
}
|
|
}
|
|
|
|
const loadChangelog = async () => {
|
|
const version = versions[currentIndex];
|
|
changelog = {
|
|
version,
|
|
page: changelogs[version]() as Promise<ChangelogImport>,
|
|
};
|
|
|
|
if (browser) {
|
|
window.location.hash = version;
|
|
}
|
|
|
|
await changelog.page;
|
|
};
|
|
|
|
const loadNext = () => {
|
|
if (currentIndex < versions.length - 1) ++currentIndex;
|
|
};
|
|
|
|
const loadPrev = () => {
|
|
if (currentIndex > 0) --currentIndex;
|
|
};
|
|
|
|
const preloadNext = () => {
|
|
if (!next) return;
|
|
changelogs[next]().catch(() => {});
|
|
};
|
|
|
|
const handleKeydown = (e: KeyboardEvent) => {
|
|
if (e.key === "ArrowLeft") loadPrev();
|
|
else if (e.key === "ArrowRight") loadNext();
|
|
};
|
|
|
|
const handleScroll = (e: WheelEvent) => {
|
|
if (!(e.target instanceof HTMLElement)) {
|
|
return;
|
|
}
|
|
|
|
if (!wrapper.contains(e.target)) {
|
|
wrapper.scrollTop += e.deltaY;
|
|
e.preventDefault();
|
|
}
|
|
};
|
|
|
|
$: prev = versions[currentIndex - 1];
|
|
$: next = versions[currentIndex + 1];
|
|
$: currentIndex, loadChangelog();
|
|
</script>
|
|
|
|
<svelte:head>
|
|
<title>
|
|
{$t("tabs.updates")} ~ {$t("general.cobalt")}
|
|
</title>
|
|
<meta
|
|
property="og:title"
|
|
content="{$t("tabs.updates")} ~ {$t("general.cobalt")}"
|
|
/>
|
|
</svelte:head>
|
|
|
|
<svelte:window on:keydown={handleKeydown} />
|
|
|
|
<div class="news" tabindex="-1" on:wheel={handleScroll}>
|
|
{#if changelog}
|
|
<div id="left-button" class="button-wrapper-desktop">
|
|
{#if prev}
|
|
<button
|
|
on:click={loadPrev}
|
|
aria-label={$t("updates.button.previous", { value: prev })}
|
|
>
|
|
<IconArrowLeft />
|
|
{prev || ""}
|
|
</button>
|
|
{/if}
|
|
</div>
|
|
<div class="changelog-wrapper" bind:this={wrapper}>
|
|
{#await changelog.page}
|
|
{#key changelog.version}
|
|
<ChangelogEntry
|
|
version={changelog.version}
|
|
skeleton
|
|
/>
|
|
{/key}
|
|
{:then page}
|
|
<svelte:component
|
|
this={page.default}
|
|
{...page.metadata}
|
|
version={changelog.version}
|
|
/>
|
|
{/await}
|
|
|
|
<div class="button-wrapper-mobile" class:only-right={!prev}>
|
|
{#if prev}
|
|
<button
|
|
on:click={loadPrev}
|
|
aria-label={$t("updates.button.previous", { value: prev })}
|
|
>
|
|
<IconArrowLeft />
|
|
{prev || ""}
|
|
</button>
|
|
{/if}
|
|
{#if next}
|
|
<button
|
|
on:click={loadNext}
|
|
on:focus={preloadNext}
|
|
on:mousemove={preloadNext}
|
|
aria-label={$t("updates.button.next", { value: next })}
|
|
>
|
|
{next || ""}
|
|
<IconArrowRight />
|
|
</button>
|
|
{/if}
|
|
</div>
|
|
</div>
|
|
<div id="right-button" class="button-wrapper-desktop">
|
|
{#if next}
|
|
<button
|
|
on:click={loadNext}
|
|
on:focus={preloadNext}
|
|
on:mousemove={preloadNext}
|
|
aria-label={$t("updates.button.next", { value: next })}
|
|
>
|
|
{next || ""}
|
|
<IconArrowRight />
|
|
</button>
|
|
{/if}
|
|
</div>
|
|
{/if}
|
|
</div>
|
|
|
|
<style>
|
|
.news {
|
|
display: flex;
|
|
width: 100%;
|
|
flex-direction: row;
|
|
justify-content: space-evenly;
|
|
}
|
|
|
|
.button-wrapper-desktop {
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: center;
|
|
height: 100%;
|
|
}
|
|
|
|
.button-wrapper-desktop button {
|
|
position: absolute;
|
|
background-color: transparent;
|
|
display: flex;
|
|
border: none;
|
|
}
|
|
|
|
button :global(svg) {
|
|
stroke-width: 1.6px;
|
|
}
|
|
|
|
.button-wrapper-desktop button {
|
|
box-shadow: none;
|
|
}
|
|
|
|
.changelog-wrapper {
|
|
flex: 1;
|
|
max-width: 850px;
|
|
overflow-x: hidden;
|
|
padding: var(--padding);
|
|
padding-top: calc(var(--padding) + 1em);
|
|
}
|
|
|
|
.button-wrapper-mobile {
|
|
display: none;
|
|
}
|
|
|
|
@media only screen and (max-width: 1150px) {
|
|
.button-wrapper-mobile {
|
|
display: flex;
|
|
padding: var(--border-radius);
|
|
padding-top: 0;
|
|
justify-content: space-between;
|
|
}
|
|
|
|
.button-wrapper-mobile.only-right {
|
|
justify-content: end;
|
|
}
|
|
|
|
.button-wrapper-desktop {
|
|
display: none;
|
|
}
|
|
}
|
|
|
|
@media screen and (max-width: 535px) {
|
|
.changelog-wrapper {
|
|
padding-top: var(--padding);
|
|
}
|
|
}
|
|
</style>
|