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'
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',
})
+87 -30
View File
@@ -3,6 +3,7 @@ import {
FlatList,
FlatListProps,
LayoutAnimation,
LayoutChangeEvent,
ListRenderItemInfo,
Pressable,
StyleSheet,
@@ -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'
@@ -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?: {
@@ -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>
)
}
@@ -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>
@@ -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}>
@@ -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>
)
}
@@ -315,6 +371,8 @@ const styles = StyleSheet.create({
marginHorizontal: 5,
},
flatListOuter: {
flexDirection: 'row',
alignItems: 'center',
height: 100,
},
flatListContainer: {
@@ -325,6 +383,5 @@ const styles = StyleSheet.create({
const commonFlatListProps: Partial<FlatListProps<any>> = {
horizontal: true,
contentContainerStyle: styles.flatListContainer,
showsHorizontalScrollIndicator: false,
}