{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "image-zoom-effect",
  "type": "registry:block",
  "title": "Image zoom effect",
  "description": "Image zoom effect",
  "files": [
    {
      "path": "components/usages/imagezoomeffectusage.tsx",
      "content": "import { Image, ImageZoom } from '@/registry/open-source/image-zoom-effect';\nimport NextImage from 'next/image';\n\nexport default function ImageZoomDemo() {\n    return (\n        <ImageZoom className=\"rounded-2xl\">\n            <Image\n                src=\"/itjustworks.jpg\"\n                alt=\"Aerial View of the Great Lake of Almaty in Kazakhstan\"\n                as={NextImage}\n                width={3840}\n                height={2160}\n            />\n        </ImageZoom>\n    );\n};",
      "type": "registry:block",
      "target": "~/example.tsx"
    },
    {
      "path": "components/usages/imagezoomeffectusage.tsx",
      "content": "import { Image, ImageZoom } from '@/registry/open-source/image-zoom-effect';\nimport NextImage from 'next/image';\n\nexport default function ImageZoomDemo() {\n    return (\n        <ImageZoom className=\"rounded-2xl\">\n            <Image\n                src=\"/itjustworks.jpg\"\n                alt=\"Aerial View of the Great Lake of Almaty in Kazakhstan\"\n                as={NextImage}\n                width={3840}\n                height={2160}\n            />\n        </ImageZoom>\n    );\n};",
      "type": "registry:ui"
    },
    {
      "path": "registry/open-source/image-zoom-effect.tsx",
      "content": "'use client';\n\nimport * as React from 'react';\nimport { motion, type Transition } from 'motion/react';\n\n// Credit:\n// https://animate-ui.com/docs/primitives/effects/image-zoom\n\ntype ImageZoomProps = {\n    zoomScale?: number;\n    transition?: Transition;\n    style?: React.CSSProperties;\n    zoomOnClick?: boolean;\n    zoomOnHover?: boolean;\n    disabled?: boolean;\n    width?: React.CSSProperties['width'];\n    height?: React.CSSProperties['height'];\n    children: React.ReactElement;\n} & React.ComponentProps<'div'>;\n\nexport function ImageZoom({\n    children,\n    zoomScale = 3,\n    transition = { type: 'spring', stiffness: 200, damping: 28 },\n    style,\n    zoomOnClick = true,\n    zoomOnHover = true,\n    disabled = false,\n    width = '100%',\n    height = '100%',\n    ...props\n}: ImageZoomProps) {\n    const [isZoomed, setIsZoomed] = React.useState(false);\n    const containerRef = React.useRef<HTMLDivElement | null>(null);\n    const isTouch =\n        typeof window !== 'undefined' && matchMedia('(pointer: coarse)').matches;\n\n    const setOriginFromEvent = React.useCallback(\n        (e: React.MouseEvent | React.TouchEvent) => {\n            if (!containerRef.current) return;\n            const rect = containerRef.current.getBoundingClientRect();\n            let clientX = 0;\n            let clientY = 0;\n\n            if ('touches' in e && e.touches[0]) {\n                clientX = e.touches[0].clientX;\n                clientY = e.touches[0].clientY;\n            } else if ('clientX' in e) {\n                clientX = (e as React.MouseEvent).clientX;\n                clientY = (e as React.MouseEvent).clientY;\n            }\n\n            const x = Math.max(0, Math.min(rect.width, clientX - rect.left));\n            const y = Math.max(0, Math.min(rect.height, clientY - rect.top));\n            const child = containerRef.current\n                .firstElementChild as HTMLElement | null;\n            if (!child) return;\n            child.style.transformOrigin = `${x}px ${y}px`;\n        },\n        [],\n    );\n\n    const handleMouseEnter = React.useCallback(() => {\n        if (disabled || isTouch || !zoomOnHover) return;\n        setIsZoomed(true);\n    }, [disabled, isTouch, zoomOnHover]);\n\n    const handleMouseLeave = React.useCallback(() => {\n        if (disabled || isTouch || !zoomOnHover) return;\n        setIsZoomed(false);\n    }, [disabled, isTouch, zoomOnHover]);\n\n    const handleMouseMove = React.useCallback(\n        (e: React.MouseEvent) => {\n            if (disabled || isTouch) return;\n            setOriginFromEvent(e);\n        },\n        [disabled, isTouch, setOriginFromEvent],\n    );\n\n    const handleClick = React.useCallback(\n        (e: React.MouseEvent) => {\n            if (disabled || !zoomOnClick) return;\n            setOriginFromEvent(e);\n            setIsZoomed((v) => !v);\n        },\n        [disabled, zoomOnClick, setOriginFromEvent],\n    );\n\n    const handleTouchStart = React.useCallback(\n        (e: React.TouchEvent) => {\n            if (disabled) return;\n            setOriginFromEvent(e);\n            if (zoomOnClick) setIsZoomed((v) => !v);\n            else setIsZoomed(true);\n        },\n        [disabled, zoomOnClick, setOriginFromEvent],\n    );\n\n    const handleTouchMove = React.useCallback(\n        (e: React.TouchEvent) => {\n            if (disabled) return;\n            setOriginFromEvent(e);\n        },\n        [disabled, setOriginFromEvent],\n    );\n\n    const handleTouchEnd = React.useCallback(() => {\n        if (disabled) return;\n        if (!zoomOnClick) setIsZoomed(false);\n    }, [disabled, zoomOnClick]);\n\n    return (\n        <div\n            ref={containerRef}\n            style={{\n                overflow: 'hidden',\n                position: 'relative',\n                width,\n                height,\n                touchAction: 'manipulation',\n                cursor: disabled ? 'default' : isZoomed ? 'zoom-out' : 'zoom-in',\n                ...style,\n            }}\n            onMouseEnter={handleMouseEnter}\n            onMouseLeave={handleMouseLeave}\n            onMouseMove={handleMouseMove}\n            onClick={handleClick}\n            onTouchStart={handleTouchStart}\n            onTouchMove={handleTouchMove}\n            onTouchEnd={handleTouchEnd}\n            role=\"img\"\n            {...props}\n        >\n            <motion.div\n                animate={{ scale: disabled ? 1 : isZoomed ? zoomScale : 1 }}\n                transition={transition}\n                style={{\n                    width: '100%',\n                    height: '100%',\n                    display: 'flex',\n                    alignItems: 'center',\n                    justifyContent: 'center',\n                    willChange: 'transform',\n                }}\n            >\n                {children}\n            </motion.div>\n        </div>\n    );\n}\n\ntype ImageProps<T extends React.ElementType = 'img'> = {\n    objectFit?: React.CSSProperties['objectFit'];\n    as?: T;\n} & React.ComponentProps<T>;\n\nexport function Image<T extends React.ElementType = 'img'>({\n    objectFit = 'cover',\n    as: Component = 'img',\n    ...props\n}: ImageProps<T>) {\n    return (\n        <Component\n            draggable={false}\n            style={{\n                width: '100%',\n                height: '100%',\n                objectFit,\n                userSelect: 'none',\n                pointerEvents: 'none',\n            }}\n            {...props}\n        />\n    );\n}",
      "type": "registry:ui"
    }
  ]
}