Les toasts d'erreur etaient persistants (duration force a 0) et restaient affiches jusqu'a fermeture manuelle, ce qui pouvait empiler des messages obsoletes a l'ecran. Aligne le comportement sur les autres types : duree par defaut 8s (plus que warning a 6s pour laisser le temps de lire). Une erreur critique peut toujours etre rendue persistante en passant explicitement showError(msg, 0).
96 lines
2.1 KiB
TypeScript
96 lines
2.1 KiB
TypeScript
import { ref } from 'vue'
|
|
|
|
export type ToastType = 'success' | 'error' | 'warning' | 'info'
|
|
|
|
export interface Toast {
|
|
id: number
|
|
message: string
|
|
type: ToastType
|
|
visible: boolean
|
|
duration: number
|
|
}
|
|
|
|
const toasts = ref<Toast[]>([])
|
|
const MAX_TOASTS = 3
|
|
let nextId = 1
|
|
|
|
// Anti-doublon : ignore un toast identique affiché dans les 2 dernières secondes
|
|
const recentMessages = new Map<string, number>()
|
|
const DEDUP_WINDOW = 2000
|
|
|
|
export function useToast() {
|
|
const showToast = (message: string, type: ToastType = 'info', duration = 3500): number => {
|
|
const dedupKey = `${type}::${message}`
|
|
const lastShown = recentMessages.get(dedupKey)
|
|
if (lastShown && Date.now() - lastShown < DEDUP_WINDOW) {
|
|
return -1
|
|
}
|
|
recentMessages.set(dedupKey, Date.now())
|
|
|
|
const id = nextId++
|
|
const toast: Toast = {
|
|
id,
|
|
message,
|
|
type,
|
|
visible: true,
|
|
duration,
|
|
}
|
|
|
|
if (toasts.value.length >= MAX_TOASTS) {
|
|
toasts.value.shift()
|
|
}
|
|
|
|
toasts.value.push(toast)
|
|
|
|
if (duration > 0) {
|
|
setTimeout(() => {
|
|
removeToast(id)
|
|
}, duration)
|
|
}
|
|
|
|
return id
|
|
}
|
|
|
|
const showSuccess = (message: string, duration = 5000): number => {
|
|
return showToast(message, 'success', duration)
|
|
}
|
|
|
|
const showError = (message: string, duration = 8000): number => {
|
|
return showToast(message, 'error', duration)
|
|
}
|
|
|
|
const showWarning = (message: string, duration = 6000): number => {
|
|
return showToast(message, 'warning', duration)
|
|
}
|
|
|
|
const showInfo = (message: string, duration = 5000): number => {
|
|
return showToast(message, 'info', duration)
|
|
}
|
|
|
|
const removeToast = (id: number): void => {
|
|
const index = toasts.value.findIndex((toast) => toast.id === id)
|
|
if (index !== -1 && toasts.value[index]) {
|
|
toasts.value[index].visible = false
|
|
setTimeout(() => {
|
|
toasts.value.splice(index, 1)
|
|
}, 300) // Animation duration
|
|
}
|
|
}
|
|
|
|
const clearAll = (): void => {
|
|
toasts.value = []
|
|
recentMessages.clear()
|
|
}
|
|
|
|
return {
|
|
toasts,
|
|
showToast,
|
|
showSuccess,
|
|
showError,
|
|
showWarning,
|
|
showInfo,
|
|
removeToast,
|
|
clearAll,
|
|
}
|
|
}
|