| import React, { useState } from "react"; | |
| import { cn } from "@/lib/utils/cn"; | |
| interface SelectProps extends React.SelectHTMLAttributes<HTMLSelectElement> { | |
| label?: string; | |
| error?: string; | |
| options: Array<{ value: string; label: string }>; | |
| } | |
| export const Select: React.FC<SelectProps> = ({ | |
| label, | |
| error, | |
| options, | |
| 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"> | |
| <select | |
| 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 appearance-none cursor-pointer", | |
| "hover:border-gray-400", | |
| 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", | |
| className | |
| )} | |
| onFocus={() => setFocused(true)} | |
| onBlur={() => setFocused(false)} | |
| {...props} | |
| > | |
| {options.map((option) => ( | |
| <option key={option.value} value={option.value}> | |
| {option.label} | |
| </option> | |
| ))} | |
| </select> | |
| {/* Custom arrow */} | |
| <div className="absolute inset-y-0 right-0 flex items-center pr-3 pointer-events-none"> | |
| <svg className="w-5 h-5 text-gray-400" fill="none" stroke="currentColor" viewBox="0 0 24 24"> | |
| <path strokeLinecap="round" strokeLinejoin="round" strokeWidth={2} d="M19 9l-7 7-7-7" /> | |
| </svg> | |
| </div> | |
| {(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> | |
| ); | |
| }; | |