UNPKG

react-native-screens-swiper

Version:

This lib. provide swiper view functionality with tab navigation on the top.

191 lines (178 loc) 6.32 kB
import React, {useRef, useState} from "react"; import {useWindowDimensions, FlatList, Keyboard} from "react-native"; import Container from "./Container"; import {usePrevious} from "../helpers/usePrevious"; import ItemComponent from "./ItemComponent"; import SwiperHeader from "./SwiperHeader"; /** * Swiper component * @param {Array[]} data Array of items to render * @param {?Function} renderPills Pills render customizer that allows wrapping header with custom components * @param {?Number} initialScrollIndex Initial screen to show * @param {?boolean} stickyHeaderEnabled Enable or disable sticky header for component * @param {?boolean} isStaticPills Enable or disable static pills * @param {Function} children React component used to render item * @param {?Number} stickyHeaderIndex Index of component to sticky to top * @param {?boolean} scrollableContainer Enable or disable scrollable container for component * @param {?Object} style Styles * @param {Function} onSwiperScroll function which returns event when screen swiped * @param {?Function} onActiveScreenChange function that is fired when active screen index changes * @param {?boolean} keyboardDismissOnScroll dismiss keyboard when screens scroll * @param rest */ export default function Swiper({ style, data, renderPills, isStaticPills, initialScrollIndex, stickyHeaderEnabled, children, stickyHeaderIndex, scrollableContainer, onSwiperScroll, onActiveScreenChange, keyboardDismissOnScroll, ...rest }) { const width = useWindowDimensions().width; const flatList = useRef(null); const containerRef = useRef(null); const scrollViewRef = useRef(null); const [currentIndex, setCurrentIndex] = useState(initialScrollIndex || 0); const prevIndex = usePrevious(currentIndex); const [buttonsCoords, setButtonsCoords] = useState([]); const [x, setX] = useState(0); // collect button layouts const onButtonLayout = index => e => { const layout = e?.nativeEvent?.layout; if (!buttonsCoords[index] || buttonsCoords[index].x !== e.x) setButtonsCoords(buttonsCoords => { let buttons = [...buttonsCoords]; buttons[index] = layout; return buttons; }); }; // Scroll to pressed button index const onButtonPress = index => { if (keyboardDismissOnScroll) Keyboard.dismiss(); flatList.current?.scrollToIndex({index}); }; // Counting coordinates on screen swipe const onFlatListScroll = e => { if (keyboardDismissOnScroll) Keyboard.dismiss(); // update X setX(e.nativeEvent.contentOffset.x); // update current screen index const index = Math.round(e.nativeEvent.contentOffset.x / width); if (index !== currentIndex) { setCurrentIndex(index); onActiveScreenChange?.(index); }; // scroll to next button if have to if (buttonsCoords[currentIndex] && prevIndex !== currentIndex) { const buttonLayout = buttonsCoords[currentIndex]; scrollViewRef.current?.scrollTo({ // target X is button's X + hald of button's width - hald of screen width x: buttonLayout.x + buttonLayout.width / 2 - (width / 2), y: 0, animated: true, }); } if (!!onSwiperScroll) onSwiperScroll(e); }; const getItemLayout = (data, index) => ({ length: width, offset: width * index, index, }); const pills = ( <SwiperHeader isStaticPills={isStaticPills} style={style} containerRef={containerRef} scrollViewRef={scrollViewRef} scrollableContainer={scrollableContainer} data={data} currentIndex={currentIndex} x={x} onButtonPress={onButtonPress} onButtonLayout={onButtonLayout} /> ); return ( <Container containerRef={containerRef} stickyHeaderEnabled={stickyHeaderEnabled} scrollableContainer={scrollableContainer} stickyHeaderIndex={stickyHeaderIndex} > {children} {renderPills ? renderPills(pills) : pills} <FlatList ref={flatList} showsHorizontalScrollIndicator={false} scrollEventThrottle={16} onScroll={onFlatListScroll} pagingEnabled keyExtractor={(item, index) => String(index)} getItemLayout={getItemLayout} horizontal={true} data={data} renderItem={({item: {component: Component, props = {}}, index}) => ( <ItemComponent key={index} index={index} Component={Component} {...props} /> )} snapToAlignment={'center'} style={styles.flatList} initialScrollIndex={initialScrollIndex} {...rest} /> </Container> ); }; const styles = { pillButton: { minWidth: 30, height: 32, backgroundColor: '#c8c8c8', flexDirection: 'row', justifyContent: 'center', alignItems: 'center', paddingHorizontal: 20, borderRadius: 5, marginHorizontal: 5, }, pillLabelActive: { color: 'white' }, pillActive: { backgroundColor: '#42a5f5', }, pillLabel: { marginLeft: 5, color: '#455a64' }, pillContainer: { paddingHorizontal: 5, }, staticPillContainer: { backgroundColor: 'white', paddingTop: 12, paddingBottom: 8, paddingHorizontal: 0, borderBottomWidth: 0, justifyContent: 'center', alignItems: 'center', }, flatList: { flex: 1, }, };