import React, { useState, useEffect, useRef, ReactNode } from 'react' import { ToastContext, ToastContextType } from './ToastContext' const ToastContainer = ({ id, children, onClick = null, toast, first = false, }) => { const [fade, setFade] = useState(first) const close = () => toast.close(id) useEffect(() => { if (first) { requestAnimationFrame(() => { setFade(false) }) } const timer = setTimeout(() => { setFade(true) }, 5e3) return () => clearTimeout(timer) }, []) return (
{ close() onClick?.() }} > {children}
) } type PositionStyleProps = { top?: number bottom?: number left?: number right?: number } type Toast = { id: number children: ReactNode } export const ToastProvider = ({ children, position = 'bottom-right', fixed = true, }) => { const [length, setLength] = useState(0) const positionRef = useRef() const positionStyleRef = useRef() const toastsRef = useRef() const toastRef = useRef() if (!toastRef.current) { let count = 0 const listeners = new Set([setLength]) const update = (length) => { listeners.forEach((fn) => fn(length)) } const toast = (child) => { const id = count++ update( toastsRef.current.unshift({ id, children: ( {child} ), }) ) return id } toast.add = toast toast.close = (id?: number) => { if (typeof id === 'number') { const index = toastsRef.current.findIndex( ({ id: toastId }) => toastId === id ) if (index !== -1) { toastsRef.current.splice(index, 1) update(toastsRef.current.length) } } else { toastsRef.current = [] update(0) } } toast.useCount = () => { const [state, setState] = useState(length) useEffect(() => { listeners.add(setState) return () => { listeners.delete(setState) } }, []) return state } toastRef.current = toast toastsRef.current = [] } if (positionRef.current !== position) { positionRef.current = position const [y, x] = position.split('-') const positionStyle: PositionStyleProps = {} if (y === 'bottom') { positionStyle.bottom = 16 } else { positionStyle.top = 16 } if (x === 'left') { positionStyle.left = 16 } else { positionStyle.right = 16 } positionStyleRef.current = positionStyle } const toasts = toastsRef.current.map(({ id, children }, index) => { let y = index * 96 if ('bottom' in positionStyleRef.current) { y *= -1 } return (
{children}
) }) return ( {children} {toasts} ) }