Files
react/components/toast/toast-container.tsx
unix e3ba8aed41 perf(toast): improve toast component performance
test(toast): update spanshots

style(button): remove unused
2020-05-11 20:35:37 +08:00

71 lines
2.0 KiB
TypeScript

import React, { useMemo, useRef, useState } from 'react'
import { createPortal } from 'react-dom'
import usePortal from '../utils/use-portal'
import useTheme from '../styles/use-theme'
import { useZEITUIContext } from '../utils/use-zeit-ui-context'
import { Toast } from './use-toast'
import ToastItem from './toast-item'
export type ToastWithID = Toast & {
id: string
willBeDestroy?: boolean
cancel: Function
}
const ToastContainer: React.FC<React.PropsWithChildren<{}>> = () => {
const portal = usePortal('toast')
const theme = useTheme()
const [hover, setHover] = useState<boolean>(false)
const timer = useRef<number | undefined>()
const { toasts, updateToastHoverStatus } = useZEITUIContext()
const toastElements = useMemo(
() =>
toasts.map((t, i) => (
<ToastItem index={i} total={toasts.length} toast={t} onHover={hover} key={`toast-${i}`} />
)),
[toasts, hover],
)
const hoverHandler = (onHover: boolean) => {
if (onHover) {
timer.current && clearTimeout(timer.current)
updateToastHoverStatus(() => true)
return setHover(true)
}
timer.current = window.setTimeout(() => {
setHover(false)
updateToastHoverStatus(() => false)
timer.current && clearTimeout(timer.current)
}, 200)
}
if (!portal) return null
if (!toasts || toasts.length === 0) return null
return createPortal(
<div
className={`toast-container ${hover ? 'hover' : ''}`}
onMouseEnter={() => hoverHandler(true)}
onMouseLeave={() => hoverHandler(false)}>
{toastElements}
<style jsx>{`
.toast-container {
position: fixed;
width: 420px;
max-width: 90vw;
bottom: ${theme.layout.gap};
right: ${theme.layout.gap};
z-index: 2000;
transition: all 400ms ease;
box-sizing: border-box;
}
.toast-container.hover {
transform: translate3d(0, -10px, 0);
}
`}</style>
</div>,
portal,
)
}
export default ToastContainer