diff options
author | Paul Frazee <pfrazee@gmail.com> | 2023-03-19 18:11:33 -0500 |
---|---|---|
committer | Paul Frazee <pfrazee@gmail.com> | 2023-03-19 18:11:33 -0500 |
commit | 23e8484986acc2926d795d0406dd3c820ec83a6c (patch) | |
tree | 39d248e5ed3354741a305b55aa1616a7ab044a91 /src | |
parent | 7a754850bc71a46f4b198e942b7427536b253587 (diff) | |
download | voidsky-23e8484986acc2926d795d0406dd3c820ec83a6c.tar.zst |
Implement pager and tabbar for desktop web
Diffstat (limited to 'src')
-rw-r--r-- | src/lib/styles.ts | 1 | ||||
-rw-r--r-- | src/view/com/util/TabBar.tsx | 75 | ||||
-rw-r--r-- | src/view/com/util/pager/Pager.tsx (renamed from src/view/com/util/Pager.tsx) | 4 | ||||
-rw-r--r-- | src/view/com/util/pager/Pager.web.tsx | 65 | ||||
-rw-r--r-- | src/view/screens/Home.tsx | 94 | ||||
-rw-r--r-- | src/view/screens/home/FeedsTabBar.tsx | 72 | ||||
-rw-r--r-- | src/view/screens/home/FeedsTabBar.web.tsx | 22 |
7 files changed, 222 insertions, 111 deletions
diff --git a/src/lib/styles.ts b/src/lib/styles.ts index 5d7f7f82d..aa255b21f 100644 --- a/src/lib/styles.ts +++ b/src/lib/styles.ts @@ -70,6 +70,7 @@ export const s = StyleSheet.create({ borderRight1: {borderRightWidth: 1}, borderBottom1: {borderBottomWidth: 1}, borderLeft1: {borderLeftWidth: 1}, + hidden: {display: 'none'}, // font weights fw600: {fontWeight: '600'}, diff --git a/src/view/com/util/TabBar.tsx b/src/view/com/util/TabBar.tsx index dd8fdcb56..4b67b8a80 100644 --- a/src/view/com/util/TabBar.tsx +++ b/src/view/com/util/TabBar.tsx @@ -7,6 +7,7 @@ import { } from 'react-native' import {Text} from './text/Text' import {usePalette} from 'lib/hooks/usePalette' +import {isDesktopWeb} from 'platform/detection' interface Layout { x: number @@ -46,8 +47,9 @@ export function TabBar({ const indicatorStyle = { backgroundColor: indicatorColor || pal.colors.link, - bottom: indicatorPosition === 'bottom' ? -1 : undefined, - top: indicatorPosition === 'top' ? -1 : undefined, + bottom: + indicatorPosition === 'bottom' ? (isDesktopWeb ? 0 : -1) : undefined, + top: indicatorPosition === 'top' ? (isDesktopWeb ? 0 : -1) : undefined, transform: [ { translateX: panX.interpolate({ @@ -112,26 +114,49 @@ export function TabBar({ ) } -const styles = StyleSheet.create({ - outer: { - flexDirection: 'row', - paddingHorizontal: 14, - }, - itemTop: { - paddingTop: 10, - paddingBottom: 10, - marginRight: 24, - }, - itemBottom: { - paddingTop: 8, - paddingBottom: 12, - marginRight: 24, - }, - indicator: { - position: 'absolute', - left: 0, - width: 1, - height: 3, - borderRadius: 4, - }, -}) +const styles = isDesktopWeb + ? StyleSheet.create({ + outer: { + flexDirection: 'row', + paddingHorizontal: 18, + }, + itemTop: { + paddingTop: 16, + paddingBottom: 14, + marginRight: 24, + }, + itemBottom: { + paddingTop: 14, + paddingBottom: 16, + marginRight: 24, + }, + indicator: { + position: 'absolute', + left: 0, + width: 1, + height: 3, + }, + }) + : StyleSheet.create({ + outer: { + flexDirection: 'row', + paddingHorizontal: 14, + }, + itemTop: { + paddingTop: 10, + paddingBottom: 10, + marginRight: 24, + }, + itemBottom: { + paddingTop: 8, + paddingBottom: 12, + marginRight: 24, + }, + indicator: { + position: 'absolute', + left: 0, + width: 1, + height: 3, + borderRadius: 4, + }, + }) diff --git a/src/view/com/util/Pager.tsx b/src/view/com/util/pager/Pager.tsx index d71cb7f7f..416828a27 100644 --- a/src/view/com/util/Pager.tsx +++ b/src/view/com/util/pager/Pager.tsx @@ -19,7 +19,7 @@ interface Props { tabBarPosition?: 'top' | 'bottom' initialPage?: number renderTabBar: RenderTabBarFn - onPageSelected?: (e: PageSelectedEvent) => void + onPageSelected?: (index: number) => void } export const Pager = ({ children, @@ -36,7 +36,7 @@ export const Pager = ({ const onPageSelectedInner = React.useCallback( (e: PageSelectedEvent) => { setSelectedPage(e.nativeEvent.position) - onPageSelected?.(e) + onPageSelected?.(e.nativeEvent.position) }, [setSelectedPage, onPageSelected], ) diff --git a/src/view/com/util/pager/Pager.web.tsx b/src/view/com/util/pager/Pager.web.tsx new file mode 100644 index 000000000..d50100de9 --- /dev/null +++ b/src/view/com/util/pager/Pager.web.tsx @@ -0,0 +1,65 @@ +import React from 'react' +import {Animated, View} from 'react-native' +import {useAnimatedValue} from 'lib/hooks/useAnimatedValue' +import {s} from 'lib/styles' + +export interface RenderTabBarFnProps { + selectedPage: number + position: Animated.Value + offset: Animated.Value + onSelect?: (index: number) => void +} +export type RenderTabBarFn = (props: RenderTabBarFnProps) => JSX.Element + +interface Props { + tabBarPosition?: 'top' | 'bottom' + initialPage?: number + renderTabBar: RenderTabBarFn + onPageSelected?: (index: number) => void +} +export const Pager = ({ + children, + tabBarPosition = 'top', + initialPage = 0, + renderTabBar, + onPageSelected, +}: React.PropsWithChildren<Props>) => { + const [selectedPage, setSelectedPage] = React.useState(initialPage) + const position = useAnimatedValue(0) + const offset = useAnimatedValue(0) + + const onTabBarSelect = React.useCallback( + (index: number) => { + setSelectedPage(index) + onPageSelected?.(index) + Animated.timing(position, { + toValue: index, + duration: 200, + useNativeDriver: true, + }).start() + }, + [setSelectedPage, onPageSelected, position], + ) + + return ( + <View> + {tabBarPosition === 'top' && + renderTabBar({ + selectedPage, + position, + offset, + onSelect: onTabBarSelect, + })} + {children.map((child, i) => ( + <View style={selectedPage === i ? undefined : s.hidden}>{child}</View> + ))} + {tabBarPosition === 'bottom' && + renderTabBar({ + selectedPage, + position, + offset, + onSelect: onTabBarSelect, + })} + </View> + ) +} diff --git a/src/view/screens/Home.tsx b/src/view/screens/Home.tsx index 822174446..4950bc0fd 100644 --- a/src/view/screens/Home.tsx +++ b/src/view/screens/Home.tsx @@ -1,11 +1,5 @@ import React from 'react' -import { - Animated, - FlatList, - StyleSheet, - View, - useWindowDimensions, -} from 'react-native' +import {FlatList, View, useWindowDimensions} from 'react-native' import {useFocusEffect, useIsFocused} from '@react-navigation/native' import {observer} from 'mobx-react-lite' import useAppState from 'react-native-appstate-hook' @@ -15,25 +9,17 @@ import {withAuthRequired} from 'view/com/auth/withAuthRequired' import {Feed} from '../com/posts/Feed' import {FollowingEmptyState} from 'view/com/posts/FollowingEmptyState' import {LoadLatestBtn} from '../com/util/LoadLatestBtn' -import {TabBar} from 'view/com/util/TabBar' -import { - Pager, - PageSelectedEvent, - RenderTabBarFnProps, -} from 'view/com/util/Pager' +import {FeedsTabBar} from './home/FeedsTabBar' +import {Pager, RenderTabBarFnProps} from 'view/com/util/pager/Pager' import {FAB} from '../com/util/FAB' import {useStores} from 'state/index' -import {usePalette} from 'lib/hooks/usePalette' import {s} from 'lib/styles' import {useOnMainScroll} from 'lib/hooks/useOnMainScroll' -import {useSafeAreaInsets} from 'react-native-safe-area-context' -import {useAnimatedValue} from 'lib/hooks/useAnimatedValue' import {useAnalytics} from 'lib/analytics' import {ComposeIcon2} from 'lib/icons' -import {clamp} from 'lodash' +import {isDesktopWeb} from 'platform/detection' const TAB_BAR_HEIGHT = 82 -const BOTTOM_BAR_HEIGHT = 48 type Props = NativeStackScreenProps<HomeTabNavigatorParams, 'Home'> export const HomeScreen = withAuthRequired((_opts: Props) => { @@ -56,9 +42,9 @@ export const HomeScreen = withAuthRequired((_opts: Props) => { ) const onPageSelected = React.useCallback( - (e: PageSelectedEvent) => { - setSelectedPage(e.nativeEvent.position) - store.shell.setIsDrawerSwipeDisabled(e.nativeEvent.position > 0) + (index: number) => { + setSelectedPage(index) + store.shell.setIsDrawerSwipeDisabled(index > 0) }, [store], ) @@ -69,7 +55,7 @@ export const HomeScreen = withAuthRequired((_opts: Props) => { const renderTabBar = React.useCallback( (props: RenderTabBarFnProps) => { - return <FloatingTabBar {...props} onPressSelected={onPressSelected} /> + return <FeedsTabBar {...props} onPressSelected={onPressSelected} /> }, [onPressSelected], ) @@ -83,7 +69,7 @@ export const HomeScreen = withAuthRequired((_opts: Props) => { <Pager onPageSelected={onPageSelected} renderTabBar={renderTabBar} - tabBarPosition="bottom" + tabBarPosition={isDesktopWeb ? 'top' : 'bottom'} initialPage={initialPage}> <FeedPage key="1" @@ -96,48 +82,6 @@ export const HomeScreen = withAuthRequired((_opts: Props) => { ) }) -const FloatingTabBar = observer( - (props: RenderTabBarFnProps & {onPressSelected: () => void}) => { - const store = useStores() - const safeAreaInsets = useSafeAreaInsets() - const pal = usePalette('default') - const interp = useAnimatedValue(0) - - const pad = React.useMemo( - () => ({ - paddingBottom: clamp(safeAreaInsets.bottom, 15, 20), - }), - [safeAreaInsets], - ) - - React.useEffect(() => { - Animated.timing(interp, { - toValue: store.shell.minimalShellMode ? 0 : 1, - duration: 100, - useNativeDriver: true, - isInteraction: false, - }).start() - }, [interp, store.shell.minimalShellMode]) - const transform = { - transform: [ - {translateY: Animated.multiply(interp, -1 * BOTTOM_BAR_HEIGHT)}, - ], - } - - return ( - <Animated.View - style={[pal.view, pal.border, styles.tabBar, pad, transform]}> - <TabBar - {...props} - items={['Following', "What's hot"]} - indicatorPosition="top" - indicatorColor={pal.colors.link} - /> - </Animated.View> - ) - }, -) - const FeedPage = observer( ({ isPageFocused, @@ -158,7 +102,7 @@ const FeedPage = observer( const isScreenFocused = useIsFocused() const winDim = useWindowDimensions() const containerStyle = React.useMemo( - () => ({height: winDim.height - TAB_BAR_HEIGHT}), + () => ({height: winDim.height - (isDesktopWeb ? 0 : TAB_BAR_HEIGHT)}), [winDim], ) @@ -249,21 +193,3 @@ const FeedPage = observer( ) }, ) - -const styles = StyleSheet.create({ - tabBar: { - position: 'absolute', - left: 0, - right: 0, - bottom: 0, - flexDirection: 'row', - alignItems: 'center', - paddingHorizontal: 10, - borderTopWidth: 1, - paddingTop: 0, - paddingBottom: 30, - }, - tabBarAvi: { - marginRight: 4, - }, -}) diff --git a/src/view/screens/home/FeedsTabBar.tsx b/src/view/screens/home/FeedsTabBar.tsx new file mode 100644 index 000000000..d34034103 --- /dev/null +++ b/src/view/screens/home/FeedsTabBar.tsx @@ -0,0 +1,72 @@ +import React from 'react' +import {Animated, StyleSheet} from 'react-native' +import {observer} from 'mobx-react-lite' +import {TabBar} from 'view/com/util/TabBar' +import {RenderTabBarFnProps} from 'view/com/util/pager/Pager' +import {useStores} from 'state/index' +import {usePalette} from 'lib/hooks/usePalette' +import {useSafeAreaInsets} from 'react-native-safe-area-context' +import {useAnimatedValue} from 'lib/hooks/useAnimatedValue' +import {clamp} from 'lodash' + +const BOTTOM_BAR_HEIGHT = 48 + +export const FeedsTabBar = observer( + (props: RenderTabBarFnProps & {onPressSelected: () => void}) => { + const store = useStores() + const safeAreaInsets = useSafeAreaInsets() + const pal = usePalette('default') + const interp = useAnimatedValue(0) + + const pad = React.useMemo( + () => ({ + paddingBottom: clamp(safeAreaInsets.bottom, 15, 20), + }), + [safeAreaInsets], + ) + + React.useEffect(() => { + Animated.timing(interp, { + toValue: store.shell.minimalShellMode ? 0 : 1, + duration: 100, + useNativeDriver: true, + isInteraction: false, + }).start() + }, [interp, store.shell.minimalShellMode]) + const transform = { + transform: [ + {translateY: Animated.multiply(interp, -1 * BOTTOM_BAR_HEIGHT)}, + ], + } + + return ( + <Animated.View + style={[pal.view, pal.border, styles.tabBar, pad, transform]}> + <TabBar + {...props} + items={['Following', "What's hot"]} + indicatorPosition="top" + indicatorColor={pal.colors.link} + /> + </Animated.View> + ) + }, +) + +const styles = StyleSheet.create({ + tabBar: { + position: 'absolute', + left: 0, + right: 0, + bottom: 0, + flexDirection: 'row', + alignItems: 'center', + paddingHorizontal: 10, + borderTopWidth: 1, + paddingTop: 0, + paddingBottom: 30, + }, + tabBarAvi: { + marginRight: 4, + }, +}) diff --git a/src/view/screens/home/FeedsTabBar.web.tsx b/src/view/screens/home/FeedsTabBar.web.tsx new file mode 100644 index 000000000..59ea42988 --- /dev/null +++ b/src/view/screens/home/FeedsTabBar.web.tsx @@ -0,0 +1,22 @@ +import React from 'react' +import {observer} from 'mobx-react-lite' +import {TabBar} from 'view/com/util/TabBar' +import {CenteredView} from 'view/com/util/Views' +import {RenderTabBarFnProps} from 'view/com/util/pager/Pager' +import {usePalette} from 'lib/hooks/usePalette' + +export const FeedsTabBar = observer( + (props: RenderTabBarFnProps & {onPressSelected: () => void}) => { + const pal = usePalette('default') + return ( + <CenteredView> + <TabBar + {...props} + items={['Following', "What's hot"]} + indicatorPosition="bottom" + indicatorColor={pal.colors.link} + /> + </CenteredView> + ) + }, +) |