Spaces:
Sleeping
Sleeping
File size: 3,039 Bytes
b4668c2 7bf1507 b4668c2 eb17509 8073be8 e584c54 01b06a3 a1a6daf e584c54 7bf1507 a1a6daf 7bf1507 8073be8 a1a6daf 8073be8 7bf1507 8073be8 0f3fab1 7bf1507 8073be8 a1a6daf 7bf1507 8073be8 a1a6daf 7bf1507 8073be8 b4668c2 8073be8 a1a6daf 38c25e1 e584c54 8073be8 7bf1507 b4668c2 8073be8 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 |
<script lang="ts">
import { onDestroy, onMount } from "svelte";
import { cubicOut } from "svelte/easing";
import { fade, fly } from "svelte/transition";
import Portal from "./Portal.svelte";
import { browser } from "$app/environment";
import CarbonClose from "~icons/carbon/close";
interface Props {
width?: string;
closeButton?: boolean;
disableFly?: boolean;
/** When false, clicking backdrop will not close the modal */
closeOnBackdrop?: boolean;
onclose?: () => void;
children?: import("svelte").Snippet;
}
let {
width = "max-w-sm",
children,
closeButton = false,
disableFly = false,
closeOnBackdrop = true,
onclose,
}: Props = $props();
let backdropEl: HTMLDivElement | undefined = $state();
let modalEl: HTMLDivElement | undefined = $state();
function handleKeydown(event: KeyboardEvent) {
// close on ESC
if (event.key === "Escape") {
event.preventDefault();
onclose?.();
}
}
function handleBackdropClick(event: MouseEvent) {
if (window?.getSelection()?.toString()) {
return;
}
if (event.target === backdropEl && closeOnBackdrop) {
onclose?.();
}
}
onMount(() => {
document.getElementById("app")?.setAttribute("inert", "true");
modalEl?.focus();
// Ensure Escape closes even if focus isn't within modal
window.addEventListener("keydown", handleKeydown, { capture: true });
});
onDestroy(() => {
if (!browser) return;
document.getElementById("app")?.removeAttribute("inert");
window.removeEventListener("keydown", handleKeydown, { capture: true });
});
</script>
<Portal>
<div
role="presentation"
tabindex="-1"
bind:this={backdropEl}
onclick={(e) => {
e.stopPropagation();
handleBackdropClick(e);
}}
transition:fade|local={{ easing: cubicOut, duration: 300 }}
class="fixed inset-0 z-40 flex items-center justify-center bg-black/80 backdrop-blur-sm dark:bg-black/50"
>
{#if disableFly}
<div
role="dialog"
tabindex="-1"
bind:this={modalEl}
onkeydown={handleKeydown}
class={[
"relative mx-auto max-h-[95dvh] max-w-[90dvw] overflow-y-auto overflow-x-hidden rounded-2xl bg-white shadow-2xl outline-none dark:bg-gray-800 dark:text-gray-200",
width,
]}
>
{#if closeButton}
<button class="absolute right-4 top-4 z-50" onclick={() => onclose?.()}>
<CarbonClose class="size-6 text-gray-700 dark:text-gray-300" />
</button>
{/if}
{@render children?.()}
</div>
{:else}
<div
role="dialog"
tabindex="-1"
bind:this={modalEl}
onkeydown={handleKeydown}
in:fly={{ y: 100 }}
class={[
"relative mx-auto max-h-[95dvh] max-w-[90dvw] overflow-y-auto overflow-x-hidden rounded-2xl bg-white shadow-2xl outline-none dark:bg-gray-800 dark:text-gray-200",
width,
]}
>
{#if closeButton}
<button class="absolute right-4 top-4 z-50" onclick={() => onclose?.()}>
<CarbonClose class="size-6 text-gray-700 dark:text-gray-300" />
</button>
{/if}
{@render children?.()}
</div>
{/if}
</div>
</Portal>
|