react-native-reanimated-bottomsheet
Version:
A configurable and performant BottomSheet widget for React Native apps built using Reanimated v2 API.
123 lines (110 loc) • 3.65 kB
JavaScript
/* eslint-disable react-hooks/rules-of-hooks */
import React, { useEffect, useImperativeHandle } from 'react';
import Animated, { useSharedValue, withSpring, useAnimatedStyle, useAnimatedGestureHandler } from 'react-native-reanimated';
import { StyleSheet } from 'react-native';
import { PanGestureHandler } from 'react-native-gesture-handler';
var GESTURE;
(function (GESTURE) {
GESTURE[GESTURE["DRAGGING_UP"] = 0] = "DRAGGING_UP";
GESTURE[GESTURE["DRAGGING_DOWN"] = 1] = "DRAGGING_DOWN";
})(GESTURE || (GESTURE = {}));
const defaultSpringConfig = {
damping: 50,
mass: 0.5,
stiffness: 121.6,
restSpeedThreshold: 0.3
};
export default /*#__PURE__*/React.forwardRef(({
initialSnap = 0,
snapPoints,
renderContent,
renderHeader,
enabledGestureInteraction = true,
hasFixedHeight = false,
springConfig = defaultSpringConfig
}, ref) => {
const maxHeight = snapPoints[snapPoints.length - 1];
const defaultHeight = initialSnap ? snapPoints[initialSnap] : snapPoints[0];
const sheetHeight = hasFixedHeight ? useSharedValue(0) : useSharedValue(defaultHeight);
const gestureDirection = useSharedValue(GESTURE.DRAGGING_UP);
useImperativeHandle(ref, () => ({
close: () => {
sheetHeight.value = withSpring(0, defaultSpringConfig);
},
snapTo: index => {
if (index < snapPoints.length - 1) {
sheetHeight.value = withSpring(snapPoints[index], defaultSpringConfig);
}
}
}));
useEffect(() => {
if (hasFixedHeight) {
sheetHeight.value = withSpring(snapPoints[0], defaultSpringConfig);
} // eslint-disable-next-line react-hooks/exhaustive-deps
}, []);
const getNearestSnap = () => {
'worklet';
let neearestSnapPoint = maxHeight;
for (let snapIndex in snapPoints) {
const numberedSnapIndex = Number(snapIndex);
if (sheetHeight.value - snapPoints[snapIndex] < 0) {
if (gestureDirection.value === GESTURE.DRAGGING_UP) {
neearestSnapPoint = snapPoints[snapIndex];
} else {
const nearestSnapIndex = numberedSnapIndex - 1 >= 0 ? numberedSnapIndex - 1 : numberedSnapIndex;
neearestSnapPoint = snapPoints[nearestSnapIndex];
}
break;
}
}
return neearestSnapPoint;
};
const adjustSnap = _ => {
'worklet';
const nearestSnapPoint = getNearestSnap();
sheetHeight.value = withSpring(nearestSnapPoint, springConfig);
};
const gestureHandler = useAnimatedGestureHandler({
onStart: (_, ctx) => {
ctx.startY = sheetHeight.value;
},
onActive: (event, ctx) => {
if (event.velocityY > 0) {
gestureDirection.value = GESTURE.DRAGGING_DOWN;
} else if (event.velocityY < 0) {
gestureDirection.value = GESTURE.DRAGGING_UP;
}
sheetHeight.value = ctx.startY + event.translationY * -1;
},
onEnd: adjustSnap
});
const animatedStyle = useAnimatedStyle(() => ({
height: sheetHeight.value
}));
return /*#__PURE__*/React.createElement(PanGestureHandler, {
enabled: enabledGestureInteraction,
onGestureEvent: gestureHandler
}, /*#__PURE__*/React.createElement(Animated.View, {
style: [styles.sheetContainer, animatedStyle]
}, /*#__PURE__*/React.createElement(Animated.View, {
style: styles.sheetInnerContainer
}, renderHeader(), renderContent())));
});
const styles = StyleSheet.create({
sheetContainer: {
width: '100%',
position: 'absolute',
bottom: 0,
height: 20,
borderRadius: 4
},
sheetInnerContainer: {
flex: 1,
shadowOpacity: 0.5,
shadowOffset: {
height: -1,
width: 0
}
}
});
//# sourceMappingURL=index.js.map