dekk-app/react-mops

View on GitHub
packages/react-mops/src/hooks/mouse-event-hooks.ts

Summary

Maintainability
D
1 day
Test Coverage
import React from "react";
import {Mops} from "../types";
import {coordinatesToDeg, degToRad, getBoundingBox, to360, withRotation} from "../utils";

/**
 * Mousemove hook
 * @param {Mops.MouseHandler} onMouseUp
 * @param {Mops.MouseHandler} onMouseMove
 * @param {number} scale
 * @param {Mops.RotationModel} [rotation]
 */
export const useMouseMove = (
    onMouseUp: Mops.MouseHandler,
    onMouseMove: Mops.MouseHandler,
    scale: number,
    rotation?: Mops.RotationModel
) => {
    const [isDown, setDown] = React.useState(false);
    const [initialPosition, setInitialPosition] = React.useState<Mops.PositionModel>({
        x: 0,
        y: 0
    });

    const getRotatedPosition = React.useCallback(
        (event: MouseEvent): Mops.PositionModel => {
            const newPosition = {
                x: (event.clientX - initialPosition.x) / scale,
                y: (event.clientY - initialPosition.y) / scale
            };
            return rotation ? withRotation(newPosition.x, newPosition.y, rotation.z) : newPosition;
        },
        [initialPosition, scale, rotation]
    );

    const handleMouseUp = React.useCallback(
        (event: MouseEvent) => {
            if (isDown) {
                event.preventDefault();
                const rotatedPosition = getRotatedPosition(event);
                setDown(false);
                onMouseUp(rotatedPosition, event.altKey, event.shiftKey, event);
            }
        },
        [setDown, onMouseUp, getRotatedPosition]
    );

    const handleMouseMove = React.useCallback(
        (event: MouseEvent) => {
            if (isDown) {
                event.preventDefault();
                const rotatedPosition = getRotatedPosition(event);
                onMouseMove(rotatedPosition, event.altKey, event.shiftKey, event);
            }
        },
        [onMouseMove, getRotatedPosition]
    );

    React.useEffect(() => {
        document.addEventListener("mouseleave", handleMouseUp);
        document.addEventListener("mouseup", handleMouseUp);
        return () => {
            document.removeEventListener("mouseleave", handleMouseUp);
            document.removeEventListener("mouseup", handleMouseUp);
        };
    }, [handleMouseUp]);

    React.useEffect(() => {
        document.addEventListener("mousemove", handleMouseMove);
        return () => {
            document.removeEventListener("mousemove", handleMouseMove);
        };
    }, [handleMouseMove]);

    const handleDown = React.useCallback(
        (event: React.MouseEvent<HTMLElement>) => {
            if ((event.target as HTMLElement).tagName !== "INPUT" && event.button !== 2) {
                event.preventDefault();
                setDown(true);
                setInitialPosition({x: event.clientX, y: event.clientY});
            }
        },
        [setInitialPosition, setDown]
    );

    return [isDown, handleDown] as [boolean, (e: React.MouseEvent<HTMLElement>) => void];
};
/**
 *
 * @param onMouseUp
 * @param onMouseMove
 * @param onMouseDown
 */
export const useMouseMoveEvent = (
    onMouseUp: (event: MouseEvent) => void,
    onMouseMove: (event: MouseEvent) => void,
    onMouseDown: (event: React.MouseEvent<HTMLElement>) => void
) => {
    const [isDown, setDown] = React.useState(false);
    const handleMouseUp = React.useCallback(
        (event: MouseEvent) => {
            if (isDown) {
                event.preventDefault();
                setDown(false);
                onMouseUp(event);
            }
        },
        [onMouseUp, setDown]
    );
    const handleFocus = React.useCallback(() => {
        setDown(false);
    }, [setDown]);
    const handleMouseMove = React.useCallback(
        (event: MouseEvent) => {
            if (isDown) {
                event.preventDefault();
                onMouseMove(event);
            }
        },
        [onMouseMove]
    );

    React.useEffect(() => {
        window.addEventListener("focus", handleFocus);
        window.addEventListener("blur", handleFocus);
        return () => {
            document.removeEventListener("focus", handleFocus);
            document.removeEventListener("blur", handleFocus);
        };
    }, [handleFocus]);

    React.useEffect(() => {
        document.addEventListener("mouseleave", handleMouseUp);
        document.addEventListener("mouseup", handleMouseUp);
        return () => {
            document.removeEventListener("mouseleave", handleMouseUp);
            document.removeEventListener("mouseup", handleMouseUp);
        };
    }, [handleMouseUp]);

    React.useEffect(() => {
        document.addEventListener("mousemove", handleMouseMove);
        return () => {
            document.removeEventListener("mousemove", handleMouseMove);
        };
    }, [handleMouseMove]);

    const handleDown = React.useCallback(
        (event: React.MouseEvent<HTMLElement>) => {
            if ((event.target as HTMLElement).tagName !== "INPUT" && event.button !== 2) {
                event.preventDefault();
                setDown(true);
                onMouseDown(event);
            }
        },
        [onMouseDown, setDown]
    );

    return [isDown, handleDown] as [boolean, (e: React.MouseEvent<HTMLElement>) => void];
};
export const useHandleMouseEvent = ({additionalAngle, contentRef, initialRotation, isRotatable}) =>
    React.useCallback(
        (event: React.MouseEvent<HTMLElement> | MouseEvent, init?: boolean) => {
            if (
                !isRotatable ||
                !contentRef ||
                !(contentRef as React.RefObject<HTMLElement>).current
            ) {
                return false;
            }
            const {clientX, clientY} = event;
            const {left, top, width, height} = (contentRef as React.RefObject<
                HTMLElement
            >).current.getBoundingClientRect();
            const pointer = {x: clientX - left, y: clientY - top};
            const center = {x: width / 2, y: height / 2};
            const deg = coordinatesToDeg(pointer, center);
            const newRotationZ = to360(initialRotation.z + (deg - additionalAngle.z));
            const newRotation = (state: Mops.RotationModel) => ({
                x: state.x,
                y: state.y,
                z: init
                    ? to360(initialRotation.z)
                    : event.shiftKey
                    ? Math.round(newRotationZ / 15) * 15
                    : newRotationZ
            });
            return {
                deg,
                rotation: newRotation
            };
        },
        [contentRef, initialRotation, isRotatable]
    );
export const useHandleMouse = ({
    addGuides,
    currentRotation,
    currentSize,
    initialPosition,
    shouldSnap,
    guideRequests,
    guides,
    hideGuides,
    removeGuides,
    showGuides,
    updateGuide
}) =>
    React.useCallback(
        ({x, y}) => {
            const newPosition = {
                x: initialPosition.x + x,
                y: initialPosition.y + y
            };
            return shouldSnap.reduce(
                (model, fn) => ({
                    ...(fn(
                        {
                            position: newPosition,
                            rotation: currentRotation,
                            size: getBoundingBox({
                                ...currentSize,
                                angle: currentRotation.z
                            })
                        },
                        {
                            addGuides,
                            guideRequests,
                            guides,
                            hideGuides,
                            removeGuides,
                            showGuides,
                            updateGuide
                        },
                        model
                    ) as Mops.SnapHandler)
                }),
                newPosition
            ) as Mops.PositionModel;
        },
        [currentSize, currentRotation, initialPosition, shouldSnap, showGuides, hideGuides]
    );