Skip to content

Commit 44d5422

Browse files
rozelefacebook-github-bot
authored andcommitted
Allow reverse-order lists (#47160)
Summary: We have a special case for treating horizontal RTL lists differently. This change adds similar functionality for capturing the correct virtualized list cell metrics when a list items children are rendered in reverse order, e.g., with `flexDirection: 'column-reverse'`. ## Changelog [General][Fixed] Fixed FlatList to support reverse ordered items Differential Revision: D64575365
1 parent 8d7aca3 commit 44d5422

3 files changed

Lines changed: 325 additions & 37 deletions

File tree

packages/virtualized-lists/Lists/ListMetricsAggregator.js

Lines changed: 14 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ export type CellMetrics = {
3838
// based implementation instead of transform.
3939
export type ListOrientation = {
4040
horizontal: boolean,
41+
reversed: boolean,
4142
rtl: boolean,
4243
};
4344

@@ -66,6 +67,7 @@ export default class ListMetricsAggregator {
6667
_orientation: ListOrientation = {
6768
horizontal: false,
6869
rtl: false,
70+
reversed: false,
6971
};
7072

7173
/**
@@ -268,9 +270,9 @@ export default class ListMetricsAggregator {
268270
layout: LayoutRectangle,
269271
referenceContentLength?: ?number,
270272
): number {
271-
const {horizontal, rtl} = this._orientation;
273+
const {horizontal, reversed, rtl} = this._orientation;
272274

273-
if (horizontal && rtl) {
275+
if ((horizontal && rtl) || reversed) {
274276
const contentLength = referenceContentLength ?? this._contentLength;
275277
invariant(
276278
contentLength != null,
@@ -289,9 +291,9 @@ export default class ListMetricsAggregator {
289291
* Converts a flow-relative offset to a cartesian offset
290292
*/
291293
cartesianOffset(flowRelativeOffset: number): number {
292-
const {horizontal, rtl} = this._orientation;
294+
const {horizontal, reversed, rtl} = this._orientation;
293295

294-
if (horizontal && rtl) {
296+
if ((horizontal && rtl) || reversed) {
295297
invariant(
296298
this._contentLength != null,
297299
'ListMetricsAggregator must be notified of list content layout before resolving offsets',
@@ -314,6 +316,14 @@ export default class ListMetricsAggregator {
314316
this._measuredCellsCount = 0;
315317
}
316318

319+
if (orientation.reversed !== this._orientation.reversed) {
320+
this._cellMetrics.clear();
321+
this._averageCellLength = 0;
322+
this._highestMeasuredCellIndex = 0;
323+
this._measuredCellsLength = 0;
324+
this._measuredCellsCount = 0;
325+
}
326+
317327
this._orientation = orientation;
318328
}
319329

packages/virtualized-lists/Lists/VirtualizedList.js

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -254,10 +254,14 @@ class VirtualizedList extends StateSafePureComponent<
254254
return;
255255
}
256256

257-
const {horizontal, rtl} = this._orientation();
258-
if (horizontal && rtl && !this._listMetrics.hasContentLength()) {
257+
const {horizontal, reversed, rtl} = this._orientation();
258+
if (
259+
((horizontal && rtl) || reversed) &&
260+
!this._listMetrics.hasContentLength()
261+
) {
262+
const mode = horizontal && rtl ? 'RTL' : 'reversed lists';
259263
console.warn(
260-
'scrollToOffset may not be called in RTL before content is laid out',
264+
`scrollToOffset may not be called in ${mode} before content is laid out`,
261265
);
262266
return;
263267
}
@@ -270,8 +274,8 @@ class VirtualizedList extends StateSafePureComponent<
270274
}
271275

272276
_scrollToParamsFromOffset(offset: number): {x?: number, y?: number} {
273-
const {horizontal, rtl} = this._orientation();
274-
if (horizontal && rtl) {
277+
const {horizontal, reversed, rtl} = this._orientation();
278+
if ((horizontal && rtl) || reversed) {
275279
// Add the visible length of the scrollview so that the offset is right-aligned
276280
const cartOffset = this._listMetrics.cartesianOffset(
277281
offset + this._scrollMetrics.visibleLength,
@@ -1491,8 +1495,17 @@ class VirtualizedList extends StateSafePureComponent<
14911495
}
14921496

14931497
_orientation(): ListOrientation {
1498+
const horizontal = horizontalOrDefault(this.props.horizontal);
1499+
const contentFlexDirection = StyleSheet.flatten(
1500+
this.props.contentContainerStyle,
1501+
)?.flexDirection;
1502+
const reversed =
1503+
(horizontal && contentFlexDirection === 'row-reverse') ||
1504+
contentFlexDirection === 'column-reverse';
1505+
14941506
return {
1495-
horizontal: horizontalOrDefault(this.props.horizontal),
1507+
horizontal,
1508+
reversed,
14961509
rtl: I18nManager.isRTL,
14971510
};
14981511
}
@@ -1737,8 +1750,8 @@ class VirtualizedList extends StateSafePureComponent<
17371750

17381751
_offsetFromScrollEvent(e: ScrollEvent): number {
17391752
const {contentOffset, contentSize, layoutMeasurement} = e.nativeEvent;
1740-
const {horizontal, rtl} = this._orientation();
1741-
if (horizontal && rtl) {
1753+
const {horizontal, reversed, rtl} = this._orientation();
1754+
if ((horizontal && rtl) || reversed) {
17421755
return (
17431756
this._selectLength(contentSize) -
17441757
(this._selectOffset(contentOffset) +

0 commit comments

Comments
 (0)