vert/src/routes/+layout.svelte

155 lines
3.7 KiB
Svelte

<script lang="ts">
import "../app.css";
import { goto } from "$app/navigation";
import { blur, duration } from "$lib/animation";
import { quintOut } from "svelte/easing";
import { files } from "$lib/store/index.svelte";
import Logo from "$lib/components/visual/svg/Logo.svelte";
import { fly } from "svelte/transition";
import featuredImage from "$lib/assets/VERT_Feature.webp";
let { children, data } = $props();
let navWidth = $state(1);
let shouldGoBack = $state(false);
const links = $derived<{
[key: string]: string;
}>({
Upload: "/",
[files.files.length > 0
? `Convert (${files.files.length})`
: `Convert`]: "/convert",
About: "/about",
});
const linkCount = $derived(Object.keys(links).length);
const linkIndex = $derived(
Object.keys(links).findIndex((link) => links[link] === data.pathname),
);
const maybeNavToHome = (e: DragEvent) => {
if (e.dataTransfer?.types.includes("Files")) {
e.preventDefault();
goto("/");
}
};
</script>
<svelte:head>
<title>VERT.sh</title>
<meta name="theme-color" content="#F2ABEE" />
<meta property="og:image" content={featuredImage} />
<meta property="twitter:image" content={featuredImage} />
</svelte:head>
<div
role="main"
class="w-full h-full flex items-center p-8 flex-col gap-16"
ondragenter={maybeNavToHome}
>
<div
class="w-full max-w-screen-md p-1 border-solid border-2 rounded-2xl border-foreground-muted-alt flex"
>
<div class="p-1">
<a
href="/"
class="px-3 relative w-full h-full mr-3 justify-center items-center bg-accent-background fill-accent-foreground rounded-xl md:flex hidden"
>
<div class="h-6 w-24 items-center flex justify-center">
<Logo />
</div>
</a>
</div>
<div
bind:clientWidth={navWidth}
class="w-full flex bg-background relative h-16"
>
<div
class="absolute pointer-events-none top-1 bg-foreground h-[calc(100%-8px)] rounded-xl"
style="width: {navWidth / linkCount - 8}px; left: {(navWidth /
linkCount) *
linkIndex +
4}px; transition: {duration - 200}ms ease left;"
></div>
{#each Object.entries(links) as [name, link] (link)}
<a
class="w-1/2 px-2 h-[calc(100%-16px)] mt-2 flex items-center justify-center rounded-xl relative font-display overflow-hidden"
href={link}
onclick={() => {
const keys = Object.keys(links);
const currentIndex = keys.findIndex(
(key) => links[key] === data.pathname,
);
const nextIndex = keys.findIndex(
(key) => links[key] === link,
);
shouldGoBack = nextIndex < currentIndex;
}}
>
<div class="grid grid-cols-1 grid-rows-1">
{#key name}
<span
class="mix-blend-difference invert col-start-1 row-start-1 text-center"
in:fly={{
duration,
easing: quintOut,
y: -50,
}}
out:fly={{
duration,
easing: quintOut,
y: 50,
}}
>
{name}
</span>
{/key}
</div>
</a>
{/each}
</div>
</div>
<div
class="w-full max-w-screen-lg h-full grid grid-cols-1 grid-rows-1 relative"
>
{#key data.pathname}
<div class="w-full h-full">
<div
class="absolute top-0 left-0 w-full h-full"
in:blur={{
duration,
easing: quintOut,
blurMultiplier: 12,
x: {
start: !shouldGoBack ? 250 : -250,
end: 0,
},
scale: {
start: 0.75,
end: 1,
},
}}
out:blur={{
duration,
easing: quintOut,
blurMultiplier: 12,
x: {
start: 0,
end: !shouldGoBack ? -250 : 250,
},
scale: {
start: 1,
end: 0.75,
},
}}
>
<div class="pb-20">
{@render children()}
</div>
</div>
</div>
{/key}
</div>
</div>