All files / src/hooks/common/images useZoomImage.tsx

0% Statements 0/31
0% Branches 0/19
0% Functions 0/6
0% Lines 0/29

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                                                                                                                                                                 
import { SxProps } from "@mui/material";
import { Dispatch, MouseEvent, SetStateAction, useState } from "react";
 
type Action = Dispatch<SetStateAction<boolean>> | (() => void) | (() => any);
 
export const useZoomImage = (
  setIsZooming?: Action,
  { safeElements }: { safeElements?: string | Array<string> } = {},
) => {
  const [enableZoom, setEnableZoom] = useState<boolean>(false);
  const [zoom, setZoom] = useState<number>(1);
  const [x, setX] = useState<number>(0);
  const [y, setY] = useState<number>(0);
 
  const zoomer = (event: MouseEvent<HTMLDivElement>) => {
    // Get the dimensions and position of the image container
    const { left, top, width, height } =
      event.currentTarget.getBoundingClientRect();
 
    // Horizontal and vertical offset of the mouse pointer from the container's top-left corner
    const offsetX = event.clientX - left;
    const offsetY = event.clientY - top;
 
    // Relative position of the mouse pointer within the container
    setX(offsetX / width);
    setY(offsetY / height);
 
    setZoom(3);
    // Trigger action if it exists
    setIsZooming && setIsZooming(true);
  };
 
  const handleClick = (event: MouseEvent<HTMLDivElement>) => {
    setEnableZoom((v) => !v);
    if (zoom === 1) {
      zoomer(event);
    } else {
      setZoom(1);
      setIsZooming && setIsZooming(false);
    }
  };
 
  const handleMouseMove = (event: MouseEvent<HTMLDivElement>) => {
    zoomer(event);
  };
 
  const handleMouseLeave = (e: MouseEvent) => {
    const el = e.relatedTarget as HTMLDivElement;
    if (el.id && safeElements?.includes(el.id)) return;
    setZoom(1);
    setIsZooming && setIsZooming(false), setEnableZoom(false);
  };
 
  const parentElZoomStyleProps: SxProps = {
    position: "relative",
    overflow: "hidden",
    cursor: zoom == 1 ? "zoom-in" : "zoom-out",
  };
 
  const zoomedElStyleProps: SxProps = {
    position: "absolute",
    top: 0,
    left: 0,
    maxWidth: "initial",
    transform: `scale(${zoom})`,
    // Set the transform origin to the mouse position within the container
    // Multiplication by 100 is to convert from decimal to percentage
    transformOrigin: `${x * 100}% ${y * 100}%`,
    // Apply a smooth transition effect to the transformation
    transition: "transform 0.3s ease",
  };
 
  return {
    parentElZoomStyleProps,
    zoomedElStyleProps,
    handleMouseLeave: enableZoom ? handleMouseLeave : undefined,
    handleMouseMove: enableZoom ? handleMouseMove : undefined,
    handleClick,
  };
};