mirror of
https://github.com/facebook/react-native.git
synced 2025-11-01 09:14:26 +00:00
Implement scrollTop and scrollLeft (#37754)
Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/37754 This adds a new method in Fabric to get the scroll position for an element, and uses it to implement the following methods as defined in as defined in https://github.com/react-native-community/discussions-and-proposals/pull/607 : * `scrollTop`: indicates the number of pixels (in device independent pixels) from the content that have moved in the vertical axis in the scroll container. * `scrollLeft`: indicates the number of pixels (in device independent pixels) from the content that have moved in the horizontal axis in the scroll container. These API can provide decimal values. Changelog: [internal] Reviewed By: javache Differential Revision: D44757811 fbshipit-source-id: e1b58db8d9f61e823b62d54620a9a0deaae1566b
This commit is contained in:
committed by
Facebook GitHub Bot
parent
7d1f7f3f5f
commit
08948810cb
@@ -91,19 +91,41 @@ export default class ReadOnlyElement extends ReadOnlyNode {
|
||||
}
|
||||
|
||||
get scrollHeight(): number {
|
||||
throw new TypeError('Unimplemented');
|
||||
throw new Error('Unimplemented');
|
||||
}
|
||||
|
||||
get scrollLeft(): number {
|
||||
throw new TypeError('Unimplemented');
|
||||
const node = getShadowNode(this);
|
||||
|
||||
if (node != null) {
|
||||
const scrollPosition = nullthrows(getFabricUIManager()).getScrollPosition(
|
||||
node,
|
||||
);
|
||||
if (scrollPosition != null) {
|
||||
return scrollPosition[0];
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
get scrollTop(): number {
|
||||
throw new TypeError('Unimplemented');
|
||||
const node = getShadowNode(this);
|
||||
|
||||
if (node != null) {
|
||||
const scrollPosition = nullthrows(getFabricUIManager()).getScrollPosition(
|
||||
node,
|
||||
);
|
||||
if (scrollPosition != null) {
|
||||
return scrollPosition[1];
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
get scrollWidth(): number {
|
||||
throw new TypeError('Unimplemented');
|
||||
throw new Error('Unimplemented');
|
||||
}
|
||||
|
||||
get tagName(): string {
|
||||
|
||||
@@ -74,10 +74,10 @@ export type Spec = {|
|
||||
+getBoundingClientRect: (
|
||||
node: Node,
|
||||
) => ?[
|
||||
/* x:*/ number,
|
||||
/* y:*/ number,
|
||||
/* width:*/ number,
|
||||
/* height:*/ number,
|
||||
/* x: */ number,
|
||||
/* y: */ number,
|
||||
/* width: */ number,
|
||||
/* height: */ number,
|
||||
],
|
||||
+getOffset: (
|
||||
node: Node,
|
||||
@@ -86,6 +86,9 @@ export type Spec = {|
|
||||
/* offsetTop: */ number,
|
||||
/* offsetLeft: */ number,
|
||||
],
|
||||
+getScrollPosition: (
|
||||
node: Node,
|
||||
) => ?[/* scrollLeft: */ number, /* scrollTop: */ number],
|
||||
|};
|
||||
|
||||
// This is exposed as a getter because apps using the legacy renderer AND
|
||||
|
||||
@@ -431,6 +431,33 @@ const FabricUIManagerMock: FabricUIManager = {
|
||||
];
|
||||
},
|
||||
),
|
||||
getScrollPosition: jest.fn(
|
||||
(node: Node): ?[/* scrollLeft: */ number, /* scrollTop: */ number] => {
|
||||
ensureHostNode(node);
|
||||
|
||||
const nodeInCurrentTree = getNodeInCurrentTree(node);
|
||||
const currentProps =
|
||||
nodeInCurrentTree != null ? fromNode(nodeInCurrentTree).props : null;
|
||||
if (currentProps == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const scrollForTests: ?{
|
||||
scrollLeft: number,
|
||||
scrollTop: number,
|
||||
...
|
||||
} =
|
||||
// $FlowExpectedError[prop-missing]
|
||||
currentProps.__scrollForTests;
|
||||
|
||||
if (scrollForTests == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const {scrollLeft, scrollTop} = scrollForTests;
|
||||
return [scrollLeft, scrollTop];
|
||||
},
|
||||
),
|
||||
};
|
||||
|
||||
global.nativeFabricUIManager = FabricUIManagerMock;
|
||||
|
||||
@@ -1013,6 +1013,71 @@ jsi::Value UIManagerBinding::get(
|
||||
});
|
||||
}
|
||||
|
||||
if (methodName == "getScrollPosition") {
|
||||
// This is a method to access scroll information for React Native nodes, to
|
||||
// implement these methods:
|
||||
// * `Element.prototype.scrollLeft`: see
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollLeft.
|
||||
// * `Element.prototype.scrollTop`: see
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollTop.
|
||||
|
||||
// It uses the version of the shadow node that is present in the current
|
||||
// revision of the shadow tree. If the node is not present or is not
|
||||
// displayed (because any of its ancestors or itself have 'display: none'),
|
||||
// it returns undefined. Otherwise, it returns the scroll position.
|
||||
|
||||
// getScrollPosition(shadowNode: ShadowNode):
|
||||
// ?[
|
||||
// /* scrollLeft: */ number,
|
||||
// /* scrollTop: */ number,
|
||||
// ]
|
||||
return jsi::Function::createFromHostFunction(
|
||||
runtime,
|
||||
name,
|
||||
1,
|
||||
[uiManager](
|
||||
jsi::Runtime &runtime,
|
||||
jsi::Value const & /*thisValue*/,
|
||||
jsi::Value const *arguments,
|
||||
size_t /*count*/) noexcept -> jsi::Value {
|
||||
auto shadowNode = shadowNodeFromValue(runtime, arguments[0]);
|
||||
|
||||
auto newestCloneOfShadowNode =
|
||||
uiManager->getNewestCloneOfShadowNode(*shadowNode);
|
||||
// The node is no longer part of an active shadow tree, or it is the
|
||||
// root node
|
||||
if (newestCloneOfShadowNode == nullptr) {
|
||||
return jsi::Value::undefined();
|
||||
}
|
||||
|
||||
// If the node is not displayed (itself or any of its ancestors has
|
||||
// "display: none", it returns an empty layout metrics object.
|
||||
auto layoutMetrics = uiManager->getRelativeLayoutMetrics(
|
||||
*shadowNode, nullptr, {/* .includeTransform = */ true});
|
||||
if (layoutMetrics == EmptyLayoutMetrics) {
|
||||
return jsi::Value::undefined();
|
||||
}
|
||||
|
||||
auto layoutableShadowNode = traitCast<LayoutableShadowNode const *>(
|
||||
newestCloneOfShadowNode.get());
|
||||
// This should never happen
|
||||
if (layoutableShadowNode == nullptr) {
|
||||
return jsi::Value::undefined();
|
||||
}
|
||||
|
||||
auto scrollPosition = layoutableShadowNode->getContentOriginOffset();
|
||||
|
||||
return jsi::Array::createWithElements(
|
||||
runtime,
|
||||
jsi::Value{
|
||||
runtime,
|
||||
scrollPosition.x == 0 ? 0 : (double)-scrollPosition.x},
|
||||
jsi::Value{
|
||||
runtime,
|
||||
scrollPosition.y == 0 ? 0 : (double)-scrollPosition.y});
|
||||
});
|
||||
}
|
||||
|
||||
return jsi::Value::undefined();
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user