index.tsx 4.4 KB

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