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}
)
}