first commit

This commit is contained in:
2026-08-07 09:40:16 +03:30
commit 0c51b30059
37 changed files with 5146 additions and 0 deletions
+61
View File
@@ -0,0 +1,61 @@
"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>
);
}