diff --git a/packages/react-native/Libraries/Debugging/DebuggingOverlay.js b/packages/react-native/Libraries/Debugging/DebuggingOverlay.js index 8d834145d093d3..7abe6d9be8a851 100644 --- a/packages/react-native/Libraries/Debugging/DebuggingOverlay.js +++ b/packages/react-native/Libraries/Debugging/DebuggingOverlay.js @@ -8,7 +8,10 @@ * @format */ -import type {Overlay} from './DebuggingOverlayNativeComponent'; +import type { + ElementRectangle, + Overlay, +} from './DebuggingOverlayNativeComponent'; import View from '../Components/View/View'; import UIManager from '../ReactNative/UIManager'; @@ -24,6 +27,8 @@ const isNativeComponentReady = type DebuggingOverlayHandle = { highlightTraceUpdates(updates: Overlay[]): void, + highlightElements(elements: ElementRectangle[]): void, + clearElementsHighlight(): void, }; function DebuggingOverlay( @@ -49,6 +54,27 @@ function DebuggingOverlay( ); } }, + highlightElements(elements) { + if (!isNativeComponentReady) { + return; + } + + if (nativeComponentRef.current != null) { + Commands.highlightElements( + nativeComponentRef.current, + JSON.stringify(elements), + ); + } + }, + clearElementsHighlight() { + if (!isNativeComponentReady) { + return; + } + + if (nativeComponentRef.current != null) { + Commands.clearElementsHighlights(nativeComponentRef.current); + } + }, }), [], ); diff --git a/packages/react-native/Libraries/Debugging/DebuggingOverlayNativeComponent.js b/packages/react-native/Libraries/Debugging/DebuggingOverlayNativeComponent.js index b9fdedbd84ae09..012a9b1887e7ab 100644 --- a/packages/react-native/Libraries/Debugging/DebuggingOverlayNativeComponent.js +++ b/packages/react-native/Libraries/Debugging/DebuggingOverlayNativeComponent.js @@ -25,6 +25,13 @@ export type Overlay = { color: ?ProcessedColorValue, }; +export type ElementRectangle = { + x: number, + y: number, + width: number, + height: number, +}; + interface NativeCommands { +draw: ( viewRef: React.ElementRef, diff --git a/packages/react-native/Libraries/Debugging/DebuggingRegistry.js b/packages/react-native/Libraries/Debugging/DebuggingRegistry.js index 38c9aa0cecc020..00d5825a773213 100644 --- a/packages/react-native/Libraries/Debugging/DebuggingRegistry.js +++ b/packages/react-native/Libraries/Debugging/DebuggingRegistry.js @@ -68,6 +68,8 @@ class DebuggingRegistry { this.#reactDevToolsAgent = agent; agent.addListener('drawTraceUpdates', this.#onDrawTraceUpdates); + agent.addListener('showNativeHighlight', this.#onHighlightElements); + agent.addListener('hideNativeHighlight', this.#onClearElementsHighlights); }; #getPublicInstanceFromInstance( @@ -131,6 +133,36 @@ class DebuggingRegistry { }, ); }; + + #onHighlightElements: ( + ...ReactDevToolsAgentEvents['showNativeHighlight'] + ) => void = node => { + // First clear highlights for every container + for (const subscriber of this.#registry) { + subscriber.debuggingOverlayRef.current?.clearElementsHighlight(); + } + + const publicInstance = this.#getPublicInstanceFromInstance(node); + if (publicInstance == null) { + return; + } + + publicInstance.measure((x, y, width, height, left, top) => { + for (const subscriber of this.#registry) { + subscriber.debuggingOverlayRef.current?.highlightElements([ + {x: left, y: top, width, height}, + ]); + } + }); + }; + + #onClearElementsHighlights: ( + ...ReactDevToolsAgentEvents['hideNativeHighlight'] + ) => void = () => { + for (const subscriber of this.#registry) { + subscriber.debuggingOverlayRef.current?.clearElementsHighlight(); + } + }; } const debuggingRegistryInstance: DebuggingRegistry = new DebuggingRegistry(); diff --git a/packages/react-native/Libraries/Inspector/ReactDevToolsOverlay.js b/packages/react-native/Libraries/Inspector/ReactDevToolsOverlay.js index c62bc27b2ebe17..dfc28608a09acb 100644 --- a/packages/react-native/Libraries/Inspector/ReactDevToolsOverlay.js +++ b/packages/react-native/Libraries/Inspector/ReactDevToolsOverlay.js @@ -11,10 +11,7 @@ import type {InspectedViewRef} from '../ReactNative/AppContainer-dev'; import type {PointerEvent} from '../Types/CoreEventTypes'; import type {PressEvent} from '../Types/CoreEventTypes'; -import type { - InstanceFromReactDevTools, - ReactDevToolsAgent, -} from '../Types/ReactDevToolsTypes'; +import type {ReactDevToolsAgent} from '../Types/ReactDevToolsTypes'; import type {InspectedElement} from './Inspector'; import View from '../Components/View/View'; @@ -42,47 +39,7 @@ export default function ReactDevToolsOverlay({ const [isInspecting, setIsInspecting] = useState(false); useEffect(() => { - let hideTimeoutId = null; - - function onAgentHideNativeHighlight() { - // we wait to actually hide in order to avoid flicker - clearTimeout(hideTimeoutId); - hideTimeoutId = setTimeout(() => { - setInspected(null); - }, 100); - } - - function onAgentShowNativeHighlight(node?: InstanceFromReactDevTools) { - clearTimeout(hideTimeoutId); - - // `canonical.publicInstance` => Fabric - // `canonical` => Legacy Fabric - // `node` => Legacy renderer - const component = - (node?.canonical && node.canonical.publicInstance) ?? - // TODO: remove this check when syncing the new version of the renderer from React to React Native. - node?.canonical ?? - node; - if (!component || !component.measure) { - return; - } - - component.measure((x, y, width, height, left, top) => { - setInspected({ - frame: {left, top, width, height}, - }); - }); - } - function cleanup() { - reactDevToolsAgent.removeListener( - 'hideNativeHighlight', - onAgentHideNativeHighlight, - ); - reactDevToolsAgent.removeListener( - 'showNativeHighlight', - onAgentShowNativeHighlight, - ); reactDevToolsAgent.removeListener('shutdown', cleanup); reactDevToolsAgent.removeListener( 'startInspectingNative', @@ -102,14 +59,6 @@ export default function ReactDevToolsOverlay({ setIsInspecting(false); } - reactDevToolsAgent.addListener( - 'hideNativeHighlight', - onAgentHideNativeHighlight, - ); - reactDevToolsAgent.addListener( - 'showNativeHighlight', - onAgentShowNativeHighlight, - ); reactDevToolsAgent.addListener('shutdown', cleanup); reactDevToolsAgent.addListener( 'startInspectingNative',