自动安装
使用以下命令之一自动安装组件,根据你的包管理器选择:
npx tofu-ui-cli@latest add dropdown-menu
手动安装 (可选)
注意
如果你使用手动安装,将无法获取更新
如果你需要手动安装,请在项目目录中创建如下路径文件:
components/ui/tofu/dropdown-menu.tsx然后复制此代码:
import React, {FC, useEffect, useRef} from 'react';
import { motion, AnimatePresence } from 'framer-motion';
import { LucideIcon } from 'lucide-react';
/**
* TofuUI DropDownMenu 下拉/触发菜单
* @author shuakami
* @version 1.0.0
* @copyright ByteFreeze&TofuUI
*/
interface MenuItem {
id: string;
text: string;
href?: string;
target?: string; // <!此注释请勿去除> _ 参数支持 '_blank' | '_self'
icon?: LucideIcon;
isSpecial?: boolean;
}
interface DropDownMenuProps {
position?: 'top' | 'bottom' | 'left' | 'right' | 'down' ;
isOpen: boolean;
menuItems: MenuItem[];
onClose?: () => void;
}
const DropDownMenu: FC<DropDownMenuProps> = ({ isOpen, menuItems, onClose, position = 'bottom' }) => {
const menuRef = useRef<HTMLDivElement>(null);
const getPositionStyles = () => {
switch (position) {
case 'top':
return {
bottom: '-15%',
transform: 'translateY(-0px)'
};
case 'bottom':
default:
return {
top: '125%',
transform: 'translateY(0px)'
}
case 'left':
return {
right: '75%',
marginTop: '-1.95rem'
};
case 'right':
return {
left: '47%',
marginTop: '-2.35rem'
};
case 'down':
return {
// 不需要内容
};
}
};
// 使用样式
const positionStyles = getPositionStyles();
useEffect(() => {
const handleClickOutside = (event: MouseEvent) => {
if (menuRef.current && !menuRef.current.contains(event.target as Node)) {
onClose?.();
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => {
document.removeEventListener('mousedown', handleClickOutside);
};
}, [onClose]);
const menuVariants = {
open: { opacity: 1, scale: 1, transition: { duration: 0.2 } },
closed: { opacity: 0, scale: 0.95, transition: { duration: 0.2 } }
};
return (
<AnimatePresence>
{isOpen && (
<motion.div
ref={menuRef}
initial="closed"
animate="open"
exit="closed"
variants={menuVariants}
className="absolute bg-white shadow-lg rounded-xl border border-gray-200"
style={{
minWidth: '300px',
padding: '0.5rem 0',
zIndex: 10,
...positionStyles
}}
onClick={(e) => e.stopPropagation()}
>
{menuItems.map((item, index) => (
<React.Fragment key={item.id}>
{item.isSpecial && (
<div className="my-0.5 h-px bg-gray-200 mx-4"/>
)}
<a
href={item.href}
target={item.target || '_self'}
className=" flex items-center text-tofu-black text-sm cursor-pointer hover:bg-tofu-light-dropdown-menu-hover rounded-md"
style={{
padding: '0.64rem 0.725rem',
margin: '4px 10px',
borderRadius: '0.375rem'
}}
>
{item.icon ? (
<item.icon size={17} className="mr-3 text-tofu-light-dropdown-menu-icon"/>
) : (
<span className="inline-block mr-2"></span>
)}
{item.text}
</a>
</React.Fragment>
))}
</motion.div>
)}
</AnimatePresence>
);
};
export default DropDownMenu;导入组件
import DropDownMenu from '@/components/ui/tofu/dropdown-menu';使用组件
const [menuOpen, setMenuOpen] = useState(false);
const handleClose = () => {
setMenuOpen(false);
};
<button onClick={() => setMenuOpen(!menuOpen)}>Toggle Menu</button>
<DropDownMenu
position="right"
isOpen={menuOpen}
menuItems={[
{ id: '1', text: 'Option 1', icon: <Github/>, href: '#option1' },
{ id: '2', text: 'Option 2', icon: <Github/>, href: '#option2' }
]}
onClose={() => console.log('Menu Closed')}
/>示例
如果你希望让它在你选定的方位显示,你可以使用'top' | 'bottom' | 'left' | 'right' | 'down'控制
如果要导入图标,你可以先从lucide-react导入,然后使用
import { SettingsIcon } from "lucide-react";
.....
const menuItems = [
{
id: 'settings',
text: '设置',
icon: SettingsIcon,
href: '#',
},
....
]
参数说明
| 参数名 | 类型 | 默认值 | 描述 |
|---|---|---|---|
| position | 'top' | 'bottom' | 'left' | 'right' | 'down' | 'down' | 菜单打开的位置 |
| isOpen | boolean | 必需 | 控制下拉菜单的显示状态 |
| menuItems | MenuItem[] | 必需 | 定义菜单项的数组,每个项目可以是一个链接或动作 |
| onClose | () => void | 可选 | 点击菜单外部或用户操作后触发的回调函数 |
| icon | LucideIcon | 可选 | 菜单项的图标 |