diff --git a/packages/react-devtools-shared/src/devtools/store.js b/packages/react-devtools-shared/src/devtools/store.js index 5466e798aad4..3137174aff85 100644 --- a/packages/react-devtools-shared/src/devtools/store.js +++ b/packages/react-devtools-shared/src/devtools/store.js @@ -972,6 +972,7 @@ export default class Store extends EventEmitter<{ id: suspense.id, environment: environmentName, endTime: suspense.endTime, + hasUniqueSuspenders: suspense.hasUniqueSuspenders, }; target.push(rootStep); } else { @@ -983,6 +984,10 @@ export default class Store extends EventEmitter<{ // If any root has a higher end time, let's use that. rootStep.endTime = suspense.endTime; } + if (!rootStep.hasUniqueSuspenders) { + // If any root has unique suspenders, the merged root should too. + rootStep.hasUniqueSuspenders = suspense.hasUniqueSuspenders; + } } this.pushTimelineStepsInDocumentOrder( suspense.children, @@ -1051,6 +1056,7 @@ export default class Store extends EventEmitter<{ // TODO: Get environment for Activity environment: null, endTime: 0, + hasUniqueSuspenders: true, }); const transitionChildren = this.getSuspenseChildren(focusedTransitionID); @@ -1106,6 +1112,7 @@ export default class Store extends EventEmitter<{ id: child.id, environment: environmentName, endTime: maxEndTime, + hasUniqueSuspenders: child.hasUniqueSuspenders, }); } this.pushTimelineStepsInDocumentOrder( 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 6b43f873ae85..d16788d7e4bb 100644 --- a/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseRects.css +++ b/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseRects.css @@ -108,3 +108,7 @@ .SuspenseRectsBoundary[data-selected='true'] > .SuspenseRectsRect { box-shadow: none; } + +.SuspenseRectsBoundaryNotSuspended { + --color-suspense: var(--color-dim); +} 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 69d4767a489f..5ea671377bca 100644 --- a/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseRects.js +++ b/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseRects.js @@ -192,7 +192,10 @@ function SuspenseRects({ className={ styles.SuspenseRectsBoundary + ' ' + - getClassNameForEnvironment(environment) + getClassNameForEnvironment(environment) + + (!suspense.hasUniqueSuspenders + ? ' ' + styles.SuspenseRectsBoundaryNotSuspended + : '') } visible={visible} selected={selected} @@ -550,6 +553,7 @@ function SuspenseRectsContainer({ let selectedBoundingBox = null; let selectedEnvironment = null; + let selectedHasUniqueSuspenders = true; if (isRootSelected) { selectedEnvironment = rootEnvironment; } else if ( @@ -563,6 +567,7 @@ function SuspenseRectsContainer({ (selectedSuspenseNode.hasUniqueSuspenders || !uniqueSuspendersOnly) ) { selectedBoundingBox = getBoundingBox(selectedSuspenseNode.rects); + selectedHasUniqueSuspenders = selectedSuspenseNode.hasUniqueSuspenders; for (let i = 0; i < timeline.length; i++) { const timelineStep = timeline[i]; if (timelineStep.id === inspectedElementID) { @@ -605,7 +610,10 @@ function SuspenseRectsContainer({ className={ styles.SuspenseRectOutline + ' ' + - getClassNameForEnvironment(selectedEnvironment) + getClassNameForEnvironment(selectedEnvironment) + + (!selectedHasUniqueSuspenders + ? ' ' + styles.SuspenseRectsBoundaryNotSuspended + : '') } rect={selectedBoundingBox} adjust={true} diff --git a/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseScrubber.css b/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseScrubber.css index 7ccb92b78bb2..757b3afe2918 100644 --- a/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseScrubber.css +++ b/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseScrubber.css @@ -54,6 +54,14 @@ background: var(--color-transition); } +.SuspenseScrubberBeadNotSuspended { + background: color-mix(in srgb, var(--color-dim) 25%, transparent); +} + +.SuspenseScrubberBeadNotSuspended.SuspenseScrubberBeadSelected { + background: var(--color-dim); +} + .SuspenseScrubberStepHighlight > .SuspenseScrubberBead { height: 0.75rem; transition: all 0.3s ease-out; diff --git a/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseScrubber.js b/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseScrubber.js index f8c0224c0bca..e6acb3cee7a9 100644 --- a/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseScrubber.js +++ b/packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseScrubber.js @@ -95,6 +95,9 @@ export default function SuspenseScrubber({ ? // The first step in the timeline is always a Transition (Initial Paint). ' ' + styles.SuspenseScrubberBeadTransition : '') + + (!step.hasUniqueSuspenders + ? ' ' + styles.SuspenseScrubberBeadNotSuspended + : '') + ' ' + getClassNameForEnvironment(environment) + (index <= value ? ' ' + styles.SuspenseScrubberBeadSelected : '') diff --git a/packages/react-devtools-shared/src/frontend/types.js b/packages/react-devtools-shared/src/frontend/types.js index a78831cf229b..fd3c02160811 100644 --- a/packages/react-devtools-shared/src/frontend/types.js +++ b/packages/react-devtools-shared/src/frontend/types.js @@ -211,6 +211,7 @@ export type SuspenseTimelineStep = { id: SuspenseNode['id'] | Element['id'], // TODO: Will become a group. environment: null | string, endTime: number, + hasUniqueSuspenders: boolean, }; export type SuspenseNode = {