replayTimelineSpans.tsx 2.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102
  1. import {Fragment, memo} from 'react';
  2. import styled from '@emotion/styled';
  3. import {divide, flattenSpans} from 'sentry/components/replays/utils';
  4. import {Tooltip} from 'sentry/components/tooltip';
  5. import {tn} from 'sentry/locale';
  6. import ConfigStore from 'sentry/stores/configStore';
  7. import {space} from 'sentry/styles/space';
  8. import useActiveReplayTab from 'sentry/utils/replays/hooks/useActiveReplayTab';
  9. import type {ReplaySpan} from 'sentry/views/replays/types';
  10. type Props = {
  11. /**
  12. * Duration, in milliseconds, of the timeline
  13. */
  14. durationMs: number;
  15. /**
  16. * The spans to render into the timeline
  17. */
  18. spans: ReplaySpan[];
  19. /**
  20. * Timestamp when the timeline begins, in milliseconds
  21. */
  22. startTimestampMs: number;
  23. /**
  24. * Extra classNames
  25. */
  26. className?: string;
  27. };
  28. function ReplayTimelineEvents({className, durationMs, spans, startTimestampMs}: Props) {
  29. const flattenedSpans = flattenSpans(spans);
  30. const {setActiveTab} = useActiveReplayTab();
  31. return (
  32. <Spans className={className}>
  33. {flattenedSpans.map((span, i) => {
  34. const sinceStart = span.startTimestamp - startTimestampMs;
  35. const startPct = divide(sinceStart, durationMs);
  36. const widthPct = divide(span.duration, durationMs);
  37. const requestsCount = tn(
  38. '%s network request',
  39. '%s network requests',
  40. span.spanCount
  41. );
  42. return (
  43. <Tooltip
  44. key={i}
  45. title={
  46. <Fragment>
  47. {requestsCount}
  48. <br />
  49. {span.duration.toFixed(2)}ms
  50. </Fragment>
  51. }
  52. skipWrapper
  53. disableForVisualTest
  54. position="bottom"
  55. >
  56. <Span
  57. isDark={ConfigStore.get('theme') === 'dark'}
  58. startPct={startPct}
  59. widthPct={widthPct}
  60. onClick={() => setActiveTab('network')}
  61. />
  62. </Tooltip>
  63. );
  64. })}
  65. </Spans>
  66. );
  67. }
  68. const Spans = styled('ul')`
  69. /* Reset defaults for <ul> */
  70. list-style: none;
  71. margin: 0;
  72. padding: 0;
  73. height: ${space(1.5)};
  74. margin-bottom: ${space(0.5)};
  75. position: relative;
  76. pointer-events: none;
  77. `;
  78. const Span = styled('li')<{isDark: boolean; startPct: number; widthPct: number}>`
  79. cursor: pointer;
  80. display: block;
  81. position: absolute;
  82. left: ${p => p.startPct * 100}%;
  83. min-width: 1px;
  84. width: ${p => p.widthPct * 100}%;
  85. height: 100%;
  86. background: ${p => (p.isDark ? p.theme.charts.colors[5] : p.theme.charts.colors[0])};
  87. border-radius: 2px;
  88. pointer-events: auto;
  89. `;
  90. export default memo(ReplayTimelineEvents);