index.tsx 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259
  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: 200};
  22. type Props = {
  23. layout?: LayoutKey;
  24. };
  25. function ReplayLayout({layout = LayoutKey.topbar}: Props) {
  26. const {ref: fullscreenRef, toggle: toggleFullscreen} = useFullscreen();
  27. const timeline = (
  28. <ErrorBoundary mini>
  29. <ReplayTimeline />
  30. </ErrorBoundary>
  31. );
  32. const video = (
  33. <VideoSection ref={fullscreenRef}>
  34. <ErrorBoundary mini>
  35. <ReplayView toggleFullscreen={toggleFullscreen} />
  36. </ErrorBoundary>
  37. </VideoSection>
  38. );
  39. if (layout === 'video_only') {
  40. return (
  41. <BodyContent>
  42. {timeline}
  43. {video}
  44. </BodyContent>
  45. );
  46. }
  47. const focusArea = (
  48. <ErrorBoundary mini>
  49. <FluidPanel title={<SmallMarginFocusTabs />}>
  50. <FocusArea />
  51. </FluidPanel>
  52. </ErrorBoundary>
  53. );
  54. if (layout === 'no_video') {
  55. return (
  56. <BodyContent>
  57. {timeline}
  58. <SplitPanel
  59. key={layout}
  60. left={{
  61. content: focusArea,
  62. default: '75%',
  63. min: MIN_CONTENT_WIDTH,
  64. }}
  65. right={{
  66. content: <SideCrumbsTags />,
  67. min: MIN_SIDEBAR_WIDTH,
  68. }}
  69. />
  70. </BodyContent>
  71. );
  72. }
  73. if (layout === 'top') {
  74. const mainSplit = (
  75. <SplitPanel
  76. key={layout + '_main'}
  77. top={{
  78. content: video,
  79. default: '50%',
  80. min: MIN_VIDEO_HEIGHT,
  81. }}
  82. bottom={{
  83. content: focusArea,
  84. min: MIN_CONTENT_HEIGHT,
  85. }}
  86. />
  87. );
  88. return (
  89. <BodyContent>
  90. {timeline}
  91. <SplitPanel
  92. key={layout}
  93. left={{
  94. content: mainSplit,
  95. default: '75%',
  96. min: MIN_CONTENT_WIDTH,
  97. }}
  98. right={{
  99. content: <SideCrumbsTags />,
  100. min: MIN_SIDEBAR_WIDTH,
  101. }}
  102. />
  103. </BodyContent>
  104. );
  105. }
  106. const sideVideoCrumbs = (
  107. <SplitPanel
  108. key={layout}
  109. top={{
  110. content: video,
  111. default: '50%',
  112. min: MIN_CONTENT_WIDTH,
  113. }}
  114. bottom={{
  115. content: <SideCrumbsTags />,
  116. min: MIN_SIDEBAR_WIDTH,
  117. }}
  118. />
  119. );
  120. if (layout === 'sidebar_right') {
  121. return (
  122. <BodyContent>
  123. {timeline}
  124. <SplitPanel
  125. key={layout}
  126. left={{
  127. content: focusArea,
  128. default: '60%',
  129. min: MIN_CONTENT_WIDTH,
  130. }}
  131. right={{
  132. content: sideVideoCrumbs,
  133. min: MIN_SIDEBAR_WIDTH,
  134. }}
  135. />
  136. </BodyContent>
  137. );
  138. }
  139. if (layout === 'sidebar_left') {
  140. return (
  141. <BodyContent>
  142. {timeline}
  143. <SplitPanel
  144. key={layout}
  145. left={{
  146. content: sideVideoCrumbs,
  147. min: MIN_SIDEBAR_WIDTH,
  148. }}
  149. right={{
  150. content: focusArea,
  151. default: '60%',
  152. min: MIN_CONTENT_WIDTH,
  153. }}
  154. />
  155. </BodyContent>
  156. );
  157. }
  158. // layout === 'topbar' or default
  159. const crumbsWithTitle = (
  160. <ErrorBoundary mini>
  161. <Breadcrumbs showTitle />
  162. </ErrorBoundary>
  163. );
  164. return (
  165. <BodyContent>
  166. {timeline}
  167. <SplitPanel
  168. key={layout}
  169. top={{
  170. content: (
  171. <SplitPanel
  172. left={{
  173. content: video,
  174. default: '70%',
  175. min: MIN_VIDEO_WIDTH,
  176. }}
  177. right={{
  178. content: crumbsWithTitle,
  179. }}
  180. />
  181. ),
  182. min: MIN_VIDEO_HEIGHT,
  183. }}
  184. bottom={{
  185. content: focusArea,
  186. default: '60%',
  187. min: MIN_CONTENT_HEIGHT,
  188. }}
  189. />
  190. </BodyContent>
  191. );
  192. }
  193. function SideCrumbsTags() {
  194. const {getParamValue} = useUrlParams('t_side', 'crumbs');
  195. const sideTabs = <SmallMarginSideTabs />;
  196. if (getParamValue() === 'tags') {
  197. return (
  198. <FluidPanel title={sideTabs}>
  199. <TagPanel />
  200. </FluidPanel>
  201. );
  202. }
  203. return (
  204. <FluidPanel title={sideTabs}>
  205. <ErrorBoundary mini>
  206. <Breadcrumbs showTitle={false} />
  207. </ErrorBoundary>
  208. </FluidPanel>
  209. );
  210. }
  211. const BodyContent = styled('main')`
  212. background: ${p => p.theme.background};
  213. width: 100%;
  214. height: 100%;
  215. display: grid;
  216. grid-template-rows: auto 1fr;
  217. overflow: hidden;
  218. padding: ${space(2)};
  219. `;
  220. const SmallMarginFocusTabs = styled(FocusTabs)`
  221. margin-bottom: ${space(1)};
  222. `;
  223. const SmallMarginSideTabs = styled(SideTabs)`
  224. margin-bottom: ${space(1)};
  225. `;
  226. const VideoSection = styled(FluidHeight)`
  227. height: 100%;
  228. background: ${p => p.theme.background};
  229. gap: ${space(1)};
  230. :fullscreen {
  231. padding: ${space(1)};
  232. }
  233. `;
  234. export default ReplayLayout;