Files
blogflare/src/lib/components/Toast.svelte
T

154 lines
3.2 KiB
Svelte

<script lang="ts">
import { toastState } from '$lib/stores/toast.svelte';
import { fade, fly } from 'svelte/transition';
</script>
<div class="toast-container">
{#each toastState.messages as msg (msg.id)}
<!-- Use fly in and fade out for a sleek animation -->
<div
class="toast-msg type-{msg.type}"
in:fly={{ y: 20, duration: 300 }}
out:fade={{ duration: 200 }}
>
{#if msg.type === 'success'}
<svg
viewBox="0 0 24 24"
width="18"
height="18"
stroke="currentColor"
stroke-width="2"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
class="toast-icon"
>
<polyline points="20 6 9 17 4 12"></polyline>
</svg>
{:else if msg.type === 'error'}
<svg
viewBox="0 0 24 24"
width="18"
height="18"
stroke="currentColor"
stroke-width="2"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
class="toast-icon"
>
<circle cx="12" cy="12" r="10"></circle>
<line x1="15" y1="9" x2="9" y2="15"></line>
<line x1="9" y1="9" x2="15" y2="15"></line>
</svg>
{:else}
<svg
viewBox="0 0 24 24"
width="18"
height="18"
stroke="currentColor"
stroke-width="2"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
class="toast-icon"
>
<circle cx="12" cy="12" r="10"></circle>
<line x1="12" y1="16" x2="12" y2="12"></line>
<line x1="12" y1="8" x2="12.01" y2="8"></line>
</svg>
{/if}
<span>{msg.text}</span>
<button class="toast-close" onclick={() => toastState.remove(msg.id)} aria-label="Close">
<svg
viewBox="0 0 24 24"
width="14"
height="14"
stroke="currentColor"
stroke-width="2"
fill="none"
stroke-linecap="round"
stroke-linejoin="round"
>
<line x1="18" y1="6" x2="6" y2="18"></line>
<line x1="6" y1="6" x2="18" y2="18"></line>
</svg>
</button>
</div>
{/each}
</div>
<style>
.toast-container {
position: fixed;
bottom: 25px;
right: 25px;
z-index: 9999;
display: flex;
flex-direction: column;
gap: 12px;
pointer-events: none; /* Let clicks pass through empty space */
}
.toast-msg {
display: flex;
align-items: center;
gap: 10px;
padding: 14px 20px;
background-color: var(--bg-color, #fff);
color: var(--text-color, #222);
border: 2px solid var(--text-color, #222);
box-shadow: 4px 4px 0 rgba(0, 0, 0, 0.15);
font-weight: 600;
font-size: 0.95rem;
min-width: 250px;
max-width: 400px;
pointer-events: auto; /* Re-enable clicks on the toast itself */
}
.toast-msg.type-success {
border-color: #419b38;
color: #419b38;
}
.toast-msg.type-success .toast-close {
color: #419b38;
}
.toast-msg.type-error {
border-color: #d9534f;
color: #d9534f;
}
.toast-msg.type-error .toast-close {
color: #d9534f;
}
.toast-icon {
flex-shrink: 0;
}
.toast-msg span {
flex-grow: 1;
word-break: break-word;
line-height: 1.4;
}
.toast-close {
background: none;
border: none;
padding: 4px;
cursor: pointer;
color: var(--text-muted, #888);
display: flex;
align-items: center;
justify-content: center;
transition: transform 0.2s;
}
.toast-close:hover {
transform: scale(1.1);
}
</style>