samlax12's picture
Upload 55 files
e85fa50 verified
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;