gridLines.tsx 3.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. import {useCallback} from 'react';
  2. import styled from '@emotion/styled';
  3. import moment from 'moment';
  4. import DateTime from 'sentry/components/dateTime';
  5. import {space} from 'sentry/styles/space';
  6. import {TimeWindow} from 'sentry/views/monitors/components/overviewTimeline/types';
  7. import {
  8. getStartFromTimeWindow,
  9. timeWindowConfig,
  10. } from 'sentry/views/monitors/components/overviewTimeline/utils';
  11. import {useTimelineCursor} from './timelineCursor';
  12. interface Props {
  13. end: Date;
  14. timeWindow: TimeWindow;
  15. width: number;
  16. showCursor?: boolean;
  17. }
  18. function clampTimeBasedOnResolution(date: moment.Moment, resolution: string) {
  19. date.startOf('minute');
  20. if (resolution === '1h') {
  21. date.minute(date.minutes() - (date.minutes() % 10));
  22. } else if (resolution === '30d') {
  23. date.startOf('day');
  24. } else {
  25. date.startOf('hour');
  26. }
  27. }
  28. interface TimeMarker {
  29. date: Date;
  30. position: number;
  31. }
  32. function getTimeMarkers(end: Date, timeWindow: TimeWindow, width: number): TimeMarker[] {
  33. const {elapsedMinutes, timeMarkerInterval} = timeWindowConfig[timeWindow];
  34. const msPerPixel = (elapsedMinutes * 60 * 1000) / width;
  35. const times: TimeMarker[] = [];
  36. const start = getStartFromTimeWindow(end, timeWindow);
  37. const firstTimeMark = moment(start);
  38. clampTimeBasedOnResolution(firstTimeMark, timeWindow);
  39. // Generate time markers which represent location of grid lines/time labels
  40. for (let i = 1; i < elapsedMinutes / timeMarkerInterval; i++) {
  41. const timeMark = moment(firstTimeMark).add(i * timeMarkerInterval, 'minute');
  42. const position = (timeMark.valueOf() - start.valueOf()) / msPerPixel;
  43. times.push({date: timeMark.toDate(), position});
  44. }
  45. return times;
  46. }
  47. export function GridLineTimeLabels({end, timeWindow, width}: Props) {
  48. return (
  49. <LabelsContainer>
  50. {getTimeMarkers(end, timeWindow, width).map(({date, position}) => (
  51. <TimeLabelContainer key={date.getTime()} left={position}>
  52. <TimeLabel date={date} {...timeWindowConfig[timeWindow].dateTimeProps} />
  53. </TimeLabelContainer>
  54. ))}
  55. </LabelsContainer>
  56. );
  57. }
  58. export function GridLineOverlay({end, timeWindow, width, showCursor}: Props) {
  59. const {cursorLabelFormat} = timeWindowConfig[timeWindow];
  60. const makeCursorText = useCallback(
  61. (percentPosition: number) => {
  62. const start = getStartFromTimeWindow(end, timeWindow);
  63. const timeOffset = (end.getTime() - start.getTime()) * percentPosition;
  64. return moment(start.getTime() + timeOffset).format(cursorLabelFormat);
  65. },
  66. [cursorLabelFormat, end, timeWindow]
  67. );
  68. const {cursorContainerRef, timelineCursor} = useTimelineCursor<HTMLDivElement>({
  69. enabled: showCursor,
  70. labelText: makeCursorText,
  71. });
  72. return (
  73. <Overlay ref={cursorContainerRef}>
  74. {timelineCursor}
  75. <GridLineContainer>
  76. {getTimeMarkers(end, timeWindow, width).map(({date, position}) => (
  77. <Gridline key={date.getTime()} left={position} />
  78. ))}
  79. </GridLineContainer>
  80. </Overlay>
  81. );
  82. }
  83. const Overlay = styled('div')`
  84. grid-row: 1;
  85. grid-column: 3;
  86. height: 100%;
  87. width: 100%;
  88. position: absolute;
  89. pointer-events: none;
  90. `;
  91. const GridLineContainer = styled('div')`
  92. position: relative;
  93. height: 100%;
  94. `;
  95. const LabelsContainer = styled('div')`
  96. position: relative;
  97. align-self: stretch;
  98. border-bottom: 1px solid ${p => p.theme.border};
  99. `;
  100. const Gridline = styled('div')<{left: number}>`
  101. position: absolute;
  102. left: ${p => p.left}px;
  103. border-left: 1px solid ${p => p.theme.innerBorder};
  104. height: 100%;
  105. `;
  106. const TimeLabelContainer = styled(Gridline)`
  107. display: flex;
  108. height: 100%;
  109. align-items: center;
  110. `;
  111. const TimeLabel = styled(DateTime)`
  112. font-variant-numeric: tabular-nums;
  113. font-size: ${p => p.theme.fontSizeSmall};
  114. color: ${p => p.theme.subText};
  115. margin-left: ${space(1)};
  116. `;