Skip to content

Instantly share code, notes, and snippets.

@moxen-dev
Last active March 22, 2023 21:02
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save moxen-dev/8808a943931f755dbd406a67e0d9a316 to your computer and use it in GitHub Desktop.
Save moxen-dev/8808a943931f755dbd406a67e0d9a316 to your computer and use it in GitHub Desktop.
Bottom Sheet using react navigation gesture handler that produces a android freezing.
import { usePathname, useSearchParams } from "expo-router";
import { createContext, forwardRef, memo, ReactElement, useCallback, useContext, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react";
import { Dimensions, KeyboardAvoidingView, StyleSheet, View } from "react-native";
import { Gesture, GestureDetector } from 'react-native-gesture-handler';
import Animated, { Extrapolate, interpolate, useAnimatedStyle, useSharedValue, withSpring } from 'react-native-reanimated';
import { React } from "sentry-expo";
import { useStore } from "../../../../store";
import colors from "../../../../styles/colors";
const { height: SCREEN_HEIGHT } = Dimensions.get("window");
const MAX_TRANSLATE_Y = -SCREEN_HEIGHT + 50;
export default function CustomBottomSheetComponent() {
const [content, setContent] = useState<ReactElement>(<View></View>);
const pathname = usePathname();
const searchParams = useSearchParams();
const ref = useRef<BottomSheetPanelRefProps>(null);
const setBottomSheet = useStore((state) => state.setBottomSheet);
const open = useCallback((content: ReactElement, height: number) => {
if (typeof height !== "number") height = 0;
ref?.current?.scrollTo(-SCREEN_HEIGHT * height);
setContent(content);
}, []);
const close = useCallback(() => {
ref?.current?.scrollTo(SCREEN_HEIGHT);
}, []);
useEffect(() => {
setBottomSheet(open, close);
}, []);
useEffect(() => {
close();
}, [pathname, searchParams]);
return (
<BottomSheetPanel ref={ref}>
{content}
</BottomSheetPanel>
);
};
type BottomSheetPanelProps = {
children: ReactElement;
};
type BottomSheetPanelRefProps = {
scrollTo: (destination: number) => void;
};
const BottomSheetPanel = forwardRef<BottomSheetPanelRefProps, BottomSheetPanelProps>(({ children }, ref) => {
const translateY = useSharedValue(0);
const context = useSharedValue({ y: 0 });
const scrollTo = useCallback((destination: number) => {
'worklet';
translateY.value = destination;
}, []);
useImperativeHandle(ref, () => ({ scrollTo }), [scrollTo]);
const gesture = useMemo(() => Gesture.Pan()
.onStart(() => {
context.value = { y: translateY.value };
})
.onUpdate((event) => {
translateY.value = event.translationY + context.value.y;
translateY.value = Math.max(translateY.value, MAX_TRANSLATE_Y);
})
.onEnd(() => {
if (translateY.value > -SCREEN_HEIGHT / 3) {
scrollTo(0);
} else if (translateY.value < -SCREEN_HEIGHT / 1.5) {
scrollTo(MAX_TRANSLATE_Y);
};
}), []);
const animatedBottomSheetStyle = useAnimatedStyle(() => {
const borderRadius = interpolate(
translateY.value,
[MAX_TRANSLATE_Y + 50, MAX_TRANSLATE_Y],
[25, 5],
Extrapolate.CLAMP,
);
return {
borderRadius,
transform: [{
translateY: translateY.value
}],
};
});
// useEffect(() => {
// scrollTo(-SCREEN_HEIGHT / 3);
// }, []);
if (!children) return null;
return (
<GestureDetector gesture={gesture}>
<Animated.View style={[styles.container, animatedBottomSheetStyle]}>
<View style={styles.line} />
{children}
</Animated.View>
</GestureDetector>
);
});
const styles = StyleSheet.create({
container: {
backgroundColor: colors.black,
height: SCREEN_HEIGHT,
width: "100%",
position: "absolute",
top: SCREEN_HEIGHT,
borderRadius: 25,
},
line: {
backgroundColor: colors.Slate[500],
width: 75,
height: 4,
alignSelf: "center",
marginVertical: 15,
borderRadius: 2,
}
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment