Skip to content

Commit e4bd884

Browse files
andrewdacenkofacebook-github-bot
authored andcommitted
Add getClientRects JS API and native module binding
Summary: Adds the JavaScript API `getClientRects()` to the Element interface, matching the Web DOM API (https://developer.mozilla.org/en-US/docs/Web/API/Element/getClientRects). For most elements, this returns an array with a single rect matching `getBoundingClientRect()`. For text elements (TextShadowNode), this returns an array of rects representing each line/fragment of the text. This is useful for getting the visual boundaries of nested `<Text>` components within a text paragraph, where text may span multiple lines. Changes: - ReadOnlyElement.js: Add getClientRects() method - NativeDOM.js: Add getClientRects spec - NativeDOM.cpp/h: Add native module binding that calls dom::getClientRects Differential Revision: D91087227
1 parent f85c416 commit e4bd884

4 files changed

Lines changed: 73 additions & 0 deletions

File tree

packages/react-native/ReactCommon/react/nativemodule/dom/NativeDOM.cpp

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -274,6 +274,30 @@ NativeDOM::getBoundingClientRect(
274274
return std::tuple{domRect.x, domRect.y, domRect.width, domRect.height};
275275
}
276276

277+
std::vector<std::tuple<
278+
/* x: */ double,
279+
/* y: */ double,
280+
/* width: */ double,
281+
/* height: */ double>>
282+
NativeDOM::getClientRects(
283+
jsi::Runtime& rt,
284+
std::shared_ptr<const ShadowNode> shadowNode) {
285+
auto currentRevision =
286+
getCurrentShadowTreeRevision(rt, shadowNode->getSurfaceId());
287+
if (currentRevision == nullptr) {
288+
return {};
289+
}
290+
291+
auto domRects = dom::getClientRects(currentRevision, *shadowNode);
292+
293+
std::vector<std::tuple<double, double, double, double>> result;
294+
result.reserve(domRects.size());
295+
for (const auto& rect : domRects) {
296+
result.emplace_back(rect.x, rect.y, rect.width, rect.height);
297+
}
298+
return result;
299+
}
300+
277301
std::tuple</* width: */ int, /* height: */ int> NativeDOM::getInnerSize(
278302
jsi::Runtime& rt,
279303
std::shared_ptr<const ShadowNode> shadowNode) {

packages/react-native/ReactCommon/react/nativemodule/dom/NativeDOM.h

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,13 @@ class NativeDOM : public NativeDOMCxxSpec<NativeDOM> {
5858
/* height: */ double>
5959
getBoundingClientRect(jsi::Runtime &rt, std::shared_ptr<const ShadowNode> shadowNode, bool includeTransform);
6060

61+
std::vector<std::tuple<
62+
/* x: */ double,
63+
/* y: */ double,
64+
/* width: */ double,
65+
/* height: */ double>>
66+
getClientRects(jsi::Runtime &rt, std::shared_ptr<const ShadowNode> shadowNode);
67+
6168
std::tuple</* width: */ int, /* height: */ int> getInnerSize(
6269
jsi::Runtime &rt,
6370
std::shared_ptr<const ShadowNode> shadowNode);

packages/react-native/src/private/webapis/dom/nodes/ReadOnlyElement.js

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -193,6 +193,18 @@ export default class ReadOnlyElement extends ReadOnlyNode {
193193
return getBoundingClientRect(this, {includeTransform: true});
194194
}
195195

196+
getClientRects(): $ReadOnlyArray<DOMRect> {
197+
const node = getNativeElementReference(this);
198+
199+
if (node != null) {
200+
const rects = NativeDOM.getClientRects(node);
201+
return rects.map(rect => new DOMRect(rect[0], rect[1], rect[2], rect[3]));
202+
}
203+
204+
// Empty array if any of the above failed
205+
return [];
206+
}
207+
196208
/**
197209
* Pointer Capture APIs
198210
*/

packages/react-native/src/private/webapis/dom/nodes/specs/NativeDOM.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -86,6 +86,12 @@ export interface Spec extends TurboModule {
8686
includeTransform: boolean,
8787
) => ReadonlyArray<number> /* [x: number, y: number, width: number, height: number] */;
8888

89+
+getClientRects: (
90+
nativeElementReference: unknown /* NativeElementReference */,
91+
) => ReadonlyArray<
92+
ReadonlyArray<number>,
93+
> /* Array<[x: number, y: number, width: number, height: number]> */;
94+
8995
+getInnerSize: (
9096
nativeElementReference: unknown /* NativeElementReference */,
9197
) => ReadonlyArray<number> /* [width: number, height: number] */;
@@ -279,6 +285,30 @@ export interface RefinedSpec {
279285
],
280286
>;
281287

288+
/**
289+
* This is a React Native implementation of `Element.prototype.getClientRects`
290+
* (see https://developer.mozilla.org/en-US/docs/Web/API/Element/getClientRects).
291+
*
292+
* For most elements, this returns an array with a single rect matching
293+
* getBoundingClientRect. For text elements (TextShadowNode), this returns
294+
* an array of rects representing each line/fragment of the text.
295+
*
296+
* This is useful for getting the visual boundaries of nested <Text>
297+
* components within a text paragraph, where text may span multiple lines.
298+
*/
299+
+getClientRects: (
300+
nativeElementReference: NativeElementReference,
301+
) => ReadonlyArray<
302+
Readonly<
303+
[
304+
/* x: */ number,
305+
/* y: */ number,
306+
/* width: */ number,
307+
/* height: */ number,
308+
],
309+
>,
310+
>;
311+
282312
/**
283313
* This is a method to access the inner size of a shadow node, to implement
284314
* these methods:

0 commit comments

Comments
 (0)