...
|
...
|
@@ -11,26 +11,31 @@ |
|
|
*/
|
|
|
'use strict';
|
|
|
|
|
|
const Animated = require('Animated');
|
|
|
const ColorPropType = require('ColorPropType');
|
|
|
const EdgeInsetsPropType = require('EdgeInsetsPropType');
|
|
|
const Platform = require('Platform');
|
|
|
const PointPropType = require('PointPropType');
|
|
|
const RCTScrollViewManager = require('NativeModules').ScrollViewManager;
|
|
|
const PropTypes = require('prop-types');
|
|
|
const React = require('React');
|
|
|
const ReactNative = require('react/lib/ReactNative');
|
|
|
const ReactNative = require('ReactNative');
|
|
|
const ScrollResponder = require('ScrollResponder');
|
|
|
const ScrollViewStickyHeader = require('ScrollViewStickyHeader');
|
|
|
const StyleSheet = require('StyleSheet');
|
|
|
const StyleSheetPropType = require('StyleSheetPropType');
|
|
|
const View = require('View');
|
|
|
const ViewPropTypes = require('ViewPropTypes');
|
|
|
const ViewStylePropTypes = require('ViewStylePropTypes');
|
|
|
|
|
|
const dismissKeyboard = require('dismissKeyboard');
|
|
|
const flattenStyle = require('flattenStyle');
|
|
|
const invariant = require('fbjs/lib/invariant');
|
|
|
const processDecelerationRate = require('processDecelerationRate');
|
|
|
const PropTypes = React.PropTypes;
|
|
|
const requireNativeComponent = require('requireNativeComponent');
|
|
|
|
|
|
|
|
|
const RCTScrollViewManager = require('NativeModules').ScrollViewManager;
|
|
|
|
|
|
/**
|
|
|
* Component that wraps platform ScrollView while providing
|
|
|
* integration with touch locking "responder" system.
|
...
|
...
|
@@ -45,10 +50,31 @@ const requireNativeComponent = require('requireNativeComponent'); |
|
|
*
|
|
|
* Doesn't yet support other contained responders from blocking this scroll
|
|
|
* view from becoming the responder.
|
|
|
*
|
|
|
*
|
|
|
* `<ScrollView>` vs [`<FlatList>`](/react-native/docs/flatlist.html) - which one to use?
|
|
|
*
|
|
|
* `ScrollView` simply renders all its react child components at once. That
|
|
|
* makes it very easy to understand and use.
|
|
|
*
|
|
|
* On the other hand, this has a performance downside. Imagine you have a very
|
|
|
* long list of items you want to display, maybe several screens worth of
|
|
|
* content. Creating JS components and native views for everything all at once,
|
|
|
* much of which may not even be shown, will contribute to slow rendering and
|
|
|
* increased memory usage.
|
|
|
*
|
|
|
* This is where `FlatList` comes into play. `FlatList` renders items lazily,
|
|
|
* just when they are about to appear, and removes items that scroll way off
|
|
|
* screen to save memory and processing time.
|
|
|
*
|
|
|
* `FlatList` is also handy if you want to render separators between your items,
|
|
|
* multiple columns, infinite scroll loading, or any number of other features it
|
|
|
* supports out of the box.
|
|
|
*/
|
|
|
// $FlowFixMe(>=0.41.0)
|
|
|
const ScrollView = React.createClass({
|
|
|
propTypes: {
|
|
|
...View.propTypes,
|
|
|
...ViewPropTypes,
|
|
|
/**
|
|
|
* Controls whether iOS should automatically adjust the content inset
|
|
|
* for scroll views that are placed behind a navigation bar or
|
...
|
...
|
@@ -143,8 +169,8 @@ const ScrollView = React.createClass({ |
|
|
/**
|
|
|
* The style of the scroll indicators.
|
|
|
* - `default` (the default), same as `black`.
|
|
|
* - `black`, scroll indicator is black. This style is good against a white content background.
|
|
|
* - `white`, scroll indicator is white. This style is good against a black content background.
|
|
|
* - `black`, scroll indicator is black. This style is good against a light background.
|
|
|
* - `white`, scroll indicator is white. This style is good against a dark background.
|
|
|
* @platform ios
|
|
|
*/
|
|
|
indicatorStyle: PropTypes.oneOf([
|
...
|
...
|
@@ -178,12 +204,18 @@ const ScrollView = React.createClass({ |
|
|
'on-drag',
|
|
|
]),
|
|
|
/**
|
|
|
* When false, tapping outside of the focused text input when the keyboard
|
|
|
* is up dismisses the keyboard. When true, the keyboard will not dismiss
|
|
|
* automatically, and the scroll view will not catch taps, but children of
|
|
|
* the scroll view can catch taps. The default value is false.
|
|
|
* Determines when the keyboard should stay visible after a tap.
|
|
|
*
|
|
|
* - 'never' (the default), tapping outside of the focused text input when the keyboard
|
|
|
* is up dismisses the keyboard. When this happens, children won't receive the tap.
|
|
|
* - 'always', the keyboard will not dismiss automatically, and the scroll view will not
|
|
|
* catch taps, but children of the scroll view can catch taps.
|
|
|
* - 'handled', the keyboard will not dismiss automatically when the tap was handled by
|
|
|
* a children, (or captured by an ancestor).
|
|
|
* - false, deprecated, use 'never' instead
|
|
|
* - true, deprecated, use 'always' instead
|
|
|
*/
|
|
|
keyboardShouldPersistTaps: PropTypes.bool,
|
|
|
keyboardShouldPersistTaps: PropTypes.oneOf(['always', 'never', 'handled', false, true]),
|
|
|
/**
|
|
|
* The maximum allowed zoom scale. The default value is 1.0.
|
|
|
* @platform ios
|
...
|
...
|
@@ -207,7 +239,8 @@ const ScrollView = React.createClass({ |
|
|
/**
|
|
|
* Called when scrollable content view of the ScrollView changes.
|
|
|
*
|
|
|
* Handler function is passed the content width and content height as parameters: `(contentWidth, contentHeight)`
|
|
|
* Handler function is passed the content width and content height as parameters:
|
|
|
* `(contentWidth, contentHeight)`
|
|
|
*
|
|
|
* It's implemented using onLayout handler attached to the content container
|
|
|
* which this ScrollView renders.
|
...
|
...
|
@@ -252,10 +285,12 @@ const ScrollView = React.createClass({ |
|
|
scrollsToTop: PropTypes.bool,
|
|
|
/**
|
|
|
* When true, shows a horizontal scroll indicator.
|
|
|
* The default value is true.
|
|
|
*/
|
|
|
showsHorizontalScrollIndicator: PropTypes.bool,
|
|
|
/**
|
|
|
* When true, shows a vertical scroll indicator.
|
|
|
* The default value is true.
|
|
|
*/
|
|
|
showsVerticalScrollIndicator: PropTypes.bool,
|
|
|
/**
|
...
|
...
|
@@ -264,7 +299,6 @@ const ScrollView = React.createClass({ |
|
|
* `stickyHeaderIndices={[0]}` will cause the first child to be fixed to the
|
|
|
* top of the scroll view. This property is not supported in conjunction
|
|
|
* with `horizontal={true}`.
|
|
|
* @platform ios
|
|
|
*/
|
|
|
stickyHeaderIndices: PropTypes.arrayOf(PropTypes.number),
|
|
|
style: StyleSheetPropType(ViewStylePropTypes),
|
...
|
...
|
@@ -304,7 +338,8 @@ const ScrollView = React.createClass({ |
|
|
|
|
|
/**
|
|
|
* A RefreshControl component, used to provide pull-to-refresh
|
|
|
* functionality for the ScrollView.
|
|
|
* functionality for the ScrollView. Only works for vertical ScrollViews
|
|
|
* (`horizontal` prop must be `false`).
|
|
|
*
|
|
|
* See [RefreshControl](docs/refreshcontrol.html).
|
|
|
*/
|
...
|
...
|
@@ -328,6 +363,25 @@ const ScrollView = React.createClass({ |
|
|
*/
|
|
|
scrollPerfTag: PropTypes.string,
|
|
|
|
|
|
/**
|
|
|
* Used to override default value of overScroll mode.
|
|
|
*
|
|
|
* Possible values:
|
|
|
*
|
|
|
* - `'auto'` - Default value, allow a user to over-scroll
|
|
|
* this view only if the content is large enough to meaningfully scroll.
|
|
|
* - `'always'` - Always allow a user to over-scroll this view.
|
|
|
* - `'never'` - Never allow a user to over-scroll this view.
|
|
|
*
|
|
|
* @platform android
|
|
|
*/
|
|
|
overScrollMode: PropTypes.oneOf([
|
|
|
'auto',
|
|
|
'always',
|
|
|
'never',
|
|
|
]),
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
|
pull to refresh
|
...
|
...
|
@@ -343,19 +397,27 @@ const ScrollView = React.createClass({ |
|
|
*/
|
|
|
yh_exposureData: PropTypes.any,
|
|
|
yh_viewVisible: PropTypes.bool,
|
|
|
|
|
|
|
|
|
},
|
|
|
|
|
|
mixins: [ScrollResponder.Mixin],
|
|
|
|
|
|
_scrollAnimatedValue: (new Animated.Value(0): Animated.Value),
|
|
|
_scrollAnimatedValueAttachment: (null: ?{detach: () => void}),
|
|
|
_stickyHeaderRefs: (new Map(): Map<number, ScrollViewStickyHeader>),
|
|
|
_headerLayoutYs: (new Map(): Map<string, number>),
|
|
|
|
|
|
|
|
|
yh_updateVisibleSubViews: function() {
|
|
|
RCTScrollViewManager.yh_updateVisibleSubViews(
|
|
|
ReactNative.findNodeHandle(this)
|
|
|
ReactNative.findNodeHandle(this._scrollViewRef)
|
|
|
);
|
|
|
},
|
|
|
|
|
|
startPullToRefresh: function() {
|
|
|
RCTScrollViewManager.startPullToRefresh(
|
|
|
ReactNative.findNodeHandle(this)
|
|
|
ReactNative.findNodeHandle(this._scrollViewRef)
|
|
|
);
|
|
|
},
|
|
|
|
...
|
...
|
@@ -365,10 +427,31 @@ const ScrollView = React.createClass({ |
|
|
);
|
|
|
},
|
|
|
|
|
|
|
|
|
getInitialState: function() {
|
|
|
return this.scrollResponderMixinGetInitialState();
|
|
|
},
|
|
|
|
|
|
componentWillMount: function() {
|
|
|
this._scrollAnimatedValue = new Animated.Value(0);
|
|
|
this._stickyHeaderRefs = new Map();
|
|
|
this._headerLayoutYs = new Map();
|
|
|
},
|
|
|
|
|
|
componentDidMount: function() {
|
|
|
this._updateAnimatedNodeAttachment();
|
|
|
},
|
|
|
|
|
|
componentDidUpdate: function() {
|
|
|
this._updateAnimatedNodeAttachment();
|
|
|
},
|
|
|
|
|
|
componentWillUnmount: function() {
|
|
|
if (this._scrollAnimatedValueAttachment) {
|
|
|
this._scrollAnimatedValueAttachment.detach();
|
|
|
}
|
|
|
},
|
|
|
|
|
|
setNativeProps: function(props: Object) {
|
|
|
this._scrollViewRef && this._scrollViewRef.setNativeProps(props);
|
|
|
},
|
...
|
...
|
@@ -394,12 +477,12 @@ const ScrollView = React.createClass({ |
|
|
/**
|
|
|
* Scrolls to a given x, y offset, either immediately or with a smooth animation.
|
|
|
*
|
|
|
* Syntax:
|
|
|
* Example:
|
|
|
*
|
|
|
* `scrollTo(options: {x: number = 0; y: number = 0; animated: boolean = true})`
|
|
|
* `scrollTo({x: 0, y: 0, animated: 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.
|
|
|
* Note: The weird function signature is due to the fact that, for historical reasons,
|
|
|
* the function also accepts separate arguments as an alternative to the options object.
|
|
|
* This is deprecated due to ambiguity (y before x), and SHOULD NOT BE USED.
|
|
|
*/
|
|
|
scrollTo: function(
|
...
|
...
|
@@ -408,24 +491,97 @@ const ScrollView = React.createClass({ |
|
|
animated?: boolean
|
|
|
) {
|
|
|
if (typeof y === 'number') {
|
|
|
console.warn('`scrollTo(y, x, animated)` is deprecated. Use `scrollTo({x: 5, y: 5, animated: true})` instead.');
|
|
|
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});
|
|
|
this.getScrollResponder().scrollResponderScrollTo(
|
|
|
{x: x || 0, y: y || 0, animated: animated !== false}
|
|
|
);
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
* If this is a vertical ScrollView scrolls to the bottom.
|
|
|
* If this is a horizontal ScrollView scrolls to the right.
|
|
|
*
|
|
|
* Use `scrollToEnd({animated: true})` for smooth animated scrolling,
|
|
|
* `scrollToEnd({animated: false})` for immediate scrolling.
|
|
|
* If no options are passed, `animated` defaults to true.
|
|
|
*/
|
|
|
scrollToEnd: function(
|
|
|
options?: { animated?: boolean },
|
|
|
) {
|
|
|
// Default to true
|
|
|
const animated = (options && options.animated) !== false;
|
|
|
this.getScrollResponder().scrollResponderScrollToEnd({
|
|
|
animated: animated,
|
|
|
});
|
|
|
},
|
|
|
|
|
|
/**
|
|
|
* Deprecated, do not use.
|
|
|
* Deprecated, use `scrollTo` instead.
|
|
|
*/
|
|
|
scrollWithoutAnimationTo: function(y: number = 0, x: number = 0) {
|
|
|
console.warn('`scrollWithoutAnimationTo` is deprecated. Use `scrollTo` instead');
|
|
|
this.scrollTo({x, y, animated: false});
|
|
|
},
|
|
|
|
|
|
_getKeyForIndex: function(index, childArray) {
|
|
|
const child = childArray[index];
|
|
|
return child && child.key;
|
|
|
},
|
|
|
|
|
|
_updateAnimatedNodeAttachment: function() {
|
|
|
if (this.props.stickyHeaderIndices && this.props.stickyHeaderIndices.length > 0) {
|
|
|
if (!this._scrollAnimatedValueAttachment) {
|
|
|
this._scrollAnimatedValueAttachment = Animated.attachNativeEvent(
|
|
|
this._scrollViewRef,
|
|
|
'onScroll',
|
|
|
[{nativeEvent: {contentOffset: {y: this._scrollAnimatedValue}}}]
|
|
|
);
|
|
|
}
|
|
|
} else {
|
|
|
if (this._scrollAnimatedValueAttachment) {
|
|
|
this._scrollAnimatedValueAttachment.detach();
|
|
|
}
|
|
|
}
|
|
|
},
|
|
|
|
|
|
_setStickyHeaderRef: function(key, ref) {
|
|
|
if (ref) {
|
|
|
this._stickyHeaderRefs.set(key, ref);
|
|
|
} else {
|
|
|
this._stickyHeaderRefs.delete(key);
|
|
|
}
|
|
|
},
|
|
|
|
|
|
_onStickyHeaderLayout: function(index, event, key) {
|
|
|
if (!this.props.stickyHeaderIndices) {
|
|
|
return;
|
|
|
}
|
|
|
const childArray = React.Children.toArray(this.props.children);
|
|
|
if (key !== this._getKeyForIndex(index, childArray)) {
|
|
|
// ignore stale layout update
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
const layoutY = event.nativeEvent.layout.y;
|
|
|
this._headerLayoutYs.set(key, layoutY);
|
|
|
|
|
|
const indexOfIndex = this.props.stickyHeaderIndices.indexOf(index);
|
|
|
const previousHeaderIndex = this.props.stickyHeaderIndices[indexOfIndex - 1];
|
|
|
if (previousHeaderIndex != null) {
|
|
|
const previousHeader = this._stickyHeaderRefs.get(
|
|
|
this._getKeyForIndex(previousHeaderIndex, childArray)
|
|
|
);
|
|
|
previousHeader && previousHeader.setNextHeaderY(layoutY);
|
|
|
}
|
|
|
},
|
|
|
|
|
|
_handleScroll: function(e: Object) {
|
|
|
if (__DEV__) {
|
|
|
if (this.props.onScroll && !this.props.scrollEventThrottle && Platform.OS === 'ios') {
|
|
|
if (this.props.onScroll && this.props.scrollEventThrottle == null && Platform.OS === 'ios') {
|
|
|
console.log( // eslint-disable-line no-console-disallow
|
|
|
'You specified `onScroll` on a <ScrollView> but not ' +
|
|
|
'`scrollEventThrottle`. You will only receive one event. ' +
|
...
|
...
|
@@ -459,6 +615,30 @@ const ScrollView = React.createClass({ |
|
|
},
|
|
|
|
|
|
render: function() {
|
|
|
let ScrollViewClass;
|
|
|
let ScrollContentContainerViewClass;
|
|
|
if (Platform.OS === 'ios') {
|
|
|
ScrollViewClass = RCTScrollView;
|
|
|
ScrollContentContainerViewClass = RCTScrollContentView;
|
|
|
} else if (Platform.OS === 'android') {
|
|
|
if (this.props.horizontal) {
|
|
|
ScrollViewClass = AndroidHorizontalScrollView;
|
|
|
} else {
|
|
|
ScrollViewClass = AndroidScrollView;
|
|
|
}
|
|
|
ScrollContentContainerViewClass = View;
|
|
|
}
|
|
|
|
|
|
invariant(
|
|
|
ScrollViewClass !== undefined,
|
|
|
'ScrollViewClass must not be undefined'
|
|
|
);
|
|
|
|
|
|
invariant(
|
|
|
ScrollContentContainerViewClass !== undefined,
|
|
|
'ScrollContentContainerViewClass must not be undefined'
|
|
|
);
|
|
|
|
|
|
const contentContainerStyle = [
|
|
|
this.props.horizontal && styles.contentContainerHorizontal,
|
|
|
this.props.contentContainerStyle,
|
...
|
...
|
@@ -482,15 +662,41 @@ const ScrollView = React.createClass({ |
|
|
};
|
|
|
}
|
|
|
|
|
|
const {stickyHeaderIndices} = this.props;
|
|
|
const hasStickyHeaders = stickyHeaderIndices && stickyHeaderIndices.length > 0;
|
|
|
const childArray = hasStickyHeaders && React.Children.toArray(this.props.children);
|
|
|
const children = hasStickyHeaders ?
|
|
|
childArray.map((child, index) => {
|
|
|
const indexOfIndex = child ? stickyHeaderIndices.indexOf(index) : -1;
|
|
|
if (indexOfIndex > -1) {
|
|
|
const key = child.key;
|
|
|
const nextIndex = stickyHeaderIndices[indexOfIndex + 1];
|
|
|
return (
|
|
|
<ScrollViewStickyHeader
|
|
|
key={key}
|
|
|
ref={(ref) => this._setStickyHeaderRef(key, ref)}
|
|
|
nextHeaderLayoutY={
|
|
|
this._headerLayoutYs.get(this._getKeyForIndex(nextIndex, childArray))
|
|
|
}
|
|
|
onLayout={(event) => this._onStickyHeaderLayout(index, event, key)}
|
|
|
scrollAnimatedValue={this._scrollAnimatedValue}>
|
|
|
{child}
|
|
|
</ScrollViewStickyHeader>
|
|
|
);
|
|
|
} else {
|
|
|
return child;
|
|
|
}
|
|
|
}) :
|
|
|
this.props.children;
|
|
|
const contentContainer =
|
|
|
<View
|
|
|
<ScrollContentContainerViewClass
|
|
|
{...contentSizeChangeProps}
|
|
|
ref={this._setInnerViewRef}
|
|
|
style={contentContainerStyle}
|
|
|
removeClippedSubviews={this.props.removeClippedSubviews}
|
|
|
collapsable={false}>
|
|
|
{this.props.children}
|
|
|
</View>;
|
|
|
{children}
|
|
|
</ScrollContentContainerViewClass>;
|
|
|
|
|
|
const alwaysBounceHorizontal =
|
|
|
this.props.alwaysBounceHorizontal !== undefined ?
|
...
|
...
|
@@ -508,23 +714,28 @@ const ScrollView = React.createClass({ |
|
|
alwaysBounceHorizontal,
|
|
|
alwaysBounceVertical,
|
|
|
style: ([baseStyle, this.props.style]: ?Array<any>),
|
|
|
onTouchStart: this.scrollResponderHandleTouchStart,
|
|
|
onTouchMove: this.scrollResponderHandleTouchMove,
|
|
|
onTouchEnd: this.scrollResponderHandleTouchEnd,
|
|
|
onScrollBeginDrag: this.scrollResponderHandleScrollBeginDrag,
|
|
|
onScrollEndDrag: this.scrollResponderHandleScrollEndDrag,
|
|
|
// Override the onContentSizeChange from props, since this event can
|
|
|
// bubble up from TextInputs
|
|
|
onContentSizeChange: null,
|
|
|
onMomentumScrollBegin: this.scrollResponderHandleMomentumScrollBegin,
|
|
|
onMomentumScrollEnd: this.scrollResponderHandleMomentumScrollEnd,
|
|
|
onStartShouldSetResponder: this.scrollResponderHandleStartShouldSetResponder,
|
|
|
onStartShouldSetResponderCapture: this.scrollResponderHandleStartShouldSetResponderCapture,
|
|
|
onScrollShouldSetResponder: this.scrollResponderHandleScrollShouldSetResponder,
|
|
|
onScroll: this._handleScroll,
|
|
|
onResponderGrant: this.scrollResponderHandleResponderGrant,
|
|
|
onResponderTerminationRequest: this.scrollResponderHandleTerminationRequest,
|
|
|
onResponderTerminate: this.scrollResponderHandleTerminate,
|
|
|
onResponderRelease: this.scrollResponderHandleResponderRelease,
|
|
|
onResponderReject: this.scrollResponderHandleResponderReject,
|
|
|
sendMomentumEvents: (this.props.onMomentumScrollBegin || this.props.onMomentumScrollEnd) ? true : false,
|
|
|
onResponderRelease: this.scrollResponderHandleResponderRelease,
|
|
|
onResponderTerminate: this.scrollResponderHandleTerminate,
|
|
|
onResponderTerminationRequest: this.scrollResponderHandleTerminationRequest,
|
|
|
onScroll: this._handleScroll,
|
|
|
onScrollBeginDrag: this.scrollResponderHandleScrollBeginDrag,
|
|
|
onScrollEndDrag: this.scrollResponderHandleScrollEndDrag,
|
|
|
onScrollShouldSetResponder: this.scrollResponderHandleScrollShouldSetResponder,
|
|
|
onStartShouldSetResponder: this.scrollResponderHandleStartShouldSetResponder,
|
|
|
onStartShouldSetResponderCapture: this.scrollResponderHandleStartShouldSetResponderCapture,
|
|
|
onTouchEnd: this.scrollResponderHandleTouchEnd,
|
|
|
onTouchMove: this.scrollResponderHandleTouchMove,
|
|
|
onTouchStart: this.scrollResponderHandleTouchStart,
|
|
|
scrollEventThrottle: hasStickyHeaders ? 1 : this.props.scrollEventThrottle,
|
|
|
sendMomentumEvents: (this.props.onMomentumScrollBegin || this.props.onMomentumScrollEnd) ?
|
|
|
true : false,
|
|
|
};
|
|
|
|
|
|
const { decelerationRate } = this.props;
|
...
|
...
|
@@ -532,28 +743,15 @@ const ScrollView = React.createClass({ |
|
|
props.decelerationRate = processDecelerationRate(decelerationRate);
|
|
|
}
|
|
|
|
|
|
let ScrollViewClass;
|
|
|
if (Platform.OS === 'ios') {
|
|
|
ScrollViewClass = RCTScrollView;
|
|
|
} else if (Platform.OS === 'android') {
|
|
|
if (this.props.horizontal) {
|
|
|
ScrollViewClass = AndroidHorizontalScrollView;
|
|
|
} else {
|
|
|
ScrollViewClass = AndroidScrollView;
|
|
|
}
|
|
|
}
|
|
|
invariant(
|
|
|
ScrollViewClass !== undefined,
|
|
|
'ScrollViewClass must not be undefined'
|
|
|
);
|
|
|
|
|
|
const refreshControl = this.props.refreshControl;
|
|
|
|
|
|
if (refreshControl) {
|
|
|
if (Platform.OS === 'ios') {
|
|
|
// On iOS the RefreshControl is a child of the ScrollView.
|
|
|
// tvOS lacks native support for RefreshControl, so don't include it in that case
|
|
|
return (
|
|
|
<ScrollViewClass {...props} ref={this._setScrollViewRef}>
|
|
|
{refreshControl}
|
|
|
{Platform.isTVOS ? null : refreshControl}
|
|
|
{contentContainer}
|
|
|
</ScrollViewClass>
|
|
|
);
|
...
|
...
|
@@ -561,6 +759,9 @@ const ScrollView = React.createClass({ |
|
|
// On Android wrap the ScrollView with a AndroidSwipeRefreshLayout.
|
|
|
// Since the ScrollView is wrapped add the style props to the
|
|
|
// AndroidSwipeRefreshLayout and use flex: 1 for the ScrollView.
|
|
|
// Note: we should only apply props.style on the wrapper
|
|
|
// however, the ScrollView still needs the baseStyle to be scrollable
|
|
|
|
|
|
return React.cloneElement(
|
|
|
refreshControl,
|
|
|
{style: props.style},
|
...
|
...
|
@@ -580,29 +781,41 @@ const ScrollView = React.createClass({ |
|
|
|
|
|
const styles = StyleSheet.create({
|
|
|
baseVertical: {
|
|
|
flex: 1,
|
|
|
flexGrow: 1,
|
|
|
flexShrink: 1,
|
|
|
flexDirection: 'column',
|
|
|
overflow: 'scroll',
|
|
|
},
|
|
|
baseHorizontal: {
|
|
|
flex: 1,
|
|
|
flexGrow: 1,
|
|
|
flexShrink: 1,
|
|
|
flexDirection: 'row',
|
|
|
overflow: 'scroll',
|
|
|
},
|
|
|
contentContainerHorizontal: {
|
|
|
flexDirection: 'row',
|
|
|
},
|
|
|
});
|
|
|
|
|
|
let nativeOnlyProps, AndroidScrollView, AndroidHorizontalScrollView, RCTScrollView;
|
|
|
let nativeOnlyProps,
|
|
|
AndroidScrollView,
|
|
|
AndroidHorizontalScrollView,
|
|
|
RCTScrollView,
|
|
|
RCTScrollContentView;
|
|
|
if (Platform.OS === 'android') {
|
|
|
nativeOnlyProps = {
|
|
|
nativeOnly: {
|
|
|
sendMomentumEvents: true,
|
|
|
}
|
|
|
};
|
|
|
AndroidScrollView = requireNativeComponent('RCTScrollView', ScrollView, nativeOnlyProps);
|
|
|
AndroidScrollView = requireNativeComponent(
|
|
|
'RCTScrollView',
|
|
|
(ScrollView: ReactClass<any>),
|
|
|
nativeOnlyProps
|
|
|
);
|
|
|
AndroidHorizontalScrollView = requireNativeComponent(
|
|
|
'AndroidHorizontalScrollView',
|
|
|
ScrollView,
|
|
|
(ScrollView: ReactClass<any>),
|
|
|
nativeOnlyProps
|
|
|
);
|
|
|
} else if (Platform.OS === 'ios') {
|
...
|
...
|
@@ -614,7 +827,12 @@ if (Platform.OS === 'android') { |
|
|
onScrollEndDrag: true,
|
|
|
}
|
|
|
};
|
|
|
RCTScrollView = requireNativeComponent('RCTScrollView', ScrollView, nativeOnlyProps);
|
|
|
RCTScrollView = requireNativeComponent(
|
|
|
'RCTScrollView',
|
|
|
(ScrollView: ReactClass<any>),
|
|
|
nativeOnlyProps,
|
|
|
);
|
|
|
RCTScrollContentView = requireNativeComponent('RCTScrollContentView', View);
|
|
|
}
|
|
|
|
|
|
module.exports = ScrollView; |
...
|
...
|
|