UNPKG

@gluestack-ui/actionsheet

Version:

A universal headless actionsheet component for React Native, Next.js & React

118 lines (117 loc) 6.02 kB
/* eslint-disable react-native/no-inline-styles */ import React, { forwardRef } from 'react'; import { Animated, Dimensions, AccessibilityInfo, Platform, Keyboard, } from 'react-native'; import { ActionsheetContext } from './context'; import { ActionsheetContentProvider } from './ActionsheetContentContext'; import { OverlayAnimatePresence } from './OverlayAnimatePresence'; import { FocusScope } from '@react-native-aria/focus'; import { mergeRefs, findNodeHandle } from '@gluestack-ui/utils'; import { useDialog } from '@react-native-aria/dialog'; import { usePreventScroll } from '@react-native-aria/overlays'; //dimentions not giving proper window height on web const windowHeight = Platform.OS === 'web' ? typeof window !== 'undefined' ? window.innerHeight : Dimensions.get('screen').height : Dimensions.get('screen').height; function ActionsheetContent(StyledActionsheetContent, AnimatePresence) { return forwardRef(({ children, _experimentalContent = false, focusScope = true, ...props }, ref) => { const { visible, handleClose, trapFocus, initialFocusRef, handleCloseBackdrop, finalFocusRef, snapPoints, preventScroll, } = React.useContext(ActionsheetContext); usePreventScroll({ isDisabled: preventScroll }); const pan = React.useRef(new Animated.ValueXY()).current; const contentSheetHeight = React.useRef(0); const [contentSheetHeightState, setContentSheetHeightState] = React.useState(0); const [animatedViewSheetHeight, setAnimatedViewSheetHeight] = React.useState(0); const animationDefaultConfig = { type: 'timing', duration: 200, }; const handleCloseCallback = React.useCallback(handleClose, [ ActionsheetContext, handleClose, ]); const contentSheetAnimatePosition = React.useMemo(() => { if (!snapPoints) { return animatedViewSheetHeight - contentSheetHeightState; } return windowHeight - snapPoints[0] * windowHeight * 0.01; }, [snapPoints, animatedViewSheetHeight, contentSheetHeightState]); const contentRef = React.useRef(null); React.useEffect(() => { if (contentRef) { const reactTag = findNodeHandle(contentRef.current); if (reactTag) { AccessibilityInfo.setAccessibilityFocus(reactTag); AccessibilityInfo.setAccessibilityFocus(reactTag); AccessibilityInfo.setAccessibilityFocus(reactTag); AccessibilityInfo.setAccessibilityFocus(reactTag); AccessibilityInfo.setAccessibilityFocus(reactTag); AccessibilityInfo.setAccessibilityFocus(reactTag); AccessibilityInfo.setAccessibilityFocus(reactTag); AccessibilityInfo.setAccessibilityFocus(reactTag); } } }, [visible, contentRef]); React.useEffect(() => { if (visible) { Keyboard.dismiss(); if (initialFocusRef && initialFocusRef.current) { initialFocusRef.current.focus(); } } else { if (finalFocusRef && finalFocusRef.current) { finalFocusRef.current.focus(); } } }, [initialFocusRef, finalFocusRef, visible]); const { dialogProps } = useDialog({ ...props }, contentRef); const mergedRef = mergeRefs([ref, contentRef]); if (_experimentalContent) { return (<StyledActionsheetContent transition={animationDefaultConfig} {...props} ref={mergedRef} {...dialogProps} onLayout={(event) => { const { height } = event.nativeEvent.layout; contentSheetHeight.current = height; }}> <ActionsheetContentProvider contentSheetHeight={contentSheetHeight} pan={pan} handleClose={handleCloseCallback} handleCloseBackdrop={handleCloseBackdrop}> {children} </ActionsheetContentProvider> </StyledActionsheetContent>); } return (<Animated.View style={{ transform: [{ translateY: pan.y }], width: '100%', height: '100%', }} onLayout={(event) => { const { height } = event.nativeEvent.layout; setAnimatedViewSheetHeight(height); }} pointerEvents="box-none"> <OverlayAnimatePresence visible={visible} AnimatePresence={AnimatePresence}> <StyledActionsheetContent initial={{ y: windowHeight, }} animate={{ y: contentSheetAnimatePosition, }} exit={{ y: windowHeight, }} transition={animationDefaultConfig} {...props} style={[ props.style, { height: snapPoints ? snapPoints[0] * windowHeight * 0.01 : undefined, }, ]} ref={mergedRef} tabIndex={Platform.OS === 'web' ? 0 : undefined} {...dialogProps} onLayout={(event) => { const { height } = event.nativeEvent.layout; contentSheetHeight.current = height; setContentSheetHeightState(height); }}> <ActionsheetContentProvider contentSheetHeight={contentSheetHeight} pan={pan} handleClose={handleCloseCallback} handleCloseBackdrop={handleCloseBackdrop} snapPoints={snapPoints}> {focusScope ? (<FocusScope contain={trapFocus} autoFocus={visible && !initialFocusRef} restoreFocus={visible && !finalFocusRef}> {children} </FocusScope>) : (<>{children}</>)} </ActionsheetContentProvider> </StyledActionsheetContent> </OverlayAnimatePresence> </Animated.View>); }); } export default ActionsheetContent;