{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "draggable-avatar",
  "type": "registry:ui",
  "title": "Draggable Avatar",
  "description": "It displays a circular, draggable avatar with customizable size and style.",
  "author": "Ahdeetai <https://aditya.is-cool.dev>",
  "registryDependencies": [],
  "dependencies": ["motion"],
  "files": [
    {
      "type": "registry:ui",
      "path": "components/ui/draggable-avatar.tsx",
      "content": "'use client';\nimport React, { useState, useRef, useEffect } from 'react';\nimport { motion, PanInfo } from 'motion/react';\n\ninterface DraggableAvatarProps {\n  image?: string;\n  borderColor?: string;\n  range?: number;\n  size?: number;\n}\n\nexport default function DraggableAvatar({\n  image = 'https://cdn.pixabay.com/photo/2023/06/26/04/38/ai-generated-8088680_1280.jpg',\n  borderColor = '#60A5FA',\n  range = 300,\n  size = 100,\n}: DraggableAvatarProps) {\n  const [isHolding, setIsHolding] = useState(false);\n  const [constraints, setConstraints] = useState({\n    left: -range,\n    right: range,\n    top: -range,\n    bottom: range,\n  });\n  const containerRef = useRef<HTMLDivElement>(null);\n  const motionRef = useRef<HTMLDivElement>(null);\n  const lineLength = 45;\n  const lineLengthHolding = 30;\n  const markerSize = 12;\n\n  useEffect(() => {\n    const updateConstraints = () => {\n      if (containerRef.current) {\n        const rect = containerRef.current.getBoundingClientRect();\n        const maxX = Math.min(range, window.innerWidth - rect.left - size);\n        const maxY = Math.min(\n          range,\n          window.innerHeight - rect.top - size - lineLength - markerSize,\n        );\n        const minX = Math.max(-range, -rect.left);\n        const minY = Math.max(-range, -rect.top);\n\n        setConstraints({\n          left: minX,\n          right: maxX,\n          top: minY,\n          bottom: maxY,\n        });\n      }\n    };\n\n    updateConstraints();\n    window.addEventListener('resize', updateConstraints);\n    return () => window.removeEventListener('resize', updateConstraints);\n  }, [range, size, lineLength, markerSize]);\n\n  const handleDragEnd = (\n    _event: MouseEvent | TouchEvent | PointerEvent,\n    info: PanInfo,\n  ) => {\n    setIsHolding(false);\n    const distance = Math.sqrt(info.offset.x ** 2 + info.offset.y ** 2);\n\n    if (distance > range && motionRef.current) {\n      const angle = Math.atan2(info.offset.y, info.offset.x);\n      const newX = Math.cos(angle) * range;\n      const newY = Math.sin(angle) * range;\n\n      motionRef.current.style.transform = `translate(${newX}px, ${newY}px)`;\n    }\n  };\n\n  const currentLineLength = isHolding ? lineLengthHolding : lineLength;\n\n  return (\n    <div ref={containerRef} className='relative'>\n      <motion.div\n        ref={motionRef}\n        drag\n        dragElastic={0}\n        dragMomentum={false}\n        dragConstraints={constraints}\n        onPointerDown={() => setIsHolding(true)}\n        onPointerUp={() => setIsHolding(false)}\n        onDragEnd={handleDragEnd}\n        transition={{ type: 'spring', stiffness: 400, damping: 35 }}\n        className='cursor-grab active:cursor-grabbing relative'\n        style={{ width: size, height: size }}\n        whileTap={{ scale: 1.05 }}\n      >\n        <motion.div\n          className='relative w-full h-full rounded-full overflow-hidden'\n          style={{\n            border: `3px solid ${borderColor}`,\n          }}\n          animate={{\n            boxShadow: isHolding\n              ? '0 20px 50px rgba(0,0,0,0.25), 0 10px 20px rgba(0,0,0,0.15)'\n              : '0 8px 30px rgba(0,0,0,0.12)',\n          }}\n          transition={{ duration: 0.2 }}\n        >\n          <img\n            src={image}\n            alt='Avatar'\n            className='w-full h-full object-cover'\n            draggable={false}\n          />\n        </motion.div>\n\n        <motion.span\n          className='absolute left-1/2'\n          style={{\n            bottom: `-${currentLineLength + markerSize + 4}px`,\n            transform: 'translateX(-50%)',\n          }}\n          animate={{\n            bottom: `-${currentLineLength + markerSize + 4}px`,\n          }}\n          transition={{ duration: 0.2 }}\n        >\n          <motion.span\n            className='block'\n            style={{\n              width: '2px',\n              backgroundColor: borderColor,\n              marginLeft: `${(markerSize - 2) / 2}px`,\n            }}\n            animate={{\n              height: `${currentLineLength}px`,\n            }}\n            transition={{ duration: 0.2 }}\n          />\n          <span\n            className='block rounded-full'\n            style={{\n              width: `${markerSize}px`,\n              height: `${markerSize}px`,\n              backgroundColor: borderColor,\n            }}\n          />\n        </motion.span>\n      </motion.div>\n    </div>\n  );\n}\n"
    }
  ]
}
