sw-fe/src/components/ui/ToastHost.jsx

65 lines
2.1 KiB
JavaScript

import React, { useEffect, useState } from "react";
export function toast({ title, message, kind = "info", ms = 4500 } = {}) {
window.dispatchEvent(new CustomEvent("sw:toast", { detail: { title, message, kind, ms } }));
}
export default function ToastHost() {
const [items, setItems] = useState([]);
useEffect(() => {
const onToast = (e) => {
const id = Math.random().toString(16).slice(2);
const it = { id, ...e.detail };
setItems((p) => [it, ...p].slice(0, 3));
setTimeout(() => setItems((p) => p.filter((x) => x.id !== id)), it.ms || 4500);
};
window.addEventListener("sw:toast", onToast);
return () => window.removeEventListener("sw:toast", onToast);
}, []);
if (!items.length) return null;
return (
<div style={{
position: "fixed",
top: 14,
left: "50%",
transform: "translateX(-50%)",
zIndex: 999999,
width: "min(560px, calc(100vw - 24px))",
display: "grid",
gap: 10
}}>
{items.map((t) => (
<div key={t.id} style={{
backdropFilter: "blur(14px)",
background: "rgba(10, 20, 40, 0.75)",
border: "1px solid rgba(255,255,255,0.12)",
borderRadius: 14,
padding: "12px 14px",
boxShadow: "0 10px 30px rgba(0,0,0,0.35)",
color: "white"
}}>
<div style={{ display: "flex", justifyContent: "space-between", gap: 12, alignItems: "center" }}>
<div style={{ fontWeight: 800, letterSpacing: 0.2 }}>
{t.title || (t.kind === "error" ? "Erreur" : "Info")}
</div>
<button onClick={() => setItems((p) => p.filter((x) => x.id !== t.id))}
style={{
border: "0",
background: "rgba(255,255,255,0.10)",
color: "white",
width: 28,
height: 28,
borderRadius: 10,
cursor: "pointer"
}}></button>
</div>
{t.message ? <div style={{ marginTop: 6, opacity: 0.9, lineHeight: 1.25 }}>{t.message}</div> : null}
</div>
))}
</div>
);
}