UNPKG

react-native-ui-lib

Version:

<p align="center"> <img src="https://user-images.githubusercontent.com/1780255/105469025-56759000-5ca0-11eb-993d-3568c1fd54f4.png" height="250px" style="display:block"/> </p> <p align="center">UI Toolset & Components Library for React Native</p> <p a

113 lines (101 loc) 3.9 kB
// TODO: support commented props import React, { useMemo, useEffect, useState, useCallback } from 'react'; import _ from 'lodash'; import { useAnimatedReaction, useSharedValue, withTiming, runOnJS } from 'react-native-reanimated'; import { useOrientation } from "../../hooks"; import { Constants, asBaseComponent } from "../../commons/new"; import { LogService } from "../../services"; import TabBarContext from "./TabBarContext"; import TabBar from "./TabBar"; import TabBarItem, { TabControllerItemProps } from "./TabBarItem"; import TabPage from "./TabPage"; import PageCarousel from "./PageCarousel"; export { TabControllerItemProps }; // TODO: should migrate selectedIndex to initialIndex (and make this prop uncontrolled) /** * @description: A performant solution for a tab controller with lazy load mechanism * @example: https://github.com/wix/react-native-ui-lib/blob/master/demo/src/screens/componentScreens/TabControllerScreen/index.tsx * @notes: This component is based on react-native-gesture-handler * @important: On Android, if using react-native-navigation, make sure to wrap your screen with gestureHandlerRootHOC * @importantLink: https://kmagiera.github.io/react-native-gesture-handler/docs/getting-started.html#with-wix-react-native-navigation-https-githubcom-wix-react-native-navigation */ function TabController({ initialIndex = 0, selectedIndex, asCarousel = false, items, onChangeIndex = _.noop, carouselPageWidth, children }) { const [screenWidth, setScreenWidth] = useState(Constants.windowWidth); if (items?.length < 2) { console.warn('TabController component expect a minimum of 2 items'); } useOrientation({ onOrientationChange: () => { setScreenWidth(Constants.windowWidth); } }); const pageWidth = useMemo(() => { return carouselPageWidth || screenWidth; }, [carouselPageWidth, screenWidth]); const ignoredItems = useMemo(() => { return _.filter(items, item => item.ignore); }, [items]); /* backwards compatibility for `selectedIndex` prop. this line eventually should be removed */ initialIndex = selectedIndex || initialIndex; /* currentPage - static page index */ const currentPage = useSharedValue(initialIndex); /* targetPage - transitioned page index (can be a fraction when transitioning between pages) */ const targetPage = useSharedValue(initialIndex); // const carouselOffset = useSharedValue(initialIndex * Math.round(pageWidth)); const setCurrentIndex = useCallback(index => { 'worklet'; currentPage.value = index; }, []); useEffect(() => { if (!_.isUndefined(selectedIndex)) { LogService.deprecationWarn({ component: 'TabController', oldProp: 'selectedIndex', newProp: 'initialIndex' }); } }, [selectedIndex]); useEffect(() => { setCurrentIndex(initialIndex); }, [initialIndex]); useAnimatedReaction(() => { return currentPage.value; }, (value, prevValue) => { if (value !== prevValue) { targetPage.value = withTiming(value); prevValue !== null && runOnJS(onChangeIndex)(value, prevValue); } }); const context = useMemo(() => { return { /* Pass Props */ initialIndex, asCarousel, pageWidth, /* Items */ items, ignoredItems, itemsCount: items.length - ignoredItems.length, /* Animated Values */ targetPage, currentPage, // carouselOffset, containerWidth: screenWidth, /* Callbacks */ onChangeIndex, setCurrentIndex }; }, [initialIndex, asCarousel, items, onChangeIndex, screenWidth]); return <TabBarContext.Provider value={context}>{children}</TabBarContext.Provider>; } TabController.TabBar = TabBar; TabController.TabBarItem = TabBarItem; TabController.TabPage = TabPage; TabController.PageCarousel = PageCarousel; export default asBaseComponent(TabController);