-
Notifications
You must be signed in to change notification settings - Fork 24.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Summary: Changelog: [General][Added] - Added an overlay similar to Inspector.js that allows directly selecting elements on RN from React DevTools This diff updates DevToolsHighlighter into DevToolsOverlay. It now also allows DevTools user to select an element to inspect directly from DevTools. Depends on facebook/react#25111 to work. TODOs: - Currently once an element selected on RN, the inspector toggle isn't turned off automatically. - Fabric support depends on facebook/react#25118 Reviewed By: lunaruan Differential Revision: D38815494 fbshipit-source-id: 7e1e3a78f6594960b5dfaec142bafd3ca4b146af
- Loading branch information
1 parent
3db19b4
commit c52df02
Showing
5 changed files
with
254 additions
and
143 deletions.
There are no files selected for viewing
This file was deleted.
Oops, something went wrong.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,167 @@ | ||
/** | ||
* 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. | ||
* | ||
* @format | ||
* @flow | ||
*/ | ||
|
||
import ElementBox from './ElementBox'; | ||
import * as React from 'react'; | ||
import type {PressEvent} from '../Types/CoreEventTypes'; | ||
import View from '../Components/View/View'; | ||
import StyleSheet from '../StyleSheet/StyleSheet'; | ||
import Dimensions from '../Utilities/Dimensions'; | ||
const getInspectorDataForViewAtPoint = require('./getInspectorDataForViewAtPoint'); | ||
const ReactNative = require('../Renderer/shims/ReactNative'); | ||
|
||
import type {HostRef} from './getInspectorDataForViewAtPoint'; | ||
|
||
const {useEffect, useState, useCallback, useRef} = React; | ||
|
||
const hook = window.__REACT_DEVTOOLS_GLOBAL_HOOK__; | ||
|
||
export default function DevtoolsOverlay({ | ||
inspectedView, | ||
}: { | ||
inspectedView: ?HostRef, | ||
}): React.Node { | ||
const [inspected, setInspected] = useState(null); | ||
const [isInspecting, setIsInspecting] = useState(false); | ||
const devToolsAgentRef = useRef(null); | ||
|
||
useEffect(() => { | ||
let devToolsAgent = null; | ||
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: any) { | ||
clearTimeout(hideTimeoutId); | ||
// Shape of `node` is different in Fabric. | ||
const component = node.canonical ?? node; | ||
|
||
component.measure((x, y, width, height, left, top) => { | ||
setInspected({ | ||
frame: {left, top, width, height}, | ||
}); | ||
}); | ||
} | ||
|
||
function cleanup() { | ||
const currentAgent = devToolsAgent; | ||
if (currentAgent != null) { | ||
currentAgent.removeListener( | ||
'hideNativeHighlight', | ||
onAgentHideNativeHighlight, | ||
); | ||
currentAgent.removeListener( | ||
'showNativeHighlight', | ||
onAgentShowNativeHighlight, | ||
); | ||
currentAgent.removeListener('shutdown', cleanup); | ||
currentAgent.removeListener( | ||
'startInspectingNative', | ||
onStartInspectingNative, | ||
); | ||
currentAgent.removeListener( | ||
'stopInspectingNative', | ||
onStopInspectingNative, | ||
); | ||
devToolsAgent = null; | ||
} | ||
devToolsAgentRef.current = null; | ||
} | ||
|
||
function onStartInspectingNative() { | ||
setIsInspecting(true); | ||
} | ||
|
||
function onStopInspectingNative() { | ||
setIsInspecting(false); | ||
} | ||
|
||
function _attachToDevtools(agent: Object) { | ||
devToolsAgent = agent; | ||
devToolsAgentRef.current = agent; | ||
agent.addListener('hideNativeHighlight', onAgentHideNativeHighlight); | ||
agent.addListener('showNativeHighlight', onAgentShowNativeHighlight); | ||
agent.addListener('shutdown', cleanup); | ||
agent.addListener('startInspectingNative', onStartInspectingNative); | ||
agent.addListener('stopInspectingNative', onStopInspectingNative); | ||
} | ||
|
||
hook.on('react-devtools', _attachToDevtools); | ||
if (hook.reactDevtoolsAgent) { | ||
_attachToDevtools(hook.reactDevtoolsAgent); | ||
} | ||
return () => { | ||
hook.off('react-devtools', _attachToDevtools); | ||
cleanup(); | ||
}; | ||
}, []); | ||
|
||
const findViewForTouchEvent = useCallback( | ||
(e: PressEvent) => { | ||
const agent = devToolsAgentRef.current; | ||
if (agent == null) { | ||
return; | ||
} | ||
const {locationX, locationY} = e.nativeEvent.touches[0]; | ||
getInspectorDataForViewAtPoint( | ||
inspectedView, | ||
locationX, | ||
locationY, | ||
viewData => { | ||
const {touchedViewTag} = viewData; | ||
if (touchedViewTag != null) { | ||
agent.selectNode(ReactNative.findNodeHandle(touchedViewTag)); | ||
return true; | ||
} | ||
return false; | ||
}, | ||
); | ||
}, | ||
[inspectedView], | ||
); | ||
|
||
const shouldSetResponser = useCallback( | ||
(e: PressEvent): boolean => { | ||
findViewForTouchEvent(e); | ||
return true; | ||
}, | ||
[findViewForTouchEvent], | ||
); | ||
|
||
let highlight = inspected ? <ElementBox frame={inspected.frame} /> : null; | ||
if (isInspecting) { | ||
return ( | ||
<View | ||
onStartShouldSetResponder={shouldSetResponser} | ||
onResponderMove={findViewForTouchEvent} | ||
nativeID="devToolsInspectorOverlay" | ||
style={[styles.inspector, {height: Dimensions.get('window').height}]}> | ||
{highlight} | ||
</View> | ||
); | ||
} | ||
return highlight; | ||
} | ||
|
||
const styles = StyleSheet.create({ | ||
inspector: { | ||
backgroundColor: 'transparent', | ||
position: 'absolute', | ||
left: 0, | ||
top: 0, | ||
right: 0, | ||
}, | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.