react-zoom-pan-pinch icon indicating copy to clipboard operation
react-zoom-pan-pinch copied to clipboard

[help] How to prevent click event propagation when clicking on an image inside TransformComponent?

Open mfyuu opened this issue 1 year ago • 0 comments

Hi, thank you for maintaining this amazing library! I'm using react-zoom-pan-pinch in my Next.js project, and I encountered an issue with event propagation.

demo

I want to close the modal when the user clicks outside the image area. I'm trying it out with alerts first. I think I only need to prevent the event from propagating when the user clicks on the image, but it's not working. If I comment out the TransformWrapper and TransformComponent, it works as intended. How can I achieve this while still using the TransformWrapper and TransformComponent?

import type { Dispatch, FC, SetStateAction } from "react";
import {
  Dialog,
  DialogContent,
  DialogDescription,
  DialogHeader,
  DialogTitle,
} from "@/components/ui/dialog";
import {
  TransformWrapper,
  TransformComponent,
  useControls,
} from "react-zoom-pan-pinch";
import { DialogClose } from "@radix-ui/react-dialog";
import { Minus, Plus, SearchX, X } from "lucide-react";
import { cn } from "@/lib/utils";

type Props = {
  open: boolean;
  onOpenChange: Dispatch<SetStateAction<boolean>>;
};

const PreviewModal: FC<Props> = ({ open, onOpenChange }) => {
  return (
    <Dialog open={open} onOpenChange={onOpenChange}>
      <DialogContent
        className="h-screen min-w-full flex items-center justify-center p-0 border-none bg-transparent"
        onClick={() => alert("clicked")}
      >
        <div className="w-full h-full relative">
          <DialogHeader className="absolute top-0 left-0 bg-black w-full z-10 opacity-50 h-14 text-white flex">
            <div className="flex items-center justify-between px-4 my-auto">
              <div>
                <DialogTitle>title</DialogTitle>
                <DialogDescription>description</DialogDescription>
              </div>
              <DialogClose className="rounded-sm opacity-70 ring-offset-background transition-opacity hover:opacity-100 disabled:pointer-events-none data-[state=open]:bg-accent data-[state=open]:text-muted-foreground">
                <X className="h-4 w-4" />
                <span className="sr-only">Close</span>
              </DialogClose>
            </div>
          </DialogHeader>

          <TransformWrapper minScale={0.5}>
            <MyControl className="absolute bottom-4 left-1/2 transform -translate-x-1/2 z-10" />
            <TransformComponent
              contentStyle={{
                width: "100%",
                height: "100%",
              }}
              wrapperStyle={{
                width: "100%",
                height: "100%",
              }}
            >
              <div className="w-[calc(100%-24rem)] h-[calc(100%-12rem)] mx-auto my-auto flex bg-red-400">
                <img
                  src="https://placehold.jp/300x200.png"
                  alt="Preview"
                  className="max-w-full max-h-full object-contain mx-auto"
                  onClick={(e) => e.stopPropagation()}
                />
              </div>
            </TransformComponent>
          </TransformWrapper>
        </div>
      </DialogContent>
    </Dialog>
  );
};

export default PreviewModal;

type MyControlProps = {
  className?: string;
};

const MyControl: FC<MyControlProps> = ({ className }) => {
  const { zoomIn, zoomOut, resetTransform } = useControls();

  return (
    <div
      className={cn(
        "inline-flex items-center gap-4 rounded-full bg-black/40 p-1 shadow-md",
        className
      )}
    >
      <button
        type="button"
        onClick={() => zoomOut()}
        className="rounded-full p-2 text-white"
      >
        <Minus />
      </button>
      <button
        type="button"
        onClick={() => resetTransform()}
        className="rounded-full p-2 text-white"
      >
        <SearchX />
      </button>
      <button
        type="button"
        onClick={() => zoomIn()}
        className="rounded-full p-2 text-white"
      >
        <Plus />
      </button>
    </div>
  );
};

If there is any information you are missing, please don't hesitate to let me know. I will provide it immediately.

mfyuu avatar Dec 06 '24 15:12 mfyuu