84 lines
2.2 KiB
Vue
84 lines
2.2 KiB
Vue
<template>
|
||
<ToastRoot :default-open="open" :duration="duration" class="ToastRoot bg-light-10 dark:bg-dark-10 border border-light-30 dark:border-dark-30" v-model:open="model">
|
||
<div class="grid grid-cols-8 p-3">
|
||
<ToastTitle v-if="title" class="font-semibold text-xl col-span-7 text-light-70 dark:text-dark-70" asChild><h4>{{ title }}</h4></ToastTitle>
|
||
<ToastClose v-if="closeable" aria-label="Close" class="text-2xl -translate-y-1/2 translate-x-1/2 cursor-pointer"><span aria-hidden>×</span></ToastClose>
|
||
<ToastDescription v-if="content" class="text-md col-span-8 text-light-70 dark:text-dark-70" asChild><span>{{ content }}</span></ToastDescription>
|
||
</div>
|
||
<TimerProgress v-if="timer" shape="thin" :delay="duration" class="mb-0 mt-0 w-full" />
|
||
</ToastRoot>
|
||
|
||
<ToastViewport class="fixed bottom-0 right-0 flex flex-col p-6 gap-2 max-w-[512px] z-50 outline-none min-w-72" />
|
||
</template>
|
||
|
||
<script setup lang="ts">
|
||
const model = defineModel<boolean>();
|
||
|
||
const { closeable = true, duration, title, content, timer = true } = defineProps<{
|
||
closeable?: boolean
|
||
duration?: number
|
||
title?: string
|
||
content?: string
|
||
timer?: boolean
|
||
open?: boolean
|
||
}>();
|
||
const timeout = ref<NodeJS.Timeout>();
|
||
|
||
watch(model, (value) => {
|
||
if(duration)
|
||
{
|
||
if(value === true)
|
||
{
|
||
timeout.value = setTimeout(() => model.value = false, duration);
|
||
}
|
||
else
|
||
{
|
||
clearTimeout(timeout.value);
|
||
}
|
||
}
|
||
})
|
||
</script>
|
||
|
||
<style>
|
||
.ToastRoot[data-state='open'] {
|
||
animation: slideIn .15s cubic-bezier(0.16, 1, 0.3, 1);
|
||
}
|
||
.ToastRoot[data-state='closed'] {
|
||
animation: hide .1s ease-in;
|
||
}
|
||
.ToastRoot[data-swipe='move'] {
|
||
transform: translateX(var(--radix-toast-swipe-move-x));
|
||
}
|
||
.ToastRoot[data-swipe='cancel'] {
|
||
transform: translateX(0);
|
||
transition: transform .2s ease-out;
|
||
}
|
||
.ToastRoot[data-swipe='end'] {
|
||
animation: swipeRight .1s ease-out;
|
||
}
|
||
|
||
@keyframes hide {
|
||
from {
|
||
opacity: 1;
|
||
}
|
||
to {
|
||
opacity: 0;
|
||
}
|
||
}
|
||
@keyframes slideIn {
|
||
from {
|
||
transform: translateX(calc(100% + var(--viewport-padding)));
|
||
}
|
||
to {
|
||
transform: translateX(0);
|
||
}
|
||
}
|
||
@keyframes swipeRight {
|
||
from {
|
||
transform: translateX(var(--radix-toast-swipe-end-x));
|
||
}
|
||
to {
|
||
transform: translateX(100%);
|
||
}
|
||
}
|
||
</style> |