11import { IPublicTypeContextMenuAction , IPublicEnumContextMenuType , IPublicTypeContextMenuItem , IPublicApiMaterial } from '@alilc/lowcode-types' ;
22import { IDesigner , INode } from './designer' ;
3- import { parseContextMenuAsReactNode , parseContextMenuProperties } from '@alilc/lowcode-utils' ;
3+ import { parseContextMenuAsReactNode , parseContextMenuProperties , uniqueId } from '@alilc/lowcode-utils' ;
44import { Menu } from '@alifd/next' ;
55import { engineConfig } from '@alilc/lowcode-editor-core' ;
66import './context-menu-actions.scss' ;
@@ -17,7 +17,100 @@ export interface IContextMenuActions {
1717 adjustMenuLayout : IPublicApiMaterial [ 'adjustContextMenuLayout' ] ;
1818}
1919
20- let destroyFn : Function | undefined ;
20+ let adjustMenuLayoutFn : Function = ( actions : IPublicTypeContextMenuAction [ ] ) => actions ;
21+
22+ export class GlobalContextMenuActions {
23+ enableContextMenu : boolean ;
24+
25+ dispose : Function [ ] ;
26+
27+ contextMenuActionsMap : Map < string , ContextMenuActions > = new Map ( ) ;
28+
29+ constructor ( ) {
30+ this . dispose = [ ] ;
31+
32+ engineConfig . onGot ( 'enableContextMenu' , ( enable ) => {
33+ if ( this . enableContextMenu === enable ) {
34+ return ;
35+ }
36+ this . enableContextMenu = enable ;
37+ this . dispose . forEach ( d => d ( ) ) ;
38+ if ( enable ) {
39+ this . initEvent ( ) ;
40+ }
41+ } ) ;
42+ }
43+
44+ handleContextMenu = (
45+ event : MouseEvent ,
46+ ) => {
47+ event . stopPropagation ( ) ;
48+ event . preventDefault ( ) ;
49+
50+ const actions : IPublicTypeContextMenuAction [ ] = [ ] ;
51+ this . contextMenuActionsMap . forEach ( ( contextMenu ) => {
52+ actions . push ( ...contextMenu . actions ) ;
53+ } ) ;
54+
55+ let destroyFn : Function | undefined ;
56+
57+ const destroy = ( ) => {
58+ destroyFn ?.( ) ;
59+ } ;
60+
61+ const menus : IPublicTypeContextMenuItem [ ] = parseContextMenuProperties ( actions , {
62+ nodes : [ ] ,
63+ destroy,
64+ event,
65+ } ) ;
66+
67+ if ( ! menus . length ) {
68+ return ;
69+ }
70+
71+ const layoutMenu = adjustMenuLayoutFn ( menus ) ;
72+
73+ const menuNode = parseContextMenuAsReactNode ( layoutMenu , {
74+ destroy,
75+ nodes : [ ] ,
76+ } ) ;
77+
78+ const target = event . target ;
79+
80+ const { top, left } = target ?. getBoundingClientRect ( ) ;
81+
82+ const menuInstance = Menu . create ( {
83+ target : event . target ,
84+ offset : [ event . clientX - left , event . clientY - top ] ,
85+ children : menuNode ,
86+ className : 'engine-context-menu' ,
87+ } ) ;
88+
89+ destroyFn = ( menuInstance as any ) . destroy ;
90+ } ;
91+
92+ initEvent ( ) {
93+ this . dispose . push (
94+ ( ( ) => {
95+ const handleContextMenu = ( e : MouseEvent ) => {
96+ this . handleContextMenu ( e ) ;
97+ } ;
98+
99+ document . addEventListener ( 'contextmenu' , handleContextMenu ) ;
100+
101+ return ( ) => {
102+ document . removeEventListener ( 'contextmenu' , handleContextMenu ) ;
103+ } ;
104+ } ) ( ) ,
105+ ) ;
106+ }
107+
108+ registerContextMenuActions ( contextMenu : ContextMenuActions ) {
109+ this . contextMenuActionsMap . set ( contextMenu . id , contextMenu ) ;
110+ }
111+ }
112+
113+ const globalContextMenuActions = new GlobalContextMenuActions ( ) ;
21114
22115export class ContextMenuActions implements IContextMenuActions {
23116 actions : IPublicTypeContextMenuAction [ ] = [ ] ;
@@ -28,6 +121,8 @@ export class ContextMenuActions implements IContextMenuActions {
28121
29122 enableContextMenu : boolean ;
30123
124+ id : string = uniqueId ( 'contextMenu' ) ; ;
125+
31126 constructor ( designer : IDesigner ) {
32127 this . designer = designer ;
33128 this . dispose = [ ] ;
@@ -42,6 +137,8 @@ export class ContextMenuActions implements IContextMenuActions {
42137 this . initEvent ( ) ;
43138 }
44139 } ) ;
140+
141+ globalContextMenuActions . registerContextMenuActions ( this ) ;
45142 }
46143
47144 handleContextMenu = (
@@ -57,7 +154,7 @@ export class ContextMenuActions implements IContextMenuActions {
57154 const { bounds } = designer . project . simulator ?. viewport || { bounds : { left : 0 , top : 0 } } ;
58155 const { left : simulatorLeft , top : simulatorTop } = bounds ;
59156
60- destroyFn ?. ( ) ;
157+ let destroyFn : Function | undefined ;
61158
62159 const destroy = ( ) => {
63160 destroyFn ?.( ) ;
@@ -66,13 +163,14 @@ export class ContextMenuActions implements IContextMenuActions {
66163 const menus : IPublicTypeContextMenuItem [ ] = parseContextMenuProperties ( actions , {
67164 nodes : nodes . map ( d => designer . shellModelFactory . createNode ( d ) ! ) ,
68165 destroy,
166+ event,
69167 } ) ;
70168
71169 if ( ! menus . length ) {
72170 return ;
73171 }
74172
75- const layoutMenu = designer . contextMenuActions . adjustMenuLayoutFn ( menus ) ;
173+ const layoutMenu = adjustMenuLayoutFn ( menus ) ;
76174
77175 const menuNode = parseContextMenuAsReactNode ( layoutMenu , {
78176 destroy,
@@ -111,22 +209,9 @@ export class ContextMenuActions implements IContextMenuActions {
111209 const nodes = designer . currentSelection . getNodes ( ) ;
112210 this . handleContextMenu ( nodes , originalEvent ) ;
113211 } ) ,
114- ( ( ) => {
115- const handleContextMenu = ( e : MouseEvent ) => {
116- this . handleContextMenu ( [ ] , e ) ;
117- } ;
118-
119- document . addEventListener ( 'contextmenu' , handleContextMenu ) ;
120-
121- return ( ) => {
122- document . removeEventListener ( 'contextmenu' , handleContextMenu ) ;
123- } ;
124- } ) ( ) ,
125212 ) ;
126213 }
127214
128- adjustMenuLayoutFn : ( actions : IPublicTypeContextMenuItem [ ] ) => IPublicTypeContextMenuItem [ ] = ( actions ) => actions ;
129-
130215 addMenuAction ( action : IPublicTypeContextMenuAction ) {
131216 this . actions . push ( {
132217 type : IPublicEnumContextMenuType . MENU_ITEM ,
@@ -142,6 +227,6 @@ export class ContextMenuActions implements IContextMenuActions {
142227 }
143228
144229 adjustMenuLayout ( fn : ( actions : IPublicTypeContextMenuItem [ ] ) => IPublicTypeContextMenuItem [ ] ) {
145- this . adjustMenuLayoutFn = fn ;
230+ adjustMenuLayoutFn = fn ;
146231 }
147232}
0 commit comments