/
ChordRibbon.tsx
102 lines (91 loc) · 3.56 KB
/
ChordRibbon.tsx
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
import { createElement, memo, useMemo, MouseEvent } from 'react'
import { SpringValues, animated } from '@react-spring/web'
import { useTooltip } from '@nivo/tooltip'
import {
ChordCommonProps,
ChordSvgProps,
RibbonAnimatedProps,
RibbonDatum,
RibbonGenerator,
} from './types'
import { computeRibbonPath } from './compute'
interface ChordRibbonProps {
ribbon: RibbonDatum
ribbonGenerator: RibbonGenerator
animatedProps: SpringValues<RibbonAnimatedProps>
borderWidth: ChordCommonProps['ribbonBorderWidth']
blendMode: NonNullable<ChordSvgProps['ribbonBlendMode']>
setCurrent: (ribbon: RibbonDatum | null) => void
isInteractive: ChordCommonProps['isInteractive']
tooltip: NonNullable<ChordSvgProps['ribbonTooltip']>
onMouseEnter: ChordSvgProps['onRibbonMouseEnter']
onMouseMove: ChordSvgProps['onRibbonMouseMove']
onMouseLeave: ChordSvgProps['onRibbonMouseLeave']
onClick: ChordSvgProps['onRibbonClick']
}
export const ChordRibbon = memo(
({
ribbon,
ribbonGenerator,
animatedProps,
borderWidth,
blendMode,
isInteractive,
setCurrent,
onMouseEnter,
onMouseMove,
onMouseLeave,
onClick,
tooltip,
}: ChordRibbonProps) => {
const { showTooltipFromEvent, hideTooltip } = useTooltip()
const handleMouseEnter = useMemo(() => {
if (!isInteractive) return undefined
return (event: MouseEvent) => {
setCurrent(ribbon)
showTooltipFromEvent(createElement(tooltip, { ribbon }), event)
onMouseEnter && onMouseEnter(ribbon, event)
}
}, [isInteractive, showTooltipFromEvent, tooltip, ribbon, onMouseEnter])
const handleMouseMove = useMemo(() => {
if (!isInteractive) return undefined
return (event: MouseEvent) => {
showTooltipFromEvent(createElement(tooltip, { ribbon }), event)
onMouseMove && onMouseMove(ribbon, event)
}
}, [isInteractive, showTooltipFromEvent, tooltip, ribbon, onMouseMove])
const handleMouseLeave = useMemo(() => {
if (!isInteractive) return undefined
return (event: MouseEvent) => {
setCurrent(null)
hideTooltip()
onMouseLeave && onMouseLeave(ribbon, event)
}
}, [isInteractive, hideTooltip, ribbon, onMouseLeave])
const handleClick = useMemo(() => {
if (!isInteractive || !onClick) return undefined
return (event: MouseEvent) => onClick(ribbon, event)
}, [isInteractive, ribbon, onClick])
return (
<animated.path
data-testid={`ribbon.${ribbon.source.id}.${ribbon.target.id}`}
d={computeRibbonPath({
sourceStartAngle: animatedProps.sourceStartAngle,
sourceEndAngle: animatedProps.sourceEndAngle,
targetStartAngle: animatedProps.targetStartAngle,
targetEndAngle: animatedProps.targetEndAngle,
ribbonGenerator,
})}
fill={animatedProps.color}
opacity={animatedProps.opacity}
strokeWidth={borderWidth}
stroke={animatedProps.borderColor}
style={{ mixBlendMode: blendMode }}
onMouseEnter={handleMouseEnter}
onMouseMove={handleMouseMove}
onMouseLeave={handleMouseLeave}
onClick={handleClick}
/>
)
}
)