@@ -57,6 +57,20 @@ function useCytoscape(options: cytoscape.CytoscapeOptions) {
5757 return [ ref , cy ] as [ React . MutableRefObject < any > , cytoscape . Core | undefined ] ;
5858}
5959
60+ function rotatePoint (
61+ { x, y } : { x : number ; y : number } ,
62+ degreesRotated : number
63+ ) {
64+ const radiansPerDegree = Math . PI / 180 ;
65+ const θ = radiansPerDegree * degreesRotated ;
66+ const cosθ = Math . cos ( θ ) ;
67+ const sinθ = Math . sin ( θ ) ;
68+ return {
69+ x : x * cosθ - y * sinθ ,
70+ y : x * sinθ + y * cosθ
71+ } ;
72+ }
73+
6074function getLayoutOptions (
6175 selectedRoots : string [ ] ,
6276 height : number ,
@@ -71,10 +85,11 @@ function getLayoutOptions(
7185 animate : true ,
7286 animationEasing : animationOptions . easing ,
7387 animationDuration : animationOptions . duration ,
74- // Rotate nodes from top -> bottom to display left -> right
7588 // @ts -ignore
76- transform : ( node : any , { x, y } : cytoscape . Position ) => ( { x : y , y : - x } ) ,
77- // swap width/height of boundingBox to compensation for the rotation
89+ // Rotate nodes counter-clockwise to transform layout from top→bottom to left→right.
90+ // The extra 5° achieves the effect of separating overlapping taxi-styled edges.
91+ transform : ( node : any , pos : cytoscape . Position ) => rotatePoint ( pos , - 95 ) ,
92+ // swap width/height of boundingBox to compensate for the rotation
7893 boundingBox : { x1 : 0 , y1 : 0 , w : height , h : width }
7994 } ;
8095}
@@ -109,20 +124,31 @@ export function Cytoscape({
109124 // is required and can trigger rendering when changed.
110125 const divStyle = { ...style , height } ;
111126
112- const dataHandler = useCallback < cytoscape . EventHandler > (
113- event => {
127+ const resetConnectedEdgeStyle = useCallback (
128+ ( node ?: cytoscape . NodeSingular ) => {
114129 if ( cy ) {
115130 cy . edges ( ) . removeClass ( 'highlight' ) ;
116131
117- if ( serviceName ) {
118- const focusedNode = cy . getElementById ( serviceName ) ;
119- focusedNode . connectedEdges ( ) . addClass ( 'highlight' ) ;
132+ if ( node ) {
133+ node . connectedEdges ( ) . addClass ( 'highlight' ) ;
120134 }
135+ }
136+ } ,
137+ [ cy ]
138+ ) ;
121139
122- // Add the "primary" class to the node if its id matches the serviceName.
123- if ( cy . nodes ( ) . length > 0 && serviceName ) {
124- cy . nodes ( ) . removeClass ( 'primary' ) ;
125- cy . getElementById ( serviceName ) . addClass ( 'primary' ) ;
140+ const dataHandler = useCallback < cytoscape . EventHandler > (
141+ event => {
142+ if ( cy ) {
143+ if ( serviceName ) {
144+ resetConnectedEdgeStyle ( cy . getElementById ( serviceName ) ) ;
145+ // Add the "primary" class to the node if its id matches the serviceName.
146+ if ( cy . nodes ( ) . length > 0 ) {
147+ cy . nodes ( ) . removeClass ( 'primary' ) ;
148+ cy . getElementById ( serviceName ) . addClass ( 'primary' ) ;
149+ }
150+ } else {
151+ resetConnectedEdgeStyle ( ) ;
126152 }
127153 if ( event . cy . elements ( ) . length > 0 ) {
128154 const selectedRoots = selectRoots ( event . cy ) ;
@@ -141,7 +167,7 @@ export function Cytoscape({
141167 }
142168 }
143169 } ,
144- [ cy , serviceName , height , width ]
170+ [ cy , resetConnectedEdgeStyle , serviceName , height , width ]
145171 ) ;
146172
147173 // Trigger a custom "data" event when data changes
@@ -162,12 +188,20 @@ export function Cytoscape({
162188 event . target . removeClass ( 'hover' ) ;
163189 event . target . connectedEdges ( ) . removeClass ( 'nodeHover' ) ;
164190 } ;
191+ const selectHandler : cytoscape . EventHandler = event => {
192+ resetConnectedEdgeStyle ( event . target ) ;
193+ } ;
194+ const unselectHandler : cytoscape . EventHandler = event => {
195+ resetConnectedEdgeStyle ( ) ;
196+ } ;
165197
166198 if ( cy ) {
167199 cy . on ( 'data' , dataHandler ) ;
168200 cy . ready ( dataHandler ) ;
169201 cy . on ( 'mouseover' , 'edge, node' , mouseoverHandler ) ;
170202 cy . on ( 'mouseout' , 'edge, node' , mouseoutHandler ) ;
203+ cy . on ( 'select' , 'node' , selectHandler ) ;
204+ cy . on ( 'unselect' , 'node' , unselectHandler ) ;
171205 }
172206
173207 return ( ) => {
@@ -181,7 +215,7 @@ export function Cytoscape({
181215 cy . removeListener ( 'mouseout' , 'edge, node' , mouseoutHandler ) ;
182216 }
183217 } ;
184- } , [ cy , dataHandler , serviceName ] ) ;
218+ } , [ cy , dataHandler , resetConnectedEdgeStyle , serviceName ] ) ;
185219
186220 return (
187221 < CytoscapeContext . Provider value = { cy } >
0 commit comments