UNPKG

4.86 kBJavaScriptView Raw
1import React, { Component } from 'react';
2import { Dimensions, Platform, ScrollView, View, } from 'react-native';
3export class PageSlider extends Component {
4 constructor(props) {
5 super(props);
6 this.offsetX = 0;
7 this.hasDoneInitialScroll = false;
8 this.scrollView = null;
9 this.onContentSizeChange = (width, height) => {
10 if (Platform.OS === 'android' &&
11 width &&
12 height &&
13 this.initialSelectedPage &&
14 !this.hasDoneInitialScroll) {
15 this.scrollToPage(this.initialSelectedPage, false);
16 this.hasDoneInitialScroll = true;
17 }
18 };
19 this.onScroll = (e) => {
20 this.offsetX = e.nativeEvent.contentOffset.x;
21 const currentPage = this.getCurrentPage();
22 this.props.onCurrentPageChange(currentPage);
23 };
24 this.onMomentumScrollEnd = () => {
25 const currentPage = this.getCurrentPage();
26 if (this.props.selectedPage !== currentPage) {
27 this.props.onSelectedPageChange(currentPage);
28 }
29 };
30 // Calculates page's width according to selected mode
31 this.getPageWidth = () => {
32 const { width } = Dimensions.get('screen');
33 if (this.props.mode === 'page') {
34 return width;
35 }
36 const { peek, pageMargin } = this.props;
37 return width - 2 * peek + 2 * pageMargin;
38 };
39 // Get currently visible page
40 this.getCurrentPage = () => {
41 const pageWidth = this.getPageWidth();
42 return Math.floor(this.offsetX / pageWidth - 0.5) + 1;
43 };
44 // Scroll to page by index
45 this.scrollToPage = (index, animated = true) => {
46 var _a;
47 const pageWidth = this.getPageWidth();
48 (_a = this.scrollView) === null || _a === void 0 ? void 0 : _a.scrollTo({ y: 0, x: index * pageWidth, animated });
49 };
50 // Android scrollView.scrollTo on component mount workaround
51 this.initialSelectedPage = this.props.selectedPage;
52 }
53 componentDidMount() {
54 if (Platform.OS === 'ios' && this.props.selectedPage) {
55 // Doesn't work in Android
56 this.scrollToPage(this.props.selectedPage, false);
57 }
58 }
59 componentDidUpdate(prevProps) {
60 const currentPage = this.getCurrentPage();
61 if (prevProps.selectedPage !== this.props.selectedPage &&
62 this.props.selectedPage !== currentPage &&
63 this.props.selectedPage !== undefined) {
64 this.scrollToPage(this.props.selectedPage);
65 }
66 }
67 render() {
68 const { children, mode, style } = this.props;
69 const { width } = Dimensions.get('screen');
70 const pageStyle = {
71 height: '100%',
72 };
73 let scrollViewProps = {};
74 // Setup pages and ScrollView according to selected mode
75 if (mode === 'page') {
76 Object.assign(pageStyle, {
77 width,
78 });
79 }
80 else if (mode === 'card') {
81 const { contentPaddingVertical, peek, pageMargin } = this.props;
82 scrollViewProps = {
83 contentContainerStyle: {
84 paddingHorizontal: peek - pageMargin,
85 paddingVertical: contentPaddingVertical,
86 },
87 decelerationRate: 'fast',
88 snapToAlignment: 'start',
89 snapToInterval: this.getPageWidth(),
90 };
91 if (Platform.OS === 'ios') {
92 /**
93 * pagingEnabled must be enabled on Android but cause warning on iOS
94 * when snapToInterval is set
95 */
96 scrollViewProps.pagingEnabled = false;
97 }
98 Object.assign(pageStyle, {
99 marginHorizontal: pageMargin,
100 width: width - 2 * peek,
101 });
102 }
103 // Wrap children
104 const pages = React.Children.map(children, (page) => {
105 // Skip no valid react elements (null, boolean, undefined and etc.)
106 if (!React.isValidElement(page)) {
107 return null;
108 }
109 return (<View key={page.key} style={pageStyle}>
110 {page}
111 </View>);
112 });
113 return (<ScrollView style={style} ref={(scrollView) => {
114 this.scrollView = scrollView;
115 }} horizontal pagingEnabled showsHorizontalScrollIndicator={false} onContentSizeChange={this.onContentSizeChange} onScroll={this.onScroll} onMomentumScrollEnd={this.onMomentumScrollEnd} scrollEventThrottle={8} {...scrollViewProps}>
116 {pages}
117 </ScrollView>);
118 }
119}
120PageSlider.defaultProps = {
121 mode: 'page',
122 pageMargin: 8,
123 peek: 24,
124};