/* @flow */
import * as React from 'react';
import { View, ViewPagerAndroid, StyleSheet, I18nManager } from 'react-native';
import { PagerRendererPropType } from './PropTypes';
import type { PagerRendererProps } from './TypeDefinitions';
type PageScrollEvent = {
nativeEvent: {
position: number,
offset: number,
},
};
type PageScrollState = 'dragging' | 'settling' | 'idle';
type PageScrollStateEvent =
| PageScrollState
| {
nativeEvent: {
pageScrollState: PageScrollState,
},
};
type Props<T> = PagerRendererProps<T> & {
keyboardDismissMode: 'none' | 'on-drag',
};
export default class PagerAndroid<T: *> extends React.Component<Props<T>> {
static propTypes = PagerRendererPropType;
static defaultProps = {
canJumpToTab: () => true,
keyboardDismissMode: 'on-drag',
};
constructor(props: Props<T>) {
super(props);
this._currentIndex = this.props.navigationState.index;
}
componentDidUpdate(prevProps: Props<T>) {
if (
prevProps.navigationState.routes.length !==
this.props.navigationState.routes.length ||
prevProps.layout.width !== this.props.layout.width
) {
this._handlePageChange(this.props.navigationState.index, false);
} else if (
prevProps.navigationState.index !== this.props.navigationState.index
) {
this._handlePageChange(this.props.navigationState.index);
}
}
_pageChangeCallabck: any;
_viewPager: ?ViewPagerAndroid;
_isIdle: boolean = true;
_currentIndex = 0;
_getPageIndex = (index: number) =>
I18nManager.isRTL
? this.props.navigationState.routes.length - (index + 1)
: index;
_setPage = (index: number, animated = true) => {
const pager = this._viewPager;
if (pager) {
const page = this._getPageIndex(index);
if (this.props.animationEnabled === false || animated === false) {
pager.setPageWithoutAnimation(page);
} else {
pager.setPage(page);
}
}
};
_handlePageChange = (index: number, animated?: boolean) => {
if (this._isIdle && this._currentIndex !== index) {
this._setPage(index, animated);
this._currentIndex = index;
}
};
_handlePageScroll = (e: PageScrollEvent) => {
this.props.offsetX.setValue(
this._getPageIndex(e.nativeEvent.position) * this.props.layout.width * -1
);
this.props.panX.setValue(
e.nativeEvent.offset *
this.props.layout.width *
(I18nManager.isRTL ? 1 : -1)
);
};
_handlePageScrollStateChanged = (e: PageScrollStateEvent) => {
// Support both React Native < 0.59 and 0.59+
this._isIdle =
typeof e !== 'string' && e.nativeEvent
? e.nativeEvent.pageScrollState === 'idle'
: e === 'idle';
let nextIndex = this._currentIndex;
const nextRoute = this.props.navigationState.routes[nextIndex];
if (this.props.canJumpToTab({ route: nextRoute })) {
this.props.jumpTo(nextRoute.key);
} else {
this._setPage(this.props.navigationState.index);
this._currentIndex = this.props.navigationState.index;
}
switch (e) {
case 'dragging':
this.props.onSwipeStart && this.props.onSwipeStart();
break;
case 'settling':
this.props.onSwipeEnd && this.props.onSwipeEnd();
break;
case 'idle':
this.props.onAnimationEnd && this.props.onAnimationEnd();
break;
}
};
_handlePageSelected = (e: PageScrollEvent) => {
const index = this._getPageIndex(e.nativeEvent.position);
this._currentIndex = index;
};
render() {
const { navigationState, swipeEnabled, keyboardDismissMode } = this.props;
const children = I18nManager.isRTL
? React.Children.toArray(this.props.children).reverse()
: React.Children.toArray(this.props.children);
const content = children.map((child, i) => {
const route = navigationState.routes[i];
const focused = i === navigationState.index;
return (
<View
key={route.key}
testID={this.props.getTestID({ route })}
accessibilityElementsHidden={!focused}
importantForAccessibility={focused ? 'auto' : 'no-hide-descendants'}
style={styles.page}
>
{child}
</View>
);
});
const initialPage = this._getPageIndex(navigationState.index);
return (
<ViewPagerAndroid
key={navigationState.routes.length}
keyboardDismissMode={keyboardDismissMode}
initialPage={initialPage}
scrollEnabled={swipeEnabled !== false}
onPageScroll={this._handlePageScroll}
onPageScrollStateChanged={this._handlePageScrollStateChanged}
onPageSelected={this._handlePageSelected}
style={styles.container}
ref={el => (this._viewPager = el)}
>
{content}
</ViewPagerAndroid>
);
}
}
const styles = StyleSheet.create({
container: {
flexGrow: 1,
},
page: {
overflow: 'hidden',
},
});