{
  "$schema": "https://ui.shadcn.com/schema/registry-item.json",
  "name": "input-placeholder-motion",
  "type": "registry:component",
  "description": "An input form component with spotlight effect.",
  "dependencies": [
    "motion/react"
  ],
  "registryDependencies": [
    "input",
    "button"
  ],
  "files": [
    {
      "path": "src/app/components/animatedComponents/forms/input-placeholder/InputPlaceholderAnimate.tsx",
      "content": "\"use client\";\r\n\r\nimport { AnimatePresence, motion } from \"motion/react\";\r\nimport { useCallback, useEffect, useRef, useState } from \"react\";\r\nimport { cn } from \"@/lib/utils\";\r\nimport { Input } from \"@/components/ui/input\";\r\n\r\n function PlaceholdersInput({\r\n  placeholders,\r\n}: {\r\n  placeholders: string[];\r\n}) {\r\n  const [currentPlaceholder, setCurrentPlaceholder] = useState(0);\r\n  const [value, setValue] = useState(\"\");\r\n  const [animating, setAnimating] = useState(false);\r\n\r\n  const inputRef = useRef<HTMLInputElement>(null);\r\n  const canvasRef = useRef<HTMLCanvasElement>(null);\r\n  const newDataRef = useRef<any[]>([]);\r\n  const intervalRef = useRef<NodeJS.Timeout | null>(null);\r\n\r\n \r\n  useEffect(() => {\r\n    const startAnimation = () => {\r\n      intervalRef.current = setInterval(() => {\r\n        setCurrentPlaceholder((p) => (p + 1) % placeholders.length);\r\n      }, 3000);\r\n    };\r\n\r\n    const handleVisibility = () => {\r\n      if (document.visibilityState === \"hidden\" && intervalRef.current) {\r\n        clearInterval(intervalRef.current);\r\n        intervalRef.current = null;\r\n      } else if (!intervalRef.current) {\r\n        startAnimation();\r\n      }\r\n    };\r\n\r\n    startAnimation();\r\n    document.addEventListener(\"visibilitychange\", handleVisibility);\r\n\r\n    return () => {\r\n      if (intervalRef.current) clearInterval(intervalRef.current);\r\n      document.removeEventListener(\"visibilitychange\", handleVisibility);\r\n    };\r\n  }, [placeholders]);\r\n\r\n\r\n  const draw = useCallback(() => {\r\n    if (!inputRef.current || !canvasRef.current) return;\r\n    const ctx = canvasRef.current.getContext(\"2d\");\r\n    if (!ctx) return;\r\n\r\n    canvasRef.current.width = 800;\r\n    canvasRef.current.height = 800;\r\n    ctx.clearRect(0, 0, 800, 800);\r\n\r\n    const style = getComputedStyle(inputRef.current);\r\n    const fontSize = parseFloat(style.fontSize);\r\n    ctx.font = `${fontSize * 2}px ${style.fontFamily}`;\r\n    ctx.fillStyle = \"#fff\";\r\n    ctx.fillText(value, 16, 40);\r\n\r\n    const imageData = ctx.getImageData(0, 0, 800, 800).data;\r\n    newDataRef.current = [];\r\n    for (let y = 0; y < 800; y++) {\r\n      for (let x = 0; x < 800; x++) {\r\n        const idx = (y * 800 + x) * 4;\r\n        if (imageData[idx] || imageData[idx + 1] || imageData[idx + 2]) {\r\n          newDataRef.current.push({\r\n            x,\r\n            y,\r\n            r: 1,\r\n            color: `rgba(${imageData[idx]},${imageData[idx + 1]},${imageData[idx + 2]},${imageData[idx + 3]})`,\r\n          });\r\n        }\r\n      }\r\n    }\r\n  }, [value]);\r\n\r\n  useEffect(() => {\r\n    draw();\r\n  }, [value, draw]);\r\n\r\n\r\n  const animateVanish = (maxX: number) => {\r\n    const frame = (pos = maxX) => {\r\n      requestAnimationFrame(() => {\r\n        newDataRef.current = newDataRef.current\r\n          .map((p) => ({\r\n            ...p,\r\n            x: p.x + (Math.random() > 0.5 ? 1 : -1),\r\n            y: p.y + (Math.random() > 0.5 ? 1 : -1),\r\n            r: Math.max(0, p.r - 0.05 * Math.random()),\r\n          }))\r\n          .filter((p) => p.r > 0);\r\n\r\n        const ctx = canvasRef.current?.getContext(\"2d\");\r\n        if (ctx) {\r\n          ctx.clearRect(pos, 0, 800, 800);\r\n          newDataRef.current.forEach(({ x, y, r, color }) => {\r\n            if (x > pos) {\r\n              ctx.fillStyle = color;\r\n              ctx.fillRect(x, y, r, r);\r\n            }\r\n          });\r\n        }\r\n\r\n        if (newDataRef.current.length > 0) {\r\n          frame(pos - 8);\r\n        } else {\r\n          setValue(\"\");\r\n          setAnimating(false);\r\n        }\r\n      });\r\n    };\r\n    frame(maxX);\r\n  };\r\n\r\n  const vanishAndSubmit = () => {\r\n    if (!value) return;\r\n    setAnimating(true);\r\n    draw();\r\n    const maxX = newDataRef.current.reduce((m, p) => Math.max(m, p.x), 0);\r\n    animateVanish(maxX);\r\n  };\r\n\r\n  const handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {\r\n    e.preventDefault();\r\n    vanishAndSubmit();\r\n  };\r\n\r\n  return (\r\n    <form\r\n      onSubmit={handleSubmit}\r\n      className={cn(\r\n        \"relative flex w-full max-w-md items-center  transition\",\r\n        value && \"bg-gray-50\"\r\n      )}\r\n    >\r\n      <canvas\r\n        ref={canvasRef}\r\n        className={cn(\r\n          \"absolute left-2 top-[20%] origin-top-left scale-50 pr-20 invert dark:invert-0 pointer-events-none\",\r\n          animating ? \"opacity-100\" : \"opacity-0\"\r\n        )}\r\n      />\r\n\r\n\r\n      <Input\r\n        ref={inputRef}\r\n        value={value}\r\n        onChange={(e) => {\r\n          if (!animating) {\r\n            setValue(e.target.value);\r\n          }\r\n        }}\r\n        onKeyDown={(e) => e.key === \"Enter\" && !animating && vanishAndSubmit()}\r\n        type=\"text\"\r\n        className={cn(\r\n          \"z-50 w-full bg-transparent pl-4 pr-20 text-sm text-black focus:outline-none dark:text-white sm:text-base\",\r\n          animating && \"text-transparent\"\r\n        )}\r\n      />\r\n\r\n    \r\n      <div className=\"pointer-events-none absolute inset-0 flex items-center rounded-full\">\r\n        <AnimatePresence mode=\"wait\">\r\n          {!value && (\r\n            <motion.p\r\n              key={currentPlaceholder}\r\n              initial={{ y: 5, opacity: 0 }}\r\n              animate={{ y: 0, opacity: 1 }}\r\n              exit={{ y: -15, opacity: 0 }}\r\n              transition={{ duration: 0.3, ease: \"linear\" }}\r\n              className=\"w-[calc(100%-2rem)] truncate pl-4 text-sm text-neutral-500 sm:pl-5 sm:text-sm dark:text-zinc-500\"\r\n            >\r\n              {placeholders[currentPlaceholder]}\r\n            </motion.p>\r\n          )}\r\n        </AnimatePresence>\r\n      </div>\r\n    </form>\r\n  );\r\n}\r\n\r\nexport default function InputPlaceholderAnimate() {\r\nconst placeholders = [\r\n  \"Enter your full name...\",\r\n  \"Type your email address...\",\r\n  \"Choose a strong password...\",\r\n  \"Confirm your password...\",\r\n];\r\n\r\n  return (\r\n    <>\r\n      <PlaceholdersInput\r\n        placeholders={placeholders}\r\n      />\r\n    </>\r\n  );\r\n}\r\n",
      "type": "registry:component",
      "target": "components/animated/forms/input-placeholder/InputPlaceholderAnimate.tsx"
    }
  ]
}