mirror of
https://git.suyu.dev/suyu/website.git
synced 2026-01-05 22:18:05 +01:00
355 lines
8.8 KiB
Svelte
355 lines
8.8 KiB
Svelte
<script lang="ts">
|
|
import "../app.pcss";
|
|
import { onMount, onDestroy } from "svelte";
|
|
import Logo from "../components/LogoWithTextHorizontal.svelte";
|
|
import { CodeBranchOutline, DiscordSolid, BarsSolid, CloseSolid } from "flowbite-svelte-icons";
|
|
import { browser } from "$app/environment";
|
|
import { writable } from "svelte/store";
|
|
import { setContext } from "svelte";
|
|
import type { TransitionConfig } from "svelte/transition";
|
|
import type { PageData } from "./$types";
|
|
import { bounceOut } from "svelte/easing";
|
|
import { generateTransition, transition } from "$lib/util/animation";
|
|
|
|
export let data: PageData;
|
|
|
|
// TODO: refactor
|
|
interface NavItem {
|
|
name: string;
|
|
href: string;
|
|
}
|
|
|
|
const token = writable("");
|
|
|
|
function transitionIn(node: HTMLElement, { duration = 360 }: TransitionConfig) {
|
|
const UA = navigator.userAgent;
|
|
const ff = UA.indexOf("Firefox") > -1;
|
|
if (!dropdownCloseFinished) {
|
|
node.animate(
|
|
[
|
|
{
|
|
top: "160px",
|
|
opacity: "0",
|
|
filter: ff ? "" : "blur(20px)",
|
|
},
|
|
{
|
|
top: "0",
|
|
opacity: "1",
|
|
filter: ff ? "" : "blur(0px)",
|
|
},
|
|
],
|
|
{
|
|
easing: transition,
|
|
duration,
|
|
},
|
|
);
|
|
|
|
return {
|
|
duration: 0,
|
|
};
|
|
}
|
|
// FUCK YOUR DEFAULT SYSTEM, SVELTEKIT!!!
|
|
node.animate(
|
|
[
|
|
{
|
|
top: "-240px",
|
|
opacity: "0",
|
|
filter: ff ? "" : "blur(20px)",
|
|
},
|
|
{
|
|
top: "0",
|
|
opacity: "1",
|
|
filter: ff ? "" : "blur(0px)",
|
|
},
|
|
],
|
|
{
|
|
easing: transition,
|
|
duration,
|
|
},
|
|
);
|
|
return {
|
|
duration,
|
|
};
|
|
}
|
|
|
|
function transitionOut(node: HTMLElement, { duration = 360 }: TransitionConfig) {
|
|
if (!dropdownCloseFinished)
|
|
return {
|
|
duration: 0,
|
|
};
|
|
const UA = navigator.userAgent;
|
|
const ff = UA.indexOf("Firefox") > -1;
|
|
node.animate(
|
|
[
|
|
{
|
|
top: "0",
|
|
opacity: "1",
|
|
filter: ff ? "" : "blur(0px)",
|
|
},
|
|
{
|
|
top: "240px",
|
|
opacity: "0",
|
|
filter: ff ? "" : "blur(80px)",
|
|
},
|
|
],
|
|
{
|
|
easing: transition,
|
|
duration: duration,
|
|
},
|
|
);
|
|
return {
|
|
duration,
|
|
};
|
|
}
|
|
|
|
let dropdownOpen = false;
|
|
let dropdownCloseFinished = true;
|
|
let dropdownOpenFinished = false;
|
|
// let dropdownOpen = true;
|
|
// let dropdownCloseFinished = false;
|
|
// let dropdownOpenFinished = true;
|
|
let scrolled = false;
|
|
let cookies: {
|
|
[key: string]: string;
|
|
} = {};
|
|
|
|
const navItems: NavItem[] = [
|
|
{
|
|
name: "Blog",
|
|
href: "/blog",
|
|
},
|
|
{
|
|
name: "Docs",
|
|
href: "/coming-soon",
|
|
},
|
|
{
|
|
name: "FAQ",
|
|
href: "/coming-soon",
|
|
},
|
|
{
|
|
name: "Discord",
|
|
href: "https://discord.gg/suyu",
|
|
},
|
|
{
|
|
name: "GitLab",
|
|
href: "https://gitlab.com/suyu-emu/",
|
|
},
|
|
];
|
|
|
|
$: {
|
|
if (browser) {
|
|
const html = document.querySelector("html")!;
|
|
if (!dropdownOpen) {
|
|
dropdownCloseFinished = false;
|
|
setTimeout(() => {
|
|
html.style.overflowY = "auto";
|
|
dropdownCloseFinished = true;
|
|
}, 360);
|
|
} else {
|
|
html.style.overflowY = "hidden";
|
|
dropdownOpenFinished = false;
|
|
setTimeout(() => {
|
|
dropdownOpenFinished = true;
|
|
}, 360);
|
|
}
|
|
}
|
|
}
|
|
|
|
function getTransition(i: number) {
|
|
return `${((i + 1) / 4) * 75}ms`;
|
|
}
|
|
|
|
if (browser) {
|
|
cookies = Object.fromEntries(
|
|
document.cookie.split("; ").map((c) => {
|
|
const [key, value] = c.split("=");
|
|
return [key, value];
|
|
}),
|
|
);
|
|
if (cookies.token) {
|
|
$token = cookies.token;
|
|
}
|
|
}
|
|
|
|
function toggleDropdown() {
|
|
dropdownOpen = !dropdownOpen;
|
|
}
|
|
|
|
setContext("token", token);
|
|
onMount(() => {
|
|
const handleScroll = () => {
|
|
scrolled = window.scrollY > 0;
|
|
};
|
|
|
|
handleScroll(); // we can't guarantee that the page starts at the top
|
|
|
|
cookies = Object.fromEntries(
|
|
document.cookie.split("; ").map((c) => {
|
|
const [key, value] = c.split("=");
|
|
return [key, value];
|
|
}),
|
|
);
|
|
|
|
window.addEventListener("scroll", handleScroll);
|
|
|
|
return () => {
|
|
window.removeEventListener("scroll", handleScroll);
|
|
};
|
|
});
|
|
</script>
|
|
|
|
<main
|
|
class={`min-h-full w-full ${dropdownOpen || !dropdownCloseFinished ? "overflow-hidden" : ""}`}
|
|
>
|
|
<header
|
|
style="transition: 180ms ease border;"
|
|
class={`${
|
|
scrolled
|
|
? "fixed top-0 z-[9999] w-full border-b-2 border-b-[#ffffff11] bg-[#131215d0]"
|
|
: "fixed top-0 z-[9999] w-full border-b-0 border-b-[transparent]"
|
|
} pl-[calc(100vw-100%)]`}
|
|
>
|
|
<nav
|
|
style="transition: 180ms ease;"
|
|
class={scrolled
|
|
? "mx-auto flex h-[72px] w-full max-w-[1300px] flex-row items-center justify-between px-8 backdrop-blur-xl"
|
|
: "mx-auto flex h-[120px] w-full max-w-[1300px] flex-row items-center justify-between px-8"}
|
|
>
|
|
<div class="flex w-full flex-row items-center justify-start gap-8">
|
|
<a
|
|
href="/"
|
|
on:click={() => {
|
|
if (dropdownOpen && window.innerWidth < 625) toggleDropdown();
|
|
}}
|
|
>
|
|
<Logo size={28} />
|
|
</a>
|
|
</div>
|
|
<div
|
|
class="flex w-full flex-row items-center justify-center gap-2 text-sm font-medium text-[#A6A5A7] max-[625px]:hidden"
|
|
>
|
|
<a href="/blog" class="px-5 py-3 transition hover:text-white">Blog</a>
|
|
<a href="/coming-soon" class="px-5 py-3 transition hover:text-white">Docs</a>
|
|
<a href="/coming-soon" class="px-5 py-3 transition hover:text-white">FAQ</a>
|
|
</div>
|
|
<div class="flex w-full flex-row items-center justify-end text-[#A6A5A7]">
|
|
<div class="flex flex-row gap-4 max-[625px]:hidden">
|
|
<a
|
|
class="p-2 transition hover:text-white"
|
|
href="https://gitlab.com/suyu-emu/suyu"
|
|
rel="noreferrer noopener"
|
|
target="_blank"
|
|
>
|
|
<CodeBranchOutline />
|
|
</a>
|
|
<a
|
|
class="p-2 transition hover:text-white"
|
|
href="https://discord.gg/suyu"
|
|
rel="noreferrer noopener"
|
|
target="_blank"
|
|
>
|
|
<DiscordSolid />
|
|
</a>
|
|
<!-- <a href={$token ? "/account" : "/signup"} class="button-sm"
|
|
>{$token ? "Account" : "Sign up"}</a
|
|
> -->
|
|
</div>
|
|
<div class="relative mr-4 hidden flex-row gap-4 max-[625px]:flex">
|
|
<button
|
|
aria-label={dropdownOpen ? "Close navigation" : "Open navigation"}
|
|
aria-expanded={dropdownOpen}
|
|
on:click={toggleDropdown}
|
|
class="-mr-4 p-4"
|
|
>
|
|
<div
|
|
style="transition: 180ms; transition-property: opacity transform;"
|
|
class={`absolute ${dropdownOpen ? "rotate-45 opacity-0" : "opacity-1"}`}
|
|
>
|
|
<BarsSolid />
|
|
</div>
|
|
<div
|
|
style="transition: 180ms; transition-property: opacity transform;"
|
|
class={dropdownOpen
|
|
? "opacity-1 rotate-0"
|
|
: "rotate-[-45deg] opacity-0"}
|
|
>
|
|
<CloseSolid />
|
|
</div>
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</nav>
|
|
</header>
|
|
<div
|
|
style="transition: 180ms ease;"
|
|
aria-hidden={!dropdownOpenFinished && !dropdownOpen}
|
|
class={`fixed left-0 z-10 h-screen w-full bg-black p-9 pt-[120px] ${dropdownOpen ? "pointer-events-auto visible opacity-100" : "pointer-events-none opacity-0"} ${!dropdownOpen && dropdownCloseFinished ? "invisible" : ""}`}
|
|
>
|
|
<div class={`flex flex-col gap-8`}>
|
|
<!-- <a href="##"><h1 class="w-full text-5xl">Blog</h1></a>
|
|
<a href="##"><h1 class="w-full text-5xl">Docs</h1></a>
|
|
<a href="##"><h1 class="w-full text-5xl">FAQ</h1></a> -->
|
|
{#each navItems as item, i}
|
|
<a
|
|
style={`transition: ${
|
|
dropdownOpen
|
|
? generateTransition([
|
|
// {
|
|
// duration: 600,
|
|
// delay: (i + 1) / 4,
|
|
// property: "transform",
|
|
// timingFunction: transition,
|
|
// },
|
|
// {
|
|
// duration: 500,
|
|
// delay: i * 1.25,
|
|
// property: "filter",
|
|
// timingFunction: transition,
|
|
// },
|
|
// {
|
|
// duration: 400,
|
|
// delay: (i + 1) / 4,
|
|
// property: "opacity",
|
|
// timingFunction: transition,
|
|
// },
|
|
{
|
|
duration: 450,
|
|
delay: i * 0.6,
|
|
property: ["transform", "opacity", "filter"],
|
|
timingFunction: transition,
|
|
},
|
|
])
|
|
: generateTransition([
|
|
{
|
|
duration: 450,
|
|
delay: 0,
|
|
property: ["transform", "opacity", "filter"],
|
|
timingFunction: transition,
|
|
},
|
|
])
|
|
}`}
|
|
class="{dropdownOpen
|
|
? 'translate-y-0 opacity-100 filter-none'
|
|
: '-translate-y-24 opacity-0 blur-md'} "
|
|
href={item.href}
|
|
on:click={() => toggleDropdown()}
|
|
>
|
|
<h1 class="w-full text-5xl">{item.name}</h1>
|
|
</a>
|
|
{/each}
|
|
</div>
|
|
</div>
|
|
<!-- svelte-ignore a11y-no-noninteractive-tabindex -->
|
|
{#key data.url}
|
|
<div
|
|
in:transitionIn={{ duration: 500 }}
|
|
out:transitionOut={{ duration: 500 }}
|
|
style="transition: 360ms {transition}; transition-property: opacity, transform;"
|
|
aria-hidden={dropdownOpenFinished && dropdownOpen}
|
|
tabindex={dropdownOpen ? 0 : -1}
|
|
class={`absolute left-[50%] z-50 mx-auto flex w-screen max-w-[1300px] translate-x-[-50%] flex-col px-8 pb-12 pt-[120px] ${dropdownOpen ? "pointer-events-none translate-y-[25vh] opacity-0" : ""} ${dropdownOpenFinished && dropdownOpen ? "invisible" : ""}`}
|
|
>
|
|
<slot />
|
|
</div>
|
|
{/key}
|
|
</main>
|