diff --git a/Libraries/Components/ScrollView/ScrollView.windows.js b/Libraries/Components/ScrollView/ScrollView.windows.js index b6d48ffb4c8..962340447c8 100644 --- a/Libraries/Components/ScrollView/ScrollView.windows.js +++ b/Libraries/Components/ScrollView/ScrollView.windows.js @@ -44,6 +44,21 @@ const requireNativeComponent = require('requireNativeComponent'); * * Doesn't yet support other contained responders from blocking this scroll * view from becoming the responder. + * + * + * `` vs `` - 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, worth of couple of your ScrollView’s + * heights. Creating JS components and native views upfront for all its items, + * which may not even be shown, will contribute to slow rendering of your + * screen and increased memory usage. + * + * This is where ListView comes into play. ListView renders items lazily, + * just when they are about to appear. This laziness comes at cost of a more + * complicated API, which is worth it unless you are rendering a small fixed + * set of items. */ const ScrollView = React.createClass({ propTypes: { @@ -177,12 +192,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 @@ -305,7 +326,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). */ @@ -329,6 +351,24 @@ 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', + ]), + /** * Enables zoom on a scroll view for Windows. * @platform windows @@ -367,11 +407,11 @@ 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, + * Note: The weird function 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. */ @@ -389,7 +429,25 @@ const ScrollView = React.createClass({ }, /** - * Deprecated, do not use. + * 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, use `scrollTo` instead. */ scrollWithoutAnimationTo: function(y: number = 0, x: number = 0) { console.warn('`scrollWithoutAnimationTo` is deprecated. Use `scrollTo` instead'); @@ -432,6 +490,33 @@ 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; + } else if (Platform.OS === 'windows') { + ScrollViewClass = RCTScrollView; + 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, @@ -456,14 +541,14 @@ const ScrollView = React.createClass({ } const contentContainer = - {this.props.children} - ; + ; const alwaysBounceHorizontal = this.props.alwaysBounceHorizontal !== undefined ? @@ -508,23 +593,6 @@ 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; - } - } else if (Platform.OS === 'windows') { - ScrollViewClass = RCTScrollView; - } - invariant( - ScrollViewClass !== undefined, - 'ScrollViewClass must not be undefined' - ); - const refreshControl = this.props.refreshControl; if (refreshControl) { if (Platform.OS === 'ios') { @@ -539,10 +607,13 @@ 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}, - + {contentContainer} ); @@ -558,12 +629,14 @@ 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', }, @@ -572,7 +645,7 @@ const styles = StyleSheet.create({ }, }); -let nativeOnlyProps, AndroidScrollView, AndroidHorizontalScrollView, RCTScrollView; +let nativeOnlyProps, AndroidScrollView, AndroidHorizontalScrollView, RCTScrollView, RCTScrollContentView; if (Platform.OS === 'android') { nativeOnlyProps = { nativeOnly: { @@ -595,8 +668,9 @@ if (Platform.OS === 'android') { } }; RCTScrollView = requireNativeComponent('RCTScrollView', ScrollView, nativeOnlyProps); + RCTScrollContentView = requireNativeComponent('RCTScrollContentView', View); } else if (Platform.OS === 'windows') { - RCTScrollView = requireNativeComponent('RCTScrollView', ScrollView); + RCTScrollView = requireNativeComponent('RCTScrollView', ScrollView); } -module.exports = ScrollView; \ No newline at end of file +module.exports = ScrollView;