index.tsx 5.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230
  1. import {Fragment} from 'react';
  2. import styled from '@emotion/styled';
  3. import ErrorBoundary from 'sentry/components/errorBoundary';
  4. import ReplayTimeline from 'sentry/components/replays/breadcrumbs/replayTimeline';
  5. import ReplayView from 'sentry/components/replays/replayView';
  6. import space from 'sentry/styles/space';
  7. import useFullscreen from 'sentry/utils/replays/hooks/useFullscreen';
  8. import useUrlParams from 'sentry/utils/replays/hooks/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 FluidPanel from 'sentry/views/replays/detail/layout/fluidPanel';
  13. import SplitPanel from 'sentry/views/replays/detail/layout/splitPanel';
  14. import SideTabs from 'sentry/views/replays/detail/sideTabs';
  15. import TagPanel from 'sentry/views/replays/detail/tagPanel';
  16. type Layout =
  17. /**
  18. * ### Sidebar Right
  19. * ┌───────────────────┐
  20. * │ Timeline │
  21. * ├──────────┬────────┤
  22. * │ Details > Video │
  23. * │ > │
  24. * │ >^^^^^^^^┤
  25. * │ > Crumbs │
  26. * │ > │
  27. * └──────────┴────────┘
  28. */
  29. | 'sidebar_right'
  30. /**
  31. * ### Sidebar Left
  32. * ┌───────────────────┐
  33. * │ Timeline │
  34. * ├────────┬──────────┤
  35. * │ Video > Details │
  36. * │ > │
  37. * │^^^^^^^ > |
  38. * │ Crumbs > │
  39. * │ > │
  40. * └────────┴──────────┘
  41. */
  42. | 'sidebar_left'
  43. /**
  44. * ### Topbar
  45. *┌────────────────────┐
  46. *│ Timeline │
  47. *├───────────┬────────┤
  48. *│ Video │ Crumbs │
  49. *│ │ │
  50. *├^^^^^^^^^^^^^^^^^^^^┤
  51. *│ Details │
  52. *│ │
  53. *└────────────────────┘
  54. */
  55. | 'topbar';
  56. const MIN_VIDEO_WIDTH = {px: 325};
  57. const MIN_CONTENT_WIDTH = {px: 325};
  58. const MIN_VIDEO_HEIGHT = {px: 200};
  59. const MIN_CONTENT_HEIGHT = {px: 200};
  60. const MIN_CRUMBS_HEIGHT = {px: 200};
  61. type Props = {
  62. layout?: Layout;
  63. showCrumbs?: boolean;
  64. showTimeline?: boolean;
  65. showVideo?: boolean;
  66. };
  67. function ReplayLayout({
  68. layout = 'topbar',
  69. showCrumbs = true,
  70. showTimeline = true,
  71. showVideo = true,
  72. }: Props) {
  73. const {ref: fullscreenRef, isFullscreen, toggle: toggleFullscreen} = useFullscreen();
  74. const timeline = showTimeline ? (
  75. <ErrorBoundary mini>
  76. <ReplayTimeline />
  77. </ErrorBoundary>
  78. ) : null;
  79. const video = showVideo ? (
  80. <VideoSection ref={fullscreenRef}>
  81. <ErrorBoundary mini>
  82. <ReplayView toggleFullscreen={toggleFullscreen} isFullscreen={isFullscreen} />
  83. </ErrorBoundary>
  84. </VideoSection>
  85. ) : null;
  86. const crumbs = showCrumbs ? (
  87. <ErrorBoundary mini>
  88. <Breadcrumbs />
  89. </ErrorBoundary>
  90. ) : null;
  91. const content = (
  92. <ErrorBoundary mini>
  93. <FluidPanel title={<FocusTabs />}>
  94. <FocusArea />
  95. </FluidPanel>
  96. </ErrorBoundary>
  97. );
  98. if (layout === 'sidebar_right') {
  99. return (
  100. <BodyContent>
  101. {timeline}
  102. <SplitPanel
  103. left={{
  104. content,
  105. default: '60%',
  106. min: MIN_CONTENT_WIDTH,
  107. }}
  108. right={{
  109. content: <SidebarContent video={video} crumbs={crumbs} />,
  110. default: '325px',
  111. min: MIN_VIDEO_WIDTH,
  112. }}
  113. />
  114. </BodyContent>
  115. );
  116. }
  117. if (layout === 'sidebar_left') {
  118. return (
  119. <BodyContent>
  120. {timeline}
  121. <SplitPanel
  122. left={{
  123. content: <SidebarContent video={video} crumbs={crumbs} />,
  124. default: '325px',
  125. min: MIN_VIDEO_WIDTH,
  126. }}
  127. right={{
  128. content,
  129. default: '60%',
  130. min: MIN_CONTENT_WIDTH,
  131. }}
  132. />
  133. </BodyContent>
  134. );
  135. }
  136. // layout === 'topbar' or default
  137. return (
  138. <BodyContent>
  139. {timeline}
  140. <SplitPanel
  141. top={{
  142. content: (
  143. <SplitPanel
  144. left={{
  145. content: video,
  146. min: MIN_VIDEO_WIDTH,
  147. }}
  148. right={{
  149. content: crumbs,
  150. default: '30%',
  151. }}
  152. />
  153. ),
  154. default: '325px',
  155. min: MIN_VIDEO_HEIGHT,
  156. }}
  157. bottom={{
  158. content,
  159. min: MIN_CONTENT_HEIGHT,
  160. }}
  161. />
  162. </BodyContent>
  163. );
  164. }
  165. function SidebarContent({video, crumbs}) {
  166. const {getParamValue} = useUrlParams('t_side', 'video');
  167. if (getParamValue() === 'tags') {
  168. return (
  169. <FluidPanel title={<SideTabs />}>
  170. <TagPanel />
  171. </FluidPanel>
  172. );
  173. }
  174. if (video && crumbs) {
  175. return (
  176. <FluidPanel title={<SideTabs />}>
  177. <SplitPanel
  178. top={{
  179. content: video,
  180. default: '55%',
  181. min: MIN_VIDEO_HEIGHT,
  182. }}
  183. bottom={{
  184. content: crumbs,
  185. min: MIN_CRUMBS_HEIGHT,
  186. }}
  187. />
  188. </FluidPanel>
  189. );
  190. }
  191. return (
  192. <Fragment>
  193. {video}
  194. {crumbs}
  195. </Fragment>
  196. );
  197. }
  198. const BodyContent = styled('main')`
  199. background: ${p => p.theme.background};
  200. width: 100%;
  201. height: 100%;
  202. display: grid;
  203. grid-template-rows: auto 1fr;
  204. overflow: hidden;
  205. padding: ${space(2)};
  206. `;
  207. export const VideoSection = styled('section')`
  208. height: 100%;
  209. display: flex;
  210. flex-grow: 1;
  211. flex-wrap: nowrap;
  212. flex-direction: column;
  213. `;
  214. export default ReplayLayout;