UNPKG

@attio/react-native-bottom-sheet-toolbox

Version:
165 lines (154 loc) 7.3 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.useBottomSheetPositionLock = useBottomSheetPositionLock; var _react = _interopRequireDefault(require("react")); var _reactNative = require("react-native"); var _reactNativeReanimated = require("react-native-reanimated"); var _context = require("../context"); var _defaultSheetSnapPointSpringConfig = require("../utils/default-sheet-snap-point-spring-config"); var _useWorkletUiCallback = require("./use-worklet-ui-callback"); function _interopRequireDefault(e) { return e && e.__esModule ? e : { default: e }; } const PIXEL_RATIO = _reactNative.PixelRatio.get(); const MIN_PERCEIVED_JUMP_DISTANCE = 10; /** * A priority lock for controlling the position of the bottom-sheet. * This allows our bottom-sheet's position to be modified by multiple sources. */ function useBottomSheetPositionLock(id, priority) { const { isReadySharedValue, currentPositionSharedValue, currentPositionDestinationSharedValue, currentPositionLastVelocitySharedValue, currentPositionLockSharedValue } = (0, _context.useBottomSheetToolboxInternalContext)(); const canLockSharedValue = (0, _reactNativeReanimated.useDerivedValue)(() => { const isReady = isReadySharedValue.get(); const currentLock = currentPositionLockSharedValue.get(); return isReady && (currentLock === null || currentLock.priority < priority); }, [isReadySharedValue, currentPositionLockSharedValue, priority]); const hasLockSharedValue = (0, _reactNativeReanimated.useDerivedValue)(() => id === currentPositionLockSharedValue.get()?.id, [currentPositionLockSharedValue, id]); const lock = (0, _useWorkletUiCallback.useWorkletUiCallback)(() => { "worklet"; const currentLock = currentPositionLockSharedValue.get(); if (currentLock !== null) { if (priority < currentLock.priority) { // Lock checks should happen separately to lock requests. // So this should never happen. However, there are super // rare race conditions where this can happen in SharedValue // world. console.warn(`Unable to lock '${id}' as '${currentLock.id}' is already engaged.`); return; } if (id !== currentLock.id) { (0, _reactNativeReanimated.cancelAnimation)(currentPositionSharedValue); } } const newLock = { id, priority }; currentPositionLockSharedValue.set(newLock); }, [currentPositionLockSharedValue, currentPositionSharedValue, id, priority]); const release = (0, _useWorkletUiCallback.useWorkletUiCallback)(() => { "worklet"; const currentLock = currentPositionLockSharedValue.get(); if (currentLock?.id !== id) return; currentPositionLockSharedValue.set(null); }, [currentPositionLockSharedValue, id]); // Release lock when we unmount const unmountFn = () => { (0, _reactNativeReanimated.runOnUI)(release)(); }; const onUnmountRef = _react.default.useRef(unmountFn); onUnmountRef.current = unmountFn; _react.default.useLayoutEffect(() => () => onUnmountRef.current(), []); const setPositionInternal = (0, _useWorkletUiCallback.useWorkletUiCallback)(v => { "worklet"; const currentLock = currentPositionLockSharedValue.get(); if (currentLock !== null && currentLock.id !== id) { console.warn(`Cannot setPosition with '${id}' as '${currentLock.id}' is already engaged.`); return; } currentPositionSharedValue.set(v); if (typeof v === "number") { currentPositionDestinationSharedValue.set(v); } }, [currentPositionLockSharedValue, id, currentPositionSharedValue, currentPositionDestinationSharedValue.set]); const setPositionAnimate = (0, _useWorkletUiCallback.useWorkletUiCallback)((v, animationConfig, callback) => { "worklet"; const animConfigWithDefault = animationConfig || _defaultSheetSnapPointSpringConfig.DEFAULT_SHEET_SNAP_POINT_SPRING_CONFIG; const springAnim = (0, _reactNativeReanimated.withSpring)(v, animConfigWithDefault, callback); const oldOnStart = springAnim.onStart; /** * This is a fun bit of code. * * Sometimes the destination value we're animating to is itself being * animated. Reanimated does a good job of handling in-flight changes to * animation configs. * * However, we want changes to the destination value to supersede the * sheet's own physical behavior (i.e. its spring config). * * Another way to think of this is we want the sheet to appear as if its * easing consistently despite changes to its destination position. * * A practical example: if the sheet is opening simultaneously with the * keyboard, we want the sheet's travel to compensate for the keyboard's * height so it remains visible at all times. * * To achieve this, we use a rough heuristic to determine if the * destination delta can be stepped without the user noticing. If so, * we perform the step. */ springAnim.onStart = (nextAnimation, current, timestamp, previousAnimation) => { oldOnStart(nextAnimation, current, timestamp, previousAnimation); const tickDelta = // biome-ignore lint/complexity/useLiteralKeys: Property 'lastTickTime' comes from an index signature, so it must be accessed with ['lastTickTime']. Date.now() - (previousAnimation ? previousAnimation["lastTickTime"] : 0); const goodFrameRate = 1000 / 60; const deviatedGoodFrameRate = goodFrameRate * 1.25; const destinationAnimating = tickDelta <= deviatedGoodFrameRate; if (destinationAnimating) { const destinationDelta = nextAnimation.toValue - currentPositionDestinationSharedValue.get(); const physicalDistance = Math.abs(destinationDelta * PIXEL_RATIO); const minPerceivedJumpDistance = MIN_PERCEIVED_JUMP_DISTANCE * PIXEL_RATIO; if (physicalDistance < minPerceivedJumpDistance) { ; nextAnimation.current += destinationDelta; } } currentPositionDestinationSharedValue.set(nextAnimation.toValue); // biome-ignore lint/complexity/useLiteralKeys: Property 'lastTickTime' comes from an index signature, so it must be accessed with ['lastTickTime']. nextAnimation["lastTickTime"] = Date.now(); }; setPositionInternal(springAnim); }, [setPositionInternal, currentPositionDestinationSharedValue.get, currentPositionDestinationSharedValue.set]); const setPosition = (0, _useWorkletUiCallback.useWorkletUiCallback)(v => { "worklet"; setPositionAnimate(v, { duration: 0 }); }, [setPositionAnimate]); const setVelocity = (0, _useWorkletUiCallback.useWorkletUiCallback)(v => { "worklet"; const currentLock = currentPositionLockSharedValue.get(); if (currentLock !== null && currentLock.id !== id) { console.warn(`Cannot setVelocity with '${id}' as '${currentLock.id}' is already engaged.`); return; } currentPositionLastVelocitySharedValue.set(v); }, [currentPositionLockSharedValue, id, currentPositionLastVelocitySharedValue]); return { canLockSharedValue, hasLockSharedValue, lock, release, setPosition, setPositionAnimate, setVelocity }; } //# sourceMappingURL=use-bottom-sheet-position-lock.js.map