scrollable horizontal flatlist on web

This commit is contained in:
Hailey
2024-02-14 15:29:11 -08:00
parent b6dd957900
commit 132f99346e
2 changed files with 88 additions and 31 deletions
+1 -1
View File
@@ -1,5 +1,5 @@
import {createSinglePathSVG} from './TEMPLATE' 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', 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',
}) })
+87 -30
View File
@@ -3,6 +3,7 @@ import {
FlatList, FlatList,
FlatListProps, FlatListProps,
LayoutAnimation, LayoutAnimation,
LayoutChangeEvent,
ListRenderItemInfo, ListRenderItemInfo,
Pressable, Pressable,
StyleSheet, StyleSheet,
@@ -22,7 +23,11 @@ import {
Description, Description,
OnboardingControls, OnboardingControls,
} from '#/screens/Onboarding/Layout' } 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 {IconCircle} from '#/components/IconCircle'
import {Image} from 'expo-image' import {Image} from 'expo-image'
import {Emoji, EmojiName, emojiItems, emojiNames} from './types' import {Emoji, EmojiName, emojiItems, emojiNames} from './types'
@@ -31,8 +36,7 @@ import {
PlaceholderCanvas, PlaceholderCanvas,
PlaceholderCanvasRef, PlaceholderCanvasRef,
} from '#/screens/Onboarding/StepProfile/PlaceholderCanvas' } from '#/screens/Onboarding/StepProfile/PlaceholderCanvas'
import {Text} from '#/components/Typography' import {useWebMediaQueries} from 'lib/hooks/useWebMediaQueries'
import {HITSLOP_10} from 'lib/constants'
interface Avatar { interface Avatar {
image?: { image?: {
@@ -157,30 +161,29 @@ function AvatarCircle() {
if (avatar.image) { if (avatar.image) {
return ( return (
<Pressable <View>
accessibilityRole="button"
hitSlop={HITSLOP_10}
onPress={onPressRemoveAvatar}>
<Image <Image
source={avatar.image.path} source={avatar.image.path}
style={[styles.imageContainer, t.atoms.border_contrast_high]} style={[styles.imageContainer, t.atoms.border_contrast_high]}
accessibilityIgnoresInvertColors accessibilityIgnoresInvertColors
/> />
<View <Pressable
accessibilityRole="button"
style={[ style={[
a.absolute, a.absolute,
a.rounded_full, a.rounded_full,
a.align_center, a.align_center,
a.justify_center, a.justify_center,
a.border, a.border,
a.shadow_lg,
t.atoms.border_contrast_high, t.atoms.border_contrast_high,
t.atoms.bg_contrast_300, t.atoms.bg_contrast_300,
{height: 40, width: 40, bottom: 5, right: 5}, {height: 40, width: 40, bottom: 5, right: 5},
]}> ]}
{/* TODO Get a trash icon for alf */} onPress={onPressRemoveAvatar}>
<Text style={[a.text_4xl, {color: t.palette.white}]}>x</Text> <Times size="lg" style={{color: 'white'}} />
</View> </Pressable>
</Pressable> </View>
) )
} }
@@ -189,7 +192,9 @@ function AvatarCircle() {
style={[ style={[
styles.imageContainer, styles.imageContainer,
t.atoms.border_contrast_high, t.atoms.border_contrast_high,
{backgroundColor: avatar.backgroundColor}, {
backgroundColor: avatar.backgroundColor,
},
]}> ]}>
<Icon height={85} width={85} style={{color: 'white'}} /> <Icon height={85} width={85} style={{color: 'white'}} />
</View> </View>
@@ -263,7 +268,7 @@ function EmojiItem({emojiName}: {emojiName: EmojiName}) {
styles.paletteContainer, styles.paletteContainer,
t.atoms.border_contrast_high, t.atoms.border_contrast_high,
{ {
borderWidth: avatar.placeholder ? 4 : 2, borderWidth: avatar.placeholder.name === emojiName ? 4 : 2,
}, },
]} ]}
onPress={onPress}> onPress={onPress}>
@@ -276,25 +281,76 @@ function emojiRenderItem({item}: ListRenderItemInfo<EmojiName>) {
} }
function Items({type}: {type: 'emojis' | 'colors'}) { function Items({type}: {type: 'emojis' | 'colors'}) {
if (type === 'colors') { const t = useTheme()
return ( const {isTabletOrDesktop} = useWebMediaQueries()
<View style={styles.flatListOuter}>
<FlatList<Color> const maxWidth = React.useRef(0)
data={colors} const width = React.useRef(0)
renderItem={colorRenderItem} const page = React.useRef(0)
{...commonFlatListProps} const ref = React.useRef<FlatList>(null)
/>
</View> 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 ( return (
<View style={styles.flatListOuter}> <View style={styles.flatListOuter}>
<FlatList<EmojiName> {isTabletOrDesktop && (
data={emojiNames} <Pressable
renderItem={emojiRenderItem} 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} {...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> </View>
) )
} }
@@ -315,6 +371,8 @@ const styles = StyleSheet.create({
marginHorizontal: 5, marginHorizontal: 5,
}, },
flatListOuter: { flatListOuter: {
flexDirection: 'row',
alignItems: 'center',
height: 100, height: 100,
}, },
flatListContainer: { flatListContainer: {
@@ -325,6 +383,5 @@ const styles = StyleSheet.create({
const commonFlatListProps: Partial<FlatListProps<any>> = { const commonFlatListProps: Partial<FlatListProps<any>> = {
horizontal: true, horizontal: true,
contentContainerStyle: styles.flatListContainer,
showsHorizontalScrollIndicator: false, showsHorizontalScrollIndicator: false,
} }