import React, { useCallback } from 'react'; import { forwardRef, Ref, useImperativeHandle, useRef, useState } from 'react'; import { Animated, Dimensions, Easing, StyleSheet, Text, TouchableWithoutFeedback, View, } from 'react-native'; import type { ObjExtend, Props, RefObject } from '../types/type'; const SegmentControl = forwardRef( ( { segments = [], activeTab = 0, style, activeStyle, textStyle, onPress, textActiveStyle, duration = 300, iconField, labelField = 'title', testID, iconActiveField, }: Props, ref: Ref ) => { ``; const [segmentItemWidth, setSegmentItemWidth] = useState(0); const [active, setActive] = useState(activeTab); const widthSegment = useCallback( ( segmentArr: T[], width: string | number | undefined = Dimensions.get('window').width ): { width: number } => { if (typeof width === 'number') { return { width: width / segmentArr.length, }; } else if (typeof width === 'string') { console.warn( '=>>>SegmentControl<<<= : Segment not supported for size string' ); return { width: 0, }; } else { return { width: Dimensions.get('window').width }; } }, [] ); const heightSegment = (height: string | number | undefined = 60) => { if (typeof height === 'number') { return { height: height - 6, }; } else if (typeof height === 'string') { console.warn( '=>>>SegmentControl<<<= : Segment not supported for size string' ); return { height: 0 }; } else { return { height: 60 }; } }; const fadeAnim = useRef(new Animated.Value(0)).current; useImperativeHandle(ref, () => ({ updateActive: (index: number = 0) => { if (index <= segments.length - 1) { setActive(index); translateXAnim(index); } else { console.warn( '=>>>SegmentControl<<<= : Active cannot exceed the length of the array' ); } }, })); const translateXAnim = useCallback( (index: number) => { Animated.timing(fadeAnim, { toValue: segmentItemWidth * index, easing: Easing.out(Easing.quad), duration: duration, useNativeDriver: false, }).start(); }, [duration, fadeAnim, segmentItemWidth] ); React.useEffect(() => { const { width } = widthSegment(segments, style?.width); setSegmentItemWidth(width); }, [segments, style?.width, widthSegment]); React.useEffect(() => { if (activeTab <= segments.length - 1) { translateXAnim(activeTab); setActive(activeTab); } else { console.warn( '=>>>SegmentControl<<<= : Active cannot exceed the length of the array' ); } }, [activeTab, translateXAnim, segments]); return ( {segments?.map((s, i) => { return ( { const value = s; translateXAnim(i); setActive(i); typeof onPress === 'function' && onPress(value, i); }} > {iconActiveField ? active === i ? s[iconActiveField as keyof typeof s] : s[iconField as keyof typeof s] : s[iconField as keyof typeof s]} {s[labelField as keyof typeof s]} ); })} ); } ); const styles = StyleSheet.create({ container: { backgroundColor: '#dddddd' }, viewWrap: { margin: 2 }, boxView: { flexDirection: 'row', justifyContent: 'center', alignItems: 'center', position: 'relative', elevation: 10, zIndex: 999, }, box: { flex: 1, flexDirection: 'row', alignItems: 'center', justifyContent: 'center', borderRadius: 50, }, boxAnimated: { backgroundColor: '#F1F2F6' }, button: { flexDirection: 'row', }, animation: { position: 'absolute', borderRadius: 7, top: 2, bottom: 2, right: 2, left: 2, borderWidth: 0.5, borderColor: 'rgba(0,0,0,0.04)', shadowColor: '#000', shadowOffset: { width: 0, height: 1, }, shadowOpacity: 0.22, shadowRadius: 2.22, elevation: 4, zIndex: 1, }, }); export default SegmentControl as ( props: Props & { ref?: Ref } ) => JSX.Element;