115 lines
3.2 KiB
Vue
115 lines
3.2 KiB
Vue
<script setup lang="ts">
|
|
// import
|
|
|
|
import type { ToastOptions } from "~/composables/global/useToast";
|
|
import { useToast } from "~/composables/global/useToast";
|
|
|
|
// type
|
|
|
|
type Props = {
|
|
id: number;
|
|
message: string;
|
|
options: ToastOptions;
|
|
};
|
|
|
|
// props
|
|
|
|
const props = defineProps<Props>();
|
|
const { id, options } = toRefs(props);
|
|
|
|
// state
|
|
|
|
const { destroyToast } = useToast();
|
|
|
|
const open = ref(true);
|
|
|
|
// methods
|
|
|
|
const onSwipeEnd = () => {
|
|
setTimeout(() => {
|
|
destroyToast(id.value);
|
|
}, 1000);
|
|
};
|
|
|
|
// computed
|
|
|
|
const statusIcon = computed(() => {
|
|
const status = options.value.status;
|
|
|
|
switch (status) {
|
|
case "success":
|
|
return {
|
|
name: "ci:check-circle",
|
|
class: "**:fill-success-500 [filter:drop-shadow(0_0_20px_var(--color-success-500))]",
|
|
};
|
|
case "error":
|
|
return {
|
|
name: "ci:circle-exclamation",
|
|
class: "**:fill-danger-500 [filter:drop-shadow(0_0_20px_var(--color-danger-500))]",
|
|
};
|
|
case "info":
|
|
return {
|
|
name: "ci:circle-info",
|
|
class: "**:fill-cyan-500 [filter:drop-shadow(0_0_20px_var(--color-cyan-500))]",
|
|
};
|
|
case "warning":
|
|
return {
|
|
name: "ci:octagon-exclamation",
|
|
class: "**:fill-warning-500 [filter:drop-shadow(0_0_10px_var(--color-warning-500))]",
|
|
};
|
|
default:
|
|
return {
|
|
name: "ci:circle-info",
|
|
class: "**:fill-slate-500 [filter:drop-shadow(0_0_10px_var(--color-slate-500))]",
|
|
};
|
|
}
|
|
});
|
|
|
|
// watch
|
|
|
|
watch(
|
|
() => open.value,
|
|
(value) => {
|
|
if (!value) onSwipeEnd();
|
|
}
|
|
);
|
|
|
|
// lifecycle
|
|
|
|
onMounted(() => {
|
|
setTimeout(() => {
|
|
open.value = false;
|
|
}, options.value.duration ?? 4000);
|
|
});
|
|
</script>
|
|
|
|
<template>
|
|
<ToastRoot
|
|
:duration="options.duration ?? 4000"
|
|
@swipeEnd="onSwipeEnd"
|
|
v-model:open="open"
|
|
class="w-full bg-white shadow-md justify-items-start shadow-black/3 border-[0.5px] flex flex-col border-slate-300 p-4 gap-x-[15px] items-center data-[state=open]:animate-toast-in data-[state=closed]:animate-toast-hide data-[swipe=move]:translate-x-[var(--reka-toast-swipe-move-x)] data-[swipe=cancel]:translate-x-0 data-[swipe=cancel]:transition-[transform_200ms_ease-out] data-[swipe=end]:animate-toast-out"
|
|
:class="options.description ? 'rounded-150' : 'rounded-full'"
|
|
>
|
|
<ToastTitle
|
|
:class="[{ 'mb-1': options.description }]"
|
|
class="w-full justify-items-start font-medium text-slate-600 text-sm flex items-center justify-between gap-2"
|
|
>
|
|
<Icon
|
|
:name="statusIcon.name"
|
|
:class="statusIcon.class"
|
|
size="24"
|
|
/>
|
|
<span class="text-start">{{ message }}</span>
|
|
</ToastTitle>
|
|
<ToastDescription
|
|
v-if="options.description"
|
|
as-child
|
|
>
|
|
<div class="text-slate-400 typo-p-xs font-medium flex items-center justify-end w-full">
|
|
{{ options.description }}
|
|
</div>
|
|
</ToastDescription>
|
|
</ToastRoot>
|
|
</template>
|