/* eslint-disable react/no-unstable-nested-components */ import Color from 'color'; import { ITraceMetaData } from 'container/GantChart'; import useThemeMode from 'hooks/useThemeMode'; import React, { useLayoutEffect, useMemo, useState } from 'react'; import { ITraceTree } from 'types/api/trace/getTraceItem'; import { SpanItemContainer, TOTAL_SPAN_HEIGHT, TraceFlameGraphContainer, } from './styles'; interface SpanItemProps { topOffset: number; leftOffset: number; width: number; spanData: ITraceTree; tooltipText: string; onSpanSelect: (id: string) => void; onSpanHover: React.Dispatch>; hoveredSpanId: string; selectedSpanId: string; } function SpanItem({ topOffset = 0, // top offset in px leftOffset = 0, // left offset in % width = 10, // width in % spanData, tooltipText, onSpanSelect, // function which gets invoked on clicking span onSpanHover, hoveredSpanId, selectedSpanId, }: SpanItemProps): JSX.Element { const { serviceColour } = spanData; const [isSelected, setIsSelected] = useState(false); // const [isLocalHover, setIsLocalHover] = useState(false); const { isDarkMode } = useThemeMode(); useLayoutEffect(() => { if ( !isSelected && (spanData.id === hoveredSpanId || spanData.id === selectedSpanId) ) { setIsSelected(true); } }, [hoveredSpanId, selectedSpanId, isSelected, spanData]); const handleHover = (hoverState: boolean): void => { // setIsLocalHover(hoverState); if (hoverState) onSpanHover(spanData.id); else onSpanHover(''); }; const handleClick = (): void => { onSpanSelect(spanData.id); }; const spanColor = useMemo((): string => { const selectedSpanColor = isDarkMode ? Color(serviceColour).lighten(0.3) : Color(serviceColour).darken(0.3); return `${isSelected ? selectedSpanColor : serviceColour}`; }, [isSelected, serviceColour, isDarkMode]); return ( { handleHover(true); }} onMouseLeave={(): void => { handleHover(false); }} topOffset={topOffset} leftOffset={leftOffset} width={width} spanColor={spanColor} selected={isSelected} zIdx={isSelected ? 1 : 0} /> ); } function TraceFlameGraph(props: { treeData: ITraceTree; traceMetaData: ITraceMetaData; onSpanHover: SpanItemProps['onSpanHover']; onSpanSelect: SpanItemProps['onSpanSelect']; hoveredSpanId: string; selectedSpanId: string; }): JSX.Element { const { treeData, traceMetaData, onSpanHover } = props; if (!treeData || treeData.id === 'empty' || !traceMetaData) { return
; } const { onSpanSelect, hoveredSpanId, selectedSpanId } = props; const { globalStart, spread, levels } = traceMetaData; function RenderSpanRecursive({ level = 0, spanData, parentLeftOffset = 0, onSpanHover, onSpanSelect, hoveredSpanId, selectedSpanId, }: { spanData: ITraceTree; level: number; parentLeftOffset: number; onSpanHover: SpanItemProps['onSpanHover']; onSpanSelect: SpanItemProps['onSpanSelect']; hoveredSpanId: string; selectedSpanId: string; }): JSX.Element { if (!spanData) { return
; } const leftOffset = ((spanData.startTime - globalStart) * 100) / spread; const width = ((spanData.value / 1e6) * 100) / spread; const toolTipText = `${spanData.name}`; return ( <> {spanData.children.map((childData) => ( ))} ); } return ( ); } export default TraceFlameGraph;