index.tsx 4.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204
  1. import styled from '@emotion/styled';
  2. import ErrorBoundary from 'sentry/components/errorBoundary';
  3. import ReplayTimeline from 'sentry/components/replays/breadcrumbs/replayTimeline';
  4. import ReplayView from 'sentry/components/replays/replayView';
  5. import space from 'sentry/styles/space';
  6. import useFullscreen from 'sentry/utils/replays/hooks/useFullscreen';
  7. import {LayoutKey} from 'sentry/utils/replays/hooks/useReplayLayout';
  8. import useUrlParams from 'sentry/utils/useUrlParams';
  9. import Breadcrumbs from 'sentry/views/replays/detail/breadcrumbs';
  10. import FocusArea from 'sentry/views/replays/detail/focusArea';
  11. import FocusTabs from 'sentry/views/replays/detail/focusTabs';
  12. import FluidHeight from 'sentry/views/replays/detail/layout/fluidHeight';
  13. import FluidPanel from 'sentry/views/replays/detail/layout/fluidPanel';
  14. import SplitPanel from 'sentry/views/replays/detail/layout/splitPanel';
  15. import SideTabs from 'sentry/views/replays/detail/sideTabs';
  16. import TagPanel from 'sentry/views/replays/detail/tagPanel';
  17. const MIN_VIDEO_WIDTH = {px: 325};
  18. const MIN_CONTENT_WIDTH = {px: 325};
  19. const MIN_SIDEBAR_WIDTH = {px: 325};
  20. const MIN_VIDEO_HEIGHT = {px: 200};
  21. const MIN_CONTENT_HEIGHT = {px: 180};
  22. const MIN_SIDEBAR_HEIGHT = {px: 120};
  23. type Props = {
  24. layout?: LayoutKey;
  25. };
  26. function ReplayLayout({layout = LayoutKey.topbar}: Props) {
  27. const {ref: fullscreenRef, toggle: toggleFullscreen} = useFullscreen();
  28. const timeline = (
  29. <ErrorBoundary mini>
  30. <ReplayTimeline />
  31. </ErrorBoundary>
  32. );
  33. const video = (
  34. <VideoSection ref={fullscreenRef}>
  35. <ErrorBoundary mini>
  36. <ReplayView toggleFullscreen={toggleFullscreen} />
  37. </ErrorBoundary>
  38. </VideoSection>
  39. );
  40. if (layout === LayoutKey.video_only) {
  41. return (
  42. <BodyContent>
  43. {timeline}
  44. {video}
  45. </BodyContent>
  46. );
  47. }
  48. const focusArea = (
  49. <ErrorBoundary mini>
  50. <FluidPanel title={<SmallMarginFocusTabs />}>
  51. <FocusArea />
  52. </FluidPanel>
  53. </ErrorBoundary>
  54. );
  55. if (layout === LayoutKey.no_video) {
  56. return (
  57. <BodyContent>
  58. {timeline}
  59. <SplitPanel
  60. key={layout}
  61. left={{
  62. content: focusArea,
  63. default: '1fr',
  64. min: MIN_CONTENT_WIDTH,
  65. }}
  66. right={{
  67. content: <SideCrumbsTags />,
  68. min: MIN_SIDEBAR_WIDTH,
  69. }}
  70. />
  71. </BodyContent>
  72. );
  73. }
  74. const sideVideoCrumbs = (
  75. <SplitPanel
  76. key={layout}
  77. top={{
  78. content: video,
  79. default: '65%',
  80. min: MIN_CONTENT_WIDTH,
  81. }}
  82. bottom={{
  83. content: <SideCrumbsTags />,
  84. min: MIN_SIDEBAR_HEIGHT,
  85. }}
  86. />
  87. );
  88. if (layout === LayoutKey.sidebar_left) {
  89. return (
  90. <BodyContent>
  91. {timeline}
  92. <SplitPanel
  93. key={layout}
  94. left={{
  95. content: sideVideoCrumbs,
  96. min: MIN_SIDEBAR_WIDTH,
  97. }}
  98. right={{
  99. content: focusArea,
  100. default: '1fr',
  101. min: MIN_CONTENT_WIDTH,
  102. }}
  103. />
  104. </BodyContent>
  105. );
  106. }
  107. // layout === 'topbar' or default
  108. const crumbsWithTitle = (
  109. <ErrorBoundary mini>
  110. <Breadcrumbs showTitle />
  111. </ErrorBoundary>
  112. );
  113. return (
  114. <BodyContent>
  115. {timeline}
  116. <SplitPanel
  117. key={layout}
  118. top={{
  119. content: (
  120. <SplitPanel
  121. left={{
  122. content: video,
  123. default: '1fr',
  124. min: MIN_VIDEO_WIDTH,
  125. }}
  126. right={{
  127. content: crumbsWithTitle,
  128. }}
  129. />
  130. ),
  131. min: MIN_VIDEO_HEIGHT,
  132. }}
  133. bottom={{
  134. content: focusArea,
  135. default: '1fr',
  136. min: MIN_CONTENT_HEIGHT,
  137. }}
  138. />
  139. </BodyContent>
  140. );
  141. }
  142. function SideCrumbsTags() {
  143. const {getParamValue} = useUrlParams('t_side', 'crumbs');
  144. const sideTabs = <SmallMarginSideTabs />;
  145. if (getParamValue() === 'tags') {
  146. return (
  147. <FluidPanel title={sideTabs}>
  148. <TagPanel />
  149. </FluidPanel>
  150. );
  151. }
  152. return (
  153. <FluidPanel title={sideTabs}>
  154. <ErrorBoundary mini>
  155. <Breadcrumbs showTitle={false} />
  156. </ErrorBoundary>
  157. </FluidPanel>
  158. );
  159. }
  160. const BodyContent = styled('main')`
  161. background: ${p => p.theme.background};
  162. width: 100%;
  163. height: 100%;
  164. display: grid;
  165. grid-template-rows: auto 1fr;
  166. overflow: hidden;
  167. padding: ${space(2)};
  168. `;
  169. const SmallMarginFocusTabs = styled(FocusTabs)`
  170. margin-bottom: ${space(1)};
  171. `;
  172. const SmallMarginSideTabs = styled(SideTabs)`
  173. margin-bottom: ${space(1)};
  174. `;
  175. const VideoSection = styled(FluidHeight)`
  176. height: 100%;
  177. background: ${p => p.theme.background};
  178. gap: ${space(1)};
  179. :fullscreen {
  180. padding: ${space(1)};
  181. }
  182. `;
  183. export default ReplayLayout;