import {Component, createContext} from 'react'; import {clamp, rectOfContent} from 'sentry/components/performance/waterfall/utils'; import {DragManagerChildrenProps} from './dragManager'; import {ParsedTraceType} from './types'; export type CursorGuideManagerChildrenProps = { displayCursorGuide: (mousePageX: number) => void; hideCursorGuide: () => void; mouseLeft: number | undefined; showCursorGuide: boolean; traceViewMouseLeft: number | undefined; }; const CursorGuideManagerContext = createContext({ showCursorGuide: false, mouseLeft: void 0, traceViewMouseLeft: void 0, displayCursorGuide: () => {}, hideCursorGuide: () => {}, }); type PropType = { children: React.ReactNode; dragProps: DragManagerChildrenProps; // this is the DOM element where the drag events occur. it's also the reference point // for calculating the relative mouse x coordinate. interactiveLayerRef: React.RefObject; trace: ParsedTraceType; }; type StateType = { mouseLeft: number | undefined; showCursorGuide: boolean; traceViewMouseLeft: number | undefined; }; export class Provider extends Component { state: StateType = { showCursorGuide: false, mouseLeft: void 0, traceViewMouseLeft: void 0, }; hasInteractiveLayer = (): boolean => !!this.props.interactiveLayerRef.current; displayCursorGuide = (mousePageX: number) => { if (!this.hasInteractiveLayer()) { return; } const {trace, dragProps} = this.props; const interactiveLayer = this.props.interactiveLayerRef.current!; const rect = rectOfContent(interactiveLayer); // duration of the entire trace in seconds const traceDuration = trace.traceEndTimestamp - trace.traceStartTimestamp; const viewStart = dragProps.viewWindowStart; const viewEnd = dragProps.viewWindowEnd; const viewStartTimestamp = trace.traceStartTimestamp + viewStart * traceDuration; const viewEndTimestamp = trace.traceEndTimestamp - (1 - viewEnd) * traceDuration; const viewDuration = viewEndTimestamp - viewStartTimestamp; // clamp mouseLeft to be within [0, 1] const mouseLeft = clamp((mousePageX - rect.x) / rect.width, 0, 1); const duration = mouseLeft * Math.abs(trace.traceEndTimestamp - trace.traceStartTimestamp); const startTimestamp = trace.traceStartTimestamp + duration; const start = (startTimestamp - viewStartTimestamp) / viewDuration; this.setState({ showCursorGuide: true, mouseLeft, traceViewMouseLeft: start, }); }; hideCursorGuide = () => { if (!this.hasInteractiveLayer()) { return; } this.setState({ showCursorGuide: false, mouseLeft: void 0, traceViewMouseLeft: void 0, }); }; render() { const childrenProps = { showCursorGuide: this.state.showCursorGuide, mouseLeft: this.state.mouseLeft, traceViewMouseLeft: this.state.traceViewMouseLeft, displayCursorGuide: this.displayCursorGuide, hideCursorGuide: this.hideCursorGuide, }; return ( {this.props.children} ); } } export const Consumer = CursorGuideManagerContext.Consumer;