0.0.23 • Published 1 year ago

@jeryqwq/react-use-contextmenu v0.0.23

Weekly downloads
-
License
MIT
Repository
github
Last release
1 year ago

react-context-menu

NPM version NPM downloads

Install

$ pnpm install @jeryqwq/react-use-contextmenu

run | build

$ npm run dev
$ npm run build

为什么会有这个组件而不是用社区的

看了几款react生态下的相关插件,无法适配当前业务的复杂性。

例如:

  • 当菜单超出屏幕时需要做UI适配,会导致顶部和底部列表的选项无法全部展示
  • 当菜单处于局部滚动条内时,当用户滚动时,菜单也应该对应的滚动,否则无法继续操作 - 未处理
  • 需要适配多级菜单和动态展开,展开式也需要对UI做判断,需要适配屏幕
  • 菜单禁用状态适配
  • 动态生成菜单

其它社区优秀插件: React ContextMenu 2020年已不维护

ContextMenu 组件描述

用于需要对该项进行多个功能选项操作时, 包含有禁用,多级,动态加载,列表右键菜单等,可以使用右键菜单进行对应的操作和展示。

基础

右键菜单分为多个部分,下面是一些相关概念。你也可以基于此实现其它相关菜单的功能

MenuItem 数据结构

DEMO

import useContextMenu from '@jeryqwq/react-use-contextmenu';
import React from 'react'


export default function MyContextMenu () {
  const { Trigger, ContextMenu } = useContextMenu()
  return <div>
    <Trigger data={{id: '1j24iej1h2r23'}}>我是触发器,单击我会把我的data传给右键菜单的处理函数</Trigger>
    <ContextMenu menus={[{ label: '操作1', value: '1' }]} onClick={(e, data, menu) => {
      alert(`data.id: ${data.id} menu.value: ${menu.value}`)
    }}/>
  </div>
}

useContextMenu

逻辑解耦, 只负责处理处理右键和定位逻辑, 返回的Trigger和ContextMenu是包含菜单功能逻辑的菜单和触发器,用的时候只要加一些配置即可,可配置唤醒方式,单击,右键等事件,默认右键唤醒

Trigger 触发器

用来根据hooks初始化时配置的操作事件唤醒菜单和传递参数。

注:为了无缝接入其它组件库,所有在Trigger上的参数都会被渲染到子元素的props上接入Antd Table

ContextMenu

展示菜单UI的组件和对应的菜单处理函数。

触发方式

可通过初始化hooks时配置触发方式来适配其它激活操作。

import useContextMenu from '@jeryqwq/react-use-contextmenu';
import React from 'react'


export default function MyContextMenu () {
  const { Trigger, ContextMenu } = useContextMenu({ event: 'click' })
  const { Trigger: Trigger2, ContextMenu: ContextMenu2 } = useContextMenu({ event: 'mousemove' })

  return <div>
    <Trigger data={{id: 'click'}}>单击触发</Trigger>
    <ContextMenu menus={[{ label: '操作1', value: '1' }]} onClick={(e, data, menu) => {
      alert(`data.id: ${data.id} menu.value: ${menu.value}`)
    }}/>
    <Trigger2 data={{id: 'mousemove'}} tag="span">鼠标移动触发</Trigger2>
    <ContextMenu2 menus={[{ label: '操作1', value: '1' }]} onClick={(e, data, menu) => {
      alert(`data.id: ${data.id} menu.value: ${menu.value}`)
    }}/>
  </div>
}

多级菜单 & 异步动态加载

import useContextMenu from '@jeryqwq/react-use-contextmenu';
import React from 'react'
import { ReloadOutlined } from '@ant-design/icons';


