@@ -17,6 +17,7 @@ import "./topology-view.css";
17
17
import { useColorProvider } from "colors" ;
18
18
import { useD3Zoom } from "chartutils" ;
19
19
import { dedupeItems } from "aggregate" ;
20
+ import { useEventBus } from "eventbus" ;
20
21
21
22
/**
22
23
* The goal of this component is to show a more accurate topology view from the perspective of actual network connections.
@@ -164,6 +165,7 @@ export const TopologyView: React.FC<TopologyViewProps> = ({
164
165
. attr ( "data-nodeid-target" , d => d . target . data . id )
165
166
. transition ( tr )
166
167
. attr ( "d" , diagonal )
168
+ . attr ( "stroke" , "#fff" )
167
169
. attr ( "stroke-width" , 1.5 ) ;
168
170
169
171
group
@@ -191,7 +193,9 @@ export const TopologyView: React.FC<TopologyViewProps> = ({
191
193
. attr ( "cx" , ( d : any ) => d . x )
192
194
. attr ( "cy" , ( d : any ) => d . y )
193
195
. attr ( "r" , d => getRadius ( d . data . group ) )
194
- . attr ( "fill" , d => colors . assign ( d . data . group ) ) ;
196
+ . attr ( "fill" , d => colors . assign ( d . data . group ) )
197
+ . attr ( "stroke" , "#fff" )
198
+ . attr ( "stroke-width" , 2 ) ;
195
199
196
200
group
197
201
. select ( ".texts" )
@@ -354,9 +358,12 @@ export const TopologyView: React.FC<TopologyViewProps> = ({
354
358
update => update ,
355
359
exit => exit . transition ( tr ) . attr ( "stroke-width" , 0 ) . remove ( )
356
360
)
361
+ . attr ( "data-nodeid-source" , d => d . source . tree . data . id )
362
+ . attr ( "data-nodeid-target" , d => d . target . tree . data . id )
357
363
. transition ( tr )
358
364
. attr ( "d" , diagonal )
359
- . attr ( "stroke-width" , 1.5 ) ;
365
+ . attr ( "stroke-width" , 1.5 )
366
+ . attr ( "stroke" , "#fff" ) ;
360
367
}
361
368
362
369
const monolithBuiltRegions = new Map < string , Region > ( ) ;
@@ -400,6 +407,85 @@ export const TopologyView: React.FC<TopologyViewProps> = ({
400
407
401
408
useD3Zoom ( svgRef ) ;
402
409
410
+ const eventBus = useEventBus ( ) ;
411
+ useEffect ( ( ) => {
412
+ const rxColor = "#0f0" ;
413
+ const txColor = "#00f" ;
414
+
415
+ function animateNode (
416
+ node : d3 . Selection < any , d3 . HierarchyNode < TreeNode > , any , any > ,
417
+ color : string
418
+ ) {
419
+ if ( node . empty ( ) ) {
420
+ return ;
421
+ }
422
+ const data = node . data ( ) [ 0 ] as d3 . HierarchyNode < TreeNode > ;
423
+ const endRadius = data ? getRadius ( data . data . group ) : 20 ;
424
+ let radiusCurrent = parseFloat ( node . attr ( "r" ) ) ;
425
+ let colorCurrent = d3 . color ( node . attr ( "stroke" ) ) ;
426
+ if ( isNaN ( radiusCurrent ) ) {
427
+ radiusCurrent = 0 ;
428
+ }
429
+ const newRadius = Math . max ( Math . min ( radiusCurrent + 5 , 40 ) , endRadius ) ;
430
+ const newColor = d3 . interpolateRgb ( colorCurrent ?. formatRgb ( ) ?? "#fff" , color ) ( 0.5 ) ;
431
+ node . transition ( "highlight" )
432
+ . duration ( 333 )
433
+ . ease ( d3 . easeCubicOut )
434
+ . attrTween ( "stroke" , ( ) => d3 . interpolateRgb ( newColor , "#fff" ) )
435
+ . attrTween ( "stroke-width" , ( ) => t => d3 . interpolateNumber ( 4 , 1.5 ) ( t ) . toString ( ) )
436
+ . attrTween (
437
+ "r" ,
438
+ ( ) => t => d3 . interpolateNumber ( newRadius , endRadius ) ( t ) . toString ( )
439
+ ) ;
440
+ }
441
+
442
+ function animateLinks (
443
+ links : d3 . Selection < any , d3 . HierarchyLink < TreeNode > , any , any > ,
444
+ color : string
445
+ ) {
446
+ links
447
+ . transition ( "highlight" )
448
+ . duration ( 333 )
449
+ . ease ( d3 . easeCubicOut )
450
+ . attrTween ( "stroke" , function ( ) {
451
+ const link = d3 . select < d3 . BaseType , d3 . HierarchyLink < TreeNode > > (
452
+ this
453
+ ) as d3 . Selection < any , d3 . HierarchyLink < TreeNode > , any , unknown > ;
454
+ let colorCurrent = d3 . color ( link . attr ( "stroke" ) ) ;
455
+ const newColor = d3 . interpolateRgb (
456
+ colorCurrent ?. formatRgb ( ) ?? "#fff" ,
457
+ color
458
+ ) ( 0.5 ) ;
459
+
460
+ return d3 . interpolateRgb ( newColor , "#fff" ) ;
461
+ } )
462
+ . attrTween ( "stroke-width" , ( ) => t => d3 . interpolateNumber ( 4 , 1.5 ) ( t ) . toString ( ) ) ;
463
+ }
464
+
465
+ const sub = eventBus . subscribe ( event => {
466
+ const color = event . direction === "rx" ? rxColor : txColor ;
467
+ const node = d3 . select < d3 . BaseType , d3 . HierarchyNode < TreeNode > > (
468
+ `[data-nodeid="${ event . node_id } "]`
469
+ ) ;
470
+ animateNode ( node , color ) ;
471
+ if ( ! node . empty ( ) ) {
472
+ const parent = d3 . select < d3 . BaseType , d3 . HierarchyNode < TreeNode > > (
473
+ `[data-nodeid="${ node . datum ( ) . parent ?. data . id } "]`
474
+ ) ;
475
+ animateNode ( parent , color ) ;
476
+ }
477
+
478
+ const links = d3 . selectAll < d3 . BaseType , d3 . HierarchyLink < TreeNode > > (
479
+ `[data-nodeid-target="${ event . node_id } "]`
480
+ ) ;
481
+ animateLinks ( links , color ) ;
482
+ } ) ;
483
+
484
+ return ( ) => {
485
+ sub . unsubscribe ( ) ;
486
+ } ;
487
+ } , [ eventBus , getRadius ] ) ;
488
+
403
489
return (
404
490
< svg
405
491
viewBox = { `${ - width / 2 } ${ - height / 2 } ${ width } ${ height } ` }
0 commit comments