Make the pager take full width (#7066)

* Wide tabs for web

* Wide tabs on mobile

* Tweak min for profile

* Driveby border fix

* Fix single tab indicator
This commit is contained in:
dan
2024-12-12 15:35:56 +00:00
committed by GitHub
parent 704e36c280
commit 88166926fa
3 changed files with 69 additions and 12 deletions
+1 -1
View File
@@ -107,7 +107,7 @@ export default function HashtagScreen({
return (
<Layout.Screen>
<Layout.Header.Outer>
<Layout.Header.Outer noBottomBorder>
<Layout.Header.BackButton />
<Layout.Header.Content>
<Layout.Header.TitleText>{headerTitle}</Layout.Header.TitleText>
+48 -10
View File
@@ -51,6 +51,7 @@ export function TabBar({
const containerSize = useSharedValue(0)
const scrollX = useSharedValue(0)
const layouts = useSharedValue<{x: number; width: number}[]>([])
const textLayouts = useSharedValue<{width: number}[]>([])
const itemsLength = items.length
const scrollToOffsetJS = useCallback(
@@ -211,21 +212,40 @@ export function TabBar({
[layouts],
)
const onTextLayout = useCallback(
(i: number, layout: {width: number}) => {
'worklet'
textLayouts.modify(ls => {
ls[i] = layout
return ls
})
},
[textLayouts],
)
const indicatorStyle = useAnimatedStyle(() => {
if (!_WORKLET) {
return {opacity: 0}
}
const layoutsValue = layouts.get()
const textLayoutsValue = textLayouts.get()
if (
layoutsValue.length !== itemsLength ||
layoutsValue.some(l => l === undefined)
textLayoutsValue.length !== itemsLength
) {
return {
opacity: 0,
}
}
if (layoutsValue.length === 1) {
return {opacity: 1}
if (textLayoutsValue.length === 1) {
return {
opacity: 1,
transform: [
{
scaleX: textLayoutsValue[0].width / contentSize.get(),
},
],
}
}
return {
opacity: 1,
@@ -240,10 +260,8 @@ export function TabBar({
{
scaleX: interpolate(
dragProgress.get(),
layoutsValue.map((l, i) => i),
layoutsValue.map(
l => (l.width - ITEM_PADDING * 2) / contentSize.get(),
),
textLayoutsValue.map((l, i) => i),
textLayoutsValue.map(l => l.width / contentSize.get()),
),
},
],
@@ -287,7 +305,7 @@ export function TabBar({
onLayout={e => {
contentSize.set(e.nativeEvent.layout.width)
}}
style={{flexDirection: 'row'}}>
style={{flexDirection: 'row', flexGrow: 1}}>
{items.map((item, i) => {
return (
<TabBarItem
@@ -298,6 +316,7 @@ export function TabBar({
item={item}
onPressItem={onPressItem}
onItemLayout={onItemLayout}
onTextLayout={onTextLayout}
/>
)
})}
@@ -328,6 +347,7 @@ function TabBarItem({
item,
onPressItem,
onItemLayout,
onTextLayout,
}: {
index: number
testID: string | undefined
@@ -335,6 +355,7 @@ function TabBarItem({
item: string
onPressItem: (index: number) => void
onItemLayout: (index: number, layout: {x: number; width: number}) => void
onTextLayout: (index: number, layout: {width: number}) => void
}) {
const t = useTheme()
const style = useAnimatedStyle(() => {
@@ -358,8 +379,15 @@ function TabBarItem({
[index, onItemLayout],
)
const handleTextLayout = useCallback(
(e: LayoutChangeEvent) => {
runOnUI(onTextLayout)(index, e.nativeEvent.layout)
},
[index, onTextLayout],
)
return (
<View onLayout={handleLayout}>
<View onLayout={handleLayout} style={{flexGrow: 1}}>
<PressableWithHover
testID={`${testID}-selector-${index}`}
style={styles.item}
@@ -370,7 +398,8 @@ function TabBarItem({
<Text
emoji
testID={testID ? `${testID}-${item}` : undefined}
style={[t.atoms.text, a.text_md, a.font_bold, {lineHeight: 20}]}>
style={[styles.itemText, t.atoms.text, a.text_md, a.font_bold]}
onLayout={handleTextLayout}>
{item}
</Text>
</Animated.View>
@@ -381,19 +410,28 @@ function TabBarItem({
const styles = StyleSheet.create({
contentContainer: {
flexGrow: 1,
backgroundColor: 'transparent',
paddingHorizontal: CONTENT_PADDING,
},
item: {
flexGrow: 1,
paddingTop: 10,
paddingHorizontal: ITEM_PADDING,
justifyContent: 'center',
},
itemInner: {
alignItems: 'center',
flexGrow: 1,
paddingBottom: 10,
borderBottomWidth: 3,
borderBottomColor: 'transparent',
},
itemText: {
lineHeight: 20,
minWidth: 45,
textAlign: 'center',
},
outerBottomBorder: {
position: 'absolute',
left: 0,
+20 -1
View File
@@ -115,12 +115,14 @@ export function TabBar({
hoverStyle={t.atoms.bg_contrast_25}
onPress={() => onPressItem(i)}
accessibilityRole="tab">
<View style={[styles.itemInner, selected && indicatorStyle]}>
<View style={styles.itemInner}>
<Text
emoji
testID={testID ? `${testID}-${item}` : undefined}
style={[
styles.itemText,
selected ? t.atoms.text : t.atoms.text_contrast_medium,
selected && indicatorStyle,
a.text_md,
a.font_bold,
{lineHeight: 20},
@@ -143,15 +145,23 @@ const desktopStyles = StyleSheet.create({
width: 598,
},
contentContainer: {
flexGrow: 1,
paddingHorizontal: 0,
backgroundColor: 'transparent',
},
item: {
flexGrow: 1,
alignItems: 'stretch',
paddingTop: 14,
paddingHorizontal: 14,
justifyContent: 'center',
},
itemInner: {
alignItems: 'center',
},
itemText: {
textAlign: 'center',
minWidth: 45,
paddingBottom: 12,
borderBottomWidth: 3,
borderBottomColor: 'transparent',
@@ -170,15 +180,24 @@ const mobileStyles = StyleSheet.create({
flexDirection: 'row',
},
contentContainer: {
flexGrow: 1,
backgroundColor: 'transparent',
paddingHorizontal: 6,
},
item: {
flexGrow: 1,
alignItems: 'stretch',
paddingTop: 10,
paddingHorizontal: 10,
justifyContent: 'center',
},
itemInner: {
flexGrow: 1,
alignItems: 'center',
},
itemText: {
textAlign: 'center',
minWidth: 45,
paddingBottom: 10,
borderBottomWidth: 2,
borderBottomColor: 'transparent',