// Hooks import { useEffect, useState } from "react"; // UI import styles from "./Notice.module.css"; import { createPortal } from "react-dom"; // Tools import { type NoticeItem, addFn, removeFn } from "./utils"; import { useHydrated } from "~/hooks/use-hydrated"; import { clsn } from "~/utils"; // Components function Notice() { const [items, setItems] = useState([]); useEffect(() => { const fn = (notice: NoticeItem) => { const key = `${Math.ceil(performance.now())}-${Math.round(Math.random() * 100)}`; setItems((prevItems) => [...prevItems, { ...notice, key }]); if (notice.duration && notice.duration > 0) { setTimeout(() => { onCloseWithKey(key); }, notice.duration); } }; addFn(fn); return () => { removeFn(fn); }; }, []); const onCloseWithKey = (key: string) => { setItems((prevItems) => { const index = prevItems.findIndex((item) => item.key === key); const nextItems = [...prevItems]; if (index > -1) { nextItems.splice(index, 1); } return nextItems; }); } const hydrated = useHydrated(); if (typeof document === "undefined") { return null; } if (!hydrated) { return null; } return createPortal((
{items.map((item) => { const showProgress = !!item.duration && item.duration > 0; return (

{item.title}

{item.content}

{showProgress && (
)}
); })}
), document.body); } export default Notice;