diff --git a/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseRects.css b/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseRects.css index 16d385d752..69a4022307 100644 --- a/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseRects.css +++ b/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseRects.css @@ -8,8 +8,8 @@ background-color: color-mix(in srgb, var(--color-transition) 5%, transparent); } -.SuspenseRectsContainer:hover:not(:has(.SuspenseRectsBoundary:hover))[data-highlighted='false'] { - outline-width: 1px; +.SuspenseRectsContainer[data-hovered='true'] { + background-color: color-mix(in srgb, var(--color-transition) 15%, transparent); } .SuspenseRectsContainer[data-highlighted='true'] { @@ -65,7 +65,7 @@ } /* highlight this boundary */ -.SuspenseRectsBoundary:hover:not(:has(.SuspenseRectsBoundary:hover)) > .SuspenseRectsRect { +.SuspenseRectsBoundary[data-hovered='true'] > .SuspenseRectsRect { background-color: color-mix(in srgb, var(--color-background) 50%, var(--color-suspense) 50%); transition: background-color 0.2s ease-out; } diff --git a/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseRects.js b/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseRects.js index 4a45187d7f..fcbb5f5f74 100644 --- a/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseRects.js +++ b/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseRects.js @@ -37,6 +37,7 @@ function ScaledRect({ visible, suspended, selected, + hovered, adjust, ...props }: { @@ -45,6 +46,7 @@ function ScaledRect({ visible: boolean, suspended: boolean, selected?: boolean, + hovered?: boolean, adjust?: boolean, ... }): React$Node { @@ -61,6 +63,7 @@ function ScaledRect({ data-visible={visible} data-suspended={suspended} data-selected={selected} + data-hovered={hovered} style={{ // Shrink one pixel so that the bottom outline will line up with the top outline of the next one. width: adjust ? 'calc(' + width + ' - 1px)' : width, @@ -80,7 +83,9 @@ function SuspenseRects({ const store = useContext(StoreContext); const treeDispatch = useContext(TreeDispatcherContext); const suspenseTreeDispatch = useContext(SuspenseTreeDispatcherContext); - const {uniqueSuspendersOnly} = useContext(SuspenseTreeStateContext); + const {uniqueSuspendersOnly, timeline, hoveredTimelineIndex} = useContext( + SuspenseTreeStateContext, + ); const {inspectedElementID} = useContext(TreeStateContext); @@ -148,6 +153,9 @@ function SuspenseRects({ // TODO: Use the nearest Suspense boundary const selected = inspectedElementID === suspenseID; + const hovered = + hoveredTimelineIndex > -1 && timeline[hoveredTimelineIndex] === suspenseID; + const boundingBox = getBoundingBox(suspense.rects); return ( @@ -156,7 +164,8 @@ function SuspenseRects({ className={styles.SuspenseRectsBoundary} visible={visible} selected={selected} - suspended={suspense.isSuspended}> + suspended={suspense.isSuspended} + hovered={hovered}> {visible && suspense.rects !== null && @@ -317,7 +326,7 @@ function SuspenseRectsContainer(): React$Node { const treeDispatch = useContext(TreeDispatcherContext); const suspenseTreeDispatch = useContext(SuspenseTreeDispatcherContext); // TODO: This relies on a full re-render of all children when the Suspense tree changes. - const {roots} = useContext(SuspenseTreeStateContext); + const {roots, hoveredTimelineIndex} = useContext(SuspenseTreeStateContext); // TODO: bbox does not consider uniqueSuspendersOnly filter const boundingBox = getDocumentBoundingRect(store, roots); @@ -361,13 +370,15 @@ function SuspenseRectsContainer(): React$Node { } const isRootSelected = roots.includes(inspectedElementID); + const isRootHovered = hoveredTimelineIndex === 0; return (
+ data-highlighted={isRootSelected} + data-hovered={isRootHovered}>
.SuspenseScrubberBead, -.SuspenseScrubberStep:hover > .SuspenseScrubberBead { +.SuspenseScrubberStepHighlight > .SuspenseScrubberBead { height: 0.75rem; transition: all 0.3s ease-out; } diff --git a/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseTimeline.js b/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseTimeline.js index 4712397632..af50a8c689 100644 --- a/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseTimeline.js +++ b/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseTimeline.js @@ -53,10 +53,19 @@ function SuspenseTimelineInput() { switchSuspenseNode(timelineIndex); } - function handleHoverSegment(hoveredValue: number) { - // TODO: Consider highlighting the rect instead. + function handleHoverSegment(hoveredIndex: number) { + const nextSelectedSuspenseID = timeline[hoveredIndex]; + suspenseTreeDispatch({ + type: 'HOVER_TIMELINE_FOR_ID', + payload: nextSelectedSuspenseID, + }); + } + function handleUnhoverSegment() { + suspenseTreeDispatch({ + type: 'HOVER_TIMELINE_FOR_ID', + payload: -1, + }); } - function handleUnhoverSegment() {} function skipPrevious() { const nextSelectedSuspenseID = timeline[timelineIndex - 1];