Files
react-native-web/src/components/ScrollView/ScrollViewBase.js
Nicolas Gallagher bcdeda5dab [fix] Flow type checking and annotations
Fixes dozens of Flow errors; adds type annotations; marks more files for
Flow type checking. Fixes a bug in 'AppState'.

15 Flow errors remaining. Several React Native files are still not type
checked (e.g., PanResponder, Touchables)

Ref #465
2017-05-27 10:44:33 -07:00

154 lines
3.7 KiB
JavaScript

/**
* Copyright (c) 2016-present, Nicolas Gallagher.
* Copyright (c) 2015-present, Facebook, Inc.
* All rights reserved.
*
* @flow
*/
import debounce from 'debounce';
import View from '../View';
import ViewPropTypes from '../View/ViewPropTypes';
import React, { Component } from 'react';
import { bool, func, number } from 'prop-types';
const normalizeScrollEvent = e => ({
nativeEvent: {
contentOffset: {
get x() {
return e.target.scrollLeft;
},
get y() {
return e.target.scrollTop;
}
},
contentSize: {
get height() {
return e.target.scrollHeight;
},
get width() {
return e.target.scrollWidth;
}
},
layoutMeasurement: {
get height() {
return e.target.offsetHeight;
},
get width() {
return e.target.offsetWidth;
}
}
},
timeStamp: Date.now()
});
/**
* Encapsulates the Web-specific scroll throttling and disabling logic
*/
export default class ScrollViewBase extends Component {
static propTypes = {
...ViewPropTypes,
onMomentumScrollBegin: func,
onMomentumScrollEnd: func,
onScroll: func,
onScrollBeginDrag: func,
onScrollEndDrag: func,
onTouchMove: func,
onWheel: func,
removeClippedSubviews: bool,
scrollEnabled: bool,
scrollEventThrottle: number,
showsHorizontalScrollIndicator: bool,
showsVerticalScrollIndicator: bool
};
static defaultProps = {
scrollEnabled: true,
scrollEventThrottle: 0
};
_debouncedOnScrollEnd = debounce(this._handleScrollEnd, 100);
_state = { isScrolling: false, scrollLastTick: 0 };
_handlePreventableScrollEvent = (handler: Function) => {
return (e: Object) => {
if (!this.props.scrollEnabled) {
e.preventDefault();
} else {
if (handler) {
handler(e);
}
}
};
};
_handleScroll = (e: SyntheticEvent) => {
e.persist();
e.stopPropagation();
const { scrollEventThrottle } = this.props;
// A scroll happened, so the scroll bumps the debounce.
this._debouncedOnScrollEnd(e);
if (this._state.isScrolling) {
// Scroll last tick may have changed, check if we need to notify
if (this._shouldEmitScrollEvent(this._state.scrollLastTick, scrollEventThrottle)) {
this._handleScrollTick(e);
}
} else {
// Weren't scrolling, so we must have just started
this._handleScrollStart(e);
}
};
_handleScrollStart(e: Object) {
this._state.isScrolling = true;
this._state.scrollLastTick = Date.now();
}
_handleScrollTick(e: Object) {
const { onScroll } = this.props;
this._state.scrollLastTick = Date.now();
if (onScroll) {
onScroll(normalizeScrollEvent(e));
}
}
_handleScrollEnd(e: Object) {
const { onScroll } = this.props;
this._state.isScrolling = false;
if (onScroll) {
onScroll(normalizeScrollEvent(e));
}
}
_shouldEmitScrollEvent(lastTick: number, eventThrottle: number) {
const timeSinceLastTick = Date.now() - lastTick;
return eventThrottle > 0 && timeSinceLastTick >= eventThrottle;
}
render() {
const {
/* eslint-disable */
onMomentumScrollBegin,
onMomentumScrollEnd,
onScrollBeginDrag,
onScrollEndDrag,
removeClippedSubviews,
scrollEnabled,
scrollEventThrottle,
showsHorizontalScrollIndicator,
showsVerticalScrollIndicator,
/* eslint-enable */
...other
} = this.props;
return (
<View
{...other}
onScroll={this._handleScroll}
onTouchMove={this._handlePreventableScrollEvent(this.props.onTouchMove)}
onWheel={this._handlePreventableScrollEvent(this.props.onWheel)}
/>
);
}
}