import React from 'react' import { useContext } from 'react' import classNames from 'classnames' import { MenuContext } from './menu' export interface MenuItemProps { index: string; disabled?: boolean; className?: string; style?: React.CSSProperties; } const MenuItem: React.FC<MenuItemProps> = (props) => { const { index, disabled, className, style, children } = props const context = useContext(MenuContext) const classes = classNames('menu-item', className, { 'is-disabled': disabled, // 实现高亮的具体逻辑 'is-active': context.index === index }) const handleClick = () => { // disabled之后就不能使用onSelect,index因为是可选的,所以可能不存在,需要用typeof来做一个判断 if (context.onSelect && !disabled && (typeof index === 'string')) { context.onSelect(index) } } return ( <li className={classes} style={style} onClick={handleClick}> {children} </li> ) } MenuItem.displayName = 'MenuItem' export default MenuItem
最后是SubMenu子组件:
import React, { useContext, FunctionComponentElement, useState } from 'react' import classNames from 'classnames' import { MenuContext } from './menu' import { MenuItemProps } from './menuItem' export interface SubMenuProps { index?: string; title: string; className?: string } const SubMenu: React.FC<SubMenuProps> = ({ index, title, children, className }) => { const context = useContext(MenuContext) // 接下来会使用string数组的一些方法,所以先进行类型断言,将其断言为string数组类型 const openedSubMenus = context.defaultOpenSubMenus as Array<string> // 使用include判断有没有index const isOpened = (index && context.mode === 'vertical') ? openedSubMenus.includes(index) : false const [ menuOpen, setOpen ] = useState(isOpened) // isOpened返回的会是true或者false,这样就是一个动态值 const classes = classNames('menu-item submenu-item', className, { 'is-active': context.index === index }) // 用于实现显示或隐藏下拉菜单 const handleClick = (e: React.MouseEvent) => { e.preventDefault() setOpen(!menuOpen) } let timer: any // toggle用于判断是打开还是关闭 const handleMouse = (e: React.MouseEvent, toggle: boolean) => { clearTimeout(timer) e.preventDefault() timer = setTimeout(()=> { setOpen(toggle) }, 300) } // 三元表达式,纵向 const clickEvents = context.mode === 'vertical' ? { onClick: handleClick } : {} const hoverEvents = context.mode === 'horizontal' ? { onMouseEnter: (e: React.MouseEvent) => { handleMouse(e, true) }, onMouseLeave: (e: React.MouseEvent) => { handleMouse(e, false) }, } : {} // 用于渲染下拉菜单中的内容 // 返回两个值,第一个是child,第二个是index,用i表示 const renderChildren = () => { const subMenuClasses = classNames('menu-submenu', { 'menu-opened': menuOpen }) // 下面功能用于实现在subMenu里只能有MenuItem const childrenComponent = React.Children.map(children, (child, i) => { const childElement = child as FunctionComponentElement<MenuItemProps> if (childElement.type.displayName === 'MenuItem') { return React.cloneElement(childElement, { index: `${index}-${i}` }) } else { console.error("Warning: SubMenu has a child which is not a MenuItem component") } }) return ( <ul className={subMenuClasses}> {childrenComponent} </ul> ) } return ( // 展开运算符,向里面添加功能,hover放在外面 <li key={index} className={classes} {...hoverEvents}> <div className="submenu-title" {...clickEvents}> {title} </div> {renderChildren()} </li> ) } SubMenu.displayName = 'SubMenu' export default SubMenu