@@ -27,8 +27,9 @@ import {
2727 ChartTooltipPanel ,
2828 chartHoverOpacity ,
2929 chartMotion ,
30- getChartHoverOpacity ,
31- getChartHoverPresence ,
30+ getChartMarkKey ,
31+ getChartMarkOpacity ,
32+ getChartMarkPresence ,
3233 useChartMarkTooltip ,
3334} from "../ui/chart" ;
3435import { RoughPath } from "@/lib/rough-svg" ;
@@ -119,6 +120,14 @@ type NodeOrIndex = SankeyNodeType<SankeyNodeDatum, SankeyLinkDatum> | number;
119120const DEFAULT_MARGIN : Margin = { top : 40 , right : 180 , bottom : 40 , left : 180 } ;
120121const intFmt = new Intl . NumberFormat ( "en-US" ) . format ;
121122
123+ function getSankeyLinkKey ( index : number ) {
124+ return getChartMarkKey ( "link" , index ) ;
125+ }
126+
127+ function getSankeyNodeKey ( index : number ) {
128+ return getChartMarkKey ( "node" , index ) ;
129+ }
130+
122131function getNodeIndex ( nodeOrIndex : NodeOrIndex ) : number | undefined {
123132 if ( typeof nodeOrIndex === "number" ) {
124133 return nodeOrIndex ;
@@ -349,23 +358,26 @@ function SankeyLinks({
349358 const roughPath = roughOptions ? createRibbonPath ( link ) : undefined ;
350359 const sourceIndex = getNodeIndex ( link . source as NodeOrIndex ) ?? - 1 ;
351360 const targetIndex = getNodeIndex ( link . target as NodeOrIndex ) ?? - 1 ;
352- const presence = getChartHoverPresence ( {
361+ const linkKey = getSankeyLinkKey ( index ) ;
362+ const presence = getChartMarkPresence ( {
353363 hover,
364+ key : linkKey ,
354365 isRelated :
355- hover ?. type === "link"
356- ? hover . key === index
357- : hover ?. key === sourceIndex || hover ?. key === targetIndex ,
366+ hover ?. type === "node"
367+ ? hover . key === getSankeyNodeKey ( sourceIndex ) ||
368+ hover . key === getSankeyNodeKey ( targetIndex )
369+ : undefined ,
358370 } ) ;
359371 const stroke = getLinkColor
360372 ? getLinkColor ( link , index )
361373 : "var(--chart-line-primary)" ;
362- const targetOpacity = getChartHoverOpacity ( {
374+ const targetOpacity = getChartMarkOpacity ( {
363375 baseOpacity : strokeOpacity ,
364376 presence,
365377 } ) ;
366378
367379 const handlePointerEnter = ( event : React . PointerEvent < Element > ) => {
368- setHover ( { key : index , type : "link" } ) ;
380+ setHover ( { key : linkKey , type : "link" } ) ;
369381 showTooltip ( event , { type : "link" , linkIndex : index } ) ;
370382 } ;
371383 const handlePointerLeave = ( ) => {
@@ -379,7 +391,7 @@ function SankeyLinks({
379391 < ChartMotionGroup
380392 animate = { { opacity : targetOpacity } }
381393 initial = { { opacity : strokeOpacity } }
382- key = { `link- ${ sourceIndex } - ${ targetIndex } - ${ link . width ?? link . value ?? "" } ` }
394+ key = { linkKey }
383395 onPointerEnter = { handlePointerEnter }
384396 onPointerLeave = { handlePointerLeave }
385397 style = { { cursor : "pointer" } }
@@ -406,7 +418,7 @@ function SankeyLinks({
406418 d = { path }
407419 fill = "none"
408420 initial = { { opacity : strokeOpacity } }
409- key = { `link- ${ sourceIndex } - ${ targetIndex } - ${ link . width ?? link . value ?? "" } ` }
421+ key = { linkKey }
410422 onPointerEnter = { handlePointerEnter }
411423 onPointerLeave = { handlePointerLeave }
412424 stroke = { stroke }
@@ -455,8 +467,10 @@ function SankeyNodes({
455467 const nodeY = node . y0 ?? 0 ;
456468 const nodeWidth = ( node . x1 ?? 0 ) - nodeX ;
457469 const nodeHeight = ( node . y1 ?? 0 ) - nodeY ;
458- const presence = getChartHoverPresence ( {
470+ const nodeKey = getSankeyNodeKey ( index ) ;
471+ const presence = getChartMarkPresence ( {
459472 hover,
473+ key : nodeKey ,
460474 isRelated : isNodeConnected ( { hover, links, nodeIndex : index } ) ,
461475 } ) ;
462476 const isLeftSide = nodeX < innerWidth / 2 ;
@@ -468,7 +482,7 @@ function SankeyNodes({
468482 presence === "faded" ? chartHoverOpacity . textFaded : 0.6 ;
469483
470484 const handlePointerEnter = ( event : React . PointerEvent < Element > ) => {
471- setHover ( { key : index , type : "node" } ) ;
485+ setHover ( { key : nodeKey , type : "node" } ) ;
472486 showTooltip ( event , { type : "node" , nodeIndex : index } ) ;
473487 } ;
474488 const handlePointerLeave = ( ) => {
@@ -514,20 +528,24 @@ function isNodeConnected({
514528 }
515529
516530 if ( hover . type === "node" ) {
517- if ( hover . key === nodeIndex ) {
531+ if ( hover . key === getSankeyNodeKey ( nodeIndex ) ) {
518532 return true ;
519533 }
520534 return links . some ( ( link ) => {
521535 const sourceIndex = getNodeIndex ( link . source as NodeOrIndex ) ;
522536 const targetIndex = getNodeIndex ( link . target as NodeOrIndex ) ;
523537 return (
524- ( sourceIndex === hover . key && targetIndex === nodeIndex ) ||
525- ( targetIndex === hover . key && sourceIndex === nodeIndex )
538+ ( sourceIndex !== undefined &&
539+ getSankeyNodeKey ( sourceIndex ) === hover . key &&
540+ targetIndex === nodeIndex ) ||
541+ ( targetIndex !== undefined &&
542+ getSankeyNodeKey ( targetIndex ) === hover . key &&
543+ sourceIndex === nodeIndex )
526544 ) ;
527545 } ) ;
528546 }
529547
530- const link = links [ Number ( hover . key ) ] ;
548+ const link = links . find ( ( _ , index ) => getSankeyLinkKey ( index ) === hover . key ) ;
531549 if ( ! link ) {
532550 return false ;
533551 }
0 commit comments