65 lines
2.1 KiB
JavaScript
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>
|
|
);
|
|
}
|