Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
117 changes: 113 additions & 4 deletions packages/react-devtools-shared/src/devtools/store.js
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,8 @@ export default class Store extends EventEmitter<{
{errorCount: number, warningCount: number},
> = new Map();

_focusedTransition: 0 | Element['id'] = 0;

// At least one of the injected renderers contains (DEV only) owner metadata.
_hasOwnerMetadata: boolean = false;

Expand Down Expand Up @@ -935,10 +937,9 @@ export default class Store extends EventEmitter<{
}

/**
* @param rootID
* @param uniqueSuspendersOnly Filters out boundaries without unique suspenders
*/
getSuspendableDocumentOrderSuspense(
getSuspendableDocumentOrderSuspenseInitialPaint(
uniqueSuspendersOnly: boolean,
): Array<SuspenseTimelineStep> {
const target: Array<SuspenseTimelineStep> = [];
Expand Down Expand Up @@ -990,6 +991,76 @@ export default class Store extends EventEmitter<{
return target;
}

_pushSuspenseChildrenInDocumentOrder(
children: Array<Element['id']>,
target: Array<SuspenseNode['id']>,
): void {
for (let i = 0; i < children.length; i++) {
const childID = children[i];
const suspense = this.getSuspenseByID(childID);
if (suspense !== null) {
target.push(suspense.id);
} else {
const childElement = this.getElementByID(childID);
if (childElement !== null) {
this._pushSuspenseChildrenInDocumentOrder(
childElement.children,
target,
);
}
}
}
}

getSuspenseChildren(id: Element['id']): Array<SuspenseNode['id']> {
const transitionChildren: Array<SuspenseNode['id']> = [];

const root = this._idToElement.get(id);
if (root === undefined) {
return transitionChildren;
}

this._pushSuspenseChildrenInDocumentOrder(
root.children,
transitionChildren,
);

return transitionChildren;
}

/**
* @param uniqueSuspendersOnly Filters out boundaries without unique suspenders
*/
getSuspendableDocumentOrderSuspenseTransition(
uniqueSuspendersOnly: boolean,
): Array<SuspenseTimelineStep> {
const target: Array<SuspenseTimelineStep> = [];
const focusedTransitionID = this._focusedTransition;
if (focusedTransitionID === null) {
return target;
}

target.push({
id: focusedTransitionID,
// TODO: Get environment for Activity
environment: null,
endTime: 0,
});

const transitionChildren = this.getSuspenseChildren(focusedTransitionID);

this.pushTimelineStepsInDocumentOrder(
transitionChildren,
target,
uniqueSuspendersOnly,
// TODO: Get environment for Activity
[],
0, // Don't pass a minimum end time at the root. The root is always first so doesn't matter.
);

return target;
}

pushTimelineStepsInDocumentOrder(
children: Array<SuspenseNode['id']>,
target: Array<SuspenseTimelineStep>,
Expand Down Expand Up @@ -1045,7 +1116,14 @@ export default class Store extends EventEmitter<{
uniqueSuspendersOnly: boolean,
): $ReadOnlyArray<SuspenseTimelineStep> {
const timeline =
this.getSuspendableDocumentOrderSuspense(uniqueSuspendersOnly);
this._focusedTransition === 0
? this.getSuspendableDocumentOrderSuspenseInitialPaint(
uniqueSuspendersOnly,
)
: this.getSuspendableDocumentOrderSuspenseTransition(
uniqueSuspendersOnly,
);

if (timeline.length === 0) {
return timeline;
}
Expand All @@ -1058,6 +1136,33 @@ export default class Store extends EventEmitter<{
return timeline;
}

getActivities(): Array<{id: Element['id'], depth: number}> {
const target: Array<{id: Element['id'], depth: number}> = [];
// TODO: Keep a live tree in the backend so we don't need to recalculate
// this each time while also including filtered Activities.
this._pushActivitiesInDocumentOrder(this.roots, target, 0);
return target;
}

_pushActivitiesInDocumentOrder(
children: $ReadOnlyArray<Element['id']>,
target: Array<{id: Element['id'], depth: number}>,
depth: number,
): void {
for (let i = 0; i < children.length; i++) {
const child = this._idToElement.get(children[i]);
if (child === undefined) {
continue;
}
if (child.type === ElementTypeActivity && child.nameProp !== null) {
target.push({id: child.id, depth});
this._pushActivitiesInDocumentOrder(child.children, target, depth + 1);
} else {
this._pushActivitiesInDocumentOrder(child.children, target, depth);
}
}
}

getRendererIDForElement(id: number): number | null {
let current = this._idToElement.get(id);
while (current !== undefined) {
Expand Down Expand Up @@ -1244,7 +1349,7 @@ export default class Store extends EventEmitter<{
const removedElementIDs: Map<number, number> = new Map();
const removedSuspenseIDs: Map<SuspenseNode['id'], SuspenseNode['id']> =
new Map();
let nextActivitySliceID = null;
let nextActivitySliceID: Element['id'] | null = null;

let i = 2;

Expand Down Expand Up @@ -2119,6 +2224,10 @@ export default class Store extends EventEmitter<{
}
}

if (nextActivitySliceID !== null) {
this._focusedTransition = nextActivitySliceID;
}

this.emit('mutated', [
addedElementIDs,
removedElementIDs,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ export type StateContext = {

// Activity slice
activityID: Element['id'] | null,
activities: $ReadOnlyArray<{id: Element['id'], depth: number}>,

// Inspection element panel
inspectedElementID: number | null,
Expand Down Expand Up @@ -172,6 +173,7 @@ type State = {

// Activity slice
activityID: Element['id'] | null,
activities: $ReadOnlyArray<{id: Element['id'], depth: number}>,

// Inspection element panel
inspectedElementID: number | null,
Expand Down Expand Up @@ -809,6 +811,7 @@ function reduceActivityState(
case 'HANDLE_STORE_MUTATION':
let {activityID} = state;
const [, , activitySliceIDChange] = action.payload;
const activities = store.getActivities();
if (activitySliceIDChange === 0 && activityID !== null) {
activityID = null;
} else if (
Expand All @@ -817,10 +820,11 @@ function reduceActivityState(
) {
activityID = activitySliceIDChange;
}
if (activityID !== state.activityID) {
if (activityID !== state.activityID || activities !== state.activities) {
return {
...state,
activityID,
activities,
};
}
}
Expand Down Expand Up @@ -863,6 +867,7 @@ function getInitialState({

// Activity slice
activityID: null,
activities: store.getActivities(),

// Inspection element panel
inspectedElementID:
Expand Down
Original file line number Diff line number Diff line change
@@ -1,20 +1,33 @@
.ActivityList {
.ActivityListContaier {
display: flex;
flex-direction: column;
}

.ActivityListHeader {
/* even if empty, provides layout alignment with the main view */
display: flex;
flex: 0 0 42px;
border-bottom: 1px solid var(--color-border);
}

.ActivityListList {
cursor: default;
list-style-type: none;
margin: 0;
padding: 0;
}

.ActivityList[data-pending-activity-slice-selection="true"] {
.ActivityListList[data-pending-activity-slice-selection="true"] {
cursor: wait;
}

.ActivityList:focus {
.ActivityListList:focus {
outline: none;
}

.ActivityListItem {
color: var(--color-component-name);
line-height: var(--line-height-data);
padding: 0 0.25rem;
user-select: none;
}
Expand All @@ -27,7 +40,7 @@
background-color: var(--color-background-inactive);
}

.ActivityList:focus .ActivityListItem[aria-selected="true"] {
.ActivityListList:focus .ActivityListItem[aria-selected="true"] {
background-color: var(--color-background-selected);
color: var(--color-text-selected);

Expand Down
Loading
Loading