mirror of
https://github.com/facebook/react.git
synced 2025-11-01 09:12:30 +00:00
Add getInspectorDataForViewAtPoint (take two) (#18388)
* Add getInspectorDataForViewAtPoint (take two) * Updates from review * Add DEV to dev-only variable * Missed this rename
This commit is contained in:
@@ -145,6 +145,7 @@ export type UpdatePayload = Array<mixed>;
|
||||
export type ChildSet = void; // Unused
|
||||
export type TimeoutHandle = TimeoutID;
|
||||
export type NoTimeout = -1;
|
||||
export type RendererInspectionConfig = $ReadOnly<{||}>;
|
||||
|
||||
type SelectionInformation = {|
|
||||
activeElementDetached: null | HTMLElement,
|
||||
|
||||
+11
-3
@@ -34,8 +34,10 @@ import ReactVersion from 'shared/ReactVersion';
|
||||
import {UIManager} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';
|
||||
|
||||
import {getClosestInstanceFromNode} from './ReactFabricComponentTree';
|
||||
import {getInspectorDataForViewTag} from './ReactNativeFiberInspector';
|
||||
|
||||
import {
|
||||
getInspectorDataForViewTag,
|
||||
getInspectorDataForViewAtPoint,
|
||||
} from './ReactNativeFiberInspector';
|
||||
import {LegacyRoot} from 'react-reconciler/src/ReactRootTags';
|
||||
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
||||
import getComponentName from 'shared/getComponentName';
|
||||
@@ -232,8 +234,14 @@ export {
|
||||
|
||||
injectIntoDevTools({
|
||||
findFiberByHostInstance: getClosestInstanceFromNode,
|
||||
getInspectorDataForViewTag: getInspectorDataForViewTag,
|
||||
bundleType: __DEV__ ? 1 : 0,
|
||||
version: ReactVersion,
|
||||
rendererPackageName: 'react-native-renderer',
|
||||
rendererConfig: {
|
||||
getInspectorDataForViewTag: getInspectorDataForViewTag,
|
||||
getInspectorDataForViewAtPoint: getInspectorDataForViewAtPoint.bind(
|
||||
null,
|
||||
findNodeHandle,
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -15,6 +15,7 @@ import type {
|
||||
MeasureOnSuccessCallback,
|
||||
NativeMethods,
|
||||
ReactNativeBaseComponentViewConfig,
|
||||
TouchedViewDataAtPoint,
|
||||
} from './ReactNativeTypes';
|
||||
|
||||
import {mountSafeCallback_NOT_REALLY_SAFE} from './NativeMethodsMixinUtils';
|
||||
@@ -80,6 +81,17 @@ export type ReactListenerEvent = Object;
|
||||
export type ReactListenerMap = Object;
|
||||
export type ReactListener = Object;
|
||||
|
||||
export type RendererInspectionConfig = $ReadOnly<{|
|
||||
// Deprecated. Replaced with getInspectorDataForViewAtPoint.
|
||||
getInspectorDataForViewTag?: (tag: number) => Object,
|
||||
getInspectorDataForViewAtPoint?: (
|
||||
inspectedView: Object,
|
||||
locationX: number,
|
||||
locationY: number,
|
||||
callback: (viewData: TouchedViewDataAtPoint) => mixed,
|
||||
) => void,
|
||||
|}>;
|
||||
|
||||
// TODO: Remove this conditional once all changes have propagated.
|
||||
if (registerEventHandler) {
|
||||
/**
|
||||
|
||||
@@ -33,12 +33,20 @@ import {
|
||||
class ReactNativeFiberHostComponent {
|
||||
_children: Array<Instance | number>;
|
||||
_nativeTag: number;
|
||||
_internalFiberInstanceHandleDEV: Object;
|
||||
viewConfig: ReactNativeBaseComponentViewConfig<>;
|
||||
|
||||
constructor(tag: number, viewConfig: ReactNativeBaseComponentViewConfig<>) {
|
||||
constructor(
|
||||
tag: number,
|
||||
viewConfig: ReactNativeBaseComponentViewConfig<>,
|
||||
internalInstanceHandleDEV: Object,
|
||||
) {
|
||||
this._nativeTag = tag;
|
||||
this._children = [];
|
||||
this.viewConfig = viewConfig;
|
||||
if (__DEV__) {
|
||||
this._internalFiberInstanceHandleDEV = internalInstanceHandleDEV;
|
||||
}
|
||||
}
|
||||
|
||||
blur() {
|
||||
|
||||
+136
-13
@@ -8,6 +8,7 @@
|
||||
*/
|
||||
|
||||
import type {Fiber} from 'react-reconciler/src/ReactFiber';
|
||||
import type {TouchedViewDataAtPoint, InspectorData} from './ReactNativeTypes';
|
||||
|
||||
import {
|
||||
findCurrentHostFiber,
|
||||
@@ -27,6 +28,7 @@ if (__DEV__) {
|
||||
}
|
||||
|
||||
let getInspectorDataForViewTag;
|
||||
let getInspectorDataForViewAtPoint;
|
||||
|
||||
if (__DEV__) {
|
||||
const traverseOwnerTreeUp = function(hierarchy, instance: any) {
|
||||
@@ -80,24 +82,39 @@ if (__DEV__) {
|
||||
const createHierarchy = function(fiberHierarchy) {
|
||||
return fiberHierarchy.map(fiber => ({
|
||||
name: getComponentName(fiber.type),
|
||||
getInspectorData: findNodeHandle => ({
|
||||
measure: callback =>
|
||||
UIManager.measure(getHostNode(fiber, findNodeHandle), callback),
|
||||
props: getHostProps(fiber),
|
||||
source: fiber._debugSource,
|
||||
}),
|
||||
getInspectorData: findNodeHandle => {
|
||||
return {
|
||||
props: getHostProps(fiber),
|
||||
source: fiber._debugSource,
|
||||
measure: callback => {
|
||||
// If this is Fabric, we'll find a ShadowNode and use that to measure.
|
||||
const hostFiber = findCurrentHostFiber(fiber);
|
||||
const shadowNode =
|
||||
hostFiber != null &&
|
||||
hostFiber.stateNode !== null &&
|
||||
hostFiber.stateNode.node;
|
||||
|
||||
if (shadowNode) {
|
||||
nativeFabricUIManager.measure(shadowNode, callback);
|
||||
} else {
|
||||
return UIManager.measure(
|
||||
getHostNode(fiber, findNodeHandle),
|
||||
callback,
|
||||
);
|
||||
}
|
||||
},
|
||||
};
|
||||
},
|
||||
}));
|
||||
};
|
||||
|
||||
getInspectorDataForViewTag = function(viewTag: number): Object {
|
||||
const closestInstance = getClosestInstanceFromNode(viewTag);
|
||||
|
||||
const getInspectorDataForInstance = function(closestInstance): InspectorData {
|
||||
// Handle case where user clicks outside of ReactNative
|
||||
if (!closestInstance) {
|
||||
return {
|
||||
hierarchy: [],
|
||||
props: emptyObject,
|
||||
selection: null,
|
||||
selectedIndex: null,
|
||||
source: null,
|
||||
};
|
||||
}
|
||||
@@ -108,15 +125,108 @@ if (__DEV__) {
|
||||
const hierarchy = createHierarchy(fiberHierarchy);
|
||||
const props = getHostProps(instance);
|
||||
const source = instance._debugSource;
|
||||
const selection = fiberHierarchy.indexOf(instance);
|
||||
const selectedIndex = fiberHierarchy.indexOf(instance);
|
||||
|
||||
return {
|
||||
hierarchy,
|
||||
props,
|
||||
selection,
|
||||
selectedIndex,
|
||||
source,
|
||||
};
|
||||
};
|
||||
|
||||
getInspectorDataForViewTag = function(viewTag: number): Object {
|
||||
const closestInstance = getClosestInstanceFromNode(viewTag);
|
||||
|
||||
// Handle case where user clicks outside of ReactNative
|
||||
if (!closestInstance) {
|
||||
return {
|
||||
hierarchy: [],
|
||||
props: emptyObject,
|
||||
selectedIndex: null,
|
||||
source: null,
|
||||
};
|
||||
}
|
||||
|
||||
const fiber = findCurrentFiberUsingSlowPath(closestInstance);
|
||||
const fiberHierarchy = getOwnerHierarchy(fiber);
|
||||
const instance = lastNonHostInstance(fiberHierarchy);
|
||||
const hierarchy = createHierarchy(fiberHierarchy);
|
||||
const props = getHostProps(instance);
|
||||
const source = instance._debugSource;
|
||||
const selectedIndex = fiberHierarchy.indexOf(instance);
|
||||
|
||||
return {
|
||||
hierarchy,
|
||||
props,
|
||||
selectedIndex,
|
||||
source,
|
||||
};
|
||||
};
|
||||
|
||||
getInspectorDataForViewAtPoint = function(
|
||||
findNodeHandle: (componentOrHandle: any) => ?number,
|
||||
inspectedView: Object,
|
||||
locationX: number,
|
||||
locationY: number,
|
||||
callback: (viewData: TouchedViewDataAtPoint) => mixed,
|
||||
): void {
|
||||
let closestInstance = null;
|
||||
|
||||
if (inspectedView._internalInstanceHandle != null) {
|
||||
// For Fabric we can look up the instance handle directly and measure it.
|
||||
nativeFabricUIManager.findNodeAtPoint(
|
||||
inspectedView._internalInstanceHandle.stateNode.node,
|
||||
locationX,
|
||||
locationY,
|
||||
internalInstanceHandle => {
|
||||
if (internalInstanceHandle == null) {
|
||||
callback({
|
||||
pointerY: locationY,
|
||||
frame: {left: 0, top: 0, width: 0, height: 0},
|
||||
...getInspectorDataForInstance(closestInstance),
|
||||
});
|
||||
}
|
||||
|
||||
closestInstance =
|
||||
internalInstanceHandle.stateNode.canonical._internalInstanceHandle;
|
||||
nativeFabricUIManager.measure(
|
||||
internalInstanceHandle.stateNode.node,
|
||||
(x, y, width, height, pageX, pageY) => {
|
||||
callback({
|
||||
pointerY: locationY,
|
||||
frame: {left: pageX, top: pageY, width, height},
|
||||
...getInspectorDataForInstance(closestInstance),
|
||||
});
|
||||
},
|
||||
);
|
||||
},
|
||||
);
|
||||
} else if (inspectedView._internalFiberInstanceHandleDEV != null) {
|
||||
// For Paper we fall back to the old strategy using the React tag.
|
||||
UIManager.findSubviewIn(
|
||||
findNodeHandle(inspectedView),
|
||||
[locationX, locationY],
|
||||
(nativeViewTag, left, top, width, height) => {
|
||||
const inspectorData = getInspectorDataForInstance(
|
||||
getClosestInstanceFromNode(nativeViewTag),
|
||||
);
|
||||
callback({
|
||||
...inspectorData,
|
||||
pointerY: locationY,
|
||||
frame: {left, top, width, height},
|
||||
touchedViewTag: nativeViewTag,
|
||||
});
|
||||
},
|
||||
);
|
||||
} else {
|
||||
console.error(
|
||||
'getInspectorDataForViewAtPoint expects to receieve a host component',
|
||||
);
|
||||
|
||||
return;
|
||||
}
|
||||
};
|
||||
} else {
|
||||
getInspectorDataForViewTag = () => {
|
||||
invariant(
|
||||
@@ -124,6 +234,19 @@ if (__DEV__) {
|
||||
'getInspectorDataForViewTag() is not available in production',
|
||||
);
|
||||
};
|
||||
|
||||
getInspectorDataForViewAtPoint = (
|
||||
findNodeHandle: (componentOrHandle: any) => ?number,
|
||||
inspectedView: Object,
|
||||
locationX: number,
|
||||
locationY: number,
|
||||
callback: (viewData: TouchedViewDataAtPoint) => mixed,
|
||||
): void => {
|
||||
invariant(
|
||||
false,
|
||||
'getInspectorDataForViewAtPoint() is not available in production.',
|
||||
);
|
||||
};
|
||||
}
|
||||
|
||||
export {getInspectorDataForViewTag};
|
||||
export {getInspectorDataForViewAtPoint, getInspectorDataForViewTag};
|
||||
|
||||
+18
-1
@@ -7,6 +7,8 @@
|
||||
* @flow
|
||||
*/
|
||||
|
||||
import type {TouchedViewDataAtPoint} from './ReactNativeTypes';
|
||||
|
||||
import invariant from 'shared/invariant';
|
||||
|
||||
// Modules provided by RN:
|
||||
@@ -46,6 +48,17 @@ export type ChildSet = void; // Unused
|
||||
export type TimeoutHandle = TimeoutID;
|
||||
export type NoTimeout = -1;
|
||||
|
||||
export type RendererInspectionConfig = $ReadOnly<{|
|
||||
// Deprecated. Replaced with getInspectorDataForViewAtPoint.
|
||||
getInspectorDataForViewTag?: (tag: number) => Object,
|
||||
getInspectorDataForViewAtPoint?: (
|
||||
inspectedView: Object,
|
||||
locationX: number,
|
||||
locationY: number,
|
||||
callback: (viewData: TouchedViewDataAtPoint) => mixed,
|
||||
) => void,
|
||||
|}>;
|
||||
|
||||
const UPDATE_SIGNAL = {};
|
||||
if (__DEV__) {
|
||||
Object.freeze(UPDATE_SIGNAL);
|
||||
@@ -112,7 +125,11 @@ export function createInstance(
|
||||
updatePayload, // props
|
||||
);
|
||||
|
||||
const component = new ReactNativeFiberHostComponent(tag, viewConfig);
|
||||
const component = new ReactNativeFiberHostComponent(
|
||||
tag,
|
||||
viewConfig,
|
||||
internalInstanceHandle,
|
||||
);
|
||||
|
||||
precacheFiberNode(internalInstanceHandle, tag);
|
||||
updateFiberProps(tag, props);
|
||||
|
||||
+11
-3
@@ -36,8 +36,10 @@ import ReactVersion from 'shared/ReactVersion';
|
||||
import {UIManager} from 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface';
|
||||
|
||||
import {getClosestInstanceFromNode} from './ReactNativeComponentTree';
|
||||
import {getInspectorDataForViewTag} from './ReactNativeFiberInspector';
|
||||
|
||||
import {
|
||||
getInspectorDataForViewTag,
|
||||
getInspectorDataForViewAtPoint,
|
||||
} from './ReactNativeFiberInspector';
|
||||
import {LegacyRoot} from 'react-reconciler/src/ReactRootTags';
|
||||
import ReactSharedInternals from 'shared/ReactSharedInternals';
|
||||
import getComponentName from 'shared/getComponentName';
|
||||
@@ -246,8 +248,14 @@ export {
|
||||
|
||||
injectIntoDevTools({
|
||||
findFiberByHostInstance: getClosestInstanceFromNode,
|
||||
getInspectorDataForViewTag: getInspectorDataForViewTag,
|
||||
bundleType: __DEV__ ? 1 : 0,
|
||||
version: ReactVersion,
|
||||
rendererPackageName: 'react-native-renderer',
|
||||
rendererConfig: {
|
||||
getInspectorDataForViewTag: getInspectorDataForViewTag,
|
||||
getInspectorDataForViewAtPoint: getInspectorDataForViewAtPoint.bind(
|
||||
null,
|
||||
findNodeHandle,
|
||||
),
|
||||
},
|
||||
});
|
||||
|
||||
@@ -100,6 +100,46 @@ type SecretInternalsType = {
|
||||
...
|
||||
};
|
||||
|
||||
type InspectorDataProps = $ReadOnly<{
|
||||
[propName: string]: string,
|
||||
...,
|
||||
}>;
|
||||
|
||||
type InspectorDataSource = $ReadOnly<{|
|
||||
fileName?: string,
|
||||
lineNumber?: number,
|
||||
|}>;
|
||||
|
||||
type InspectorDataGetter = (
|
||||
(componentOrHandle: any) => ?number,
|
||||
) => $ReadOnly<{|
|
||||
measure: Function,
|
||||
props: InspectorDataProps,
|
||||
source: InspectorDataSource,
|
||||
|}>;
|
||||
|
||||
export type InspectorData = $ReadOnly<{|
|
||||
hierarchy: Array<{|
|
||||
name: ?string,
|
||||
getInspectorData: InspectorDataGetter,
|
||||
|}>,
|
||||
selectedIndex: ?number,
|
||||
props: InspectorDataProps,
|
||||
source: ?InspectorDataSource,
|
||||
|}>;
|
||||
|
||||
export type TouchedViewDataAtPoint = $ReadOnly<{|
|
||||
pointerY: number,
|
||||
touchedViewTag?: number,
|
||||
frame: $ReadOnly<{|
|
||||
top: number,
|
||||
left: number,
|
||||
width: number,
|
||||
height: number,
|
||||
|}>,
|
||||
...InspectorData,
|
||||
|}>;
|
||||
|
||||
/**
|
||||
* Flat ReactNative renderer bundles are too big for Flow to parse efficiently.
|
||||
* Provide minimal Flow typing for the high-level RN API and call it a day.
|
||||
|
||||
@@ -16,6 +16,7 @@ import type {
|
||||
Container,
|
||||
PublicInstance,
|
||||
} from './ReactFiberHostConfig';
|
||||
import type {RendererInspectionConfig} from './ReactFiberHostConfig';
|
||||
import {FundamentalComponent} from './ReactWorkTags';
|
||||
import type {ReactNodeList, Thenable} from 'shared/ReactTypes';
|
||||
import type {ExpirationTime} from './ReactFiberExpirationTime';
|
||||
@@ -107,10 +108,7 @@ type DevToolsConfig = {|
|
||||
// Note: this actually *does* depend on Fiber internal fields.
|
||||
// Used by "inspect clicked DOM element" in React DevTools.
|
||||
findFiberByHostInstance?: (instance: Instance | TextInstance) => Fiber | null,
|
||||
// Used by RN in-app inspector.
|
||||
// This API is unfortunately RN-specific.
|
||||
// TODO: Change it to accept Fiber instead and type it properly.
|
||||
getInspectorDataForViewTag?: (tag: number) => Object,
|
||||
rendererConfig?: RendererInspectionConfig,
|
||||
|};
|
||||
|
||||
let didWarnAboutNestedUpdates;
|
||||
@@ -516,7 +514,7 @@ export function injectIntoDevTools(devToolsConfig: DevToolsConfig): boolean {
|
||||
bundleType: devToolsConfig.bundleType,
|
||||
version: devToolsConfig.version,
|
||||
rendererPackageName: devToolsConfig.rendererPackageName,
|
||||
getInspectorDataForViewTag: devToolsConfig.getInspectorDataForViewTag,
|
||||
rendererConfig: devToolsConfig.rendererConfig,
|
||||
overrideHookState,
|
||||
overrideProps,
|
||||
setSuspenseHandler,
|
||||
|
||||
@@ -37,6 +37,7 @@ export opaque type UpdatePayload = mixed; // eslint-disable-line no-undef
|
||||
export opaque type ChildSet = mixed; // eslint-disable-line no-undef
|
||||
export opaque type TimeoutHandle = mixed; // eslint-disable-line no-undef
|
||||
export opaque type NoTimeout = mixed; // eslint-disable-line no-undef
|
||||
export opaque type RendererInspectionConfig = mixed; // eslint-disable-line no-undef
|
||||
export type EventResponder = any;
|
||||
export type ReactListenerEvent = Object;
|
||||
export type ReactListenerMap = Object;
|
||||
|
||||
@@ -48,6 +48,7 @@ export type EventResponder = any;
|
||||
export type ReactListenerEvent = Object;
|
||||
export type ReactListenerMap = Object;
|
||||
export type ReactListener = Object;
|
||||
export type RendererInspectionConfig = $ReadOnly<{||}>;
|
||||
|
||||
export * from 'react-reconciler/src/ReactFiberHostConfigWithNoPersistence';
|
||||
export * from 'react-reconciler/src/ReactFiberHostConfigWithNoHydration';
|
||||
|
||||
@@ -351,5 +351,6 @@
|
||||
"350": "Cannot read from mutable source during the current render without tearing. This is a bug in React. Please file an issue.",
|
||||
"351": "Unsupported type.",
|
||||
"352": "React Blocks (and Lazy Components) are expected to be replaced by a compiler on the server. Try configuring your compiler set up and avoid using React.lazy inside of Blocks.",
|
||||
"353": "A server block should never encode any other slots. This is a bug in React."
|
||||
"353": "A server block should never encode any other slots. This is a bug in React.",
|
||||
"354": "getInspectorDataForViewAtPoint() is not available in production."
|
||||
}
|
||||
|
||||
+18
@@ -18,6 +18,7 @@ import type {
|
||||
} from 'react-native-renderer/src/ReactNativeTypes';
|
||||
import type {RNTopLevelEventType} from 'legacy-events/TopLevelEventTypes';
|
||||
import type {CapturedError} from 'react-reconciler/src/ReactCapturedValue';
|
||||
import type {Fiber} from 'react-reconciler/src/ReactFiber';
|
||||
|
||||
type DeepDifferOptions = {|+unsafelyIgnoreFunctions?: boolean|};
|
||||
|
||||
@@ -96,6 +97,17 @@ declare module 'react-native/Libraries/ReactPrivate/ReactNativePrivateInterface'
|
||||
) => Promise<any>,
|
||||
setJSResponder: (reactTag: number, blockNativeResponder: boolean) => void,
|
||||
clearJSResponder: () => void,
|
||||
findSubviewIn: (
|
||||
reactTag: ?number,
|
||||
point: Array<number>,
|
||||
callback: (
|
||||
nativeViewTag: number,
|
||||
left: number,
|
||||
top: number,
|
||||
width: number,
|
||||
height: number,
|
||||
) => void,
|
||||
) => void,
|
||||
...
|
||||
};
|
||||
declare export var BatchedBridge: {
|
||||
@@ -156,6 +168,12 @@ declare var nativeFabricUIManager: {
|
||||
onFail: () => void,
|
||||
onSuccess: MeasureLayoutOnSuccessCallback,
|
||||
) => void,
|
||||
findNodeAtPoint: (
|
||||
node: Node,
|
||||
locationX: number,
|
||||
locationY: number,
|
||||
callback: (Fiber) => void,
|
||||
) => void,
|
||||
...
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user