91 lines
3.7 KiB
React
91 lines
3.7 KiB
React
import React, { useState } from 'react';
|
||
import './SideMenu.css';
|
||
|
||
/**
|
||
* Aurora Admin SideMenu(React)
|
||
* 对齐 组件7.txt SideMenu 规范:多级、图标、徽章、选中态#F0F5FF+左指示条、折叠悬浮
|
||
*
|
||
* props:
|
||
* - value / defaultValue : 当前选中 key
|
||
* - items : [{ key, label, icon?, badge?, children?, disabled? }]
|
||
* - collapsed
|
||
* - onSelect(key)
|
||
*/
|
||
export default function SideMenu({ value, defaultValue = '', items = [], collapsed = false, onSelect }) {
|
||
const [active, setActive] = useState(value !== undefined ? value : defaultValue);
|
||
const [openKeys, setOpenKeys] = useState([]);
|
||
const [hoverKey, setHoverKey] = useState('');
|
||
const [popTop, setPopTop] = useState(0);
|
||
const current = value !== undefined ? value : active;
|
||
|
||
const select = (key) => { if (value === undefined) setActive(key); onSelect && onSelect(key); setHoverKey(''); };
|
||
const onTopClick = (it) => {
|
||
if (it.disabled) return;
|
||
if (it.children && it.children.length) {
|
||
if (collapsed) return;
|
||
setOpenKeys(openKeys.includes(it.key) ? openKeys.filter(k => k !== it.key) : [...openKeys, it.key]);
|
||
} else select(it.key);
|
||
};
|
||
const onHover = (it, e) => { if (collapsed && it.children) { setHoverKey(it.key); setPopTop(e.currentTarget.offsetTop); } };
|
||
const popItem = items.find(x => x.key === hoverKey) || null;
|
||
|
||
return (
|
||
<div className={`aa-sidemenu${collapsed ? ' collapsed' : ''}`} onMouseLeave={() => setHoverKey('')}>
|
||
{items.map(it => {
|
||
const hasChild = it.children && it.children.length;
|
||
const isOpen = openKeys.includes(it.key);
|
||
return (
|
||
<div key={it.key}>
|
||
<div
|
||
className={`aa-menu-item${current === it.key ? ' is-active' : ''}${it.disabled ? ' is-disabled' : ''}${hasChild && isOpen ? ' is-open' : ''}`}
|
||
onClick={() => onTopClick(it)}
|
||
onMouseEnter={(e) => onHover(it, e)}
|
||
>
|
||
<span className="aa-menu-icon">{it.icon}</span>
|
||
<span className="aa-menu-label">{it.label}</span>
|
||
{it.badge && <span className="aa-menu-badge">{it.badge}</span>}
|
||
{hasChild && <span className="aa-menu-arrow">›</span>}
|
||
</div>
|
||
{hasChild && isOpen && !collapsed && (
|
||
<div className="aa-menu-children">
|
||
{it.children.map(c => (
|
||
<div key={c.key} className={`aa-menu-item${current === c.key ? ' is-active' : ''}`} onClick={() => select(c.key)}>
|
||
<span className="aa-menu-label">{c.label}</span>
|
||
</div>
|
||
))}
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
})}
|
||
|
||
{collapsed && hoverKey && popItem && (
|
||
<div className="aa-menu-pop" style={{ top: popTop }}>
|
||
<div className="aa-menu-pop-title">{popItem.label}</div>
|
||
{popItem.children && popItem.children.length ? (
|
||
popItem.children.map(c => (
|
||
<div key={c.key} className={`aa-menu-item${current === c.key ? ' is-active' : ''}`} onClick={() => select(c.key)}>
|
||
<span className="aa-menu-label">{c.label}</span>
|
||
</div>
|
||
))
|
||
) : (
|
||
<div className="aa-menu-item is-active" onClick={() => select(popItem.key)}>
|
||
<span className="aa-menu-label">{popItem.label}</span>
|
||
</div>
|
||
)}
|
||
</div>
|
||
)}
|
||
</div>
|
||
);
|
||
}
|
||
|
||
/*
|
||
用法示例:
|
||
import SideMenu from './SideMenu';
|
||
const items = [
|
||
{ key: 'dashboard', label: '仪表盘', icon: '▦' },
|
||
{ key: 'user', label: '用户管理', icon: '◍', badge: 8, children: [{ key: 'list', label: '用户列表' }] }
|
||
];
|
||
<SideMenu items={items} defaultValue="list" collapsed={false} onSelect={setActive} />
|
||
*/
|