Add subtle hover to all components except feed

This commit is contained in:
Eric Bailey
2025-04-07 10:10:38 -05:00
parent 2cc39b1458
commit 67c79dd8ba
5 changed files with 175 additions and 147 deletions
+34
View File
@@ -0,0 +1,34 @@
import {View} from 'react-native'
import {atoms as a, useTheme, type ViewStyleProp} from '#/alf'
export function SubtleHover({style, hover}: ViewStyleProp & {hover: boolean}) {
const t = useTheme()
let opacity: number
switch (t.name) {
case 'dark':
opacity = 0.4
break
case 'dim':
opacity = 0.45
break
case 'light':
opacity = 0.5
break
}
return (
<View
style={[
a.absolute,
a.inset_0,
a.pointer_events_none,
a.transition_opacity,
t.atoms.bg_contrast_25,
style,
{opacity: hover ? opacity : 0},
]}
/>
)
}
+19 -25
View File
@@ -52,6 +52,7 @@ import {StarterPack} from '#/components/icons/StarterPack'
import {UserCircle_Stroke2_Corner0_Rounded as Person} from '#/components/icons/UserCircle' import {UserCircle_Stroke2_Corner0_Rounded as Person} from '#/components/icons/UserCircle'
import {Loader} from '#/components/Loader' import {Loader} from '#/components/Loader'
import * as ProfileCard from '#/components/ProfileCard' import * as ProfileCard from '#/components/ProfileCard'
import {SubtleHover} from '#/components/SubtleHover'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import * as ModuleHeader from './components/ModuleHeader' import * as ModuleHeader from './components/ModuleHeader'
import { import {
@@ -69,33 +70,26 @@ function LoadMore({item}: {item: ExploreScreenItems & {type: 'loadMore'}}) {
onPress={item.onLoadMore} onPress={item.onLoadMore}
style={[a.relative, a.w_full]}> style={[a.relative, a.w_full]}>
{({hovered, pressed}) => ( {({hovered, pressed}) => (
<View <>
style={[ <SubtleHover hover={hovered || pressed} />
a.flex_1, <View
a.flex_row,
a.align_center,
a.justify_center,
a.px_lg,
a.py_md,
a.gap_sm,
(hovered || pressed) && t.atoms.bg_contrast_25,
]}>
<Text
style={[ style={[
a.leading_snug, a.flex_1,
hovered ? t.atoms.text : t.atoms.text_contrast_medium, a.flex_row,
a.align_center,
a.justify_center,
a.px_lg,
a.py_md,
a.gap_sm,
]}> ]}>
{item.message} <Text style={[a.leading_snug]}>{item.message}</Text>
</Text> {item.isLoadingMore ? (
{item.isLoadingMore ? ( <Loader size="sm" />
<Loader size="sm" /> ) : (
) : ( <ChevronDownIcon size="sm" style={t.atoms.text_contrast_medium} />
<ChevronDownIcon )}
size="sm" </View>
style={hovered ? t.atoms.text : t.atoms.text_contrast_medium} </>
/>
)}
</View>
)} )}
</Button> </Button>
) )
@@ -19,6 +19,7 @@ import {PlusSmall_Stroke2_Corner0_Rounded as Plus} from '#/components/icons/Plus
import {Link} from '#/components/Link' import {Link} from '#/components/Link'
import {MediaInsetBorder} from '#/components/MediaInsetBorder' import {MediaInsetBorder} from '#/components/MediaInsetBorder'
import {useStarterPackLink} from '#/components/StarterPack/StarterPackCard' import {useStarterPackLink} from '#/components/StarterPack/StarterPackCard'
import {SubtleHover} from '#/components/SubtleHover'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import * as bsky from '#/types/bsky' import * as bsky from '#/types/bsky'
@@ -48,75 +49,80 @@ export function StarterPackCard({
.map(item => item.subject) .map(item => item.subject)
return ( return (
<View <Link
style={[ to={link.to}
a.w_full, label={link.label}
a.p_lg, onHoverIn={link.precache}
a.gap_md, onPress={link.precache}>
a.border, {s => (
a.rounded_sm, <>
a.overflow_hidden, <SubtleHover hover={s.hovered || s.pressed} />
t.atoms.border_contrast_low,
]}>
<View aria-hidden style={[a.absolute, a.inset_0, a.z_40]}>
<Link
to={link.to}
label={link.label}
style={[a.absolute, a.inset_0]}
onHoverIn={link.precache}
onPress={link.precache}>
<View />
</Link>
</View>
<AvatarStack <View
profiles={profiles ?? []} style={[
numPending={profileCount} a.w_full,
total={view.list?.listItemCount} a.p_lg,
/> a.gap_md,
a.border,
a.rounded_sm,
a.overflow_hidden,
t.atoms.border_contrast_low,
]}>
<AvatarStack
profiles={profiles ?? []}
numPending={profileCount}
total={view.list?.listItemCount}
/>
<View <View
style={[ style={[
a.w_full, a.w_full,
a.flex_row, a.flex_row,
a.align_start, a.align_start,
a.gap_lg, a.gap_lg,
web({ web({
position: 'static', position: 'static',
zIndex: 'unset', zIndex: 'unset',
}), }),
]}> ]}>
<View style={[a.flex_1]}> <View style={[a.flex_1]}>
<Text <Text
emoji emoji
style={[a.text_md, a.font_bold, a.leading_snug]} style={[a.text_md, a.font_bold, a.leading_snug]}
numberOfLines={1}> numberOfLines={1}>
{view.record.name} {view.record.name}
</Text> </Text>
<Text <Text
emoji emoji
style={[a.text_sm, a.leading_snug, t.atoms.text_contrast_medium]} style={[
numberOfLines={1}> a.text_sm,
{view.creator?.did === currentAccount?.did a.leading_snug,
? _(msg`By you`) t.atoms.text_contrast_medium,
: _(msg`By ${sanitizeHandle(view.creator.handle, '@')}`)} ]}
</Text> numberOfLines={1}>
</View> {view.creator?.did === currentAccount?.did
<Link ? _(msg`By you`)
to={link.to} : _(msg`By ${sanitizeHandle(view.creator.handle, '@')}`)}
label={link.label} </Text>
onHoverIn={link.precache} </View>
onPress={link.precache} <Link
variant="solid" to={link.to}
color="secondary" label={link.label}
size="small" onHoverIn={link.precache}
style={[a.z_50]}> onPress={link.precache}
<ButtonText> variant="solid"
<Trans>Open pack</Trans> color="secondary"
</ButtonText> size="small"
</Link> style={[a.z_50]}>
</View> <ButtonText>
</View> <Trans>Open pack</Trans>
</ButtonText>
</Link>
</View>
</View>
</>
)}
</Link>
) )
} }
@@ -17,6 +17,7 @@ import {atoms as a} from '#/alf'
import {Button} from '#/components/Button' import {Button} from '#/components/Button'
import * as ProfileCard from '#/components/ProfileCard' import * as ProfileCard from '#/components/ProfileCard'
import {boostInterests, Tabs} from '#/components/ProgressGuide/FollowDialog' import {boostInterests, Tabs} from '#/components/ProgressGuide/FollowDialog'
import {SubtleHover} from '#/components/SubtleHover'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
import type * as bsky from '#/types/bsky' import type * as bsky from '#/types/bsky'
@@ -133,10 +134,7 @@ let Tab = ({
a.py_sm, a.py_sm,
a.border, a.border,
active || hovered || pressed || focused active || hovered || pressed || focused
? [ ? [t.atoms.bg_contrast_25, t.atoms.border_contrast_medium]
t.atoms.bg_contrast_25,
{borderColor: t.atoms.bg_contrast_25.backgroundColor},
]
: [t.atoms.bg, t.atoms.border_contrast_low], : [t.atoms.bg, t.atoms.border_contrast_low],
]}> ]}>
<Text <Text
@@ -187,48 +185,50 @@ let SuggestedProfileCard = ({
) )
}}> }}>
{s => ( {s => (
<View <>
style={[ <SubtleHover hover={s.hovered || s.pressed} />
a.flex_1, <View
a.w_full, style={[
a.py_lg, a.flex_1,
a.px_lg, a.w_full,
a.border_t, a.py_lg,
t.atoms.border_contrast_low, a.px_lg,
(s.hovered || s.pressed) && [t.atoms.bg_contrast_25], a.border_t,
]}> t.atoms.border_contrast_low,
<ProfileCard.Outer> ]}>
<ProfileCard.Header> <ProfileCard.Outer>
<ProfileCard.Avatar <ProfileCard.Header>
profile={profile} <ProfileCard.Avatar
moderationOpts={moderationOpts} profile={profile}
/> moderationOpts={moderationOpts}
<ProfileCard.NameAndHandle />
profile={profile} <ProfileCard.NameAndHandle
moderationOpts={moderationOpts} profile={profile}
/> moderationOpts={moderationOpts}
<ProfileCard.FollowButton />
profile={profile} <ProfileCard.FollowButton
moderationOpts={moderationOpts} profile={profile}
withIcon={false} moderationOpts={moderationOpts}
logContext="ExploreSuggestedAccounts" withIcon={false}
onFollow={() => { logContext="ExploreSuggestedAccounts"
logger.metric( onFollow={() => {
'suggestedUser:follow', logger.metric(
{ 'suggestedUser:follow',
logContext: 'Explore', {
location: 'Card', logContext: 'Explore',
recId, location: 'Card',
position, recId,
}, position,
{statsig: true}, },
) {statsig: true},
}} )
/> }}
</ProfileCard.Header> />
<ProfileCard.Description profile={profile} numberOfLines={2} /> </ProfileCard.Header>
</ProfileCard.Outer> <ProfileCard.Description profile={profile} numberOfLines={2} />
</View> </ProfileCard.Outer>
</View>
</>
)} )}
</ProfileCard.Link> </ProfileCard.Link>
) )
@@ -17,6 +17,7 @@ import {type Props as SVGIconProps} from '#/components/icons/common'
import {Flame_Stroke2_Corner1_Rounded as FlameIcon} from '#/components/icons/Flame' import {Flame_Stroke2_Corner1_Rounded as FlameIcon} from '#/components/icons/Flame'
import {Trending3_Stroke2_Corner1_Rounded as TrendingIcon} from '#/components/icons/Trending' import {Trending3_Stroke2_Corner1_Rounded as TrendingIcon} from '#/components/icons/Trending'
import {Link} from '#/components/Link' import {Link} from '#/components/Link'
import {SubtleHover} from '#/components/SubtleHover'
import {Text} from '#/components/Typography' import {Text} from '#/components/Typography'
const TOPIC_COUNT = 5 const TOPIC_COUNT = 5
@@ -92,15 +93,8 @@ export function TrendRow({
PressableComponent={Pressable}> PressableComponent={Pressable}>
{({hovered, pressed}) => ( {({hovered, pressed}) => (
<> <>
<View <SubtleHover hover={hovered || pressed} />
style={[ <View style={[gutters, a.w_full, a.py_lg, a.flex_row, a.gap_2xs]}>
gutters,
a.w_full,
a.py_lg,
a.flex_row,
a.gap_2xs,
(hovered || pressed) && t.atoms.bg_contrast_25,
]}>
<View style={[a.flex_1, a.gap_xs]}> <View style={[a.flex_1, a.gap_xs]}>
<View style={[a.flex_row]}> <View style={[a.flex_row]}>
<Text <Text