|
<script lang="ts" module> |
|
import { autofocus } from "$lib/actions/autofocus.js"; |
|
import { clickOutside } from "$lib/actions/click-outside.js"; |
|
import IconCross from "~icons/carbon/close"; |
|
|
|
type Prompt = { |
|
label: string; |
|
value?: string; |
|
placeholder?: string; |
|
callback: (value: string) => void; |
|
}; |
|
|
|
let prompts = $state<Prompt[]>([]); |
|
const current = $derived(prompts[0]); |
|
|
|
export function resolvePrompt() { |
|
if (!current) return; |
|
current.callback(current.value ?? ""); |
|
prompts.splice(0, 1); |
|
} |
|
|
|
export async function prompt(label: string, defaultValue?: string): Promise<string> { |
|
return new Promise(res => { |
|
prompts.push({ label, value: defaultValue, callback: res }); |
|
}); |
|
} |
|
</script> |
|
|
|
<script lang="ts"> |
|
let dialog: HTMLDialogElement | undefined = $state(); |
|
|
|
$effect(() => { |
|
if (current) { |
|
dialog?.showModal(); |
|
} else { |
|
dialog?.close(); |
|
} |
|
}); |
|
|
|
function onSubmit(e: Event) { |
|
e.preventDefault(); |
|
resolvePrompt(); |
|
} |
|
</script> |
|
|
|
<dialog bind:this={dialog} onclose={resolvePrompt}> |
|
{#if current} |
|
<div class="fixed inset-0 z-50 flex items-center justify-center overflow-hidden bg-black/85"> |
|
<form |
|
onsubmit={onSubmit} |
|
class="relative w-xl rounded-lg bg-white shadow-sm dark:bg-gray-900" |
|
use:clickOutside={resolvePrompt} |
|
> |
|
<div class="flex items-center justify-between rounded-t border-b p-4 md:px-5 md:py-4 dark:border-gray-800"> |
|
<h3 class="flex items-center gap-2.5 text-lg font-semibold text-gray-900 dark:text-white"> |
|
{current.label ?? "Prompt"} |
|
</h3> |
|
<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={resolvePrompt} |
|
> |
|
<div class="text-xl"> |
|
<IconCross /> |
|
</div> |
|
<span class="sr-only">Close modal</span> |
|
</button> |
|
</div> |
|
|
|
<div class="p-4 md:p-5"> |
|
<label class="flex flex-col gap-2 font-medium text-gray-900 dark:text-white"> |
|
<input |
|
bind:value={current.value} |
|
placeholder={current.placeholder} |
|
required |
|
use:autofocus |
|
type="text" |
|
class="block w-full rounded-lg border border-gray-300 bg-gray-50 p-2.5 text-sm text-gray-900 focus:border-blue-500 focus:ring-blue-500 dark:border-gray-600 dark:bg-gray-700 dark:text-white dark:placeholder-gray-400 dark:focus:border-blue-500 dark:focus:ring-blue-500" |
|
/> |
|
</label> |
|
</div> |
|
|
|
|
|
<div class="flex rounded-b border-t border-gray-200 p-4 md:p-5 dark:border-gray-800"> |
|
<button |
|
type="submit" |
|
class="ml-auto rounded-lg bg-black px-5 py-2.5 text-sm font-medium text-white hover:bg-gray-900 focus:ring-4 focus:ring-gray-300 focus:outline-hidden dark:border-gray-700 dark:bg-gray-800 dark:hover:bg-gray-700 dark:focus:ring-gray-700" |
|
>Submit</button |
|
> |
|
</div> |
|
</form> |
|
</div> |
|
{/if} |
|
</dialog> |
|
|