All files UncontrolledActivated.tsx

100% Statements 65/65
96.67% Branches 29/30
100% Functions 8/8
100% Lines 53/53

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 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 1533x                   3x 3x 3x 3x         3x                                 3x 48x 96x 48x 48x 96x 96x 48x 96x 96x 96x 96x 96x   48x 48x 48x 48x 48x     48x 2x 2x       48x 3x 2x 2x       48x 4x   2x 1x         48x     48x     48x 10x 10x   10x 10x         48x 15x       15x 15x 5x           48x     48x   48x                 48x                           48x                               3x  
import React, {
  FC,
  ReactNode,
  RefObject,
  memo,
  useCallback,
  useEffect,
  useRef,
  useState
} from 'react'
import { createPortal } from 'react-dom'
import useEvent from 'react-use/lib/useEvent'
import useWindowSize from 'react-use/lib/useWindowSize'
import {
  getModalContentStyle,
  getModalOverlayStyle,
  pseudoParentEl
} from './helpers'
import './styles.css'
 
interface Props {
  children: ReactNode
  closeText?: string
  onUnload: () => void
  onLoad: () => void
  overlayBgColorEnd?: string
  overlayBgColorStart?: string
  parentRef: RefObject<HTMLElement>
  portalEl?: HTMLElement
  scrollableEl?: HTMLElement | Window
  transitionDuration?: number
  zoomMargin?: number
  zoomZindex?: number
}
 
const UncontrolledActivated: FC<Props> = ({
  children,
  closeText = 'Unzoom Image',
  onUnload,
  onLoad,
  overlayBgColorEnd = 'rgba(255, 255, 255, 0.95)',
  overlayBgColorStart = 'rgba(255, 255, 255, 0)',
  parentRef,
  portalEl = document.body,
  scrollableEl = window,
  transitionDuration = 300,
  zoomMargin = 0,
  zoomZindex = 2147483647
}: Props) => {
  const btnRef = useRef<HTMLButtonElement>(null)
  const [, forceUpdate] = useState<number>(0)
  const [isLoaded, setIsLoaded] = useState<boolean>(false)
  const [isUnloading, setIsUnloading] = useState<boolean>(false)
  const { width: innerWidth, height: innerHeight } = useWindowSize()
 
  // on click, begin unloading
  const handleClick = useCallback(e => {
    e.preventDefault()
    setIsUnloading(true)
  }, [])
 
  // on escape, begin unloading
  const handleKeyDown = useCallback(e => {
    if (e.key === 'Escape' || e.keyCode === 27) {
      e.stopPropagation()
      setIsUnloading(true)
    }
  }, [])
 
  const handleScroll = useCallback(() => {
    forceUpdate(n => n + 1)
 
    if (!isUnloading) {
      setIsUnloading(true)
    }
  }, [isUnloading])
 
  // listen for keydown on the document
  useEvent('keydown', handleKeyDown, document)
 
  // listen for scroll and close
  useEvent('scroll', handleScroll, scrollableEl)
 
  // set loaded on mount and focus
  useEffect(() => {
    setIsLoaded(true)
    onLoad()
 
    Eif (btnRef.current) {
      btnRef.current.focus({ preventScroll: true })
    }
  }, [onLoad])
 
  // if unloading, tell parent that we're all done here after Nms
  useEffect(() => {
    const unloadTimeout = isUnloading
      ? setTimeout(onUnload, transitionDuration)
      : null
 
    return (): void => {
      if (unloadTimeout) {
        clearTimeout(unloadTimeout)
      }
    }
  }, [isUnloading, onUnload, transitionDuration])
 
  // use parent element or fake one if it's not yet loaded
  const parentEl = parentRef.current || pseudoParentEl
 
  // get parent item's dimensions
  const { height, left, top, width } = parentEl.getBoundingClientRect()
 
  const overlayStyle = getModalOverlayStyle({
    isLoaded,
    isUnloading,
    overlayBgColorEnd,
    overlayBgColorStart,
    transitionDuration,
    zoomZindex
  })
 
  const contentStyle = getModalContentStyle({
    height,
    isLoaded,
    innerHeight,
    innerWidth,
    isUnloading,
    left,
    originalTransform: parentEl.style.transform,
    top,
    transitionDuration,
    width,
    zoomMargin
  })
 
  return createPortal(
    <div aria-modal data-rmiz-overlay role="dialog" style={overlayStyle}>
      <div data-rmiz-modal-content style={contentStyle}>
        {children}
      </div>
      <button
        aria-label={closeText}
        data-rmiz-btn-close
        onClick={handleClick}
        ref={btnRef}
      />
    </div>,
    portalEl
  )
}
 
export default memo(UncontrolledActivated)