All files / src/features/Merchants/MerchantSidePanel/hooks useDrawOnCanvas.tsx

69.49% Statements 41/59
37.5% Branches 9/24
62.5% Functions 5/8
80% Lines 40/50

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                        3x                 4x 4x 4x   4x 1x 1x 1x 1x 1x 1x 1x 1x   1x 1x 1x 1x 1x 1x 1x     4x 1x 1x 1x 1x 1x       4x 1x   1x 1x 1x     4x                     4x 1x   1x 1x 1x                         4x             4x   4x                      
import { palette } from "@palette";
import React, { useCallback, useEffect, useRef, useState } from "react";
 
type TMousePosition = {
  x: number;
  y: number;
};
 
type DrawingEvent =
  | React.MouseEvent<HTMLCanvasElement>
  | React.TouchEvent<HTMLCanvasElement>;
 
const useDrawOnCanvas = ({
  height,
  width,
  onSave,
}: {
  height: number;
  width: number;
  onSave?: (canvas: HTMLCanvasElement) => void;
}) => {
  const canvasRef = useRef<HTMLCanvasElement>(null);
  const contextRef = useRef<CanvasRenderingContext2D | null>(null);
  const [isDrawing, setIsDrawing] = useState(false);
 
  useEffect(() => {
    const canvas = canvasRef.current;
    Iif (!canvas) return;
    canvas.width = width * 2;
    canvas.height = height * 2;
    canvas.style.width = `${width}px`;
    canvas.style.height = `${height}px`;
    canvas.style.background = "#fff";
    canvas.style.borderRadius = "12px";
 
    const context = canvas.getContext("2d");
    Iif (!context) return;
    context.scale(2, 2);
    context.lineCap = "round";
    context.strokeStyle = palette.neutral[80];
    context.lineWidth = 3;
    contextRef.current = context;
  }, []);
 
  const onDrawStart = useCallback((event: DrawingEvent) => {
    const coordinates = getCoordinates(event);
    Eif (coordinates) {
      setIsDrawing(true);
      contextRef.current?.beginPath();
      contextRef.current?.moveTo(coordinates.x, coordinates.y);
    }
  }, []);
 
  const onDrawEnd = useCallback(() => {
    Iif (!canvasRef.current || !contextRef.current) return;
 
    contextRef.current.closePath();
    Eif (onSave) onSave(canvasRef.current);
    setIsDrawing(false);
  }, []);
 
  const onDraw = useCallback(
    (event: DrawingEvent) => {
      if (!isDrawing) return;
 
      const newPosition = getCoordinates(event);
      if (!newPosition) return;
      drawLine(newPosition);
    },
    [isDrawing],
  );
 
  const getCoordinates = (event: any) => {
    Iif (!canvasRef.current) return null;
 
    Eif (["mousedown", "mouseup", "mousemove"].includes(event.type)) {
      const { offsetX, offsetY } = event.nativeEvent;
      return {
        x: offsetX,
        y: offsetY,
      };
    }
    event.stopPropagation();
    const rect = canvasRef.current.getBoundingClientRect();
    return {
      x: event.touches[0].pageX - rect.x,
      y: event.touches[0].pageY - rect.y,
    };
  };
 
  const drawLine = (newPosition: TMousePosition) => {
    if (!canvasRef.current || !contextRef.current) return;
 
    contextRef.current?.lineTo(newPosition.x, newPosition.y);
    contextRef.current?.stroke();
  };
 
  const clear = () => contextRef.current?.clearRect(0, 0, width, height);
 
  return {
    canvasRef,
    onDrawStart,
    onDraw,
    onDrawEnd,
    isDrawing,
    clear,
  };
};
 
export default useDrawOnCanvas;