154 lines
3.2 KiB
Svelte
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>
|