{
  "name": "parallax-hero-images",
  "type": "registry:ui",
  "dependencies": [
    "motion"
  ],
  "files": [
    {
      "path": "components/ui/parallax-hero-images.tsx",
      "content": "\"use client\";\nimport React, { useEffect, useState, useMemo, useCallback, memo } from \"react\";\nimport {\n  motion,\n  useMotionValue,\n  useSpring,\n  useTransform,\n  MotionValue,\n} from \"motion/react\";\nimport { cn } from \"@/lib/utils\";\n\ntype ImagePosition = {\n  src: string;\n  position:\n    | \"top-left\"\n    | \"top-right\"\n    | \"mid-left\"\n    | \"mid-right\"\n    | \"bottom-left\"\n    | \"bottom-right\"\n    | \"far-left\"\n    | \"far-right\";\n  depth: number;\n  delay: number;\n};\n\nconst positionStyles: Record<\n  ImagePosition[\"position\"],\n  { top: string; left?: string; right?: string }\n> = {\n  \"top-left\": { top: \"8%\", left: \"4%\" },\n  \"top-right\": { top: \"8%\", right: \"4%\" },\n  \"mid-left\": { top: \"38%\", left: \"6%\" },\n  \"mid-right\": { top: \"38%\", right: \"6%\" },\n  \"bottom-left\": { top: \"68%\", left: \"4%\" },\n  \"bottom-right\": { top: \"68%\", right: \"4%\" },\n  \"far-left\": { top: \"52%\", left: \"2%\" },\n  \"far-right\": { top: \"52%\", right: \"2%\" },\n};\n\nconst positionOrder: ImagePosition[\"position\"][] = [\n  \"top-left\",\n  \"top-right\",\n  \"mid-left\",\n  \"mid-right\",\n  \"bottom-left\",\n  \"bottom-right\",\n  \"far-left\",\n  \"far-right\",\n];\n\ntype DepthVariant = \"default\" | \"edge-focus\";\n\nconst depthValuesByVariant: Record<DepthVariant, number[]> = {\n  default: [0.3, 0.35, 0.9, 0.85, 0.4, 0.45, 0.25, 0.2],\n  \"edge-focus\": [0.85, 0.9, 0.3, 0.35, 0.8, 0.85, 0.4, 0.45],\n};\n\nconst SPRING_CONFIG = { damping: 25, stiffness: 120 };\n\nexport interface ParallaxHeroImagesProps {\n  images: string[];\n  className?: string;\n  imageClassName?: string;\n  variant?: DepthVariant;\n}\n\nexport const ParallaxHeroImages = ({\n  images,\n  className,\n  imageClassName,\n  variant = \"default\",\n}: ParallaxHeroImagesProps) => {\n  const mouseX = useMotionValue(0);\n  const mouseY = useMotionValue(0);\n\n  const smoothMouseX = useSpring(mouseX, SPRING_CONFIG);\n  const smoothMouseY = useSpring(mouseY, SPRING_CONFIG);\n\n  const positions = useMemo(() => {\n    const limitedImages = images.slice(0, 8);\n    const depthValues = depthValuesByVariant[variant];\n    return limitedImages.map((src, index) => ({\n      src,\n      position: positionOrder[index],\n      depth: depthValues[index],\n      delay: index * 0.12,\n    }));\n  }, [images, variant]);\n\n  useEffect(() => {\n    const handleMouseMove = (e: MouseEvent) => {\n      const x = (e.clientX / window.innerWidth) * 2 - 1;\n      const y = (e.clientY / window.innerHeight) * 2 - 1;\n      mouseX.set(x);\n      mouseY.set(y);\n    };\n\n    window.addEventListener(\"mousemove\", handleMouseMove);\n    return () => window.removeEventListener(\"mousemove\", handleMouseMove);\n  }, [mouseX, mouseY]);\n\n  return (\n    <div\n      className={cn(\n        \"pointer-events-none absolute inset-0 overflow-hidden\",\n        className,\n      )}\n    >\n      {positions.map((pos, index) => (\n        <ParallaxImage\n          key={`${pos.src}-${index}`}\n          src={pos.src}\n          position={pos.position}\n          depth={pos.depth}\n          delay={pos.delay}\n          imageClassName={imageClassName}\n          smoothMouseX={smoothMouseX}\n          smoothMouseY={smoothMouseY}\n        />\n      ))}\n    </div>\n  );\n};\n\ninterface ParallaxImageProps extends ImagePosition {\n  imageClassName?: string;\n  smoothMouseX: MotionValue<number>;\n  smoothMouseY: MotionValue<number>;\n}\n\nconst ParallaxImage = memo(function ParallaxImage({\n  src,\n  position,\n  depth,\n  delay,\n  imageClassName,\n  smoothMouseX,\n  smoothMouseY,\n}: ParallaxImageProps) {\n  const maxOffset = 40;\n\n  const translateX = useTransform(\n    smoothMouseX,\n    [-1, 1],\n    [-maxOffset * depth, maxOffset * depth],\n  );\n\n  const translateY = useTransform(\n    smoothMouseY,\n    [-1, 1],\n    [-maxOffset * depth, maxOffset * depth],\n  );\n\n  const posStyle = positionStyles[position];\n\n  return (\n    <motion.div\n      className=\"absolute\"\n      style={{\n        top: posStyle.top,\n        left: posStyle.left,\n        right: posStyle.right,\n        x: translateX,\n        y: translateY,\n        zIndex: Math.round(depth * 10),\n      }}\n      initial={{ opacity: 0, filter: \"blur(20px)\", scale: 0.9 }}\n      animate={{ opacity: 1, filter: \"blur(0px)\", scale: 1 }}\n      transition={{\n        duration: 0.8,\n        delay: delay,\n        ease: [0.25, 0.1, 0.25, 1],\n      }}\n    >\n      <img\n        src={src}\n        alt=\"\"\n        loading=\"lazy\"\n        decoding=\"async\"\n        className={cn(\n          \"aspect-4/3 h-20 w-32 rounded-lg object-cover shadow-sm ring-1 ring-black/10 sm:h-40 sm:w-56 md:h-52 md:w-80 dark:ring-white/10\",\n          imageClassName,\n        )}\n      />\n    </motion.div>\n  );\n});\n",
      "type": "registry:ui",
      "target": "components/ui/parallax-hero-images.tsx"
    }
  ],
  "author": "Manu Arora <hi@manuarora.in>",
  "title": "Parallax Hero Images"
}