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