用react实现一款Dropdown下拉菜单组件

4,391 阅读3分钟

这是我参与11月更文挑战的第2天,活动详情查看:2021最后一次更文挑战

TIP 👉 不畏浮云遮望眼,只缘身在最高层。——王安石《登飞来峰》

前言

Web Component是前端界一直非常热衷的一个领域,用第三方组件化的框架去实现的话,你需要依赖框架本身很多东西,很多时候我们只是简单的几个组件,不是很大,也不是很多,所以为了保证组件的`轻量,简单`,其实这个时候我们并不想采用第三方的框架。

Dropdown 下拉菜单

import

import Dropdown from '@/components/Dropdown/Dropdown';

利用子组件如下:
const MenuItem = Dropdown.MenuItem;
const SubMenu = Dropdown.SubMenu;

Props

1. trigger
  • 类型:string (必填)
  • 默认值:click | hover
  • 说明:下拉触发方式, click点击触发菜单,hover触发菜单
2. onClickMenu
  • 类型:func
  • 默认值:无
  • 说明:点击触发回调函数更改子组件的classname,入参:
    • {string} trigger 下拉触发方式
3. title
  • 类型:string
  • 默认值:无
  • 说明:下拉按钮提示标题

MenuItem 子内容组件

import

import Dropdown from '@/components/Dropdown/Dropdown';

Props

1. trigger
  • 类型:string (必填)
  • 默认值:click | hover
  • 说明:下拉触发方式, click点击触发菜单,hover触发菜单
2. className
  • 类型:string (必填)
  • 默认值:click | hover
  • 说明:子内容外层div的class:'dropdownType-click' | 'dropdownType-hover'
3. disabled
  • 类型:bool
  • 默认值:false
  • 说明:下拉的子内容某项置灰,true表示置灰,false表示正常
4. onClickMenuItem
  • 类型:func (必填)
  • 默认值:无
  • 说明:点击某个单独的子内容执行动作,入参:
    • {string | number} value | key 选中的某项子内容

SubMenu 子内容的父级

import

import Dropdown from '@/components/Dropdown/Dropdown';

Props

1. trigger
  • 类型:string (必填)
  • 默认值:click | hover
  • 说明:下拉触发方式, click点击触发菜单,hover触发菜单
2. className
  • 类型:string (必填)
  • 默认值:click | hover
  • 说明:子内容外层div的class:'dropdownType-click' | 'dropdownType-hover'
4. title
  • 类型:string
  • 默认值:无
  • 说明:下拉按钮提示标题

让我们来实现dropdown.js

import React from 'react';
import PropTypes from 'prop-types';
import './dropdown.scss';

/**
 * Dropdown                  组件所传属性描述
 * trigger                   下拉触发方式 (string)    click  hover
 * onClickMenu               点击更换classname (funtion)
 * title                     下拉菜单的名称 (string)
 */
class Dropdown extends React.Component {

    // 入参类型检查
    static propTypes = {
        // 下拉触发方式 为其中一值即可
        trigger: PropTypes.oneOf(['click', 'hover']),
        // 点击下拉 更换classname改变样式的func.
        onClickMenu: PropTypes.func,
        // 下拉方式 值为鼠标右击
        contextMenu: PropTypes.string,
        // 下拉的按钮文字
        title: PropTypes.string,
    }

    constructor(props) {
        super(props);
        this.checkboxWrapper = React.createRef();
        this.state = {
            trigger : props.trigger,
            // clickX: null,
            visible:false,//操控鼠标右击识别开关
            flag:false,//操控鼠标点击识别开关
        }
    }

    componentDidMount() {
        document.addEventListener('click', this.hideAllMenu);
    }

    hideAllMenu = ()=>{
        this.setState({flag : false });
    }

    showMenu(e){
        let trigger = this.props.trigger;
        e.nativeEvent.stopImmediatePropagation();
        // e.stopPropagation(); 
        console.log('>>>>', e)
        this.setState({flag : !this.state.flag });
        
        this.props.onClickMenu(this.state.trigger);//联动MenuItem的classname 必走 因classname要传值
    }
    render(){
        return (
            <div ref={this.checkboxWrapper} className={'dropdown-box '+this.props.trigger}
                 onClick={(e)=>{this.props.trigger == 'click' && this.showMenu(e)}}
            >{this.state.flag}
                <div className="drop-title">{this.props.title}
                    <i className="icon icon-shangjiantou mouseEnter" />
                    <i className="icon icon-xiajiantou mouseLeave" />
                    <i className={"icon " + (this.state.flag ? "icon-shangjiantou" : "icon-xiajiantou")} />
                </div>
                <div ref={'testDiv'} className="drop-cont">
                    {this.props.contextMenu ? (this.state.visible && this.props.children) : (
                        this.props.trigger == 'click' ? (this.state.flag && this.props.children) : this.props.children)}
                </div>
            </div>
        )
    }
}

/**
 * MenuItem              组件所传属性描述
 * trigger               下拉触发方式 (string)    click  hover
 * className             联动点击更换classname (string)
 * disabled              置灰状态  true:灰  false :正常
 * onClickMenuItem       点击某个单独的项执行动作 (funtion)
 */
class  MenuItem extends  React.Component{

    // 入参类型检查
    static propTypes = {
        //下拉触发方式 为其中一值即可
        trigger: PropTypes.oneOf(['click', 'hover']),
        //点击更换classname 必填
        className: PropTypes.string.isRequired,
        //下拉的某项置灰  true:灰  false :正常
        disabled: PropTypes.bool,
        //点击某个单独的项执行动作
        onClickMenuItem: PropTypes.func,
    }

    constructor(props) {
        super(props);
        this.state = {
            flag:true
        };
    }
    menuAction(){
        this.props.onClickMenuItem && this.props.onClickMenuItem();
    }
    render(){
        return(
           <div className={'menuItem dropdownType-' + (this.props.trigger == 'hover' ? this.props.trigger : this.props.className) +
           ' '+ (this.props.disabled ? 'disabled' : '')}
                onClick={(e)=>{this.menuAction(e)}}>
               {this.props.children}
           </div>
        )
    }
}

/**
 * SubMenu               组件所传属性描述
 * title                 有多级项的父级名称 (string)
 * className             由父联动过来的点击更换classname (string)
 * trigger               下拉触发方式 (string)    click  hover
 */
class  SubMenu extends  React.Component{

    // 入参类型检查
    static propTypes = {
        //下拉触发方式 为其中一值即可
        trigger: PropTypes.oneOf(['click', 'hover']),
        //点击更换classname 必填
        className: PropTypes.string.isRequired,
        // 下拉的按钮文字
        title: PropTypes.string,
    }

    constructor(props) {
        super(props);
        this.state = {
        };
    }
    render(){
        return(
            <div className={'submenu dropdownType-' + (this.props.trigger == 'hover' ? this.props.trigger : this.props.className)}>
                {this.props.title}
                <i className="icon icon-youjiantou"></i>
                <div className={'submenuItem-box'}>
                    {this.props.children}
                </div>
            </div>
        )
    }
}

Dropdown.MenuItem = MenuItem;
Dropdown.SubMenu = SubMenu;

export default Dropdown;

样式这边就不粘贴了

「欢迎在评论区讨论」

希望看完的朋友可以给个赞,鼓励一下