import * as React from 'react'; import { StyleSheet, View, type ViewStyle } from 'react-native'; import useLatestCallback from 'use-latest-callback'; import type { DrawerProps } from '../types'; import { DrawerProgressContext } from '../utils/DrawerProgressContext'; import { getDrawerWidthWeb } from '../utils/getDrawerWidth'; import { useFakeSharedValue } from '../utils/useFakeSharedValue'; import { Overlay } from './Overlay'; export function Drawer({ direction = 'ltr', drawerPosition = direction === 'rtl' ? 'right' : 'left', drawerStyle, drawerType = 'front', onClose, onTransitionStart, onTransitionEnd, open, overlayStyle, overlayAccessibilityLabel, renderDrawerContent, children, style, }: DrawerProps) { const drawerWidth = getDrawerWidthWeb({ drawerStyle, }); const progress = useFakeSharedValue(open ? 1 : 0); React.useEffect(() => { progress.value = open ? 1 : 0; }, [open, progress]); const drawerRef = React.useRef(null); const onTransitionStartLatest = useLatestCallback((e: TransitionEvent) => { if (e.target === e.currentTarget && e.propertyName === 'transform') { onTransitionStart?.(open === false); } }); const onTransitionEndLatest = useLatestCallback((e: TransitionEvent) => { if (e.target === e.currentTarget && e.propertyName === 'transform') { onTransitionEnd?.(open === false); } }); React.useEffect(() => { const element = drawerRef.current as HTMLDivElement | null; element?.addEventListener('transitionstart', onTransitionStartLatest); element?.addEventListener('transitionend', onTransitionEndLatest); return () => { element?.removeEventListener('transitionstart', onTransitionStartLatest); element?.removeEventListener('transitionend', onTransitionEndLatest); }; }, [onTransitionEndLatest, onTransitionStartLatest]); const isOpen = drawerType === 'permanent' ? true : open; const isRight = drawerPosition === 'right'; const drawerTranslateX = // The drawer stays in place at open position when `drawerType` is `back` open || drawerType === 'back' ? drawerPosition === 'left' ? '100%' : '-100%' : 0; const drawerAnimatedStyle = drawerType !== 'permanent' ? { transition: 'transform 0.3s', transform: `translateX(${drawerTranslateX})`, } : null; const contentTranslateX = open ? // The screen content stays in place when `drawerType` is `front` drawerType === 'front' ? 0 : `calc(${drawerWidth} * ${drawerPosition === 'left' ? 1 : -1})` : 0; const contentAnimatedStyle = drawerType !== 'permanent' ? { transition: 'transform 0.3s', transform: `translateX(${contentTranslateX})`, } : null; const drawerElement = ( {renderDrawerContent()} ); const mainContent = ( {children} {drawerType !== 'permanent' ? ( onClose()} style={overlayStyle} accessibilityLabel={overlayAccessibilityLabel} /> ) : null} ); return ( {!isRight && drawerElement} {mainContent} {isRight && drawerElement} ); } const styles = StyleSheet.create({ container: { flex: 1, flexDirection: 'row', }, drawer: { top: 0, bottom: 0, maxWidth: '100%', backgroundColor: 'white', }, content: { flex: 1, }, });