All files / atom/toast/src index.js

61.36% Statements 27/44
55.55% Branches 20/36
53.84% Functions 7/13
64.1% Lines 25/39

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124              1x                     2x 2x   2x 2x 2x   2x         2x           2x 2x     2x 2x 2x         2x     2x   2x 2x           2x             2x     2x 2x                           2x   2x                       1x   1x                                              
import {useCallback, useEffect, useRef, useState} from 'react'
 
import cx from 'classnames'
import PropTypes from 'prop-types'
 
import {AUTO_CLOSE_TIMES, BASE_CLASS, EFFECT_DELAY, POSITIONS} from './config.js'
 
const AtomToast = ({
  autoClose = true,
  autoCloseTime = AUTO_CLOSE_TIMES.medium,
  children,
  effect = true,
  globalClose = false,
  iconClose = null,
  onClose,
  position = POSITIONS.topRight,
  show: showFromProps = true
}) => {
  const [show, setShow] = useState(showFromProps)
  const [delay, setDelay] = useState(true)
 
  const autoCloseTimeout = useRef()
  const delayTimeout = useRef()
  const toastRef = useRef()
 
  const containerClassName = cx(`${BASE_CLASS}-container`, `${BASE_CLASS}-position--${position}`, {
    [`${BASE_CLASS}-effect--${position}`]: effect,
    [`${BASE_CLASS}-effect--hide`]: effect && delay
  })
 
  const handleClose = useCallback(() => {
    if (effect) setDelay(true)
    setShow(false)
    if (!effect && typeof onClose === 'function') onClose()
  }, [effect, onClose])
 
  useEffect(() => {
    setShow(showFromProps)
  }, [showFromProps])
 
  useEffect(() => {
    Eif (autoClose) {
      autoCloseTimeout.current = setTimeout(() => {
        handleClose()
      }, autoCloseTime)
    }
 
    return () => clearTimeout(autoCloseTimeout.current)
  }, [autoClose, autoCloseTime, handleClose])
 
  useEffect(() => {
    // open effect
    Eif (effect && show && delay) {
      delayTimeout.current = setTimeout(() => {
        setDelay(false)
      }, EFFECT_DELAY.open)
    }
 
    // close effect
    Iif (effect && !show) {
      delayTimeout.current = setTimeout(() => {
        setDelay(false)
        typeof onClose === 'function' && onClose()
      }, EFFECT_DELAY.close)
    }
 
    return () => clearTimeout(delayTimeout.current)
  }, [delay, effect, onClose, show])
 
  useEffect(() => {
    Iif (globalClose) {
      const handleClickOutside = e => {
        if (!toastRef.current.contains(e.target)) {
          handleClose()
        }
      }
      window.addEventListener('mousedown', handleClickOutside)
 
      return () => {
        window.removeEventListener('mousedown', handleClickOutside)
      }
    }
  }, [globalClose, handleClose])
 
  Iif (!show && !delay) return null
 
  return (
    <div className={containerClassName}>
      <div className={BASE_CLASS} ref={toastRef}>
        <div onClick={handleClose} className={`${BASE_CLASS}-icon`}>
          {iconClose}
        </div>
        {children}
      </div>
    </div>
  )
}
 
AtomToast.displayName = 'AtomToast'
 
AtomToast.propTypes = {
  /** Enable/disable auto close */
  autoClose: PropTypes.bool,
  /** Auto close times: 'short' (3000s), 'medium' (6000s), 'long' (9000s) */
  autoCloseTime: PropTypes.oneOf(Object.keys(AUTO_CLOSE_TIMES)),
  /** Toast content */
  children: PropTypes.node.isRequired,
  /** Enable/disable toast transition */
  effect: PropTypes.bool,
  /** Custom close icon  */
  iconClose: PropTypes.node,
  /** On close callback */
  onClose: PropTypes.func,
  /** Positions: 'top-left', 'top', 'top-right', 'bottom-left', 'bottom', 'bottom-right' */
  position: PropTypes.oneOf(Object.values(POSITIONS)),
  /** Show/hide notification */
  show: PropTypes.bool,
  /** Enable/disable global close */
  globalClose: PropTypes.bool
}
 
export {POSITIONS as atomToastPositions, AUTO_CLOSE_TIMES as atomToastAutoCloseTimes}
export default AtomToast