1+ import { IPublicTypeContextMenuAction , IPublicEnumContextMenuType , IPublicTypeContextMenuItem , IPublicApiMaterial } from '@alilc/lowcode-types' ;
2+ import { IDesigner , INode } from './designer' ;
3+ import { parseContextMenuAsReactNode , parseContextMenuProperties } from '@alilc/lowcode-utils' ;
4+ import { Menu } from '@alifd/next' ;
5+ import { engineConfig } from '@alilc/lowcode-editor-core' ;
6+ import './context-menu-actions.scss' ;
7+
8+ export interface IContextMenuActions {
9+ actions : IPublicTypeContextMenuAction [ ] ;
10+
11+ adjustMenuLayoutFn : ( actions : IPublicTypeContextMenuItem [ ] ) => IPublicTypeContextMenuItem [ ] ;
12+
13+ addMenuAction : IPublicApiMaterial [ 'addContextMenuOption' ] ;
14+
15+ removeMenuAction : IPublicApiMaterial [ 'removeContextMenuOption' ] ;
16+
17+ adjustMenuLayout : IPublicApiMaterial [ 'adjustContextMenuLayout' ] ;
18+ }
19+
20+ export class ContextMenuActions implements IContextMenuActions {
21+ actions : IPublicTypeContextMenuAction [ ] = [ ] ;
22+
23+ designer : IDesigner ;
24+
25+ dispose : Function [ ] ;
26+
27+ enableContextMenu : boolean ;
28+
29+ constructor ( designer : IDesigner ) {
30+ this . designer = designer ;
31+ this . dispose = [ ] ;
32+
33+ engineConfig . onGot ( 'enableContextMenu' , ( enable ) => {
34+ if ( this . enableContextMenu === enable ) {
35+ return ;
36+ }
37+ this . enableContextMenu = enable ;
38+ this . dispose . forEach ( d => d ( ) ) ;
39+ if ( enable ) {
40+ this . initEvent ( ) ;
41+ }
42+ } ) ;
43+ }
44+
45+ handleContextMenu = (
46+ nodes : INode [ ] ,
47+ event : MouseEvent ,
48+ ) => {
49+ const designer = this . designer ;
50+ event . stopPropagation ( ) ;
51+ event . preventDefault ( ) ;
52+
53+ const actions = designer . contextMenuActions . actions ;
54+
55+ const { bounds } = designer . project . simulator ?. viewport || { bounds : { left : 0 , top : 0 } } ;
56+ const { left : simulatorLeft , top : simulatorTop } = bounds ;
57+
58+ let destroyFn : Function | undefined ;
59+
60+ const destroy = ( ) => {
61+ destroyFn ?.( ) ;
62+ } ;
63+
64+ const menus : IPublicTypeContextMenuItem [ ] = parseContextMenuProperties ( actions , {
65+ nodes : nodes . map ( d => designer . shellModelFactory . createNode ( d ) ! ) ,
66+ destroy,
67+ } ) ;
68+
69+ if ( ! menus . length ) {
70+ return ;
71+ }
72+
73+ const layoutMenu = designer . contextMenuActions . adjustMenuLayoutFn ( menus ) ;
74+
75+ const menuNode = parseContextMenuAsReactNode ( layoutMenu , {
76+ destroy,
77+ nodes : nodes . map ( d => designer . shellModelFactory . createNode ( d ) ! ) ,
78+ designer,
79+ } ) ;
80+
81+ const target = event . target ;
82+
83+ const { top, left } = target ?. getBoundingClientRect ( ) ;
84+
85+ const menuInstance = Menu . create ( {
86+ target : event . target ,
87+ offset : [ event . clientX - left + simulatorLeft , event . clientY - top + simulatorTop ] ,
88+ children : menuNode ,
89+ className : 'engine-context-menu' ,
90+ } ) ;
91+
92+ destroyFn = ( menuInstance as any ) . destroy ;
93+ } ;
94+
95+ initEvent ( ) {
96+ const designer = this . designer ;
97+ this . dispose . push (
98+ designer . editor . eventBus . on ( 'designer.builtinSimulator.contextmenu' , ( {
99+ node,
100+ originalEvent,
101+ } : {
102+ node : INode ;
103+ originalEvent : MouseEvent ;
104+ } ) => {
105+ // 如果右键的节点不在 当前选中的节点中,选中该节点
106+ if ( ! designer . currentSelection . has ( node . id ) ) {
107+ designer . currentSelection . select ( node . id ) ;
108+ }
109+ const nodes = designer . currentSelection . getNodes ( ) ;
110+ this . handleContextMenu ( nodes , originalEvent ) ;
111+ } ) ,
112+ ( ( ) => {
113+ const handleContextMenu = ( e : MouseEvent ) => {
114+ this . handleContextMenu ( [ ] , e ) ;
115+ } ;
116+
117+ document . addEventListener ( 'contextmenu' , handleContextMenu ) ;
118+
119+ return ( ) => {
120+ document . removeEventListener ( 'contextmenu' , handleContextMenu ) ;
121+ } ;
122+ } ) ( ) ,
123+ ) ;
124+ }
125+
126+ adjustMenuLayoutFn : ( actions : IPublicTypeContextMenuItem [ ] ) => IPublicTypeContextMenuItem [ ] = ( actions ) => actions ;
127+
128+ addMenuAction ( action : IPublicTypeContextMenuAction ) {
129+ this . actions . push ( {
130+ type : IPublicEnumContextMenuType . MENU_ITEM ,
131+ ...action ,
132+ } ) ;
133+ }
134+
135+ removeMenuAction ( name : string ) {
136+ const i = this . actions . findIndex ( ( action ) => action . name === name ) ;
137+ if ( i > - 1 ) {
138+ this . actions . splice ( i , 1 ) ;
139+ }
140+ }
141+
142+ adjustMenuLayout ( fn : ( actions : IPublicTypeContextMenuItem [ ] ) => IPublicTypeContextMenuItem [ ] ) {
143+ this . adjustMenuLayoutFn = fn ;
144+ }
145+ }
0 commit comments