|
<script lang="ts"> |
|
import { clickOutside } from "$lib/attachments/click-outside.js"; |
|
import type { Snippet } from "svelte"; |
|
import type { EventHandler } from "svelte/elements"; |
|
import IconCross from "~icons/carbon/close"; |
|
|
|
interface Props { |
|
title: string | Snippet; |
|
children: Snippet; |
|
footer?: Snippet; |
|
onClose?: () => void; |
|
open: boolean; |
|
onSubmit?: EventHandler<SubmitEvent>; |
|
class?: string; |
|
} |
|
|
|
let { children, onClose, open, title, footer, onSubmit, class: classes }: Props = $props(); |
|
|
|
let dialog: HTMLDialogElement | undefined = $state(); |
|
|
|
$effect(() => { |
|
if (open) { |
|
dialog?.showModal(); |
|
} else { |
|
dialog?.close(); |
|
} |
|
}); |
|
</script> |
|
|
|
<dialog bind:this={dialog} onclose={onClose}> |
|
{#if open} |
|
<form class="fixed inset-0 z-50 flex items-center justify-center overflow-hidden bg-black/85" onsubmit={onSubmit}> |
|
<div |
|
class={["relative w-xl rounded-xl bg-white shadow-sm dark:bg-gray-900", classes]} |
|
{@attach clickOutside(() => onClose?.())} |
|
> |
|
<div class="flex items-center justify-between rounded-t border-b p-4 md:px-5 md:py-4 dark:border-gray-800"> |
|
{#if typeof title === "string"} |
|
<h3 class="flex items-center gap-2.5 text-lg font-semibold text-gray-900 dark:text-white"> |
|
{title} |
|
</h3> |
|
{:else} |
|
{@render title()} |
|
{/if} |
|
<button |
|
type="button" |
|
class="ms-auto inline-flex h-8 w-8 items-center justify-center rounded-lg bg-transparent text-sm text-gray-400 hover:bg-gray-200 hover:text-gray-900 dark:hover:bg-gray-600 dark:hover:text-white" |
|
onclick={onClose} |
|
> |
|
<div class="text-xl"> |
|
<IconCross /> |
|
</div> |
|
<span class="sr-only">Close modal</span> |
|
</button> |
|
</div> |
|
|
|
<div class="p-4 md:p-5"> |
|
{@render children()} |
|
</div> |
|
|
|
{#if footer} |
|
|
|
<div class="flex rounded-b border-t border-gray-200 p-4 md:p-5 dark:border-gray-800"> |
|
{@render footer()} |
|
</div> |
|
{/if} |
|
</div> |
|
</form> |
|
{/if} |
|
</dialog> |
|
|