mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Implement getBoundingClientRect in React Native
Summary: We already have `getBoundingClientRect` implemented in Fabric, so we can expose this as a proper method in `ReactNativeElement` (behind a feature flag). Changelog: [internal] bypass-github-export-checks Reviewed By: javache Differential Revision: D44065187 fbshipit-source-id: bd87e72f78d135079f5440b0b0bd4c572b05ba2a
This commit is contained in:
committed by
Facebook GitHub Bot
parent
3838a1894c
commit
be7f2ab4f6
@@ -12,8 +12,11 @@
|
||||
|
||||
import type HTMLCollection from '../OldStyleCollections/HTMLCollection';
|
||||
|
||||
import {getFabricUIManager} from '../../ReactNative/FabricUIManager';
|
||||
import DOMRect from '../Geometry/DOMRect';
|
||||
import {createHTMLCollection} from '../OldStyleCollections/HTMLCollection';
|
||||
import ReadOnlyNode, {getChildNodes} from './ReadOnlyNode';
|
||||
import ReadOnlyNode, {getChildNodes, getShadowNode} from './ReadOnlyNode';
|
||||
import nullthrows from 'nullthrows';
|
||||
|
||||
export default class ReadOnlyElement extends ReadOnlyNode {
|
||||
get childElementCount(): number {
|
||||
@@ -124,6 +127,23 @@ export default class ReadOnlyElement extends ReadOnlyNode {
|
||||
throw new TypeError('Unimplemented');
|
||||
}
|
||||
|
||||
getBoundingClientRect(): DOMRect {
|
||||
const shadowNode = getShadowNode(this);
|
||||
|
||||
if (shadowNode != null) {
|
||||
const rect = nullthrows(getFabricUIManager()).getBoundingClientRect(
|
||||
shadowNode,
|
||||
);
|
||||
|
||||
if (rect) {
|
||||
return new DOMRect(rect[0], rect[1], rect[2], rect[3]);
|
||||
}
|
||||
}
|
||||
|
||||
// Empty rect if any of the above failed
|
||||
return new DOMRect(0, 0, 0, 0);
|
||||
}
|
||||
|
||||
getClientRects(): DOMRectList {
|
||||
throw new TypeError('Unimplemented');
|
||||
}
|
||||
|
||||
@@ -76,7 +76,7 @@ export default class ReadOnlyNode {
|
||||
*/
|
||||
get nodeName(): string {
|
||||
throw new TypeError(
|
||||
'Unexpected access to `nodeName` in abstract class `ReadOnlyNode`',
|
||||
'`nodeName` is abstract and must be implemented in a subclass of `ReadOnlyNode`',
|
||||
);
|
||||
}
|
||||
|
||||
|
||||
@@ -58,7 +58,7 @@ export type Spec = {|
|
||||
+findShadowNodeByTag_DEPRECATED: (reactTag: number) => ?Node,
|
||||
+getBoundingClientRect: (
|
||||
node: Node,
|
||||
) => [
|
||||
) => ?[
|
||||
/* x:*/ number,
|
||||
/* y:*/ number,
|
||||
/* width:*/ number,
|
||||
|
||||
@@ -113,6 +113,17 @@ function getAncestors(root: Node, node: Node): ?$ReadOnlyArray<[Node, number]> {
|
||||
return null;
|
||||
}
|
||||
|
||||
function getNodeInCurrentTree(node: Node): ?Node {
|
||||
const ancestors = getAncestorsInCurrentTree(node);
|
||||
if (ancestors == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const [parent, position] = ancestors[ancestors.length - 1];
|
||||
const nodeInCurrentTree = fromNode(parent).children[position];
|
||||
return nodeInCurrentTree;
|
||||
}
|
||||
|
||||
const FabricUIManagerMock: FabricUIManager = {
|
||||
createNode: jest.fn(
|
||||
(
|
||||
@@ -215,7 +226,7 @@ const FabricUIManagerMock: FabricUIManager = {
|
||||
getBoundingClientRect: jest.fn(
|
||||
(
|
||||
node: Node,
|
||||
): [
|
||||
): ?[
|
||||
/* x:*/ number,
|
||||
/* y:*/ number,
|
||||
/* width:*/ number,
|
||||
@@ -223,7 +234,28 @@ const FabricUIManagerMock: FabricUIManager = {
|
||||
] => {
|
||||
ensureHostNode(node);
|
||||
|
||||
return [10, 10, 100, 100];
|
||||
const nodeInCurrentTree = getNodeInCurrentTree(node);
|
||||
const currentProps =
|
||||
nodeInCurrentTree != null ? fromNode(nodeInCurrentTree).props : null;
|
||||
if (currentProps == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const boundingClientRectForTests: ?{
|
||||
x: number,
|
||||
y: number,
|
||||
width: number,
|
||||
height: number,
|
||||
} =
|
||||
// $FlowExpectedError[prop-missing]
|
||||
currentProps.__boundingClientRectForTests;
|
||||
|
||||
if (boundingClientRectForTests == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const {x, y, width, height} = boundingClientRectForTests;
|
||||
return [x, y, width, height];
|
||||
},
|
||||
),
|
||||
setNativeProps: jest.fn((node: Node, newProps: NodeProps): void => {}),
|
||||
@@ -242,13 +274,12 @@ const FabricUIManagerMock: FabricUIManager = {
|
||||
}),
|
||||
getChildNodes: jest.fn(
|
||||
(node: Node): $ReadOnlyArray<InternalInstanceHandle> => {
|
||||
const ancestors = getAncestorsInCurrentTree(node);
|
||||
if (ancestors == null) {
|
||||
const nodeInCurrentTree = getNodeInCurrentTree(node);
|
||||
|
||||
if (nodeInCurrentTree == null) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const [parent, position] = ancestors[ancestors.length - 1];
|
||||
const nodeInCurrentTree = fromNode(parent).children[position];
|
||||
return fromNode(nodeInCurrentTree).children.map(
|
||||
child => fromNode(child).instanceHandle,
|
||||
);
|
||||
|
||||
@@ -728,10 +728,20 @@ jsi::Value UIManagerBinding::get(
|
||||
*/
|
||||
|
||||
if (methodName == "getBoundingClientRect") {
|
||||
// This is a React Native implementation of
|
||||
// `Element.prototype.getBoundingClientRect` (see
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/Element/getBoundingClientRect).
|
||||
|
||||
// This is similar to `measureInWindow`, except it's explicitly synchronous
|
||||
// (returns the result instead of passing it to a callback).
|
||||
// The behavior is similar to `Element.prototype.getBoundingClientRect` from
|
||||
// Web.
|
||||
|
||||
// getBoundingClientRect(shadowNode: ShadowNode):
|
||||
// [
|
||||
// /* x: */ number,
|
||||
// /* y: */ number,
|
||||
// /* width: */ number,
|
||||
// /* height: */ number
|
||||
// ]
|
||||
return jsi::Function::createFromHostFunction(
|
||||
runtime,
|
||||
name,
|
||||
|
||||
Reference in New Issue
Block a user