{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "lens",
  "type": "registry:ui",
  "title": "Lens",
  "description": "A interactive component that enables zooming into images, videos and other elements.",
  "dependencies": [
    "motion"
  ],
  "files": [
    {
      "path": "registry/magicui/lens.tsx",
      "content": "\"use client\"\n\nimport React, { useCallback, useMemo, useRef, useState } from \"react\"\nimport { AnimatePresence, motion, useMotionTemplate } from \"motion/react\"\n\ninterface Position {\n  /** The x coordinate of the lens */\n  x: number\n  /** The y coordinate of the lens */\n  y: number\n}\n\ninterface LensProps {\n  /** The children of the lens */\n  children: React.ReactNode\n  /** The zoom factor of the lens */\n  zoomFactor?: number\n  /** The size of the lens */\n  lensSize?: number\n  /** The position of the lens */\n  position?: Position\n  /** The default position of the lens */\n  defaultPosition?: Position\n  /** Whether the lens is static */\n  isStatic?: boolean\n  /** The duration of the animation */\n  duration?: number\n  /** The color of the lens */\n  lensColor?: string\n  /** The aria label of the lens */\n  ariaLabel?: string\n}\n\nexport function Lens({\n  children,\n  zoomFactor = 1.3,\n  lensSize = 170,\n  isStatic = false,\n  position = { x: 0, y: 0 },\n  defaultPosition,\n  duration = 0.1,\n  lensColor = \"black\",\n  ariaLabel = \"Zoom Area\",\n}: LensProps) {\n  if (zoomFactor < 1) {\n    throw new Error(\"zoomFactor must be greater than 1\")\n  }\n  if (lensSize < 0) {\n    throw new Error(\"lensSize must be greater than 0\")\n  }\n\n  const [isHovering, setIsHovering] = useState(false)\n  const [mousePosition, setMousePosition] = useState<Position>(position)\n  const containerRef = useRef<HTMLDivElement>(null)\n\n  const currentPosition = useMemo(() => {\n    if (isStatic) return position\n    if (defaultPosition && !isHovering) return defaultPosition\n    return mousePosition\n  }, [isStatic, position, defaultPosition, isHovering, mousePosition])\n\n  const handleMouseMove = useCallback((e: React.MouseEvent<HTMLDivElement>) => {\n    const rect = e.currentTarget.getBoundingClientRect()\n    setMousePosition({\n      x: e.clientX - rect.left,\n      y: e.clientY - rect.top,\n    })\n  }, [])\n\n  const handleKeyDown = useCallback((e: React.KeyboardEvent) => {\n    if (e.key === \"Escape\") setIsHovering(false)\n  }, [])\n\n  const maskImage = useMotionTemplate`radial-gradient(circle ${\n    lensSize / 2\n  }px at ${currentPosition.x}px ${\n    currentPosition.y\n  }px, ${lensColor} 100%, transparent 100%)`\n\n  const LensContent = useMemo(() => {\n    const { x, y } = currentPosition\n\n    return (\n      <motion.div\n        initial={{ opacity: 0, scale: 0.58 }}\n        animate={{ opacity: 1, scale: 1 }}\n        exit={{ opacity: 0, scale: 0.8 }}\n        transition={{ duration }}\n        className=\"absolute inset-0 overflow-hidden\"\n        style={{\n          maskImage,\n          WebkitMaskImage: maskImage,\n          transformOrigin: `${x}px ${y}px`,\n          zIndex: 50,\n        }}\n      >\n        <div\n          className=\"absolute inset-0\"\n          style={{\n            transform: `scale(${zoomFactor})`,\n            transformOrigin: `${x}px ${y}px`,\n          }}\n        >\n          {children}\n        </div>\n      </motion.div>\n    )\n  }, [currentPosition, maskImage, zoomFactor, children, duration])\n\n  return (\n    <div\n      ref={containerRef}\n      className=\"relative z-20 overflow-hidden rounded-xl\"\n      onMouseEnter={() => setIsHovering(true)}\n      onMouseLeave={() => setIsHovering(false)}\n      onMouseMove={handleMouseMove}\n      onKeyDown={handleKeyDown}\n      role=\"region\"\n      aria-label={ariaLabel}\n      tabIndex={0}\n    >\n      {children}\n      {isStatic || defaultPosition ? (\n        LensContent\n      ) : (\n        <AnimatePresence mode=\"popLayout\">\n          {isHovering && LensContent}\n        </AnimatePresence>\n      )}\n    </div>\n  )\n}\n",
      "type": "registry:ui"
    }
  ]
}