traceContextPanel.tsx 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186
  1. import {useCallback, useEffect, useMemo, useRef, useState} from 'react';
  2. import styled from '@emotion/styled';
  3. import EventTagsTree from 'sentry/components/events/eventTags/eventTagsTree';
  4. import {IconGrabbable} from 'sentry/icons';
  5. import {t} from 'sentry/locale';
  6. import {space} from 'sentry/styles/space';
  7. import type {EventTransaction} from 'sentry/types/event';
  8. import type {UseApiQueryResult} from 'sentry/utils/queryClient';
  9. import type RequestError from 'sentry/utils/requestError/requestError';
  10. import type {SectionKey} from 'sentry/views/issueDetails/streamline/context';
  11. import {FoldSection} from 'sentry/views/issueDetails/streamline/foldSection';
  12. import {TraceContextVitals} from 'sentry/views/performance/newTraceDetails/traceContextVitals';
  13. import type {TraceTree} from 'sentry/views/performance/newTraceDetails/traceModels/traceTree';
  14. import {
  15. DEFAULT_TRACE_VIEW_PREFERENCES,
  16. loadTraceViewPreferences,
  17. } from 'sentry/views/performance/newTraceDetails/traceState/tracePreferences';
  18. import {useTraceStateDispatch} from 'sentry/views/performance/newTraceDetails/traceState/traceStateProvider';
  19. const MIN_HEIGHT = 0;
  20. const DEFAULT_HEIGHT = 150;
  21. const MAX_HEIGHT = 700;
  22. type Props = {
  23. rootEvent: UseApiQueryResult<EventTransaction, RequestError>;
  24. tree: TraceTree;
  25. };
  26. export function TraceContextPanel({tree, rootEvent}: Props) {
  27. const [height, setHeight] = useState(DEFAULT_HEIGHT);
  28. const containerRef = useRef<HTMLDivElement>(null);
  29. const traceDispatch = useTraceStateDispatch();
  30. const preferences = useMemo(
  31. () =>
  32. loadTraceViewPreferences('trace-view-preferences') ||
  33. DEFAULT_TRACE_VIEW_PREFERENCES,
  34. []
  35. );
  36. useEffect(() => {
  37. const loadedHeight = preferences.drawer.sizes['trace context height'];
  38. if (containerRef.current && loadedHeight !== undefined) {
  39. setHeight(loadedHeight);
  40. containerRef.current.style.setProperty('--panel-height', `${loadedHeight}px`);
  41. }
  42. }, [preferences.drawer.sizes, containerRef]);
  43. const handleMouseDown = useCallback(
  44. (event: React.MouseEvent) => {
  45. event.preventDefault();
  46. if (!containerRef.current) {
  47. return;
  48. }
  49. const startY = event.clientY;
  50. const startHeight = height;
  51. function handleMouseMove(moveEvent: MouseEvent) {
  52. if (!containerRef.current) {
  53. return;
  54. }
  55. const deltaY = moveEvent.clientY - startY;
  56. const newHeight = Math.max(
  57. MIN_HEIGHT,
  58. Math.min(startHeight - deltaY, MAX_HEIGHT)
  59. );
  60. containerRef.current.style.setProperty('--panel-height', `${newHeight}px`);
  61. }
  62. function handleMouseUp() {
  63. if (!containerRef.current) {
  64. return;
  65. }
  66. const finalHeight = parseInt(
  67. getComputedStyle(containerRef.current).getPropertyValue('--panel-height'),
  68. 10
  69. );
  70. setHeight(finalHeight);
  71. traceDispatch({type: 'set trace context height', payload: finalHeight});
  72. window.removeEventListener('mousemove', handleMouseMove);
  73. window.removeEventListener('mouseup', handleMouseUp);
  74. }
  75. window.addEventListener('mousemove', handleMouseMove);
  76. window.addEventListener('mouseup', handleMouseUp);
  77. },
  78. [height, traceDispatch]
  79. );
  80. const renderTags = useCallback(() => {
  81. if (!rootEvent.data) {
  82. return null;
  83. }
  84. return (
  85. <EventTagsTree
  86. event={rootEvent.data}
  87. meta={rootEvent.data._meta}
  88. projectSlug={rootEvent.data.projectSlug ?? ''}
  89. tags={rootEvent.data.tags ?? []}
  90. />
  91. );
  92. }, [rootEvent.data]);
  93. return (
  94. <Container>
  95. <GrabberContainer onMouseDown={handleMouseDown}>
  96. <IconGrabbable color="gray500" />
  97. </GrabberContainer>
  98. <TraceContextContainer ref={containerRef}>
  99. <VitalMetersContainer>
  100. <TraceContextVitals tree={tree} />
  101. </VitalMetersContainer>
  102. <TraceTagsContainer>
  103. <FoldSection sectionKey={'trace_tags' as SectionKey} title={t('Trace Tags')}>
  104. {renderTags()}
  105. </FoldSection>
  106. </TraceTagsContainer>
  107. </TraceContextContainer>
  108. </Container>
  109. );
  110. }
  111. const Container = styled('div')`
  112. width: 100%;
  113. display: flex;
  114. flex-direction: column;
  115. justify-content: center;
  116. `;
  117. const TraceContextContainer = styled('div')`
  118. display: flex;
  119. flex-direction: column;
  120. align-items: center;
  121. width: 100%;
  122. margin-top: ${space(1)};
  123. --panel-height: ${DEFAULT_HEIGHT}px;
  124. height: var(--panel-height);
  125. &[style*='--panel-height: 0px'] {
  126. display: none;
  127. }
  128. `;
  129. const GrabberContainer = styled(Container)`
  130. align-items: center;
  131. background: ${p => p.theme.background};
  132. border: 1px solid ${p => p.theme.border};
  133. border-radius: 0 0 ${p => p.theme.borderRadius} ${p => p.theme.borderRadius};
  134. display: flex;
  135. width: 100%;
  136. cursor: row-resize;
  137. padding: ${space(0.5)};
  138. & > svg {
  139. transform: rotate(90deg);
  140. }
  141. `;
  142. const VitalMetersContainer = styled('div')`
  143. display: flex;
  144. justify-content: space-between;
  145. flex-direction: row;
  146. gap: ${space(1)};
  147. width: 100%;
  148. margin-bottom: ${space(1)};
  149. `;
  150. const TraceTagsContainer = styled('div')`
  151. background-color: ${p => p.theme.background};
  152. width: 100%;
  153. border: 1px solid ${p => p.theme.border};
  154. border-radius: ${p => p.theme.borderRadius};
  155. padding: 0 ${space(0.5)};
  156. `;