[devtools] Restore all Transitions for Tree updates (#33042)

This commit is contained in:
Sebastian "Sebbie" Silbermann 2025-04-30 19:51:40 +02:00 committed by GitHub
parent 408d055a3b
commit fa8e3a251e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 50 additions and 9 deletions

View File

@ -23,6 +23,7 @@ import type {Element as ElementType} from 'react-devtools-shared/src/frontend/ty
import styles from './Element.css';
import Icon from '../Icon';
import {useChangeOwnerAction} from './OwnersListContext';
type Props = {
data: ItemData,
@ -66,9 +67,10 @@ export default function Element({data, index, style}: Props): React.Node {
warningCount: number,
}>(errorsAndWarningsSubscription);
const changeOwnerAction = useChangeOwnerAction();
const handleDoubleClick = () => {
if (id !== null) {
dispatch({type: 'SELECT_OWNER', payload: id});
changeOwnerAction(id);
}
};

View File

@ -13,7 +13,7 @@ import * as React from 'react';
import {createContext, useCallback, useContext, useEffect} from 'react';
import {createResource} from '../../cache';
import {BridgeContext, StoreContext} from '../context';
import {TreeStateContext} from './TreeContext';
import {TreeDispatcherContext, TreeStateContext} from './TreeContext';
import {backendToFrontendSerializedElementMapper} from 'react-devtools-shared/src/utils';
import type {OwnersList} from 'react-devtools-shared/src/backend/types';
@ -70,6 +70,43 @@ type Props = {
children: React$Node,
};
function useChangeOwnerAction(): (nextOwnerID: number) => void {
const bridge = useContext(BridgeContext);
const store = useContext(StoreContext);
const treeAction = useContext(TreeDispatcherContext);
return useCallback(
function changeOwnerAction(nextOwnerID: number) {
treeAction({type: 'SELECT_OWNER', payload: nextOwnerID});
const element = store.getElementByID(nextOwnerID);
if (element !== null) {
if (!inProgressRequests.has(element)) {
let resolveFn:
| ResolveFn
| ((
result:
| Promise<Array<SerializedElement>>
| Array<SerializedElement>,
) => void) = ((null: any): ResolveFn);
const promise = new Promise(resolve => {
resolveFn = resolve;
});
// $FlowFixMe[incompatible-call] found when upgrading Flow
inProgressRequests.set(element, {promise, resolveFn});
}
const rendererID = store.getRendererIDForElement(nextOwnerID);
if (rendererID !== null) {
bridge.send('getOwnersList', {id: nextOwnerID, rendererID});
}
}
},
[bridge, store],
);
}
function OwnersListContextController({children}: Props): React.Node {
const bridge = useContext(BridgeContext);
const store = useContext(StoreContext);
@ -95,8 +132,6 @@ function OwnersListContextController({children}: Props): React.Node {
if (element !== null) {
const request = inProgressRequests.get(element);
if (request != null) {
inProgressRequests.delete(element);
request.resolveFn(
ownersList.owners === null
? null
@ -129,4 +164,4 @@ function OwnersListContextController({children}: Props): React.Node {
);
}
export {OwnersListContext, OwnersListContextController};
export {OwnersListContext, OwnersListContextController, useChangeOwnerAction};

View File

@ -20,7 +20,7 @@ import Button from '../Button';
import ButtonIcon from '../ButtonIcon';
import Toggle from '../Toggle';
import ElementBadges from './ElementBadges';
import {OwnersListContext} from './OwnersListContext';
import {OwnersListContext, useChangeOwnerAction} from './OwnersListContext';
import {TreeDispatcherContext, TreeStateContext} from './TreeContext';
import {useIsOverflowing} from '../hooks';
import {StoreContext} from '../context';
@ -81,6 +81,7 @@ export default function OwnerStack(): React.Node {
const read = useContext(OwnersListContext);
const {ownerID} = useContext(TreeStateContext);
const treeDispatch = useContext(TreeDispatcherContext);
const changeOwnerAction = useChangeOwnerAction();
const [state, dispatch] = useReducer<State, State, Action>(dialogReducer, {
ownerID: null,
@ -116,7 +117,7 @@ export default function OwnerStack(): React.Node {
type: 'UPDATE_SELECTED_INDEX',
selectedIndex: index >= 0 ? index : 0,
});
treeDispatch({type: 'SELECT_OWNER', payload: owner.id});
changeOwnerAction(owner.id);
} else {
dispatch({
type: 'UPDATE_SELECTED_INDEX',

View File

@ -38,6 +38,7 @@ import ButtonIcon from '../ButtonIcon';
import Button from '../Button';
import {logEvent} from 'react-devtools-shared/src/Logger';
import {useExtensionComponentsPanelVisibility} from 'react-devtools-shared/src/frontend/hooks/useExtensionComponentsPanelVisibility';
import {useChangeOwnerAction} from './OwnersListContext';
// Never indent more than this number of pixels (even if we have the room).
const DEFAULT_INDENTATION_SIZE = 12;
@ -217,13 +218,14 @@ export default function Tree(): React.Node {
const handleBlur = useCallback(() => setTreeFocused(false), []);
const handleFocus = useCallback(() => setTreeFocused(true), []);
const changeOwnerAction = useChangeOwnerAction();
const handleKeyPress = useCallback(
(event: $FlowFixMe) => {
switch (event.key) {
case 'Enter':
case ' ':
if (inspectedElementID !== null) {
dispatch({type: 'SELECT_OWNER', payload: inspectedElementID});
changeOwnerAction(inspectedElementID);
}
break;
default:

View File

@ -148,6 +148,7 @@ const TreeStateContext: ReactContext<StateContext> =
createContext<StateContext>(((null: any): StateContext));
TreeStateContext.displayName = 'TreeStateContext';
// TODO: `dispatch` is an Action and should be named accordingly.
const TreeDispatcherContext: ReactContext<DispatcherContext> =
createContext<DispatcherContext>(((null: any): DispatcherContext));
TreeDispatcherContext.displayName = 'TreeDispatcherContext';
@ -953,7 +954,7 @@ function TreeContextController({
return (
<TreeStateContext.Provider value={state}>
<TreeDispatcherContext.Provider value={dispatch}>
<TreeDispatcherContext.Provider value={transitionDispatch}>
{children}
</TreeDispatcherContext.Provider>
</TreeStateContext.Provider>