import { type PointerEvent, useEffect, useState, type FC, useRef } from "react"; import { TouchableWithoutFeedback, useWindowDimensions, PanResponder, Platform, Animated, Easing, View } from "react-native"; import type ISnackBarProps from "./type"; import stylesheet, { getSnackBarType, useStyles } from "./stylesheet"; import { NCoreUIKitTheme } from "../../core/hooks"; import { useSafeAreaInsets } from "react-native-safe-area-context"; import { type SafePointerEvent, safeGlobal } from "../../types"; import { X as XIcon } from "lucide-react-native"; import { Portal } from "../../helpers/portalize"; import { windowHeight } from "../../utils"; import Button from "../button"; import Text from "../text"; const SnackBar: FC = ({ isCloseOnPressActionButton = true, closeAnimationDelay = 350, openAnimationDelay = 200, spreadBehaviour = "free", autoCloseDelay = 5000, contentContainerStyle, isCloseOnPress = true, isFullWidth = false, isShowAction = true, icon: CustomIcon, isInlineSafeArea, type = "neutral", customTheme, children, onClosed, subTitle, onPress, action, style, title, id }) => { const { radiuses, colors, spaces } = NCoreUIKitTheme.useContext(customTheme); const { top } = useSafeAreaInsets(); const [ isMeasured, setIsMeasured ] = useState(false); const { width: windowWidth } = useWindowDimensions(); const contentHeight = useRef(windowHeight); const transformAnim = useRef(new Animated.Value(-contentHeight.current + -top + -spaces.spacingSm)).current; const opacityAnim = useRef(new Animated.Value(0)).current; const currentType = getSnackBarType({ type }); const { contentContainer: contentContainerDynamicStyle, containerObject: containerObjectDynamicStyle, iconContainer: iconContainerDynamicStyle, container: containerDynamicStyle, subTitle: subTitleDynamicStyle, action: actionDynamicStyle, title: titleDynamicStyle } = useStyles({ isInlineSafeArea, safeAreaTop: top, spreadBehaviour, currentType, isFullWidth, radiuses, colors, spaces, type }); useEffect(() => { if(isMeasured) { transformAnim.setValue(-contentHeight.current + -top + -spaces.spacingSm); Animated.parallel([ Animated.timing(opacityAnim, { duration: openAnimationDelay, easing: Easing.linear, useNativeDriver: true, toValue: 1 }), Animated.timing(transformAnim, { duration: openAnimationDelay, easing: Easing.linear, useNativeDriver: true, toValue: 0 }) ]).start(); setTimeout(() => { closeAnimation(); }, autoCloseDelay); } }, [ isMeasured ]); const panResponder = useRef( PanResponder.create({ onMoveShouldSetPanResponderCapture: (_, gestureState) => { const { dy } = gestureState; if(Math.abs(dy) < 20) { return false; } return true; }, onPanResponderTerminationRequest: () => false, onPanResponderMove: (_, gestureState) => { const { moveY, y0, dy } = gestureState; if(moveY > y0) { return; } const op = moveY / y0; opacityAnim.setValue(op); transformAnim.setValue(dy); }, onStartShouldSetPanResponder: () => false, onPanResponderEnd: (_, gestureState) => { const { moveY, y0, vy } = gestureState; if(vy < -1) { closeAnimation(); return; } if(y0 - moveY > 25) { closeAnimation(); return; } Animated.parallel([ Animated.timing(opacityAnim, { duration: openAnimationDelay, easing: Easing.linear, useNativeDriver: true, toValue: 1 }), Animated.timing(transformAnim, { duration: openAnimationDelay, easing: Easing.linear, useNativeDriver: true, toValue: 0 }) ]).start(); }, onShouldBlockNativeResponder: () => true }) ).current; const closeAnimation = (_onClosed?: (props: { id: string; }) => void) => { Animated.parallel([ Animated.timing(transformAnim, { toValue: -contentHeight.current + -top + -spaces.spacingSm, duration: closeAnimationDelay, useNativeDriver: true, easing: Easing.linear }), Animated.timing(opacityAnim, { duration: closeAnimationDelay, easing: Easing.linear, useNativeDriver: true, toValue: 0 }) ]).start(({ finished }) => { if(finished) { if(onClosed) onClosed({ id }); if(_onClosed) _onClosed({ id }); } }); }; const renderIcon = () => { if(!CustomIcon) { return null; } return ; }; const renderContent = () => { return {title} { subTitle ? {subTitle} : null } ; }; const renderAction = () => { if(!isShowAction) { return null; } return