feat: toast package (#1073)
This commit is contained in:
115
packages/toast/src/ToastTimer.tsx
Normal file
115
packages/toast/src/ToastTimer.tsx
Normal file
@@ -0,0 +1,115 @@
|
||||
import { useCallback, useEffect, useRef, FunctionComponent } from 'react'
|
||||
import { Toast } from './Toast'
|
||||
import { Toast as ToastPropType } from './types'
|
||||
import { ToastType } from './enums'
|
||||
import { dismissToast } from './toastStore'
|
||||
|
||||
type Props = {
|
||||
toast: ToastPropType
|
||||
index: number
|
||||
}
|
||||
|
||||
const getDefaultForAutoClose = (hasActions: boolean, type: ToastType) => {
|
||||
return !hasActions && ![ToastType.Loading, ToastType.Progress].includes(type)
|
||||
}
|
||||
|
||||
const getDefaultToastDuration = (type: ToastType) => (type === ToastType.Error ? 8000 : 4000)
|
||||
|
||||
export const ToastTimer: FunctionComponent<Props> = ({ toast, index }) => {
|
||||
const toastElementRef = useRef<HTMLDivElement>(null)
|
||||
const toastTimerIdRef = useRef<number>()
|
||||
|
||||
const hasActions = Boolean(toast.actions?.length)
|
||||
const shouldAutoClose = toast.autoClose ?? getDefaultForAutoClose(hasActions, toast.type)
|
||||
const duration = toast.duration ?? getDefaultToastDuration(toast.type)
|
||||
|
||||
const startTimeRef = useRef(duration)
|
||||
const remainingTimeRef = useRef(duration)
|
||||
|
||||
const dismissToastOnEnd = useCallback(() => {
|
||||
dismissToast(toast.id)
|
||||
}, [toast.id])
|
||||
|
||||
const clearTimer = useCallback(() => {
|
||||
if (toastTimerIdRef.current) {
|
||||
clearTimeout(toastTimerIdRef.current)
|
||||
}
|
||||
}, [])
|
||||
|
||||
const pauseTimer = useCallback(() => {
|
||||
clearTimer()
|
||||
remainingTimeRef.current -= Date.now() - startTimeRef.current
|
||||
}, [clearTimer])
|
||||
|
||||
const resumeTimer = useCallback(() => {
|
||||
startTimeRef.current = Date.now()
|
||||
clearTimer()
|
||||
toastTimerIdRef.current = window.setTimeout(dismissToastOnEnd, remainingTimeRef.current)
|
||||
}, [clearTimer, dismissToastOnEnd])
|
||||
|
||||
const handleMouseEnter = useCallback(() => {
|
||||
pauseTimer()
|
||||
}, [pauseTimer])
|
||||
|
||||
const handleMouseLeave = useCallback(() => {
|
||||
resumeTimer()
|
||||
}, [resumeTimer])
|
||||
|
||||
const handlePageVisibility = useCallback(() => {
|
||||
if (document.visibilityState === 'hidden') {
|
||||
pauseTimer()
|
||||
} else {
|
||||
resumeTimer()
|
||||
}
|
||||
}, [pauseTimer, resumeTimer])
|
||||
|
||||
const handlePageFocus = useCallback(() => {
|
||||
resumeTimer()
|
||||
}, [resumeTimer])
|
||||
|
||||
const handlePageBlur = useCallback(() => {
|
||||
pauseTimer()
|
||||
}, [pauseTimer])
|
||||
|
||||
useEffect(() => {
|
||||
clearTimer()
|
||||
|
||||
if (shouldAutoClose) {
|
||||
resumeTimer()
|
||||
}
|
||||
|
||||
const toastElement = toastElementRef.current
|
||||
if (toastElement) {
|
||||
toastElement.addEventListener('mouseenter', handleMouseEnter)
|
||||
toastElement.addEventListener('mouseleave', handleMouseLeave)
|
||||
}
|
||||
document.addEventListener('visibilitychange', handlePageVisibility)
|
||||
window.addEventListener('focus', handlePageFocus)
|
||||
window.addEventListener('blur', handlePageBlur)
|
||||
|
||||
return () => {
|
||||
clearTimer()
|
||||
if (toastElement) {
|
||||
toastElement.removeEventListener('mouseenter', handleMouseEnter)
|
||||
toastElement.removeEventListener('mouseleave', handleMouseLeave)
|
||||
}
|
||||
document.removeEventListener('visibilitychange', handlePageVisibility)
|
||||
window.removeEventListener('focus', handlePageFocus)
|
||||
window.removeEventListener('blur', handlePageBlur)
|
||||
}
|
||||
}, [
|
||||
clearTimer,
|
||||
dismissToastOnEnd,
|
||||
duration,
|
||||
handleMouseEnter,
|
||||
handleMouseLeave,
|
||||
handlePageBlur,
|
||||
handlePageFocus,
|
||||
handlePageVisibility,
|
||||
resumeTimer,
|
||||
shouldAutoClose,
|
||||
toast.id,
|
||||
])
|
||||
|
||||
return <Toast toast={toast} index={index} ref={toastElementRef} />
|
||||
}
|
||||
Reference in New Issue
Block a user