about summary refs log tree commit diff
path: root/src/view/com/util/TabBar.tsx
blob: d9f48577c9962629d8761f60dd7de5344d590d5b (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
import React, {createRef, useState, useMemo} from 'react'
import {
  Animated,
  StyleSheet,
  TouchableWithoutFeedback,
  View,
} from 'react-native'
import {Text} from './text/Text'
import {usePalette} from 'lib/hooks/usePalette'

interface Layout {
  x: number
  width: number
}

export function TabBar({
  selectedPage,
  items,
  position,
  offset,
  onSelect,
}: {
  selectedPage: number
  items: string[]
  position: Animated.Value
  offset: Animated.Value
  onSelect?: (index: number) => void
}) {
  const pal = usePalette('default')
  const [itemLayouts, setItemLayouts] = useState<Layout[]>(
    items.map(() => ({x: 0, width: 0})),
  )
  const itemRefs = useMemo(
    () => Array.from({length: items.length}).map(() => createRef<View>()),
    [items.length],
  )
  const panX = Animated.add(position, offset)

  const underlineStyle = {
    backgroundColor: pal.colors.link,
    left: panX.interpolate({
      inputRange: items.map((_item, i) => i),
      outputRange: itemLayouts.map(l => l.x),
    }),
    width: panX.interpolate({
      inputRange: items.map((_item, i) => i),
      outputRange: itemLayouts.map(l => l.width),
    }),
  }

  const onLayout = () => {
    const promises = []
    for (let i = 0; i < items.length; i++) {
      promises.push(
        new Promise<Layout>(resolve => {
          itemRefs[i].current?.measure(
            (x: number, _y: number, width: number) => {
              resolve({x, width})
            },
          )
        }),
      )
    }
    Promise.all(promises).then((layouts: Layout[]) => {
      setItemLayouts(layouts)
    })
  }

  const onPressItem = (index: number) => {
    onSelect?.(index)
  }

  return (
    <View style={[pal.view, styles.outer]} onLayout={onLayout}>
      <Animated.View style={[styles.underline, underlineStyle]} />
      {items.map((item, i) => {
        const selected = i === selectedPage
        return (
          <TouchableWithoutFeedback key={i} onPress={() => onPressItem(i)}>
            <View style={styles.item} ref={itemRefs[i]}>
              <Text
                type="xl-medium"
                style={selected ? pal.text : pal.textLight}>
                {item}
              </Text>
            </View>
          </TouchableWithoutFeedback>
        )
      })}
    </View>
  )
}

const styles = StyleSheet.create({
  outer: {
    flexDirection: 'row',
    paddingHorizontal: 14,
  },
  item: {
    paddingTop: 6,
    paddingBottom: 14,
    marginRight: 24,
  },
  underline: {
    position: 'absolute',
    height: 3,
    bottom: 0,
    borderRadius: 4,
  },
})