scrubber.tsx 4.5 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 ? (
  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 PlayerScrubber = styled(Scrubber)`
  109. height: ${space(0.5)};
  110. ${Meter} {
  111. border-radius: ${p => p.theme.borderRadius};
  112. background: ${p => p.theme.translucentInnerBorder};
  113. }
  114. ${RangeWrapper} {
  115. height: 32px;
  116. top: -14px;
  117. }
  118. ${Range},
  119. ${SliderAndInputWrapper} {
  120. height: 100%;
  121. }
  122. input {
  123. margin: 0;
  124. }
  125. ${PlaybackTimeValue} {
  126. background: ${p => p.theme.purple200};
  127. border-radius: ${p => p.theme.borderRadius};
  128. /**
  129. * Draw the circle (appears on hover) to mark the currentTime of the video
  130. * "---------o-------------------- duration = 1:00"
  131. * ^
  132. * PlaybackTimeValue @ 20s
  133. */
  134. :after {
  135. background: ${p => p.theme.purple300};
  136. }
  137. }
  138. ${MouseTrackingValue} {
  139. background: ${p => p.theme.translucentBorder};
  140. border-radius: ${p => p.theme.borderRadius};
  141. /**
  142. * Draw a square so users can see their mouse position when it is left or right of the currentTime
  143. * "----□----o--------------------- duration = 1:00"
  144. * ^ ^
  145. * | PlaybackTimeValue @ 20s
  146. * MouseTrackingValue @ 10s
  147. */
  148. :after {
  149. background: ${p => p.theme.gray300};
  150. }
  151. }
  152. ${PlaybackTimeValue}:after,
  153. ${MouseTrackingValue}:after {
  154. content: '';
  155. display: block;
  156. width: ${space(2)};
  157. height: ${space(2)}; /* equal to width */
  158. pointer-events: none;
  159. box-sizing: content-box;
  160. border-radius: ${space(2)}; /* greater than or equal to width */
  161. border: solid ${p => p.theme.white};
  162. border-width: ${space(0.25)};
  163. position: absolute;
  164. top: -${space(1)}; /* Half of the height */
  165. right: -${space(1.5)}; /* Half of (width + borderWidth) */
  166. z-index: ${p => p.theme.zIndex.initial};
  167. }
  168. `;