scrubber.tsx 5.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222
  1. import styled from '@emotion/styled';
  2. import RangeSlider from 'sentry/components/forms/controls/rangeSlider';
  3. import SliderAndInputWrapper from 'sentry/components/forms/controls/rangeSlider/sliderAndInputWrapper';
  4. import * as Progress from 'sentry/components/replays/progress';
  5. import {useReplayContext} from 'sentry/components/replays/replayContext';
  6. import {divide} from 'sentry/components/replays/utils';
  7. import {space} from 'sentry/styles/space';
  8. type Props = {
  9. className?: string;
  10. };
  11. function Scrubber({className}: Props) {
  12. const {currentHoverTime, currentTime, replay, setCurrentTime} = useReplayContext();
  13. const durationMs = replay?.getDurationMs();
  14. const percentComplete = divide(currentTime, durationMs);
  15. const hoverPlace = divide(currentHoverTime || 0, durationMs);
  16. return (
  17. <Wrapper className={className}>
  18. <Meter>
  19. {currentHoverTime ? (
  20. <MouseTrackingValue
  21. style={{
  22. width: hoverPlace * 100 + '%',
  23. }}
  24. />
  25. ) : null}
  26. <PlaybackTimeValue
  27. style={{
  28. width: percentComplete * 100 + '%',
  29. }}
  30. />
  31. </Meter>
  32. <RangeWrapper>
  33. <Range
  34. name="replay-timeline"
  35. min={0}
  36. max={durationMs}
  37. value={Math.round(currentTime)}
  38. onChange={value => setCurrentTime(value || 0)}
  39. showLabel={false}
  40. />
  41. </RangeWrapper>
  42. </Wrapper>
  43. );
  44. }
  45. const Meter = styled(Progress.Meter)`
  46. background: ${p => p.theme.gray200};
  47. `;
  48. const RangeWrapper = styled('div')`
  49. overflow: hidden;
  50. width: 100%;
  51. `;
  52. const Range = styled(RangeSlider)`
  53. input {
  54. cursor: pointer;
  55. opacity: 0;
  56. height: 100%;
  57. &::-webkit-slider-thumb {
  58. height: 0px;
  59. width: 0px;
  60. }
  61. &::-moz-range-thumb {
  62. height: 0px;
  63. width: 0px;
  64. }
  65. &::-ms-thumb {
  66. height: 0px;
  67. width: 0px;
  68. }
  69. }
  70. `;
  71. // Need the named value so we can target it separatly from PlaybackTimeValue
  72. const PlaybackTimeValue = styled(Progress.Value)``;
  73. const MouseTrackingValue = styled(Progress.Value)``;
  74. const Wrapper = styled('div')`
  75. position: relative;
  76. width: 100%;
  77. & > * {
  78. position: absolute;
  79. top: 0;
  80. left: 0;
  81. }
  82. `;
  83. export const TimelineScrubber = styled(Scrubber)`
  84. height: 100%;
  85. ${Meter} {
  86. background: transparent;
  87. }
  88. ${RangeWrapper},
  89. ${Range},
  90. ${SliderAndInputWrapper} {
  91. height: 100%;
  92. }
  93. ${PlaybackTimeValue} {
  94. background: ${p => p.theme.purple100};
  95. }
  96. /**
  97. * Draw lines so users can see the currenTime & their mouse position
  98. * "----|----|--------------------- duration = 1:00"
  99. * ^ ^
  100. * | PlaybackTimeValue @ 20s
  101. * MouseTrackingValue @ 10s
  102. */
  103. ${PlaybackTimeValue},
  104. ${MouseTrackingValue} {
  105. border-right: ${space(0.25)} solid ${p => p.theme.purple300};
  106. }
  107. `;
  108. export const CompactTimelineScrubber = styled(Scrubber)`
  109. height: 100%;
  110. ${Meter} {
  111. background: transparent;
  112. }
  113. ${RangeWrapper},
  114. ${Range},
  115. ${SliderAndInputWrapper} {
  116. height: 100%;
  117. }
  118. /**
  119. * Draw lines so users can see the currenTime & their mouse position
  120. * "----|----|--------------------- duration = 1:00"
  121. * ^ ^
  122. * | PlaybackTimeValue @ 20s
  123. * MouseTrackingValue @ 10s
  124. */
  125. ${PlaybackTimeValue},
  126. ${MouseTrackingValue} {
  127. border-right: ${space(0.25)} solid ${p => p.theme.purple300};
  128. }
  129. `;
  130. export const PlayerScrubber = styled(Scrubber)`
  131. height: ${space(0.5)};
  132. ${Meter} {
  133. border-radius: ${p => p.theme.borderRadius};
  134. background: ${p => p.theme.translucentInnerBorder};
  135. }
  136. ${RangeWrapper} {
  137. height: 32px;
  138. top: -14px;
  139. }
  140. ${Range},
  141. ${SliderAndInputWrapper} {
  142. height: 100%;
  143. }
  144. input {
  145. margin: 0;
  146. }
  147. ${PlaybackTimeValue} {
  148. background: ${p => p.theme.purple200};
  149. border-radius: ${p => p.theme.borderRadius};
  150. /**
  151. * Draw the circle (appears on hover) to mark the currentTime of the video
  152. * "---------o-------------------- duration = 1:00"
  153. * ^
  154. * PlaybackTimeValue @ 20s
  155. */
  156. :after {
  157. background: ${p => p.theme.purple300};
  158. }
  159. }
  160. ${MouseTrackingValue} {
  161. background: ${p => p.theme.translucentBorder};
  162. border-radius: ${p => p.theme.borderRadius};
  163. /**
  164. * Draw a square so users can see their mouse position when it is left or right of the currentTime
  165. * "----□----o--------------------- duration = 1:00"
  166. * ^ ^
  167. * | PlaybackTimeValue @ 20s
  168. * MouseTrackingValue @ 10s
  169. */
  170. :after {
  171. background: ${p => p.theme.gray300};
  172. }
  173. }
  174. ${PlaybackTimeValue}:after,
  175. ${MouseTrackingValue}:after {
  176. content: '';
  177. display: block;
  178. width: ${space(2)};
  179. height: ${space(2)}; /* equal to width */
  180. pointer-events: none;
  181. box-sizing: content-box;
  182. border-radius: ${space(2)}; /* greater than or equal to width */
  183. border: solid ${p => p.theme.white};
  184. border-width: ${space(0.25)};
  185. position: absolute;
  186. top: -${space(1)}; /* Half of the height */
  187. right: -${space(1.5)}; /* Half of (width + borderWidth) */
  188. z-index: ${p => p.theme.zIndex.initial};
  189. }
  190. `;