@uiw/react-native
Version:
UIW for React Native
90 lines • 2.96 kB
JavaScript
import React, { useEffect, useMemo, useRef } from 'react';
import { Animated, SafeAreaView, StyleSheet, View } from 'react-native';
import { useTheme } from '@shopify/restyle';
import WheelPickerItem from './WheelPickerItem';
export default function WheelPicker({
data,
value,
indicatorBackgroundColor,
containerStyle,
itemStyle,
itemTextStyle,
itemHeight = 40,
onChange
}) {
const theme = useTheme();
const flatListRef = useRef(null);
const scrollY = useRef(new Animated.Value(0)).current;
const containerHeight = 5 * itemHeight;
const paddedOptions = useMemo(() => {
const array = [...data];
for (let i = 0; i < 2; i++) {
array.unshift(undefined);
array.push(undefined);
}
return array;
}, [data]);
let selectedIndex = data.findIndex(item => item?.value === value);
if (selectedIndex === -1) {
selectedIndex = 0;
}
const offsets = useMemo(() => [...Array(paddedOptions.length)].map((_, i) => i * itemHeight), [paddedOptions, itemHeight]);
const currentScrollIndex = Animated.add(Animated.divide(scrollY, itemHeight), 2);
const handleMomentumScrollEnd = event => {
const offsetY = Math.min(itemHeight * (data.length - 1), Math.max(event.nativeEvent.contentOffset.y, 0));
let index = offsetY / itemHeight + 1;
const currentItem = data[index - 1];
if (currentItem) {
onChange?.(currentItem.value);
}
};
useEffect(() => {
flatListRef.current?.scrollToIndex({
index: selectedIndex,
animated: false
});
}, [selectedIndex]);
return <SafeAreaView style={[styles.container, {
height: containerHeight
}, containerStyle]}>
<View style={[styles.selectedIndicator, {
transform: [{
translateY: -itemHeight / 2
}],
height: itemHeight,
backgroundColor: indicatorBackgroundColor ?? theme.colors.gray100,
borderRadius: 2
}]} />
<Animated.FlatList ref={flatListRef} style={styles.scrollView} showsVerticalScrollIndicator={false} onScroll={Animated.event([{
nativeEvent: {
contentOffset: {
y: scrollY
}
}
}], {
useNativeDriver: true
})} onMomentumScrollEnd={handleMomentumScrollEnd} snapToOffsets={offsets} decelerationRate={'fast'} initialScrollIndex={selectedIndex} getItemLayout={(_, index) => ({
length: itemHeight,
offset: itemHeight * index,
index
})} data={paddedOptions} keyExtractor={(_, index) => index.toString()} renderItem={({
item: option,
index
}) => <WheelPickerItem index={index} option={option} style={itemStyle} textStyle={itemTextStyle} height={itemHeight} currentIndex={currentScrollIndex} visibleRest={2} />} />
</SafeAreaView>;
}
const styles = StyleSheet.create({
container: {
position: 'relative',
flex: 1
},
selectedIndicator: {
position: 'absolute',
width: '100%',
top: '50%'
},
scrollView: {
overflow: 'hidden',
flex: 1
}
});