function useMouseAndTouchTracker(
  environment,
  handleBlur,
  downshiftElementsRefs,
) {
  const mouseAndTouchTrackersRef = useRef({
    isMouseDown: false,
    isTouchMove: false,
    isTouchEnd: false,
  })

  useEffect(() => {
    if (isReactNative || !environment) {
      return noop
    }

    const downshiftElements = downshiftElementsRefs.map(ref => ref.current)

    function onMouseDown() {
      mouseAndTouchTrackersRef.current.isTouchEnd = false // reset this one.
      mouseAndTouchTrackersRef.current.isMouseDown = true
    }
    function onMouseUp(event) {
      mouseAndTouchTrackersRef.current.isMouseDown = false

      if (
        !targetWithinDownshift(event.target, downshiftElements, environment)
      ) {
        handleBlur()
      }
    }
    function onTouchStart() {
      mouseAndTouchTrackersRef.current.isTouchEnd = false
      mouseAndTouchTrackersRef.current.isTouchMove = false
    }
    function onTouchMove() {
      mouseAndTouchTrackersRef.current.isTouchMove = true
    }
    function onTouchEnd(event) {
      mouseAndTouchTrackersRef.current.isTouchEnd = true

      if (
        !mouseAndTouchTrackersRef.current.isTouchMove &&
        !targetWithinDownshift(
          event.target,
          downshiftElements,
          environment,
          false,
        )
      ) {
        handleBlur()
      }
    }

    environment.addEventListener('mousedown', onMouseDown)
    environment.addEventListener('mouseup', onMouseUp)
    environment.addEventListener('touchstart', onTouchStart)
    environment.addEventListener('touchmove', onTouchMove)
    environment.addEventListener('touchend', onTouchEnd)

    return function cleanup() {
      environment.removeEventListener('mousedown', onMouseDown)
      environment.removeEventListener('mouseup', onMouseUp)
      environment.removeEventListener('touchstart', onTouchStart)
      environment.removeEventListener('touchmove', onTouchMove)
      environment.removeEventListener('touchend', onTouchEnd)
    }
  }, [downshiftElementsRefs, environment, handleBlur])

  return mouseAndTouchTrackersRef.current
}
Tracks mouse and touch events, such as mouseDown, touchMove and touchEnd.

@param {Window} environment The environment to add the event listeners to, for instance window.
@param {() => void} handleBlur The function that is called if mouseDown or touchEnd occured outside the downshiftElements.
@param {Array<{current: HTMLElement}>} downshiftElementsRefs The refs for the elements that should not trigger a blur action from mouseDown or touchEnd.
@returns {{isMouseDown: boolean, isTouchMove: boolean, isTouchEnd: boolean}} The mouse and touch events information, if any of are happening.