9
9
TextStyle ,
10
10
I18nManager ,
11
11
LayoutChangeEvent ,
12
+ LayoutRectangle ,
12
13
} from "react-native" ;
13
14
import styles from "./SegmentedControl.style" ;
14
15
@@ -19,7 +20,7 @@ interface SegmentedControlProps {
19
20
activeTabColor ?: string ;
20
21
gap ?: number ;
21
22
style ?: StyleProp < ViewStyle > ;
22
- tabStyle ?: StyleProp < ViewStyle > ;
23
+ tabStyle ?: StyleProp < ViewStyle > | ( ( index : number ) => StyleProp < ViewStyle > ) ;
23
24
textStyle ?: StyleProp < TextStyle > ;
24
25
selectedTabStyle ?: StyleProp < ViewStyle > ;
25
26
onChange : ( index : number ) => void ;
@@ -40,10 +41,11 @@ const SegmentedControl: React.FC<SegmentedControlProps> = ({
40
41
activeTabColor = "#fff" ,
41
42
} ) => {
42
43
const [ slideAnimation , _ ] = useState ( new Animated . Value ( 0 ) ) ;
43
- const [ width , setWidth ] = useState < number > ( 0 ) ;
44
- const translateValue = width / tabs . length ;
45
- const tabWidth = Math . max ( width / tabs . length - gap * 2 , 0 ) ;
46
44
const [ localCurrentIndex , setCurrentIndex ] = useState < number > ( initialIndex ) ;
45
+ const [ tabLayouts , setTabLayouts ] = useState < {
46
+ [ tabIndex : number ] : LayoutRectangle ;
47
+ } > ( { } ) ;
48
+
47
49
const currentIndex = value ?? localCurrentIndex ;
48
50
49
51
const handleTabPress = useCallback (
@@ -54,33 +56,50 @@ const SegmentedControl: React.FC<SegmentedControlProps> = ({
54
56
[ onChange ] ,
55
57
) ;
56
58
57
- const handleLayout = useCallback (
58
- ( { nativeEvent } : LayoutChangeEvent ) => {
59
- setWidth ( nativeEvent . layout . width ) ;
60
- } ,
61
- [ setWidth ] ,
62
- ) ;
63
-
64
59
useEffect ( ( ) => {
65
60
Animated . spring ( slideAnimation , {
66
- toValue : ( I18nManager . isRTL ? - 1 : 1 ) * currentIndex * translateValue ,
61
+ toValue :
62
+ ( I18nManager . isRTL ? - 1 : 1 ) * ( tabLayouts [ currentIndex ] ?. x || 0 ) ,
67
63
stiffness : 180 ,
68
64
damping : 25 ,
69
65
mass : 1 ,
70
66
useNativeDriver : true ,
71
67
} ) . start ( ) ;
72
- } , [ currentIndex , slideAnimation , translateValue ] ) ;
68
+ } , [ currentIndex , slideAnimation , tabLayouts ] ) ;
69
+
70
+ const onLayoutTab = useCallback (
71
+ ( index : number , { nativeEvent } : LayoutChangeEvent ) => {
72
+ setTabLayouts ( ( prev ) => ( { ...prev , [ index ] : nativeEvent . layout } ) ) ;
73
+ } ,
74
+ [ ] ,
75
+ ) ;
76
+
77
+ const tabSpecificStyle = useCallback (
78
+ ( tabIndex : number ) => {
79
+ if ( typeof tabStyle === "function" ) {
80
+ return tabStyle ( tabIndex ) ;
81
+ }
82
+
83
+ return tabStyle ;
84
+ } ,
85
+ [ tabStyle ] ,
86
+ ) ;
73
87
74
88
const renderSelectedTab = useCallback (
75
89
( ) => (
76
90
< Animated . View
77
91
style = { [
78
- styles . activeTab ( tabWidth , gap , activeTabColor , slideAnimation ) ,
92
+ styles . activeTab (
93
+ tabLayouts [ currentIndex ] ?. width || 0 ,
94
+ gap ,
95
+ activeTabColor ,
96
+ slideAnimation ,
97
+ ) ,
79
98
selectedTabStyle ,
80
99
] }
81
100
/>
82
101
) ,
83
- [ activeTabColor , gap , selectedTabStyle , slideAnimation , tabWidth ] ,
102
+ [ activeTabColor , gap , selectedTabStyle , slideAnimation , tabLayouts ] ,
84
103
) ;
85
104
86
105
const renderTab = ( tab : any , index : number ) => {
@@ -90,8 +109,9 @@ const SegmentedControl: React.FC<SegmentedControlProps> = ({
90
109
< TouchableOpacity
91
110
key = { index }
92
111
activeOpacity = { 0.5 }
93
- style = { [ styles . tab , tabStyle ] }
112
+ style = { [ styles . tab , tabSpecificStyle ( index ) ] }
94
113
onPress = { ( ) => handleTabPress ( index ) }
114
+ onLayout = { ( e ) => onLayoutTab ( index , e ) }
95
115
>
96
116
{ ! isTabText ? (
97
117
tab
@@ -112,9 +132,11 @@ const SegmentedControl: React.FC<SegmentedControlProps> = ({
112
132
} ;
113
133
114
134
return (
115
- < View style = { [ styles . container , style ] } onLayout = { handleLayout } >
135
+ < View style = { [ styles . container , style ] } >
116
136
{ renderSelectedTab ( ) }
117
- { tabs . map ( ( tab , index : number ) => renderTab ( tab , index ) ) }
137
+ < View style = { [ styles . tabsContainer , { marginHorizontal : gap } ] } >
138
+ { tabs . map ( ( tab , index : number ) => renderTab ( tab , index ) ) }
139
+ </ View >
118
140
</ View >
119
141
) ;
120
142
} ;
0 commit comments