Repository: facebook/react
Pull request: Scheduling Profiler: Inline snapshots

Iterated a little bit on the snapshot UI to add borders and improve the UX when scrolling (so the hovered tooltip image now updates)

https://user-images.githubusercontent.com/29597/129425551-021179f9-4d42-4ee9-86ea-9188ede28f95.mp4

The motion is really fluid:

https://user-images.githubusercontent.com/29597/129426583-d4959eee-153e-46b7-be6b-2f068998cd2b.mp4


Changed files:
filename: packages/react-devtools-scheduling-profiler/src/EventTooltip.css
patch: @@ -11,7 +11,6 @@
   user-select: none;
   pointer-events: none;
   background-color: var(--color-tooltip-background);
-  border: 1px solid var(border);
   box-shadow: 1px 1px 2px var(--color-shadow);
   color: var(--color-tooltip-text);
   font-size: 11px;
@@ -72,4 +71,8 @@
 .InfoText,
 .WarningText {
   color: var(--color-warning-text-color);
+}
+
+.Image {
+  border: 1px solid var(--color-border);
 }
\ No newline at end of file

filename: packages/react-devtools-scheduling-profiler/src/EventTooltip.js
patch: @@ -315,6 +315,7 @@ const TooltipSnapshot = ({
   return (
     <div className={styles.Tooltip} ref={tooltipRef}>
       <img
+        className={styles.Image}
         src={snapshot.imageSource}
         style={{width: snapshot.width / 2, height: snapshot.height / 2}}
       />

filename: packages/react-devtools-scheduling-profiler/src/content-views/SnapshotsView.js
patch: @@ -10,7 +10,7 @@
 import type {Snapshot, ReactProfilerData} from '../types';
 import type {
   Interaction,
-  MouseMoveInteraction,
+  Point,
   Rect,
   Size,
   Surface,
@@ -67,6 +67,10 @@ export class SnapshotsView extends View {
     // draw them at fixed intervals and just show the nearest one.
     while (x < visibleArea.origin.x + visibleArea.size.width) {
       const snapshot = this._findClosestSnapshot(x);
+      if (snapshot === null) {
+        // This shold never happen.
+        break;
+      }
 
       const scaledHeight = SNAPSHOT_HEIGHT;
       const scaledWidth = (snapshot.width * SNAPSHOT_HEIGHT) / snapshot.height;
@@ -97,7 +101,11 @@ export class SnapshotsView extends View {
   handleInteraction(interaction: Interaction, viewRefs: ViewRefs) {
     switch (interaction.type) {
       case 'mousemove':
-        this._handleMouseMove(interaction, viewRefs);
+      case 'wheel-control':
+      case 'wheel-meta':
+      case 'wheel-plain':
+      case 'wheel-shift':
+        this._updateHover(interaction.payload.location, viewRefs);
         break;
     }
   }
@@ -125,6 +133,14 @@ export class SnapshotsView extends View {
       context.clip();
     }
 
+    context.fillStyle = COLORS.REACT_RESIZE_BAR_BORDER;
+    context.fillRect(
+      imageRect.origin.x,
+      imageRect.origin.y,
+      imageRect.size.width,
+      imageRect.size.height,
+    );
+
     // $FlowFixMe Flow doesn't know about the 9 argument variant of drawImage()
     context.drawImage(
       snapshot.image,
@@ -138,20 +154,20 @@ export class SnapshotsView extends View {
       snapshot.height,
 
       // Canvas coordinates
-      imageRect.origin.x,
-      imageRect.origin.y,
+      imageRect.origin.x + BORDER_SIZE,
+      imageRect.origin.y + BORDER_SIZE,
 
       // Scaled image size
-      imageRect.size.width,
-      imageRect.size.height,
+      imageRect.size.width - BORDER_SIZE * 2,
+      imageRect.size.height - BORDER_SIZE * 2,
     );
 
     if (shouldClip) {
       context.restore();
     }
   }
 
-  _findClosestSnapshot(x: number): Snapshot {
+  _findClosestSnapshot(x: number): Snapshot | null {
     const frame = this.frame;
     const scaleFactor = positioningScaleFactor(
       this._intrinsicSize.width,
@@ -178,28 +194,25 @@ export class SnapshotsView extends View {
       }
     }
 
-    return snapshots[stopIndex];
+    return snapshots[stopIndex] || null;
   }
 
   /**
    * @private
    */
-  _handleMouseMove(interaction: MouseMoveInteraction, viewRefs: ViewRefs) {
+  _updateHover(location: Point, viewRefs: ViewRefs) {
     const {onHover, visibleArea} = this;
     if (!onHover) {
       return;
     }
 
-    const {location} = interaction.payload;
     if (!rectContainsPoint(location, visibleArea)) {
       onHover(null);
       return;
     }
 
     const snapshot = this._findClosestSnapshot(location.x);
-    if (snapshot) {
-      this.currentCursor = 'context-menu';
-      viewRefs.hoveredView = this;
+    if (snapshot !== null) {
       onHover(snapshot);
     } else {
       onHover(null);

filename: packages/react-devtools-scheduling-profiler/src/content-views/constants.js
patch: @@ -23,7 +23,7 @@ export const REACT_MEASURE_HEIGHT = 14;
 export const BORDER_SIZE = 1;
 export const FLAMECHART_FRAME_HEIGHT = 14;
 export const TEXT_PADDING = 3;
-export const SNAPSHOT_HEIGHT = 50;
+export const SNAPSHOT_HEIGHT = 35;
 
 export const INTERVAL_TIMES = [
   1,