import {forwardRef, useCallback} from 'react'; import styled from '@emotion/styled'; import {IconSettings, IconUser} from 'sentry/icons'; import space from 'sentry/styles/space'; import {Flamegraph} from 'sentry/utils/profiling/flamegraph'; import {FlamegraphFrame} from 'sentry/utils/profiling/flamegraphFrame'; import {VirtualizedTreeNode} from 'sentry/utils/profiling/hooks/useVirtualizedTree/VirtualizedTreeNode'; import {FrameCallersTableCell} from './frameStack'; function computeRelativeWeight(base: number, value: number) { // Make sure we dont divide by zero if (!base || !value) { return 0; } return (value / base) * 100; } interface FrameStackTableRowProps { formatDuration: Flamegraph['formatter']; frameColor: string; node: VirtualizedTreeNode; onClick: React.MouseEventHandler; onContextMenu: React.MouseEventHandler; onExpandClick: ( node: VirtualizedTreeNode, opts?: {expandChildren: boolean} ) => void; onKeyDown: React.KeyboardEventHandler; onMouseEnter: React.MouseEventHandler; referenceNode: FlamegraphFrame; style: React.CSSProperties; tabIndex: number; } export const FrameStackTableRow = forwardRef( ( { node, referenceNode, onExpandClick, onContextMenu, formatDuration, frameColor, tabIndex, onKeyDown, onClick, onMouseEnter, style, }, ref ) => { const isSelected = tabIndex === 0; const handleExpanding = useCallback( (evt: React.MouseEvent) => { evt.stopPropagation(); onExpandClick(node, {expandChildren: evt.metaKey}); }, [node, onExpandClick] ); return ( {formatDuration(node.node.node.selfWeight)} {formatDuration(node.node.node.totalWeight)} {node.node.node.frame.is_application ? ( ) : ( )} {/* @TODO FIX COLOR */} {node.node.children.length > 0 ? '\u203A' : null} {node.node.frame.name} ); } ); const Weight = styled( (props: {isSelected: boolean; weight: number; padded?: boolean}) => { const {weight, padded: __, isSelected: _, ...rest} = props; return (
{weight.toFixed(1)}%
); } )` display: inline-block; min-width: 7ch; padding-right: ${p => (p.padded ? space(0.5) : 0)}; color: ${p => (p.isSelected ? p.theme.white : p.theme.subText)}; opacity: ${p => (p.isSelected ? 0.8 : 1)}; `; const FrameWeightTypeContainer = styled('div')` display: flex; align-items: center; justify-content: flex-end; position: relative; `; const FrameTypeIndicator = styled('div')<{isSelected: boolean}>` flex-shrink: 0; width: 26px; height: 12px; display: flex; align-items: center; justify-content: center; color: ${p => (p.isSelected ? p.theme.white : p.theme.subText)}; opacity: ${p => (p.isSelected ? 0.8 : 1)}; `; const FrameWeightContainer = styled('div')` display: flex; align-items: center; position: relative; justify-content: flex-end; flex: 1 1 100%; `; const BackgroundWeightBar = styled('div')` pointer-events: none; position: absolute; right: 0; top: 0; background-color: ${props => props.theme.yellow100}; border-bottom: 1px solid ${props => props.theme.yellow200}; transform-origin: center right; height: 100%; width: 100%; `; const FrameCallersRow = styled('div')<{isSelected: boolean}>` display: flex; width: 100%; color: ${p => (p.isSelected ? p.theme.white : 'inherit')}; scroll-margin-top: 24px; font-size: ${p => p.theme.fontSizeSmall}; line-height: 24px; &:focus { outline: none; } &[data-hovered='true']:not([tabindex='0']) { > div:first-child, > div:nth-child(2) { background-color: ${p => p.theme.surface100} !important; } } `; const FrameNameContainer = styled('div')` display: flex; align-items: center; `; const FrameChildrenIndicator = styled('button')<{open: boolean}>` width: 10px; height: 10px; display: flex; padding: 0; border: none; background-color: transparent; align-items: center; justify-content: center; user-select: none; transform: ${p => (p.open ? 'rotate(90deg)' : 'rotate(0deg)')}; `; const FrameName = styled('span')` margin-left: ${space(0.5)}; `; const FrameColorIndicator = styled('div')` width: 12px; height: 12px; border-radius: 2px; display: inline-block; flex-shrink: 0; margin-right: ${space(0.5)}; `;