Skip to content

Commit

Permalink
Add inverted option
Browse files Browse the repository at this point in the history
Reviewed By: bvaughn

Differential Revision: D5210277

fbshipit-source-id: 5a8b196b90a2ac6d20397113ef4bd76446ea9fa3
  • Loading branch information
sahrens authored and facebook-github-bot committed Jun 13, 2017
1 parent 0a3e6e0 commit 1d30f83
Show file tree
Hide file tree
Showing 8 changed files with 186 additions and 7 deletions.
4 changes: 4 additions & 0 deletions Libraries/Lists/FlatList.js
Expand Up @@ -131,6 +131,10 @@ type OptionalProps<ItemT> = {
* `getItemLayout` to be implemented.
*/
initialScrollIndex?: ?number,
/**
* Reverses the direction of scroll. Uses scale transforms of -1.
*/
inverted?: ?boolean,
/**
* Used to extract a unique key for a given item at the specified index. Key is used for caching
* and as the react key to track item re-ordering. The default extractor checks `item.key`, then
Expand Down
5 changes: 4 additions & 1 deletion Libraries/Lists/SectionList.js
Expand Up @@ -124,12 +124,15 @@ type OptionalProps<SectionT: SectionBase<any>> = {
* to improve perceived performance of scroll-to-top actions.
*/
initialNumToRender: number,
/**
* Reverses the direction of scroll. Uses scale transforms of -1.
*/
inverted?: ?boolean,
/**
* Used to extract a unique key for a given item at the specified index. Key is used for caching
* and as the react key to track item re-ordering. The default extractor checks item.key, then
* falls back to using the index, like react does.
*/

keyExtractor: (item: Item, index: number) => string,
/**
* Called once when the scroll position gets within `onEndReachedThreshold` of the rendered
Expand Down
47 changes: 43 additions & 4 deletions Libraries/Lists/VirtualizedList.js
Expand Up @@ -19,6 +19,7 @@ const React = require('React');
const ReactNative = require('ReactNative');
const RefreshControl = require('RefreshControl');
const ScrollView = require('ScrollView');
const StyleSheet = require('StyleSheet');
const View = require('View');
const ViewabilityHelper = require('ViewabilityHelper');

Expand All @@ -29,6 +30,7 @@ const warning = require('fbjs/lib/warning');

const {computeWindowedRenderLimits} = require('VirtualizeUtils');

import type {StyleObj} from 'StyleSheetTypes';
import type {ViewabilityConfig, ViewToken} from 'ViewabilityHelper';

type Item = any;
Expand Down Expand Up @@ -87,6 +89,10 @@ type OptionalProps = {
* `getItemLayout` to be implemented.
*/
initialScrollIndex?: ?number,
/**
* Reverses the direction of scroll. Uses scale transforms of -1.
*/
inverted?: ?boolean,
keyExtractor: (item: Item, index: number) => string,
/**
* Rendered when the list is empty. Can be a React Component Class, a render function, or
Expand Down Expand Up @@ -424,6 +430,7 @@ class VirtualizedList extends React.PureComponent<OptionalProps, Props, State> {
stickyIndicesFromProps: Set<number>,
first: number,
last: number,
inversionStyle: ?StyleObj,
) {
const {
ItemSeparatorComponent,
Expand All @@ -449,6 +456,7 @@ class VirtualizedList extends React.PureComponent<OptionalProps, Props, State> {
cellKey={key}
fillRateHelper={this._fillRateHelper}
index={ii}
inversionStyle={inversionStyle}
item={item}
key={key}
prevCellKey={prevCellKey}
Expand Down Expand Up @@ -501,6 +509,11 @@ class VirtualizedList extends React.PureComponent<OptionalProps, Props, State> {
} = this.props;
const {data, horizontal} = this.props;
const isVirtualizationDisabled = this._isVirtualizationDisabled();
const inversionStyle = this.props.inverted
? this.props.horizontal
? styles.horizontallyInverted
: styles.verticallyInverted
: null;
const cells = [];
const stickyIndicesFromProps = new Set(this.props.stickyHeaderIndices);
const stickyHeaderIndices = [];
Expand All @@ -509,7 +522,10 @@ class VirtualizedList extends React.PureComponent<OptionalProps, Props, State> {
? ListHeaderComponent // $FlowFixMe
: <ListHeaderComponent />;
cells.push(
<View key="$header" onLayout={this._onLayoutHeader}>
<View
key="$header"
onLayout={this._onLayoutHeader}
style={inversionStyle}>
{element}
</View>,
);
Expand All @@ -528,6 +544,7 @@ class VirtualizedList extends React.PureComponent<OptionalProps, Props, State> {
stickyIndicesFromProps,
0,
lastInitialIndex,
inversionStyle,
);
const firstAfterInitial = Math.max(lastInitialIndex + 1, first);
if (!isVirtualizationDisabled && first > lastInitialIndex + 1) {
Expand All @@ -550,6 +567,7 @@ class VirtualizedList extends React.PureComponent<OptionalProps, Props, State> {
stickyIndicesFromProps,
ii,
ii,
inversionStyle,
);
const trailSpace =
this._getFrameMetricsApprox(first).offset -
Expand Down Expand Up @@ -578,6 +596,7 @@ class VirtualizedList extends React.PureComponent<OptionalProps, Props, State> {
stickyIndicesFromProps,
firstAfterInitial,
last,
inversionStyle,
);
if (!this._hasWarned.keys && _usedIndexForKey) {
console.warn(
Expand Down Expand Up @@ -608,7 +627,10 @@ class VirtualizedList extends React.PureComponent<OptionalProps, Props, State> {
? ListEmptyComponent // $FlowFixMe
: <ListEmptyComponent />;
cells.push(
<View key="$empty" onLayout={this._onLayoutEmpty}>
<View
key="$empty"
onLayout={this._onLayoutEmpty}
style={inversionStyle}>
{element}
</View>,
);
Expand All @@ -618,7 +640,10 @@ class VirtualizedList extends React.PureComponent<OptionalProps, Props, State> {
? ListFooterComponent // $FlowFixMe
: <ListFooterComponent />;
cells.push(
<View key="$footer" onLayout={this._onLayoutFooter}>
<View
key="$footer"
onLayout={this._onLayoutFooter}
style={inversionStyle}>
{element}
</View>,
);
Expand All @@ -634,6 +659,9 @@ class VirtualizedList extends React.PureComponent<OptionalProps, Props, State> {
scrollEventThrottle: this.props.scrollEventThrottle, // TODO: Android support
stickyHeaderIndices,
};
if (inversionStyle) {
scrollProps.style = [inversionStyle, this.props.style];
}
const ret = React.cloneElement(
(this.props.renderScrollComponent || this._defaultRenderScrollComponent)(
scrollProps,
Expand Down Expand Up @@ -1086,6 +1114,7 @@ class CellRenderer extends React.Component {
cellKey: string,
fillRateHelper: FillRateHelper,
index: number,
inversionStyle: ?StyleObj,
item: Item,
onLayout: (event: Object) => void, // This is extracted by ScrollViewStickyHeader
onUnmount: (cellKey: string) => void,
Expand Down Expand Up @@ -1144,6 +1173,7 @@ class CellRenderer extends React.Component {
fillRateHelper,
item,
index,
inversionStyle,
parentProps,
} = this.props;
const {renderItem, getItemLayout} = parentProps;
Expand All @@ -1161,7 +1191,7 @@ class CellRenderer extends React.Component {
// NOTE: that when this is a sticky header, `onLayout` will get automatically extracted and
// called explicitly by `ScrollViewStickyHeader`.
return (
<View onLayout={onLayout}>
<View onLayout={onLayout} style={inversionStyle}>
{element}
{ItemSeparatorComponent &&
<ItemSeparatorComponent {...this.state.separatorProps} />}
Expand All @@ -1170,4 +1200,13 @@ class CellRenderer extends React.Component {
}
}

const styles = StyleSheet.create({
verticallyInverted: {
transform: [{scaleY: -1}],
},
horizontallyInverted: {
transform: [{scaleX: -1}],
},
});

module.exports = VirtualizedList;
5 changes: 3 additions & 2 deletions Libraries/Lists/__tests__/VirtualizedList-test.js
Expand Up @@ -93,10 +93,11 @@ describe('VirtualizedList', () => {
data={new Array(5).fill().map((_, ii) => ({id: String(ii)}))}
getItem={(data, index) => data[index]}
getItemCount={data => data.length}
keyExtractor={(item, index) => item.id}
getItemLayout={({index}) => ({length: 50, offset: index * 50})}
refreshing={false}
inverted={true}
keyExtractor={(item, index) => item.id}
onRefresh={jest.fn()}
refreshing={false}
renderItem={({item}) => <item value={item.id} />}
/>,
);
Expand Down
8 changes: 8 additions & 0 deletions Libraries/Lists/__tests__/__snapshots__/FlatList-test.js.snap
Expand Up @@ -61,11 +61,13 @@ exports[`FlatList renders all the bells and whistles 1`] = `
<View>
<View
onLayout={[Function]}
style={null}
>
<header />
</View>
<View
onLayout={undefined}
style={null}
>
<View
style={
Expand All @@ -88,6 +90,7 @@ exports[`FlatList renders all the bells and whistles 1`] = `
</View>
<View
onLayout={undefined}
style={null}
>
<View
style={
Expand All @@ -110,6 +113,7 @@ exports[`FlatList renders all the bells and whistles 1`] = `
</View>
<View
onLayout={undefined}
style={null}
>
<View
style={
Expand All @@ -128,6 +132,7 @@ exports[`FlatList renders all the bells and whistles 1`] = `
</View>
<View
onLayout={[Function]}
style={null}
>
<footer />
</View>
Expand Down Expand Up @@ -233,20 +238,23 @@ exports[`FlatList renders simple list 1`] = `
<View>
<View
onLayout={[Function]}
style={null}
>
<item
value="i1"
/>
</View>
<View
onLayout={[Function]}
style={null}
>
<item
value="i2"
/>
</View>
<View
onLayout={[Function]}
style={null}
>
<item
value="i3"
Expand Down

0 comments on commit 1d30f83

Please sign in to comment.