react-native/Libraries/Components/ScrollView/RecyclerViewBackedScrollVie...

177 lines
5.5 KiB
JavaScript

/**
* Copyright 2004-present Facebook. All Rights Reserved.
*
* @providesModule RecyclerViewBackedScrollView
*/
'use strict';
var React = require('React');
var ScrollResponder = require('ScrollResponder');
var ScrollView = require('ScrollView');
var View = require('View');
var StyleSheet = require('StyleSheet');
var requireNativeComponent = require('requireNativeComponent');
var INNERVIEW = 'InnerView';
/**
* Wrapper around android native recycler view.
*
* It simply renders rows passed as children in a separate recycler view cells
* similarly to how `ScrollView` is doing it. Thanks to the fact that it uses
* native `RecyclerView` though, rows that are out of sight are going to be
* automatically detached (similarly on how this would work with
* `removeClippedSubviews = true` on a `ScrollView.js`).
*
* CAUTION: This is an experimental component and should only be used together
* with javascript implementation of list view (see ListView.js). In order to
* use it pass this component as `renderScrollComponent` to the list view. For
* now only horizontal scrolling is supported.
*
* Example:
*
* ```
* getInitialState: function() {
* var ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
* return {
* dataSource: ds.cloneWithRows(['row 1', 'row 2']),
* };
* },
*
* render: function() {
* return (
* <ListView
* dataSource={this.state.dataSource}
* renderRow={rowData => <Text>{rowData}</Text>}
* renderScrollComponent={props => <RecyclerViewBackedScrollView {...props} />}
* />
* );
* },
* ```
*/
var RecyclerViewBackedScrollView = React.createClass({
propTypes: {
...ScrollView.propTypes,
},
mixins: [ScrollResponder.Mixin],
getInitialState: function() {
return this.scrollResponderMixinGetInitialState();
},
getScrollResponder: function() {
return this;
},
setNativeProps: function(props: Object) {
this.refs[INNERVIEW].setNativeProps(props);
},
_handleContentSizeChange: function(event) {
var {width, height} = event.nativeEvent;
this.props.onContentSizeChange(width, height);
},
/**
* A helper function to scroll to a specific point in the scrollview.
* This is currently used to help focus on child textviews, but can also
* be used to quickly scroll to any element we want to focus. Syntax:
*
* scrollResponderScrollTo(options: {x: number = 0; y: number = 0; animated: boolean = true})
*
* Note: The weird argument signature is due to the fact that, for historical reasons,
* the function also accepts separate arguments as as alternative to the options object.
* This is deprecated due to ambiguity (y before x), and SHOULD NOT BE USED.
*/
scrollTo: function(
y?: number | { x?: number, y?: number, animated?: boolean },
x?: number,
animated?: boolean
) {
if (typeof y === 'number') {
console.warn('`scrollTo(y, x, animated)` is deprecated. Use `scrollTo({x: 5, y: 5, animated: true})` instead.');
} else {
({x, y, animated} = y || {});
}
this.getScrollResponder().scrollResponderScrollTo({x: x || 0, y: y || 0, animated: animated !== false});
},
render: function() {
var recyclerProps = {
...this.props,
onTouchStart: this.scrollResponderHandleTouchStart,
onTouchMove: this.scrollResponderHandleTouchMove,
onTouchEnd: this.scrollResponderHandleTouchEnd,
onScrollBeginDrag: this.scrollResponderHandleScrollBeginDrag,
onScrollEndDrag: this.scrollResponderHandleScrollEndDrag,
onMomentumScrollBegin: this.scrollResponderHandleMomentumScrollBegin,
onMomentumScrollEnd: this.scrollResponderHandleMomentumScrollEnd,
onStartShouldSetResponder: this.scrollResponderHandleStartShouldSetResponder,
onStartShouldSetResponderCapture: this.scrollResponderHandleStartShouldSetResponderCapture,
onScrollShouldSetResponder: this.scrollResponderHandleScrollShouldSetResponder,
onResponderGrant: this.scrollResponderHandleResponderGrant,
onResponderRelease: this.scrollResponderHandleResponderRelease,
onResponderReject: this.scrollResponderHandleResponderReject,
onScroll: this.scrollResponderHandleScroll,
ref: INNERVIEW,
};
if (this.props.onContentSizeChange) {
recyclerProps.onContentSizeChange = this._handleContentSizeChange;
}
var wrappedChildren = React.Children.map(this.props.children, (child) => {
if (!child) {
return null;
}
return (
<View
collapsable={false}
style={styles.absolute}>
{child}
</View>
);
});
const refreshControl = this.props.refreshControl;
if (refreshControl) {
// Wrap the NativeAndroidRecyclerView with a AndroidSwipeRefreshLayout.
return React.cloneElement(
refreshControl,
{style: [styles.base, this.props.style]},
<NativeAndroidRecyclerView {...recyclerProps} style={styles.base}>
{wrappedChildren}
</NativeAndroidRecyclerView>
);
}
return (
<NativeAndroidRecyclerView {...recyclerProps} style={[styles.base, this.props.style]}>
{wrappedChildren}
</NativeAndroidRecyclerView>
);
},
});
var styles = StyleSheet.create({
absolute: {
position: 'absolute',
top: 0,
left: 0,
right: 0,
},
base: {
flex: 1,
},
});
var NativeAndroidRecyclerView = requireNativeComponent(
'AndroidRecyclerViewBackedScrollView',
RecyclerViewBackedScrollView
);
module.exports = RecyclerViewBackedScrollView;