mirror of https://git.lenooby09.tech/LeNooby09/social-app.git
1import React, {createRef, useState, useMemo} from 'react'
2import {
3 Animated,
4 StyleSheet,
5 TouchableWithoutFeedback,
6 View,
7} from 'react-native'
8import {Text} from './text/Text'
9import {usePalette} from '../../lib/hooks/usePalette'
10
11interface Layout {
12 x: number
13 width: number
14}
15
16export function Selector({
17 selectedIndex,
18 items,
19 panX,
20 onSelect,
21}: {
22 selectedIndex: number
23 items: string[]
24 panX: Animated.Value
25 onSelect?: (index: number) => void
26}) {
27 const pal = usePalette('default')
28 const [itemLayouts, setItemLayouts] = useState<undefined | Layout[]>(
29 undefined,
30 )
31 const itemRefs = useMemo(
32 () => Array.from({length: items.length}).map(() => createRef<View>()),
33 [items.length],
34 )
35
36 const currentLayouts = useMemo(() => {
37 const left = itemLayouts?.[selectedIndex - 1] || {x: 0, width: 0}
38 const middle = itemLayouts?.[selectedIndex] || {x: 0, width: 0}
39 const right = itemLayouts?.[selectedIndex + 1] || {
40 x: middle.x + 20,
41 width: middle.width,
42 }
43 return [left, middle, right]
44 }, [selectedIndex, itemLayouts])
45
46 const underlineStyle = {
47 backgroundColor: pal.colors.text,
48 left: panX.interpolate({
49 inputRange: [-1, 0, 1],
50 outputRange: [
51 currentLayouts[0].x,
52 currentLayouts[1].x,
53 currentLayouts[2].x,
54 ],
55 }),
56 width: panX.interpolate({
57 inputRange: [-1, 0, 1],
58 outputRange: [
59 currentLayouts[0].width,
60 currentLayouts[1].width,
61 currentLayouts[2].width,
62 ],
63 }),
64 }
65
66 const onLayout = () => {
67 const promises = []
68 for (let i = 0; i < items.length; i++) {
69 promises.push(
70 new Promise<Layout>(resolve => {
71 itemRefs[i].current?.measure(
72 (x: number, _y: number, width: number) => {
73 resolve({x, width})
74 },
75 )
76 }),
77 )
78 }
79 Promise.all(promises).then((layouts: Layout[]) => {
80 setItemLayouts(layouts)
81 })
82 }
83
84 const onPressItem = (index: number) => {
85 onSelect?.(index)
86 }
87
88 return (
89 <View style={[pal.view, styles.outer]} onLayout={onLayout}>
90 <Animated.View style={[styles.underline, underlineStyle]} />
91 {items.map((item, i) => {
92 const selected = i === selectedIndex
93 return (
94 <TouchableWithoutFeedback key={i} onPress={() => onPressItem(i)}>
95 <View style={styles.item} ref={itemRefs[i]}>
96 <Text
97 style={
98 selected
99 ? [styles.labelSelected, pal.text]
100 : [styles.label, pal.textLight]
101 }>
102 {item}
103 </Text>
104 </View>
105 </TouchableWithoutFeedback>
106 )
107 })}
108 </View>
109 )
110}
111
112const styles = StyleSheet.create({
113 outer: {
114 flexDirection: 'row',
115 paddingTop: 8,
116 paddingBottom: 12,
117 paddingHorizontal: 14,
118 },
119 item: {
120 marginRight: 14,
121 paddingHorizontal: 10,
122 },
123 label: {
124 fontWeight: '600',
125 },
126 labelSelected: {
127 fontWeight: '600',
128 },
129 underline: {
130 position: 'absolute',
131 height: 4,
132 bottom: 0,
133 },
134})