|
<script lang="ts"> |
|
import { createEventDispatcher, onDestroy, onMount } from "svelte"; |
|
import { cubicOut } from "svelte/easing"; |
|
import { fade } from "svelte/transition"; |
|
import Portal from "./Portal.svelte"; |
|
import { browser } from "$app/environment"; |
|
|
|
export let width = "max-w-sm"; |
|
|
|
let backdropEl: HTMLDivElement; |
|
let modalEl: HTMLDivElement; |
|
|
|
const dispatch = createEventDispatcher<{ close: void }>(); |
|
|
|
function handleKeydown(event: KeyboardEvent) { |
|
|
|
if (event.key === "Escape") { |
|
event.preventDefault(); |
|
dispatch("close"); |
|
} |
|
} |
|
|
|
function handleBackdropClick(event: MouseEvent) { |
|
if (window?.getSelection()?.toString()) { |
|
return; |
|
} |
|
if (event.target === backdropEl) { |
|
dispatch("close"); |
|
} |
|
} |
|
|
|
onMount(() => { |
|
document.getElementById("app")?.setAttribute("inert", "true"); |
|
modalEl.focus(); |
|
}); |
|
|
|
onDestroy(() => { |
|
if (!browser) return; |
|
|
|
if (document.querySelectorAll('[role="dialog"]:not(#app *)').length === 1) { |
|
document.getElementById("app")?.removeAttribute("inert"); |
|
} |
|
}); |
|
</script> |
|
|
|
<Portal> |
|
|
|
<div |
|
role="presentation" |
|
tabindex="-1" |
|
bind:this={backdropEl} |
|
on:click|stopPropagation={handleBackdropClick} |
|
transition:fade|global={{ easing: cubicOut, duration: 300 }} |
|
class="fixed inset-0 z-40 flex items-center justify-center bg-black/80 p-8 backdrop-blur-sm dark:bg-black/50" |
|
> |
|
<div |
|
role="dialog" |
|
tabindex="-1" |
|
bind:this={modalEl} |
|
on:keydown={handleKeydown} |
|
class="max-h-[90dvh] overflow-y-auto overflow-x-hidden rounded-2xl bg-white shadow-2xl outline-none sm:-mt-10 {width}" |
|
> |
|
<slot /> |
|
</div> |
|
</div> |
|
</Portal> |
|
|