{
  "name": "image-viewer-basic",
  "type": "registry:component",
  "dependencies": [
    "@radix-ui/react-dialog",
    "lucide-react",
    "react-zoom-pan-pinch"
  ],
  "files": [
    {
      "type": "registry:component",
      "content": "\"use client\";\n\nimport { cn } from \"@/lib/utils\";\nimport {\n  Dialog,\n  DialogClose,\n  DialogContent,\n  DialogDescription,\n  DialogOverlay,\n  DialogPortal,\n  DialogTitle,\n  DialogTrigger,\n} from \"@radix-ui/react-dialog\";\nimport { MinusCircle, PlusCircle, X } from \"lucide-react\";\nimport { TransformComponent, TransformWrapper } from \"react-zoom-pan-pinch\";\n\nconst DEFAULT_PLACEHOLDER_URL =\n  \"https://raw.githubusercontent.com/stackzero-labs/ui/refs/heads/main/public/placeholders/headphone-2.jpg\";\n\ninterface ImageViewerProps {\n  className?: string;\n  classNameImageViewer?: string;\n  classNameThumbnailViewer?: string;\n  imageTitle?: string;\n  imageUrl: string;\n  thumbnailUrl?: string;\n  placeholderUrl?: string;\n  Placeholder?: React.ComponentType<{ className?: string }>;\n  showControls?: boolean;\n}\n\nconst ImageViewer_Basic = ({\n  className,\n  classNameImageViewer,\n  classNameThumbnailViewer,\n  imageTitle,\n  imageUrl,\n  placeholderUrl = DEFAULT_PLACEHOLDER_URL,\n  showControls = true,\n  thumbnailUrl,\n}: ImageViewerProps) => {\n  const handleImgError = (event: React.SyntheticEvent<HTMLImageElement>) => {\n    console.error(\"Image failed to load\", event.currentTarget.src);\n    event.currentTarget.src = placeholderUrl;\n  };\n\n  return (\n    <Dialog>\n      <DialogTrigger asChild>\n        <div className={cn(\"cursor-pointer\", className)}>\n          {/* You can swap this with your preferred image optization technique, like using  next/image */}\n          <img\n            src={thumbnailUrl || imageUrl}\n            alt={`${imageTitle ?? \"Image\"} - Preview`}\n            width=\"100%\"\n            className={cn(\n              \"h-auto w-full rounded-lg object-contain transition-opacity hover:opacity-90\",\n              classNameThumbnailViewer\n            )}\n            onError={handleImgError}\n          />\n        </div>\n      </DialogTrigger>\n      <DialogPortal>\n        <DialogOverlay className=\"fixed inset-0 z-50 bg-black/80\" />\n        <DialogContent className=\"bg-background fixed inset-0 z-50 flex flex-col items-center justify-center p-0\">\n          <DialogTitle className=\"sr-only\">{imageTitle || \"Image\"}</DialogTitle>\n          <DialogDescription className=\"sr-only\">\n            {imageTitle || \"Image\"}\n          </DialogDescription>\n          <div className=\"relative flex h-screen w-screen items-center justify-center\">\n            <TransformWrapper\n              initialScale={1}\n              initialPositionX={0}\n              initialPositionY={0}\n            >\n              {({ zoomIn, zoomOut }) => (\n                <>\n                  <TransformComponent>\n                    {/* You can swap this with your preferred image optization technique, like using  next/image */}\n                    <img\n                      src={imageUrl}\n                      alt={`${imageTitle ?? \"Image\"} - Full`}\n                      className={cn(\n                        \"max-h-[90vh] max-w-[90vw] object-contain\",\n                        classNameImageViewer\n                      )}\n                      onError={handleImgError}\n                    />\n                  </TransformComponent>\n                  {showControls && (\n                    <div className=\"absolute bottom-4 left-1/2 z-10 flex -translate-x-1/2 gap-2\">\n                      <button\n                        onClick={() => zoomOut()}\n                        className=\"cursor-pointer rounded-full bg-black/50 p-2 text-white transition-colors hover:bg-black/70\"\n                        aria-label=\"Zoom out\"\n                      >\n                        <MinusCircle className=\"size-6\" />\n                      </button>\n                      <button\n                        onClick={() => zoomIn()}\n                        className=\"cursor-pointer rounded-full bg-black/50 p-2 text-white transition-colors hover:bg-black/70\"\n                        aria-label=\"Zoom in\"\n                      >\n                        <PlusCircle className=\"size-6\" />\n                      </button>\n                    </div>\n                  )}\n                </>\n              )}\n            </TransformWrapper>\n            <DialogClose asChild>\n              <button\n                className=\"absolute top-4 right-4 z-10 cursor-pointer rounded-full bg-black/50 p-2 text-white transition-colors hover:bg-black/70\"\n                aria-label=\"Close\"\n              >\n                <X className=\"size-6\" />\n              </button>\n            </DialogClose>\n          </div>\n        </DialogContent>\n      </DialogPortal>\n    </Dialog>\n  );\n};\n\nexport default ImageViewer_Basic;\n",
      "path": "/components/commerce-ui/components/image-viewer/basic/image-viewer-basic.tsx",
      "target": "components/commerce-ui/image-viewer-basic.tsx"
    }
  ]
}