import { Animated, StyleSheet } from 'react-native' import { useMemo, useCallback } from 'use-memo-one' import React, { ReactNode, useEffect, useRef, memo } from 'react' import { State, Directions, FlingGestureHandler } from 'react-native-gesture-handler' import type { SharedProps, ModalfyParams, ModalStackItem, ModalEventName, ModalEventCallback, ModalPendingClosingAction, ModalOnCloseEventCallback, ModalOnAnimateEventCallback, } from '../types' import { getStackItemOptions, vh } from '../utils' type Props
= SharedProps
& { zIndex: number position: number hideBackdrop: () => void stackItem: ModalStackItem
wasOpenCallbackCalled: boolean wasClosedByBackdropPress: boolean pendingClosingAction?: ModalPendingClosingAction } const StackItem =
({ stack, zIndex, getParam, position, stackItem, openModal, closeModal, closeModals, hideBackdrop, currentModal, closeAllModals, eventListeners, clearListeners, registerListener, removeClosingAction, pendingClosingAction, wasOpenCallbackCalled, wasClosedByBackdropPress, }: Props
) => {
const { animatedValue, translateY } = useMemo(
() => ({
animatedValue: new Animated.Value(-1),
translateY: new Animated.Value(0),
}),
[],
)
const animatedListenerId = useRef ) => void,
modalStackItemCallback?: () => void,
) => {
if (!closeModalCallback && animationIn) {
animationIn(animatedValue, toValue, modalStackItemCallback)
} else if (closeModalCallback && animationOut) {
animationOut(animatedValue, toValue, () => {
closeModalCallback(stackItem)
modalStackItemCallback?.()
})
} else {
Animated.timing(animatedValue, {
toValue,
useNativeDriver: true,
...(closeModalCallback ? animateOutConfig : animateInConfig),
}).start(({ finished }) => {
if (finished) {
closeModalCallback?.(stackItem)
modalStackItemCallback?.()
}
})
}
},
[stackItem, animationIn, animationOut, animatedValue, animateInConfig, animateOutConfig],
)
const closeStackItem = useCallback(
(modalName, callback?: () => void) => {
const onCloseFns = () => {
onCloseListener.current({ type: 'closeModal', origin: wasClosedByBackdropPress ? 'backdrop' : 'default' })
closeModal(modalName)
callback?.()
}
if (stack.openedItemsSize === 1) hideBackdrop()
if (!modalName || modalName === currentModal) {
updateAnimatedValue(position - 1, onCloseFns)
} else onCloseFns()
},
[
currentModal,
closeModal,
hideBackdrop,
onCloseListener,
position,
stack.openedItemsSize,
updateAnimatedValue,
wasClosedByBackdropPress,
],
)
const closeStackItems = useCallback(
(closingElement, callback?: () => void) => {
const onCloseFns = () => {
onCloseListener.current({ type: 'closeModals', origin: 'default' })
let output = closeModals(closingElement)
callback?.()
return output
}
if (stack.openedItemsSize === 1) hideBackdrop()
if (closingElement === currentModal && position === 1) {
return updateAnimatedValue(position - 1, onCloseFns)
} else return onCloseFns()
},
[closeModals, currentModal, hideBackdrop, position, stack.openedItemsSize, updateAnimatedValue],
)
const closeAllStackItems = useCallback(
(callback?: () => void) => {
if (stack.openedItemsSize === 1) hideBackdrop()
updateAnimatedValue(position - 1, () => {
onCloseListener.current({ type: 'closeAllModals', origin: wasClosedByBackdropPress ? 'backdrop' : 'default' })
closeAllModals()
callback?.()
})
},
[closeAllModals, hideBackdrop, position, stack.openedItemsSize, updateAnimatedValue, wasClosedByBackdropPress],
)
const onFling = useCallback(
({ nativeEvent }) => {
if (!disableFlingGesture && nativeEvent.oldState === State.ACTIVE) {
const toValue = verticalPosition === 'top' ? vh(-100) : vh(100)
if (stack.openedItemsSize === 1) hideBackdrop()
Animated.timing(translateY, {
toValue,
useNativeDriver: true,
...animateOutConfig,
}).start(({ finished }) => {
if (finished) {
onCloseListener.current({ type: 'closeModal', origin: 'fling' })
closeModal(stackItem)
}
})
}
},
[
animateOutConfig,
closeModal,
disableFlingGesture,
hideBackdrop,
stack.openedItemsSize,
stackItem,
translateY,
verticalPosition,
],
)
const renderAnimatedComponent = (): ReactNode => {
const Component = stackItem.component
const addListener = (eventName: ModalEventName, handler: ModalEventCallback) =>
registerListener(stackItem.hash, eventName, handler)
const removeAllListeners = () => clearListeners(stackItem.hash)
return (