cursorGuideHandler.tsx 3.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119
  1. import {Component, createContext} from 'react';
  2. import {clamp, rectOfContent} from 'sentry/components/performance/waterfall/utils';
  3. import {DragManagerChildrenProps} from './dragManager';
  4. import {ParsedTraceType} from './types';
  5. export type CursorGuideManagerChildrenProps = {
  6. displayCursorGuide: (mousePageX: number) => void;
  7. hideCursorGuide: () => void;
  8. mouseLeft: number | undefined;
  9. showCursorGuide: boolean;
  10. traceViewMouseLeft: number | undefined;
  11. };
  12. const CursorGuideManagerContext = createContext<CursorGuideManagerChildrenProps>({
  13. showCursorGuide: false,
  14. mouseLeft: void 0,
  15. traceViewMouseLeft: void 0,
  16. displayCursorGuide: () => {},
  17. hideCursorGuide: () => {},
  18. });
  19. type PropType = {
  20. children: React.ReactNode;
  21. dragProps: DragManagerChildrenProps;
  22. // this is the DOM element where the drag events occur. it's also the reference point
  23. // for calculating the relative mouse x coordinate.
  24. interactiveLayerRef: React.RefObject<HTMLDivElement>;
  25. trace: ParsedTraceType;
  26. };
  27. type StateType = {
  28. mouseLeft: number | undefined;
  29. showCursorGuide: boolean;
  30. traceViewMouseLeft: number | undefined;
  31. };
  32. export class Provider extends Component<PropType, StateType> {
  33. state: StateType = {
  34. showCursorGuide: false,
  35. mouseLeft: void 0,
  36. traceViewMouseLeft: void 0,
  37. };
  38. hasInteractiveLayer = (): boolean => !!this.props.interactiveLayerRef.current;
  39. displayCursorGuide = (mousePageX: number) => {
  40. if (!this.hasInteractiveLayer()) {
  41. return;
  42. }
  43. const {trace, dragProps} = this.props;
  44. const interactiveLayer = this.props.interactiveLayerRef.current!;
  45. const rect = rectOfContent(interactiveLayer);
  46. // duration of the entire trace in seconds
  47. const traceDuration = trace.traceEndTimestamp - trace.traceStartTimestamp;
  48. const viewStart = dragProps.viewWindowStart;
  49. const viewEnd = dragProps.viewWindowEnd;
  50. const viewStartTimestamp = trace.traceStartTimestamp + viewStart * traceDuration;
  51. const viewEndTimestamp = trace.traceEndTimestamp - (1 - viewEnd) * traceDuration;
  52. const viewDuration = viewEndTimestamp - viewStartTimestamp;
  53. // clamp mouseLeft to be within [0, 1]
  54. const mouseLeft = clamp((mousePageX - rect.x) / rect.width, 0, 1);
  55. const duration =
  56. mouseLeft * Math.abs(trace.traceEndTimestamp - trace.traceStartTimestamp);
  57. const startTimestamp = trace.traceStartTimestamp + duration;
  58. const start = (startTimestamp - viewStartTimestamp) / viewDuration;
  59. this.setState({
  60. showCursorGuide: true,
  61. mouseLeft,
  62. traceViewMouseLeft: start,
  63. });
  64. };
  65. hideCursorGuide = () => {
  66. if (!this.hasInteractiveLayer()) {
  67. return;
  68. }
  69. this.setState({
  70. showCursorGuide: false,
  71. mouseLeft: void 0,
  72. traceViewMouseLeft: void 0,
  73. });
  74. };
  75. render() {
  76. const childrenProps = {
  77. showCursorGuide: this.state.showCursorGuide,
  78. mouseLeft: this.state.mouseLeft,
  79. traceViewMouseLeft: this.state.traceViewMouseLeft,
  80. displayCursorGuide: this.displayCursorGuide,
  81. hideCursorGuide: this.hideCursorGuide,
  82. };
  83. return (
  84. <CursorGuideManagerContext.Provider value={childrenProps}>
  85. {this.props.children}
  86. </CursorGuideManagerContext.Provider>
  87. );
  88. }
  89. }
  90. export const Consumer = CursorGuideManagerContext.Consumer;