import React from "react"; import { render } from "react-dom"; import Stack from "./Stack"; import { Island } from "./Island"; import "./Toast.css"; import { close } from "./icons"; const TOAST_TIMEOUT = 7000; function ToastRenderer(props: { toasts: Map; onCloseRequest: (id: number) => void; }) { return ( {[...props.toasts.entries()].map(([id, toast]) => (
{toast}
))}
); } let toastsRootNode: HTMLDivElement; function getToastsRootNode() { return toastsRootNode || (toastsRootNode = initToastsRootNode()); } function initToastsRootNode() { const div = window.document.createElement("div"); document.body.appendChild(div); div.className = "Toast__container"; return div; } function renderToasts( toasts: Map, onCloseRequest: (id: number) => void, ) { render( , getToastsRootNode(), ); } let incrementalId = 0; function getToastId() { return incrementalId++; } class ToastManager { private toasts = new Map(); private timers = new Map(); public push(message: React.ReactNode, shiftAfterMs: number) { const id = getToastId(); this.toasts.set(id, message); if (isFinite(shiftAfterMs)) { const handle = window.setTimeout(() => this.pop(id), shiftAfterMs); this.timers.set(id, handle); } this.render(); } private pop = (id: number) => { const handle = this.timers.get(id); if (handle) { window.clearTimeout(handle); this.timers.delete(id); } this.toasts.delete(id); this.render(); }; private render() { renderToasts(this.toasts, this.pop); } } let toastManagerInstance: ToastManager; function getToastManager(): ToastManager { return toastManagerInstance ?? (toastManagerInstance = new ToastManager()); } export function push(message: React.ReactNode, manualClose = false) { const toastManager = getToastManager(); toastManager.push(message, manualClose ? Infinity : TOAST_TIMEOUT); }