82 lines
2.3 KiB
JavaScript
82 lines
2.3 KiB
JavaScript
import React, { useState, useEffect, useRef } from 'react';
|
|
import './Dropdown.css';
|
|
|
|
/**
|
|
* Dropdown - 通用下拉菜单组件
|
|
*
|
|
* @param {Object} props
|
|
* @param {React.ReactNode} props.trigger - 触发器元素(按钮)
|
|
* @param {Array<Object>} props.items - 菜单项数组
|
|
* - label: string - 显示文本
|
|
* - icon: React.ReactNode - 图标(可选)
|
|
* - onClick: function - 点击回调
|
|
* - className: string - 自定义样式类(可选)
|
|
* - danger: boolean - 是否为危险操作(红色)
|
|
* @param {string} props.align - 对齐方式: 'left' | 'right',默认 'right'
|
|
* @param {string} props.className - 外层容器自定义类名
|
|
*/
|
|
const Dropdown = ({
|
|
trigger,
|
|
items = [],
|
|
align = 'right',
|
|
className = ''
|
|
}) => {
|
|
const [isOpen, setIsOpen] = useState(false);
|
|
const dropdownRef = useRef(null);
|
|
|
|
// 点击外部关闭下拉菜单
|
|
useEffect(() => {
|
|
const handleClickOutside = (event) => {
|
|
if (dropdownRef.current && !dropdownRef.current.contains(event.target)) {
|
|
setIsOpen(false);
|
|
}
|
|
};
|
|
|
|
if (isOpen) {
|
|
document.addEventListener('mousedown', handleClickOutside);
|
|
return () => document.removeEventListener('mousedown', handleClickOutside);
|
|
}
|
|
}, [isOpen]);
|
|
|
|
const handleTriggerClick = (e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
setIsOpen(!isOpen);
|
|
};
|
|
|
|
const handleItemClick = (item, e) => {
|
|
e.preventDefault();
|
|
e.stopPropagation();
|
|
if (item.onClick) {
|
|
item.onClick(e);
|
|
}
|
|
setIsOpen(false);
|
|
};
|
|
|
|
return (
|
|
<div className={`dropdown-container ${className}`} ref={dropdownRef}>
|
|
<div className="dropdown-trigger-wrapper" onClick={handleTriggerClick}>
|
|
{trigger}
|
|
</div>
|
|
|
|
{isOpen && (
|
|
<div className={`dropdown-menu-wrapper dropdown-align-${align}`}>
|
|
{items.map((item, index) => (
|
|
<button
|
|
key={index}
|
|
className={`dropdown-menu-item ${item.danger ? 'danger' : ''} ${item.className || ''}`}
|
|
onClick={(e) => handleItemClick(item, e)}
|
|
disabled={item.disabled}
|
|
>
|
|
{item.icon && <span className="dropdown-item-icon">{item.icon}</span>}
|
|
<span className="dropdown-item-label">{item.label}</span>
|
|
</button>
|
|
))}
|
|
</div>
|
|
)}
|
|
</div>
|
|
);
|
|
};
|
|
|
|
export default Dropdown;
|