feat: whyrating - initial project from turbostarter boilerplate
This commit is contained in:
139
packages/ui/mobile/src/components/slider.tsx
Normal file
139
packages/ui/mobile/src/components/slider.tsx
Normal file
@@ -0,0 +1,139 @@
|
||||
import { createContext, useContext } from "react";
|
||||
import { Dimensions, View } from "react-native";
|
||||
import { Gesture, GestureDetector } from "react-native-gesture-handler";
|
||||
import Animated, {
|
||||
useSharedValue,
|
||||
useAnimatedScrollHandler,
|
||||
useAnimatedStyle,
|
||||
interpolate,
|
||||
Extrapolation,
|
||||
} from "react-native-reanimated";
|
||||
|
||||
import { cn } from "@turbostarter/ui";
|
||||
|
||||
import type { FlatList } from "react-native";
|
||||
import type { ViewProps } from "react-native";
|
||||
import type {
|
||||
SharedValue,
|
||||
ScrollHandlerProcessed,
|
||||
FlatListPropsWithLayout,
|
||||
} from "react-native-reanimated";
|
||||
|
||||
interface SliderContextType {
|
||||
threshold: number;
|
||||
scrollX: SharedValue<number> | null;
|
||||
onScroll: ScrollHandlerProcessed<Record<string, unknown>>;
|
||||
}
|
||||
|
||||
const SliderContext = createContext<SliderContextType>({
|
||||
threshold: Dimensions.get("window").width,
|
||||
scrollX: null,
|
||||
onScroll: () => null,
|
||||
});
|
||||
|
||||
const Slider = ({
|
||||
className,
|
||||
threshold = Dimensions.get("window").width,
|
||||
...props
|
||||
}: ViewProps & { threshold?: number }) => {
|
||||
const scrollX = useSharedValue(0);
|
||||
|
||||
const onScroll = useAnimatedScrollHandler({
|
||||
onScroll: ({ contentOffset: { x } }) => {
|
||||
scrollX.value = x;
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<SliderContext.Provider value={{ scrollX, onScroll, threshold }}>
|
||||
<View {...props} className={cn("flex-1 items-center gap-4", className)} />
|
||||
</SliderContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
const SliderList = <ItemT,>(
|
||||
props: FlatListPropsWithLayout<ItemT> & {
|
||||
ref?: React.ForwardedRef<FlatList>;
|
||||
},
|
||||
) => {
|
||||
const { onScroll, threshold } = useContext(SliderContext);
|
||||
const native = Gesture.Native();
|
||||
|
||||
return (
|
||||
<GestureDetector gesture={native}>
|
||||
<Animated.FlatList
|
||||
onScroll={onScroll}
|
||||
horizontal
|
||||
showsHorizontalScrollIndicator={false}
|
||||
initialNumToRender={1}
|
||||
maxToRenderPerBatch={1}
|
||||
scrollEventThrottle={16}
|
||||
decelerationRate="fast"
|
||||
snapToAlignment="start"
|
||||
pagingEnabled
|
||||
snapToInterval={threshold}
|
||||
{...props}
|
||||
/>
|
||||
</GestureDetector>
|
||||
);
|
||||
};
|
||||
|
||||
const SliderListItem = ({
|
||||
index,
|
||||
style,
|
||||
...props
|
||||
}: ViewProps & { index: number }) => {
|
||||
const { scrollX, threshold } = useContext(SliderContext);
|
||||
const opacity = useAnimatedStyle(() => ({
|
||||
opacity: interpolate(
|
||||
scrollX?.value ?? 0,
|
||||
[(index - 1) * threshold, index * threshold, (index + 1) * threshold],
|
||||
[0, 1, 0],
|
||||
Extrapolation.CLAMP,
|
||||
),
|
||||
}));
|
||||
|
||||
return <Animated.View style={[style, opacity]} {...props} />;
|
||||
};
|
||||
|
||||
const SliderPaginationDots = ({ className, ...props }: ViewProps) => {
|
||||
return <View className={cn("flex-row gap-1.5", className)} {...props} />;
|
||||
};
|
||||
|
||||
const SliderPaginationDot = ({
|
||||
className,
|
||||
index = 0,
|
||||
...props
|
||||
}: ViewProps & { index?: number }) => {
|
||||
const { scrollX, threshold } = useContext(SliderContext);
|
||||
|
||||
const animatedDot = useAnimatedStyle(() => ({
|
||||
opacity: interpolate(
|
||||
scrollX?.value ?? 0,
|
||||
[(index - 1) * threshold, index * threshold, (index + 1) * threshold],
|
||||
[0.7, 1, 0.7],
|
||||
),
|
||||
width: interpolate(
|
||||
scrollX?.value ?? 0,
|
||||
[(index - 1) * threshold, index * threshold, (index + 1) * threshold],
|
||||
[11, 28, 11],
|
||||
Extrapolation.CLAMP,
|
||||
),
|
||||
}));
|
||||
|
||||
return (
|
||||
<Animated.View
|
||||
className={cn("bg-primary h-3 rounded-full opacity-50", className)}
|
||||
style={animatedDot}
|
||||
{...props}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export {
|
||||
Slider,
|
||||
SliderList,
|
||||
SliderListItem,
|
||||
SliderPaginationDots,
|
||||
SliderPaginationDot,
|
||||
};
|
||||
Reference in New Issue
Block a user