11/*******************************************************************************
2- * Copyright (c) 2023, 2025 Obeo.
2+ * Copyright (c) 2023, 2026 Obeo.
33 * This program and the accompanying materials
44 * are made available under the terms of the Eclipse Public License v2.0
55 * which accompanies this distribution, and is available at
1111 * Obeo - initial API and implementation
1212 *******************************************************************************/
1313
14- import { gql , useMutation , useQuery } from '@apollo/client' ;
14+ import { gql , useQuery } from '@apollo/client' ;
1515import { IconOverlay , useMultiToast } from '@eclipse-sirius/sirius-components-core' ;
1616import ListItemIcon from '@mui/material/ListItemIcon' ;
1717import Menu from '@mui/material/Menu' ;
1818import MenuItem from '@mui/material/MenuItem' ;
1919import Typography from '@mui/material/Typography' ;
20- import { Edge , Node , useReactFlow , useStoreApi , XYPosition } from '@xyflow/react' ;
20+ import { Edge , Node , useReactFlow } from '@xyflow/react' ;
2121import { memo , useContext , useEffect } from 'react' ;
2222import { DiagramContext } from '../../contexts/DiagramContext' ;
2323import { DiagramContextValue } from '../../contexts/DiagramContext.types' ;
24- import { DiagramDialogVariable } from '../../dialog/DialogContextExtensionPoints.types' ;
25- import { useDialog } from '../../dialog/useDialog' ;
2624import { EdgeData , NodeData } from '../DiagramRenderer.types' ;
27- import { isCursorNearCenterOfTheNode } from '../edge/EdgeLayout' ;
2825import {
2926 ConnectorContextualMenuProps ,
3027 GetConnectorToolsData ,
3128 GetConnectorToolsVariables ,
3229 GQLDiagramDescription ,
33- GQLErrorPayload ,
34- GQLInvokeSingleClickOnTwoDiagramElementsToolData ,
35- GQLInvokeSingleClickOnTwoDiagramElementsToolInput ,
36- GQLInvokeSingleClickOnTwoDiagramElementsToolPayload ,
37- GQLInvokeSingleClickOnTwoDiagramElementsToolSuccessPayload ,
38- GQLInvokeSingleClickOnTwoDiagramElementsToolVariables ,
3930 GQLRepresentationDescription ,
4031 GQLTool ,
41- GQLToolVariable ,
4232} from './ConnectorContextualMenu.types' ;
4333import { useConnector } from './useConnector' ;
44- import { GQLSingleClickOnTwoDiagramElementsTool } from './useConnector.types ' ;
34+ import { useSingleClickOnTwoDiagramElementTool } from './useSingleClickOnTwoDiagramElementTool ' ;
4535
4636export const getConnectorToolsQuery = gql `
4737 query getConnectorTools(
@@ -74,56 +64,18 @@ export const getConnectorToolsQuery = gql`
7464 }
7565` ;
7666
77- export const invokeSingleClickOnTwoDiagramElementsToolMutation = gql `
78- mutation invokeSingleClickOnTwoDiagramElementsTool($input: InvokeSingleClickOnTwoDiagramElementsToolInput!) {
79- invokeSingleClickOnTwoDiagramElementsTool(input: $input) {
80- __typename
81- ... on InvokeSingleClickOnTwoDiagramElementsToolSuccessPayload {
82- id
83- newSelection {
84- entries {
85- id
86- }
87- }
88- messages {
89- body
90- level
91- }
92- }
93- ... on ErrorPayload {
94- messages {
95- body
96- level
97- }
98- }
99- }
100- }
101- ` ;
102-
10367const isDiagramDescription = (
10468 representationDescription : GQLRepresentationDescription
10569) : representationDescription is GQLDiagramDescription => representationDescription . __typename === 'DiagramDescription' ;
10670
107- const isErrorPayload = ( payload : GQLInvokeSingleClickOnTwoDiagramElementsToolPayload ) : payload is GQLErrorPayload =>
108- payload . __typename === 'ErrorPayload' ;
109- const isSuccessPayload = (
110- payload : GQLInvokeSingleClickOnTwoDiagramElementsToolPayload
111- ) : payload is GQLInvokeSingleClickOnTwoDiagramElementsToolSuccessPayload =>
112- payload . __typename === 'InvokeSingleClickOnTwoDiagramElementsToolSuccessPayload' ;
113-
114- const isSingleClickOnTwoDiagramElementsTool = ( tool : GQLTool ) : tool is GQLSingleClickOnTwoDiagramElementsTool =>
115- tool . __typename === 'SingleClickOnTwoDiagramElementsTool' ;
116-
11771const ConnectorContextualMenuComponent = memo ( ( { } : ConnectorContextualMenuProps ) => {
118- const { editingContextId, diagramId, registerPostToolSelection } = useContext < DiagramContextValue > ( DiagramContext ) ;
119- const store = useStoreApi < Node < NodeData > , Edge < EdgeData > > ( ) ;
72+ const { editingContextId, diagramId } = useContext < DiagramContextValue > ( DiagramContext ) ;
12073 const { connection, position, onConnectorContextualMenuClose, addTempConnectionLine, removeTempConnectionLine } =
12174 useConnector ( ) ;
12275 const { addMessages, addErrorMessage } = useMultiToast ( ) ;
123-
124- const { showDialog, isOpened } = useDialog ( ) ;
125-
126- const { getNodes, screenToFlowPosition } = useReactFlow < Node < NodeData > , Edge < EdgeData > > ( ) ;
76+ const { screenToFlowPosition } = useReactFlow < Node < NodeData > , Edge < EdgeData > > ( ) ;
77+ const { invokeConnectorTool, data : invokeSingleClickOnTwoDiagramElementToolCalled } =
78+ useSingleClickOnTwoDiagramElementTool ( ) ;
12779
12880 const connectionSource : HTMLElement | null = connection
12981 ? document . querySelector ( `[data-id="${ connection . source } "]` )
@@ -147,116 +99,12 @@ const ConnectorContextualMenuComponent = memo(({}: ConnectorContextualMenuProps)
14799 skip : ! connectionSource || ! connectionTarget ,
148100 } ) ;
149101
150- const invokeOpenSelectionDialog = ( tool : GQLSingleClickOnTwoDiagramElementsTool ) => {
151- const onConfirm = ( variables : GQLToolVariable [ ] ) => {
152- invokeToolMutation ( tool , variables ) ;
153- } ;
154-
155- const onClose = ( ) => {
156- onShouldConnectorContextualMenuClose ( ) ;
157- } ;
158-
159- const sourceNode = getNodes ( ) . find ( ( node ) => node . id === sourceDiagramElementId ) ;
160- const targetNode = getNodes ( ) . find ( ( node ) => node . id === targetDiagramElementId ) ;
161- if ( sourceNode && targetNode ) {
162- const variables : DiagramDialogVariable [ ] = [
163- { name : 'targetObjectId' , value : sourceNode . data . targetObjectId } ,
164- { name : 'sourceDiagramElementTargetObjectId' , value : sourceNode . data . targetObjectId } ,
165- { name : 'targetDiagramElementTargetObjectId' , value : targetNode . data . targetObjectId } ,
166- ] ;
167- showDialog ( tool . dialogDescriptionId , variables , onConfirm , onClose ) ;
168- }
169- } ;
170-
171102 useEffect ( ( ) => {
172103 if ( error ) {
173104 addErrorMessage ( error . message ) ;
174105 }
175106 } , [ error ] ) ;
176107
177- const [
178- invokeSingleClickOnTwoDiagramElementsTool ,
179- {
180- data : invokeSingleClickOnTwoDiagramElementToolData ,
181- error : invokeSingleClickOnTwoDiagramElementToolError ,
182- called : invokeSingleClickOnTwoDiagramElementToolCalled ,
183- reset,
184- } ,
185- ] = useMutation <
186- GQLInvokeSingleClickOnTwoDiagramElementsToolData ,
187- GQLInvokeSingleClickOnTwoDiagramElementsToolVariables
188- > ( invokeSingleClickOnTwoDiagramElementsToolMutation ) ;
189-
190- const invokeTool = ( tool : GQLTool ) => {
191- if ( isSingleClickOnTwoDiagramElementsTool ( tool ) ) {
192- if ( tool . dialogDescriptionId ) {
193- if ( ! isOpened ) {
194- invokeOpenSelectionDialog ( tool ) ;
195- }
196- } else {
197- invokeToolMutation ( tool , [ ] ) ;
198- }
199- }
200- } ;
201-
202- const invokeToolMutation = ( tool : GQLTool , variables : GQLToolVariable [ ] ) => {
203- let targetHandlePosition : XYPosition = { x : 0 , y : 0 } ;
204- if ( position ) {
205- targetHandlePosition = screenToFlowPosition ( { x : position . x , y : position . y } ) ;
206- }
207- const target = store . getState ( ) . nodeLookup . get ( targetDiagramElementId ) ;
208- if ( target && position ) {
209- const isNearCenter = isCursorNearCenterOfTheNode ( target , {
210- x : targetHandlePosition . x ,
211- y : targetHandlePosition . y ,
212- } ) ;
213-
214- if ( isNearCenter ) {
215- targetHandlePosition = { x : 0 , y : 0 } ;
216- }
217- }
218-
219- const input : GQLInvokeSingleClickOnTwoDiagramElementsToolInput = {
220- id : crypto . randomUUID ( ) ,
221- editingContextId,
222- representationId : diagramId ,
223- diagramSourceElementId : sourceDiagramElementId ,
224- diagramTargetElementId : targetDiagramElementId ,
225- toolId : tool . id ,
226- sourcePositionX : 0 ,
227- sourcePositionY : 0 ,
228- targetPositionX : targetHandlePosition . x ,
229- targetPositionY : targetHandlePosition . y ,
230- variables,
231- } ;
232- invokeSingleClickOnTwoDiagramElementsTool ( { variables : { input } } ) ;
233- } ;
234-
235- const onShouldConnectorContextualMenuClose = ( ) => {
236- onConnectorContextualMenuClose ( ) ;
237- reset ( ) ;
238- } ;
239-
240- useEffect ( ( ) => {
241- if ( invokeSingleClickOnTwoDiagramElementToolData ) {
242- const payload = invokeSingleClickOnTwoDiagramElementToolData . invokeSingleClickOnTwoDiagramElementsTool ;
243- if ( isErrorPayload ( payload ) ) {
244- addMessages ( payload . messages ) ;
245- }
246- if ( isSuccessPayload ( payload ) ) {
247- const { id, newSelection } = payload ;
248- if ( newSelection ?. entries . length ?? 0 > 0 ) {
249- registerPostToolSelection ( id , newSelection ) ;
250- }
251- addMessages ( payload . messages ) ;
252- onShouldConnectorContextualMenuClose ( ) ;
253- }
254- }
255- if ( invokeSingleClickOnTwoDiagramElementToolError ?. message ) {
256- addErrorMessage ( invokeSingleClickOnTwoDiagramElementToolError . message ) ;
257- }
258- } , [ invokeSingleClickOnTwoDiagramElementToolData , invokeSingleClickOnTwoDiagramElementToolError ] ) ;
259-
260108 const connectorTools : GQLTool [ ] = [ ] ;
261109 const representationDescription : GQLRepresentationDescription | null | undefined =
262110 data ?. viewer . editingContext ?. representation ?. description ;
@@ -272,6 +120,7 @@ const ConnectorContextualMenuComponent = memo(({}: ConnectorContextualMenuProps)
272120
273121 useEffect ( ( ) => {
274122 if ( ! loading && connection && data && connectorTools . length === 0 ) {
123+ onConnectorContextualMenuClose ( ) ;
275124 addMessages ( [ { body : 'No edge found between source and target selected' , level : 'WARNING' } ] ) ;
276125 }
277126 } , [ loading , data , connection , connectorTools . length ] ) ;
@@ -284,7 +133,12 @@ const ConnectorContextualMenuComponent = memo(({}: ConnectorContextualMenuProps)
284133 if ( ! invokeSingleClickOnTwoDiagramElementToolCalled && connectorTools . length === 1 && connectorTools [ 0 ] ) {
285134 invokeTool ( connectorTools [ 0 ] ) ;
286135 }
287- } , [ connectorTools ] ) ;
136+ } , [ connectorTools . length ] ) ;
137+
138+ const invokeTool = ( tool : GQLTool ) => {
139+ const { x : cursorPositionX , y : cursorPositionY } = screenToFlowPosition ( { x : position . x , y : position . y } ) ;
140+ invokeConnectorTool ( tool , sourceDiagramElementId , targetDiagramElementId , cursorPositionX , cursorPositionY ) ;
141+ } ;
288142
289143 if ( ! data || connectorTools . length <= 1 ) {
290144 return null ;
@@ -293,7 +147,7 @@ const ConnectorContextualMenuComponent = memo(({}: ConnectorContextualMenuProps)
293147 return (
294148 < Menu
295149 open = { ! ! connection }
296- onClose = { onShouldConnectorContextualMenuClose }
150+ onClose = { onConnectorContextualMenuClose }
297151 anchorEl = { connectionTarget }
298152 anchorReference = "anchorPosition"
299153 data-testid = "connectorContextualMenu"
0 commit comments