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
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
.SuspenseBreadcrumbsList {
margin: 0;
padding: 0;
list-style: none;
display: flex;
flex-direction: row;
flex-wrap: nowrap;
}

.SuspenseBreadcrumbsListItem {
display: inline;
}

.SuspenseBreadcrumbsListItem[aria-current="true"] .SuspenseBreadcrumbsButton {
color: var(--color-button-active);
}

.SuspenseBreadcrumbsButton {
background: var(--color-button-background);
border: none;
border-radius: 0.25rem;
padding: 0.25rem;
white-space: nowrap;
}

.SuspenseBreadcrumbsButton:hover {
background-color: var(--color-button-background-hover);
color: var(--color-button-hover);
}

.SuspenseBreadcrumbsButton:focus-visible {
background: var(--color-button-background-focus);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/**
* Copyright (c) Meta Platforms, Inc. and affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
* @flow
*/

import type {SuspenseNode} from 'react-devtools-shared/src/frontend/types';

import * as React from 'react';
import {useContext} from 'react';
import {
TreeDispatcherContext,
TreeStateContext,
} from '../Components/TreeContext';
import {StoreContext} from '../context';
import {useHighlightHostInstance} from '../hooks';
import styles from './SuspenseBreadcrumbs.css';
import typeof {SyntheticMouseEvent} from 'react-dom-bindings/src/events/SyntheticEvent';

export default function SuspenseBreadcrumbs(): React$Node {
const store = useContext(StoreContext);
const dispatch = useContext(TreeDispatcherContext);
const {inspectedElementID} = useContext(TreeStateContext);

const {highlightHostInstance, clearHighlightHostInstance} =
useHighlightHostInstance();

// TODO: Use the nearest Suspense boundary
const inspectedSuspenseID = inspectedElementID;
if (inspectedSuspenseID === null) {
return null;
}

const suspense = store.getSuspenseByID(inspectedSuspenseID);
if (suspense === null) {
return null;
}

const lineage: SuspenseNode[] = [];
let next: null | SuspenseNode = suspense;
while (next !== null) {
if (next.parentID === 0) {
next = null;
} else {
lineage.unshift(next);
next = store.getSuspenseByID(next.parentID);
}
}

function handleClick(node: SuspenseNode, event: SyntheticMouseEvent) {
event.preventDefault();
dispatch({type: 'SELECT_ELEMENT_BY_ID', payload: node.id});
}

return (
<ol className={styles.SuspenseBreadcrumbsList}>
{lineage.map((node, index) => {
return (
<li
key={node.id}
className={styles.SuspenseBreadcrumbsListItem}
aria-current={index === lineage.length - 1}
onPointerEnter={highlightHostInstance.bind(null, node.id)}
onPointerLeave={clearHighlightHostInstance}>
<button
className={styles.SuspenseBreadcrumbsButton}
onClick={handleClick.bind(null, node)}
type="button">
{node.name}
</button>
</li>
);
})}
</ol>
);
}
Original file line number Diff line number Diff line change
Expand Up @@ -108,14 +108,22 @@
overflow: auto;
}

.TimelineWrapper {
.SuspenseTreeViewHeader {
padding: 0.25rem;
display: flex;
flex-direction: row;
display: grid;
grid-template-columns: auto 1fr auto;
align-items: flex-start;
}

.Timeline {
flex-grow: 1;
align-self: anchor-center;
.SuspenseTreeViewHeaderMain {
display: grid;
grid-template-rows: auto auto;
}

.SuspenseBreadcrumbs {
/**
* TODO: Switch to single item view on overflow like OwnerStack does.
* OwnerStack has more constraints that make it easier so it won't be a 1:1 port.
*/
overflow-x: auto;
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import InspectedElementErrorBoundary from '../Components/InspectedElementErrorBo
import InspectedElement from '../Components/InspectedElement';
import portaledContent from '../portaledContent';
import styles from './SuspenseTab.css';
import SuspenseBreadcrumbs from './SuspenseBreadcrumbs';
import SuspenseRects from './SuspenseRects';
import SuspenseTimeline from './SuspenseTimeline';
import SuspenseTreeList from './SuspenseTreeList';
Expand Down Expand Up @@ -304,10 +305,15 @@ function SuspenseTab(_: {}) {
/>
</div>
<div className={styles.TreeView}>
<div className={styles.TimelineWrapper}>
<div className={styles.SuspenseTreeViewHeader}>
<ToggleTreeList dispatch={dispatch} state={state} />
<div className={styles.Timeline}>
<SuspenseTimeline />
<div className={styles.SuspenseTreeViewHeaderMain}>
<div className={styles.SuspenseTimeline}>
<SuspenseTimeline />
</div>
<div className={styles.SuspenseBreadcrumbs}>
<SuspenseBreadcrumbs />
</div>
</div>
<ToggleInspectedElement
dispatch={dispatch}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
width: 100%;
display: flex;
flex-direction: row;
padding: 0 0.25rem;
}

.SuspenseTimelineInput {
Expand Down
Loading