index.tsx 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164
  1. import {useRef} from 'react';
  2. import styled from '@emotion/styled';
  3. import ErrorBoundary from 'sentry/components/errorBoundary';
  4. import ReplayController from 'sentry/components/replays/replayController';
  5. import ReplayView from 'sentry/components/replays/replayView';
  6. import {space} from 'sentry/styles/space';
  7. import useReplayLayout, {LayoutKey} from 'sentry/utils/replays/hooks/useReplayLayout';
  8. import {useDimensions} from 'sentry/utils/useDimensions';
  9. import useFullscreen from 'sentry/utils/window/useFullscreen';
  10. import FluidHeight from 'sentry/views/replays/detail/layout/fluidHeight';
  11. import FluidPanel from 'sentry/views/replays/detail/layout/fluidPanel';
  12. import FocusArea from 'sentry/views/replays/detail/layout/focusArea';
  13. import FocusTabs from 'sentry/views/replays/detail/layout/focusTabs';
  14. import SplitPanel from 'sentry/views/replays/detail/layout/splitPanel';
  15. const MIN_CONTENT_WIDTH = 340;
  16. const MIN_SIDEBAR_WIDTH = 325;
  17. const MIN_VIDEO_HEIGHT = 200;
  18. const MIN_CONTENT_HEIGHT = 180;
  19. const DIVIDER_SIZE = 16;
  20. function ReplayLayout({isVideoReplay}: {isVideoReplay?: boolean}) {
  21. const {getLayout} = useReplayLayout();
  22. const layout = getLayout() ?? LayoutKey.TOPBAR;
  23. const fullscreenRef = useRef(null);
  24. const {toggle: toggleFullscreen} = useFullscreen({
  25. elementRef: fullscreenRef,
  26. });
  27. const measureRef = useRef<HTMLDivElement>(null);
  28. const {width, height} = useDimensions({elementRef: measureRef});
  29. const video = (
  30. <VideoSection ref={fullscreenRef}>
  31. <ErrorBoundary mini>
  32. <ReplayView toggleFullscreen={toggleFullscreen} />
  33. </ErrorBoundary>
  34. </VideoSection>
  35. );
  36. const controller = (
  37. <ErrorBoundary mini>
  38. <ReplayController
  39. toggleFullscreen={toggleFullscreen}
  40. hideFastForward={isVideoReplay}
  41. />
  42. </ErrorBoundary>
  43. );
  44. if (layout === LayoutKey.VIDEO_ONLY) {
  45. return (
  46. <BodyContent>
  47. {video}
  48. {controller}
  49. </BodyContent>
  50. );
  51. }
  52. const focusArea = (
  53. <FluidPanel title={<SmallMarginFocusTabs />}>
  54. <ErrorBoundary mini>
  55. <FocusArea />
  56. </ErrorBoundary>
  57. </FluidPanel>
  58. );
  59. const hasSize = width + height > 0;
  60. if (layout === LayoutKey.NO_VIDEO) {
  61. return (
  62. <BodyContent>
  63. <FluidHeight ref={measureRef}>
  64. {hasSize ? <PanelContainer key={layout}>{focusArea}</PanelContainer> : null}
  65. </FluidHeight>
  66. </BodyContent>
  67. );
  68. }
  69. if (layout === LayoutKey.SIDEBAR_LEFT) {
  70. return (
  71. <BodyContent>
  72. <FluidHeight ref={measureRef}>
  73. {hasSize ? (
  74. <SplitPanel
  75. key={layout}
  76. availableSize={width}
  77. left={{
  78. content: <PanelContainer key={layout}>{video}</PanelContainer>,
  79. default: width * 0.5,
  80. min: MIN_SIDEBAR_WIDTH,
  81. max: width - MIN_CONTENT_WIDTH,
  82. }}
  83. right={focusArea}
  84. />
  85. ) : null}
  86. </FluidHeight>
  87. {controller}
  88. </BodyContent>
  89. );
  90. }
  91. // layout === 'topbar'
  92. return (
  93. <BodyContent>
  94. <FluidHeight ref={measureRef}>
  95. {hasSize ? (
  96. <SplitPanel
  97. key={layout}
  98. availableSize={height}
  99. top={{
  100. content: <PanelContainer>{video}</PanelContainer>,
  101. default: (height - DIVIDER_SIZE) * 0.5,
  102. min: MIN_VIDEO_HEIGHT,
  103. max: height - DIVIDER_SIZE - MIN_CONTENT_HEIGHT,
  104. }}
  105. bottom={focusArea}
  106. />
  107. ) : null}
  108. </FluidHeight>
  109. {controller}
  110. </BodyContent>
  111. );
  112. }
  113. const BodyContent = styled('main')`
  114. background: ${p => p.theme.background};
  115. width: 100%;
  116. height: 100%;
  117. display: grid;
  118. grid-template-rows: 1fr auto;
  119. gap: ${space(2)};
  120. overflow: hidden;
  121. padding: ${space(2)};
  122. `;
  123. const SmallMarginFocusTabs = styled(FocusTabs)`
  124. margin-bottom: ${space(1)};
  125. `;
  126. const VideoSection = styled(FluidHeight)`
  127. background: ${p => p.theme.background};
  128. gap: ${space(1)};
  129. :fullscreen {
  130. padding: ${space(1)};
  131. }
  132. `;
  133. const PanelContainer = styled('div')`
  134. width: 100%;
  135. height: 100%;
  136. position: relative;
  137. display: grid;
  138. overflow: auto;
  139. &.disable-iframe-pointer iframe {
  140. pointer-events: none !important;
  141. }
  142. `;
  143. export default ReplayLayout;