mirror of
https://git.suyu.dev/suyu/website.git
synced 2026-01-03 21:24:31 +01:00
custom model to prevent layout shift
This commit is contained in:
parent
736f657fc9
commit
4f134c2059
4 changed files with 131 additions and 30 deletions
61
src/components/ModalRoot.svelte
Normal file
61
src/components/ModalRoot.svelte
Normal file
|
|
@ -0,0 +1,61 @@
|
|||
<script lang="ts">
|
||||
import { ModalManager, type Modal } from "$lib/util/modal";
|
||||
import { browser } from "$app/environment";
|
||||
import { onMount } from "svelte";
|
||||
|
||||
const closeDuration = 180;
|
||||
|
||||
let modal: Modal | null = null;
|
||||
let closing = false;
|
||||
|
||||
$: pointer = modal ? "pointer-events-auto" : "pointer-events-none";
|
||||
$: opacity = closing || !modal ? "opacity-0" : "opacity-100";
|
||||
$: scale = !closing && modal ? "scale-100" : "scale-[0.9]";
|
||||
|
||||
function closeModal() {
|
||||
if (closing) return;
|
||||
closing = true;
|
||||
setTimeout(() => {
|
||||
closing = false;
|
||||
modal = null;
|
||||
}, closeDuration);
|
||||
}
|
||||
|
||||
function modalBgClick(e: MouseEvent) {
|
||||
if (e.target === e.currentTarget) closeModal();
|
||||
}
|
||||
|
||||
function modalEsc(e: KeyboardEvent) {
|
||||
console.log(e.key);
|
||||
if (e.key === "Escape") closeModal();
|
||||
}
|
||||
|
||||
onMount(() => {
|
||||
const id = ModalManager.addListener((d) => {
|
||||
modal = d;
|
||||
});
|
||||
return () => {
|
||||
ModalManager.removeListener(id);
|
||||
};
|
||||
});
|
||||
</script>
|
||||
|
||||
<div
|
||||
style="transition: 180ms ease; transition-property: opacity;"
|
||||
class={`fixed inset-0 z-50 h-screen w-screen ${opacity} ${pointer} flex items-center justify-center bg-[rgba(0,0,0,0.5)]`}
|
||||
tabindex="-1"
|
||||
id="modal-root"
|
||||
role="presentation"
|
||||
on:click={modalBgClick}
|
||||
on:keydown={modalEsc}
|
||||
>
|
||||
<div
|
||||
style="transition: 180ms ease; transition-property: transform;"
|
||||
class={`${scale} relative w-full max-w-[750px] rounded-[2.25rem] border border-solid border-gray-700 bg-black p-10 shadow-2xl shadow-stone-900`}
|
||||
>
|
||||
{#if modal}
|
||||
<h1 class="text-5xl">{modal.title}</h1>
|
||||
<p class="mt-4 text-lg leading-relaxed text-[#A6A5A7]">{modal.message}</p>
|
||||
{/if}
|
||||
</div>
|
||||
</div>
|
||||
27
src/lib/util/modal/index.ts
Normal file
27
src/lib/util/modal/index.ts
Normal file
|
|
@ -0,0 +1,27 @@
|
|||
import { v4 } from "uuid";
|
||||
|
||||
export interface Modal {
|
||||
title: string;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export class ModalManager {
|
||||
private static listeners: {
|
||||
id: string;
|
||||
callback: (data: Modal) => void;
|
||||
}[] = [];
|
||||
|
||||
static addListener(callback: (data: Modal) => void) {
|
||||
const id = v4();
|
||||
this.listeners.push({ id, callback });
|
||||
return id;
|
||||
}
|
||||
|
||||
static removeListener(id: string) {
|
||||
this.listeners = this.listeners.filter((listener) => listener.id !== id);
|
||||
}
|
||||
|
||||
static notify(data: Modal) {
|
||||
this.listeners.forEach((listener) => listener.callback(data));
|
||||
}
|
||||
}
|
||||
|
|
@ -4,6 +4,7 @@
|
|||
import Logo from "../components/LogoWithTextHorizontal.svelte";
|
||||
import { CodeBranchOutline, DiscordSolid, DownloadOutline } from "flowbite-svelte-icons";
|
||||
import { browser } from "$app/environment";
|
||||
import ModalManager from "$components/ModalRoot.svelte";
|
||||
|
||||
let scrolled = false;
|
||||
let cookies: {
|
||||
|
|
@ -43,8 +44,8 @@
|
|||
<header
|
||||
style="transition: 180ms ease;"
|
||||
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]"}
|
||||
? "fixed top-0 z-40 w-full border-b-2 border-b-[#ffffff11] bg-[#131215d0]"
|
||||
: "fixed top-0 z-40 w-full border-b-0 border-b-[transparent]"}
|
||||
>
|
||||
<nav
|
||||
style="transition: 180ms ease;"
|
||||
|
|
@ -91,3 +92,5 @@
|
|||
<slot />
|
||||
</div>
|
||||
</main>
|
||||
|
||||
<ModalManager />
|
||||
|
|
|
|||
|
|
@ -1,16 +1,17 @@
|
|||
<script lang="ts">
|
||||
import { XCircleOutline } from "flowbite-svelte-icons";
|
||||
import { Dialog } from "radix-svelte";
|
||||
import type { ResolvedProps } from "radix-svelte/internal/helpers";
|
||||
// import { XCircleOutline } from "flowbite-svelte-icons";
|
||||
// import { Dialog } from "radix-svelte";
|
||||
// import type { ResolvedProps } from "radix-svelte/internal/helpers";
|
||||
|
||||
import embedImage from "$assets/branding/suyu__Embed-Image.png";
|
||||
import suyuWindow from "$assets/mockups/suyuwindow.png";
|
||||
import { ModalManager } from "$lib/util/modal";
|
||||
|
||||
let rootOpen: boolean;
|
||||
let rootModal: boolean = true;
|
||||
let portalContainer: HTMLElement | string;
|
||||
let contentOpenAutoFocus: boolean = true;
|
||||
let contentCloseAutoFocus: boolean = true;
|
||||
// let rootOpen: boolean;
|
||||
// let rootModal: boolean = true;
|
||||
// let portalContainer: HTMLElement | string;
|
||||
// let contentOpenAutoFocus: boolean = true;
|
||||
// let contentCloseAutoFocus: boolean = true;
|
||||
|
||||
let metadata = {
|
||||
url: "https://suyu.dev",
|
||||
|
|
@ -19,6 +20,14 @@
|
|||
"suyu is a familiar C++ based Nintendo Switch emulator with a focus on compatibility. Completely free and open-source, forever. Download it here.",
|
||||
image: embedImage,
|
||||
};
|
||||
|
||||
function openModal() {
|
||||
ModalManager.notify({
|
||||
title: "suyu isn't available for download yet.",
|
||||
message:
|
||||
"Official downloads for suyu aren't available yet, but you're welcome to compile it yourself from the source code. If you're not comfortable with that, we recommend you wait for the official release.",
|
||||
});
|
||||
}
|
||||
</script>
|
||||
|
||||
<svelte:head>
|
||||
|
|
@ -47,7 +56,7 @@
|
|||
viewBox="0 0 512 525"
|
||||
fill="none"
|
||||
style="animation-duration: 300s; transform-origin: 50% 50%; animation-iteration-count: infinite; animation-timing-function: linear; animation-name: spin; animation-delay: 0s; animation-direction: normal; animation-fill-mode: none; animation-play-state: running;"
|
||||
class="absolute -bottom-[18rem] right-0 z-0 animate-spin opacity-20"
|
||||
class="pointer-events-none absolute -bottom-[18rem] right-0 z-0 animate-spin opacity-20"
|
||||
>
|
||||
<path
|
||||
d="M511.5 262.12C511.5 353.613 465.547 434.182 396.019 480.947C408.179 457.937 415.083 431.597 415.083 403.617C415.083 313.723 343.816 240.744 255.992 240.744C191.257 240.744 138.692 186.941 138.692 120.622C138.692 54.3027 191.257 0.5 255.992 0.5C397.026 0.5 511.5 117.695 511.5 262.12ZM255.992 53.5225C243.745 53.5225 233.816 63.7047 233.816 76.2224C233.816 88.7388 243.745 98.9223 255.992 98.9223C268.257 98.9223 278.173 88.7387 278.173 76.2224C278.173 63.7048 268.257 53.5225 255.992 53.5225ZM299.355 97.9223C287.104 97.9223 277.173 108.104 277.173 120.622C277.173 133.139 287.104 143.322 299.355 143.322C311.62 143.322 321.536 133.139 321.536 120.622C321.536 108.104 311.62 97.9223 299.355 97.9223ZM212.635 97.9223C200.382 97.9223 190.455 108.104 190.455 120.622C190.455 133.139 200.382 143.322 212.635 143.322C224.889 143.322 234.816 133.139 234.816 120.622C234.816 108.104 224.888 97.9223 212.635 97.9223ZM255.992 142.322C243.745 142.322 233.816 152.505 233.816 165.021C233.816 177.539 243.745 187.721 255.992 187.721C268.257 187.721 278.173 177.538 278.173 165.021C278.173 152.505 268.257 142.322 255.992 142.322Z"
|
||||
|
|
@ -66,24 +75,25 @@
|
|||
and open-source, forever.
|
||||
</p>
|
||||
<div class="flex flex-row gap-4">
|
||||
<Dialog.Root bind:modal={rootModal} bind:open={rootOpen}>
|
||||
<Dialog.Trigger class="cta-button">
|
||||
Download <svg
|
||||
class=""
|
||||
style="--icon-color:#000"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="#000"
|
||||
role="img"
|
||||
focusable="false"
|
||||
aria-hidden="true"
|
||||
><path
|
||||
d="M5.46967 11.4697C5.17678 11.7626 5.17678 12.2374 5.46967 12.5303C5.76256 12.8232 6.23744 12.8232 6.53033 12.5303L10.5303 8.53033C10.8207 8.23999 10.8236 7.77014 10.5368 7.47624L6.63419 3.47624C6.34492 3.17976 5.87009 3.17391 5.57361 3.46318C5.27713 3.75244 5.27128 4.22728 5.56054 4.52376L8.94583 7.99351L5.46967 11.4697Z"
|
||||
></path></svg
|
||||
>
|
||||
</Dialog.Trigger>
|
||||
<Dialog.Portal>
|
||||
<!-- <Dialog.Root bind:modal={rootModal} bind:open={rootOpen}> -->
|
||||
<button on:click={openModal} class="cta-button">
|
||||
Download <svg
|
||||
class=""
|
||||
style="--icon-color:#000"
|
||||
width="16"
|
||||
height="16"
|
||||
viewBox="0 0 16 16"
|
||||
fill="#000"
|
||||
role="img"
|
||||
focusable="false"
|
||||
aria-hidden="true"
|
||||
><path
|
||||
d="M5.46967 11.4697C5.17678 11.7626 5.17678 12.2374 5.46967 12.5303C5.76256 12.8232 6.23744 12.8232 6.53033 12.5303L10.5303 8.53033C10.8207 8.23999 10.8236 7.77014 10.5368 7.47624L6.63419 3.47624C6.34492 3.17976 5.87009 3.17391 5.57361 3.46318C5.27713 3.75244 5.27128 4.22728 5.56054 4.52376L8.94583 7.99351L5.46967 11.4697Z"
|
||||
></path></svg
|
||||
>
|
||||
</button>
|
||||
<!-- </Dialog.Trigger> -->
|
||||
<!-- <Dialog.Portal>
|
||||
<Dialog.Overlay
|
||||
class="fixed inset-0 z-[9999] bg-black/80 data-[state=open]:animate-[overlayShow_555ms_cubic-bezier(.16,1,.3,1)]"
|
||||
/>
|
||||
|
|
@ -112,7 +122,7 @@
|
|||
</Dialog.Close>
|
||||
</Dialog.Content>
|
||||
</Dialog.Portal>
|
||||
</Dialog.Root>
|
||||
</Dialog.Root> -->
|
||||
<a
|
||||
href="https://gitlab.com/suyu-emu/"
|
||||
target="_blank"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue