62 lines
1.8 KiB
TypeScript
62 lines
1.8 KiB
TypeScript
"use client";
|
|
|
|
import { useEffect, useState } from "react";
|
|
import { AlertCircle, CheckCircle2, Info } from "lucide-react";
|
|
import { subscribe, ToastItem } from "@/lib/toast";
|
|
|
|
const STYLE: Record<
|
|
ToastItem["type"],
|
|
{ border: string; icon: React.ReactNode }
|
|
> = {
|
|
error: { border: "#e5484d", icon: <AlertCircle size={18} color="#ff8a8d" /> },
|
|
success: { border: "#3fa34d", icon: <CheckCircle2 size={18} color="#7be58a" /> },
|
|
info: { border: "#e9b949", icon: <Info size={18} color="#e9b949" /> },
|
|
};
|
|
|
|
export default function Toaster() {
|
|
const [items, setItems] = useState<ToastItem[]>([]);
|
|
useEffect(() => subscribe(setItems), []);
|
|
|
|
return (
|
|
<div
|
|
style={{
|
|
position: "fixed",
|
|
bottom: 20,
|
|
left: "50%",
|
|
transform: "translateX(-50%)",
|
|
zIndex: 9999,
|
|
display: "flex",
|
|
flexDirection: "column",
|
|
gap: 10,
|
|
alignItems: "center",
|
|
pointerEvents: "none",
|
|
}}
|
|
>
|
|
{items.map((t) => (
|
|
<div
|
|
key={t.id}
|
|
style={{
|
|
pointerEvents: "auto",
|
|
display: "flex",
|
|
alignItems: "center",
|
|
gap: 10,
|
|
maxWidth: "min(90vw, 460px)",
|
|
padding: "12px 16px",
|
|
borderRadius: 12,
|
|
background: "linear-gradient(180deg, #17345c, #0c2848)",
|
|
border: `1px solid ${STYLE[t.type].border}`,
|
|
boxShadow: "0 8px 24px rgba(0,0,0,.5)",
|
|
color: "#eaf0f8",
|
|
fontSize: 14,
|
|
animation: "toastIn .18s ease-out",
|
|
}}
|
|
>
|
|
{STYLE[t.type].icon}
|
|
<span style={{ flex: 1 }}>{t.msg}</span>
|
|
</div>
|
|
))}
|
|
<style>{`@keyframes toastIn{from{opacity:0;transform:translateY(8px)}to{opacity:1;transform:none}}`}</style>
|
|
</div>
|
|
);
|
|
}
|