diff --git a/index.d.ts b/index.d.ts index 075ae3dc..bd36f7cc 100644 --- a/index.d.ts +++ b/index.d.ts @@ -1,69 +1,81 @@ -declare module 'rn-viewpager' { +declare module "rn-viewpager" { + import * as React from "react"; + import { + ImageURISource, + ViewProperties, + NativeScrollEvent, + NativeSyntheticEvent + } from "react-native"; + import { ViewPagerAndroidOnPageScrollEventData } from "@react-native-community/viewpager"; - import * as React from 'react'; - import { ImageURISource, ViewProperties, NativeScrollEvent, NativeSyntheticEvent, ViewPagerAndroidOnPageScrollEventData } from 'react-native'; - - interface ViewPagerProps extends ViewProperties { - initialPage?: number; - keyboardDismissMode?: 'none' | 'on-drag'; - onPageScroll?(e: ViewPagerAndroidOnPageScrollEventData): void; - onPageScrollStateChanged?(state: 'idle' | 'settling' | 'dragging'): void; - onPageSelected?(e: ViewPagerAndroidOnPageScrollEventData): void; - scrollEnabled?: boolean; - } - - export class ViewPager extends React.Component { - setPage(selectedPage: number): void; - setPageWithoutAnimation(selectedPage: number): void; - } - - interface IndicatorViewPagerProps extends ViewProperties { - indicator: React.ReactNode; - pagerStyle?: ViewProperties['style']; - autoPlayEnable?: boolean; - autoPlayInterval?: boolean; - horizontalScroll?: boolean; + interface ViewPagerProps extends ViewProperties { + initialPage?: number; + keyboardDismissMode?: "none" | "on-drag"; + onPageScroll?(e: ViewPagerAndroidOnPageScrollEventData): void; + onPageScrollStateChanged?(state: "idle" | "settling" | "dragging"): void; + onPageSelected?(e: ViewPagerAndroidOnPageScrollEventData): void; + scrollEnabled?: boolean; + } - } - export class IndicatorViewPager extends React.Component { - setPage(selectedPage: number): void; - setPageWithoutAnimation(selectedPage: number): void; - } + export class ViewPager extends React.Component { + setPage(selectedPage: number): void; + setPageWithoutAnimation(selectedPage: number): void; + } - interface PagerDotIndicatorProps extends ViewProperties { - pageCount: number; - dotStyle?: ViewProperties['style']; - selectedDotStyle?: ViewProperties['style']; - hideSingle?: boolean; - } + interface IndicatorViewPagerProps extends ViewProperties { + indicator: React.ReactNode; + pagerStyle?: ViewProperties["style"]; + autoPlayEnable?: boolean; + autoPlayInterval?: boolean; + horizontalScroll?: boolean; + } + export class IndicatorViewPager extends React.Component< + IndicatorViewPagerProps + > { + setPage(selectedPage: number): void; + setPageWithoutAnimation(selectedPage: number): void; + } - export class PagerDotIndicator extends React.Component {} + interface PagerDotIndicatorProps extends ViewProperties { + pageCount: number; + dotStyle?: ViewProperties["style"]; + selectedDotStyle?: ViewProperties["style"]; + hideSingle?: boolean; + } - interface PageTitleIndicatorProps extends ViewProperties { - titles: string[]; - trackScroll?: boolean; - itemStyle?: ViewProperties['style']; - itemTextStyle?: ViewProperties['style']; - selectedItemTextStyle?: ViewProperties['style']; - selectedBorderStyle?: ViewProperties['style']; - renderTitle(index: number, title: string, isSelected: boolean): JSX.Element; - } - export class PagerTitleIndicator extends React.Component {} + export class PagerDotIndicator extends React.Component< + PagerDotIndicatorProps + > {} - interface PagerTabIndicatorProps extends ViewProperties { - tabs: Array<{ - text: string, - iconSource: ImageURISource, - selectedIconSource: ImageURISource - }>; - itemStyle?: ViewProperties['style']; - selectedItemStyle?: ViewProperties['style']; - iconStyle?: ViewProperties['style']; - selectedIconStyle?: ViewProperties['style']; - textStyle?: ViewProperties['style']; - selectedTextStyle?: ViewProperties['style']; - changePageWithAnimation?: boolean; - } + interface PageTitleIndicatorProps extends ViewProperties { + titles: string[]; + trackScroll?: boolean; + itemStyle?: ViewProperties["style"]; + itemTextStyle?: ViewProperties["style"]; + selectedItemTextStyle?: ViewProperties["style"]; + selectedBorderStyle?: ViewProperties["style"]; + renderTitle(index: number, title: string, isSelected: boolean): JSX.Element; + } + export class PagerTitleIndicator extends React.Component< + PageTitleIndicatorProps + > {} - export class PagerTabIndicator extends React.PureComponent {} + interface PagerTabIndicatorProps extends ViewProperties { + tabs: Array<{ + text: string; + iconSource: ImageURISource; + selectedIconSource: ImageURISource; + }>; + itemStyle?: ViewProperties["style"]; + selectedItemStyle?: ViewProperties["style"]; + iconStyle?: ViewProperties["style"]; + selectedIconStyle?: ViewProperties["style"]; + textStyle?: ViewProperties["style"]; + selectedTextStyle?: ViewProperties["style"]; + changePageWithAnimation?: boolean; } + + export class PagerTabIndicator extends React.PureComponent< + PagerTabIndicatorProps + > {} +} diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..8f9262d9 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,13 @@ +{ + "name": "rn-viewpager", + "version": "1.2.9", + "lockfileVersion": 1, + "requires": true, + "dependencies": { + "@react-native-community/viewpager": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/@react-native-community/viewpager/-/viewpager-1.1.7.tgz", + "integrity": "sha512-k9v2KJtAprNPq7IZmedD2VLMePvPW+ohX3uDnkpoKritBji+/RtRmTKrdtPi3Uvp0toq/KtPttAds1dr7AZNpw==" + } + } +} diff --git a/package.json b/package.json index 4119d541..c1a84554 100644 --- a/package.json +++ b/package.json @@ -32,5 +32,8 @@ "bugs": { "url": "https://github.com/zbtang/React-Native-ViewPager/issues" }, - "homepage": "https://github.com/zbtang/React-Native-ViewPager#readme" + "homepage": "https://github.com/zbtang/React-Native-ViewPager#readme", + "dependencies": { + "@react-native-community/viewpager": "^1.1.7" + } } diff --git a/viewpager/ViewPager.js b/viewpager/ViewPager.js index 732db756..34154ff2 100644 --- a/viewpager/ViewPager.js +++ b/viewpager/ViewPager.js @@ -2,183 +2,229 @@ * Created by tangzhibin on 16/2/28. */ -'use strict' +"use strict"; -import { PanResponder, Platform, ScrollView, StyleSheet, View, ViewPagerAndroid } from 'react-native' -import React, { Component } from 'react' +import { + PanResponder, + Platform, + ScrollView, + StyleSheet, + View +} from "react-native"; +import ViewPagerAndroid from "react-native-viewpager"; +import React, { Component } from "react"; -const SCROLLVIEW_REF = 'scrollView' -const VIEWPAGER_REF = 'viewPager' +const SCROLLVIEW_REF = "scrollView"; +const VIEWPAGER_REF = "viewPager"; const SCROLL_STATE = { - idle: 'idle', - settling: 'settling', - dragging: 'dragging' -} + idle: "idle", + settling: "settling", + dragging: "dragging" +}; export default class ViewPager extends Component { - static propTypes = {...ViewPagerAndroid.propTypes} - - static defaultProps = { - initialPage: 0, - keyboardDismissMode: 'on-drag', - onPageScroll: null, - onPageSelected: null, - onPageScrollStateChanged: null, - pageMargin: 0, - horizontalScroll: true + static propTypes = { ...ViewPagerAndroid.propTypes }; + + static defaultProps = { + initialPage: 0, + keyboardDismissMode: "on-drag", + onPageScroll: null, + onPageSelected: null, + onPageScrollStateChanged: null, + pageMargin: 0, + horizontalScroll: true + }; + + _scrollState = SCROLL_STATE.idle; + + _preScrollX = null; + + _panResponder = PanResponder.create({ + onStartShouldSetPanResponder: () => true, + onMoveShouldSetPanResponder: () => true, + onPanResponderGrant: () => this._setScrollState(SCROLL_STATE.dragging), + onPanResponderMove: () => null, + onPanResponderRelease: () => this._setScrollState(SCROLL_STATE.settling), + onPanResponderTerminate: () => null, + onPanResponderTerminationRequest: (evt, gestureState) => true + }); + + constructor(props) { + super(props); + this._onPageScrollOnAndroid = this._onPageScrollOnAndroid.bind(this); + this._onPageSelectedOnAndroid = this._onPageSelectedOnAndroid.bind(this); + this._renderOnIOS = this._renderOnIOS.bind(this); + this._onScrollOnIOS = this._onScrollOnIOS.bind(this); + this._onScrollViewLayout = this._onScrollViewLayout.bind(this); + this._childrenWithOverridenStyle = this._childrenWithOverridenStyle.bind( + this + ); + this._setScrollState = this._setScrollState.bind(this); + this.setPageWithoutAnimation = this.setPageWithoutAnimation.bind(this); + this.setPage = this.setPage.bind(this); + this.state = { width: 0, height: 0, page: props.initialPage }; + } + + render() { + return this.props.forceScrollView || Platform.OS === "ios" ? ( + this._renderOnIOS() + ) : ( + + ); + } + + _onPageScrollOnAndroid(e) { + if (this.props.onPageScroll) this.props.onPageScroll(e.nativeEvent); + } + + _onPageSelectedOnAndroid(e) { + if (this.props.onPageSelected) this.props.onPageSelected(e.nativeEvent); + } + + _renderOnIOS() { + let childrenCount = this.props.children ? this.props.children.length : 0; + let initialPage = Math.min( + Math.max(0, this.props.initialPage), + childrenCount - 1 + ); + let needMonitorScroll = + !!this.props.onPageScroll || + !!this.props.onPageSelected || + !!this.props.onPageScrollStateChanged; + let needMonitorTouch = !!this.props.onPageScrollStateChanged; + let props = { + ...this.props, + ref: SCROLLVIEW_REF, + onLayout: this._onScrollViewLayout, + horizontal: true, + pagingEnabled: this.props.horizontalScroll ? true : false, + scrollEnabled: this.props.horizontalScroll ? true : false, + scrollsToTop: false, + showsHorizontalScrollIndicator: false, + showsVerticalScrollIndicator: false, + children: this._childrenWithOverridenStyle(), + contentOffset: { x: this.state.width * initialPage, y: 0 }, + decelerationRate: 0.9, + onScroll: needMonitorScroll ? this._onScrollOnIOS : null, + scrollEventThrottle: needMonitorScroll + ? this.props.onPageScroll + ? 8 + : 1 + : 0 + }; + if (needMonitorTouch) + props = Object.assign(props, this._panResponder.panHandlers); + const scrollViewStyle = { + overflow: "visible", + marginHorizontal: -this.props.pageMargin / 2 + }; + if (this.props.style && !this.props.style.height) + return ( + + ); + else + return ( + + + + ); + } + + _onScrollOnIOS(e) { + let { x } = e.nativeEvent.contentOffset, + offset, + position = Math.floor(x / this.state.width); + if (x === this._preScrollX) return; + this._preScrollX = x; + offset = x / this.state.width - position; + + if (this.props.onPageScroll) this.props.onPageScroll({ offset, position }); + + if (this.props.onPageSelected && offset === 0) { + this.props.onPageSelected({ position }); + this.props.onPageScrollStateChanged && + this._setScrollState(SCROLL_STATE.idle); + this.setState({ page: position }); } - - - _scrollState = SCROLL_STATE.idle - - _preScrollX = null - - _panResponder = PanResponder.create({ - onStartShouldSetPanResponder: () => true, - onMoveShouldSetPanResponder: () => true, - onPanResponderGrant: () => this._setScrollState(SCROLL_STATE.dragging), - onPanResponderMove: () => null, - onPanResponderRelease: () => this._setScrollState(SCROLL_STATE.settling), - onPanResponderTerminate: () => null, - onPanResponderTerminationRequest: (evt, gestureState) => true - }) - - constructor (props) { - super(props) - this._onPageScrollOnAndroid = this._onPageScrollOnAndroid.bind(this) - this._onPageSelectedOnAndroid = this._onPageSelectedOnAndroid.bind(this) - this._renderOnIOS = this._renderOnIOS.bind(this) - this._onScrollOnIOS = this._onScrollOnIOS.bind(this) - this._onScrollViewLayout = this._onScrollViewLayout.bind(this) - this._childrenWithOverridenStyle = this._childrenWithOverridenStyle.bind(this) - this._setScrollState = this._setScrollState.bind(this) - this.setPageWithoutAnimation = this.setPageWithoutAnimation.bind(this) - this.setPage = this.setPage.bind(this) - this.state = {width: 0, height: 0, page: props.initialPage} + } + + _onScrollViewLayout(event) { + let { width, height } = event.nativeEvent.layout; + this.setState( + { width, height }, + () => + Platform.OS === "ios" && this.setPageWithoutAnimation(this.state.page) + ); + } + + _childrenWithOverridenStyle() { + if (this.state.width === 0 || this.state.height === 0) return null; + return React.Children.map(this.props.children, child => { + if (!child) return null; + let newProps = { + ...child.props, + style: [ + child.props.style, + { + width: this.state.width, + height: this.state.height, + position: null + } + ], + collapsable: false + }; + if ( + child.type && + child.type.displayName && + child.type.displayName !== "RCTView" && + child.type.displayName !== "View" + ) { + console.warn( + "Each ViewPager child must be a . Was " + child.type.displayName + ); + } + return React.createElement(child.type, newProps); + }); + } + + _setScrollState(scrollState) { + if (scrollState === this._scrollState) return; + this.props.onPageScrollStateChanged && + this.props.onPageScrollStateChanged(scrollState); + this._scrollState = scrollState; + } + + setPageWithoutAnimation(selectedPage) { + this.setState({ page: selectedPage }); + if (this.props.forceScrollView || Platform.OS === "ios") + this.refs[SCROLLVIEW_REF].scrollTo({ + x: this.state.width * selectedPage, + animated: false + }); + else { + this.refs[VIEWPAGER_REF].setPageWithoutAnimation(selectedPage); + if (this.props.onPageSelected) + this.props.onPageSelected({ position: selectedPage }); } - - render () { - return (this.props.forceScrollView || Platform.OS === 'ios') ? this._renderOnIOS() : ( - - ) + } + + setPage(selectedPage) { + this.setState({ page: selectedPage }); + if (this.props.forceScrollView || Platform.OS === "ios") + this.refs[SCROLLVIEW_REF].scrollTo({ + x: this.state.width * selectedPage + }); + else { + this.refs[VIEWPAGER_REF].setPage(selectedPage); + if (this.props.onPageSelected) + this.props.onPageSelected({ position: selectedPage }); } - - _onPageScrollOnAndroid (e) { - if (this.props.onPageScroll) this.props.onPageScroll(e.nativeEvent) - } - - _onPageSelectedOnAndroid (e) { - if (this.props.onPageSelected) this.props.onPageSelected(e.nativeEvent) - } - - _renderOnIOS () { - let childrenCount = this.props.children ? this.props.children.length : 0 - let initialPage = Math.min(Math.max(0, this.props.initialPage), childrenCount - 1) - let needMonitorScroll = !!this.props.onPageScroll || !!this.props.onPageSelected || !!this.props.onPageScrollStateChanged - let needMonitorTouch = !!this.props.onPageScrollStateChanged - let props = { - ...this.props, - ref: SCROLLVIEW_REF, - onLayout: this._onScrollViewLayout, - horizontal: true, - pagingEnabled: this.props.horizontalScroll ? true : false, - scrollEnabled: this.props.horizontalScroll ? true : false, - scrollsToTop: false, - showsHorizontalScrollIndicator: false, - showsVerticalScrollIndicator: false, - children: this._childrenWithOverridenStyle(), - contentOffset: {x: this.state.width * initialPage, y: 0}, - decelerationRate: 0.9, - onScroll: needMonitorScroll ? this._onScrollOnIOS : null, - scrollEventThrottle: needMonitorScroll ? ( this.props.onPageScroll ? 8 : 1) : 0 - } - if (needMonitorTouch) props = Object.assign(props, this._panResponder.panHandlers) - const scrollViewStyle = { - overflow: 'visible', - marginHorizontal: -this.props.pageMargin / 2 - } - if (this.props.style && !this.props.style.height) - return - else return ( - - - - ) - } - - _onScrollOnIOS (e) { - let {x} = e.nativeEvent.contentOffset, offset, position = Math.floor(x / this.state.width) - if (x === this._preScrollX) return - this._preScrollX = x - offset = x / this.state.width - position - - if (this.props.onPageScroll) this.props.onPageScroll({offset, position}) - - if (this.props.onPageSelected && offset === 0) { - this.props.onPageSelected({position}) - this.props.onPageScrollStateChanged && this._setScrollState(SCROLL_STATE.idle) - this.setState({page: position}) - } - } - - _onScrollViewLayout (event) { - let {width, height} = event.nativeEvent.layout - this.setState({width, height}, () => Platform.OS === 'ios' && this.setPageWithoutAnimation(this.state.page)) - } - - _childrenWithOverridenStyle () { - if (this.state.width === 0 || this.state.height === 0) return null - return React.Children.map(this.props.children, (child) => { - if (!child)return null - let newProps = { - ...child.props, - style: [child.props.style, { - width: this.state.width, - height: this.state.height, - position: null - }], - collapsable: false - } - if (child.type && - child.type.displayName && - (child.type.displayName !== 'RCTView') && - (child.type.displayName !== 'View')) { - console.warn('Each ViewPager child must be a . Was ' + child.type.displayName) - } - return React.createElement(child.type, newProps) - }) - } - - _setScrollState (scrollState) { - if (scrollState === this._scrollState) return - this.props.onPageScrollStateChanged && this.props.onPageScrollStateChanged(scrollState) - this._scrollState = scrollState - } - - setPageWithoutAnimation (selectedPage) { - this.setState({page: selectedPage}) - if (this.props.forceScrollView || Platform.OS === 'ios') - this.refs[SCROLLVIEW_REF].scrollTo({x: this.state.width * selectedPage, animated: false}) - else { - this.refs[VIEWPAGER_REF].setPageWithoutAnimation(selectedPage) - if (this.props.onPageSelected) this.props.onPageSelected({position: selectedPage}) - } - } - - setPage (selectedPage) { - this.setState({page: selectedPage}) - if (this.props.forceScrollView || Platform.OS === 'ios') this.refs[SCROLLVIEW_REF].scrollTo({x: this.state.width * selectedPage}) - else { - this.refs[VIEWPAGER_REF].setPage(selectedPage) - if (this.props.onPageSelected) this.props.onPageSelected({position: selectedPage}) - } - } - + } }