Build out header, WIP
This commit is contained in:
@@ -20,6 +20,7 @@ export * from '#/alf/types'
|
|||||||
export * from '#/alf/util/flatten'
|
export * from '#/alf/util/flatten'
|
||||||
export * from '#/alf/util/platform'
|
export * from '#/alf/util/platform'
|
||||||
export * from '#/alf/util/themeSelector'
|
export * from '#/alf/util/themeSelector'
|
||||||
|
export * from '#/alf/util/useGutterStyles'
|
||||||
|
|
||||||
export type Alf = {
|
export type Alf = {
|
||||||
themeName: ThemeName
|
themeName: ThemeName
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
import React from 'react'
|
||||||
|
|
||||||
|
import {atoms as a, useBreakpoints, ViewStyleProp} from '#/alf'
|
||||||
|
|
||||||
|
export function useGutterStyles({
|
||||||
|
top,
|
||||||
|
bottom,
|
||||||
|
}: {
|
||||||
|
top?: boolean
|
||||||
|
bottom?: boolean
|
||||||
|
} = {}) {
|
||||||
|
const {gtMobile} = useBreakpoints()
|
||||||
|
return React.useMemo<ViewStyleProp['style']>(() => {
|
||||||
|
return [
|
||||||
|
a.px_lg,
|
||||||
|
top && a.pt_md,
|
||||||
|
bottom && a.pb_md,
|
||||||
|
gtMobile && [a.px_xl, top && a.pt_lg, bottom && a.pb_lg],
|
||||||
|
]
|
||||||
|
}, [gtMobile, top, bottom])
|
||||||
|
}
|
||||||
+89
-15
@@ -7,11 +7,18 @@ import {
|
|||||||
} from 'react-native-keyboard-controller'
|
} from 'react-native-keyboard-controller'
|
||||||
import Animated, {AnimatedScrollViewProps} from 'react-native-reanimated'
|
import Animated, {AnimatedScrollViewProps} from 'react-native-reanimated'
|
||||||
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
import {useSafeAreaInsets} from 'react-native-safe-area-context'
|
||||||
|
import {msg} from '@lingui/macro'
|
||||||
|
import {useLingui} from '@lingui/react'
|
||||||
|
import {useNavigation} from '@react-navigation/native'
|
||||||
|
|
||||||
|
import {NavigationProp} from '#/lib/routes/types'
|
||||||
import {isWeb} from '#/platform/detection'
|
import {isWeb} from '#/platform/detection'
|
||||||
import {ViewHeader} from '#/view/com/util/ViewHeader'
|
import {ViewHeader} from '#/view/com/util/ViewHeader'
|
||||||
import {CenteredView} from '#/view/com/util/Views'
|
import {CenteredView} from '#/view/com/util/Views'
|
||||||
import {atoms as a, useBreakpoints, useTheme, ViewStyleProp} from '#/alf'
|
import {atoms as a, useBreakpoints, useGutterStyles,useTheme} from '#/alf'
|
||||||
|
import {Button, ButtonIcon} from '#/components/Button'
|
||||||
|
import {ChevronLeft_Stroke2_Corner0_Rounded as ChevronLeft} from '#/components/icons/Chevron'
|
||||||
|
import {Text} from '#/components/Typography'
|
||||||
|
|
||||||
// Every screen should have a Layout component wrapping it.
|
// Every screen should have a Layout component wrapping it.
|
||||||
// This component provides a default padding for the top of the screen.
|
// This component provides a default padding for the top of the screen.
|
||||||
@@ -190,28 +197,95 @@ export const Center = React.forwardRef(function LayoutContent(
|
|||||||
)
|
)
|
||||||
})
|
})
|
||||||
|
|
||||||
export function Gutter({
|
export function HeaderNew({children}: {children: React.ReactNode}) {
|
||||||
|
const t = useTheme()
|
||||||
|
const gutter = useGutterStyles({top: true, bottom: true})
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View style={[a.w_full, a.border_b, t.atoms.border_contrast_low]}>
|
||||||
|
<View style={[a.flex_row, a.align_start, gutter, {gap: 9}]}>
|
||||||
|
{children}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
HeaderNew.BackButton = function HeaderBackButton() {
|
||||||
|
const {_} = useLingui()
|
||||||
|
const navigation = useNavigation<NavigationProp>()
|
||||||
|
|
||||||
|
const onPressBack = React.useCallback(() => {
|
||||||
|
if (navigation.canGoBack()) {
|
||||||
|
navigation.goBack()
|
||||||
|
} else {
|
||||||
|
navigation.navigate('Home')
|
||||||
|
}
|
||||||
|
}, [navigation])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Button
|
||||||
|
label={_(msg`Go back`)}
|
||||||
|
size="small"
|
||||||
|
variant="solid"
|
||||||
|
color="secondary"
|
||||||
|
shape="round"
|
||||||
|
onPress={onPressBack}
|
||||||
|
style={[{marginLeft: -3}]}>
|
||||||
|
<ButtonIcon icon={ChevronLeft} />
|
||||||
|
</Button>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
HeaderNew.Content = function HeaderContent({
|
||||||
children,
|
children,
|
||||||
top,
|
right,
|
||||||
bottom,
|
}: {
|
||||||
style,
|
children: React.ReactNode
|
||||||
}: ViewStyleProp & {
|
right?: React.ReactNode
|
||||||
|
}) {
|
||||||
|
return (
|
||||||
|
<View style={[a.flex_row, a.align_start, a.gap_xs]}>
|
||||||
|
<View style={[a.flex_1]}>{children}</View>
|
||||||
|
{right && <View style={[a.gap_xs, a.flex_1]}>{right}</View>}
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
HeaderNew.TitleText = function HeaderTitleText({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
children: React.ReactNode
|
children: React.ReactNode
|
||||||
top?: boolean
|
|
||||||
bottom?: boolean
|
|
||||||
}) {
|
}) {
|
||||||
const {gtMobile} = useBreakpoints()
|
const {gtMobile} = useBreakpoints()
|
||||||
return (
|
return (
|
||||||
<View
|
<Text
|
||||||
style={[
|
style={[
|
||||||
a.px_lg,
|
a.text_xl,
|
||||||
top && a.pt_lg,
|
a.font_heavy,
|
||||||
bottom && a.pb_lg,
|
a.leading_tight,
|
||||||
gtMobile && [a.px_xl, top && a.pt_xl, bottom && a.pb_xl],
|
{paddingTop: 6},
|
||||||
style,
|
gtMobile && [
|
||||||
|
a.text_3xl,
|
||||||
|
{
|
||||||
|
paddingTop: 2,
|
||||||
|
},
|
||||||
|
],
|
||||||
]}>
|
]}>
|
||||||
{children}
|
{children}
|
||||||
</View>
|
</Text>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
HeaderNew.SubtitleText = function HeaderSubtitleText({
|
||||||
|
children,
|
||||||
|
}: {
|
||||||
|
children: React.ReactNode
|
||||||
|
}) {
|
||||||
|
const t = useTheme()
|
||||||
|
return (
|
||||||
|
<Text style={[a.text_sm, a.leading_tight, t.atoms.text_contrast_medium]}>
|
||||||
|
{children}
|
||||||
|
</Text>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,8 +38,14 @@ export function AccountSettingsScreen({}: Props) {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<Layout.Screen>
|
<Layout.Screen>
|
||||||
<Layout.Header title={_(msg`Account`)} />
|
|
||||||
<Layout.Content>
|
<Layout.Content>
|
||||||
|
<Layout.HeaderNew>
|
||||||
|
<Layout.HeaderNew.BackButton />
|
||||||
|
<Layout.HeaderNew.Content>
|
||||||
|
<Layout.HeaderNew.TitleText>Account</Layout.HeaderNew.TitleText>
|
||||||
|
</Layout.HeaderNew.Content>
|
||||||
|
</Layout.HeaderNew>
|
||||||
|
|
||||||
<SettingsList.Container>
|
<SettingsList.Container>
|
||||||
<SettingsList.Item>
|
<SettingsList.Item>
|
||||||
<SettingsList.ItemIcon icon={EnvelopeIcon} />
|
<SettingsList.ItemIcon icon={EnvelopeIcon} />
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ import React, {useContext, useMemo} from 'react'
|
|||||||
import {GestureResponderEvent, StyleProp, View, ViewStyle} from 'react-native'
|
import {GestureResponderEvent, StyleProp, View, ViewStyle} from 'react-native'
|
||||||
|
|
||||||
import {HITSLOP_10} from '#/lib/constants'
|
import {HITSLOP_10} from '#/lib/constants'
|
||||||
import {atoms as a, useTheme, ViewStyleProp} from '#/alf'
|
import {atoms as a, useGutterStyles,useTheme, ViewStyleProp} from '#/alf'
|
||||||
import * as Button from '#/components/Button'
|
import * as Button from '#/components/Button'
|
||||||
import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRightIcon} from '#/components/icons/Chevron'
|
import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRightIcon} from '#/components/icons/Chevron'
|
||||||
import {Link, LinkProps} from '#/components/Link'
|
import {Link, LinkProps} from '#/components/Link'
|
||||||
@@ -80,6 +80,7 @@ export function Item({
|
|||||||
iconInset?: boolean
|
iconInset?: boolean
|
||||||
style?: StyleProp<ViewStyle>
|
style?: StyleProp<ViewStyle>
|
||||||
}) {
|
}) {
|
||||||
|
const gutter = useGutterStyles()
|
||||||
const context = useContext(ItemContext)
|
const context = useContext(ItemContext)
|
||||||
const childContext = useMemo(() => {
|
const childContext = useMemo(() => {
|
||||||
if (typeof destructive !== 'boolean') return context
|
if (typeof destructive !== 'boolean') return context
|
||||||
@@ -88,7 +89,7 @@ export function Item({
|
|||||||
return (
|
return (
|
||||||
<View
|
<View
|
||||||
style={[
|
style={[
|
||||||
a.px_xl,
|
gutter,
|
||||||
a.py_sm,
|
a.py_sm,
|
||||||
a.align_center,
|
a.align_center,
|
||||||
a.gap_md,
|
a.gap_md,
|
||||||
|
|||||||
@@ -1,9 +1,6 @@
|
|||||||
import React from 'react'
|
import React from 'react'
|
||||||
import {StyleSheet, TouchableOpacity, View} from 'react-native'
|
import {StyleSheet, View} from 'react-native'
|
||||||
import {
|
import {FontAwesomeIconStyle} from '@fortawesome/react-native-fontawesome'
|
||||||
FontAwesomeIcon,
|
|
||||||
FontAwesomeIconStyle,
|
|
||||||
} from '@fortawesome/react-native-fontawesome'
|
|
||||||
import {msg, Trans} from '@lingui/macro'
|
import {msg, Trans} from '@lingui/macro'
|
||||||
import {useLingui} from '@lingui/react'
|
import {useLingui} from '@lingui/react'
|
||||||
import {
|
import {
|
||||||
@@ -14,9 +11,9 @@ import {
|
|||||||
|
|
||||||
import {usePalette} from '#/lib/hooks/usePalette'
|
import {usePalette} from '#/lib/hooks/usePalette'
|
||||||
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
|
import {useWebMediaQueries} from '#/lib/hooks/useWebMediaQueries'
|
||||||
import {getCurrentRoute, isStateAtTabRoot, isTab} from '#/lib/routes/helpers'
|
import {getCurrentRoute, isTab} from '#/lib/routes/helpers'
|
||||||
import {makeProfileLink} from '#/lib/routes/links'
|
import {makeProfileLink} from '#/lib/routes/links'
|
||||||
import {CommonNavigatorParams, NavigationProp} from '#/lib/routes/types'
|
import {CommonNavigatorParams} from '#/lib/routes/types'
|
||||||
import {isInvalidHandle} from '#/lib/strings/handles'
|
import {isInvalidHandle} from '#/lib/strings/handles'
|
||||||
import {emitSoftReset} from '#/state/events'
|
import {emitSoftReset} from '#/state/events'
|
||||||
import {useFetchHandle} from '#/state/queries/handle'
|
import {useFetchHandle} from '#/state/queries/handle'
|
||||||
@@ -101,47 +98,6 @@ function ProfileCard() {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const HIDDEN_BACK_BNT_ROUTES = ['StarterPackWizard', 'StarterPackEdit']
|
|
||||||
|
|
||||||
function BackBtn() {
|
|
||||||
const {isTablet} = useWebMediaQueries()
|
|
||||||
const pal = usePalette('default')
|
|
||||||
const navigation = useNavigation<NavigationProp>()
|
|
||||||
const {_} = useLingui()
|
|
||||||
const shouldShow = useNavigationState(
|
|
||||||
state =>
|
|
||||||
!isStateAtTabRoot(state) &&
|
|
||||||
!HIDDEN_BACK_BNT_ROUTES.includes(getCurrentRoute(state).name),
|
|
||||||
)
|
|
||||||
|
|
||||||
const onPressBack = React.useCallback(() => {
|
|
||||||
if (navigation.canGoBack()) {
|
|
||||||
navigation.goBack()
|
|
||||||
} else {
|
|
||||||
navigation.navigate('Home')
|
|
||||||
}
|
|
||||||
}, [navigation])
|
|
||||||
|
|
||||||
if (!shouldShow || isTablet) {
|
|
||||||
return <></>
|
|
||||||
}
|
|
||||||
return (
|
|
||||||
<TouchableOpacity
|
|
||||||
testID="viewHeaderBackOrMenuBtn"
|
|
||||||
onPress={onPressBack}
|
|
||||||
style={styles.backBtn}
|
|
||||||
accessibilityRole="button"
|
|
||||||
accessibilityLabel={_(msg`Go back`)}
|
|
||||||
accessibilityHint="">
|
|
||||||
<FontAwesomeIcon
|
|
||||||
size={24}
|
|
||||||
icon="angle-left"
|
|
||||||
style={pal.text as FontAwesomeIconStyle}
|
|
||||||
/>
|
|
||||||
</TouchableOpacity>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
interface NavItemProps {
|
interface NavItemProps {
|
||||||
count?: string
|
count?: string
|
||||||
href: string
|
href: string
|
||||||
@@ -381,8 +337,6 @@ export function DesktopLeftNav() {
|
|||||||
|
|
||||||
{hasSession && (
|
{hasSession && (
|
||||||
<>
|
<>
|
||||||
<BackBtn />
|
|
||||||
|
|
||||||
<NavItem
|
<NavItem
|
||||||
href="/"
|
href="/"
|
||||||
icon={
|
icon={
|
||||||
|
|||||||
Reference in New Issue
Block a user