import { Platform } from 'react-native'; import type { SharedValue } from 'react-native-reanimated'; import { FadeIn, FadeOut, interpolate, useAnimatedStyle } from 'react-native-reanimated'; import { useAnimationSettings } from '../../providers/animation-settings'; import type { PopupOverlayAnimation } from '../types'; import { getAnimationState, getAnimationValueProperty, getIsAnimationDisabledValue, } from '../utils'; const IS_WEB = Platform.OS === 'web'; export interface UsePopupOverlayAnimationOptions { /** Animation progress shared value (0=idle, 1=open, 2=close) */ progress?: SharedValue; /** Dragging state shared value */ isDragging?: SharedValue; /** Gesture release animation running state (for components with swipe gestures) */ isGestureReleaseAnimationRunning?: SharedValue; /** Animation configuration for overlay */ animation?: PopupOverlayAnimation; } /** * Animation hook for popup overlay (backdrop) components. * Shared by Dialog, Select, BottomSheet, Popover, etc. * * Returns progress-based opacity style and entering/exiting layout animations. * On web, entering/exiting are undefined (CSS transitions drive the visuals). */ export function usePopupOverlayAnimation(options: UsePopupOverlayAnimationOptions) { const { progress, isDragging, isGestureReleaseAnimationRunning, animation } = options; const animationSettingsContext = useAnimationSettings(); const isAllAnimationsDisabled = animationSettingsContext?.isAllAnimationsDisabled; const { animationConfig, isAnimationDisabled } = getAnimationState(animation); const isAnimationDisabledValue = getIsAnimationDisabledValue({ isAnimationDisabled, isAllAnimationsDisabled, }); const opacityValue = getAnimationValueProperty({ animationValue: animationConfig?.opacity, property: 'value', defaultValue: [0, 1, 0] as [number, number, number], }); const rContainerStyle = useAnimatedStyle(() => { if (progress?.get() === undefined) { return {}; } if (isAnimationDisabledValue) { return { opacity: progress.get() > 0 ? 1 : 0, }; } if ((isDragging?.get() || isGestureReleaseAnimationRunning?.get()) && progress.get() <= 1) { return { opacity: 1, }; } return { opacity: interpolate(progress.get(), [0, 1, 2], opacityValue), }; }); const enteringValue = animationConfig?.entering ?? (isAnimationDisabledValue ? undefined : FadeIn.duration(200)); const exitingValue = animationConfig?.exiting ?? (isAnimationDisabledValue ? undefined : FadeOut.duration(150)); return { rContainerStyle, entering: isAnimationDisabledValue || IS_WEB ? undefined : enteringValue, exiting: isAnimationDisabledValue || IS_WEB ? undefined : exitingValue, }; }