@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
JavaScript
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 :