File size: 1,790 Bytes
cf47645 f33fa43 cf47645 f33fa43 cf47645 f33fa43 cf47645 9104321 cf47645 f33fa43 cf47645 9104321 f33fa43 9104321 cf47645 f33fa43 cf47645 f33fa43 cf47645 9104321 f33fa43 cf47645 f33fa43 cf47645 9104321 cf47645 |
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 |
<script lang="ts">
import { autoUpdate, computePosition } from "@floating-ui/dom";
import { Toaster } from "melt/builders";
import { type Snippet } from "svelte";
import { fly } from "svelte/transition";
interface Props {
children: Snippet<[{ addToast: typeof toaster.addToast; trigger: typeof trigger }]>;
closeDelay?: number;
}
const { children, closeDelay = 2000 }: Props = $props();
const id = $props.id();
const trigger = {
id,
} as const;
type ToastData = {
content: string;
variant: "info" | "danger";
};
const toaster = new Toaster<ToastData>({
hover: null,
closeDelay: () => closeDelay,
});
function float(node: HTMLElement) {
const triggerEl = document.getElementById(trigger.id);
if (!triggerEl) return;
const compute = () =>
computePosition(triggerEl, node, {
placement: "top",
strategy: "absolute",
}).then(({ x, y }) => {
Object.assign(node.style, {
left: `${x}px`,
top: `${y - 8}px`,
});
});
return {
destroy: autoUpdate(triggerEl, node, compute),
};
}
const classMap: Record<ToastData["variant"], string> = {
info: "border border-blue-400 bg-gradient-to-b from-blue-500 to-blue-600",
danger: "border border-red-400 bg-gradient-to-b from-red-500 to-red-600",
};
</script>
{@render children({ trigger, addToast: toaster.addToast })}
{#each toaster.toasts as toast (toast.id)}
<div
data-local-toast
data-variant={toast.data.variant}
class="rounded-full px-2 py-1 text-xs {classMap[toast.data.variant]}"
in:fly={{ y: 10 }}
out:fly={{ y: -4 }}
use:float
>
{toast.data.content}
</div>
{/each}
<style>
[data-local-toast] {
/* Float on top of the UI */
position: absolute;
/* Avoid layout interference */
width: max-content;
top: 0;
left: 0;
}
</style>
|