From eb41627dc147280eec3002afd1324c01ee8ed2ff Mon Sep 17 00:00:00 2001 From: Nick Gerleman Date: Fri, 4 Aug 2023 10:36:45 -0700 Subject: [PATCH 1/2] Return right edge in RTL Cell metrics Summary: Returned measurements from the measurements cache in RTL calculate offset as distance from the left edge of the cell to the right edge of the content, when it should instead be the distance from the right edge of the cell (the logical beginning). Changelog: [General][Fixed] - Return right edge in RTL cell metrics Differential Revision: D47978631 fbshipit-source-id: 70eb23ea2578c164832cc9f2efb060b56b10fc00 --- .../virtualized-lists/Lists/ListMetricsAggregator.js | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/packages/virtualized-lists/Lists/ListMetricsAggregator.js b/packages/virtualized-lists/Lists/ListMetricsAggregator.js index 46c58af2cc1735..df0b02ce998d73 100644 --- a/packages/virtualized-lists/Lists/ListMetricsAggregator.js +++ b/packages/virtualized-lists/Lists/ListMetricsAggregator.js @@ -256,8 +256,8 @@ export default class ListMetricsAggregator { } /** - * Converts a cartesian offset along the x or y axis to a flow-relative - * offset, (e.g. starting from the left in LTR, but right in RTL). + * Finds the flow-relative offset (e.g. starting from the left in LTR, but + * right in RTL) from a layout box. */ flowRelativeOffset(layout: Layout, referenceContentLength?: ?number): number { const {horizontal, rtl} = this._orientation; @@ -268,7 +268,10 @@ export default class ListMetricsAggregator { contentLength != null, 'ListMetricsAggregator must be notified of list content layout before resolving offsets', ); - return contentLength - this._selectOffset(layout); + return ( + contentLength - + (this._selectOffset(layout) + this._selectLength(layout)) + ); } else { return this._selectOffset(layout); } From 0949fa62586890d94b7c32e3a7158203118e7a95 Mon Sep 17 00:00:00 2001 From: Nick Gerleman Date: Fri, 4 Aug 2023 10:36:58 -0700 Subject: [PATCH 2/2] Right align scrollToIndex in RTL (#38737) Summary: Pull Request resolved: https://github.com/facebook/react-native/pull/38737 This fixes up behavior on Android so that `scrollToIndex` aligns the right edge of the cell of the given index to the right edge of the scrollview viewport. We do not incorporate RTL on iOS which inverts x/y coordinates from scroller (but not layout). Changelog: [General][Fixed] - Right align scrollToIndex in RTL Reviewed By: lenaic Differential Revision: D47978637 fbshipit-source-id: c4a37e5864f80f1750c394f9ce8a47db735a49a8 --- .../Lists/VirtualizedList.js | 70 +++++++++---------- 1 file changed, 35 insertions(+), 35 deletions(-) diff --git a/packages/virtualized-lists/Lists/VirtualizedList.js b/packages/virtualized-lists/Lists/VirtualizedList.js index 350e500e1e0d01..f9d3a5aef7792c 100644 --- a/packages/virtualized-lists/Lists/VirtualizedList.js +++ b/packages/virtualized-lists/Lists/VirtualizedList.js @@ -276,10 +276,23 @@ class VirtualizedList extends StateSafePureComponent { scrollRef.scrollTo({ animated, - ...this._cartesianScrollOffset(offset), + ...this._scrollToParamsFromOffset(offset), }); } + _scrollToParamsFromOffset(offset: number): {x?: number, y?: number} { + const {horizontal, rtl} = this._orientation(); + if (horizontal && rtl) { + // Add the visible length of the scrollview so that the offset is right-aligned + const cartOffset = this._listMetrics.cartesianOffset( + offset + this._scrollMetrics.visibleLength, + ); + return horizontal ? {x: cartOffset} : {y: cartOffset}; + } else { + return horizontal ? {x: offset} : {y: offset}; + } + } + recordInteraction() { this._nestedChildLists.forEach(childList => { childList.recordInteraction(); @@ -1480,39 +1493,6 @@ class VirtualizedList extends StateSafePureComponent { : metrics.width; } - _flowRelativeScrollOffset( - metrics: $ReadOnly<{ - x: number, - y: number, - ... - }>, - contentSize: $ReadOnly<{ - width: number, - height: number, - ... - }>, - ): number { - let offset = this._selectOffset(metrics); - - const {horizontal, rtl} = this._orientation(); - if (horizontal && rtl && Platform.OS !== 'ios') { - offset = this._selectLength(contentSize) - offset; - } - - return offset; - } - - _cartesianScrollOffset(offset: number): {x?: number, y?: number} { - const {horizontal, rtl} = this._orientation(); - const normalizedOffset = - horizontal && rtl && Platform.OS !== 'ios' - ? this._listMetrics.getContentLength() - offset - : offset; - - const cartOffset = this._listMetrics.cartesianOffset(normalizedOffset); - return horizontal ? {x: cartOffset} : {y: cartOffset}; - } - _selectOffset({x, y}: $ReadOnly<{x: number, y: number, ...}>): number { return this._orientation().horizontal ? x : y; } @@ -1688,7 +1668,7 @@ class VirtualizedList extends StateSafePureComponent { const timestamp = e.timeStamp; let visibleLength = this._selectLength(e.nativeEvent.layoutMeasurement); let contentLength = this._selectLength(e.nativeEvent.contentSize); - let offset = this._flowRelativeScrollOffset( + let offset = this._offsetFromScrollEvent( e.nativeEvent.contentOffset, e.nativeEvent.contentSize, ); @@ -1755,6 +1735,26 @@ class VirtualizedList extends StateSafePureComponent { this._scheduleCellsToRenderUpdate(); }; + _offsetFromScrollEvent( + contentOffset: $ReadOnly<{ + x: number, + y: number, + ... + }>, + contentSize: $ReadOnly<{ + width: number, + height: number, + ... + }>, + ): number { + const {horizontal, rtl} = this._orientation(); + if (Platform.OS === 'ios' || !(horizontal && rtl)) { + return this._selectOffset(contentOffset); + } + + return this._selectLength(contentSize) - this._selectOffset(contentOffset); + } + _scheduleCellsToRenderUpdate(opts?: {allowImmediateExecution?: boolean}) { const allowImmediateExecution = opts?.allowImmediateExecution ?? true;