All files / src/gestures use-tap-gesture.ts

89.19% Statements 33/37
78.57% Branches 33/42
83.33% Functions 5/6
94.12% Lines 32/34

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 8333x   33x 33x 33x 33x 33x 33x             33x 72x 72x 72x 72x 72x   72x 72x 72x     57x 57x       19x 19x 19x 19x       19x           18x                       21x   21x 21x   21x               21x   21x     72x           72x    
import { useRef } from "react"
import { EventInfo } from "../events/types"
import { isNodeOrChild } from "./utils/is-node-or-child"
import { addPointerEvent, usePointerEvent } from "../events/use-pointer-event"
import { useUnmountEffect } from "../utils/use-unmount-effect"
import { pipe } from "popmotion"
import { AnimationType } from "../render/utils/types"
import { isDragActive } from "./drag/utils/lock"
import { FeatureProps } from "../motion/features/types"
 
/**
 * @param handlers -
 * @internal
 */
export function useTapGesture({
    onTap,
    onTapStart,
    onTapCancel,
    whileTap,
    visualElement,
}: FeatureProps) {
    const hasPressListeners = onTap || onTapStart || onTapCancel || whileTap
    const isPressing = useRef(false)
    const cancelPointerEndListeners = useRef<Function | null>(null)
 
    function removePointerEndListener() {
        cancelPointerEndListeners.current?.()
        cancelPointerEndListeners.current = null
    }
 
    function checkPointerEnd() {
        removePointerEndListener()
        isPressing.current = false
        visualElement.animationState?.setActive(AnimationType.Tap, false)
        return !isDragActive()
    }
 
    function onPointerUp(event: PointerEvent, info: EventInfo) {
        if (!checkPointerEnd()) return
 
        /**
         * We only count this as a tap gesture if the event.target is the same
         * as, or a child of, this component's element
         */
        !isNodeOrChild(visualElement.getInstance(), event.target as Element)
            ? onTapCancel?.(event, info)
            : onTap?.(event, info)
    }
 
    function onPointerCancel(event: PointerEvent, info: EventInfo) {
        if (!checkPointerEnd()) return
 
        onTapCancel?.(event, info)
    }
 
    function onPointerDown(event: PointerEvent, info: EventInfo) {
        removePointerEndListener()
 
        Iif (isPressing.current) return
        isPressing.current = true
 
        cancelPointerEndListeners.current = pipe(
            addPointerEvent(window, "pointerup", onPointerUp),
            addPointerEvent(window, "pointercancel", onPointerCancel)
        )
 
        /**
         * Ensure we trigger animations before firing event callback
         */
        visualElement.animationState?.setActive(AnimationType.Tap, true)
 
        onTapStart?.(event, info)
    }
 
    usePointerEvent(
        visualElement,
        "pointerdown",
        hasPressListeners ? onPointerDown : undefined
    )
 
    useUnmountEffect(removePointerEndListener)
}