| import React, { useState } from "react"; | |
| import { cn } from "@/lib/utils/cn"; | |
| interface InputProps extends React.InputHTMLAttributes<HTMLInputElement> { | |
| label?: string; | |
| error?: string; | |
| } | |
| export const Input: React.FC<InputProps> = ({ | |
| label, | |
| error, | |
| className, | |
| ...props | |
| }) => { | |
| const [focused, setFocused] = useState(false); | |
| const hasValue = props.value !== undefined && props.value !== ""; | |
| return ( | |
| <div className="w-full"> | |
| {label && ( | |
| <label className="block text-sm font-semibold text-gray-700 mb-2"> | |
| {label} | |
| </label> | |
| )} | |
| <div className="relative"> | |
| <input | |
| className={cn( | |
| "w-full px-4 py-3 rounded-xl border-2 transition-all duration-250 focus:outline-none focus:ring-2 focus:ring-offset-2", | |
| "bg-white/80 backdrop-blur-sm", | |
| error | |
| ? "border-red-400 focus:border-red-500 focus:ring-red-500" | |
| : focused || hasValue | |
| ? "border-blue-400 focus:border-blue-500 focus:ring-blue-500" | |
| : "border-gray-300 focus:border-blue-500 focus:ring-blue-500", | |
| "hover:border-gray-400", | |
| className | |
| )} | |
| onFocus={() => setFocused(true)} | |
| onBlur={() => setFocused(false)} | |
| {...props} | |
| /> | |
| {(focused || hasValue) && !error && ( | |
| <div className="absolute inset-0 rounded-xl border-2 border-blue-500 pointer-events-none animate-pulse-glow opacity-50"></div> | |
| )} | |
| </div> | |
| {error && ( | |
| <p className="mt-2 text-sm text-red-600 font-medium animate-slide-in">{error}</p> | |
| )} | |
| </div> | |
| ); | |
| }; | |