2022-03-09 10:43:02 +05:30
import { CaretDownFilled , CaretRightFilled } from '@ant-design/icons' ;
import { Col } from 'antd' ;
import { StyledCol , StyledRow } from 'components/Styled' ;
import { IIntervalUnit } from 'container/TraceDetail/utils' ;
import useThemeMode from 'hooks/useThemeMode' ;
import { SPAN_DETAILS_LEFT_COL_WIDTH } from 'pages/TraceDetail/constants' ;
import React , { useEffect , useRef , useState } from 'react' ;
import { pushDStree } from 'store/actions' ;
2022-03-03 19:04:23 +05:30
2022-03-09 10:43:02 +05:30
import { ITraceMetaData } from '..' ;
import SpanLength from '../SpanLength' ;
import SpanName from '../SpanName' ;
import { getMetaDataFromSpanTree , getTopLeftFromBody } from '../utils' ;
2022-03-03 19:04:23 +05:30
import {
CardComponent ,
CardContainer ,
CaretContainer ,
HoverCard ,
2022-03-09 10:43:02 +05:30
styles ,
Wrapper ,
2022-03-03 19:04:23 +05:30
} from './styles' ;
const Trace = ( props : TraceProps ) : JSX . Element = > {
const {
name ,
activeHoverId ,
setActiveHoverId ,
globalSpread ,
globalStart ,
serviceName ,
startTime ,
value ,
serviceColour ,
id ,
setActiveSelectedId ,
activeSelectedId ,
level ,
activeSpanPath ,
isExpandAll ,
intervalUnit ,
} = props ;
2022-03-09 10:43:02 +05:30
const { isDarkMode } = useThemeMode ( ) ;
2022-03-03 19:04:23 +05:30
const [ isOpen , setOpen ] = useState < boolean > ( activeSpanPath [ level ] === id ) ;
2022-03-04 11:34:33 +05:30
const localTreeExpandInteraction = useRef < boolean | 0 > ( 0 ) ; // Boolean is for the state of the expansion whereas the number i.e. 0 is for skipping the user interaction.
2022-03-03 19:04:23 +05:30
useEffect ( ( ) = > {
2022-03-04 11:34:33 +05:30
if ( localTreeExpandInteraction . current !== 0 ) {
setOpen ( localTreeExpandInteraction . current ) ;
localTreeExpandInteraction . current = 0 ;
2022-03-09 10:43:02 +05:30
} else if ( ! isOpen ) {
setOpen ( activeSpanPath [ level ] === id ) ;
2022-03-04 11:34:33 +05:30
}
2022-03-09 10:43:02 +05:30
} , [ activeSpanPath , isOpen ] ) ;
2022-03-03 19:04:23 +05:30
useEffect ( ( ) = > {
if ( isExpandAll ) {
2022-03-09 10:43:02 +05:30
setOpen ( isExpandAll ) ;
} else {
setOpen ( activeSpanPath [ level ] === id ) ;
2022-03-03 19:04:23 +05:30
}
2022-03-09 10:43:02 +05:30
} , [ isExpandAll ] ) ;
2022-03-03 19:04:23 +05:30
const isOnlyChild = props . children . length === 1 ;
const [ top , setTop ] = useState < number > ( 0 ) ;
const ref = useRef < HTMLUListElement > ( null ) ;
React . useEffect ( ( ) = > {
if ( activeSelectedId === id ) {
2022-03-09 10:43:02 +05:30
ref . current ? . scrollIntoView ( {
block : 'nearest' ,
behavior : 'auto' ,
inline : 'nearest' ,
} ) ;
2022-03-03 19:04:23 +05:30
}
2022-03-09 10:43:02 +05:30
} , [ activeSelectedId ] ) ;
2022-03-03 19:04:23 +05:30
const onMouseEnterHandler = ( ) = > {
setActiveHoverId ( props . id ) ;
if ( ref . current ) {
const { top } = getTopLeftFromBody ( ref . current ) ;
setTop ( top ) ;
}
} ;
const onMouseLeaveHandler = ( ) = > {
setActiveHoverId ( '' ) ;
} ;
const onClick = ( ) = > {
setActiveSelectedId ( id ) ;
2022-03-09 10:43:02 +05:30
} ;
2022-03-04 11:34:33 +05:30
const onClickTreeExpansion = ( event ) = > {
2022-03-09 10:43:02 +05:30
event . stopPropagation ( ) ;
setOpen ( ( state ) = > {
localTreeExpandInteraction . current = ! isOpen ;
return ! state ;
} ) ;
} ;
2022-03-03 19:04:23 +05:30
const { totalSpans } = getMetaDataFromSpanTree ( props ) ;
const inMsCount = value ;
const nodeLeftOffset = ( ( startTime - globalStart ) * 1 e2 ) / globalSpread ;
const width = ( value * 1 e2 ) / ( globalSpread * 1 e6 ) ;
2022-03-09 10:43:02 +05:30
const panelWidth = SPAN_DETAILS_LEFT_COL_WIDTH - level * ( 16 + 1 ) - 48 ;
2022-03-03 19:04:23 +05:30
return (
< >
< Wrapper
onMouseEnter = { onMouseEnterHandler }
onMouseLeave = { onMouseLeaveHandler }
isOnlyChild = { isOnlyChild }
ref = { ref }
>
< HoverCard
top = { top }
isHovered = { activeHoverId === id }
isSelected = { activeSelectedId === id }
isDarkMode = { isDarkMode }
/ >
2022-03-09 10:43:02 +05:30
< CardContainer onClick = { onClick } >
< StyledCol flex = { ` ${ panelWidth } px ` } styledclass = { [ styles . overFlowHidden ] } >
< StyledRow styledclass = { [ styles . flexNoWrap ] } >
2022-03-03 19:04:23 +05:30
< Col >
{ totalSpans !== 1 && (
2022-03-09 10:43:02 +05:30
< CardComponent isDarkMode = { isDarkMode } onClick = { onClickTreeExpansion } >
2022-03-03 19:04:23 +05:30
{ totalSpans }
< CaretContainer >
{ isOpen ? < CaretDownFilled / > : < CaretRightFilled / > }
< / CaretContainer >
< / CardComponent >
) }
< / Col >
< Col >
< SpanName name = { name } serviceName = { serviceName } / >
< / Col >
2022-03-09 10:43:02 +05:30
< / StyledRow >
< / StyledCol >
< Col flex = { '1' } >
2022-03-03 19:04:23 +05:30
< SpanLength
leftOffset = { nodeLeftOffset . toString ( ) }
width = { width . toString ( ) }
bgColor = { serviceColour }
id = { id }
2022-03-09 10:43:02 +05:30
inMsCount = { inMsCount / 1 e6 }
2022-03-03 19:04:23 +05:30
intervalUnit = { intervalUnit }
/ >
< / Col >
< / CardContainer >
{ isOpen && (
< >
{ props . children . map ( ( child ) = > (
< Trace
key = { child . id }
activeHoverId = { props . activeHoverId }
setActiveHoverId = { props . setActiveHoverId }
{ . . . child }
globalSpread = { globalSpread }
globalStart = { globalStart }
setActiveSelectedId = { setActiveSelectedId }
activeSelectedId = { activeSelectedId }
level = { level + 1 }
activeSpanPath = { activeSpanPath }
isExpandAll = { isExpandAll }
intervalUnit = { intervalUnit }
/ >
) ) }
< / >
) }
< / Wrapper >
< / >
) ;
} ;
interface ITraceGlobal {
globalSpread : ITraceMetaData [ 'spread' ] ;
globalStart : ITraceMetaData [ 'globalStart' ] ;
}
interface TraceProps extends pushDStree , ITraceGlobal {
activeHoverId : string ;
setActiveHoverId : React.Dispatch < React.SetStateAction < string > > ;
setActiveSelectedId : React.Dispatch < React.SetStateAction < string > > ;
activeSelectedId : string ;
level : number ;
activeSpanPath : string [ ] ;
isExpandAll : boolean ;
intervalUnit : IIntervalUnit ;
}
export default Trace ;