export default function MyContextMenu () {
  const { Trigger, ContextMenu } = useContextMenu()


  return <div>
    <Trigger data={{id: '123'}}>右键我</Trigger>
    <ContextMenu menus={[{ label: '操作1', value: '1' },
    {
  label: '动态异步菜单',
  value: 'key2',
  icon: <ReloadOutlined />,
  children: () => {
    return new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve([
          { label: '操作2-1',
            value: 'key2-1'
          },
          { label: '操作2-2',
            value: 'key2-2'
          },
          { label: '操作2-3',
            value: 'key2-3'
          },
          { label: '操作2-4',
            value: 'key2-4'
          }
        ])
      }, 1000)
    })
  }
},
    ]} onClick={(e, data, menu) => {
      alert(`data.id: ${data.id} menu.value: ${menu.value}`)
    }}/>
  </div>
}

已存在DOM节点触发

给任意已渲染的dom添加触发器

import useContextMenu from '@jeryqwq/react-use-contextmenu';
import React, { useState, useLayoutEffect } from 'react'

export default function MyContextMenu () {
  const { Trigger, ContextMenu } = useContextMenu()

  return <div>
    <Trigger data={{id: '1j24iej1h2r23'}} getEl={() => { return document.getElementsByClassName('any class name')[0] }}></Trigger>
    <ContextMenu menus={[{ label: '操作1', value: '1' }]} onClick={(e, data, menu) => {
      alert(`data.id: ${data.id} menu.value: ${menu.value}`)
    }}/>
  </div>
}

Antd Table列表右键菜单

无缝接入ant design Table组件, 为每项加入菜单, 重写render row方法,把所有属性同步到Trigger即可

import React, {  useState } from 'react';
import useContextMenu from '@jeryqwq/react-use-contextmenu';
import { Button, Table  } from 'antd'
import { ReloadOutlined } from '@ant-design/icons';


const menus = [{
  label: '操作1',
  value: 'key1',
  children: [
    { label: '操作1-1',
      value: 'key1-1',
    },
    { label: '操作1-2',
      value: 'key1-2'
    }
  ],
  icon: <ReloadOutlined />,
},
{
  label: '操作3',
  value: 'key3',
},
{
  label: '操作被禁用',
  value: 'key4',
  disabled: true,
}]

export default () => {
  const { Trigger, ContextMenu } = useContextMenu()
  return <div >
    <ContextMenu menus={menus} onClick={(e, item, menu)=> {
      console.log(item, menu, '----')
    }}/>
    <Table 
      dataSource={[
      {
        key: '1',
        name: '胡彦斌',
        age: 32,
        address: '西湖区湖底公园1号',
      },
      {
        key: '2',
        name: '胡彦祖',
        age: 42,
        address: '西湖区湖底公园1号',
      },
    ]}
    columns={ [
      {
        title: '姓名',
        dataIndex: 'name',
        key: 'name',
      },
      {
        title: '年龄',
        dataIndex: 'age',
        key: 'age',
      },
      {
        title: '住址',
        dataIndex: 'address',
        key: 'address',
      },
    ]}
    components={{
      body: {
        row: (item) => {
          const { index,
          moveRow,
          className,
          style,
          ...restProps} = item;
          return <Trigger data={item} tag="tr" className={`${className}`}
            style={style}
            {...restProps}
            >
          </Trigger>
        }
      }
    }}
    />
  </div>
}

指定父元素渲染 | getMenuContainer

菜单渲染父节点。默认fixed渲染到 元素中,如果你遇到菜单滚动定位问题,试试修改为滚动的区域,并相对其定位。

import { useContextMenu } from '@vis/components';
import React from 'react'

export default function MyContextMenu () {
  const { Trigger, ContextMenu } = useContextMenu({
    getMenuContainer() {
      return document.getElementById('my-el')
    }
  })

  return <div style={{height: 200, overflow: 'scroll'}}>
    <div  id="my-el" style={{width: 500, height: 800, background: 'red', position: 'relative'}}>
      <Trigger data={{id: 'click'}}>单击触发</Trigger>
      <ContextMenu menus={[{ label: '操作1', value: '1' }]} onClick={(e, data, menu) => {
        alert(`data.id: ${data.id} menu.value: ${menu.value}`)
      }}/>
    </div>
    
  </div>
}