UNPKG

@crossed/sheet

Version:

A Cross Platform(Android & iOS) ActionSheet with a robust and flexible api, native performance and zero dependency code for react native. Create anything you want inside ActionSheet.

1,323 lines 51.6 kB
import { Fragment, jsx, jsxs } from "react/jsx-runtime"; import React, { forwardRef, useCallback, useEffect, useImperativeHandle, useMemo, useRef, useState } from "react"; import { Animated, BackHandler, Dimensions, Easing, Keyboard, Modal, PanResponder, Platform, SafeAreaView, StatusBar, TouchableOpacity, View } from "react-native"; import { GestureHandlerRootView, PanGestureHandler } from "react-native-gesture-handler"; import { DraggableNodesContext, PanGestureRefContext } from "./context"; import EventManager, { actionSheetEventManager } from "./eventmanager"; import { RouterContext, RouterParamsContext, useRouter } from "./hooks/use-router"; import { resolveScrollRef, ScrollState } from "./hooks/use-scroll-handlers"; import useSheetManager from "./hooks/use-sheet-manager"; import { useKeyboard } from "./hooks/useKeyboard"; import { SheetProvider, useProviderContext, useSheetIDContext, useSheetPayload, useSheetRef } from "./provider"; import { getZIndexFromStack, isRenderedOnTop, SheetManager } from "./sheetmanager"; import { styles } from "./styles"; import { getElevation, SUPPORTED_ORIENTATIONS } from "./utils"; const EVENTS_INTERNAL = { safeAreaLayout: "safeAreaLayout" }; var ActionSheet_default = forwardRef( function ActionSheet({ animated = true, closeOnPressBack = true, springOffset = 50, elevation = 5, defaultOverlayOpacity = 0.3, overlayColor = "black", closable = true, closeOnTouchBackdrop = true, onTouchBackdrop, drawUnderStatusBar = false, gestureEnabled = false, isModal = true, snapPoints = [100], initialSnapIndex = 0, overdrawEnabled = true, overdrawFactor = 15, overdrawSize = 100, zIndex = 999, keyboardHandlerEnabled = true, ExtraOverlayComponent, payload, safeAreaInsets, routes, initialRoute, onBeforeShow, enableRouterBackNavigation, onBeforeClose, enableGesturesInScrollView = true, disableDragBeyondMinimumSnapPoint, ...props }, ref) { var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j, _k, _l, _m, _n; snapPoints = snapPoints[snapPoints.length - 1] !== 100 ? [...snapPoints, 100] : snapPoints; const initialValue = useRef(-1); const actionSheetHeight = useRef(0); const safeAreaPaddings = useRef( safeAreaInsets || { top: 0, left: 0, bottom: 0, right: 0 } ); const internalEventManager = React.useMemo(() => new EventManager(), []); const currentContext = useProviderContext(); const currentSnapIndex = useRef(initialSnapIndex); const sheetRef = useSheetRef(); const dimensionsRef = useRef({ width: 0, height: 0, portrait: true }); const minTranslateValue = useRef(0); const keyboardWasVisible = useRef(false); const prevKeyboardHeight = useRef(0); const id = useSheetIDContext(); const sheetId = props.id || id; const lock = useRef(false); const panViewRef = useRef(null); const rootViewContainerRef = useRef(null); const gestureBoundaries = useRef({}); const hiding = useRef(false); const payloadRef = useRef(payload); const sheetPayload = useSheetPayload(); const panHandlerRef = useRef(); const closing = useRef(false); const draggableNodes = useRef([]); const sheetLayoutRef = useRef(); const [dimensions, setDimensions] = useState({ width: Dimensions.get("window").width, height: 0, portrait: true, paddingBottom: (props == null ? void 0 : props.useBottomSafeAreaPadding) ? 25 : 0 }); const rootViewLayoutEventValues = useRef({}); if (safeAreaInsets) { safeAreaPaddings.current = safeAreaInsets; } const { visible, setVisible } = useSheetManager({ id: sheetId, onHide: (data) => { hideSheet(void 0, data, true); }, onBeforeShow: (data) => { var _a2; (_a2 = routerRef.current) == null ? void 0 : _a2.initialNavigation(); onBeforeShow == null ? void 0 : onBeforeShow(data); }, onContextUpdate: () => { if (sheetId) { SheetManager.add(sheetId, currentContext); SheetManager.registerRef(sheetId, currentContext, { current: getRef() }); } } }); const animations = useMemo( () => ({ opacity: new Animated.Value(0), translateY: new Animated.Value(0), underlayTranslateY: new Animated.Value(100), keyboardTranslate: new Animated.Value(0), routeOpacity: new Animated.Value(0) }), [] ); const animationListeners = useRef({}); const router = useRouter({ routes, getRef: () => getRef(), initialRoute, onNavigate: props.onNavigate, onNavigateBack: props.onNavigateBack, routeOpacity: animations.routeOpacity }); const routerRef = useRef(router); payloadRef.current = payload; routerRef.current = router; const keyboard = useKeyboard(keyboardHandlerEnabled); const prevSnapIndex = useRef(initialSnapIndex); const draggableNodesContext = React.useMemo( () => ({ nodes: draggableNodes }), [] ); const notifyOffsetChange = (value) => { internalEventManager.publish("onoffsetchange", value); }; const notifySnapIndexChanged = React.useCallback(() => { var _a2; if (prevSnapIndex.current !== currentSnapIndex.current) { prevSnapIndex.current = currentSnapIndex.current; (_a2 = props.onSnapIndexChange) == null ? void 0 : _a2.call(props, currentSnapIndex.current); } }, [props.onSnapIndexChange]); const returnAnimation = React.useCallback( (velocity) => { if (!animated) { animations.translateY.setValue(initialValue.current); return; } const config = props.openAnimationConfig; const correctedValue = initialValue.current > minTranslateValue.current ? initialValue.current : 0; notifyOffsetChange(correctedValue); if (!config) { Animated.spring(animations.translateY, { toValue: initialValue.current, useNativeDriver: true, friction: 8, ...config, velocity: typeof velocity !== "number" ? void 0 : velocity }).start(); } else { Animated.spring(animations.translateY, { toValue: initialValue.current, useNativeDriver: true, ...config, velocity: typeof velocity !== "number" ? void 0 : velocity }).start(); } notifySnapIndexChanged(); }, // eslint-disable-next-line react-hooks/exhaustive-deps [animated, props.openAnimationConfig] ); const opacityAnimation = React.useCallback( (opacity) => { Animated.timing(animations.opacity, { duration: 150, easing: Easing.in(Easing.ease), toValue: opacity, useNativeDriver: true }).start(); }, // eslint-disable-next-line react-hooks/exhaustive-deps [] ); const hideAnimation = React.useCallback( (vy, callback) => { if (!animated) { callback == null ? void 0 : callback({ finished: true }); return; } const config = props.closeAnimationConfig; opacityAnimation(0); const animation = Animated.spring(animations.translateY, { velocity: typeof vy !== "number" ? 3 : vy + 1, toValue: dimensionsRef.current.height * 1.3, useNativeDriver: true, ...config }); animation.start(); setTimeout(() => { animation.stop(); callback == null ? void 0 : callback({ finished: true }); }, 150); }, // eslint-disable-next-line react-hooks/exhaustive-deps [animated, opacityAnimation, props.closeAnimationConfig] ); const getCurrentPosition = React.useCallback(() => { return animations.translateY._value <= minTranslateValue.current + 5 ? 0 : animations.translateY._value; }, []); const getNextPosition = React.useCallback( (snapIndex) => { return actionSheetHeight.current + minTranslateValue.current - actionSheetHeight.current * snapPoints[snapIndex] / 100; }, [snapPoints] ); const hardwareBackPressEvent = useRef(); const Root = isModal && !(props == null ? void 0 : props.backgroundInteractionEnabled) ? Modal : Animated.View; useEffect(() => { let animationListener; if (drawUnderStatusBar || props.onChange) { animationListener = animations.translateY.addListener((value) => { var _a2; const correctedValue = value.value > minTranslateValue.current ? value.value - minTranslateValue.current : 0; (_a2 = props == null ? void 0 : props.onChange) == null ? void 0 : _a2.call(props, correctedValue, actionSheetHeight.current); if (drawUnderStatusBar) { if (lock.current) return; const correctedHeight = keyboard.keyboardShown ? dimensionsRef.current.height - (keyboard.keyboardHeight + safeAreaPaddings.current.bottom) : dimensionsRef.current.height - safeAreaPaddings.current.bottom; if (actionSheetHeight.current >= correctedHeight - 1) { if (value.value < 100) { animations.underlayTranslateY.setValue( Math.max(value.value - 20, -20) ); } else { if (animations.underlayTranslateY._value !== 100) { animations.underlayTranslateY.setValue(100); } } } else { if (animations.underlayTranslateY._value !== 100) { animations.underlayTranslateY.setValue(100); } } } }); } animationListeners.current.translateY = animationListener; return () => { animationListener && animations.translateY.removeListener(animationListener); }; }, [props == null ? void 0 : props.id, keyboard.keyboardShown, keyboard.keyboardHeight]); const onSheetLayout = React.useCallback( (event) => { var _a2, _b2; sheetLayoutRef.current = { ...event.nativeEvent.layout }; if (rootViewLayoutEventValues.current.resizing) return; if (closing.current) return; const rootViewHeight = (_a2 = dimensionsRef.current) == null ? void 0 : _a2.height; actionSheetHeight.current = event.nativeEvent.layout.height > dimensionsRef.current.height ? dimensionsRef.current.height : event.nativeEvent.layout.height; minTranslateValue.current = rootViewHeight - (actionSheetHeight.current + safeAreaPaddings.current.bottom); if (initialValue.current < 0) { animations.translateY.setValue(rootViewHeight * 1.1); } const nextInitialValue = actionSheetHeight.current + minTranslateValue.current - actionSheetHeight.current * snapPoints[currentSnapIndex.current] / 100; initialValue.current = (keyboard.keyboardShown || keyboardWasVisible.current) && initialValue.current <= nextInitialValue && initialValue.current >= minTranslateValue.current ? initialValue.current : nextInitialValue; const sheetBottomEdgePosition = initialValue.current + actionSheetHeight.current * snapPoints[currentSnapIndex.current] / 100; const sheetPositionWithKeyboard = sheetBottomEdgePosition - (((_b2 = dimensionsRef.current) == null ? void 0 : _b2.height) - keyboard.keyboardHeight); initialValue.current = sheetPositionWithKeyboard > 0 ? initialValue.current - sheetPositionWithKeyboard : initialValue.current; if (keyboard.keyboardShown) { minTranslateValue.current = minTranslateValue.current - (keyboard.keyboardHeight + safeAreaPaddings.current.bottom); keyboardWasVisible.current = true; prevKeyboardHeight.current = keyboard.keyboardHeight; } else { keyboardWasVisible.current = false; } opacityAnimation(1); setTimeout(() => { returnAnimation(); }, 1); if (initialValue.current > 100) { if (lock.current) return; animations.underlayTranslateY.setValue(100); } if (Platform.OS === "web") { document.body.style.overflowY = "hidden"; document.documentElement.style.overflowY = "hidden"; } }, [ snapPoints, keyboard.keyboardShown, keyboard.keyboardHeight, opacityAnimation, animations.translateY, animations.underlayTranslateY, returnAnimation ] ); const onRootViewLayout = React.useCallback( (event) => { var _a2, _b2; if (keyboard.keyboardShown && !isModal) { return; } rootViewLayoutEventValues.current.resizing = true; const rootViewHeight = event.nativeEvent.layout.height; const rootViewWidth = event.nativeEvent.layout.width; (_a2 = rootViewLayoutEventValues.current.sub) == null ? void 0 : _a2.unsubscribe(); rootViewLayoutEventValues.current.sub = internalEventManager.subscribe( EVENTS_INTERNAL.safeAreaLayout, () => { var _a3; (_a3 = rootViewLayoutEventValues.current.sub) == null ? void 0 : _a3.unsubscribe(); const safeMarginFromTop = Platform.OS === "ios" ? safeAreaPaddings.current.top < 20 ? 20 : safeAreaPaddings.current.top : StatusBar.currentHeight || 0; const height = rootViewHeight - safeMarginFromTop; const width = rootViewWidth; dimensionsRef.current = { width, height, portrait: width < height }; setDimensions({ ...dimensionsRef.current }); rootViewLayoutEventValues.current.resizing = false; if (sheetLayoutRef.current) { onSheetLayout({ nativeEvent: { layout: sheetLayoutRef.current } }); } } ); clearTimeout(rootViewLayoutEventValues.current.timer); clearTimeout(rootViewLayoutEventValues.current.layouTimer); if (safeAreaPaddings.current.top !== void 0 || Platform.OS !== "ios") { rootViewLayoutEventValues.current.layouTimer = setTimeout( () => { internalEventManager.publish(EVENTS_INTERNAL.safeAreaLayout); }, Platform.OS === "ios" || rootViewLayoutEventValues.current.firstEventFired ? 0 : 300 ); } if (!((_b2 = rootViewLayoutEventValues.current) == null ? void 0 : _b2.firstEventFired)) { rootViewLayoutEventValues.current.firstEventFired = true; } }, [keyboard.keyboardShown, isModal, internalEventManager, onSheetLayout] ); const hideSheet = React.useCallback( (vy, data, isSheetManagerOrRef) => { if (hiding.current) return; if (!closable && !isSheetManagerOrRef) { returnAnimation(vy); return; } hiding.current = true; onBeforeClose == null ? void 0 : onBeforeClose(data || payloadRef.current || data); setTimeout(() => { if (closable) { closing.current = true; Keyboard.dismiss(); animationListeners.current.translateY && animations.translateY.removeListener( animationListeners.current.translateY ); animationListeners.current.translateY = void 0; } hideAnimation(vy, ({ finished }) => { var _a2, _b2; if (finished) { if (closable || isSheetManagerOrRef) { setVisible(false); if (props.onClose) { (_a2 = props.onClose) == null ? void 0 : _a2.call( props, data || payloadRef.current || data ); hiding.current = false; } (_b2 = hardwareBackPressEvent.current) == null ? void 0 : _b2.remove(); if (sheetId) { SheetManager.remove(sheetId, currentContext); hiding.current = false; actionSheetEventManager.publish( `onclose_${sheetId}`, data || payloadRef.current || data, currentContext ); } else { hiding.current = false; } currentSnapIndex.current = initialSnapIndex; closing.current = false; setTimeout(() => { keyboard.reset(); }); } else { animations.opacity.setValue(1); returnAnimation(); } } }); }, 1); if (Platform.OS === "web") { document.body.style.overflowY = "auto"; document.documentElement.style.overflowY = "auto"; } }, // eslint-disable-next-line react-hooks/exhaustive-deps [ closable, hideAnimation, props.onClose, returnAnimation, setVisible, keyboard ] ); const onHardwareBackPress = React.useCallback(() => { var _a2, _b2; if (visible && enableRouterBackNavigation && ((_a2 = routerRef.current) == null ? void 0 : _a2.canGoBack())) { (_b2 = routerRef.current) == null ? void 0 : _b2.goBack(); return true; } if (visible && closable && closeOnPressBack) { hideSheet(); return true; } return false; }, [ closable, closeOnPressBack, hideSheet, enableRouterBackNavigation, visible ]); const snapForward = React.useCallback( (vy) => { if (currentSnapIndex.current === snapPoints.length - 1) { initialValue.current = getNextPosition(currentSnapIndex.current); returnAnimation(vy); return; } let nextSnapPoint = 0; let nextSnapIndex = 0; if (getCurrentPosition() === 0) { nextSnapPoint = snapPoints[nextSnapIndex = snapPoints.length - 1]; } else { for (let i = currentSnapIndex.current; i < snapPoints.length; i++) { if (getNextPosition(i) < getCurrentPosition()) { nextSnapPoint = snapPoints[nextSnapIndex = i]; break; } } } if (nextSnapPoint > 100) { console.warn("Snap points should range between 0 to 100."); returnAnimation(vy); return; } currentSnapIndex.current = nextSnapIndex; initialValue.current = getNextPosition(currentSnapIndex.current); returnAnimation(vy); }, [getCurrentPosition, getNextPosition, returnAnimation, snapPoints] ); const snapBackward = React.useCallback( (vy) => { if (currentSnapIndex.current === 0) { if (closable) { initialValue.current = dimensionsRef.current.height * 1.3; hideSheet(vy); } else { initialValue.current = getNextPosition(currentSnapIndex.current); returnAnimation(vy); } return; } let nextSnapPoint = 0; let nextSnapIndex = 0; for (let i = currentSnapIndex.current; i > -1; i--) { if (getNextPosition(i) > getCurrentPosition()) { nextSnapPoint = snapPoints[nextSnapIndex = i]; break; } } if (nextSnapPoint < 0) { console.warn("Snap points should range between 0 to 100."); returnAnimation(vy); return; } currentSnapIndex.current = nextSnapIndex; initialValue.current = getNextPosition(currentSnapIndex.current); returnAnimation(vy); }, [ closable, getCurrentPosition, getNextPosition, hideSheet, returnAnimation, snapPoints ] ); function getRectBoundary(rect) { if (rect) { const { w, h, px, py } = rect; return { ...rect, boundryX: px + w, boundryY: py + h }; } return { w: 0, h: 0, px: 0, py: 0, x: 0, y: 0, boundryX: 0, boundryY: 0 }; } const getActiveDraggableNodes = React.useCallback( (absoluteX, absoluteY) => { var _a2; if (((_a2 = draggableNodes.current) == null ? void 0 : _a2.length) === 0) return []; const activeNodes = []; for (const node of draggableNodes.current) { const rect = getRectBoundary(node.rect.current); if (rect.boundryX === 0 && rect.boundryY === 0) continue; if (absoluteX > rect.px && absoluteY > rect.py && absoluteX < rect.boundryX && absoluteY < rect.boundryY) { activeNodes.push({ rectWithBoundry: rect, node }); } } return activeNodes; }, [] ); const panHandlers = React.useMemo(() => { let velocity = 0; let prevDeltaY = 0; let offsets = []; let lockGesture = false; let gestureEventCounter = 0; let isScrollingGesture = false; let deltaYOnGestureStart = 0; let start; function scrollable(value) { if (Platform.OS === "ios") return; for (let i = 0; i < draggableNodes.current.length; i++) { const node = draggableNodes.current[i]; const scrollRef = resolveScrollRef(node.ref); scrollRef == null ? void 0 : scrollRef.setNativeProps({ scrollEnabled: value }); } } return Platform.OS === "web" ? { enabled: false } : { onBegan: () => { if (Platform.OS === "android") { scrollable(false); } }, onGestureEvent(event) { var _a2, _b2; if (sheetId && !isRenderedOnTop(sheetId, currentContext)) return; const gesture = event.nativeEvent; let deltaY = gesture.translationY; const swipingDown = prevDeltaY < deltaY; prevDeltaY = deltaY; if (!start) { start = { x: event.nativeEvent.absoluteX, y: event.nativeEvent.absoluteY }; } const activeDraggableNodes = getActiveDraggableNodes( start.x, start.y ); const isFullOpen = getCurrentPosition() === 0; let blockSwipeGesture = false; if (activeDraggableNodes.length > 0) { if (isFullOpen) { if (swipingDown) { for (const node of activeDraggableNodes) { if (!node.node.offset.current) continue; const { y } = node.node.offset.current; if (y === ScrollState.END) { blockSwipeGesture = true; continue; } if (y < 1 && node.node.handlerConfig.hasRefreshControl) { const refreshControlBounds = node.rectWithBoundry.py + node.rectWithBoundry.h * node.node.handlerConfig.refreshControlBoundary; if (!refreshControlBounds) continue; if (event.nativeEvent.absoluteY < refreshControlBounds) { lockGesture = true; blockSwipeGesture = false; continue; } else { blockSwipeGesture = false; continue; } } if (y > 1) { blockSwipeGesture = true; } } } else { for (const node of activeDraggableNodes) { if (!node.node.offset.current) continue; const { y } = node.node.offset.current; if ( // Scroll has not reached end y !== ScrollState.END ) { blockSwipeGesture = true; } } } } } gestureEventCounter++; if (isFullOpen && (blockSwipeGesture || lockGesture)) { isScrollingGesture = true; scrollable(true); return; } const startY = event.nativeEvent.y - event.nativeEvent.translationY; if (!enableGesturesInScrollView && startY > 100) { return; } if (gestureEventCounter < 2) { return; } if (swipingDown || !isFullOpen) { if (Platform.OS === "ios") { for (let i = 0; i < draggableNodes.current.length; i++) { const node = draggableNodes.current[i]; const scrollRef = resolveScrollRef(node.ref); if (!offsets[i] || ((_a2 = node.offset.current) == null ? void 0 : _a2.y) === 0) { offsets[i] = ((_b2 = node.offset.current) == null ? void 0 : _b2.y) || 0; } if (scrollRef == null ? void 0 : scrollRef.current) { scrollRef.current.scrollTo({ x: 0, y: offsets[i], animated: false }); } } } } if (!isFullOpen) { isScrollingGesture = false; blockSwipeGesture = false; } if (isScrollingGesture && !swipingDown) { return scrollable(true); } else { scrollable(false); } isScrollingGesture = false; if (!deltaYOnGestureStart) { deltaYOnGestureStart = deltaY; } deltaY = deltaY - deltaYOnGestureStart; const value = initialValue.current + deltaY; velocity = 1; const correctedValue = value <= minTranslateValue.current ? minTranslateValue.current - value : value; if (correctedValue / overdrawFactor >= overdrawSize && deltaY <= 0) { return; } const minSnapPoint = getNextPosition(0); const translateYValue = value <= minTranslateValue.current ? overdrawEnabled ? minTranslateValue.current - correctedValue / overdrawFactor : minTranslateValue.current : value; if (!closable && disableDragBeyondMinimumSnapPoint) { animations.translateY.setValue( translateYValue >= minSnapPoint ? minSnapPoint : translateYValue ); } else { animations.translateY.setValue(translateYValue); } }, failOffsetX: [-20, 20], activeOffsetY: [-5, 5], onEnded() { deltaYOnGestureStart = 0; offsets = []; start = void 0; isScrollingGesture = false; gestureEventCounter = 0; lockGesture = false; const isMovingUp = getCurrentPosition() < initialValue.current; scrollable(true); if (!isMovingUp && getCurrentPosition() < initialValue.current + springOffset || isMovingUp && getCurrentPosition() > initialValue.current - springOffset) { returnAnimation(1); velocity = 0; return; } if (!isMovingUp) { snapBackward(velocity); } else { snapForward(velocity); } velocity = 0; }, enabled: gestureEnabled }; }, [ animations.translateY, closable, currentContext, disableDragBeyondMinimumSnapPoint, enableGesturesInScrollView, gestureEnabled, getActiveDraggableNodes, getCurrentPosition, getNextPosition, overdrawEnabled, overdrawFactor, overdrawSize, returnAnimation, sheetId, snapBackward, snapForward, springOffset ]); const handlers = React.useMemo(() => { let prevDeltaY = 0; let lockGesture = false; let offsets = []; let start; let deltaYOnGestureStart = 0; return !gestureEnabled || Platform.OS !== "web" ? { panHandlers: {} } : PanResponder.create({ onMoveShouldSetPanResponder: (_event, gesture) => { if (sheetId && !isRenderedOnTop(sheetId, currentContext)) return false; const vy = gesture.vy < 0 ? gesture.vy * -1 : gesture.vy; const vx = gesture.vx < 0 ? gesture.vx * -1 : gesture.vx; if (vy < 0.05 || vx > 0.05) { return false; } const activeDraggableNodes = getActiveDraggableNodes( _event.nativeEvent.pageX, _event.nativeEvent.pageY ); for (const node of activeDraggableNodes) { const scrollRef = resolveScrollRef(node.node.ref); offsets.push(scrollRef.scrollTop); } return true; }, onStartShouldSetPanResponder: (_event, _gesture) => { if (sheetId && !isRenderedOnTop(sheetId, currentContext)) return false; const activeDraggableNodes = getActiveDraggableNodes( _event.nativeEvent.pageX, _event.nativeEvent.pageY ); for (const node of activeDraggableNodes) { const scrollRef = resolveScrollRef(node.node.ref); offsets.push(scrollRef.scrollTop); } return true; }, onPanResponderMove: (_event, gesture) => { let deltaY = gesture.dy; const swipingDown = prevDeltaY < deltaY; prevDeltaY = deltaY; const isFullOpen = getCurrentPosition() === 0; let blockSwipeGesture = false; if (!start) { start = { x: _event.nativeEvent.pageX, y: _event.nativeEvent.pageY }; } const activeDraggableNodes = getActiveDraggableNodes( start.x, start.y ); if (activeDraggableNodes.length > 0) { if (isFullOpen) { if (swipingDown) { for (let i = 0; i < activeDraggableNodes.length; i++) { const node = activeDraggableNodes[i]; if (!node.node.offset.current) continue; const { y } = node.node.offset.current; if (y === ScrollState.END) { blockSwipeGesture = true; const scrollRef = resolveScrollRef(node.node.ref); offsets[i] = scrollRef.scrollTop; continue; } if (y === 0 && node.node.handlerConfig.hasRefreshControl) { const refreshControlBounds = node.rectWithBoundry.py + node.rectWithBoundry.h * node.node.handlerConfig.refreshControlBoundary; if (!refreshControlBounds) continue; if (_event.nativeEvent.pageY < refreshControlBounds) { lockGesture = true; blockSwipeGesture = false; continue; } else { blockSwipeGesture = false; continue; } } if (y > 5) { blockSwipeGesture = true; const scrollRef = resolveScrollRef(node.node.ref); offsets[i] = scrollRef.scrollTop; } } } else { for (let i = 0; i < activeDraggableNodes.length; i++) { const node = activeDraggableNodes[i]; if (!node.node.offset.current) continue; const { y } = node.node.offset.current; if (y > -1) { blockSwipeGesture = true; } } } } else { for (let i = 0; i < activeDraggableNodes.length; i++) { const node = activeDraggableNodes[i]; const scrollRef = resolveScrollRef(node.node.ref); scrollRef.scrollTop = offsets[i]; } } } if (blockSwipeGesture || lockGesture) { return; } const startY = gesture.moveY - gesture.dy; if (!enableGesturesInScrollView && startY > 100) { return; } if (!deltaYOnGestureStart) { deltaYOnGestureStart = deltaY; } deltaY = deltaY - deltaYOnGestureStart; const value = initialValue.current + deltaY; const correctedValue = value <= minTranslateValue.current ? minTranslateValue.current - value : value; if (correctedValue / overdrawFactor >= overdrawSize && gesture.dy <= 0) { return; } const minSnapPoint = getNextPosition(0); const translateYValue = value <= minTranslateValue.current ? overdrawEnabled ? minTranslateValue.current - correctedValue / overdrawFactor : minTranslateValue.current : value; if (!closable && disableDragBeyondMinimumSnapPoint) { animations.translateY.setValue( translateYValue >= minSnapPoint ? minSnapPoint : translateYValue ); } else { animations.translateY.setValue(translateYValue); } }, onPanResponderEnd: (_event, gesture) => { start = void 0; offsets = []; prevDeltaY = 0; deltaYOnGestureStart = 0; const isMovingUp = getCurrentPosition() < initialValue.current; if (!isMovingUp && getCurrentPosition() < initialValue.current + springOffset || isMovingUp && getCurrentPosition() > initialValue.current - springOffset) { returnAnimation(gesture.vy); return; } if (!isMovingUp) { snapBackward(gesture.vy); } else { snapForward(gesture.vy); } } }); }, [ gestureEnabled, sheetId, currentContext, getActiveDraggableNodes, getCurrentPosition, enableGesturesInScrollView, overdrawFactor, overdrawSize, getNextPosition, overdrawEnabled, closable, disableDragBeyondMinimumSnapPoint, animations.translateY, springOffset, returnAnimation, snapBackward, snapForward ]); const onTouch = (event) => { onTouchBackdrop == null ? void 0 : onTouchBackdrop(event); if (enableRouterBackNavigation && router.canGoBack()) { router.goBack(); return; } if (closeOnTouchBackdrop && closable) { hideSheet(); } }; const getRef = useCallback( () => ({ show: (snapIndex) => { var _a2; if (typeof snapIndex === "number") { currentSnapIndex.current = snapIndex; } onBeforeShow == null ? void 0 : onBeforeShow(); (_a2 = routerRef.current) == null ? void 0 : _a2.initialNavigation(); setVisible(true); }, hide: (data) => { hideSheet(void 0, data, true); }, setModalVisible: (_visible) => { if (_visible) { setVisible(true); } else { hideSheet(); } }, snapToOffset: (offset) => { initialValue.current = actionSheetHeight.current + minTranslateValue.current - actionSheetHeight.current * offset / 100; Animated.spring(animations.translateY, { toValue: initialValue.current, useNativeDriver: true, ...props.openAnimationConfig }).start(); }, snapToRelativeOffset: (offset) => { if (offset === 0) { getRef().snapToIndex(currentSnapIndex.current); return; } const availableHeight = actionSheetHeight.current + minTranslateValue.current; initialValue.current = initialValue.current + initialValue.current * (offset / 100); if (initialValue.current > availableHeight) { getRef().snapToOffset(100); return; } Animated.spring(animations.translateY, { toValue: initialValue.current, useNativeDriver: true, ...props.openAnimationConfig }).start(); }, snapToIndex: (index) => { if (index > snapPoints.length || index < 0) return; currentSnapIndex.current = index; initialValue.current = getNextPosition(index); Animated.spring(animations.translateY, { toValue: initialValue.current, useNativeDriver: true, ...props.openAnimationConfig }).start(); notifySnapIndexChanged(); }, handleChildScrollEnd: () => { console.warn( "handleChildScrollEnd has been removed. Please use `useScrollHandlers` hook to enable scrolling in ActionSheet" ); }, modifyGesturesForLayout: (_id, layout, scrollOffset) => { gestureBoundaries.current[_id] = { ...layout, scrollOffset }; }, currentSnapIndex: () => currentSnapIndex.current, isGestureEnabled: () => gestureEnabled, isOpen: () => visible, keyboardHandler: (enabled) => { keyboard.pauseKeyboardHandler.current = enabled; }, ev: internalEventManager }), [ internalEventManager, onBeforeShow, setVisible, hideSheet, animations.translateY, props.openAnimationConfig, snapPoints.length, getNextPosition, notifySnapIndexChanged, gestureEnabled, visible, keyboard.pauseKeyboardHandler ] ); useImperativeHandle(ref, getRef, [getRef]); useEffect(() => { if (sheetId) { SheetManager.registerRef(sheetId, currentContext, { current: getRef() }); } sheetRef.current = getRef(); }, [currentContext, getRef, sheetId, sheetRef]); const onRequestClose = React.useCallback(() => { var _a2, _b2; if (enableRouterBackNavigation && ((_a2 = routerRef.current) == null ? void 0 : _a2.canGoBack())) { (_b2 = routerRef.current) == null ? void 0 : _b2.goBack(); return; } if (visible && closeOnPressBack) { hideSheet(); } }, [hideSheet, enableRouterBackNavigation, closeOnPressBack, visible]); const rootProps = React.useMemo( () => { var _a2, _b2; return isModal && !props.backgroundInteractionEnabled ? { visible: true, animationType: "none", testID: ((_a2 = props.testIDs) == null ? void 0 : _a2.modal) || props.testID, supportedOrientations: SUPPORTED_ORIENTATIONS, onShow: props.onOpen, onRequestClose, transparent: true, /** * Always true, it causes issue with keyboard handling. */ statusBarTranslucent: true } : { testID: ((_b2 = props.testIDs) == null ? void 0 : _b2.root) || props.testID, onLayout: () => { var _a3; hardwareBackPressEvent.current = BackHandler.addEventListener( "hardwareBackPress", onHardwareBackPress ); (_a3 = props == null ? void 0 : props.onOpen) == null ? void 0 : _a3.call(props); }, style: { position: "absolute", zIndex: zIndex ? zIndex : sheetId ? getZIndexFromStack(sheetId, currentContext) : 999, width: "100%", height: "100%" }, pointerEvents: (props == null ? void 0 : props.backgroundInteractionEnabled) ? "box-none" : "auto" }; }, [ currentContext, isModal, onHardwareBackPress, onRequestClose, props, zIndex, sheetId ] ); const renderRoute = useCallback( (route) => { var _a2; const RouteComponent = route.component; return /* @__PURE__ */ jsx( Animated.View, { style: { display: route.name !== ((_a2 = router.currentRoute) == null ? void 0 : _a2.name) ? "none" : "flex", opacity: animations.routeOpacity }, children: /* @__PURE__ */ jsx(RouterParamsContext.Provider, { value: route == null ? void 0 : route.params, children: /* @__PURE__ */ jsx( RouteComponent, { router, params: route == null ? void 0 : route.params, payload: sheetPayload } ) }) }, route.name ); }, [animations.routeOpacity, router, sheetPayload] ); const context = { ref: panHandlerRef, eventManager: internalEventManager }; return /* @__PURE__ */ jsxs(View, { children: [ Platform.OS === "ios" && !safeAreaInsets ? /* @__PURE__ */ jsx( SafeAreaView, { pointerEvents: "none", collapsable: false, onLayout: (event) => { const height = event.nativeEvent.layout.height; if (height !== void 0) { safeAreaPaddings.current.top = height; clearTimeout(rootViewLayoutEventValues.current.timer); rootViewLayoutEventValues.current.timer = setTimeout(() => { internalEventManager.publish(EVENTS_INTERNAL.safeAreaLayout); }, 0); } }, style: { position: "absolute", width: 1, height: 0, top: 0, left: 0, backgroundColor: "transparent" }, children: /* @__PURE__ */ jsx(View, {}) } ) : null, Platform.OS === "ios" && !safeAreaInsets ? /* @__PURE__ */ jsx( SafeAreaView, { pointerEvents: "none", collapsable: false, onLayout: (event) => { const height = event.nativeEvent.layout.height; if (height !== void 0) { safeAreaPaddings.current.bottom = height; clearTimeout(rootViewLayoutEventValues.current.timer); rootViewLayoutEventValues.current.timer = setTimeout(() => { internalEventManager.publish(EVENTS_INTERNAL.safeAreaLayout); }, 0); } }, style: { position: "absolute", width: 1, height: 1, bottom: 0, left: 0, backgroundColor: "transparent" }, children: /* @__PURE__ */ jsx(View, {}) } ) : null, visible ? /* @__PURE__ */ jsx(Root, { ...rootProps, children: /* @__PURE__ */ jsx( GestureHandlerRoot, { isModal, style: styles.parentContainer, children: /* @__PURE__ */ jsx(PanGestureRefContext.Provider, { value: context, children: /* @__PURE__ */ jsx(DraggableNodesContext.Provider, { value: draggableNodesContext, children: /* @__PURE__ */ jsxs( Animated.View, { onLayout: onRootViewLayout, ref: rootViewContainerRef, pointerEvents: (props == null ? void 0 : props.backgroundInteractionEnabled) ? "box-none" : "auto", style: [ styles.parentContainer, { opacity: animations.opacity, width: "100%", justifyContent: "flex-end", transform: [ { translateY: animations.keyboardTranslate } ] } ], children: [ !(props == null ? void 0 : props.backgroundInteractionEnabled) ? /* @__PURE__ */ jsx( TouchableOpacity, { onPress: onTouch, activeOpacity: defaultOverlayOpacity, testID: (_a = props.testIDs) == null ? void 0 : _a.backdrop, style: { height: dimensions.height + (safeAreaPaddings.current.top || 0) + 100, width: "100%", position: "absolute", backgroundColor: overlayColor, opacity: defaultOverlayOpacity }, ...props.backdropProps ? props.backdropProps : {} } ) : null, /* @__PURE__ */ jsxs( Animated.View, { pointerEvents: "box-none", style: { borderTopRightRadius: ((_b = props.containerStyle) == null ? void 0 : _b.borderTopRightRadius) || 10, borderTopLeftRadius: ((_c = props.containerStyle) == null ? void 0 : _c.borderTopLeftRadius) || 10, backgroundColor: ((_d = props.containerStyle) == null ? void 0 : _d.backgroundColor) || "white", borderBottomLeftRadius: ((_e = props.containerStyle) == null ? void 0 : _e.borderBottomLeftRadius) || void 0, borderBottomRightRadius: ((_f = props.containerStyle) == null ? void 0 : _f.borderBottomRightRadius) || void 0, borderRadius: ((_g = props.containerStyle) == null ? void 0 : _g.borderRadius) || void 0, width: ((_h = props.containerStyle) == null ? void 0 : _h.width) || "100%", ...getElevation( typeof elevation === "number" ? elevation : 5 ), flex: void 0, height: dimensions.height, maxHeight: dimensions.height, paddingBottom: keyboard.keyboardShown ? keyboard.keyboardHeight || 0 : safeAreaPaddings.current.bottom, //zIndex: 10, transform: [ { translateY: animations.translateY } ] }, children: [ dimensions.height === 0 ? null : /* @__PURE__ */ jsx(PanGestureHandler, { ...panHandlers, ref: panHandlerRef, children: /* @__PURE__ */ jsxs( Animated.View, { ...handlers.panHandlers, onLayout: onSheetLayout, ref: panViewRef, testID: (_i = props.testIDs) == null ? void 0 : _i.sheet, style: [ styles.container, { borderTopRightRadius: 10, borderTopLeftRadius: 10 }, props.containerStyle, { maxHeight: keyboard.keyboardShown ? dimensions.height - keyboard.keyboardHeight : dimensions.height, marginTop: keyboard.keyboardShown ? 0.5 : 0 } ], children: [ drawUnderStatusBar ? /* @__PURE__ */ jsx( Animated.View, { style: { height: 100, position: "absolute", top: -50, backgroundColor: ((_j = props.containerStyle) == null ? void 0 : _j.backgroundColor) || "white", width: "100%", borderTopRightRadius: ((_k = props.containerStyle) == null ? void 0 : _k.borderRadius) || 10, borderTopLeftRadius: ((_l = props.containerStyle) == null ? void 0 : _l.borderRadius) || 10, transform: [ { translateY: animations.underlayTranslateY } ] } } ) : null, gestureEnabled || props.headerAlwaysVisible ? props.CustomHeaderComponent ? props.CustomHeaderComponent : /* @__PURE__ */ jsx( Animated.View, { style: [ styles.indicator, props.indicatorStyle ] } ) : null, /* @__PURE__ */ jsx( View, { style: { flexShrink: 1 }, children: (router == null ? void 0 : router.hasRoutes()) ? /* @__PURE__ */ jsx(RouterContext.Provider, { value: router, children: router == null ? void 0 : router.stack.map(renderRoute) }) : props == null ? void 0 :