1
1
import { cn } from "@follow/utils"
2
+ import { useEffect } from "react"
2
3
import { Image , ScrollView , StyleSheet , TouchableOpacity , View } from "react-native"
4
+ import Animated , { useAnimatedStyle , useSharedValue , withTiming } from "react-native-reanimated"
3
5
import { useSafeAreaInsets } from "react-native-safe-area-context"
4
6
5
7
import { FallbackIcon } from "@/src/components/ui/icon/fallback-icon"
@@ -17,12 +19,12 @@ export const CollectionPanel = () => {
17
19
const insets = useSafeAreaInsets ( )
18
20
return (
19
21
< View
20
- className = "bg-quaternary -system-fill dark:bg-tertiary-system-background"
22
+ className = "bg-secondary -system-fill dark:bg-tertiary-system-background"
21
23
style = { { width : 65 } }
22
24
>
23
25
< ScrollView
24
26
contentContainerClassName = "flex gap-4 px-3.5"
25
- contentContainerStyle = { { paddingTop : insets . top + 10 , paddingBottom : insets . bottom } }
27
+ contentContainerStyle = { { marginTop : insets . top + 10 , paddingBottom : insets . bottom } }
26
28
>
27
29
< View className = "flex-1 items-center" >
28
30
< Logo width = { 37 } height = { 37 } color = "#222" />
@@ -46,16 +48,38 @@ const styles = StyleSheet.create({
46
48
} ,
47
49
} )
48
50
51
+ const ActiveIndicator = ( { isActive } : { isActive : boolean } ) => {
52
+ const scaleY = useSharedValue ( 1 )
53
+
54
+ useEffect ( ( ) => {
55
+ if ( isActive ) {
56
+ scaleY . value = withTiming ( 1 , { duration : 200 } )
57
+ } else {
58
+ scaleY . value = withTiming ( 0 , { duration : 200 } )
59
+ }
60
+ } , [ isActive , scaleY ] )
61
+
62
+ const animatedStyle = useAnimatedStyle ( ( ) => {
63
+ return {
64
+ transform : [ { scaleY : scaleY . value } ] ,
65
+ }
66
+ } )
67
+
68
+ return (
69
+ < Animated . View
70
+ className = "absolute -left-3.5 h-9 w-1 rounded-r-xl bg-black"
71
+ style = { animatedStyle }
72
+ />
73
+ )
74
+ }
75
+
49
76
const ViewButton = ( { viewDef } : { viewDef : ViewDefinition } ) => {
50
77
const selectedCollection = useSelectedCollection ( )
51
78
const isActive = selectedCollection . type === "view" && selectedCollection . viewId === viewDef . view
52
79
53
80
return (
54
81
< TouchableOpacity
55
- className = { cn (
56
- "flex aspect-square items-center justify-center rounded-full p-3" ,
57
- isActive ? "bg-secondary-system-fill" : "bg-system-background" ,
58
- ) }
82
+ className = "relative flex aspect-square items-center justify-center rounded-full p-3"
59
83
onPress = { ( ) =>
60
84
selectCollection ( {
61
85
type : "view" ,
@@ -64,6 +88,7 @@ const ViewButton = ({ viewDef }: { viewDef: ViewDefinition }) => {
64
88
}
65
89
style = { { backgroundColor : viewDef . activeColor } }
66
90
>
91
+ < ActiveIndicator isActive = { isActive } />
67
92
< viewDef . icon key = { viewDef . name } color = { "#fff" } />
68
93
</ TouchableOpacity >
69
94
)
@@ -77,22 +102,22 @@ const ListButton = ({ listId }: { listId: string }) => {
77
102
78
103
return (
79
104
< TouchableOpacity
80
- className = { cn (
81
- "flex aspect-square items-center justify-center overflow-hidden rounded-full p-3" ,
82
- isActive ? "bg-system-fill" : "bg-system-background" ,
83
- ) }
105
+ className = { cn ( "relative flex aspect-square items-center justify-center rounded-full p-3" ) }
84
106
onPress = { ( ) =>
85
107
selectCollection ( {
86
108
type : "list" ,
87
109
listId,
88
110
} )
89
111
}
90
112
>
91
- { list . image ? (
92
- < Image source = { { uri : list . image , width : 41 , height : 41 } } resizeMode = "cover" />
93
- ) : (
94
- < FallbackIcon title = { list . title } size = { 41 } />
95
- ) }
113
+ < ActiveIndicator isActive = { isActive } />
114
+ < View className = "overflow-hidden rounded-full" >
115
+ { list . image ? (
116
+ < Image source = { { uri : list . image , width : 41 , height : 41 } } resizeMode = "cover" />
117
+ ) : (
118
+ < FallbackIcon title = { list . title } size = { 41 } />
119
+ ) }
120
+ </ View >
96
121
</ TouchableOpacity >
97
122
)
98
123
}
0 commit comments