replayController.tsx 3.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. import React from 'react';
  2. import styled from '@emotion/styled';
  3. import Button from 'sentry/components/button';
  4. import ButtonBar from 'sentry/components/buttonBar';
  5. import CompactSelect from 'sentry/components/forms/compactSelect';
  6. import {useReplayContext} from 'sentry/components/replays/replayContext';
  7. import useFullscreen from 'sentry/components/replays/useFullscreen';
  8. import {IconArrow, IconPause, IconPlay, IconRefresh, IconResize} from 'sentry/icons';
  9. import {t} from 'sentry/locale';
  10. import space from 'sentry/styles/space';
  11. import {formatTime} from './utils';
  12. const SECOND = 1000;
  13. interface Props {
  14. speedOptions?: number[];
  15. toggleFullscreen?: () => void;
  16. }
  17. function ReplayPlayPauseBar() {
  18. const {currentTime, isPlaying, setCurrentTime, togglePlayPause} = useReplayContext();
  19. return (
  20. <ButtonBar merged>
  21. <Button
  22. size="xsmall"
  23. title={t('Go back 10 seconds')}
  24. icon={<IconRefresh size="sm" />}
  25. onClick={() => setCurrentTime(currentTime - 10 * SECOND)}
  26. aria-label={t('Go back 10 seconds')}
  27. />
  28. <Button
  29. size="xsmall"
  30. title={isPlaying ? t('Pause the Replay') : t('Play the Replay')}
  31. icon={isPlaying ? <IconPause size="sm" /> : <IconPlay size="sm" />}
  32. onClick={() => togglePlayPause(!isPlaying)}
  33. aria-label={isPlaying ? t('Pause the Replay') : t('Play the Replay')}
  34. />
  35. <Button
  36. size="xsmall"
  37. title={t('Go forward 10 seconds')}
  38. icon={<IconClockwise size="sm" />}
  39. onClick={() => setCurrentTime(currentTime + 10 * SECOND)}
  40. aria-label={t('Go forward 10 seconds')}
  41. />
  42. </ButtonBar>
  43. );
  44. }
  45. function ReplayCurrentTime() {
  46. const {currentTime, duration} = useReplayContext();
  47. return (
  48. <span>
  49. {formatTime(currentTime)} / {duration ? formatTime(duration) : '??:??'}
  50. </span>
  51. );
  52. }
  53. function ReplayPlaybackSpeed({speedOptions}: {speedOptions: number[]}) {
  54. const {setSpeed, speed} = useReplayContext();
  55. return (
  56. <CompactSelect
  57. triggerProps={{
  58. size: 'xsmall',
  59. prefix: t('Speed'),
  60. }}
  61. value={speed}
  62. options={speedOptions.map(speedOption => ({
  63. value: speedOption,
  64. label: `${speedOption}x`,
  65. disabled: speedOption === speed,
  66. }))}
  67. onChange={opt => {
  68. setSpeed(opt.value);
  69. }}
  70. />
  71. );
  72. }
  73. const ReplayControls = ({
  74. toggleFullscreen = () => {},
  75. speedOptions = [0.1, 0.25, 0.5, 1, 2, 4],
  76. }: Props) => {
  77. const {isFullscreen} = useFullscreen();
  78. const {isSkippingInactive, toggleSkipInactive} = useReplayContext();
  79. return (
  80. <ButtonGrid>
  81. <ReplayPlayPauseBar />
  82. <ReplayCurrentTime />
  83. {/* TODO(replay): Need a better icon for the FastForward toggle */}
  84. <Button
  85. size="xsmall"
  86. title={t('Fast-forward idle moments')}
  87. aria-label={t('Fast-forward idle moments')}
  88. icon={<IconArrow size="sm" direction="right" />}
  89. priority={isSkippingInactive ? 'primary' : undefined}
  90. onClick={() => toggleSkipInactive(!isSkippingInactive)}
  91. />
  92. <ReplayPlaybackSpeed speedOptions={speedOptions} />
  93. <Button
  94. size="xsmall"
  95. title={isFullscreen ? t('Exit full screen') : t('View in full screen')}
  96. aria-label={isFullscreen ? t('Exit full screen') : t('View in full screen')}
  97. icon={<IconResize size="sm" />}
  98. priority={isFullscreen ? 'primary' : undefined}
  99. onClick={toggleFullscreen}
  100. />
  101. </ButtonGrid>
  102. );
  103. };
  104. const IconClockwise = styled(IconRefresh)`
  105. transform: scaleX(-1);
  106. `;
  107. const ButtonGrid = styled('div')`
  108. display: grid;
  109. grid-column-gap: ${space(1)};
  110. grid-template-columns: max-content auto max-content max-content max-content;
  111. align-items: center;
  112. `;
  113. export default ReplayControls;