mirror of
https://github.com/zebrajr/react.git
synced 2025-12-06 12:20:20 +01:00
[DevTools] Add breadcrumbs to Suspense tab (#34312)
This commit is contained in:
parent
8d7b5e4903
commit
89a803fcec
|
|
@ -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);
|
||||
}
|
||||
79
packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseBreadcrumbs.js
vendored
Normal file
79
packages/react-devtools-shared/src/devtools/views/SuspenseTab/SuspenseBreadcrumbs.js
vendored
Normal file
|
|
@ -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>
|
||||
);
|
||||
}
|
||||
|
|
@ -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;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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';
|
||||
|
|
@ -304,11 +305,16 @@ function SuspenseTab(_: {}) {
|
|||
/>
|
||||
</div>
|
||||
<div className={styles.TreeView}>
|
||||
<div className={styles.TimelineWrapper}>
|
||||
<div className={styles.SuspenseTreeViewHeader}>
|
||||
<ToggleTreeList dispatch={dispatch} state={state} />
|
||||
<div className={styles.Timeline}>
|
||||
<div className={styles.SuspenseTreeViewHeaderMain}>
|
||||
<div className={styles.SuspenseTimeline}>
|
||||
<SuspenseTimeline />
|
||||
</div>
|
||||
<div className={styles.SuspenseBreadcrumbs}>
|
||||
<SuspenseBreadcrumbs />
|
||||
</div>
|
||||
</div>
|
||||
<ToggleInspectedElement
|
||||
dispatch={dispatch}
|
||||
state={state}
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@
|
|||
width: 100%;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
padding: 0 0.25rem;
|
||||
}
|
||||
|
||||
.SuspenseTimelineInput {
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user