All files / src/gestures/drag use-drag-controls.ts

100% Statements 17/17
50% Branches 1/2
100% Functions 7/7
100% Lines 13/13

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  30x                                                   30x 4x             30x 2x   2x                                   30x                   2x 2x           30x   30x                                                       30x 8x    
import * as React from "react"
import { useConstant } from "../../utils/use-constant"
import {
    VisualElementDragControls,
    DragControlOptions,
} from "./VisualElementDragControls"
 
/**
 * Can manually trigger a drag gesture on one or more `drag`-enabled `motion` components.
 *
 * ```jsx
 * const dragControls = useDragControls()
 *
 * function startDrag(event) {
 *   dragControls.start(event, { snapToCursor: true })
 * }
 *
 * return (
 *   <>
 *     <div onPointerDown={startDrag} />
 *     <motion.div drag="x" dragControls={dragControls} />
 *   </>
 * )
 * ```
 *
 * @public
 */
export class DragControls {
    private componentControls = new Set<VisualElementDragControls>()
 
    /**
     * Subscribe a component's internal `VisualElementDragControls` to the user-facing API.
     *
     * @internal
     */
    subscribe(controls: VisualElementDragControls): () => void {
        this.componentControls.add(controls)
 
        return () => this.componentControls.delete(controls)
    }
 
    /**
     * Start a drag gesture on every `motion` component that has this set of drag controls
     * passed into it via the `dragControls` prop.
     *
     * ```jsx
     * dragControls.start(e, {
     *   snapToCursor: true
     * })
     * ```
     *
     * @param event - PointerEvent
     * @param options - Options
     *
     * @public
     */
    start(
        event:
            | React.MouseEvent
            | React.TouchEvent
            | React.PointerEvent
            | MouseEvent
            | TouchEvent
            | PointerEvent,
        options?: DragControlOptions
    ) {
        this.componentControls.forEach((controls) => {
            controls.start(
                (event as React.MouseEvent).nativeEvent || event,
                options
            )
        })
    }
}
 
const createDragControls = () => new DragControls()
 
/**
 * Usually, dragging is initiated by pressing down on a `motion` component with a `drag` prop
 * and moving it. For some use-cases, for instance clicking at an arbitrary point on a video scrubber, we
 * might want to initiate that dragging from a different component than the draggable one.
 *
 * By creating a `dragControls` using the `useDragControls` hook, we can pass this into
 * the draggable component's `dragControls` prop. It exposes a `start` method
 * that can start dragging from pointer events on other components.
 *
 * ```jsx
 * const dragControls = useDragControls()
 *
 * function startDrag(event) {
 *   dragControls.start(event, { snapToCursor: true })
 * }
 *
 * return (
 *   <>
 *     <div onPointerDown={startDrag} />
 *     <motion.div drag="x" dragControls={dragControls} />
 *   </>
 * )
 * ```
 *
 * @public
 */
export function useDragControls() {
    return useConstant(createDragControls)
}