import { readonly, ref } from 'vue' const toasts = ref([]) let toastSeq = 0 const TOAST_EXIT_MS = 220 function clearToastTimer(toast) { if (toast?.timerId) { window.clearTimeout(toast.timerId) toast.timerId = 0 } } function removeToast(id) { toasts.value = toasts.value.filter((toast) => toast.id !== id) } function dismissToast(id) { const target = toasts.value.find((toast) => toast.id === id) if (!target || target.isClosing) return clearToastTimer(target) target.isClosing = true target.timerId = window.setTimeout(() => removeToast(id), TOAST_EXIT_MS) } function showToast(message, { type = 'info', duration = 2600 } = {}) { if (!message) return '' const duplicated = toasts.value.find((toast) => toast.message === message && toast.type === type && !toast.isClosing) if (duplicated) { duplicated.count = (duplicated.count || 1) + 1 clearToastTimer(duplicated) if (duration > 0) { duplicated.timerId = window.setTimeout(() => dismissToast(duplicated.id), duration) } toasts.value = [...toasts.value] return duplicated.id } const id = `toast-${++toastSeq}` const nextToast = { id, message, type, count: 1, isClosing: false, timerId: 0 } toasts.value = [...toasts.value, nextToast] if (duration > 0) { nextToast.timerId = window.setTimeout(() => dismissToast(id), duration) } return id } export function useToast() { return { toasts: readonly(toasts), dismissToast, showToast, success: (message, options = {}) => showToast(message, { type: 'success', ...options }), error: (message, options = {}) => showToast(message, { type: 'error', ...options }), info: (message, options = {}) => showToast(message, { type: 'info', ...options }), } }