import GorhomBottomSheet from '@gorhom/bottom-sheet'; import { forwardRef, useMemo } from 'react'; import type { Text as RNText } from 'react-native'; import Animated, { ReduceMotion, useSharedValue, } from 'react-native-reanimated'; import { withUniwind } from 'uniwind'; import { BottomSheetContentContainer, CloseIcon, FullWindowOverlay, } from '../../helpers/components'; import { HeroText } from '../../helpers/components/hero-text'; import { AnimationSettingsProvider, useAnimationSettings, } from '../../helpers/contexts/animation-settings-context'; import { usePopupBottomSheetContentAnimation } from '../../helpers/hooks/use-popup-bottom-sheet-content-animation'; import { usePopupOverlayAnimation } from '../../helpers/hooks/use-popup-overlay-animation'; import { usePopupRootAnimation } from '../../helpers/hooks/use-popup-root-animation'; import { useThemeColor } from '../../helpers/theme'; import * as BottomSheetPrimitives from '../../primitives/bottom-sheet'; import * as BottomSheetPrimitivesTypes from '../../primitives/bottom-sheet/bottom-sheet.types'; import { BottomSheetAnimationProvider, useBottomSheetAnimation, useBottomSheetContentAnimation, } from './bottom-sheet.animation'; import { DISPLAY_NAME } from './bottom-sheet.constants'; import bottomSheetStyles, { styleSheet } from './bottom-sheet.styles'; import type { BottomSheetCloseProps, BottomSheetContentProps, BottomSheetDescriptionProps, BottomSheetOverlayProps, BottomSheetPortalProps, BottomSheetRootProps, BottomSheetTitleProps, BottomSheetTriggerProps, } from './bottom-sheet.types'; const AnimatedOverlay = Animated.createAnimatedComponent( BottomSheetPrimitives.Overlay ); const StyledGorhomBottomSheet = withUniwind(GorhomBottomSheet); const useBottomSheet = BottomSheetPrimitives.useRootContext; // -------------------------------------------------- const BottomSheetRoot = forwardRef< BottomSheetPrimitivesTypes.RootRef, BottomSheetRootProps >( ( { children, closeDelay = 300, isDismissKeyboardOnClose = true, isOpen: isOpenProp, isDefaultOpen, onOpenChange: onOpenChangeProp, animation, ...props }, ref ) => { const { internalIsOpen, componentState, progress, onOpenChange, isAllAnimationsDisabled, } = usePopupRootAnimation({ isOpen: isOpenProp, isDefaultOpen, onOpenChange: onOpenChangeProp, closeDelay, isDismissKeyboardOnClose, animation, }); const animationContextValue = useMemo( () => ({ bottomSheetState: componentState, progress, }), [componentState, progress] ); const animationSettingsContextValue = useMemo( () => ({ isAllAnimationsDisabled, }), [isAllAnimationsDisabled] ); return ( {children} ); } ); // -------------------------------------------------- const BottomSheetTrigger = forwardRef< BottomSheetPrimitivesTypes.TriggerRef, BottomSheetTriggerProps >((props, ref) => { return ; }); // -------------------------------------------------- const BottomSheetPortal = ({ children, ...props }: BottomSheetPortalProps) => { const animationSettingsContext = useAnimationSettings(); const animationContext = useBottomSheetAnimation(); return ( {children} ); }; // -------------------------------------------------- const BottomSheetOverlay = forwardRef< BottomSheetPrimitivesTypes.OverlayRef, BottomSheetOverlayProps >( ( { className, style, animation, isAnimatedStyleActive = true, ...props }, ref ) => { const { progress } = useBottomSheetAnimation(); const isDragging = useSharedValue(false); const overlayClassName = bottomSheetStyles.overlay({ className }); const { rContainerStyle } = usePopupOverlayAnimation({ progress, isDragging, animation, }); const overlayStyle = isAnimatedStyleActive ? [rContainerStyle, style] : style; return ( ); } ); // -------------------------------------------------- const BottomSheetContent = forwardRef< GorhomBottomSheet, BottomSheetContentProps >( ( { children, backgroundClassName, handleIndicatorClassName, contentContainerClassName, contentContainerProps, animationConfigs, animation, ...restProps }, ref ) => { const { onOpenChange } = useBottomSheet(); const { bottomSheetState, progress } = useBottomSheetAnimation(); const { isAnimationDisabledValue } = useBottomSheetContentAnimation({ animation, }); const { animatedIndex } = usePopupBottomSheetContentAnimation({ progress, componentState: bottomSheetState, }); const contentBackgroundClassName = bottomSheetStyles.contentBackground({ className: backgroundClassName, }); const contentHandleIndicatorClassName = bottomSheetStyles.contentHandleIndicator({ className: handleIndicatorClassName, }); const contentContainerClassNameValue = bottomSheetStyles.contentContainer({ className: contentContainerClassName, }); const onClose = () => { onOpenChange(false); restProps.onClose?.(); }; const mergedAnimationConfigs = useMemo( () => ({ ...animationConfigs, reduceMotion: isAnimationDisabledValue ? ReduceMotion.Always : animationConfigs?.reduceMotion, }), [animationConfigs, isAnimationDisabledValue] ); return ( {children} ); } ); // -------------------------------------------------- const BottomSheetClose = forwardRef< BottomSheetPrimitivesTypes.CloseRef, BottomSheetCloseProps >(({ className, iconProps, hitSlop = 12, children, ...props }, ref) => { const themeColorMuted = useThemeColor('muted'); const tvStyles = bottomSheetStyles.close({ className }); return ( {children || ( )} ); }); // -------------------------------------------------- const BottomSheetTitle = forwardRef( ({ className, children, ...props }, ref) => { const { nativeID } = useBottomSheet(); const tvStyles = bottomSheetStyles.label({ className }); return ( {children} ); } ); // -------------------------------------------------- const BottomSheetDescription = forwardRef( ({ className, children, ...props }, ref) => { const { nativeID } = useBottomSheet(); const tvStyles = bottomSheetStyles.description({ className, }); return ( {children} ); } ); // -------------------------------------------------- BottomSheetRoot.displayName = DISPLAY_NAME.ROOT; BottomSheetTrigger.displayName = DISPLAY_NAME.TRIGGER; BottomSheetPortal.displayName = DISPLAY_NAME.PORTAL; BottomSheetOverlay.displayName = DISPLAY_NAME.OVERLAY; BottomSheetContent.displayName = DISPLAY_NAME.CONTENT; BottomSheetClose.displayName = DISPLAY_NAME.CLOSE; BottomSheetTitle.displayName = DISPLAY_NAME.TITLE; BottomSheetDescription.displayName = DISPLAY_NAME.DESCRIPTION; /** * Compound BottomSheet component with sub-components * * @component BottomSheet.Root - Main container that manages open/close state. * Provides the bottom sheet context to child components. * * @component BottomSheet.Trigger - Button or element that opens the bottom sheet. * Accepts any pressable element as children. * * @component BottomSheet.Portal - Portal container for bottom sheet overlay and content. * Renders children in a portal with full window overlay. * * @component BottomSheet.Overlay - Background overlay that covers the screen. * Typically closes the bottom sheet when clicked. * * @component BottomSheet.Content - The bottom sheet content container. * Uses @gorhom/bottom-sheet for rendering. Contains the main bottom sheet UI elements. * * @component BottomSheet.Close - Close button for the bottom sheet. * Can accept custom children or uses default close icon. * * @component BottomSheet.Title - The bottom sheet title text. * Automatically linked for accessibility. * * @component BottomSheet.Description - The bottom sheet description text. * Automatically linked for accessibility. */ const BottomSheet = Object.assign(BottomSheetRoot, { /** @optional Trigger element to open the bottom sheet */ Trigger: BottomSheetTrigger, /** @optional Portal container for overlay and content */ Portal: BottomSheetPortal, /** @optional Background overlay */ Overlay: BottomSheetOverlay, /** @optional Main bottom sheet content container */ Content: BottomSheetContent, /** @optional Close button for the bottom sheet */ Close: BottomSheetClose, /** @optional Bottom sheet title text */ Title: BottomSheetTitle, /** @optional Bottom sheet description text */ Description: BottomSheetDescription, }); export { useBottomSheet, useBottomSheetAnimation }; export default BottomSheet;