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); } 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;