12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697 |
- 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<number, React.ReactNode>;
- onCloseRequest: (id: number) => void;
- }) {
- return (
- <Stack.Col gap={2} align="center">
- {[...props.toasts.entries()].map(([id, toast]) => (
- <Island key={id} padding={3}>
- <div className="Toast">
- <div className="Toast__content">{toast}</div>
- <button
- className="Toast__close"
- onClick={() => props.onCloseRequest(id)}
- >
- {close}
- </button>
- </div>
- </Island>
- ))}
- </Stack.Col>
- );
- }
- 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<number, React.ReactNode>,
- onCloseRequest: (id: number) => void,
- ) {
- render(
- <ToastRenderer toasts={toasts} onCloseRequest={onCloseRequest} />,
- getToastsRootNode(),
- );
- }
- let incrementalId = 0;
- function getToastId() {
- return incrementalId++;
- }
- class ToastManager {
- private toasts = new Map<number, React.ReactNode>();
- private timers = new Map<number, number>();
- 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);
- }
|