5
5
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
6
6
*/
7
7
8
- import { Box } from '@mui/material' ;
8
+ import { Box , Tooltip } from '@mui/material' ;
9
9
import { Controls , ReactFlow , useEdgesState , useNodesState , useReactFlow } from '@xyflow/react' ;
10
10
import CenterFocusIcon from '@mui/icons-material/CenterFocusStrong' ;
11
- import { useCallback , useEffect , useLayoutEffect , useMemo , useRef } from 'react' ;
11
+ import { useCallback , useEffect , useLayoutEffect , useMemo , useRef , useState } from 'react' ;
12
12
import { reorderNetworkModificationTreeNodes , setModificationsDrawerOpen , setToggleOptions } from '../redux/actions' ;
13
13
import { useDispatch , useSelector } from 'react-redux' ;
14
- import { isSameNode } from './graph/util/model-functions' ;
14
+ import { isRootNode , isSameNode } from './graph/util/model-functions' ;
15
15
import PropTypes from 'prop-types' ;
16
16
import CropFreeIcon from '@mui/icons-material/CropFree' ;
17
17
import { nodeTypes } from './graph/util/model-constants' ;
@@ -31,21 +31,32 @@ import { groupIdSuffix } from './graph/nodes/labeled-group-node.type';
31
31
import { StudyDisplayMode } from './network-modification.type' ;
32
32
import { useSyncNavigationActions } from 'hooks/use-sync-navigation-actions' ;
33
33
import { NodeType } from './graph/tree-node.type' ;
34
-
35
- const styles = ( theme ) => ( {
36
- flexGrow : 1 ,
37
- height : '100%' ,
38
- backgroundColor : theme . reactflow . backgroundColor ,
39
- '.react-flow' : {
40
- '--xy-edge-stroke' : theme . reactflow . edge . stroke ,
41
- } ,
42
- '.react-flow__attribution a' : {
43
- color : theme . palette . text . primary ,
44
- } ,
45
- '.react-flow__attribution' : {
46
- backgroundColor : theme . palette . background . paper ,
47
- } ,
48
- } ) ;
34
+ import { useIntl } from 'react-intl' ;
35
+
36
+ const styles = {
37
+ modificationTree : ( theme ) => ( {
38
+ flexGrow : 1 ,
39
+ height : '100%' ,
40
+ backgroundColor : theme . reactflow . backgroundColor ,
41
+ '.react-flow' : {
42
+ '--xy-edge-stroke' : theme . reactflow . edge . stroke ,
43
+ } ,
44
+ '.react-flow__attribution a' : {
45
+ color : theme . palette . text . primary ,
46
+ } ,
47
+ '.react-flow__attribution' : {
48
+ backgroundColor : theme . palette . background . paper ,
49
+ } ,
50
+ } ) ,
51
+ labelBox : ( theme ) => ( {
52
+ flexGrow : 1 ,
53
+ display : 'flex' ,
54
+ alignItems : 'flex-end' ,
55
+ whiteSpace : 'normal' ,
56
+ wordBreak : 'break-word' ,
57
+ fontWeight : 'bold' ,
58
+ } ) ,
59
+ } ;
49
60
50
61
const NetworkModificationTree = ( { onNodeContextMenu, studyUuid, onTreePanelResize } ) => {
51
62
const dispatch = useDispatch ( ) ;
@@ -65,8 +76,13 @@ const NetworkModificationTree = ({ onNodeContextMenu, studyUuid, onTreePanelResi
65
76
const [ nodes , setNodes , onNodesChange ] = useNodesState ( [ ] ) ;
66
77
const [ edges , setEdges , onEdgesChange ] = useEdgesState ( [ ] ) ;
67
78
79
+ const [ tooltipOpen , setTooltipOpen ] = useState ( false ) ;
80
+ const [ tooltipContent , setTooltipContent ] = useState ( ) ;
81
+
68
82
const nodesMap = useMemo ( ( ) => new Map ( nodes . map ( ( n ) => [ n . id , n ] ) ) , [ nodes ] ) ;
69
83
84
+ const intl = useIntl ( ) ;
85
+
70
86
const updateNodePositions = useCallback ( ( ) => {
71
87
if ( treeModel && treeModel . treeNodes ?. length > 0 ) {
72
88
const [ treeNodeWithUpdatedPosition , securityGroupNodes ] = getTreeNodesWithUpdatedPositions (
@@ -304,55 +320,102 @@ const NetworkModificationTree = ({ onNodeContextMenu, studyUuid, onTreePanelResi
304
320
}
305
321
} , [ onTreePanelResize , handleFocusNode ] ) ;
306
322
323
+ const handleNodeMouseEnter = useCallback (
324
+ ( event , node ) => {
325
+ if ( ! node ?. data || isRootNode ( node ) ) {
326
+ return ;
327
+ }
328
+
329
+ const content = (
330
+ < Box style = { { whiteSpace : 'pre-line' } } >
331
+ < Box sx = { styles . labelBox } > { node . data . label } </ Box >
332
+ < Box >
333
+ { intl . formatMessage ( { id : 'nodeStatus' } ) } :{ ' ' }
334
+ { node . data . globalBuildStatus
335
+ ? intl . formatMessage ( { id : node . data . globalBuildStatus } )
336
+ : intl . formatMessage ( { id : 'NOT_BUILT' } ) }
337
+ </ Box >
338
+ < Box >
339
+ { intl . formatMessage ( { id : 'nodeType' } ) } : { intl . formatMessage ( { id : node . data . nodeType } ) }
340
+ </ Box >
341
+ </ Box >
342
+ ) ;
343
+
344
+ setTooltipContent ( content ) ;
345
+ setTooltipOpen ( true ) ;
346
+ } ,
347
+ [ intl ]
348
+ ) ;
349
+
350
+ const handleNodeMouseLeave = useCallback ( ( ) => {
351
+ setTooltipOpen ( false ) ;
352
+ } , [ ] ) ;
353
+
307
354
return (
308
- < Box sx = { styles } >
309
- < ReactFlow
310
- nodes = { nodes }
311
- edges = { edges }
312
- onNodesChange = { handleNodesChange }
313
- onEdgesChange = { onEdgesChange }
314
- fitView
315
- snapToGrid
316
- snapGrid = { snapGrid }
317
- onNodeContextMenu = { onNodeContextMenu }
318
- onNodeClick = { onNodeClick }
319
- elementsSelectable
320
- selectNodesOnDrag = { false }
321
- nodeTypes = { nodeTypes }
322
- minZoom = { 0.1 } // Lower value allows for more zoom out
323
- //maxZoom={2} // Higher value allows for more zoom in
324
- onNodeDragStop = { handlePostNodeDragging }
325
- nodeClickDistance = { 5 } // to avoid triggering onNodeDragStop instead of onNodeClick sometimes
326
- disableKeyboardA11y
327
- deleteKeyCode = { null }
328
- defaultEdgeOptions = { {
329
- type : 'smoothstep' ,
330
- pathOptions : {
331
- // TODO This negative offset and borderRadius values are needed to have round corners on the edge,
332
- // but because the nodes are not totally opaque, we can see the edges behind the nodes.
333
- // When the nodes are redesigned and hopefully the colors are set without transparency, we can use
334
- // the round edges by un-commenting the two lines below.
335
- //offset: -24,
336
- //borderRadius: 48,
355
+ < Box sx = { styles . modificationTree } >
356
+ < Tooltip
357
+ open = { tooltipOpen }
358
+ title = { tooltipContent }
359
+ componentsProps = { {
360
+ tooltip : {
361
+ sx : {
362
+ maxWidth : '720px' ,
363
+ } ,
337
364
} ,
338
365
} }
366
+ followCursor
367
+ placement = "right"
339
368
>
340
- < Controls
341
- position = "bottom-right"
342
- style = { { margin : '10px' , marginBottom : '30px' } }
343
- showZoom = { false }
344
- showInteractive = { false }
345
- showFitView = { false }
369
+ < ReactFlow
370
+ nodes = { nodes }
371
+ edges = { edges }
372
+ onNodesChange = { handleNodesChange }
373
+ onEdgesChange = { onEdgesChange }
374
+ fitView
375
+ snapToGrid
376
+ snapGrid = { snapGrid }
377
+ onNodeContextMenu = { onNodeContextMenu }
378
+ onNodeClick = { onNodeClick }
379
+ onNodeMouseEnter = { handleNodeMouseEnter }
380
+ onNodeMouseLeave = { handleNodeMouseLeave }
381
+ elementsSelectable
382
+ selectNodesOnDrag = { false }
383
+ nodeTypes = { nodeTypes }
384
+ minZoom = { 0.1 } // Lower value allows for more zoom out
385
+ //maxZoom={2} // Higher value allows for more zoom in
386
+ onNodeDragStop = { handlePostNodeDragging }
387
+ nodeClickDistance = { 5 } // to avoid triggering onNodeDragStop instead of onNodeClick sometimes
388
+ disableKeyboardA11y
389
+ deleteKeyCode = { null }
390
+ defaultEdgeOptions = { {
391
+ type : 'smoothstep' ,
392
+ pathOptions : {
393
+ // TODO This negative offset and borderRadius values are needed to have round corners on the edge,
394
+ // but because the nodes are not totally opaque, we can see the edges behind the nodes.
395
+ // When the nodes are redesigned and hopefully the colors are set without transparency, we can use
396
+ // the round edges by un-commenting the two lines below.
397
+ //offset: -24,
398
+ //borderRadius: 48,
399
+ } ,
400
+ } }
346
401
>
347
- < TreeControlButton titleId = "DisplayTheWholeTree" onClick = { fitView } >
348
- < CropFreeIcon />
349
- </ TreeControlButton >
350
- < TreeControlButton titleId = "CenterSelectedNode" onClick = { handleFocusNode } >
351
- < CenterFocusIcon />
352
- </ TreeControlButton >
353
- </ Controls >
354
- < RootNetworkPanel />
355
- </ ReactFlow >
402
+ < Controls
403
+ position = "bottom-right"
404
+ style = { { margin : '10px' , marginBottom : '30px' } }
405
+ showZoom = { false }
406
+ showInteractive = { false }
407
+ showFitView = { false }
408
+ >
409
+ < TreeControlButton titleId = "DisplayTheWholeTree" onClick = { fitView } >
410
+ < CropFreeIcon />
411
+ </ TreeControlButton >
412
+ < TreeControlButton titleId = "CenterSelectedNode" onClick = { handleFocusNode } >
413
+ < CenterFocusIcon />
414
+ </ TreeControlButton >
415
+ </ Controls >
416
+ < RootNetworkPanel />
417
+ </ ReactFlow >
418
+ </ Tooltip >
356
419
</ Box >
357
420
) ;
358
421
} ;
0 commit comments