Skip to content

Commit

Permalink
scrollable horizontal flatlist on web
Browse files Browse the repository at this point in the history
  • Loading branch information
haileyok committed Feb 14, 2024
1 parent b6dd957 commit 132f993
Show file tree
Hide file tree
Showing 2 changed files with 88 additions and 31 deletions.
2 changes: 1 addition & 1 deletion src/components/icons/Times.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import {createSinglePathSVG} from './TEMPLATE'

export const PlusLarge_Stroke2_Corner0_Rounded = createSinglePathSVG({
export const TimesLarge_Stroke2_Corner0_Rounded = createSinglePathSVG({
path: 'M4.293 4.293a1 1 0 0 1 1.414 0L12 10.586l6.293-6.293a1 1 0 1 1 1.414 1.414L13.414 12l6.293 6.293a1 1 0 0 1-1.414 1.414L12 13.414l-6.293 6.293a1 1 0 0 1-1.414-1.414L10.586 12 4.293 5.707a1 1 0 0 1 0-1.414Z',
})
117 changes: 87 additions & 30 deletions src/screens/Onboarding/StepProfile/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import {
FlatList,
FlatListProps,
LayoutAnimation,
LayoutChangeEvent,
ListRenderItemInfo,
Pressable,
StyleSheet,
Expand All @@ -22,7 +23,11 @@ import {
Description,
OnboardingControls,
} from '#/screens/Onboarding/Layout'
import {ChevronRight_Stroke2_Corner0_Rounded as ChevronRight} from '#/components/icons/Chevron'
import {
ChevronLeft_Stroke2_Corner0_Rounded,
ChevronRight_Stroke2_Corner0_Rounded as ChevronRight,
} from '#/components/icons/Chevron'
import {TimesLarge_Stroke2_Corner0_Rounded as Times} from '#/components/icons/Times'
import {IconCircle} from '#/components/IconCircle'
import {Image} from 'expo-image'
import {Emoji, EmojiName, emojiItems, emojiNames} from './types'
Expand All @@ -31,8 +36,7 @@ import {
PlaceholderCanvas,
PlaceholderCanvasRef,
} from '#/screens/Onboarding/StepProfile/PlaceholderCanvas'
import {Text} from '#/components/Typography'
import {HITSLOP_10} from 'lib/constants'
import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'

interface Avatar {
image?: {
Expand Down Expand Up @@ -157,30 +161,29 @@ function AvatarCircle() {

if (avatar.image) {
return (
<Pressable
accessibilityRole="button"
hitSlop={HITSLOP_10}
onPress={onPressRemoveAvatar}>
<View>
<Image
source={avatar.image.path}
style={[styles.imageContainer, t.atoms.border_contrast_high]}
accessibilityIgnoresInvertColors
/>
<View
<Pressable
accessibilityRole="button"
style={[
a.absolute,
a.rounded_full,
a.align_center,
a.justify_center,
a.border,
a.shadow_lg,
t.atoms.border_contrast_high,
t.atoms.bg_contrast_300,
{height: 40, width: 40, bottom: 5, right: 5},
]}>
{/* TODO Get a trash icon for alf */}
<Text style={[a.text_4xl, {color: t.palette.white}]}>x</Text>
</View>
</Pressable>
]}
onPress={onPressRemoveAvatar}>
<Times size="lg" style={{color: 'white'}} />
</Pressable>
</View>
)
}

Expand All @@ -189,7 +192,9 @@ function AvatarCircle() {
style={[
styles.imageContainer,
t.atoms.border_contrast_high,
{backgroundColor: avatar.backgroundColor},
{
backgroundColor: avatar.backgroundColor,
},
]}>
<Icon height={85} width={85} style={{color: 'white'}} />
</View>
Expand Down Expand Up @@ -263,7 +268,7 @@ function EmojiItem({emojiName}: {emojiName: EmojiName}) {
styles.paletteContainer,
t.atoms.border_contrast_high,
{
borderWidth: avatar.placeholder ? 4 : 2,
borderWidth: avatar.placeholder.name === emojiName ? 4 : 2,
},
]}
onPress={onPress}>
Expand All @@ -276,25 +281,76 @@ function emojiRenderItem({item}: ListRenderItemInfo<EmojiName>) {
}

function Items({type}: {type: 'emojis' | 'colors'}) {
if (type === 'colors') {
return (
<View style={styles.flatListOuter}>
<FlatList<Color>
data={colors}
renderItem={colorRenderItem}
{...commonFlatListProps}
/>
</View>
)
}
const t = useTheme()
const {isTabletOrDesktop} = useWebMediaQueries()

const maxWidth = React.useRef(0)
const width = React.useRef(0)
const page = React.useRef(0)
const ref = React.useRef<FlatList>(null)

const onLayout = React.useCallback((e: LayoutChangeEvent) => {
width.current = e.nativeEvent.layout.width
}, [])

const onContentSizeChanged = React.useCallback((w: number, _: number) => {
maxWidth.current = w
}, [])

const onLeftPress = React.useCallback(() => {
if (page.current === 0) return
page.current -= 1
ref.current?.scrollToOffset({
offset: width.current * page.current - width.current + 50,
})
}, [])

const onRightPress = React.useCallback(() => {
const newOffset = width.current * (page.current + 1) - 50
if (newOffset >= maxWidth.current + width.current) return

page.current += 1
ref.current?.scrollToOffset({
offset: newOffset,
})
}, [])

return (
<View style={styles.flatListOuter}>
<FlatList<EmojiName>
data={emojiNames}
renderItem={emojiRenderItem}
{isTabletOrDesktop && (
<Pressable
onPress={onLeftPress}
style={[
a.align_center,
a.justify_center,
t.atoms.bg_contrast_100,
{height: 40, width: 40, borderRadius: 100},
]}
accessibilityRole="button">
<ChevronLeft_Stroke2_Corner0_Rounded style={t.atoms.text} />
</Pressable>
)}
<FlatList
data={type === 'colors' ? colors : emojiNames}
renderItem={type === 'colors' ? colorRenderItem : emojiRenderItem}
ref={ref}
{...commonFlatListProps}
onLayout={onLayout}
onContentSizeChange={onContentSizeChanged}
/>
{isTabletOrDesktop && (
<Pressable
onPress={onRightPress}
style={[
a.align_center,
a.justify_center,
t.atoms.bg_contrast_100,
{height: 40, width: 40, borderRadius: 100},
]}
accessibilityRole="button">
<ChevronRight style={t.atoms.text} />
</Pressable>
)}
</View>
)
}
Expand All @@ -315,6 +371,8 @@ const styles = StyleSheet.create({
marginHorizontal: 5,
},
flatListOuter: {
flexDirection: 'row',
alignItems: 'center',
height: 100,
},
flatListContainer: {
Expand All @@ -325,6 +383,5 @@ const styles = StyleSheet.create({

const commonFlatListProps: Partial<FlatListProps<any>> = {
horizontal: true,
contentContainerStyle: styles.flatListContainer,
showsHorizontalScrollIndicator: false,
}

0 comments on commit 132f993

Please sign in to comment.