index.tsx 4.1 KB

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