profilingFlamechartLayout.tsx 4.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. import {cloneElement, useMemo, useRef} from 'react';
  2. import styled from '@emotion/styled';
  3. import {FlamegraphPreferences} from 'sentry/utils/profiling/flamegraph/flamegraphStateProvider/flamegraphPreferences';
  4. import {FlamegraphTheme} from 'sentry/utils/profiling/flamegraph/flamegraphTheme';
  5. import {useFlamegraphPreferencesValue} from 'sentry/utils/profiling/flamegraph/useFlamegraphPreferences';
  6. import {useFlamegraphTheme} from 'sentry/utils/profiling/flamegraph/useFlamegraphTheme';
  7. import {
  8. useResizableDrawer,
  9. UseResizableDrawerOptions,
  10. } from 'sentry/utils/profiling/hooks/useResizableDrawer';
  11. interface ProfilingFlamechartLayoutProps {
  12. flamechart: React.ReactElement;
  13. frameStack: React.ReactElement;
  14. minimap: React.ReactElement;
  15. }
  16. export function ProfilingFlamechartLayout(props: ProfilingFlamechartLayoutProps) {
  17. const flamegraphTheme = useFlamegraphTheme();
  18. const {layout} = useFlamegraphPreferencesValue();
  19. const frameStackRef = useRef<HTMLDivElement>(null);
  20. const resizableOptions: UseResizableDrawerOptions = useMemo(() => {
  21. const initialDimensions: [number, number] = [
  22. // Half the screen minus the ~sidebar width
  23. window.innerWidth * 0.5 - 220,
  24. (flamegraphTheme.SIZES.FLAMEGRAPH_DEPTH_OFFSET + 2) *
  25. flamegraphTheme.SIZES.BAR_HEIGHT,
  26. ];
  27. // 664 is approximately the width where we start to scroll inside
  28. // 30 is the min height to where the drawer can still be resized
  29. const min: [number, number] = [664, 30];
  30. const onResize = (newDimensions: [number, number]) => {
  31. if (!frameStackRef.current) {
  32. return;
  33. }
  34. if (layout === 'table_left' || layout === 'table_right') {
  35. frameStackRef.current.style.width = `${newDimensions[0]}px`;
  36. frameStackRef.current.style.height = `100%`;
  37. } else {
  38. frameStackRef.current.style.height = `${newDimensions[1]}px`;
  39. frameStackRef.current.style.width = `100%`;
  40. }
  41. };
  42. return {
  43. initialDimensions,
  44. onResize,
  45. direction:
  46. layout === 'table_left'
  47. ? 'horizontal-ltr'
  48. : layout === 'table_right'
  49. ? 'horizontal-rtl'
  50. : 'vertical',
  51. min,
  52. };
  53. }, [
  54. flamegraphTheme.SIZES.FLAMEGRAPH_DEPTH_OFFSET,
  55. flamegraphTheme.SIZES.BAR_HEIGHT,
  56. layout,
  57. ]);
  58. const {onMouseDown} = useResizableDrawer(resizableOptions);
  59. return (
  60. <ProfilingFlamechartLayoutContainer>
  61. <ProfilingFlamechartGrid layout={layout}>
  62. <MinimapContainer height={flamegraphTheme.SIZES.MINIMAP_HEIGHT}>
  63. {props.minimap}
  64. </MinimapContainer>
  65. <ZoomViewContainer>{props.flamechart}</ZoomViewContainer>
  66. <FrameStackContainer ref={frameStackRef} layout={layout}>
  67. {cloneElement(props.frameStack, {onResize: onMouseDown})}
  68. </FrameStackContainer>
  69. </ProfilingFlamechartGrid>
  70. </ProfilingFlamechartLayoutContainer>
  71. );
  72. }
  73. const ProfilingFlamechartLayoutContainer = styled('div')`
  74. display: flex;
  75. flex: 1 1 100%;
  76. `;
  77. const ProfilingFlamechartGrid = styled('div')<{
  78. layout?: FlamegraphPreferences['layout'];
  79. }>`
  80. display: grid;
  81. width: 100%;
  82. grid-template-rows: ${({layout}) =>
  83. layout === 'table_bottom'
  84. ? 'auto 1fr'
  85. : layout === 'table_right'
  86. ? '100px auto'
  87. : '100px auto'};
  88. grid-template-columns: ${({layout}) =>
  89. layout === 'table_bottom'
  90. ? '100%'
  91. : layout === 'table_left'
  92. ? `min-content auto`
  93. : `auto min-content`};
  94. /* false positive for grid layout */
  95. /* stylelint-disable */
  96. grid-template-areas: ${({layout}) =>
  97. layout === 'table_bottom'
  98. ? `
  99. 'minimap'
  100. 'flamegraph'
  101. 'frame-stack'
  102. `
  103. : layout === 'table_right'
  104. ? `
  105. 'minimap frame-stack'
  106. 'flamegraph frame-stack'
  107. `
  108. : layout === 'table_left'
  109. ? `
  110. 'frame-stack minimap'
  111. 'frame-stack flamegraph'
  112. `
  113. : ''};
  114. `;
  115. const MinimapContainer = styled('div')<{
  116. height: FlamegraphTheme['SIZES']['MINIMAP_HEIGHT'];
  117. }>`
  118. position: relative;
  119. height: ${p => p.height}px;
  120. grid-area: minimap;
  121. `;
  122. const ZoomViewContainer = styled('div')`
  123. display: flex;
  124. flex-direction: column;
  125. flex: 1 1 100%;
  126. grid-area: flamegraph;
  127. `;
  128. const FrameStackContainer = styled('div')<{layout: FlamegraphPreferences['layout']}>`
  129. grid-area: frame-stack;
  130. position: relative;
  131. overflow: auto;
  132. > div {
  133. position: absolute;
  134. left: 0;
  135. top: 0;
  136. width: 100%;
  137. height: 100%;
  138. }
  139. `;