diff --git a/src/CanvasPage.css b/src/CanvasPage.css index 0a22311f084dd..e5d238a0d9d2c 100644 --- a/src/CanvasPage.css +++ b/src/CanvasPage.css @@ -4,5 +4,4 @@ bottom: 0.5rem; left: 0.5rem; right: 0.5rem; - overflow: hidden; } diff --git a/src/CanvasPage.js b/src/CanvasPage.js index a1256879e6fed..bf2ff72f9323d 100644 --- a/src/CanvasPage.js +++ b/src/CanvasPage.js @@ -250,25 +250,19 @@ function AutoSizedCanvas({data, height, width}: AutoSizedCanvasProps) { const interactor = useCallback( interaction => { - if ( - hoveredEvent && - (hoveredEvent.event || - hoveredEvent.measure || - hoveredEvent.flamechartStackFrame || - hoveredEvent.userTimingMark) - ) { - setMouseLocation({ - x: interaction.payload.event.x, - y: interaction.payload.event.y, - }); - } if (canvasRef.current === null) { return; } surfaceRef.current.handleInteraction(interaction); - surfaceRef.current.displayIfNeeded(); + // Defer drawing to canvas until React's commit phase, to avoid drawing + // twice and to ensure that both the canvas and DOM elements managed by + // React are in sync. + setMouseLocation({ + x: interaction.payload.event.x, + y: interaction.payload.event.y, + }); }, - [surfaceRef, hoveredEvent, setMouseLocation], + [surfaceRef, canvasRef, hoveredEvent, setMouseLocation], ); useCanvasInteraction(canvasRef, interactor); @@ -387,10 +381,7 @@ function AutoSizedCanvas({data, height, width}: AutoSizedCanvasProps) { hoveredEvent, ]); - // When React component renders, rerender surface. - // TODO: See if displaying on rAF would make more sense since we're somewhat - // decoupled from React and we don't want to render canvas multiple times per - // frame. + // Draw to canvas in React's commit phase useLayoutEffect(() => { surfaceRef.current.displayIfNeeded(); }); diff --git a/src/EventTooltip.css b/src/EventTooltip.css index f60e83208cc55..789736cb38308 100644 --- a/src/EventTooltip.css +++ b/src/EventTooltip.css @@ -1,5 +1,5 @@ .Tooltip { - position: absolute; + position: fixed; display: inline-block; border-radius: 0.125rem; max-width: 300px; @@ -31,12 +31,20 @@ } .DetailsGridURL { + word-break: break-all; + max-height: 50vh; overflow: hidden; } +.FlamechartStackFrameName { + word-break: break-word; + margin-left: 0.4rem; +} + .ComponentName { font-weight: bold; - word-wrap: break-word; + word-break: break-word; + margin-right: 0.4rem; } .ComponentStack { @@ -61,3 +69,11 @@ white-space: pre; --gradient-height: 5em; } + +.ReactMeasureLabel { + margin-left: 0.4rem; +} + +.UserTimingLabel { + word-break: break-word; +} diff --git a/src/EventTooltip.js b/src/EventTooltip.js index da94f3800e503..54c7d33327276 100644 --- a/src/EventTooltip.js +++ b/src/EventTooltip.js @@ -32,14 +32,14 @@ function formatDuration(ms) { return prettyMilliseconds(ms, {millisecondsDecimalDigits: 3}); } -function trimComponentName(name) { - if (name.length > 128) { - return name.substring(0, 127) + '...'; +function trimmedString(string: string, length: number): string { + if (string.length > length) { + return `${string.substr(0, length - 1)}…`; } - return name; + return string; } -function getReactEventLabel(type) { +function getReactEventLabel(type): string | null { switch (type) { case 'schedule-render': return 'render scheduled'; @@ -58,7 +58,25 @@ function getReactEventLabel(type) { } } -function getReactMeasureLabel(type: string) { +function getReactEventColor(event: ReactEvent): string | null { + switch (event.type) { + case 'schedule-render': + return COLORS.REACT_SCHEDULE_HOVER; + case 'schedule-state-update': + case 'schedule-force-update': + return event.isCascading + ? COLORS.REACT_SCHEDULE_CASCADING_HOVER + : COLORS.REACT_SCHEDULE_HOVER; + case 'suspense-suspend': + case 'suspense-resolved': + case 'suspense-rejected': + return COLORS.REACT_SUSPEND_HOVER; + default: + return null; + } +} + +function getReactMeasureLabel(type): string | null { switch (type) { case 'commit': return 'commit'; @@ -85,65 +103,18 @@ export default function EventTooltip({data, hoveredEvent, origin}: Props) { return null; } - const {event, flamechartStackFrame, measure, userTimingMark} = hoveredEvent; + const {event, measure, flamechartStackFrame, userTimingMark} = hoveredEvent; if (event !== null) { - switch (event.type) { - case 'schedule-render': - return ( - - ); - case 'schedule-state-update': // eslint-disable-line no-case-declarations - case 'schedule-force-update': - const color = event.isCascading - ? COLORS.REACT_SCHEDULE_CASCADING_HOVER - : COLORS.REACT_SCHEDULE_HOVER; - return ( - - ); - case 'suspense-suspend': - case 'suspense-resolved': - case 'suspense-rejected': - return ( - - ); - default: - console.warn(`Unexpected event type "${event.type}"`); - break; - } + return ; } else if (measure !== null) { - switch (measure.type) { - case 'commit': - case 'render-idle': - case 'render': - case 'layout-effects': - case 'passive-effects': - return ( - - ); - default: - console.warn(`Unexpected measure type "${measure.type}"`); - break; - } + return ( + + ); } else if (flamechartStackFrame !== null) { return ( - {formatDuration(duration)} {trimComponentName(name)} + {formatDuration(duration)} + {name}
Timestamp:
{formatTimestamp(timestamp)}
@@ -210,24 +182,28 @@ const TooltipFlamechartNode = ({ }; const TooltipReactEvent = ({ - color, event, tooltipRef, }: { - color: string, event: ReactEvent, tooltipRef: Return, }) => { - const {componentName, componentStack, timestamp, type} = event; - const label = getReactEventLabel(type); + const label = getReactEventLabel(event.type); + const color = getReactEventColor(event); + if (!label || !color) { + console.warn(`Unexpected event type "${event.type}"`); + return null; + } + + const {componentName, componentStack, timestamp} = event; return (
{componentName && ( - {trimComponentName(componentName)} + {trimmedString(componentName, 768)} - )}{' '} + )} {label}
@@ -255,13 +231,19 @@ const TooltipReactMeasure = ({ measure: ReactMeasure, tooltipRef: Return, }) => { - const {batchUID, duration, timestamp, type, lanes} = measure; - const label = getReactMeasureLabel(type); + const label = getReactMeasureLabel(measure.type); + if (!label) { + console.warn(`Unexpected measure type "${measure.type}"`); + return null; + } + + const {batchUID, duration, timestamp, lanes} = measure; const [startTime, stopTime] = getBatchRange(batchUID, data); return (
- {formatDuration(duration)} {label} + {formatDuration(duration)} + {label}
Timestamp:
@@ -287,7 +269,7 @@ const TooltipUserTimingMark = ({ const {name, timestamp} = mark; return (
- {name} + {name}
Timestamp:
diff --git a/src/utils/useSmartTooltip.js b/src/utils/useSmartTooltip.js index 652721f96d2ad..fede223a231ad 100644 --- a/src/utils/useSmartTooltip.js +++ b/src/utils/useSmartTooltip.js @@ -57,7 +57,7 @@ export default function useSmartTooltip({ element.style.left = `${mouseX + TOOLTIP_OFFSET}px`; } } - }); + }, [mouseX, mouseY, ref]); return ref; }