Files
BetiX/resources/js/components/ui/Notification.vue
Dolo 0280278978
Some checks failed
linter / quality (push) Has been cancelled
tests / ci (8.4) (push) Has been cancelled
tests / ci (8.5) (push) Has been cancelled
Initialer Laravel Commit für BetiX
2026-04-04 18:01:50 +02:00

68 lines
2.8 KiB
Vue

<script setup>
import { ref, nextTick, onMounted, watch } from 'vue';
const props = defineProps({
toasts: {
type: Array,
required: true
}
});
const emit = defineEmits(['close']);
const closeToast = (id) => {
emit('close', id);
};
// Re-run lucide icons when toasts change
watch(() => props.toasts, () => {
nextTick(() => {
if (window.lucide) window.lucide.createIcons();
});
}, { deep: true });
onMounted(() => {
nextTick(() => {
if (window.lucide) window.lucide.createIcons();
});
});
</script>
<template>
<teleport to="body">
<div id="notif-container">
<div v-for="toast in toasts" :key="toast.id"
:class="['toast', toast.type, { active: toast.active, 'fly-out': toast.flyingOut }]"
@click="closeToast(toast.id)">
<div class="toast-icon"><i :data-lucide="toast.icon"></i></div>
<div style="flex:1">
<div class="toast-title">{{ toast.title }}</div>
<div class="toast-desc">{{ toast.desc }}</div>
</div>
<div class="toast-progress" :style="{ transform: `scaleX(${toast.progress/100})` }"></div>
</div>
</div>
</teleport>
</template>
<style scoped>
#notif-container { position: fixed; top: 85px; right: 20px; display: flex; flex-direction: column; gap: 12px; z-index: 5000; pointer-events: auto; backdrop-filter: none; }
.toast { background: rgba(13,13,13,0.95); border: 1px solid #222; padding: 14px 20px; border-radius: 12px; display: flex; align-items: center; gap: 14px; width: 300px; transform: translateX(120%); transition: 0.4s cubic-bezier(0.2, 0, 0, 1); box-shadow: 0 10px 40px rgba(0,0,0,0.9); position: relative; overflow: hidden; cursor: pointer; backdrop-filter: none; }
.toast.active { transform: translateX(0); }
.toast.fly-out { transform: translate(100px, -200px) scale(0); opacity: 0; }
.toast-icon { padding: 8px; border-radius: 10px; color: white; display: flex; align-items: center; justify-content: center; }
.toast-progress { position: absolute; bottom: 0; left: 0; height: 3px; background: rgba(255,255,255,0.1); width: 100%; transform-origin: left; }
.toast:hover { transform: scale(1.05) translateX(-5px); z-index: 1001; }
.toast.green { border-left: 4px solid var(--green); }
.toast.green .toast-icon { background: rgba(0,255,157,0.1); color: var(--green); }
.toast.magenta { border-left: 4px solid var(--magenta); }
.toast.magenta .toast-icon { background: rgba(255,0,122,0.1); color: var(--magenta); }
.toast-title { font-size: 11px; font-weight: 900; color: #fff; letter-spacing: 0.5px; margin-bottom: 2px; text-transform: uppercase; }
.toast-desc { font-size: 11px; color: #bbb; font-weight: 500; }
:global(:root) {
--green: #00ff9d;
--magenta: #ff007a;
}
</style>