diff --git a/Libraries/Lists/FlatList.js b/Libraries/Lists/FlatList.js index 4e9cdb85ff..d5d785e141 100644 --- a/Libraries/Lists/FlatList.js +++ b/Libraries/Lists/FlatList.js @@ -231,7 +231,8 @@ type DefaultProps = typeof defaultProps; * } * * This is a convenience wrapper around [``](docs/virtualizedlist.html), - * and thus inherits it's props that aren't explicitly listed here along with the following caveats: + * and thus inherits it's props (as well as those of `ScrollView`) that aren't explicitly listed + * here, along with the following caveats: * * - Internal state is not preserved when content scrolls out of the render window. Make sure all * your data is captured in the item data or external stores like Flux, Redux, or Relay. @@ -264,17 +265,22 @@ class FlatList extends React.PureComponent, vo /** * Scrolls to the item at a the specified index such that it is positioned in the viewable area * such that `viewPosition` 0 places it at the top, 1 at the bottom, and 0.5 centered in the - * middle. + * middle. `viewOffset` is a fixed number of pixels to offset the final target position. * - * May be janky without `getItemLayout` prop. + * Note: cannot scroll to locations outside the render window without specifying the + * `getItemLayout` prop. */ - scrollToIndex(params: {animated?: ?boolean, index: number, viewPosition?: number}) { + scrollToIndex(params: { + animated?: ?boolean, index: number, viewOffset?: number, viewPosition?: number, + }) { this._listRef.scrollToIndex(params); } /** - * Requires linear scan through data - use `scrollToIndex` instead if possible. May be janky - * without `getItemLayout` prop. + * Requires linear scan through data - use `scrollToIndex` instead if possible. + * + * Note: cannot scroll to locations outside the render window without specifying the + * `getItemLayout` prop. */ scrollToItem(params: {animated?: ?boolean, item: ItemT, viewPosition?: number}) { this._listRef.scrollToItem(params); diff --git a/Libraries/Lists/MetroListView.js b/Libraries/Lists/MetroListView.js index c961078778..eae4a6f326 100644 --- a/Libraries/Lists/MetroListView.js +++ b/Libraries/Lists/MetroListView.js @@ -61,6 +61,9 @@ class MetroListView extends React.Component { scrollToItem(params: {animated?: ?boolean, item: Item, viewPosition?: number}) { throw new Error('scrollToItem not supported in legacy ListView.'); } + scrollToLocation() { + throw new Error('scrollToLocation not supported in legacy ListView.'); + } scrollToOffset(params: {animated?: ?boolean, offset: number}) { const {animated, offset} = params; this._listRef.scrollTo( diff --git a/Libraries/Lists/SectionList.js b/Libraries/Lists/SectionList.js index dd87a354ea..651e3508b2 100644 --- a/Libraries/Lists/SectionList.js +++ b/Libraries/Lists/SectionList.js @@ -167,8 +167,9 @@ type DefaultProps = typeof defaultProps; * ]} * /> * - * This is a convenience wrapper around [``](/react-native/docs/virtualizedlist.html), - * and thus inherits the following caveats: + * This is a convenience wrapper around [``](docs/virtualizedlist.html), + * and thus inherits it's props (as well as those of `ScrollView`) that aren't explicitly listed + * here, along with the following caveats: * * - Internal state is not preserved when content scrolls out of the render window. Make sure all * your data is captured in the item data or external stores like Flux, Redux, or Relay. @@ -194,6 +195,26 @@ class SectionList> props: Props; static defaultProps: DefaultProps = defaultProps; + /** + * Scrolls to the item at the specified `sectionIndex` and `itemIndex` (within the section) + * positioned in the viewable area such that `viewPosition` 0 places it at the top (and may be + * covered by a sticky header), 1 at the bottom, and 0.5 centered in the middle. `viewOffset` is a + * fixed number of pixels to offset the final target position, e.g. to compensate for sticky + * headers. + * + * Note: cannot scroll to locations outside the render window without specifying the + * `getItemLayout` prop. + */ + scrollToLocation(params: { + animated?: ?boolean, + itemIndex: number, + sectionIndex: number, + viewOffset?: number, + viewPosition?: number, + }) { + this._wrapperListRef.scrollToLocation(params); + } + /** * Tells the list an interaction has occured, which should trigger viewability calculations, e.g. * if `waitForInteractions` is true and the user has not scrolled. This is typically called by diff --git a/Libraries/Lists/VirtualizedList.js b/Libraries/Lists/VirtualizedList.js index 6bbd7aef09..49ca40a4a6 100644 --- a/Libraries/Lists/VirtualizedList.js +++ b/Libraries/Lists/VirtualizedList.js @@ -178,9 +178,11 @@ class VirtualizedList extends React.PureComponent { } // scrollToIndex may be janky without getItemLayout prop - scrollToIndex(params: {animated?: ?boolean, index: number, viewPosition?: number}) { + scrollToIndex(params: { + animated?: ?boolean, index: number, viewOffset?: number, viewPosition?: number + }) { const {data, horizontal, getItemCount, getItemLayout} = this.props; - const {animated, index, viewPosition} = params; + const {animated, index, viewOffset, viewPosition} = params; invariant( index >= 0 && index < getItemCount(data), `scrollToIndex out of range: ${index} vs ${getItemCount(data) - 1}`, @@ -194,7 +196,7 @@ class VirtualizedList extends React.PureComponent { const offset = Math.max( 0, frame.offset - (viewPosition || 0) * (this._scrollMetrics.visibleLength - frame.length), - ); + ) - (viewOffset || 0); this._scrollRef.scrollTo(horizontal ? {x: offset, animated} : {y: offset, animated}); } diff --git a/Libraries/Lists/VirtualizedSectionList.js b/Libraries/Lists/VirtualizedSectionList.js index fdf6110fff..d35eb2bcdc 100644 --- a/Libraries/Lists/VirtualizedSectionList.js +++ b/Libraries/Lists/VirtualizedSectionList.js @@ -119,6 +119,20 @@ class VirtualizedSectionList data: [], }; + scrollToLocation(params: { + animated?: ?boolean, itemIndex: number, sectionIndex: number, viewPosition?: number + }) { + let index = params.itemIndex + 1; + for (let ii = 0; ii < params.sectionIndex; ii++) { + index += this.props.sections[ii].data.length + 1; + } + const toIndexParams = { + ...params, + index, + }; + this._listRef.scrollToIndex(toIndexParams); + } + getListRef(): VirtualizedList { return this._listRef; } @@ -194,8 +208,7 @@ class VirtualizedSectionList const {renderSectionHeader} = this.props; return renderSectionHeader ? renderSectionHeader({section: info.section}) : null; } else { - const renderItem = info.section.renderItem || - this.props.renderItem; + const renderItem = info.section.renderItem || this.props.renderItem; const SeparatorComponent = this._getSeparatorComponent(index, info); invariant(renderItem, 'no renderItem!'); return (