imetting_frontend/src/components/Dropdown.jsx

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;