mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import React, {useRef, useMemo, useEffect, useState, useCallback} from 'react'
2import {StyleSheet, View, ScrollView, LayoutChangeEvent} from 'react-native'
3import {Text} from '../util/text/Text'
4import {PressableWithHover} from '../util/PressableWithHover'
5import {usePalette} from 'lib/hooks/usePalette'
6import {isDesktopWeb, isMobileWeb} from 'platform/detection'
7import {DraggableScrollView} from './DraggableScrollView'
8
9export interface TabBarProps {
10 testID?: string
11 selectedPage: number
12 items: string[]
13 indicatorColor?: string
14 onSelect?: (index: number) => void
15 onPressSelected?: () => void
16}
17
18export function TabBar({
19 testID,
20 selectedPage,
21 items,
22 indicatorColor,
23 onSelect,
24 onPressSelected,
25}: TabBarProps) {
26 const pal = usePalette('default')
27 const scrollElRef = useRef<ScrollView>(null)
28 const [itemXs, setItemXs] = useState<number[]>([])
29 const indicatorStyle = useMemo(
30 () => ({borderBottomColor: indicatorColor || pal.colors.link}),
31 [indicatorColor, pal],
32 )
33
34 // scrolls to the selected item when the page changes
35 useEffect(() => {
36 scrollElRef.current?.scrollTo({
37 x: itemXs[selectedPage] || 0,
38 })
39 }, [scrollElRef, itemXs, selectedPage])
40
41 const onPressItem = useCallback(
42 (index: number) => {
43 onSelect?.(index)
44 if (index === selectedPage) {
45 onPressSelected?.()
46 }
47 },
48 [onSelect, selectedPage, onPressSelected],
49 )
50
51 // calculates the x position of each item on mount and on layout change
52 const onItemLayout = React.useCallback(
53 (e: LayoutChangeEvent, index: number) => {
54 const x = e.nativeEvent.layout.x
55 setItemXs(prev => {
56 const Xs = [...prev]
57 Xs[index] = x
58 return Xs
59 })
60 },
61 [],
62 )
63
64 return (
65 <View testID={testID} style={[pal.view, styles.outer]}>
66 <DraggableScrollView
67 horizontal={true}
68 showsHorizontalScrollIndicator={false}
69 ref={scrollElRef}
70 contentContainerStyle={styles.contentContainer}>
71 {items.map((item, i) => {
72 const selected = i === selectedPage
73 return (
74 <PressableWithHover
75 key={item}
76 onLayout={e => onItemLayout(e, i)}
77 style={[styles.item, selected && indicatorStyle]}
78 hoverStyle={pal.viewLight}
79 onPress={() => onPressItem(i)}>
80 <Text
81 type={isDesktopWeb ? 'xl-bold' : 'lg-bold'}
82 testID={testID ? `${testID}-${item}` : undefined}
83 style={selected ? pal.text : pal.textLight}>
84 {item}
85 </Text>
86 </PressableWithHover>
87 )
88 })}
89 </DraggableScrollView>
90 </View>
91 )
92}
93
94const styles = isDesktopWeb
95 ? StyleSheet.create({
96 outer: {
97 flexDirection: 'row',
98 width: 598,
99 },
100 contentContainer: {
101 columnGap: 8,
102 marginLeft: 14,
103 paddingRight: 14,
104 backgroundColor: 'transparent',
105 },
106 item: {
107 paddingTop: 14,
108 paddingBottom: 12,
109 paddingHorizontal: 10,
110 borderBottomWidth: 3,
111 borderBottomColor: 'transparent',
112 justifyContent: 'center',
113 },
114 })
115 : StyleSheet.create({
116 outer: {
117 flex: 1,
118 flexDirection: 'row',
119 backgroundColor: 'transparent',
120 maxWidth: '100%',
121 },
122 contentContainer: {
123 columnGap: isMobileWeb ? 0 : 20,
124 marginLeft: isMobileWeb ? 0 : 18,
125 paddingRight: isMobileWeb ? 0 : 36,
126 backgroundColor: 'transparent',
127 },
128 item: {
129 paddingTop: 10,
130 paddingBottom: 10,
131 paddingHorizontal: isMobileWeb ? 8 : 0,
132 borderBottomWidth: 3,
133 borderBottomColor: 'transparent',
134 justifyContent: 'center',
135 },
136 })