UNPKG

@rn-vui/base

Version:
173 lines (172 loc) 7.92 kB
var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; import React, { useEffect } from 'react'; import { Animated, Easing, ScrollView, StyleSheet, View, } from 'react-native'; import { defaultTheme } from '../helpers'; export const TabBase = (_a) => { var _b, _c, _d; var { theme = defaultTheme, children, scrollable = false, onChange = () => { }, indicatorStyle, disableIndicator, variant = 'primary', style, dense, iconPosition, buttonStyle, titleStyle, containerStyle, value: activeIndex = 0, animationType = 'spring', animationConfig = {} } = _a, rest = __rest(_a, ["theme", "children", "scrollable", "onChange", "indicatorStyle", "disableIndicator", "variant", "style", "dense", "iconPosition", "buttonStyle", "titleStyle", "containerStyle", "value", "animationType", "animationConfig"]); const translateX = React.useRef(new Animated.Value(0)); const currentIndex = React.useRef(0); const onIndexChangeRef = React.useRef((value) => value); const setIndicatorRerenderKey = React.useState(0)[1]; const animate = React.useCallback((toValue, onDone = () => { }) => { var _a; currentIndex.current = toValue; (_a = onIndexChangeRef.current) === null || _a === void 0 ? void 0 : _a.call(onIndexChangeRef, toValue); Animated[animationType](translateX.current, Object.assign({ toValue: toValue, useNativeDriver: true, easing: Easing.inOut(Easing.ease), duration: 150 }, animationConfig)).start(() => { onDone === null || onDone === void 0 ? void 0 : onDone(toValue); }); }, [animationConfig, animationType]); const scrollViewRef = React.useRef(null); const scrollViewPosition = React.useRef(0); const validChildren = React.useMemo(() => React.Children.toArray(children), [children]); const tabItemPositions = React.useRef([]); const [tabContainerWidth, setTabContainerWidth] = React.useState(0); const scrollHandler = React.useCallback((currValue) => { var _a; if (tabItemPositions.current.length >= currValue + 1) { const itemPosition = tabItemPositions.current[currValue].position; const itemWidth = tabItemPositions.current[currValue].width; const tabContainerCurrentWidth = tabContainerWidth; const scrollX = itemPosition + itemWidth / 2 - tabContainerCurrentWidth / 2; (_a = scrollViewRef.current) === null || _a === void 0 ? void 0 : _a.scrollTo({ x: scrollX, y: 0, animated: true, }); } }, [tabContainerWidth]); useEffect(() => { if (activeIndex !== currentIndex.current && tabContainerWidth > 0 && tabItemPositions.current.length > 0) { setIndicatorRerenderKey((prev) => prev + 1); animate(activeIndex); } }, [ activeIndex, animate, scrollHandler, tabContainerWidth, setIndicatorRerenderKey, ]); React.useEffect(() => { if (onIndexChangeRef) { onIndexChangeRef.current = (changedIndex) => { scrollHandler(changedIndex); onChange(changedIndex); return changedIndex; }; } }, [onIndexChangeRef, scrollHandler, onChange]); const onScrollHandler = React.useCallback((event) => { scrollViewPosition.current = event.nativeEvent.contentOffset.x; }, []); const indicatorWidth = (_b = tabItemPositions.current[activeIndex]) === null || _b === void 0 ? void 0 : _b.width; const indicatorTranslateX = () => { const countItems = validChildren.length; if (countItems < 2 || tabItemPositions.current.length !== countItems) { return 0; } const { inputRange, outputRange } = tabItemPositions.current.reduce((prev, curr, index) => { prev.inputRange.push(index); prev.outputRange.push(curr.position + curr.width / 2 - indicatorWidth / 2); return prev; }, { inputRange: [], outputRange: [] }); return translateX.current.interpolate({ inputRange, outputRange, extrapolate: 'clamp', }); }; const indicatorScaleX = () => { const countItems = validChildren.length; if (countItems < 2 || tabItemPositions.current.length !== countItems) { return 0; } const inputRange = []; const outputRange = []; tabItemPositions.current.reduce((prev, curr, index) => { inputRange.push(index); outputRange.push(curr.width / prev.width); return prev; }, tabItemPositions.current[activeIndex]); return translateX.current.interpolate({ inputRange, outputRange, extrapolate: 'extend', }); }; return (React.createElement(View, Object.assign({}, rest, { accessible: true, accessibilityRole: "tablist", style: [ variant === 'primary' && { backgroundColor: (_c = theme === null || theme === void 0 ? void 0 : theme.colors) === null || _c === void 0 ? void 0 : _c.primary, }, styles.viewStyle, style, ], onLayout: ({ nativeEvent: { layout } }) => { setTabContainerWidth(layout.width); } }), React.createElement(scrollable ? ScrollView : React.Fragment, Object.assign(Object.assign({}, (scrollable && { horizontal: true, ref: scrollViewRef, onScroll: onScrollHandler, showsHorizontalScrollIndicator: false, })), { children: (React.createElement(React.Fragment, null, validChildren.map((child, index) => (React.createElement(View, { key: index, style: { flex: 1, flexDirection: 'column', }, onLayout: ({ nativeEvent: { layout } }) => { tabItemPositions.current[index] = { position: layout.x, width: layout.width, }; } }, React.cloneElement(child, { onPress: () => { animate(index); onChange === null || onChange === void 0 ? void 0 : onChange(index); }, active: index === activeIndex, variant, _parentProps: { dense, iconPosition, buttonStyle, containerStyle, titleStyle, }, })))), !disableIndicator && (React.createElement(Animated.View, { style: [ styles.indicator, { backgroundColor: (_d = theme === null || theme === void 0 ? void 0 : theme.colors) === null || _d === void 0 ? void 0 : _d.secondary, transform: [ { translateX: indicatorTranslateX() }, { scaleX: indicatorScaleX() }, ], width: indicatorWidth, }, indicatorStyle, ] })))) })))); }; const styles = StyleSheet.create({ viewStyle: { flexDirection: 'row', position: 'relative', }, indicator: { display: 'flex', position: 'absolute', height: 2, bottom: 0, }, }); TabBase.displayName = 'Tab';