Spaces:
Sleeping
Sleeping
import React, { useState, useRef, useEffect } from 'react'; | |
interface DropdownOption { | |
id: string; | |
label: React.ReactNode; | |
value: string; | |
} | |
interface DropdownProps { | |
options: DropdownOption[]; | |
value?: string; | |
onChange: (value: string) => void; | |
placeholder?: string; | |
label?: string; | |
error?: string; | |
className?: string; | |
disabled?: boolean; | |
} | |
const Dropdown: React.FC<DropdownProps> = ({ | |
options, | |
value, | |
onChange, | |
placeholder = 'θ―·ιζ©', | |
label, | |
error, | |
className = '', | |
disabled = false | |
}) => { | |
const [isOpen, setIsOpen] = useState(false); | |
const dropdownRef = useRef<HTMLDivElement>(null); | |
const selectedOption = options.find(option => option.value === value); | |
// ε€ηηΉε»ε€ι¨ε ³ιδΈζθε | |
useEffect(() => { | |
const handleClickOutside = (event: MouseEvent) => { | |
if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) { | |
setIsOpen(false); | |
} | |
}; | |
document.addEventListener('mousedown', handleClickOutside); | |
return () => { | |
document.removeEventListener('mousedown', handleClickOutside); | |
}; | |
}, []); | |
const toggleDropdown = () => { | |
if (!disabled) { | |
setIsOpen(!isOpen); | |
} | |
}; | |
const handleOptionSelect = (optionValue: string) => { | |
onChange(optionValue); | |
setIsOpen(false); | |
}; | |
return ( | |
<div className={`mb-4 ${className}`}> | |
{label && ( | |
<label className="block text-sm font-medium mb-1 text-gray-700"> | |
{label} | |
</label> | |
)} | |
<div className="relative" ref={dropdownRef}> | |
<button | |
type="button" | |
onClick={toggleDropdown} | |
className={` | |
ios-input flex items-center justify-between w-full | |
${disabled ? 'opacity-50 cursor-not-allowed' : 'cursor-pointer'} | |
${error ? 'border-red-500' : ''} | |
`} | |
disabled={disabled} | |
> | |
<span className={`${!selectedOption ? 'text-gray-400' : ''}`}> | |
{selectedOption ? selectedOption.label : placeholder} | |
</span> | |
<svg | |
xmlns="http://www.w3.org/2000/svg" | |
className={`h-5 w-5 text-gray-400 transition-transform duration-200 ${isOpen ? 'transform rotate-180' : ''}`} | |
viewBox="0 0 20 20" | |
fill="currentColor" | |
> | |
<path | |
fillRule="evenodd" | |
d="M5.293 7.293a1 1 0 011.414 0L10 10.586l3.293-3.293a1 1 0 111.414 1.414l-4 4a1 1 0 01-1.414 0l-4-4a1 1 0 010-1.414z" | |
clipRule="evenodd" | |
/> | |
</svg> | |
</button> | |
{isOpen && ( | |
<div className="absolute z-10 mt-1 w-full bg-white rounded-md shadow-lg max-h-60 overflow-auto"> | |
<div className="py-1"> | |
{options.map((option) => ( | |
<div | |
key={option.id} | |
className={` | |
px-4 py-2 text-sm hover:bg-gray-100 cursor-pointer | |
${option.value === value ? 'bg-blue-50 text-blue-600' : ''} | |
`} | |
onClick={() => handleOptionSelect(option.value)} | |
> | |
{option.label} | |
</div> | |
))} | |
</div> | |
</div> | |
)} | |
</div> | |
{error && <p className="mt-1 text-sm text-red-600">{error}</p>} | |
</div> | |
); | |
}; | |
export default Dropdown; |