flamegraphOptionsContextMenu.tsx 5.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138
  1. import {Fragment} from 'react';
  2. import {t} from 'sentry/locale';
  3. import {
  4. FlamegraphAxisOptions,
  5. FlamegraphColorCodings,
  6. FlamegraphSorting,
  7. FlamegraphViewOptions,
  8. } from 'sentry/utils/profiling/flamegraph/flamegraphStateProvider/reducers/flamegraphPreferences';
  9. import {useFlamegraphPreferences} from 'sentry/utils/profiling/flamegraph/hooks/useFlamegraphPreferences';
  10. import {useDispatchFlamegraphState} from 'sentry/utils/profiling/flamegraph/hooks/useFlamegraphState';
  11. import {FlamegraphFrame} from 'sentry/utils/profiling/flamegraphFrame';
  12. import {useContextMenu} from 'sentry/utils/profiling/hooks/useContextMenu';
  13. import {
  14. ProfilingContextMenu,
  15. ProfilingContextMenuGroup,
  16. ProfilingContextMenuHeading,
  17. ProfilingContextMenuItemCheckbox,
  18. ProfilingContextMenuLayer,
  19. } from './ProfilingContextMenu/profilingContextMenu';
  20. const FLAMEGRAPH_COLOR_CODINGS: FlamegraphColorCodings = [
  21. 'by symbol name',
  22. 'by system / application',
  23. 'by library',
  24. 'by recursion',
  25. 'by frequency',
  26. ];
  27. const FLAMEGRAPH_VIEW_OPTIONS: FlamegraphViewOptions = ['top down', 'bottom up'];
  28. const FLAMEGRAPH_SORTING_OPTIONS: FlamegraphSorting = ['left heavy', 'call order'];
  29. const FLAMEGRAPH_AXIS_OPTIONS: FlamegraphAxisOptions = ['standalone', 'transaction'];
  30. interface FlameGraphOptionsContextMenuProps {
  31. contextMenu: ReturnType<typeof useContextMenu>;
  32. hoveredNode: FlamegraphFrame | null;
  33. isHighlightingAllOccurences: boolean;
  34. onHighlightAllOccurencesClick: () => void;
  35. }
  36. export function FlamegraphOptionsContextMenu(props: FlameGraphOptionsContextMenuProps) {
  37. const preferences = useFlamegraphPreferences();
  38. const dispatch = useDispatchFlamegraphState();
  39. return props.contextMenu.open ? (
  40. <Fragment>
  41. <ProfilingContextMenuLayer onClick={() => props.contextMenu.setOpen(false)} />
  42. <ProfilingContextMenu
  43. {...props.contextMenu.getMenuProps()}
  44. style={{
  45. position: 'absolute',
  46. left: props.contextMenu.position?.left ?? -9999,
  47. top: props.contextMenu.position?.top ?? -9999,
  48. maxHeight: props.contextMenu.containerCoordinates?.height ?? 'auto',
  49. }}
  50. >
  51. {props.hoveredNode ? (
  52. <ProfilingContextMenuGroup>
  53. <ProfilingContextMenuHeading>{t('Frame')}</ProfilingContextMenuHeading>
  54. <ProfilingContextMenuItemCheckbox
  55. {...props.contextMenu.getMenuItemProps({
  56. onClick: props.onHighlightAllOccurencesClick,
  57. })}
  58. onClick={e => {
  59. // We need to prevent the click from propagating to the context menu layer.
  60. e.preventDefault();
  61. props.onHighlightAllOccurencesClick();
  62. }}
  63. checked={props.isHighlightingAllOccurences}
  64. >
  65. {t('Highlight all occurrences')}
  66. </ProfilingContextMenuItemCheckbox>
  67. </ProfilingContextMenuGroup>
  68. ) : null}
  69. <ProfilingContextMenuGroup>
  70. <ProfilingContextMenuHeading>{t('Color Coding')}</ProfilingContextMenuHeading>
  71. {FLAMEGRAPH_COLOR_CODINGS.map((coding, idx) => (
  72. <ProfilingContextMenuItemCheckbox
  73. key={idx}
  74. {...props.contextMenu.getMenuItemProps({
  75. onClick: () => dispatch({type: 'set color coding', payload: coding}),
  76. })}
  77. onClick={() => dispatch({type: 'set color coding', payload: coding})}
  78. checked={preferences.colorCoding === coding}
  79. >
  80. {coding}
  81. </ProfilingContextMenuItemCheckbox>
  82. ))}
  83. </ProfilingContextMenuGroup>
  84. <ProfilingContextMenuGroup>
  85. <ProfilingContextMenuHeading>{t('View')}</ProfilingContextMenuHeading>
  86. {FLAMEGRAPH_VIEW_OPTIONS.map((view, idx) => (
  87. <ProfilingContextMenuItemCheckbox
  88. key={idx}
  89. {...props.contextMenu.getMenuItemProps({
  90. onClick: () => dispatch({type: 'set view', payload: view}),
  91. })}
  92. onClick={() => dispatch({type: 'set view', payload: view})}
  93. checked={preferences.view === view}
  94. >
  95. {view}
  96. </ProfilingContextMenuItemCheckbox>
  97. ))}
  98. </ProfilingContextMenuGroup>
  99. <ProfilingContextMenuGroup>
  100. <ProfilingContextMenuHeading>{t('Sorting')}</ProfilingContextMenuHeading>
  101. {FLAMEGRAPH_SORTING_OPTIONS.map((sorting, idx) => (
  102. <ProfilingContextMenuItemCheckbox
  103. key={idx}
  104. {...props.contextMenu.getMenuItemProps({
  105. onClick: () => dispatch({type: 'set sorting', payload: sorting}),
  106. })}
  107. onClick={() => dispatch({type: 'set sorting', payload: sorting})}
  108. checked={preferences.sorting === sorting}
  109. >
  110. {sorting}
  111. </ProfilingContextMenuItemCheckbox>
  112. ))}
  113. </ProfilingContextMenuGroup>
  114. <ProfilingContextMenuGroup>
  115. <ProfilingContextMenuHeading>{t('X Axis')}</ProfilingContextMenuHeading>
  116. {FLAMEGRAPH_AXIS_OPTIONS.map((axis, idx) => (
  117. <ProfilingContextMenuItemCheckbox
  118. key={idx}
  119. {...props.contextMenu.getMenuItemProps({
  120. onClick: () => dispatch({type: 'set xAxis', payload: axis}),
  121. })}
  122. onClick={() => dispatch({type: 'set xAxis', payload: axis})}
  123. checked={preferences.xAxis === axis}
  124. >
  125. {axis}
  126. </ProfilingContextMenuItemCheckbox>
  127. ))}
  128. </ProfilingContextMenuGroup>
  129. </ProfilingContextMenu>
  130. </Fragment>
  131. ) : null;
  132. }