59 lines
1.7 KiB
Vue
59 lines
1.7 KiB
Vue
<script setup lang="ts">
|
|
import { useNotificationStore } from '@/stores/notificationStore';
|
|
|
|
const store = useNotificationStore();
|
|
</script>
|
|
|
|
<template>
|
|
<div class="toast-stack">
|
|
<TransitionGroup name="toast">
|
|
<v-card
|
|
v-for="notification in store.toasts"
|
|
:key="notification.id"
|
|
class="toast-card glass"
|
|
rounded="lg"
|
|
elevation="4"
|
|
max-width="360"
|
|
>
|
|
<v-card-text class="d-flex align-center ga-3">
|
|
<v-icon icon="mdi-trophy" color="amber" size="32" />
|
|
<div>
|
|
<div class="text-subtitle-2 font-weight-bold">{{ notification.title }}</div>
|
|
<div class="text-body-2 text-medium-emphasis">{{ notification.body }}</div>
|
|
</div>
|
|
<v-btn
|
|
icon="mdi-close"
|
|
variant="text"
|
|
size="small"
|
|
class="ml-auto"
|
|
@click="store.dismiss(notification)"
|
|
/>
|
|
</v-card-text>
|
|
</v-card>
|
|
</TransitionGroup>
|
|
</div>
|
|
</template>
|
|
|
|
<style scoped>
|
|
.toast-stack {
|
|
position: fixed;
|
|
bottom: 1.5rem;
|
|
right: 1.5rem;
|
|
display: flex;
|
|
flex-direction: column;
|
|
gap: 0.75rem;
|
|
z-index: 9999;
|
|
max-height: 50dvh;
|
|
overflow-y: scroll;
|
|
scrollbar-width: none;
|
|
}
|
|
|
|
.toast-stack::-webkit-scrollbar { display: none; }
|
|
.toast-card { flex-shrink: 0; }
|
|
|
|
.toast-enter-from { opacity: 0; transform: translateX(100%); }
|
|
.toast-leave-to { opacity: 0; transform: translateX(100%); }
|
|
.toast-enter-active,
|
|
.toast-leave-active { transition: all 0.3s ease; }
|
|
</style>
|