react-native-swipe-up-down
Version:
210 lines (196 loc) • 5.09 kB
JavaScript
import {
Dimensions,
LayoutAnimation,
PanResponder,
Platform,
StyleSheet,
TouchableOpacity,
View,
} from "react-native";
import React, {
forwardRef,
useImperativeHandle,
useRef,
useState,
} from "react";
import SwipeIcon from "./components/SwipeIcon";
import images from "./assets/images";
const MARGIN_TOP = Platform.OS === "ios" ? 24 : 0;
const DEVICE_HEIGHT = Dimensions.get("window").height - MARGIN_TOP;
const CustomAnimation = {
duration: 300,
create: {
type: LayoutAnimation.Types.spring,
property: LayoutAnimation.Properties.scaleY,
springDamping: 0.8,
},
update: {
type: LayoutAnimation.Types.spring,
springDamping: 0.8,
},
};
const SwipeUpDown = forwardRef(
(
{
disablePressToShow,
swipeHeight = 60,
extraMarginTop = MARGIN_TOP,
itemMini = null,
itemFull = null,
style,
onShowMini,
onShowFull,
animation,
disableSwipeIcon,
},
ref
) => {
const SWIPE_HEIGHT = swipeHeight;
let top = SWIPE_HEIGHT;
let height = SWIPE_HEIGHT;
const customStyle = {
style: {
bottom: 0,
top,
height,
},
};
const checkCollapsed = useRef(true);
const viewRef = useRef();
const swipeIconRef = useRef();
const [collapsed, setCollapsed] = useState(true);
const onPanResponderMove = (event, gestureState) => {
if (gestureState.dy > 0 && !checkCollapsed.current) {
// SWIPE DOWN
customStyle.style.top = top + gestureState.dy;
customStyle.style.height = DEVICE_HEIGHT - gestureState.dy;
if (customStyle.style.height <= DEVICE_HEIGHT / 3) {
swipeIconRef.current?.setData({ icon: images.minus });
if (itemMini) {
setCollapsed(true);
}
}
updateNativeProps();
} else if (checkCollapsed.current && gestureState.dy < -swipeHeight) {
// SWIPE UP
top = 0;
customStyle.style.top = DEVICE_HEIGHT + gestureState.dy;
customStyle.style.height = -gestureState.dy + SWIPE_HEIGHT;
swipeIconRef.current?.setData({
icon: images.minus,
showIcon: true,
});
if (customStyle.style.top <= DEVICE_HEIGHT / 2) {
swipeIconRef.current?.setData({
icon: images.arrow_down,
showIcon: true,
});
setCollapsed(false);
}
updateNativeProps();
}
};
const onPanResponderRelease = (event, gestureState) => {
if (gestureState.dy < -100 || gestureState.dy < 100) {
showFull();
} else {
showMini();
}
};
const panResponder = React.useRef(
PanResponder.create({
onMoveShouldSetPanResponder: () => true,
onPanResponderMove,
onPanResponderRelease,
})
).current;
useImperativeHandle(
ref,
() => ({
showFull,
showMini,
}),
[]
);
const updateNativeProps = () => {
switch (animation) {
case "linear":
LayoutAnimation.linear();
break;
case "spring":
LayoutAnimation.configureNext(CustomAnimation);
break;
case "easeInEaseOut":
LayoutAnimation.easeInEaseOut();
break;
case "none":
default:
break;
}
viewRef.current.setNativeProps(customStyle);
};
const showFull = () => {
customStyle.style.top = 0;
customStyle.style.height = DEVICE_HEIGHT;
swipeIconRef.current?.setData({
icon: images.arrow_down,
showIcon: true,
});
updateNativeProps();
setCollapsed(false);
checkCollapsed.current = false;
onShowFull?.();
};
const showMini = () => {
customStyle.style.top = itemMini ? null : DEVICE_HEIGHT;
customStyle.style.height = itemMini ? SWIPE_HEIGHT : 0;
swipeIconRef.current?.setData({ showIcon: false });
updateNativeProps();
setCollapsed(true);
checkCollapsed.current = true;
onShowMini?.();
};
return (
<View
ref={viewRef}
{...panResponder.panHandlers}
style={[
styles.wrapSwipe,
{
height: SWIPE_HEIGHT,
marginTop: extraMarginTop,
},
!itemMini && collapsed && { marginBottom: -SWIPE_HEIGHT },
style,
]}
>
{!disableSwipeIcon && <SwipeIcon ref={swipeIconRef} />}
{collapsed ? (
itemMini ? (
<TouchableOpacity
activeOpacity={0.8}
style={{ height: swipeHeight }}
disabled={disablePressToShow}
onPress={showFull}
>
{itemMini}
</TouchableOpacity>
) : null
) : (
itemFull
)}
</View>
);
}
);
export default SwipeUpDown;
const styles = StyleSheet.create({
wrapSwipe: {
borderTopLeftRadius: 10,
borderTopRightRadius: 10,
position: "absolute",
bottom: 0,
left: 0,
right: 0,
},
});