40 lines
1.4 KiB
TypeScript
40 lines
1.4 KiB
TypeScript
|
|
type ToastKind = 'success' | 'reject' | 'info'
|
||
|
|
|
||
|
|
let container: HTMLElement | null = null
|
||
|
|
let hideTimer: number | undefined
|
||
|
|
|
||
|
|
function ensureContainer(): HTMLElement {
|
||
|
|
if (container && document.body.contains(container)) return container
|
||
|
|
container = document.createElement('div')
|
||
|
|
container.className =
|
||
|
|
'fixed top-20 left-1/2 -translate-x-1/2 z-[100] flex items-center gap-2 px-4 py-2 rounded-full text-xs font-bold shadow-2xl border border-slate-700 bg-slate-800 text-slate-100 transition-all duration-300 opacity-0 pointer-events-none'
|
||
|
|
document.body.append(container)
|
||
|
|
return container
|
||
|
|
}
|
||
|
|
|
||
|
|
const ICONS: Record<ToastKind, string> = {
|
||
|
|
success: 'fa-solid fa-circle-check text-emerald-400',
|
||
|
|
reject: 'fa-solid fa-circle-xmark text-rose-400',
|
||
|
|
info: 'fa-solid fa-circle-info text-sky-400',
|
||
|
|
}
|
||
|
|
|
||
|
|
export function showToast(message: string, kind: ToastKind = 'success'): void {
|
||
|
|
const el = ensureContainer()
|
||
|
|
el.replaceChildren()
|
||
|
|
|
||
|
|
const icon = document.createElement('i')
|
||
|
|
icon.className = ICONS[kind]
|
||
|
|
const text = document.createElement('span')
|
||
|
|
text.textContent = message
|
||
|
|
el.append(icon, text)
|
||
|
|
|
||
|
|
el.classList.remove('opacity-0', '-translate-y-2', 'pointer-events-none')
|
||
|
|
el.classList.add('opacity-100', 'translate-y-0')
|
||
|
|
|
||
|
|
window.clearTimeout(hideTimer)
|
||
|
|
hideTimer = window.setTimeout(() => {
|
||
|
|
el.classList.remove('opacity-100', 'translate-y-0')
|
||
|
|
el.classList.add('opacity-0', '-translate-y-2', 'pointer-events-none')
|
||
|
|
}, 2600)
|
||
|
|
}
|