2022-03-09 10:43:02 +05:30
import { CaretDownFilled , CaretRightFilled } from '@ant-design/icons' ;
2023-01-11 14:39:06 +05:30
import { Col , Typography } from 'antd' ;
2022-03-09 10:43:02 +05:30
import { StyledCol , StyledRow } from 'components/Styled' ;
import { IIntervalUnit } from 'container/TraceDetail/utils' ;
2023-01-11 14:39:06 +05:30
import { useIsDarkMode } from 'hooks/useDarkMode' ;
2022-03-09 10:43:02 +05:30
import { SPAN_DETAILS_LEFT_COL_WIDTH } from 'pages/TraceDetail/constants' ;
2023-05-19 13:14:32 +05:30
import {
Dispatch ,
MouseEventHandler ,
SetStateAction ,
useEffect ,
useMemo ,
useRef ,
useState ,
} from 'react' ;
2022-03-24 12:06:57 +05:30
import { ITraceTree } from 'types/api/trace/getTraceItem' ;
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' ;
2023-01-11 14:39:06 +05:30
import { getIconStyles } from './utils' ;
2022-03-03 19:04:23 +05:30
2022-03-22 12:10:31 +05:30
function Trace ( props : TraceProps ) : JSX . Element {
2022-03-03 19:04:23 +05:30
const {
name ,
activeHoverId ,
setActiveHoverId ,
globalSpread ,
globalStart ,
serviceName ,
startTime ,
value ,
serviceColour ,
id ,
setActiveSelectedId ,
activeSelectedId ,
level ,
activeSpanPath ,
isExpandAll ,
intervalUnit ,
2022-03-24 12:06:57 +05:30
children ,
2022-07-08 16:18:08 +05:30
isMissing ,
2022-03-03 19:04:23 +05:30
} = props ;
2023-01-11 14:39:06 +05:30
const isDarkMode = useIsDarkMode ( ) ;
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-24 12:06:57 +05:30
} , [ activeSpanPath , isOpen , id , level ] ) ;
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-24 12:06:57 +05:30
} , [ isExpandAll , activeSpanPath , id , level ] ) ;
2022-03-03 19:04:23 +05:30
2022-03-24 12:06:57 +05:30
const isOnlyChild = children . length === 1 ;
2022-03-03 19:04:23 +05:30
const [ top , setTop ] = useState < number > ( 0 ) ;
const ref = useRef < HTMLUListElement > ( null ) ;
2023-05-19 13:14:32 +05:30
useEffect ( ( ) = > {
2022-03-03 19:04:23 +05:30
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-24 12:06:57 +05:30
} , [ activeSelectedId , id ] ) ;
2022-03-03 19:04:23 +05:30
2022-03-24 12:06:57 +05:30
const onMouseEnterHandler = ( ) : void = > {
setActiveHoverId ( id ) ;
2022-03-03 19:04:23 +05:30
if ( ref . current ) {
const { top } = getTopLeftFromBody ( ref . current ) ;
setTop ( top ) ;
}
} ;
2022-03-24 12:06:57 +05:30
const onMouseLeaveHandler = ( ) : void = > {
2022-03-03 19:04:23 +05:30
setActiveHoverId ( '' ) ;
} ;
2022-03-24 12:06:57 +05:30
const onClick = ( ) : void = > {
2022-03-03 19:04:23 +05:30
setActiveSelectedId ( id ) ;
2022-03-09 10:43:02 +05:30
} ;
2022-03-04 11:34:33 +05:30
2023-05-19 13:14:32 +05:30
const onClickTreeExpansion : MouseEventHandler < HTMLDivElement > = (
2022-03-24 12:06:57 +05:30
event ,
) : void = > {
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
2023-01-11 14:39:06 +05:30
const iconStyles = useMemo ( ( ) = > getIconStyles ( isDarkMode ) , [ isDarkMode ] ) ;
const icon = useMemo (
( ) = >
isOpen ? (
< CaretDownFilled style = { iconStyles } / >
) : (
< CaretRightFilled style = { iconStyles } / >
) ,
[ isOpen , iconStyles ] ,
) ;
2022-03-03 19:04:23 +05:30
return (
2022-03-22 12:10:31 +05:30
< Wrapper
onMouseEnter = { onMouseEnterHandler }
onMouseLeave = { onMouseLeaveHandler }
isOnlyChild = { isOnlyChild }
ref = { ref }
2022-03-24 12:06:57 +05:30
isDarkMode = { isDarkMode }
2022-03-22 12:10:31 +05:30
>
< HoverCard
top = { top }
isHovered = { activeHoverId === id }
isSelected = { activeSelectedId === id }
isDarkMode = { isDarkMode }
/ >
2022-07-08 16:18:08 +05:30
< CardContainer isMissing = { isMissing } onClick = { onClick } >
2022-03-22 12:10:31 +05:30
< StyledCol flex = { ` ${ panelWidth } px ` } styledclass = { [ styles . overFlowHidden ] } >
< StyledRow styledclass = { [ styles . flexNoWrap ] } >
< Col >
{ totalSpans !== 1 && (
2022-03-24 12:06:57 +05:30
< CardComponent
isOnlyChild = { isOnlyChild }
isDarkMode = { isDarkMode }
onClick = { onClickTreeExpansion }
>
2023-01-11 14:39:06 +05:30
< Typography > { totalSpans } < / Typography >
< CaretContainer > { icon } < / CaretContainer >
2022-03-22 12:10:31 +05:30
< / CardComponent >
) }
< / Col >
< Col >
< SpanName name = { name } serviceName = { serviceName } / >
< / Col >
< / StyledRow >
< / StyledCol >
< Col flex = "1" >
< SpanLength
leftOffset = { nodeLeftOffset . toString ( ) }
width = { width . toString ( ) }
bgColor = { serviceColour }
inMsCount = { inMsCount / 1 e6 }
/ >
< / Col >
< / CardContainer >
{ isOpen && (
< >
2022-03-24 12:06:57 +05:30
{ children . map ( ( child ) = > (
2022-03-22 12:10:31 +05:30
< Trace
key = { child . id }
2022-03-24 12:06:57 +05:30
activeHoverId = { activeHoverId }
setActiveHoverId = { setActiveHoverId }
// eslint-disable-next-line react/jsx-props-no-spreading
2022-03-22 12:10:31 +05:30
{ . . . child }
globalSpread = { globalSpread }
globalStart = { globalStart }
setActiveSelectedId = { setActiveSelectedId }
activeSelectedId = { activeSelectedId }
level = { level + 1 }
activeSpanPath = { activeSpanPath }
isExpandAll = { isExpandAll }
2022-03-03 19:04:23 +05:30
intervalUnit = { intervalUnit }
2022-07-08 16:18:08 +05:30
isMissing = { child . isMissing }
2022-03-03 19:04:23 +05:30
/ >
2022-03-22 12:10:31 +05:30
) ) }
< / >
) }
< / Wrapper >
2022-03-03 19:04:23 +05:30
) ;
2022-03-22 12:10:31 +05:30
}
2022-03-03 19:04:23 +05:30
2022-07-08 16:18:08 +05:30
Trace . defaultProps = {
isMissing : false ,
} ;
2022-03-03 19:04:23 +05:30
interface ITraceGlobal {
globalSpread : ITraceMetaData [ 'spread' ] ;
globalStart : ITraceMetaData [ 'globalStart' ] ;
}
2022-03-24 12:06:57 +05:30
interface TraceProps extends ITraceTree , ITraceGlobal {
2022-03-03 19:04:23 +05:30
activeHoverId : string ;
2023-05-19 13:14:32 +05:30
setActiveHoverId : Dispatch < SetStateAction < string > > ;
setActiveSelectedId : Dispatch < SetStateAction < string > > ;
2022-03-03 19:04:23 +05:30
activeSelectedId : string ;
level : number ;
activeSpanPath : string [ ] ;
isExpandAll : boolean ;
intervalUnit : IIntervalUnit ;
2022-07-08 16:18:08 +05:30
isMissing? : boolean ;
2022-03-03 19:04:23 +05:30
}
export default Trace ;