react+ts make wheels to imitate the antd Menu component (upgraded version)

Menu

I made a simple version of the Menu component before, and then upgraded this component to support the drop-down menu function. Look at the renderings The
Insert picture description here
horizontal drop-down menu will open when the mouse passes, and close when the mouse leaves. The vertical drop-down menu is opened when the mouse is clicked, and closed when clicked again.
Following the previous primary version, we need one more component, the SubMenu component. The essence of this component is also a li tag, and then there is a div and an ul inside. The div is used to hold the title, and the ul is used to hold the component of the drop-down menu. , So the MenuItem component is placed inside the SubMenu component.
Look at the code:
interface
Insert picture description here
component code

import React, {
    
    useState, useContext} from 'react'
import {
    
    MenuContext}  from './conText'
import cx from 'classnames'
export interface ISubMenu {
    
    
    className?: string,
    index?: string,
    title?: string,
}

const SubMenu: React.FC<ISubMenu> = (props) => {
    
    
    const {
    
     className, index, title, children } = props
    const value = useContext(MenuContext);
    const isOpen = index? (value.defaultOpenSubMenu as Array<string>).includes(index) : false
    const [menuOpen, setmenuOpen] = useState(isOpen);
    const classes = cx('menu-item submenu-item', className, {
    
    'menu-submeu-open':menuOpen})
    const renderChildren = () => {
    
    
        const childrenElement = React.Children.map(children, (child, i) => {
    
    
            const chlidElement = child as React.FunctionComponentElement<ISubMenu>
            if (chlidElement?.type?.displayName !== 'MenuItem') {
    
    //传入的子组件不是MenuItem就报错
                console.error('Waring: Menu has a child which is not a MenuItem')
            } else {
    
    
                return React.cloneElement(chlidElement, {
    
    
                    index: `${
    
    index}-${
    
    i}`
                  })

            }
        })
        return (
            <ul className="menu-submenu">
                {
    
    childrenElement}
            </ul>
        )
    }
    const handleClick = (e: React.MouseEvent)=>{
    
     //竖向鼠标点击显示
        e.preventDefault()
        setmenuOpen(!menuOpen)
    }
    let timer:any //防抖函数
    const handleMouse = (e: React.MouseEvent, toggle: boolean) => {
    
     //横向鼠标经过即显示
        clearTimeout(timer)
        e.preventDefault()
        timer = setTimeout(()=>{
    
    
            setmenuOpen(toggle)
        },50)
    }
    const clickEvents = value.type === 'vertical'?{
    
    
        onClick:(e: React.MouseEvent)=>{
    
    handleClick(e)}
    }:{
    
    }
    const MouseEvents = value.type !== 'vertical'?{
    
    
        onMouseEnter:(e: React.MouseEvent)=>{
    
    handleMouse(e, true)},
        onMouseLeave:(e: React.MouseEvent)=>{
    
    handleMouse(e, false)}
    }: {
    
    }
    return (
        <li key={
    
    index} className={
    
    classes} {
    
    ...MouseEvents}>
            <div className="submenu-title" {
    
    ...clickEvents}>{
    
    title}</div>
            {
    
    renderChildren()}
        </li>
    )
}

SubMenu.displayName = 'SubMenu'
export default SubMenu

Look at it step by step. First, look at the structure, which
Insert picture description here
is also a li tag, and then Insert picture description here
this is the same as our primary version, because we have to determine whether the SubMenu contains a MenuItem tag, and we must manually add the index value to it. This The index value is returned to the callback function and used to determine whether the MenuItem is currently in the active state. Since our outer layer uses the index value Index as the index value, if we continue to use the Index value here, it will conflict with the outside. So we let the Index value be string, and pass in the index value of another data structure to him. When the Insert picture description here
first index is the outer Menu, add the index index value to each son, and i is the subMenu traversed by him. The index value of the component is just another way of writing. So the index value of our drop-down menu will become 2-1, 2-2, etc.

Next is the method of clicking or displaying the drop-down menu

Insert picture description here
First write two functions, corresponding to click display and mouse over display. Set a useState value to determine whether the drop-down menu is closed. When the mouse passes by, when the mouse moves at a high speed, it is easy to cause the event to be triggered multiple times, so the thinking of anti-shake function is adopted to create a timer. Used to judge.
Then it is judged that when it is vertical, the click event is established, and when it is not vertical, that is, when it is horizontal, the mouse event is established. Then through destructuring assignment, the mouse passing event is placed on the outer layer li, and the click event is placed on the div. In this way, when the type is horizontal, only the mouse event is valid, and when the type is vertical, only the click event is valid.

Set to open by default

There may be multiple drop-down menus, and some are to be opened by default. It can be set like this.
Through an array passed in by the user, it is judged whether the index value of the current drop-down menu is in this array. If it is, it will be displayed by default. If not, it will be displayed. Not displayed by default. This array is passed by the user to the Menu component, and the Menu component is shared through the context, see the code
Insert picture description here
Insert picture description here
Insert picture description here

Insert picture description here
As mentioned earlier, this index is automatically added when the Menu component traverses the sub-components, and then the array is obtained through the shared context data. Note that we used it when we defined it? Insert picture description here
That is, its value may be undefined, so we must type Assert, tell the compiler that this thing is an array of strings, so that no error will be reported when writing the code, by judging whether Index is in this array, and when the setting is turned on by default at the beginning, set it according to this judgment, if If yes, it will be true at the beginning, and it will be turned on by default.
Even if the basic functions of the Menu component are completed, you can add different and diverse functions according to your needs or ideas in the future.

Guess you like

Origin blog.csdn.net/lin_fightin/article/details/113516257