replayTimelineSpans.tsx 2.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104
  1. import {memo, useCallback} from 'react';
  2. import styled from '@emotion/styled';
  3. import CountTooltipContent from 'sentry/components/replays/countTooltipContent';
  4. import {divide, flattenFrames} from 'sentry/components/replays/utils';
  5. import {Tooltip} from 'sentry/components/tooltip';
  6. import {t} from 'sentry/locale';
  7. import ConfigStore from 'sentry/stores/configStore';
  8. import {space} from 'sentry/styles/space';
  9. import toPercent from 'sentry/utils/number/toPercent';
  10. import useActiveReplayTab, {TabKey} from 'sentry/utils/replays/hooks/useActiveReplayTab';
  11. import type {SpanFrame} from 'sentry/utils/replays/types';
  12. type Props = {
  13. /**
  14. * Duration, in milliseconds, of the timeline
  15. */
  16. durationMs: number;
  17. /**
  18. * The spans to render into the timeline
  19. */
  20. frames: SpanFrame[];
  21. /**
  22. * Timestamp when the timeline begins, in milliseconds
  23. */
  24. startTimestampMs: number;
  25. /**
  26. * Extra classNames
  27. */
  28. className?: string;
  29. };
  30. function ReplayTimelineSpans({className, durationMs, frames, startTimestampMs}: Props) {
  31. const flattened = flattenFrames(frames);
  32. const {setActiveTab} = useActiveReplayTab();
  33. const isDark = ConfigStore.get('theme') === 'dark';
  34. const handleClick = useCallback(() => setActiveTab(TabKey.NETWORK), [setActiveTab]);
  35. return (
  36. <Spans isDark={isDark} className={className}>
  37. {flattened.map((span, i) => {
  38. const sinceStart = span.startTimestamp - startTimestampMs;
  39. const startPct = divide(sinceStart, durationMs);
  40. const widthPct = divide(span.duration, durationMs);
  41. return (
  42. <Tooltip
  43. key={i}
  44. title={
  45. <CountTooltipContent>
  46. <dt>{t('Network Requests:')}</dt>
  47. <dd>{span.frameCount}</dd>
  48. <dt>{t('Duration:')}</dt>
  49. <dd>{span.duration.toLocaleString()}ms</dd>
  50. </CountTooltipContent>
  51. }
  52. skipWrapper
  53. position="bottom"
  54. >
  55. <Span
  56. style={{
  57. left: toPercent(startPct),
  58. width: toPercent(widthPct),
  59. }}
  60. onClick={handleClick}
  61. />
  62. </Tooltip>
  63. );
  64. })}
  65. </Spans>
  66. );
  67. }
  68. const Spans = styled('ul')<{isDark: boolean}>`
  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. & > li {
  78. background: ${p => (p.isDark ? p.theme.charts.colors[5] : p.theme.charts.colors[0])};
  79. }
  80. `;
  81. const Span = styled('li')`
  82. cursor: pointer;
  83. display: block;
  84. position: absolute;
  85. min-width: 1px;
  86. height: 100%;
  87. border-radius: 2px;
  88. pointer-events: auto;
  89. `;
  90. export default memo(ReplayTimelineSpans);