import { useTimeout, useUpdateEffect } from '@chakra-ui/hooks'; import { isFunction, __DEV__ } from '@chakra-ui/utils'; import ReachAlert from '@reach/alert'; import { motion, useIsPresent, Variants } from 'framer-motion'; import * as React from 'react'; import { ToastOptions } from './toast.types'; import { getToastStyle } from './toast.utils'; /** * @todo After Gerrit refactors this implementation, allow users to change the toast transition * direction from a `ToastProvider` component. * * Here's an API example: * * ```jsx * ; * ``` */ const toastMotionVariants: Variants = { initial: props => { const { position } = props; const dir = ['top', 'bottom'].includes(position) ? 'y' : 'x'; let factor = ['top-right', 'bottom-right'].includes(position) ? 1 : -1; if (position === 'bottom') factor = 1; return { opacity: 0, [dir]: factor * 24, }; }, animate: { opacity: 1, y: 0, x: 0, scale: 1, transition: { duration: 0.4, ease: [0.4, 0, 0.2, 1], }, }, exit: { opacity: 0, scale: 0.85, transition: { duration: 0.2, ease: [0.4, 0, 1, 1], }, }, }; export interface ToastProps extends ToastOptions {} export const Toast: React.FC = props => { const { id, message, onCloseComplete, onRequestRemove, requestClose = false, position = 'bottom', duration = 5000, } = props; const [delay, setDelay] = React.useState(duration); const isPresent = useIsPresent(); useUpdateEffect(() => { if (!isPresent) { onCloseComplete?.(); } }, [isPresent]); useUpdateEffect(() => { setDelay(duration); }, [duration]); const onMouseEnter = () => setDelay(null); const onMouseLeave = () => setDelay(duration); const close = () => { if (isPresent) onRequestRemove(); }; React.useEffect(() => { if (isPresent && requestClose) { onRequestRemove(); } }, [isPresent, requestClose, onRequestRemove]); useTimeout(close, delay); const style = React.useMemo(() => getToastStyle(position), [position]); return ( {isFunction(message) ? message({ id, onClose: close }) : message} ); }; if (__DEV__) { Toast.displayName = 'Toast'; }