@gluestack-ui/actionsheet
Version:
A universal headless actionsheet component for React Native, Next.js & React
118 lines (117 loc) • 6.02 kB
JSX
/* 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;