import * as React from "react"; import { cx } from "@emotion/css"; import { TransitionGroup, Transition } from "react-transition-group"; import { ToastProps } from "./Toast"; import { toaster, preTransitionStyle, transitionStyles } from "../style"; import { margin, marginAt, listReset } from "../../shared/styles/styleUtils"; export const DEFAULT_DELAY_TIME = 3000; const MARGINAL_DELAY = 1000; const animationDuration = 300; type Toast = React.ReactElement; interface ToasterProps { /** * Timer before toast is auto-dismissed */ dismissTime?: number; /** * Human-readable selector used for writing tests */ "data-cy"?: string; /** * Allows custom styling */ className?: string; /** * Children should use the Toast component */ children?: Toast | Toast[]; } const Toaster = ({ dismissTime = DEFAULT_DELAY_TIME, children, className, "data-cy": dataCy = "toaster" }: ToasterProps) => { const timeouts = React.useRef([]); const [toasts, setToasts] = React.useState([]); const dismissToast = (toastToDismiss: Toast) => { if (toastToDismiss && toastToDismiss.props.onDismiss) { toastToDismiss.props.onDismiss(); } setToasts(toasts => toasts.filter(toast => toastToDismiss.props.id !== toast.props.id) ); }; const restartTimeouts = () => { toasts.forEach((toast: Toast, index: number) => { if (toast.props.autodismiss) { timeouts.current.push( window.setTimeout( () => { dismissToast(toast); }, dismissTime + MARGINAL_DELAY * index ) ); } }); }; const clearTimeouts = () => { timeouts.current.forEach(clearTimeout); }; React.useEffect(() => { return () => { clearTimeouts(); }; }, []); React.useEffect(() => { setToasts(toasts => { const toastChildren = React.Children.toArray(children) as Toast[]; const childIds = toastChildren.map(toast => toast.props.id); const currentIds = toasts.map(toast => toast.props.id); if ( !currentIds.every(e => childIds.includes(e)) || !currentIds.length || childIds.length !== currentIds.length ) { return toastChildren; } return toasts; }); }, [children]); React.useEffect(() => { restartTimeouts(); }, [toasts]); return (
    {toasts.map((toast: Toast) => { const handleDismiss = () => { dismissToast(toast); }; return ( {state => (
  1. {React.cloneElement(toast, { dismissToast: handleDismiss })}
  2. )}
    ); })}
); }; export default React.memo(Toaster);