scrubber.tsx 4.7 KB

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