UNPKG

react-native-paper-tabs

Version:

Smooth and fast cross platform Material Design Tabs for React Native Paper

150 lines (137 loc) 4.1 kB
import * as React from 'react'; import type { AnimatedColorArgs, AnimatedTextStyle, AnimatedViewStyle, IndicatorArgs, IndicatorReturns, OffsetScrollArgs, } from './utils'; import { Animated, Platform } from 'react-native'; export function useAnimatedText({ childrenCount, tabIndex, position, offset, textColor, activeColor, }: AnimatedColorArgs): AnimatedTextStyle { const childrenA = Array(childrenCount).fill(undefined); const positionWithOffset = Animated.add(position!, offset!); const inputRange = childrenA.map((_, i) => i); return { color: childrenA.length <= 1 ? activeColor : positionWithOffset!.interpolate({ inputRange: inputRange, outputRange: childrenA.map((_, i) => i === tabIndex ? activeColor : textColor ), }), opacity: childrenA.length <= 1 ? 1 : positionWithOffset.interpolate({ inputRange: inputRange, outputRange: childrenA.map((_, i) => (i === tabIndex ? 1 : 0.6)), }), }; } export function useIndicator({ childrenCount, position, offset, layouts, tabsLayout, }: IndicatorArgs): IndicatorReturns { const [renderIndex, setRenderIndex] = React.useState(0); const style = React.useMemo<AnimatedViewStyle | null>(() => { /* eslint-disable @typescript-eslint/no-unused-vars */ // @ts-ignore let _ = renderIndex; const childrenA = Array(childrenCount).fill(undefined); const inputRange = childrenA.map((__, i) => i); const positionWithOffset = Animated.add(position!, offset!); const getTranslateX = (i: number) => { const cl = layouts.current?.[i]; if (!cl) { return 0; } return (cl.x + cl.width / 2) / cl.width; }; const getScaleX = (i: number) => { return layouts.current?.[i]?.width || 0; }; return position && tabsLayout && layouts.current ? { transform: [ { scaleX: childrenA.length <= 1 ? getScaleX(0) : positionWithOffset.interpolate({ inputRange, outputRange: childrenA.map((__, i) => getScaleX(i)), // extrapolate: 'clamp', }), }, { translateX: childrenA.length <= 1 ? getTranslateX(0) : positionWithOffset.interpolate({ inputRange, outputRange: childrenA.map((__, i) => getTranslateX(i)), // extrapolate: 'clamp', }), }, ], } : null; }, [position, offset, tabsLayout, layouts, renderIndex, childrenCount]); const onUpdateTabLayout = React.useCallback(() => { setRenderIndex((prev) => prev + 1); }, [setRenderIndex]); return [undefined, onUpdateTabLayout, style]; } export function useOffsetScroller({ index, offset, updateScroll, mode, }: OffsetScrollArgs) { // we want native to scroll before the index changes const direction = React.useRef<undefined | 'next' | 'prev'>(undefined); React.useEffect(() => { // android does not work unfortunately if ( offset !== undefined && offset !== null && Platform.OS !== 'android' && mode === 'scrollable' ) { const id = offset.addListener((nOffset) => { const newOffset = nOffset.value; const oldDirection = direction.current; if (newOffset > 0.1) { direction.current = 'next'; } else if (newOffset < -0.1) { direction.current = 'prev'; } if (direction.current) { if (oldDirection !== direction.current) { updateScroll(direction.current); } } }); return () => { offset.removeListener(id); }; } return undefined; }, [offset, updateScroll, direction, mode]); React.useEffect(() => { direction.current = undefined; }, [index]); }