$ pnpm install @jeryqwq/react-use-contextmenu
$ npm run dev
$ npm run build
看了几款react生态下的相关插件,无法适配当前业务的复杂性。
例如:
- 当菜单超出屏幕时需要做UI适配,会导致顶部和底部列表的选项无法全部展示
- 当菜单处于局部滚动条内时,当用户滚动时,菜单也应该对应的滚动,否则无法继续操作
- 需要适配多级菜单和动态展开,展开式也需要对UI做判断,需要适配屏幕
- 菜单禁用状态适配
- 动态生成菜单
其它社区优秀插件: React ContextMenu 2020年已不维护
用于需要对该项进行多个功能选项操作时, 包含有禁用,多级,动态加载,列表右键菜单等,可以使用右键菜单进行对应的操作和展示。
右键菜单分为多个部分,下面是一些相关概念。你也可以基于此实现其它相关菜单的功能
属性名 | 描述 | 类型 | 默认值 |
---|---|---|---|
value | 唯一值 | string | (必选) |
label | 展示的名称或者自定义jsx | ReactNode | (必选) |
children | 子节点加载方法或者子节点数组 | ContextMenuItem[] | ((item: ContextMenuItem) => ContextMenuItem[]) | -- |
icon | 图表 | ReactNode | -- |
disabled | 是否禁用,禁用后不会处罚单击函数 | boolean | -- |
render | 自定义渲染 | (_: ContextMenuItem) => ReactNode | -- |
loadding | 自定义加载图表 | ReactNode | -- |
onClick | 自定义单独处理函数 | (e: MouseEvent<Element, MouseEvent>, item: ContextMenuItem, data: any) => void | -- |
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>
}
逻辑解耦, 只负责处理处理右键和定位逻辑, 返回的Trigger和ContextMenu是包含菜单功能逻辑的菜单和触发器,用的时候只要加一些配置即可,可配置唤醒方式,单击,右键等事件,默认右键唤醒
属性名 | 描述 | 类型 | 默认值 |
---|---|---|---|
event | w3c规范的所有的事件字符串内容 | keyof HTMLElementEventMap | contextmenu |
用来根据hooks初始化时配置的操作事件唤醒菜单和传递参数。
注:为了无缝接入其它组件库,所有在Trigger上的参数都会被渲染到子元素的props上
接入Antd Table
属性名 | 描述 | 类型 | 默认值 |
---|---|---|---|
children | 该属性的子节点 | ReactNode | (必选) |
data | 需要传递给菜单的数据 | any | (必选) |
getEl | 如果是其它元素,可用此方法绑定任意触发DOM | () => HTMLDivElement | -- |
style | 外层的css样式 | CSSProperties | -- |
tag | 渲染Trigger的标签,用来适配一些其它框架 | string | div |
handle | 可在这里动态触发修改菜单配置实现动态菜单 | (e: MouseEvent, data: any) => void | -- |
展示菜单UI的组件和对应的菜单处理函数。
属性名 | 描述 | 类型 | 默认值 |
---|---|---|---|
menus | 菜单数据 | ContextMenuItem[] | (必选) |
onClick | 单击处理函数 ,分别是 event, data(触发器数据), node(菜单) | HandleClick | (必选) |
loadding | 自定义加载图表 | ReactNode | -- |
可通过初始化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添加触发器
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>
}
无缝接入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>
}
菜单渲染父节点。默认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>
}