@attio/react-native-bottom-sheet-toolbox
Version:
108 lines (105 loc) • 4.42 kB
JavaScript
;
import * as React from "react";
import { Dimensions } from "react-native";
import { Gesture } from "react-native-gesture-handler";
import { runOnJS, useAnimatedReaction, useSharedValue } from "react-native-reanimated";
import { useBottomSheetToolboxInternalContext } from "../context";
import { BottomSheetPositionBuiltInLockPriority, friction } from "../utils";
import { useBottomSheetPositionLock } from "./use-bottom-sheet-position-lock";
const LOCK_ID_NAMESPACE = "useBottomSheetDragGesture";
// Gesture velocity is calculated between gesture updates.
// If the user does not move their finger, the gesture doesn't see update
// and so the velocity is miss-leading (i.e. not 0)
// We detect this by checking the time between the last gesture update.
const SUPPRESS_VELOCITY_TIME_MS = 150;
export function useBottomSheetDragGesture({
id,
disabled = false,
clamped = false,
frozenSharedValue
}) {
const {
currentPositionSharedValue,
currentPositionLastVelocitySharedValue,
wrapperHeightSharedValue,
normalizedSnapPointsSharedValue
} = useBottomSheetToolboxInternalContext();
const {
lock,
release,
setPosition,
setVelocity,
canLockSharedValue,
hasLockSharedValue
} = useBottomSheetPositionLock(`${LOCK_ID_NAMESPACE}:${id}`, BottomSheetPositionBuiltInLockPriority.GESTURE);
const lastLockRef = React.useRef(id);
React.useEffect(() => {
if (id !== lastLockRef.current) throw new Error("Lock id can't be changed.");
}, [id]);
const startPositionSharedValue = useSharedValue(0);
const lastUpdateTime = useSharedValue(0);
const [enabled, setEnabled] = React.useState(false);
useAnimatedReaction(() => canLockSharedValue.get() || hasLockSharedValue.get(), (lockable, lastLockable) => {
if (lockable === lastLockable) return;
runOnJS(setEnabled)(lockable);
}, [canLockSharedValue, hasLockSharedValue.get]);
const [windowHeight, setWindowHeight] = React.useState(Dimensions.get("window").height);
React.useEffect(() => {
const handle = Dimensions.addEventListener("change", ({
window
}) => {
setWindowHeight(window.height);
});
return () => handle.remove();
}, []);
const frozenOriginOffsetYSharedValue = useSharedValue(0);
return Gesture.Pan().onBegin(() => {
if (!canLockSharedValue.get()) return;
startPositionSharedValue.set(currentPositionSharedValue.get());
frozenOriginOffsetYSharedValue.set(0);
}).onUpdate(evt => {
const normalizedSnapPoints = normalizedSnapPointsSharedValue.get();
const wrapperHeight = wrapperHeightSharedValue.get();
const lockable = canLockSharedValue.get() || hasLockSharedValue.get();
if (!lockable || !normalizedSnapPoints || !wrapperHeight) return;
// We want to lock onUpdate, rather than onBegin, so nested
// touches do no unnecessarily lock the position.
if (!hasLockSharedValue.get()) {
lock();
}
if (frozenSharedValue?.get()) {
frozenOriginOffsetYSharedValue.set(evt.translationY);
return;
}
setVelocity(evt.velocityY);
const rawPosition = startPositionSharedValue.get() - evt.translationY + frozenOriginOffsetYSharedValue.get();
const maxSnapPoint = Math.max(...normalizedSnapPoints);
const overshoot = Math.max(0, rawPosition - maxSnapPoint);
if (clamped) {
const clampedPosition = Math.max(Math.min(rawPosition, maxSnapPoint), 0);
setPosition(clampedPosition);
} else if (overshoot > 0) {
const topInset = Math.max(1, windowHeight - wrapperHeight);
const frictionRatio = friction(overshoot / topInset);
const frictionPosition = maxSnapPoint + topInset * frictionRatio;
const finalPosition = Math.min(frictionPosition, wrapperHeight + topInset);
setPosition(finalPosition);
} else {
setPosition(rawPosition);
}
lastUpdateTime.set(Date.now());
}).onFinalize(evt => {
if (!hasLockSharedValue.get()) return;
if (frozenSharedValue?.get()) {
setVelocity(0);
} else {
const currentTime = Date.now();
const velocity = -evt.velocityY;
const timeSinceLastUpdate = currentTime - lastUpdateTime.get();
currentPositionLastVelocitySharedValue.set(timeSinceLastUpdate > SUPPRESS_VELOCITY_TIME_MS ? 0 : velocity);
setVelocity(velocity);
}
release();
}).enabled(enabled && !disabled);
}
//# sourceMappingURL=use-bottom-sheet-drag-gesture.js.map